223 lines
8.8 KiB
Rust
223 lines
8.8 KiB
Rust
// (c) 2023 John A. Breaux
|
|
// This code is licensed under MIT license (see LICENSE for details)
|
|
|
|
//! Contains the definition of a Chip-8 [Insn]
|
|
#![allow(clippy::bad_bit_mask)]
|
|
|
|
pub mod disassembler;
|
|
|
|
use imperative_rs::InstructionSet;
|
|
use std::fmt::Display;
|
|
|
|
|
|
#[allow(non_camel_case_types, non_snake_case, missing_docs)]
|
|
#[derive(Clone, Copy, Debug, InstructionSet, PartialEq, Eq)]
|
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
/// Represents a Chip-8 Instruction
|
|
pub enum Insn {
|
|
// Base instruction set
|
|
/// | 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: u16 },
|
|
/// | Baaa | Jump to &adr + v0
|
|
#[opcode = "0xbAAA"]
|
|
jmpr { A: u16 },
|
|
/// | 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 { y: usize, x: 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 },
|
|
|
|
// Super Chip extensions
|
|
/// | 00cN | Scroll the screen down
|
|
#[opcode = "0x00cn"]
|
|
scd { n: u8 },
|
|
/// | 00fb | Scroll the screen right
|
|
#[opcode = "0x00fb"]
|
|
scr,
|
|
/// | 00fc | Scroll the screen left
|
|
#[opcode = "0x00fc"]
|
|
scl,
|
|
/// | 00fd | Exit (halt and catch fire)
|
|
#[opcode = "0x00fd"]
|
|
halt,
|
|
/// | 00fe | Return to low-resolution mode
|
|
#[opcode = "0x00fe"]
|
|
lores,
|
|
/// | 00ff | Enter high-resolution mode
|
|
#[opcode = "0x00ff"]
|
|
hires,
|
|
/// | fx30 | Load hires font address into vX
|
|
#[opcode = "0xfx30"]
|
|
hfont { x: usize },
|
|
/// | fx75 | Save to "flag registers"
|
|
#[opcode = "0xfx75"]
|
|
flgo { x: usize },
|
|
/// | fx85 | Load from "flag registers"
|
|
#[opcode = "0xfx85"]
|
|
flgi { x: usize },
|
|
|
|
// XO-Chip instructions
|
|
/// | 00dN | Scroll the screen up
|
|
#[opcode = "0x00dn"]
|
|
scu { n: u8 },
|
|
/// | 5XY2 | DMA Load from I to vX..vY
|
|
#[opcode = "0x5xy2"]
|
|
dmaro { y: usize, x: usize },
|
|
/// | 5XY3 | DMA Load from I to vX..vY
|
|
#[opcode = "0x5xy3"]
|
|
dmari { y: usize, x: usize },
|
|
/// | F000 | Load long address into character I
|
|
#[opcode = "0xf000_iiii"]
|
|
long { i: usize },
|
|
}
|
|
|
|
impl Display for Insn {
|
|
#[rustfmt::skip]
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
// Base instruction set
|
|
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{y:X}, v{x: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{y:X}, v{x: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 { y, x, 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}"),
|
|
// Super Chip extensions
|
|
Insn::scd { n } => write!(f, "scd #{n:x}"),
|
|
Insn::scr => write!(f, "scr "),
|
|
Insn::scl => write!(f, "scl "),
|
|
Insn::halt => write!(f, "halt "),
|
|
Insn::lores => write!(f, "lores "),
|
|
Insn::hires => write!(f, "hires "),
|
|
Insn::hfont { x } => write!(f, "hfont v{x:X}"),
|
|
Insn::flgo { x } => write!(f, "flgo v{x:X}"),
|
|
Insn::flgi { x } => write!(f, "flgi v{x:X}"),
|
|
// XO-Chip extensions
|
|
Insn::scu { n } => write!(f, "scu #{n:x}"),
|
|
Insn::dmaro { y, x } => write!(f, "dmaro v{x:X}..v{y:X}"),
|
|
Insn::dmari { y, x } => write!(f, "dmari v{x:X}..v{y:X}"),
|
|
Insn::long { i } => write!(f, "long ${i:04x}"),
|
|
}
|
|
}
|
|
}
|