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 error;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
|
pub struct Chip8 {
|
||||||
|
pub cpu: cpu::CPU,
|
||||||
|
pub bus: bus::Bus,
|
||||||
|
}
|
||||||
|
|
||||||
/// Common imports for chumpulator
|
/// Common imports for chumpulator
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
pub use super::Chip8;
|
||||||
pub use crate::bus;
|
pub use crate::bus;
|
||||||
pub use cpu::{disassemble::Disassemble, CPU};
|
|
||||||
pub use io::{*, WindowBuilder};
|
|
||||||
pub use bus::{Bus, Read, Region::*, Write};
|
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>,
|
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<()> {
|
fn main() -> Result<()> {
|
||||||
let options = Arguments::parse_args_default_or_exit();
|
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
|
/// Parses a hexadecimal string into a u16
|
||||||
fn parse_hex(value: &str) -> std::result::Result<u16, std::num::ParseIntError> {
|
fn parse_hex(value: &str) -> std::result::Result<u16, std::num::ParseIntError> {
|
||||||
u16::from_str_radix(value, 16)
|
u16::from_str_radix(value, 16)
|
||||||
|
Loading…
Reference in New Issue
Block a user