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);
|
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,
|
||||||
|
23
src/error.rs
23
src/error.rs
@ -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
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