main.rs/lib.rs: Refactor to make it more funny
This commit is contained in:
parent
5355e10218
commit
47fa41fd01
11
src/lib.rs
11
src/lib.rs
@ -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, *};
|
||||
}
|
||||
|
254
src/main.rs
254
src/main.rs
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user