Dual Core C64

Exploring reconfigurability of the ARM-powered C64 I added a Z80 emulator to the existing 6510 emulator. And for dynamic testing what better than cartridgeless C64 CP/M.
So, heterogeneous multi-software-core C64 is obtained. Of course non-parallel concurrency is obtained, as only one hardware core (ARM) is available.

For photos and videos, a very visual (and retro) effect was included, setting screen border color  according to working core (Light Blue: 6510, Red:Z80)

C64 CP/M Background
In 80's Commodore developed a CP/M-cartridge that contained a Z80 to benefit C64 of software
available for CP/M. See more about this at Ruud Baltissen's site.

As the original cartridge shares buses between 6510 and Z80 (and also VIC), not allowing simultaneous processors operation, the presented ARM based, non-parallel dual core, is enough for C64 CP/M execution.
Some CP/M BIOS portions, like disk access take advantage of C64 kernal (ROM) and were written for 6510, and CP/M core, running on Z80, calls them continually (as border colors in the video).

6510 core:
In previous post an ARM based C64 was presented, with a C coded 6502 emulator modified for 6510-like operation. It's based on the great Mike Chambers fake6502 emulator.

Z80 emulator
Looking for free portable Z80 C coded emulator I found Marcel de Kogel's Z80emu: "written in pure C, which can be used on just about every 32+ bit system". It was easyly integrated to the existing project IDE: a bare-metal LPC1769 Eclipse workspace.
For ARM compilation "low-endian CPU" option must be declared in Z80.h at "Machine dependent definitions" section.
Z80 use in the C64 cartridge is limited, as  IORQ and interrupts are not used, only memory access must be implemented.

Z80 memory access
User must provide Z80emu with the Z80_RDMEM() and Z80_WRMEM() functions. As buses are shared with 6510 CPU, the same memory access C functions used by 6510 emulator are used by Z80 emulator: externalread() and externalwrite()

/* Read a byte from given memory location                                   */
unsigned Z80_RDMEM(dword A){

/* Write a byte to given memory location                                    */
void Z80_WRMEM(dword A,byte V){
externalwrite(((A&0xffff)+0x1000), V&0xff);

Note the 0x1000 term added to adresses, recreating the 74LS283 adder included in the CP/M
cartridge for address-space shift. This is so because of the conflict betwheen 6510's I/O port and Z80's reset vector, both located at 0x0000.

Core switching
Without a core scheduler, the C64 CP/M cartridge, implementes a simple scheme. The Z80 is enabled or dissabled writing a byte to an address in the range $DE00/$DEFF with LSB = 0 or 1.
So, core switch is entrusted to software, look how CP/M does it:

6510 assembler code, part of C64 CP/M Bios: http://www.z80.eu/c64/BIOS65.ASM

MODESW = $DE00 1 = Z80 OFF, 0 = Z80 ON

LDA #0 turn Z80 back on
NOP delay only

Z80 assembler code, part of Z80 bootstrap routine for the C64: http://www.z80.eu/c64/C64BOOT.ASM


Note MODESW definitios due to address shift.

This functionality was implemented as follows:
A catch in externalwrite() function enables changes the value of a "processor flag" variable (like the Flip Flop in the cartridge )
In main loop, according to "processor flag" variable, one of the following action is performed:

  • Z80 instruction execution
  • 6510 instruction execution and 6510 interrupt  treatment

Recently I found Kernal64, a Scala Commodore 64 emulator supporting CP/M.
Its autor resolved this, long before, in a very simmilar way.

CP/M loading
For previous C64 IEC testing the Uno2IEC was used, but this simple Arduno based drive emulator does not support sector read and write needed by CP/M disk access.
Not having available a disk drive or highly compatible device (SD2IEC, uIEC, etc.) another solution is necessary, described on its own post: Software-core C64 diskless CP/M boot

More photos

3 comentarios:

  1. Hi! Nice project, very inspirative.
    BTW: Know you that CP/M Cartridge for C64 works also on C128 and makes available 8502 and two Z80? Also direct switch from internal Z80 to external works? In case of SCPU128 - it disables internal Z80, but external Z80 is still alive... I'm interest in your project - you have also very described CP/M and Z80...
    I'm 128er.

  2. Really really fascinating, are you planning to release the code? are you using the plain CMSIS or mbed? have you tried the dma transer for the data? I do belive you can go faster than that , I managed to squeeze quiet a lot of data out of the 1769 in the past...

  3. The LPC1769 is so fast you could manage to hook it up to the bus directly, with some clever assembler routines you could have it reacting to DMA requests from the VIC in an interrupt routine. (in assembler it enters the interrupt routine in about 100 ns) I did something similar with a LPC1769 connected to a Videopac console emulating the cartridge ROM and in the free time outside the interrupt routine I emulate the speech synthesizer. The LPC1769 is 5 Volt tolerant on the IO.