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