Skip to content →

loading KERNAL, BASIC and CHR

So, to make our C64 boot, we need some of the original software that ran on it, of course. First we need to take a look at the memory map.

 $FFFF = 65535 ┌───────────────┬───────────────┐
               │---------------│|||||||||||||||│     ||| = read by e.g. PEEK
               │---------------│|||||||||||||||│     --- = written by e.g. POKE
               │---------------│|||||||||||||||│     +++ = read + write
               │---------------│||| KERNAL ||||│     sonst = no access with BASIC
               │---------------│|||||||||||||||│
               │---------------│|||||||||||||||│
               │---------------│|||||||||||||||│
 $E000 = 57344 ├───────────────┼───────────────┼───────────────┐
               │               │               │+++++++++++++++│
               │               │     CHAR      │+++++ I/O +++++│
               │               │               │+++++++++++++++│
 $D000 = 53248 ├───────────────┼───────────────┴───────────────┘
               │+++++++++++++++│
               │+++++++++++++++│
               │+++++++++++++++│
 $C000 = 49152 ├───────────────┼───────────────┐
               │---------------│|||||||||||||||│
               │---------------│|||||||||||||||│
               │---------------│||| BASIC- ||||│
               │---------------│|||  ROM   ||||│
               │---------------│|||||||||||||||│
               │---------------│|||||||||||||||│
               │---------------│|||||||||||||||│
 $A000 = 40960 ├───────────────┼───────────────┘
               │+++++++++++++++│
               │+++ BASIC- ++++│
               │+++  RAM   ++++│
 $0800 = 2048  │+++++++++++++++│-┐
 $0400 = 1024  │+++++++++++++++│-┘Video-RAM
 $0000         └───────────────┘-┘Zeropage

As you can see, we need to load the KERNAL, the CHAR (-ROM) and the BASIC-ROM into our 64k adress space we have available.
These files can be obtained from your original C64, or can be downloaded from the Zimmers page.
Please respect copyright laws where they might apply.

So, with the roms at our hands, we load them and make them adressable.

void loadFirmware(string filename) {

	//	load fw
	unsigned char cartridge[0x4000];
	FILE* file = fopen(filename.c_str(), "rb");
	int pos = 0;
	while (fread(&cartridge[pos], 1, 1, file)) {
		pos++;
	}
	fclose(file);

	//	map BASIC interpreter (first 8kb) to 0xa000 - 0xbfff
	for (int i = 0; i < 0x2000; i++) {
		basic[i] = cartridge[i];
	}

	//	map KERNAL
	for (int i = 0; i < 0x2000; i++) {
		kernal[i] = cartridge[0x2000 + i];
	}
}

void loadCHRROM(string filename) {
	
	//	load file
	unsigned char cartridge[0x4000];
	FILE* file = fopen(filename.c_str(), "rb");
	int pos = 0;
	while (fread(&cartridge[pos], 1, 1, file)) {
		pos++;
	}
	fclose(file);

	//	map first character ROM
	for (int i = 0; i < 0x800; i++) {
		chr1[i] = cartridge[i];
	}
	//	map second character ROM
	for (int i = 0; i < 0x800; i++) {
		chr2[i] = cartridge[i + 0x800];
	}
}

This all happens in our MMU class, where we control and direct memory writes and reads. So we can prevent access to memory, where the code shouldn’t be allowed to read from / write to.

uint8_t readFromMem(uint16_t adr) {
	switch (adr)
	{
                ...
		default:
			if (adr >= 0x1000 && adr < 0x1800) {		//	CHR1 
				return chr1[adr % 0x1000];
			}
			if (adr >= 0x1800 && adr < 0x2000) {		//	CHR2 
				return chr2[adr % 0x1800];
			}
			if (adr >= 0xa000 && adr < 0xc000) {		//	BASIC
				return basic[adr % 0xa000];
			}
			if (adr >= 0xe000 && adr < 0x10000) {		//	KERNAL
				return kernal[adr % 0xe000];
			}
			return memory[adr];
			break;
	}
}

void writeToMem(uint16_t adr, uint8_t val) {
...

So, what are we loading here exactly?

  • KERNAL – The basic software, close to a BIOS, that talks to the hardware and peripherals
  • BASIC-ROM – The BASIC interpreter that can run the routines, programmed for this machine
  • CHR1/CHR2 – The 2 character sets that the C64 contains
both character sets on the c64

With the ROMs in place, it’s time to set our PC to the reset vector, 0xfffc for the 6502, and let it run. But now, we will need to have graphical output, so it’s time to implement the VIC next.

Comments

Leave a Reply