Commit d7bf97b3 authored by Arthur Carlsson's avatar Arthur Carlsson

Added traits for read-only and read/writable memory

parent 886c45e6
Pipeline #25843482 failed with stage
in 1 minute and 45 seconds
use byteorder::BigEndian;
use rom::Rom;
use cpu::mem;
pub struct Cart {
rom: Rom<BigEndian>,
pub enum Cart {
}
impl Cart {
pub fn new(buf: Vec<u8>) -> Cart {
let mut rom = Rom::new(buf);
match rom[0] {
0x37 => rom.byte_swap(),
0x40 => rom.endian_swap(),
_ => (),
}
pub fn new(buf: Vec<u8>) -> mem::Mem<Cart> {
let buf = match buf[0] {
0x37 => mem::byte_swap(buf.clone()),
0x40 => mem::endian_swap(buf.clone()),
_ => buf
};
Cart { rom: rom }
}
pub fn title(&self) -> String {
self.rom.read_ascii_string(0x0020, 20)
mem::Mem::new(buf)
}
}
impl Cart {
pub fn read_word(&self, addr: u32) -> u32 {
self.rom.read_word(addr)
impl mem::ReadOnly for Cart {
type ByteOrder = BigEndian;
}
impl mem::Mem<Cart> {
pub fn title(&self) -> String {
self.read_ascii_string(0x0020, 20)
}
}
......@@ -88,7 +88,7 @@ mod tests {
use byteorder::BigEndian;
enum TestMemCtx {}
impl mem::MemCtx for TestMemCtx {
impl mem::ReadWrite for TestMemCtx {
type ByteOrder = BigEndian;
}
......
......@@ -2,21 +2,50 @@ use byteorder;
use byteorder::ByteOrder;
use std::marker::PhantomData;
pub trait MemCtx {
/// Marker trait for a read-only memory
pub trait ReadOnly {
type ByteOrder: byteorder::ByteOrder;
}
pub struct Mem<T: MemCtx> {
/// Marker trait for readable and writable memory
pub trait ReadWrite {
type ByteOrder: byteorder::ByteOrder;
}
/// Reader for the memory as seen from the CPU's perspective
pub trait Read {
fn readb(&self, addr: u32) -> u8;
/// Reads a word from the memory at the given address.
fn readw(&self, addr: u32) -> u32;
}
/// Writer for the memory as seen from the CPU's perspective
pub trait Write {
/// Writes a byte to the given address
fn writeb(&mut self, addr: u32, byte: u8);
/// Writes a word to the given address
///
/// TODO: What happens if there is no room for the word? I.e. the address is just at the end of the memory?
fn writew(&mut self, addr: u32, word: u32);
}
pub struct Mem<T> {
buf: Vec<u8>,
phantom: PhantomData<T>,
phantom: PhantomData<T>
}
impl<T> ReadOnly for T where T: ReadWrite {
type ByteOrder = T::ByteOrder;
}
impl<T: MemCtx> Mem<T> {
impl<T> Mem<T> {
/// Creates a new memory buffer with the pre-filled memory content
pub fn new(data: Vec<u8>) -> Self {
Self {
buf: data,
phantom: PhantomData,
phantom: PhantomData
}
}
......@@ -24,19 +53,18 @@ impl<T: MemCtx> Mem<T> {
pub fn with_capacity(size: usize) -> Self {
Self {
buf: vec![0xab; size],
phantom: PhantomData,
phantom: PhantomData
}
}
}
/// Reader for the memory as seen from the CPU's perspective
pub trait Read {
fn readb(&self, addr: u32) -> u8;
/// Reads a word from the memory at the given address.
fn readw(&self, addr: u32) -> u32;
pub fn read_ascii_string(&self, addr: u32, count: usize) -> String {
String::from_utf8_lossy(&self.buf[addr as usize..addr as usize + count])
.trim_right_matches('\0')
.to_string()
}
}
impl<T: MemCtx> Read for Mem<T> {
impl<T> Read for Mem<T> where T: ReadOnly {
fn readb(&self, addr: u32) -> u8 {
self.buf[addr as usize]
}
......@@ -46,18 +74,7 @@ impl<T: MemCtx> Read for Mem<T> {
}
}
/// Writer for the memory as seen from the CPU's perspective
pub trait Write {
/// Writes a byte to the given address
fn writeb(&mut self, addr: u32, byte: u8);
/// Writes a word to the given address
///
/// TODO: What happens if there is no room for the word? I.e. the address is just at the end of the memory.
fn writew(&mut self, addr: u32, word: u32);
}
impl<T: MemCtx> Write for Mem<T> {
impl<T> Write for Mem<T> where T: ReadWrite {
fn writeb(&mut self, addr: u32, byte: u8) {
self.buf[addr as usize] = byte
}
......@@ -66,3 +83,19 @@ impl<T: MemCtx> Write for Mem<T> {
T::ByteOrder::write_u32(&mut self.buf[addr as usize..addr as usize + 4], word)
}
}
pub fn byte_swap(mut buf: Vec<u8>) -> Vec<u8> {
for addr in 0..buf.len() / 2 {
let x1 = buf[2 * addr];
let x2 = buf[2 * addr + 1];
buf[2 * addr] = x2;
buf[2 * addr + 1] = x1;
}
buf
}
pub fn endian_swap(buf: Vec<u8>) -> Vec<u8> {
unimplemented!();
}
......@@ -7,7 +7,6 @@ mod cart;
mod cpu;
mod n64;
mod pif_rom;
mod rom;
mod rsp;
pub use cart::Cart;
......
......@@ -25,7 +25,6 @@ impl MemoryMap {
fn write<'a, T>(&'a mut self, f: impl FnOnce(&'a mut mem::Write, u32, T), paddr: u32, data: T) {
let (writer, offset) = match paddr {
0x0400_0000...0x040F_FFFF => (&mut self.rsp as &mut mem::Write, 0x0400_0000),
0x1FC0_0000...0x1FC0_07BF => (&mut self.pif_rom as &mut mem::Write, 0x1FC0_0000),
_ => panic!("Unknown physical address: 0x{:X}", paddr),
};
......
......@@ -7,7 +7,7 @@ use rsp::Rsp;
pub struct N64 {
cpu: Cpu,
mem_map: MemoryMap,
cart: Option<Box<Cart>>,
cart: Option<mem::Mem<Cart>>,
}
impl N64 {
......@@ -19,8 +19,8 @@ impl N64 {
}
}
pub fn insert_cart(&mut self, cart: Cart) {
self.cart = Some(Box::new(cart))
pub fn insert_cart(&mut self, cart: mem::Mem<Cart>) {
self.cart = Some(cart)
}
pub fn power_on(&mut self) {
......
......@@ -9,6 +9,6 @@ impl PifRom {
}
}
impl mem::MemCtx for PifRom {
impl mem::ReadOnly for PifRom {
type ByteOrder = BigEndian;
}
use std::marker::PhantomData;
use std::ops::{Index, RangeFull};
use byteorder::ByteOrder;
pub struct Rom<T: ByteOrder> {
buf: Vec<u8>,
byte_order: PhantomData<T>,
}
impl<T: ByteOrder> Rom<T> {
pub fn new(buf: Vec<u8>) -> Rom<T> {
Rom {
buf: buf,
byte_order: PhantomData,
}
}
pub fn read_word(&self, addr: u32) -> u32 {
T::read_u32(&self.buf[addr as usize..])
}
pub fn read_ascii_string(&self, addr: u32, count: usize) -> String {
String::from_utf8_lossy(&self.buf[addr as usize..addr as usize + count])
.trim_right_matches('\0')
.to_string()
}
pub fn byte_swap(&mut self) {
for addr in 0..self.buf.len() / 2 {
let x1 = self.buf[2 * addr];
let x2 = self.buf[2 * addr + 1];
self.buf[2 * addr] = x2;
self.buf[2 * addr + 1] = x1;
}
}
pub fn endian_swap(&mut self) {
panic!("TODO")
}
}
impl<T: ByteOrder> Index<RangeFull> for Rom<T> {
type Output = [u8];
fn index(&self, _index: RangeFull) -> &[u8] {
&self.buf[..]
}
}
impl<T: ByteOrder> Index<u32> for Rom<T> {
type Output = u8;
fn index(&self, index: u32) -> &u8 {
&self.buf[index as usize]
}
}
#[cfg(test)]
mod tests {
use super::*;
use byteorder::{BigEndian, LittleEndian};
#[test]
fn test_that_the_byte_order_type_is_honored() {
let be = Rom::<BigEndian>::new(vec![0, 0, 0, 1]);
assert_eq!(1, be.read_word(0));
let le = Rom::<LittleEndian>::new(vec![1, 0, 0, 0]);
assert_eq!(1, le.read_word(0));
}
#[test]
fn test_that_a_string_can_be_read() {
let rom = Rom::<LittleEndian>::new(vec![
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21,
]);
assert_eq!("Hello World!", rom.read_ascii_string(0, 12));
}
#[test]
fn test_that_strings_are_read_and_zeros_trimmed() {
let rom = Rom::<LittleEndian>::new(vec![
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);
assert_eq!("Hello", rom.read_ascii_string(0, 12));
}
#[test]
fn test_that_strings_can_be_read_from_the_middle_of_a_buffer() {
let rom = Rom::<LittleEndian>::new(vec![
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21,
]);
assert_eq!("Wor", rom.read_ascii_string(6, 3));
}
#[test]
fn test_that_byte_swap_swaps_bytes() {
let mut rom = Rom::<LittleEndian>::new(vec![0x12, 0x34, 0x56, 0x78]);
rom.byte_swap();
assert_eq!([0x34, 0x12, 0x78, 0x56], rom[..]);
}
}
......@@ -9,6 +9,6 @@ impl Rsp {
}
}
impl mem::MemCtx for Rsp {
impl mem::ReadWrite for Rsp {
type ByteOrder = BigEndian;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment