Run CP/M on your C64 – using Emulation!

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:


Commodore 64 CP/M Emulator

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 Version8080 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 ][).

1 thought on “Run CP/M on your C64 – using Emulation!”

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

Leave a Comment