ED64 - HOW TO WRITE A
COMMODORE 64 EMULATOR
By ir. Marc Dendooven
Chapter 7 Memory management.
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.
Bugfix
The V flag was not correctly tested in the ADC and SBC instructions. This has now been corrected.
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;
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.
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;
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.
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).