mirror of
https://github.com/MoleskiCoder/EightBitNet.git
synced 2026-10-23 23:02:16 +00:00
111 lines
3.6 KiB
C#
111 lines
3.6 KiB
C#
// <copyright file="NmiTests.cs" company="Adrian Conlon">
|
|
// Copyright (c) Adrian Conlon. All rights reserved.
|
|
// </copyright>
|
|
namespace Z80.UnitTests
|
|
{
|
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
|
|
[TestClass]
|
|
public class NmiTests
|
|
{
|
|
private readonly Board board = new();
|
|
private readonly Z80 cpu;
|
|
|
|
public NmiTests() => this.cpu = this.board.CPU;
|
|
|
|
[TestInitialize]
|
|
public void Initialise()
|
|
{
|
|
this.board.RaisePOWER();
|
|
this.cpu.PC.Joined = 0x0100;
|
|
this.cpu.SP.Joined = 0xFF00;
|
|
}
|
|
|
|
[TestCleanup]
|
|
public void Cleanup() => this.board.LowerPOWER();
|
|
|
|
[TestMethod]
|
|
public void Nmi_Jumps_To_0x0066()
|
|
{
|
|
this.cpu.LowerNMI();
|
|
this.cpu.Step();
|
|
Assert.AreEqual(0x0066, this.cpu.PC.Joined, "NMI must jump to 0x0066");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Nmi_Saves_PC_On_Stack()
|
|
{
|
|
this.cpu.PC.Joined = 0x1234;
|
|
this.cpu.LowerNMI();
|
|
this.cpu.Step();
|
|
|
|
// PushShort pushes High first then Low, so:
|
|
// mem[SP-1] = PC.High, mem[SP-2] = PC.Low
|
|
var low = this.board.Peek(this.cpu.SP.Joined); // mem[SP] = PC.Low
|
|
var high = this.board.Peek((ushort)(this.cpu.SP.Joined + 1)); // mem[SP+1] = PC.High
|
|
var savedPc = (ushort)((high << 8) | low);
|
|
Assert.AreEqual(0x1234, savedPc, "NMI must save PC on the stack");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Nmi_Clears_IFF1()
|
|
{
|
|
this.cpu.IFF1 = true;
|
|
this.cpu.LowerNMI();
|
|
this.cpu.Step();
|
|
Assert.IsFalse(this.cpu.IFF1, "NMI must clear IFF1");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Nmi_Preserves_IFF1_In_IFF2()
|
|
{
|
|
this.cpu.IFF1 = true;
|
|
this.cpu.IFF2 = false;
|
|
this.cpu.LowerNMI();
|
|
this.cpu.Step();
|
|
Assert.IsTrue(this.cpu.IFF2, "NMI must copy IFF1 into IFF2 before clearing IFF1");
|
|
Assert.IsFalse(this.cpu.IFF1, "NMI must clear IFF1");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Nmi_Fires_Even_When_IFF1_Disabled()
|
|
{
|
|
this.cpu.IFF1 = false;
|
|
this.cpu.LowerNMI();
|
|
this.cpu.Step();
|
|
Assert.AreEqual(0x0066, this.cpu.PC.Joined, "NMI must fire regardless of IFF1");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Nmi_Exits_Halt_State()
|
|
{
|
|
this.board.Poke(0x0100, 0x76); // HALT
|
|
this.cpu.Step(); // enter HALT
|
|
Assert.IsTrue(this.cpu.HALT.Lowered(), "CPU must be halted before NMI test");
|
|
|
|
this.cpu.LowerNMI();
|
|
this.cpu.Step(); // NMI exits HALT
|
|
Assert.IsTrue(this.cpu.HALT.Raised(), "NMI must raise HALT pin");
|
|
Assert.AreEqual(0x0066, this.cpu.PC.Joined, "NMI must jump to 0x0066 from HALT state");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Retn_Restores_IFF1_From_IFF2()
|
|
{
|
|
// RETN (ED 45) at 0x0066
|
|
this.board.Poke(0x0066, 0xED);
|
|
this.board.Poke(0x0067, 0x45);
|
|
|
|
this.cpu.IFF1 = true;
|
|
this.cpu.LowerNMI();
|
|
this.cpu.Step(); // handles NMI: IFF2=true, IFF1=false, jumps to 0x0066
|
|
|
|
Assert.IsFalse(this.cpu.IFF1, "IFF1 must be false after NMI");
|
|
Assert.IsTrue(this.cpu.IFF2, "IFF2 must be true after NMI");
|
|
|
|
this.cpu.Step(); // executes RETN at 0x0066: IFF1 = IFF2 = true
|
|
Assert.IsTrue(this.cpu.IFF1, "RETN must restore IFF1 from IFF2");
|
|
}
|
|
}
|
|
}
|