From 8625bec05978c7cb8372212b19d46e97397c221b Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Sat, 14 Aug 2021 00:10:51 -0500 Subject: [PATCH] feat: clock bus on instruction read-write Commit also includes general work towards passing mem-timings. Note: while cpu_instrs.gb passes, instr_timing.gb and mem_timing.gb both are stuck in infinite loops (Currently, it seems like a timing issue). This is a major regression that hopefully shouldn't last for too long. --- src/apu.rs | 2 +- src/apu/gen.rs | 2 +- src/bus.rs | 22 ++-- src/cpu.rs | 133 +++++++++++---------- src/instruction.rs | 281 ++++++++++++++++++++++++++++++--------------- src/ppu.rs | 2 +- src/ppu/dma.rs | 2 +- src/timer.rs | 76 +++++++++--- src/work_ram.rs | 31 +---- 9 files changed, 332 insertions(+), 219 deletions(-) diff --git a/src/apu.rs b/src/apu.rs index 4a133b1..5b39157 100644 --- a/src/apu.rs +++ b/src/apu.rs @@ -94,7 +94,7 @@ impl BusIo for Apu { } impl Apu { - pub(crate) fn clock(&mut self, div: u16) { + pub(crate) fn tick(&mut self, div: u16) { use FrameSequencerState::*; self.sample_counter += SAMPLE_INCREMENT; diff --git a/src/apu/gen.rs b/src/apu/gen.rs index 3376589..1a1cc91 100644 --- a/src/apu/gen.rs +++ b/src/apu/gen.rs @@ -3,7 +3,7 @@ use rtrb::{Consumer, Producer, PushError, RingBuffer}; pub(crate) const SAMPLE_RATE: u32 = 48000; // Hz const CHANNEL_COUNT: usize = 2; -const BUFFER_CAPACITY: usize = 4096 * CHANNEL_COUNT; // # of samples * the # of channels +const BUFFER_CAPACITY: usize = 512 * CHANNEL_COUNT; // # of samples * the # of channels pub struct AudioSPSC { inner: RingBuffer, diff --git a/src/bus.rs b/src/bus.rs index c8bdfa4..aa0765d 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -72,14 +72,20 @@ impl Bus { } pub(crate) fn clock(&mut self) { - self.ppu.clock(); - self.timer.clock(); - self.apu.clock(self.timer.divider); - self.clock_dma(); + self.tick(4); } - fn clock_dma(&mut self) { - if let Some((src_addr, dest_addr)) = self.ppu.dma.clock() { + fn tick(&mut self, limit: u8) { + for _ in 0..limit { + self.timer.tick(); + self.ppu.tick(); + self.apu.tick(self.timer.divider); + self.dma_tick() + } + } + + fn dma_tick(&mut self) { + if let Some((src_addr, dest_addr)) = self.ppu.dma.tick() { let byte = self.oam_read_byte(src_addr); self.oam_write_byte(dest_addr, byte); } @@ -217,7 +223,7 @@ impl BusIo for Bus { 0x01 => self.serial.next, 0x02 => self.serial.ctrl.into(), 0x04 => (self.timer.divider >> 8) as u8, - 0x05 => self.timer.counter, + 0x05 => self.timer.tima(), 0x06 => self.timer.modulo, 0x07 => self.timer.ctrl.into(), 0x0F => self.interrupt_flag().into(), @@ -317,7 +323,7 @@ impl BusIo for Bus { 0x01 => self.serial.next = byte, 0x02 => self.serial.ctrl = byte.into(), 0x04 => self.timer.divider = 0x0000, - 0x05 => self.timer.counter = byte, + 0x05 => self.timer.set_tima(byte), 0x06 => self.timer.modulo = byte, 0x07 => self.timer.ctrl = byte.into(), 0x0F => self.set_interrupt_flag(byte), diff --git a/src/cpu.rs b/src/cpu.rs index 5523f30..25c86fc 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -65,10 +65,6 @@ impl Cpu { self.halted.as_ref() } - fn inc_pc(&mut self) { - self.reg.pc += 1; - } - pub fn load_cartridge(&mut self, path: &str) -> std::io::Result<()> { self.bus.load_cartridge(path) } @@ -79,25 +75,16 @@ impl Cpu { } impl Cpu { - fn fetch(&self) -> u8 { - self.bus.read_byte(self.reg.pc) - } - - pub(crate) fn imm_byte(&mut self) -> u8 { - let byte = self.bus.read_byte(self.reg.pc); + fn fetch(&mut self) -> u8 { + let byte = self.read_byte(self.reg.pc); + self.bus.clock(); self.reg.pc += 1; byte } - pub(crate) fn imm_word(&mut self) -> u16 { - let word = self.bus.read_word(self.reg.pc); - self.reg.pc += 2; - word - } - pub(crate) fn decode(&mut self, opcode: u8) -> Instruction { if opcode == 0xCB { - Instruction::decode(self.imm_byte(), true) + Instruction::decode(self.fetch(), true) } else { Instruction::decode(opcode, false) } @@ -108,44 +95,44 @@ impl Cpu { } pub fn step(&mut self) -> Cycle { - // Log instructions - // if !self.bus.boot_mapped() { + // // Log instructions + // if self.reg.pc > 0xFF { // let out = std::io::stdout(); - // let _ = self._log_state(out.lock()); + // let _ = self._print_debug(out.lock()); // } - // FIXME: The Halt instruction takes less cycles than it should in Blargg's 2nd cpu_instrs test - let cycles = match self.halted() { + // FIXME: The Halt instruction takes more cycles than it should in Blargg's 2nd cpu_instrs test + let elapsed = match self.halted() { Some(state) => { use HaltState::*; match state { - ImeEnabled | NonePending => Cycle::new(4), + ImeEnabled | NonePending => { + self.bus.clock(); + Cycle::new(4) + } SomePending => todo!("Implement HALT bug"), } } None => { let opcode = self.fetch(); - self.inc_pc(); - let instr = self.decode(opcode); - let cycles = self.execute(instr); - + let elapsed = self.execute(instr); self.check_ime(); - - cycles + elapsed } }; - let pending: u32 = cycles.into(); - for _ in 0..pending { - self.bus.clock(); + // For use in Blargg's Test ROMs + if self.read_byte(0xFF02) == 0x81 { + let c = self.read_byte(0xFF01) as char; + self.write_byte(0xFF02, 0x00); + eprint!("{}", c); } - // TODO: This is in the wrong place + // TODO: Is this in the wrong place? self.handle_interrupts(); - - cycles + elapsed } } @@ -159,12 +146,6 @@ impl BusIo for Cpu { } } -impl Cpu { - pub(crate) fn write_word(&mut self, addr: u16, word: u16) { - self.bus.write_word(addr, word) - } -} - impl Cpu { pub fn ppu(&mut self) -> &Ppu { &self.bus.ppu @@ -193,9 +174,17 @@ impl Cpu { } } + pub(crate) fn int_request(&self) -> u8 { + self.read_byte(0xFF0F) + } + + pub(crate) fn int_enable(&self) -> u8 { + self.read_byte(0xFFFF) + } + fn handle_interrupts(&mut self) { - let req = self.read_byte(0xFF0F); - let enabled = self.read_byte(0xFFFF); + let req = self.int_request(); + let enabled = self.int_enable(); if self.halted.is_some() { // When we're here either a HALT with IME set or @@ -363,10 +352,9 @@ impl Cpu { } impl Cpu { - fn _debug_log(&self, mut w: impl std::io::Write, instr: &Instruction) -> std::io::Result<()> { - let pc = self.reg.pc - 1; + fn _print_debug(&self, mut w: impl std::io::Write) -> std::io::Result<()> { write!(w, "A: {:02X} ", self.reg.a)?; - write!(w, "F: {:04b} ", u8::from(self.flags) >> 4)?; + write!(w, "F: {:02X} ", u8::from(self.flags))?; write!(w, "B: {:02X} ", self.reg.b)?; write!(w, "C: {:02X} ", self.reg.c)?; write!(w, "D: {:02X} ", self.reg.d)?; @@ -374,31 +362,40 @@ impl Cpu { write!(w, "H: {:02X} ", self.reg.h)?; write!(w, "L: {:02X} ", self.reg.l)?; write!(w, "SP: {:04X} ", self.reg.sp)?; - write!(w, "PC: 00:{:04X} ", pc)?; - write!(w, "({:02X} ", self.read_byte(pc))?; - write!(w, "{:02X} ", self.read_byte(pc + 1))?; - write!(w, "{:02X} ", self.read_byte(pc + 2))?; - write!(w, "{:02X}) ", self.read_byte(pc + 3))?; - writeln!(w, "| {:?}", instr)?; + write!(w, "PC: 00:{:04X} ", self.reg.pc)?; + write!(w, "({:02X} ", self.read_byte(self.reg.pc))?; + write!(w, "{:02X} ", self.read_byte(self.reg.pc + 1))?; + write!(w, "{:02X} ", self.read_byte(self.reg.pc + 2))?; + write!(w, "{:02X})", self.read_byte(self.reg.pc + 3))?; + writeln!(w, "| {:?}", self._dbg_instr())?; w.flush() } - fn _log_state(&self, mut writer: impl std::io::Write) -> std::io::Result<()> { - write!(writer, "A: {:02X} ", self.reg.a)?; - write!(writer, "F: {:02X} ", u8::from(self.flags))?; - write!(writer, "B: {:02X} ", self.reg.b)?; - write!(writer, "C: {:02X} ", self.reg.c)?; - write!(writer, "D: {:02X} ", self.reg.d)?; - write!(writer, "E: {:02X} ", self.reg.e)?; - write!(writer, "H: {:02X} ", self.reg.h)?; - write!(writer, "L: {:02X} ", self.reg.l)?; - write!(writer, "SP: {:04X} ", self.reg.sp)?; - write!(writer, "PC: 00:{:04X} ", self.reg.pc)?; - write!(writer, "({:02X} ", self.read_byte(self.reg.pc))?; - write!(writer, "{:02X} ", self.read_byte(self.reg.pc + 1))?; - write!(writer, "{:02X} ", self.read_byte(self.reg.pc + 2))?; - writeln!(writer, "{:02X})", self.read_byte(self.reg.pc + 3))?; - writer.flush() + fn _print_logs(&self, mut w: impl std::io::Write) -> std::io::Result<()> { + write!(w, "A: {:02X} ", self.reg.a)?; + write!(w, "F: {:02X} ", u8::from(self.flags))?; + write!(w, "B: {:02X} ", self.reg.b)?; + write!(w, "C: {:02X} ", self.reg.c)?; + write!(w, "D: {:02X} ", self.reg.d)?; + write!(w, "E: {:02X} ", self.reg.e)?; + write!(w, "H: {:02X} ", self.reg.h)?; + write!(w, "L: {:02X} ", self.reg.l)?; + write!(w, "SP: {:04X} ", self.reg.sp)?; + write!(w, "PC: 00:{:04X} ", self.reg.pc)?; + write!(w, "({:02X} ", self.read_byte(self.reg.pc))?; + write!(w, "{:02X} ", self.read_byte(self.reg.pc + 1))?; + write!(w, "{:02X} ", self.read_byte(self.reg.pc + 2))?; + writeln!(w, "{:02X})", self.read_byte(self.reg.pc + 3))?; + w.flush() + } + + fn _dbg_instr(&self) -> Instruction { + let byte = self.read_byte(self.reg.pc); + if byte == 0xCB { + Instruction::decode(self.read_byte(self.reg.pc + 1), true) + } else { + Instruction::decode(byte, false) + } } } diff --git a/src/instruction.rs b/src/instruction.rs index 1acdec7..180bb70 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -8,7 +8,7 @@ use self::table::{ register, }; use self::table::{Group1RegisterPair, Group2RegisterPair, Group3RegisterPair, Register}; -use crate::bus::BusIo; +use crate::bus::{Bus, BusIo}; use crate::cpu::{Cpu, Flags, HaltState, ImeState, Register as CpuRegister, RegisterPair}; #[allow(clippy::upper_case_acronyms)] @@ -120,14 +120,15 @@ impl Instruction { Instruction::LD(target, src) => match (target, src) { (LDTarget::IndirectImmediateWord, LDSource::SP) => { // LD (u16), SP | Store stack pointer in byte at 16-bit register - let word = cpu.imm_word(); - cpu.write_word(word, cpu.register_pair(RegisterPair::SP)); + let addr = Self::imm_word(cpu); + let sp = cpu.register_pair(RegisterPair::SP); + Self::write_word(&mut cpu.bus, addr, sp); Cycle::new(20) } (LDTarget::Group1(pair), LDSource::ImmediateWord) => { // LD r16, u16 | Store u16 in 16-bit register use Group1RegisterPair::*; - let word = cpu.imm_word(); + let word = Self::imm_word(cpu); match pair { BC | DE | HL | SP => cpu.set_register_pair(pair.as_register_pair(), word), @@ -141,16 +142,16 @@ impl Instruction { match pair { Group2RegisterPair::BC | Group2RegisterPair::DE => { let addr = cpu.register_pair(pair.as_register_pair()); - cpu.write_byte(addr, acc); + Self::write_byte(&mut cpu.bus, addr, acc); } Group2RegisterPair::IncrementHL => { let addr = cpu.register_pair(RegisterPair::HL); - cpu.write_byte(addr, acc); + Self::write_byte(&mut cpu.bus, addr, acc); cpu.set_register_pair(RegisterPair::HL, addr + 1); } Group2RegisterPair::DecrementHL => { let addr = cpu.register_pair(RegisterPair::HL); - cpu.write_byte(addr, acc); + Self::write_byte(&mut cpu.bus, addr, acc); cpu.set_register_pair(RegisterPair::HL, addr - 1); } } @@ -161,18 +162,18 @@ impl Instruction { match pair { Group2RegisterPair::BC | Group2RegisterPair::DE => { let addr = cpu.register_pair(pair.as_register_pair()); - let byte = cpu.read_byte(addr); + let byte = Self::read_byte(&mut cpu.bus, addr); cpu.set_register(CpuRegister::A, byte); } Group2RegisterPair::IncrementHL => { let addr = cpu.register_pair(RegisterPair::HL); - let byte = cpu.read_byte(addr); + let byte = Self::read_byte(&mut cpu.bus, addr); cpu.set_register(CpuRegister::A, byte); cpu.set_register_pair(RegisterPair::HL, addr + 1); } Group2RegisterPair::DecrementHL => { let addr = cpu.register_pair(RegisterPair::HL); - let byte = cpu.read_byte(addr); + let byte = Self::read_byte(&mut cpu.bus, addr); cpu.set_register(CpuRegister::A, byte); cpu.set_register_pair(RegisterPair::HL, addr - 1); } @@ -182,7 +183,7 @@ impl Instruction { (LDTarget::Register(reg), LDSource::ImmediateByte) => { // LD r8, u8 | Store u8 in 8-bit register use Register::*; - let right = cpu.imm_byte(); + let right = Self::imm_byte(cpu); match reg { A | B | C | D | E | H | L => { @@ -191,7 +192,7 @@ impl Instruction { } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - cpu.write_byte(addr, right); + Self::write_byte(&mut cpu.bus, addr, right); Cycle::new(12) } } @@ -199,12 +200,14 @@ impl Instruction { (LDTarget::IoWithC, LDSource::A) => { // LD (0xFF00 + C), A | Store accumulator in byte at address 0xFF00 + C let addr = 0xFF00 + cpu.register(CpuRegister::C) as u16; - cpu.write_byte(addr, cpu.register(CpuRegister::A)); + let acc = cpu.register(CpuRegister::A); + Self::write_byte(&mut cpu.bus, addr, acc); Cycle::new(8) } (LDTarget::A, LDSource::IoWithC) => { // LD A, (0xFF00 + C) | Store byte at 0xFF00 + C in register A - let byte = cpu.read_byte(0xFF00 + cpu.register(CpuRegister::C) as u16); + let addr = 0xFF00 + cpu.register(CpuRegister::C) as u16; + let byte = Self::read_byte(&mut cpu.bus, addr); cpu.set_register(CpuRegister::A, byte); Cycle::new(8) } @@ -223,14 +226,14 @@ impl Instruction { } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - cpu.write_byte(addr, right); + Self::write_byte(&mut cpu.bus, addr, right); Cycle::new(8) } } } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let right = cpu.read_byte(addr); + let right = Self::read_byte(&mut cpu.bus, addr); match target { B | C | D | E | H | L | A => { @@ -246,32 +249,34 @@ impl Instruction { } (LDTarget::IoWithImmediateOffset, LDSource::A) => { // LD (0xFF00 + u8), A | Store accumulator in byte at address 0xFF00 + u8 - let offset = cpu.imm_byte(); - cpu.write_byte(0xFF00 + (offset as u16), cpu.register(CpuRegister::A)); + let addr = 0xFF00 + Self::imm_byte(cpu) as u16; + let acc = cpu.register(CpuRegister::A); + Self::write_byte(&mut cpu.bus, addr, acc); Cycle::new(12) } (LDTarget::A, LDSource::IoWithImmediateOffset) => { // LD A, (0xFF00 + u8) | Store byte at address 0xFF00 + u8 in accumulator - let offset = cpu.imm_byte(); - let byte = cpu.read_byte(0xFF00 + (offset as u16)); + let addr = 0xFF00 + Self::imm_byte(cpu) as u16; + let byte = Self::read_byte(&mut cpu.bus, addr); cpu.set_register(CpuRegister::A, byte); Cycle::new(12) } (LDTarget::SP, LDSource::HL) => { // LD SP, HL | Store HL in stack pointer cpu.set_register_pair(RegisterPair::SP, cpu.register_pair(RegisterPair::HL)); - Cycle::new(8) + Cycle::new(8) // performs an internal operation that takes 4 cycles } (LDTarget::IndirectImmediateWord, LDSource::A) => { // LD (u16), A | Store accumulator in byte at 16-bit register - let word = cpu.imm_word(); - cpu.write_byte(word, cpu.register(CpuRegister::A)); + let addr = Self::imm_word(cpu); + let acc = cpu.register(CpuRegister::A); + Self::write_byte(&mut cpu.bus, addr, acc); Cycle::new(16) } (LDTarget::A, LDSource::IndirectImmediateWord) => { // LD A, (u16) | Store byte at 16-bit register in accumulator - let addr = cpu.imm_word(); - let byte = cpu.read_byte(addr); + let addr = Self::imm_word(cpu); + let byte = Self::read_byte(&mut cpu.bus, addr); cpu.set_register(CpuRegister::A, byte); Cycle::new(16) } @@ -283,14 +288,14 @@ impl Instruction { // JR i8 | Add i8 bytes from program counter let flags: Flags = *cpu.flags(); - let byte = cpu.imm_byte(); // Note: This modifies the PC we access immediately after + let byte = Self::imm_byte(cpu) as i8; // Note: This modifies the PC we access immediately after let pc = cpu.register_pair(RegisterPair::PC); - let addr = pc.wrapping_add(byte as i8 as u16); + let addr = pc.wrapping_add(byte as u16); match cond { JumpCondition::NotZero => { if !flags.z() { - cpu.set_register_pair(RegisterPair::PC, addr); + Self::jump(cpu, addr); Cycle::new(12) } else { Cycle::new(8) @@ -298,7 +303,7 @@ impl Instruction { } JumpCondition::Zero => { if flags.z() { - cpu.set_register_pair(RegisterPair::PC, addr); + Self::jump(cpu, addr); Cycle::new(12) } else { Cycle::new(8) @@ -306,7 +311,7 @@ impl Instruction { } JumpCondition::NotCarry => { if !flags.c() { - cpu.set_register_pair(RegisterPair::PC, addr); + Self::jump(cpu, addr); Cycle::new(12) } else { Cycle::new(8) @@ -314,14 +319,14 @@ impl Instruction { } JumpCondition::Carry => { if flags.c() { - cpu.set_register_pair(RegisterPair::PC, addr); + Self::jump(cpu, addr); Cycle::new(12) } else { Cycle::new(8) } } JumpCondition::Always => { - cpu.set_register_pair(RegisterPair::PC, addr); + Self::jump(cpu, addr); Cycle::new(12) } } @@ -329,6 +334,7 @@ impl Instruction { Instruction::ADD(target, src) => match (target, src) { (AddTarget::HL, AddSource::Group1(pair)) => { // ADD HL, r16 | Add 16-bit register to HL + // FIXME: Memory Timings are not properly emulated for this instruction use Group1RegisterPair::*; let mut flags: Flags = *cpu.flags(); @@ -359,7 +365,7 @@ impl Instruction { } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let right = cpu.read_byte(addr); + let right = Self::read_byte(&mut cpu.bus, addr); (Cycle::new(8), Self::add(left, right, &mut flags)) } }; @@ -370,10 +376,11 @@ impl Instruction { } (AddTarget::SP, AddSource::ImmediateSignedByte) => { // ADD SP, i8 | Add i8 to stack pointer + // FIXME: Memory Timings are not properly emulated for this instruction let mut flags: Flags = *cpu.flags(); let left = cpu.register_pair(RegisterPair::SP); - let sum = Self::add_u16_i8(left, cpu.imm_byte() as i8, &mut flags); + let sum = Self::add_u16_i8(left, Self::imm_byte(cpu) as i8, &mut flags); cpu.set_register_pair(RegisterPair::SP, sum); cpu.set_flags(flags); Cycle::new(16) @@ -383,7 +390,7 @@ impl Instruction { let mut flags: Flags = *cpu.flags(); let left = cpu.register(CpuRegister::A); - let sum = Self::add(left, cpu.imm_byte(), &mut flags); + let sum = Self::add(left, Self::imm_byte(cpu), &mut flags); cpu.set_register(CpuRegister::A, sum); cpu.set_flags(flags); Cycle::new(8) @@ -405,7 +412,8 @@ impl Instruction { } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - cpu.write_byte(addr, Self::inc(cpu.read_byte(addr), &mut flags)); + let left = Self::read_byte(&mut cpu.bus, addr); + Self::write_byte(&mut cpu.bus, addr, Self::inc(left, &mut flags)); Cycle::new(12) } }; @@ -415,6 +423,7 @@ impl Instruction { AllRegisters::Group1(pair) => { // INC r16 | Increment 16-bit register // Note: No flags are set with this version of the INC instruction + // FIXME: Memory Timings are not properly emulated for this instruction use Group1RegisterPair::*; match pair { @@ -443,7 +452,8 @@ impl Instruction { } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - cpu.write_byte(addr, Self::dec(cpu.read_byte(addr), &mut flags)); + let left = Self::read_byte(&mut cpu.bus, addr); + Self::write_byte(&mut cpu.bus, addr, Self::dec(left, &mut flags)); Cycle::new(12) } }; @@ -452,6 +462,7 @@ impl Instruction { } AllRegisters::Group1(pair) => { // DEC r16 | Decrement Register Pair + // FIXME: Memory Timings are not properly emulated for this instruction use Group1RegisterPair::*; match pair { @@ -578,12 +589,10 @@ impl Instruction { Instruction::HALT => { // HALT | Enter CPU low power consumption mode until interrupt occurs use HaltState::*; - let req = cpu.read_byte(0xFF0F); - let enabled = cpu.read_byte(0xFFFF); let halt_state = match *cpu.ime() { ImeState::Enabled => ImeEnabled, - _ if req & enabled != 0 => SomePending, + _ if cpu.int_request() & cpu.int_enable() != 0 => SomePending, _ => NonePending, }; cpu.halt(halt_state); @@ -604,7 +613,8 @@ impl Instruction { (Cycle::new(4), sum) } IndirectHL => { - let right = cpu.read_byte(cpu.register_pair(RegisterPair::HL)); + let addr = cpu.register_pair(RegisterPair::HL); + let right = Self::read_byte(&mut cpu.bus, addr); let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags); (Cycle::new(8), sum) } @@ -618,7 +628,7 @@ impl Instruction { let mut flags: Flags = *cpu.flags(); let left = cpu.register(CpuRegister::A); - let right = cpu.imm_byte(); + let right = Self::imm_byte(cpu); let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags); cpu.set_register(CpuRegister::A, sum); cpu.set_flags(flags); @@ -639,7 +649,8 @@ impl Instruction { (Cycle::new(4), Self::sub(left, right, &mut flags)) } IndirectHL => { - let right = cpu.read_byte(cpu.register_pair(RegisterPair::HL)); + let addr = cpu.register_pair(RegisterPair::HL); + let right = Self::read_byte(&mut cpu.bus, addr); (Cycle::new(8), Self::sub(left, right, &mut flags)) } }; @@ -652,7 +663,7 @@ impl Instruction { let mut flags: Flags = *cpu.flags(); let left = cpu.register(CpuRegister::A); - let right = cpu.imm_byte(); + let right = Self::imm_byte(cpu); cpu.set_register(CpuRegister::A, Self::sub(left, right, &mut flags)); cpu.set_flags(flags); Cycle::new(8) @@ -673,7 +684,8 @@ impl Instruction { (Cycle::new(4), diff) } IndirectHL => { - let right = cpu.read_byte(cpu.register_pair(RegisterPair::HL)); + let addr = cpu.register_pair(RegisterPair::HL); + let right = Self::read_byte(&mut cpu.bus, addr); let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags); (Cycle::new(8), diff) } @@ -687,7 +699,8 @@ impl Instruction { let mut flags: Flags = *cpu.flags(); let left = cpu.register(CpuRegister::A); - let diff = Self::sub_with_carry(left, cpu.imm_byte(), flags.c(), &mut flags); + let right = Self::imm_byte(cpu); + let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags); cpu.set_register(CpuRegister::A, diff); cpu.set_flags(flags); Cycle::new(8) @@ -704,7 +717,8 @@ impl Instruction { (Cycle::new(4), left & cpu.register(reg.cpu_register())) } IndirectHL => { - let right = cpu.read_byte(cpu.register_pair(RegisterPair::HL)); + let addr = cpu.register_pair(RegisterPair::HL); + let right = Self::read_byte(&mut cpu.bus, addr); (Cycle::new(8), left & right) } }; @@ -714,7 +728,7 @@ impl Instruction { } AluSource::ImmediateByte => { // AND u8 | Perform bitwise AND on accumulator and u8 - let acc = cpu.register(CpuRegister::A) & cpu.imm_byte(); + let acc = cpu.register(CpuRegister::A) & Self::imm_byte(cpu); cpu.set_register(CpuRegister::A, acc); cpu.update_flags(acc == 0, false, true, false); Cycle::new(8) @@ -731,7 +745,8 @@ impl Instruction { (Cycle::new(4), left ^ cpu.register(reg.cpu_register())) } IndirectHL => { - let right = cpu.read_byte(cpu.register_pair(RegisterPair::HL)); + let addr = cpu.register_pair(RegisterPair::HL); + let right = Self::read_byte(&mut cpu.bus, addr); (Cycle::new(8), left ^ right) } }; @@ -741,7 +756,7 @@ impl Instruction { } AluSource::ImmediateByte => { // XOR u8 | Perform bitwise XOR on accumulator and u8 - let acc = cpu.register(CpuRegister::A) ^ cpu.imm_byte(); + let acc = cpu.register(CpuRegister::A) ^ Self::imm_byte(cpu); cpu.set_register(CpuRegister::A, acc); cpu.update_flags(acc == 0, false, false, false); Cycle::new(8) @@ -758,7 +773,8 @@ impl Instruction { (Cycle::new(4), left | cpu.register(reg.cpu_register())) } IndirectHL => { - let right = cpu.read_byte(cpu.register_pair(RegisterPair::HL)); + let addr = cpu.register_pair(RegisterPair::HL); + let right = Self::read_byte(&mut cpu.bus, addr); (Cycle::new(8), left | right) } }; @@ -768,7 +784,7 @@ impl Instruction { } AluSource::ImmediateByte => { // OR u8 | Perform bitwise OR on accumulator and u8 - let acc = cpu.register(CpuRegister::A) | cpu.imm_byte(); + let acc = cpu.register(CpuRegister::A) | Self::imm_byte(cpu); cpu.set_register(CpuRegister::A, acc); cpu.update_flags(acc == 0, false, false, false); Cycle::new(8) @@ -788,7 +804,8 @@ impl Instruction { Cycle::new(4) } IndirectHL => { - let right = cpu.read_byte(cpu.register_pair(RegisterPair::HL)); + let addr = cpu.register_pair(RegisterPair::HL); + let right = Self::read_byte(&mut cpu.bus, addr); let _ = Self::sub(left, right, &mut flags); Cycle::new(8) } @@ -801,7 +818,7 @@ impl Instruction { let mut flags: Flags = *cpu.flags(); let left = cpu.register(CpuRegister::A); - let _ = Self::sub(left, cpu.imm_byte(), &mut flags); + let _ = Self::sub(left, Self::imm_byte(cpu), &mut flags); cpu.set_flags(flags); Cycle::new(8) } @@ -811,48 +828,57 @@ impl Instruction { let mut flags: Flags = *cpu.flags(); let left = cpu.register_pair(RegisterPair::SP); - let sum = Self::add_u16_i8(left, cpu.imm_byte() as i8, &mut flags); + let sum = Self::add_u16_i8(left, Self::imm_byte(cpu) as i8, &mut flags); cpu.set_register_pair(RegisterPair::HL, sum); cpu.set_flags(flags); + cpu.bus.clock(); // FIXME: Is this in the right place? Cycle::new(12) } Instruction::RET(cond) => { // RET cond | Return from subroutine if condition is true // RET | Return from subroutine - let flags: &Flags = cpu.flags(); + let flags: Flags = *cpu.flags(); match cond { JumpCondition::NotZero => { + cpu.bus.clock(); // internal branch decision + if !flags.z() { let addr = Self::pop(cpu); - cpu.set_register_pair(RegisterPair::PC, addr); + Self::jump(cpu, addr); Cycle::new(20) } else { Cycle::new(8) } } JumpCondition::Zero => { + cpu.bus.clock(); // internal branch decision + if flags.z() { let addr = Self::pop(cpu); - cpu.set_register_pair(RegisterPair::PC, addr); + Self::jump(cpu, addr); Cycle::new(20) } else { Cycle::new(8) } } JumpCondition::NotCarry => { + cpu.bus.clock(); // internal branch decision + if !flags.c() { let addr = Self::pop(cpu); - cpu.set_register_pair(RegisterPair::PC, addr); + Self::jump(cpu, addr); Cycle::new(20) } else { Cycle::new(8) } } JumpCondition::Carry => { + cpu.bus.clock(); // internal branch decision + if flags.c() { let addr = Self::pop(cpu); - cpu.set_register_pair(RegisterPair::PC, addr); + Self::jump(cpu, addr); Cycle::new(20) } else { Cycle::new(8) @@ -860,7 +886,7 @@ impl Instruction { } JumpCondition::Always => { let addr = Self::pop(cpu); - cpu.set_register_pair(RegisterPair::PC, addr); + Self::jump(cpu, addr); Cycle::new(16) } } @@ -880,7 +906,7 @@ impl Instruction { Instruction::RETI => { // RETI | Return from subroutine, then enable interrupts let addr = Self::pop(cpu); - cpu.set_register_pair(RegisterPair::PC, addr); + Self::jump(cpu, addr); cpu.set_ime(ImeState::Enabled); Cycle::new(16) } @@ -896,12 +922,12 @@ impl Instruction { // JP u16 | Store u16 in program counter let flags: Flags = *cpu.flags(); - let addr = cpu.imm_word(); + let addr = Self::imm_word(cpu); match cond { JumpCondition::NotZero => { if !flags.z() { - cpu.set_register_pair(RegisterPair::PC, addr); + Self::jump(cpu, addr); Cycle::new(16) } else { Cycle::new(12) @@ -909,7 +935,7 @@ impl Instruction { } JumpCondition::Zero => { if flags.z() { - cpu.set_register_pair(RegisterPair::PC, addr); + Self::jump(cpu, addr); Cycle::new(16) } else { Cycle::new(12) @@ -917,7 +943,7 @@ impl Instruction { } JumpCondition::NotCarry => { if !flags.c() { - cpu.set_register_pair(RegisterPair::PC, addr); + Self::jump(cpu, addr); Cycle::new(16) } else { Cycle::new(12) @@ -925,14 +951,14 @@ impl Instruction { } JumpCondition::Carry => { if flags.c() { - cpu.set_register_pair(RegisterPair::PC, addr); + Self::jump(cpu, addr); Cycle::new(16) } else { Cycle::new(12) } } JumpCondition::Always => { - cpu.set_register_pair(RegisterPair::PC, addr); + Self::jump(cpu, addr); Cycle::new(16) } } @@ -953,12 +979,13 @@ impl Instruction { // CALL u16 | Push PC on the stack then store u16 in program counter let flags: Flags = *cpu.flags(); - let addr = cpu.imm_word(); + let addr = Self::imm_word(cpu); let return_addr = cpu.register_pair(RegisterPair::PC); match cond { JumpCondition::NotZero => { if !flags.z() { + cpu.bus.clock(); // internal branch decision Self::push(cpu, return_addr); cpu.set_register_pair(RegisterPair::PC, addr); Cycle::new(24) @@ -968,6 +995,7 @@ impl Instruction { } JumpCondition::Zero => { if flags.z() { + cpu.bus.clock(); // internal branch decision Self::push(cpu, return_addr); cpu.set_register_pair(RegisterPair::PC, addr); Cycle::new(24) @@ -977,6 +1005,7 @@ impl Instruction { } JumpCondition::NotCarry => { if !flags.c() { + cpu.bus.clock(); // internal branch decision Self::push(cpu, return_addr); cpu.set_register_pair(RegisterPair::PC, addr); Cycle::new(24) @@ -986,6 +1015,7 @@ impl Instruction { } JumpCondition::Carry => { if flags.c() { + cpu.bus.clock(); // internal branch decision Self::push(cpu, return_addr); cpu.set_register_pair(RegisterPair::PC, addr); Cycle::new(24) @@ -994,6 +1024,7 @@ impl Instruction { } } JumpCondition::Always => { + cpu.bus.clock(); // internal branch decision Self::push(cpu, return_addr); cpu.set_register_pair(RegisterPair::PC, addr); Cycle::new(24) @@ -1004,6 +1035,8 @@ impl Instruction { // PUSH r16 | Push r16 onto the stack use Group3RegisterPair::*; + cpu.bus.clock(); // internal + match pair { BC | DE | HL | AF => { let word = cpu.register_pair(pair.as_register_pair()); @@ -1030,9 +1063,9 @@ impl Instruction { } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let byte = cpu.read_byte(addr); + let byte = Self::read_byte(&mut cpu.bus, addr); let rotated = byte.rotate_left(1); - cpu.write_byte(addr, rotated); + Self::write_byte(&mut cpu.bus, addr, rotated); (Cycle::new(16), byte >> 7, rotated) } }; @@ -1053,9 +1086,9 @@ impl Instruction { } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let byte = cpu.read_byte(addr); + let byte = Self::read_byte(&mut cpu.bus, addr); let rotated = byte.rotate_right(1); - cpu.write_byte(addr, rotated); + Self::write_byte(&mut cpu.bus, addr, rotated); (Cycle::new(16), byte & 0x01, rotated) } }; @@ -1078,9 +1111,9 @@ impl Instruction { } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let byte = cpu.read_byte(addr); + let byte = Self::read_byte(&mut cpu.bus, addr); let (rotated, carry) = Self::rl_thru_carry(byte, flags.c()); - cpu.write_byte(addr, rotated); + Self::write_byte(&mut cpu.bus, addr, rotated); (Cycle::new(16), rotated, carry) } }; @@ -1103,9 +1136,9 @@ impl Instruction { } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let byte = cpu.read_byte(addr); + let byte = Self::read_byte(&mut cpu.bus, addr); let (rotated, carry) = Self::rr_thru_carry(byte, flags.c()); - cpu.write_byte(addr, rotated); + Self::write_byte(&mut cpu.bus, addr, rotated); (Cycle::new(16), rotated, carry) } }; @@ -1126,9 +1159,9 @@ impl Instruction { } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let byte = cpu.read_byte(addr); + let byte = Self::read_byte(&mut cpu.bus, addr); let shifted = byte << 1; - cpu.write_byte(addr, shifted); + Self::write_byte(&mut cpu.bus, addr, shifted); (Cycle::new(16), (byte >> 7) & 0x01, shifted) } }; @@ -1149,9 +1182,9 @@ impl Instruction { } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let byte = cpu.read_byte(addr); + let byte = Self::read_byte(&mut cpu.bus, addr); let shifted = ((byte >> 7) & 0x01) << 7 | byte >> 1; - cpu.write_byte(addr, shifted); + Self::write_byte(&mut cpu.bus, addr, shifted); (Cycle::new(16), byte & 0x01, shifted) } }; @@ -1172,8 +1205,8 @@ impl Instruction { IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let swapped = Self::swap_bits(cpu.read_byte(addr)); - cpu.write_byte(addr, swapped); + let swapped = Self::swap_bits(Self::read_byte(&mut cpu.bus, addr)); + Self::write_byte(&mut cpu.bus, addr, swapped); (Cycle::new(16), swapped) } }; @@ -1194,9 +1227,9 @@ impl Instruction { } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let byte = cpu.read_byte(addr); + let byte = Self::read_byte(&mut cpu.bus, addr); let shifted = byte >> 1; - cpu.write_byte(addr, shifted); + Self::write_byte(&mut cpu.bus, addr, shifted); (Cycle::new(16), byte & 0x01, shifted) } }; @@ -1216,7 +1249,7 @@ impl Instruction { } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let byte = cpu.read_byte(addr); + let byte = Self::read_byte(&mut cpu.bus, addr); (Cycle::new(12), ((byte >> bit) & 0x01) == 0x01) } }; @@ -1239,8 +1272,8 @@ impl Instruction { } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let byte = cpu.read_byte(addr); - cpu.write_byte(addr, byte & !(1 << bit)); + let byte = Self::read_byte(&mut cpu.bus, addr); + Self::write_byte(&mut cpu.bus, addr, byte & !(1 << bit)); Cycle::new(16) } } @@ -1258,8 +1291,8 @@ impl Instruction { } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let byte = cpu.read_byte(addr); - cpu.write_byte(addr, byte | (1u8 << bit)); + let byte = Self::read_byte(&mut cpu.bus, addr); + Self::write_byte(&mut cpu.bus, addr, byte | (1u8 << bit)); Cycle::new(16) } } @@ -1269,14 +1302,14 @@ impl Instruction { /// PUSHes a u16 onto the stack /// - /// Mutates the stack pointer and the stack + /// Mutates the stack pointer and the stack (8 cycles) fn push(cpu: &mut Cpu, value: u16) { let mut sp = cpu.register_pair(RegisterPair::SP); sp -= 1; - cpu.write_byte(sp, (value >> 8) as u8); + Self::write_byte(&mut cpu.bus, sp, (value >> 8) as u8); sp -= 1; - cpu.write_byte(sp, value as u8); + Self::write_byte(&mut cpu.bus, sp, value as u8); cpu.set_register_pair(RegisterPair::SP, sp); } @@ -1284,12 +1317,13 @@ impl Instruction { /// POPs a u16 from the stack /// /// Mutates the stack pointer and returns the u16 which was popped from the stack + /// (8 cycles) fn pop(cpu: &mut Cpu) -> u16 { let mut sp = cpu.register_pair(RegisterPair::SP); - let low = cpu.read_byte(sp); + let low = Self::read_byte(&mut cpu.bus, sp); sp += 1; - let high = cpu.read_byte(sp); + let high = Self::read_byte(&mut cpu.bus, sp); sp += 1; cpu.set_register_pair(RegisterPair::SP, sp); @@ -1441,11 +1475,68 @@ impl Instruction { } pub(crate) fn reset(cpu: &mut Cpu, vector: u8) -> Cycle { + cpu.bus.clock(); // internal let addr = cpu.register_pair(RegisterPair::PC); Self::push(cpu, addr); cpu.set_register_pair(RegisterPair::PC, vector as u16); Cycle::new(16) } + + /// Read u8 from memory (4 cycles) + fn read_byte(bus: &mut Bus, addr: u16) -> u8 { + let byte = bus.read_byte(addr); + bus.clock(); + byte + } + + /// Write u8 to memory (4 cycles) + fn write_byte(bus: &mut Bus, addr: u16, byte: u8) { + bus.write_byte(addr, byte); + bus.clock(); + } + + /// Write u16 to memory (8 cycles) + fn write_word(bus: &mut Bus, addr: u16, word: u16) { + Self::write_byte(bus, addr, word as u8); + Self::write_byte(bus, addr + 1, (word >> 8) as u8); + } + + /// Read u16 from memory (8 cycles) + fn read_word(bus: &mut Bus, addr: u16) -> u16 { + // Must preserve the order, can't one-line this. + let low = Self::read_byte(bus, addr); + let high = Self::read_byte(bus, addr + 1); + (high as u16) << 8 | low as u16 + } + + /// Fetch u16 from memory, increment the program counter by two + /// (8 cycles) + fn imm_word(cpu: &mut Cpu) -> u16 { + let pc = cpu.register_pair(RegisterPair::PC); + let word = Self::read_word(&mut cpu.bus, pc); + cpu.set_register_pair(RegisterPair::PC, pc + 2); + + word + } + + /// Fetch u8 from memory, increment the program counter by one + /// (4 cycles) + fn imm_byte(cpu: &mut Cpu) -> u8 { + let pc = cpu.register_pair(RegisterPair::PC); + let byte = Self::read_byte(&mut cpu.bus, pc); + cpu.set_register_pair(RegisterPair::PC, pc + 1); + + byte + } + + /// Set program counter to Address. + /// + /// This is explicitly meant to emulate the exact behaviour of JP, JR RET, RETI and CALL + /// (4 cycles) + fn jump(cpu: &mut Cpu, addr: u16) { + cpu.set_register_pair(RegisterPair::PC, addr); + cpu.bus.clock(); + } } impl Instruction { diff --git a/src/ppu.rs b/src/ppu.rs index eab435f..e789ca5 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -70,7 +70,7 @@ impl BusIo for Ppu { } impl Ppu { - pub(crate) fn clock(&mut self) { + pub(crate) fn tick(&mut self) { self.cycle += 1; if !self.ctrl.lcd_enabled() { diff --git a/src/ppu/dma.rs b/src/ppu/dma.rs index fe47f78..f0afa30 100644 --- a/src/ppu/dma.rs +++ b/src/ppu/dma.rs @@ -9,7 +9,7 @@ pub(crate) struct DirectMemoryAccess { } impl DirectMemoryAccess { - pub(crate) fn clock(&mut self) -> Option<(u16, u16)> { + pub(crate) fn tick(&mut self) -> Option<(u16, u16)> { match self.state { DmaState::Pending => { self.cycle += 1; diff --git a/src/timer.rs b/src/timer.rs index c496edf..264c44c 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -5,19 +5,35 @@ pub(crate) struct Timer { /// 0xFF07 | TAC - Timer Control pub(crate) ctrl: TimerControl, /// 0xFF05 | TIMA - Timer Counter - pub(crate) counter: u8, + counter: u8, /// 0xFF06 | TMA - Timer Modulo pub(crate) modulo: u8, /// 0xFF04 | DIV - Divider Register pub(crate) divider: u16, - prev_and_result: Option, + + and_result: Option, interrupt: bool, + state: State, } impl Timer { - pub(crate) fn clock(&mut self) { + pub(crate) fn tick(&mut self) { + use State::*; use TimerSpeed::*; + if let TIMAOverflow(step) | AbortedTIMAOverflow(step) = self.state { + if step < 4 { + self.state = TIMAOverflow(step + 1); + return; + } + + if self.state == TIMAOverflow(step) { + self.counter = self.modulo; + self.interrupt = true; + } + self.state = Normal; + } + self.divider = self.divider.wrapping_add(1); // Get Bit Position @@ -29,27 +45,36 @@ impl Timer { }; let bit = (self.divider >> bit) as u8 & 0x01; - let timer_enable = self.ctrl.enabled() as u8; - let and_result = bit & timer_enable; + let new_result = bit & self.ctrl.enabled() as u8; - if let Some(0x01) = self.prev_and_result { - if and_result == 0x00 { + if let Some(0x01) = self.and_result { + if new_result == 0x00 { // Falling Edge, increase TIMA Register - self.increment_tima(); + self.inc_counter(); } } - self.prev_and_result = Some(and_result); + self.and_result = Some(new_result); } - fn increment_tima(&mut self) { - let (result, did_overflow) = self.counter.overflowing_add(1); + /// 0xFF05 | TIMA - Timer Counter + pub(crate) fn tima(&self) -> u8 { + self.counter + } - self.counter = if did_overflow { - self.interrupt = true; - self.modulo - } else { - result + /// 0xFF05 | TIMA - Timer Counter + pub(crate) fn set_tima(&mut self, byte: u8) { + use State::*; + + match self.state { + Normal => self.counter = byte, + TIMAOverflow(step) => { + if step < 4 { + self.counter = byte; + self.state = AbortedTIMAOverflow(step); + } + } + AbortedTIMAOverflow(_) => self.counter = byte, } } @@ -60,6 +85,15 @@ impl Timer { pub(crate) fn set_interrupt(&mut self, value: bool) { self.interrupt = value; } + + fn inc_counter(&mut self) { + let (sum, did_overflow) = self.counter.overflowing_add(1); + self.counter = if did_overflow { 0 } else { sum }; + + if did_overflow { + self.state = State::TIMAOverflow(0); + } + } } impl Default for Timer { @@ -70,7 +104,8 @@ impl Default for Timer { modulo: 0, divider: 0, interrupt: false, - prev_and_result: None, + and_result: None, + state: State::Normal, } } } @@ -132,3 +167,10 @@ impl From for u8 { ctrl.0 } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum State { + TIMAOverflow(u8), + AbortedTIMAOverflow(u8), + Normal, +} diff --git a/src/work_ram.rs b/src/work_ram.rs index c1118d9..f553a63 100644 --- a/src/work_ram.rs +++ b/src/work_ram.rs @@ -28,48 +28,25 @@ impl Default for WorkRam { } } -#[derive(Debug, Clone, Copy)] -enum BankNumber { - One = 1, - Two = 2, - Three = 3, - Four = 4, - Five = 5, - Six = 6, - Seven = 7, -} - #[derive(Debug)] pub(crate) struct VariableWorkRam { - current: BankNumber, - bank_n: Box<[[u8; VARIABLE_WORK_RAM_SIZE]; 7]>, // 4K for Variable amount of Banks (Banks 1 -> 7) in Game Boy Colour + buf: Box<[u8; VARIABLE_WORK_RAM_SIZE]>, // 4K for Variable amount of Banks (Banks 1 -> 7) in Game Boy Colour } impl Default for VariableWorkRam { fn default() -> Self { Self { - current: BankNumber::One, - bank_n: Box::new([[0u8; VARIABLE_WORK_RAM_SIZE]; 7]), + buf: Box::new([0u8; VARIABLE_WORK_RAM_SIZE]), } } } -impl VariableWorkRam { - fn set_current_bank(&mut self, bank: BankNumber) { - self.current = bank; - } - - fn get_current_bank(&self) -> &BankNumber { - &self.current - } -} - impl BusIo for VariableWorkRam { fn write_byte(&mut self, addr: u16, byte: u8) { - self.bank_n[self.current as usize][addr as usize - VARIABLE_WORK_RAM_START_ADDRESS] = byte; + self.buf[addr as usize - VARIABLE_WORK_RAM_START_ADDRESS] = byte; } fn read_byte(&self, addr: u16) -> u8 { - self.bank_n[self.current as usize][addr as usize - VARIABLE_WORK_RAM_START_ADDRESS] + self.buf[addr as usize - VARIABLE_WORK_RAM_START_ADDRESS] } }