ED64 - HOW TO WRITE A COMMODORE 64 EMULATOR
By ir. Marc Dendooven


Chapter 7 Memory management.

  1. Introduction

    The emulator works fine for basic programs (if they do not try to access non-emulated hardware) and for small machine code if placed in a usable ram space (like the $C000 region). On the c64, the rom and memory mapped io can be banked in or out to make place for larger programs in ram. Also the video rom, which is for the moment invisible for the user, can be banked in. This will be emulated in this chapter.

  2. Bugfix

The V flag was not correctly tested in the ADC and SBC instructions. This has now been corrected.

  1. IO access

IO access has been done by a function called io. In fact this function just treats input functionality. In will be renamed to ioIn. Output will be handled by a procedure called ioOut. For the moment this procedure will do nothing.

function ioIn(address : word) : byte;
begin
    case address of
        $D012 : ioIn := $00
    else
        ioIn := $FF
    end
end;

procedure ioOut(address : word ; value : byte);
begin
    ;
end;

  1. How banking works

    The memory map can be changed by 5 (hardware) lines called ..., ..., HIRAM, LORAM and CHAREN. The first two are changed if a cartridge is present. We will ignore them. HIRAM, LORAM and CHAREN are driven by the io port of the 6510 processor. This port can be accessed at address 0001. Address 0000 sets the direction for this port.

  2. Accessing the io port

    the port can be set by intercepting it in the poke.

procedure poke(address : word ; value : byte);
begin
    ram[address] := value;
    case address of
        $0001 : ioport(value);
        $400..$7E7 : VicText(address-$400,value)
    end
end;

We can represent LORAM, HIRAM and CHAREN by booleans. They can be found respectively at bit 0,1 and 2 of the (masked) io register.

procedure ioport(port : byte);
var maskedPort : byte;
begin
    maskedPort := port and ram[0];
    loram := boolean (maskedport and 1);
    hiram := boolean (maskedPort and 2);
    charen := boolean (maskedport and 4);
end;

Since the effective banking signals are a logical combination of those lines we define :

hiANDlo := hiram and loram;
hiORlo := hiram or loram;

  1. Banking

    Effectively banking can be emulated by choosing the right destination in the peek and poke instructions:

procedure poke(address : word ; value : byte);
begin
    ram[address] := value;
    case address of
        $0001 :         ioport(value);
        $400..$7E7 :    VicText(address-$400,value);
        $D000..$DFFF :  if hiORlo then ioOut(address,value)
    end
end;

function peek(address : word) : byte;
begin
    case address of
        $A000..$BFFF : if hiANDlo then peek := basic_rom[address]
                                  else peek := ram[address];
        $D000..$DFFF : if hiORlo
                                  then
                                    begin
                                        if charen then peek := ioIn(address)
                                                  else peek := char_rom[address];
                                    end
                                  else peek := ram[address];
        $E000..$FFFF : if hiram then peek := kernal_rom[address]
                                else peek := ram[address];
    else
        peek := ram[address]
    end
end;


remark : again, this is not the most performant solution, but is simple and will do for now.


  1. Conclusion

    In this chapter we enabled banking rom and io. By doing so the emulator can now run programs which use underlying ram. These are machine code programs which doesn't use the rom's (including compiled programs) and alternatives for the rom's loaded in ram. Also the character rom can now be read. See memio.pas
    In the next chapter we will emulate some io hardware (the VIC).