From d91fa7d75b5ca42eeba30fe66c8809e9a8ae3d22 Mon Sep 17 00:00:00 2001 From: Kevin Hamacher Date: Sat, 28 May 2016 14:10:36 +0200 Subject: [PATCH] Fix HL+, add cpu instr testsuite --- src/cartridge.rs | 4 +- src/cpu.rs | 1406 +++++++++-------- src/display.rs | 2 +- src/interconnect.rs | 12 - tests/cpu_instrs/cpu_instrs.gb | Bin 0 -> 65536 bytes tests/cpu_instrs/individual/01-special.gb | Bin 0 -> 32768 bytes tests/cpu_instrs/individual/02-interrupts.gb | Bin 0 -> 32768 bytes tests/cpu_instrs/individual/03-op sp,hl.gb | Bin 0 -> 32768 bytes tests/cpu_instrs/individual/04-op r,imm.gb | Bin 0 -> 32768 bytes tests/cpu_instrs/individual/05-op rp.gb | Bin 0 -> 32768 bytes tests/cpu_instrs/individual/06-ld r,r.gb | Bin 0 -> 32768 bytes .../individual/07-jr,jp,call,ret,rst.gb | Bin 0 -> 32768 bytes tests/cpu_instrs/individual/08-misc instrs.gb | Bin 0 -> 32768 bytes tests/cpu_instrs/individual/09-op r,r.gb | Bin 0 -> 32768 bytes tests/cpu_instrs/individual/10-bit ops.gb | Bin 0 -> 32768 bytes tests/cpu_instrs/individual/11-op a,(hl).gb | Bin 0 -> 32768 bytes tests/cpu_instrs/readme.txt | 119 ++ 17 files changed, 900 insertions(+), 643 deletions(-) create mode 100644 tests/cpu_instrs/cpu_instrs.gb create mode 100644 tests/cpu_instrs/individual/01-special.gb create mode 100644 tests/cpu_instrs/individual/02-interrupts.gb create mode 100644 tests/cpu_instrs/individual/03-op sp,hl.gb create mode 100644 tests/cpu_instrs/individual/04-op r,imm.gb create mode 100644 tests/cpu_instrs/individual/05-op rp.gb create mode 100644 tests/cpu_instrs/individual/06-ld r,r.gb create mode 100644 tests/cpu_instrs/individual/07-jr,jp,call,ret,rst.gb create mode 100644 tests/cpu_instrs/individual/08-misc instrs.gb create mode 100644 tests/cpu_instrs/individual/09-op r,r.gb create mode 100644 tests/cpu_instrs/individual/10-bit ops.gb create mode 100644 tests/cpu_instrs/individual/11-op a,(hl).gb create mode 100644 tests/cpu_instrs/readme.txt diff --git a/src/cartridge.rs b/src/cartridge.rs index 504459d..9b7bd9e 100644 --- a/src/cartridge.rs +++ b/src/cartridge.rs @@ -85,9 +85,9 @@ impl Cartridge { match addr { 0x0000 ... 0x3FFF => self.rom[addr as usize], 0x4000 ... 0x7FFF => { + let addr = addr - 0x4000; let abs_addr: usize = addr as usize + self.bank_no as usize * 0x4000; let val: u8 = self.rom[abs_addr]; - // println!("Access {:04X}{:04X} = {:02X}", self.bank_no, addr, val); val }, 0xA000 ... 0xBFFF => { @@ -143,7 +143,7 @@ impl Cartridge { pub fn write_byte(&mut self, addr: u16, val: u8) { match self.mbc_type { - MemoryBankControllerType::None => panic!("Cartridge: No MBC found, can not write to ROM"), + MemoryBankControllerType::None => println!("Cartridge: No MBC found, can not write to ROM ({:02X} to {:04X})", val, addr), MemoryBankControllerType::MBC3 => self.write_byte_mbc3(addr, val), _ => panic!("MBC not supported.") } diff --git a/src/cpu.rs b/src/cpu.rs index f089a19..d816933 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -36,6 +36,8 @@ pub struct CPU { ime: bool, debug: bool, + + halted: bool, } fn to_u16(bytes: Box<[u8]>) -> u16 { @@ -52,6 +54,7 @@ impl CPU { interconnect: interconnect, ime: false, // Is this correct? debug: false, + halted: false, } } @@ -125,6 +128,30 @@ impl CPU { self.clear_flag(FLAG_N); self.clear_flag(FLAG_H); }, + 0x18 ... 0x1F => { // RR + let reg_id = (instruction - 0x18) as usize; + let val = self.get_8bit_reg(reg_id); + if self.debug { + println!("RR {}", REG_NAMES[reg_id]); + } + + let carry = self.flags & FLAG_C > 0; + if !carry { + // No carry before, now we got a carry => set it + if val & 0x80 == 0x80 { + self.set_flag(FLAG_C); + } + self.set_8bit_reg(reg_id, val >> 1); + } else { + if val & 0x80 == 0 { + self.clear_flag(FLAG_C); + } + self.set_8bit_reg(reg_id, val >> 1 | 1 << 7); + } + self.clear_flag(FLAG_Z); + self.clear_flag(FLAG_N); + self.clear_flag(FLAG_H); + }, 0x30 ... 0x37 => { let reg_id = (instruction - 0x30) as usize; if self.debug { @@ -132,7 +159,15 @@ impl CPU { } let v: u8 = self.get_8bit_reg(reg_id); self.set_8bit_reg(reg_id, v << 4 | v >> 4); - } + }, + 0x38 ... 0x3F => { + let reg_id = (instruction - 0x38) as usize; + if self.debug { + println!("SRL {}", REG_NAMES[reg_id]); + } + let v: u8 = self.get_8bit_reg(reg_id); + self.set_8bit_reg(reg_id, v << 1); + }, 0x40 ... 0x47 => { // Test 0th bit let reg_id = (instruction - 0x40) as usize; @@ -362,10 +397,9 @@ impl CPU { } let val = self.get_8bit_reg(reg_id).wrapping_add(1); self.set_8bit_reg(reg_id, val); - if val == 0 { - self.flags |= FLAG_Z; - } - self.flags &= !FLAG_N; + self.set_clear_flag(FLAG_Z, val == 0); + self.set_clear_flag(FLAG_C, val == 0); + self.clear_flag(FLAG_N); 4 } @@ -382,12 +416,10 @@ impl CPU { } let val = self.get_8bit_reg(reg_id).wrapping_sub(1); self.set_8bit_reg(reg_id, val); - if val == 0 { - self.flags |= FLAG_Z; - } else { - self.flags &= !FLAG_Z; - } - self.flags |= FLAG_N; + + self.set_clear_flag(FLAG_Z, val == 0); + self.set_clear_flag(FLAG_C, val == 255); + self.set_flag(FLAG_N); 4 } @@ -409,6 +441,14 @@ impl CPU { self.flags &= !flag; } + fn set_clear_flag(&mut self, flag: u8, dep: bool) { + if dep { + self.set_flag(flag); + } else { + self.clear_flag(flag); + } + } + fn int_(&mut self, val: u8){ if self.debug { println!("INT {:02X}", val); @@ -429,6 +469,16 @@ impl CPU { 16 } + fn handle_interrupt(&mut self, offset: u8, flag: u8) { + // Remove interrupt requested flag + let new_flag = self.interconnect.read_byte(0xFF0F) & !flag; + self.interconnect.write_byte(0xFF0F, new_flag); + + // Run interrupt handler + self.int_(offset); + self.halted = false; + } + pub fn run_instruction(&mut self) { // self.debug = !self.interconnect.is_boot_rom(); // Check for interrupts. @@ -441,645 +491,745 @@ impl CPU { if e_pending & interconnect::INTERRUPT_DISPLAY_VBLANK > 0 { println!("Handling vblank interrupt"); - self.interconnect.write_byte(0xFF0F, pending & !interconnect::INTERRUPT_DISPLAY_VBLANK); - self.int_(0x40); - return; + self.handle_interrupt(0x40, interconnect::INTERRUPT_DISPLAY_VBLANK); } else if e_pending & interconnect::INTERRUPT_DISPLAY_STAT > 0 { println!("Handling display stat interrupt"); - self.interconnect.write_byte(0xFF0F, pending & !interconnect::INTERRUPT_DISPLAY_STAT); - self.int_(0x48); - return; + self.handle_interrupt(0x40, interconnect::INTERRUPT_DISPLAY_STAT); } else if e_pending > 0 { panic!("Unknown pending interrupt: {:02X}", e_pending); } } - // We need to double-check the flags - let instruction = self.read_byte(self.ip); - if self.debug { - print!("{:#04x}: [SP: {:#04X}] i={:02X}. ", &self.ip, &self.sp, &instruction); - for i in 0 .. 6 { - print!("{}: {:02X} ", REG_NAMES[i], self.get_8bit_reg(i)); + + let mut cycles: u8 = 1; + if !self.halted { + // We need to double-check the flags + let instruction = self.read_byte(self.ip); + if self.debug { + print!("{:#04x}: [SP: {:#04X}] i={:02X}. ", &self.ip, &self.sp, &instruction); + for i in 0 .. 6 { + print!("{}: {:02X} ", REG_NAMES[i], self.get_8bit_reg(i)); + } + print!("A: {:02X} ", self.regs[REG_A]); + print!("I: {:02X} ", self.interconnect.read_byte(0xFFFF)); + + // Flags + print!("Z={} ", self.flags & FLAG_Z != 0); + print!("N={} ", self.flags & FLAG_N != 0); + print!("H={} ", self.flags & FLAG_H != 0); + print!("C={} ", self.flags & FLAG_C != 0); } - print!("A: {:02X} ", self.regs[REG_A]); - print!("I: {:02X} ", self.interconnect.read_byte(0xFFFF)); - // Flags - print!("Z={} ", self.flags & FLAG_Z != 0); - print!("N={} ", self.flags & FLAG_N != 0); - print!("H={} ", self.flags & FLAG_H != 0); - print!("C={} ", self.flags & FLAG_C != 0); - } + self.ip = self.ip.wrapping_add(1); - self.ip = self.ip.wrapping_add(1); - - let cycles: u8 = match instruction { - 0x00 => { - if self.debug { - println!("NOP"); - } - 4 - }, - 0x01 => self.ld_rr_vv(REG_N_B, REG_N_C), - 0x02 => self.ld_dref_rr_a(REG_N_B, REG_N_C), - 0x04 => self.reg_inc(REG_N_B), - 0x05 => self.reg_dec(REG_N_B), - 0x06 => self.ld_r_v(REG_N_B), - - 0x08 => { - let a: u16 = to_u16(self.load_args(2)); - if self.debug{ - println!("LD ({:04X}), sp", a); - } - self.interconnect.write_word(a, self.sp); - 20 - } - 0x09 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_B, REG_N_C), - 0x0B => self.dec_rr(REG_N_B, REG_N_C), - 0x0C => self.reg_inc(REG_N_C), - 0x0D => self.reg_dec(REG_N_C), - 0x0E => self.ld_r_v(REG_N_C), - 0x11 => self.ld_rr_vv(REG_N_D, REG_N_E), - 0x12 => self.ld_dref_rr_a(REG_D, REG_E), - 0x13 => self.inc_rr(REG_D, REG_E), - 0x14 => self.reg_inc(REG_N_D), - 0x15 => self.reg_dec(REG_N_D), - 0x16 => self.ld_r_v(REG_N_D), - 0x17 => { - if self.debug { - println!("RLA"); - } - let carry = self.flags & FLAG_C > 0; - if !carry { - // No carry before, now we got a carry => set it - if self.regs[REG_A] & 0x80 == 0x80 { - self.set_flag(FLAG_C); + cycles = match instruction { + 0x00 => { + if self.debug { + println!("NOP"); } - self.regs[REG_A] = self.regs[REG_A] << 1; - } else { - if self.regs[REG_A] & 0x80 == 0 { - self.clear_flag(FLAG_C); + 4 + }, + 0x01 => self.ld_rr_vv(REG_N_B, REG_N_C), + 0x02 => self.ld_dref_rr_a(REG_N_B, REG_N_C), + 0x03 => self.inc_rr(REG_N_B, REG_N_C), + 0x04 => self.reg_inc(REG_N_B), + 0x05 => self.reg_dec(REG_N_B), + 0x06 => self.ld_r_v(REG_N_B), + + 0x08 => { + let a: u16 = to_u16(self.load_args(2)); + if self.debug{ + println!("LD ({:04X}), sp", a); } - self.regs[REG_A] = self.regs[REG_A] << 1 | 1; - } - self.clear_flag(FLAG_Z); - self.clear_flag(FLAG_N); - self.clear_flag(FLAG_H); - 4 - }, - 0x18 => { - let args = self.load_args(1); - if self.debug { - println!("JR {:02X}", args[0] as i8); - } - let off: i8 = args[0] as i8; - if off < 0 { - self.ip -= (-off) as u16; - } else { - self.ip += off as u16; - } - 12 - }, - 0x19 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_D, REG_N_E), - 0x1A => { - if self.debug { - println!("LD A, (DE)"); - } - self.regs[REG_A] = self.interconnect.read_byte(self.get_pair_value(REG_D, REG_E)); - 8 - }, - 0x1B => self.dec_rr(REG_N_D, REG_N_E), - 0x1C => self.reg_inc(REG_N_E), - 0x1D => self.reg_dec(REG_N_E), - 0x1E => self.ld_r_v(REG_N_E), - 0x20 => { - let args = self.load_args(1); - if self.debug { - println!("JR NZ {:02x}", args[0] as i8); - } - if self.flags & FLAG_Z == 0 { - let offset = args[0] as i8; - if offset < 0 { - self.ip -= (-offset) as u16 - } else { - self.ip += offset as u16; - } - 12 - } else { - 8 - } - } - 0x21 => self.ld_rr_vv(REG_N_H, REG_N_L), - 0x22 => { - if self.debug { - println!("LD (HL+), A"); - } - let addr: u16 = self.get_pair_value(REG_H, REG_L); - self.interconnect.write_byte(addr, self.regs[REG_A]); - self.set_pair_value(REG_H, REG_L, addr + 1); - 8 - }, - 0x23 => self.inc_rr(REG_H, REG_L), - 0x24 => self.reg_inc(REG_N_H), - 0x25 => self.reg_dec(REG_N_H), - 0x26 => self.ld_r_v(REG_N_H), - 0x28 => { - let args = self.load_args(1); - let target; - if (args[0] as i8) < 0 { - target = self.ip - (-(args[0] as i8)) as u16; - } else { - target = self.ip + args[0] as u16; - } - if self.debug { - println!("JR Z, {:04X}", target); - } - if self.flags & FLAG_Z > 0 { - self.ip = target; - 12 - } else { - 8 - } - }, - 0x29 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_H, REG_N_L), - 0x2A => { - if self.debug { - println!("LD A, (HL+)"); - } - let addr: u16 = self.get_pair_value(REG_H, REG_L); - self.regs[REG_A] = self.interconnect.read_byte(addr); - 8 - }, - 0x2B => self.dec_rr(REG_N_H, REG_N_L), - 0x2C => self.reg_inc(REG_N_L), - 0x2D => self.reg_dec(REG_N_L), - 0x2E => self.ld_r_v(REG_N_L), - 0x2F => { - if self.debug { - println!("CPL"); - } - self.regs[REG_A] = !self.regs[REG_A]; - 4 - }, - 0x30 => { - let args = self.load_args(1); - if self.debug { - println!("JR NC {:02x}", args[0] as i8); - } - if self.flags & FLAG_C == 0 { - let offset = args[0] as i8; - if offset < 0 { - self.ip -= (-offset) as u16 - } else { - self.ip += offset as u16; - } - 12 - } else { - 8 - } - }, - 0x31 => { - let args = self.load_args(2); - self.sp = to_u16(args); - if self.debug { - println!("LD SP, {:04x}", self.sp); - } - 12 - }, - 0x32 => { - if self.debug { - println!("LD (HL-), A"); - } - let mut addr = self.get_pair_value(REG_H, REG_L); - self.interconnect.write_byte(addr, self.regs[REG_A]); - - addr -= 1; - self.set_pair_value(REG_H, REG_L, addr); - 8 - }, - 0x36 => { - let args = self.load_args(1); - if self.debug { - println!("LD (HL), {:02x}", args[0]); - } - let addr = self.get_pair_value(REG_H, REG_L); - self.interconnect.write_byte(addr, args[0]); - 12 - }, - 0x37 => { - if self.debug { - println!("SCF"); - } - self.set_flag(FLAG_C); - 4 - }, - 0x38 => { - let args = self.load_args(1); - if self.debug { - println!("JR C {:02x}", args[0] as i8); - } - if self.flags & FLAG_C > 0 { - let offset = args[0] as i8; - if offset < 0 { - self.ip -= (-offset) as u16 - } else { - self.ip += offset as u16; - } - 12 - } else { - 8 - } - }, - 0x39 => { - if self.debug { - println!("ADD HL, SP"); - } - let sp = self.sp; - let v = self.get_pair_value(REG_N_H, REG_N_L).wrapping_add(sp); - self.set_pair_value(REG_N_H, REG_N_L, v); - 8 - } - 0x3C => self.reg_inc(REG_N_A), - 0x3D => self.reg_dec(REG_N_A), - 0x3E => self.ld_r_v(REG_N_A), - 0x3F => { - if self.debug { - println!("CCF"); - } - self.flags ^= FLAG_C; - 4 - } - - // LDs - 0x40 ... 0x47 => self.ld_r_r(REG_N_B, (instruction - 0x40) as usize), - 0x48 ... 0x4F => self.ld_r_r(REG_N_C, (instruction - 0x48) as usize), - 0x50 ... 0x57 => self.ld_r_r(REG_N_D, (instruction - 0x50) as usize), - 0x58 ... 0x5F => self.ld_r_r(REG_N_E, (instruction - 0x58) as usize), - 0x60 ... 0x67 => self.ld_r_r(REG_N_H, (instruction - 0x60) as usize), - 0x68 ... 0x6F => self.ld_r_r(REG_N_L, (instruction - 0x68) as usize), - 0x70 ... 0x75 | 0x77 => self.ld_r_r(REG_N_HL, (instruction - 0x70) as usize), - 0x78 ... 0x7F => self.ld_r_r(REG_N_A, (instruction - 0x78) as usize), - - // ADD - 0x80 ... 0x87 => { - let reg_id = (instruction - 0x80) as usize; - if self.debug { - println!("ADD {}", REG_NAMES[reg_id]); - } - self.regs[REG_A] = self.regs[REG_A].wrapping_add(self.get_8bit_reg(reg_id)); - - self.clear_flag(FLAG_N); - 4 - } - - // ADC - 0x88 ... 0x8F => { - let reg_id = (instruction - 0x88) as usize; - println!("ADC {}", REG_NAMES[reg_id]); - self.regs[REG_A] = self.regs[REG_A].wrapping_add(self.get_8bit_reg(reg_id)); - if self.flags & FLAG_C == FLAG_C { - self.regs[REG_A] = self.regs[REG_A].wrapping_add(1); - } - - self.set_flag(FLAG_N); - 4 - } - - // SUBs - 0x90 ... 0x97 => { - let reg_id = (instruction - 0x90) as usize; - if self.debug { - println!("SUB {}", REG_NAMES[reg_id]); - } - self.regs[REG_A] = self.regs[REG_A].wrapping_sub(self.get_8bit_reg(reg_id)); - self.set_flag(FLAG_N); - if self.regs[REG_A] == 0 { - self.set_flag(FLAG_Z); - } else { - self.clear_flag(FLAG_Z); - } - // TODO: H, C - 4 - } - - // SBC - 0x98 ... 0x9F => { - let reg_id = (instruction - 0x98) as usize; - if self.debug { - println!("SBC {}", REG_NAMES[reg_id]); - } - self.regs[REG_A] = self.regs[REG_A].wrapping_sub(self.get_8bit_reg(reg_id)); - if self.flags & FLAG_C == FLAG_C { - self.regs[REG_A] = self.regs[REG_A].wrapping_sub(1); - } - self.set_flag(FLAG_N); - if self.regs[REG_A] == 0 { - self.set_flag(FLAG_Z); - } else { - self.clear_flag(FLAG_Z); - } - 4 - } - - // AND - 0xA0 ... 0xA7 => { - let reg_id = (instruction - 0xA0) as usize; - if self.debug { - println!("AND {}", REG_NAMES[reg_id]); - } - self.regs[REG_A] &= self.get_8bit_reg(reg_id); - self.clear_flag(FLAG_N); - if self.regs[REG_A] == 0 { - self.set_flag(FLAG_Z); - } else { - self.clear_flag(FLAG_Z); - } - 4 - } - - // XOR - 0xA8 ... 0xAF => { - let reg_id = (instruction - 0xA8) as usize; - if self.debug { - println!("XOR {}", REG_NAMES[reg_id]); - } - self.regs[REG_A] ^= self.get_8bit_reg(reg_id); - if self.regs[REG_A] == 0 { - self.set_flag(FLAG_Z); - } else { - self.set_flag(FLAG_Z); - } - - self.clear_flag(FLAG_C); - self.clear_flag(FLAG_N); - self.clear_flag(FLAG_H); - 4 - }, - - // OR - 0xB0 ... 0xB7 => { - let reg_id = (instruction - 0xB0) as usize; - if self.debug { - println!("OR {}", REG_NAMES[reg_id]); - } - self.regs[REG_A] |= self.get_8bit_reg(reg_id); - self.clear_flag(FLAG_N); - if self.regs[REG_A] == 0 { - self.set_flag(FLAG_Z); - } else { - self.clear_flag(FLAG_Z); - } - 4 - } - - // CP - 0xB8 ... 0xBF => { - let reg_id = (instruction - 0xB8) as usize; - if self.debug { - println!("CP {}", REG_NAMES[reg_id]); - } - let val = self.get_8bit_reg(reg_id); - - self.set_flag(FLAG_N); - if self.regs[REG_A] < val { - self.set_flag(FLAG_C); - self.clear_flag(FLAG_Z); - } else if self.regs[REG_A] == val { - self.clear_flag(FLAG_C); - self.set_flag(FLAG_Z); - } else { - self.clear_flag(FLAG_C); - self.clear_flag(FLAG_Z); - } - 4 - }, - 0xC0 => { - if self.debug { - println!("RET NZ"); - } - if self.flags & FLAG_Z == 0 { - self.ret(); + self.interconnect.write_word(a, self.sp); 20 - } else { + } + 0x09 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_B, REG_N_C), + 0x0B => self.dec_rr(REG_N_B, REG_N_C), + 0x0C => self.reg_inc(REG_N_C), + 0x0D => self.reg_dec(REG_N_C), + 0x0E => self.ld_r_v(REG_N_C), + 0x11 => self.ld_rr_vv(REG_N_D, REG_N_E), + 0x12 => self.ld_dref_rr_a(REG_D, REG_E), + 0x13 => self.inc_rr(REG_D, REG_E), + 0x14 => self.reg_inc(REG_N_D), + 0x15 => self.reg_dec(REG_N_D), + 0x16 => self.ld_r_v(REG_N_D), + 0x17 => { + if self.debug { + println!("RLA"); + } + let carry = self.flags & FLAG_C > 0; + if !carry { + // No carry before, now we got a carry => set it + if self.regs[REG_A] & 0x80 == 0x80 { + self.set_flag(FLAG_C); + } + self.regs[REG_A] = self.regs[REG_A] << 1; + } else { + if self.regs[REG_A] & 0x80 == 0 { + self.clear_flag(FLAG_C); + } + self.regs[REG_A] = self.regs[REG_A] << 1 | 1; + } + self.clear_flag(FLAG_Z); + self.clear_flag(FLAG_N); + self.clear_flag(FLAG_H); + 4 + }, + 0x18 => { + let args = self.load_args(1); + if self.debug { + println!("JR {:02X}", args[0] as i8); + } + let off: i8 = args[0] as i8; + if off < 0 { + self.ip -= (-off) as u16; + } else { + self.ip += off as u16; + } + 12 + }, + 0x19 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_D, REG_N_E), + 0x1A => { + if self.debug { + println!("LD A, (DE)"); + } + self.regs[REG_A] = self.interconnect.read_byte(self.get_pair_value(REG_D, REG_E)); + 8 + }, + 0x1B => self.dec_rr(REG_N_D, REG_N_E), + 0x1C => self.reg_inc(REG_N_E), + 0x1D => self.reg_dec(REG_N_E), + 0x1E => self.ld_r_v(REG_N_E), + 0x1F => { // UNTESTED + if self.debug { + println!("RRA"); + } + let carry = self.flags & FLAG_C > 0; + if !carry { + // No carry before, now we got a carry => set it + if self.regs[REG_A] & 1 == 1 { + self.set_flag(FLAG_C); + } + self.regs[REG_A] = self.regs[REG_A] >> 1; + } else { + if self.regs[REG_A] & 1 == 0 { + self.clear_flag(FLAG_C); + } + self.regs[REG_A] = self.regs[REG_A] >> 1 | 0x80; + } + self.clear_flag(FLAG_Z); + self.clear_flag(FLAG_N); + self.clear_flag(FLAG_H); + 4 + }, + 0x20 => { + let args = self.load_args(1); + if self.debug { + println!("JR NZ {:02x}", args[0] as i8); + } + if self.flags & FLAG_Z == 0 { + let offset = args[0] as i8; + if offset < 0 { + self.ip -= (-offset) as u16 + } else { + self.ip += offset as u16; + } + 12 + } else { + 8 + } + } + 0x21 => self.ld_rr_vv(REG_N_H, REG_N_L), + 0x22 => { + if self.debug { + println!("LD (HL+), A"); + } + let addr: u16 = self.get_pair_value(REG_H, REG_L); + self.interconnect.write_byte(addr, self.regs[REG_A]); + self.set_pair_value(REG_H, REG_L, addr.wrapping_add(1)); + 8 + }, + 0x23 => self.inc_rr(REG_H, REG_L), + 0x24 => self.reg_inc(REG_N_H), + 0x25 => self.reg_dec(REG_N_H), + 0x26 => self.ld_r_v(REG_N_H), + 0x28 => { + let args = self.load_args(1); + let target; + if (args[0] as i8) < 0 { + target = self.ip - (-(args[0] as i8)) as u16; + } else { + target = self.ip + args[0] as u16; + } + if self.debug { + println!("JR Z, {:04X}", target); + } + if self.flags & FLAG_Z > 0 { + self.ip = target; + 12 + } else { + 8 + } + }, + 0x29 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_H, REG_N_L), + 0x2A => { + if self.debug { + println!("LD A, (HL+)"); + } + let addr: u16 = self.get_pair_value(REG_H, REG_L); + self.regs[REG_A] = self.interconnect.read_byte(addr); + self.set_pair_value(REG_H, REG_L, addr+1); + 8 + }, + 0x2B => self.dec_rr(REG_N_H, REG_N_L), + 0x2C => self.reg_inc(REG_N_L), + 0x2D => self.reg_dec(REG_N_L), + 0x2E => self.ld_r_v(REG_N_L), + 0x2F => { + if self.debug { + println!("CPL"); + } + self.regs[REG_A] = !self.regs[REG_A]; + 4 + }, + 0x30 => { + let args = self.load_args(1); + if self.debug { + println!("JR NC {:02x}", args[0] as i8); + } + if self.flags & FLAG_C == 0 { + let offset = args[0] as i8; + if offset < 0 { + self.ip -= (-offset) as u16 + } else { + self.ip += offset as u16; + } + 12 + } else { + 8 + } + }, + 0x31 => { + let args = self.load_args(2); + self.sp = to_u16(args); + if self.debug { + println!("LD SP, {:04x}", self.sp); + } + 12 + }, + 0x32 => { + if self.debug { + println!("LD (HL-), A"); + } + let mut addr = self.get_pair_value(REG_H, REG_L); + self.interconnect.write_byte(addr, self.regs[REG_A]); + + addr -= 1; + self.set_pair_value(REG_H, REG_L, addr); + 8 + }, + 0x35 => self.reg_dec(REG_N_HL), + 0x36 => { + let args = self.load_args(1); + if self.debug { + println!("LD (HL), {:02x}", args[0]); + } + let addr = self.get_pair_value(REG_H, REG_L); + self.interconnect.write_byte(addr, args[0]); + 12 + }, + 0x37 => { + if self.debug { + println!("SCF"); + } + self.set_flag(FLAG_C); + 4 + }, + 0x38 => { + let args = self.load_args(1); + if self.debug { + println!("JR C {:02x}", args[0] as i8); + } + if self.flags & FLAG_C > 0 { + let offset = args[0] as i8; + if offset < 0 { + self.ip -= (-offset) as u16 + } else { + self.ip += offset as u16; + } + 12 + } else { + 8 + } + }, + 0x39 => { + if self.debug { + println!("ADD HL, SP"); + } + let sp = self.sp; + let v = self.get_pair_value(REG_N_H, REG_N_L).wrapping_add(sp); + self.set_pair_value(REG_N_H, REG_N_L, v); 8 } - }, - 0xC1 => self.pop_rr(REG_B, REG_C), - 0xC2 => { - let addr: u16 = to_u16(self.load_args(2)); - if self.debug { - println!("JP NZ {:04X}", addr); + 0x3C => self.reg_inc(REG_N_A), + 0x3D => self.reg_dec(REG_N_A), + 0x3E => self.ld_r_v(REG_N_A), + 0x3F => { + if self.debug { + println!("CCF"); + } + self.flags ^= FLAG_C; + 4 } - if self.flags & FLAG_Z == 0 { + + // LDs + 0x40 ... 0x47 => self.ld_r_r(REG_N_B, (instruction - 0x40) as usize), + 0x48 ... 0x4F => self.ld_r_r(REG_N_C, (instruction - 0x48) as usize), + 0x50 ... 0x57 => self.ld_r_r(REG_N_D, (instruction - 0x50) as usize), + 0x58 ... 0x5F => self.ld_r_r(REG_N_E, (instruction - 0x58) as usize), + 0x60 ... 0x67 => self.ld_r_r(REG_N_H, (instruction - 0x60) as usize), + 0x68 ... 0x6F => self.ld_r_r(REG_N_L, (instruction - 0x68) as usize), + 0x70 ... 0x75 | 0x77 => self.ld_r_r(REG_N_HL, (instruction - 0x70) as usize), + 0x78 ... 0x7F => self.ld_r_r(REG_N_A, (instruction - 0x78) as usize), + + // HALT + 0x76 => { + if self.debug { + println!("HALT"); + } + self.halted = true; + 4 + }, + + // ADD + 0x80 ... 0x87 => { + let reg_id = (instruction - 0x80) as usize; + if self.debug { + println!("ADD {}", REG_NAMES[reg_id]); + } + self.regs[REG_A] = self.regs[REG_A].wrapping_add(self.get_8bit_reg(reg_id)); + + self.clear_flag(FLAG_N); + 4 + } + + // ADC + 0x88 ... 0x8F => { + let reg_id = (instruction - 0x88) as usize; + println!("ADC {}", REG_NAMES[reg_id]); + self.regs[REG_A] = self.regs[REG_A].wrapping_add(self.get_8bit_reg(reg_id)); + if self.flags & FLAG_C == FLAG_C { + self.regs[REG_A] = self.regs[REG_A].wrapping_add(1); + } + + self.set_flag(FLAG_N); + 4 + } + + // SUBs + 0x90 ... 0x97 => { + let reg_id = (instruction - 0x90) as usize; + if self.debug { + println!("SUB {}", REG_NAMES[reg_id]); + } + self.regs[REG_A] = self.regs[REG_A].wrapping_sub(self.get_8bit_reg(reg_id)); + self.set_flag(FLAG_N); + if self.regs[REG_A] == 0 { + self.set_flag(FLAG_Z); + } else { + self.clear_flag(FLAG_Z); + } + // TODO: H, C + 4 + } + + // SBC + 0x98 ... 0x9F => { + let reg_id = (instruction - 0x98) as usize; + if self.debug { + println!("SBC {}", REG_NAMES[reg_id]); + } + self.regs[REG_A] = self.regs[REG_A].wrapping_sub(self.get_8bit_reg(reg_id)); + if self.flags & FLAG_C == FLAG_C { + self.regs[REG_A] = self.regs[REG_A].wrapping_sub(1); + } + self.set_flag(FLAG_N); + if self.regs[REG_A] == 0 { + self.set_flag(FLAG_Z); + } else { + self.clear_flag(FLAG_Z); + } + 4 + } + + // AND + 0xA0 ... 0xA7 => { + let reg_id = (instruction - 0xA0) as usize; + if self.debug { + println!("AND {}", REG_NAMES[reg_id]); + } + self.regs[REG_A] &= self.get_8bit_reg(reg_id); + self.clear_flag(FLAG_N); + if self.regs[REG_A] == 0 { + self.set_flag(FLAG_Z); + } else { + self.clear_flag(FLAG_Z); + } + 4 + } + + // XOR + 0xA8 ... 0xAF => { + let reg_id = (instruction - 0xA8) as usize; + if self.debug { + println!("XOR {}", REG_NAMES[reg_id]); + } + self.regs[REG_A] ^= self.get_8bit_reg(reg_id); + if self.regs[REG_A] == 0 { + self.set_flag(FLAG_Z); + } else { + self.set_flag(FLAG_Z); + } + + self.clear_flag(FLAG_C); + self.clear_flag(FLAG_N); + self.clear_flag(FLAG_H); + 4 + }, + + // OR + 0xB0 ... 0xB7 => { + let reg_id = (instruction - 0xB0) as usize; + if self.debug { + println!("OR {}", REG_NAMES[reg_id]); + } + self.regs[REG_A] |= self.get_8bit_reg(reg_id); + self.clear_flag(FLAG_N); + if self.regs[REG_A] == 0 { + self.set_flag(FLAG_Z); + } else { + self.clear_flag(FLAG_Z); + } + 4 + } + + // CP + 0xB8 ... 0xBF => { + let reg_id = (instruction - 0xB8) as usize; + if self.debug { + println!("CP {}", REG_NAMES[reg_id]); + } + let val = self.get_8bit_reg(reg_id); + let rval = self.regs[REG_A]; + + self.set_flag(FLAG_N); + self.set_clear_flag(FLAG_C, rval < val); + self.set_clear_flag(FLAG_Z, rval == val); + 4 + }, + 0xC0 => { + if self.debug { + println!("RET NZ"); + } + if self.flags & FLAG_Z == 0 { + self.ret(); + 20 + } else { + 8 + } + }, + 0xC1 => self.pop_rr(REG_B, REG_C), + 0xC2 => { + let addr: u16 = to_u16(self.load_args(2)); + if self.debug { + println!("JP NZ {:04X}", addr); + } + if self.flags & FLAG_Z == 0 { + self.ip = addr; + 16 + } else { + 12 + } + }, + 0xC3 => { + let addr: u16 = to_u16(self.load_args(2)); + if self.debug { + println!("JP {:04X}", addr); + } self.ip = addr; 16 - } else { - 12 } - }, - 0xC3 => { - let addr: u16 = to_u16(self.load_args(2)); - if self.debug { - println!("JP {:04X}", addr); - } - self.ip = addr; - 16 - } - 0xC4 => { - let target = to_u16(self.load_args(2)); - if self.debug { - println!("CALL NZ {:04X}", &target); - } - if self.flags & FLAG_Z == 0 { - self.call(target); - 24 - } else { - 12 - } - }, - 0xC5 => self.push_rr(REG_B, REG_C), - 0xC6 => { - let val = self.load_args(1)[0]; - if self.debug { - println!("ADD {:02X}", val); - } - self.regs[REG_A] = self.regs[REG_A].wrapping_add(val); - 8 - }, - 0xC7 => self.rst(0x00), - 0xC8 => { - if self.debug { - println!("RET Z"); - } - if self.flags & FLAG_Z > 0 { - self.ret(); - 20 - } else { + 0xC4 => { + let target = to_u16(self.load_args(2)); + if self.debug { + println!("CALL NZ {:04X}", &target); + } + if self.flags & FLAG_Z == 0 { + self.call(target); + 24 + } else { + 12 + } + }, + 0xC5 => self.push_rr(REG_B, REG_C), + 0xC6 => { + let val = self.load_args(1)[0]; + if self.debug { + println!("ADD {:02X}", val); + } + self.regs[REG_A] = self.regs[REG_A].wrapping_add(val); 8 - } - }, - 0xC9 => { - if self.debug { - println!("RET"); - } - let new_ip: u16 = self.pop(); - self.ip = new_ip; - 16 - }, - 0xCB => { - // Prefix CB. This is annoying. - self.run_prefix_instruction(); - 12 // TODO: Verify that this is the case for all prefix instructions. - }, - 0xCC => { - let target = to_u16(self.load_args(2)); - if self.debug { - println!("CALL Z {:04X}", &target); - } - if self.flags & FLAG_Z > 0 { + }, + 0xC7 => self.rst(0x00), + 0xC8 => { + if self.debug { + println!("RET Z"); + } + if self.flags & FLAG_Z > 0 { + self.ret(); + 20 + } else { + 8 + } + }, + 0xC9 => { + if self.debug { + println!("RET"); + } + let new_ip: u16 = self.pop(); + self.ip = new_ip; + 16 + }, + 0xCB => { + // Prefix CB. This is annoying. + self.run_prefix_instruction(); + 12 // TODO: Verify that this is the case for all prefix instructions. + }, + 0xCC => { + let target = to_u16(self.load_args(2)); + if self.debug { + println!("CALL Z {:04X}", &target); + } + if self.flags & FLAG_Z > 0 { + self.call(target); + 24 + } else { + 12 + } + }, + 0xCD => { + let target = to_u16(self.load_args(2)); + if self.debug { + println!("CALL {:04X}", &target); + } self.call(target); 24 - } else { - 12 - } - }, - 0xCD => { - let target = to_u16(self.load_args(2)); - if self.debug { - println!("CALL {:04X}", &target); - } - self.call(target); - 24 - }, - 0xCF => self.rst(0x08), - 0xD1 => self.pop_rr(REG_D, REG_E), - 0xD5 => self.push_rr(REG_D, REG_E), - 0xD6 => { - let val = self.load_args(1)[0]; - if self.debug { - println!("SUB {:02X}", val); - } - self.regs[REG_A] = self.regs[REG_A].wrapping_sub(val); - 8 - }, - 0xD7 => self.rst(0x10), - 0xDF => self.rst(0x18), - 0xE0 => { - let args = self.load_args(1); - if self.debug { - println!("LDH {:02X}, A", args[0]); - } - self.interconnect.write_byte(0xFF00 + args[0] as u16, self.regs[REG_A]); - 12 - }, - 0xE1 => self.pop_rr(REG_N_H, REG_N_L), - 0xE2 => { - if self.debug { - println!("LD (C), A"); - println!("[{:04X}] = {:02X}", 0xFF00 + self.regs[REG_C] as u16, self.regs[REG_A]); - } - self.interconnect.write_byte(0xFF00 + self.regs[REG_C] as u16, self.regs[REG_A]); - 8 - }, - 0xE5 => self.push_rr(REG_H, REG_L), - 0xE6 => { - let val = self.load_args(1)[0]; - if self.debug { - println!("AND {:02X}", val); - } - self.regs[REG_A] &= val; - 8 - }, - 0xE7 => self.rst(0x20), - 0xE9 => { - if self.debug { - println!("JP (HL)"); - } - let new_ip = (self.ip as i16 + self.get_8bit_reg(REG_N_HL) as i16) as u16; - self.ip = new_ip; - 4 - }, - 0xEA => { - let addr = to_u16(self.load_args(2)); - if self.debug{ - println!("LD ({:04X}), A", addr); - } - self.interconnect.write_byte(addr, self.regs[REG_A]); - 16 - }, - 0xEF => self.rst(0x28), - 0xF0 => { - let args = self.load_args(1); - if self.debug { - println!("LDH A, {:02X}", args[0]); - } - self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + args[0] as u16); - 12 - }, - 0xF1 => self.pop_rr(REG_N_A, REG_N_F), - 0xF2 => { - if self.debug{ - println!("LD A, (C)"); - } - self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + self.regs[REG_C] as u16); - 8 - }, - 0xF3 => { - if self.debug { - println!("DI"); - } - self.ime = false; - 4 - }, - 0xF5 => self.push_rr(REG_N_A, REG_N_F), - 0xF6 => { - let val = self.load_args(1)[0]; - if self.debug { - println!("OR {:02X}", val); - } - self.regs[REG_A] |= val; - 8 - }, - 0xF7 => self.rst(0x30), - 0xFB => { - // Enable interrupts - TODO - if self.debug { - println!("EI"); - } - self.ime = true; // interrupt master enable - 4 - }, - 0xFA => { - let addr: u16 = to_u16(self.load_args(2)); - if self.debug { - println!("LD A, ({:04X})", addr); - } - let val: u8 = self.interconnect.read_byte(addr); - self.set_8bit_reg(REG_N_A, val); - 16 - }, - 0xFE => { - let args = self.load_args(1); - if self.debug { - println!("CP {:02X}", args[0]); - } - self.set_flag(FLAG_N); - if args[0] > self.regs[REG_A] { - self.set_flag(FLAG_C); - } else { - self.clear_flag(FLAG_C); - } - if args[0] == self.regs[REG_A] { - self.set_flag(FLAG_Z); - } else { - self.clear_flag(FLAG_Z); - } + }, + 0xCE => { + let arg = self.load_args(1)[0]; + if self.debug { + println!("ADC {:02X}", arg); + } + self.regs[REG_A] = self.regs[REG_A].wrapping_add(arg); + if self.flags & FLAG_C > 0 { + self.regs[REG_A] = self.regs[REG_A].wrapping_add(1); + } + if self.regs[REG_A] == 0 { + self.set_flag(FLAG_Z); + } else { + self.clear_flag(FLAG_Z); + } + self.clear_flag(FLAG_N); + // TODO: FLAG_H, FLAG_C + 8 + }, + 0xCF => self.rst(0x08), + 0xD0 => { + if self.debug { + println!("RET NC"); + } + if self.flags & FLAG_C == 0 { + self.ret(); + 20 + } else { + 8 + } + }, + 0xD1 => self.pop_rr(REG_D, REG_E), + 0xD5 => self.push_rr(REG_D, REG_E), + 0xD6 => { + let val = self.load_args(1)[0]; + if self.debug { + println!("SUB {:02X}", val); + } - // TODO H - 8 - }, - 0xFF => self.rst(0x38), - _ => panic!("Unknown instruction: {:02x}", instruction) - }; + let rval = self.regs[REG_A]; + self.set_clear_flag(FLAG_C, val > rval); + self.set_clear_flag(FLAG_Z, val == rval); + self.set_flag(FLAG_N); + + self.regs[REG_A] = rval.wrapping_sub(val); + 8 + }, + 0xD7 => self.rst(0x10), + 0xD8 => { + if self.debug { + println!("RET C"); + } + if self.flags & FLAG_C > 0 { + self.ret(); + 20 + } else { + 8 + } + } + 0xDE => { + let arg = self.load_args(1)[0]; + if self.debug { + println!("SBC {:02X}", arg); + } + self.regs[REG_A] = self.regs[REG_A].wrapping_sub(arg); + if self.flags & FLAG_C > 0 { + self.regs[REG_A] = self.regs[REG_A].wrapping_sub(1); + } + if self.regs[REG_A] == 0 { + self.set_flag(FLAG_Z); + } else { + self.clear_flag(FLAG_Z); + } + self.set_flag(FLAG_N); + // TODO: FLAG_H, FLAG_C + 8 + }, + 0xDF => self.rst(0x18), + 0xE0 => { + let args = self.load_args(1); + if self.debug { + println!("LDH {:02X}, A", args[0]); + } + self.interconnect.write_byte(0xFF00 + args[0] as u16, self.regs[REG_A]); + 12 + }, + 0xE1 => self.pop_rr(REG_N_H, REG_N_L), + 0xE2 => { + if self.debug { + println!("LD (C), A"); + println!("[{:04X}] = {:02X}", 0xFF00 + self.regs[REG_C] as u16, self.regs[REG_A]); + } + self.interconnect.write_byte(0xFF00 + self.regs[REG_C] as u16, self.regs[REG_A]); + 8 + }, + 0xE5 => self.push_rr(REG_H, REG_L), + 0xE6 => { + let val = self.load_args(1)[0]; + if self.debug { + println!("AND {:02X}", val); + } + self.regs[REG_A] &= val; + 8 + }, + 0xE7 => self.rst(0x20), + 0xE9 => { + if self.debug { + println!("JP (HL)"); + } + let new_ip = (self.ip as i16 + self.get_8bit_reg(REG_N_HL) as i16) as u16; + self.ip = new_ip; + 4 + }, + 0xEA => { + let addr = to_u16(self.load_args(2)); + if self.debug{ + println!("LD ({:04X}), A", addr); + } + self.interconnect.write_byte(addr, self.regs[REG_A]); + 16 + }, + 0xEE => { + let arg = self.load_args(1)[0]; + if self.debug { + println!("XOR {:02X}", arg); + } + self.regs[REG_A] ^= arg; + if self.regs[REG_A] == 0 { + self.set_flag(FLAG_Z); + } else { + self.clear_flag(FLAG_Z); + } + self.clear_flag(FLAG_H); + self.clear_flag(FLAG_C); + self.clear_flag(FLAG_N); + 8 + }, + 0xEF => self.rst(0x28), + 0xF0 => { + let args = self.load_args(1); + if self.debug { + println!("LDH A, {:02X}", args[0]); + } + self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + args[0] as u16); + 12 + }, + 0xF1 => self.pop_rr(REG_N_A, REG_N_F), + 0xF2 => { + if self.debug{ + println!("LD A, (C)"); + } + self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + self.regs[REG_C] as u16); + 8 + }, + 0xF3 => { + if self.debug { + println!("DI"); + } + self.ime = false; + 4 + }, + 0xF5 => self.push_rr(REG_N_A, REG_N_F), + 0xF6 => { + let val = self.load_args(1)[0]; + if self.debug { + println!("OR {:02X}", val); + } + self.regs[REG_A] |= val; + 8 + }, + 0xF7 => self.rst(0x30), + 0xFB => { + // Enable interrupts - TODO + if self.debug { + println!("EI"); + } + self.ime = true; // interrupt master enable + 4 + }, + 0xFA => { + let addr: u16 = to_u16(self.load_args(2)); + if self.debug { + println!("LD A, ({:04X})", addr); + } + let val: u8 = self.interconnect.read_byte(addr); + self.set_8bit_reg(REG_N_A, val); + 16 + }, + 0xFE => { + let args = self.load_args(1); + let rval = self.regs[REG_A]; + if self.debug { + println!("CP {:02X}", args[0]); + } + self.set_flag(FLAG_N); + self.set_clear_flag(FLAG_C, args[0] > rval); + self.set_clear_flag(FLAG_Z, args[0] == rval); + + // TODO H + 8 + }, + 0xFF => self.rst(0x38), + _ => panic!("Unknown instruction: {:02x}", instruction) + }; + } // self.dump_stack(); self.interconnect.tick(cycles); } diff --git a/src/display.rs b/src/display.rs index ffd1ab3..3002992 100644 --- a/src/display.rs +++ b/src/display.rs @@ -135,7 +135,7 @@ impl Display { self.vram[(addr - 0x8000) as usize] = val; } 0xFE00 ... 0xFE9F => { - println!("OAM: Write {:02X} to {:04X}", val, addr); + // println!("OAM: Write {:02X} to {:04X}", val, addr); self.oam[(addr - 0xFE00) as usize] = val; } 0xFF40 => self.control = val, diff --git a/src/interconnect.rs b/src/interconnect.rs index 95e0ba1..4b4aa0c 100644 --- a/src/interconnect.rs +++ b/src/interconnect.rs @@ -63,18 +63,6 @@ impl Interconnect { self.disable_bootrom == 0 } - - pub fn vblank_interrupt(&mut self) -> bool { - // This should set 0xFF0F accordingly and the CPU should - // handle the interrupt generation. So this is WRONG! - // the interrupt WAITS - if self.display.vblank_interrupt() && self.interrupt & INTERRUPT_DISPLAY_VBLANK > 0 { - true - } else { - false - } - } - pub fn read_byte(&self, addr: u16) -> u8 { // TODO: Make this more beautiful. // TODO: if some flag set, use bios, otherwise only use rom diff --git a/tests/cpu_instrs/cpu_instrs.gb b/tests/cpu_instrs/cpu_instrs.gb new file mode 100644 index 0000000000000000000000000000000000000000..7b06221b23dcd84644e7910bb8ba91a957c9ba04 GIT binary patch literal 65536 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 MfFZyT_;UpQ3qRkrD*ylh literal 0 HcmV?d00001 diff --git a/tests/cpu_instrs/individual/01-special.gb b/tests/cpu_instrs/individual/01-special.gb new file mode 100644 index 0000000000000000000000000000000000000000..ad3e9984f967b77b7ffdf768842ce3c04517d059 GIT binary patch literal 32768 zcmeI)+iw(A7y$5Zx80$Z?a~4dM`&TnrIv<M@$w=uCK3`2FB)GYyojYQGo9TnBOD?LkTIcgH`+#5Y&z&DBJTQ~SxVyN5AgC$ z=FIFj=klHNn@isc@IT+|W#Mz*8^+l>SX?y#V-STpsA*XJ+UpRze71LXdiska6BGOP z^v157oxXhJ{fY6rv_FZBU>pjr8QZ`6Q{6v4+|~eQtiEk&qx|dgI{D|M4Q4ZR*bx55 z&zvSm0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J|1AMqp1c%MZmd?| zT}pr&#L%`X4^^m7ti5y zCSPXpCX27N_$kxz`37sD5B~JBzQxO?<@iZ9;}5Zde}GN<2iX^Xie2=F*?B*k#AZVl zoAR>{Ji@N}+0O0k3g2b=XYS>9S>EiDQf)k(Ta;7mkn3kz)X(CD#j(BP7oM_Wwfre7 zr{H98CDNwq!^(}(t|W#-PGQDhySA-Z?G6_sxIhWlGQ*#*f!Q%Qf?3pUPEsm&skr9=F1Tw-yRWvJlB@(WNuEg@IFD_~FOp-tK+7 zU+NjGD10`ls%j}bId0i!C)tmcR#n)lzi*&tp%n`)Gz#i#s~_!@cRaZ9p+}+6Ty1Oc z?H{UZB0IWv$Xm8TAp}dao_~I5c76gAR7Yhm=pT_fw`_qz1HeV^4j-Kg!+2OOdk1s% zPs`th^miIwev$uT*>76e(5hdnk|$cF`OeZ?vTr%OK64%1+4{_J8z0DHb~7A`0sjWi zZ^X)Y)|MAnB6YFR)vgtrT)s2Io@|D9rRx0~)I1g%H=eOAhGQp*We23>8 z$9HdJb_6FQ-oDnR8NYd@+g4g6-{zI%)>^(b<9na9-`%^vP#K7iWpKiH#8I*{*PGm2 z4X#0xn^(Bf@;f8)4Q(Z^u7K|yX2oXM;?isR2R!GZXx!J*vSCBRO1EjnnuD0*TH*Yy z4-69v)`;Tp19W)FTnCQ>a7>kNDq@ye<;q_>vnn+`= zw4Rbgtm=A7v`>p>ih}J8v5T`NMdKo`*l(T{5Lpx^=8EZY$KRr7B@eC_^FQ0GYrt@T zv|&KXFvO16bzR4ClL^Bx5=qRjr*s|X6`Xa@biEf-0@h+T15`16rh+6k?1z2&u`e!F zY-z~Nl`iFSIWPv(Y3wNGi<=tc!v;Y-n7Aj1F{Q|_ig6H8Y$6YCK|VuO8Q@r#N^>}# zp^B-CdNGBH3+7uDbXx@-V)arOl0lq=`gkID>plRKOkKGds*;A|2fVm`5YoC>j~3*` z*hpjCsHBix^ug>A`q7 zpooPB#^S-l`zQor7>hLGzru)a;szr?N<6CpK;=5l2XAul{KObjM86DisA72iicAJRtiVPC5ef0$kIf%WY58UCBiFHDI~ zGb`PCbAn;$KmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J tBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JB=Bz&_#K5wPf-8> literal 0 HcmV?d00001 diff --git a/tests/cpu_instrs/individual/02-interrupts.gb b/tests/cpu_instrs/individual/02-interrupts.gb new file mode 100644 index 0000000000000000000000000000000000000000..20895940ad31ca0229cb4ff8bb7c7ef79ca8db10 GIT binary patch literal 32768 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 literal 0 HcmV?d00001 diff --git a/tests/cpu_instrs/individual/04-op r,imm.gb b/tests/cpu_instrs/individual/04-op r,imm.gb new file mode 100644 index 0000000000000000000000000000000000000000..58ca7b8ac3c2b16efc9c8e401e790671c00022a2 GIT binary patch literal 32768 zcmeI4ZEO?g9l-x~k|Pbqu|w1yAuneVtPr4hD}zWgk1{}@y`p91iI-s1C}m)=&Ci}PS^)m|6_7tDiNZ`%vs1o!oe z{kL!2xc1Vyb332yci*{q~E)78){-D{jD zeGra=>Vuv*I6wB8Q-VZ*2oM1xKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&IhyW2F z0z`la5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&IhyW2F z0z`la5P|t3>m;#d?jCJ;y z(NpT*EU`-esoHf)x?9lnDOH~z(_3PCdrUv8YL>nv_Rt1z3kfTxm(`eNg~hCOK+ISN z#T(Wk@tPG8-?WCrD^?1k^xKxD-l*yzZjSq=?kNHK+9yU_V%t^wK0^d!2$)W zC96Ld6P;zq=)J0jxn}io^-Is}-MeEuWEjMs8!uKH!OogBoj1jLKDU0Lf5*d_8e^a+ z7(1hG%Jj_o)`E(%V#KuN$4O6#bXy?OM>2rSBUr#FwTmp7AScq2w?mH!p0%GrK}Nw%0)h zkvx>7cI2|&euXTWYc*Ih*JfzAs*0^xdYiG+J6sl^>G-cVwl^OteOR31&o=GqtPeOXaO2HrJ}~XrpNYEHs`Z3v(ffb)Do!P z+_fG^n0+B z5|)BxFz2h%{Rg@U+jqG#k@o{&g|{*V?UW?oZv3NIRXH*&=H{Lau8qe>PJ04Q^gX@R zU)#nvetKcR^RN4UTbS?sYkNEPbnNcfzf5`~-m4DehkrHTzm^|4-eC^sNAWt6AH(Zt z6i*cWujBfb^ZKjfm2Cr%@!d;w<)$aQa(A&8C$o51+?!-uax;^-Snf~aYSfn|^^@bB z3o0fK$P}k1^_yxbEeXZD{4O(8S}L85l#9$_8(ElkdIBLL52QCyMV)>fbq^iM0=vqSBu;_VBs}j(Shq;o*Gw z@Zp+M-x&S=YewCKwf@_yyY6kMDgJiFvDoHKU4Obf)p$0ycGVk4JAV=A{mHY*l}~-S z_D9V>aDQ%k@6{uz>&dF$uk$4wt@oEBRy6qNu722&r(+5wdJ4g-;aBZTj|*pCX83~lE@ZY zjASj=z$9OjAvAufMw(#@vAPWI=!!CGOuS&OEogXTwip9`|B@x%g+@!$qC*&CI&br zlwdii;FKsv(P5Li#x2(j$dNKZ2`(*|q zI*2L?L==Vp@VP9@=r~cUaTKDX_?o>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&IhyW2F0{;a9e+RrWAo2hJ literal 0 HcmV?d00001 diff --git a/tests/cpu_instrs/individual/05-op rp.gb b/tests/cpu_instrs/individual/05-op rp.gb new file mode 100644 index 0000000000000000000000000000000000000000..1c19d922f728bd11dcd8b289813fd0409f438157 GIT binary patch literal 32768 zcmeI4Uu;v?8Nfd~$t48iI3#12{2@0ucp<>J9fC!gIm##kRUOS<)=E`rZ#B5I!%6~Z zV2I;7$;7&8539O|@lu8+wVJfSkhbwpo33MQb7w9pT|pPENv{WHnnfeK)m?)L_P%o+ z*w$`&m^7){z9XM|&UeoD=X}3=&bfIC!2fwkG`p{UpsH7v!jhVUFaaJ|3iVB$uYM1_ z?_C+ZGc|Sd_{EERzd7i=du8gq<0mej-^e~eEQYen8B;yq_{7|2Ufj_Hn%CF6tX;a_ zvQ+vwoB*v0`V-*(%&(jlNCb!g5g-CYfCvx)B0vO)01+SpM1Tko0U|&IhyW2F0z`la z5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&IhyW2F0z`la z5CI}U1c(3;_}?OskS1@`$#b1DTy?#D8T7N4ZT7xVqZPMEe|3b4u}ov{R!54u9Wi{` zD3|n4bI?U#6)R?qS>x7(ZYQtjx+W^y*FUxX>Gh`q^_jlRicD*#?0X?R^TCcIGv@}5 z&P+TrG9#^)ZU-u>4y@b1Za8}_UerhPZZndvF~wS@i*A<4yG-3KH(YvFGh8Mvfl2di zgVSb>wDuQUtTEowde2Jtin=kQ8O?E{BX0QP#sy8cjWzLyHrOpD?YL3V;<_CcbM_H2 zYabP->=E&%9TBhFqvAC?8Ai1uaWQQtw>~L;XeYPr6z>|(YIf$!#>RN@PGh;D&z)M7 zk`pe|PKq8oi33Z#yU)M7EADMDcEwXNE(S9ZHq^c*&y5X)Q9Pn&bGF~#o3AxT^L3b@ zfVt$1eR0uUfvmAp({ZgiV^aJ2iw6(x*$r6+iG7o$S~J*Fx25Nn*vRL$4Gr#jI8$d1 zl>~Ed^lZ;!v_uH_MD?=kDmSzXu?==%4C^@7i48pQNA(uIzC!Gxw5%|8L0VcEKQGl6 zCeBGUi2=A{nHwHAD)fk-`bM}qFcZ%`zOHMB6$uxLq_cp~U0<@m`aAY6_fMY8{+@er{j?R>Vg;<#RyQkR&|_B1r|{{?0ihnvd>FQg&7wOc(IAmY21y*@|1CA zvS&rr#0A;X?6h%9D`zC3bdUG4qvhq&g-AIi{W@_Xp0gKPL3VVZ+3-S>M;4kqy3pjv zLX+42b5nf@KpVhXfEs{i06)NDfE5610K8zVO?iF%Vev-+A9~lW)bswQ{W)y$5MEC{ z<=^h-1I1`IflwQhcn~39mp5Dpj!)d=(Tc^Wwjo)lP2y z>F+*wZtaQJQ+ zn+6Z#QR7y|?KCbJpXeo-D|MKuddz`IU9G0j@})XyjHQFkg)StwDu>hz_Ev3K3Q1KO9#S1^tFYs;Y*<7+;Af3a-m(6<~@oh#>)I z(M*LPU%nUwffuwxn_;xYRC$R)YNmW6l}dqnC>ljWzFsUrCmt#|@}baXN5+smevtb? zh`jJPSe$rjFen20x>24%e`=5~ozL?nRJmZhm5$j;=;zrBZU{Sa7<_%9)T8$RP}UTA zUJMEh{X2Luf8eAQo)2^4qO3+yZkHp7o!j6z6Oakw!uTTeVFY}SNFU2E!VB^I`y4Td z`gb`1Y^(a*AJ4xkM-;~&I^;6vfimBC_#Qa{FUmX&|F=urjK5$65aD~(0Z<*s_25f( z_K#N-Wp3w$M=ryn=)q;7p7P-v)vb$t0OM+k|Ft1+T0b-_9T;xle}34Oi;Y=so7MmQ zTb4MMHl9x#8#8!}17i8nxpAD(g9s1-B0vO)01+SpM1Tko0U|&IhyW2F0z`la5CI}U z1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&IhyW2F0z`la5CJ0a HUn1}~7IqB+ literal 0 HcmV?d00001 diff --git a/tests/cpu_instrs/individual/06-ld r,r.gb b/tests/cpu_instrs/individual/06-ld r,r.gb new file mode 100644 index 0000000000000000000000000000000000000000..d497bfd1275361bc847fa94dc87b43729f180b5d GIT binary patch literal 32768 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 literal 0 HcmV?d00001 diff --git a/tests/cpu_instrs/individual/07-jr,jp,call,ret,rst.gb b/tests/cpu_instrs/individual/07-jr,jp,call,ret,rst.gb new file mode 100644 index 0000000000000000000000000000000000000000..5c8d20bb3a1a0458f29afe40b8d3ca125b13a391 GIT binary patch literal 32768 zcmeI4Z){W76~K?3#H7JEf3-bALS7(vAxefRTZy#StF%K!)0(C02P#!X7!@8ZwUQ7J zIK=jxWa_$U8m%_9>pqlK>)0P{FeYsjvJZ9~+j%pOkq+1ck>t4$P%WC-pf?f&_Rf6{ ztm~$Hm^2Ah=jq*h-?{hvx##!pz0aR4DPUgG|EVo_e~9Zywe`%$vV3|OEG^m#!(fMH zP+HmW+IPWm`E>WS$;pdvkBsbkvDpE*sC2oM1xKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko z0U|&IhyW2F0z`la5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+SpM1Tko z0U|&IhyW2F0z`la5CJ0a*&+}TM=zAP?l!pKjOFY&C@01ZHmw$gGLMV@FonEY6=LTK zJsHLDsTCnVmlpmn;FB~I0a#uS>Vx`_KCBqg^D~XZ`KQ{x(YB@SsIxTDo>-o!ZE#g= z^Ui*}xqtTku7k6~&kf9qYs9P0{HngzJ*~aTiEu{or>vSURip{UOcAUslCo%ukt-W} zKBQVSoB|UU+A_0Es`1cAhESybBGht1oX;rgY)GvRtMy@Zbyz(WQVex%_`VH3W}-$| z&4=EBKfCbo6OZ@BIbc6m|kSCTWv z>eVf&V$Gi_!31rX%Z&O;Sg__Hsdk1Goa>A_8hU2?-n~0_K$1b^mCknMDrtPi4d?x^_=AlH#E}0bw+v+%Mg~~bv*G0uk(1#T%yy~wxOiK+wLU+c0*!&b1WP%VJQgPlV#(`k z*a5w%@lZnjVMq;x)$WAa6*5?n+7M#qx>5N`QGYXL=7OFKb^mB4awK8!8NVCKJ8ei%3$6GwMT!qd3D`D z?%Sf_MAw!Cw%DDAvv5$72syrM-4D?E4Qlgaa*<&5c{L!-~5(Q-7fidltK>J_G+ zyk5R5$`SNMor)O!a&*yfEiv*W|R}64R_;Tg`&cdVK4{x ztZ{8TK2pjT;aJymTb-p1eB&opxGMgA-*5Bsoqus}Q(x1brUR|w2a(QDPdfP99_PjM z;L#>Mm>$Ab%7I&tYFLiec z7t6gVT#f3pG4=Rp^YVg;1(MmhDfLPymk@2)dH$62=bjZ$`EoJw50S&+8Dp^-B>jtx zdKVk)Uu^K;VuJ&V4Nm;;rix&Za7{N`B$q{Mw@9P@Si~Ruvp@EMKX%C<8}r9b`(vs< zcFrHW?2m;c7?(hi;H(6i1Q#WU1N;TxZvfK(*8%e<*9WI7?@XPK%uPwRO0ARYd#BYyV_EAYuZzq&VD|XU+Go}SW(|`) zS*-%_yt(_W)Rt|JHpeHvCYRg}mY@2;6Cb|!leV_+_x-$j|1WpsUiuS%e&)@l zFDzD~mX&vkyvLhu=}o!UMI)@JSK{W`GBy;)Z_K`UBMdn}9l`c{jy$%kCba@j&s3ep z=2G>Vx^`T6p%H#;E5-$Xx2Z&&whBCVUS{9I0{^{~h?C5c>cYbpgP^~zyi(ac zZgWR%rM1<+TD1y2g-b;II;!j9>O)i*~v$IY%vy(6O>!_R5(S`a70Kd8<1 zGe+YIttGcad?b?-o6FSY<3{F>ZC~y_fSa3JsawWz!dRo1=uEa=i1(J^^Hp;Bv=ag}TS;+I!@$fZc94^&E9*SLcAg#qfCJGq)_n!F)0P?rt{&0UZQn8GN$LZ|rxw z-RRfrk!9KA#rSTY+l})wTQ@MbyBk9Sj-r_i5}&>xfsJ2ihc>-vi>dM}0I}KJg;*>G z^8P>o4f%ZWlFWFhV9NVFkC`%th_mC(Vnw^_k!%3i2w_rxB&2Y_72?OG5e8$8;11Q(Ie2AUf zU_Y}V6U2q_1!%_z_#TlyUcCs>Y5g-CYfCvx)B0vO)01+SpM1Tko0U|&Ih`?u;z&`;#5pP@o literal 0 HcmV?d00001 diff --git a/tests/cpu_instrs/individual/08-misc instrs.gb b/tests/cpu_instrs/individual/08-misc instrs.gb new file mode 100644 index 0000000000000000000000000000000000000000..4da139be35c0f82db12136c2b65b2571122a026b GIT binary patch literal 32768 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 literal 0 HcmV?d00001 diff --git a/tests/cpu_instrs/individual/10-bit ops.gb b/tests/cpu_instrs/individual/10-bit ops.gb new file mode 100644 index 0000000000000000000000000000000000000000..8988458ea0eff185d7f3bd78ff85bb7717edccfa GIT binary patch literal 32768 zcmeI4dvp_38oi~z4V`7uv(jXK=+9GPz|xr1ELesUV0gf$D8JyZD~1HQ(r%0 z>KxFJP~yPgDOtxSn`lru^G zR}QiOQb?^-m#I~1wXC_nYE7>WjUGI9@DqbS$cn~1?2Achrua$rwhIM|+jbQ#X{&y+ zv`ri!p2-Sj%o{X!P>J`5Ga#4y^qw+bghz;!WI->vd^(S;1!MM2a40$tr67sd`Gk<5>&2e_WqZUP{;J(# zw7+_n7~v{%C?2P4w=;*T?QZ{2yd<~Z9Xja)j|A#%)Z(axsAWl&)I#b2X_1+ro;=ew zeOe573CRsf>`z=qg_v)^|quv(Dl; zO$__#klm!7WHjZRip9CvSDf;8HS0b#OC6wQN&yP`qMGSB54TnP(5v zrmqj5d>3pldU9e`bQ;_Em~4vw$9+G^&UXH>`I+-F=VmS%B!1y4bQJq5w-sj{^Dp}# zQ?2w@(S4b}n(kE<^o^o?*PuMJPubkiwQT^r=~vx3zRM?ad{?M-oM@#li>oK3WZ$(D zv{*V%&}vlL_bVSZWcTed(Sf()@(Ja%BZ#7*;|d#j%Y*IWp0eP6affSzvsJrQ2Hx^p z&Dw7@x%gI-OKvq;daKDJ|GBA*`G6w;M*`jtI12CqzyiRZ13m~i2Jj)khXD%#ivS-1 zd=&68z+V794mb|*Nx<=dPXSHZf>z=?p904;!%0jB^?1)K&r9dHKVvw$-JX8}G3 zXak%LSPWPKI2Ujp;C#RZfX@RiqFn~K7;p(-DPS33Ip9*jO2B1+Re;riF97}u@YjGZ z0lo~l9Pl@QD*%5B_zK`kz*T^&0oMS&3iuk}8-Qy8YXE--_$J^wz~2LI0Ne=p7U0`} z?*MKB`~%>-fLj2!0@eb)2lzhVHo)zGe+2vx@FT#F0Y3rU0aypP3vf5!9>99QPXRvz z+zaRcbOJU2x&Ze9x&ikC9sra94+0`!6QBZk2=FlA5x_41zXWUs{0i`EKo8(Cz;6J* z1w0Pe0(cTo1@r=*0z3_P2Cxw|g&n1$rg&VBpGYWpOhG2GW(Zp$cs}C*x=*rfWds_@Urwv%HW_IMi)@#+|H9cl-F?Ro;^JG%g_jh+11`X?dEv0+X@QrcfT0JSVZA*70 zZdX}{Z-^nd4ju10PJbWo**XnnlKb?B1pn9UCWQj{G67=ll90FO&~z_{saH9;#LEer4muUR#Pv z*XsKWZ~kq!@tZ=U58fVKYuArCG|#(b++#0a?mRj+-$w$+odVlfsz_LJ8}Glb-SyS1#Vrxs}c4Ovkm@H z!Ih%sl$Fo@;T@O`RkS7MP`}(5qFze$CDVb&m*SBw`%*p1aTK0OhqZ=C z`i;7l86{q32Y_b{@pM#}DJ`W2|Gq_uEt1YxMCT<6LjH`LJ zKyQd8hxuT(&RJ-+nn7UpLIufeq5>*ng;)bUk4-V#DkxTkrOd!&v&B-z^edQ}$wc~A z_F=tbh|Obh+2?w10v5$2uv{rTeQ3-qyW&v26o1wniv$8WsIXe0%xYyHdTy~;$gkaI zwOVaZvn|-nL{CgX35M7W|qES1_Qguj%-TEmP%z;1vIt=k2W?of^}g< z1sSq@=`x4okwRErX!~hcrjRVYnfW1z>|$}~3dggW%>t0Gqro=vXEn3b**r_3iwni; z7dGn$`K)?_9_(S+4)J-m#+&Z}An34|u8U@aME=8gseWLjEvz0X9G7Hk1<5I@i-maso^$YjQYWVZ0= zJu(7Dl35t`zuA}>d%*;tjIGr$fUf715545@`Y{TEiP?q26PFS}h^4eh&vJ2n*V3gi zfYN@D{Yi4SY4DVp3&i4*`7!LT6iwAh%T&E4MgQ%{>v|8BYE&j1qK%v-1aIz~up55k z1ULasfD_;ZH~~(86W|0m0ZxDu-~>1UPJk2O1ULasfD_;ZH~~(86W|0m0ZxDu-~>1U lPJk2O1ULasfD_;ZH~~(86W|0m0ZxDu-~>1UPT)T!@K+QR9VGw& literal 0 HcmV?d00001 diff --git a/tests/cpu_instrs/individual/11-op a,(hl).gb b/tests/cpu_instrs/individual/11-op a,(hl).gb new file mode 100644 index 0000000000000000000000000000000000000000..0634b7fdd19bd62c16860ab1bad05760d6ac5965 GIT binary patch literal 32768 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 HpAz^7^EI)n literal 0 HcmV?d00001 diff --git a/tests/cpu_instrs/readme.txt b/tests/cpu_instrs/readme.txt new file mode 100644 index 0000000..1aa7adb --- /dev/null +++ b/tests/cpu_instrs/readme.txt @@ -0,0 +1,119 @@ +Game Boy CPU Instruction Behavior Test +-------------------------------------- +This ROM tests the behavior of all CPU instructions except STOP and the +11 illegal opcodes. The tests are fairly thorough, running instructions +with boundary data and verifying both the result and that other +registers are not modified. Instructions which perform the same +operation on different registers are each tested just as thoroughly, in +case an emulator implements each independently. Some sub-tests take half +minute to complete. + +Failed instructions are listed as + + [CB] opcode + +Some errors cannot of course be diagnosed properly, since the test +framework itself relies on basic instruction behavior being correct. + + +Internal operation +------------------ +The main tests use a framework that runs each instruction in a loop, +varying the register values on input and examining them on output. +Rather than keep a table of correct values, it simply calculates a +CRC-32 checksum of all the output, then compares this with the correct +value. Instructions are divided into several groups, each with a +different set of input values suited for their behavior; for example, +the bit test instructions are fed $01, $02, $04 ... $40, $80, to ensure +each bit is handled properly, while the arithmetic instructions are fed +$01, $0F, $10, $7F, $FF, to exercise carry and half-carry. A few +instructions require a custom test due to their uniqueness. + + +Multi-ROM +--------- +In the main directory is a single ROM which runs all the tests. It +prints a test's number, runs the test, then "ok" if it passes, otherwise +a failure code. Once all tests have completed it either reports that all +tests passed, or prints the number of failed tests. Finally, it makes +several beeps. If a test fails, it can be run on its own by finding the +corresponding ROM in individual/. + +Ths compact format on screen is to avoid having the results scroll off +the top, so the test can be started and allowed to run without having to +constantly monitor the display. + +Currently there is no well-defined way for an emulator test rig to +programatically find the result of the test; contact me if you're trying +to do completely automated testing of your emulator. One simple approach +is to take a screenshot after all tests have run, or even just a +checksum of one, and compare this with a previous run. + + +Failure codes +------------- +Failed tests may print a failure code, and also short description of the +problem. For more information about a failure code, look in the +corresponding source file in source/; the point in the code where +"set_test n" occurs is where that failure code will be generated. +Failure code 1 is a general failure of the test; any further information +will be printed. + +Note that once a sub-test fails, no further tests for that file are run. + + +Console output +-------------- +Information is printed on screen in a way that needs only minimum LCD +support, and won't hang if LCD output isn't supported at all. +Specifically, while polling LY to wait for vblank, it will time out if +it takes too long, so LY always reading back as the same value won't +hang the test. It's also OK if scrolling isn't supported; in this case, +text will appear starting at the top of the screen. + +Everything printed on screen is also sent to the game link port by +writing the character to SB, then writing $81 to SC. This is useful for +tests which print lots of information that scrolls off screen. + + +Source code +----------- +Source code is included for all tests, in source/. It can be used to +build the individual test ROMs. Code for the multi test isn't included +due to the complexity of putting everything together. + +Code is written for the wla-dx assembler. To assemble a particular test, +execute + + wla -o "source_filename.s" test.o + wlalink linkfile test.gb + +Test code uses a common shell framework contained in common/. + + +Internal framework operation +---------------------------- +Tests use a common framework for setting things up, reporting results, +and ending. All files first include "shell.inc", which sets up the ROM +header and shell code, and includes other commonly-used modules. + +One oddity is that test code is first copied to internal RAM at $D000, +then executed there. This allows self-modification, and ensures the code +is executed the same way it is on my devcart, which doesn't have a +rewritable ROM as most do. + +Some macros are used to simplify common tasks: + + Macro Behavior + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + wreg addr,data Writes data to addr using LDH + lda addr Loads byte from addr into A using LDH + sta addr Stores A at addr using LDH + delay n Delays n cycles, where NOP = 1 cycle + delay_msec n Delays n milliseconds + set_test n,"Cause" Sets failure code and optional string + +Routines and macros are documented where they are defined. + +-- +Shay Green