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)))); } } }
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 TransferRepeat(OpCode op, bool decrement, bool isOutput) { using (var fixture = new ExecuteFixture()) { const ushort HL = 100; // Change HL so we don't need to worry about overflow. var repeats = Rng.Byte(2, 10); fixture.Operation.OpCode(op); fixture.With(c => c.Registers.B = repeats, c => c.Registers.HL = HL); fixture.RuntimeTiming((repeats - 1) * 5, (repeats - 1) * 21); if (decrement) { if (isOutput) { 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.Assert(c => c.Mmu.Verify(x => x.WriteByte(It.Is <ushort>(a => a <= HL && a > HL - repeats), It.Is <byte>(b => b < repeats)), Times.Exactly(repeats))); } } else { if (isOutput) { 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))); } else { fixture.Assert(c => c.Mmu.Verify(x => x.WriteByte(It.Is <ushort>(a => a >= HL && a < HL + repeats), It.Is <byte>(b => b < repeats)), Times.Exactly(repeats))); } } if (isOutput) { fixture.Assert(c => c.Io.Verify(x => x.WriteByteToPort(c.Registers.C, It.Is <byte>(b => b > 0 && b <= repeats), It.Is <byte>(b => b < repeats)))); } else { fixture.With(c => c.Io.Setup(x => x.ReadByteFromPort(c.Registers.C, It.Is <byte>(b => b > 0 && b <= repeats))) .Returns <byte, byte>((p, b) => (byte)(b - 1))); fixture.Assert(c => c.Io.Verify(x => x.ReadByteFromPort(c.Registers.C, It.Is <byte>(b => b > 0 && b <= repeats)), Times.Exactly(repeats))); } fixture.Assert(c => c.Registers.B.ShouldBe((byte)0)); } }
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)); } }
public void JumpRelativeWithTest_Success(FlagTest test) { using (var fixture = new ExecuteFixture().DoNotHalt().RuntimeTiming(1, 5)) { fixture.With(c => c.Flags.Setup(GetFlagExpression(test)).Returns(GetPositiveValue(test))); SetupRelativeJump(fixture, OpCode.JumpRelative, test); } }
private static void TestCall(OpCode op, Expression <Action <IAlu, byte, byte> > f) { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(op).RandomRegister(out var o).RandomLiterals(); fixture.With(c => c.Alu.Setup(c.AluAction(f, Operand.A, o)).Verifiable()); } }
public void JumpWithTest_Fail(FlagTest test) { using (var fixture = new ExecuteFixture().DoNotHalt()) { fixture.With(c => c.Flags.Setup(GetFlagExpression(test)).Returns(!GetPositiveValue(test))); SetupJump(fixture, OpCode.Jump, test, false); } }
public void CallWithTest_Success(FlagTest test) { using (var fixture = new ExecuteFixture().DoNotHalt().RuntimeTiming(2, 7)) { fixture.With(c => c.Flags.Setup(GetFlagExpression(test)).Returns(GetPositiveValue(test))); SetupCall(fixture, OpCode.Call, test); } }
public void BitTest(Operand o, byte bit) { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(OpCode.BitTest).Operands(o).ByteLiteral(bit); fixture.With(c => c.Alu.Setup(x => x.BitTest(c.Operand8(o), bit)).Verifiable()); } }
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)); } }
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)); } }
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 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)); } }
public void ReturnFromNonmaskableInterrupt() { using (var fixture = new ExecuteFixture().DoNotHalt()) { SetupReturn(fixture, OpCode.ReturnFromNonmaskableInterrupt, FlagTest.None); fixture.With(c => c.MockRegisters.Setup(x => x.InterruptFlipFlop2).Returns(true)); fixture.Assert(c => c.MockRegisters.VerifySet(x => x.InterruptFlipFlop1 = true)); } }
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 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 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)); } }
private static void Test(OpCode op, Operand o, bool isOutput) { var addressMsb = o == Operand.n ? Operand.A : Operand.B; using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(op).RandomRegister(out var r).Operand2(o).RandomLiterals(); if (isOutput) { fixture.With(c => c.Io.Setup(x => x.WriteByteToPort(c.Operand8(o), c.InitialRegister8(addressMsb), c.Operand8(r))).Verifiable()); } else { fixture.With(c => c.Io.Setup(x => x.ReadByteFromPort(c.Operand8(o), c.InitialRegister8(addressMsb))).Returns(c.Byte).Verifiable()); fixture.Assert(c => c.Operand8(r).ShouldBe(c.Byte)); } } }
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)); } }
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))); } }
private static void TestCallAssign(OpCode op, Expression <Func <IAlu, byte, byte> > f, bool alternativeFlags = false) { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(op).RandomRegister(out var o).RandomLiterals().UseAlternativeFlagAffection(alternativeFlags); fixture.With(c => c.Alu.Setup(c.Alu8Call(f, o)).Returns(c.Byte).Verifiable()); fixture.Assert(c => c.Operand8(o).ShouldBe(c.Byte)); if (alternativeFlags) { fixture.AssertFlags(zero: false, sign: false); } } }
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 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)))); } } }
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_reading_16bit_operands(Operand r) { using (var fixture = new ExecuteFixture()) { fixture.Operation.OpCode(OpCode.Load16).Random16BitRegister(out var o).Operand2(r).RandomLiterals(); var value = Rng.Word(); fixture.With(c => { if (r == Operand.SPd) { c.Alu.Setup(a => a.AddDisplacement(c.MockRegisters.Object.StackPointer, c.Operation.Displacement)).Returns(value); } else { value = c.Operand16(r); } }); fixture.Assert(c => c.Operand16(o).ShouldBe(value)); } }
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); } }