CPU::Emulator::Memory::Banked - banked memory for a CPU emulator
my $memory = CPU::Emulator::Memory::Banked->new(); $memory->poke(0xBEEF, ord('s')); my $value = $memory->peek(0xBEEF); # 115 == ord('s') $memory->bank( address => 0x8000, size => 0x4000, type => 'ROM', file => '.../somerom.rom', writethrough => 1 ); my $value = $memory->peek(0xBEEF); # read from ROM instead $memory->poke(0xBEEF, 0); # write to underlying RAM
This class adds multiple memory banks to the flat memory space provided by CPU::Emulator::Memory. These temporarily replace chunks of memory with other chunk, to simulate bank-switching. Those chunks can be of arbitrary size, and can be either RAM, ROM, or 'dynamic', meaning that instead of being just dumb storage, when you read or write them perl code gets run.
It inherits all the methods from CPU::Emulator::Memory, including the constructor, and also implements ...
This method performs a bank switch. This changes your view of the memory, mapping another block of memory in place of part of the main RAM. The main RAM's contents are preserved (although see below for an exception). It takes several named parameters, three of which are compulsory:
The base address at which to swap in the extra bank of memory.
The size of the bank to swap. This means that you'll be swapping addresses $base_address to $base_address + $size - 1. This defaults to the size of the given
file, if supplied.
Either 'ROM' (for read-only memory), or 'RAM' to swap in a block of RAM. Support will be added in the future for type 'dynamic' which will let you run arbitrary perl code for reads and writes to/from the memory.
When you change memory banks, any banks already loaded which would overlap are unloaded.
The following optional parameters are also supported:
A file which backs the memory. For ROM memory this is compulsory, for RAM it is optional.
Note, however, that for RAM it must be a read/writeable *file* which will be created if necessary, whereas for ROM it must be a readable file or a readable *file handle*. It is envisioned that ROMs will often be initialised from data embedded in your program. You can turn a string into a filehandle using IO::Scalar - there's an example of this in the tests.
This is only meaningful for ROM. If set, then any writes to the addresses affected will be directed through to the underlying main RAM. Otherwise writes will be ignored.
Coderefs which will be called when 'dynamic' memory is read/written. Both are compulsory for 'dynamic' memory. They are called with a reference to the memory object, the address being accessed, and (for function_write) the byte to be written. function_read should return a byte. function_write's return value is ignored.
This method unloads a bank of memory, making the main RAM visible again at the affected addresses. It takes a single named parameter 'address' to tell which bank to switch.
This is replaced by a version that is aware of memory banks but has the same interface. peek8 and peek16 are wrappers around it and so are unchanged.
This method is replaced by a bank-aware version with the same interface. poke8 and poke16 are wrappers around it and so are unchanged.
The private method _readROM may be useful for subclasses. If passed a filename, it is just a wrapper around the parent class's _read_file. If passed a reference to a filehandle, it reads from that.
All those inherited from the parent class.
No others known.
I welcome feedback about my code, including constructive criticism and bug reports. The best bug reports include files that I can add to the test suite, which fail with the current code and will pass once I've fixed the bug.
Feature requests are far more likely to get implemented if you submit a patch yourself.
Copyright 2008 David Cantrell <email@example.com>
This module is free-as-in-speech software, and may be used, distributed, and modified under the same terms as Perl itself.
This module is also free-as-in-mason software.