August 18th, 2024

Getting back into C programming for CP/M

Kevin Boone is revisiting C programming for CP/M after 40 years, using the limited Aztec C compiler while emphasizing efficiency and authenticity in coding within the constraints of the Z80 CPU.

Read original articleLink Icon
NostalgiaInterestAppreciation
Getting back into C programming for CP/M

Kevin Boone has rekindled his interest in C programming for CP/M after a 40-year hiatus, acquiring a Z80-based CP/M machine to explore retrocomputing. He has developed several utilities, including KCalc-CPM and cpmlife, using a modern Z80 cross-compiler but prefers to use CP/M tools for authenticity. Boone is utilizing the 1982 Aztec C compiler, which is available for free from the Aztec Museum. The article discusses the differences between programming in C for CP/M and modern C development, highlighting the limitations of the Aztec compiler, such as its pre-ANSI C syntax, lack of function prototypes, and smaller data type sizes. The compiler generates 8080 assembly code, which is compatible with Z80, but does not leverage advanced Z80 features. Boone notes that the Aztec C library is minimal, lacking many modern functions, and emphasizes the importance of efficiency in coding due to the constraints of the Z80 CPU. He also addresses command-line argument handling and device I/O, explaining how CP/M's architecture requires different approaches compared to modern systems. Overall, Boone's exploration illustrates the challenges and nuances of programming in a retro environment while maintaining a connection to contemporary practices.

- Kevin Boone is revisiting C programming for CP/M after 40 years.

- He uses the Aztec C compiler, which has limitations compared to modern compilers.

- The article highlights differences in syntax, data types, and library functions between CP/M and modern C.

- Efficiency in coding is crucial due to the constraints of the Z80 CPU.

- Boone emphasizes the importance of using CP/M tools for authentic development.

AI: What people are saying
The comments reflect a nostalgic and technical discussion about programming in C on CP/M systems, particularly with the Aztec C compiler.
  • Many commenters share their experiences with CP/M and various C compilers, highlighting the limitations and challenges of programming in that era.
  • There is a consensus that modern development practices differ significantly from those in the past, often lamenting the complexity of current methodologies.
  • Several users express interest in retro computing and the fun of programming on older hardware, with some actively collecting retro machines.
  • Technical discussions arise regarding the performance and optimization of code on limited systems, with suggestions for modern techniques to enhance retro programming.
  • Some comments address misconceptions about CP/M's capabilities, particularly regarding memory limits and file system constraints.
Link Icon 21 comments
By @stevekemp - 5 months
I put together a simple CP/M emulator here:

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

By @jmclnx - 5 months
>The Aztec C compiler would have originally be distributed on floppy disks, and is very small by moden standards.

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.

By @PaulHoule - 5 months
Personally I see the use of a cross-compiler and other dev tools on a bigger machine as even more retro than running them in an 8-bit micro because it is what many software vendors did at the dawn of the microcomputer age.

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)

By @mark-r - 5 months
> There's no obvious way to create a lower-case filename on CP/M

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.

By @anonymousiam - 5 months
Many of the complaints by the author are in the context of differences between C today and C back then, but back when CP/M was in common use, C compilers typically did not do much optimization, and K&R C was all there was.

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.)

https://www.bdsoft.com/dist/bdsc-guide.pdf

By @jart - 5 months
> Many programmers, including myself, have gotten out of the habit of doing this on modern systems like Linux, because a malloc() call always succeeds, regardless how much memory is available.

Not if you use setrusage().

By @opless - 5 months
> CP/M systems rarely had more than 64Mb of RAM

True. But I think you meant KB.

Back in the days when CP/M was king, even 20MB Winchester hard drives were rare

By @dpb001 - 5 months
This brought back some memories. Back in the day I couldn't afford the Aztec compiler (or it wouldn't fit onto my dual floppy 48K Heathkit H89, can't remember which). I ended up buying Leor Zolman's BDS C compiler. Just looked him up and it looks like he's still around!

https://www.bdsoft.com

By @nils-m-holm - 5 months
Funny how most of the article reads (to me) "back in the days things were done in the obvious way, while now everything is weird". In other words I still program like in the 1980's. :)

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

By @pjmlp - 5 months
A very good example how C wasn't as portable and as high performance back in the 1980's, as many nowadays think it was.
By @zabzonk - 5 months
I did a shedload of programming in CP/M back in the 80s, and frankly I'd rather do it in Z80 assembler (assuming we were targeting Z80-based systems) than the rather poor compilers (not just C compilers) that were available. Using a compiler/linker on a floppy-based CP/M machine was quite a pain, as the compiler took up a lot more space than an assembler, and was typically much slower.

And I like writing assembler!

By @jacknews - 5 months
I would be more interested to see how modern techniques could improve the then-state of the art.

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.

By @mianos - 5 months
I remember using Aztec after using Software Toolworks C for a a few years. It was incredibly advanced in terms of standard C at the time. It was the first time I could just type code from the "C Programming Language" in and it would work unchanged.
By @ghuysmans - 5 months
Nice article, thanks!

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?

By @kelsey98765431 - 5 months
RIP Gary, you had so much more to give.

My next whiskey will be in your honor my man.

See you space cowboy

By @philkrylov - 5 months
> for (int i = 0; i < strlen (str); i++) {...}

> A modern compiler will optimize this redundancy away

No, it won't: https://godbolt.org/z/xbzz4cEq3

By @nj5rq - 5 months
It's the first time I ever see the function parameters declared like this:

    int my_function (a, b)
    int a; char *b;
      {
      ... body of function ...
      }
What do you even call this?
By @kragen - 5 months
minor 1000× error: 'CP/M systems rarely had more than 64Mb of RAM' should read 'CP/M systems rarely had more than 64 kibibytes of RAM' (because memory addresses were 16 bits and there wasn't much demand for bank-switching in cp/m's heyday, though later 8-bit machines like the nes and the msx did use bank-switching extensively)

(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 version

cp/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

By @Rochus - 5 months
Interesting; but why actually C and not PL/M?
By @ddingus - 5 months
I think the author has a typo:

"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.