Skip to content

Commit

Permalink
Support maskable interrupts mode 1
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanizag committed Oct 30, 2023
1 parent 5104ffc commit 5e0c5f0
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 16 deletions.
37 changes: 31 additions & 6 deletions src/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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();
Expand All @@ -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();
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/decoder_8080.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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])),
Expand Down
4 changes: 2 additions & 2 deletions src/decoder_z80.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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])),
Expand Down
5 changes: 5 additions & 0 deletions src/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
17 changes: 13 additions & 4 deletions src/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
})
)
}
Expand Down
4 changes: 4 additions & 0 deletions src/opcode_ld.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
21 changes: 19 additions & 2 deletions src/registers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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 {
Expand All @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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,
}
Expand Down

0 comments on commit 5e0c5f0

Please sign in to comment.