feat: clock bus on instruction read-write
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
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.
This commit is contained in:
parent
0637b771e3
commit
8625bec059
|
@ -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;
|
||||
|
||||
|
|
|
@ -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<T> {
|
||||
inner: RingBuffer<T>,
|
||||
|
|
22
src/bus.rs
22
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),
|
||||
|
|
133
src/cpu.rs
133
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
76
src/timer.rs
76
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<u8>,
|
||||
|
||||
and_result: Option<u8>,
|
||||
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<TimerControl> for u8 {
|
|||
ctrl.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum State {
|
||||
TIMAOverflow(u8),
|
||||
AbortedTIMAOverflow(u8),
|
||||
Normal,
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue