Refactor disassembler to use imperative-rs
It's like MAGIC. Easily cut out 200 LOC
This commit is contained in:
parent
e54f66f6c4
commit
f60a4b3cc2
22
Cargo.lock
generated
22
Cargo.lock
generated
@ -182,6 +182,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"gumdrop",
|
||||
"iced",
|
||||
"imperative-rs",
|
||||
"minifb",
|
||||
"owo-colors",
|
||||
"rand",
|
||||
@ -1086,6 +1087,27 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "imperative-rs"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fed4f0a3a8fe169e919b74f6ba77c473d356ef05d0d65da06467b8836e508aa3"
|
||||
dependencies = [
|
||||
"imperative-rs-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imperative-rs-derive"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b842fd8a24b2b715ac0baffae4aa72aa59ecb8e90d3de175180dbb5b1caaab"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
|
@ -28,6 +28,7 @@ overflow-checks = false
|
||||
[dependencies]
|
||||
gumdrop = "^0.8.1"
|
||||
iced = {version = "0.8.0", optional = true}
|
||||
imperative-rs = "0.3.1"
|
||||
minifb = { version = "^0.24.0", features = ["wayland"] }
|
||||
owo-colors = "^3"
|
||||
rand = "^0.8.5"
|
||||
|
@ -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, *};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user