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

View File

@ -125,7 +125,7 @@ impl CPU {
///
/// Corresponds to [Insn::scr]
#[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
for i in (0..16 * 64_usize).step_by(16) {
//let line: u128 = bus.read(self.screen + i) >> 4;
@ -137,7 +137,7 @@ impl CPU {
///
/// Corresponds to [Insn::scl]
#[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
for i in (0..16 * 64_usize).step_by(16) {
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) {
let w_bytes = w / 8;
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() {
let line = line as u16;
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) {
self.v[0xf] = 0;
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();
for (line, sprite) in sprite.chunks_exact(2).enumerate() {
let sprite = u16::from_be_bytes(
@ -656,7 +656,7 @@ impl CPU {
let i = self.i as usize;
for (reg, value) in self
.mem
.get_mut(i..=i + x)
.grab_mut(i..=i + x)
.unwrap_or_default()
.iter_mut()
.enumerate()
@ -679,7 +679,7 @@ impl CPU {
let i = self.i as usize;
for (reg, value) in self
.mem
.get(i..=i + x)
.grab(i..=i + x)
.unwrap_or_default()
.iter()
.enumerate()
@ -719,7 +719,7 @@ impl CPU {
// TODO: Save these, maybe
for (reg, value) in self
.mem
.get_mut(0..=x)
.grab_mut(0..=x)
.unwrap_or_default()
.iter_mut()
.enumerate()
@ -734,7 +734,7 @@ impl CPU {
/// Corresponds to [Insn::flgi]
#[inline(always)]
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;
}
}

View File

@ -24,21 +24,14 @@ use std::{
#[macro_export]
macro_rules! bus {
($($name:path $(:)? [$range:expr] $(= $data:expr)?) ,* $(,)?) => {
$crate::cpu::bus::Bus::default()
$(
.add_region_owned($name, $range)
$(
.load_region_owned($name, $data)
)?
)*
$crate::cpu::bus::Bus::default()$(.add_region_owned($name, $range)$(.load_region_owned($name, $data))?)*
};
}
pub mod read;
pub use read::{Get, ReadWrite};
pub use crate::traits::auto_cast::{AutoCast, Grab};
// 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
/// # Examples
/// ```rust
@ -51,7 +44,7 @@ impl Get<u8> for Bus {
///# }
/// ```
#[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
I: SliceIndex<[u8]>,
{
@ -70,7 +63,7 @@ impl Get<u8> for Bus {
///# }
/// ```
#[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
I: SliceIndex<[u8]>,
{
@ -294,7 +287,7 @@ impl Bus {
#[inline(always)]
pub fn get_region(&self, name: Region) -> Option<&[u8]> {
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
@ -311,7 +304,7 @@ impl Bus {
#[inline(always)]
pub fn get_region_mut(&mut self, name: Region) -> Option<&mut [u8]> {
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

View File

@ -959,7 +959,7 @@ mod io {
let addr = cpu.i as usize;
assert_eq!(
cpu.mem
.get(addr..addr.wrapping_add(5))
.grab(addr..addr.wrapping_add(5))
.expect("Region at addr should exist!"),
test.output,
);
@ -1004,7 +1004,10 @@ mod io {
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
let bus = cpu
.mem
.get_mut(addr..addr + DATA.len())
.grab_mut(addr..addr + DATA.len())
.expect("Getting a mutable slice at addr 0x0456 should not fail");
assert_eq!(bus[0..=len], DATA[0..=len]);
assert_eq!(bus[len + 1..], [0; 16][len + 1..]);
@ -1046,7 +1049,7 @@ mod io {
// Load some test data into memory
let addr = 0x456;
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")
.write_all(DATA)
.unwrap();

View File

@ -10,10 +10,11 @@
pub mod cpu;
pub mod error;
pub mod traits;
// Common imports for Chirp
pub use cpu::{
bus::{Bus, Get, ReadWrite, Region::*},
bus::{Bus, Region::*},
flags::Flags,
instruction::disassembler::{Dis, Disassembler},
mode::Mode,
@ -21,6 +22,7 @@ pub use cpu::{
CPU,
};
pub use error::{Error, Result};
pub use traits::auto_cast::{AutoCast, Grab};
/// Holds the state of a Chip-8
#[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
// 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)]
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
/// 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
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
I: SliceIndex<[T]>;
/// 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
I: SliceIndex<[T]>;
}
/// 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`
///
/// # 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]
pub trait FallibleReadWrite<T>: Get<u8> {
pub trait FallibleAutoCast<T>: Grab<u8> {
/// The [Err] type
type Error: Debug;
/// 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) ,* $(,)?) =>{
$(
#[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)]
fn read(&self, addr: impl Into<usize>) -> $t {
self.read_fallible(addr).ok().unwrap_or_default()
@ -68,13 +70,13 @@ macro_rules! impl_rw {($($t:ty) ,* $(,)?) =>{
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;
#[inline(always)]
fn read_fallible(&self, addr: impl Into<usize>) -> $crate::error::Result<$t> {
let addr: usize = addr.into();
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
Ok(<$t>::from_be_bytes(bytes.try_into()?))
} else {
@ -84,7 +86,7 @@ macro_rules! impl_rw {($($t:ty) ,* $(,)?) =>{
#[inline(always)]
fn write_fallible(&mut self, addr: impl Into<usize>, data: $t) -> std::result::Result<(), Self::Error> {
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
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!(u8, u16, u32, u64, u128);
impl_rw!(f32, f64);