From 5e0c5f085ef3ff298ab939fba50ef954f988fb0f Mon Sep 17 00:00:00 2001 From: Ivan Izaguirre Date: Mon, 30 Oct 2023 23:36:53 +0100 Subject: [PATCH] Support maskable interrupts mode 1 --- src/cpu.rs | 37 +++++++++++++++++++++++++++++++------ src/decoder_8080.rs | 4 ++-- src/decoder_z80.rs | 4 ++-- src/environment.rs | 5 +++++ src/opcode.rs | 17 +++++++++++++---- src/opcode_ld.rs | 4 ++++ src/registers.rs | 21 +++++++++++++++++++-- src/state.rs | 6 ++++++ 8 files changed, 82 insertions(+), 16 deletions(-) diff --git a/src/cpu.rs b/src/cpu.rs index a989fce..3c76873 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -6,6 +6,7 @@ use super::opcode::*; use super::registers::*; use super::state::*; +const IRQ_ADDRESS: u16 = 0x0036; const NMI_ADDRESS: u16 = 0x0066; /// The Z80 cpu emulator. @@ -74,18 +75,33 @@ impl Cpu { if env.state.reset_pending { env.state.reset_pending = false; env.state.nmi_pending = false; + env.state.int_pending = false; env.state.halted = false; - env.state.reg.set_pc(0x0000); - env.state.reg.set8(Reg8::I, 0x00); - env.state.reg.set8(Reg8::R, 0x00); - env.state.reg.set_interrupts(false); - env.state.reg.set_interrupt_mode(0); + env.state.reg.reset(); + env.state.cycle = env.state.cycle.wrapping_add(3); } else if env.state.nmi_pending { env.state.nmi_pending = false; env.state.halted = false; env.state.reg.start_nmi(); + env.state.cycle = env.state.cycle.wrapping_add(11); env.subroutine_call(NMI_ADDRESS); + } else if env.state.int_pending { + let (int_enabled, int_mode) = env.state.reg.get_interrupt_mode(); + if int_enabled && !env.state.int_just_enabled { + env.state.int_pending = false; + env.state.halted = false; + env.state.reg.set_interrupts(false); + match int_mode { + 0 => panic!("Interrupt mode 0 not implemented"), + 1 => { + env.state.cycle = env.state.cycle.wrapping_add(13); + env.subroutine_call(IRQ_ADDRESS); + }, + 2 => panic!("Interrupt mode 2 not implemented"), + _ => panic!("Invalid interrupt mode") + } + } } let pc = env.state.reg.pc(); @@ -95,6 +111,7 @@ impl Cpu { } env.clear_branch_taken(); + env.clear_int_just_enabled(); opcode.execute(&mut env); env.advance_cycles(opcode); env.clear_index(); @@ -151,7 +168,15 @@ impl Cpu { /// Returns if the Cpu has executed a HALT pub fn is_halted(&self) -> bool { - self.state.halted && !self.state.nmi_pending && !self.state.reset_pending + self.state.halted + && !self.state.nmi_pending + && !self.state.reset_pending + && !self.state.int_pending + } + + /// Maskable interrupt request + pub fn signal_interrupt(&mut self) { + self.state.int_pending = true } /// Non maskable interrupt request diff --git a/src/decoder_8080.rs b/src/decoder_8080.rs index 63442df..79b2ddc 100644 --- a/src/decoder_8080.rs +++ b/src/decoder_8080.rs @@ -131,8 +131,8 @@ impl Decoder8080 { 3 => Some(build_in_a_n()), // IN A, (n) 4 => Some(build_ex_psp_hl()), // EX (SP), HL 5 => Some(build_ex_de_hl()), // EX DE, HL - 6 => Some(build_conf_interrupts(false)), // DI - 7 => Some(build_conf_interrupts(true)), // EI + 6 => Some(build_disable_interrupts()), // DI + 7 => Some(build_enable_interrupts()), // EI _ => panic!("Unreachable") } 4 => Some(build_call_eq(CC[p.y])), diff --git a/src/decoder_z80.rs b/src/decoder_z80.rs index 07335c5..7f479ee 100644 --- a/src/decoder_z80.rs +++ b/src/decoder_z80.rs @@ -237,8 +237,8 @@ impl DecoderZ80 { 3 => Some(build_in_a_n()), // IN A, (n) 4 => Some(build_ex_psp_hl()), // EX (SP), HL 5 => Some(build_ex_de_hl()), // EX DE, HL - 6 => Some(build_conf_interrupts(false)), // DI - 7 => Some(build_conf_interrupts(true)), // EI + 6 => Some(build_disable_interrupts()), // DI + 7 => Some(build_enable_interrupts()), // EI _ => panic!("Unreachable") } 4 => Some(build_call_eq(CC[p.y])), diff --git a/src/environment.rs b/src/environment.rs index 7474732..87344d0 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -89,6 +89,11 @@ impl <'a> Environment<'_> { self.state.branch_taken = false; } + pub fn clear_int_just_enabled(&mut self) { + self.state.int_just_enabled = false; + } + + pub fn set_branch_taken(&mut self) { self.state.branch_taken = true; } diff --git a/src/opcode.rs b/src/opcode.rs index d7d0275..8b91cdf 100644 --- a/src/opcode.rs +++ b/src/opcode.rs @@ -100,12 +100,21 @@ pub fn build_push_rr(rr: Reg16) -> Opcode { ) } -pub fn build_conf_interrupts(enable: bool) -> Opcode { - let name = if enable {"EI"} else {"DI"}; +pub fn build_disable_interrupts() -> Opcode { Opcode::new( - name.to_string(), + "DI".to_string(), Box::new(move |env: &mut Environment| { - env.state.reg.set_interrupts(enable); + env.state.reg.set_interrupts(false); + }) + ) +} + +pub fn build_enable_interrupts() -> Opcode { + Opcode::new( + "EI".to_string(), + Box::new(move |env: &mut Environment| { + env.state.reg.set_interrupts(true); + env.state.int_just_enabled = true; }) ) } diff --git a/src/opcode_ld.rs b/src/opcode_ld.rs index c0f131a..3117734 100644 --- a/src/opcode_ld.rs +++ b/src/opcode_ld.rs @@ -60,6 +60,10 @@ pub fn build_ld_r_r(dst: Reg8, src: Reg8, _special: bool) -> Opcode { Box::new(move |env: &mut Environment| { let value = env.state.reg.get8(src); env.state.reg.set8(dst, value); + if dst == Reg8::A && (src == Reg8::I || src == Reg8::R) { + // LDA A,I and LDA A,R copy the IFF2 flag into the P flag + env.state.reg.update_p_flag_with_iff2(); + } }) ) } else { diff --git a/src/registers.rs b/src/registers.rs index 6198933..143617a 100644 --- a/src/registers.rs +++ b/src/registers.rs @@ -116,11 +116,20 @@ impl Registers { mode8080: false }; - reg.set16(Reg16::AF, 0xffff); - reg.set16(Reg16::SP, 0xffff); + reg.reset(); reg } + pub(crate) fn reset(&mut self) { + self.set8(Reg8::I, 0x00); + self.set8(Reg8::R, 0x00); + self.set_interrupts(false); + self.set_interrupt_mode(0); + self.set16(Reg16::AF, 0xffff); + self.set16(Reg16::SP, 0xffff); + self.set_pc(0x0000); + } + pub(crate) fn set_8080(&mut self) { self.mode8080 = true; self.set16(Reg16::AF, 0xffff); @@ -356,6 +365,10 @@ impl Registers { } } + pub(crate) fn update_p_flag_with_iff2(&mut self) { + self.put_flag(Flag::P, self.iff2) + } + /// Returns the program counter #[inline] pub fn pc(&self) -> u16 { @@ -377,6 +390,10 @@ impl Registers { self.im = im; } + pub(crate) fn get_interrupt_mode(&self) -> (bool, u8) { + (self.iff1, self.im) + } + pub(crate) fn start_nmi(&mut self) { self.iff2 = self.iff1; self.iff1 = false; diff --git a/src/state.rs b/src/state.rs index 1b2791d..1456534 100644 --- a/src/state.rs +++ b/src/state.rs @@ -12,10 +12,14 @@ pub struct State { pub branch_taken: bool, /// Halt state of the CPU pub halted: bool, + /// Maskable interrupt signaled + pub int_pending: bool, /// Non maskable interrupt signaled pub nmi_pending: bool, /// Reset signaled pub reset_pending: bool, + /// Interrupts just enabled + pub int_just_enabled: bool, // Alternate index management pub index: Reg16, // Using HL, IX or IY pub displacement: i8, // Used for (IX+d) and (iY+d) @@ -29,8 +33,10 @@ impl State { cycle: 0, branch_taken: false, halted: false, + int_pending: false, nmi_pending: false, reset_pending: false, + int_just_enabled: false, index: Reg16::HL, displacement: 0, }