Timer implement

This commit is contained in:
Franco Colmenarez 2021-10-30 09:13:31 -05:00
parent abbd46ebed
commit 61db367f31
6 changed files with 68 additions and 11 deletions

View File

@ -7,6 +7,7 @@ use crate::utils::{
use crate::rom::ROM; use crate::rom::ROM;
use crate::ppu::{PPU, LCDStatus, LCDStatusModeFlag}; use crate::ppu::{PPU, LCDStatus, LCDStatusModeFlag};
use crate::cpu::{Interrupt}; use crate::cpu::{Interrupt};
use crate::timer::{TIMER_DIVIDER_REGISTER_ADDRESS};
pub struct AddressRange { pub struct AddressRange {
begin: u16, begin: u16,
@ -107,6 +108,8 @@ impl Bus {
self.data[(WORK_RAM_1.begin() + (address - ECHO_RAM.begin())) as usize] = data; // Copy to the working RAM self.data[(WORK_RAM_1.begin() + (address - ECHO_RAM.begin())) as usize] = data; // Copy to the working RAM
} else if VIDEO_RAM.in_range(address) { } else if VIDEO_RAM.in_range(address) {
self.data[address as usize] = data; self.data[address as usize] = data;
} else if address == TIMER_DIVIDER_REGISTER_ADDRESS {
self.data[address as usize] = 0x00;
} else { } else {
self.data[address as usize] = data; self.data[address as usize] = data;
} }

View File

@ -830,6 +830,12 @@ pub enum Opcode {
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct Cycles(pub usize); pub struct Cycles(pub usize);
impl Cycles {
pub fn to_t(&self) -> usize {
self.0 * 4
}
}
pub struct CPU { pub struct CPU {
registers: Registers, registers: Registers,
cycles: Cycles, cycles: Cycles,

View File

@ -3,6 +3,7 @@ use std::{thread, time};
use crate::cpu::{CPU, Cycles}; use crate::cpu::{CPU, Cycles};
use crate::ppu::PPU; use crate::ppu::PPU;
use crate::bus::Bus; use crate::bus::Bus;
use crate::timer::Timer;
pub struct Emulator { pub struct Emulator {
cpu: CPU, cpu: CPU,
@ -31,6 +32,7 @@ impl Emulator {
while self.cpu.get_cycles().0 <= cpu_cycles.0 { while self.cpu.get_cycles().0 <= cpu_cycles.0 {
self.cpu.run(&mut self.bus); self.cpu.run(&mut self.bus);
self.ppu.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles()); self.ppu.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles());
Timer::do_cycles(&mut self.bus, self.cpu.get_last_op_cycles());
} }
} }

View File

@ -1,6 +1,7 @@
pub mod utils; pub mod utils;
pub mod cpu; pub mod cpu;
pub mod ppu; pub mod ppu;
pub mod timer;
pub mod rom; pub mod rom;
pub mod bus; pub mod bus;
pub mod emulator; pub mod emulator;

View File

@ -118,11 +118,6 @@ impl PPU {
} }
} }
let lyc_compare = PPU::get_lcd_y(bus) == bus.read(LCD_Y_COMPARE_ADDRESS);
PPU::set_lcd_status(bus, LCDStatus::LYCInterrupt, lyc_compare);
if lyc_compare {
bus.set_interrupt(Interrupt::LCDSTAT, true);
}
self.increment_cycles(Cycles(1)); self.increment_cycles(Cycles(1));
// Horizontal scan completed // Horizontal scan completed
@ -131,6 +126,12 @@ impl PPU {
PPU::set_lcd_y(bus, PPU::get_lcd_y(bus) + 1); PPU::set_lcd_y(bus, PPU::get_lcd_y(bus) + 1);
let lyc_compare = PPU::get_lcd_y(bus) == bus.read(LCD_Y_COMPARE_ADDRESS);
if lyc_compare {
PPU::set_lcd_status(bus, LCDStatus::LYCInterrupt, lyc_compare);
bus.set_interrupt(Interrupt::LCDSTAT, true);
}
// Frame completed // Frame completed
if PPU::get_lcd_y(bus) > 153 { if PPU::get_lcd_y(bus) > 153 {
PPU::set_lcd_y(bus, 0); PPU::set_lcd_y(bus, 0);

View File

@ -1,11 +1,55 @@
const DIVIDER_REGISTER_ADDRESS: u16 = 0xFF04; use crate::cpu::{Interrupt, Cycles};
const TIMER_COUNTER_ADDRESS: u16 = 0xFF05; use crate::bus::Bus;
const TIMER_MODULO_ADDRESS: u16 = 0xFF05; use crate::utils::{
const TIMER_CONTROL_ADDRESS: u16 = 0xFF05; BitIndex,
get_bit,
};
struct Timer; pub const TIMER_DIVIDER_REGISTER_ADDRESS: u16 = 0xFF04;
pub const TIMER_COUNTER_ADDRESS: u16 = 0xFF05;
pub const TIMER_MODULO_ADDRESS: u16 = 0xFF06;
pub const TIMER_CONTROL_ADDRESS: u16 = 0xFF07;
pub struct Timer;
impl Timer { impl Timer {
pub fn cycle() {
pub fn do_cycles(bus: &mut Bus, cycles: Cycles) {
let mut count = 0;
while count < cycles.to_t() {
Timer::cycle(bus);
count += 1;
}
}
fn cycle(bus: &mut Bus) {
let div = bus.read(TIMER_DIVIDER_REGISTER_ADDRESS);
bus.write(TIMER_DIVIDER_REGISTER_ADDRESS, div.wrapping_add(1));
if Timer::is_timer_enabled(bus) {
let tima = bus.read(TIMER_COUNTER_ADDRESS);
let tima_increment = Timer::get_tima_increment(bus);
if tima.checked_add(tima_increment) == None {
bus.write(TIMER_COUNTER_ADDRESS, bus.read(TIMER_MODULO_ADDRESS));
bus.set_interrupt(Interrupt::Timer, true);
} else {
bus.write(TIMER_COUNTER_ADDRESS, tima.wrapping_add(tima_increment));
}
}
}
fn is_timer_enabled(bus: &Bus) -> bool {
get_bit(bus.read(TIMER_CONTROL_ADDRESS), BitIndex::I2)
}
fn get_tima_increment(bus: &Bus) -> u8 {
let clock_select = bus.read(TIMER_CONTROL_ADDRESS) & 0b0000_0011;
match clock_select {
0b00 => (4096 as u16 / 1026 as u16 / 4 as u16).to_be_bytes()[1],
0b01 => (4096 as u16 / 16 as u16 / 4 as u16).to_be_bytes()[1],
0b10 => (4096 as u16 / 64 as u16 / 4 as u16).to_be_bytes()[1],
0b11 => (4096 as u16 / 256 as u16 / 4 as u16).to_be_bytes()[1],
_ => 1,
}
} }
} }