As good as working

This commit is contained in:
Kevin Hamacher 2017-01-19 22:34:15 +01:00
parent fc15b398ba
commit 2367e891a4
5 changed files with 272 additions and 108 deletions

View File

@ -37,8 +37,25 @@ pub struct CPU {
halted: bool,
}
fn to_u16(bytes: Box<[u8]>) -> u16 {
(bytes[1] as u16) << 8 | (bytes[0] as u16)
fn to_u16(args: Args) -> u16 {
match args {
Args::Double(a, b) => (a as u16) | ((b as u16) << 8),
_ => panic!("to_u16 only works with Args::Double")
}
}
enum Args {
Single(u8),
Double(u8, u8),
}
impl Args {
fn single_val(&self) -> u8 {
match self {
&Args::Single(x) => x,
_ => panic!("single_val only works with Args::Single"),
}
}
}
impl CPU {
@ -61,13 +78,22 @@ impl CPU {
}
#[inline]
fn load_args(&mut self, num_args: u8) -> Box<[u8]> {
let mut args = Vec::new();
for i in 0..num_args {
args.push(self.read_byte(self.ip + i as u16));
fn load_args(&mut self, num_args: u8) -> Args {
match num_args {
1 => {
let val = self.read_byte(self.ip);
self.ip += 1;
Args::Single(val)
}
2 => {
let b1 = self.read_byte(self.ip);
self.ip += 1;
let b2 = self.read_byte(self.ip);
self.ip += 1;
Args::Double(b1, b2)
}
_ => panic!("load_args only supports two bytes")
}
self.ip += num_args as u16;
args.into_boxed_slice()
}
#[inline]
@ -572,7 +598,7 @@ impl CPU {
}
#[inline]
fn call_condition(&mut self, cond_str: String, cond: bool) -> u8 {
fn call_condition(&mut self, cond_str: &'static str, cond: bool) -> u8 {
let dst = to_u16(self.load_args(2));
if self.debug {
println!("CALL {} {:04X}", cond_str, dst);
@ -586,7 +612,7 @@ impl CPU {
}
#[inline]
fn ret_condition(&mut self, cond_str: String, cond: bool) -> u8 {
fn ret_condition(&mut self, cond_str: &'static str, cond: bool) -> u8 {
if self.debug {
println!("RET {}", cond_str);
}
@ -609,8 +635,8 @@ impl CPU {
}
#[inline]
fn jmp_r_condition(&mut self, cond_str: String, cond: bool) -> u8 {
let t = self.load_args(1)[0];
fn jmp_r_condition(&mut self, cond_str: &'static str, cond: bool) -> u8 {
let t = self.load_args(1).single_val();
if self.debug {
println!("JR {} {:02X}", cond_str, t);
}
@ -628,7 +654,7 @@ impl CPU {
}
#[inline]
fn jmp_p_condition(&mut self, cond_str: String, cond: bool) -> u8 {
fn jmp_p_condition(&mut self, cond_str: &'static str, cond: bool) -> u8 {
let t = to_u16(self.load_args(2));
if self.debug {
println!("JP {} {:04X}", cond_str, t);
@ -722,7 +748,7 @@ impl CPU {
#[inline]
fn ld_r_v(&mut self, r: usize) -> u8 {
let val: u8 = self.load_args(1)[0];
let val: u8 = self.load_args(1).single_val();
if self.debug {
println!("LD {}, {:02X}", REG_NAMES[r], val);
}
@ -859,20 +885,35 @@ impl CPU {
self.halted = false;
}
#[allow(unused_variables)]
pub fn run(&mut self) {
let mut running_sum: i32 = 0;
const INSTRUCTIONS_PER_ROUND: usize = 500;
loop {
// Some attempt at limiting our speed
let mut cycles: i32 = 0;
let start = Instant::now();
for _ in 0 .. 1000 {
cycles += self.run_instruction() as i32;
let mut cycles_executed: usize = 0;
for _ in 0..INSTRUCTIONS_PER_ROUND {
cycles_executed += self.run_instruction() as usize;
}
let gb_dur = cycles * 238;
// Time that the gameboy would have required to execute the
// instructions above.
//
// The gameboy has a freq of 4.194304MHz
// This means it takes it 238.418569ns to execute a single
// cycle.
let gb_dur = cycles_executed as i32 * 238;
let our_dur = start.elapsed().subsec_nanos() as i32;
let delta = gb_dur - our_dur;
if delta > (20 * 238) { // We're at least 20 cycles faster.
sleep(Duration::new(0, delta as u32));
let full_cycles: i32 = delta / 238;
running_sum += full_cycles;
if full_cycles > 0 {
// println!("[+] [{}] {} cycles, gb: {}, we: {}", running_sum, cycles_executed, gb_dur, our_dur);
sleep(Duration::new(0, (full_cycles - 10) as u32 * 238 as u32));
} else {
println!("[-] [{}] {} cycles, gb: {}, we: {}", running_sum, cycles_executed, gb_dur, our_dur);
}
}
}
@ -918,6 +959,16 @@ impl CPU {
pub fn run_instruction(&mut self) -> u8 {
// self.debug = !self.interconnect.is_boot_rom();
/*
if self.ip >= 100 && self.ip < 120 {
self.debug = true;
} else {
self.debug = false;
}
*/
// self.debug = true;
// Check for interrupts.
if self.ime {
self.check_interrupts(true);
@ -927,7 +978,7 @@ impl CPU {
}
}
let mut cycles: u8 = 1;
let mut cycles: u8 = 255;
let instruction: u8;
if !self.halted {
// We need to double-check the flags
@ -1017,7 +1068,7 @@ impl CPU {
}
0x10 => {
println!("STOP 0 {:02X} not implemented.", self.load_args(1)[0]);
println!("STOP 0 {:02X} not implemented.", self.load_args(1).single_val());
4
},
0x11 => self.ld_rr_vv(REG_N_D, REG_N_E),
@ -1044,8 +1095,11 @@ impl CPU {
4
},
0x18 => {
let dst = self.load_args(1)[0];
let dst = self.load_args(1).single_val();
self.jmp_r(dst);
if self.debug {
println!("JMPR {:02X}", dst);
}
12
},
0x19 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_D, REG_N_E),
@ -1081,7 +1135,7 @@ impl CPU {
0x20 => {
let c = self.flags & FLAG_Z == 0;
self.jmp_r_condition("NZ".to_owned(), c)
self.jmp_r_condition("NZ", c)
}
0x21 => self.ld_rr_vv(REG_N_H, REG_N_L),
0x22 => {
@ -1137,7 +1191,7 @@ impl CPU {
},
0x28 => {
let c = self.flags & FLAG_Z == FLAG_Z;
self.jmp_r_condition("Z".to_owned(), c)
self.jmp_r_condition("Z", c)
},
0x29 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_H, REG_N_L),
0x2A => {
@ -1166,7 +1220,7 @@ impl CPU {
0x30 => {
let c = self.flags & FLAG_C == 0;
self.jmp_r_condition("NC".to_owned(), c)
self.jmp_r_condition("NC", c)
},
0x31 => {
let args = self.load_args(2);
@ -1207,7 +1261,7 @@ impl CPU {
},
0x38 => {
let c = self.flags & FLAG_C == FLAG_C;
self.jmp_r_condition("C".to_owned(), c)
self.jmp_r_condition("C", c)
},
0x39 => {
if self.debug {
@ -1379,12 +1433,12 @@ impl CPU {
0xC0 => {
let c = self.flags & FLAG_Z == 0;
self.ret_condition("NZ".to_owned(), c)
self.ret_condition("NZ", c)
},
0xC1 => self.pop_rr(REG_N_B, REG_N_C),
0xC2 => {
let c = self.flags & FLAG_Z == 0;
self.jmp_p_condition("NZ".to_owned(), c)
self.jmp_p_condition("NZ", c)
},
0xC3 => {
let dst = to_u16(self.load_args(2));
@ -1396,11 +1450,11 @@ impl CPU {
},
0xC4 => {
let c = self.flags & FLAG_Z == 0;
self.call_condition("NZ".to_owned(), c)
self.call_condition("NZ", c)
}
0xC5 => self.push_rr(REG_N_B, REG_N_C),
0xC6 => {
let val = self.load_args(1)[0];
let val = self.load_args(1).single_val();
if self.debug {
println!("ADD A, {:02X}", val);
}
@ -1412,7 +1466,7 @@ impl CPU {
0xC7 => self.rst(0x00),
0xC8 => {
let c = self.flags & FLAG_Z == FLAG_Z;
self.ret_condition("Z".to_owned(), c)
self.ret_condition("Z", c)
},
0xC9 => {
if self.debug {
@ -1423,7 +1477,7 @@ impl CPU {
},
0xCA => {
let c = self.flags & FLAG_Z == FLAG_Z;
self.jmp_p_condition("Z".to_owned(), c)
self.jmp_p_condition("Z", c)
}
0xCB => {
self.run_prefix_instruction();
@ -1431,11 +1485,11 @@ impl CPU {
},
0xCC => {
let c = self.flags & FLAG_Z == FLAG_Z;
self.call_condition("Z".to_owned(), c)
self.call_condition("Z", c)
},
0xCD => self.call_condition("".to_owned(), true),
0xCD => self.call_condition("", true),
0xCE => {
let arg = self.load_args(1)[0];
let arg = self.load_args(1).single_val();
if self.debug {
println!("ADC A, {:02X}", arg);
}
@ -1447,21 +1501,21 @@ impl CPU {
0xD0 => {
let c = self.flags & FLAG_C == 0;
self.ret_condition("NC".to_owned(), c)
self.ret_condition("NC", c)
},
0xD1 => self.pop_rr(REG_N_D, REG_N_E),
0xD2 => {
let c = self.flags & FLAG_C == 0;
self.jmp_p_condition("NC".to_owned(), c)
self.jmp_p_condition("NC", c)
}
0xD3 => panic!("NON-EXISTING OPCODE"),
0xD4 => {
let c = self.flags & FLAG_C == 0;
self.call_condition("NC".to_owned(), c)
self.call_condition("NC", c)
}
0xD5 => self.push_rr(REG_N_D, REG_N_E),
0xD6 => {
let val = self.load_args(1)[0];
let val = self.load_args(1).single_val();
if self.debug {
println!("SUB {:02X}", val);
}
@ -1472,7 +1526,7 @@ impl CPU {
0xD7 => self.rst(0x10),
0xD8 => {
let c = self.flags & FLAG_C == FLAG_C;
self.ret_condition("C".to_owned(), c)
self.ret_condition("C", c)
},
0xD9 => {
if self.debug {
@ -1482,16 +1536,16 @@ impl CPU {
}
0xDA => {
let c = self.flags & FLAG_C == FLAG_C;
self.jmp_p_condition("C".to_owned(), c)
self.jmp_p_condition("C", c)
},
0xDB => panic!("NON-EXISTING OPCODE"),
0xDC => {
let c = self.flags & FLAG_C == FLAG_C;
self.call_condition("C".to_owned(), c)
self.call_condition("C", c)
}
0xDD => panic!("NON-EXISTING OPCODE"),
0xDE => {
let arg = self.load_args(1)[0];
let arg = self.load_args(1).single_val();
if self.debug {
println!("SBC {:02X}", arg);
}
@ -1501,11 +1555,11 @@ impl CPU {
0xDF => self.rst(0x18),
0xE0 => {
let args = self.load_args(1);
let arg = self.load_args(1).single_val();
if self.debug {
println!("LDH {:02X}, A", args[0]);
println!("LDH {:02X}, A", arg);
}
self.interconnect.write_byte(0xFF00 + args[0] as u16, self.regs[REG_A]);
self.interconnect.write_byte(0xFF00 + arg as u16, self.regs[REG_A]);
12
},
0xE1 => self.pop_rr(REG_N_H, REG_N_L),
@ -1520,7 +1574,7 @@ impl CPU {
0xE3 | 0xE4 => panic!("NON-EXISTING OPCODE"),
0xE5 => self.push_rr(REG_N_H, REG_N_L),
0xE6 => {
let val = self.load_args(1)[0];
let val = self.load_args(1).single_val();
if self.debug {
println!("AND {:02X}", val);
}
@ -1535,7 +1589,7 @@ impl CPU {
},
0xE7 => self.rst(0x20),
0xE8 => {
let arg = self.load_args(1)[0] as i8;
let arg = self.load_args(1).single_val() as i8;
if self.debug {
println!("ADD SP, {:02X}", arg);
}
@ -1570,7 +1624,7 @@ impl CPU {
},
0xEB ... 0xED => panic!("NON-EXISTING OPCODE"),
0xEE => {
let arg = self.load_args(1)[0];
let arg = self.load_args(1).single_val();
if self.debug {
println!("XOR {:02X}", arg);
}
@ -1585,11 +1639,11 @@ impl CPU {
0xEF => self.rst(0x28),
0xF0 => {
let args = self.load_args(1);
let arg = self.load_args(1).single_val();
if self.debug {
println!("LDH A, {:02X}", args[0]);
println!("LDH A, {:02X}", arg);
}
self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + args[0] as u16);
self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + arg as u16);
12
},
0xF1 => self.pop_rr(REG_N_A, REG_N_F),
@ -1611,7 +1665,7 @@ impl CPU {
0xF4 => panic!("NON-EXISTING OPCODE"),
0xF5 => self.push_rr(REG_N_A, REG_N_F),
0xF6 => {
let val = self.load_args(1)[0];
let val = self.load_args(1).single_val();
if self.debug {
println!("OR {:02X}", val);
}
@ -1625,7 +1679,7 @@ impl CPU {
},
0xF7 => self.rst(0x30),
0xF8 => {
let arg = self.load_args(1)[0] as i8;
let arg = self.load_args(1).single_val() as i8;
if self.debug {
println!("LD HL, SP+{:02X}", arg);
}
@ -1672,12 +1726,12 @@ impl CPU {
0xFC | 0xFD => panic!("NON-EXISTING OPCODE"),
0xFE => {
let args = self.load_args(1);
let arg = self.load_args(1).single_val();
if self.debug {
println!("CP {:02X}", args[0]);
println!("CP {:02X}", arg);
}
self.cp_r(args[0]);
self.cp_r(arg);
8
},
0xFF => self.rst(0x38),

View File

@ -199,7 +199,7 @@ impl Interconnect {
// TODO: if some flag set, use bios, otherwise only use rom
// For now, just use bios
match addr {
0x0000 ... 0x100 => {
0x0000 ... 0xFF => {
if self.disable_bootrom == 0 {
self.bios[addr as usize]
} else {

View File

@ -8,17 +8,15 @@ use self::pulse_simple::Playback;
use std::sync::{Arc, Mutex};
use std::thread;
//use std::f64::consts::PI;
//const TICK_RATE: usize = 4194304;
const OUTPUT_SAMPLE_RATE: usize = 32768;
//const OUTPUT_SAMPLE_RATE: usize = 1024;
//const TICKS_PER_SAMPLE: usize = TICK_RATE / OUTPUT_SAMPLE_RATE;
const OUTPUT_SAMPLE_RATE: usize = 48100;
//const OUTPUT_SAMPLE_RATE: usize = 32768;
/// Represents an 'audio module', transforming an input sample to an output sample
trait AudioModule<T> {
fn transform(&self, sample: T) -> T;
}
/// Clockable audio components
trait AudioComponent {
fn clock(&mut self);
}
@ -31,26 +29,33 @@ struct Channel1 {
envelope: envelope::VolumeEnvelope,
tick_state: u8,
stored_regs: [u8; 5]
}
impl Channel1 {
pub fn write_byte(&mut self, addr: u16, val: u8) {
match addr {
0xFF10 => {
println!("Channel1: Sweep not implemented yet :<");
self.wave_gen.set_sweep_reg(val);
self.stored_regs[0] = val;
}
0xFF11 => {
self.length_counter.set_length(val & 0x3F);
self.wave_gen.set_duty_cycle(val >> 6);
self.stored_regs[1] = val;
}
0xFF12 => {
self.envelope.set_register(val);
self.stored_regs[2] = val;
}
0xFF13 => {
// Set lower frequency
self.wave_gen.set_lower_freq(val);
self.stored_regs[3] = val;
}
0xFF14 => {
self.stored_regs[4] = val;
// Set higher
self.wave_gen.set_higher_freq(val);
@ -67,7 +72,8 @@ impl Channel1 {
pub fn read_byte(&self, addr: u16) -> u8 {
match addr {
_ => panic!("Channel1 does not support reading ({:04X}", addr),
0xFF10...0xFF14 => self.stored_regs[(addr - 0xFF10) as usize],
_ => {println!("Channel1 does not support reading ({:04X})", addr); 0},
}
}
@ -88,7 +94,7 @@ impl Channel1 {
}
if self.tick_state == 2 || self.tick_state == 6 {
// TODO: Sweep
self.wave_gen.sweep_clock();
}
if self.tick_state == 7 {
@ -144,7 +150,7 @@ impl Channel2 {
pub fn read_byte(&self, addr: u16) -> u8 {
match addr {
_ => panic!("Channel2 does not support reading ({:04X}", addr),
_ => {println!("Channel2 does not support reading ({:04X})", addr); 0},
}
}
@ -192,7 +198,7 @@ struct Channel3 {
impl Channel3 {
pub fn write_byte(&mut self, addr: u16, val: u8) {
println!("C3 WB: {:04X} = {:02X} ({:08b})", addr, val, val);
// println!("C3 WB: {:04X} = {:02X} ({:08b})", addr, val, val);
match addr {
0xFF1A => {
self.wave_gen.enabled = (val & (1 << 7)) > 0;
@ -226,7 +232,7 @@ impl Channel3 {
pub fn read_byte(&self, addr: u16) -> u8 {
match addr {
_ => panic!("Channel3 does not support reading ({:04X}", addr),
_ => panic!("Channel3 does not support reading ({:04X})", addr),
}
}
@ -237,8 +243,7 @@ impl Channel3 {
pub fn sample(&self) -> u8 {
let input = self.wave_gen.sample();
// Follow the chain
let input = self.length_counter.transform(input);
// self.envelope.transform(input)
// let input = self.length_counter.transform(input);
input
}
@ -291,7 +296,10 @@ impl SoundManager {
pub fn launch_thread(&self) {
let obj = self.sound_object.clone();
thread::spawn(move || {
if false {
return;
}
thread::Builder::new().name("Audio".into()).spawn(move || {
// PulseAudio playback object
let playback = Playback::new("GBC", "Output", None, (OUTPUT_SAMPLE_RATE) as _ );
@ -299,20 +307,9 @@ impl SoundManager {
let mut counter = 0;
loop {
{
let (s1, s2) = {
let mut c_obj = obj.lock().unwrap();
c_obj.channel1.sample_clock();
c_obj.channel2.sample_clock();
c_obj.channel3.sample_clock();
// let s = c_obj.sample();
let s1 = c_obj.channel1.sample();
let s2 = c_obj.channel2.sample();
let s3 = c_obj.channel3.sample();
let samps = [[s3 as u8, s3 as u8]];
playback.write(&samps[..]);
// Check for 512 Hz timer
if counter >= (OUTPUT_SAMPLE_RATE / 512) {
c_obj.clock();
@ -320,11 +317,18 @@ impl SoundManager {
} else {
counter += 1;
}
}
// Sample clock
c_obj.sample_clock();
// Get sample
c_obj.sample()
};
let samps = [[s1, s2]];
// No sleep needed, it seems like the playback.write is blocking
// thread::sleep(time::Duration::new(0, sleep_duration));
playback.write(&samps[..]);
}
});
}).unwrap();
}
}
@ -339,9 +343,65 @@ impl Sound {
self.channel3.clock();
}
pub fn sample(&self) -> u8 {
// TODO: Mixing
(self.channel1.sample() >> 1) + (self.channel2.sample() >> 1) + (self.channel3.sample() >> 1)
pub fn sample_clock(&mut self) {
self.channel1.sample_clock();
self.channel2.sample_clock();
self.channel3.sample_clock();
}
pub fn sample(&self) -> (u8, u8) {
// Some basic mixing
// Output value
let mut s = [0u16; 2];
let s01_volume = self.sound_channel_volume_control & 7;
let s02_volume = (self.sound_channel_volume_control >> 4) & 7;
let c1_sample = self.channel1.sample();
let c2_sample = self.channel2.sample();
let c3_sample = self.channel3.sample();
let c4_sample = 0;
if self.enabled != 0 {
if s01_volume > 0 {
if self.sound_output_terminal_selector & 1 > 0 {
// Output Channel1
s[0] += c1_sample as _;
}
if self.sound_output_terminal_selector & 2 > 0 && false {
// Output Channel2
s[0] += c2_sample as _;
}
if self.sound_output_terminal_selector & 4 > 0 {
// Output Channel3
s[0] += c3_sample as _;
}
if self.sound_output_terminal_selector & 8 > 0 {
// Output Channel4
s[0] += c4_sample as _;
}
}
if s02_volume > 0 {
if self.sound_output_terminal_selector & 0x10 > 0 && false {
// Output Channel1
s[1] += c1_sample as _;
}
if self.sound_output_terminal_selector & 0x20 > 0 {
// Output Channel2
s[1] += c2_sample as _;
}
if self.sound_output_terminal_selector & 0x40 > 0 {
// Output Channel3
s[1] += c3_sample as _;
}
if self.sound_output_terminal_selector & 0x80 > 0 {
// Output Channel4
s[1] += c4_sample as _;
}
}
}
// (self.channel1.sample() >> 1) + (self.channel2.sample() >> 1) + (self.channel3.sample() >> 1)
((s[0] >> 2) as _, (s[1] >> 2) as _)
}
pub fn write_byte(&mut self, addr: u16, val: u8) {

View File

@ -23,6 +23,18 @@ pub struct SquareWaveGenerator {
duty_cycle: DutyCycle,
gb_reg_freq: u16,
// Sweep settings (if existing)
sweep_change_period: u8,
// selects the inc/dec mode of the sweep. Dec if set
sweep_dec: bool,
// Sweep speed (n/128th)
sweep_change: u8,
// Current sweep clock cycle
sweep_clock: u8,
// sweep_freq_shadow: f32,
}
impl SquareWaveGenerator {
@ -42,6 +54,35 @@ impl SquareWaveGenerator {
self.update_frequency();
}
pub fn set_sweep_reg(&mut self, reg: u8) {
//Bit 6-4 - Sweep Time
//Bit 3 - Sweep Increase/Decrease
// 0: Addition (frequency increases)
// 1: Subtraction (frequency decreases)
//Bit 2-0 - Number of sweep shift (n: 0-7)
self.sweep_change_period = (reg >> 4) & 7;
self.sweep_dec = reg & (1 << 3) > 0;
self.sweep_change = reg & 7;
self.sweep_clock = 0;
}
pub fn sweep_clock(&mut self) {
// self.sweep_freq_shadow = self.frequency;
if self.sweep_change_period > 0 {
if self.sweep_change > 0 {
if self.sweep_clock >= self.sweep_change_period {
self.sweep_clock = 0;
let change = self.frequency / (2f32.powi(self.sweep_change as _));
self.frequency += if self.sweep_dec {
change
} else {
-change
};
}
}
}
}
pub fn reset(&mut self) {
self.time = 0f32;
}

View File

@ -11,6 +11,11 @@ pub struct Timer {
gb_ticks: u16,
}
// Timer speed contains 4 different scales:
// [0] = 64 = 4096 Hz
// [1] = 1 = 262144 Hz
// [2] = 4 = 65536 Hz
// [3] = 16 = 16384 Hz
const TIMER_SPEED: [u16; 4] = [64, 1, 4, 16];
const TIMER_ENABLE: u8 = (1 << 2);
@ -19,34 +24,38 @@ impl Timer {
Timer::default()
}
/// This clock is ticking at 262.144 Hz
fn timer_clock_tick(&mut self) {
// The div reg will always tick
self.div_tick_counter += 1;
if self.div_tick_counter >= TIMER_SPEED[3] {
// 262144 Hz / 16 = 16384 Hz
if self.div_tick_counter >= 16 {
self.div = self.div.wrapping_add(1);
self.div_tick_counter -= TIMER_SPEED[3];
self.div_tick_counter -= 16;
}
if (self.tac & TIMER_ENABLE) == TIMER_ENABLE { // Is timer enabled?
// Check if the timer is enabled
if (self.tac & TIMER_ENABLE) != 0 {
self.tick_counter += 1;
let req_ticks = TIMER_SPEED[(self.tac & 3) as usize];
if self.tick_counter >= req_ticks {
if self.tick_counter == req_ticks {
if self.tima == 0xFF {
self.timer_interrupt = true;
self.tima = 0;
self.tima = self.tma;
} else {
self.tima += 1;
}
self.tick_counter -= req_ticks;
self.tick_counter = 0;
}
}
}
pub fn tick(&mut self, ticks: u16) {
// One tick 1/4.194304MHz on regular speed => 16 gb ticks
// One tick 1/4.194304MHz on regular speed => 16 gb ticks = prescaler
self.gb_ticks += ticks;
// If we're in GBC fast mode, we'd require 32 ticks instead of 16
while self.gb_ticks >= 16 {
self.timer_clock_tick();
self.gb_ticks -= 16;
@ -54,26 +63,26 @@ impl Timer {
}
pub fn write_byte(&mut self, addr: u16, val: u8) {
// println!("Timer WR: {:04X} = {:02X}", addr, val);
match addr {
0xFF04 => self.div = 0,
0xFF05 => self.tima = val,
0xFF06 => self.tma = val,
0xFF07 => self.tac = val,
_ => println!("Timer: Write {:02X} to {:04X} unsupported", val, addr),
_ => unreachable!(),
}
}
pub fn read_byte(&self, addr: u16) -> u8 {
match addr {
let r = match addr {
0xFF04 => self.div,
0xFF05 => self.tima,
0xFF06 => self.tma,
0xFF07 => self.tac,
_ => {
println!("Timer: Read from {:04X} unsupported", addr);
0
},
}
_ => unreachable!(),
};
// println!("Timer RD: {:04X} = {:02X}", addr, r);
r
}
pub fn timer_interrupt(&mut self) -> bool {