private static void Transfer(OpCode op, bool decrement, bool isOutput) { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(op); if (isOutput) { fixture.With(c => c.Mmu.Setup(x => x.ReadByte(c.Registers.HL)).Returns(c.Byte).Verifiable()); fixture.Assert(c => c.Io.Verify(x => x.WriteByteToPort(c.Registers.C, c.InitialRegisters.B, c.Byte))); } else { fixture.With(c => c.Io.Setup(x => x.ReadByteFromPort(c.Registers.C, c.InitialRegisters.B)).Returns(c.Byte).Verifiable()); fixture.Assert(c => c.Mmu.Verify(x => x.WriteByte(c.InitialRegisters.HL, c.Byte))); } fixture.Assert(c => c.Registers.B.ShouldBe(unchecked ((byte)(c.InitialRegisters.B - 1)))); fixture.AssertFlags(c => c.Registers.B, subtract: true, setResult: true); if (decrement) { fixture.Assert(c => c.Registers.HL.ShouldBe(unchecked ((ushort)(c.InitialRegister16(Operand.HL) - 1)))); } else { fixture.Assert(c => c.Registers.HL.ShouldBe(unchecked ((ushort)(c.InitialRegister16(Operand.HL) + 1)))); } } }
public void TransferRepeat(OpCode op, bool decrement) { var repeats = Rng.Word(2, 10); using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(op); fixture.RuntimeTiming((repeats - 1) * 5, (repeats - 1) * 21); const ushort HL = 100, DE = 1000; // Avoiding overflows. fixture.With(c => c.Registers.BC = repeats, c => c.Registers.HL = HL, c => c.Registers.DE = DE); if (decrement) { fixture.Assert(c => c.Mmu.Verify(x => x.TransferByte(It.Is <ushort>(b => b > HL - repeats && b <= HL), It.Is <ushort>(b => b > DE - repeats && b <= DE)), Times.Exactly(repeats))); fixture.Assert(c => c.Registers.HL.ShouldBe((ushort)(HL - repeats)), c => c.Registers.DE.ShouldBe((ushort)(DE - repeats))); } else { fixture.Assert(c => c.Mmu.Verify(x => x.TransferByte(It.Is <ushort>(b => b >= HL && b < HL + repeats), It.Is <ushort>(b => b >= DE && b < DE + repeats)), Times.Exactly(repeats))); fixture.Assert(c => c.Registers.HL.ShouldBe((ushort)(HL + repeats)), c => c.Registers.DE.ShouldBe((ushort)(DE + repeats))); } fixture.Assert(c => c.Registers.BC.ShouldBe((ushort)0)); } }
private static void TestRepeat(OpCode op, bool decrement) { using (var fixture = new ExecuteFixture()) { const ushort HL = 100; // Change HL so we don't need to worry about overflow. var repeats = Rng.Word(2, 10); fixture.Operation.OpCode(op); fixture.With(c => c.Registers.BC = repeats, c => c.Registers.HL = HL); fixture.RuntimeTiming((repeats - 1) * 5, (repeats - 1) * 21); if (decrement) { fixture.With(c => c.Mmu.Setup(x => x.ReadByte(It.Is <ushort>(a => a <= HL && a > HL - repeats))).Returns <ushort>(a => (byte)(HL - a))); fixture.Assert(c => c.Mmu.Verify(x => x.ReadByte(It.Is <ushort>(a => a <= HL && a > HL - repeats)), Times.Exactly(repeats))); } else { fixture.With(c => c.Mmu.Setup(x => x.ReadByte(It.Is <ushort>(a => a >= HL && a < HL + repeats))).Returns <ushort>(a => (byte)(a - HL))); fixture.Assert(c => c.Mmu.Verify(x => x.ReadByte(It.Is <ushort>(a => a >= HL && a < HL + repeats)), Times.Exactly(repeats))); } fixture.Assert(c => c.Alu.Verify(x => x.Compare(c.Accumulator.A, It.Is <byte>(b => b < repeats)))); fixture.Assert(c => c.Registers.BC.ShouldBe((ushort)0)); } }
private static void SetupReturn(ExecuteFixture fixture, OpCode op, FlagTest test, bool success = true) { fixture.Operation.OpCode(op).FlagTest(test); if (success) { fixture.With(c => c.Mmu.Setup(x => x.ReadWord(c.InitialStackPointer)).Returns(c.Word).Verifiable()); fixture.Assert(c => c.MockRegisters.VerifySet(r => r.StackPointer = c.PoppedStackPointer), c => c.MockRegisters.VerifySet(r => r.ProgramCounter = c.Word)); } else { fixture.Assert(c => c.Mmu.Verify(x => x.ReadWord(c.InitialStackPointer), Times.Never), c => c.MockRegisters.VerifySet(r => r.StackPointer = c.PoppedStackPointer, Times.Never)); } }
private static void SetupRelativeJump(ExecuteFixture fixture, OpCode op, FlagTest test, bool success = true) { fixture.Operation.OpCode(op).RandomLiterals().FlagTest(test); var times = success ? Times.Exactly(1) : Times.Never(); fixture.Assert(c => c.MockRegisters.VerifySet(r => r.ProgramCounter = unchecked ((ushort)(c.InitialProgramCounter + c.BlockLength + c.Operation.Displacement)), times)); }
private static void SetupJump(ExecuteFixture fixture, OpCode op, FlagTest test, bool success = true) { fixture.Operation.OpCode(op).Operands(Operand.nn).RandomLiterals().FlagTest(test); var times = success ? Times.Exactly(1) : Times.Never(); fixture.Assert(c => c.MockRegisters.VerifySet(r => r.ProgramCounter = c.Operation.WordLiteral, times)); }
private static void SetupCall(ExecuteFixture fixture, OpCode op, FlagTest test, bool success = true) { SetupJump(fixture, op, test, success); var times = success ? Times.Exactly(1) : Times.Never(); fixture.Assert(c => c.MockRegisters.VerifySet(r => r.StackPointer = c.PushedStackPointer, times), c => c.Mmu.Verify(x => x.WriteWord(c.StackPointer, c.SyncedProgramCounter), times)); }
public void ReturnFromInterrupt() { using (var fixture = new ExecuteFixture().DoNotHalt()) { SetupReturn(fixture, OpCode.ReturnFromInterrupt, FlagTest.None); fixture.Assert(c => c.MockRegisters.VerifySet(x => x.InterruptFlipFlop1 = true)); } }
public void Decrement16() { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(OpCode.Decrement16).Random16BitRegister(out var o); fixture.Assert(c => c.Operand16(o).ShouldBe(unchecked ((ushort)(c.InitialRegister16(o) - 1)))); } }
private static void ManageInterrupts(OpCode o, bool value) { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(o); fixture.Assert(c => c.MockRegisters.VerifySet(r => r.InterruptFlipFlop1 = value), c => c.MockRegisters.VerifySet(r => r.InterruptFlipFlop2 = value)); } }
public void ExchangeAccumulatorAndFlags() { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(OpCode.ExchangeAccumulatorAndFlags); fixture.Assert(c => c.MockRegisters.Verify(x => x.SwitchToAlternativeAccumulatorAndFlagsRegisters(), Times.Once)); } }
private static void SetInterruptMode(OpCode op, InterruptMode mode) { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(op); fixture.Assert(c => c.MockRegisters.VerifySet(x => x.InterruptMode = mode)); } }
public void When_writing_16bit_operands(Operand r) { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(OpCode.Load16).Operands(r).Random16BitRegister2(out var o).RandomLiterals(); fixture.Assert(c => c.Operand16(r).ShouldBe(c.Operand16(o))); } }
public void ExchangeGeneralPurpose() { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(OpCode.ExchangeGeneralPurpose); fixture.Assert(c => c.MockRegisters.Verify(x => x.SwitchToAlternativeGeneralPurposeRegisters(), Times.Once)); } }
public void DecimalArithmeticAdjust() { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(OpCode.DecimalArithmeticAdjust); fixture.With(c => c.Alu.Setup(alu => alu.DecimalAdjust(c.Accumulator.A, true)).Returns(c.Byte).Verifiable()); fixture.Assert(c => c.Accumulator.A.ShouldBe(c.Byte)); } }
public void NegateOnesComplement() { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(OpCode.NegateOnesComplement); fixture.Assert(c => c.Accumulator.A.ShouldBe((byte)~c.InitialAccumulator.A)); fixture.AssertFlags(c => c.Accumulator.A, halfCarry: true, subtract: true); } }
public void DecrementJumpRelativeIfNonZero_Success() { using (var fixture = new ExecuteFixture().DoNotHalt().RuntimeTiming(1, 5)) { fixture.With(x => x.Registers.B = 2); SetupRelativeJump(fixture, OpCode.DecrementJumpRelativeIfNonZero, FlagTest.None); fixture.Assert(x => x.Registers.B.ShouldBe((byte)1)); } }
public void Exchange(Operand o0, Operand o1) { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(OpCode.Exchange).Operands(o0, o1); fixture.Assert(c => c.Operand16(o0).ShouldBe(c.InitialRegister16(o1)), c => c.Operand16(o1).ShouldBe(c.InitialRegister16(o0))); } }
public void DecrementJumpRelativeIfNonZero_Fail() { using (var fixture = new ExecuteFixture().DoNotHalt()) { fixture.With(x => x.Registers.B = 1); SetupRelativeJump(fixture, OpCode.DecrementJumpRelativeIfNonZero, FlagTest.None, false); fixture.Assert(x => x.Registers.B.ShouldBe((byte)0)); } }
public void BitReset(Operand o, byte bit) { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(OpCode.BitReset).Operands(o).ByteLiteral(bit); fixture.With(c => c.Alu.Setup(x => x.BitReset(c.Operand8(o), bit)).Returns(c.Byte).Verifiable()); fixture.Assert(c => c.Operand8(o).ShouldBe(c.Byte)); } }
private static void TestAccumulatorAssign(OpCode op, Expression <Func <IAlu, byte, byte, byte> > f) { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(op).RandomRegister(out var o).RandomLiterals(); fixture.With(c => c.Alu.Setup(c.Alu8Call(f, Operand.A, o)).Returns(c.Byte).Verifiable()); fixture.Assert(c => c.Accumulator.A.ShouldBe(c.Byte)); } }
private static void TestAssign(OpCode op, Expression <Func <IAlu, ushort, ushort, ushort> > f) { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(op).Random16BitRegisters(out var o1, out var o2); fixture.With(c => c.Alu.Setup(c.Alu16Call(f, o1, o2)).Returns(c.Word).Verifiable()); fixture.Assert(c => c.Operand16(o1).ShouldBe(c.Word)); } }
public void NegateTwosComplement() { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(OpCode.NegateTwosComplement); fixture.With(c => c.Alu.Setup(alu => alu.Subtract(0, c.Accumulator.A)).Returns(c.Byte).Verifiable()); fixture.Assert(c => c.Accumulator.A.ShouldBe(c.Byte)); } }
private static void LoadIndexWrite(OpCode o) { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(o).Operands(Operand.mHL, Operand.A); fixture.Assert(c => c.Mmu.Verify(x => x.WriteByte(c.InitialRegisters.HL, c.Accumulator.A))); switch (o) { case OpCode.LoadIncrement: fixture.Assert(c => c.Registers.HL.ShouldBe(unchecked ((ushort)(c.InitialRegisters.HL + 1)))); break; case OpCode.LoadDecrement: fixture.Assert(c => c.Registers.HL.ShouldBe(unchecked ((ushort)(c.InitialRegisters.HL - 1)))); break; } } }
private static void Test(OpCode op, bool decrement) { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(op); fixture.With(c => c.Mmu.Setup(x => x.ReadByte(c.Registers.HL)).Returns(c.Byte).Verifiable()); fixture.Assert(c => c.Alu.Verify(x => x.Compare(c.Accumulator.A, c.Byte)), c => c.Registers.BC.ShouldBe(unchecked ((ushort)(c.InitialRegister16(Operand.BC) - 1)))); if (decrement) { fixture.Assert(c => c.Registers.HL.ShouldBe(unchecked ((ushort)(c.InitialRegister16(Operand.HL) - 1)))); } else { fixture.Assert(c => c.Registers.HL.ShouldBe(unchecked ((ushort)(c.InitialRegister16(Operand.HL) + 1)))); } } }
public void When_reading_8bit_operands(Operand r) { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(OpCode.Load).RandomRegister(out var o).Operand2(r).RandomLiterals(); var value = Rng.Byte(); fixture.With(c => { switch (c.Operation.Operand2) { case Operand.mHL: c.Mmu.Setup(m => m.ReadByte(It.Is <ushort>(a => a == c.Registers.HL))).Returns(value).Verifiable(); break; case Operand.mIXd: c.Mmu.Setup(m => m.ReadByte(It.Is <ushort>(a => a == c.MockRegisters.Object.IX + c.Operation.Displacement))).Returns(value).Verifiable(); break; case Operand.mIYd: c.Mmu.Setup(m => m.ReadByte(It.Is <ushort>(a => a == c.MockRegisters.Object.IY + c.Operation.Displacement))).Returns(value).Verifiable(); break; case Operand.mnn: c.Mmu.Setup(m => m.ReadByte(It.Is <ushort>(a => a == c.Operation.WordLiteral))).Returns(value).Verifiable(); break; case Operand.mCl: c.Mmu.Setup(m => m.ReadByte(It.Is <ushort>(a => a == c.Registers.C + 0xff00))).Returns(value).Verifiable(); break; case Operand.mnl: c.Mmu.Setup(m => m.ReadByte(It.Is <ushort>(a => a == c.Operation.ByteLiteral + 0xff00))).Returns(value).Verifiable(); break; case Operand.mBC: c.Mmu.Setup(m => m.ReadByte(It.Is <ushort>(a => a == c.Registers.BC))).Returns(value).Verifiable(); break; case Operand.mDE: c.Mmu.Setup(m => m.ReadByte(It.Is <ushort>(a => a == c.Registers.DE))).Returns(value).Verifiable(); break; case Operand.mSP: c.Mmu.Setup(m => m.ReadByte(It.Is <ushort>(a => a == c.MockRegisters.Object.StackPointer))).Returns(value).Verifiable(); break; default: value = c.Operand8(c.Operation.Operand2); break; } }); fixture.Assert(c => c.Operand8(o).ShouldBe(value)); } }
private static void LoadIndexRead(OpCode o) { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(o).Operands(Operand.A, Operand.mHL); fixture.With(c => c.Mmu.Setup(x => x.ReadByte(c.InitialRegisters.HL)).Returns(c.Byte).Verifiable()); fixture.Assert(c => c.Accumulator.A.ShouldBe(c.Byte)); switch (o) { case OpCode.LoadIncrement: fixture.Assert(c => c.Registers.HL.ShouldBe(unchecked ((ushort)(c.InitialRegisters.HL + 1)))); break; case OpCode.LoadDecrement: fixture.Assert(c => c.Registers.HL.ShouldBe(unchecked ((ushort)(c.InitialRegisters.HL - 1)))); break; } } }
public void When_writing_8bit_operands(Operand r) { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(OpCode.Load).Operands(r).RandomRegister2(out var o).RandomLiterals(); fixture.Assert(c => { var value = c.Operand8(c.Operation.Operand2); switch (c.Operation.Operand1) { case Operand.mHL: c.Mmu.Verify(m => m.WriteByte(It.Is <ushort>(a => a == c.Registers.HL), value)); break; case Operand.mIXd: c.Mmu.Verify(m => m.WriteByte(It.Is <ushort>(a => a == c.MockRegisters.Object.IX + c.Operation.Displacement), value)); break; case Operand.mIYd: c.Mmu.Verify(m => m.WriteByte(It.Is <ushort>(a => a == c.MockRegisters.Object.IY + c.Operation.Displacement), value)); break; case Operand.mnn: c.Mmu.Verify(m => m.WriteByte(It.Is <ushort>(a => a == c.Operation.WordLiteral), value)); break; case Operand.mCl: c.Mmu.Verify(m => m.WriteByte(It.Is <ushort>(a => a == c.Registers.C + 0xff00), value)); break; case Operand.mnl: c.Mmu.Verify(m => m.WriteByte(It.Is <ushort>(a => a == c.Operation.ByteLiteral + 0xff00), value)); break; case Operand.mBC: c.Mmu.Verify(m => m.WriteByte(It.Is <ushort>(a => a == c.Registers.BC), value)); break; case Operand.mDE: c.Mmu.Verify(m => m.WriteByte(It.Is <ushort>(a => a == c.Registers.DE), value)); break; case Operand.mSP: c.Mmu.Verify(m => m.WriteByte(It.Is <ushort>(a => a == c.MockRegisters.Object.StackPointer), value)); break; default: c.Operand8(c.Operation.Operand1).ShouldBe(value); break; } }); } }
public void RotateRightDigit() { using (var fixture = new ExecuteFixture()) { var result = RngFactory.Build <AccumulatorAndResult>()(); fixture.Operation.OpCode(OpCode.RotateRightDigit); fixture.With(c => c.Mmu.Setup(x => x.ReadByte(c.Registers.HL)).Returns(c.Byte).Verifiable(), c => c.Alu.Setup(x => x.RotateRightDigit(c.InitialAccumulator.A, c.Byte)).Returns(result).Verifiable()); fixture.Assert(c => c.Accumulator.A.ShouldBe(result.Accumulator), c => c.Mmu.Verify(x => x.WriteByte(c.Registers.HL, result.Result))); } }
public void Transfer(OpCode op, bool decrement, bool overflow) { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(op); fixture.With(c => c.Registers.BC = (ushort)(overflow ? 2 : 1)); fixture.Assert(c => c.Mmu.Verify(x => x.TransferByte(c.InitialRegisters.HL, c.InitialRegisters.DE)), c => c.Registers.BC.ShouldBe((ushort)(overflow ? 1 : 0))); if (decrement) { fixture.Assert(c => c.Registers.HL.ShouldBe(unchecked ((ushort)(c.InitialRegisters.HL - 1))), c => c.Registers.DE.ShouldBe(unchecked ((ushort)(c.InitialRegisters.DE - 1)))); } else { fixture.Assert(c => c.Registers.HL.ShouldBe(unchecked ((ushort)(c.InitialRegisters.HL + 1))), c => c.Registers.DE.ShouldBe(unchecked ((ushort)(c.InitialRegisters.DE + 1)))); } fixture.AssertFlags(halfCarry: false, parityOverflow: overflow, subtract: false); } }