21

CP/M, the operating system for Intel 8080 and Z80 (and others as well, but later) used to call 0005h for syscalls. The application would load the syscall number into a specific register (I think it was C) and then do call 0005h.

But why? Both the Intel 8080 and the Z80 have these very handy rst instructions for jumping to a hard coded address early in the address space, and that's apparently for exactly this kind of purpose.

I'm proposing that CP/M could have used rst 8 or something, instead of call 0005h. This would have meant a saving of 2 bytes and six tacts per syscall, just by using the mechanism that the CPUs provide as standard. And it fits in with the scheme of using rst 0 for a warm reboot, and rst 56 for the debugging.

Omar and Lorraine
  • 38,883
  • 14
  • 134
  • 274
  • 2
    The original CP/M was written in PL/M and cross-compiled on a Fortran system. It had to have BIOS/BDOS combined on a single paper tape and loaded that way. It may not have generated anything more than the most basic 8080 code. And it's possible that, when rewriting BIOS in real ASM, the same call method was used. All supposition of course, especially given the non-living nature of the author (no insult intended, just stating facts). – paxdiablo Jul 26 '22 at 09:12
  • 3
    @paxdiablo - presumably the author was living at the time... – Jon Custer Jul 26 '22 at 13:48
  • 1
    It is hard today to imagine how simple an operating system CP/M could be and still be widely successful. It is primarily providing well-defined I/O byte operations on various ports (serial, parallel, etc) and translating files into floppy sectors. Plus some standard utilities and a command line. That's about it :) For 2.2 that was. CP/M-3 and MP/M had a lot more tricks up their sleeve. – Thorbjørn Ravn Andersen Jul 26 '22 at 14:10
  • 4
    @JonCuster: I think he means that you can't ask all the reasons for these things any more to Gary Kildall – chthon Jul 26 '22 at 15:41
  • 5
    @chthon - that was meant in jest... Should have attached a smiley face I guess. – Jon Custer Jul 26 '22 at 15:42
  • Was CP/M from the start aimed at a single computer, or was the idea to have a system that could be thrown at almost anything running an 8080? I think the answer to this would influence the use or no use of RST instructions. – UncleBod Jul 27 '22 at 11:33
  • @UncleBod Interesting point. Given that Kindall put in the BIOS/BDOS distinction, with the former being effectively a HAL, then he meant for CP/M to run on anything that had an 8080 or compatible in it. – Omar and Lorraine Jul 27 '22 at 11:36
  • Another thing is: was the syscall 0005 on the first version, or was this changed in 1976(?) when CP/M was rewritten and BIOS added to make it easier to port to other systems? (https://landley.net/history/mirror/cpm/history.html) – UncleBod Jul 27 '22 at 20:35
  • @UncleBod: I'm not sure how adding call-5 would help with that split, I thought it was the border between application and CP/M, not the border between BDOS and BIOS. Or am I misunderstanding your question? – paxdiablo Aug 05 '22 at 04:58
  • @paxdiablo CALL 5 makes sure that all RST functions can be used for whatever the hardware guys thought they needed them for. Thus easier to port to other computers, which apparently was the idea of the BDOS/BIOS split. I suspect that this was the time when BIOS was (re) written in assembler, so you got a better control on the entry points. – UncleBod Aug 05 '22 at 06:15
  • @paxdiablo it is also a question of what came first. It might be, as you stated in your comment, that the PL/M compiler placed the entry point at 0005 and Kidall kept it there because it was easier than moving it somewhere else. We also don't know if Kidall was aware about the RST system or if the was aware but decided not to use it. – UncleBod Aug 05 '22 at 06:33

3 Answers3

27

CP/M, the operating system for Intel 8080 and Z80 (and others as well, but later)

CP/M is an 8080 operating system. Like DOS is an 8086 system. That other CPUs may as well execute it due to close compatibility with the 8080 is not CP/M's fault.

used to call 0005h for syscalls. [...]

It's the lowest memory address that can be assumed to be usable without giving up much functionality of the target system (or any at all).

CP/M, unlike developer-specific OSes, where machine and OS were developed in step, is an add-on product meant to be applicable to as many different designs as possible. As such it's important to be as unintrusive and adaptable as possible.

Address 5 is a great choice:

  • Existing hardware interrupts can (continue to) use any RST
  • Software can use all RST for whatever function it needs
  • Only the last 3 bytes of RST 0 are reserved
  • CP/M uses (has to use) RST 0 anyway

The last point is quite important, as RST 0 also dubs as warm start entry point for CP/M due being the RESET entry point (*1), so CP/M is already in control of these 8 bytes. It only needs the first 3 to jump to its warm boot code, leaving 5 bytes for other use ... like using 3 for a generic call interface without spending a rare RST resource (*1).

I'm proposing that CP/M could have used rst 8 or something,

There are only 8 RST locations available. Each the OS takes is one hardware and applications can't use.

This would have meant a saving of 2 bytes and six tacts per syscall, just by using the mechanism that the CPUs provide as standard.

"Six tacts per call" is not really a considerable benefit with syscalls usually taking at least several hundred to many thousands.

And it fits in with the scheme of using rst 0 for a warm reboot,

Using RST 0 for warm boot is a way of reusing RESET (*2).

rst 56 for the debugging.

I guess you mean RST 7? (*3) That's already a good example how a user program is benefiting from CP/M keeping its hands off RST instructions as much as possible.


Bottom line: Using address 5 comes with the least cost in terms of predefined system resources CP/M requires from a system to be ported to.


*1 - In fact, the other two bytes are used in a similar way, address 3 holds the I/O byte and 4 the disk byte. Cramping all RAM needs into the first 256 bytes, when a quarter (64 Bytes) is already reserved for the RST table and a whopping half (128 Bytes) for the file buffer, is a tough job.

*2 - It's all about how the system is built. RESET does start, like RST 0, execution at address 0. On a cold start an 8080 system needs to have some ROM there to start up. Since CP/M (usually) wants RAM at that location, that ROM will be mapped out at some point.

Thus CP/M can now (re)use that entry point as warm restart, so if any application executes an RST 0, it ends up in CP/M's warm start routine, while a follow-up RESET (usually) would start again from ROM code.

Even better, if the hardware somehow distinguishes between power-up reset and later reset, it may execute from address 0 in RAM instead, making RESET go direct to CP/M warm start.

What a nice way to kill (at least) two birds with one stone.

*3 - As 8080 software using Intel notation may be more appropriate - not to mention that it avoids any confusion about which addresses are legal or not.

tripleee
  • 134
  • 7
Raffzahn
  • 222,541
  • 22
  • 631
  • 918
  • I wonder what factors favored the use of a single jump vector at address 5, versus reserving space in low RAM for a jump table, or having BIOS called handled via jump table at an arbitrary address versus reserving an area of RAM for such a jump table which both the OS and user programs could use to make BIOS calls? If CP/M required that the computer-vendor-handled part of the boot process place a BIOS vector table in RAM, along with the top-of-RAM address at a certain spot, load a particular disk sector at e.g. 0x200, and jump there, and if Digital Research were to designate a particular... – supercat Jul 26 '22 at 15:58
  • ...sector that each CPU vendor should use as its boot location, then it would be practical for software vendors to release software on disks that could boot on multiple machines interchangeably. – supercat Jul 26 '22 at 16:03
  • 2
    @supercat As always: Need for a small RAM footprint (CP/M needs only 256 Bytes), advantage of a single entry point and most of all: No space to code any of such complex matters. Those were the 70s. No system builder wants to take a 3 week course to get CP/M booting on his system. He'd have written the needed OS parts by himself in that time. Remember CP/M is not just for Desktop, but intended as well for systems with RAM as small as a single KiB or even less. – Raffzahn Jul 26 '22 at 16:40
  • How did the costs of RAM and ROM compare? Mask ROM would of course have a high setup code while being very cheap per unit, but what about bipolar PROMs, UV EPROMS, or OTPROMs? I would think that if one wanted to run CP/M from ROM, having a version that was machine-independent except for a choice of 0xC000 or 0xE000 start address would for many CPU makers be cheaper than having to have a machine-specific version of CP/M in ROM. – supercat Jul 26 '22 at 17:07
  • 2
    @supercat What RAM? The ROM was needed anyway for the application. And what kind of ROM doesn't matter for the principle. Also, CP/M was always machine specific due the BIOS. The point of CP/M is just to encapsulate machine specific details there.. – Raffzahn Jul 26 '22 at 17:43
  • Were BDOS and BIOS always in the same storage medium? I would have expected that on many machines it would be practical to have BIOS in a small individually-programmed ROM and BDOS in a larger mask ROM. If a vendor isn't making enough identical machines to justify a mask ROM order, I would think that having a boot ROM with just enough smarts to load a sector of data from a floppy, and loading everything else from there, would be the most practical way to do things. – supercat Jul 27 '22 at 15:35
  • Just on "CP/M is already in control of these 8 bytes". Technically, using RST 0 as boot only requires it to have control of the use of RST 0 and the first three bytes (well, one byte if it's an RST 1-7 but then you would probably do that directly rather than via RST 0, unless you were insane). It could have easily left those other five for applications to use. But, a very good answer anyway, despite my pedantry :-) – paxdiablo Aug 05 '22 at 05:06
  • As another point (assuming accumulator is guaranteed zero on boot), you could use RST 0 combined with the accumulator to jump to a service routine that routed to the correct function (along the lines of MSDOS INT 21 Fn <content-of-AX/AH?>). That would overload RST 0, while still letting it be used as boot and without wasting another RST. Calls would then be register setup with params (including accumulator) then the one-byte RST 0, saving two bytes per call. – paxdiablo Aug 05 '22 at 05:13
6

It's very likely because CP/M was designed for the 8080 microprocessor and appears to have chosen to keep RST instructions free for the interrupt circuitry.

CP/M chose to not rely on any of the eight RST instructions being available. It left them all for interrupt service routines or other functions. There's no official CP/M documentation I'm aware of that says this but it's pretty clear of its advantages in simplifying the system's electronics circuit.

From a purely software perspective, using a 1-byte RST restart instruction instead of the 3-byte CALL 0005h needed would indeed appear to be the better solution.

However, CP/M was originally written for the Intel 8080 microprocessor. For the hardware design, RST instructions are valuable for interrupt handlers: they simplify the circuitry needed for 8080 interrupts and allow for faster interrupt response. CP/M does not know how many interrupt sources a system might need RSTs for, so it uses none of them.

The 8080 has a single interrupt request pin, /INTR, and a basic interrupt handling scheme:

  • Interrupts are maskable.
  • When enabled and a valid LOW is detected on /INTR, the 8080 completes any instruction execution then performs an interrupt acknowledge cycle and reads an instruction from the bus.
  • External circuitry must then place a valid 8080 instruction opcode on the data bus, followed by any further instruction bytes in subsequent cycles.

Interrupt acknowledge response circuitry to drive a 1-byte RST instruction onto the data bus is much simpler with 1970s parts than circuitry to drive a 3-byte CALL xxxx instruction onto the bus. This brings the electronics cost down, an important factor with much more expensive 1970s parts and manufacturing.

For a single interrupt, the interrupt response circuit can even just use permanent pull-up resistors to present a RST 7 during an 8080 interrupt acknowledge cycle. Simple address decoding can support that by disabling reads of all memory and I/O devices during that interrupt acknowledge.

Moving on from the 8080, the later Z80 has a more powerful maskable interrupt scheme with three Interrupt Modes:

  • Z80 IM 0: same as 8080 (see above)
  • Z80 IM 1: does CALL 0038h, needs no external electronics
  • Z80 IM 2: reads vector number from data bus D7..D1, uses it and I register to read a vector from a memory address, does CALL vectorAddress.

So a Z80 CP/M system can be built using IM 1 for a single ISR that polls interrupting devices, needing no interrupt response electronics and leaving all RST instructions free. Or it could be built using IM 2 with less/no polling and an ISR for each interrupt, but needing some external electronics and leaving all RST instructions free. However, CP/M, and all its applications based around CALL 0005h, were long established by then and did not change for the Z80.

Note that the address 0005h is most likely used because each RST instruction CALLs an address on an 8-byte multiple and 0005h allows a 3-byte JP xxxx instruction to be placed in the final 3 bytes of the first 8-byte RST handler space, RST 0. That leaves the most space remaining in the RST 0 handler...all of 5 bytes!

If CP/M had been created later and for Z80 systems, there certainly would be benefits from using a restart instruction for BDOS calls instead of CALL 0005h:

  • The RST is more compact. The three extra RAM bytes used to push up the BIOS handler from 0005h to 0008h are outweighed by all of the 2-byte shorter RST 1 BIOS calls in the CP/M application programs. Most programs would have a lot more than two BIOS calls.

  • The RST is faster. Even this small speed improvement is always worthwhile in such slow systems. It can become very significant for repeatedly-invoked calls, such as for text display, byte-at-a-time file handling etc.

Many of the Z80 systems that arrived well after CP/M considered their RST instructions to be a precious resource for allocating with care. For example, the ZX Spectrum ROM used them for error handling, printing characters, BASIC line character fetching, its floating-point calculator, workspace RAM and the regular IM 1 interrupt.

TonyM
  • 3,704
  • 1
  • 19
  • 31
  • 3
    If this is an answer to "why?" then it's saying "I don't know". Which may in fact be the best answer available. Perhaps it simply didn't occur to Kildall to do otherwise. "To call an OS service, use CALL". – dave Jul 27 '22 at 03:48
  • Thanks @MarkWilliams but it was probably because I was half-asleep after long days away and what was there was a wrong answer :-) I've now rewritten it completely, after a ton of sleep and clarity. – TonyM Jul 27 '22 at 18:41
  • You're aware that the same RST can be used with Z80 interrupt handling? Interrupt mode 0 is fully 8080 compatible, thus as well based on injecting an instruction. Usually an RST, thus they are for Z80 systems a resource as precious as for 8080. – Raffzahn Jul 27 '22 at 21:17
  • @Raffzahn, very aware of IM0 but not relevant here. A Z80 can use IM1 as said, for polled interrupts with a shared vector and no external circuitry. Or IM2 for separate interrupt vectors with less external circuitry than IM0 and no use of RSTs. IM2 does need a RAM table of 2..256 bytes: a word for each interrupt source. More expensive Z80 systems can keep that table in external RAM to free the Z80 address map. – TonyM Jul 27 '22 at 21:47
  • @TonyM No need to tell me. I did use more thanone Z80 by now, inculding several in systems base on IM0. Point is that you base part on the answr on the claim that RST are not used with Z80, which is simply not true. I do not intend to argue, but to help improve your answer. – Raffzahn Jul 28 '22 at 00:28
  • @Raffzahn, the answer's fine as it is, says what I wanted to answer. Nowhere does it say IM0/RSTs are not used on Z80 systems, though, nothing's based on anything like that. Instead it says "So it's an easier route to make RST instructions available on those systems" etc. – TonyM Jul 28 '22 at 10:14
4

One additional point... if you write an application that's specific to one particular CP/M system and you know that there's an unused RST on the machine, it's super easy to copy the three bytes from 0005h to that vector and then use RST for your syscalls.

TeaRex
  • 703
  • 3
  • 11
  • Jup, neat trick. Except you also had do convince the compiler to generate that RST instead of a call 5. Easy with Assembler, less easy with any other language. Usually not worth the effort. – Raffzahn Jul 30 '22 at 10:37