auto_cast: Move out of bus module

This commit is contained in:
John 2023-04-29 19:52:38 -05:00
parent beab7c968b
commit f4d7e514bc
7 changed files with 47 additions and 39 deletions

View File

@ -14,7 +14,7 @@ pub mod mode;
pub mod quirks; pub mod quirks;
use self::{ use self::{
bus::{Bus, Get, ReadWrite, Region::*}, bus::{Bus, Region::*},
flags::Flags, flags::Flags,
instruction::{ instruction::{
disassembler::{Dis, Disassembler}, disassembler::{Dis, Disassembler},
@ -26,6 +26,7 @@ use self::{
use crate::{ use crate::{
bus, bus,
error::{Error, Result}, error::{Error, Result},
traits::auto_cast::{AutoCast, Grab},
}; };
use imperative_rs::InstructionSet; use imperative_rs::InstructionSet;
use owo_colors::OwoColorize; use owo_colors::OwoColorize;
@ -492,7 +493,7 @@ impl CPU {
// Fetch slice of memory starting at pc, for var-width opcode 0xf000_iiii // Fetch slice of memory starting at pc, for var-width opcode 0xf000_iiii
let opchunk = self let opchunk = self
.mem .mem
.get(self.pc as usize..) .grab(self.pc as usize..)
.ok_or(Error::InvalidAddressRange { .ok_or(Error::InvalidAddressRange {
range: (self.pc as usize..).into(), range: (self.pc as usize..).into(),
})?; })?;

View File

@ -125,7 +125,7 @@ impl CPU {
/// ///
/// Corresponds to [Insn::scr] /// Corresponds to [Insn::scr]
#[inline(always)] #[inline(always)]
pub(super) fn scroll_right(&mut self, screen: &mut impl ReadWrite<u128>) { pub(super) fn scroll_right(&mut self, screen: &mut impl AutoCast<u128>) {
// Get a line from the bus // Get a line from the bus
for i in (0..16 * 64_usize).step_by(16) { for i in (0..16 * 64_usize).step_by(16) {
//let line: u128 = bus.read(self.screen + i) >> 4; //let line: u128 = bus.read(self.screen + i) >> 4;
@ -137,7 +137,7 @@ impl CPU {
/// ///
/// Corresponds to [Insn::scl] /// Corresponds to [Insn::scl]
#[inline(always)] #[inline(always)]
pub(super) fn scroll_left(&mut self, screen: &mut impl ReadWrite<u128>) { pub(super) fn scroll_left(&mut self, screen: &mut impl AutoCast<u128>) {
// Get a line from the bus // Get a line from the bus
for i in (0..16 * 64_usize).step_by(16) { for i in (0..16 * 64_usize).step_by(16) {
let line: u128 = u128::wrapping_shl(screen.read(i), 4); let line: u128 = u128::wrapping_shl(screen.read(i), 4);
@ -461,7 +461,7 @@ impl CPU {
pub(super) fn draw_sprite(&mut self, x: u16, y: u16, n: Nib, w: u16, h: u16, screen: &mut Bus) { pub(super) fn draw_sprite(&mut self, x: u16, y: u16, n: Nib, w: u16, h: u16, screen: &mut Bus) {
let w_bytes = w / 8; let w_bytes = w / 8;
self.v[0xf] = 0; self.v[0xf] = 0;
if let Some(sprite) = self.mem.get(self.i as usize..(self.i + n as u16) as usize) { if let Some(sprite) = self.mem.grab(self.i as usize..(self.i + n as u16) as usize) {
for (line, &sprite) in sprite.iter().enumerate() { for (line, &sprite) in sprite.iter().enumerate() {
let line = line as u16; let line = line as u16;
let sprite = ((sprite as u16) << (8 - (x % 8))).to_be_bytes(); let sprite = ((sprite as u16) << (8 - (x % 8))).to_be_bytes();
@ -511,7 +511,7 @@ impl CPU {
pub(super) fn draw_schip_sprite(&mut self, x: u16, y: u16, w: u16, screen: &mut Bus) { pub(super) fn draw_schip_sprite(&mut self, x: u16, y: u16, w: u16, screen: &mut Bus) {
self.v[0xf] = 0; self.v[0xf] = 0;
let w_bytes = w / 8; let w_bytes = w / 8;
if let Some(sprite) = self.mem.get(self.i as usize..(self.i + 32) as usize) { if let Some(sprite) = self.mem.grab(self.i as usize..(self.i + 32) as usize) {
let sprite = sprite.to_owned(); let sprite = sprite.to_owned();
for (line, sprite) in sprite.chunks_exact(2).enumerate() { for (line, sprite) in sprite.chunks_exact(2).enumerate() {
let sprite = u16::from_be_bytes( let sprite = u16::from_be_bytes(
@ -656,7 +656,7 @@ impl CPU {
let i = self.i as usize; let i = self.i as usize;
for (reg, value) in self for (reg, value) in self
.mem .mem
.get_mut(i..=i + x) .grab_mut(i..=i + x)
.unwrap_or_default() .unwrap_or_default()
.iter_mut() .iter_mut()
.enumerate() .enumerate()
@ -679,7 +679,7 @@ impl CPU {
let i = self.i as usize; let i = self.i as usize;
for (reg, value) in self for (reg, value) in self
.mem .mem
.get(i..=i + x) .grab(i..=i + x)
.unwrap_or_default() .unwrap_or_default()
.iter() .iter()
.enumerate() .enumerate()
@ -719,7 +719,7 @@ impl CPU {
// TODO: Save these, maybe // TODO: Save these, maybe
for (reg, value) in self for (reg, value) in self
.mem .mem
.get_mut(0..=x) .grab_mut(0..=x)
.unwrap_or_default() .unwrap_or_default()
.iter_mut() .iter_mut()
.enumerate() .enumerate()
@ -734,7 +734,7 @@ impl CPU {
/// Corresponds to [Insn::flgi] /// Corresponds to [Insn::flgi]
#[inline(always)] #[inline(always)]
pub(super) fn load_flags(&mut self, x: Reg) { pub(super) fn load_flags(&mut self, x: Reg) {
for (reg, value) in self.mem.get(0..=x).unwrap_or_default().iter().enumerate() { for (reg, value) in self.mem.grab(0..=x).unwrap_or_default().iter().enumerate() {
self.v[reg] = *value; self.v[reg] = *value;
} }
} }

View File

@ -24,21 +24,14 @@ use std::{
#[macro_export] #[macro_export]
macro_rules! bus { macro_rules! bus {
($($name:path $(:)? [$range:expr] $(= $data:expr)?) ,* $(,)?) => { ($($name:path $(:)? [$range:expr] $(= $data:expr)?) ,* $(,)?) => {
$crate::cpu::bus::Bus::default() $crate::cpu::bus::Bus::default()$(.add_region_owned($name, $range)$(.load_region_owned($name, $data))?)*
$(
.add_region_owned($name, $range)
$(
.load_region_owned($name, $data)
)?
)*
}; };
} }
pub mod read; pub use crate::traits::auto_cast::{AutoCast, Grab};
pub use read::{Get, ReadWrite};
// Traits Read and Write are here purely to make implementing other things more bearable // Traits Read and Write are here purely to make implementing other things more bearable
impl Get<u8> for Bus { impl Grab<u8> for Bus {
/// Gets a slice of [Bus] memory /// Gets a slice of [Bus] memory
/// # Examples /// # Examples
/// ```rust /// ```rust
@ -51,7 +44,7 @@ impl Get<u8> for Bus {
///# } ///# }
/// ``` /// ```
#[inline(always)] #[inline(always)]
fn get<I>(&self, index: I) -> Option<&<I as SliceIndex<[u8]>>::Output> fn grab<I>(&self, index: I) -> Option<&<I as SliceIndex<[u8]>>::Output>
where where
I: SliceIndex<[u8]>, I: SliceIndex<[u8]>,
{ {
@ -70,7 +63,7 @@ impl Get<u8> for Bus {
///# } ///# }
/// ``` /// ```
#[inline(always)] #[inline(always)]
fn get_mut<I>(&mut self, index: I) -> Option<&mut <I as SliceIndex<[u8]>>::Output> fn grab_mut<I>(&mut self, index: I) -> Option<&mut <I as SliceIndex<[u8]>>::Output>
where where
I: SliceIndex<[u8]>, I: SliceIndex<[u8]>,
{ {
@ -294,7 +287,7 @@ impl Bus {
#[inline(always)] #[inline(always)]
pub fn get_region(&self, name: Region) -> Option<&[u8]> { pub fn get_region(&self, name: Region) -> Option<&[u8]> {
debug_assert!(self.region.get(name as usize).is_some()); debug_assert!(self.region.get(name as usize).is_some());
self.get(self.region.get(name as usize)?.clone()?) self.grab(self.region.get(name as usize)?.clone()?)
} }
/// Gets a mutable slice of a named region of memory /// Gets a mutable slice of a named region of memory
@ -311,7 +304,7 @@ impl Bus {
#[inline(always)] #[inline(always)]
pub fn get_region_mut(&mut self, name: Region) -> Option<&mut [u8]> { pub fn get_region_mut(&mut self, name: Region) -> Option<&mut [u8]> {
debug_assert!(self.region.get(name as usize).is_some()); debug_assert!(self.region.get(name as usize).is_some());
self.get_mut(self.region.get(name as usize)?.clone()?) self.grab_mut(self.region.get(name as usize)?.clone()?)
} }
/// Prints the region of memory called `Screen` at 1bpp using box characters /// Prints the region of memory called `Screen` at 1bpp using box characters

View File

@ -959,7 +959,7 @@ mod io {
let addr = cpu.i as usize; let addr = cpu.i as usize;
assert_eq!( assert_eq!(
cpu.mem cpu.mem
.get(addr..addr.wrapping_add(5)) .grab(addr..addr.wrapping_add(5))
.expect("Region at addr should exist!"), .expect("Region at addr should exist!"),
test.output, test.output,
); );
@ -1004,7 +1004,10 @@ mod io {
cpu.bcd_convert(5); cpu.bcd_convert(5);
assert_eq!(cpu.mem.get(addr..addr.saturating_add(3)), Some(test.output)) assert_eq!(
cpu.mem.grab(addr..addr.saturating_add(3)),
Some(test.output)
)
} }
} }
} }
@ -1028,7 +1031,7 @@ mod io {
// Check that bus grabbed the correct data // Check that bus grabbed the correct data
let bus = cpu let bus = cpu
.mem .mem
.get_mut(addr..addr + DATA.len()) .grab_mut(addr..addr + DATA.len())
.expect("Getting a mutable slice at addr 0x0456 should not fail"); .expect("Getting a mutable slice at addr 0x0456 should not fail");
assert_eq!(bus[0..=len], DATA[0..=len]); assert_eq!(bus[0..=len], DATA[0..=len]);
assert_eq!(bus[len + 1..], [0; 16][len + 1..]); assert_eq!(bus[len + 1..], [0; 16][len + 1..]);
@ -1046,7 +1049,7 @@ mod io {
// Load some test data into memory // Load some test data into memory
let addr = 0x456; let addr = 0x456;
cpu.mem cpu.mem
.get_mut(addr..addr + DATA.len()) .grab_mut(addr..addr + DATA.len())
.expect("Getting a mutable slice at addr 0x0456..0x0466 should not fail") .expect("Getting a mutable slice at addr 0x0456..0x0466 should not fail")
.write_all(DATA) .write_all(DATA)
.unwrap(); .unwrap();

View File

@ -10,10 +10,11 @@
pub mod cpu; pub mod cpu;
pub mod error; pub mod error;
pub mod traits;
// Common imports for Chirp // Common imports for Chirp
pub use cpu::{ pub use cpu::{
bus::{Bus, Get, ReadWrite, Region::*}, bus::{Bus, Region::*},
flags::Flags, flags::Flags,
instruction::disassembler::{Dis, Disassembler}, instruction::disassembler::{Dis, Disassembler},
mode::Mode, mode::Mode,
@ -21,6 +22,7 @@ pub use cpu::{
CPU, CPU,
}; };
pub use error::{Error, Result}; pub use error::{Error, Result};
pub use traits::auto_cast::{AutoCast, Grab};
/// Holds the state of a Chip-8 /// Holds the state of a Chip-8
#[derive(Clone, Debug, Default, PartialEq)] #[derive(Clone, Debug, Default, PartialEq)]

6
src/traits.rs Normal file
View File

@ -0,0 +1,6 @@
// (c) 2023 John A. Breaux
// This code is licensed under MIT license (see LICENSE for details)
//! Traits useful for Chirp
pub mod auto_cast;

View File

@ -1,7 +1,9 @@
// (c) 2023 John A. Breaux // (c) 2023 John A. Breaux
// This code is licensed under MIT license (see LICENSE for details) // This code is licensed under MIT license (see LICENSE for details)
//! Trait for getting a generic integer for a structure. //! Traits for automatically serializing and deserializing Rust primitive types.
//!
//! Users of this module should impl [Get]`<u8>` for their type, which notably returns `&[u8]` and `&mut [u8]`
#[allow(unused_imports)] #[allow(unused_imports)]
use core::mem::size_of; use core::mem::size_of;
@ -11,20 +13,20 @@ use std::{fmt::Debug, slice::SliceIndex};
/// ///
/// This is similar to the [SliceIndex] method `.get(...)`, however implementing this trait /// This is similar to the [SliceIndex] method `.get(...)`, however implementing this trait
/// for [u8] will auto-impl [ReadWrite]<([i8], [u8], [i16], [u16] ... [i128], [u128])> /// for [u8] will auto-impl [ReadWrite]<([i8], [u8], [i16], [u16] ... [i128], [u128])>
pub trait Get<T> { pub trait Grab<T> {
/// Gets the slice of Self at [SliceIndex] I /// Gets the slice of Self at [SliceIndex] I
fn get<I>(&self, index: I) -> Option<&<I as SliceIndex<[T]>>::Output> fn grab<I>(&self, index: I) -> Option<&<I as SliceIndex<[T]>>::Output>
where where
I: SliceIndex<[T]>; I: SliceIndex<[T]>;
/// Gets a mutable slice of Self at [SliceIndex] I /// Gets a mutable slice of Self at [SliceIndex] I
fn get_mut<I>(&mut self, index: I) -> Option<&mut <I as SliceIndex<[T]>>::Output> fn grab_mut<I>(&mut self, index: I) -> Option<&mut <I as SliceIndex<[T]>>::Output>
where where
I: SliceIndex<[T]>; I: SliceIndex<[T]>;
} }
/// Read or Write a T at address `addr` /// Read or Write a T at address `addr`
pub trait ReadWrite<T>: FallibleReadWrite<T> { pub trait AutoCast<T>: FallibleAutoCast<T> {
/// Reads a T from address `addr` /// Reads a T from address `addr`
/// ///
/// # May Panic /// # May Panic
@ -41,7 +43,7 @@ pub trait ReadWrite<T>: FallibleReadWrite<T> {
} }
/// Read a T from address `addr`, and return the value as a [Result] /// Read a T from address `addr`, and return the value as a [Result]
pub trait FallibleReadWrite<T>: Get<u8> { pub trait FallibleAutoCast<T>: Grab<u8> {
/// The [Err] type /// The [Err] type
type Error: Debug; type Error: Debug;
/// Read a T from address `addr`, returning the value as a [Result] /// Read a T from address `addr`, returning the value as a [Result]
@ -58,7 +60,7 @@ pub trait FallibleReadWrite<T>: Get<u8> {
macro_rules! impl_rw {($($t:ty) ,* $(,)?) =>{ macro_rules! impl_rw {($($t:ty) ,* $(,)?) =>{
$( $(
#[doc = concat!("Read or Write [", stringify!($t), "] at address `addr`")] #[doc = concat!("Read or Write [", stringify!($t), "] at address `addr`")]
impl<T: Get<u8> + FallibleReadWrite<$t>> ReadWrite<$t> for T { impl<T: Grab<u8> + FallibleAutoCast<$t>> AutoCast<$t> for T {
#[inline(always)] #[inline(always)]
fn read(&self, addr: impl Into<usize>) -> $t { fn read(&self, addr: impl Into<usize>) -> $t {
self.read_fallible(addr).ok().unwrap_or_default() self.read_fallible(addr).ok().unwrap_or_default()
@ -68,13 +70,13 @@ macro_rules! impl_rw {($($t:ty) ,* $(,)?) =>{
self.write_fallible(addr, data).ok(); self.write_fallible(addr, data).ok();
} }
} }
impl<T: Get<u8>> FallibleReadWrite<$t> for T { impl<T: Grab<u8>> FallibleAutoCast<$t> for T {
type Error = $crate::error::Error; type Error = $crate::error::Error;
#[inline(always)] #[inline(always)]
fn read_fallible(&self, addr: impl Into<usize>) -> $crate::error::Result<$t> { fn read_fallible(&self, addr: impl Into<usize>) -> $crate::error::Result<$t> {
let addr: usize = addr.into(); let addr: usize = addr.into();
let range = addr..addr + core::mem::size_of::<$t>(); let range = addr..addr + core::mem::size_of::<$t>();
if let Some(bytes) = self.get(range.clone()) { if let Some(bytes) = self.grab(range.clone()) {
// Chip-8 is a big-endian system // Chip-8 is a big-endian system
Ok(<$t>::from_be_bytes(bytes.try_into()?)) Ok(<$t>::from_be_bytes(bytes.try_into()?))
} else { } else {
@ -84,7 +86,7 @@ macro_rules! impl_rw {($($t:ty) ,* $(,)?) =>{
#[inline(always)] #[inline(always)]
fn write_fallible(&mut self, addr: impl Into<usize>, data: $t) -> std::result::Result<(), Self::Error> { fn write_fallible(&mut self, addr: impl Into<usize>, data: $t) -> std::result::Result<(), Self::Error> {
let addr: usize = addr.into(); let addr: usize = addr.into();
if let Some(slice) = self.get_mut(addr..addr + core::mem::size_of::<$t>()) { if let Some(slice) = self.grab_mut(addr..addr + core::mem::size_of::<$t>()) {
// Chip-8 is a big-endian system // Chip-8 is a big-endian system
data.to_be_bytes().as_mut().swap_with_slice(slice); data.to_be_bytes().as_mut().swap_with_slice(slice);
} }
@ -94,6 +96,7 @@ macro_rules! impl_rw {($($t:ty) ,* $(,)?) =>{
)* )*
}} }}
// Using macro to be "generic" over types without traits in common
impl_rw!(i8, i16, i32, i64, i128); impl_rw!(i8, i16, i32, i64, i128);
impl_rw!(u8, u16, u32, u64, u128); impl_rw!(u8, u16, u32, u64, u128);
impl_rw!(f32, f64); impl_rw!(f32, f64);