https://github.com/skx/cpmulator/
Alongside that there is a collection of CP/M binaries, including the Aztec C compiler:
https://github.com/skx/cpm-dist/
So you can easily have a stab at compiling code. I added a simple file-manager, in C, along with other sources, to give a useful demo. (Of course I spend more time writing code in Z80 assembler, or Turbo Pascal, rather than C).
The author has a followup post here for thos interested:
* Getting back into C programming for CP/M -- part 2 * https://kevinboone.me/cpm-c2.html
I also like RunCPM, which can also run on some microcontroller boards, for a very tiny CP/M 2.2 system: https://github.com/MockbaTheBorg/RunCPM
The README links to the author's CP/M software library as well.
If I remember correctly, Aztec C was from Mark Williams. It was also the basis for the c Compiler that came with Coherent OS.
But yes, things were far easier in the 80s, even on Minis which I worked on back then. These days development is just a series of Meetings, Agile Points, Scrums with maybe 2 hours of real work per week. Many people now tend to do their real work off-hours, a sad situation.
But I am looking for 1 more piece of hardware, then I can set up a DOS Machine to play with myself :)
>The Aztec compiler pre-dates ANSI C, and follows the archaic Kernigan & Ritchie syntax
I still do not like ANSI C standards after all these years.
This was totally me ~15 years ago in a Scrum place with an open floor plan, doing most of my work after everyone left in the evening or on holidays because it was quiet and I could finally get some stuff done. I wrote big pieces of the product by myself.
My first C compiler was on a VAX. I did have some C compiler for my ZX Spectrum at some much later point but I don't remember doing much with it. Then a series of compilers for PCs. One random memory is some sort of REPL C, maybe Interactive-C or something? But pretty quickly it was Microsoft and Borland.
EDIT: On a more serious note re: meetings and such. Part of the difference is that working in much larger teams and projects becomes less efficient and requires more communication. Mature projects also require less of the builder thing and more of the maintainer thing. Software lasts a long time and inevitably maintenance becomes the work most people end up doing.
Computing was a smaller world back then, the company was founded by Robert Swartz (father of Aaron Swartz) and named after his father William Mark Swartz.
Think about early video game development at large companies: One person (maybe two), six months. The company gave them room to practice their art, and the result sold a million copies.
These days everyone wants to cosplay Big Tech and worship abstraction layers, so you can't get all of the "stakeholders" in the same meeting in six months.
It's the only practical approach when offices are designed to be distracting and productivity-killing, meetings are incessant, and groupware delivers a constant stream of interrupts which one is expected to monitor and respond to quickly - especially while working remotely.
That would have been "Mark Williams C", also marketed as "Let's C" for MDSOS.
And you're absolutely right about all the agile bull...
Also if you like the Z80 you should try
https://en.wikipedia.org/wiki/Zilog_eZ80
Which is crazy fast not to mention the only 8-bit architecture that got extended to 24-bit addressing in a sane way with index registers. (Sorry the 65816 sucks)
They really didn't have any choice if they wanted to actually accomplish something.
The 8-bit machines of the day, CP/M running 1-2MHz 8080s, 2-4MHz Z80, with no memory, and glacial disk drives (with not a lot of capacity).
Go ahead and fire up a CP/M simulator that lets you change the clock rate, and dial it down to heritage levels (and even then it's not quite the same, the I/O is still too fast). Watching the clock tick by as you load the editor, load the file, make your changes, quit the editor, load the compiler, load the linker, test the program, then back to the editor. There is friction here, the process just drrraaagggsss.
Turbo Pascal was usable for small programs. In memory editor, compiling to memory, running from memory. Ziinng! Start writing things to disk, and you were back to square one. The best thing Turbo did was eliminate the linking step (at the cost of having to INCLUDE and recompile things every time).
It was a different time.
As someone who lived through that, we simply didn't know any better. Each generation got incrementally faster. There were few leaps in orders of magnitude.
But going back, whoo boy. Amazing anything got accomplished.
That's because the FAT file system used by CP/M didn't allow lower case letters, at all. In this case "no obvious way" == "impossible".
The stack problems mentioned were real. The stack size was set at compile time, and there was no way to extend it. Plus the stack was not just used by your software, but also hardware interrupts and their functions.
That's not true.
You could use lowercase file names.
Just fire up MS-BASIC, and save your file as "test.bas". You now have a lower case file name.
The problem is that all of the CCP utilities implicitly upshifted everything from the command line. So, with the stock set, you were out of luck.
You can go back into BASIC and KILL the file, so all was not lost.
But, the file system was perfectly capable of coping with lower case file names, just nothing else was.
Sure it did. Just start Microsoft BASIC on CP/M, type a program and save it as "hello". It will appear in the directory as "hello.BAS". Of course the CCP, the console command processor, will convert all file names to upper case, so you can neither type nor copy nor erase the file, but still it exists. You can even load it from MBASIC using LOAD.
You can have any characters you like in your CP/M file names. Sometimes I ended up with file names consisting of all blanks. I usually used a disk editor to deal with those, but there were lots of more convenient tools for the job.
I did not use Aztec C until a few years after I switched from CP/M to DOS, but I really liked it, and used it for several 68k bare-metal projects. I did poke around with BDS C on CP/M, but was immediately turned off by the lack of standard floating point support. (It did offer an odd BCD float library.)
Not if you use setrusage().
What I develop I always make sure I test on NetBSD and OpenBSD. That keeps me honest and those systems will find issues that Linux does not care about. I found many issues by testing on those systems.
Also, ignoring malloc() returns is dangerous if you want to port your application to a UNIX like AIX.
Is it old version of setrlimit()? Couldn't locate it in any of the man.*bsd.org.
Or if memory overcommit is disabled or an 'unreasonable' amount of memory was requested. So, no, malloc() doesn't always succeed.
True. But I think you meant KB.
Back in the days when CP/M was king, even 20MB Winchester hard drives were rare
CP/M programming is a lot of fun, even these days! I have a growing collection of retro machines running CP/M, my latest compiler has a CP/M backend, and I have even written a book about the design of a CP/M compiler: http://t3x.org/t3x/0/book.html
I get it though, if what you’re selling is “best practices” you’re obviously going to over complicate things. You’re likely also going to be very successful in marketing it to a profession where things are just… bad. I mean, in how many other branches of engineering is it considered natural that things just flat out fail as often as they do in IT? So it’s easy to sell “best practices”. Of course after three decades of peddling various principles and strategies and so on, our business is in even worse state than it was before.
In my country we’ve spent a literal metric fuck ton of money trying to replace some of the COBOL systems powering a lot of our most critical financial systems. From the core or our tax agency to banking. So far no one have been capable of doing it, despite various major contractors applying all sorts of “modern” strategies and tools.
Within Perl 1.0, part of the Configure script, you can see the list of machines it could build on here: https://github.com/kaworu/perl1/blob/ba165bbde4eef698ff9cc69...
attrlist="mc68000 sun gcos unix ibm gimpel interdata tss os mert pyr"
attrlist="$attrlist vax pdp11 i8086 z8000 u3b2 u3b5 u3b20 u3b200"
attrlist="$attrlist ns32000 ns16000 iAPX286 mc300 mc500 mc700 sparc"C worked ok on a pdp-11/45, but that had 256K of memory and 10s of MB of fixed disk. That level of hardware didn't appear for micro systems until the 68k generation, or I suppose IBM PC, but I don't remember the PC being too important in C coding circles until the 386, much later.
A contemporary source is the opinionated "DTACK-Grounded" newsletter from 1981-1985. http://www.easy68k.com/paulrsm/dg/ Hal Hardenbergh raved about the fast 68000 chip and it's wonderfully easy assembly, but lamented that everyone switched to "portable" Pascal and C to write 16-bit programs so they seemed even slower than 8-bit ones. His favorite example was a direct comparison: Lotus 1-2-3, written in 8088 assembly, vs Context MBA with the same features but written in Pascal for portability. 1-2-3 was MUCH faster than Context on the PC, and no one remembers Context today. Or the $16,000 Unix-based AT&T workstation whose floating-point benchmarks are beaten by a $69 VIC-20. (Obviously due to the C-written runtime, which even followed the C standard of promoting all single precision calculations to double so single was no faster!)
His opinion of C was "slightly-disguised PDP/11 assembly". Not too bad for the 68000, but a terrible fit for the 8088 or Z80.
And I like writing assembler!
A lot of the modern stack is layers of abstraction, which probably wouldn't be appropriate for such limited machines, but maybe superoptimizers and so on, and just more modern algorithms, etc, could help show what's really possible on these old machines. Sort of retro demoscene, but for useful apps.
Minor nitpick: PUN is not a device in Windows 11 (I haven't tested on previous versions). > echo hello>pun: > type pun hello
In the section about paging, are there actual systems working in the megabyte range?
My next whiskey will be in your honor my man.
See you space cowboy
> A modern compiler will optimize this redundancy away
No, it won't: https://godbolt.org/z/xbzz4cEq3
int my_function (a, b)
int a; char *b;
{
... body of function ...
}
What do you even call this?(disclaimer, i never programmed in c on cp/m, and although i used to use cp/m daily, i haven't used it for about 35 years)
he's using aztec c, but anyone who's considering this needs to know that aztec c isn't under a free-software license. bds c is a properly open-source alternative which seemed to be more popular at the time (though it wasn't open source then)
https://www.aztecmuseum.ca/docs/az80106d.txt says
> This compiler is both the MS-DOS cross-compiler and the native mode CP/M 80 Aztec CZ80 Version 1.06d (C) Copyright Manx Software Systems, Inc. and also includes the earlier Aztec CZ80 Version 1.05 for native mode CP/M 80. I cannot provide you with a legally licenced copy.
> I herewith grant you a non-exclusive conditional licence to use any and all of my work included with this compiler for whatever use you deem fit, provided you do not take credit for my work, and that you leave my copyright notices intact in all of it.
> I believe everything I have written to be correct. Regardless, I, Bill Buckels...
but https://en.wikipedia.org/wiki/Aztec_C explains that manx software 'was started by Harry Suckow, with partners Thomas Fenwick, and James Goodnow II, the two principal developers (...) Suckow is still the copyright holder for Aztec C.'
so it's not just that the source code has been lost; the licensing situation is basically 'don't ask, don't tell'
bds c comes with some integration with an open-source (?) cp/m text editor whose name i forget, so you can quickly jump to compiler errors even though you don't have enough ram to have both the compiler and the editor in memory at once. other ides for cp/m such as turbo pascal and the f83 forth system do manage this. f83 also has multithreading, virtual memory, and 'go to definition' but it's even more untyped than k&r c
bds c is not quite a subset of k&r c, and i doubt boone's claim that aztec c is a strict subset of k&r c as implemented by gcc
sdcc is another free-software compiler that can generate z80 code https://sdcc.sourceforge.net/doc/sdccman.pdf#subsection.3.3.... but it can't run on a z80 itself; it's purely a cross-compiler
a thing that might not be apparent if you're using a modernized system is how constraining floppy disks are. the data transfer rate was about 2 kilobytes per second, the drive was obtrusively loud, and the total disk capacity was typically 90 kilobytes (up to over a megabyte for some 8-inchers). this means that if a person needed data from the disk, such as wordstar's printing overlay, you had to request it and then wait for the disk to find it. so it wasn't a good idea to do this for no user-apparent reason
with respect to
int elems[5][300];
...
int i, j;
for (i = 0; i < m; i++)
{
for (j = 0; j < n; j++)
{
int elem = elems[i][j];
... process the value ...
}
}
if i wanted efficiency on a compiler that didn't do the strength-reduction for me, i would write it as int elems[5][300];
...
int i, *p, *end, elem;
for (i = 0; i < m; i++) {
end = elems[i+1];
for (p = elems[i]; p != end; p++) {
elem = *p;
... process the value ...
}
}
this avoids any multiplications in the inner loop while obscuring the structure of the program less than boone's versioncp/m machines are interesting to me as being a good approximation of the weakest computers on which self-hosted development is tolerable. as boone points out, you don't have valgrind, you don't have type-checking for subroutine arguments (in k&r c; you do in pascal), the cpu is slow, the fcb interface is bletcherous, and, as i said, floppy disks are very limited; but the machine is big enough and fast enough to support high-level languages, a filesystem, and full-screen tuis like wordstar, supercalc, turbo pascal, the ucsd p-system, etc.
(second disclaimer: i say 'tolerable' but i also wrote a c program in ed on my cellphone last night; your liver may vary)
on the other hand, if you want to develop on a (logically) small computer, there are many interesting logically small computers available today, including the popular and easy-to-use atmega328p; the astounding rp2350; the popular and astonishing arm stm32f103c8t6 (and its improved chinese clones such as the gd32f103); the ultra-low-power ambiq apollo3; the 1.5¢ cy8c4045fni-ds400t, a 48-megahertz arm with 32 kibibytes of flash and 4 kibibytes of sram; and the tiny and simple 1.8¢ pic12f-like ny8a051h. the avr and arm instruction sets are much nicer than the z80 (though the ny8a051h isn't), and the hardware is vastly cheaper, lower power, physically smaller, and faster. and flash memory is also vastly cheaper, lower power, physically smaller, and faster than a floppy disk
F83 is a top drawer Forth system. It implements the Forth 83 standard, and it’s in the public domain.
It runs on CP/M and MS-DOS.
It has the editor, a single step debugger, virtual memory, multi tasking, an assembler, full source code, and it is self hosting (on top of its host OS) via its meta-compiler. But you could readily port it to a system with no real OS, needing little more than console I/O and some kind of block device.
It’s a hybrid block file based system. Historically you would just hand entire block volumes that would then be managed by Forth directly. Here, you create pre-allocated files that are then used in a customary Forth block style. It did not use text files for source code. It also has shadow screens for documentation. Shadow screens are where you divide the block volume in two, and the first half is source code, and the second is documentation. So if you have a 200 block volume, then screen 4 is code and screen 104 is documentation.
If you want to see the source code of a word, just type VIEW <word> and it will show you the screen that defines the word. The compiler embeds the source screen in each definition. A simple command will bring up the shadow screen to show the documentation.
It also has a word decompiler, and a threaded dictionary for better search performance. It has hundreds of words.
The real trick is imagining the work that went into this system. The bootstrapping process that converted this from a likely hand keyed, assembly based FIG Forth to what became F83. Starting with a pure, block screen based system to one that works with a file system, shifting code around on disk with little more than offsets and a copy routine that’s little more than a block move.
On a slow, 2 floppy 8080, where resetting the system was the routine way to get back to a known state. Using a powerful, yet very dangerous language, rife with traps. It’s like doing self surgery with a mirror.
The holes they must have blasted in the floor trying to avoid shooting themselves in the foot. Repeatedly. I’ve spent hundreds of hours studying this system, it’s really an amazing effort.
"CP/M systems rarely had more than 64Mb of RAM"
This probably was intended as 64Kb of RAM.
Great piece. I found it interesting. Nice work.