use c8::{Chip8, Display, KeyCode, HEIGHT, WIDTH};
use minifb::{Key, Scale, Window, WindowOptions};
use std::{
sync::{mpsc::Sender, Arc},
thread,
time::Duration,
};
fn main() {
let program = include_bytes!("roms/6-keypad.ch8");
let mut c8 = Chip8::new(program);
run::<700>(&mut c8);
}
pub enum Event {
Key(KeyCode),
Timer,
Tick,
}
pub fn run<const F: u64>(c8: &mut Chip8) {
let (tx, rx) = std::sync::mpsc::channel();
let tx1 = tx.clone();
thread::spawn(move || {
let millis = 1000000 / 60;
let int = Duration::from_micros(millis);
loop {
thread::sleep(int);
tx1.send(Event::Timer).unwrap();
}
});
let tx2 = tx.clone();
thread::spawn(move || {
let millis = 1000000 / F;
let int = Duration::from_micros(millis);
loop {
thread::sleep(int);
tx2.send(Event::Tick).unwrap();
}
});
show_display(c8.display.clone(), Some(tx));
loop {
c8.step();
let event = rx.recv().unwrap();
match event {
Event::Key(k) => c8.set_key_pressed(k),
Event::Timer => c8.decr_timers(),
Event::Tick => {}
}
}
}
pub fn show_display(display: Arc<Display>, sender: Option<Sender<Event>>) {
struct Input {
sender: Sender<Event>,
}
impl Input {
fn new(sender: Sender<Event>) -> Input {
Input { sender }
}
}
impl minifb::InputCallback for Input {
fn add_char(&mut self, uni_char: u32) {
if let Some(c) = char::from_u32(uni_char) {
if let Ok(key) = c.try_into() {
self.sender.send(Event::Key(key)).unwrap();
}
}
}
}
std::thread::spawn(move || {
let mut window = Window::new(
"Chip8",
WIDTH,
HEIGHT,
WindowOptions {
scale: Scale::X16,
..WindowOptions::default()
},
)
.unwrap_or_else(|e| {
panic!("{}", e);
});
window.limit_update_rate(Some(std::time::Duration::from_micros(16600)));
if let Some(sender) = sender {
let input = Box::new(Input::new(sender));
window.set_input_callback(input);
}
while window.is_open() && !window.is_key_down(Key::Escape) {
window
.update_with_buffer(display.get_buffer(), WIDTH, HEIGHT)
.unwrap();
}
});
}