feat: implement timers
This commit is contained in:
parent
4db6f1de6e
commit
dc45688e4f
20
src/bus.rs
20
src/bus.rs
|
@ -83,13 +83,13 @@ impl Bus {
|
||||||
|
|
||||||
match self.cartridge.as_ref() {
|
match self.cartridge.as_ref() {
|
||||||
Some(cart) => cart.read_byte(addr),
|
Some(cart) => cart.read_byte(addr),
|
||||||
None => panic!("Tried to read from a non-existant cartridge"),
|
None => panic!("Tried to read from a non-existent cartridge"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0x4000..=0x7FFF => match self.cartridge.as_ref() {
|
0x4000..=0x7FFF => match self.cartridge.as_ref() {
|
||||||
// 16KB ROM Bank 01 -> NN (switchable via MB)
|
// 16KB ROM Bank 01 -> NN (switchable via MB)
|
||||||
Some(cart) => cart.read_byte(addr),
|
Some(cart) => cart.read_byte(addr),
|
||||||
None => panic!("Tried to read from a non-existant cartridge"),
|
None => panic!("Tried to read from a non-existent cartridge"),
|
||||||
},
|
},
|
||||||
0x8000..=0x9FFF => {
|
0x8000..=0x9FFF => {
|
||||||
// 8KB Video RAM
|
// 8KB Video RAM
|
||||||
|
@ -98,7 +98,7 @@ impl Bus {
|
||||||
0xA000..=0xBFFF => match self.cartridge.as_ref() {
|
0xA000..=0xBFFF => match self.cartridge.as_ref() {
|
||||||
// 8KB External RAM
|
// 8KB External RAM
|
||||||
Some(cart) => cart.read_byte(addr),
|
Some(cart) => cart.read_byte(addr),
|
||||||
None => panic!("Tried to read from the external RAM of a non-existant cartridge"),
|
None => panic!("Tried to read from the external RAM of a non-existent cartridge"),
|
||||||
},
|
},
|
||||||
0xC000..=0xCFFF => {
|
0xC000..=0xCFFF => {
|
||||||
// 4KB Work RAM Bank 0
|
// 4KB Work RAM Bank 0
|
||||||
|
@ -123,7 +123,9 @@ impl Bus {
|
||||||
0xFF00 => self.joypad.status.into(),
|
0xFF00 => self.joypad.status.into(),
|
||||||
0xFF01 => self.serial.next,
|
0xFF01 => self.serial.next,
|
||||||
0xFF02 => self.serial.control.into(),
|
0xFF02 => self.serial.control.into(),
|
||||||
|
0xFF04 => self.timer.divider,
|
||||||
0xFF05 => self.timer.counter,
|
0xFF05 => self.timer.counter,
|
||||||
|
0xFF06 => self.timer.modulo,
|
||||||
0xFF07 => self.timer.control.into(),
|
0xFF07 => self.timer.control.into(),
|
||||||
0xFF0F => self.interrupt_flag().into(),
|
0xFF0F => self.interrupt_flag().into(),
|
||||||
0xFF11 => self.sound.ch1.sound_duty.into(),
|
0xFF11 => self.sound.ch1.sound_duty.into(),
|
||||||
|
@ -210,7 +212,9 @@ impl Bus {
|
||||||
0xFF00 => self.joypad.status = byte.into(),
|
0xFF00 => self.joypad.status = byte.into(),
|
||||||
0xFF01 => self.serial.next = byte,
|
0xFF01 => self.serial.next = byte,
|
||||||
0xFF02 => self.serial.control = byte.into(),
|
0xFF02 => self.serial.control = byte.into(),
|
||||||
|
0xFF04 => self.timer.divider = 0x00,
|
||||||
0xFF05 => self.timer.counter = byte.into(),
|
0xFF05 => self.timer.counter = byte.into(),
|
||||||
|
0xFF06 => self.timer.modulo = byte.into(),
|
||||||
0xFF07 => self.timer.control = byte.into(),
|
0xFF07 => self.timer.control = byte.into(),
|
||||||
0xFF0F => self.set_interrupt_flag(byte),
|
0xFF0F => self.set_interrupt_flag(byte),
|
||||||
0xFF11 => self.sound.ch1.sound_duty = byte.into(),
|
0xFF11 => self.sound.ch1.sound_duty = byte.into(),
|
||||||
|
@ -276,9 +280,12 @@ impl Bus {
|
||||||
let vblank = self.ppu.interrupt.vblank();
|
let vblank = self.ppu.interrupt.vblank();
|
||||||
let lcd_stat = self.ppu.interrupt.lcd_stat();
|
let lcd_stat = self.ppu.interrupt.lcd_stat();
|
||||||
|
|
||||||
// Read the currrent interrupt information from the Joypad
|
// Read the current interrupt information from the Joypad
|
||||||
let joypad = self.joypad.interrupt();
|
let joypad = self.joypad.interrupt();
|
||||||
|
|
||||||
|
// Read the current interrupt information from the Timer
|
||||||
|
let timer = self.timer.interrupt();
|
||||||
|
|
||||||
// Copy the Interrupt Flag register 0xFF0F
|
// Copy the Interrupt Flag register 0xFF0F
|
||||||
let mut flag = self.interrupt.flag;
|
let mut flag = self.interrupt.flag;
|
||||||
|
|
||||||
|
@ -286,6 +293,7 @@ impl Bus {
|
||||||
flag.set_vblank(vblank);
|
flag.set_vblank(vblank);
|
||||||
flag.set_lcd_stat(lcd_stat);
|
flag.set_lcd_stat(lcd_stat);
|
||||||
flag.set_joypad(joypad);
|
flag.set_joypad(joypad);
|
||||||
|
flag.set_timer(timer);
|
||||||
flag
|
flag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,6 +304,7 @@ impl Bus {
|
||||||
let vblank = self.interrupt.flag.vblank();
|
let vblank = self.interrupt.flag.vblank();
|
||||||
let lcd_stat = self.interrupt.flag.lcd_stat();
|
let lcd_stat = self.interrupt.flag.lcd_stat();
|
||||||
let joypad = self.interrupt.flag.joypad();
|
let joypad = self.interrupt.flag.joypad();
|
||||||
|
let timer = self.interrupt.flag.timer();
|
||||||
|
|
||||||
// Update the PPU's instance of the following interrupts
|
// Update the PPU's instance of the following interrupts
|
||||||
self.ppu.interrupt.set_vblank(vblank);
|
self.ppu.interrupt.set_vblank(vblank);
|
||||||
|
@ -303,5 +312,8 @@ impl Bus {
|
||||||
|
|
||||||
// Update the Joypad's instance of the following interrupts
|
// Update the Joypad's instance of the following interrupts
|
||||||
self.joypad.set_interrupt(joypad);
|
self.joypad.set_interrupt(joypad);
|
||||||
|
|
||||||
|
// Update the Timer's instance of the following interrupts
|
||||||
|
self.timer.set_interrupt(timer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2146,6 +2146,34 @@ impl std::ops::RemAssign<u32> for Cycles {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::ops::Sub for Cycles {
|
||||||
|
type Output = Cycles;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
Self(self.0 - rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Sub<u32> for Cycles {
|
||||||
|
type Output = Cycles;
|
||||||
|
|
||||||
|
fn sub(self, rhs: u32) -> Self::Output {
|
||||||
|
Self(self.0 - rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::SubAssign for Cycles {
|
||||||
|
fn sub_assign(&mut self, rhs: Self) {
|
||||||
|
*self = Self(self.0 - rhs.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::SubAssign<u32> for Cycles {
|
||||||
|
fn sub_assign(&mut self, rhs: u32) {
|
||||||
|
*self = Self(self.0 - rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<u32> for Cycles {
|
impl From<u32> for Cycles {
|
||||||
fn from(num: u32) -> Self {
|
fn from(num: u32) -> Self {
|
||||||
Self(num)
|
Self(num)
|
||||||
|
|
80
src/timer.rs
80
src/timer.rs
|
@ -1,16 +1,69 @@
|
||||||
|
use crate::Cycles;
|
||||||
|
use crate::LR35902_CLOCK_SPEED;
|
||||||
use bitfield::bitfield;
|
use bitfield::bitfield;
|
||||||
|
|
||||||
use crate::instruction::Cycles;
|
const DIVIDER_REGISTER_HZ: u32 = 16384;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Timer {
|
pub struct Timer {
|
||||||
pub control: TimerControl,
|
pub control: TimerControl,
|
||||||
pub counter: u8,
|
pub counter: u8,
|
||||||
|
pub modulo: u8,
|
||||||
|
pub divider: u8,
|
||||||
|
divider_cycles: Cycles,
|
||||||
|
timer_cycles: Cycles,
|
||||||
|
interrupt: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Timer {
|
impl Timer {
|
||||||
pub fn step(&mut self, _cycles: Cycles) {
|
pub fn step(&mut self, cycles: Cycles) {
|
||||||
//
|
self.timer_cycles += cycles;
|
||||||
|
self.divider_cycles += cycles;
|
||||||
|
|
||||||
|
// Divider Register
|
||||||
|
let divider_wait = Cycles::new(LR35902_CLOCK_SPEED / DIVIDER_REGISTER_HZ);
|
||||||
|
let timer_wait = self.timer_cycles();
|
||||||
|
|
||||||
|
if self.divider_cycles >= divider_wait {
|
||||||
|
// The Divider Timer has ticked
|
||||||
|
self.divider_cycles %= divider_wait;
|
||||||
|
|
||||||
|
self.divider = self.divider.wrapping_add(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.control.enabled() {
|
||||||
|
if self.timer_cycles >= timer_wait {
|
||||||
|
// The Timer has ticked
|
||||||
|
self.timer_cycles %= timer_wait;
|
||||||
|
|
||||||
|
let (result, did_overflow) = self.counter.overflowing_add(1);
|
||||||
|
self.counter = if did_overflow {
|
||||||
|
self.interrupt = true;
|
||||||
|
self.modulo
|
||||||
|
} else {
|
||||||
|
result
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn timer_cycles(&self) -> Cycles {
|
||||||
|
let difference = match self.control.speed() {
|
||||||
|
TimerSpeed::Hz4096 => LR35902_CLOCK_SPEED / 4096,
|
||||||
|
TimerSpeed::Hz262144 => LR35902_CLOCK_SPEED / 262144,
|
||||||
|
TimerSpeed::Hz65536 => LR35902_CLOCK_SPEED / 65536,
|
||||||
|
TimerSpeed::Hz16384 => LR35902_CLOCK_SPEED / 16384,
|
||||||
|
};
|
||||||
|
|
||||||
|
Cycles::new(difference)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interrupt(&self) -> bool {
|
||||||
|
self.interrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_interrupt(&mut self, value: bool) {
|
||||||
|
self.interrupt = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,26 +71,31 @@ impl Default for Timer {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
control: Default::default(),
|
control: Default::default(),
|
||||||
|
timer_cycles: Default::default(),
|
||||||
|
divider_cycles: Default::default(),
|
||||||
counter: 0,
|
counter: 0,
|
||||||
|
modulo: 0,
|
||||||
|
divider: 0,
|
||||||
|
interrupt: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum TimerSpeed {
|
pub enum TimerSpeed {
|
||||||
Freq4096Hz = 0,
|
Hz4096 = 0,
|
||||||
Freq262144Hz = 1,
|
Hz262144 = 1,
|
||||||
Freq65536Hz = 2,
|
Hz65536 = 2,
|
||||||
Freq16384Hz = 3,
|
Hz16384 = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<u8> for TimerSpeed {
|
impl From<u8> for TimerSpeed {
|
||||||
fn from(byte: u8) -> Self {
|
fn from(byte: u8) -> Self {
|
||||||
match byte {
|
match byte {
|
||||||
0b00 => Self::Freq4096Hz,
|
0b00 => Self::Hz4096,
|
||||||
0b01 => Self::Freq262144Hz,
|
0b01 => Self::Hz262144,
|
||||||
0b10 => Self::Freq65536Hz,
|
0b10 => Self::Hz65536,
|
||||||
0b11 => Self::Freq16384Hz,
|
0b11 => Self::Hz16384,
|
||||||
_ => unreachable!("{:#04X} is not a valid value for TimerSpeed", byte),
|
_ => unreachable!("{:#04X} is not a valid value for TimerSpeed", byte),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue