8 ms·
Getting back into C programming for CP/M
- Rochus 2y agoInteresting; but why actually C and not PL/M?
- katzinsky 2y agoYeah C does not work well on these odd 8-bit ISAs. Pascal, basic, and PL/M (and fortran?) seem to have been way more common and Pascal environments on these were really on the edge of what the contemporary hardware could handle.
- PaulHoule 2y agoMy take it was the other away around. In its own strange way C was portable to machines with unusual word sizes like the DEC PDP-10 with 36 bit words. I used C on Z-80 on CP/M and on the 6809 with Microware’s OS-9. In the 1980s there were books on FORTRAN, COBOL and PASCAL. I know compilers for the first two existed for micros but I never saw them, these were mainly on minicomputers and mainframes and I didn’t touch them until I was using 32-bit machines in college There were academics who saw the popularity of BASIC as a crisis and unsuccessfully tried to push alternatives like PASCAL and LOGO, the first of which was an unmitigated disaster because ISO Pascal gave you only what you need to work leetcode problems, even BASIC was better for “systems programming” because at least you had PEEK and POKE though neither language would let you hook interrupts. Early PASCALs for micros were also based on the atrociously slow UCSD Pascal. Towards the end of the 1980s there was the excellent Turbo Pascal for the 8086 that did what NiklausWirthDont and I thought was better than C but I switched to C because it portable to 32-but machines. I’d also contrast chips like the Z-80 and 6809 which had enough registers and address modes to compile code for and others like the 6502 where you are likely to resort to virtual machine techniques right away, see https://en.wikipedia.org/wiki/SWEET16 https://en.wikipedia.org/wiki/SWEET16 I saw plenty of spammy books on microcomputers in the late 1970s and early 1980s that seemed to copy press releases from vendors and many of these said a lot about PL/M being a big deal although I never saw a compiler, source code, or knew anybody who coded it.
- pvg 2y agoTowards the end of the 1980s there was the excellent Turbo Pascal for the 8086 TurboPascal was released at the tail end of 1983 targeting CP/M and the Z80. It was hugely popular on the platform.
- PaulHoule 2y agoCorrect. My own experience in Turbo Pascal started with (I think) version 4 when I got an 80286 machine in 1987. In that time frame Borland was coming out with a new version every year that radically improved the language, it got OO functionality in 5.5, inline assembly in 6, etc. I remember replacing many of the stdlib functions such as move and copy w/ ones that were twice as fast because the used 16 bit instructions that were faster on the 80286. With the IDE and interactive debugger it was one my favorite programming environments ever.
- pjmlp 2y agoHaving been a Borland's compilers fanboy since Turbo BASIC, all the C++ and TP versions for MS-DOS, a few ones on Windows 3.x, Borland C++ 4.5, and first editions of Delphi and C++ Builder, until their management messed up, it kind of sadens me the zig-zag turns we have had since the 2000 with VMs and interpreted languages, until we finally got the renaissance of AOT toolchains. If only Native AOT was as easy to produce a binary as a plain Turbo Pascal 6 project using Turbo Vision.
- adrian_b 2y agoMicrosoft had FORTRAN and COBOL compilers for CP/M. I have used them on both Intel 8080 and Zilog Z80. The MS FORTRAN compiler was decent enough. It could be used to make programs that were much faster than those using the Microsoft BASIC interpreter. Even if you preferred to write some program in assembly, if that program needed to do some numeric computations it was convenient to use the MS Fortran run-time library, which contained most of the Fortran implementation work, because the Fortran compiler generated machine code which consisted mostly of invocations of the functions from the run-time library. However, for that you had to reverse-engineer the library first, because it was not documented by Microsoft. Nevertheless, reverse-engineering CP/M applications was very easy, because an experienced programmer could read a hexadecimal dump almost as easy as the assembly language source code. Microsoft used a few code obfuscation tricks, but those could not be very effective in such small programs.
- flyinghamster 2y agoI don't ever recall seeing PL/M compilers advertised by anyone back in the day. I have a feeling that the few that existed were offered at "meet our sales guy at the golf course" pricing.
- jasomill 2y agoPL/M was developed by Gary Kildall for Intel, and the only compilers I've used ran under Intel's ISIS family of operating systems, originally on Intel Intellec MDS hardware, and only much later under first-party ISIS emulation on MS-DOS. AFAIK, Intel never marketed its first-party development systems to hobbyists, so you're probably in the ballpark with "golf course" pricing.
- guestbest 2y agoPL/M is a less transferable skill and a ‘dead’ language. I think Gary Kildall promotes PL/M but honestly C is the best for portability and popularity followed by Forth and Pascal.
- pjmlp 2y agoA very good example how C wasn't as portable and as high performance back in the 1980's, as many nowadays think it was.
- tyingq 2y agoNot on 8 bit machines, no. Look at what Perl did with C in that timeframe though, and it's true for a different subset of machines. 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/ba165bbde4eef698ff9cc697ab2ff7d0f388714d/Configure#L121 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"
- pjmlp 2y agoNot even on 16 bit home machines, which is why any serious game, or winning demoscene entries, were written in Assembly, until we reached the days of 486 and DOS extenders. As a read through the Amiga and PC literature of the time will show.
- kelsey98765431 2y agoThe problem here is much more the unix wars and a lack of confidence in BSD under legal fire rather than a lack of ability. The principal concern of unix vendors of the early PC era was to maintain their market share in mini and mainframe product sectors rather than growth into the consumer market. This spurred a rewrite of BSD fragments tied to the legacy unix codebase which fully portablized C and the GCC downstream projects which ended up benefiting the weird hobby OS linux disproportionately, and had it not had to be written from scratch we may have ended up with a wonderful 286-BSD rather than a 486-BSD, which at the time was still not fully clean room foss and unburdened. This was a time when large customers of OS products were trying to squeeze all the performance juice out of the existing systems instead of looking at new paradigms. We have things like the full SAST and warn-free release of sunOS around this time, where Sun was focused on getting a rock stable platform to then optimize around rather than efforts to produce products for the emerging Micro market. We can see that the concept of a portable unix system and c library as early as Xenix on the Apple Lisa in 1984. That's only 3 short years after the IBM collaboration for PC-DOS, showing even a rookie uncoordinated and low technical skill team such as microsoft (Paraphrasing Dave Cutler, chief NT kernel lead - Zachary, G. Pascal (2014). Showstopper!: The Breakneck Race to Create Windows NT and the Next Generation at Microsoft. Open Road Media. ISBN 978-1-4804-9484-8).
- jart 2y ago> 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().
- fuhsnn 2y ago>setrusage() Is it old version of setrlimit()? Couldn't locate it in any of the man.*bsd.org.
- jmclnx 2y agoNot me :) 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.
- antirez 2y agoIgnoring failures is a bad idea, but in many applications quitting on malloc() retiring NULL is the most sensibile thing to do. Many, but not all kinds of applications.
- guenthert 2y ago> Not if you use setrusage(). Or if memory overcommit is disabled or an 'unreasonable' amount of memory was requested. So, no, malloc() doesn't always succeed.
- ghuysmans 2y agoNice 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?
- layer8 2y agoPUN wasn’t even in MS-DOS. However, later versions of CP/M used AUX instead of PUN, which DOS adopted and still exists in Windows.
- anonymousiam 2y agoIt's been a lot of years, but as I recall, the raw I/O devices had one set of names, and the logical devices had another. So things like STAT PUN:=PTP: (if I remembered the syntax correctly) would set the logical "punch" device to be the physical paper tape punch, which was the default. I may also be confusing CP/M I/O redirection syntax (which only worked if your BIOS supported it), with DEC RT-11 syntax. It has been over 40 years since I have used either one.
- deleted 2y ago[deleted]
- nils-m-holm 2y agoFunny 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 http://t3x.org/t3x/0/book.html
- devjab 2y agoThere has been an incredible amount of principles and practices added to our profession. Most of which are silly. Like Clean Code which is just out right terrible in terms of causing CPU cache misses as well as getting you into vtable for your class hierarchies. Most modern developers wouldn’t know what a L1 cache is though, so they don’t think too much about the cost. What is worse is that people like uncle Bob haven’t actually worked in programming for several decades. Yet these are the people who teach modern programmers how they are supposed to write code. 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.
- deleted 2y ago[deleted]
- ImHereToVote 2y agoThe issue is that there is a vast chasm between software written by some competent guy and software written by a development team.
- zabzonk 2y agoI 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!
- julian55 2y agoYes, I agree. I did write some C software for Z80 but mostly I used assembler.
- PaulHoule 2y agoPersonally 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 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)
- whartung 2y ago> because it is what many software vendors did at the dawn of the microcomputer age. 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.
- pjmlp 2y agoYeah, all my learnings were from books, bought or local library, computer magazines, and occasional demoscene meetings. I was able to connect to BBS only during the summer internship I did, at the end of my vocational school training in computer programming. When I afterwards arrived into the university, Gopher was still a thing. Lots of paper based programming, and wild guessing, there was no Stack Overflow to help us out.
- jmclnx 2y ago>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.
- nils-m-holm 2y ago> If I remember correctly, Aztec C was from Mark Williams. It was also the basis for the c Compiler that came with Coherent OS. That would have been "Mark Williams C", also marketed as "Let's C" for MDSOS.
- flyinghamster 2y agoYup. Let's C was the cut-down version of MWC86, with no large-model support. This limited you to 64K code and 64K data. I got a copy of it one Christmas, but never used it much because of this limitation.
- jmclnx 2y agoCorrect, that was it, "Lets C".
- reaperducer 2y agoThese days development is just a series of Meetings, Agile Points, Scrums with maybe 2 hours of real work per week. 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.
- 2y ago
- stevekemp 2y agoI put together a simple CP/M emulator here: https://github.com/skx/cpmulator/ 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/ 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 https://kevinboone.me/cpm-c2.html
- musicale 2y agoNice. File system integration is a nice feature that I'd like to see in more retrocomputing emulators. 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 https://github.com/MockbaTheBorg/RunCPM The README links to the author's CP/M software library as well.
- anonymousiam 2y agoMany 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 https://www.bdsoft.com/dist/bdsc-guide.pdf
- mark-r 2y ago> 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.
- blueflow 2y agoNot FAT, but something even more rudimentary.
- mark-r 2y agoSorry, my bad.
- whartung 2y ago> That's because the FAT file system used by CP/M didn't allow lower case letters, at all. 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.
- nils-m-holm 2y ago> That's because the FAT file system used by CP/M didn't allow lower case letters, at all. 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.
- jacknews 2y agoI 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.
- 082349872349872 2y agoI'm pretty sure this: https://ourworldindata.org/grapher/historical-cost-of-computer-memory-and-storage https://ourworldindata.org/grapher/historical-cost-of-comput... has had way more to do with changes in the way we program than anything we've learned about modern techniques.
- dpb001 2y agoThis 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 https://www.bdsoft.com
- kelsey98765431 2y agoRIP Gary, you had so much more to give. My next whiskey will be in your honor my man. See you space cowboy
- kragen 2y agominor 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 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 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.8 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
- opless 2y ago> 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
- nj5rq 2y agoIt'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?
- omerhj 2y agoK&R C
- zabzonk 2y agoAltho idiomatic that is perhaps slightly confusing because K&R 2nd ed uses the modern way of specifying parameters. I would prefer to say "pre ANSI C" or something of that kind.
- memsom 2y agoYou would be rewriting history if we changed that now. It has been referred to K&R style C since the ANSI standard. The second edition of the C Programming Language was ANSI. My copy of the second edition has "based on the draft-proposed ANSI C" on the cover, but later ones just have "ANSI C". I think mine is almost identical to the ANSI version. Every copy of K&R that uses ANSI has ANSI written somewhere on the cover. I've seen the first edition, and the content is pretty similar if not identical, save for the ANSI changes. But it is all in the K&R style.
- mianos 2y agoI 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.
- ddingus 2y agoI 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.
- philkrylov 2y ago> 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 https://godbolt.org/z/xbzz4cEq3