main.rs/lib.rs: Refactor to make it more funny

This commit is contained in:
John 2023-03-25 18:19:06 -05:00
parent 5355e10218
commit 47fa41fd01
2 changed files with 162 additions and 103 deletions

View File

@ -12,12 +12,19 @@ pub mod cpu;
pub mod error;
pub mod io;
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Chip8 {
pub cpu: cpu::CPU,
pub bus: bus::Bus,
}
/// Common imports for chumpulator
pub mod prelude {
use super::*;
pub use super::Chip8;
pub use crate::bus;
pub use cpu::{disassemble::Disassemble, CPU};
pub use io::{*, WindowBuilder};
pub use bus::{Bus, Read, Region::*, Write};
pub use cpu::{disassemble::Disassemble, ControlFlags, CPU};
pub use error::Result;
pub use io::{WindowBuilder, *};
}

View File

@ -36,112 +36,164 @@ struct Arguments {
pub step: Option<usize>,
}
#[derive(Debug)]
struct State {
pub speed: usize,
pub step: Option<usize>,
pub rate: u64,
pub ch8: Chip8,
pub win: Window,
pub fb: FrameBuffer,
pub ft: Instant,
}
impl State {
fn new(options: Arguments) -> Result<Self> {
let mut state = State {
speed: options.speed,
step: options.step,
rate: options.frame_rate,
ch8: Chip8 { bus: bus! {
// Load the charset into ROM
Charset [0x0050..0x00A0] = include_bytes!("mem/charset.bin"),
// Load the ROM file into RAM
Program [0x0200..0x1000] = &read(options.file)?,
// Create a screen
Screen [0x1000..0x1100],
// Create a stack
Stack [0x0EA0..0x0F00],
},
cpu: CPU::new(
0x1000,
0x50,
0x200,
0xefe,
Disassemble::default(),
options.breakpoints,
ControlFlags {
authentic: options.authentic,
debug: options.debug,
pause: options.pause,
..Default::default()
},
)},
win: WindowBuilder::default().build()?,
fb: FrameBuffer::new(64, 32),
ft: Instant::now(),
};
state.fb.render(&mut state.win, &state.ch8.bus);
Ok(state)
}
fn tick_cpu(&mut self) {
if !self.ch8.cpu.flags.pause {
let rate = self.speed;
match self.step {
Some(ticks) => {
self.ch8.cpu.multistep(&mut self.ch8.bus, ticks, rate);
// Pause the CPU and clear step
self.ch8.cpu.flags.pause = true;
self.step = None;
},
None => {
self.ch8.cpu.multistep(&mut self.ch8.bus, rate, rate);
},
}
}
}
fn frame(&mut self) -> Option<()> {
{
if self.ch8.cpu.flags.pause {
self.win.set_title("Chirp ⏸")
} else {
self.win.set_title("Chirp ▶");
}
// update framebuffer
self.fb.render(&mut self.win, &mut self.ch8.bus);
// get key input (has to happen after render)
chirp::io::get_keys(&mut self.win, &mut self.ch8.cpu);
}
Some(())
}
fn keys(&mut self) -> Option<()> {
// handle keybinds for the UI
for key in self.win.get_keys_pressed(KeyRepeat::No) {
use Key::*;
match key {
F1 | Comma => self.ch8.cpu.dump(),
F2 | Period => self
.ch8.bus
.print_screen()
.expect("The 'screen' memory region exists"),
F3 => {chirp::io::debug_dump_screen(&self.ch8.bus).expect("Unable to write debug screen dump"); eprintln!("Screen dumped to file.")},
F4 | Slash => {
eprintln!(
"{}",
endis("Debug", {
self.ch8.cpu.flags.debug();
self.ch8.cpu.flags.debug
})
)
}
F5 | Backslash => eprintln!(
"{}",
endis("Pause", {
self.ch8.cpu.flags.pause();
self.ch8.cpu.flags.pause
})
),
F6 | Enter => {
eprintln!("Step");
self.ch8.cpu.singlestep(&mut self.ch8.bus);
}
F7 => {
eprintln!("Set breakpoint {:x}", self.ch8.cpu.pc());
self.ch8.cpu.set_break(self.ch8.cpu.pc());
}
F8 => {
eprintln!("Unset breakpoint {:x}", self.ch8.cpu.pc());
self.ch8.cpu.unset_break(self.ch8.cpu.pc());
}
F9 | Delete => {
eprintln!("Soft reset state.cpu {:x}", self.ch8.cpu.pc());
self.ch8.cpu.soft_reset();
self.ch8.bus.clear_region(Screen);
}
F10 | Backspace => {
eprintln!("Hard reset state.cpu");
self.ch8.cpu = CPU::default();
self.ch8.bus.clear_region(Screen);
}
Escape => return None,
_ => (),
}
}
Some(())
}
fn wait_for_next_frame(&mut self) {
let rate = 1_000_000_000 / self.rate + 1;
std::thread::sleep(Duration::from_nanos(rate).saturating_sub(self.ft.elapsed()));
self.ft = Instant::now();
}
}
impl Iterator for State {
type Item = ();
fn next(&mut self) -> Option<Self::Item> {
self.wait_for_next_frame();
self.keys()?;
self.tick_cpu();
self.frame();
Some(())
}
}
fn main() -> Result<()> {
let options = Arguments::parse_args_default_or_exit();
let state = State::new(options)?;
Ok(for _ in state {})
}
// Create the data bus
let mut bus = bus! {
// Load the charset into ROM
"charset" [0x0050..0x00A0] = include_bytes!("mem/charset.bin"),
// Load the ROM file into RAM
"userram" [0x0200..0x0F00] = &read(options.file)?,
// Create a screen
"screen" [0x0F00..0x1000],
// Create some stack memory
//"stack" [0x2000..0x2100],
};
let mut cpu = CPU::new(0xf00, 0x50, 0x200, 0x20fe, Disassemble::default());
for point in options.breakpoints {
cpu.set_break(point);
}
cpu.flags.authentic = options.authentic;
cpu.flags.debug = options.debug;
cpu.flags.pause = options.pause;
let mut framebuffer = FrameBuffer::new(64, 32);
let mut window = WindowBuilder::default().build()?;
let mut frame_time = Instant::now();
let mut step_time = Instant::now();
framebuffer.render(&mut window, &mut bus);
cpu.flags.pause = false;
cpu.flags.debug = true;
loop {
if !cpu.flags.pause {
cpu.tick(&mut bus);
}
while frame_time.elapsed() > Duration::from_micros(16000) {
if cpu.flags.pause {
window.set_title("Chirp ⏸")
} else {
window.set_title("Chirp ▶")
}
frame_time += Duration::from_micros(16000);
// tick sound and delay timers
cpu.tick_timer();
// update framebuffer
framebuffer.render(&mut window, &mut bus);
// get key input (has to happen after framebuffer)
get_keys(&mut window, &mut cpu);
// handle keys at the
for key in window.get_keys_pressed(KeyRepeat::No) {
use Key::*;
match key {
F1 => cpu.dump(),
F2 => bus
.print_screen()
.expect("The 'screen' memory region exists"),
F3 => {
println!(
"{}",
endis("Debug", {
cpu.flags.debug();
cpu.flags.debug
})
)
}
F4 => println!(
"{}",
endis("Pause", {
cpu.flags.pause();
cpu.flags.pause
})
),
F5 => {
println!("Step");
cpu.singlestep(&mut bus)
}
F6 => {
println!("Set breakpoint {:x}", cpu.pc());
cpu.set_break(cpu.pc())
}
F7 => {
println!("Unset breakpoint {:x}", cpu.pc());
cpu.unset_break(cpu.pc())
}
F8 => {
println!("Soft reset CPU {:x}", cpu.pc());
cpu.soft_reset();
bus.clear_region("screen");
}
F9 => {
println!("Hard reset CPU");
cpu = CPU::default();
bus.clear_region("screen");
}
Escape => return Ok(()),
_ => (),
}
}
}
std::thread::sleep(Duration::from_micros(1666).saturating_sub(step_time.elapsed()));
step_time = Instant::now();
}
//Ok(())
/// Parses a hexadecimal string into a u16
fn parse_hex(value: &str) -> std::result::Result<u16, std::num::ParseIntError> {
u16::from_str_radix(value, 16)