diff --git a/examples/sdl/src/main.rs b/examples/sdl/src/main.rs index 5cf73b2fdb0a90f08d73f0fc03eaf29465502d46..ab884e78741e0e6a3c5a9428c25a681baf1316f7 100644 --- a/examples/sdl/src/main.rs +++ b/examples/sdl/src/main.rs @@ -3,8 +3,8 @@ use boytacean::{ ppu::{DISPLAY_HEIGHT, DISPLAY_WIDTH}, }; use sdl2::{ - pixels::PixelFormatEnum, video::Window, AudioSubsystem, EventPump, TimerSubsystem, - VideoSubsystem, + event::Event, pixels::PixelFormatEnum, video::Window, AudioSubsystem, EventPump, + TimerSubsystem, VideoSubsystem, }; /// The base title to be used in the window. @@ -76,6 +76,7 @@ fn main() { let mut game_boy = GameBoy::new(); game_boy.load_boot_default(); game_boy.load_rom("../../res/roms/ld_r_r.gb"); + //game_boy.load_rom("../../res/roms/opus5.gb"); let mut counter = 0; @@ -84,7 +85,12 @@ fn main() { break; } - while let Some(event) = graphics.event_pump.poll_event() {} + while let Some(event) = graphics.event_pump.poll_event() { + match event { + Event::Quit { .. } => break 'main, + _ => (), + } + } let mut counter_ticks = 0u32; diff --git a/src/cpu.rs b/src/cpu.rs index dc9ef41a77b19a1327712f6aba838e23a0f3400f..38a3d7d2ec55b5dd1079fdfe6d45e0f94fb2d569 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -41,8 +41,16 @@ impl Cpu { } } - println!("Implemented {}/{} instructions", implemented, INSTRUCTIONS.len()); - println!("Implemented {}/{} extended instructions", implemented_ext, EXTENDED.len()); + println!( + "Implemented {}/{} instructions", + implemented, + INSTRUCTIONS.len() + ); + println!( + "Implemented {}/{} extended instructions", + implemented_ext, + EXTENDED.len() + ); Cpu { pc: 0x0, diff --git a/src/inst.rs b/src/inst.rs index 17e43c9b5532088fe74096be0d36c918228e327c..93ff6fddab12d297d235db789f3ea852b68a0d62 100644 --- a/src/inst.rs +++ b/src/inst.rs @@ -65,7 +65,7 @@ pub const INSTRUCTIONS: [(fn(&mut Cpu), u8, &'static str); 256] = [ (noimpl, 4, "! UNIMP !"), (noimpl, 4, "! UNIMP !"), (noimpl, 4, "! UNIMP !"), - (noimpl, 4, "! UNIMP !"), + (inc_a, 4, "INC A"), (dec_a, 4, "DEC A"), (ld_a_u8, 8, "LD A, u8"), (noimpl, 4, "! UNIMP !"), @@ -73,7 +73,7 @@ pub const INSTRUCTIONS: [(fn(&mut Cpu), u8, &'static str); 256] = [ (ld_b_b, 4, "LD B, B"), (noimpl, 4, "! UNIMP !"), (noimpl, 4, "! UNIMP !"), - (noimpl, 4, "! UNIMP !"), + (ld_b_e, 4, "LD B, E"), (ld_b_h, 4, "LD B, H"), (noimpl, 4, "! UNIMP !"), (ld_b_mhl, 8, "LD B, [HL]"), @@ -139,9 +139,9 @@ pub const INSTRUCTIONS: [(fn(&mut Cpu), u8, &'static str); 256] = [ (noimpl, 4, "! UNIMP !"), // 0x8 opcodes (noimpl, 4, "! UNIMP !"), + (add_a_c, 4, "ADD A, C"), (noimpl, 4, "! UNIMP !"), - (noimpl, 4, "! UNIMP !"), - (noimpl, 4, "! UNIMP !"), + (add_a_e, 4, "ADD A, E"), (noimpl, 4, "! UNIMP !"), (noimpl, 4, "! UNIMP !"), (add_a_mhl, 8, "ADD A, [HL]"), @@ -162,7 +162,7 @@ pub const INSTRUCTIONS: [(fn(&mut Cpu), u8, &'static str); 256] = [ (noimpl, 4, "! UNIMP !"), (noimpl, 4, "! UNIMP !"), (noimpl, 4, "! UNIMP !"), - (noimpl, 4, "! UNIMP !"), + (sub_a_a, 4, "SUB A, A"), (noimpl, 4, "! UNIMP !"), (noimpl, 4, "! UNIMP !"), (noimpl, 4, "! UNIMP !"), @@ -208,7 +208,7 @@ pub const INSTRUCTIONS: [(fn(&mut Cpu), u8, &'static str); 256] = [ // 0xc opcodes (ret_nz, 8, "RET NZ"), (pop_bc, 12, "POP BC"), - (noimpl, 4, "! UNIMP !"), + (jp_nz_u16, 12, "JP NZ, u16"), (jp_u16, 16, "JP u16"), (call_nz_u16, 12, "CALL NZ, u16"), (push_bc, 16, "PUSH BC"), @@ -216,9 +216,9 @@ pub const INSTRUCTIONS: [(fn(&mut Cpu), u8, &'static str); 256] = [ (noimpl, 4, "! UNIMP !"), (noimpl, 4, "! UNIMP !"), (ret, 16, "RET"), + (jp_z_u16, 12, "JP Z, u16"), (noimpl, 4, "! UNIMP !"), - (noimpl, 4, "! UNIMP !"), - (noimpl, 4, "! UNIMP !"), + (call_z_u16, 12, "CALL Z, u16"), (call_u16, 24, "CALL u16"), (noimpl, 4, "! UNIMP !"), (rst_08h, 16, "RST 08h"), @@ -233,9 +233,9 @@ pub const INSTRUCTIONS: [(fn(&mut Cpu), u8, &'static str); 256] = [ (noimpl, 4, "! UNIMP !"), (noimpl, 4, "! UNIMP !"), (noimpl, 4, "! UNIMP !"), + (jp_c_u16, 12, "JP C, u16"), (noimpl, 4, "! UNIMP !"), - (noimpl, 4, "! UNIMP !"), - (noimpl, 4, "! UNIMP !"), + (call_c_u16, 12, "CALL C, u16"), (noimpl, 4, "! UNIMP !"), (noimpl, 4, "! UNIMP !"), (noimpl, 4, "! UNIMP !"), @@ -275,7 +275,7 @@ pub const INSTRUCTIONS: [(fn(&mut Cpu), u8, &'static str); 256] = [ (rst_38h, 16, "RST 38h"), ]; -pub const EXTENDED: [(fn(&mut Cpu), u8, &'static str); 176] = [ +pub const EXTENDED: [(fn(&mut Cpu), u8, &'static str); 256] = [ // 0x0 opcodes (noimpl, 4, "! UNIMP !"), (noimpl, 4, "! UNIMP !"), @@ -463,6 +463,91 @@ pub const EXTENDED: [(fn(&mut Cpu), u8, &'static str); 176] = [ (noimpl, 4, "! UNIMP !"), (noimpl, 4, "! UNIMP !"), (noimpl, 4, "! UNIMP !"), + // 0xb opcodes + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + // 0xc opcodes + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + // 0xd opcodes + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + // 0xe opcodes + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (set_4_a, 8, "SET 4, A"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + // 0xf opcodes + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), + (noimpl, 4, "! UNIMP !"), ]; fn nop(_cpu: &mut Cpu) {} @@ -804,6 +889,16 @@ fn jr_c_i8(cpu: &mut Cpu) { cpu.ticks = cpu.ticks.wrapping_add(4); } +fn inc_a(cpu: &mut Cpu) { + let value = cpu.a.wrapping_add(1); + + cpu.set_sub(false); + cpu.set_zero(value == 0); + cpu.set_half_carry((value & 0xf) == 0xf); + + cpu.a = value; +} + fn dec_a(cpu: &mut Cpu) { let a = cpu.a; let value = a.wrapping_sub(1); @@ -824,6 +919,10 @@ fn ld_b_b(cpu: &mut Cpu) { cpu.b = cpu.b; } +fn ld_b_e(cpu: &mut Cpu) { + cpu.b = cpu.e; +} + fn ld_b_h(cpu: &mut Cpu) { cpu.b = cpu.h; } @@ -903,6 +1002,14 @@ fn ld_a_l(cpu: &mut Cpu) { cpu.a = cpu.l; } +fn add_a_c(cpu: &mut Cpu) { + cpu.a = add_set_flags(cpu, cpu.a, cpu.c); +} + +fn add_a_e(cpu: &mut Cpu) { + cpu.a = add_set_flags(cpu, cpu.a, cpu.e); +} + fn add_a_mhl(cpu: &mut Cpu) { let byte = cpu.mmu.read(cpu.hl()); cpu.a = add_set_flags(cpu, cpu.a, byte); @@ -912,6 +1019,10 @@ fn sub_a_b(cpu: &mut Cpu) { cpu.a = sub_set_flags(cpu, cpu.a, cpu.b); } +fn sub_a_a(cpu: &mut Cpu) { + cpu.a = sub_set_flags(cpu, cpu.a, cpu.a); +} + fn and_a_c(cpu: &mut Cpu) { cpu.a &= cpu.c; @@ -995,6 +1106,17 @@ fn pop_bc(cpu: &mut Cpu) { cpu.set_bc(word); } +fn jp_nz_u16(cpu: &mut Cpu) { + let word = cpu.read_u16(); + + if cpu.get_zero() { + return; + } + + cpu.pc = word; + cpu.ticks = cpu.ticks.wrapping_add(4); +} + fn jp_u16(cpu: &mut Cpu) { let word = cpu.read_u16(); cpu.pc = word; @@ -1025,6 +1147,29 @@ fn ret(cpu: &mut Cpu) { cpu.pc = cpu.pop_word(); } +fn jp_z_u16(cpu: &mut Cpu) { + let word = cpu.read_u16(); + + if !cpu.get_zero() { + return; + } + + cpu.pc = word; + cpu.ticks = cpu.ticks.wrapping_add(4); +} + +fn call_z_u16(cpu: &mut Cpu) { + let word = cpu.read_u16(); + + if !cpu.get_zero() { + return; + } + + cpu.push_word(cpu.pc); + cpu.pc = word; + cpu.ticks = cpu.ticks.wrapping_add(12); +} + fn call_u16(cpu: &mut Cpu) { let word = cpu.read_u16(); cpu.push_word(cpu.pc); @@ -1049,6 +1194,29 @@ fn sub_a_u8(cpu: &mut Cpu) { cpu.a = sub_set_flags(cpu, cpu.a, byte); } +fn jp_c_u16(cpu: &mut Cpu) { + let word = cpu.read_u16(); + + if !cpu.get_carry() { + return; + } + + cpu.pc = word; + cpu.ticks = cpu.ticks.wrapping_add(4); +} + +fn call_c_u16(cpu: &mut Cpu) { + let word = cpu.read_u16(); + + if !cpu.get_carry() { + return; + } + + cpu.push_word(cpu.pc); + cpu.pc = word; + cpu.ticks = cpu.ticks.wrapping_add(12); +} + fn ld_mff00u8_a(cpu: &mut Cpu) { let byte = cpu.read_u8(); cpu.mmu.write(0xff00 + byte as u16, cpu.a); @@ -1158,6 +1326,15 @@ fn bit_7_h(cpu: &mut Cpu) { bit_h(cpu, 7); } +fn set_4_a(cpu: &mut Cpu) { + cpu.a = set(cpu.a, 4); +} + +/// Helper function to set one bit in a u8. +fn set(value: u8, bit: u8) -> u8 { + value | (1u8 << (bit as usize)) +} + /// Helper function that rotates (shifts) left the given /// byte (probably from a register) and updates the /// proper flag registers.