use super::*;
#[test]
fn a_non_maskable_interrupt_out_of_a_halt_returns_past_the_instruction_that_halted() {
let mut cpu = Cpu::new();
cpu.reset();
cpu.registers_mut().sp = 0x0100;
let mut ram = Ram::with(&[0x00, 0x76]);
cpu.step_by_edges(&mut ram);
cpu.step_by_edges(&mut ram);
assert!(cpu.is_halted());
cpu.request_nmi();
cpu.step_by_edges(&mut ram);
cpu.step_by_edges(&mut ram);
assert!(!cpu.is_halted());
assert_eq!(cpu.registers().pc, 0x0066);
assert_eq!(ram.bytes[0x00FE], 0x02);
assert_eq!(ram.bytes[0x00FF], 0x00);
}
#[test]
fn a_machine_that_answers_the_stackless_reports_keeps_the_return_address_out_of_memory() {
let mut cpu = Cpu::new();
cpu.reset();
cpu.set_z80n_enabled(true);
cpu.registers_mut().sp = 0x0100;
let mut machine = Stackless::new(&[0x00, 0x00]);
cpu.step_by_edges(&mut machine);
let accepting = take_a_non_maskable_interrupt(&mut cpu, &mut machine);
assert_eq!(machine.captured, Some(0x0002));
assert_eq!(machine.writes, 0);
assert_eq!(machine.bytes[0x00FE], 0x00);
assert_eq!(machine.bytes[0x00FF], 0x00);
assert_eq!(cpu.registers().sp, 0x00FE);
assert_eq!(cpu.registers().pc, 0x0066);
let returning = cpu.step_by_edges(&mut machine);
assert_eq!(machine.reads_of_the_stack, 2);
assert_eq!(machine.captured, None);
assert_eq!(cpu.registers().pc, 0x0002);
assert_eq!(cpu.registers().sp, 0x0100);
let mut plain = Cpu::new();
plain.reset();
plain.registers_mut().sp = 0x0100;
let mut ram = Ram::with(&[0x00, 0x00]);
ram.bytes[0x0066] = 0xED;
ram.bytes[0x0067] = 0x45;
plain.step_by_edges(&mut ram);
let plain_accepting = take_a_non_maskable_interrupt(&mut plain, &mut ram);
let plain_returning = plain.step_by_edges(&mut ram);
assert_eq!(accepting, plain_accepting);
assert_eq!(returning, plain_returning);
assert_eq!(ram.bytes[0x00FE], 0x02);
assert_eq!(ram.bytes[0x00FF], 0x00);
assert_eq!(plain.registers().pc, 0x0002);
}
#[test]
fn setting_the_interrupt_enable_takes_effect_within_the_instruction() {
let (cpu, _, t) = run(&[0xFB], 1);
assert_eq!(t, 4);
assert!(cpu.registers().iff1);
assert!(cpu.registers().iff2);
let (cpu, _, _) = run(&[0xFB, 0xF3], 2);
assert!(!cpu.registers().iff1);
assert!(!cpu.registers().iff2);
}
#[test]
fn a_return_from_an_interrupt_reports_itself_so_a_daisy_chain_can_release() {
let mut cpu = Cpu::new();
cpu.reset();
cpu.registers_mut().sp = 0x0040;
let mut ram = Ram::with(&[0xED, 0x4D]);
cpu.step_by_edges(&mut ram);
assert_eq!(ram.returns, 1);
}
#[test]
fn a_return_from_a_non_maskable_interrupt_is_not_part_of_the_chain() {
let mut cpu = Cpu::new();
cpu.reset();
cpu.registers_mut().sp = 0x0040;
let mut ram = Ram::with(&[0xED, 0x45]);
cpu.step_by_edges(&mut ram);
assert_eq!(ram.returns, 0);
}
#[test]
fn the_interrupt_enable_is_readable_through_the_parity_flag() {
let (cpu, _, _) = run(&[0xFB, 0xED, 0x57], 2);
assert_ne!(cpu.flags() & crate::flag::P_MASK, 0);
let (cpu, _, _) = run(&[0xF3, 0xED, 0x57], 2);
assert_eq!(cpu.flags() & crate::flag::P_MASK, 0);
}
#[test]
fn a_non_maskable_interrupt_keeps_the_saved_enable_for_the_return_to_restore() {
let mut cpu = Cpu::new();
cpu.reset();
cpu.registers_mut().sp = 0x0200;
let mut ram = Ram::with(&[0xFB, 0x76]);
ram.bytes[0x0066] = 0xED;
ram.bytes[0x0067] = 0x57;
cpu.step_by_edges(&mut ram);
cpu.step_by_edges(&mut ram);
assert!(cpu.is_halted());
assert!(cpu.registers().iff1 && cpu.registers().iff2);
cpu.request_nmi();
cpu.step_by_edges(&mut ram);
cpu.step_by_edges(&mut ram);
assert_eq!(cpu.registers().pc, 0x0066);
assert!(!cpu.registers().iff1, "acceptance must disable interrupts");
assert!(cpu.registers().iff2, "the saved copy must survive for RETN");
cpu.step_by_edges(&mut ram);
assert_ne!(
cpu.flags() & crate::flag::P_MASK,
0,
"the saved copy must be readable through the parity flag"
);
}