From b4ad9a3cc36575e8c81f48a607945498320d2512 Mon Sep 17 00:00:00 2001 From: Ivan Izaguirre Date: Wed, 21 Jul 2021 23:31:38 +0200 Subject: [PATCH] Support non maskable interrupts --- src/cpu.rs | 24 ++++++++++++++++++++++-- src/environment.rs | 10 ++++++++++ src/opcode_alu.rs | 4 ++-- src/opcode_jumps.rs | 24 ++++++++---------------- src/registers.rs | 11 +++++++++++ src/state.rs | 3 +++ 6 files changed, 56 insertions(+), 20 deletions(-) diff --git a/src/cpu.rs b/src/cpu.rs index 927ae8a..4d14453 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -6,6 +6,8 @@ use super::opcode::*; use super::registers::*; use super::state::*; +const NMI_ADDRESS: u16 = 0x0066; + /// The Z80 cpu emulator. /// /// Executes Z80 instructions changing the cpu State and Machine @@ -59,8 +61,21 @@ impl Cpu { /// * `sys` - A representation of the emulated machine that has the Machine trait /// pub fn execute_instruction(&mut self, sys: &mut dyn Machine) { - let pc = self.state.reg.pc(); + if self.is_halted() && !self.state.nmi_pending { + // The CPU is in HALT state. Only interrupts can execute. + return + } + let mut env = Environment::new(&mut self.state, sys); + if env.state.nmi_pending { + env.state.nmi_pending = false; + env.state.halted = false; + env.state.reg.start_nmi(); + env.subroutine_call(NMI_ADDRESS); + } + + + let pc = env.state.reg.pc(); let opcode = self.decoder.decode(&mut env); if self.trace { print!("==> {:04x}: {:20}", pc, opcode.disasm(&mut env)); @@ -81,7 +96,7 @@ impl Cpu { self.state.reg.get8(Reg8::F) ); println!(" [{:02x} {:02x} {:02x}]", sys.peek(pc), - sys.peek(pc+1), sys.peek(pc+2)); + sys.peek(pc.wrapping_add(1)), sys.peek(pc.wrapping_add(2))); } } @@ -104,6 +119,11 @@ impl Cpu { pub fn is_halted(&self) -> bool { self.state.halted } + + /// Non maskable interrupt request + pub fn signal_nmi(&mut self) { + self.state.nmi_pending = true + } } diff --git a/src/environment.rs b/src/environment.rs index 7f8b06c..c5751d3 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -66,6 +66,16 @@ impl <'a> Environment<'_> { (l as u16) + ((h as u16) << 8) } + pub fn subroutine_call(&mut self, address: u16) { + self.push(self.state.reg.pc()); + self.state.reg.set_pc(address); + } + + pub fn subroutine_return(&mut self) { + let pc = self.pop(); + self.state.reg.set_pc(pc); + } + pub fn set_index(&mut self, index: Reg16) { self.state.index = index; self.state.index_changed = true; diff --git a/src/opcode_alu.rs b/src/opcode_alu.rs index 898ca09..be48603 100644 --- a/src/opcode_alu.rs +++ b/src/opcode_alu.rs @@ -7,7 +7,7 @@ pub fn build_operator_a_r(r: Reg8, (op, name): (Operator, &str)) -> Opcode { if r != Reg8::_HL && r != Reg8::H && r != Reg8::L { // Fast version Opcode { - name: format!("{} A, {:?}", name, r), + name: format!("{} A, {}", name, r), action: Box::new(move |env: &mut Environment| { let a = env.state.reg.a(); let b = env.state.reg.get8(r); @@ -17,7 +17,7 @@ pub fn build_operator_a_r(r: Reg8, (op, name): (Operator, &str)) -> Opcode { } } else { Opcode { - name: format!("{} A, {:?}", name, r), + name: format!("{} A, {}", name, r), action: Box::new(move |env: &mut Environment| { env.load_displacement(r); diff --git a/src/opcode_jumps.rs b/src/opcode_jumps.rs index 0591722..c1d3130 100644 --- a/src/opcode_jumps.rs +++ b/src/opcode_jumps.rs @@ -87,8 +87,7 @@ pub fn build_call() -> Opcode { name: "CALL nn".to_string(), action: Box::new(move |env: &mut Environment| { let address = env.advance_immediate16(); - env.push(env.state.reg.pc()); - env.state.reg.set_pc(address); + env.subroutine_call(address); }) } } @@ -99,8 +98,7 @@ pub fn build_call_eq((flag, value, name): (Flag, bool, &str)) -> Opcode { action: Box::new(move |env: &mut Environment| { let address = env.advance_immediate16(); if env.state.reg.get_flag(flag) == value { - env.push(env.state.reg.pc()); - env.state.reg.set_pc(address); + env.subroutine_call(address); } }) } @@ -111,23 +109,18 @@ pub fn build_rst(d: u8) -> Opcode { name: format!("RST {:02x}h", d), action: Box::new(move |env: &mut Environment| { let address = d as u16; - env.push(env.state.reg.pc()); - env.state.reg.set_pc(address); + env.subroutine_call(address); }) } } // Returns -fn operation_return(env: &mut Environment) { - let pc = env.pop(); - env.state.reg.set_pc(pc); -} pub fn build_ret() -> Opcode { Opcode { name: "RET".to_string(), action: Box::new(move |env: &mut Environment| { - operation_return(env); + env.subroutine_return(); }) } } @@ -136,7 +129,7 @@ pub fn build_reti() -> Opcode { Opcode { name: "RETI".to_string(), action: Box::new(move |env: &mut Environment| { - operation_return(env); + env.subroutine_return(); }) } } @@ -145,9 +138,8 @@ pub fn build_retn() -> Opcode { Opcode { name: "RETN".to_string(), action: Box::new(move |env: &mut Environment| { - operation_return(env); - - // TODO: "The contents of IIF2 is copied back into IIF1" + env.subroutine_return(); + env.state.reg.end_nmi(); }) } } @@ -157,7 +149,7 @@ pub fn build_ret_eq((flag, value, name): (Flag, bool, &str)) -> Opcode { name: format!("RET {}", name), action: Box::new(move |env: &mut Environment| { if env.state.reg.get_flag(flag) == value { - operation_return(env); + env.subroutine_return(); } }) } diff --git a/src/registers.rs b/src/registers.rs index a908ecb..57ddc19 100644 --- a/src/registers.rs +++ b/src/registers.rs @@ -373,12 +373,23 @@ impl Registers { } pub(crate) fn set_interrupts(&mut self, v: bool) { + self.iff1 = v; self.iff2 = v; } pub(crate) fn set_interrupt_mode(&mut self, im: u8) { self.im = im; } + + pub(crate) fn start_nmi(&mut self) { + self.iff2 = self.iff1; + self.iff1 = false; + } + + pub(crate) fn end_nmi(&mut self) { + self.iff1 = self.iff2; + } + } #[cfg(test)] diff --git a/src/state.rs b/src/state.rs index 804abed..b9a28b6 100644 --- a/src/state.rs +++ b/src/state.rs @@ -9,6 +9,8 @@ pub struct State { pub reg: Registers, /// Halt state of the CPU pub halted: bool, + /// Non maskable interrupt signaled + pub nmi_pending: bool, // Alternate index management pub index: Reg16, // Using HL, IX or IY pub displacement: i8, // Used for (IX+d) and (iY+d) @@ -22,6 +24,7 @@ impl State { State { reg: Registers::new(), halted: false, + nmi_pending: false, index: Reg16::HL, displacement: 0, displacement_loaded: false,