From 00850645cdd12565dc0ce62465ef5741c70bb71a Mon Sep 17 00:00:00 2001 From: EliseZeroTwo Date: Sun, 19 Dec 2021 12:27:41 +0100 Subject: [PATCH] feat: fix a few instructions, add halt --- deemgee-opcode/src/lib.rs | 7 +- deemgee/src/gameboy.rs | 17 +- deemgee/src/gameboy/cpu.rs | 52 ++- deemgee/src/gameboy/cpu/alu.rs | 68 ++-- deemgee/src/gameboy/cpu/flow.rs | 5 +- deemgee/src/gameboy/cpu/load_store_move.rs | 49 +++ deemgee/src/gameboy/cpu/misc.rs | 16 +- deemgee/src/gameboy/cpu/prefixed.rs | 410 ++++++++++++++++++++- deemgee/src/gameboy/interrupts.rs | 10 +- deemgee/src/gameboy/joypad.rs | 24 ++ deemgee/src/gameboy/mapper.rs | 2 + deemgee/src/gameboy/mapper/mbc1.rs | 114 ++++++ deemgee/src/gameboy/ppu.rs | 8 +- deemgee/src/gameboy/serial.rs | 22 +- deemgee/src/gameboy/timer.rs | 4 +- deemgee/src/main.rs | 18 +- 16 files changed, 766 insertions(+), 60 deletions(-) create mode 100644 deemgee/src/gameboy/mapper/mbc1.rs diff --git a/deemgee-opcode/src/lib.rs b/deemgee-opcode/src/lib.rs index 7334cd5..9655432 100644 --- a/deemgee-opcode/src/lib.rs +++ b/deemgee-opcode/src/lib.rs @@ -87,13 +87,15 @@ pub fn opcode(item: TokenStream) -> TokenStream { let log = if extended.value { quote::quote! { if state.registers.cycle == 1 && state.log_instructions { - log::debug!("Prefixed OP {} ({:#02X})", #readable, #opcode); + log::debug!("(PC: {:#02X}) Prefixed OP {} ({:#02X})", state.registers.pc, #readable, #opcode); + #regs } } } else { quote::quote! { if state.registers.cycle == 0 && state.log_instructions { - log::debug!("OP {} ({:#02X})", #readable, #opcode); + log::debug!("(PC: {:#02X}) OP {} ({:#02X})", state.registers.pc, #readable, #opcode); + #regs } } }; @@ -101,7 +103,6 @@ pub fn opcode(item: TokenStream) -> TokenStream { let out = quote::quote! { #fn_sig { #log - #regs #match_statement } diff --git a/deemgee/src/gameboy.rs b/deemgee/src/gameboy.rs index c58746f..cef02cf 100644 --- a/deemgee/src/gameboy.rs +++ b/deemgee/src/gameboy.rs @@ -15,7 +15,12 @@ use memory::Memory; use ppu::Ppu; use timer::Timer; -use self::{cpu::Registers, mapper::NoMBC, serial::Serial, sound::Sound}; +use self::{ + cpu::Registers, + mapper::{mbc1::MBC1, NoMBC}, + serial::Serial, + sound::Sound, +}; pub struct DmaState { pub base: u8, @@ -53,6 +58,9 @@ pub struct Gameboy { pub mem_write_breakpoints: [bool; u16::MAX as usize + 1], trigger_bp: bool, pub log_instructions: bool, + pub halt: bool, + pub halt_bug: bool, + pub used_halt_bug: bool, } impl Gameboy { @@ -74,6 +82,9 @@ impl Gameboy { mem_write_breakpoints: [false; u16::MAX as usize + 1], trigger_bp: false, log_instructions: false, + halt: false, + halt_bug: false, + used_halt_bug: false, } } @@ -100,6 +111,7 @@ impl Gameboy { pub fn load_cartridge(&mut self, bytes: Vec) { match bytes[0x147] { 0 => self.cartridge = Some(Box::new(NoMBC::new(bytes))), + 1 => self.cartridge = Some(Box::new(MBC1::new(bytes))), other => unimplemented!("Cartidge type: {:#X}", other), } } @@ -235,6 +247,9 @@ impl Gameboy { 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 --git a/deemgee/src/gameboy/cpu.rs b/deemgee/src/gameboy/cpu.rs index 5ed2e74..ea3fc2d 100644 --- a/deemgee/src/gameboy/cpu.rs +++ b/deemgee/src/gameboy/cpu.rs @@ -104,6 +104,24 @@ impl Registers { pub fn tick_cpu(state: &mut Gameboy) { state.registers.mem_op_happened = false; + if state.joypad.interrupt_triggered { + state.joypad.interrupt_triggered = false; + state.interrupts.write_if_joypad(true); + } + + if state.registers.cycle == 0 && state.halt { + if (state.interrupts.read_ie_vblank() && state.interrupts.read_if_vblank()) + || (state.interrupts.read_ie_lcd_stat() && state.interrupts.read_if_lcd_stat()) + || (state.interrupts.read_ie_timer() && state.interrupts.read_if_timer()) + || (state.interrupts.read_ie_serial() && state.interrupts.read_if_serial()) + || (state.interrupts.read_ie_joypad() && state.interrupts.read_if_joypad()) + { + state.halt = false; + } else { + return; + } + } + // TODO: Interrupts if state.registers.cycle == 0 && state.interrupts.ime { if state.interrupts.read_ie_vblank() && state.interrupts.read_if_vblank() { @@ -129,6 +147,17 @@ pub fn tick_cpu(state: &mut Gameboy) { } } + if state.registers.cycle == 0 && state.interrupts.ei_queued { + state.interrupts.ime = state.interrupts.ei_queued; + state.interrupts.ei_queued = false; + } + + if state.registers.cycle == 0 && state.halt_bug { + state.used_halt_bug = true; + state.halt_bug = false; + state.registers.pc = state.registers.pc.overflowing_sub(1).0; + } + let result = if let Some(idx) = state.registers.in_interrupt_vector { match state.registers.cycle { 0 => { @@ -138,11 +167,11 @@ pub fn tick_cpu(state: &mut Gameboy) { } 1 => CycleResult::NeedsMore, 2 => { - state.cpu_push_stack((state.registers.pc.overflowing_add(3).0 >> 8) as u8); + state.cpu_push_stack((state.registers.pc >> 8) as u8); CycleResult::NeedsMore } 3 => { - state.cpu_push_stack(state.registers.pc.overflowing_add(3).0 as u8); + state.cpu_push_stack(state.registers.pc as u8); CycleResult::NeedsMore } 4 => { @@ -179,6 +208,7 @@ pub fn tick_cpu(state: &mut Gameboy) { let result: CycleResult = match opcode { 0x00 => misc::nop, 0x01 => load_store_move::ld_bc_imm_u16, + 0x02 => load_store_move::ld_deref_bc_a, 0x03 => alu::inc_bc, 0x04 => alu::inc_b, 0x05 => alu::dec_b, @@ -191,6 +221,7 @@ pub fn tick_cpu(state: &mut Gameboy) { 0x0d => alu::dec_c, 0x0e => load_store_move::ld_c_imm_u8, 0x11 => load_store_move::ld_de_imm_u16, + 0x12 => load_store_move::ld_deref_de_a, 0x13 => alu::inc_de, 0x14 => alu::inc_d, 0x15 => alu::dec_d, @@ -203,6 +234,7 @@ pub fn tick_cpu(state: &mut Gameboy) { 0x1c => alu::inc_e, 0x1d => alu::dec_e, 0x1e => load_store_move::ld_e_imm_u8, + 0x1f => alu::rra, 0x20 => flow::jr_nz_i8, 0x21 => load_store_move::ld_hl_imm_u16, 0x22 => load_store_move::ld_hl_plus_a, @@ -288,6 +320,7 @@ pub fn tick_cpu(state: &mut Gameboy) { 0x73 => load_store_move::ld_deref_hl_e, 0x74 => load_store_move::ld_deref_hl_h, 0x75 => load_store_move::ld_deref_hl_l, + 0x76 => misc::halt, 0x77 => load_store_move::ld_deref_hl_a, 0x78 => load_store_move::ld_a_b, 0x79 => load_store_move::ld_a_c, @@ -380,6 +413,7 @@ pub fn tick_cpu(state: &mut Gameboy) { 0xD0 => flow::ret_nc, 0xD1 => load_store_move::pop_de, 0xD2 => flow::jp_nc_u16, + 0xD3 => panic!("Executing bad opcode {:#02X}", opcode), 0xD4 => flow::call_nc_u16, 0xD5 => load_store_move::push_de, 0xD6 => alu::sub_a_imm_u8, @@ -387,17 +421,21 @@ pub fn tick_cpu(state: &mut Gameboy) { 0xD8 => flow::ret_c, 0xD9 => flow::reti, 0xDA => flow::jp_c_u16, + 0xDB => panic!("Executing bad opcode {:#02X}", opcode), 0xDC => flow::call_c_u16, + 0xDD => panic!("Executing bad opcode {:#02X}", opcode), 0xDE => alu::sbc_a_imm_u8, 0xDF => flow::rst_0x18, 0xE0 => load_store_move::ldh_imm_u8_a, 0xE1 => load_store_move::pop_hl, 0xE2 => load_store_move::ldh_deref_c_a, + 0xE3 | 0xE4 => panic!("Executing bad opcode {:#02X}", opcode), 0xE5 => load_store_move::push_hl, 0xE6 => alu::and_a_imm_u8, 0xE7 => flow::rst_0x20, 0xE9 => flow::jp_hl, 0xEA => load_store_move::ld_deref_imm_u16_a, + 0xEB | 0xEC | 0xED => panic!("Executing bad opcode {:#02X}", opcode), 0xEE => alu::xor_a_imm_u8, 0xEF => flow::rst_0x28, 0xF0 => load_store_move::ldh_a_imm_u8, @@ -407,9 +445,11 @@ pub fn tick_cpu(state: &mut Gameboy) { 0xF5 => load_store_move::push_af, 0xF6 => alu::or_a_imm_u8, 0xF7 => flow::rst_0x30, + 0xF8 => load_store_move::ld_hl_sp_i8, 0xF9 => load_store_move::ld_sp_hl, 0xFA => load_store_move::ld_a_deref_imm_u16, 0xFB => misc::ei, + 0xFC | 0xFD => panic!("Executing bad opcode {:#02X}", opcode), 0xFE => alu::cp_a_imm_u8, 0xFF => flow::rst_0x38, unknown => { @@ -425,13 +465,16 @@ pub fn tick_cpu(state: &mut Gameboy) { }; if result == CycleResult::Finished { + if state.used_halt_bug { + state.registers.pc = state.registers.pc.overflowing_add(1).0; + } + match state.registers.opcode_bytecount { - Some(len) => state.registers.pc += len as u16, + Some(len) => state.registers.pc = state.registers.pc.overflowing_add(len as u16).0, None => panic!("Forgot to set opcode len"), } if !state.registers.mem_op_happened { - log::trace!("Memory bus clear, precaching next opcode"); state.cpu_read_u8(state.registers.pc); } @@ -439,7 +482,6 @@ pub fn tick_cpu(state: &mut Gameboy) { state.registers.current_prefixed_opcode = None; state.registers.current_opcode = None; state.registers.opcode_bytecount = None; - log::trace!("Cycle finished"); } else { state.registers.cycle += 1; } diff --git a/deemgee/src/gameboy/cpu/alu.rs b/deemgee/src/gameboy/cpu/alu.rs index 263f4d3..fca8aa1 100644 --- a/deemgee/src/gameboy/cpu/alu.rs +++ b/deemgee/src/gameboy/cpu/alu.rs @@ -17,12 +17,7 @@ pub fn sub_with_carry(lhs: u8, rhs: u8, carry: bool) -> CarryResult { let (result, second_carry) = first_res.overflowing_sub(carry_u8); let carry = first_carry || second_carry; - - let first_hc_res = (lhs & 0xF).overflowing_sub(rhs & 0xF).0; - let first_half_carry = (first_hc_res >> 4) & 0b1 == 1; - let second_half_carry = ((first_hc_res.overflowing_sub(carry_u8).0) >> 4) & 0b1 == 1; - - let half_carry = first_half_carry || second_half_carry; + let half_carry = (lhs & 0xF) < (rhs & 0xF) + carry_u8; CarryResult { result, carry, half_carry } } @@ -34,26 +29,21 @@ pub fn add_with_carry(lhs: u8, rhs: u8, carry: bool) -> CarryResult { let (result, second_carry) = first_res.overflowing_add(carry_u8); let carry = first_carry || second_carry; - - let first_hc_res = (lhs & 0xF) + (rhs & 0xF); - let first_half_carry = (first_hc_res >> 4) & 0b1 == 1; - let second_half_carry = ((first_hc_res + carry_u8) >> 4) & 0b1 == 1; - - let half_carry = first_half_carry || second_half_carry; + let half_carry = (lhs & 0xF) + (rhs & 0xF) > 0xF; CarryResult { result, carry, half_carry } } pub fn add(lhs: u8, rhs: u8) -> CarryResult { let (result, carry) = lhs.overflowing_add(rhs); - let half_carry = (((lhs & 0xF) + (rhs & 0xF)) >> 4) & 0b1 == 1; + let half_carry = (lhs & 0xF) + (rhs & 0xF) > 0xF; CarryResult { result, carry, half_carry } } pub fn sub(lhs: u8, rhs: u8) -> CarryResult { let (result, carry) = lhs.overflowing_sub(rhs); - let half_carry = (((lhs & 0xF).overflowing_sub(rhs & 0xF).0) >> 4) & 0b1 == 1; + let half_carry = (lhs & 0xF) < (rhs & 0xF); CarryResult { result, carry, half_carry } } @@ -511,6 +501,25 @@ opcode!(rla, 0x17, "RLA", false, { } }); +opcode!(rra, 0x1f, "RRA", false, { + 0 => { + let carry = state.registers.a & 0b1 == 1; + state.registers.a >>= 1; + + if state.registers.get_carry() { + state.registers.a = state.registers.a.wrapping_add(1 << 7); + } + + state.registers.set_zero(false); + state.registers.set_subtract(false); + state.registers.set_half_carry(false); + state.registers.set_carry(carry); + + state.registers.opcode_bytecount = Some(1); + CycleResult::Finished + } +}); + macro_rules! define_inc_u16_reg { ($op:literal, $lreg:ident, $rreg:ident) => { paste::paste! { @@ -818,15 +827,18 @@ macro_rules! define_add_hl_u16_reg { CycleResult::NeedsMore }, 1 => { + let CarryResult { result, carry, half_carry } = add(state.registers.h, state.registers.$lreg); + state.registers.h = result; + state.registers.set_half_carry(half_carry); + state.registers.set_carry(carry); + if state.registers.take_hold() != 0 { - let CarryResult { result, carry, half_carry } = add(state.registers.h, state.registers.$lreg); + let CarryResult { result, carry: s_carry, half_carry: s_half_carry } = add(state.registers.h, 1); state.registers.h = result; - state.registers.set_half_carry(half_carry); - state.registers.set_carry(carry); - } else { - state.registers.set_half_carry(false); - state.registers.set_carry(false); + state.registers.set_half_carry(half_carry || s_half_carry); + state.registers.set_carry(carry || s_carry); } + state.registers.set_subtract(false); state.registers.opcode_bytecount = Some(1); CycleResult::Finished @@ -848,16 +860,18 @@ opcode!(add_hl_sp, 0x39, "ADD HL, SP", false, { CycleResult::NeedsMore }, 1 => { + let CarryResult { result, carry, half_carry } = add(state.registers.h, (state.registers.sp >> 8) as u8); + state.registers.h = result; + state.registers.set_half_carry(half_carry); + state.registers.set_carry(carry); + if state.registers.take_hold() != 0 { - let CarryResult { result, carry, half_carry } = - add(state.registers.h, (state.registers.sp >> 8) as u8); + let CarryResult { result, carry: s_carry, half_carry: s_half_carry } = add(state.registers.h, 1); state.registers.h = result; - state.registers.set_half_carry(half_carry); - state.registers.set_carry(carry); - } else { - state.registers.set_half_carry(false); - state.registers.set_carry(false); + state.registers.set_half_carry(half_carry || s_half_carry); + state.registers.set_carry(carry || s_carry); } + state.registers.set_subtract(false); state.registers.opcode_bytecount = Some(1); CycleResult::Finished diff --git a/deemgee/src/gameboy/cpu/flow.rs b/deemgee/src/gameboy/cpu/flow.rs index 0561925..6cd213f 100644 --- a/deemgee/src/gameboy/cpu/flow.rs +++ b/deemgee/src/gameboy/cpu/flow.rs @@ -477,6 +477,7 @@ opcode!(ret, 0xC9, "RET", false, { }, 3 => { let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold(); + println!("RET to {:#04X}", address); state.registers.pc = address; state.registers.opcode_bytecount = Some(0); CycleResult::Finished @@ -634,11 +635,11 @@ macro_rules! define_rst { CycleResult::NeedsMore }, 1 => { - state.cpu_push_stack((state.registers.pc >> 8) as u8); + state.cpu_push_stack((state.registers.pc.overflowing_add(1).0 >> 8) as u8); CycleResult::NeedsMore }, 2 => { - state.cpu_push_stack((state.registers.pc & 0xFF) as u8); + state.cpu_push_stack((state.registers.pc.overflowing_add(1).0 & 0xFF) as u8); CycleResult::NeedsMore }, 3 => { diff --git a/deemgee/src/gameboy/cpu/load_store_move.rs b/deemgee/src/gameboy/cpu/load_store_move.rs index 10c841d..729e259 100644 --- a/deemgee/src/gameboy/cpu/load_store_move.rs +++ b/deemgee/src/gameboy/cpu/load_store_move.rs @@ -168,6 +168,28 @@ define_ld_reg_deref!(0x7E, a, hl); define_ld_reg_deref!(0x0A, a, bc); define_ld_reg_deref!(0x1A, a, de); +opcode!(ld_deref_bc_a, 0x02, "LD (BC),A", false, { + 0 => { + state.cpu_write_u8(state.registers.get_bc(), state.registers.a); + CycleResult::NeedsMore + }, + 1 => { + state.registers.opcode_bytecount = Some(1); + CycleResult::Finished + } +}); + +opcode!(ld_deref_de_a, 0x12, "LD (DE),A", false, { + 0 => { + state.cpu_write_u8(state.registers.get_de(), state.registers.a); + CycleResult::NeedsMore + }, + 1 => { + state.registers.opcode_bytecount = Some(1); + CycleResult::Finished + } +}); + opcode!(ld_hl_plus_a, 0x22, "LD (HL+),A", false, { 0 => { state.cpu_write_u8(state.registers.get_hl(), state.registers.a); @@ -222,6 +244,33 @@ opcode!(ld_a_hl_minus, 0x3A, "LD A,(HL-)", false, { } }); +opcode!(ld_hl_sp_i8, 0xF8, "LD HL,SP+i8", false, { + 0 => { + state.cpu_read_u8(state.registers.pc.overflowing_add(1).0); + CycleResult::NeedsMore + }, + 1 => { + let val = state.registers.take_mem() as i8; + + let rhs = if val < 0 { + state.registers.sp.overflowing_sub((val as u8 & !(1u8 << 7)) as u16).0 + } else { + state.registers.sp.overflowing_add(val as u16).0 + }; + + state.registers.set_hl(rhs); + state.registers.set_zero(false); + state.registers.set_subtract(false); + state.registers.set_half_carry((state.registers.sp & 0xF) + (val as u16 & 0xF) > 0xF); + state.registers.set_carry((state.registers.sp & 0xFF) + (val as u16 & 0xFF) > 0xFF); + CycleResult::NeedsMore + }, + 2 => { + state.registers.opcode_bytecount = Some(2); + CycleResult::Finished + } +}); + macro_rules! define_ld_reg_imm_u8 { ($op:literal, $lreg:ident) => { paste::paste! { diff --git a/deemgee/src/gameboy/cpu/misc.rs b/deemgee/src/gameboy/cpu/misc.rs index 3396aec..345e028 100644 --- a/deemgee/src/gameboy/cpu/misc.rs +++ b/deemgee/src/gameboy/cpu/misc.rs @@ -12,7 +12,7 @@ opcode!(nop, 0x00, "NOP", false, { opcode!(di, 0xF3, "DI", false, { 0 => { - state.interrupts.ime = false; + state.interrupts.cpu_set_ime(false); state.registers.opcode_bytecount = Some(1); CycleResult::Finished } @@ -20,8 +20,20 @@ opcode!(di, 0xF3, "DI", false, { opcode!(ei, 0xFB, "EI", false, { 0 => { - state.interrupts.ime = true; + state.interrupts.cpu_set_ime(true); state.registers.opcode_bytecount = Some(1); CycleResult::Finished } }); + +opcode!(halt, 0x76, "HALT", false, { + 0 => { + if !state.interrupts.ime && (state.interrupts.interrupt_enable & state.interrupts.interrupt_flag & 0x1F != 0) { + state.halt_bug = true; + } else { + state.halt = true; + } + state.registers.opcode_bytecount = Some(1); + CycleResult::Finished + } +}); diff --git a/deemgee/src/gameboy/cpu/prefixed.rs b/deemgee/src/gameboy/cpu/prefixed.rs index 71c5ca7..0ac5177 100644 --- a/deemgee/src/gameboy/cpu/prefixed.rs +++ b/deemgee/src/gameboy/cpu/prefixed.rs @@ -26,6 +26,13 @@ pub fn prefixed_handler(state: &mut Gameboy) -> CycleResult { 0x14 => rl_h, 0x15 => rl_l, 0x17 => rl_a, + 0x18 => rr_b, + 0x19 => rr_c, + 0x1a => rr_d, + 0x1b => rr_e, + 0x1c => rr_h, + 0x1d => rr_l, + 0x1f => rr_a, 0x30 => swap_b, 0x31 => swap_c, 0x32 => swap_d, @@ -33,6 +40,13 @@ pub fn prefixed_handler(state: &mut Gameboy) -> CycleResult { 0x34 => swap_h, 0x35 => swap_l, 0x37 => swap_a, + 0x38 => srl_b, + 0x39 => srl_c, + 0x3a => srl_d, + 0x3b => srl_e, + 0x3c => srl_h, + 0x3d => srl_l, + 0x3f => srl_a, 0x40 => bit_0_b, 0x41 => bit_0_c, 0x42 => bit_0_d, @@ -97,6 +111,134 @@ pub fn prefixed_handler(state: &mut Gameboy) -> CycleResult { 0x7d => bit_7_l, 0x7e => bit_7_deref_hl, 0x7f => bit_7_a, + 0x80 => res_0_b, + 0x81 => res_0_c, + 0x82 => res_0_d, + 0x83 => res_0_e, + 0x84 => res_0_h, + 0x85 => res_0_l, + 0x86 => res_0_deref_hl, + 0x87 => res_0_a, + 0x88 => res_1_b, + 0x89 => res_1_c, + 0x8A => res_1_d, + 0x8B => res_1_e, + 0x8C => res_1_h, + 0x8D => res_1_l, + 0x8E => res_1_deref_hl, + 0x8F => res_1_a, + 0x90 => res_2_b, + 0x91 => res_2_c, + 0x92 => res_2_d, + 0x93 => res_2_e, + 0x94 => res_2_h, + 0x95 => res_2_l, + 0x96 => res_2_deref_hl, + 0x97 => res_2_a, + 0x98 => res_3_b, + 0x99 => res_3_c, + 0x9A => res_3_d, + 0x9B => res_3_e, + 0x9C => res_3_h, + 0x9D => res_3_l, + 0x9E => res_3_deref_hl, + 0x9F => res_3_a, + 0xA0 => res_4_b, + 0xA1 => res_4_c, + 0xA2 => res_4_d, + 0xA3 => res_4_e, + 0xA4 => res_4_h, + 0xA5 => res_4_l, + 0xA6 => res_4_deref_hl, + 0xA7 => res_4_a, + 0xA8 => res_5_b, + 0xA9 => res_5_c, + 0xAA => res_5_d, + 0xAB => res_5_e, + 0xAC => res_5_h, + 0xAD => res_5_l, + 0xAE => res_5_deref_hl, + 0xAF => res_5_a, + 0xB0 => res_6_b, + 0xB1 => res_6_c, + 0xB2 => res_6_d, + 0xB3 => res_6_e, + 0xB4 => res_6_h, + 0xB5 => res_6_l, + 0xB6 => res_6_deref_hl, + 0xB7 => res_6_a, + 0xB8 => res_7_b, + 0xB9 => res_7_c, + 0xBA => res_7_d, + 0xBB => res_7_e, + 0xBC => res_7_h, + 0xBD => res_7_l, + 0xBE => res_7_deref_hl, + 0xBF => res_7_a, + 0xC0 => set_0_b, + 0xC1 => set_0_c, + 0xC2 => set_0_d, + 0xC3 => set_0_e, + 0xC4 => set_0_h, + 0xC5 => set_0_l, + 0xC6 => set_0_deref_hl, + 0xC7 => set_0_a, + 0xC8 => set_1_b, + 0xC9 => set_1_c, + 0xCA => set_1_d, + 0xCB => set_1_e, + 0xCC => set_1_h, + 0xCD => set_1_l, + 0xCE => set_1_deref_hl, + 0xCF => set_1_a, + 0xD0 => set_2_b, + 0xD1 => set_2_c, + 0xD2 => set_2_d, + 0xD3 => set_2_e, + 0xD4 => set_2_h, + 0xD5 => set_2_l, + 0xD6 => set_2_deref_hl, + 0xD7 => set_2_a, + 0xD8 => set_3_b, + 0xD9 => set_3_c, + 0xDA => set_3_d, + 0xDB => set_3_e, + 0xDC => set_3_h, + 0xDD => set_3_l, + 0xDE => set_3_deref_hl, + 0xDF => set_3_a, + 0xE0 => set_4_b, + 0xE1 => set_4_c, + 0xE2 => set_4_d, + 0xE3 => set_4_e, + 0xE4 => set_4_h, + 0xE5 => set_4_l, + 0xE6 => set_4_deref_hl, + 0xE7 => set_4_a, + 0xE8 => set_5_b, + 0xE9 => set_5_c, + 0xEA => set_5_d, + 0xEB => set_5_e, + 0xEC => set_5_h, + 0xED => set_5_l, + 0xEE => set_5_deref_hl, + 0xEF => set_5_a, + 0xF0 => set_6_b, + 0xF1 => set_6_c, + 0xF2 => set_6_d, + 0xF3 => set_6_e, + 0xF4 => set_6_h, + 0xF5 => set_6_l, + 0xF6 => set_6_deref_hl, + 0xF7 => set_6_a, + 0xF8 => set_7_b, + 0xF9 => set_7_c, + 0xFA => set_7_d, + 0xFB => set_7_e, + 0xFC => set_7_h, + 0xFD => set_7_l, + 0xFE => set_7_deref_hl, + 0xFF => set_7_a, unknown => panic!( "Unrecognized prefixed opcode: {:#X}\nRegisters: {:#?}", unknown, state.registers @@ -109,7 +251,7 @@ pub fn prefixed_handler(state: &mut Gameboy) -> CycleResult { macro_rules! define_bit_reg { ($op:literal, $bit:literal, $reg:ident) => { paste::paste! { - opcode!([], $op, std::concat!("BIT ", std::stringify!($bit), ",", std::stringify!($reg)), false, { + opcode!([], $op, std::concat!("BIT ", std::stringify!($bit), ",", std::stringify!($reg)), true, { 1 => { state.registers.set_zero(state.registers.$reg & (1 << $bit) == 0); state.registers.set_subtract(false); @@ -182,7 +324,7 @@ define_bit_reg!(0x7f, 7, a); macro_rules! define_bit_deref_hl { ($op:literal, $bit:literal) => { paste::paste! { - opcode!([], $op, std::concat!("BIT ", std::stringify!($bit), ",(HL)"), false, { + opcode!([], $op, std::concat!("BIT ", std::stringify!($bit), ",(HL)"), true, { 1 => { state.cpu_read_u8(state.registers.get_hl()); CycleResult::NeedsMore @@ -212,7 +354,7 @@ define_bit_deref_hl!(0x7e, 7); macro_rules! define_rl_reg { ($op:literal, $reg:ident) => { paste::paste! { - opcode!([], $op, std::concat!("RL ", std::stringify!($reg)), false, { + opcode!([], $op, std::concat!("RL ", std::stringify!($reg)), true, { 1 => { let carry = state.registers.$reg >> 7 == 1; state.registers.$reg <<= 1; @@ -244,7 +386,7 @@ define_rl_reg!(0x17, a); macro_rules! define_swap_reg { ($op:literal, $reg:ident) => { paste::paste! { - opcode!([], $op, std::concat!("SWAP ", std::stringify!($reg)), false, { + opcode!([], $op, std::concat!("SWAP ", std::stringify!($reg)), true, { 1 => { state.registers.$reg = (state.registers.$reg >> 4) | (state.registers.$reg << 4); @@ -267,3 +409,263 @@ define_swap_reg!(0x33, e); define_swap_reg!(0x34, h); define_swap_reg!(0x35, l); define_swap_reg!(0x37, a); + +macro_rules! define_srl_reg { + ($op:literal, $reg:ident) => { + paste::paste! { + opcode!([], $op, std::concat!("SRL ", std::stringify!($reg)), true, { + 1 => { + let carry = state.registers.$reg & 0b1 == 1; + state.registers.$reg >>= 1; + + state.registers.set_zero(state.registers.$reg == 0); + state.registers.set_subtract(false); + state.registers.set_half_carry(false); + state.registers.set_carry(carry); + state.registers.opcode_bytecount = Some(2); + CycleResult::Finished + } + }); + } + }; +} + +define_srl_reg!(0x38, b); +define_srl_reg!(0x39, c); +define_srl_reg!(0x3a, d); +define_srl_reg!(0x3b, e); +define_srl_reg!(0x3c, h); +define_srl_reg!(0x3d, l); +define_srl_reg!(0x3f, a); + +macro_rules! define_rr_reg { + ($op:literal, $reg:ident) => { + paste::paste! { + opcode!([], $op, std::concat!("RR ", std::stringify!($reg)), true, { + 1 => { + let carry = state.registers.$reg & 0b1 == 1; + state.registers.$reg >>= 1; + + state.registers.set_zero(state.registers.$reg == 0); + state.registers.set_subtract(false); + state.registers.set_half_carry(false); + state.registers.set_carry(carry); + state.registers.opcode_bytecount = Some(2); + CycleResult::Finished + } + }); + } + }; +} + +define_rr_reg!(0x18, b); +define_rr_reg!(0x19, c); +define_rr_reg!(0x1a, d); +define_rr_reg!(0x1b, e); +define_rr_reg!(0x1c, h); +define_rr_reg!(0x1d, l); +define_rr_reg!(0x1f, a); + +macro_rules! define_res_idx_reg { + ($op:literal, $idx:literal, $reg:ident) => { + paste::paste! { + opcode!([], $op, std::concat!("RES ", std::stringify!($idx), ", ", std::stringify!($reg)), true, { + 1 => { + state.registers.$reg &= !(1u8 << $idx); + state.registers.opcode_bytecount = Some(2); + CycleResult::Finished + } + }); + } + }; +} + +define_res_idx_reg!(0x80, 0, b); +define_res_idx_reg!(0x81, 0, c); +define_res_idx_reg!(0x82, 0, d); +define_res_idx_reg!(0x83, 0, e); +define_res_idx_reg!(0x84, 0, h); +define_res_idx_reg!(0x85, 0, l); +define_res_idx_reg!(0x87, 0, a); +define_res_idx_reg!(0x88, 1, b); +define_res_idx_reg!(0x89, 1, c); +define_res_idx_reg!(0x8a, 1, d); +define_res_idx_reg!(0x8b, 1, e); +define_res_idx_reg!(0x8c, 1, h); +define_res_idx_reg!(0x8d, 1, l); +define_res_idx_reg!(0x8f, 1, a); +define_res_idx_reg!(0x90, 2, b); +define_res_idx_reg!(0x91, 2, c); +define_res_idx_reg!(0x92, 2, d); +define_res_idx_reg!(0x93, 2, e); +define_res_idx_reg!(0x94, 2, h); +define_res_idx_reg!(0x95, 2, l); +define_res_idx_reg!(0x97, 2, a); +define_res_idx_reg!(0x98, 3, b); +define_res_idx_reg!(0x99, 3, c); +define_res_idx_reg!(0x9a, 3, d); +define_res_idx_reg!(0x9b, 3, e); +define_res_idx_reg!(0x9c, 3, h); +define_res_idx_reg!(0x9d, 3, l); +define_res_idx_reg!(0x9f, 3, a); +define_res_idx_reg!(0xa0, 4, b); +define_res_idx_reg!(0xa1, 4, c); +define_res_idx_reg!(0xa2, 4, d); +define_res_idx_reg!(0xa3, 4, e); +define_res_idx_reg!(0xa4, 4, h); +define_res_idx_reg!(0xa5, 4, l); +define_res_idx_reg!(0xa7, 4, a); +define_res_idx_reg!(0xa8, 5, b); +define_res_idx_reg!(0xa9, 5, c); +define_res_idx_reg!(0xaa, 5, d); +define_res_idx_reg!(0xab, 5, e); +define_res_idx_reg!(0xac, 5, h); +define_res_idx_reg!(0xad, 5, l); +define_res_idx_reg!(0xaf, 5, a); +define_res_idx_reg!(0xb0, 6, b); +define_res_idx_reg!(0xb1, 6, c); +define_res_idx_reg!(0xb2, 6, d); +define_res_idx_reg!(0xb3, 6, e); +define_res_idx_reg!(0xb4, 6, h); +define_res_idx_reg!(0xb5, 6, l); +define_res_idx_reg!(0xb7, 6, a); +define_res_idx_reg!(0xb8, 7, b); +define_res_idx_reg!(0xb9, 7, c); +define_res_idx_reg!(0xba, 7, d); +define_res_idx_reg!(0xbb, 7, e); +define_res_idx_reg!(0xbc, 7, h); +define_res_idx_reg!(0xbd, 7, l); +define_res_idx_reg!(0xbf, 7, a); + +macro_rules! define_res_idx_deref_hl { + ($op:literal, $idx:literal) => { + paste::paste! { + opcode!([], $op, std::concat!("RES ", std::stringify!($idx), ", (HL)"), true, { + 1 => { + state.cpu_read_u8(state.registers.get_hl()); + CycleResult::NeedsMore + }, + 2 => { + let res = state.registers.take_mem() & !(1u8 << $idx); + state.cpu_write_u8(state.registers.get_hl(), res); + CycleResult::NeedsMore + }, + 3 => { + state.registers.opcode_bytecount = Some(2); + CycleResult::Finished + } + }); + } + }; +} + +define_res_idx_deref_hl!(0x86, 0); +define_res_idx_deref_hl!(0x8E, 1); +define_res_idx_deref_hl!(0x96, 2); +define_res_idx_deref_hl!(0x9E, 3); +define_res_idx_deref_hl!(0xA6, 4); +define_res_idx_deref_hl!(0xAE, 5); +define_res_idx_deref_hl!(0xB6, 6); +define_res_idx_deref_hl!(0xBE, 7); + +macro_rules! define_set_idx_reg { + ($op:literal, $idx:literal, $reg:ident) => { + paste::paste! { + opcode!([], $op, std::concat!("SET ", std::stringify!($idx), ", ", std::stringify!($reg)), true, { + 1 => { + state.registers.$reg |= 1u8 << $idx; + state.registers.opcode_bytecount = Some(2); + CycleResult::Finished + } + }); + } + }; +} + +define_set_idx_reg!(0xc0, 0, b); +define_set_idx_reg!(0xc1, 0, c); +define_set_idx_reg!(0xc2, 0, d); +define_set_idx_reg!(0xc3, 0, e); +define_set_idx_reg!(0xc4, 0, h); +define_set_idx_reg!(0xc5, 0, l); +define_set_idx_reg!(0xc7, 0, a); +define_set_idx_reg!(0xc8, 1, b); +define_set_idx_reg!(0xc9, 1, c); +define_set_idx_reg!(0xca, 1, d); +define_set_idx_reg!(0xcb, 1, e); +define_set_idx_reg!(0xcc, 1, h); +define_set_idx_reg!(0xcd, 1, l); +define_set_idx_reg!(0xcf, 1, a); +define_set_idx_reg!(0xd0, 2, b); +define_set_idx_reg!(0xd1, 2, c); +define_set_idx_reg!(0xd2, 2, d); +define_set_idx_reg!(0xd3, 2, e); +define_set_idx_reg!(0xd4, 2, h); +define_set_idx_reg!(0xd5, 2, l); +define_set_idx_reg!(0xd7, 2, a); +define_set_idx_reg!(0xd8, 3, b); +define_set_idx_reg!(0xd9, 3, c); +define_set_idx_reg!(0xda, 3, d); +define_set_idx_reg!(0xdb, 3, e); +define_set_idx_reg!(0xdc, 3, h); +define_set_idx_reg!(0xdd, 3, l); +define_set_idx_reg!(0xdf, 3, a); +define_set_idx_reg!(0xe0, 4, b); +define_set_idx_reg!(0xe1, 4, c); +define_set_idx_reg!(0xe2, 4, d); +define_set_idx_reg!(0xe3, 4, e); +define_set_idx_reg!(0xe4, 4, h); +define_set_idx_reg!(0xe5, 4, l); +define_set_idx_reg!(0xe7, 4, a); +define_set_idx_reg!(0xe8, 5, b); +define_set_idx_reg!(0xe9, 5, c); +define_set_idx_reg!(0xea, 5, d); +define_set_idx_reg!(0xeb, 5, e); +define_set_idx_reg!(0xec, 5, h); +define_set_idx_reg!(0xed, 5, l); +define_set_idx_reg!(0xef, 5, a); +define_set_idx_reg!(0xf0, 6, b); +define_set_idx_reg!(0xf1, 6, c); +define_set_idx_reg!(0xf2, 6, d); +define_set_idx_reg!(0xf3, 6, e); +define_set_idx_reg!(0xf4, 6, h); +define_set_idx_reg!(0xf5, 6, l); +define_set_idx_reg!(0xf7, 6, a); +define_set_idx_reg!(0xf8, 7, b); +define_set_idx_reg!(0xf9, 7, c); +define_set_idx_reg!(0xfa, 7, d); +define_set_idx_reg!(0xfb, 7, e); +define_set_idx_reg!(0xfc, 7, h); +define_set_idx_reg!(0xfd, 7, l); +define_set_idx_reg!(0xff, 7, a); + +macro_rules! define_set_idx_deref_hl { + ($op:literal, $idx:literal) => { + paste::paste! { + opcode!([], $op, std::concat!("SET ", std::stringify!($idx), ", (HL)"), true, { + 1 => { + state.cpu_read_u8(state.registers.get_hl()); + CycleResult::NeedsMore + }, + 2 => { + let res = state.registers.take_mem() | (1u8 << $idx); + state.cpu_write_u8(state.registers.get_hl(), res); + CycleResult::NeedsMore + }, + 3 => { + state.registers.opcode_bytecount = Some(2); + CycleResult::Finished + } + }); + } + }; +} + +define_set_idx_deref_hl!(0xC6, 0); +define_set_idx_deref_hl!(0xCE, 1); +define_set_idx_deref_hl!(0xD6, 2); +define_set_idx_deref_hl!(0xDE, 3); +define_set_idx_deref_hl!(0xE6, 4); +define_set_idx_deref_hl!(0xEE, 5); +define_set_idx_deref_hl!(0xF6, 6); +define_set_idx_deref_hl!(0xFE, 7); diff --git a/deemgee/src/gameboy/interrupts.rs b/deemgee/src/gameboy/interrupts.rs index f08742b..0a43f61 100644 --- a/deemgee/src/gameboy/interrupts.rs +++ b/deemgee/src/gameboy/interrupts.rs @@ -16,13 +16,14 @@ macro_rules! define_bitfield_u8_gs { pub struct Interrupts { pub ime: bool, + pub ei_queued: bool, pub interrupt_enable: u8, pub interrupt_flag: u8, } impl Interrupts { pub fn new() -> Self { - Self { ime: false, interrupt_enable: 0, interrupt_flag: 0 } + Self { ime: false, interrupt_enable: 0, interrupt_flag: 0, ei_queued: false } } define_bitfield_u8_gs!(ie_vblank, 0, interrupt_enable); @@ -35,4 +36,11 @@ impl Interrupts { define_bitfield_u8_gs!(if_timer, 2, interrupt_flag); define_bitfield_u8_gs!(if_serial, 3, interrupt_flag); define_bitfield_u8_gs!(if_joypad, 4, interrupt_flag); + + pub fn cpu_set_ime(&mut self, val: bool) { + self.ei_queued = val; + if !val { + self.ime = false; + } + } } diff --git a/deemgee/src/gameboy/joypad.rs b/deemgee/src/gameboy/joypad.rs index e55d4ce..7347bef 100644 --- a/deemgee/src/gameboy/joypad.rs +++ b/deemgee/src/gameboy/joypad.rs @@ -4,6 +4,19 @@ pub enum JoypadMode { Direction, } +macro_rules! joypad_input { + ($input:ident, $mode:ident) => { + paste::paste! { + pub fn [](&mut self, val: bool) { + if val && self.mode == JoypadMode::$mode { + self.interrupt_triggered = true; + } + self.$input = val + } + } + }; +} + pub struct Joypad { mode: JoypadMode, pub down: bool, @@ -14,6 +27,7 @@ pub struct Joypad { pub select: bool, pub b: bool, pub a: bool, + pub interrupt_triggered: bool, } impl Joypad { @@ -28,6 +42,7 @@ impl Joypad { select: false, b: false, a: false, + interrupt_triggered: false, } } @@ -50,6 +65,15 @@ impl Joypad { } } + joypad_input!(a, Action); + joypad_input!(b, Action); + joypad_input!(start, Action); + joypad_input!(select, Action); + joypad_input!(up, Direction); + joypad_input!(down, Direction); + joypad_input!(left, Direction); + joypad_input!(right, Direction); + pub fn cpu_write(&mut self, content: u8) { if (content >> 5) & 0b1 == 0 { self.mode = JoypadMode::Action; diff --git a/deemgee/src/gameboy/mapper.rs b/deemgee/src/gameboy/mapper.rs index 9061e5c..8c09e6c 100644 --- a/deemgee/src/gameboy/mapper.rs +++ b/deemgee/src/gameboy/mapper.rs @@ -1,3 +1,5 @@ +pub mod mbc1; + pub trait Mapper { fn read_rom_u8(&self, address: u16) -> u8; fn write_rom_u8(&mut self, address: u16, value: u8); diff --git a/deemgee/src/gameboy/mapper/mbc1.rs b/deemgee/src/gameboy/mapper/mbc1.rs new file mode 100644 index 0000000..4e378cb --- /dev/null +++ b/deemgee/src/gameboy/mapper/mbc1.rs @@ -0,0 +1,114 @@ +use super::Mapper; + +pub struct MBC1 { + rom: Vec, + ram: Option>, + rom_bank_count: u8, + ram_bank_count: u8, + ram_enabled: bool, + rom_bank_number: u8, + extra_2_bit_reg: u8, + banking_mode_select: bool, +} + +impl MBC1 { + pub fn new(data: Vec) -> Self { + let rom_bank_count = 0b10 << data[0x148]; + let ram_bank_count = match data[0x149] { + 0 | 1 => 0, + 2 => 1, + 3 => 4, + 4 => 16, + 5 => 8, + _ => panic!("Bad RAM bank count for MBC1"), + }; + + let ram = match ram_bank_count { + 0 | 1 => None, + 2 | 3 | 4 | 5 => Some(vec![0u8; ram_bank_count as usize * (8 * 1024)]), + _ => panic!("Bad RAM bank count for MBC1"), + }; + + assert_eq!(data.len(), rom_bank_count as usize * (16 * 1024)); + + Self { + rom: data, + rom_bank_count, + ram_enabled: false, + rom_bank_number: 1, + extra_2_bit_reg: 0, + ram, + ram_bank_count, + banking_mode_select: false, + } + } + + fn set_ram_enabled(&mut self, val: u8) { + self.ram_enabled = val & 0xA != 0; + } + + fn set_rom_bank_number(&mut self, mut val: u8) { + if val == 0 { + val = 1; + } + self.rom_bank_number = val & 0b1_1111; + } + + fn set_extra_2_bit_reg(&mut self, val: u8) { + self.extra_2_bit_reg = val & 0b11; + } + + fn set_banking_mode_select(&mut self, val: u8) { + self.banking_mode_select = val & 0b1 == 1; + } + + fn is_large_rom(&self) -> bool { + self.rom_bank_count > 4 + } + + fn is_large_ram(&self) -> bool { + self.ram_bank_count > 2 + } +} + +impl Mapper for MBC1 { + fn read_rom_u8(&self, address: u16) -> u8 { + if address <= 0x3FFF { + self.rom[if self.is_large_rom() && self.banking_mode_select { + ((self.extra_2_bit_reg << 5) as usize * 0x4000) + address as usize + } else { + address as usize + }] + } else { + self.rom[if self.is_large_rom() { + (self.rom_bank_number | (self.extra_2_bit_reg << 5)) as usize * 0x4000 + } else { + self.rom_bank_number as usize * 0x4000 + }] + } + } + + fn write_rom_u8(&mut self, address: u16, value: u8) { + match address { + 0..=0x1FFF => self.set_ram_enabled(value), + 0x2000..=0x3FFF => self.set_rom_bank_number(value), + 0x4000..=0x5FFF => self.set_extra_2_bit_reg(value), + 0x6000..=0x7FFF => self.set_banking_mode_select(value), + _ => unreachable!(), + } + } + + fn read_eram_u8(&self, address: u16) -> u8 { + match self.ram.as_ref() { + Some(ram) => unimplemented!(), + None => 0, + } + } + + fn write_eram_u8(&mut self, address: u16, value: u8) { + match self.ram.as_ref() { + Some(ram) => unimplemented!(), + None => {} + } + } +} diff --git a/deemgee/src/gameboy/ppu.rs b/deemgee/src/gameboy/ppu.rs index 14e1855..ccf7784 100644 --- a/deemgee/src/gameboy/ppu.rs +++ b/deemgee/src/gameboy/ppu.rs @@ -317,18 +317,18 @@ impl Ppu { } if entry.y_flip() { - tile_y_idx = 8 - tile_y_idx; + tile_y_idx = 7 - tile_y_idx; } let tile_idx = if is_second_tile { entry.tile_idx | 1 } else { entry.tile_idx & 0xFE }; for x in 0..8 { - let fb_x = entry.x.overflowing_sub(8 - x).0; + let fb_x = entry.x.overflowing_sub(7 - x).0; let sprite_line_base = fb_x as usize * 4; - let tile_x_idx = if entry.x_flip() { 8 - x } else { x }; + let tile_x_idx = if entry.x_flip() { 7 - x } else { x }; let color = Self::parse_sprite_tile_color( self.read_obj_tile(tile_idx), @@ -386,7 +386,7 @@ impl Ppu { } fn set_mode(&mut self, interrupts: &mut Interrupts, mode: PPUMode) { - log::debug!("PPU switching mode to {:?} @ {}", mode, self.cycle_counter); + log::trace!("PPU switching mode to {:?} @ {}", mode, self.cycle_counter); self.stat &= !0b11; self.stat |= mode.mode_flag(); self.cycle_counter = 0; diff --git a/deemgee/src/gameboy/serial.rs b/deemgee/src/gameboy/serial.rs index 7c9dfe5..c06612d 100644 --- a/deemgee/src/gameboy/serial.rs +++ b/deemgee/src/gameboy/serial.rs @@ -1,11 +1,15 @@ +use std::io::Write; + pub struct Serial { pub sb: u8, pub sc: u8, + + internal_tick: u16, } impl Serial { pub fn new() -> Serial { - Self { sb: 0, sc: 0 } + Self { sb: 0, sc: 0, internal_tick: 0 } } pub fn set_transfer_in_process(&mut self, value: bool) { @@ -25,4 +29,20 @@ impl Serial { self.sc &= !0b1; self.sc |= conductor as u8; } + + pub fn tick(&mut self) -> bool { + if self.get_transfer_in_process() && self.is_conductor() { + if self.internal_tick < 128 { + self.internal_tick += 1; + } else { + print!("{}", self.sb as char); + std::io::stdout().flush().expect("flushing stdout failed"); + self.sb = 0; + self.set_transfer_in_process(false); + self.internal_tick = 0; + return true; + } + } + false + } } diff --git a/deemgee/src/gameboy/timer.rs b/deemgee/src/gameboy/timer.rs index 3310ce8..4f299f5 100644 --- a/deemgee/src/gameboy/timer.rs +++ b/deemgee/src/gameboy/timer.rs @@ -30,7 +30,7 @@ impl Timer { if self.enable { self.tima_counter = self.tima_counter.overflowing_add(1).0; if self.tima_counter >= self.clock.cycles() { - self.tima += self.tima.overflowing_add(1).0; + self.tima = self.tima.overflowing_add(1).0; return self.tima == 0; } @@ -80,7 +80,7 @@ impl TimerClock { 1 => Self::C16, 2 => Self::C64, 3 => Self::C256, - 4 => Self::C1024, + 0 => Self::C1024, _ => unreachable!(), } } diff --git a/deemgee/src/main.rs b/deemgee/src/main.rs index d05e752..bfdcb64 100644 --- a/deemgee/src/main.rs +++ b/deemgee/src/main.rs @@ -103,14 +103,16 @@ pub fn run_gameboy( 'outer: loop { while let Ok(event) = rx.try_recv() { match event { - window::WindowEvent::AToggle => gameboy.joypad.a = !gameboy.joypad.a, - window::WindowEvent::BToggle => gameboy.joypad.b = !gameboy.joypad.b, - window::WindowEvent::SelectToggle => gameboy.joypad.select = !gameboy.joypad.select, - window::WindowEvent::StartToggle => gameboy.joypad.start = !gameboy.joypad.start, - window::WindowEvent::UpToggle => gameboy.joypad.up = !gameboy.joypad.up, - window::WindowEvent::DownToggle => gameboy.joypad.down = !gameboy.joypad.down, - window::WindowEvent::LeftToggle => gameboy.joypad.left = !gameboy.joypad.left, - window::WindowEvent::RightToggle => gameboy.joypad.right = !gameboy.joypad.right, + window::WindowEvent::AToggle => gameboy.joypad.set_a(!gameboy.joypad.a), + window::WindowEvent::BToggle => gameboy.joypad.set_b(!gameboy.joypad.b), + window::WindowEvent::SelectToggle => { + gameboy.joypad.set_select(!gameboy.joypad.select) + } + window::WindowEvent::StartToggle => gameboy.joypad.set_start(!gameboy.joypad.start), + window::WindowEvent::UpToggle => gameboy.joypad.set_up(!gameboy.joypad.up), + window::WindowEvent::DownToggle => gameboy.joypad.set_down(!gameboy.joypad.down), + window::WindowEvent::LeftToggle => gameboy.joypad.set_left(!gameboy.joypad.left), + window::WindowEvent::RightToggle => gameboy.joypad.set_right(!gameboy.joypad.right), window::WindowEvent::PauseToggle => paused = !paused, window::WindowEvent::LogToggle => { gameboy.log_instructions = !gameboy.log_instructions