In this episode of computer archeology, we deconstruct a very interesting case of borrowing code from multiple places – but first try this D64 disk image with any Commodore 64 emulator or a real C64:
The CP/M operating system was the first home computer OS that ran on machines from many different vendors, as long as they had an Intel 8080 compatible CPU – which the 8 bit machines from Commodore, Apple, Atari and BBC didn’t: They used the MOS 6502 CPU.
The Commodore 64 CPM Cartridge
In 1983, Commodore released the the “CP/M Cartridge” for the Commodore 64, which contained a Z80 CPU and came with CP/M 2.2: The OS and its apps would run on the Z80, which would switch over to the computer’s 6502 to call code in the original ROM for screen, keyboard and disk accesses. It wasn’t very good, didn’t sell well, and was quickly discontinued.
Roßmöller’s CP/M Emulator
But it was also possible to run CP/M on a C64 without a hardware extension: All that is necessary is to emulate the Intel 8080 instruction set on the MOS 6502. And the German company Roßmöller did exactly that. In 1988, a little while after releasing their 4 MHz speeder cartridge “Turbo Process” for the C64, they started selling
CP/M Emulator – CP/M 2.2-kompatibler C64 ohne zusätzl. Hardware
for 10 DM.
The product is directly based on Commodore’s adaption of CP/M for the C64 CP/M cartridge. Commodore’s disk contains a small bootloader that loads BIOS and the Z80 bootstrap from the first sectors on disk (as specified by CP/M) – the bootloader of the CP/M Emulator directly contains slightly patched versions of BIOS and the Z80 bootstrap, as well as the 8080 emulator:
CP/M Cartridge | CP/M Emulator |
---|---|
0 "CP/M DISK " 65 2A 1 "CPM " PRG 0 BLOCKS FREE. |
0 "CP/M EMULATOR " 32 0B 12 "CP/M " PRG 0 BLOCKS FREE. |
The first sectors on disk still contain the original versions of BIOS and the Z80 bootstrap – and curiously, all references on disk to “Digital Research”, the CP/M license holder, have been replaced with “Roßmöller”, hinting towards a very questionable licensing status – not inconsistent with Roßmöller’s reputation at the time.
CP/M Cartridge | CP/M Emulator |
---|---|
COMMODORE 64 44k CP/M vers 2.2 Copyright (C) 1979, Digital Research Copyright (C) 1982, Commodore A> |
R 03 07 COMMODORE 64 44k CP/M vers 2.2 =============================== Copyright (C) 1988, ROSSMOELLER A> |
Emulator Performance
The Roßmöller 8080 emulator contained in the boot program is less than 1.4 KB in size – and it’s super slow: Executing a single 8080 instruction takes between 300 and 800 cycles (485 on average). This makes the CP/M emulator about 100 times slower than a similarly spec’ed 8080-based machine. Even with the 8 MHz speeder, the emulator was still 12 times slower. It was in fact so slow that it failed to register normal keypresses on stock C64 systems unless you held the keys for a second.
8080 Simulator for the 6502
There is a simple reason the emulator is this slow: It wasn’t written for speed. It wasn’t even written as an emulator. As it turns out, the Roßmöller 8080 emulator is a binary-patched version of the 8080 Simulator for the 6502, KIM-1 Version “8080 simulator-debug package” by Dann McCreary, written in 1978.
8080 Simulator for the 6502 is an interactive 8080 simulator primarily aimed at teaching 8080 assembly – and optimized for size, since it had to to fit into the 2 KB of RAM of the KIM-1.
In the following implementation of the memory access instructions STAX/LDAX, you can see that the Roßmöller version just relocated everything by $CA pages:
8080 Simulator for the 6502 | Roßmöller CP/M Emulator |
---|---|
0249 DIRECT PHA ; TEMP SAVE 024A JSR PC/PNT ; PC TO POINTER 024D JSR DBLINC 0250 LDXIM (SCR) 0252 JSR MEM/RP 0255 LDXIM (PNT) 0257 JSR SRC/RP 025A PLA ; RECOVER TEMP 025B LSRA ; LOAD? 025C BCS STORE ; NO, STORE 025E ASLA ; MAKE INDEX 025F TAX 0260 BEQ LDAD ; LOAD A DIRECT? 0262 JMP MEM/RP ; NO, LOAD HL DIRECT |
.,CC49 48 PHA .,CC4A 20 5D CD JSR $CD5D .,CC4D 20 80 CD JSR $CD80 .,CC50 A2 19 LDX #$19 .,CC52 20 A0 CD JSR $CDA0 .,CC55 A2 17 LDX #$17 .,CC57 20 70 CD JSR $CD70 .,CC5A 68 PLA .,CC5B 4A LSR A .,CC5C B0 0E BCS $CC6C .,CC5E 0A ASL A .,CC5F AA TAX .,CC60 F0 03 BEQ $CC65 .,CC62 4C A0 CD JMP $CDA0 |
Roßmöller Changes
But there are also sections in the emulator that had to be adapted for use in the CP/M context:
- The memory model of the C64 CP/M cartridge made the 8080 CPU see the machine’s RAM moved up by $1000 – so the 8080 RST area and the 6502 zero page would not be at the same spot. The emulator also uses this layout, so every memory access has to go through an expensive 16 bit addidion. (Avoiding this addition would have been the better strategy – and ironically, Dann’s original design document explains why he decided not to shift memory!)
- The interpreter was extended to special case “LD ($CE00),A” (which switches back to the 6502) and to support the “LDIR” instruction (a Z80 extension used by CP/M), but it seems the people patching the emulator didn’t understand the code well enough to work “LDIR” into the instruction decoder or put the address check into the “LD (), A” handler – instead, the interpreter loop checks for these instruction before the normal decode. (Ironically, the emulator already contains a special opcode to switch to 6502 mode, so it would have been easier to patch the code that switches modes.)
-
The following code increments the 8080 program counter in 45 cycles, while it could also have been done is as little as 6 cycles:
ldx #pc - registers ldy #const_one - registers clc : lda registers,x adc registers,y sta registers,x iny inx tya and #1 bne :- ; do it twice
Roßmöller did not make these modifications in source, but binary patched the original KIM-1 version: Changing the exact page alignment of the emulator would have required an extensive rework of the extremely optimized instruction decoder.
But it doesn’t mean the patches are done in an elegant way: They are very wasteful, weird… and just horrible. The following example stores a 16 bit register in memory at the address pointed to by PNT:
8080 Simulator for the 6502 | Roßmöller CP/M Emulator |
---|---|
017D RP/MEM LDYIM #$01 ; CLEAR INDEX 017F RPLP LDAZX REGS+1 ; GET NEXT RP DATA 0181 STAIY PNT ; STORE IN MEMORY 0183 DEX 0184 DEY 0185 BEQ RPLP 0187 RTS |
.,CB7D A0 01 LDY #$01 .,CB7F 20 44 CE JSR $CE44 .,CB82 EA NOP .,CB83 CA DEX .,CB84 88 DEY .,CB85 F0 F8 BEQ $CB7F .,CB87 60 RTS .,CE44 20 C0 CE JSR $CEC0 .,CE47 B5 36 LDA $36,X .,CE49 91 C1 STA ($C1),Y .,CE4B 60 RTS .,CEC0 08 PHP .,CEC1 20 20 CE JSR $CE20 .,CEC4 28 PLP .,CEC5 60 RTS .,CE20 A5 4C LDA $4C .,CE22 85 C1 STA $C1 .,CE24 A5 4D LDA $4D .,CE26 18 CLC .,CE27 69 10 ADC #$10 .,CE29 85 C2 STA $C2 .,CE2B 60 RTS |
Since for CP/M, memory is shifted by $1000, the Roßmöller version has to copy PNT into a temporary zero page location, add $1000 and use this one instead of PNT. The patch at $CE44 does the extra work. It calls the common routine $CEC0, which is supposed to make the copy + $1000 – and this function itself is a wrapper around a library function that saves the status bits for no reason. A more optimized version would have replaced all callers to $CB7D by code that does all this in one go. There was no reason to reuse logic; there are kilobytes of unused space!
Was the Emulator Licensed?
Dann McCreary confirmed that Roßmöller never licensed the emulator from him:
I don’t have all the old records, but it seems likely that they bought a KIM-1 version directly from me at some point, or got it from one of my customers.
After I commented that he was unlikely to sell any more copies of the KIM-1 version around the time the Roßmöller emulator was written, Dann wrote:
I made random sales all around the globe… but you’re probably right, 1987 sounds pretty late… Perhaps one of the Rossmuller principals had bought from me years earlier?
Given that all Digital Research copyright messages were also completely removed from the product, it seems unlikely that the CP/M part was licensed.
Conclusion
Dann McCreary:
Back in the day, I was asked repeatedly whether the simulator was suitable for running CP/M, and my response invariably was that (in my opinion) it would run unacceptably slow. That is primarily why I never attempted the task myself. Of course, my code was completely optimized for space and not for speed… Those were the days of a 1.2K RAM space (KIM-1), or if you were lucky, 16K (Apple ][).
This is very interesting, thanks. You mention that Roßmöller had this reputation already, can you elaborate on that? I recall seeing ads for their products in magazines like 64’er all the time, so they were quite well-known.