Struct rz80::Memory
[−]
[src]
pub struct Memory { pub heap: [u8; 131072], // some fields omitted }
memory access
The Memory object wraps access to the Z80's 64 KByte address space. All memory access goes through a page table with a page-size of 1 KByte. The page table mapping allows a very simple implementation of bank-switching, which was a popular way in 8-bit computers to manage more than 64 KBytes of memory.
Memory Layers
Mapped memory is assigned to 1 out of (currently) 4 layers. If 2 memory chunks are mapped to the same CPU address range on different layers, only the memory assigned to the higher-priority layer is visible to the CPU (layer number 0 has the highest priority and layer number 3 the lowest).
The layer concept is easier to visualize than to describe:
+---------------------------------------+
LAYER 3 |333333333333333333333333333333333333333|
+-------+---------------+---------------+
LAYER 2 |222222222222222|
+---------------+ +-------+
LAYER 1 |1111111|
+---------+ +-------+
LAYER 0 |000000000|
+---------+
+-------+-----+---------+-------+-------+
CPU VISIBLE: |3333333|22222|000000000|3333333|1111111|
+-------+-----+---------+-------+-------+
The Heap
The Memory class will never keep references to external memory, instead it comes with it's own few hundred KBytes of embedded memory which is used as 'heap'. A single memory page maps 1 KByte of memory from the Z80 address range to 1 KByte of memory somewhere on the embedded heap.
Mapping Memory
This 'maps' a chunk of memory in Z80 address range to a chunk of memory of the same size in the embedded heap on one of the four memory layers.
The simple form performs the memory mapping but does not copy any data into the mapped memory region:
use rz80::Memory; let mut mem = Memory::new(); // map 32 KByte at heap address 0x08000 to CPU addr 0x0000 // on layer 0 as writable: mem.map(0, 0x08000, 0x0000, true, 32*1024); // map another 32 KByte at heap address 0x10000 to CPU addr 0x8000 // on layer 1 as read-only: mem.map(1, 0x10000, 0x8000, false, 32*1024);
The method map_bytes() performs a memory mapping as above, but also copies a range of bytes into the mapped memory. This is useful to initialize the memory with a ROM dump.
use rz80::Memory; let mut mem = Memory::new(); let rom = [0xFFu8; 4096]; // assume that 'rom' is a system ROM dump, and map it as read-only to CPU address 0xF000 mem.map_bytes(0, 0x00000, 0xF000, false, &rom);
Reading and Writing Memory
The most common operations are reading and writing 8- and 16-bit unsigned values:
use rz80::Memory; // new_64k() is a shortcut method to get a 64k RAM mapping let mut mem = Memory::new_64k(); // write and read unsigned bytes mem.w8(0x0100, 0x23); let b = mem.r8(0x0100); assert!(b == 0x23); // ...same with 16-bit unsigned words mem.w16(0x0200, 0x1234); let w = mem.r16(0x0200); assert!(w == 0x1234); // memory is little endian and wraps around at 64k mem.w16(0xFFFF, 0x1122); let l = mem.r8(0xFFFF); let h = mem.r8(0x0000); assert!(l == 0x22); assert!(h == 0x11);
There is a special method to read an 8-bit signed value. This exists for the Z80's indexed and relative addressing instructions:
use rz80::Memory; let mut mem = Memory::new_64k(); mem.w8(0x0100, 0xF0); let s = mem.rs8(0x0100); assert!(s == -16);
Trying to write to ROM areas will silently fail, unless the w8f() method is used:
use rz80::Memory; let mut mem = Memory::new(); let rom = [0x11u8; 1024]; mem.map_bytes(0, 0x00000, 0x0000, false, &rom); let b0 = mem.r8(0x0100); assert!(b0 == 0x11); // try to write read-only memory mem.w8(0x0100, 0x33); let b1 = mem.r8(0x0100); assert!(b1 == 0x11); // force-write to read-only memory mem.w8f(0x0100, 0x33); let b2 = mem.r8(0x0100); assert!(b2 == 0x33);
You can write a whole chunk of memory, ignoring write protection, this is useful to load program dumps into emulator memory:
use rz80::Memory; let mut mem = Memory::new_64k(); let dump : &[u8] = &[1, 2, 3]; mem.write(0x0100, dump); assert!(mem.r8(0x0100) == 1 && mem.r8(0x0101) == 2 && mem.r8(0x0102) == 3);
Fields
heap: [u8; 131072]
'host' memory
Methods
impl Memory
[src]
fn new() -> Memory
return new, unmapped memory object
fn new_64k() -> Memory
return new memory object with 64 kByte mapped, writable memory (for testing)
fn map(&mut self, layer: usize, heap_offset: usize, addr: usize, writable: bool, size: usize)
map a chunk of uninitialized heap memory to CPU-mapped memory
fn map_bytes(&mut self, layer: usize, heap_offset: usize, addr: usize, writable: bool, content: &[u8])
map a chunk of heap memory, and initialize it
fn unmap(&mut self, layer: usize, size: usize, addr: usize)
unmap a chunk heap memory
fn unmap_layer(&mut self, layer: usize)
unmap all pages in a layer
fn unmap_all(&mut self)
unmap all pages in all layers
fn r8(&self, addr: RegT) -> RegT
read unsigned byte from 16-bit address
fn rs8(&self, addr: RegT) -> RegT
read signed byte from 16-bit address
fn w8(&mut self, addr: RegT, val: RegT)
write unsigned byte to 16-bit address
fn w8f(&mut self, addr: RegT, val: RegT)
write unsigned byte, ignore write-protection flag
fn r16(&self, addr: RegT) -> RegT
read unsigned word from 16-bit address
fn w16(&mut self, addr: RegT, val: RegT)
write unsigned word to 16-bit address
fn write(&mut self, addr: RegT, data: &[u8])
write a whole chunk of memory, ignore write-protection