Skip to content

Commit

Permalink
The IRQ signal is not edge triggered
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanizag committed Nov 1, 2023
1 parent 5e0c5f0 commit 2b01c13
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 9 deletions.
13 changes: 6 additions & 7 deletions src/cpu.rs
Expand Up @@ -75,7 +75,6 @@ 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.reset();
env.state.cycle = env.state.cycle.wrapping_add(3);
Expand All @@ -86,10 +85,9 @@ impl Cpu {
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 {
} else if env.state.int_signaled {
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 {
Expand Down Expand Up @@ -171,12 +169,13 @@ impl Cpu {
self.state.halted
&& !self.state.nmi_pending
&& !self.state.reset_pending
&& !self.state.int_pending
&& !self.state.int_signaled
}

/// Maskable interrupt request
pub fn signal_interrupt(&mut self) {
self.state.int_pending = true
/// Maskable interrupt request. It stays signaled until is is
/// deactivated by calling signal_interrupt(false).
pub fn signal_interrupt(&mut self, active: bool) {
self.state.int_signaled = active
}

/// Non maskable interrupt request
Expand Down
4 changes: 2 additions & 2 deletions src/state.rs
Expand Up @@ -13,7 +13,7 @@ pub struct State {
/// Halt state of the CPU
pub halted: bool,
/// Maskable interrupt signaled
pub int_pending: bool,
pub int_signaled: bool,
/// Non maskable interrupt signaled
pub nmi_pending: bool,
/// Reset signaled
Expand All @@ -33,7 +33,7 @@ impl State {
cycle: 0,
branch_taken: false,
halted: false,
int_pending: false,
int_signaled: false,
nmi_pending: false,
reset_pending: false,
int_just_enabled: false,
Expand Down
111 changes: 111 additions & 0 deletions tests/interrupts.rs
@@ -0,0 +1,111 @@
use iz80::*;

const IRQ_ADDRESS: u16 = 0x0036;

#[test]
fn test_ei() {
let mut sys = PlainMachine::new();
let mut cpu = Cpu::new_z80();

sys.poke(0x0000, 0xfb); // EI
sys.poke(0x0001, 0xed); // IM 1
sys.poke(0x0002, 0x56);

cpu.execute_instruction(&mut sys);
cpu.execute_instruction(&mut sys);
cpu.execute_instruction(&mut sys);
cpu.signal_interrupt(true);
cpu.execute_instruction(&mut sys);

assert_eq!(IRQ_ADDRESS+1, cpu.registers().pc());
}

#[test]
fn test_di() {
let mut sys = PlainMachine::new();
let mut cpu = Cpu::new_z80();

sys.poke(0x0000, 0xf3); // DI
sys.poke(0x0001, 0xed); // IM 1
sys.poke(0x0002, 0x56);

cpu.execute_instruction(&mut sys);
cpu.execute_instruction(&mut sys);
cpu.execute_instruction(&mut sys);
cpu.signal_interrupt(true);
cpu.execute_instruction(&mut sys);

assert_eq!(5, cpu.registers().pc());
}

#[test]
fn test_ei_on_handler() {
let mut sys = PlainMachine::new();
let mut cpu = Cpu::new_z80();

sys.poke(0x0000, 0xfb); // EI
sys.poke(0x0001, 0xed); // IM 1
sys.poke(0x0002, 0x56);

sys.poke(IRQ_ADDRESS, 0x00); // NOP
sys.poke(IRQ_ADDRESS+1, 0xfb); // EI
sys.poke(IRQ_ADDRESS+2, 0xed); // RETI
sys.poke(IRQ_ADDRESS+3, 0x4d);

cpu.execute_instruction(&mut sys);
cpu.execute_instruction(&mut sys);
cpu.execute_instruction(&mut sys);
cpu.signal_interrupt(true);

cpu.execute_instruction(&mut sys);
// On the handler, NOP executed
assert_eq!(IRQ_ADDRESS+1, cpu.registers().pc());

cpu.signal_interrupt(false);

cpu.execute_instruction(&mut sys);
// On the handler, EI executed

cpu.execute_instruction(&mut sys);
// RETI executed, even if interrupts are raised and enabled
assert_eq!(4, cpu.registers().pc());

cpu.execute_instruction(&mut sys);
// INT not raised, executions continues
assert_eq!(5, cpu.registers().pc());
}

#[test]
fn test_ei_on_handler_int_not_lowered() {
let mut sys = PlainMachine::new();
let mut cpu = Cpu::new_z80();

sys.poke(0x0000, 0xfb); // EI
sys.poke(0x0001, 0xed); // IM 1
sys.poke(0x0002, 0x56);

sys.poke(IRQ_ADDRESS, 0x00); // NOP
sys.poke(IRQ_ADDRESS+1, 0xfb); // EI
sys.poke(IRQ_ADDRESS+2, 0xed); // RETI
sys.poke(IRQ_ADDRESS+3, 0x4d);

cpu.execute_instruction(&mut sys);
cpu.execute_instruction(&mut sys);
cpu.execute_instruction(&mut sys);
cpu.signal_interrupt(true);

cpu.execute_instruction(&mut sys);
// On the handler, NOP executed
assert_eq!(IRQ_ADDRESS+1, cpu.registers().pc());

cpu.execute_instruction(&mut sys);
// On the handler, EI executed

cpu.execute_instruction(&mut sys);
// RETI executed, even if interrupts are raised and enabled
assert_eq!(4, cpu.registers().pc());

cpu.execute_instruction(&mut sys);
// On the handler again, as interrupts are raised and enabled
assert_eq!(IRQ_ADDRESS+1, cpu.registers().pc());
}

0 comments on commit 2b01c13

Please sign in to comment.