feat: majorly improved PPU
This commit is contained in:
parent
ce077f9069
commit
85d61764e8
12 changed files with 1092 additions and 623 deletions
|
@ -3,7 +3,7 @@ mod interrupts;
|
|||
mod joypad;
|
||||
pub mod mapper;
|
||||
mod memory;
|
||||
mod ppu;
|
||||
pub mod ppu;
|
||||
mod serial;
|
||||
mod sound;
|
||||
mod timer;
|
||||
|
@ -25,18 +25,16 @@ use self::{
|
|||
pub struct DmaState {
|
||||
pub base: u8,
|
||||
pub remaining_cycles: u8,
|
||||
pub remaining_delay: u8,
|
||||
}
|
||||
|
||||
impl DmaState {
|
||||
pub fn new() -> Self {
|
||||
Self { base: 0, remaining_cycles: 0, remaining_delay: 0 }
|
||||
Self { base: 0, remaining_cycles: 0 }
|
||||
}
|
||||
|
||||
pub fn init_request(&mut self, base: u8) {
|
||||
self.base = base;
|
||||
self.remaining_cycles = 0xA0;
|
||||
self.remaining_delay = 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,6 +133,8 @@ pub struct Gameboy {
|
|||
pub stop: bool,
|
||||
|
||||
pub pc_history: RingBuffer<u16, 0x200>,
|
||||
|
||||
pub tick_count: u8,
|
||||
}
|
||||
|
||||
impl Gameboy {
|
||||
|
@ -147,7 +147,7 @@ impl Gameboy {
|
|||
joypad: Joypad::new(),
|
||||
serial: Serial::new(),
|
||||
dma: DmaState::new(),
|
||||
ppu: Ppu::new(),
|
||||
ppu: Ppu::new(bootrom.is_some()),
|
||||
registers: match bootrom.is_some() {
|
||||
true => Registers::default(),
|
||||
false => Registers::post_rom(),
|
||||
|
@ -164,6 +164,7 @@ impl Gameboy {
|
|||
used_halt_bug: false,
|
||||
stop: false,
|
||||
pc_history: RingBuffer::new(),
|
||||
tick_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,6 +189,9 @@ impl Gameboy {
|
|||
}
|
||||
|
||||
pub fn load_cartridge(&mut self, bytes: Vec<u8>) {
|
||||
if bytes.len() < 0x150 {
|
||||
panic!("Bad cartridge (len < 0x150)");
|
||||
}
|
||||
match bytes[0x147] {
|
||||
0 => self.cartridge = Some(Box::new(NoMBC::new(bytes))),
|
||||
1 => self.cartridge = Some(Box::new(MBC1::new(bytes))),
|
||||
|
@ -201,173 +205,217 @@ impl Gameboy {
|
|||
log::info!("\n-- Registers --\nAF: {:04X}\nBC: {:04X}\nDE: {:04X}\nHL: {:04X}\nSP: {:04X}\nPC: {:04X}\nZero: {}\nSubtract: {}\nHalf-Carry: {}\nCarry: {}\n-- Interrupts --\nIME: {}\nIE VBlank: {}\nIE LCD Stat: {}\nIE Timer: {}\nIE Serial: {}\nIE Joypad: {}\nIF VBlank: {}\nIF LCD Stat: {}\nIF Timer: {}\nIF Serial: {}\nIF Joypad: {}\n", self.registers.get_af(), self.registers.get_bc(), self.registers.get_de(), self.registers.get_hl(), self.registers.get_sp(), self.registers.pc, self.registers.get_zero(), self.registers.get_subtract(), self.registers.get_half_carry(), self.registers.get_carry(), self.interrupts.ime, self.interrupts.read_ie_vblank(), self.interrupts.read_ie_lcd_stat(), self.interrupts.read_ie_timer(), self.interrupts.read_ie_serial(), self.interrupts.read_ie_joypad(), self.interrupts.read_if_vblank(), self.interrupts.read_if_lcd_stat(), self.interrupts.read_if_timer(), self.interrupts.read_if_serial(), self.interrupts.read_if_joypad());
|
||||
}
|
||||
|
||||
pub fn tick_4(&mut self) -> (bool, Option<i64>) {
|
||||
let mut request_redraw = false;
|
||||
let mut debug_time = None;
|
||||
for _ in 0..4 {
|
||||
let (t_request_redraw, t_debug_time) = self.tick();
|
||||
request_redraw |= t_request_redraw;
|
||||
if t_debug_time.is_some() {
|
||||
assert!(debug_time.is_none());
|
||||
debug_time = t_debug_time;
|
||||
}
|
||||
}
|
||||
|
||||
(request_redraw, debug_time)
|
||||
}
|
||||
|
||||
pub fn tick(&mut self) -> (bool, Option<i64>) {
|
||||
if self.breakpoints[self.registers.pc as usize] && !self.single_step {
|
||||
self.single_step = true;
|
||||
log::info!("Breakpoint hit @ {:#X}", self.registers.pc);
|
||||
}
|
||||
if self.tick_count == 0 {
|
||||
if self.breakpoints[self.registers.pc as usize] && !self.single_step {
|
||||
self.single_step = true;
|
||||
log::info!("Breakpoint hit @ {:#X}", self.registers.pc);
|
||||
}
|
||||
|
||||
let mut diff = None;
|
||||
let mut diff = None;
|
||||
|
||||
if self.trigger_bp || (self.single_step && self.registers.cycle == 0) {
|
||||
let entered_step = chrono::Utc::now();
|
||||
self.trigger_bp = false;
|
||||
self.single_step = true;
|
||||
let mut input = String::new();
|
||||
let mut exit = true;
|
||||
match std::io::stdin().read_line(&mut input) {
|
||||
Ok(_) => {
|
||||
let lower = input.trim_end().to_lowercase();
|
||||
let (lhs, rhs) = lower.split_once(' ').unwrap_or_else(|| (lower.as_str(), ""));
|
||||
match lhs {
|
||||
"read" => match u16::from_str_radix(rhs, 16) {
|
||||
Ok(address) => {
|
||||
let res = self.internal_cpu_read_u8(address);
|
||||
log::info!("{:#X}: {:#X} ({:#b})", address, res, res);
|
||||
}
|
||||
Err(_) => log::error!("Failed to parse input as hex u16 (f.ex 420C)"),
|
||||
},
|
||||
"regs" => self.log_state(),
|
||||
"op" => {
|
||||
self.log_next_opcode();
|
||||
}
|
||||
"bp" => match u16::from_str_radix(rhs, 16) {
|
||||
Ok(address) => {
|
||||
let bp = &mut self.breakpoints[address as usize];
|
||||
*bp = !*bp;
|
||||
match *bp {
|
||||
true => log::info!("Set breakpoint @ {:#X}", address),
|
||||
false => log::info!("Cleared breakpoint @ {:#X}", address),
|
||||
if self.trigger_bp || (self.single_step && self.registers.cycle == 0) {
|
||||
let entered_step = chrono::Utc::now();
|
||||
self.trigger_bp = false;
|
||||
self.single_step = true;
|
||||
let mut input = String::new();
|
||||
let mut exit = true;
|
||||
match std::io::stdin().read_line(&mut input) {
|
||||
Ok(_) => {
|
||||
let lower = input.trim_end().to_lowercase();
|
||||
let (lhs, rhs) =
|
||||
lower.split_once(' ').unwrap_or_else(|| (lower.as_str(), ""));
|
||||
match lhs {
|
||||
"read" => match u16::from_str_radix(rhs, 16) {
|
||||
Ok(address) => {
|
||||
let res = self.internal_cpu_read_u8(address);
|
||||
log::info!("{:#X}: {:#X} ({:#b})", address, res, res);
|
||||
}
|
||||
Err(_) => {
|
||||
log::error!("Failed to parse input as hex u16 (f.ex 420C)")
|
||||
}
|
||||
},
|
||||
"regs" => self.log_state(),
|
||||
"op" => {
|
||||
self.log_next_opcode();
|
||||
}
|
||||
Err(_) => log::error!("Failed to parse input as hex u16 (f.ex 420C)"),
|
||||
},
|
||||
"bpr" => match u16::from_str_radix(rhs, 16) {
|
||||
Ok(address) => {
|
||||
let bp = &mut self.mem_read_breakpoints[address as usize];
|
||||
*bp = !*bp;
|
||||
match *bp {
|
||||
true => log::info!("Set breakpoint on read @ {:#X}", address),
|
||||
false => {
|
||||
log::info!("Cleared breakpoint on read @ {:#X}", address)
|
||||
"bp" => match u16::from_str_radix(rhs, 16) {
|
||||
Ok(address) => {
|
||||
let bp = &mut self.breakpoints[address as usize];
|
||||
*bp = !*bp;
|
||||
match *bp {
|
||||
true => log::info!("Set breakpoint @ {:#X}", address),
|
||||
false => log::info!("Cleared breakpoint @ {:#X}", address),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => log::error!("Failed to parse input as hex u16 (f.ex 420C)"),
|
||||
},
|
||||
"bpw" => match u16::from_str_radix(rhs, 16) {
|
||||
Ok(address) => {
|
||||
let bp = &mut self.mem_write_breakpoints[address as usize];
|
||||
*bp = !*bp;
|
||||
match *bp {
|
||||
true => log::info!("Set breakpoint on write @ {:#X}", address),
|
||||
false => {
|
||||
log::info!("Cleared breakpoint on write @ {:#X}", address)
|
||||
Err(_) => {
|
||||
log::error!("Failed to parse input as hex u16 (f.ex 420C)")
|
||||
}
|
||||
},
|
||||
"bpr" => match u16::from_str_radix(rhs, 16) {
|
||||
Ok(address) => {
|
||||
let bp = &mut self.mem_read_breakpoints[address as usize];
|
||||
*bp = !*bp;
|
||||
match *bp {
|
||||
true => {
|
||||
log::info!("Set breakpoint on read @ {:#X}", address)
|
||||
}
|
||||
false => {
|
||||
log::info!(
|
||||
"Cleared breakpoint on read @ {:#X}",
|
||||
address
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => log::error!("Failed to parse input as hex u16 (f.ex 420C)"),
|
||||
},
|
||||
"c" => {
|
||||
self.single_step = false;
|
||||
log::info!("Continuing");
|
||||
exit = false;
|
||||
}
|
||||
"timer" => {
|
||||
println!("-- Timer Info --\n{:#?}\n-- End of Timer Info --", self.timer)
|
||||
}
|
||||
"p" | "pause" => {
|
||||
self.single_step = true;
|
||||
log::info!("Single step activated");
|
||||
exit = false;
|
||||
}
|
||||
"pch" => {
|
||||
println!("-- Start of PC History (new to old) --");
|
||||
for (idx, pc) in self.pc_history.to_vec().iter().rev().enumerate() {
|
||||
println!("{}: {:#04X}", idx + 1, pc);
|
||||
}
|
||||
println!("-- End of PC History --");
|
||||
}
|
||||
"s" | "step" | "" => {
|
||||
self.log_next_opcode();
|
||||
exit = false;
|
||||
}
|
||||
"ls" => {
|
||||
self.log_state();
|
||||
exit = false;
|
||||
}
|
||||
"dumpbgtiles" => {
|
||||
self.ppu.dump_bg_tiles();
|
||||
}
|
||||
"dumpfb" => {
|
||||
println!("Written to: {}", self.ppu.dump_fb());
|
||||
}
|
||||
"dumpoam" => {
|
||||
for x in 0..self.ppu.oam.len() {
|
||||
if x % 0x10 == 0 {
|
||||
print!("\n{:X}: ", 0xFE00 + x)
|
||||
Err(_) => {
|
||||
log::error!("Failed to parse input as hex u16 (f.ex 420C)")
|
||||
}
|
||||
|
||||
let mem_val = self.ppu.oam[x];
|
||||
print!("{:02X} ", mem_val);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
"dumpvram" => {
|
||||
for x in 0..0x200 {
|
||||
if x % 0x10 == 0 {
|
||||
print!("\n{:X}: ", 0x8000 + x)
|
||||
},
|
||||
"bpw" => match u16::from_str_radix(rhs, 16) {
|
||||
Ok(address) => {
|
||||
let bp = &mut self.mem_write_breakpoints[address as usize];
|
||||
*bp = !*bp;
|
||||
match *bp {
|
||||
true => {
|
||||
log::info!("Set breakpoint on write @ {:#X}", address)
|
||||
}
|
||||
false => {
|
||||
log::info!(
|
||||
"Cleared breakpoint on write @ {:#X}",
|
||||
address
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mem_val = self.ppu.vram[x];
|
||||
print!("{:02X} ", mem_val);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
"dumptilemap" => {
|
||||
let base = match (self.ppu.lcdc >> 3) & 0b1 == 1 {
|
||||
true => 0x1C00,
|
||||
false => 0x1800,
|
||||
};
|
||||
|
||||
for x in 0..0x400 {
|
||||
if x % 0x10 == 0 {
|
||||
print!("\n{:X}: ", 0x8000 + base + x)
|
||||
Err(_) => {
|
||||
log::error!("Failed to parse input as hex u16 (f.ex 420C)")
|
||||
}
|
||||
|
||||
let mem_val = self.ppu.vram[base + x];
|
||||
print!("{:02X} ", mem_val);
|
||||
},
|
||||
"c" => {
|
||||
self.single_step = false;
|
||||
log::info!("Continuing");
|
||||
exit = false;
|
||||
}
|
||||
println!();
|
||||
"timer" => {
|
||||
println!(
|
||||
"-- Timer Info --\n{:#?}\n-- End of Timer Info --",
|
||||
self.timer
|
||||
)
|
||||
}
|
||||
"p" | "pause" => {
|
||||
self.single_step = true;
|
||||
log::info!("Single step activated");
|
||||
exit = false;
|
||||
}
|
||||
"pch" => {
|
||||
println!("-- Start of PC History (new to old) --");
|
||||
for (idx, pc) in self.pc_history.to_vec().iter().rev().enumerate() {
|
||||
println!("{}: {:#04X}", idx + 1, pc);
|
||||
}
|
||||
println!("-- End of PC History --");
|
||||
}
|
||||
"s" | "step" | "" => {
|
||||
self.log_next_opcode();
|
||||
exit = false;
|
||||
}
|
||||
"ls" => {
|
||||
self.log_state();
|
||||
exit = false;
|
||||
}
|
||||
"dumpbgtiles" => {
|
||||
self.ppu.dump_bg_tiles();
|
||||
}
|
||||
"dumpfb" => {
|
||||
println!("Written to: {}", self.ppu.dump_fb());
|
||||
}
|
||||
"dumpoam" => {
|
||||
for x in 0..self.ppu.oam.len() {
|
||||
if x % 0x10 == 0 {
|
||||
print!("\n{:X}: ", 0xFE00 + x)
|
||||
}
|
||||
|
||||
let mem_val = self.ppu.oam[x];
|
||||
print!("{:02X} ", mem_val);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
"dumpvram" => {
|
||||
for x in 0..0x200 {
|
||||
if x % 0x10 == 0 {
|
||||
print!("\n{:X}: ", 0x8000 + x)
|
||||
}
|
||||
|
||||
let mem_val = self.ppu.vram[x];
|
||||
print!("{:02X} ", mem_val);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
"dumptilemap" => {
|
||||
let base = match (self.ppu.lcdc >> 3) & 0b1 == 1 {
|
||||
true => 0x1C00,
|
||||
false => 0x1800,
|
||||
};
|
||||
|
||||
for x in 0..0x400 {
|
||||
if x % 0x10 == 0 {
|
||||
print!("\n{:X}: ", 0x8000 + base + x)
|
||||
}
|
||||
|
||||
let mem_val = self.ppu.vram[base + x];
|
||||
print!("{:02X} ", mem_val);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Err(stdin_err) => panic!("Failed to lock stdin: {:?}", stdin_err),
|
||||
}
|
||||
Err(stdin_err) => panic!("Failed to lock stdin: {:?}", stdin_err),
|
||||
|
||||
diff = Some((chrono::Utc::now() - entered_step).num_milliseconds());
|
||||
if exit {
|
||||
return (false, diff);
|
||||
}
|
||||
}
|
||||
if self.timer.tick() {
|
||||
self.interrupts.write_if_timer(true);
|
||||
}
|
||||
|
||||
diff = Some((chrono::Utc::now() - entered_step).num_milliseconds());
|
||||
if exit {
|
||||
return (false, diff);
|
||||
cpu::tick_cpu(self);
|
||||
let redraw_requested = self.ppu.tick(&mut self.interrupts);
|
||||
self.tick_dma();
|
||||
if self.serial.tick() {
|
||||
self.interrupts.write_if_serial(true);
|
||||
}
|
||||
self.tick_count += 1;
|
||||
(redraw_requested, diff)
|
||||
} else {
|
||||
let redraw_requested = self.ppu.tick(&mut self.interrupts);
|
||||
self.tick_count += 1;
|
||||
self.tick_count %= 4;
|
||||
(redraw_requested, None)
|
||||
}
|
||||
if self.timer.tick() {
|
||||
self.interrupts.write_if_timer(true);
|
||||
}
|
||||
|
||||
cpu::tick_cpu(self);
|
||||
let redraw_requested = self.ppu.tick(&mut self.interrupts);
|
||||
self.tick_dma();
|
||||
if self.serial.tick() {
|
||||
self.interrupts.write_if_serial(true);
|
||||
}
|
||||
(redraw_requested, diff)
|
||||
}
|
||||
|
||||
fn tick_dma(&mut self) {
|
||||
if self.dma.remaining_delay > 0 {
|
||||
self.dma.remaining_delay -= 1;
|
||||
} else if self.dma.remaining_cycles > 0 {
|
||||
self.ppu.dma_occuring = self.dma.remaining_cycles > 0;
|
||||
if self.dma.remaining_cycles > 0 {
|
||||
let offset = 0xA0 - self.dma.remaining_cycles;
|
||||
|
||||
let value = if self.dma.base <= 0x7F {
|
||||
|
@ -376,13 +424,14 @@ impl Gameboy {
|
|||
None => 0xFF,
|
||||
}
|
||||
} else if self.dma.base <= 0x9F {
|
||||
self.ppu.dma_read_vram(offset)
|
||||
let address = (((self.dma.base as usize) << 8) | offset as usize) - 0x8000;
|
||||
self.ppu.vram[address]
|
||||
} else if self.dma.base <= 0xDF {
|
||||
let address = (self.dma.base as u16) << 8 | offset as u16;
|
||||
self.memory.wram[address as usize - 0xC000]
|
||||
let address = ((self.dma.base as usize) << 8 | offset as usize) - 0xC000;
|
||||
self.memory.wram[address]
|
||||
} else if self.dma.base <= 0xFD {
|
||||
let address = (self.dma.base as u16) << 8 | offset as u16;
|
||||
self.memory.wram[address as usize - 0xE000]
|
||||
let address = ((self.dma.base as usize) << 8 | offset as usize) - 0xE000;
|
||||
self.memory.wram[address]
|
||||
} else {
|
||||
0xFF
|
||||
};
|
||||
|
@ -430,13 +479,15 @@ impl Gameboy {
|
|||
0xFF27..=0xFF2F => 0xFF,
|
||||
0xFF30..=0xFF3F => self.sound.wave_pattern_ram[address as usize - 0xFF30],
|
||||
0xFF40 => self.ppu.lcdc,
|
||||
0xFF41 => self.ppu.stat,
|
||||
0xFF41 => self.ppu.get_stat(),
|
||||
0xFF42 => self.ppu.scy,
|
||||
0xFF43 => self.ppu.scx,
|
||||
0xFF44 => self.ppu.ly,
|
||||
0xFF45 => self.ppu.lyc,
|
||||
0xFF46 => self.dma.base,
|
||||
0xFF47..=0xFF49 => 0xFF,
|
||||
0xFF47 => self.ppu.bgp.value(),
|
||||
0xFF48 => self.ppu.obp[0].value(),
|
||||
0xFF49 => self.ppu.obp[1].value(),
|
||||
0xFF4A => self.ppu.wy,
|
||||
0xFF4B => self.ppu.wx,
|
||||
0xFF4C..=0xFF4E => 0xFF, // Unused
|
||||
|
@ -489,18 +540,29 @@ impl Gameboy {
|
|||
0xFF26 => self.sound.nr52 = value,
|
||||
0xFF27..=0xFF2F => {}
|
||||
0xFF30..=0xFF3F => self.sound.wave_pattern_ram[address as usize - 0xFF30] = value,
|
||||
0xFF40 => self.ppu.lcdc = value,
|
||||
0xFF41 => self.ppu.cpu_write_stat(value),
|
||||
0xFF40 => {
|
||||
let old_value = self.ppu.lcdc;
|
||||
self.ppu.lcdc = value;
|
||||
|
||||
if value >> 7 == 0 && old_value >> 7 == 1 {
|
||||
self.ppu.stop();
|
||||
} else if value >> 7 == 1 && old_value >> 7 == 0 {
|
||||
self.ppu.start(&mut self.interrupts);
|
||||
}
|
||||
}
|
||||
0xFF41 => self.ppu.set_stat(&mut self.interrupts, value),
|
||||
0xFF42 => self.ppu.scy = value,
|
||||
0xFF43 => self.ppu.scx = value,
|
||||
0xFF44 => {} // LY is read only
|
||||
0xFF45 => self.ppu.lyc = value,
|
||||
0xFF45 => self.ppu.set_lyc(&mut self.interrupts, value),
|
||||
0xFF46 => {
|
||||
if self.dma.remaining_cycles == 0 {
|
||||
self.dma.init_request(value);
|
||||
}
|
||||
}
|
||||
0xFF47..=0xFF49 => {}
|
||||
0xFF47 => self.ppu.bgp.write_bgp(value),
|
||||
0xFF48 => self.ppu.obp[0].write_obp(value),
|
||||
0xFF49 => self.ppu.obp[1].write_obp(value),
|
||||
0xFF4A => self.ppu.wy = value,
|
||||
0xFF4B => self.ppu.wx = value,
|
||||
0xFF4C..=0xFF4E => {} // Unused
|
||||
|
|
|
@ -188,7 +188,8 @@ pub fn tick_cpu(state: &mut Gameboy) {
|
|||
}
|
||||
|
||||
if state.registers.cycle == 0 && state.interrupts.ei_queued {
|
||||
state.interrupts.cycle_passed = true;
|
||||
state.interrupts.ime = state.interrupts.ei_queued;
|
||||
state.interrupts.ei_queued = false;
|
||||
}
|
||||
|
||||
if state.registers.cycle == 0 && state.halt_bug {
|
||||
|
@ -527,12 +528,6 @@ pub fn tick_cpu(state: &mut Gameboy) {
|
|||
state.registers.pc = state.registers.pc.overflowing_add(1).0;
|
||||
}
|
||||
|
||||
if state.interrupts.cycle_passed && state.interrupts.ei_queued {
|
||||
state.interrupts.cycle_passed = false;
|
||||
state.interrupts.ei_queued = false;
|
||||
state.interrupts.ime = true;
|
||||
}
|
||||
|
||||
if result == CycleResult::Finished {
|
||||
match state.registers.opcode_bytecount {
|
||||
Some(len) => state.registers.pc = state.registers.pc.overflowing_add(len as u16).0,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
pub enum JoypadMode {
|
||||
Action,
|
||||
Direction,
|
||||
Both,
|
||||
}
|
||||
|
||||
macro_rules! joypad_input {
|
||||
|
@ -17,6 +18,7 @@ macro_rules! joypad_input {
|
|||
};
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Joypad {
|
||||
mode: JoypadMode,
|
||||
pub down: bool,
|
||||
|
@ -33,7 +35,7 @@ pub struct Joypad {
|
|||
impl Joypad {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
mode: JoypadMode::Action,
|
||||
mode: JoypadMode::Direction,
|
||||
down: false,
|
||||
up: false,
|
||||
left: false,
|
||||
|
@ -62,6 +64,7 @@ impl Joypad {
|
|||
| ((!self.left as u8) << 1)
|
||||
| (!self.right as u8)
|
||||
}
|
||||
JoypadMode::Both => 0x3F,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,10 +80,10 @@ impl Joypad {
|
|||
pub fn cpu_write(&mut self, content: u8) {
|
||||
if (content >> 5) & 0b1 == 0 {
|
||||
self.mode = JoypadMode::Action;
|
||||
}
|
||||
|
||||
if (content >> 4) & 0b1 == 0 {
|
||||
} else if (content >> 4) & 0b1 == 0 {
|
||||
self.mode = JoypadMode::Direction;
|
||||
} else {
|
||||
self.mode = JoypadMode::Both;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ impl MBC1 {
|
|||
}
|
||||
|
||||
fn is_large_rom(&self) -> bool {
|
||||
self.rom_bank_count > 4
|
||||
self.rom_bank_count >= 64
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -15,7 +15,7 @@ pub fn setup_test_emulator<const ROM_LENGTH: usize>(
|
|||
|
||||
gameboy.cartridge = Some(Box::new(cartridge));
|
||||
|
||||
gameboy.tick(); // Prefetch instruction
|
||||
gameboy.tick_4(); // Prefetch instruction
|
||||
assert!(gameboy.registers.mem_read_hold.is_some()); // Assert prefetch happened and opcode is now sitting in the memory bus
|
||||
assert_eq!(gameboy.registers.cycle, 0); // Assert tick really did just prefetch instruction and not run the opcode at
|
||||
// all
|
||||
|
|
|
@ -171,7 +171,7 @@ pub fn run_gameboy(
|
|||
}
|
||||
}
|
||||
|
||||
let (redraw_needed, time_spent_debugging) = gameboy.tick();
|
||||
let (redraw_needed, time_spent_debugging) = gameboy.tick_4();
|
||||
|
||||
if let Some(diff) = time_spent_debugging {
|
||||
goal = goal + Duration::milliseconds(diff);
|
||||
|
|
|
@ -50,9 +50,6 @@ pub enum GameboyEvent {
|
|||
Framebuffer(Vec<u8>),
|
||||
}
|
||||
|
||||
pub const FB_HEIGHT: u32 = 144;
|
||||
pub const FB_WIDTH: u32 = 160;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Keymap {
|
||||
pub down: bool,
|
||||
|
@ -108,7 +105,12 @@ pub fn run_window(
|
|||
let mut pixels = {
|
||||
let window_size = window.inner_size();
|
||||
let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
|
||||
Pixels::new(FB_WIDTH, FB_HEIGHT, surface_texture).unwrap()
|
||||
Pixels::new(
|
||||
crate::gameboy::ppu::FB_WIDTH,
|
||||
crate::gameboy::ppu::FB_HEIGHT,
|
||||
surface_texture,
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let mut redraw_happened = true;
|
||||
|
|
|
@ -9,11 +9,11 @@ macro_rules! conditional_jump_relative_testgen {
|
|||
|
||||
emulator.registers.[<set_ $flag>](false);
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x103);
|
||||
}
|
||||
|
||||
|
@ -23,9 +23,9 @@ macro_rules! conditional_jump_relative_testgen {
|
|||
|
||||
emulator.registers.[<set_ $flag>](true);
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x102);
|
||||
}
|
||||
|
||||
|
@ -35,11 +35,11 @@ macro_rules! conditional_jump_relative_testgen {
|
|||
|
||||
emulator.registers.[<set_ $flag>](true);
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x103);
|
||||
}
|
||||
|
||||
|
@ -49,9 +49,9 @@ macro_rules! conditional_jump_relative_testgen {
|
|||
|
||||
emulator.registers.[<set_ $flag>](false);
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x102);
|
||||
}
|
||||
}
|
||||
|
@ -65,11 +65,11 @@ conditional_jump_relative_testgen!(carry, 0x30, 0x38);
|
|||
fn test_jr_i8() {
|
||||
let mut emulator = setup_test_emulator([0x18, 0x1]);
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x103);
|
||||
}
|
||||
|
||||
|
@ -77,13 +77,13 @@ fn test_jr_i8() {
|
|||
fn test_jp_u16() {
|
||||
let mut emulator = setup_test_emulator([0xC3, 0xFE, 0xCA]);
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0xCAFE);
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ fn test_jp_hl() {
|
|||
|
||||
emulator.registers.set_hl(0xCAFE);
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0xCAFE);
|
||||
}
|
||||
|
||||
|
@ -106,13 +106,13 @@ macro_rules! conditional_jump_testgen {
|
|||
|
||||
emulator.registers.[<set_ $flag>](false);
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0xCAFE);
|
||||
}
|
||||
|
||||
|
@ -122,11 +122,11 @@ macro_rules! conditional_jump_testgen {
|
|||
|
||||
emulator.registers.[<set_ $flag>](true);
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x103);
|
||||
}
|
||||
|
||||
|
@ -136,13 +136,13 @@ macro_rules! conditional_jump_testgen {
|
|||
|
||||
emulator.registers.[<set_ $flag>](true);
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0xCAFE);
|
||||
}
|
||||
|
||||
|
@ -152,11 +152,11 @@ macro_rules! conditional_jump_testgen {
|
|||
|
||||
emulator.registers.[<set_ $flag>](false);
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x103);
|
||||
}
|
||||
}
|
||||
|
@ -172,19 +172,19 @@ fn test_call_u16() {
|
|||
|
||||
let orignal_sp = emulator.registers.sp;
|
||||
|
||||
emulator.tick(); // <-- Read first u8
|
||||
emulator.tick_4(); // <-- Read first u8
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick(); // <-- Read second u8
|
||||
emulator.tick_4(); // <-- Read second u8
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick(); // <-- Push next instruction PC hi to stack
|
||||
emulator.tick_4(); // <-- Push next instruction PC hi to stack
|
||||
assert_eq!(emulator.registers.sp, orignal_sp - 1);
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick(); // <-- Push next instruction PC lo to stack
|
||||
emulator.tick_4(); // <-- Push next instruction PC lo to stack
|
||||
assert_eq!(emulator.registers.sp, orignal_sp - 2);
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0xCAFE);
|
||||
|
||||
assert_eq!(emulator.debug_read_u8(orignal_sp - 1), 0x01);
|
||||
|
@ -202,19 +202,19 @@ macro_rules! conditional_call_testgen {
|
|||
|
||||
emulator.registers.[<set_ $flag>](false);
|
||||
|
||||
emulator.tick(); // <-- Read first u8
|
||||
emulator.tick_4(); // <-- Read first u8
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick(); // <-- Read second u8
|
||||
emulator.tick_4(); // <-- Read second u8
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick(); // <-- Check flag
|
||||
emulator.tick_4(); // <-- Check flag
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick(); // <-- Push next instruction PC hi to stack
|
||||
emulator.tick_4(); // <-- Push next instruction PC hi to stack
|
||||
assert_eq!(emulator.registers.sp, orignal_sp - 1);
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick(); // <-- Push next instruction PC lo to stack
|
||||
emulator.tick_4(); // <-- Push next instruction PC lo to stack
|
||||
assert_eq!(emulator.registers.sp, orignal_sp - 2);
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0xCAFE);
|
||||
|
||||
assert_eq!(emulator.debug_read_u8(orignal_sp - 1), 0x01);
|
||||
|
@ -229,11 +229,11 @@ macro_rules! conditional_call_testgen {
|
|||
|
||||
emulator.registers.[<set_ $flag>](true);
|
||||
|
||||
emulator.tick(); // <-- Read first u8
|
||||
emulator.tick_4(); // <-- Read first u8
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick(); // <-- Read second u8
|
||||
emulator.tick_4(); // <-- Read second u8
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick(); // <-- Check flag
|
||||
emulator.tick_4(); // <-- Check flag
|
||||
assert_eq!(emulator.registers.pc, 0x103);
|
||||
|
||||
assert_eq!(emulator.registers.sp, orignal_sp);
|
||||
|
@ -247,19 +247,19 @@ macro_rules! conditional_call_testgen {
|
|||
|
||||
emulator.registers.[<set_ $flag>](true);
|
||||
|
||||
emulator.tick(); // <-- Read first u8
|
||||
emulator.tick_4(); // <-- Read first u8
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick(); // <-- Read second u8
|
||||
emulator.tick_4(); // <-- Read second u8
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick(); // <-- Check flag
|
||||
emulator.tick_4(); // <-- Check flag
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick(); // <-- Push next instruction PC hi to stack
|
||||
emulator.tick_4(); // <-- Push next instruction PC hi to stack
|
||||
assert_eq!(emulator.registers.sp, orignal_sp - 1);
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick(); // <-- Push next instruction PC lo to stack
|
||||
emulator.tick_4(); // <-- Push next instruction PC lo to stack
|
||||
assert_eq!(emulator.registers.sp, orignal_sp - 2);
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0xCAFE);
|
||||
|
||||
assert_eq!(emulator.debug_read_u8(orignal_sp - 1), 0x01);
|
||||
|
@ -274,11 +274,11 @@ macro_rules! conditional_call_testgen {
|
|||
|
||||
emulator.registers.[<set_ $flag>](false);
|
||||
|
||||
emulator.tick(); // <-- Read first u8
|
||||
emulator.tick_4(); // <-- Read first u8
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick(); // <-- Read second u8
|
||||
emulator.tick_4(); // <-- Read second u8
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick(); // <-- Check flag
|
||||
emulator.tick_4(); // <-- Check flag
|
||||
assert_eq!(emulator.registers.pc, 0x103);
|
||||
|
||||
assert_eq!(emulator.registers.sp, orignal_sp);
|
||||
|
@ -301,15 +301,15 @@ fn test_ret() {
|
|||
|
||||
let orignal_sp = emulator.registers.sp;
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
assert_eq!(emulator.registers.sp, orignal_sp + 1);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
assert_eq!(emulator.registers.sp, orignal_sp + 2);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0xCAFE);
|
||||
}
|
||||
|
||||
|
@ -326,15 +326,15 @@ fn test_reti() {
|
|||
|
||||
let orignal_sp = emulator.registers.sp;
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
assert_eq!(emulator.registers.sp, orignal_sp + 1);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
assert_eq!(emulator.registers.sp, orignal_sp + 2);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0xCAFE);
|
||||
assert_eq!(emulator.interrupts.ime, true);
|
||||
}
|
||||
|
@ -355,17 +355,17 @@ macro_rules! conditional_ret_testgen {
|
|||
|
||||
let orignal_sp = emulator.registers.sp;
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
assert_eq!(emulator.registers.sp, orignal_sp + 1);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
assert_eq!(emulator.registers.sp, orignal_sp + 2);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0xCAFE);
|
||||
}
|
||||
|
||||
|
@ -382,9 +382,9 @@ macro_rules! conditional_ret_testgen {
|
|||
|
||||
let orignal_sp = emulator.registers.sp;
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x101);
|
||||
assert_eq!(emulator.registers.sp, orignal_sp);
|
||||
}
|
||||
|
@ -402,17 +402,17 @@ macro_rules! conditional_ret_testgen {
|
|||
|
||||
let orignal_sp = emulator.registers.sp;
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
assert_eq!(emulator.registers.sp, orignal_sp + 1);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
assert_eq!(emulator.registers.sp, orignal_sp + 2);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0xCAFE);
|
||||
}
|
||||
|
||||
|
@ -429,9 +429,9 @@ macro_rules! conditional_ret_testgen {
|
|||
|
||||
let orignal_sp = emulator.registers.sp;
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, 0x101);
|
||||
assert_eq!(emulator.registers.sp, orignal_sp);
|
||||
}
|
||||
|
@ -451,16 +451,16 @@ macro_rules! rst_testgen {
|
|||
|
||||
let original_sp = emulator.registers.sp;
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.sp, original_sp);
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.sp, original_sp - 1);
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.sp, original_sp - 2);
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.pc, $addr);
|
||||
|
||||
assert_eq!(emulator.debug_read_u8(original_sp - 1), 0x01);
|
||||
|
|
|
@ -10,15 +10,15 @@ macro_rules! ld_reg_imm_u16_testgen {
|
|||
emulator.registers.$hireg = 0x00;
|
||||
emulator.registers.$loreg = 0x00;
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.$hireg, 0x00);
|
||||
assert_eq!(emulator.registers.$loreg, 0x00);
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.$hireg, 0x00);
|
||||
assert_eq!(emulator.registers.$loreg, 0xFE);
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.$hireg, 0xCA);
|
||||
assert_eq!(emulator.registers.$loreg, 0xFE);
|
||||
assert_eq!(emulator.registers.pc, 0x103);
|
||||
|
@ -37,13 +37,13 @@ fn test_ld_reg_sp_imm_u16() {
|
|||
|
||||
emulator.registers.sp = 0x0000;
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.sp, 0x0000);
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.sp, 0x00FE);
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.sp, 0xCAFE);
|
||||
assert_eq!(emulator.registers.pc, 0x103);
|
||||
}
|
||||
|
@ -55,10 +55,10 @@ fn test_ld_sp_hl() {
|
|||
emulator.registers.sp = 0x0000;
|
||||
emulator.registers.set_hl(0xCAFE);
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.sp, 0x00FE);
|
||||
assert_eq!(emulator.registers.pc, 0x100);
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers.sp, 0xCAFE);
|
||||
assert_eq!(emulator.registers.pc, 0x101);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ fn test_nop() {
|
|||
state
|
||||
};
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers, expected_register_state);
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ fn test_di() {
|
|||
|
||||
emulator.interrupts.ime = true;
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers, expected_register_state);
|
||||
assert!(!emulator.interrupts.ime);
|
||||
}
|
||||
|
@ -43,11 +43,11 @@ fn test_ei() {
|
|||
|
||||
emulator.interrupts.ime = false;
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers, expected_register_state);
|
||||
assert!(!emulator.interrupts.ime);
|
||||
emulator.tick(); // <-- Execute the NOP that comes after as the `EI` instruction only takes
|
||||
// effect a cycle after
|
||||
emulator.tick_4(); // <-- Execute the NOP that comes after as the `EI` instruction only takes
|
||||
// effect a cycle after
|
||||
assert!(emulator.interrupts.ime);
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ fn test_halt() {
|
|||
|
||||
emulator.interrupts.ime = true;
|
||||
|
||||
emulator.tick();
|
||||
emulator.tick_4();
|
||||
assert_eq!(emulator.registers, expected_register_state);
|
||||
assert!(emulator.halt);
|
||||
}
|
||||
|
|
9
tests.md
Normal file
9
tests.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Passing Tests
|
||||
|
||||
* blargg/cpu_instrs.gb
|
||||
* blargg/instr_timing.gb
|
||||
* blargg/mem_timing.gb
|
||||
* mts/acceptance/ppu/intr_1_2_timing-GS.gb (VBlank intr -> OAM intr timing)
|
||||
* mts/acceptance/ppu/intr_2_0_timing.gb (VBlank intr -> HBlank intr timing) (I think this shouldn't pass, i believe i'm triggering the HBlank IRQ one CPU cycle late)
|
||||
* mts/acceptance/ppu/stat_lyc_onoff.gb (LY==LYC handling with PPU being enabled and disabled)
|
||||
* mts/acceptance/ppu/stat_irq_blocking.gb (LCD status IRQ blocking)
|
Loading…
Reference in a new issue