AnyRange: Add AnyRange for taking any range in error
This commit is contained in:
		
							
								
								
									
										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() })+ | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user