implement timer logic
This commit is contained in:
parent
f8f79bc2e6
commit
cbb5c2db78
6 changed files with 42 additions and 24 deletions
BIN
roms/pong.ch8
BIN
roms/pong.ch8
Binary file not shown.
BIN
roms/snake.ch8
BIN
roms/snake.ch8
Binary file not shown.
|
@ -7,6 +7,7 @@ use rand::Rng;
|
|||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
use std::time::Instant;
|
||||
use std::{thread, time};
|
||||
|
||||
/// Represents the display's width in pixels.
|
||||
|
@ -110,9 +111,12 @@ where
|
|||
|
||||
/// Emulation loop executes the fetch -> decode -> execute pipeline
|
||||
fn emulation_loop<T>(&mut self) -> Result<(), anyhow::Error> {
|
||||
let mut start_time = Instant::now();
|
||||
let mut last_program_counter = self.program_counter;
|
||||
loop {
|
||||
// fetch instruction
|
||||
self.handle_timers(&mut start_time);
|
||||
|
||||
// fetch instruction & decode it
|
||||
let instruction = self.fetch_instruction()?;
|
||||
self.program_counter += 2;
|
||||
|
||||
|
@ -121,7 +125,7 @@ where
|
|||
}
|
||||
last_program_counter = self.program_counter;
|
||||
|
||||
// decode & execute
|
||||
// execute
|
||||
self.execute_instruction(instruction)?;
|
||||
|
||||
// insert some delay
|
||||
|
@ -129,6 +133,30 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Handles the timers logic.
|
||||
fn handle_timers(&mut self, start_time: &mut Instant) {
|
||||
// Handle 60hz timers
|
||||
let elapsed_time = start_time.elapsed().as_micros();
|
||||
// 16667 us which is 1/60 of a second
|
||||
if elapsed_time > 16667 {
|
||||
if self.delay_timer > 0 {
|
||||
self.delay_timer -= 1
|
||||
}
|
||||
if self.sound_timer > 0 {
|
||||
self.delay_timer -= 1
|
||||
} else {
|
||||
self.do_beep()
|
||||
}
|
||||
*start_time = Instant::now()
|
||||
}
|
||||
}
|
||||
|
||||
/// Should make an audible beep.
|
||||
fn do_beep(&mut self) {
|
||||
// beep, for now
|
||||
}
|
||||
|
||||
/// Executes the instruction
|
||||
fn execute_instruction(&mut self, instruction: Instruction) -> Result<(), anyhow::Error> {
|
||||
match instruction.processor_instruction() {
|
||||
ProcessorInstruction::ClearScreen => {
|
||||
|
|
|
@ -138,16 +138,6 @@ impl Instruction {
|
|||
Self::grab_last_nibble(data),
|
||||
)
|
||||
}
|
||||
/*
|
||||
00EE and 2NNN:
|
||||
|
||||
2NNN calls the subroutine at memory location NNN. In other words, just like 1NNN,
|
||||
you should set PC to NNN. However, the difference between a jump and a call is that
|
||||
this instruction should first push the current PC to the stack, so the subroutine can return later.
|
||||
|
||||
Returning from a subroutine is done with 00EE, and it does this by removing
|
||||
(“popping”) the last address from the stack and setting the PC to it.
|
||||
*/
|
||||
(0x0, 0x0, 0xE, 0xE) => ProcessorInstruction::Return,
|
||||
(0x2, _, _, _) => ProcessorInstruction::Call(Self::grab_inner_data(data)),
|
||||
(0x8, _, _, 0x0) => ProcessorInstruction::Set(
|
||||
|
@ -207,15 +197,15 @@ impl Instruction {
|
|||
Self::grab_first_nibble(data),
|
||||
Self::grab_middle_nibble(data),
|
||||
),
|
||||
(0xF, _, 0x0, 0x7) => ProcessorInstruction::SetVXToDelayTimer(
|
||||
Self::grab_first_nibble(data)
|
||||
),
|
||||
(0xF, _, 0x1, 0x5) => ProcessorInstruction::SetDelayTimer(
|
||||
Self::grab_first_nibble(data)
|
||||
),
|
||||
(0xF, _, 0x1, 0x8) => ProcessorInstruction::SetSoundTimer(
|
||||
Self::grab_first_nibble(data)
|
||||
),
|
||||
(0xF, _, 0x0, 0x7) => {
|
||||
ProcessorInstruction::SetVXToDelayTimer(Self::grab_first_nibble(data))
|
||||
}
|
||||
(0xF, _, 0x1, 0x5) => {
|
||||
ProcessorInstruction::SetDelayTimer(Self::grab_first_nibble(data))
|
||||
}
|
||||
(0xF, _, 0x1, 0x8) => {
|
||||
ProcessorInstruction::SetSoundTimer(Self::grab_first_nibble(data))
|
||||
}
|
||||
// Unknown instruction
|
||||
_ => ProcessorInstruction::UnknownInstruction,
|
||||
}
|
||||
|
|
|
@ -4,16 +4,16 @@ use env_logger;
|
|||
|
||||
mod display;
|
||||
mod emulator;
|
||||
mod input;
|
||||
mod instruction;
|
||||
mod stack;
|
||||
mod input;
|
||||
|
||||
fn main() -> Result<(), anyhow::Error> {
|
||||
env_logger::init();
|
||||
|
||||
let mut emulator = Emulator::new(RatatuiDisplay::new());
|
||||
|
||||
emulator.emulate(String::from("./roms/3-corax+.ch8"))?;
|
||||
emulator.emulate(String::from("./roms/ibm-logo.ch8"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue