From acc7629516218765c4a7a366de5999edd783be55 Mon Sep 17 00:00:00 2001 From: John Breaux Date: Mon, 3 Apr 2023 02:01:25 -0500 Subject: [PATCH] schip: Add preliminary SuperChip support (no test) --- justfile | 11 +- src/bin/chirp-minifb/main.rs | 66 +++++----- src/bin/chirp-minifb/ui.rs | 18 ++- src/bus.rs | 76 ++++++++++- src/cpu.rs | 235 +++++++++++++++++++++++++++++++---- src/cpu/disassembler.rs | 41 ++++++ src/error.rs | 6 + src/lib.rs | 2 +- tests/chip8_test_suite.rs | 2 +- tests/integration.rs | 14 +-- 10 files changed, 384 insertions(+), 87 deletions(-) diff --git a/justfile b/justfile index 1c4562e..496979f 100644 --- a/justfile +++ b/justfile @@ -7,11 +7,14 @@ rat: test: cargo nextest run -chirp: - cargo run --bin chirp-minifb -- chip8-test-suite/bin/chip8-test-suite.ch8 +run rom: + cargo run --bin chirp-minifb -- '{{rom}}' + +debug rom: + cargo run --bin chirp-minifb -- -d '{{rom}}' # Run at 2100000 instructions per frame, and output per-frame runtime statistics bench: - cargo run --bin chirp-minifb --release -- chip8Archive/roms/1dcell.ch8 -xP -s10 -S2100000 + cargo run --bin chirp-minifb --release -- chip8Archive/roms/1dcell.ch8 -Ps10 -S2100000 -m xochip flame rom: CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph -F 15300 --open --bin chirp-minifb -- '{{rom}}' -s10 @@ -23,5 +26,5 @@ cover: cargo llvm-cov --open --doctests tokei: - tokei --exclude tests/chip8-test-suite + tokei --exclude chip8-test-suite --exclude chip8Archive diff --git a/src/bin/chirp-minifb/main.rs b/src/bin/chirp-minifb/main.rs index dca6b2f..c38dc1a 100644 --- a/src/bin/chirp-minifb/main.rs +++ b/src/bin/chirp-minifb/main.rs @@ -36,33 +36,39 @@ struct Arguments { pub step: Option, #[options(help = "Enable performance benchmarking on stderr (requires -S)")] pub perf: bool, - #[options( - short = "z", - help = "Disable setting vF to 0 after a bitwise operation." - )] - pub vfreset: bool, - #[options( - short = "x", - help = "Disable waiting for vblank after issuing a draw call." - )] - pub drawsync: bool, #[options( - short = "c", - help = "Use CHIP-48 style DMA instructions, which don't touch I." + help = "Run in (Chip8, SChip, XOChip) mode.", + //parse(from_str = "parse_mode") )] - pub memory: bool, - #[options( - short = "v", - help = "Use CHIP-48 style bit-shifts, which don't touch vY." - )] - pub shift: bool, - #[options( - short = "b", - help = "Use SUPER-CHIP style indexed jump, which is indexed relative to v[adr]." - )] - pub jumping: bool, + pub mode: Option, + // #[options( + // short = "z", + // help = "Disable setting vF to 0 after a bitwise operation." + // )] + // pub vfreset: bool, + // #[options( + // short = "x", + // help = "Disable waiting for vblank after issuing a draw call." + // )] + // pub drawsync: bool, + + // #[options( + // short = "c", + // help = "Use CHIP-48 style DMA instructions, which don't touch I." + // )] + // pub memory: bool, + // #[options( + // short = "v", + // help = "Use CHIP-48 style bit-shifts, which don't touch vY." + // )] + // pub shift: bool, + // #[options( + // short = "b", + // help = "Use SUPER-CHIP style indexed jump, which is indexed relative to v[adr]." + // )] + // pub jumping: bool, #[options( long = "break", help = "Set breakpoints for the emulator to stop at.", @@ -105,7 +111,7 @@ impl State { // Load the ROM file into RAM Program [0x0200..0x1000] = &read(&options.file)?, // Create a screen - Screen [0x1000..0x1100], + Screen [0x1000..0x1400], // Create a stack Stack [0x0EA0..0x0F00], }, @@ -117,13 +123,7 @@ impl State { Dis::default(), options.breakpoints, ControlFlags { - quirks: chirp::cpu::Quirks { - bin_ops: options.vfreset, - shift: options.shift, - draw_wait: options.drawsync, - dma_inc: options.memory, - stupid_jumps: options.jumping, - }, + quirks: options.mode.unwrap_or_default().into(), debug: options.debug, pause: options.pause, monotonic: options.speed, @@ -131,7 +131,7 @@ impl State { }, ), }, - ui: UIBuilder::new(64, 32, &options.file).build()?, + ui: UIBuilder::new(128, 64, &options.file).build()?, ft: Instant::now(), }; state.ch8.bus.write(0x1feu16, options.data); @@ -202,7 +202,7 @@ impl Iterator for State { } } -fn main() -> Result<()> { +pub fn main() -> Result<()> { let options = Arguments::parse_args_default_or_exit(); let state = State::new(options)?; for result in state { diff --git a/src/bin/chirp-minifb/ui.rs b/src/bin/chirp-minifb/ui.rs index 5a72c36..b0b1673 100644 --- a/src/bin/chirp-minifb/ui.rs +++ b/src/bin/chirp-minifb/ui.rs @@ -56,14 +56,14 @@ impl UIBuilder { impl Default for UIBuilder { fn default() -> Self { UIBuilder { - width: 64, - height: 32, + width: 128, + height: 64, name: Some("Chip-8 Interpreter"), rom: None, window_options: WindowOptions { title: true, resize: false, - scale: Scale::X16, + scale: Scale::X8, scale_mode: ScaleMode::AspectRatioStretch, none: true, ..Default::default() @@ -80,9 +80,13 @@ pub struct FrameBufferFormat { impl Default for FrameBufferFormat { fn default() -> Self { + // FrameBufferFormat { + // fg: 0x0011a434, + // bg: 0x001E2431, + // } FrameBufferFormat { - fg: 0x0011a434, - bg: 0x001E2431, + fg: 0xc4c4c4, + bg: 0x000000, } } } @@ -112,6 +116,8 @@ impl FrameBuffer { self.format.fg } else { self.format.bg + // .wrapping_add(0x001104 * (idx / self.width) as u32) + // .wrapping_add(0x141000 * (idx & 3) as u32) } } } @@ -123,7 +129,7 @@ impl FrameBuffer { impl Default for FrameBuffer { fn default() -> Self { - Self::new(64, 32) + Self::new(128, 64) } } diff --git a/src/bus.rs b/src/bus.rs index f330009..93b0140 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -308,14 +308,14 @@ impl Bus { const REGION: Region = Region::Screen; if let Some(screen) = self.get_region(REGION) { for (index, byte) in screen.iter().enumerate() { - if index % 8 == 0 { - print!("|"); + if index % 16 == 0 { + print!("{index:03x}|"); } print!( "{}", - format!("{byte:08b}").replace('0', " ").replace('1', "██") + format!("{byte:08b}").replace('0', " ").replace('1', "█") ); - if index % 8 == 7 { + if index % 16 == 15 { println!("|"); } } @@ -339,7 +339,43 @@ impl Read for Bus { fn read(&self, addr: impl Into) -> u16 { let addr: usize = addr.into(); if let Some(bytes) = self.memory.get(addr..addr + 2) { - u16::from_be_bytes(bytes.try_into().expect("asked for 2 bytes, got != 2 bytes")) + u16::from_be_bytes(bytes.try_into().expect("Should get 2 bytes")) + } else { + 0xc5c5 + } + } +} + +impl Read for Bus { + /// Read a u16 from address `addr` + fn read(&self, addr: impl Into) -> u32 { + let addr: usize = addr.into(); + if let Some(bytes) = self.memory.get(addr..addr + 4) { + u32::from_be_bytes(bytes.try_into().expect("Should get 4 bytes")) + } else { + 0xc5c5 + } + } +} + +impl Read for Bus { + /// Read a u16 from address `addr` + fn read(&self, addr: impl Into) -> u64 { + let addr: usize = addr.into(); + if let Some(bytes) = self.memory.get(addr..addr + 8) { + u64::from_be_bytes(bytes.try_into().expect("Should get 8 bytes")) + } else { + 0xc5c5 + } + } +} + +impl Read for Bus { + /// Read a u16 from address `addr` + fn read(&self, addr: impl Into) -> u128 { + let addr: usize = addr.into(); + if let Some(bytes) = self.memory.get(addr..addr + 16) { + u128::from_be_bytes(bytes.try_into().expect("Should get 16 bytes")) } else { 0xc5c5 } @@ -366,6 +402,36 @@ impl Write for Bus { } } +impl Write for Bus { + /// Write a u16 to address `addr` + fn write(&mut self, addr: impl Into, data: u32) { + let addr: usize = addr.into(); + if let Some(slice) = self.get_mut(addr..addr + 4) { + data.to_be_bytes().as_mut().swap_with_slice(slice); + } + } +} + +impl Write for Bus { + /// Write a u16 to address `addr` + fn write(&mut self, addr: impl Into, data: u64) { + let addr: usize = addr.into(); + if let Some(slice) = self.get_mut(addr..addr + 8) { + data.to_be_bytes().as_mut().swap_with_slice(slice); + } + } +} + +impl Write for Bus { + /// Write a u16 to address `addr` + fn write(&mut self, addr: impl Into, data: u128) { + let addr: usize = addr.into(); + if let Some(slice) = self.get_mut(addr..addr + 16) { + data.to_be_bytes().as_mut().swap_with_slice(slice); + } + } +} + #[cfg(target_feature = "rhexdump")] impl Display for Bus { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { diff --git a/src/cpu.rs b/src/cpu.rs index 6af16ce..36a1a95 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -22,12 +22,39 @@ use crate::{ use imperative_rs::InstructionSet; use owo_colors::OwoColorize; use rand::random; -use std::time::Instant; +use std::{str::FromStr, time::Instant}; type Reg = usize; type Adr = u16; type Nib = u8; +/// Selects the memory behavior of the interpreter +#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Mode { + /// VIP emulation mode + #[default] + Chip8, + /// Chip-48 emulation mode + SChip, + /// XO-Chip emulation mode + XOChip, +} + +impl FromStr for Mode { + type Err = Error; + + fn from_str(s: &str) -> std::result::Result { + match s.to_lowercase().as_str() { + "chip8" | "chip-8" => Ok(Mode::Chip8), + "schip" | "superchip" => Ok(Mode::SChip), + "xo-chip" | "xochip" => Ok(Mode::XOChip), + _ => Err(Error::InvalidMode { + mode: s.to_string(), + }), + } + } +} + /// Controls the authenticity behavior of the CPU on a granular level. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Quirks { @@ -51,7 +78,7 @@ impl From for Quirks { shift: true, draw_wait: true, dma_inc: true, - stupid_jumps: false, + stupid_jumps: true, } } else { Quirks { @@ -65,6 +92,22 @@ impl From for Quirks { } } +impl From for Quirks { + fn from(value: Mode) -> Self { + match value { + Mode::Chip8 => false.into(), + Mode::SChip => true.into(), + Mode::XOChip => Self { + bin_ops: true, + shift: false, + draw_wait: true, + dma_inc: false, + stupid_jumps: false, + }, + } + } +} + impl Default for Quirks { fn default() -> Self { Self::from(false) @@ -82,9 +125,13 @@ pub struct ControlFlags { pub keypause: bool, /// Set when the emulator is waiting for a frame to be drawn pub draw_wait: bool, + /// Set when the emulator is in high-res mode + pub draw_mode: bool, /// Set to the last key that's been *released* after a keypause pub lastkey: Option, - /// Represents the set of emulator [Quirks] to enable + /// Represents the current emulator [Mode] + pub mode: Mode, + /// Represents the set of emulator [Quirks] to enable, independent of the [Mode] pub quirks: Quirks, /// Represents the number of instructions to run per tick of the internal timer pub monotonic: Option, @@ -704,6 +751,7 @@ impl CPU { #[rustfmt::skip] fn execute(&mut self, bus: &mut Bus, instruction: Insn) { match instruction { + // Core Chip-8 instructions Insn::cls => self.clear_screen(bus), Insn::ret => self.ret(bus), Insn::jmp { A } => self.jump(A), @@ -713,7 +761,7 @@ impl CPU { Insn::se { y, x } => self.skip_equals(x, y), Insn::movb { B, x } => self.load_immediate(x, B), Insn::addb { B, x } => self.add_immediate(x, B), - Insn::mov { x, y } => self.load(x, y), + Insn::mov { y, x } => self.load(x, y), Insn::or { y, x } => self.or(x, y), Insn::and { y, x } => self.and(x, y), Insn::xor { y, x } => self.xor(x, y), @@ -726,7 +774,7 @@ impl CPU { Insn::movI { A } => self.load_i_immediate(A), Insn::jmpr { A } => self.jump_indexed(A), Insn::rand { B, x } => self.rand(x, B), - Insn::draw { x, y, n } => self.draw(x, y, n, bus), + Insn::draw { y, x, n } => self.draw(x, y, n, bus), Insn::sek { x } => self.skip_key_equals(x), Insn::snek { x } => self.skip_key_not_equals(x), Insn::getdt { x } => self.load_delay_timer(x), @@ -738,6 +786,16 @@ impl CPU { Insn::bcd { x } => self.bcd_convert(x, bus), Insn::dmao { x } => self.store_dma(x, bus), Insn::dmai { x } => self.load_dma(x, bus), + // Super-Chip extensions + Insn::scd { n } => self.scroll_down(n, bus), + Insn::scr => self.scr(bus), + Insn::scl => self.scl(bus), + Insn::halt => self.flags.pause(), + Insn::lores => self.flags.draw_mode = false, + Insn::hires => self.flags.draw_mode = true, + Insn::hfont { x } => self.load_big_sprite(x), + Insn::flgo { x } => self.store_flags(x, bus), + Insn::flgi { x } => self.load_flags(x, bus), } } } @@ -755,9 +813,7 @@ impl CPU { /// |`00e0`| Clears the screen memory to 0 #[inline(always)] fn clear_screen(&mut self, bus: &mut Bus) { - if let Some(screen) = bus.get_region_mut(Region::Screen) { - screen.fill(0); - } + bus.clear_region(Region::Screen); } /// |`00ee`| Returns from subroutine #[inline(always)] @@ -765,6 +821,37 @@ impl CPU { self.sp = self.sp.wrapping_add(2); self.pc = bus.read(self.sp); } + + /// |`00cN`| Scroll the screen down N lines + #[inline(always)] + fn scroll_down(&mut self, n: Nib, bus: &mut Bus) { + // Get a line from the bus + for i in (16 * n as usize..16 * 15).step_by(16).rev() { + let i = i + self.screen as usize; + let line: u128 = bus.read(i); + bus.write(i - (n as usize * 16), line); + bus.write(i, 0u128); + } + } + + /// |`00fb`| Scroll the screen right + #[inline(always)] + fn scr(&mut self, bus: &mut (impl Read + Write)) { + // Get a line from the bus + for i in (0..16 * 64).step_by(16) { + //let line: u128 = bus.read(self.screen + i) >> 4; + bus.write(self.screen + i, bus.read(self.screen + i) >> 4); + } + } + /// |`00fc`| Scroll the screen right + #[inline(always)] + fn scl(&mut self, bus: &mut (impl Read + Write)) { + // Get a line from the bus + for i in (0..16 * 64).step_by(16) { + let line: u128 = (bus.read(self.screen + i) & !(0xf << 124)) << 4; + bus.write(self.screen + i, line); + } + } } // |`1aaa`| Sets pc to an absolute address @@ -994,6 +1081,15 @@ impl CPU { } } +/// TODO: Do this more idiomatically, using some iterator chain? +fn doublewide(value: u16) -> u32 { + let mut out: u32 = 0; + for i in 0..16 { + out |= ((value as u32 & 0x1 << i) * 3) << i; + } + out +} + // |`Dxyn`| Draws n-byte sprite to the screen at coordinates (vX, vY) impl CPU { /// |`Dxyn`| Draws n-byte sprite to the screen at coordinates (vX, vY) @@ -1002,31 +1098,86 @@ impl CPU { /// On the original chip-8 interpreter, this will wait for a VBI #[inline(always)] fn draw(&mut self, x: Reg, y: Reg, n: Nib, bus: &mut Bus) { - let (x, y) = (self.v[x] as u16 % 64, self.v[y] as u16 % 32); + // lmaotch + match self.flags.draw_mode { + true => self.draw_hires(x, y, n, bus), + false => self.draw_lores(x, y, n, bus), + } + } + #[inline(always)] + fn draw_lores(&mut self, x: Reg, y: Reg, n: Nib, bus: &mut Bus) { if !self.flags.quirks.draw_wait { self.flags.draw_wait = true; } + let (w, h) = (64, 32); + let (x, y) = (self.v[x] as u16 % w, self.v[y] as u16 % h); self.v[0xf] = 0; - for byte in 0..n as u16 { - if y + byte > 32 { - return; + if let Some(sprite) = bus.get(self.i as usize..(self.i + n as u16) as usize) { + let sprite = sprite.to_vec(); + for (line, sprite) in sprite.iter().enumerate() { + let (line, sprite) = (line as u16, *sprite); + // clip + if y + line >= h { + break; + } + // clip + let sprite = (sprite as u16) << (8 - (x % 8)) + & if (x % w) >= (w - 8) { 0xff00 } else { 0xffff }; + // scale + let addr = |x, y| -> u16 { ((y + (2 * line)) * 8 + (x / 8)) * 2 + self.screen }; + let y = y << 1; + let sprite = doublewide(sprite); + for scale in 0..2 { + let screen: u32 = bus.read(addr(x, y + scale)); + bus.write(addr(x, y + scale), screen ^ sprite); + if screen & sprite != 0 { + self.v[0xf] = 1; + } + } } - // Calculate the lower bound address based on the X,Y position on the screen - let addr = (y + byte) * 8 + (x & 0x3f) / 8 + self.screen; - // Read a byte of sprite data into a u16, and shift it x % 8 bits - let sprite: u8 = bus.read(self.i + byte); - let sprite = - (sprite as u16) << (8 - (x & 7)) & if x % 64 > 56 { 0xff00 } else { 0xffff }; - // Read a u16 from the bus containing the two bytes which might need to be updated - let mut screen: u16 = bus.read(addr); - // Save the bits-toggled-off flag if necessary - if screen & sprite != 0 { - self.v[0xF] = 1 + } + } + // Super-Chip extension high-resolution graphics mode + #[inline(always)] + fn draw_hires(&mut self, x: Reg, y: Reg, n: Nib, bus: &mut Bus) { + if !self.flags.quirks.draw_wait { + self.flags.draw_wait = true; + } + let (w, h) = (128, 64); + let (x, y) = (self.v[x] as u16 % w, self.v[y] as u16 % h); + let w_bytes = w / 8; + self.v[0xf] = 0; + if n == 0 { + if let Some(sprite) = bus.get(self.i as usize..(self.i + 32) as usize) { + let sprite = sprite.to_owned(); + for (line, sprite) in sprite.chunks(2).enumerate() { + let sprite = u16::from_be_bytes( + sprite + .try_into() + .expect("Chunks should only return 2 bytes"), + ); + let addr = (y + line as u16) * w_bytes + x / 8 + self.screen; + let sprite = (sprite as u32) << (16 - (x % 8)); + let screen: u32 = bus.read(addr); + bus.write(addr, screen ^ sprite); + if screen & sprite != 0 { + self.v[0xf] += 1; + } + } + } + } else { + if let Some(sprite) = bus.get(self.i as usize..(self.i + n as u16) as usize) { + let sprite = sprite.to_vec(); + for (line, sprite) in sprite.iter().enumerate() { + let addr = (y + line as u16) * w_bytes + x / 8 + self.screen; + let sprite = (*sprite as u16) << (8 - (x % 8)); + let screen: u16 = bus.read(addr); + bus.write(addr, screen ^ sprite); + if screen & sprite != 0 { + self.v[0xf] += 1; + } + } } - // Update the screen word by XORing the sprite byte - screen ^= sprite; - // Save the result to the screen - bus.write(addr, screen); } } } @@ -1164,4 +1315,34 @@ impl CPU { self.i += x as Adr + 1; } } + + /// |`Fx30`| (Super-Chip) 16x16 equivalent of Fx29 + #[inline(always)] + fn load_big_sprite(&mut self, x: Reg) { + self.i = self.font + (5 * 8) + (16 * (self.v[x] as Adr % 0x10)); + } + + /// |`Fx75`| (Super-Chip) Save to "flag registers" + /// I just chuck it in 0x0..0xf. Screw it. + #[inline(always)] + fn store_flags(&mut self, x: Reg, bus: &mut Bus) { + // TODO: Save these, maybe + for (reg, value) in bus + .get_mut(0..=x) + .unwrap_or_default() + .iter_mut() + .enumerate() + { + *value = self.v[reg] + } + } + + /// |`Fx85`| (Super-Chip) Load from "flag registers" + /// I just chuck it in 0x0..0xf. Screw it. + #[inline(always)] + fn load_flags(&mut self, x: Reg, bus: &mut Bus) { + for (reg, value) in bus.get(0..=x).unwrap_or_default().iter().enumerate() { + self.v[reg] = *value; + } + } } diff --git a/src/cpu/disassembler.rs b/src/cpu/disassembler.rs index 8bc585c..9927f9e 100644 --- a/src/cpu/disassembler.rs +++ b/src/cpu/disassembler.rs @@ -9,6 +9,7 @@ use std::fmt::Display; #[derive(Clone, Copy, Debug, InstructionSet, PartialEq, Eq)] /// Implements a Disassembler using imperative_rs pub enum Insn { + // Base instruction set /// | 00e0 | Clear screen memory to 0s #[opcode = "0x00e0"] cls, @@ -111,12 +112,42 @@ pub enum Insn { // | 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 | Enter high-resolution mode + #[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 }, } 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}"), @@ -151,6 +182,16 @@ impl Display for Insn { 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}"), } } } diff --git a/src/error.rs b/src/error.rs index bc35717..fc3e995 100644 --- a/src/error.rs +++ b/src/error.rs @@ -52,6 +52,12 @@ pub enum Error { /// The offending register reg: usize, }, + /// Tried to convert string into mode, but it did not match. + #[error("Invalid mode: {mode}")] + InvalidMode { + /// The string which failed to become a mode + mode: String, + }, /// Error originated in [std::io] #[error(transparent)] IoError(#[from] std::io::Error), diff --git a/src/lib.rs b/src/lib.rs index b2db64b..6ff4e71 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,7 @@ pub mod error; // Common imports for Chirp pub use bus::{Bus, Read, Region::*, Write}; -pub use cpu::{disassembler::Dis, ControlFlags, CPU}; +pub use cpu::{disassembler::Dis, ControlFlags, Mode, CPU}; pub use error::Result; /// Holds the state of a Chip-8 diff --git a/tests/chip8_test_suite.rs b/tests/chip8_test_suite.rs index f5feba1..0d27b25 100644 --- a/tests/chip8_test_suite.rs +++ b/tests/chip8_test_suite.rs @@ -17,7 +17,7 @@ fn setup_environment() -> (CPU, Bus) { Charset [0x0050..0x00A0] = include_bytes!("../src/mem/charset.bin"), // Load the ROM file into RAM Program [0x0200..0x1000] = include_bytes!("../chip8-test-suite/bin/chip8-test-suite.ch8"), - // Create a screen, and fill it with garbage data + // Create a screen, and fill it with Screen [0x0F00..0x1000] = include_bytes!("chip8_test_suite.rs"), }, ) diff --git a/tests/integration.rs b/tests/integration.rs index 14003aa..2258eca 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -132,8 +132,8 @@ mod cpu { keypause: false, draw_wait: false, lastkey: None, - quirks: Default::default(), monotonic: None, + ..Default::default() }; let cf2 = cf1.clone(); assert_eq!(cf1, cf2) @@ -151,9 +151,7 @@ mod cpu { pause: false, keypause: false, draw_wait: false, - lastkey: Default::default(), - quirks: Default::default(), - monotonic: Default::default() + ..Default::default() } ) } @@ -165,9 +163,7 @@ mod cpu { pause: true, keypause: true, draw_wait: true, - lastkey: Default::default(), - quirks: Default::default(), - monotonic: Default::default(), + ..Default::default() }; assert_ne!(cf1, cf2); } @@ -179,9 +175,7 @@ mod cpu { pause: true, keypause: true, draw_wait: true, - lastkey: Default::default(), - quirks: Default::default(), - monotonic: Default::default(), + ..Default::default() }; assert!(cf1 < cf2); assert_eq!(ControlFlags::default(), cf1.min(cf2));