AnyRange: Add AnyRange for taking any range in error

This commit is contained in:
John 2023-04-23 12:01:47 -05:00
parent 92dc899510
commit 33da1089a2
4 changed files with 131 additions and 23 deletions

View File

@ -476,22 +476,27 @@ impl CPU {
return Ok(self); return Ok(self);
} }
self.cycle += 1; self.cycle += 1;
let opchunk = self
.screen
.get(self.pc as usize..)
.ok_or(Error::InvalidAddressRange {
range: (self.pc as usize..).into(),
})?;
// fetch opcode // fetch opcode
let opcode: &[u8; 2] = if let Some(slice) = bus.get(self.pc as usize..self.pc as usize + 2) let opcode: &[u8; 2] =
{ if let Some(slice) = self.screen.get(self.pc as usize..self.pc as usize + 2) {
slice slice
.try_into() .try_into()
.expect("`slice` should be exactly 2 bytes.") .expect("`slice` should be exactly 4 bytes.")
} else { } else {
return Err(Error::InvalidAddressRange { return Err(Error::InvalidAddressRange {
range: self.pc as usize..self.pc as usize + 2, range: (self.pc as usize..self.pc as usize + 4).into(),
}); });
}; };
// Print opcode disassembly: // Print opcode disassembly:
if self.flags.debug { if self.flags.debug {
self.timers.insn = Instant::now(); std::println!(
std::print!(
"{:3} {:03x}: {:<36}", "{:3} {:03x}: {:<36}",
self.cycle.bright_black(), self.cycle.bright_black(),
self.pc, self.pc,

View File

@ -3,7 +3,8 @@
//! Error type for Chirp //! Error type for Chirp
use std::ops::Range; pub mod any_range;
use any_range::AnyRange;
use crate::cpu::bus::Region; use crate::cpu::bus::Region;
use thiserror::Error; use thiserror::Error;
@ -15,7 +16,7 @@ pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum Error { pub enum Error {
/// Represents a breakpoint being hit /// Represents a breakpoint being hit
#[error("Breakpoint hit: {addr:03x} ({next:04x})")] #[error("breakpoint hit: {addr:03x} ({next:04x})")]
BreakpointHit { BreakpointHit {
/// The address of the breakpoint /// The address of the breakpoint
addr: u16, addr: u16,
@ -23,37 +24,37 @@ pub enum Error {
next: u16, next: u16,
}, },
/// Represents an unimplemented operation /// Represents an unimplemented operation
#[error("Unrecognized opcode: {word:04x}")] #[error("opcode {word:04x} not recognized")]
UnimplementedInstruction { UnimplementedInstruction {
/// The offending word /// The offending word
word: u16, word: u16,
}, },
/// The region you asked for was not defined /// The region you asked for was not defined
#[error("No {region} found on bus")] #[error("region {region} is not present on bus")]
MissingRegion { MissingRegion {
/// The offending [Region] /// The offending [Region]
region: Region, region: Region,
}, },
/// Tried to fetch [Range] from bus, received nothing /// Tried to fetch data at [AnyRange] from bus, received nothing
#[error("Invalid range {range:04x?} for bus")] #[error("range {range:04x?} is not present on bus")]
InvalidAddressRange { InvalidAddressRange {
/// The offending [Range] /// The offending [AnyRange]
range: Range<usize>, range: AnyRange<usize>,
}, },
/// Tried to press a key that doesn't exist /// Tried to press a key that doesn't exist
#[error("Invalid key: {key:X}")] #[error("tried to press key {key:X} which does not exist")]
InvalidKey { InvalidKey {
/// The offending key /// The offending key
key: usize, key: usize,
}, },
/// Tried to get/set an out-of-bounds register /// Tried to get/set an out-of-bounds register
#[error("Invalid register: v{reg:X}")] #[error("tried to access register v{reg:X} which does not exist")]
InvalidRegister { InvalidRegister {
/// The offending register /// The offending register
reg: usize, reg: usize,
}, },
/// Tried to convert string into mode, but it did not match. /// Tried to convert string into mode, but it did not match.
#[error("Invalid mode: {mode}")] #[error("no suitable conversion of \"{mode}\" into Mode")]
InvalidMode { InvalidMode {
/// The string which failed to become a mode /// The string which failed to become a mode
mode: String, mode: String,

69
src/error/any_range.rs Normal file
View File

@ -0,0 +1,69 @@
// (c) 2023 John A. Breaux
// This code is licensed under MIT license (see LICENSE for details)
//! Holder for any [Range]
// This is super over-engineered, considering it was originally meant to help with only one LOC
use std::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
use thiserror::Error;
#[macro_use]
mod macros;
#[derive(Clone, Debug, Error, PartialEq, Eq, Hash)]
#[error("Failed to convert variant {0} back into range.")]
/// Emitted when conversion back into a [std::ops]::Range\* fails.
pub struct AnyRangeError<Idx>(AnyRange<Idx>);
/// Holder for any [Range]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum AnyRange<Idx> {
/// Bounded exclusive [Range] i.e. `0..10`
Range(Range<Idx>),
/// Unbounded [RangeFrom], i.e. `0..`
RangeFrom(RangeFrom<Idx>),
/// Unbounded [RangeFull], i.e. `..`
RangeFull(RangeFull),
/// Bounded inclusive [RangeInclusive], i.e. `0..=10`
RangeInclusive(RangeInclusive<Idx>),
/// Unbounded [RangeTo], i.e. `..10`
RangeTo(RangeTo<Idx>),
/// Unbounded inclusive [RangeToInclusive], i.e. `..=10`
RangeToInclusive(RangeToInclusive<Idx>),
}
variant_from! {
match impl<Idx> (From, TryInto) for AnyRange<Idx> {
type Error = AnyRangeError<Idx>;
Range<Idx> => AnyRange::Range,
RangeFrom<Idx> => AnyRange::RangeFrom,
RangeFull => AnyRange::RangeFull,
RangeInclusive<Idx> => AnyRange::RangeInclusive,
RangeTo<Idx> => AnyRange::RangeTo,
RangeToInclusive<Idx> => AnyRange::RangeToInclusive,
}
}
/// Convenient conversion functions from [AnyRange] to the inner type
impl<Idx> AnyRange<Idx> {
try_into_fn! {
/// Converts from [AnyRange::Range] into a [Range], else [None]
pub fn range(self) -> Option<Range<Idx>>;
/// Converts from [AnyRange::RangeFrom] into a [RangeFrom], else [None]
pub fn range_from(self) -> Option<RangeFrom<Idx>>;
/// Converts from [AnyRange::RangeFull] into a [RangeFull], else [None]
pub fn range_full(self) -> Option<RangeFull>;
/// Converts from [AnyRange::RangeInclusive] into a [RangeInclusive], else [None]
pub fn range_inclusive(self) -> Option<RangeInclusive<Idx>>;
/// Converts from [AnyRange::RangeTo] into a [RangeTo], else [None]
pub fn range_to(self) -> Option<RangeTo<Idx>>;
/// Converts from [AnyRange::RangeToInclusive] into a [RangeToInclusive], else [None]
pub fn range_to_inclusive(self) -> Option<RangeToInclusive<Idx>>;
}
}
impl<Idx> From<AnyRange<Idx>> for AnyRangeError<Idx> {
fn from(value: AnyRange<Idx>) -> Self {
AnyRangeError(value)
}
}

View File

@ -0,0 +1,33 @@
//! Macros for AnyRange
/// Generate From and TryFrom impls for each variant
macro_rules! variant_from {(
match impl<$T:ident> $_:tt for $enum:path {
type Error = $error:path;
$($src:path => $variant:path),+$(,)?
}
) => {
// The forward direction is infallible
$(impl<$T> From<$src> for $enum {
fn from(value: $src) -> Self { $variant(value) }
})+
// The reverse direction could fail if the $variant doesn't hold $src
$(impl<$T> TryFrom<$enum> for $src {
type Error = $error;
fn try_from(value: $enum) -> Result<Self, Self::Error> {
if let $variant(r) = value { Ok(r) } else { Err(value.into()) }
}
})+
}}
/// Turns a list of function prototypes into functions which use [TryInto], returning [Option]
///
/// # Examples
/// ```rust,ignore
/// try_into_fn! { fn range(self) -> Option<Other>; }
/// ```
macro_rules! try_into_fn {
($($(#[$doc:meta])? $pub:vis fn $name:ident $args:tt -> $ret:ty);+ $(;)?) => {
$($(#[$doc])? $pub fn $name $args -> $ret { $args.try_into().ok() })+
}
}