AnyRange: Add AnyRange for taking any range in error
This commit is contained in:
parent
92dc899510
commit
33da1089a2
17
src/cpu.rs
17
src/cpu.rs
@ -476,22 +476,27 @@ impl CPU {
|
||||
return Ok(self);
|
||||
}
|
||||
self.cycle += 1;
|
||||
let opchunk = self
|
||||
.screen
|
||||
.get(self.pc as usize..)
|
||||
.ok_or(Error::InvalidAddressRange {
|
||||
range: (self.pc as usize..).into(),
|
||||
})?;
|
||||
// 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
|
||||
.try_into()
|
||||
.expect("`slice` should be exactly 2 bytes.")
|
||||
.expect("`slice` should be exactly 4 bytes.")
|
||||
} else {
|
||||
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:
|
||||
if self.flags.debug {
|
||||
self.timers.insn = Instant::now();
|
||||
std::print!(
|
||||
std::println!(
|
||||
"{:3} {:03x}: {:<36}",
|
||||
self.cycle.bright_black(),
|
||||
self.pc,
|
||||
|
23
src/error.rs
23
src/error.rs
@ -3,7 +3,8 @@
|
||||
|
||||
//! Error type for Chirp
|
||||
|
||||
use std::ops::Range;
|
||||
pub mod any_range;
|
||||
use any_range::AnyRange;
|
||||
|
||||
use crate::cpu::bus::Region;
|
||||
use thiserror::Error;
|
||||
@ -15,7 +16,7 @@ pub type Result<T> = std::result::Result<T, Error>;
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
/// Represents a breakpoint being hit
|
||||
#[error("Breakpoint hit: {addr:03x} ({next:04x})")]
|
||||
#[error("breakpoint hit: {addr:03x} ({next:04x})")]
|
||||
BreakpointHit {
|
||||
/// The address of the breakpoint
|
||||
addr: u16,
|
||||
@ -23,37 +24,37 @@ pub enum Error {
|
||||
next: u16,
|
||||
},
|
||||
/// Represents an unimplemented operation
|
||||
#[error("Unrecognized opcode: {word:04x}")]
|
||||
#[error("opcode {word:04x} not recognized")]
|
||||
UnimplementedInstruction {
|
||||
/// The offending word
|
||||
word: u16,
|
||||
},
|
||||
/// The region you asked for was not defined
|
||||
#[error("No {region} found on bus")]
|
||||
#[error("region {region} is not present on bus")]
|
||||
MissingRegion {
|
||||
/// The offending [Region]
|
||||
region: Region,
|
||||
},
|
||||
/// Tried to fetch [Range] from bus, received nothing
|
||||
#[error("Invalid range {range:04x?} for bus")]
|
||||
/// Tried to fetch data at [AnyRange] from bus, received nothing
|
||||
#[error("range {range:04x?} is not present on bus")]
|
||||
InvalidAddressRange {
|
||||
/// The offending [Range]
|
||||
range: Range<usize>,
|
||||
/// The offending [AnyRange]
|
||||
range: AnyRange<usize>,
|
||||
},
|
||||
/// 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 {
|
||||
/// The offending key
|
||||
key: usize,
|
||||
},
|
||||
/// 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 {
|
||||
/// The offending register
|
||||
reg: usize,
|
||||
},
|
||||
/// Tried to convert string into mode, but it did not match.
|
||||
#[error("Invalid mode: {mode}")]
|
||||
#[error("no suitable conversion of \"{mode}\" into Mode")]
|
||||
InvalidMode {
|
||||
/// The string which failed to become a mode
|
||||
mode: String,
|
||||
|
69
src/error/any_range.rs
Normal file
69
src/error/any_range.rs
Normal 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)
|
||||
}
|
||||
}
|
33
src/error/any_range/macros.rs
Normal file
33
src/error/any_range/macros.rs
Normal 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() })+
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user