use meowgb_core::setup_test_emulator; macro_rules! conditional_jump_relative_testgen { ($flag:ident, $not_opcode:literal, $opcode:literal) => { paste::paste! { #[test] fn []() { let mut emulator = setup_test_emulator([$not_opcode, 0x1]); emulator.registers.[](false); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x103); } #[test] fn []() { let mut emulator = setup_test_emulator([$not_opcode, 0x1]); emulator.registers.[](true); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x102); } #[test] fn []() { let mut emulator = setup_test_emulator([$opcode, 0x1]); emulator.registers.[](true); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x103); } #[test] fn []() { let mut emulator = setup_test_emulator([$opcode, 0x1]); emulator.registers.[](false); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x102); } } }; } conditional_jump_relative_testgen!(zero, 0x20, 0x28); conditional_jump_relative_testgen!(carry, 0x30, 0x38); #[test] fn test_jr_i8() { let mut emulator = setup_test_emulator([0x18, 0x1]); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x103); } #[test] fn test_jp_u16() { let mut emulator = setup_test_emulator([0xC3, 0xFE, 0xCA]); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0xCAFE); } #[test] fn test_jp_hl() { let mut emulator = setup_test_emulator([0xE9]); emulator.registers.set_hl(0xCAFE); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0xCAFE); } macro_rules! conditional_jump_testgen { ($flag:ident, $not_opcode:literal, $opcode:literal) => { paste::paste! { #[test] fn []() { let mut emulator = setup_test_emulator([$not_opcode, 0xFE, 0xCA]); emulator.registers.[](false); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0xCAFE); } #[test] fn []() { let mut emulator = setup_test_emulator([$not_opcode, 0xFE, 0xCA]); emulator.registers.[](true); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x103); } #[test] fn []() { let mut emulator = setup_test_emulator([$opcode, 0xFE, 0xCA]); emulator.registers.[](true); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0xCAFE); } #[test] fn []() { let mut emulator = setup_test_emulator([$opcode, 0xFE, 0xCA]); emulator.registers.[](false); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x103); } } }; } conditional_jump_testgen!(zero, 0xC2, 0xCA); conditional_jump_testgen!(carry, 0xD2, 0xDA); #[test] fn test_call_u16() { let mut emulator = setup_test_emulator([0xCD, 0xFE, 0xCA]); let orignal_sp = emulator.registers.sp; emulator.tick_4(); // <-- Read first u8 assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); // <-- Read second u8 assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); 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_4(); // <-- Push next instruction PC lo to stack assert_eq!(emulator.registers.sp, orignal_sp - 2); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0xCAFE); assert_eq!(emulator.debug_read_u8(orignal_sp - 1), 0x01); assert_eq!(emulator.debug_read_u8(orignal_sp - 2), 0x03); } macro_rules! conditional_call_testgen { ($flag:ident, $not_opcode:literal, $opcode:literal) => { paste::paste! { #[test] fn []() { let mut emulator = setup_test_emulator([$not_opcode, 0xFE, 0xCA]); let orignal_sp = emulator.registers.sp; emulator.registers.[](false); emulator.tick_4(); // <-- Read first u8 assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); // <-- Read second u8 assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); // <-- Check flag assert_eq!(emulator.registers.pc, 0x100); 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_4(); // <-- Push next instruction PC lo to stack assert_eq!(emulator.registers.sp, orignal_sp - 2); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0xCAFE); assert_eq!(emulator.debug_read_u8(orignal_sp - 1), 0x01); assert_eq!(emulator.debug_read_u8(orignal_sp - 2), 0x03); } #[test] fn []() { let mut emulator = setup_test_emulator([$not_opcode, 0xFE, 0xCA]); let orignal_sp = emulator.registers.sp; emulator.registers.[](true); emulator.tick_4(); // <-- Read first u8 assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); // <-- Read second u8 assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); // <-- Check flag assert_eq!(emulator.registers.pc, 0x103); assert_eq!(emulator.registers.sp, orignal_sp); } #[test] fn []() { let mut emulator = setup_test_emulator([$opcode, 0xFE, 0xCA]); let orignal_sp = emulator.registers.sp; emulator.registers.[](true); emulator.tick_4(); // <-- Read first u8 assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); // <-- Read second u8 assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); // <-- Check flag assert_eq!(emulator.registers.pc, 0x100); 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_4(); // <-- Push next instruction PC lo to stack assert_eq!(emulator.registers.sp, orignal_sp - 2); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0xCAFE); assert_eq!(emulator.debug_read_u8(orignal_sp - 1), 0x01); assert_eq!(emulator.debug_read_u8(orignal_sp - 2), 0x03); } #[test] fn []() { let mut emulator = setup_test_emulator([$opcode, 0xFE, 0xCA]); let orignal_sp = emulator.registers.sp; emulator.registers.[](false); emulator.tick_4(); // <-- Read first u8 assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); // <-- Read second u8 assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); // <-- Check flag assert_eq!(emulator.registers.pc, 0x103); assert_eq!(emulator.registers.sp, orignal_sp); } } }; } conditional_call_testgen!(zero, 0xC4, 0xCC); conditional_call_testgen!(carry, 0xD4, 0xDC); #[test] fn test_ret() { let mut emulator = setup_test_emulator([0xC9]); emulator.registers.sp = emulator.registers.sp.overflowing_sub(1).0; emulator.debug_write_u8(emulator.registers.sp, 0xCA); emulator.registers.sp = emulator.registers.sp.overflowing_sub(1).0; emulator.debug_write_u8(emulator.registers.sp, 0xFE); let orignal_sp = emulator.registers.sp; emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); assert_eq!(emulator.registers.sp, orignal_sp + 1); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); assert_eq!(emulator.registers.sp, orignal_sp + 2); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0xCAFE); } #[test] fn test_reti() { let mut emulator = setup_test_emulator([0xD9]); emulator.interrupts.ime = false; emulator.registers.sp = emulator.registers.sp.overflowing_sub(1).0; emulator.debug_write_u8(emulator.registers.sp, 0xCA); emulator.registers.sp = emulator.registers.sp.overflowing_sub(1).0; emulator.debug_write_u8(emulator.registers.sp, 0xFE); let orignal_sp = emulator.registers.sp; emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); assert_eq!(emulator.registers.sp, orignal_sp + 1); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); assert_eq!(emulator.registers.sp, orignal_sp + 2); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0xCAFE); assert_eq!(emulator.interrupts.ime, true); } macro_rules! conditional_ret_testgen { ($flag:ident, $not_opcode:literal, $opcode:literal) => { paste::paste! { #[test] fn []() { let mut emulator = setup_test_emulator([$not_opcode]); emulator.registers.[](false); emulator.registers.sp = emulator.registers.sp.overflowing_sub(1).0; emulator.debug_write_u8(emulator.registers.sp, 0xCA); emulator.registers.sp = emulator.registers.sp.overflowing_sub(1).0; emulator.debug_write_u8(emulator.registers.sp, 0xFE); let orignal_sp = emulator.registers.sp; emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); assert_eq!(emulator.registers.sp, orignal_sp + 1); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); assert_eq!(emulator.registers.sp, orignal_sp + 2); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0xCAFE); } #[test] fn []() { let mut emulator = setup_test_emulator([$not_opcode]); emulator.registers.[](true); emulator.registers.sp = emulator.registers.sp.overflowing_sub(1).0; emulator.debug_write_u8(emulator.registers.sp, 0xCA); emulator.registers.sp = emulator.registers.sp.overflowing_sub(1).0; emulator.debug_write_u8(emulator.registers.sp, 0xFE); let orignal_sp = emulator.registers.sp; emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x101); assert_eq!(emulator.registers.sp, orignal_sp); } #[test] fn []() { let mut emulator = setup_test_emulator([$opcode]); emulator.registers.[](true); emulator.registers.sp = emulator.registers.sp.overflowing_sub(1).0; emulator.debug_write_u8(emulator.registers.sp, 0xCA); emulator.registers.sp = emulator.registers.sp.overflowing_sub(1).0; emulator.debug_write_u8(emulator.registers.sp, 0xFE); let orignal_sp = emulator.registers.sp; emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); assert_eq!(emulator.registers.sp, orignal_sp + 1); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); assert_eq!(emulator.registers.sp, orignal_sp + 2); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0xCAFE); } #[test] fn []() { let mut emulator = setup_test_emulator([$opcode]); emulator.registers.[](false); emulator.registers.sp = emulator.registers.sp.overflowing_sub(1).0; emulator.debug_write_u8(emulator.registers.sp, 0xCA); emulator.registers.sp = emulator.registers.sp.overflowing_sub(1).0; emulator.debug_write_u8(emulator.registers.sp, 0xFE); let orignal_sp = emulator.registers.sp; emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, 0x101); assert_eq!(emulator.registers.sp, orignal_sp); } } }; } conditional_ret_testgen!(zero, 0xC0, 0xC8); conditional_ret_testgen!(carry, 0xD0, 0xD8); macro_rules! rst_testgen { ($opcode:literal, $addr:literal) => { paste::paste! { #[test] fn []() { let mut emulator = setup_test_emulator([$opcode]); let original_sp = emulator.registers.sp; emulator.tick_4(); assert_eq!(emulator.registers.sp, original_sp); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.sp, original_sp - 1); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.sp, original_sp - 2); assert_eq!(emulator.registers.pc, 0x100); emulator.tick_4(); assert_eq!(emulator.registers.pc, $addr); assert_eq!(emulator.debug_read_u8(original_sp - 1), 0x01); assert_eq!(emulator.debug_read_u8(original_sp - 2), 0x01); } } }; } rst_testgen!(0xC7, 0x0); rst_testgen!(0xCF, 0x08); rst_testgen!(0xD7, 0x10); rst_testgen!(0xDF, 0x18); rst_testgen!(0xE7, 0x20); rst_testgen!(0xEF, 0x28); rst_testgen!(0xF7, 0x30); rst_testgen!(0xFF, 0x38);