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 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
//! Implementation of Falcon code and data memory in SRAM.
use byteorder::{ByteOrder, LittleEndian};
pub use tlb::*;
mod tlb;
/// The size of a physical memory page in Falcon code space.
pub const PAGE_SIZE: usize = 0x100;
/// Representation of the Falcon memory space.
///
/// It consists of separate memory spaces for data and code,
/// where data is stored in a flat piece of memory and code
/// in virtual memory pages, translated by a hidden TLB.
pub struct Memory {
/// Representation of the Falcon data space.
///
/// This is a linear piece of memory with byte-oriented addressing,
/// used for variables and stack memory. It can be addressed in 8-bit,
/// 16-bit and 32-bit quantities where unaligned memory access leads
/// to data corruption.
pub data: Vec<u8>,
/// Representation of the Falcon code space.
///
/// Code segment uses primitive paging in 0x100 byte pages.
/// Address translation is done in hidden TLB memory, with one entry
/// for each physical page.
pub code: Vec<u8>,
/// Representation of the hidden Falcon TLB.
///
/// The TLB is used for address translation via an array of entries,
/// each representing a physical page index.
pub tlb: Tlb,
}
impl Memory {
/// Creates a new instance of the memory, initialized to all zeroes by
/// default.
pub fn new() -> Self {
// TODO: Compute these values through UC_CAPS MMIO.
Memory {
data: vec![0; 0x4000],
code: vec![0; PAGE_SIZE * 0x80],
tlb: Tlb::new(),
}
}
/// Reads a byte from a given address in Falcon data space.
pub fn read_data_byte(&self, address: u32) -> u8 {
self.data[address as usize]
}
/// Reads a halfword from a given address in Falcon data space.
pub fn read_data_halfword(&self, mut address: u32) -> u16 {
// Enforce aligned memory access.
address &= !1;
LittleEndian::read_u16(&self.data[address as usize..])
}
/// Reads a word from a given address in Falcon data space.
pub fn read_data_word(&self, mut address: u32) -> u32 {
// Enforce aligned memory access.
address &= !3;
LittleEndian::read_u32(&self.data[address as usize..])
}
/// Reads a word from a given physical address in code space.
pub fn read_code_addr(&self, address: u16) -> u32 {
LittleEndian::read_u32(&self.code[address as usize..])
}
/// Writes a byte to a given address in Falcon data space.
pub fn write_data_byte(&mut self, address: u32, value: u8) {
self.data[address as usize] = value;
}
/// Writes a halfword to a given address in Falcon data space.
pub fn write_data_halfword(&mut self, mut address: u32, mut value: u16) {
// If the address is unaligned, fuck up the written value.
if (address & 1) != 0 {
value = (value & 0xFF) << (address as u16 & 1) * 8;
}
// Enforce aligned memory access.
address &= !1;
LittleEndian::write_u16(&mut self.data[address as usize..], value);
}
/// Writes a word to a given address in Falcon data space.
pub fn write_data_word(&mut self, mut address: u32, mut value: u32) {
// If the address is unaligned, fuck up the written value.
if (address & 1) != 0 {
value = (value & 0xFF) << (address & 3) * 8;
} else if (address & 2) != 0 {
value = (value & 0xFFFF) << (address & 3) * 8;
}
// Enforce aligned memory access.
address &= !3;
LittleEndian::write_u32(&mut self.data[address as usize..], value);
}
/// Writes a word to a given physical address in code space.
pub fn write_code_addr(&mut self, address: u16, value: u32) {
LittleEndian::write_u32(&mut self.code[address as usize..], value);
}
}