Make the Z80 implementation M-Cycle accurate. I think!
Signed-off-by: Adrian Conlon <Adrian.conlon@gmail.com>
This commit is contained in:
parent
c7c9963db3
commit
c15ec96862
|
@ -214,7 +214,7 @@ namespace EightBit
|
|||
|
||||
protected void Jump(ushort destination) => this.PC.Word = destination;
|
||||
|
||||
protected void Call(ushort destination)
|
||||
protected virtual void Call(ushort destination)
|
||||
{
|
||||
this.PushWord(this.PC);
|
||||
this.Jump(destination);
|
||||
|
|
|
@ -33,6 +33,8 @@ namespace Fuse
|
|||
private readonly EightBit.InputOutput ports = new EightBit.InputOutput();
|
||||
private readonly EightBit.Z80 cpu;
|
||||
|
||||
private int totalCycles = 0;
|
||||
|
||||
public TestRunner(Test<RegisterState> test, Result<RegisterState> result)
|
||||
{
|
||||
this.cpu = new EightBit.Z80(this, this.ports);
|
||||
|
@ -93,17 +95,18 @@ namespace Fuse
|
|||
{
|
||||
this.ports.ReadPort += this.Ports_ReadPort;
|
||||
this.ports.WrittenPort += this.Ports_WrittenPort;
|
||||
this.cpu.ExecutedInstruction += this.Cpu_ExecutedInstruction;
|
||||
}
|
||||
|
||||
protected override void OnReadByte()
|
||||
{
|
||||
this.actualEvents.Add(new TestEvent(this.cpu.Cycles, "MR", this.Address.Word, this.Data));
|
||||
this.actualEvents.Add(new TestEvent(this.totalCycles + this.cpu.Cycles, "MR", this.Address.Word, this.Data));
|
||||
base.OnReadByte();
|
||||
}
|
||||
|
||||
protected override void OnWrittenByte()
|
||||
{
|
||||
this.actualEvents.Add(new TestEvent(this.cpu.Cycles, "MW", this.Address.Word, this.Data));
|
||||
this.actualEvents.Add(new TestEvent(this.totalCycles + this.cpu.Cycles, "MW", this.Address.Word, this.Data));
|
||||
base.OnWrittenByte();
|
||||
}
|
||||
|
||||
|
@ -113,9 +116,11 @@ namespace Fuse
|
|||
System.Console.Error.WriteLine(output);
|
||||
}
|
||||
|
||||
private void Ports_WrittenPort(object sender, EightBit.PortEventArgs e) => this.actualEvents.Add(new TestEvent(this.cpu.Cycles, "PW", this.Address.Word, this.Data));
|
||||
private void Ports_WrittenPort(object sender, EightBit.PortEventArgs e) => this.actualEvents.Add(new TestEvent(this.totalCycles + this.cpu.Cycles, "PW", this.Address.Word, this.Data));
|
||||
|
||||
private void Ports_ReadPort(object sender, EightBit.PortEventArgs e) => this.actualEvents.Add(new TestEvent(this.cpu.Cycles, "PR", this.Address.Word, this.Data));
|
||||
private void Ports_ReadPort(object sender, EightBit.PortEventArgs e) => this.actualEvents.Add(new TestEvent(this.totalCycles + this.cpu.Cycles, "PR", this.Address.Word, this.Data));
|
||||
|
||||
private void Cpu_ExecutedInstruction(object sender, System.EventArgs e) => this.totalCycles += this.cpu.Cycles;
|
||||
|
||||
private static void DumpDifference(string highDescription, string lowDescription, EightBit.Register16 expected, EightBit.Register16 actual)
|
||||
{
|
||||
|
@ -401,7 +406,7 @@ namespace Fuse
|
|||
var equalAddress = expectation.Address == actual.Address;
|
||||
var equalValue = expectation.Value == actual.Value;
|
||||
|
||||
var equal = /* equalCycles && */equalSpecifier && equalAddress && equalValue;
|
||||
var equal = equalCycles && equalSpecifier && equalAddress && equalValue;
|
||||
eventFailure = !equal;
|
||||
}
|
||||
|
||||
|
|
325
Z80/Z80.cs
325
Z80/Z80.cs
|
@ -494,6 +494,7 @@ namespace EightBit
|
|||
|
||||
protected override void BusWrite()
|
||||
{
|
||||
this.Tick(3);
|
||||
this.LowerMREQ();
|
||||
this.LowerWR();
|
||||
base.BusWrite();
|
||||
|
@ -503,6 +504,7 @@ namespace EightBit
|
|||
|
||||
protected override byte BusRead()
|
||||
{
|
||||
this.Tick(3);
|
||||
this.LowerMREQ();
|
||||
this.LowerRD();
|
||||
var returned = base.BusRead();
|
||||
|
@ -515,6 +517,8 @@ namespace EightBit
|
|||
{
|
||||
base.HandleRESET();
|
||||
this.DisableInterrupts();
|
||||
this.IV = this.REFRESH = 0;
|
||||
this.SP.Word = this.AF.Word = (ushort)Mask.Mask16;
|
||||
this.Tick(3);
|
||||
}
|
||||
|
||||
|
@ -527,24 +531,31 @@ namespace EightBit
|
|||
this.RaiseIORQ();
|
||||
this.RaiseM1();
|
||||
this.DisableInterrupts();
|
||||
this.Tick(5);
|
||||
switch (this.IM)
|
||||
{
|
||||
case 0: // i8080 equivalent
|
||||
this.Execute(data);
|
||||
break;
|
||||
case 1:
|
||||
this.Restart(7 << 3);
|
||||
this.Tick(13);
|
||||
this.Tick();
|
||||
this.Restart(7 << 3); // 7 cycles
|
||||
break;
|
||||
case 2:
|
||||
this.Tick(7);
|
||||
this.Call(this.MEMPTR.Word = new Register16(data, this.IV).Word);
|
||||
this.Tick(19);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException("Invalid interrupt mode");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Call(ushort destination)
|
||||
{
|
||||
this.Tick();
|
||||
base.Call(destination);
|
||||
}
|
||||
|
||||
private static byte SetBit(byte f, StatusBits flag) => SetBit(f, (byte)flag);
|
||||
|
||||
private static byte SetBit(byte f, StatusBits flag, int condition) => SetBit(f, (byte)flag, condition);
|
||||
|
@ -765,7 +776,18 @@ namespace EightBit
|
|||
var memoryZ = z == 6;
|
||||
var indirect = (!this.displaced && memoryZ) || this.displaced;
|
||||
var direct = !indirect;
|
||||
var operand = !this.displaced ? this.R(z) : this.BusRead(this.DisplacedAddress);
|
||||
|
||||
byte operand;
|
||||
if (this.displaced)
|
||||
{
|
||||
this.Tick(2);
|
||||
operand = this.BusRead(this.DisplacedAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
operand = this.R(z);
|
||||
}
|
||||
|
||||
var update = x != 1; // BIT does not update
|
||||
switch (x)
|
||||
{
|
||||
|
@ -801,28 +823,15 @@ namespace EightBit
|
|||
}
|
||||
|
||||
this.F = AdjustSZP(this.F, operand);
|
||||
this.Tick(4);
|
||||
break;
|
||||
case 1: // BIT y, r[z]
|
||||
this.Tick(4);
|
||||
this.BIT(y, operand);
|
||||
if (direct)
|
||||
{
|
||||
this.F = AdjustXY(this.F, operand);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.F = AdjustXY(this.F, this.MEMPTR.High);
|
||||
this.Tick(4);
|
||||
}
|
||||
|
||||
this.F = direct ? AdjustXY(this.F, operand) : AdjustXY(this.F, this.MEMPTR.High);
|
||||
break;
|
||||
case 2: // RES y, r[z]
|
||||
this.Tick(4);
|
||||
operand = RES(y, operand);
|
||||
break;
|
||||
case 3: // SET y, r[z]
|
||||
this.Tick(4);
|
||||
operand = SET(y, operand);
|
||||
break;
|
||||
default:
|
||||
|
@ -831,13 +840,10 @@ namespace EightBit
|
|||
|
||||
if (update)
|
||||
{
|
||||
this.Tick();
|
||||
if (!this.displaced)
|
||||
{
|
||||
this.R(z, operand);
|
||||
if (memoryZ)
|
||||
{
|
||||
this.Tick(7);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -846,8 +852,6 @@ namespace EightBit
|
|||
{
|
||||
this.R2(z, operand);
|
||||
}
|
||||
|
||||
this.Tick(15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -873,15 +877,12 @@ namespace EightBit
|
|||
|
||||
this.F = AdjustSZPXY(this.F, this.Bus.Data);
|
||||
this.F = ClearBit(this.F, StatusBits.NF | StatusBits.HC);
|
||||
this.Tick(4);
|
||||
break;
|
||||
case 1: // Output to port with 16-bit address
|
||||
this.MEMPTR.Word = this.Bus.Address.Word = this.BC.Word;
|
||||
this.MEMPTR.Word++;
|
||||
this.Bus.Data = y != 6 ? this.R(y) : (byte)0;
|
||||
|
||||
this.WritePort();
|
||||
this.Tick(4);
|
||||
break;
|
||||
case 2: // 16-bit add/subtract with carry
|
||||
switch (q)
|
||||
|
@ -896,7 +897,6 @@ namespace EightBit
|
|||
throw new NotSupportedException("Invalid operation mode");
|
||||
}
|
||||
|
||||
this.Tick(7);
|
||||
break;
|
||||
case 3: // Retrieve/store register pair from/to immediate address
|
||||
this.Bus.Address.Word = this.FetchWord().Word;
|
||||
|
@ -912,7 +912,6 @@ namespace EightBit
|
|||
throw new NotSupportedException("Invalid operation mode");
|
||||
}
|
||||
|
||||
this.Tick(12);
|
||||
break;
|
||||
case 4: // Negate accumulator
|
||||
this.NEG();
|
||||
|
@ -928,7 +927,6 @@ namespace EightBit
|
|||
break;
|
||||
}
|
||||
|
||||
this.Tick(6);
|
||||
break;
|
||||
case 6: // Set interrupt mode
|
||||
switch (y)
|
||||
|
@ -957,31 +955,25 @@ namespace EightBit
|
|||
{
|
||||
case 0: // LD I,A
|
||||
this.IV = this.A;
|
||||
this.Tick();
|
||||
break;
|
||||
case 1: // LD R,A
|
||||
this.REFRESH = this.A;
|
||||
this.Tick();
|
||||
break;
|
||||
case 2: // LD A,I
|
||||
this.F = AdjustSZXY(this.F, this.A = this.IV);
|
||||
this.F = ClearBit(this.F, StatusBits.NF | StatusBits.HC);
|
||||
this.F = SetBit(this.F, StatusBits.PF, this.IFF2);
|
||||
this.Tick();
|
||||
break;
|
||||
case 3: // LD A,R
|
||||
this.F = AdjustSZXY(this.F, this.A = this.REFRESH);
|
||||
this.F = ClearBit(this.F, StatusBits.NF | StatusBits.HC);
|
||||
this.F = SetBit(this.F, StatusBits.PF, this.IFF2);
|
||||
this.Tick();
|
||||
break;
|
||||
case 4: // RRD
|
||||
this.RRD();
|
||||
this.Tick(10);
|
||||
break;
|
||||
case 5: // RLD
|
||||
this.RLD();
|
||||
this.Tick(10);
|
||||
break;
|
||||
case 6: // NOP
|
||||
case 7: // NOP
|
||||
|
@ -1013,18 +1005,18 @@ namespace EightBit
|
|||
{
|
||||
this.MEMPTR.Word = --this.PC.Word;
|
||||
--this.PC.Word;
|
||||
this.Tick(5);
|
||||
}
|
||||
|
||||
this.Tick(7);
|
||||
break;
|
||||
case 7: // LDDR
|
||||
if (this.LDDR())
|
||||
{
|
||||
this.MEMPTR.Word = --this.PC.Word;
|
||||
--this.PC.Word;
|
||||
this.Tick(5);
|
||||
}
|
||||
|
||||
this.Tick(7);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1046,19 +1038,21 @@ namespace EightBit
|
|||
this.Tick(5);
|
||||
}
|
||||
|
||||
this.Tick(5);
|
||||
break;
|
||||
case 7: // CPDR
|
||||
if (this.CPDR())
|
||||
{
|
||||
this.MEMPTR.Word = --this.PC.Word;
|
||||
--this.PC.Word;
|
||||
this.Tick(5);
|
||||
this.Tick(3);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.MEMPTR.Word = (ushort)(this.PC.Word - 2);
|
||||
}
|
||||
|
||||
this.Tick(7);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1106,7 +1100,7 @@ namespace EightBit
|
|||
this.PC.Word -= 2;
|
||||
this.Tick(5);
|
||||
}
|
||||
|
||||
this.Tick(3);
|
||||
break;
|
||||
case 7: // OTDR
|
||||
if (this.OTDR())
|
||||
|
@ -1114,14 +1108,13 @@ namespace EightBit
|
|||
this.PC.Word -= 2;
|
||||
this.Tick(5);
|
||||
}
|
||||
|
||||
this.Tick(3);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
this.Tick(8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1144,27 +1137,22 @@ namespace EightBit
|
|||
this.ExxAF();
|
||||
break;
|
||||
case 2: // DJNZ d
|
||||
this.Tick();
|
||||
if (this.JumpRelativeConditional(--this.B != 0))
|
||||
{
|
||||
this.Tick(5);
|
||||
this.Tick(2);
|
||||
}
|
||||
|
||||
this.Tick(4);
|
||||
this.Tick(3);
|
||||
break;
|
||||
case 3: // JR d
|
||||
this.JumpRelative((sbyte)this.FetchByte());
|
||||
this.Tick(8);
|
||||
break;
|
||||
case 4: // JR cc,d
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
if (this.JumpRelativeConditionalFlag(y - 4))
|
||||
{
|
||||
this.Tick(5);
|
||||
}
|
||||
|
||||
this.Tick(3);
|
||||
this.JumpRelativeConditionalFlag(y - 4);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException("Invalid operation mode");
|
||||
|
@ -1176,11 +1164,9 @@ namespace EightBit
|
|||
{
|
||||
case 0: // LD rp,nn
|
||||
this.RP(p).Word = this.FetchWord().Word;
|
||||
this.Tick(6);
|
||||
break;
|
||||
case 1: // ADD HL,rp
|
||||
this.HL2().Word = this.Add(this.HL2(), this.RP(p));
|
||||
this.Tick(7);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException("Invalid operation mode");
|
||||
|
@ -1198,26 +1184,22 @@ namespace EightBit
|
|||
++this.MEMPTR.Word;
|
||||
this.MEMPTR.High = this.Bus.Data = this.A;
|
||||
this.BusWrite();
|
||||
this.Tick(3);
|
||||
break;
|
||||
case 1: // LD (DE),A
|
||||
this.MEMPTR.Word = this.Bus.Address.Word = this.DE.Word;
|
||||
++this.MEMPTR.Word;
|
||||
this.MEMPTR.High = this.Bus.Data = this.A;
|
||||
this.BusWrite();
|
||||
this.Tick(3);
|
||||
break;
|
||||
case 2: // LD (nn),HL
|
||||
this.Bus.Address.Word = this.FetchWord().Word;
|
||||
this.SetWord(this.HL2());
|
||||
this.Tick(12);
|
||||
break;
|
||||
case 3: // LD (nn),A
|
||||
this.MEMPTR.Word = this.Bus.Address.Word = this.FetchWord().Word;
|
||||
++this.MEMPTR.Word;
|
||||
this.MEMPTR.High = this.Bus.Data = this.A;
|
||||
this.BusWrite();
|
||||
this.Tick(9);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException("Invalid operation mode");
|
||||
|
@ -1231,24 +1213,20 @@ namespace EightBit
|
|||
this.MEMPTR.Word = this.Bus.Address.Word = this.BC.Word;
|
||||
++this.MEMPTR.Word;
|
||||
this.A = this.BusRead();
|
||||
this.Tick(3);
|
||||
break;
|
||||
case 1: // LD A,(DE)
|
||||
this.MEMPTR.Word = this.Bus.Address.Word = this.DE.Word;
|
||||
++this.MEMPTR.Word;
|
||||
this.A = this.BusRead();
|
||||
this.Tick(3);
|
||||
break;
|
||||
case 2: // LD HL,(nn)
|
||||
this.Bus.Address.Word = this.FetchWord().Word;
|
||||
this.HL2().Word = this.GetWord().Word;
|
||||
this.Tick(12);
|
||||
break;
|
||||
case 3: // LD A,(nn)
|
||||
this.MEMPTR.Word = this.Bus.Address.Word = this.FetchWord().Word;
|
||||
++this.MEMPTR.Word;
|
||||
this.A = this.BusRead();
|
||||
this.Tick(9);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException("Invalid operation mode");
|
||||
|
@ -1273,41 +1251,50 @@ namespace EightBit
|
|||
throw new NotSupportedException("Invalid operation mode");
|
||||
}
|
||||
|
||||
this.Tick(2);
|
||||
break;
|
||||
case 4: // 8-bit INC
|
||||
if (this.displaced && memoryY)
|
||||
{
|
||||
this.FetchDisplacement();
|
||||
}
|
||||
if (memoryY && this.displaced)
|
||||
{
|
||||
this.FetchDisplacement();
|
||||
this.Tick(5);
|
||||
}
|
||||
|
||||
this.R(y, this.Increment(this.R(y)));
|
||||
break;
|
||||
var original = this.R(y);
|
||||
this.Tick();
|
||||
this.R(y, this.Increment(original));
|
||||
break;
|
||||
}
|
||||
case 5: // 8-bit DEC
|
||||
if (memoryY)
|
||||
{
|
||||
this.Tick(7);
|
||||
if (this.displaced)
|
||||
if (memoryY && this.displaced)
|
||||
{
|
||||
this.FetchDisplacement();
|
||||
this.Tick(5);
|
||||
}
|
||||
|
||||
var original = this.R(y);
|
||||
this.Tick();
|
||||
this.R(y, this.Decrement(original));
|
||||
break;
|
||||
}
|
||||
|
||||
this.R(y, this.Decrement(this.R(y)));
|
||||
break;
|
||||
case 6: // 8-bit load immediate
|
||||
if (memoryY)
|
||||
{
|
||||
this.Tick(3);
|
||||
if (this.displaced)
|
||||
if (memoryY && this.displaced)
|
||||
{
|
||||
this.FetchDisplacement();
|
||||
}
|
||||
}
|
||||
|
||||
this.R(y, this.FetchByte()); // LD r,n
|
||||
this.Tick(3);
|
||||
break;
|
||||
var value = this.FetchByte();
|
||||
if (this.displaced)
|
||||
{
|
||||
this.Tick(2);
|
||||
}
|
||||
|
||||
this.R(y, value); // LD r,n
|
||||
break;
|
||||
}
|
||||
case 7: // Assorted operations on accumulator/flags
|
||||
switch (y)
|
||||
{
|
||||
|
@ -1361,10 +1348,20 @@ namespace EightBit
|
|||
switch (y)
|
||||
{
|
||||
case 4:
|
||||
if (this.displaced)
|
||||
{
|
||||
this.Tick(5);
|
||||
}
|
||||
|
||||
this.H = this.R(z);
|
||||
normal = false;
|
||||
break;
|
||||
case 5:
|
||||
if (this.displaced)
|
||||
{
|
||||
this.Tick(5);
|
||||
}
|
||||
|
||||
this.L = this.R(z);
|
||||
normal = false;
|
||||
break;
|
||||
|
@ -1376,10 +1373,20 @@ namespace EightBit
|
|||
switch (z)
|
||||
{
|
||||
case 4:
|
||||
if (this.displaced)
|
||||
{
|
||||
this.Tick(5);
|
||||
}
|
||||
|
||||
this.R(y, this.H);
|
||||
normal = false;
|
||||
break;
|
||||
case 5:
|
||||
if (this.displaced)
|
||||
{
|
||||
this.Tick(5);
|
||||
}
|
||||
|
||||
this.R(y, this.L);
|
||||
normal = false;
|
||||
break;
|
||||
|
@ -1389,13 +1396,12 @@ namespace EightBit
|
|||
|
||||
if (normal)
|
||||
{
|
||||
this.R(y, this.R(z));
|
||||
}
|
||||
if (this.displaced)
|
||||
{
|
||||
this.Tick(5);
|
||||
}
|
||||
|
||||
// M operations
|
||||
if (memoryY || memoryZ)
|
||||
{
|
||||
this.Tick(3);
|
||||
this.R(y, this.R(z));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1408,10 +1414,10 @@ namespace EightBit
|
|||
{ // Operate on accumulator and register/memory location
|
||||
if (memoryZ)
|
||||
{
|
||||
this.Tick(3);
|
||||
if (this.displaced)
|
||||
{
|
||||
this.FetchDisplacement();
|
||||
this.Tick(5);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1453,26 +1459,19 @@ namespace EightBit
|
|||
switch (z)
|
||||
{
|
||||
case 0: // Conditional return
|
||||
if (this.ReturnConditionalFlag(y))
|
||||
{
|
||||
this.Tick(6);
|
||||
}
|
||||
|
||||
this.Tick();
|
||||
this.ReturnConditionalFlag(y);
|
||||
break;
|
||||
case 1: // POP & various ops
|
||||
switch (q)
|
||||
{
|
||||
case 0: // POP rp2[p]
|
||||
this.RP2(p).Word = this.PopWord().Word;
|
||||
this.Tick(6);
|
||||
break;
|
||||
case 1:
|
||||
switch (p)
|
||||
{
|
||||
case 0: // RET
|
||||
this.Return();
|
||||
this.Tick(6);
|
||||
break;
|
||||
case 1: // EXX
|
||||
this.Exx();
|
||||
|
@ -1495,14 +1494,12 @@ namespace EightBit
|
|||
break;
|
||||
case 2: // Conditional jump
|
||||
this.JumpConditionalFlag(y);
|
||||
this.Tick(6);
|
||||
break;
|
||||
case 3: // Assorted operations
|
||||
switch (y)
|
||||
{
|
||||
case 0: // JP nn
|
||||
this.Jump(this.MEMPTR.Word = this.FetchWord().Word);
|
||||
this.Tick(6);
|
||||
break;
|
||||
case 1: // CB prefix
|
||||
this.prefixCB = true;
|
||||
|
@ -1519,15 +1516,12 @@ namespace EightBit
|
|||
break;
|
||||
case 2: // OUT (n),A
|
||||
this.WritePort(this.FetchByte());
|
||||
this.Tick(7);
|
||||
break;
|
||||
case 3: // IN A,(n)
|
||||
this.A = this.ReadPort(this.FetchByte());
|
||||
this.Tick(7);
|
||||
break;
|
||||
case 4: // EX (SP),HL
|
||||
this.XHTL(this.HL2());
|
||||
this.Tick(15);
|
||||
break;
|
||||
case 5: // EX DE,HL
|
||||
(this.DE.Word, this.HL.Word) = (this.HL.Word, this.DE.Word);
|
||||
|
@ -1544,26 +1538,21 @@ namespace EightBit
|
|||
|
||||
break;
|
||||
case 4: // Conditional call: CALL cc[y], nn
|
||||
if (this.CallConditionalFlag(y))
|
||||
{
|
||||
this.Tick(7);
|
||||
}
|
||||
|
||||
this.Tick(6);
|
||||
this.CallConditionalFlag(y);
|
||||
break;
|
||||
case 5: // PUSH & various ops
|
||||
switch (q)
|
||||
{
|
||||
case 0: // PUSH rp2[p]
|
||||
this.Tick();
|
||||
this.PushWord(this.RP2(p));
|
||||
this.Tick(7);
|
||||
break;
|
||||
case 1:
|
||||
switch (p)
|
||||
{
|
||||
case 0: // CALL nn
|
||||
this.Call(this.MEMPTR.Word = this.FetchWord().Word);
|
||||
this.Tick(13);
|
||||
this.MEMPTR.Word = this.FetchWord().Word;
|
||||
this.Call(this.MEMPTR.Word);
|
||||
break;
|
||||
case 1: // DD prefix
|
||||
this.displaced = this.prefixDD = true;
|
||||
|
@ -1620,13 +1609,11 @@ namespace EightBit
|
|||
throw new NotSupportedException("Invalid operation mode");
|
||||
}
|
||||
|
||||
this.Tick(3);
|
||||
break;
|
||||
}
|
||||
|
||||
case 7: // Restart: RST y * 8
|
||||
this.Restart((byte)(y << 3));
|
||||
this.Tick(7);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException("Invalid operation mode");
|
||||
|
@ -1640,12 +1627,12 @@ namespace EightBit
|
|||
{
|
||||
this.RaiseNMI();
|
||||
this.RaiseHALT();
|
||||
this.IFF2 = this.IFF1;
|
||||
this.IFF1 = false;
|
||||
this.LowerM1();
|
||||
var discarded = this.Bus.Data;
|
||||
this.RaiseM1();
|
||||
this.Restart(0x66);
|
||||
this.Tick(13);
|
||||
}
|
||||
|
||||
private void FetchDisplacement() => this.displacement = (sbyte)this.FetchByte();
|
||||
|
@ -1671,18 +1658,16 @@ namespace EightBit
|
|||
// instruction so that no other concurrent operation can be performed.
|
||||
private byte ReadInitialOpCode()
|
||||
{
|
||||
this.Tick();
|
||||
this.LowerM1();
|
||||
var returned = this.BusRead(this.PC.Word);
|
||||
this.RaiseM1();
|
||||
this.Tick(2);
|
||||
this.Bus.Address.Low = this.REFRESH;
|
||||
this.Bus.Address.High = this.IV;
|
||||
this.LowerRFSH();
|
||||
this.LowerMREQ();
|
||||
this.Tick();
|
||||
this.RaiseMREQ();
|
||||
this.RaiseRFSH();
|
||||
this.Tick();
|
||||
return returned;
|
||||
}
|
||||
|
||||
|
@ -1729,97 +1714,33 @@ namespace EightBit
|
|||
|
||||
private void RetI() => this.RetN();
|
||||
|
||||
private bool JumpRelativeConditionalFlag(int flag)
|
||||
private bool ConvertCondition(int flag) => flag switch
|
||||
{
|
||||
switch (flag)
|
||||
0 => (this.F & (byte)StatusBits.ZF) == 0,
|
||||
1 => (this.F & (byte)StatusBits.ZF) != 0,
|
||||
2 => (this.F & (byte)StatusBits.CF) == 0,
|
||||
3 => (this.F & (byte)StatusBits.CF) != 0,
|
||||
4 => (this.F & (byte)StatusBits.PF) == 0,
|
||||
5 => (this.F & (byte)StatusBits.PF) != 0,
|
||||
6 => (this.F & (byte)StatusBits.SF) == 0,
|
||||
7 => (this.F & (byte)StatusBits.SF) != 0,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(flag)),
|
||||
};
|
||||
|
||||
private void ReturnConditionalFlag(int flag)
|
||||
{
|
||||
if (this.ConvertCondition(flag))
|
||||
{
|
||||
case 0: // NZ
|
||||
return this.JumpRelativeConditional((this.F & (byte)StatusBits.ZF) == 0);
|
||||
case 1: // Z
|
||||
return this.JumpRelativeConditional((this.F & (byte)StatusBits.ZF) != 0);
|
||||
case 2: // NC
|
||||
return this.JumpRelativeConditional((this.F & (byte)StatusBits.CF) == 0);
|
||||
case 3: // C
|
||||
return this.JumpRelativeConditional((this.F & (byte)StatusBits.CF) != 0);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(flag));
|
||||
this.Tick();
|
||||
this.Return();
|
||||
}
|
||||
}
|
||||
|
||||
private bool ReturnConditionalFlag(int flag)
|
||||
{
|
||||
switch (flag)
|
||||
{
|
||||
case 0: // NZ
|
||||
return this.ReturnConditional((this.F & (byte)StatusBits.ZF) == 0);
|
||||
case 1: // Z
|
||||
return this.ReturnConditional((this.F & (byte)StatusBits.ZF) != 0);
|
||||
case 2: // NC
|
||||
return this.ReturnConditional((this.F & (byte)StatusBits.CF) == 0);
|
||||
case 3: // C
|
||||
return this.ReturnConditional((this.F & (byte)StatusBits.CF) != 0);
|
||||
case 4: // PO
|
||||
return this.ReturnConditional((this.F & (byte)StatusBits.PF) == 0);
|
||||
case 5: // PE
|
||||
return this.ReturnConditional((this.F & (byte)StatusBits.PF) != 0);
|
||||
case 6: // P
|
||||
return this.ReturnConditional((this.F & (byte)StatusBits.SF) == 0);
|
||||
case 7: // M
|
||||
return this.ReturnConditional((this.F & (byte)StatusBits.SF) != 0);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(flag));
|
||||
}
|
||||
}
|
||||
private void JumpRelativeConditionalFlag(int flag) => this.JumpRelativeConditional(this.ConvertCondition(flag));
|
||||
|
||||
private bool JumpConditionalFlag(int flag)
|
||||
{
|
||||
switch (flag)
|
||||
{
|
||||
case 0: // NZ
|
||||
return this.JumpConditional((this.F & (byte)StatusBits.ZF) == 0);
|
||||
case 1: // Z
|
||||
return this.JumpConditional((this.F & (byte)StatusBits.ZF) != 0);
|
||||
case 2: // NC
|
||||
return this.JumpConditional((this.F & (byte)StatusBits.CF) == 0);
|
||||
case 3: // C
|
||||
return this.JumpConditional((this.F & (byte)StatusBits.CF) != 0);
|
||||
case 4: // PO
|
||||
return this.JumpConditional((this.F & (byte)StatusBits.PF) == 0);
|
||||
case 5: // PE
|
||||
return this.JumpConditional((this.F & (byte)StatusBits.PF) != 0);
|
||||
case 6: // P
|
||||
return this.JumpConditional((this.F & (byte)StatusBits.SF) == 0);
|
||||
case 7: // M
|
||||
return this.JumpConditional((this.F & (byte)StatusBits.SF) != 0);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(flag));
|
||||
}
|
||||
}
|
||||
private void JumpConditionalFlag(int flag) => this.JumpConditional(this.ConvertCondition(flag));
|
||||
|
||||
private bool CallConditionalFlag(int flag)
|
||||
{
|
||||
switch (flag)
|
||||
{
|
||||
case 0: // NZ
|
||||
return this.CallConditional((this.F & (byte)StatusBits.ZF) == 0);
|
||||
case 1: // Z
|
||||
return this.CallConditional((this.F & (byte)StatusBits.ZF) != 0);
|
||||
case 2: // NC
|
||||
return this.CallConditional((this.F & (byte)StatusBits.CF) == 0);
|
||||
case 3: // C
|
||||
return this.CallConditional((this.F & (byte)StatusBits.CF) != 0);
|
||||
case 4: // PO
|
||||
return this.CallConditional((this.F & (byte)StatusBits.PF) == 0);
|
||||
case 5: // PE
|
||||
return this.CallConditional((this.F & (byte)StatusBits.PF) != 0);
|
||||
case 6: // P
|
||||
return this.CallConditional((this.F & (byte)StatusBits.SF) == 0);
|
||||
case 7: // M
|
||||
return this.CallConditional((this.F & (byte)StatusBits.SF) != 0);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(flag));
|
||||
}
|
||||
}
|
||||
private void CallConditionalFlag(int flag) => this.CallConditional(this.ConvertCondition(flag));
|
||||
|
||||
private ushort SBC(Register16 operand, Register16 value)
|
||||
{
|
||||
|
@ -2075,6 +1996,7 @@ namespace EightBit
|
|||
this.MEMPTR.Low = this.BusRead(this.SP.Word);
|
||||
++this.Bus.Address.Word;
|
||||
this.MEMPTR.High = this.BusRead();
|
||||
this.Tick();
|
||||
this.BusWrite(exchange.High);
|
||||
exchange.High = this.MEMPTR.High;
|
||||
--this.Bus.Address.Word;
|
||||
|
@ -2153,7 +2075,9 @@ namespace EightBit
|
|||
private void BlockIn(Register16 source, ushort destination)
|
||||
{
|
||||
this.MEMPTR.Word = this.Bus.Address.Word = source.Word;
|
||||
this.Tick();
|
||||
var value = this.ReadPort();
|
||||
this.Tick(3);
|
||||
this.BusWrite(destination, value);
|
||||
source.High = this.Decrement(source.High);
|
||||
this.F = SetBit(this.F, StatusBits.NF);
|
||||
|
@ -2185,6 +2109,7 @@ namespace EightBit
|
|||
|
||||
private void BlockOut(ushort source, Register16 destination)
|
||||
{
|
||||
this.Tick();
|
||||
var value = this.BusRead(source);
|
||||
destination.High = this.Decrement(destination.High);
|
||||
this.Bus.Address.Word = destination.Word;
|
||||
|
@ -2240,6 +2165,7 @@ namespace EightBit
|
|||
this.MEMPTR.Word = this.Bus.Address.Word = this.HL.Word;
|
||||
++this.MEMPTR.Word;
|
||||
var memory = this.BusRead();
|
||||
this.Tick(4);
|
||||
this.BusWrite((byte)(PromoteNibble(this.A) | HighNibble(memory)));
|
||||
this.A = (byte)(HigherNibble(this.A) | LowerNibble(memory));
|
||||
this.F = AdjustSZPXY(this.F, this.A);
|
||||
|
@ -2251,6 +2177,7 @@ namespace EightBit
|
|||
this.MEMPTR.Word = this.Bus.Address.Word = this.HL.Word;
|
||||
++this.MEMPTR.Word;
|
||||
var memory = this.BusRead();
|
||||
this.Tick(4);
|
||||
this.BusWrite((byte)(PromoteNibble(memory) | LowNibble(this.A)));
|
||||
this.A = (byte)(HigherNibble(this.A) | HighNibble(memory));
|
||||
this.F = AdjustSZPXY(this.F, this.A);
|
||||
|
@ -2267,6 +2194,7 @@ namespace EightBit
|
|||
|
||||
private void WritePort()
|
||||
{
|
||||
this.Tick();
|
||||
this.LowerIORQ();
|
||||
this.LowerWR();
|
||||
this.ports.Write(this.Bus.Address.Low, this.Bus.Data);
|
||||
|
@ -2283,6 +2211,7 @@ namespace EightBit
|
|||
|
||||
private byte ReadPort()
|
||||
{
|
||||
this.Tick();
|
||||
this.LowerIORQ();
|
||||
this.LowerRD();
|
||||
var returned = this.Bus.Data = this.ports.Read(this.Bus.Address.Low);
|
||||
|
|
Loading…
Reference in New Issue