1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
mod naive;
mod paged;

pub use naive::NaiveMemory;
pub use paged::PagedMemory;

use crate::addr::Addr;

/// A trait representing a generic interface to a device that can be accessed through
/// memory operations.
///
/// This can be used to implement several virtual hardware devices including RAMs, ROMs,
/// and memory-mapped devices, like uarts or video buffers.
///
/// It is mandatory to implement `load_byte` and `store_byte`. All other operations are
/// optional and will default to being defined in terms of these two.
///
/// Several hardware-level notions are ignored, including whether the memory is
/// readable, writable, or executable, or whether it is a memory or a device. There is no notion
/// of atomic access.
///
/// There are two implementations provided in this library:
///
/// [`NaiveMemory`](crate::memory::NaiveMemory) is an incredibly simple, but inefficient
/// implementation that uses a hashmap to assign each memory addresses to its current value.
/// It is here only as an illustration of the `Memory` interface.
///
/// [`PagedMemory`](crate::memory::PagedMemory) is a *slightly* less naive implementation.
/// Whenever an address is first accessed, it will allocate a 4KiB page containing that address.
/// The pages are then at least contiguous in the host machine's memory.
pub trait Memory {
    fn load_byte(&self, addr: Addr) -> u8;

    fn load_halfword(&self, addr: Addr) -> u16 {
        let mut val = self.load_byte(addr) as u16;
        val |= (self.load_byte(addr + 1) as u16) << 8;
        val
    }

    fn load_word(&self, addr: Addr) -> u32 {
        let mut val = self.load_byte(addr) as u32;
        val |= (self.load_byte(addr + 1) as u32) << 8;
        val |= (self.load_byte(addr + 2) as u32) << 16;
        val |= (self.load_byte(addr + 3) as u32) << 24;
        val
    }

    fn store_byte(&mut self, addr: Addr, value: u8);

    fn store_halfword(&mut self, addr: Addr, value: u16) {
        self.store_byte(addr, (value & 0x000000ff) as u8);
        self.store_byte(addr + 1, ((value & 0x0000ff00) >> 8) as u8);
    }

    fn store_word(&mut self, addr: Addr, value: u32) {
        self.store_byte(addr, (value & 0x000000ff) as u8);
        self.store_byte(addr + 1, ((value & 0x0000ff00) >> 8) as u8);
        self.store_byte(addr + 2, ((value & 0x00ff0000) >> 16) as u8);
        self.store_byte(addr + 3, ((value & 0xff000000) >> 24) as u8);
    }

    fn memcpy(&mut self, base_addr: Addr, buf: &[u8]) {
        for (i, byte) in buf.iter().enumerate() {
            self.store_byte(base_addr + i, *byte);
        }
    }
}