Refactor disassembler to use imperative-rs
It's like MAGIC. Easily cut out 200 LOC
This commit is contained in:
		| @@ -1,7 +1,7 @@ | ||||
| use chirp::{error::Result, prelude::*}; | ||||
| use chirp::{cpu::Disassembler, error::Result, prelude::*}; | ||||
| use gumdrop::*; | ||||
| use std::{fs::read, path::PathBuf}; | ||||
| use owo_colors::OwoColorize; | ||||
| use std::{fs::read, path::PathBuf}; | ||||
|  | ||||
| #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Options, Hash)] | ||||
| struct Arguments { | ||||
| @@ -22,18 +22,20 @@ fn parse_hex(value: &str) -> std::result::Result<u16, std::num::ParseIntError> { | ||||
| fn main() -> Result<()> { | ||||
|     let options = Arguments::parse_args_default_or_exit(); | ||||
|     let contents = &read(&options.file)?; | ||||
|     let disassembler = Disassemble::default(); | ||||
|     let disassembler = Dis::default(); | ||||
|     for (addr, insn) in contents[options.offset..].chunks_exact(2).enumerate() { | ||||
|         let insn = u16::from_be_bytes( | ||||
|             insn.try_into() | ||||
|                 .expect("Iterated over 2-byte chunks, got <2 bytes"), | ||||
|         ); | ||||
|         println!( | ||||
|             "{:03x}: {} {:04x}", | ||||
|             2 * addr + 0x200 + options.offset, | ||||
|             disassembler.instruction(insn), | ||||
|             insn.bright_black(), | ||||
|  | ||||
|             "{}", | ||||
|             format_args!( | ||||
|                 "{:03x}: {} {:04x}", | ||||
|                 2 * addr + 0x200 + options.offset, | ||||
|                 disassembler.once(insn), | ||||
|                 insn.bright_black(), | ||||
|             ) | ||||
|         ); | ||||
|     } | ||||
|     Ok(()) | ||||
|   | ||||
| @@ -104,7 +104,7 @@ impl State { | ||||
|                     0x50, | ||||
|                     0x200, | ||||
|                     0xefe, | ||||
|                     Disassemble::default(), | ||||
|                     Dis::default(), | ||||
|                     options.breakpoints, | ||||
|                     ControlFlags { | ||||
|                         quirks: chirp::cpu::Quirks { | ||||
|   | ||||
							
								
								
									
										21
									
								
								src/cpu.rs
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								src/cpu.rs
									
									
									
									
									
								
							| @@ -6,9 +6,16 @@ | ||||
| #[cfg(test)] | ||||
| mod tests; | ||||
|  | ||||
| pub mod disassemble; | ||||
| /// Disassembles Chip-8 instructions | ||||
| pub trait Disassembler { | ||||
|     /// Disassemble a single instruction | ||||
|     fn once(&self, insn: u16) -> String; | ||||
| } | ||||
|  | ||||
| use self::disassemble::Disassemble; | ||||
| pub mod disassembler; | ||||
| pub mod old_disassembler; | ||||
|  | ||||
| use self::disassembler::Dis; | ||||
| use crate::bus::{Bus, Read, Region, Write}; | ||||
| use owo_colors::OwoColorize; | ||||
| use rand::random; | ||||
| @@ -138,7 +145,7 @@ pub struct CPU { | ||||
|     timer: Instant, | ||||
|     cycle: usize, | ||||
|     breakpoints: Vec<Adr>, | ||||
|     disassembler: Disassemble, | ||||
|     disassembler: Dis, | ||||
| } | ||||
|  | ||||
| // public interface | ||||
| @@ -164,7 +171,7 @@ impl CPU { | ||||
|         font: Adr, | ||||
|         pc: Adr, | ||||
|         sp: Adr, | ||||
|         disassembler: Disassemble, | ||||
|         disassembler: Dis, | ||||
|         breakpoints: Vec<Adr>, | ||||
|         flags: ControlFlags, | ||||
|     ) -> Self { | ||||
| @@ -549,7 +556,7 @@ impl CPU { | ||||
|         self.pc = self.pc.wrapping_add(2); | ||||
|         // decode opcode | ||||
|  | ||||
|         use disassemble::{a, b, i, n, x, y}; | ||||
|         use old_disassembler::{a, b, i, n, x, y}; | ||||
|         let (i, x, y, n, b, a) = ( | ||||
|             i(opcode), | ||||
|             x(opcode), | ||||
| @@ -674,7 +681,7 @@ impl CPU { | ||||
|                 "{:3} {:03x}: {:<36}{:?}", | ||||
|                 self.cycle.bright_black(), | ||||
|                 pc, | ||||
|                 self.disassembler.instruction(opcode), | ||||
|                 self.disassembler.once(opcode), | ||||
|                 elapsed.dimmed() | ||||
|             ); | ||||
|         } | ||||
| @@ -765,7 +772,7 @@ impl Default for CPU { | ||||
|             }, | ||||
|             timer: Instant::now(), | ||||
|             breakpoints: vec![], | ||||
|             disassembler: Disassemble::default(), | ||||
|             disassembler: Dis::default(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										184
									
								
								src/cpu/disassembler.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								src/cpu/disassembler.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,184 @@ | ||||
| //! A disassembler for Chip-8 opcodes | ||||
|  | ||||
| use super::Disassembler; | ||||
| use imperative_rs::InstructionSet; | ||||
| use owo_colors::{OwoColorize, Style}; | ||||
| use std::fmt::Display; | ||||
|  | ||||
| #[allow(non_camel_case_types, non_snake_case, missing_docs)] | ||||
| #[derive(Clone, Copy, InstructionSet, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||||
| /// Implements a Disassembler using imperative_rs | ||||
| pub enum Insn { | ||||
|     /// | 00e0 | Clear screen memory to 0s | ||||
|     #[opcode = "0x00e0"] | ||||
|     cls, | ||||
|     /// | 00ee | Return from subroutine | ||||
|     #[opcode = "0x00ee"] | ||||
|     ret, | ||||
|     /// | 1aaa | Jumps to an absolute address | ||||
|     #[opcode = "0x1AAA"] | ||||
|     jmp { A: u16 }, | ||||
|     /// | 2aaa | Pushes pc onto the stack, then jumps to a | ||||
|     #[opcode = "0x2AAA"] | ||||
|     call { A: u16 }, | ||||
|     /// | 3xbb | Skips next instruction if register X == b | ||||
|     #[opcode = "0x3xBB"] | ||||
|     seb { B: u8, x: usize }, | ||||
|     /// | 4xbb | Skips next instruction if register X != b | ||||
|     #[opcode = "0x4xBB"] | ||||
|     sneb { B: u8, x: usize }, | ||||
|     /// | 9XY0 | Skip next instruction if vX == vY  | | ||||
|     #[opcode = "0x5xy0"] | ||||
|     se { y: usize, x: usize }, | ||||
|     /// | 6xbb | Loads immediate byte b into register vX | ||||
|     #[opcode = "0x6xBB"] | ||||
|     movb { B: u8, x: usize }, | ||||
|     /// | 7xbb | Adds immediate byte b to register vX | ||||
|     #[opcode = "0x7xBB"] | ||||
|     addb { B: u8, x: usize }, | ||||
|     /// | 8xy0 | Loads the value of y into x | ||||
|     #[opcode = "0x8xy0"] | ||||
|     mov { x: usize, y: usize }, | ||||
|     /// | 8xy1 | Performs bitwise or of vX and vY, and stores the result in vX | ||||
|     #[opcode = "0x8xy1"] | ||||
|     or { y: usize, x: usize }, | ||||
|     /// | 8xy2 | Performs bitwise and of vX and vY, and stores the result in vX | ||||
|     #[opcode = "0x8xy2"] | ||||
|     and { y: usize, x: usize }, | ||||
|     /// | 8xy3 | Performs bitwise xor of vX and vY, and stores the result in vX | ||||
|     #[opcode = "0x8xy3"] | ||||
|     xor { y: usize, x: usize }, | ||||
|     /// | 8xy4 | Performs addition of vX and vY, and stores the result in vX | ||||
|     #[opcode = "0x8xy4"] | ||||
|     add { y: usize, x: usize }, | ||||
|     /// | 8xy5 | Performs subtraction of vX and vY, and stores the result in vX | ||||
|     #[opcode = "0x8xy5"] | ||||
|     sub { y: usize, x: usize }, | ||||
|     /// | 8xy6 | Performs bitwise right shift of vX (or vY) | ||||
|     #[opcode = "0x8xy6"] | ||||
|     shr { y: usize, x: usize }, | ||||
|     /// | 8xy7 | Performs subtraction of vY and vX, and stores the result in vX | ||||
|     #[opcode = "0x8xy7"] | ||||
|     bsub { y: usize, x: usize }, | ||||
|     /// | 8xyE | Performs bitwise left shift of vX | ||||
|     #[opcode = "0x8xye"] | ||||
|     shl { y: usize, x: usize }, | ||||
|     /// | 9XY0 | Skip next instruction if vX != vY | ||||
|     #[opcode = "0x9xy0"] | ||||
|     sne { y: usize, x: usize }, | ||||
|     /// | Aaaa | Load address #a into register I | ||||
|     #[opcode = "0xaAAA"] | ||||
|     movI { A: usize }, | ||||
|     /// | Baaa | Jump to &adr + v0 | ||||
|     #[opcode = "0xbAAA"] | ||||
|     jmpr { A: usize }, | ||||
|     /// | Cxbb | Stores a random number & the provided byte into vX | ||||
|     #[opcode = "0xcxBB"] | ||||
|     rand { B: u8, x: usize }, | ||||
|     /// | Dxyn | Draws n-byte sprite to the screen at coordinates (vX, vY) | ||||
|     #[opcode = "0xdxyn"] | ||||
|     draw { x: usize, y: usize, n: u8 }, | ||||
|     /// | eX9e | Skip next instruction if key == vX | ||||
|     #[opcode = "0xex9e"] | ||||
|     sek { x: usize }, | ||||
|     /// | eXa1 | Skip next instruction if key != vX | ||||
|     #[opcode = "0xexa1"] | ||||
|     snek { x: usize }, | ||||
|     // | fX07 | Set vX to value in delay timer | ||||
|     #[opcode = "0xfx07"] | ||||
|     getdt { x: usize }, | ||||
|     // | fX0a | Wait for input, store key in vX | ||||
|     #[opcode = "0xfx0a"] | ||||
|     waitk { x: usize }, | ||||
|     // | fX15 | Set sound timer to the value in vX | ||||
|     #[opcode = "0xfx15"] | ||||
|     setdt { x: usize }, | ||||
|     // | fX18 | set delay timer to the value in vX | ||||
|     #[opcode = "0xfx18"] | ||||
|     movst { x: usize }, | ||||
|     // | fX1e | Add vX to I | ||||
|     #[opcode = "0xfx1e"] | ||||
|     addI { x: usize }, | ||||
|     // | fX29 | Load sprite for character x into I | ||||
|     #[opcode = "0xfx29"] | ||||
|     font { x: usize }, | ||||
|     // | fX33 | BCD convert X into I[0..3] | ||||
|     #[opcode = "0xfx33"] | ||||
|     bcd { x: usize }, | ||||
|     // | fX55 | DMA Stor from I to registers 0..X | ||||
|     #[opcode = "0xfx55"] | ||||
|     dmao { x: usize }, | ||||
|     // | fX65 | DMA Load from I to registers 0..X | ||||
|     #[opcode = "0xfx65"] | ||||
|     dmai { x: usize }, | ||||
| } | ||||
|  | ||||
| impl Display for Insn { | ||||
|     #[rustfmt::skip] | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||
|         match self { | ||||
|             Insn::cls              => write!(f, "cls    "), | ||||
|             Insn::ret              => write!(f, "ret    "), | ||||
|             Insn::jmp { A }        => write!(f, "jmp    {A:03x}"), | ||||
|             Insn::call { A }       => write!(f, "call   {A:03x}"), | ||||
|             Insn::seb { B, x }     => write!(f, "se     #{B:02x}, v{x:X}"), | ||||
|             Insn::sneb { B, x }    => write!(f, "sne    #{B:02x}, v{x:X}"), | ||||
|             Insn::se { y, x }      => write!(f, "se     v{x:X}, v{y:X}"), | ||||
|             Insn::movb { B, x }    => write!(f, "mov    #{B:02x}, v{x:X}"), | ||||
|             Insn::addb { B, x }    => write!(f, "add    #{B:02x}, v{x:X}"), | ||||
|             Insn::mov { x, y }     => write!(f, "mov    v{y:X}, v{x:X}"), | ||||
|             Insn::or { y, x }      => write!(f, "or     v{y:X}, v{x:X}"), | ||||
|             Insn::and { y, x }     => write!(f, "and    v{y:X}, v{x:X}"), | ||||
|             Insn::xor { y, x }     => write!(f, "xor    v{y:X}, v{x:X}"), | ||||
|             Insn::add { y, x }     => write!(f, "add    v{y:X}, v{x:X}"), | ||||
|             Insn::sub { y, x }     => write!(f, "sub    v{y:X}, v{x:X}"), | ||||
|             Insn::shr { y, x }     => write!(f, "shr    v{y:X}, v{x:X}"), | ||||
|             Insn::bsub { y, x }    => write!(f, "bsub   v{y:X}, v{x:X}"), | ||||
|             Insn::shl { y, x }     => write!(f, "shl    v{y:X}, v{x:X}"), | ||||
|             Insn::sne { y, x }     => write!(f, "sne    v{x:X}, v{y:X}"), | ||||
|             Insn::movI { A }       => write!(f, "mov    ${A:03x}, I"), | ||||
|             Insn::jmpr { A }       => write!(f, "jmp    ${A:03x}+v0"), | ||||
|             Insn::rand { B, x }    => write!(f, "rand   #{B:02x}, v{x:X}"), | ||||
|             Insn::draw { x, y, n } => write!(f, "draw   #{n:x}, v{x:X}, v{y:X}"), | ||||
|             Insn::sek { x }        => write!(f, "sek    v{x:X}"), | ||||
|             Insn::snek { x }       => write!(f, "snek   v{x:X}"), | ||||
|             Insn::getdt { x }      => write!(f, "mov    DT, v{x:X}"), | ||||
|             Insn::waitk { x }      => write!(f, "waitk  v{x:X}"), | ||||
|             Insn::setdt { x }      => write!(f, "mov    v{x:X}, DT"), | ||||
|             Insn::movst { x }      => write!(f, "mov    v{x:X}, ST"), | ||||
|             Insn::addI { x }       => write!(f, "add    v{x:X}, I"), | ||||
|             Insn::font { x }       => write!(f, "font   v{x:X}, I"), | ||||
|             Insn::bcd { x }        => write!(f, "bcd    v{x:X}, &I"), | ||||
|             Insn::dmao { x }       => write!(f, "dmao   v{x:X}"), | ||||
|             Insn::dmai { x }       => write!(f, "dmai   v{x:X}"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Disassembles Chip-8 instructions, printing them in the provided [owo_colors::Style]s | ||||
| #[derive(Clone, Copy, Debug, PartialEq)] | ||||
| pub struct Dis { | ||||
|     /// Styles invalid instructions | ||||
|     pub invalid: Style, | ||||
|     /// Styles valid instruction | ||||
|     pub normal: Style, | ||||
| } | ||||
|  | ||||
| impl Default for Dis { | ||||
|     fn default() -> Self { | ||||
|         Self { | ||||
|             invalid: Style::new().bold().red(), | ||||
|             normal: Style::new().green(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Disassembler for Dis { | ||||
|     fn once(&self, insn: u16) -> String { | ||||
|         if let Some((_, insn)) = Insn::decode(&insn.to_be_bytes()).ok() { | ||||
|             format!("{}", insn.style(self.normal)) | ||||
|         } else { | ||||
|             format!("{}", format_args!("inval  {insn:04x}").style(self.invalid)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -3,7 +3,7 @@ | ||||
| 
 | ||||
| //! A disassembler for Chip-8 opcodes
 | ||||
| 
 | ||||
| use super::{Adr, Nib, Reg}; | ||||
| use super::{Adr, Disassembler, Nib, Reg}; | ||||
| use owo_colors::{OwoColorize, Style}; | ||||
| type Ins = Nib; | ||||
| 
 | ||||
| @@ -40,22 +40,22 @@ pub fn a(ins: u16) -> Adr { | ||||
| 
 | ||||
| /// Disassembles Chip-8 instructions, printing them in the provided [owo_colors::Style]s
 | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| pub struct Disassemble { | ||||
| pub struct DeprecatedDisassembler { | ||||
|     invalid: Style, | ||||
|     normal: Style, | ||||
| } | ||||
| 
 | ||||
| impl Default for Disassemble { | ||||
| impl Default for DeprecatedDisassembler { | ||||
|     fn default() -> Self { | ||||
|         Disassemble::builder().build() | ||||
|         DeprecatedDisassembler::builder().build() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Public API
 | ||||
| impl Disassemble { | ||||
| impl DeprecatedDisassembler { | ||||
|     /// Returns a new Disassemble with the provided Styles
 | ||||
|     pub fn new(invalid: Style, normal: Style) -> Disassemble { | ||||
|         Disassemble { invalid, normal } | ||||
|     pub fn new(invalid: Style, normal: Style) -> DeprecatedDisassembler { | ||||
|         DeprecatedDisassembler { invalid, normal } | ||||
|     } | ||||
|     /// Creates a [DisassembleBuilder], for partial configuration
 | ||||
|     pub fn builder() -> DisassembleBuilder { | ||||
| @@ -183,8 +183,14 @@ impl Disassemble { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Disassembler for DeprecatedDisassembler { | ||||
|     fn once(&self, insn: u16) -> String { | ||||
|         self.instruction(insn) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Private api
 | ||||
| impl Disassemble { | ||||
| impl DeprecatedDisassembler { | ||||
|     /// Unused instructions
 | ||||
|     fn unimplemented(&self, opcode: u16) -> String { | ||||
|         format!("inval  {opcode:04x}") | ||||
| @@ -400,8 +406,8 @@ impl DisassembleBuilder { | ||||
|         self | ||||
|     } | ||||
|     /// Builds a Disassemble
 | ||||
|     pub fn build(self) -> Disassemble { | ||||
|         Disassemble { | ||||
|     pub fn build(self) -> DeprecatedDisassembler { | ||||
|         DeprecatedDisassembler { | ||||
|             invalid: if let Some(style) = self.invalid { | ||||
|                 style | ||||
|             } else { | ||||
| @@ -19,7 +19,7 @@ pub mod prelude { | ||||
|     use super::*; | ||||
|     pub use crate::bus; | ||||
|     pub use bus::{Bus, Read, Region::*, Write}; | ||||
|     pub use cpu::{disassemble::Disassemble, ControlFlags, CPU}; | ||||
|     pub use cpu::{disassembler::Dis, ControlFlags, CPU}; | ||||
|     pub use error::Result; | ||||
|     pub use io::{UIBuilder, *}; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user