auto_cast: Move out of bus module
This commit is contained in:
parent
beab7c968b
commit
f4d7e514bc
@ -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(),
|
||||||
})?;
|
})?;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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
6
src/traits.rs
Normal 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;
|
@ -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);
|
Loading…
Reference in New Issue
Block a user