Add OPL4 playback and analyzer support
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu
|
||||
{
|
||||
/// <summary>
|
||||
/// Represent masks for the Z80 flags register
|
||||
/// </summary>
|
||||
public static class CpuFlags
|
||||
{
|
||||
#region Flags
|
||||
|
||||
/// <summary>
|
||||
/// Sign Flag
|
||||
/// [Set if the 2-complement value is negative (copy of MSB)]
|
||||
/// </summary>
|
||||
public const byte S = 0x80;
|
||||
|
||||
/// <summary>
|
||||
/// Zero Flag
|
||||
/// [Set if the value is zero]
|
||||
/// </summary>
|
||||
public const byte Z = 0x40;
|
||||
|
||||
/// <summary>
|
||||
/// Undocumented flag F5
|
||||
/// </summary>
|
||||
public const byte F5 = 0x20;
|
||||
|
||||
/// <summary>
|
||||
/// Half Carry Flag
|
||||
/// [Carry from bit 3 to bit 4]
|
||||
/// </summary>
|
||||
public const byte H = 0x10;
|
||||
|
||||
/// <summary>
|
||||
/// Undocumented flag F3
|
||||
/// </summary>
|
||||
public const byte F3 = 0x08;
|
||||
|
||||
/// <summary>
|
||||
/// P/V - Parity/Overflow Flag
|
||||
/// [Parity set if even number of bits set.
|
||||
/// Overflow set if the 2-complement result does not fit in the register]
|
||||
/// </summary>
|
||||
public const byte P = 0x04;
|
||||
|
||||
/// <summary>
|
||||
/// Add/Subtract Flag
|
||||
/// [Set if the last operation was a subtraction]
|
||||
/// </summary>
|
||||
public const byte N = 0x02;
|
||||
|
||||
/// <summary>
|
||||
/// Carry Flag
|
||||
/// [Set if the result did not fit in the register]
|
||||
/// </summary>
|
||||
public const byte C = 0x01;
|
||||
|
||||
#endregion Flags
|
||||
|
||||
|
||||
#region Inverted Masks
|
||||
|
||||
public const byte NotS = S ^ 0xFF;
|
||||
public const byte NotZ = Z ^ 0xFF;
|
||||
public const byte NotF5 = F5 ^ 0xFF;
|
||||
public const byte NotH = H ^ 0xFF;
|
||||
public const byte NotF3 = F3 ^ 0xFF;
|
||||
public const byte NotP = P ^ 0xFF;
|
||||
public const byte NotN = N ^ 0xFF;
|
||||
public const byte NotC = C ^ 0xFF;
|
||||
|
||||
#endregion Inverted Masks
|
||||
|
||||
|
||||
#region Internal flag sets (used by engine)
|
||||
|
||||
internal const byte F3F5 = F3 | F5;
|
||||
internal const byte SF3F5 = S | F3 | F5;
|
||||
internal const byte PCF3F5 = P | C | F3 | F5;
|
||||
internal const byte HNCF3F5 = H | N | C | F3 | F5;
|
||||
internal const byte HPNF3F5 = H | P | N | F3 | F5;
|
||||
internal const byte HC = H | C;
|
||||
internal const byte SZP = S | Z | P;
|
||||
internal const byte SZPC = S | Z | P | C;
|
||||
internal const byte HN = H | N;
|
||||
|
||||
internal const byte NotF3F5 = F3F5 ^ 0xFF;
|
||||
internal const byte NotSF3F5 = SF3F5 ^ 0xFF;
|
||||
internal const byte NotPCF3F5 = PCF3F5 ^ 0xFF;
|
||||
internal const byte NotHNCF3F5 = HNCF3F5 ^ 0xFF;
|
||||
internal const byte NotHPNF3F5 = HPNF3F5 ^ 0xFF;
|
||||
internal const byte NotHC = HC ^ 0xFF;
|
||||
internal const byte NotSZP = SZP ^ 0xFF;
|
||||
internal const byte NotSZPC = SZPC ^ 0xFF;
|
||||
internal const byte NotHN = HN ^ 0xFF;
|
||||
|
||||
#endregion Internal flag sets (used by engine)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu
|
||||
{
|
||||
public enum CpuModeEx
|
||||
{
|
||||
None = 0,
|
||||
Cb,
|
||||
Ed,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu
|
||||
{
|
||||
public enum CpuModeIndex
|
||||
{
|
||||
None = 0,
|
||||
Ix,
|
||||
Iy,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Description: Z80 CPU Emulator [registers part]
|
||||
* Date: 18.03.2007
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using ZXMAK2.Engine.Cpu.Processor;
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public sealed class CpuRegs
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public ushort AF = 0;
|
||||
[FieldOffset(2)]
|
||||
public ushort BC = 0;
|
||||
[FieldOffset(4)]
|
||||
public ushort DE = 0;
|
||||
[FieldOffset(6)]
|
||||
public ushort HL = 0;
|
||||
[FieldOffset(8)]
|
||||
public ushort _AF = 0;
|
||||
[FieldOffset(10)]
|
||||
public ushort _BC = 0;
|
||||
[FieldOffset(12)]
|
||||
public ushort _DE = 0;
|
||||
[FieldOffset(14)]
|
||||
public ushort _HL = 0;
|
||||
[FieldOffset(16)]
|
||||
public ushort IX = 0;
|
||||
[FieldOffset(18)]
|
||||
public ushort IY = 0;
|
||||
[FieldOffset(20)]
|
||||
public ushort IR = 0;
|
||||
[FieldOffset(22)]
|
||||
public ushort PC = 0;
|
||||
[FieldOffset(24)]
|
||||
public ushort SP = 0;
|
||||
[FieldOffset(26)]
|
||||
public ushort MW = 0; // MEMPTR
|
||||
[FieldOffset(28)]
|
||||
public byte Q = 0; // TODO: Q flags
|
||||
|
||||
|
||||
[FieldOffset(1)]
|
||||
public byte A;
|
||||
[FieldOffset(0)]
|
||||
public byte F;
|
||||
[FieldOffset(3)]
|
||||
public byte B;
|
||||
[FieldOffset(2)]
|
||||
public byte C;
|
||||
[FieldOffset(5)]
|
||||
public byte D;
|
||||
[FieldOffset(4)]
|
||||
public byte E;
|
||||
[FieldOffset(7)]
|
||||
public byte H;
|
||||
[FieldOffset(6)]
|
||||
public byte L;
|
||||
[FieldOffset(17)]
|
||||
public byte XH;
|
||||
[FieldOffset(16)]
|
||||
public byte XL;
|
||||
[FieldOffset(19)]
|
||||
public byte YH;
|
||||
[FieldOffset(18)]
|
||||
public byte YL;
|
||||
[FieldOffset(21)]
|
||||
public byte I;
|
||||
[FieldOffset(20)]
|
||||
public byte R;
|
||||
|
||||
[FieldOffset(27)]
|
||||
public byte MH;
|
||||
[FieldOffset(26)]
|
||||
public byte ML;
|
||||
|
||||
[FieldOffset(23)]
|
||||
public byte PCH = 0;
|
||||
[FieldOffset(22)]
|
||||
public byte PCL = 0;
|
||||
|
||||
|
||||
#region Access Lamda Generators
|
||||
|
||||
// Access field from an object context
|
||||
// makes one native instruction lesser
|
||||
|
||||
internal Func<byte> CreateRegGetter(int r)
|
||||
{
|
||||
return r switch
|
||||
{
|
||||
CpuRegId.B => () => B,
|
||||
CpuRegId.C => () => C,
|
||||
CpuRegId.D => () => D,
|
||||
CpuRegId.E => () => E,
|
||||
CpuRegId.H => () => H,
|
||||
CpuRegId.L => () => L,
|
||||
CpuRegId.A => () => A,
|
||||
CpuRegId.F => () => F,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(r)),
|
||||
};
|
||||
}
|
||||
|
||||
internal Action<byte> CreateRegSetter(int r)
|
||||
{
|
||||
return r switch
|
||||
{
|
||||
CpuRegId.B => arg => B = arg,
|
||||
CpuRegId.C => arg => C = arg,
|
||||
CpuRegId.D => arg => D = arg,
|
||||
CpuRegId.E => arg => E = arg,
|
||||
CpuRegId.H => arg => H = arg,
|
||||
CpuRegId.L => arg => L = arg,
|
||||
CpuRegId.A => arg => A = arg,
|
||||
CpuRegId.F => arg => F = arg,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(r)),
|
||||
};
|
||||
}
|
||||
|
||||
internal Func<ushort> CreatePairGetter(int rr)
|
||||
{
|
||||
return rr switch
|
||||
{
|
||||
CpuRegId.Bc => () => BC,
|
||||
CpuRegId.De => () => DE,
|
||||
CpuRegId.Hl => () => HL,
|
||||
CpuRegId.Sp => () => SP,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(rr)),
|
||||
};
|
||||
}
|
||||
|
||||
internal Action<ushort> CreatePairSetter(int rr)
|
||||
{
|
||||
return rr switch
|
||||
{
|
||||
CpuRegId.Bc => arg => BC = arg,
|
||||
CpuRegId.De => arg => DE = arg,
|
||||
CpuRegId.Hl => arg => HL = arg,
|
||||
CpuRegId.Sp => arg => SP = arg,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(rr)),
|
||||
};
|
||||
}
|
||||
|
||||
#endregion Access Lamda Generators
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu
|
||||
{
|
||||
public enum CpuType
|
||||
{
|
||||
ZILOG_NMOS = 0,
|
||||
ZILOG_CMOS = 1,
|
||||
NEC_NMOS = 2, // NEC
|
||||
ST_CMOS = 3, // Z84?
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
using ZXMAK2.Engine.Cpu.Processor;
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu
|
||||
{
|
||||
public sealed class CpuUnit : Z80Cpu
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu.Processor
|
||||
{
|
||||
internal static class CpuRegId
|
||||
{
|
||||
public const int Bc = 0;
|
||||
public const int De = 1;
|
||||
public const int Hl = 2;
|
||||
public const int Sp = 3;
|
||||
|
||||
public const int B = 0;
|
||||
public const int C = 1;
|
||||
public const int D = 2;
|
||||
public const int E = 3;
|
||||
public const int H = 4;
|
||||
public const int L = 5;
|
||||
public const int F = 6;
|
||||
public const int A = 7;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,681 @@
|
||||
/*
|
||||
* Copyright 2015 Alex Makeev
|
||||
* Copyright 2009 SMT (ALU tables)
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --------------------------------------------------------------------
|
||||
* Most of the tables based on the source code of UnrealSpeccy emulator
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu.Processor
|
||||
{
|
||||
public static class CpuTables
|
||||
{
|
||||
#region .ctor
|
||||
|
||||
static CpuTables()
|
||||
{
|
||||
Logf = CreateLogf();
|
||||
Adcf = CreateAdcf();
|
||||
|
||||
// === make_rot === credits to SMT (Unreal)
|
||||
Rlcaf = new byte[0x100];
|
||||
Rrcaf = new byte[0x100];
|
||||
for (var i = 0; i < 0x100; i++)
|
||||
{
|
||||
Rlcaf[i] = (byte)(Rlcf[i] & 0x3B); // rra,rla uses same tables
|
||||
}
|
||||
for (var i = 0; i < 0x100; i++)
|
||||
{
|
||||
Rrcaf[i] = (byte)(Rrcf[i] & 0x3B);
|
||||
}
|
||||
|
||||
// === make_sbc === credits to SMT (Unreal)
|
||||
Sbcf = new byte[0x20000];
|
||||
Cpf = new byte[0x10000];
|
||||
Cpf8b = new byte[0x10000];
|
||||
for (var c = 0; c < 2; c++)
|
||||
{
|
||||
for (var x = 0; x < 0x100; x++)
|
||||
{
|
||||
for (var y = 0; y < 0x100; y++)
|
||||
{
|
||||
var res = x - y - c;
|
||||
var fl = res & CpuFlags.SF3F5;
|
||||
if ((res & 0xFF) == 0) fl |= CpuFlags.Z;
|
||||
if ((res & 0x10000) != 0) fl |= CpuFlags.C;
|
||||
var r = (sbyte)x - (sbyte)y - c;
|
||||
if (r >= 0x80 || r < -0x80) fl |= CpuFlags.P;
|
||||
if ((((x & 0x0F) - (res & 0x0F) - c) & 0x10) != 0) fl |= CpuFlags.H;
|
||||
fl |= CpuFlags.N;
|
||||
Sbcf[c * 0x10000 + x * 0x100 + y] = (byte)fl;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < 0x10000; i++)
|
||||
{
|
||||
Cpf[i] = (byte)((Sbcf[i] & CpuFlags.NotF3F5) | (i & CpuFlags.F3F5));
|
||||
var val = (byte)((i >> 8) - (i & 0xFF) - ((Sbcf[i] & CpuFlags.H) >> 4));
|
||||
Cpf8b[i] = (byte)((Sbcf[i] & CpuFlags.NotPCF3F5) + (val & CpuFlags.F3) + ((val << 4) & CpuFlags.F5));
|
||||
}
|
||||
|
||||
Parity = new byte[256];
|
||||
for (var i = 0; i < Parity.Length; i++)
|
||||
{
|
||||
// Bit Twiddling Hacks: Compute parity in parallel, 8-bit
|
||||
var p = (0x6996 >> ((i ^ (i >> 4)) & 0xf)) & 1;
|
||||
Parity[i] = p == 0 ? CpuFlags.P : (byte)0;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] CreateLogf()
|
||||
{
|
||||
// === make_log === credits to SMT (Unreal)
|
||||
var log_f = new byte[0x100];
|
||||
for (var x = 0; x < 0x100; x++)
|
||||
{
|
||||
var fl = x & CpuFlags.SF3F5;
|
||||
var p = CpuFlags.P;
|
||||
for (var i = 0x80; i != 0; i /= 2)
|
||||
{
|
||||
if ((x & i) == i) p ^= CpuFlags.P;
|
||||
}
|
||||
log_f[x] = (byte)(fl | p);
|
||||
}
|
||||
log_f[0] |= CpuFlags.Z;
|
||||
return log_f;
|
||||
}
|
||||
|
||||
private static byte[] CreateAdcf()
|
||||
{
|
||||
// === make_adc === credits to SMT (Unreal)
|
||||
var adcf = new byte[0x20000];
|
||||
for (var c = 0; c < 2; c++)
|
||||
{
|
||||
for (var x = 0; x < 0x100; x++)
|
||||
{
|
||||
for (var y = 0; y < 0x100; y++)
|
||||
{
|
||||
uint res = (uint)(x + y + c);
|
||||
byte fl = 0;
|
||||
if ((res & 0xFF) == 0) fl |= CpuFlags.Z;
|
||||
fl |= (byte)(res & CpuFlags.SF3F5);
|
||||
if (res >= 0x100) fl |= CpuFlags.C;
|
||||
if ((((x & 0x0F) + (y & 0x0F) + c) & 0x10) != 0) fl |= CpuFlags.H;
|
||||
int ri = (sbyte)x + (sbyte)y + c;
|
||||
if (ri >= 0x80 || ri <= -0x81) fl |= CpuFlags.P;
|
||||
adcf[c * 0x10000 + x * 0x100 + y] = fl;
|
||||
}
|
||||
}
|
||||
}
|
||||
return adcf;
|
||||
}
|
||||
|
||||
#endregion .ctor
|
||||
|
||||
|
||||
public static readonly byte[] Logf;
|
||||
public static readonly byte[] Adcf; // flags for adc and add
|
||||
public static readonly byte[] Rlcaf; // flags for RLCA
|
||||
public static readonly byte[] Rrcaf; // flags for RRCA
|
||||
public static readonly byte[] Sbcf; // flags for sub and sbc
|
||||
public static readonly byte[] Cpf; // flags for cp
|
||||
public static readonly byte[] Cpf8b; // flags for cpi(r)/cpd(r)
|
||||
public static readonly byte[] Parity; // parity calculator
|
||||
|
||||
|
||||
public static readonly byte[] Rlcf =
|
||||
// credits to SMT (Unreal)
|
||||
[ // for rlc r. may be for rlca (0x3B mask)
|
||||
0x44,0x00,0x00,0x04,0x08,0x0c,0x0c,0x08
|
||||
,0x00,0x04,0x04,0x00,0x0c,0x08,0x08,0x0c
|
||||
,0x20,0x24,0x24,0x20,0x2c,0x28,0x28,0x2c
|
||||
,0x24,0x20,0x20,0x24,0x28,0x2c,0x2c,0x28
|
||||
,0x00,0x04,0x04,0x00,0x0c,0x08,0x08,0x0c
|
||||
,0x04,0x00,0x00,0x04,0x08,0x0c,0x0c,0x08
|
||||
,0x24,0x20,0x20,0x24,0x28,0x2c,0x2c,0x28
|
||||
,0x20,0x24,0x24,0x20,0x2c,0x28,0x28,0x2c
|
||||
,0x80,0x84,0x84,0x80,0x8c,0x88,0x88,0x8c
|
||||
,0x84,0x80,0x80,0x84,0x88,0x8c,0x8c,0x88
|
||||
,0xa4,0xa0,0xa0,0xa4,0xa8,0xac,0xac,0xa8
|
||||
,0xa0,0xa4,0xa4,0xa0,0xac,0xa8,0xa8,0xac
|
||||
,0x84,0x80,0x80,0x84,0x88,0x8c,0x8c,0x88
|
||||
,0x80,0x84,0x84,0x80,0x8c,0x88,0x88,0x8c
|
||||
,0xa0,0xa4,0xa4,0xa0,0xac,0xa8,0xa8,0xac
|
||||
,0xa4,0xa0,0xa0,0xa4,0xa8,0xac,0xac,0xa8
|
||||
,0x01,0x05,0x05,0x01,0x0d,0x09,0x09,0x0d
|
||||
,0x05,0x01,0x01,0x05,0x09,0x0d,0x0d,0x09
|
||||
,0x25,0x21,0x21,0x25,0x29,0x2d,0x2d,0x29
|
||||
,0x21,0x25,0x25,0x21,0x2d,0x29,0x29,0x2d
|
||||
,0x05,0x01,0x01,0x05,0x09,0x0d,0x0d,0x09
|
||||
,0x01,0x05,0x05,0x01,0x0d,0x09,0x09,0x0d
|
||||
,0x21,0x25,0x25,0x21,0x2d,0x29,0x29,0x2d
|
||||
,0x25,0x21,0x21,0x25,0x29,0x2d,0x2d,0x29
|
||||
,0x85,0x81,0x81,0x85,0x89,0x8d,0x8d,0x89
|
||||
,0x81,0x85,0x85,0x81,0x8d,0x89,0x89,0x8d
|
||||
,0xa1,0xa5,0xa5,0xa1,0xad,0xa9,0xa9,0xad
|
||||
,0xa5,0xa1,0xa1,0xa5,0xa9,0xad,0xad,0xa9
|
||||
,0x81,0x85,0x85,0x81,0x8d,0x89,0x89,0x8d
|
||||
,0x85,0x81,0x81,0x85,0x89,0x8d,0x8d,0x89
|
||||
,0xa5,0xa1,0xa1,0xa5,0xa9,0xad,0xad,0xa9
|
||||
,0xa1,0xa5,0xa5,0xa1,0xad,0xa9,0xa9,0xad
|
||||
];
|
||||
|
||||
public static readonly byte[] Rrcf =
|
||||
// credits to SMT (Unreal)
|
||||
[ // rrc r. may be for rlca (0x3B mask)
|
||||
0x44,0x81,0x00,0x85,0x00,0x85,0x04,0x81
|
||||
,0x00,0x85,0x04,0x81,0x04,0x81,0x00,0x85
|
||||
,0x08,0x8d,0x0c,0x89,0x0c,0x89,0x08,0x8d
|
||||
,0x0c,0x89,0x08,0x8d,0x08,0x8d,0x0c,0x89
|
||||
,0x00,0x85,0x04,0x81,0x04,0x81,0x00,0x85
|
||||
,0x04,0x81,0x00,0x85,0x00,0x85,0x04,0x81
|
||||
,0x0c,0x89,0x08,0x8d,0x08,0x8d,0x0c,0x89
|
||||
,0x08,0x8d,0x0c,0x89,0x0c,0x89,0x08,0x8d
|
||||
,0x20,0xa5,0x24,0xa1,0x24,0xa1,0x20,0xa5
|
||||
,0x24,0xa1,0x20,0xa5,0x20,0xa5,0x24,0xa1
|
||||
,0x2c,0xa9,0x28,0xad,0x28,0xad,0x2c,0xa9
|
||||
,0x28,0xad,0x2c,0xa9,0x2c,0xa9,0x28,0xad
|
||||
,0x24,0xa1,0x20,0xa5,0x20,0xa5,0x24,0xa1
|
||||
,0x20,0xa5,0x24,0xa1,0x24,0xa1,0x20,0xa5
|
||||
,0x28,0xad,0x2c,0xa9,0x2c,0xa9,0x28,0xad
|
||||
,0x2c,0xa9,0x28,0xad,0x28,0xad,0x2c,0xa9
|
||||
,0x00,0x85,0x04,0x81,0x04,0x81,0x00,0x85
|
||||
,0x04,0x81,0x00,0x85,0x00,0x85,0x04,0x81
|
||||
,0x0c,0x89,0x08,0x8d,0x08,0x8d,0x0c,0x89
|
||||
,0x08,0x8d,0x0c,0x89,0x0c,0x89,0x08,0x8d
|
||||
,0x04,0x81,0x00,0x85,0x00,0x85,0x04,0x81
|
||||
,0x00,0x85,0x04,0x81,0x04,0x81,0x00,0x85
|
||||
,0x08,0x8d,0x0c,0x89,0x0c,0x89,0x08,0x8d
|
||||
,0x0c,0x89,0x08,0x8d,0x08,0x8d,0x0c,0x89
|
||||
,0x24,0xa1,0x20,0xa5,0x20,0xa5,0x24,0xa1
|
||||
,0x20,0xa5,0x24,0xa1,0x24,0xa1,0x20,0xa5
|
||||
,0x28,0xad,0x2c,0xa9,0x2c,0xa9,0x28,0xad
|
||||
,0x2c,0xa9,0x28,0xad,0x28,0xad,0x2c,0xa9
|
||||
,0x20,0xa5,0x24,0xa1,0x24,0xa1,0x20,0xa5
|
||||
,0x24,0xa1,0x20,0xa5,0x20,0xa5,0x24,0xa1
|
||||
,0x2c,0xa9,0x28,0xad,0x28,0xad,0x2c,0xa9
|
||||
,0x28,0xad,0x2c,0xa9,0x2c,0xa9,0x28,0xad
|
||||
];
|
||||
|
||||
public static readonly byte[] Incf =
|
||||
// credits to SMT (Unreal)
|
||||
[
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08
|
||||
,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x10
|
||||
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08
|
||||
,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x30
|
||||
,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x28
|
||||
,0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x30
|
||||
,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x28
|
||||
,0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x10
|
||||
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08
|
||||
,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x10
|
||||
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08
|
||||
,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x30
|
||||
,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x28
|
||||
,0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x30
|
||||
,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x28
|
||||
,0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x94
|
||||
,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x88
|
||||
,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x90
|
||||
,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x88
|
||||
,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0xb0
|
||||
,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa8
|
||||
,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xb0
|
||||
,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa8
|
||||
,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0x90
|
||||
,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x88
|
||||
,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x90
|
||||
,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x88
|
||||
,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0xb0
|
||||
,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa8
|
||||
,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xb0
|
||||
,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa8
|
||||
,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0x50
|
||||
];
|
||||
|
||||
public static readonly byte[] Decf =
|
||||
// credits to SMT (Unreal)
|
||||
[
|
||||
0xba,0x42,0x02,0x02,0x02,0x02,0x02,0x02
|
||||
,0x02,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a
|
||||
,0x1a,0x02,0x02,0x02,0x02,0x02,0x02,0x02
|
||||
,0x02,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a
|
||||
,0x1a,0x22,0x22,0x22,0x22,0x22,0x22,0x22
|
||||
,0x22,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a
|
||||
,0x3a,0x22,0x22,0x22,0x22,0x22,0x22,0x22
|
||||
,0x22,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a
|
||||
,0x3a,0x02,0x02,0x02,0x02,0x02,0x02,0x02
|
||||
,0x02,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a
|
||||
,0x1a,0x02,0x02,0x02,0x02,0x02,0x02,0x02
|
||||
,0x02,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a
|
||||
,0x1a,0x22,0x22,0x22,0x22,0x22,0x22,0x22
|
||||
,0x22,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a
|
||||
,0x3a,0x22,0x22,0x22,0x22,0x22,0x22,0x22
|
||||
,0x22,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a
|
||||
,0x3e,0x82,0x82,0x82,0x82,0x82,0x82,0x82
|
||||
,0x82,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a
|
||||
,0x9a,0x82,0x82,0x82,0x82,0x82,0x82,0x82
|
||||
,0x82,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a
|
||||
,0x9a,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2
|
||||
,0xa2,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa
|
||||
,0xba,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2
|
||||
,0xa2,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa
|
||||
,0xba,0x82,0x82,0x82,0x82,0x82,0x82,0x82
|
||||
,0x82,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a
|
||||
,0x9a,0x82,0x82,0x82,0x82,0x82,0x82,0x82
|
||||
,0x82,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a
|
||||
,0x9a,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2
|
||||
,0xa2,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa
|
||||
,0xba,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2
|
||||
,0xa2,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa
|
||||
];
|
||||
|
||||
public static readonly ushort[] Daaf =
|
||||
// credits to SMT (converted from Unreal)
|
||||
[
|
||||
0x0044,0x0100,0x0200,0x0304,0x0400,0x0504,0x0604,0x0700,0x0808,0x090C,
|
||||
0x1010,0x1114,0x1214,0x1310,0x1414,0x1510,0x1000,0x1104,0x1204,0x1300,
|
||||
0x1404,0x1500,0x1600,0x1704,0x180C,0x1908,0x2030,0x2134,0x2234,0x2330,
|
||||
0x2434,0x2530,0x2020,0x2124,0x2224,0x2320,0x2424,0x2520,0x2620,0x2724,
|
||||
0x282C,0x2928,0x3034,0x3130,0x3230,0x3334,0x3430,0x3534,0x3024,0x3120,
|
||||
0x3220,0x3324,0x3420,0x3524,0x3624,0x3720,0x3828,0x392C,0x4010,0x4114,
|
||||
0x4214,0x4310,0x4414,0x4510,0x4000,0x4104,0x4204,0x4300,0x4404,0x4500,
|
||||
0x4600,0x4704,0x480C,0x4908,0x5014,0x5110,0x5210,0x5314,0x5410,0x5514,
|
||||
0x5004,0x5100,0x5200,0x5304,0x5400,0x5504,0x5604,0x5700,0x5808,0x590C,
|
||||
0x6034,0x6130,0x6230,0x6334,0x6430,0x6534,0x6024,0x6120,0x6220,0x6324,
|
||||
0x6420,0x6524,0x6624,0x6720,0x6828,0x692C,0x7030,0x7134,0x7234,0x7330,
|
||||
0x7434,0x7530,0x7020,0x7124,0x7224,0x7320,0x7424,0x7520,0x7620,0x7724,
|
||||
0x782C,0x7928,0x8090,0x8194,0x8294,0x8390,0x8494,0x8590,0x8080,0x8184,
|
||||
0x8284,0x8380,0x8484,0x8580,0x8680,0x8784,0x888C,0x8988,0x9094,0x9190,
|
||||
0x9290,0x9394,0x9490,0x9594,0x9084,0x9180,0x9280,0x9384,0x9480,0x9584,
|
||||
0x9684,0x9780,0x9888,0x998C,0x0055,0x0111,0x0211,0x0315,0x0411,0x0515,
|
||||
0x0045,0x0101,0x0201,0x0305,0x0401,0x0505,0x0605,0x0701,0x0809,0x090D,
|
||||
0x1011,0x1115,0x1215,0x1311,0x1415,0x1511,0x1001,0x1105,0x1205,0x1301,
|
||||
0x1405,0x1501,0x1601,0x1705,0x180D,0x1909,0x2031,0x2135,0x2235,0x2331,
|
||||
0x2435,0x2531,0x2021,0x2125,0x2225,0x2321,0x2425,0x2521,0x2621,0x2725,
|
||||
0x282D,0x2929,0x3035,0x3131,0x3231,0x3335,0x3431,0x3535,0x3025,0x3121,
|
||||
0x3221,0x3325,0x3421,0x3525,0x3625,0x3721,0x3829,0x392D,0x4011,0x4115,
|
||||
0x4215,0x4311,0x4415,0x4511,0x4001,0x4105,0x4205,0x4301,0x4405,0x4501,
|
||||
0x4601,0x4705,0x480D,0x4909,0x5015,0x5111,0x5211,0x5315,0x5411,0x5515,
|
||||
0x5005,0x5101,0x5201,0x5305,0x5401,0x5505,0x5605,0x5701,0x5809,0x590D,
|
||||
0x6035,0x6131,0x6231,0x6335,0x6431,0x6535,0x6025,0x6121,0x6221,0x6325,
|
||||
0x6421,0x6525,0x6625,0x6721,0x6829,0x692D,0x7031,0x7135,0x7235,0x7331,
|
||||
0x7435,0x7531,0x7021,0x7125,0x7225,0x7321,0x7425,0x7521,0x7621,0x7725,
|
||||
0x782D,0x7929,0x8091,0x8195,0x8295,0x8391,0x8495,0x8591,0x8081,0x8185,
|
||||
0x8285,0x8381,0x8485,0x8581,0x8681,0x8785,0x888D,0x8989,0x9095,0x9191,
|
||||
0x9291,0x9395,0x9491,0x9595,0x9085,0x9181,0x9281,0x9385,0x9481,0x9585,
|
||||
0x9685,0x9781,0x9889,0x998D,0xA0B5,0xA1B1,0xA2B1,0xA3B5,0xA4B1,0xA5B5,
|
||||
0xA0A5,0xA1A1,0xA2A1,0xA3A5,0xA4A1,0xA5A5,0xA6A5,0xA7A1,0xA8A9,0xA9AD,
|
||||
0xB0B1,0xB1B5,0xB2B5,0xB3B1,0xB4B5,0xB5B1,0xB0A1,0xB1A5,0xB2A5,0xB3A1,
|
||||
0xB4A5,0xB5A1,0xB6A1,0xB7A5,0xB8AD,0xB9A9,0xC095,0xC191,0xC291,0xC395,
|
||||
0xC491,0xC595,0xC085,0xC181,0xC281,0xC385,0xC481,0xC585,0xC685,0xC781,
|
||||
0xC889,0xC98D,0xD091,0xD195,0xD295,0xD391,0xD495,0xD591,0xD081,0xD185,
|
||||
0xD285,0xD381,0xD485,0xD581,0xD681,0xD785,0xD88D,0xD989,0xE0B1,0xE1B5,
|
||||
0xE2B5,0xE3B1,0xE4B5,0xE5B1,0xE0A1,0xE1A5,0xE2A5,0xE3A1,0xE4A5,0xE5A1,
|
||||
0xE6A1,0xE7A5,0xE8AD,0xE9A9,0xF0B5,0xF1B1,0xF2B1,0xF3B5,0xF4B1,0xF5B5,
|
||||
0xF0A5,0xF1A1,0xF2A1,0xF3A5,0xF4A1,0xF5A5,0xF6A5,0xF7A1,0xF8A9,0xF9AD,
|
||||
0x0055,0x0111,0x0211,0x0315,0x0411,0x0515,0x0045,0x0101,0x0201,0x0305,
|
||||
0x0401,0x0505,0x0605,0x0701,0x0809,0x090D,0x1011,0x1115,0x1215,0x1311,
|
||||
0x1415,0x1511,0x1001,0x1105,0x1205,0x1301,0x1405,0x1501,0x1601,0x1705,
|
||||
0x180D,0x1909,0x2031,0x2135,0x2235,0x2331,0x2435,0x2531,0x2021,0x2125,
|
||||
0x2225,0x2321,0x2425,0x2521,0x2621,0x2725,0x282D,0x2929,0x3035,0x3131,
|
||||
0x3231,0x3335,0x3431,0x3535,0x3025,0x3121,0x3221,0x3325,0x3421,0x3525,
|
||||
0x3625,0x3721,0x3829,0x392D,0x4011,0x4115,0x4215,0x4311,0x4415,0x4511,
|
||||
0x4001,0x4105,0x4205,0x4301,0x4405,0x4501,0x4601,0x4705,0x480D,0x4909,
|
||||
0x5015,0x5111,0x5211,0x5315,0x5411,0x5515,0x5005,0x5101,0x5201,0x5305,
|
||||
0x5401,0x5505,0x5605,0x5701,0x5809,0x590D,0x6035,0x6131,0x6231,0x6335,
|
||||
0x6431,0x6535,0x0046,0x0102,0x0202,0x0306,0x0402,0x0506,0x0606,0x0702,
|
||||
0x080A,0x090E,0x0402,0x0506,0x0606,0x0702,0x080A,0x090E,0x1002,0x1106,
|
||||
0x1206,0x1302,0x1406,0x1502,0x1602,0x1706,0x180E,0x190A,0x1406,0x1502,
|
||||
0x1602,0x1706,0x180E,0x190A,0x2022,0x2126,0x2226,0x2322,0x2426,0x2522,
|
||||
0x2622,0x2726,0x282E,0x292A,0x2426,0x2522,0x2622,0x2726,0x282E,0x292A,
|
||||
0x3026,0x3122,0x3222,0x3326,0x3422,0x3526,0x3626,0x3722,0x382A,0x392E,
|
||||
0x3422,0x3526,0x3626,0x3722,0x382A,0x392E,0x4002,0x4106,0x4206,0x4302,
|
||||
0x4406,0x4502,0x4602,0x4706,0x480E,0x490A,0x4406,0x4502,0x4602,0x4706,
|
||||
0x480E,0x490A,0x5006,0x5102,0x5202,0x5306,0x5402,0x5506,0x5606,0x5702,
|
||||
0x580A,0x590E,0x5402,0x5506,0x5606,0x5702,0x580A,0x590E,0x6026,0x6122,
|
||||
0x6222,0x6326,0x6422,0x6526,0x6626,0x6722,0x682A,0x692E,0x6422,0x6526,
|
||||
0x6626,0x6722,0x682A,0x692E,0x7022,0x7126,0x7226,0x7322,0x7426,0x7522,
|
||||
0x7622,0x7726,0x782E,0x792A,0x7426,0x7522,0x7622,0x7726,0x782E,0x792A,
|
||||
0x8082,0x8186,0x8286,0x8382,0x8486,0x8582,0x8682,0x8786,0x888E,0x898A,
|
||||
0x8486,0x8582,0x8682,0x8786,0x888E,0x898A,0x9086,0x9182,0x9282,0x9386,
|
||||
0x9482,0x9586,0x9686,0x9782,0x988A,0x998E,0x3423,0x3527,0x3627,0x3723,
|
||||
0x382B,0x392F,0x4003,0x4107,0x4207,0x4303,0x4407,0x4503,0x4603,0x4707,
|
||||
0x480F,0x490B,0x4407,0x4503,0x4603,0x4707,0x480F,0x490B,0x5007,0x5103,
|
||||
0x5203,0x5307,0x5403,0x5507,0x5607,0x5703,0x580B,0x590F,0x5403,0x5507,
|
||||
0x5607,0x5703,0x580B,0x590F,0x6027,0x6123,0x6223,0x6327,0x6423,0x6527,
|
||||
0x6627,0x6723,0x682B,0x692F,0x6423,0x6527,0x6627,0x6723,0x682B,0x692F,
|
||||
0x7023,0x7127,0x7227,0x7323,0x7427,0x7523,0x7623,0x7727,0x782F,0x792B,
|
||||
0x7427,0x7523,0x7623,0x7727,0x782F,0x792B,0x8083,0x8187,0x8287,0x8383,
|
||||
0x8487,0x8583,0x8683,0x8787,0x888F,0x898B,0x8487,0x8583,0x8683,0x8787,
|
||||
0x888F,0x898B,0x9087,0x9183,0x9283,0x9387,0x9483,0x9587,0x9687,0x9783,
|
||||
0x988B,0x998F,0x9483,0x9587,0x9687,0x9783,0x988B,0x998F,0xA0A7,0xA1A3,
|
||||
0xA2A3,0xA3A7,0xA4A3,0xA5A7,0xA6A7,0xA7A3,0xA8AB,0xA9AF,0xA4A3,0xA5A7,
|
||||
0xA6A7,0xA7A3,0xA8AB,0xA9AF,0xB0A3,0xB1A7,0xB2A7,0xB3A3,0xB4A7,0xB5A3,
|
||||
0xB6A3,0xB7A7,0xB8AF,0xB9AB,0xB4A7,0xB5A3,0xB6A3,0xB7A7,0xB8AF,0xB9AB,
|
||||
0xC087,0xC183,0xC283,0xC387,0xC483,0xC587,0xC687,0xC783,0xC88B,0xC98F,
|
||||
0xC483,0xC587,0xC687,0xC783,0xC88B,0xC98F,0xD083,0xD187,0xD287,0xD383,
|
||||
0xD487,0xD583,0xD683,0xD787,0xD88F,0xD98B,0xD487,0xD583,0xD683,0xD787,
|
||||
0xD88F,0xD98B,0xE0A3,0xE1A7,0xE2A7,0xE3A3,0xE4A7,0xE5A3,0xE6A3,0xE7A7,
|
||||
0xE8AF,0xE9AB,0xE4A7,0xE5A3,0xE6A3,0xE7A7,0xE8AF,0xE9AB,0xF0A7,0xF1A3,
|
||||
0xF2A3,0xF3A7,0xF4A3,0xF5A7,0xF6A7,0xF7A3,0xF8AB,0xF9AF,0xF4A3,0xF5A7,
|
||||
0xF6A7,0xF7A3,0xF8AB,0xF9AF,0x0047,0x0103,0x0203,0x0307,0x0403,0x0507,
|
||||
0x0607,0x0703,0x080B,0x090F,0x0403,0x0507,0x0607,0x0703,0x080B,0x090F,
|
||||
0x1003,0x1107,0x1207,0x1303,0x1407,0x1503,0x1603,0x1707,0x180F,0x190B,
|
||||
0x1407,0x1503,0x1603,0x1707,0x180F,0x190B,0x2023,0x2127,0x2227,0x2323,
|
||||
0x2427,0x2523,0x2623,0x2727,0x282F,0x292B,0x2427,0x2523,0x2623,0x2727,
|
||||
0x282F,0x292B,0x3027,0x3123,0x3223,0x3327,0x3423,0x3527,0x3627,0x3723,
|
||||
0x382B,0x392F,0x3423,0x3527,0x3627,0x3723,0x382B,0x392F,0x4003,0x4107,
|
||||
0x4207,0x4303,0x4407,0x4503,0x4603,0x4707,0x480F,0x490B,0x4407,0x4503,
|
||||
0x4603,0x4707,0x480F,0x490B,0x5007,0x5103,0x5203,0x5307,0x5403,0x5507,
|
||||
0x5607,0x5703,0x580B,0x590F,0x5403,0x5507,0x5607,0x5703,0x580B,0x590F,
|
||||
0x6027,0x6123,0x6223,0x6327,0x6423,0x6527,0x6627,0x6723,0x682B,0x692F,
|
||||
0x6423,0x6527,0x6627,0x6723,0x682B,0x692F,0x7023,0x7127,0x7227,0x7323,
|
||||
0x7427,0x7523,0x7623,0x7727,0x782F,0x792B,0x7427,0x7523,0x7623,0x7727,
|
||||
0x782F,0x792B,0x8083,0x8187,0x8287,0x8383,0x8487,0x8583,0x8683,0x8787,
|
||||
0x888F,0x898B,0x8487,0x8583,0x8683,0x8787,0x888F,0x898B,0x9087,0x9183,
|
||||
0x9283,0x9387,0x9483,0x9587,0x9687,0x9783,0x988B,0x998F,0x9483,0x9587,
|
||||
0x9687,0x9783,0x988B,0x998F,0x0604,0x0700,0x0808,0x090C,0x0A0C,0x0B08,
|
||||
0x0C0C,0x0D08,0x0E08,0x0F0C,0x1010,0x1114,0x1214,0x1310,0x1414,0x1510,
|
||||
0x1600,0x1704,0x180C,0x1908,0x1A08,0x1B0C,0x1C08,0x1D0C,0x1E0C,0x1F08,
|
||||
0x2030,0x2134,0x2234,0x2330,0x2434,0x2530,0x2620,0x2724,0x282C,0x2928,
|
||||
0x2A28,0x2B2C,0x2C28,0x2D2C,0x2E2C,0x2F28,0x3034,0x3130,0x3230,0x3334,
|
||||
0x3430,0x3534,0x3624,0x3720,0x3828,0x392C,0x3A2C,0x3B28,0x3C2C,0x3D28,
|
||||
0x3E28,0x3F2C,0x4010,0x4114,0x4214,0x4310,0x4414,0x4510,0x4600,0x4704,
|
||||
0x480C,0x4908,0x4A08,0x4B0C,0x4C08,0x4D0C,0x4E0C,0x4F08,0x5014,0x5110,
|
||||
0x5210,0x5314,0x5410,0x5514,0x5604,0x5700,0x5808,0x590C,0x5A0C,0x5B08,
|
||||
0x5C0C,0x5D08,0x5E08,0x5F0C,0x6034,0x6130,0x6230,0x6334,0x6430,0x6534,
|
||||
0x6624,0x6720,0x6828,0x692C,0x6A2C,0x6B28,0x6C2C,0x6D28,0x6E28,0x6F2C,
|
||||
0x7030,0x7134,0x7234,0x7330,0x7434,0x7530,0x7620,0x7724,0x782C,0x7928,
|
||||
0x7A28,0x7B2C,0x7C28,0x7D2C,0x7E2C,0x7F28,0x8090,0x8194,0x8294,0x8390,
|
||||
0x8494,0x8590,0x8680,0x8784,0x888C,0x8988,0x8A88,0x8B8C,0x8C88,0x8D8C,
|
||||
0x8E8C,0x8F88,0x9094,0x9190,0x9290,0x9394,0x9490,0x9594,0x9684,0x9780,
|
||||
0x9888,0x998C,0x9A8C,0x9B88,0x9C8C,0x9D88,0x9E88,0x9F8C,0x0055,0x0111,
|
||||
0x0211,0x0315,0x0411,0x0515,0x0605,0x0701,0x0809,0x090D,0x0A0D,0x0B09,
|
||||
0x0C0D,0x0D09,0x0E09,0x0F0D,0x1011,0x1115,0x1215,0x1311,0x1415,0x1511,
|
||||
0x1601,0x1705,0x180D,0x1909,0x1A09,0x1B0D,0x1C09,0x1D0D,0x1E0D,0x1F09,
|
||||
0x2031,0x2135,0x2235,0x2331,0x2435,0x2531,0x2621,0x2725,0x282D,0x2929,
|
||||
0x2A29,0x2B2D,0x2C29,0x2D2D,0x2E2D,0x2F29,0x3035,0x3131,0x3231,0x3335,
|
||||
0x3431,0x3535,0x3625,0x3721,0x3829,0x392D,0x3A2D,0x3B29,0x3C2D,0x3D29,
|
||||
0x3E29,0x3F2D,0x4011,0x4115,0x4215,0x4311,0x4415,0x4511,0x4601,0x4705,
|
||||
0x480D,0x4909,0x4A09,0x4B0D,0x4C09,0x4D0D,0x4E0D,0x4F09,0x5015,0x5111,
|
||||
0x5211,0x5315,0x5411,0x5515,0x5605,0x5701,0x5809,0x590D,0x5A0D,0x5B09,
|
||||
0x5C0D,0x5D09,0x5E09,0x5F0D,0x6035,0x6131,0x6231,0x6335,0x6431,0x6535,
|
||||
0x6625,0x6721,0x6829,0x692D,0x6A2D,0x6B29,0x6C2D,0x6D29,0x6E29,0x6F2D,
|
||||
0x7031,0x7135,0x7235,0x7331,0x7435,0x7531,0x7621,0x7725,0x782D,0x7929,
|
||||
0x7A29,0x7B2D,0x7C29,0x7D2D,0x7E2D,0x7F29,0x8091,0x8195,0x8295,0x8391,
|
||||
0x8495,0x8591,0x8681,0x8785,0x888D,0x8989,0x8A89,0x8B8D,0x8C89,0x8D8D,
|
||||
0x8E8D,0x8F89,0x9095,0x9191,0x9291,0x9395,0x9491,0x9595,0x9685,0x9781,
|
||||
0x9889,0x998D,0x9A8D,0x9B89,0x9C8D,0x9D89,0x9E89,0x9F8D,0xA0B5,0xA1B1,
|
||||
0xA2B1,0xA3B5,0xA4B1,0xA5B5,0xA6A5,0xA7A1,0xA8A9,0xA9AD,0xAAAD,0xABA9,
|
||||
0xACAD,0xADA9,0xAEA9,0xAFAD,0xB0B1,0xB1B5,0xB2B5,0xB3B1,0xB4B5,0xB5B1,
|
||||
0xB6A1,0xB7A5,0xB8AD,0xB9A9,0xBAA9,0xBBAD,0xBCA9,0xBDAD,0xBEAD,0xBFA9,
|
||||
0xC095,0xC191,0xC291,0xC395,0xC491,0xC595,0xC685,0xC781,0xC889,0xC98D,
|
||||
0xCA8D,0xCB89,0xCC8D,0xCD89,0xCE89,0xCF8D,0xD091,0xD195,0xD295,0xD391,
|
||||
0xD495,0xD591,0xD681,0xD785,0xD88D,0xD989,0xDA89,0xDB8D,0xDC89,0xDD8D,
|
||||
0xDE8D,0xDF89,0xE0B1,0xE1B5,0xE2B5,0xE3B1,0xE4B5,0xE5B1,0xE6A1,0xE7A5,
|
||||
0xE8AD,0xE9A9,0xEAA9,0xEBAD,0xECA9,0xEDAD,0xEEAD,0xEFA9,0xF0B5,0xF1B1,
|
||||
0xF2B1,0xF3B5,0xF4B1,0xF5B5,0xF6A5,0xF7A1,0xF8A9,0xF9AD,0xFAAD,0xFBA9,
|
||||
0xFCAD,0xFDA9,0xFEA9,0xFFAD,0x0055,0x0111,0x0211,0x0315,0x0411,0x0515,
|
||||
0x0605,0x0701,0x0809,0x090D,0x0A0D,0x0B09,0x0C0D,0x0D09,0x0E09,0x0F0D,
|
||||
0x1011,0x1115,0x1215,0x1311,0x1415,0x1511,0x1601,0x1705,0x180D,0x1909,
|
||||
0x1A09,0x1B0D,0x1C09,0x1D0D,0x1E0D,0x1F09,0x2031,0x2135,0x2235,0x2331,
|
||||
0x2435,0x2531,0x2621,0x2725,0x282D,0x2929,0x2A29,0x2B2D,0x2C29,0x2D2D,
|
||||
0x2E2D,0x2F29,0x3035,0x3131,0x3231,0x3335,0x3431,0x3535,0x3625,0x3721,
|
||||
0x3829,0x392D,0x3A2D,0x3B29,0x3C2D,0x3D29,0x3E29,0x3F2D,0x4011,0x4115,
|
||||
0x4215,0x4311,0x4415,0x4511,0x4601,0x4705,0x480D,0x4909,0x4A09,0x4B0D,
|
||||
0x4C09,0x4D0D,0x4E0D,0x4F09,0x5015,0x5111,0x5211,0x5315,0x5411,0x5515,
|
||||
0x5605,0x5701,0x5809,0x590D,0x5A0D,0x5B09,0x5C0D,0x5D09,0x5E09,0x5F0D,
|
||||
0x6035,0x6131,0x6231,0x6335,0x6431,0x6535,0xFABE,0xFBBA,0xFCBE,0xFDBA,
|
||||
0xFEBA,0xFFBE,0x0046,0x0102,0x0202,0x0306,0x0402,0x0506,0x0606,0x0702,
|
||||
0x080A,0x090E,0x0A1E,0x0B1A,0x0C1E,0x0D1A,0x0E1A,0x0F1E,0x1002,0x1106,
|
||||
0x1206,0x1302,0x1406,0x1502,0x1602,0x1706,0x180E,0x190A,0x1A1A,0x1B1E,
|
||||
0x1C1A,0x1D1E,0x1E1E,0x1F1A,0x2022,0x2126,0x2226,0x2322,0x2426,0x2522,
|
||||
0x2622,0x2726,0x282E,0x292A,0x2A3A,0x2B3E,0x2C3A,0x2D3E,0x2E3E,0x2F3A,
|
||||
0x3026,0x3122,0x3222,0x3326,0x3422,0x3526,0x3626,0x3722,0x382A,0x392E,
|
||||
0x3A3E,0x3B3A,0x3C3E,0x3D3A,0x3E3A,0x3F3E,0x4002,0x4106,0x4206,0x4302,
|
||||
0x4406,0x4502,0x4602,0x4706,0x480E,0x490A,0x4A1A,0x4B1E,0x4C1A,0x4D1E,
|
||||
0x4E1E,0x4F1A,0x5006,0x5102,0x5202,0x5306,0x5402,0x5506,0x5606,0x5702,
|
||||
0x580A,0x590E,0x5A1E,0x5B1A,0x5C1E,0x5D1A,0x5E1A,0x5F1E,0x6026,0x6122,
|
||||
0x6222,0x6326,0x6422,0x6526,0x6626,0x6722,0x682A,0x692E,0x6A3E,0x6B3A,
|
||||
0x6C3E,0x6D3A,0x6E3A,0x6F3E,0x7022,0x7126,0x7226,0x7322,0x7426,0x7522,
|
||||
0x7622,0x7726,0x782E,0x792A,0x7A3A,0x7B3E,0x7C3A,0x7D3E,0x7E3E,0x7F3A,
|
||||
0x8082,0x8186,0x8286,0x8382,0x8486,0x8582,0x8682,0x8786,0x888E,0x898A,
|
||||
0x8A9A,0x8B9E,0x8C9A,0x8D9E,0x8E9E,0x8F9A,0x9086,0x9182,0x9282,0x9386,
|
||||
0x3423,0x3527,0x3627,0x3723,0x382B,0x392F,0x3A3F,0x3B3B,0x3C3F,0x3D3B,
|
||||
0x3E3B,0x3F3F,0x4003,0x4107,0x4207,0x4303,0x4407,0x4503,0x4603,0x4707,
|
||||
0x480F,0x490B,0x4A1B,0x4B1F,0x4C1B,0x4D1F,0x4E1F,0x4F1B,0x5007,0x5103,
|
||||
0x5203,0x5307,0x5403,0x5507,0x5607,0x5703,0x580B,0x590F,0x5A1F,0x5B1B,
|
||||
0x5C1F,0x5D1B,0x5E1B,0x5F1F,0x6027,0x6123,0x6223,0x6327,0x6423,0x6527,
|
||||
0x6627,0x6723,0x682B,0x692F,0x6A3F,0x6B3B,0x6C3F,0x6D3B,0x6E3B,0x6F3F,
|
||||
0x7023,0x7127,0x7227,0x7323,0x7427,0x7523,0x7623,0x7727,0x782F,0x792B,
|
||||
0x7A3B,0x7B3F,0x7C3B,0x7D3F,0x7E3F,0x7F3B,0x8083,0x8187,0x8287,0x8383,
|
||||
0x8487,0x8583,0x8683,0x8787,0x888F,0x898B,0x8A9B,0x8B9F,0x8C9B,0x8D9F,
|
||||
0x8E9F,0x8F9B,0x9087,0x9183,0x9283,0x9387,0x9483,0x9587,0x9687,0x9783,
|
||||
0x988B,0x998F,0x9A9F,0x9B9B,0x9C9F,0x9D9B,0x9E9B,0x9F9F,0xA0A7,0xA1A3,
|
||||
0xA2A3,0xA3A7,0xA4A3,0xA5A7,0xA6A7,0xA7A3,0xA8AB,0xA9AF,0xAABF,0xABBB,
|
||||
0xACBF,0xADBB,0xAEBB,0xAFBF,0xB0A3,0xB1A7,0xB2A7,0xB3A3,0xB4A7,0xB5A3,
|
||||
0xB6A3,0xB7A7,0xB8AF,0xB9AB,0xBABB,0xBBBF,0xBCBB,0xBDBF,0xBEBF,0xBFBB,
|
||||
0xC087,0xC183,0xC283,0xC387,0xC483,0xC587,0xC687,0xC783,0xC88B,0xC98F,
|
||||
0xCA9F,0xCB9B,0xCC9F,0xCD9B,0xCE9B,0xCF9F,0xD083,0xD187,0xD287,0xD383,
|
||||
0xD487,0xD583,0xD683,0xD787,0xD88F,0xD98B,0xDA9B,0xDB9F,0xDC9B,0xDD9F,
|
||||
0xDE9F,0xDF9B,0xE0A3,0xE1A7,0xE2A7,0xE3A3,0xE4A7,0xE5A3,0xE6A3,0xE7A7,
|
||||
0xE8AF,0xE9AB,0xEABB,0xEBBF,0xECBB,0xEDBF,0xEEBF,0xEFBB,0xF0A7,0xF1A3,
|
||||
0xF2A3,0xF3A7,0xF4A3,0xF5A7,0xF6A7,0xF7A3,0xF8AB,0xF9AF,0xFABF,0xFBBB,
|
||||
0xFCBF,0xFDBB,0xFEBB,0xFFBF,0x0047,0x0103,0x0203,0x0307,0x0403,0x0507,
|
||||
0x0607,0x0703,0x080B,0x090F,0x0A1F,0x0B1B,0x0C1F,0x0D1B,0x0E1B,0x0F1F,
|
||||
0x1003,0x1107,0x1207,0x1303,0x1407,0x1503,0x1603,0x1707,0x180F,0x190B,
|
||||
0x1A1B,0x1B1F,0x1C1B,0x1D1F,0x1E1F,0x1F1B,0x2023,0x2127,0x2227,0x2323,
|
||||
0x2427,0x2523,0x2623,0x2727,0x282F,0x292B,0x2A3B,0x2B3F,0x2C3B,0x2D3F,
|
||||
0x2E3F,0x2F3B,0x3027,0x3123,0x3223,0x3327,0x3423,0x3527,0x3627,0x3723,
|
||||
0x382B,0x392F,0x3A3F,0x3B3B,0x3C3F,0x3D3B,0x3E3B,0x3F3F,0x4003,0x4107,
|
||||
0x4207,0x4303,0x4407,0x4503,0x4603,0x4707,0x480F,0x490B,0x4A1B,0x4B1F,
|
||||
0x4C1B,0x4D1F,0x4E1F,0x4F1B,0x5007,0x5103,0x5203,0x5307,0x5403,0x5507,
|
||||
0x5607,0x5703,0x580B,0x590F,0x5A1F,0x5B1B,0x5C1F,0x5D1B,0x5E1B,0x5F1F,
|
||||
0x6027,0x6123,0x6223,0x6327,0x6423,0x6527,0x6627,0x6723,0x682B,0x692F,
|
||||
0x6A3F,0x6B3B,0x6C3F,0x6D3B,0x6E3B,0x6F3F,0x7023,0x7127,0x7227,0x7323,
|
||||
0x7427,0x7523,0x7623,0x7727,0x782F,0x792B,0x7A3B,0x7B3F,0x7C3B,0x7D3F,
|
||||
0x7E3F,0x7F3B,0x8083,0x8187,0x8287,0x8383,0x8487,0x8583,0x8683,0x8787,
|
||||
0x888F,0x898B,0x8A9B,0x8B9F,0x8C9B,0x8D9F,0x8E9F,0x8F9B,0x9087,0x9183,
|
||||
0x9283,0x9387,0x9483,0x9587,0x9687,0x9783,0x988B,0x998F,
|
||||
];
|
||||
|
||||
public static readonly byte[] Rl0 =
|
||||
[
|
||||
0x44,0x00,0x00,0x04,0x08,0x0c,0x0c,0x08
|
||||
,0x00,0x04,0x04,0x00,0x0c,0x08,0x08,0x0c
|
||||
,0x20,0x24,0x24,0x20,0x2c,0x28,0x28,0x2c
|
||||
,0x24,0x20,0x20,0x24,0x28,0x2c,0x2c,0x28
|
||||
,0x00,0x04,0x04,0x00,0x0c,0x08,0x08,0x0c
|
||||
,0x04,0x00,0x00,0x04,0x08,0x0c,0x0c,0x08
|
||||
,0x24,0x20,0x20,0x24,0x28,0x2c,0x2c,0x28
|
||||
,0x20,0x24,0x24,0x20,0x2c,0x28,0x28,0x2c
|
||||
,0x80,0x84,0x84,0x80,0x8c,0x88,0x88,0x8c
|
||||
,0x84,0x80,0x80,0x84,0x88,0x8c,0x8c,0x88
|
||||
,0xa4,0xa0,0xa0,0xa4,0xa8,0xac,0xac,0xa8
|
||||
,0xa0,0xa4,0xa4,0xa0,0xac,0xa8,0xa8,0xac
|
||||
,0x84,0x80,0x80,0x84,0x88,0x8c,0x8c,0x88
|
||||
,0x80,0x84,0x84,0x80,0x8c,0x88,0x88,0x8c
|
||||
,0xa0,0xa4,0xa4,0xa0,0xac,0xa8,0xa8,0xac
|
||||
,0xa4,0xa0,0xa0,0xa4,0xa8,0xac,0xac,0xa8
|
||||
,0x45,0x01,0x01,0x05,0x09,0x0d,0x0d,0x09
|
||||
,0x01,0x05,0x05,0x01,0x0d,0x09,0x09,0x0d
|
||||
,0x21,0x25,0x25,0x21,0x2d,0x29,0x29,0x2d
|
||||
,0x25,0x21,0x21,0x25,0x29,0x2d,0x2d,0x29
|
||||
,0x01,0x05,0x05,0x01,0x0d,0x09,0x09,0x0d
|
||||
,0x05,0x01,0x01,0x05,0x09,0x0d,0x0d,0x09
|
||||
,0x25,0x21,0x21,0x25,0x29,0x2d,0x2d,0x29
|
||||
,0x21,0x25,0x25,0x21,0x2d,0x29,0x29,0x2d
|
||||
,0x81,0x85,0x85,0x81,0x8d,0x89,0x89,0x8d
|
||||
,0x85,0x81,0x81,0x85,0x89,0x8d,0x8d,0x89
|
||||
,0xa5,0xa1,0xa1,0xa5,0xa9,0xad,0xad,0xa9
|
||||
,0xa1,0xa5,0xa5,0xa1,0xad,0xa9,0xa9,0xad
|
||||
,0x85,0x81,0x81,0x85,0x89,0x8d,0x8d,0x89
|
||||
,0x81,0x85,0x85,0x81,0x8d,0x89,0x89,0x8d
|
||||
,0xa1,0xa5,0xa5,0xa1,0xad,0xa9,0xa9,0xad
|
||||
,0xa5,0xa1,0xa1,0xa5,0xa9,0xad,0xad,0xa9
|
||||
];
|
||||
|
||||
public static readonly byte[] Rl1 =
|
||||
[
|
||||
0x00,0x04,0x04,0x00,0x0c,0x08,0x08,0x0c
|
||||
,0x04,0x00,0x00,0x04,0x08,0x0c,0x0c,0x08
|
||||
,0x24,0x20,0x20,0x24,0x28,0x2c,0x2c,0x28
|
||||
,0x20,0x24,0x24,0x20,0x2c,0x28,0x28,0x2c
|
||||
,0x04,0x00,0x00,0x04,0x08,0x0c,0x0c,0x08
|
||||
,0x00,0x04,0x04,0x00,0x0c,0x08,0x08,0x0c
|
||||
,0x20,0x24,0x24,0x20,0x2c,0x28,0x28,0x2c
|
||||
,0x24,0x20,0x20,0x24,0x28,0x2c,0x2c,0x28
|
||||
,0x84,0x80,0x80,0x84,0x88,0x8c,0x8c,0x88
|
||||
,0x80,0x84,0x84,0x80,0x8c,0x88,0x88,0x8c
|
||||
,0xa0,0xa4,0xa4,0xa0,0xac,0xa8,0xa8,0xac
|
||||
,0xa4,0xa0,0xa0,0xa4,0xa8,0xac,0xac,0xa8
|
||||
,0x80,0x84,0x84,0x80,0x8c,0x88,0x88,0x8c
|
||||
,0x84,0x80,0x80,0x84,0x88,0x8c,0x8c,0x88
|
||||
,0xa4,0xa0,0xa0,0xa4,0xa8,0xac,0xac,0xa8
|
||||
,0xa0,0xa4,0xa4,0xa0,0xac,0xa8,0xa8,0xac
|
||||
,0x01,0x05,0x05,0x01,0x0d,0x09,0x09,0x0d
|
||||
,0x05,0x01,0x01,0x05,0x09,0x0d,0x0d,0x09
|
||||
,0x25,0x21,0x21,0x25,0x29,0x2d,0x2d,0x29
|
||||
,0x21,0x25,0x25,0x21,0x2d,0x29,0x29,0x2d
|
||||
,0x05,0x01,0x01,0x05,0x09,0x0d,0x0d,0x09
|
||||
,0x01,0x05,0x05,0x01,0x0d,0x09,0x09,0x0d
|
||||
,0x21,0x25,0x25,0x21,0x2d,0x29,0x29,0x2d
|
||||
,0x25,0x21,0x21,0x25,0x29,0x2d,0x2d,0x29
|
||||
,0x85,0x81,0x81,0x85,0x89,0x8d,0x8d,0x89
|
||||
,0x81,0x85,0x85,0x81,0x8d,0x89,0x89,0x8d
|
||||
,0xa1,0xa5,0xa5,0xa1,0xad,0xa9,0xa9,0xad
|
||||
,0xa5,0xa1,0xa1,0xa5,0xa9,0xad,0xad,0xa9
|
||||
,0x81,0x85,0x85,0x81,0x8d,0x89,0x89,0x8d
|
||||
,0x85,0x81,0x81,0x85,0x89,0x8d,0x8d,0x89
|
||||
,0xa5,0xa1,0xa1,0xa5,0xa9,0xad,0xad,0xa9
|
||||
,0xa1,0xa5,0xa5,0xa1,0xad,0xa9,0xa9,0xad
|
||||
];
|
||||
|
||||
public static readonly byte[] Rr0 =
|
||||
[
|
||||
0x44,0x45,0x00,0x01,0x00,0x01,0x04,0x05
|
||||
,0x00,0x01,0x04,0x05,0x04,0x05,0x00,0x01
|
||||
,0x08,0x09,0x0c,0x0d,0x0c,0x0d,0x08,0x09
|
||||
,0x0c,0x0d,0x08,0x09,0x08,0x09,0x0c,0x0d
|
||||
,0x00,0x01,0x04,0x05,0x04,0x05,0x00,0x01
|
||||
,0x04,0x05,0x00,0x01,0x00,0x01,0x04,0x05
|
||||
,0x0c,0x0d,0x08,0x09,0x08,0x09,0x0c,0x0d
|
||||
,0x08,0x09,0x0c,0x0d,0x0c,0x0d,0x08,0x09
|
||||
,0x20,0x21,0x24,0x25,0x24,0x25,0x20,0x21
|
||||
,0x24,0x25,0x20,0x21,0x20,0x21,0x24,0x25
|
||||
,0x2c,0x2d,0x28,0x29,0x28,0x29,0x2c,0x2d
|
||||
,0x28,0x29,0x2c,0x2d,0x2c,0x2d,0x28,0x29
|
||||
,0x24,0x25,0x20,0x21,0x20,0x21,0x24,0x25
|
||||
,0x20,0x21,0x24,0x25,0x24,0x25,0x20,0x21
|
||||
,0x28,0x29,0x2c,0x2d,0x2c,0x2d,0x28,0x29
|
||||
,0x2c,0x2d,0x28,0x29,0x28,0x29,0x2c,0x2d
|
||||
,0x00,0x01,0x04,0x05,0x04,0x05,0x00,0x01
|
||||
,0x04,0x05,0x00,0x01,0x00,0x01,0x04,0x05
|
||||
,0x0c,0x0d,0x08,0x09,0x08,0x09,0x0c,0x0d
|
||||
,0x08,0x09,0x0c,0x0d,0x0c,0x0d,0x08,0x09
|
||||
,0x04,0x05,0x00,0x01,0x00,0x01,0x04,0x05
|
||||
,0x00,0x01,0x04,0x05,0x04,0x05,0x00,0x01
|
||||
,0x08,0x09,0x0c,0x0d,0x0c,0x0d,0x08,0x09
|
||||
,0x0c,0x0d,0x08,0x09,0x08,0x09,0x0c,0x0d
|
||||
,0x24,0x25,0x20,0x21,0x20,0x21,0x24,0x25
|
||||
,0x20,0x21,0x24,0x25,0x24,0x25,0x20,0x21
|
||||
,0x28,0x29,0x2c,0x2d,0x2c,0x2d,0x28,0x29
|
||||
,0x2c,0x2d,0x28,0x29,0x28,0x29,0x2c,0x2d
|
||||
,0x20,0x21,0x24,0x25,0x24,0x25,0x20,0x21
|
||||
,0x24,0x25,0x20,0x21,0x20,0x21,0x24,0x25
|
||||
,0x2c,0x2d,0x28,0x29,0x28,0x29,0x2c,0x2d
|
||||
,0x28,0x29,0x2c,0x2d,0x2c,0x2d,0x28,0x29
|
||||
];
|
||||
|
||||
public static readonly byte[] Rr1 =
|
||||
[
|
||||
0x80,0x81,0x84,0x85,0x84,0x85,0x80,0x81
|
||||
,0x84,0x85,0x80,0x81,0x80,0x81,0x84,0x85
|
||||
,0x8c,0x8d,0x88,0x89,0x88,0x89,0x8c,0x8d
|
||||
,0x88,0x89,0x8c,0x8d,0x8c,0x8d,0x88,0x89
|
||||
,0x84,0x85,0x80,0x81,0x80,0x81,0x84,0x85
|
||||
,0x80,0x81,0x84,0x85,0x84,0x85,0x80,0x81
|
||||
,0x88,0x89,0x8c,0x8d,0x8c,0x8d,0x88,0x89
|
||||
,0x8c,0x8d,0x88,0x89,0x88,0x89,0x8c,0x8d
|
||||
,0xa4,0xa5,0xa0,0xa1,0xa0,0xa1,0xa4,0xa5
|
||||
,0xa0,0xa1,0xa4,0xa5,0xa4,0xa5,0xa0,0xa1
|
||||
,0xa8,0xa9,0xac,0xad,0xac,0xad,0xa8,0xa9
|
||||
,0xac,0xad,0xa8,0xa9,0xa8,0xa9,0xac,0xad
|
||||
,0xa0,0xa1,0xa4,0xa5,0xa4,0xa5,0xa0,0xa1
|
||||
,0xa4,0xa5,0xa0,0xa1,0xa0,0xa1,0xa4,0xa5
|
||||
,0xac,0xad,0xa8,0xa9,0xa8,0xa9,0xac,0xad
|
||||
,0xa8,0xa9,0xac,0xad,0xac,0xad,0xa8,0xa9
|
||||
,0x84,0x85,0x80,0x81,0x80,0x81,0x84,0x85
|
||||
,0x80,0x81,0x84,0x85,0x84,0x85,0x80,0x81
|
||||
,0x88,0x89,0x8c,0x8d,0x8c,0x8d,0x88,0x89
|
||||
,0x8c,0x8d,0x88,0x89,0x88,0x89,0x8c,0x8d
|
||||
,0x80,0x81,0x84,0x85,0x84,0x85,0x80,0x81
|
||||
,0x84,0x85,0x80,0x81,0x80,0x81,0x84,0x85
|
||||
,0x8c,0x8d,0x88,0x89,0x88,0x89,0x8c,0x8d
|
||||
,0x88,0x89,0x8c,0x8d,0x8c,0x8d,0x88,0x89
|
||||
,0xa0,0xa1,0xa4,0xa5,0xa4,0xa5,0xa0,0xa1
|
||||
,0xa4,0xa5,0xa0,0xa1,0xa0,0xa1,0xa4,0xa5
|
||||
,0xac,0xad,0xa8,0xa9,0xa8,0xa9,0xac,0xad
|
||||
,0xa8,0xa9,0xac,0xad,0xac,0xad,0xa8,0xa9
|
||||
,0xa4,0xa5,0xa0,0xa1,0xa0,0xa1,0xa4,0xa5
|
||||
,0xa0,0xa1,0xa4,0xa5,0xa4,0xa5,0xa0,0xa1
|
||||
,0xa8,0xa9,0xac,0xad,0xac,0xad,0xa8,0xa9
|
||||
,0xac,0xad,0xa8,0xa9,0xa8,0xa9,0xac,0xad
|
||||
];
|
||||
|
||||
public static readonly byte[] Sraf =
|
||||
[
|
||||
0x44,0x45,0x00,0x01,0x00,0x01,0x04,0x05
|
||||
,0x00,0x01,0x04,0x05,0x04,0x05,0x00,0x01
|
||||
,0x08,0x09,0x0c,0x0d,0x0c,0x0d,0x08,0x09
|
||||
,0x0c,0x0d,0x08,0x09,0x08,0x09,0x0c,0x0d
|
||||
,0x00,0x01,0x04,0x05,0x04,0x05,0x00,0x01
|
||||
,0x04,0x05,0x00,0x01,0x00,0x01,0x04,0x05
|
||||
,0x0c,0x0d,0x08,0x09,0x08,0x09,0x0c,0x0d
|
||||
,0x08,0x09,0x0c,0x0d,0x0c,0x0d,0x08,0x09
|
||||
,0x20,0x21,0x24,0x25,0x24,0x25,0x20,0x21
|
||||
,0x24,0x25,0x20,0x21,0x20,0x21,0x24,0x25
|
||||
,0x2c,0x2d,0x28,0x29,0x28,0x29,0x2c,0x2d
|
||||
,0x28,0x29,0x2c,0x2d,0x2c,0x2d,0x28,0x29
|
||||
,0x24,0x25,0x20,0x21,0x20,0x21,0x24,0x25
|
||||
,0x20,0x21,0x24,0x25,0x24,0x25,0x20,0x21
|
||||
,0x28,0x29,0x2c,0x2d,0x2c,0x2d,0x28,0x29
|
||||
,0x2c,0x2d,0x28,0x29,0x28,0x29,0x2c,0x2d
|
||||
,0x84,0x85,0x80,0x81,0x80,0x81,0x84,0x85
|
||||
,0x80,0x81,0x84,0x85,0x84,0x85,0x80,0x81
|
||||
,0x88,0x89,0x8c,0x8d,0x8c,0x8d,0x88,0x89
|
||||
,0x8c,0x8d,0x88,0x89,0x88,0x89,0x8c,0x8d
|
||||
,0x80,0x81,0x84,0x85,0x84,0x85,0x80,0x81
|
||||
,0x84,0x85,0x80,0x81,0x80,0x81,0x84,0x85
|
||||
,0x8c,0x8d,0x88,0x89,0x88,0x89,0x8c,0x8d
|
||||
,0x88,0x89,0x8c,0x8d,0x8c,0x8d,0x88,0x89
|
||||
,0xa0,0xa1,0xa4,0xa5,0xa4,0xa5,0xa0,0xa1
|
||||
,0xa4,0xa5,0xa0,0xa1,0xa0,0xa1,0xa4,0xa5
|
||||
,0xac,0xad,0xa8,0xa9,0xa8,0xa9,0xac,0xad
|
||||
,0xa8,0xa9,0xac,0xad,0xac,0xad,0xa8,0xa9
|
||||
,0xa4,0xa5,0xa0,0xa1,0xa0,0xa1,0xa4,0xa5
|
||||
,0xa0,0xa1,0xa4,0xa5,0xa4,0xa5,0xa0,0xa1
|
||||
,0xa8,0xa9,0xac,0xad,0xac,0xad,0xa8,0xa9
|
||||
,0xac,0xad,0xa8,0xa9,0xa8,0xa9,0xac,0xad
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Description: Z80 CPU Emulator [ALU part]
|
||||
* Date: 13.04.2007
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu.Processor
|
||||
{
|
||||
public partial class Z80Cpu
|
||||
{
|
||||
#region ALU
|
||||
|
||||
private void ALU_ADDR(byte src)
|
||||
{
|
||||
regs.F = CpuTables.Adcf[regs.A + src * 0x100];
|
||||
regs.A += src;
|
||||
}
|
||||
|
||||
private void ALU_ADCR(byte src)
|
||||
{
|
||||
var carry = regs.F & CpuFlags.C;
|
||||
regs.F = CpuTables.Adcf[regs.A + src * 0x100 + 0x10000 * carry];
|
||||
regs.A += (byte)(src + carry);
|
||||
}
|
||||
|
||||
private void ALU_SUBR(byte src)
|
||||
{
|
||||
regs.F = CpuTables.Sbcf[regs.A * 0x100 + src];
|
||||
regs.A -= src;
|
||||
}
|
||||
|
||||
private void ALU_SBCR(byte src)
|
||||
{
|
||||
var carry = regs.F & CpuFlags.C;
|
||||
regs.F = CpuTables.Sbcf[regs.A * 0x100 + src + 0x10000 * carry];
|
||||
regs.A -= (byte)(src + carry);
|
||||
}
|
||||
|
||||
private void ALU_ANDR(byte src)
|
||||
{
|
||||
regs.A &= src;
|
||||
regs.F = (byte)(CpuTables.Logf[regs.A] | CpuFlags.H);
|
||||
}
|
||||
|
||||
private void ALU_XORR(byte src)
|
||||
{
|
||||
regs.A ^= src;
|
||||
regs.F = CpuTables.Logf[regs.A];
|
||||
}
|
||||
|
||||
private void ALU_ORR(byte src)
|
||||
{
|
||||
regs.A |= src;
|
||||
regs.F = CpuTables.Logf[regs.A];
|
||||
}
|
||||
|
||||
private void ALU_CPR(byte src)
|
||||
{
|
||||
regs.F = CpuTables.Cpf[regs.A * 0x100 + src];
|
||||
}
|
||||
|
||||
private byte ALU_INCR(byte x)
|
||||
{
|
||||
regs.F = (byte)(CpuTables.Incf[x] | (regs.F & CpuFlags.C));
|
||||
x++;
|
||||
return x;
|
||||
}
|
||||
|
||||
private byte ALU_DECR(byte x)
|
||||
{
|
||||
regs.F = (byte)(CpuTables.Decf[x] | (regs.F & CpuFlags.C));
|
||||
x--;
|
||||
return x;
|
||||
}
|
||||
|
||||
private ushort ALU_ADDHLRR(ushort rhl, ushort rde)
|
||||
{
|
||||
regs.F = (byte)(regs.F & CpuFlags.NotHNCF3F5);
|
||||
regs.F |= (byte)((((rhl & 0x0FFF) + (rde & 0x0FFF)) >> 8) & CpuFlags.H);
|
||||
uint res = (uint)((rhl & 0xFFFF) + (rde & 0xFFFF));
|
||||
|
||||
if ((res & 0x10000) != 0) regs.F |= CpuFlags.C;
|
||||
regs.F |= (byte)((res >> 8) & CpuFlags.F3F5);
|
||||
return (ushort)res;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ALU #CB
|
||||
|
||||
private byte ALU_RLC(int x)
|
||||
{
|
||||
regs.F = CpuTables.Rlcf[x];
|
||||
x <<= 1;
|
||||
if ((x & 0x100) != 0) x |= 0x01;
|
||||
return (byte)x;
|
||||
}
|
||||
|
||||
private byte ALU_RRC(int x)
|
||||
{
|
||||
regs.F = CpuTables.Rrcf[x];
|
||||
if ((x & 0x01) != 0) x = (x >> 1) | 0x80;
|
||||
else x >>= 1;
|
||||
return (byte)x;
|
||||
}
|
||||
|
||||
private byte ALU_RL(int x)
|
||||
{
|
||||
if ((regs.F & CpuFlags.C) != 0)
|
||||
{
|
||||
regs.F = CpuTables.Rl1[x];
|
||||
x <<= 1;
|
||||
x++;
|
||||
}
|
||||
else
|
||||
{
|
||||
regs.F = CpuTables.Rl0[x];
|
||||
x <<= 1;
|
||||
}
|
||||
return (byte)x;
|
||||
}
|
||||
|
||||
private byte ALU_RR(int x)
|
||||
{
|
||||
if ((regs.F & CpuFlags.C) != 0)
|
||||
{
|
||||
regs.F = CpuTables.Rr1[x];
|
||||
x >>= 1;
|
||||
x += 0x80;
|
||||
}
|
||||
else
|
||||
{
|
||||
regs.F = CpuTables.Rr0[x];
|
||||
x >>= 1;
|
||||
}
|
||||
return (byte)x;
|
||||
}
|
||||
|
||||
private byte ALU_SLA(int x)
|
||||
{
|
||||
regs.F = CpuTables.Rl0[x];
|
||||
x <<= 1;
|
||||
return (byte)x;
|
||||
}
|
||||
|
||||
private byte ALU_SRA(int x)
|
||||
{
|
||||
regs.F = CpuTables.Sraf[x];
|
||||
x = (x >> 1) + (x & 0x80);
|
||||
return (byte)x;
|
||||
}
|
||||
|
||||
private byte ALU_SLL(int x)
|
||||
{
|
||||
regs.F = CpuTables.Rl1[x];
|
||||
x <<= 1;
|
||||
x++;
|
||||
return (byte)x;
|
||||
}
|
||||
|
||||
private byte ALU_SRL(int x)
|
||||
{
|
||||
regs.F = CpuTables.Rr0[x];
|
||||
x >>= 1;
|
||||
return (byte)x;
|
||||
}
|
||||
|
||||
private void ALU_BIT(byte src, int bit)
|
||||
{
|
||||
regs.F = (byte)(CpuTables.Logf[src & (1 << bit)] | CpuFlags.H | (regs.F & CpuFlags.C) | (src & CpuFlags.F3F5));
|
||||
}
|
||||
|
||||
private void ALU_BITMEM(byte src, int bit)
|
||||
{
|
||||
regs.F = (byte)(CpuTables.Logf[src & (1 << bit)] | CpuFlags.H | (regs.F & CpuFlags.C));
|
||||
regs.F = (byte)((regs.F & CpuFlags.NotF3F5) | (regs.MH & CpuFlags.F3F5));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,722 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Description: Z80 CPU Emulator [direct opcode part]
|
||||
* Date: 13.04.2007
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu.Processor
|
||||
{
|
||||
public partial class Z80Cpu
|
||||
{
|
||||
#region direct/DD/FD
|
||||
|
||||
private void INA_NN_(byte cmd) // IN A,(N) [11T]
|
||||
{
|
||||
// 11T (4, 3, 4)
|
||||
|
||||
regs.MW = RDMEM(regs.PC++); Tact += 3;
|
||||
regs.MW += (ushort)(regs.A << 8);
|
||||
|
||||
regs.A = RDPORT(regs.MW); Tact += 4;
|
||||
regs.MW++;
|
||||
}
|
||||
|
||||
private void OUT_NN_A(byte cmd) // OUT (N),A [11T]+
|
||||
{
|
||||
// 11T (4, 3, 4)
|
||||
|
||||
regs.MW = RDMEM(regs.PC++); Tact += 3;
|
||||
regs.MW += (ushort)(regs.A << 8);
|
||||
|
||||
WRPORT(regs.MW, regs.A); Tact += 4;
|
||||
regs.ML++;
|
||||
}
|
||||
|
||||
private void DI(byte cmd)
|
||||
{
|
||||
IFF1 = false;
|
||||
IFF2 = false;
|
||||
}
|
||||
|
||||
private void EI(byte cmd)
|
||||
{
|
||||
IFF1 = true;
|
||||
IFF2 = true;
|
||||
BINT = true;
|
||||
}
|
||||
|
||||
private void LDSPHL(byte cmd) // LD SP,HL
|
||||
{
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
regs.SP = regs.HL;
|
||||
}
|
||||
|
||||
private void EX_SP_HL(byte cmd) // EX (SP),HL
|
||||
{
|
||||
// 19T (4, 3, 4, 3, 5)
|
||||
|
||||
ushort tmpsp = regs.SP;
|
||||
regs.MW = RDMEM(tmpsp); Tact += 3;
|
||||
tmpsp++;
|
||||
|
||||
regs.MW += (ushort)(RDMEM(tmpsp) * 0x100); Tact += 3;
|
||||
RDNOMREQ(tmpsp); Tact += 1;
|
||||
|
||||
WRMEM(tmpsp, regs.H); Tact += 3;
|
||||
tmpsp--;
|
||||
|
||||
WRMEM(tmpsp, regs.L); Tact += 3;
|
||||
WRNOMREQ(tmpsp); Tact++;
|
||||
WRNOMREQ(tmpsp); Tact++;
|
||||
regs.HL = regs.MW;
|
||||
}
|
||||
|
||||
private void JP_HL_(byte cmd) // JP (HL)
|
||||
{
|
||||
regs.PC = regs.HL;
|
||||
}
|
||||
|
||||
private void EXDEHL(byte cmd) // EX DE,HL
|
||||
{
|
||||
(regs.DE, regs.HL) = (regs.HL, regs.DE);
|
||||
}
|
||||
|
||||
private void EXAFAF(byte cmd) // EX AF,AF'
|
||||
{
|
||||
(regs._AF, regs.AF) = (regs.AF, regs._AF);
|
||||
}
|
||||
|
||||
private void EXX(byte cmd) // EXX
|
||||
{
|
||||
(regs._BC, regs.BC) = (regs.BC, regs._BC);
|
||||
(regs._DE, regs.DE) = (regs.DE, regs._DE);
|
||||
(regs._HL, regs.HL) = (regs.HL, regs._HL);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region logical
|
||||
|
||||
private void RLCA(byte cmd)
|
||||
{
|
||||
regs.F = (byte)(CpuTables.Rlcaf[regs.A] | (regs.F & CpuFlags.SZP));
|
||||
int x = regs.A;
|
||||
x <<= 1;
|
||||
if ((x & 0x100) != 0) x |= 0x01;
|
||||
regs.A = (byte)x;
|
||||
}
|
||||
|
||||
private void RRCA(byte cmd)
|
||||
{
|
||||
regs.F = (byte)(CpuTables.Rrcaf[regs.A] | (regs.F & CpuFlags.SZP));
|
||||
int x = regs.A;
|
||||
if ((x & 0x01) != 0) x = (x >> 1) | 0x80;
|
||||
else x >>= 1;
|
||||
regs.A = (byte)x;
|
||||
}
|
||||
|
||||
private void RLA(byte cmd)
|
||||
{
|
||||
var carry = (regs.F & CpuFlags.C) != 0;
|
||||
regs.F = (byte)(CpuTables.Rlcaf[regs.A] | (regs.F & CpuFlags.SZP)); // use same table with rlca
|
||||
regs.A = (byte)(regs.A << 1);
|
||||
if (carry) regs.A |= 0x01;
|
||||
}
|
||||
|
||||
private void RRA(byte cmd)
|
||||
{
|
||||
var carry = (regs.F & CpuFlags.C) != 0;
|
||||
regs.F = (byte)(CpuTables.Rrcaf[regs.A] | (regs.F & CpuFlags.SZP)); // use same table with rrca
|
||||
regs.A = (byte)(regs.A >> 1);
|
||||
if (carry) regs.A |= 0x80;
|
||||
}
|
||||
|
||||
private void DAA(byte cmd)
|
||||
{
|
||||
regs.AF = CpuTables.Daaf[regs.A + 0x100 * ((regs.F & 3) + ((regs.F >> 2) & 4))];
|
||||
}
|
||||
|
||||
private void CPL(byte cmd)
|
||||
{
|
||||
regs.A = (byte)~regs.A;
|
||||
regs.F = (byte)((regs.F & CpuFlags.NotF3F5) | CpuFlags.HN | (regs.A & CpuFlags.F3F5));
|
||||
}
|
||||
|
||||
private void SCF_ZILOG(byte cmd)
|
||||
{
|
||||
regs.F = (byte)((regs.F & CpuFlags.SZP) |
|
||||
(((regs.F ^ regs.Q) | regs.A) & CpuFlags.F3F5) |
|
||||
CpuFlags.C);
|
||||
}
|
||||
|
||||
private void SCF_ST_CMOS(byte cmd)
|
||||
{
|
||||
regs.F = (byte)((regs.F & CpuFlags.SZP) |
|
||||
((((regs.F ^ regs.Q) & CpuFlags.F5) | regs.A) & CpuFlags.F3F5) |
|
||||
CpuFlags.C);
|
||||
}
|
||||
|
||||
private void SCF_NEC_NMOS(byte cmd)
|
||||
{
|
||||
regs.F = (byte)((regs.F & CpuFlags.SZP) |
|
||||
(regs.A & CpuFlags.F3F5) |
|
||||
CpuFlags.C);
|
||||
}
|
||||
|
||||
private void CCF_ZILOG(byte cmd)
|
||||
{
|
||||
regs.F = (byte)(((regs.F & CpuFlags.SZPC) ^ CpuFlags.C) |
|
||||
(((regs.F ^ regs.Q) | regs.A) & CpuFlags.F3F5) |
|
||||
((regs.F & CpuFlags.C) << 4));
|
||||
}
|
||||
|
||||
private void CCF_ST_CMOS(byte cmd)
|
||||
{
|
||||
regs.F = (byte)(((regs.F & CpuFlags.SZPC) ^ CpuFlags.C) |
|
||||
((((regs.F ^ regs.Q) & CpuFlags.F5) | regs.A) & CpuFlags.F3F5) |
|
||||
((regs.F & CpuFlags.C) << 4));
|
||||
}
|
||||
|
||||
private void CCF_NEC_NMOS(byte cmd)
|
||||
{
|
||||
regs.F = (byte)(((regs.F & CpuFlags.SZPC) ^ CpuFlags.C) |
|
||||
(regs.A & CpuFlags.F3F5) |
|
||||
((regs.F & CpuFlags.C) << 4));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region jmp/call/ret/jr
|
||||
|
||||
private static readonly byte[] s_conds =
|
||||
[
|
||||
CpuFlags.Z,
|
||||
CpuFlags.C,
|
||||
CpuFlags.P,
|
||||
CpuFlags.S
|
||||
];
|
||||
|
||||
private void DJNZ(byte cmd) // DJNZ nn
|
||||
{
|
||||
// B==0 => 8T (5, 3)
|
||||
// B!=0 => 13T (5, 3, 5)
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
int drel = (sbyte)RDMEM(regs.PC); Tact += 3;
|
||||
regs.PC++;
|
||||
|
||||
if (--regs.B != 0)
|
||||
{
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
regs.MW = (ushort)(regs.PC + drel);
|
||||
regs.PC = regs.MW;
|
||||
}
|
||||
}
|
||||
|
||||
private void JRNN(byte cmd) // JR nn
|
||||
{
|
||||
// 12T (4, 3, 5)
|
||||
|
||||
int drel = (sbyte)RDMEM(regs.PC); Tact += 3;
|
||||
regs.PC++;
|
||||
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
regs.MW = (ushort)(regs.PC + drel);
|
||||
regs.PC = regs.MW;
|
||||
}
|
||||
|
||||
private void JRXNN(byte cmd) // JR x,nn
|
||||
{
|
||||
// false => 7T (4, 3)
|
||||
// true => 12 (4, 3, 5)
|
||||
var cond = (cmd & 0x18) >> 3;
|
||||
var mask = s_conds[cond >> 1];
|
||||
var f = regs.AF & mask;
|
||||
if ((cond & 1) != 0) f ^= mask;
|
||||
|
||||
int drel = (sbyte)RDMEM(regs.PC); Tact += 3;
|
||||
regs.PC++;
|
||||
|
||||
if (f == 0)
|
||||
{
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
regs.MW = (ushort)(regs.PC + drel);
|
||||
regs.PC = regs.MW;
|
||||
}
|
||||
}
|
||||
|
||||
private void CALLNNNN(byte cmd) // CALL
|
||||
{
|
||||
// 17T (4, 3, 4, 3, 3)
|
||||
|
||||
regs.MW = RDMEM(regs.PC); Tact += 3;
|
||||
regs.PC++;
|
||||
|
||||
regs.MW += (ushort)(RDMEM(regs.PC) * 0x100); Tact += 3;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
regs.PC++;
|
||||
regs.SP--;
|
||||
|
||||
WRMEM(regs.SP, (byte)(regs.PC >> 8)); Tact += 3;
|
||||
regs.SP--;
|
||||
|
||||
WRMEM(regs.SP, (byte)regs.PC); Tact += 3;
|
||||
regs.PC = regs.MW;
|
||||
}
|
||||
|
||||
private void CALLXNNNN(byte cmd) // CALL x,#nn
|
||||
{
|
||||
// false => 10T (4, 3, 3)
|
||||
// true => 17T (4, 3, 4, 3, 3)
|
||||
var cond = (cmd & 0x38) >> 3;
|
||||
var mask = s_conds[cond >> 1];
|
||||
var f = regs.AF & mask;
|
||||
if ((cond & 1) != 0) f ^= mask;
|
||||
|
||||
regs.MW = RDMEM(regs.PC); Tact += 3;
|
||||
regs.PC++;
|
||||
|
||||
regs.MW += (ushort)(RDMEM(regs.PC) * 0x100); Tact += 3;
|
||||
if (f == 0)
|
||||
{
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
}
|
||||
regs.PC++;
|
||||
|
||||
if (f == 0)
|
||||
{
|
||||
regs.SP--;
|
||||
|
||||
WRMEM(regs.SP, (byte)(regs.PC >> 8)); Tact += 3;
|
||||
regs.SP--;
|
||||
|
||||
WRMEM(regs.SP, (byte)regs.PC); Tact += 3;
|
||||
regs.PC = regs.MW;
|
||||
}
|
||||
}
|
||||
|
||||
private void RET(byte cmd) // RET
|
||||
{
|
||||
// 10T (4, 3, 3)
|
||||
|
||||
regs.MW = RDMEM(regs.SP); Tact += 3;
|
||||
regs.SP++;
|
||||
|
||||
regs.MW += (ushort)(RDMEM(regs.SP) * 0x100); Tact += 3;
|
||||
regs.SP++;
|
||||
regs.PC = regs.MW;
|
||||
}
|
||||
|
||||
private void RETX(byte cmd) // RET x
|
||||
{
|
||||
// false => 5T (5)
|
||||
// true => 11T (5, 3, 3)
|
||||
var cond = (cmd & 0x38) >> 3;
|
||||
var mask = s_conds[cond >> 1];
|
||||
var f = regs.AF & mask;
|
||||
if ((cond & 1) != 0) f ^= mask;
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
if (f == 0)
|
||||
{
|
||||
regs.MW = RDMEM(regs.SP); Tact += 3;
|
||||
regs.SP++;
|
||||
|
||||
regs.MW += (ushort)(RDMEM(regs.SP) * 0x100); Tact += 3;
|
||||
regs.SP++;
|
||||
regs.PC = regs.MW;
|
||||
}
|
||||
}
|
||||
|
||||
private void JPNNNN(byte cmd) // JP nnnn
|
||||
{
|
||||
// 10T (4, 3, 3)
|
||||
|
||||
regs.MW = RDMEM(regs.PC); Tact += 3;
|
||||
regs.PC++;
|
||||
|
||||
regs.MW += (ushort)(RDMEM(regs.PC) * 0x100); Tact += 3;
|
||||
regs.PC = regs.MW;
|
||||
}
|
||||
|
||||
private void JPXNN(byte cmd) // JP x,#nn ???
|
||||
{
|
||||
// 10T (4, 3, 3)
|
||||
var cond = (cmd & 0x38) >> 3;
|
||||
var mask = s_conds[cond >> 1];
|
||||
var f = regs.AF & mask;
|
||||
if ((cond & 1) != 0) f ^= mask;
|
||||
|
||||
regs.MW = RDMEM(regs.PC); Tact += 3;
|
||||
regs.PC++;
|
||||
|
||||
regs.MW += (ushort)(RDMEM(regs.PC) * 0x100); Tact += 3;
|
||||
regs.PC++;
|
||||
|
||||
if (f == 0)
|
||||
regs.PC = regs.MW;
|
||||
}
|
||||
|
||||
private void RSTNN(byte cmd) // RST #nn ?TIME?
|
||||
{
|
||||
// 11T (5, 3, 3)
|
||||
var rst = (ushort)(cmd & 0x38);
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
regs.SP--;
|
||||
|
||||
WRMEM(regs.SP, (byte)(regs.PC >> 8)); Tact += 3;
|
||||
regs.SP--;
|
||||
|
||||
WRMEM(regs.SP, (byte)regs.PC); Tact += 3;
|
||||
regs.MW = rst;
|
||||
regs.PC = regs.MW;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region push/pop
|
||||
|
||||
private void PUSHRR(byte cmd) // PUSH RR ?TIME?
|
||||
{
|
||||
// 11T (5, 3, 3)
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
RDNOMREQ(regs.IR); Tact += 1;
|
||||
ushort val = rr == CpuRegId.Sp ? regs.AF : _pairGetters[rr]();
|
||||
|
||||
regs.SP--;
|
||||
WRMEM(regs.SP, (byte)(val >> 8)); Tact += 3;
|
||||
|
||||
regs.SP--;
|
||||
WRMEM(regs.SP, (byte)val); Tact += 3;
|
||||
}
|
||||
|
||||
private void POPRR(byte cmd) // POP RR
|
||||
{
|
||||
// 10T (4, 3, 3)
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
var val = (ushort)RDMEM(regs.SP);
|
||||
regs.SP++;
|
||||
Tact += 3;
|
||||
|
||||
val |= (ushort)(RDMEM(regs.SP) << 8);
|
||||
regs.SP++;
|
||||
if (rr == CpuRegId.Sp) regs.AF = val;
|
||||
else _pairSetters[rr](val);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ALU
|
||||
|
||||
private void ALUAN(byte cmd)
|
||||
{
|
||||
// 7T (4, 3)
|
||||
var op = (cmd & 0x38) >> 3;
|
||||
|
||||
var val = RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
_alualg[op](val);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void ALUAR(byte cmd) // ADD/ADC/SUB/SBC/AND/XOR/OR/CP A,R
|
||||
{
|
||||
var r = cmd & 0x07;
|
||||
var op = (cmd & 0x38) >> 3;
|
||||
|
||||
_alualg[op](_regGetters[r]());
|
||||
}
|
||||
|
||||
private void ALUA_HL_(byte cmd) // ADD/ADC/SUB/SBC/AND/XOR/OR/CP A,(HL)
|
||||
{
|
||||
// 7T (4, 3)
|
||||
var op = (cmd & 0x38) >> 3;
|
||||
|
||||
var val = RDMEM(regs.HL);
|
||||
_alualg[op](val);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void ADDHLRR(byte cmd) // ADD HL,RR
|
||||
{
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
regs.MW = (ushort)(regs.HL + 1);
|
||||
regs.HL = ALU_ADDHLRR(regs.HL, _pairGetters[(cmd & 0x30) >> 4]());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region loads
|
||||
|
||||
private void LDA_RR_(byte cmd) // LD A,(RR)
|
||||
{
|
||||
// 7T (4, 3)
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
var rrValue = _pairGetters[rr]();
|
||||
regs.A = RDMEM(rrValue);
|
||||
regs.MW = (ushort)(rrValue + 1);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void LDA_NN_(byte cmd) // LD A,(nnnn)
|
||||
{
|
||||
// 13T (4, 3, 3, 3)
|
||||
|
||||
ushort adr = RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
adr += (ushort)(RDMEM(regs.PC) * 0x100);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
regs.A = RDMEM(adr);
|
||||
regs.MW = (ushort)(adr + 1);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void LDHL_NN_(byte cmd) // LD HL,(nnnn)
|
||||
{
|
||||
// 16T (4, 3, 3, 3, 3)
|
||||
|
||||
var adr = (ushort)RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
adr += (ushort)(RDMEM(regs.PC) * 0x100);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
var val = (ushort)RDMEM(adr);
|
||||
regs.MW = (ushort)(adr + 1);
|
||||
Tact += 3;
|
||||
|
||||
val += (ushort)(RDMEM(regs.MW) * 0x100);
|
||||
regs.HL = val;
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void LD_RR_A(byte cmd) // LD (RR),A
|
||||
{
|
||||
// 7T (4, 3)
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
var rrValue = _pairGetters[rr]();
|
||||
WRMEM(rrValue, regs.A);
|
||||
regs.MH = regs.A;
|
||||
regs.ML = (byte)(rrValue + 1);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void LD_NN_A(byte cmd) // LD (nnnn),A
|
||||
{
|
||||
// 13T (4, 3, 3, 3)
|
||||
|
||||
ushort adr = RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
adr += (ushort)(RDMEM(regs.PC) * 0x100);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
WRMEM(adr, regs.A);
|
||||
regs.MH = regs.A;
|
||||
regs.ML = (byte)(adr + 1);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void LD_NN_HL(byte cmd) // LD (nnnn),HL
|
||||
{
|
||||
// 16T (4, 3, 3, 3, 3)
|
||||
|
||||
ushort adr = RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
adr += (ushort)(RDMEM(regs.PC) * 0x100);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
WRMEM(adr, regs.L);
|
||||
regs.MW = (ushort)(adr + 1);
|
||||
Tact += 3;
|
||||
|
||||
WRMEM(regs.MW, regs.H);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void LDRRNNNN(byte cmd) // LD RR,nnnn
|
||||
{
|
||||
// 10T (4, 3, 3)
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
ushort val = RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
val |= (ushort)(RDMEM(regs.PC) << 8);
|
||||
regs.PC++;
|
||||
_pairSetters[rr](val);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void LDRNN(byte cmd) // LD R,nn
|
||||
{
|
||||
// 7T (4, 3)
|
||||
var r = (cmd & 0x38) >> 3;
|
||||
|
||||
_regSetters[r](RDMEM(regs.PC));
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void LD_HL_NN(byte cmd) // LD (HL),nn
|
||||
{
|
||||
// 10T (4, 3, 3)
|
||||
|
||||
var val = RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
WRMEM(regs.HL, val);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void LDRdRs(byte cmd) // LD R1,R2
|
||||
{
|
||||
var rsrc = cmd & 0x07;
|
||||
var rdst = (cmd & 0x38) >> 3;
|
||||
_regSetters[rdst](_regGetters[rsrc]());
|
||||
}
|
||||
|
||||
private void LD_HL_R(byte cmd) // LD (HL),R
|
||||
{
|
||||
// 7T (4, 3)
|
||||
var r = cmd & 0x07;
|
||||
|
||||
WRMEM(regs.HL, _regGetters[r]());
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void LDR_HL_(byte cmd) // LD R,(HL)
|
||||
{
|
||||
// 7T (4, 3)
|
||||
var r = (cmd & 0x38) >> 3;
|
||||
|
||||
_regSetters[r](RDMEM(regs.HL));
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INC/DEC
|
||||
|
||||
private void DECRR(byte cmd) // DEC RR
|
||||
{
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
_pairSetters[rr]((ushort)(_pairGetters[rr]() - 1));
|
||||
}
|
||||
|
||||
private void INCRR(byte cmd) // INC RR
|
||||
{
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
_pairSetters[rr]((ushort)(_pairGetters[rr]() + 1));
|
||||
}
|
||||
|
||||
private void DECR(byte cmd) // DEC R
|
||||
{
|
||||
var r = (cmd & 0x38) >> 3;
|
||||
|
||||
_regSetters[r](ALU_DECR(_regGetters[r]()));
|
||||
}
|
||||
|
||||
private void INCR(byte cmd) // INC R
|
||||
{
|
||||
var r = (cmd & 0x38) >> 3;
|
||||
|
||||
_regSetters[r](ALU_INCR(_regGetters[r]()));
|
||||
}
|
||||
|
||||
private void DEC_HL_(byte cmd) // DEC (HL)
|
||||
{
|
||||
// 11T (4, 4, 3)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val = ALU_DECR(val);
|
||||
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void INC_HL_(byte cmd) // INC (HL)
|
||||
{
|
||||
// 11T (4, 4, 3)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val = ALU_INCR(val);
|
||||
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void HALT(byte cmd)
|
||||
{
|
||||
HALTED = true;
|
||||
regs.PC--; // workaround for Z80 snapshot halt issue + comfortable debugging
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Description: Z80 CPU Emulator [CB prefixed opcodes part]
|
||||
* Date: 13.04.2007
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu.Processor
|
||||
{
|
||||
public partial class Z80Cpu
|
||||
{
|
||||
private void CB_RLC(byte cmd) // RLC r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
_regSetters[r](ALU_RLC(_regGetters[r]()));
|
||||
}
|
||||
|
||||
private void CB_RRC(byte cmd) // RRC r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
_regSetters[r](ALU_RRC(_regGetters[r]()));
|
||||
}
|
||||
|
||||
private void CB_RL(byte cmd) // RL r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
_regSetters[r](ALU_RL(_regGetters[r]()));
|
||||
}
|
||||
|
||||
private void CB_RR(byte cmd) // RR r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
_regSetters[r](ALU_RR(_regGetters[r]()));
|
||||
}
|
||||
|
||||
private void CB_SLA(byte cmd) // SLA r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
_regSetters[r](ALU_SLA(_regGetters[r]()));
|
||||
}
|
||||
|
||||
private void CB_SRA(byte cmd) // SRA r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
_regSetters[r](ALU_SRA(_regGetters[r]()));
|
||||
}
|
||||
|
||||
private void CB_SLL(byte cmd) // *SLL r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
_regSetters[r](ALU_SLL(_regGetters[r]()));
|
||||
}
|
||||
|
||||
private void CB_SRL(byte cmd) // SRL r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
_regSetters[r](ALU_SRL(_regGetters[r]()));
|
||||
}
|
||||
|
||||
private void CB_BIT(byte cmd) // BIT r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
var b = (cmd & 0x38) >> 3;
|
||||
|
||||
ALU_BIT(_regGetters[r](), b);
|
||||
}
|
||||
|
||||
private void CB_RES(byte cmd) // RES r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
var m = ~(1 << ((cmd & 0x38) >> 3));
|
||||
|
||||
_regSetters[r]((byte)(_regGetters[r]() & m));
|
||||
}
|
||||
|
||||
private void CB_SET(byte cmd) // SET r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
var m = 1 << ((cmd & 0x38) >> 3);
|
||||
|
||||
_regSetters[r]((byte)(_regGetters[r]() | m));
|
||||
}
|
||||
|
||||
private void CB_RLCHL(byte cmd) // RLC (HL)
|
||||
{
|
||||
// 15T (4, 4, 4, 3)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val = ALU_RLC(val);
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void CB_RRCHL(byte cmd) // RRC (HL)
|
||||
{
|
||||
// 15T (4, 4, 4, 3)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val = ALU_RRC(val);
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void CB_RLHL(byte cmd) // RL (HL)
|
||||
{
|
||||
// 15T (4, 4, 4, 3)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val = ALU_RL(val);
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void CB_RRHL(byte cmd) // RR (HL) [15T]
|
||||
{
|
||||
// 15T (4, 4, 4, 3)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val = ALU_RR(val);
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void CB_SLAHL(byte cmd) // SLA (HL) [15T]
|
||||
{
|
||||
// 15T (4, 4, 4, 3)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val = ALU_SLA(val);
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void CB_SRAHL(byte cmd) // SRA (HL) [15T]
|
||||
{
|
||||
// 15T (4, 4, 4, 3)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val = ALU_SRA(val);
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void CB_SLLHL(byte cmd) // *SLL (HL) [15T]
|
||||
{
|
||||
// 15T (4, 4, 4, 3)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val = ALU_SLL(val);
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void CB_SRLHL(byte cmd) // SRL (HL)
|
||||
{
|
||||
// 15T (4, 4, 4, 3)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val = ALU_SRL(val);
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void CB_BITHL(byte cmd) // BIT (HL) [12T]
|
||||
{
|
||||
// 12T (4, 4, 4)
|
||||
var b = (cmd & 0x38) >> 3;
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 4;
|
||||
ALU_BITMEM(val, b);
|
||||
}
|
||||
|
||||
private void CB_RESHL(byte cmd) // RES (HL) [15T]
|
||||
{
|
||||
// 15 (4, 4, 4, 3)
|
||||
var m = (byte)~(1 << ((cmd & 0x38) >> 3));
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val &= m;
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void CB_SETHL(byte cmd) // SET (HL) [15T]
|
||||
{
|
||||
// 15T (4, 4, 4, 3)
|
||||
var m = (byte)(1 << ((cmd & 0x38) >> 3));
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val |= m;
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,880 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Description: Z80 CPU Emulator [ED prefixed opcode part]
|
||||
* Date: 13.04.2007
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
namespace ZXMAK2.Engine.Cpu.Processor
|
||||
{
|
||||
public partial class Z80Cpu
|
||||
{
|
||||
private void ED_LDI(byte cmd)
|
||||
{
|
||||
// 16T (4, 4, 3, 5)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
|
||||
WRMEM(regs.DE, val); Tact += 3;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
|
||||
regs.HL++;
|
||||
regs.DE++;
|
||||
regs.BC--;
|
||||
val += regs.A;
|
||||
|
||||
regs.F = (byte)((regs.F & CpuFlags.NotHPNF3F5) |
|
||||
(val & CpuFlags.F3) | ((val << 4) & CpuFlags.F5));
|
||||
if (regs.BC != 0) regs.F |= CpuFlags.P;
|
||||
}
|
||||
|
||||
private void ED_CPI(byte cmd)
|
||||
{
|
||||
// 16T (4, 4, 3, 5)
|
||||
|
||||
var cf = regs.F & CpuFlags.C;
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
|
||||
regs.HL++;
|
||||
regs.F = (byte)(CpuTables.Cpf8b[regs.A * 0x100 + val] + cf);
|
||||
if (--regs.BC != 0) regs.F |= CpuFlags.P;
|
||||
regs.MW++;
|
||||
}
|
||||
|
||||
private void ED_INI(byte cmd) // INI [16T]
|
||||
{
|
||||
// 16T (4, 5, 3, 4)
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
var val = RDPORT(regs.BC); Tact += 3;
|
||||
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
|
||||
regs.MW = (ushort)(regs.BC + 1);
|
||||
regs.HL++;
|
||||
regs.B--;
|
||||
|
||||
//FUSE
|
||||
byte flgtmp = (byte)(val + regs.C + 1);
|
||||
regs.F = (byte)(CpuTables.Logf[regs.B] & CpuFlags.NotP);
|
||||
if ((CpuTables.Logf[(flgtmp & 0x07) ^ regs.B] & CpuFlags.P) != 0) regs.F |= CpuFlags.P;
|
||||
if (flgtmp < val) regs.F |= CpuFlags.HC;
|
||||
if ((val & 0x80) != 0) regs.F |= CpuFlags.N;
|
||||
|
||||
Tact++; //?? really?
|
||||
}
|
||||
|
||||
private void ED_OUTI(byte cmd) // OUTI [16T]
|
||||
{
|
||||
// 16 (4, 5, 3, 4)
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
regs.B--;
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
|
||||
WRPORT(regs.BC, val); Tact += 3;
|
||||
|
||||
regs.MW = (ushort)(regs.BC + 1);
|
||||
regs.HL++;
|
||||
|
||||
//FUSE
|
||||
byte flgtmp = (byte)(val + regs.L);
|
||||
regs.F = (byte)(CpuTables.Logf[regs.B] & CpuFlags.NotP);
|
||||
if ((CpuTables.Logf[(flgtmp & 0x07) ^ regs.B] & CpuFlags.P) != 0) regs.F |= CpuFlags.P;
|
||||
if (flgtmp < val) regs.F |= CpuFlags.HC;
|
||||
if ((val & 0x80) != 0) regs.F |= CpuFlags.N;
|
||||
|
||||
Tact++; //?? really?
|
||||
}
|
||||
|
||||
private void ED_LDD(byte cmd)
|
||||
{
|
||||
// 16T (4, 4, 3, 5)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
|
||||
WRMEM(regs.DE, val); Tact += 3;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
|
||||
regs.HL--;
|
||||
regs.DE--;
|
||||
regs.BC--;
|
||||
val += regs.A;
|
||||
|
||||
regs.F = (byte)((regs.F & CpuFlags.NotHPNF3F5) |
|
||||
(val & CpuFlags.F3) | ((val << 4) & CpuFlags.F5));
|
||||
if (regs.BC != 0) regs.F |= CpuFlags.P;
|
||||
}
|
||||
|
||||
private void ED_CPD(byte cmd)
|
||||
{
|
||||
// 16T (4, 4, 3, 5)
|
||||
|
||||
var cf = regs.F & CpuFlags.C;
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
|
||||
regs.HL--;
|
||||
regs.BC--;
|
||||
regs.MW--;
|
||||
regs.F = (byte)(CpuTables.Cpf8b[regs.A * 0x100 + val] + cf);
|
||||
if (regs.BC != 0) regs.F |= CpuFlags.P;
|
||||
}
|
||||
|
||||
private void ED_IND(byte cmd) // IND [16T]
|
||||
{
|
||||
// 16T (4, 5, 3, 4)
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
var val = RDPORT(regs.BC); Tact += 3;
|
||||
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
|
||||
regs.MW = (ushort)(regs.BC - 1);
|
||||
regs.HL--;
|
||||
regs.B--;
|
||||
|
||||
//FUSE
|
||||
byte flgtmp = (byte)(val + regs.C - 1);
|
||||
regs.F = (byte)(CpuTables.Logf[regs.B] & CpuFlags.NotP);
|
||||
if ((CpuTables.Logf[(flgtmp & 0x07) ^ regs.B] & CpuFlags.P) != 0) regs.F |= CpuFlags.P;
|
||||
if (flgtmp < val) regs.F |= CpuFlags.HC;
|
||||
if ((val & 0x80) != 0) regs.F |= CpuFlags.N;
|
||||
|
||||
Tact++; // ?? really?
|
||||
}
|
||||
|
||||
private void ED_OUTD(byte cmd) // OUTD [16T]
|
||||
{
|
||||
// 16T (4, 5, 3, 4)
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
regs.B--;
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
|
||||
WRPORT(regs.BC, val); Tact += 3;
|
||||
|
||||
regs.MW = (ushort)(regs.BC - 1);
|
||||
regs.HL--;
|
||||
|
||||
//FUSE
|
||||
byte flgtmp = (byte)(val + regs.L);
|
||||
regs.F = (byte)(CpuTables.Logf[regs.B] & CpuFlags.NotP);
|
||||
if ((CpuTables.Logf[(flgtmp & 0x07) ^ regs.B] & CpuFlags.P) != 0) regs.F |= CpuFlags.P;
|
||||
if (flgtmp < val) regs.F |= CpuFlags.HC;
|
||||
if ((val & 0x80) != 0) regs.F |= CpuFlags.N;
|
||||
|
||||
Tact++; // ?? really?
|
||||
}
|
||||
|
||||
private void ED_LDIR(byte cmd)
|
||||
{
|
||||
//BC==0 => 16T (4, 4, 3, 5)
|
||||
//BC!=0 => 21T (4, 4, 3, 5, 5)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
|
||||
WRMEM(regs.DE, val); Tact += 3;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
|
||||
regs.BC--;
|
||||
val += regs.A;
|
||||
|
||||
regs.F &= CpuFlags.NotHPNF3F5;
|
||||
if (regs.BC != 0)
|
||||
{
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
regs.PC--;
|
||||
regs.MW = regs.PC;
|
||||
regs.PC--;
|
||||
|
||||
// YF = PCi.13; XF = PCi.11
|
||||
regs.F |= (byte)(regs.PCH & CpuFlags.F3F5);
|
||||
regs.F |= CpuFlags.P;
|
||||
}
|
||||
else
|
||||
{
|
||||
// YF = PCi.13; XF = PCi.11
|
||||
regs.F |= (byte)((val & CpuFlags.F3) | ((val << 4) & CpuFlags.F5));
|
||||
}
|
||||
regs.HL++;
|
||||
regs.DE++;
|
||||
}
|
||||
|
||||
private void ED_CPIR(byte cmd)
|
||||
{
|
||||
//BC==0 => 16T (4, 4, 3, 5)
|
||||
//BC!=0 => 21T (4, 4, 3, 5, 5)
|
||||
|
||||
regs.MW++;
|
||||
var cf = regs.F & CpuFlags.C;
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
|
||||
regs.BC--;
|
||||
regs.F = (byte)(CpuTables.Cpf8b[regs.A * 0x100 + val] + cf);
|
||||
|
||||
if (regs.BC != 0)
|
||||
{
|
||||
regs.F |= CpuFlags.P;
|
||||
if ((regs.F & CpuFlags.Z) == 0)
|
||||
{
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
regs.PC--;
|
||||
regs.MW = regs.PC;
|
||||
regs.PC--;
|
||||
|
||||
// YF = PCi.13; XF = PCi.11
|
||||
regs.F &= CpuFlags.NotF3F5;
|
||||
regs.F |= (byte)(regs.PCH & CpuFlags.F3F5);
|
||||
}
|
||||
}
|
||||
regs.HL++;
|
||||
}
|
||||
|
||||
private void ED_INIR(byte cmd) // INIR [16T/21T]
|
||||
{
|
||||
// B==0 => 16T (4, 5, 3, 4)
|
||||
// B!=0 => 21T (4, 5, 3, 4, 5)
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
var val = RDPORT(regs.BC); Tact += 3;
|
||||
|
||||
var nf = (byte)((val >> 6) & CpuFlags.N);
|
||||
var t = val + ((regs.C + 1) & 0xff);
|
||||
var hcf = t > 255 ? CpuFlags.HC : 0;
|
||||
|
||||
WRMEM(regs.HL++, val); Tact += 3;
|
||||
regs.MW = (ushort)(regs.BC + 1);
|
||||
|
||||
regs.B--; Tact++;
|
||||
var pf = CpuTables.Parity[(t & 7) ^ regs.B];
|
||||
|
||||
if (regs.B != 0)
|
||||
{
|
||||
WRNOMREQ(regs.HL); Tact++;
|
||||
WRNOMREQ(regs.HL); Tact++;
|
||||
WRNOMREQ(regs.HL); Tact++;
|
||||
WRNOMREQ(regs.HL); Tact++;
|
||||
WRNOMREQ(regs.HL); Tact++;
|
||||
regs.PC -= 2;
|
||||
|
||||
regs.F = (byte)(
|
||||
/* ZF = 0 */
|
||||
(regs.B & CpuFlags.S) | /* SF = Bo.7 */
|
||||
(regs.PCH & CpuFlags.F3F5) | /* YF = PCi.13; XF = PCi.11 */
|
||||
nf); /* NF = IO.7 */
|
||||
if (hcf != 0)
|
||||
{
|
||||
regs.F |= CpuFlags.C;
|
||||
if ((val & 0x80) != 0)
|
||||
{
|
||||
// PF = PF ^ Parity((B - 1) & 0x7) ^ 1;
|
||||
// HF = (B & 0x0F) == 0x00;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[(regs.B - 1) & 7] ^ CpuFlags.P);
|
||||
if ((regs.B & 0x0f) == 0x00)
|
||||
regs.F |= CpuFlags.H;
|
||||
}
|
||||
else
|
||||
{
|
||||
// PF = PF ^ Parity((B + 1) & 0x7) ^ 1;
|
||||
// HF = (B & 0x0F) == 0x0F;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[(regs.B + 1) & 7] ^ CpuFlags.P);
|
||||
if ((regs.B & 0x0f) == 0x0f)
|
||||
regs.F |= CpuFlags.H;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// PF = PF ^ Parity(B & 0x7) ^ 1;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[regs.B & 7] ^ CpuFlags.P);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
regs.F = (byte)(CpuFlags.Z | /* ZF = 1; SF, YF, XF = 0 */
|
||||
hcf | /* HF, CF = T > 255 */
|
||||
pf | /* PF = ((T & 7) ^ Bo).parity */
|
||||
nf); /* NF = IO.7 */
|
||||
}
|
||||
}
|
||||
|
||||
private void ED_OTIR(byte cmd) // OTIR [16T/21T]
|
||||
{
|
||||
// B==0 => 16T (4, 5, 3, 4)
|
||||
// B!=0 => 21T (4, 5, 3, 4, 5)
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
var val = RDMEM(regs.HL++); Tact += 3;
|
||||
regs.MW = (ushort)(regs.BC + 1);
|
||||
|
||||
var nf = (byte)((val >> 6) & CpuFlags.N);
|
||||
var t = val + regs.L;
|
||||
var hcf = t > 255 ? CpuFlags.HC : 0;
|
||||
|
||||
regs.B--;
|
||||
var pf = CpuTables.Parity[(t & 7) ^ regs.B];
|
||||
|
||||
WRPORT(regs.BC, val); Tact += 3;
|
||||
Tact++;
|
||||
|
||||
if (regs.B != 0)
|
||||
{
|
||||
RDNOMREQ(regs.BC); Tact++;
|
||||
RDNOMREQ(regs.BC); Tact++;
|
||||
RDNOMREQ(regs.BC); Tact++;
|
||||
RDNOMREQ(regs.BC); Tact++;
|
||||
RDNOMREQ(regs.BC); Tact++;
|
||||
regs.PC -= 2;
|
||||
|
||||
regs.F = (byte)(
|
||||
/* ZF = 0 */
|
||||
(regs.B & CpuFlags.S) | /* SF = Bo.7 */
|
||||
(regs.PCH & CpuFlags.F3F5) |/* YF = PCi.13; XF = PCi.11 */
|
||||
nf); /* NF = IO.7 */
|
||||
if (hcf != 0)
|
||||
{
|
||||
regs.F |= CpuFlags.C;
|
||||
if ((val & 0x80) != 0)
|
||||
{
|
||||
// PF = PF ^ Parity((B - 1) & 0x7) ^ 1;
|
||||
// HF = (B & 0x0F) == 0x00;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[(regs.B - 1) & 7] ^ CpuFlags.P);
|
||||
if ((regs.B & 0x0f) == 0x00)
|
||||
regs.F |= CpuFlags.H;
|
||||
}
|
||||
else
|
||||
{
|
||||
// PF = PF ^ Parity((B + 1) & 0x7) ^ 1;
|
||||
// HF = (B & 0x0F) == 0x0F;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[(regs.B + 1) & 7] ^ CpuFlags.P);
|
||||
if ((regs.B & 0x0f) == 0x0f)
|
||||
regs.F |= CpuFlags.H;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// PF = PF ^ Parity(B & 0x7) ^ 1;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[regs.B & 7] ^ CpuFlags.P);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
regs.F = (byte)(CpuFlags.Z | /* ZF = 1; SF, YF, XF = 0 */
|
||||
hcf | /* HF, CF = T > 255 */
|
||||
pf | /* PF = ((T & 7) ^ Bo).parity */
|
||||
nf); /* NF = IO.7 */
|
||||
}
|
||||
}
|
||||
|
||||
private void ED_LDDR(byte cmd)
|
||||
{
|
||||
//BC==0 => 16T (4, 4, 3, 5)
|
||||
//BC!=0 => 21T (4, 4, 3, 5, 5)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
|
||||
WRMEM(regs.DE, val); Tact += 3;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
|
||||
regs.BC--;
|
||||
val += regs.A;
|
||||
|
||||
regs.F &= CpuFlags.NotHPNF3F5;
|
||||
if (regs.BC != 0)
|
||||
{
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
regs.PC--;
|
||||
regs.MW = regs.PC;
|
||||
regs.PC--;
|
||||
|
||||
// YF = PCi.13; XF = PCi.11
|
||||
regs.F |= (byte)(regs.PCH & CpuFlags.F3F5);
|
||||
regs.F |= CpuFlags.P;
|
||||
}
|
||||
else
|
||||
{
|
||||
// YF = PCi.13; XF = PCi.11
|
||||
regs.F |= (byte)((val & CpuFlags.F3) | ((val << 4) & CpuFlags.F5));
|
||||
}
|
||||
regs.HL--;
|
||||
regs.DE--;
|
||||
}
|
||||
|
||||
private void ED_CPDR(byte cmd)
|
||||
{
|
||||
// BC==0 => 16T (4, 4, 3, 5)
|
||||
// BC!=0 => 21T (4, 4, 3, 5, 5)
|
||||
|
||||
regs.MW--;
|
||||
var cf = regs.F & CpuFlags.C;
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
regs.BC--;
|
||||
regs.F = (byte)(CpuTables.Cpf8b[regs.A * 0x100 + val] + cf);
|
||||
|
||||
if (regs.BC != 0)
|
||||
{
|
||||
regs.F |= CpuFlags.P;
|
||||
if ((regs.F & CpuFlags.Z) == 0)
|
||||
{
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
regs.PC--;
|
||||
regs.MW = regs.PC;
|
||||
regs.PC--;
|
||||
|
||||
// YF = PCi.13; XF = PCi.11
|
||||
regs.F &= CpuFlags.NotF3F5;
|
||||
regs.F |= (byte)(regs.PCH & CpuFlags.F3F5);
|
||||
}
|
||||
}
|
||||
regs.HL--;
|
||||
}
|
||||
|
||||
private void ED_INDR(byte cmd) // INDR [16T/21T]
|
||||
{
|
||||
// B==0 => 16 (4, 5, 3, 4)
|
||||
// B!=0 => 21 (4, 5, 3, 4, 5)
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
var val = RDPORT(regs.BC); Tact += 3;
|
||||
|
||||
var nf = (byte)((val >> 6) & CpuFlags.N);
|
||||
var t = val + ((regs.C - 1) & 0xff);
|
||||
var hcf = t > 255 ? CpuFlags.HC : 0;
|
||||
|
||||
WRMEM(regs.HL--, val); Tact += 3;
|
||||
regs.MW = (ushort)(regs.BC - 1);
|
||||
|
||||
regs.B--; Tact++;
|
||||
var pf = CpuTables.Parity[(t & 7) ^ regs.B];
|
||||
|
||||
if (regs.B != 0)
|
||||
{
|
||||
WRNOMREQ(regs.HL); Tact++;
|
||||
WRNOMREQ(regs.HL); Tact++;
|
||||
WRNOMREQ(regs.HL); Tact++;
|
||||
WRNOMREQ(regs.HL); Tact++;
|
||||
WRNOMREQ(regs.HL); Tact++;
|
||||
regs.PC -= 2;
|
||||
|
||||
regs.F = (byte)(
|
||||
/* ZF = 0 */
|
||||
(regs.B & CpuFlags.S) | /* SF = Bo.7 */
|
||||
(regs.PCH & CpuFlags.F3F5) |/* YF = PCi.13; XF = PCi.11 */
|
||||
nf); /* NF = IO.7 */
|
||||
if (hcf != 0)
|
||||
{
|
||||
regs.F |= CpuFlags.C;
|
||||
if ((val & 0x80) != 0)
|
||||
{
|
||||
// PF = PF ^ Parity((B - 1) & 0x7) ^ 1;
|
||||
// HF = (B & 0x0F) == 0x00;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[(regs.B - 1) & 7] ^ CpuFlags.P);
|
||||
if ((regs.B & 0x0f) == 0x00)
|
||||
regs.F |= CpuFlags.H;
|
||||
}
|
||||
else
|
||||
{
|
||||
// PF = PF ^ Parity((B + 1) & 0x7) ^ 1;
|
||||
// HF = (B & 0x0F) == 0x0F;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[(regs.B + 1) & 7] ^ CpuFlags.P);
|
||||
if ((regs.B & 0x0f) == 0x0f)
|
||||
regs.F |= CpuFlags.H;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// PF = PF ^ Parity(B & 0x7) ^ 1;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[regs.B & 7] ^ CpuFlags.P);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
regs.F = (byte)(CpuFlags.Z | /* ZF = 1; SF, YF, XF = 0 */
|
||||
hcf | /* HF, CF = T > 255 */
|
||||
pf | /* PF = ((T & 7) ^ Bo).parity */
|
||||
nf); /* NF = IO.7 */
|
||||
}
|
||||
}
|
||||
|
||||
private void ED_OTDR(byte cmd) //OTDR [16T/21T]
|
||||
{
|
||||
// B==0 => 16T (4, 5, 3, 4)
|
||||
// B!=0 => 21T (4, 5, 3, 4, 5)
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
var val = RDMEM(regs.HL--); Tact += 3;
|
||||
regs.MW = (ushort)(regs.BC - 1);
|
||||
|
||||
var nf = (byte)((val >> 6) & CpuFlags.N);
|
||||
var t = val + regs.L;
|
||||
var hcf = t > 255 ? CpuFlags.HC : 0;
|
||||
|
||||
regs.B--;
|
||||
var pf = CpuTables.Parity[(t & 7) ^ regs.B];
|
||||
|
||||
WRPORT(regs.BC, val); Tact += 3;
|
||||
Tact++;
|
||||
|
||||
if (regs.B != 0)
|
||||
{
|
||||
RDNOMREQ(regs.BC); Tact++;
|
||||
RDNOMREQ(regs.BC); Tact++;
|
||||
RDNOMREQ(regs.BC); Tact++;
|
||||
RDNOMREQ(regs.BC); Tact++;
|
||||
RDNOMREQ(regs.BC); Tact++;
|
||||
regs.PC -= 2;
|
||||
|
||||
regs.F = (byte)(
|
||||
/* ZF = 0 */
|
||||
(regs.B & CpuFlags.S) | /* SF = Bo.7 */
|
||||
(regs.PCH & CpuFlags.F3F5) |/* YF = PCi.13; XF = PCi.11 */
|
||||
nf); /* NF = IO.7 */
|
||||
if (hcf != 0)
|
||||
{
|
||||
regs.F |= CpuFlags.C;
|
||||
if ((val & 0x80) != 0)
|
||||
{
|
||||
// PF = PF ^ Parity((B - 1) & 0x7) ^ 1;
|
||||
// HF = (B & 0x0F) == 0x00;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[(regs.B - 1) & 7] ^ CpuFlags.P);
|
||||
if ((regs.B & 0x0f) == 0x00)
|
||||
regs.F |= CpuFlags.H;
|
||||
}
|
||||
else
|
||||
{
|
||||
// PF = PF ^ Parity((B + 1) & 0x7) ^ 1;
|
||||
// HF = (B & 0x0F) == 0x0F;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[(regs.B + 1) & 7] ^ CpuFlags.P);
|
||||
if ((regs.B & 0x0f) == 0x0f)
|
||||
regs.F |= CpuFlags.H;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// PF = PF ^ Parity(B & 0x7) ^ 1;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[regs.B & 7] ^ CpuFlags.P);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
regs.F = (byte)(CpuFlags.Z | /* ZF = 1; SF, YF, XF = 0 */
|
||||
hcf | /* HF, CF = T > 255 */
|
||||
pf | /* PF = ((T & 7) ^ Bo).parity */
|
||||
nf); /* NF = IO.7 */
|
||||
}
|
||||
}
|
||||
|
||||
private void ED_INRC(byte cmd) // in R,(c) [12T]
|
||||
{
|
||||
// 12T (4, 4, 4)
|
||||
var r = (cmd & 0x38) >> 3;
|
||||
|
||||
regs.MW = regs.BC;
|
||||
var pval = RDPORT(regs.BC);
|
||||
regs.MW++;
|
||||
if (r != CpuRegId.F)
|
||||
_regSetters[r](pval);
|
||||
regs.F = (byte)(CpuTables.Logf[pval] | (regs.F & CpuFlags.C));
|
||||
Tact += 4;
|
||||
}
|
||||
|
||||
private void ED_OUTCR_NMOS(byte cmd) // out (c),R [12T]
|
||||
{
|
||||
// 12T (4, 4, 4)
|
||||
var r = (cmd & 0x38) >> 3;
|
||||
|
||||
regs.MW = regs.BC;
|
||||
if (r != CpuRegId.F)
|
||||
WRPORT(regs.BC, _regGetters[r]());
|
||||
else
|
||||
WRPORT(regs.BC, 0x00); // 0 for Z80 and 0xFF for Z84
|
||||
regs.MW++;
|
||||
Tact += 4;
|
||||
}
|
||||
|
||||
private void ED_OUTCR_CMOS(byte cmd) // out (c),R [12T]
|
||||
{
|
||||
// 12T (4, 4, 4)
|
||||
var r = (cmd & 0x38) >> 3;
|
||||
|
||||
regs.MW = regs.BC;
|
||||
if (r != CpuRegId.F)
|
||||
WRPORT(regs.BC, _regGetters[r]());
|
||||
else
|
||||
WRPORT(regs.BC, 0xFF); // 0 for Z80 and 0xFF for Z84
|
||||
regs.MW++;
|
||||
Tact += 4;
|
||||
}
|
||||
|
||||
private void ED_ADCHLRR(byte cmd) // adc hl,RR
|
||||
{
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
regs.MW = (ushort)(regs.HL + 1);
|
||||
byte fl = (byte)((((regs.HL & 0x0FFF) + (_pairGetters[rr]() & 0x0FFF) + (regs.F & CpuFlags.C)) >> 8) & CpuFlags.H);
|
||||
uint tmp = (uint)((regs.HL & 0xFFFF) + (_pairGetters[rr]() & 0xFFFF) + (regs.F & CpuFlags.C)); // AF???
|
||||
if ((tmp & 0x10000) != 0) fl |= CpuFlags.C;
|
||||
if ((tmp & 0xFFFF) == 0) fl |= CpuFlags.Z;
|
||||
int ri = (int)(short)regs.HL + (int)(short)_pairGetters[rr]() + (int)(regs.F & CpuFlags.C);
|
||||
if (ri < -0x8000 || ri >= 0x8000) fl |= CpuFlags.P;
|
||||
regs.HL = (ushort)tmp;
|
||||
regs.F = (byte)(fl | (regs.H & CpuFlags.SF3F5));
|
||||
}
|
||||
|
||||
private void ED_SBCHLRR(byte cmd) // sbc hl,RR
|
||||
{
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
RDNOMREQ(regs.IR); Tact += 1;
|
||||
RDNOMREQ(regs.IR); Tact += 1;
|
||||
RDNOMREQ(regs.IR); Tact += 1;
|
||||
RDNOMREQ(regs.IR); Tact += 1;
|
||||
RDNOMREQ(regs.IR); Tact += 1;
|
||||
RDNOMREQ(regs.IR); Tact += 1;
|
||||
RDNOMREQ(regs.IR); Tact += 1;
|
||||
|
||||
regs.MW = (ushort)(regs.HL + 1);
|
||||
byte fl = CpuFlags.N;
|
||||
fl |= (byte)((((regs.HL & 0x0FFF) - (_pairGetters[rr]() & 0x0FFF) - (regs.F & CpuFlags.C)) >> 8) & CpuFlags.H);
|
||||
uint tmp = (uint)((regs.HL & 0xFFFF) - (_pairGetters[rr]() & 0xFFFF) - (regs.F & CpuFlags.C)); // AF???
|
||||
if ((tmp & 0x10000) != 0) fl |= CpuFlags.C;
|
||||
if ((tmp & 0xFFFF) == 0) fl |= CpuFlags.Z;
|
||||
int ri = (int)(short)regs.HL - (int)(short)_pairGetters[rr]() - (int)(regs.F & CpuFlags.C);
|
||||
if (ri < -0x8000 || ri >= 0x8000) fl |= CpuFlags.P;
|
||||
regs.HL = (ushort)tmp;
|
||||
regs.F = (byte)(fl | (regs.H & CpuFlags.SF3F5));
|
||||
}
|
||||
|
||||
private void ED_LDRR_NN_(byte cmd) // ld RR,(NN)
|
||||
{
|
||||
// 20T (4, 4, 3, 3, 3, 3)
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
var adr = (ushort)RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
adr += (ushort)(RDMEM(regs.PC) * 0x100);
|
||||
regs.PC++;
|
||||
regs.MW = (ushort)(adr + 1);
|
||||
Tact += 3;
|
||||
|
||||
var val = (ushort)RDMEM(adr);
|
||||
Tact += 3;
|
||||
|
||||
val += (ushort)(RDMEM(regs.MW) * 0x100);
|
||||
_pairSetters[rr](val);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void ED_LD_NN_RR(byte cmd) // ld (NN),RR
|
||||
{
|
||||
// 20 (4, 4, 3, 3, 3, 3)
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
var adr = (ushort)RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
adr += (ushort)(RDMEM(regs.PC) * 0x100);
|
||||
regs.PC++;
|
||||
regs.MW = (ushort)(adr + 1);
|
||||
var val = _pairGetters[rr]();
|
||||
Tact += 3;
|
||||
|
||||
WRMEM(adr, (byte)val);
|
||||
Tact += 3;
|
||||
|
||||
WRMEM(regs.MW, (byte)(val >> 8));
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void ED_RETN(byte cmd) // reti/retn
|
||||
{
|
||||
// 14T (4, 4, 3, 3)
|
||||
|
||||
IFF1 = IFF2;
|
||||
var adr = (ushort)RDMEM(regs.SP);
|
||||
Tact += 3;
|
||||
|
||||
adr += (ushort)(RDMEM(++regs.SP) * 0x100);
|
||||
++regs.SP;
|
||||
regs.PC = adr;
|
||||
regs.MW = adr;
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void ED_IM(byte cmd) // im X
|
||||
{
|
||||
var mode = (byte)((cmd & 0x18) >> 3);
|
||||
if (mode < 2) mode = 1;
|
||||
mode--;
|
||||
|
||||
IM = mode;
|
||||
}
|
||||
|
||||
private void ED_LDXRA(byte cmd) // ld I/R,a
|
||||
{
|
||||
var ir = (cmd & 0x08) == 0;
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
if (ir) // I
|
||||
regs.I = regs.A;
|
||||
else
|
||||
regs.R = regs.A;
|
||||
}
|
||||
|
||||
private void ED_LDAXR(byte cmd) // ld a,I/R
|
||||
{
|
||||
var ir = (cmd & 0x08) == 0;
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
if (ir) // I
|
||||
regs.A = regs.I;
|
||||
else
|
||||
regs.A = regs.R;
|
||||
|
||||
regs.F = (byte)(((regs.F & CpuFlags.C) | CpuTables.Logf[regs.A]) & CpuFlags.NotP);
|
||||
switch (_type)
|
||||
{
|
||||
case CpuType.ZILOG_NMOS:
|
||||
case CpuType.NEC_NMOS:
|
||||
case CpuType.ST_CMOS:
|
||||
SCANSIG?.Invoke();
|
||||
if (!(INT && IFF1) && IFF2)
|
||||
{
|
||||
regs.F |= CpuFlags.P;
|
||||
}
|
||||
break;
|
||||
case CpuType.ZILOG_CMOS:
|
||||
if (IFF2)
|
||||
{
|
||||
regs.F |= CpuFlags.P;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private void ED_RRD(byte cmd) // RRD
|
||||
{
|
||||
// 18T (4, 4, 3, 4, 3)
|
||||
|
||||
var tmp = RDMEM(regs.HL); Tact += 3;
|
||||
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
|
||||
regs.MW = (ushort)(regs.HL + 1);
|
||||
var val = (byte)((regs.A << 4) | (tmp >> 4));
|
||||
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
regs.A = (byte)((regs.A & 0xF0) | (tmp & 0x0F));
|
||||
regs.F = (byte)(CpuTables.Logf[regs.A] | (regs.F & CpuFlags.C));
|
||||
}
|
||||
|
||||
private void ED_RLD(byte cmd) // RLD
|
||||
{
|
||||
// 18T (4, 4, 3, 4, 3)
|
||||
|
||||
var tmp = RDMEM(regs.HL); Tact += 3;
|
||||
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
|
||||
regs.MW = (ushort)(regs.HL + 1);
|
||||
var val = (byte)((regs.A & 0x0F) | (tmp << 4));
|
||||
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
regs.A = (byte)((regs.A & 0xF0) | (tmp >> 4));
|
||||
regs.F = (byte)(CpuTables.Logf[regs.A] | (regs.F & CpuFlags.C));
|
||||
}
|
||||
|
||||
private void ED_NEG(byte cmd) // NEG
|
||||
{
|
||||
regs.F = CpuTables.Sbcf[regs.A];
|
||||
regs.A = (byte)-regs.A;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Description: Z80 CPU Emulator [DD/FD prefixed opcode part]
|
||||
* Date: 13.04.2007
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu.Processor
|
||||
{
|
||||
public partial class Z80Cpu
|
||||
{
|
||||
#region FXxx ops...
|
||||
|
||||
private void FX_LDSPHL(byte cmd) // LD SP,IX
|
||||
{
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.SP = regs.IX;
|
||||
else
|
||||
regs.SP = regs.IY;
|
||||
}
|
||||
|
||||
private void FX_EX_SP_HL(byte cmd) // EX (SP),IX
|
||||
{
|
||||
// 23T (4, 4, 3, 4, 3, 5)
|
||||
|
||||
var tmpsp = regs.SP;
|
||||
regs.MW = RDMEM(tmpsp); Tact += 3;
|
||||
tmpsp++;
|
||||
|
||||
regs.MW += (ushort)(RDMEM(tmpsp) * 0x100); Tact += 3;
|
||||
RDNOMREQ(tmpsp); Tact++;
|
||||
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
{
|
||||
WRMEM(tmpsp, regs.XH); Tact += 3;
|
||||
tmpsp--;
|
||||
|
||||
WRMEM(tmpsp, regs.XL); Tact += 3;
|
||||
WRNOMREQ(tmpsp); Tact++;
|
||||
WRNOMREQ(tmpsp); Tact++;
|
||||
regs.IX = regs.MW;
|
||||
}
|
||||
else
|
||||
{
|
||||
WRMEM(tmpsp, regs.YH); Tact += 3;
|
||||
tmpsp--;
|
||||
|
||||
WRMEM(tmpsp, regs.YL); Tact += 3;
|
||||
WRNOMREQ(tmpsp); Tact++;
|
||||
WRNOMREQ(tmpsp); Tact++;
|
||||
regs.IY = regs.MW;
|
||||
}
|
||||
}
|
||||
|
||||
private void FX_JP_HL_(byte cmd) // JP (IX)
|
||||
{
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.PC = regs.IX;
|
||||
else
|
||||
regs.PC = regs.IY;
|
||||
}
|
||||
|
||||
private void FX_PUSHIX(byte cmd) // PUSH IX
|
||||
{
|
||||
// 15 (4, 5, 3, 3)
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
var val = FX == CpuModeIndex.Ix ? regs.IX : regs.IY;
|
||||
regs.SP--;
|
||||
|
||||
WRMEM(regs.SP, (byte)(val >> 8)); Tact += 3;
|
||||
regs.SP--;
|
||||
|
||||
WRMEM(regs.SP, (byte)val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_POPIX(byte cmd) // POP IX
|
||||
{
|
||||
// 14T (4, 4, 3, 3)
|
||||
|
||||
var val = (ushort)RDMEM(regs.SP);
|
||||
regs.SP++;
|
||||
Tact += 3;
|
||||
|
||||
val |= (ushort)(RDMEM(regs.SP) << 8);
|
||||
regs.SP++;
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.IX = val;
|
||||
else
|
||||
regs.IY = val;
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_ALUAXH(byte cmd) // ADD/ADC/SUB/SBC/AND/XOR/OR/CP A,XH
|
||||
{
|
||||
byte val;
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
val = (byte)(regs.IX >> 8);
|
||||
else
|
||||
val = (byte)(regs.IY >> 8);
|
||||
_alualg[(cmd & 0x38) >> 3](val);
|
||||
}
|
||||
|
||||
private void FX_ALUAXL(byte cmd) // ADD/ADC/SUB/SBC/AND/XOR/OR/CP A,XL
|
||||
{
|
||||
byte val;
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
val = (byte)regs.IX;
|
||||
else
|
||||
val = (byte)regs.IY;
|
||||
_alualg[(cmd & 0x38) >> 3](val);
|
||||
}
|
||||
|
||||
private void FX_ALUA_IX_(byte cmd) // ADD/ADC/SUB/SBC/AND/XOR/OR/CP A,(IX)
|
||||
{
|
||||
// 19T (4, 4, 3, 5, 3)
|
||||
var op = (cmd & 0x38) >> 3;
|
||||
|
||||
int drel = (sbyte)RDMEM(regs.PC); Tact += 3;
|
||||
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
|
||||
regs.PC++;
|
||||
regs.MW = FX == CpuModeIndex.Ix ? regs.IX : regs.IY;
|
||||
regs.MW = (ushort)(regs.MW + drel);
|
||||
|
||||
var val = RDMEM(regs.MW); Tact += 3;
|
||||
_alualg[op](val);
|
||||
}
|
||||
|
||||
private void FX_ADDIXRR(byte cmd) // ADD IX,RR
|
||||
{
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
var rde = rr switch
|
||||
{
|
||||
0 => regs.BC,
|
||||
1 => regs.DE,
|
||||
2 => FX == CpuModeIndex.Ix ? regs.IX : regs.IY,
|
||||
3 => regs.SP,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(cmd), "(cmd & 0x30) >> 4 is out of range"),
|
||||
};
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
{
|
||||
regs.MW = (ushort)(regs.IX + 1);
|
||||
regs.IX = ALU_ADDHLRR(regs.IX, rde);
|
||||
}
|
||||
else
|
||||
{
|
||||
regs.MW = (ushort)(regs.IY + 1);
|
||||
regs.IY = ALU_ADDHLRR(regs.IY, rde);
|
||||
}
|
||||
}
|
||||
|
||||
private void FX_DECIX(byte cmd) // DEC IX
|
||||
{
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.IX--;
|
||||
else
|
||||
regs.IY--;
|
||||
}
|
||||
|
||||
private void FX_INCIX(byte cmd) // INC IX
|
||||
{
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.IX++;
|
||||
else
|
||||
regs.IY++;
|
||||
}
|
||||
|
||||
private void FX_LDIX_N_(byte cmd) // LD IX,(nnnn)
|
||||
{
|
||||
// 20 (4, 4, 3, 3, 3, 3)
|
||||
|
||||
var adr = (ushort)RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
adr += (ushort)(RDMEM(regs.PC) * 0x100);
|
||||
regs.PC++;
|
||||
regs.MW = (ushort)(adr + 1);
|
||||
Tact += 3;
|
||||
|
||||
var val = (ushort)RDMEM(adr);
|
||||
Tact += 3;
|
||||
|
||||
val += (ushort)(RDMEM(regs.MW) * 0x100);
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.IX = val;
|
||||
else
|
||||
regs.IY = val;
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_LD_NN_IX(byte cmd) // LD (nnnn),IX
|
||||
{
|
||||
// 20 (4, 4, 3, 3, 3, 3)
|
||||
|
||||
var hl = FX == CpuModeIndex.Ix ? regs.IX : regs.IY;
|
||||
var adr = (ushort)RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
adr += (ushort)(RDMEM(regs.PC) * 0x100);
|
||||
regs.PC++;
|
||||
regs.MW = (ushort)(adr + 1);
|
||||
Tact += 3;
|
||||
|
||||
WRMEM(adr, (byte)hl);
|
||||
Tact += 3;
|
||||
|
||||
WRMEM(regs.MW, (byte)(hl >> 8));
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_LDIXNNNN(byte cmd) // LD IX,nnnn
|
||||
{
|
||||
// 14 (4, 4, 3, 3)
|
||||
|
||||
var val = (ushort)RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
val |= (ushort)(RDMEM(regs.PC) << 8);
|
||||
regs.PC++;
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.IX = val;
|
||||
else
|
||||
regs.IY = val;
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_DEC_IX_(byte cmd) // DEC (IX)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
|
||||
int drel = (sbyte)RDMEM(regs.PC); Tact += 3;
|
||||
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
|
||||
regs.PC++;
|
||||
regs.MW = FX == CpuModeIndex.Ix ? regs.IX : regs.IY;
|
||||
regs.MW = (ushort)(regs.MW + drel);
|
||||
|
||||
var val = RDMEM(regs.MW); Tact += 3;
|
||||
RDNOMREQ(regs.MW); Tact++;
|
||||
val = ALU_DECR(val);
|
||||
WRMEM(regs.MW, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_INC_IX_(byte cmd) // INC (IX)
|
||||
{
|
||||
//23T (4, 4, 3, 5, 4, 3)
|
||||
|
||||
int drel = (sbyte)RDMEM(regs.PC); Tact += 3;
|
||||
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
|
||||
regs.PC++;
|
||||
regs.MW = FX == CpuModeIndex.Ix ? regs.IX : regs.IY;
|
||||
regs.MW = (ushort)(regs.MW + drel);
|
||||
|
||||
var val = RDMEM(regs.MW); Tact += 3;
|
||||
RDNOMREQ(regs.MW); Tact++;
|
||||
val = ALU_INCR(val);
|
||||
WRMEM(regs.MW, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_LD_IX_NN(byte cmd) // LD (IX),nn
|
||||
{
|
||||
// 19 (4, 4, 3, 5, 3)
|
||||
|
||||
int drel = (sbyte)RDMEM(regs.PC); Tact += 3;
|
||||
regs.PC++;
|
||||
|
||||
var val = RDMEM(regs.PC); Tact += 3;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
regs.PC++;
|
||||
regs.MW = FX == CpuModeIndex.Ix ? regs.IX : regs.IY;
|
||||
regs.MW = (ushort)(regs.MW + drel);
|
||||
WRMEM(regs.MW, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_LD_IX_R(byte cmd) // LD (IX),R
|
||||
{
|
||||
// 19T (4, 4, 3, 5, 3)
|
||||
var r = cmd & 0x07;
|
||||
|
||||
int drel = (sbyte)RDMEM(regs.PC); Tact += 3;
|
||||
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
|
||||
regs.PC++;
|
||||
regs.MW = FX == CpuModeIndex.Ix ? regs.IX : regs.IY;
|
||||
regs.MW = (ushort)(regs.MW + drel);
|
||||
WRMEM(regs.MW, _regGetters[r]()); Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_LDR_IX_(byte cmd) // LD R,(IX)
|
||||
{
|
||||
// 19T (4, 4, 3, 5, 3)
|
||||
var r = (cmd & 0x38) >> 3;
|
||||
|
||||
int drel = (sbyte)RDMEM(regs.PC); Tact += 3;
|
||||
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
|
||||
regs.PC++;
|
||||
regs.MW = FX == CpuModeIndex.Ix ? regs.IX : regs.IY;
|
||||
regs.MW = (ushort)(regs.MW + drel);
|
||||
|
||||
_regSetters[r](RDMEM(regs.MW)); Tact += 3;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region RdRs...
|
||||
|
||||
private void FX_LDHL(byte cmd)
|
||||
{
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.XH = regs.XL;
|
||||
else
|
||||
regs.YH = regs.YL;
|
||||
}
|
||||
|
||||
private void FX_LDLH(byte cmd)
|
||||
{
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.XL = regs.XH;
|
||||
else
|
||||
regs.YL = regs.YH;
|
||||
}
|
||||
|
||||
private void FX_LDRL(byte cmd)
|
||||
{
|
||||
var r = (cmd & 0x38) >> 3;
|
||||
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
_regSetters[r](regs.XL);
|
||||
else
|
||||
_regSetters[r](regs.YL);
|
||||
}
|
||||
|
||||
private void FX_LDRH(byte cmd)
|
||||
{
|
||||
var r = (cmd & 0x38) >> 3;
|
||||
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
_regSetters[r](regs.XH);
|
||||
else
|
||||
_regSetters[r](regs.YH);
|
||||
}
|
||||
|
||||
private void FX_LDLR(byte cmd)
|
||||
{
|
||||
var r = cmd & 0x07;
|
||||
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.XL = _regGetters[r]();
|
||||
else
|
||||
regs.YL = _regGetters[r]();
|
||||
}
|
||||
|
||||
private void FX_LDHR(byte cmd)
|
||||
{
|
||||
var r = cmd & 0x07;
|
||||
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.XH = _regGetters[r]();
|
||||
else
|
||||
regs.YH = _regGetters[r]();
|
||||
}
|
||||
|
||||
private void FX_LDLNN(byte cmd) // LD XL,nn
|
||||
{
|
||||
// 11T (4, 4, 3)
|
||||
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.XL = RDMEM(regs.PC);
|
||||
else
|
||||
regs.YL = RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_LDHNN(byte cmd) // LD XH,nn
|
||||
{
|
||||
// 11T (4, 4, 3)
|
||||
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.XH = RDMEM(regs.PC);
|
||||
else
|
||||
regs.YH = RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_INCL(byte cmd) // INC XL
|
||||
{
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.XL = ALU_INCR(regs.XL);
|
||||
else
|
||||
regs.YL = ALU_INCR(regs.YL);
|
||||
}
|
||||
|
||||
private void FX_INCH(byte cmd) // INC XH
|
||||
{
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.XH = ALU_INCR(regs.XH);
|
||||
else
|
||||
regs.YH = ALU_INCR(regs.YH);
|
||||
}
|
||||
|
||||
private void FX_DECL(byte cmd) // DEC XL
|
||||
{
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.XL = ALU_DECR(regs.XL);
|
||||
else
|
||||
regs.YL = ALU_DECR(regs.YL);
|
||||
}
|
||||
|
||||
private void FX_DECH(byte cmd) // DEC XH
|
||||
{
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.XH = ALU_DECR(regs.XH);
|
||||
else
|
||||
regs.YH = ALU_DECR(regs.YH);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright 2007, 2011, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Description: Z80 CPU Emulator [FXCB prefixed opcodes part]
|
||||
* Date: 25.09.2011
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu.Processor
|
||||
{
|
||||
public partial class Z80Cpu
|
||||
{
|
||||
private void FXCB_RLC(byte cmd, ushort adr) // *RLC r
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var r = cmd & 7;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_RLC(val);
|
||||
_regSetters[r](val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_RRC(byte cmd, ushort adr) // *RRC r
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var r = cmd & 7;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_RRC(val);
|
||||
_regSetters[r](val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_RL(byte cmd, ushort adr) // *RL r
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var r = cmd & 7;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_RL(val);
|
||||
_regSetters[r](val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_RR(byte cmd, ushort adr) // *RR r
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var r = cmd & 7;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_RR(val);
|
||||
_regSetters[r](val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_SLA(byte cmd, ushort adr) // *SLA r
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var r = cmd & 7;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_SLA(val);
|
||||
_regSetters[r](val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_SRA(byte cmd, ushort adr) // *SRA r
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var r = cmd & 7;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_SRA(val);
|
||||
_regSetters[r](val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_SLL(byte cmd, ushort adr) // **SLL r
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var r = cmd & 7;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_SLL(val);
|
||||
_regSetters[r](val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_SRL(byte cmd, ushort adr) // *SRL r
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var r = cmd & 7;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_SRL(val);
|
||||
_regSetters[r](val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_RES(byte cmd, ushort adr) // *RES b,r,(IX+drel)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var m = (byte)~(1 << ((cmd & 0x38) >> 3));
|
||||
var r = cmd & 7;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val &= m;
|
||||
_regSetters[r](val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_SET(byte cmd, ushort adr) // *SET b,r,(IX+drel)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var m = (byte)(1 << ((cmd & 0x38) >> 3));
|
||||
var r = cmd & 7;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val |= m;
|
||||
_regSetters[r](val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_RLCIX(byte cmd, ushort adr) // RLC (IX+) [23T]
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_RLC(val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_RRCIX(byte cmd, ushort adr) // RRC (IX+)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_RRC(val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_RLIX(byte cmd, ushort adr) // RL (IX+)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_RL(val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_RRIX(byte cmd, ushort adr) // RR (IX+)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_RR(val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_SLAIX(byte cmd, ushort adr) // SLA (IX+)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_SLA(val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_SRAIX(byte cmd, ushort adr) // SRA (IX+)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_SRA(val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_SLLIX(byte cmd, ushort adr) // *SLL (IX+)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_SLL(val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_SRLIX(byte cmd, ushort adr) // SRL (IX+)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_SRL(val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_BITIX(byte cmd, ushort adr) // BIT (IX+)
|
||||
{
|
||||
// 20T (4, 4, 3, 5, 4)
|
||||
var b = (cmd & 0x38) >> 3;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
ALU_BITMEM(val, b);
|
||||
}
|
||||
|
||||
private void FXCB_RESIX(byte cmd, ushort adr) // RES (IX+)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var m = (byte)~(1 << ((cmd & 0x38) >> 3));
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val &= m;
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_SETIX(byte cmd, ushort adr) // SET (IX+)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var m = (byte)(1 << ((cmd & 0x38) >> 3));
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val |= m;
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu.Processor
|
||||
{
|
||||
public partial class Z80Cpu
|
||||
{
|
||||
private readonly Action<byte>[] _opcodes;
|
||||
private readonly Action<byte>[] _opcodesFx;
|
||||
private readonly Action<byte>[] _opcodesEd;
|
||||
private readonly Action<byte>[] _opcodesCb;
|
||||
private readonly Action<byte, ushort>[] _opcodesFxCb;
|
||||
private readonly Action<byte>[] _alualg;
|
||||
private readonly Func<ushort>[] _pairGetters;
|
||||
private readonly Action<ushort>[] _pairSetters;
|
||||
private readonly Func<byte>[] _regGetters;
|
||||
private readonly Action<byte>[] _regSetters;
|
||||
|
||||
|
||||
|
||||
private Action<byte>[] CreateOpcodes()
|
||||
{
|
||||
Action<byte> scf;
|
||||
Action<byte> ccf;
|
||||
switch (_type)
|
||||
{
|
||||
case CpuType.ZILOG_NMOS:
|
||||
case CpuType.ZILOG_CMOS:
|
||||
scf = SCF_ZILOG;
|
||||
ccf = CCF_ZILOG;
|
||||
break;
|
||||
case CpuType.NEC_NMOS:
|
||||
scf = SCF_NEC_NMOS;
|
||||
ccf = CCF_NEC_NMOS;
|
||||
break;
|
||||
case CpuType.ST_CMOS:
|
||||
scf = SCF_ST_CMOS;
|
||||
ccf = CCF_ST_CMOS;
|
||||
break;
|
||||
default: throw new NotImplementedException();
|
||||
}
|
||||
|
||||
var opcodes = new Action<byte>[256]
|
||||
{
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
null, LDRRNNNN, LD_RR_A, INCRR, INCR, DECR, LDRNN, RLCA, EXAFAF, ADDHLRR, LDA_RR_, DECRR, INCR, DECR, LDRNN, RRCA, // 00..0F
|
||||
DJNZ, LDRRNNNN, LD_RR_A, INCRR, INCR, DECR, LDRNN, RLA, JRNN, ADDHLRR, LDA_RR_, DECRR, INCR, DECR, LDRNN, RRA, // 10..1F
|
||||
JRXNN, LDRRNNNN, LD_NN_HL, INCRR, INCR, DECR, LDRNN, DAA, JRXNN, ADDHLRR, LDHL_NN_, DECRR, INCR, DECR, LDRNN, CPL, // 20..2F
|
||||
JRXNN, LDRRNNNN, LD_NN_A, INCRR, INC_HL_, DEC_HL_, LD_HL_NN, scf, JRXNN, ADDHLRR, LDA_NN_, DECRR, INCR, DECR, LDRNN, ccf, // 30..3F
|
||||
|
||||
null, LDRdRs, LDRdRs, LDRdRs, LDRdRs, LDRdRs, LDR_HL_, LDRdRs, LDRdRs, null, LDRdRs, LDRdRs, LDRdRs, LDRdRs, LDR_HL_, LDRdRs, // 40..4F
|
||||
LDRdRs, LDRdRs, null, LDRdRs, LDRdRs, LDRdRs, LDR_HL_, LDRdRs, LDRdRs, LDRdRs, LDRdRs, null, LDRdRs, LDRdRs, LDR_HL_, LDRdRs, // 50..5F
|
||||
LDRdRs, LDRdRs, LDRdRs, LDRdRs, null, LDRdRs, LDR_HL_, LDRdRs, LDRdRs, LDRdRs, LDRdRs, LDRdRs, LDRdRs, null, LDR_HL_, LDRdRs, // 60..6F
|
||||
LD_HL_R, LD_HL_R, LD_HL_R, LD_HL_R, LD_HL_R, LD_HL_R, HALT, LD_HL_R, LDRdRs, LDRdRs, LDRdRs, LDRdRs, LDRdRs, LDRdRs, LDR_HL_, null, // 70..7F
|
||||
|
||||
ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUA_HL_, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUA_HL_, ALUAR, // 80..8F
|
||||
ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUA_HL_, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUA_HL_, ALUAR, // 90..9F
|
||||
ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUA_HL_, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUA_HL_, ALUAR, // A0..AF
|
||||
ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUA_HL_, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUA_HL_, ALUAR, // B0..BF
|
||||
|
||||
RETX, POPRR, JPXNN, JPNNNN, CALLXNNNN, PUSHRR, ALUAN, RSTNN, RETX, RET, JPXNN, null, CALLXNNNN, CALLNNNN, ALUAN, RSTNN, // C0..CF
|
||||
RETX, POPRR, JPXNN, OUT_NN_A, CALLXNNNN, PUSHRR, ALUAN, RSTNN, RETX, EXX, JPXNN, INA_NN_, CALLXNNNN, null, ALUAN, RSTNN, // D0..DF
|
||||
RETX, POPRR, JPXNN, EX_SP_HL, CALLXNNNN, PUSHRR, ALUAN, RSTNN, RETX, JP_HL_, JPXNN, EXDEHL, CALLXNNNN, null, ALUAN, RSTNN, // E0..EF
|
||||
RETX, POPRR, JPXNN, DI, CALLXNNNN, PUSHRR, ALUAN, RSTNN, RETX, LDSPHL, JPXNN, EI, CALLXNNNN, null, ALUAN, RSTNN, // F0..FF
|
||||
};
|
||||
return opcodes;
|
||||
// patch opcodes with optimized version
|
||||
//for (var cmd = 0; cmd < 0x100; cmd++)
|
||||
//{
|
||||
// opcodes[cmd] =
|
||||
// EmitOpcodeLdRegReg(cmd) ??
|
||||
// EmitOpcodeJrXdisp(cmd) ??
|
||||
// opcodes[cmd];
|
||||
//}
|
||||
//return opcodes;
|
||||
}
|
||||
|
||||
private Action<byte>[] CreateOpcodesFx()
|
||||
{
|
||||
Action<byte> scf;
|
||||
Action<byte> ccf;
|
||||
switch (_type)
|
||||
{
|
||||
case CpuType.ZILOG_NMOS:
|
||||
case CpuType.ZILOG_CMOS:
|
||||
scf = SCF_ZILOG;
|
||||
ccf = CCF_ZILOG;
|
||||
break;
|
||||
case CpuType.NEC_NMOS:
|
||||
scf = SCF_NEC_NMOS;
|
||||
ccf = CCF_NEC_NMOS;
|
||||
break;
|
||||
case CpuType.ST_CMOS:
|
||||
scf = SCF_ST_CMOS;
|
||||
ccf = CCF_ST_CMOS;
|
||||
break;
|
||||
default: throw new NotImplementedException();
|
||||
}
|
||||
|
||||
return
|
||||
[
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
null, LDRRNNNN, LD_RR_A, INCRR, INCR, DECR, LDRNN, RLCA, EXAFAF, FX_ADDIXRR, LDA_RR_, DECRR, INCR, DECR, LDRNN, RRCA, // 00..0F
|
||||
DJNZ, LDRRNNNN, LD_RR_A, INCRR, INCR, DECR, LDRNN, RLA, JRNN, FX_ADDIXRR, LDA_RR_, DECRR, INCR, DECR, LDRNN, RRA, // 10..1F
|
||||
JRXNN, FX_LDIXNNNN, FX_LD_NN_IX, FX_INCIX, FX_INCH, FX_DECH, FX_LDHNN, DAA, JRXNN, FX_ADDIXRR, FX_LDIX_N_, FX_DECIX, FX_INCL, FX_DECL, FX_LDLNN, CPL, // 20..2F
|
||||
JRXNN, LDRRNNNN, LD_NN_A, INCRR, FX_INC_IX_, FX_DEC_IX_, FX_LD_IX_NN, scf, JRXNN, FX_ADDIXRR, LDA_NN_, DECRR, INCR, DECR, LDRNN, ccf, // 30..3F
|
||||
|
||||
null, LDRdRs, LDRdRs, LDRdRs, FX_LDRH, FX_LDRL, FX_LDR_IX_, LDRdRs, LDRdRs, null, LDRdRs, LDRdRs, FX_LDRH, FX_LDRL, FX_LDR_IX_, LDRdRs, // 40..4F
|
||||
LDRdRs, LDRdRs, null, LDRdRs, FX_LDRH, FX_LDRL, FX_LDR_IX_, LDRdRs, LDRdRs, LDRdRs, LDRdRs, null, FX_LDRH, FX_LDRL, FX_LDR_IX_, LDRdRs, // 50..5F
|
||||
FX_LDHR, FX_LDHR, FX_LDHR, FX_LDHR, null, FX_LDHL, FX_LDR_IX_, FX_LDHR, FX_LDLR, FX_LDLR, FX_LDLR, FX_LDLR, FX_LDLH, null, FX_LDR_IX_, FX_LDLR,// 60..6F
|
||||
FX_LD_IX_R, FX_LD_IX_R, FX_LD_IX_R, FX_LD_IX_R, FX_LD_IX_R, FX_LD_IX_R, HALT, FX_LD_IX_R, LDRdRs, LDRdRs, LDRdRs, LDRdRs, FX_LDRH, FX_LDRL, FX_LDR_IX_, null, // 70..7F
|
||||
|
||||
ALUAR, ALUAR, ALUAR, ALUAR, FX_ALUAXH, FX_ALUAXL, FX_ALUA_IX_, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, FX_ALUAXH, FX_ALUAXL, FX_ALUA_IX_, ALUAR, // 80..8F
|
||||
ALUAR, ALUAR, ALUAR, ALUAR, FX_ALUAXH, FX_ALUAXL, FX_ALUA_IX_, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, FX_ALUAXH, FX_ALUAXL, FX_ALUA_IX_, ALUAR, // 90..9F
|
||||
ALUAR, ALUAR, ALUAR, ALUAR, FX_ALUAXH, FX_ALUAXL, FX_ALUA_IX_, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, FX_ALUAXH, FX_ALUAXL, FX_ALUA_IX_, ALUAR, // A0..AF
|
||||
ALUAR, ALUAR, ALUAR, ALUAR, FX_ALUAXH, FX_ALUAXL, FX_ALUA_IX_, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, FX_ALUAXH, FX_ALUAXL, FX_ALUA_IX_, ALUAR, // B0..BF
|
||||
|
||||
RETX, POPRR, JPXNN, JPNNNN, CALLXNNNN, PUSHRR, ALUAN, RSTNN, RETX, RET, JPXNN, null, CALLXNNNN, CALLNNNN, ALUAN, RSTNN, // C0..CF
|
||||
RETX, POPRR, JPXNN, OUT_NN_A, CALLXNNNN, PUSHRR, ALUAN, RSTNN, RETX, EXX, JPXNN, INA_NN_, CALLXNNNN, null, ALUAN, RSTNN, // D0..DF
|
||||
RETX, FX_POPIX, JPXNN, FX_EX_SP_HL, CALLXNNNN, FX_PUSHIX, ALUAN, RSTNN, RETX, FX_JP_HL_, JPXNN, EXDEHL, CALLXNNNN, null, ALUAN, RSTNN, // E0..EF
|
||||
RETX, POPRR, JPXNN, DI, CALLXNNNN, PUSHRR, ALUAN, RSTNN, RETX, FX_LDSPHL, JPXNN, EI, CALLXNNNN, null, ALUAN, RSTNN, // F0..FF
|
||||
];
|
||||
}
|
||||
|
||||
private Action<byte>[] CreateOpcodesEd()
|
||||
{
|
||||
Action<byte> ed_outcr = _type switch
|
||||
{
|
||||
CpuType.ZILOG_NMOS or CpuType.NEC_NMOS => ED_OUTCR_NMOS,
|
||||
CpuType.ST_CMOS or CpuType.ZILOG_CMOS => ED_OUTCR_CMOS,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
return
|
||||
[
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, // 00..0F
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, // 10..1F
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, // 20..2F
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, // 30..3F
|
||||
ED_INRC, ed_outcr, ED_SBCHLRR, ED_LD_NN_RR, ED_NEG, ED_RETN, ED_IM, ED_LDXRA, ED_INRC, ed_outcr, ED_ADCHLRR, ED_LDRR_NN_, ED_NEG, ED_RETN, ED_IM, ED_LDXRA, // 40..4F
|
||||
ED_INRC, ed_outcr, ED_SBCHLRR, ED_LD_NN_RR, ED_NEG, ED_RETN, ED_IM, ED_LDAXR, ED_INRC, ed_outcr, ED_ADCHLRR, ED_LDRR_NN_, ED_NEG, ED_RETN, ED_IM, ED_LDAXR, // 50..5F
|
||||
ED_INRC, ed_outcr, ED_SBCHLRR, ED_LD_NN_RR, ED_NEG, ED_RETN, ED_IM, ED_RRD, ED_INRC, ed_outcr, ED_ADCHLRR, ED_LDRR_NN_, ED_NEG, ED_RETN, ED_IM, ED_RLD, // 60..6F
|
||||
ED_INRC, ed_outcr, ED_SBCHLRR, ED_LD_NN_RR, ED_NEG, ED_RETN, ED_IM, null, ED_INRC, ed_outcr, ED_ADCHLRR, ED_LDRR_NN_, ED_NEG, ED_RETN, ED_IM, null, // 70..7F
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, // 80..8F
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, // 90..9F
|
||||
ED_LDI, ED_CPI, ED_INI, ED_OUTI, null, null, null, null, ED_LDD, ED_CPD, ED_IND, ED_OUTD, null, null, null, null, // A0..AF
|
||||
ED_LDIR, ED_CPIR, ED_INIR, ED_OTIR, null, null, null, null, ED_LDDR, ED_CPDR, ED_INDR, ED_OTDR, null, null, null, null, // B0..BF
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, // C0..CF
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, // D0..DF
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, // E0..EF
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null // F0..FF
|
||||
];
|
||||
}
|
||||
|
||||
private Action<byte>[] CreateOpcodesCb()
|
||||
{
|
||||
return
|
||||
[
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
CB_RLC, CB_RLC, CB_RLC, CB_RLC, CB_RLC, CB_RLC, CB_RLCHL, CB_RLC, CB_RRC, CB_RRC, CB_RRC, CB_RRC, CB_RRC, CB_RRC, CB_RRCHL, CB_RRC, // 00..0F
|
||||
CB_RL, CB_RL, CB_RL, CB_RL, CB_RL, CB_RL, CB_RLHL, CB_RL, CB_RR, CB_RR, CB_RR, CB_RR, CB_RR, CB_RR, CB_RRHL, CB_RR, // 10..1F
|
||||
CB_SLA, CB_SLA, CB_SLA, CB_SLA, CB_SLA, CB_SLA, CB_SLAHL, CB_SLA, CB_SRA, CB_SRA, CB_SRA, CB_SRA, CB_SRA, CB_SRA, CB_SRAHL, CB_SRA, // 20..2F
|
||||
CB_SLL, CB_SLL, CB_SLL, CB_SLL, CB_SLL, CB_SLL, CB_SLLHL, CB_SLL, CB_SRL, CB_SRL, CB_SRL, CB_SRL, CB_SRL, CB_SRL, CB_SRLHL, CB_SRL, // 30..3F
|
||||
CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BITHL, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BITHL, CB_BIT, // 40..4F
|
||||
CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BITHL, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BITHL, CB_BIT, // 50..5F
|
||||
CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BITHL, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BITHL, CB_BIT, // 60..6F
|
||||
CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BITHL, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BITHL, CB_BIT, // 70..7F
|
||||
CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RESHL, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RESHL, CB_RES, // 80..8F
|
||||
CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RESHL, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RESHL, CB_RES, // 90..9F
|
||||
CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RESHL, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RESHL, CB_RES, // A0..AF
|
||||
CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RESHL, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RESHL, CB_RES, // B0..BF
|
||||
CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SETHL, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SETHL, CB_SET, // C0..CF
|
||||
CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SETHL, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SETHL, CB_SET, // D0..DF
|
||||
CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SETHL, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SETHL, CB_SET, // E0..EF
|
||||
CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SETHL, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SETHL, CB_SET, // F0..FF
|
||||
];
|
||||
}
|
||||
|
||||
private Action<byte, ushort>[] CreateOpcodesFxCb()
|
||||
{
|
||||
return
|
||||
[
|
||||
FXCB_RLC, FXCB_RLC, FXCB_RLC, FXCB_RLC, FXCB_RLC, FXCB_RLC, FXCB_RLCIX, FXCB_RLC, FXCB_RRC, FXCB_RRC, FXCB_RRC, FXCB_RRC, FXCB_RRC, FXCB_RRC, FXCB_RRCIX, FXCB_RRC, // 00..0F
|
||||
FXCB_RL, FXCB_RL, FXCB_RL, FXCB_RL, FXCB_RL, FXCB_RL, FXCB_RLIX, FXCB_RL, FXCB_RR, FXCB_RR, FXCB_RR, FXCB_RR, FXCB_RR, FXCB_RR, FXCB_RRIX, FXCB_RR, // 10..1F
|
||||
FXCB_SLA, FXCB_SLA, FXCB_SLA, FXCB_SLA, FXCB_SLA, FXCB_SLA, FXCB_SLAIX, FXCB_SLA, FXCB_SRA, FXCB_SRA, FXCB_SRA, FXCB_SRA, FXCB_SRA, FXCB_SRA, FXCB_SRAIX, FXCB_SRA, // 20..2F
|
||||
FXCB_SLL, FXCB_SLL, FXCB_SLL, FXCB_SLL, FXCB_SLL, FXCB_SLL, FXCB_SLLIX, FXCB_SLL, FXCB_SRL, FXCB_SRL, FXCB_SRL, FXCB_SRL, FXCB_SRL, FXCB_SRL, FXCB_SRLIX, FXCB_SRL, // 30..3F
|
||||
FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, // 40..4F
|
||||
FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, // 50..5F
|
||||
FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, // 60..6F
|
||||
FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, // 70..7F
|
||||
FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RESIX, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RESIX, FXCB_RES, // 80..8F
|
||||
FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RESIX, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RESIX, FXCB_RES, // 90..9F
|
||||
FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RESIX, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RESIX, FXCB_RES, // A0..AF
|
||||
FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RESIX, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RESIX, FXCB_RES, // B0..BF
|
||||
FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SETIX, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SETIX, FXCB_SET, // C0..CF
|
||||
FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SETIX, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SETIX, FXCB_SET, // D0..DF
|
||||
FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SETIX, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SETIX, FXCB_SET, // E0..EF
|
||||
FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SETIX, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SETIX, FXCB_SET, // F0..FF
|
||||
];
|
||||
}
|
||||
|
||||
private Action<byte>[] CreateAluAlg()
|
||||
{
|
||||
return
|
||||
[
|
||||
ALU_ADDR, ALU_ADCR, ALU_SUBR, ALU_SBCR,
|
||||
ALU_ANDR, ALU_XORR, ALU_ORR, ALU_CPR,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region Optimized emitters
|
||||
|
||||
//private Action<byte> EmitOpcodeLdRegReg(int cmd)
|
||||
//{
|
||||
// if (cmd < 0x40 || cmd >= 0x80)
|
||||
// {
|
||||
// return null;
|
||||
// }
|
||||
// var rsrc = cmd & 0x07;
|
||||
// var rdst = (cmd & 0x38) >> 3;
|
||||
// if (rsrc == rdst)
|
||||
// {
|
||||
// return rsrc == 6 ? HALT : (Action<byte>)null;
|
||||
// }
|
||||
// var getter = rsrc != 6 ? regs.CreateRegGetter(rsrc) :
|
||||
// new Func<byte>(() =>
|
||||
// {
|
||||
// // LD R,(HL)
|
||||
// // 7T (4, 3)
|
||||
// var value = RDMEM(regs.HL); Tact += 3;
|
||||
// return value;
|
||||
// });
|
||||
// var setter = rdst != 6 ? regs.CreateRegSetter(rdst) :
|
||||
// new Action<byte>(arg =>
|
||||
// {
|
||||
// // LD (HL),R
|
||||
// // 7T (4, 3)
|
||||
// WRMEM(regs.HL, arg); Tact += 3;
|
||||
// });
|
||||
// return arg => setter(getter());
|
||||
//}
|
||||
|
||||
//private Action<byte> EmitOpcodeJrXdisp(int cmd)
|
||||
//{
|
||||
// if (cmd < 0x20 || cmd >= 0x40 || (cmd & 7) != 0)
|
||||
// {
|
||||
// return null;
|
||||
// }
|
||||
// // JR x,disp
|
||||
// // false => 7T (4, 3)
|
||||
// // true => 12 (4, 3, 5)
|
||||
// var cond = (cmd & 0x18) >> 3;
|
||||
// var mask = s_conds[cond >> 1];
|
||||
// var xor = (cond & 1) == 0 ? -1 : 0;
|
||||
// return new Action<byte>(arg =>
|
||||
// {
|
||||
// int drel = (sbyte)RDMEM(regs.PC); Tact += 3;
|
||||
// regs.PC++;
|
||||
// var f = (regs.AF ^ xor) & mask;
|
||||
// if (f == 0)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// RDNOMREQ(regs.PC); Tact++;
|
||||
// RDNOMREQ(regs.PC); Tact++;
|
||||
// RDNOMREQ(regs.PC); Tact++;
|
||||
// RDNOMREQ(regs.PC); Tact++;
|
||||
// RDNOMREQ(regs.PC); Tact++;
|
||||
// regs.MW = (ushort)(regs.PC + drel);
|
||||
// regs.PC = regs.MW;
|
||||
// });
|
||||
//}
|
||||
|
||||
#endregion Optimized emitters
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Description: Z80 CPU Emulator
|
||||
* Date: 13.04.2007
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu.Processor
|
||||
{
|
||||
public partial class Z80Cpu
|
||||
{
|
||||
public readonly CpuRegs regs = new();
|
||||
public int RzxCounter;
|
||||
public long Tact;
|
||||
public bool HALTED;
|
||||
public bool IFF1;
|
||||
public bool IFF2;
|
||||
public byte IM;
|
||||
public bool BINT; // last opcode was EI or DD/FD prefix (to prevent INT handling)
|
||||
public CpuModeIndex FX;
|
||||
public CpuModeEx XFX;
|
||||
public ushort LPC; // last opcode PC
|
||||
|
||||
public bool INT;
|
||||
public bool NMI;
|
||||
public bool RST;
|
||||
public byte BUS = 0xFF; // state of free data bus
|
||||
|
||||
public Action RESET;
|
||||
public Action NMIACK_M1;
|
||||
public Action INTACK_M1;
|
||||
public Func<ushort, byte> RDMEM_M1;
|
||||
public Func<ushort, byte> RDMEM;
|
||||
public Action<ushort, byte> WRMEM;
|
||||
public Func<ushort, byte> RDPORT;
|
||||
public Action<ushort, byte> WRPORT;
|
||||
public Action<ushort> RDNOMREQ;
|
||||
public Action<ushort> WRNOMREQ;
|
||||
public Action SCANSIG;
|
||||
|
||||
private CpuType _type = CpuType.NEC_NMOS;
|
||||
public CpuType Type
|
||||
{
|
||||
get { return _type; }
|
||||
set { _type = value; InitOpcodeTables(); }
|
||||
}
|
||||
|
||||
public Z80Cpu()
|
||||
{
|
||||
_pairGetters = [.. Enumerable.Range(0, 4).Select(regs.CreatePairGetter)];
|
||||
_pairSetters = [.. Enumerable.Range(0, 4).Select(regs.CreatePairSetter)];
|
||||
_regGetters = [.. Enumerable.Range(0, 8).Select(regs.CreateRegGetter)];
|
||||
_regSetters = [.. Enumerable.Range(0, 8).Select(regs.CreateRegSetter)];
|
||||
_alualg = CreateAluAlg();
|
||||
|
||||
_opcodes = new Action<byte>[256];
|
||||
_opcodesFx = new Action<byte>[256];
|
||||
_opcodesEd = new Action<byte>[256];
|
||||
_opcodesCb = new Action<byte>[256];
|
||||
_opcodesFxCb = new Action<byte, ushort>[256];
|
||||
InitOpcodeTables();
|
||||
|
||||
regs.AF = 0xFF;
|
||||
regs.BC = 0xFF;
|
||||
regs.DE = 0xFF;
|
||||
regs.HL = 0xFF;
|
||||
regs._AF = 0xFF;
|
||||
regs._BC = 0xFF;
|
||||
regs._DE = 0xFF;
|
||||
regs._HL = 0xFF;
|
||||
regs.IX = 0xFF;
|
||||
regs.IY = 0xFF;
|
||||
regs.IR = 0xFF;
|
||||
regs.PC = 0xFF;
|
||||
regs.SP = 0xFF;
|
||||
regs.MW = 0xFF;
|
||||
}
|
||||
|
||||
private void InitOpcodeTables()
|
||||
{
|
||||
InitOpcodeTable(_opcodes, CreateOpcodes());
|
||||
InitOpcodeTable(_opcodesFx, CreateOpcodesFx());
|
||||
InitOpcodeTable(_opcodesEd, CreateOpcodesEd());
|
||||
InitOpcodeTable(_opcodesCb, CreateOpcodesCb());
|
||||
InitOpcodeTable(_opcodesFxCb, CreateOpcodesFxCb());
|
||||
}
|
||||
|
||||
private static void InitOpcodeTable<T>(T[] opcodes, T[] actions)
|
||||
{
|
||||
if (opcodes.Length != actions.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(opcodes));
|
||||
|
||||
for (var i = 0; i < opcodes.Length; i++)
|
||||
{
|
||||
opcodes[i] = actions[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void ExecCycle()
|
||||
{
|
||||
byte cmd = 0;
|
||||
if (XFX == CpuModeEx.None && FX == CpuModeIndex.None)
|
||||
{
|
||||
if (ProcessSignals())
|
||||
return;
|
||||
LPC = regs.PC;
|
||||
cmd = RDMEM_M1(LPC);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ProcessSignals())
|
||||
return;
|
||||
cmd = RDMEM(regs.PC);
|
||||
}
|
||||
Tact += 3;
|
||||
regs.PC++;
|
||||
if (XFX == CpuModeEx.Cb)
|
||||
{
|
||||
BINT = false;
|
||||
if (FX != CpuModeIndex.None)
|
||||
{
|
||||
// elapsed T: 4, 4, 3
|
||||
// will be T: 4, 4, 3, 5
|
||||
int drel = (sbyte)cmd;
|
||||
|
||||
regs.MW = FX == CpuModeIndex.Ix ? (ushort)(regs.IX + drel) : (ushort)(regs.IY + drel);
|
||||
cmd = RDMEM(regs.PC); Tact += 3;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
|
||||
regs.PC++;
|
||||
_opcodesFxCb[cmd](cmd, regs.MW);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Refresh();
|
||||
regs.R = (byte)(((regs.R + 1) & 0x7F) | (regs.R & 0x80));
|
||||
Tact += 1;
|
||||
RzxCounter++;
|
||||
|
||||
_opcodesCb[cmd](cmd);
|
||||
}
|
||||
XFX = CpuModeEx.None;
|
||||
FX = CpuModeIndex.None;
|
||||
}
|
||||
else if (XFX == CpuModeEx.Ed)
|
||||
{
|
||||
//Refresh();
|
||||
regs.R = (byte)(((regs.R + 1) & 0x7F) | (regs.R & 0x80));
|
||||
Tact += 1;
|
||||
RzxCounter++;
|
||||
|
||||
BINT = false;
|
||||
_opcodesEd[cmd]?.Invoke(cmd);
|
||||
|
||||
XFX = CpuModeEx.None;
|
||||
FX = CpuModeIndex.None;
|
||||
}
|
||||
else if (cmd == 0xDD)
|
||||
{
|
||||
//Refresh();
|
||||
regs.R = (byte)(((regs.R + 1) & 0x7F) | (regs.R & 0x80));
|
||||
Tact += 1;
|
||||
RzxCounter++;
|
||||
|
||||
FX = CpuModeIndex.Ix;
|
||||
BINT = true;
|
||||
}
|
||||
else if (cmd == 0xFD)
|
||||
{
|
||||
//Refresh();
|
||||
regs.R = (byte)(((regs.R + 1) & 0x7F) | (regs.R & 0x80));
|
||||
Tact += 1;
|
||||
RzxCounter++;
|
||||
|
||||
FX = CpuModeIndex.Iy;
|
||||
BINT = true;
|
||||
}
|
||||
else if (cmd == 0xCB)
|
||||
{
|
||||
//Refresh();
|
||||
regs.R = (byte)(((regs.R + 1) & 0x7F) | (regs.R & 0x80));
|
||||
Tact += 1;
|
||||
RzxCounter++;
|
||||
|
||||
XFX = CpuModeEx.Cb;
|
||||
BINT = true;
|
||||
}
|
||||
else if (cmd == 0xED)
|
||||
{
|
||||
//Refresh();
|
||||
regs.R = (byte)(((regs.R + 1) & 0x7F) | (regs.R & 0x80));
|
||||
Tact += 1;
|
||||
RzxCounter++;
|
||||
|
||||
XFX = CpuModeEx.Ed;
|
||||
BINT = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Refresh();
|
||||
regs.R = (byte)(((regs.R + 1) & 0x7F) | (regs.R & 0x80));
|
||||
Tact += 1;
|
||||
RzxCounter++;
|
||||
|
||||
BINT = false;
|
||||
(FX == CpuModeIndex.None ? _opcodes[cmd] : _opcodesFx[cmd])?.Invoke(cmd);
|
||||
FX = CpuModeIndex.None;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ProcessSignals()
|
||||
{
|
||||
if (RST) // RESET
|
||||
{
|
||||
// 3T
|
||||
RESET();
|
||||
//Refresh(); //+1T
|
||||
regs.R = (byte)(((regs.R + 1) & 0x7F) | (regs.R & 0x80));
|
||||
Tact += 1;
|
||||
RzxCounter++;
|
||||
|
||||
|
||||
FX = CpuModeIndex.None;
|
||||
XFX = CpuModeEx.None;
|
||||
HALTED = false;
|
||||
|
||||
IFF1 = false;
|
||||
IFF2 = false;
|
||||
regs.PC = 0;
|
||||
regs.IR = 0;
|
||||
IM = 0;
|
||||
//regs.SP = 0xFFFF;
|
||||
//regs.AF = 0xFFFF;
|
||||
|
||||
Tact += 2; // total should be 3T?
|
||||
return true;
|
||||
}
|
||||
else if (NMI)
|
||||
{
|
||||
// 11T (5, 3, 3)
|
||||
|
||||
if (HALTED) // workaround for Z80 snapshot halt issue + comfortable debugging
|
||||
regs.PC++;
|
||||
|
||||
// M1
|
||||
NMIACK_M1();
|
||||
Tact += 4;
|
||||
//Refresh();
|
||||
regs.R = (byte)(((regs.R + 1) & 0x7F) | (regs.R & 0x80));
|
||||
Tact += 1;
|
||||
RzxCounter++;
|
||||
|
||||
|
||||
IFF2 = IFF1;
|
||||
IFF1 = false;
|
||||
HALTED = false;
|
||||
regs.SP--;
|
||||
|
||||
// M2
|
||||
WRMEM(regs.SP, (byte)(regs.PC >> 8));
|
||||
Tact += 3;
|
||||
regs.SP--;
|
||||
|
||||
// M3
|
||||
WRMEM(regs.SP, (byte)regs.PC);
|
||||
regs.PC = 0x0066;
|
||||
Tact += 3;
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (INT && (!BINT) && IFF1)
|
||||
{
|
||||
// http://www.z80.info/interrup.htm
|
||||
// IM0: 13T (7,3,3) [RST]
|
||||
// IM1: 13T (7,3,3)
|
||||
// IM2: 19T (7,3,3,3,3)
|
||||
|
||||
if (HALTED) // workaround for Z80 snapshot halt issue + comfortable debugging
|
||||
regs.PC++;
|
||||
|
||||
|
||||
INTACK_M1();
|
||||
// M1: 7T = interrupt acknowledgement; SP--
|
||||
regs.SP--;
|
||||
//if (HALTED) ??
|
||||
// Tact += 2;
|
||||
Tact += 4 + 2;
|
||||
//Refresh();
|
||||
//RzxCounter--; // fix because INTAK should not be calculated
|
||||
regs.R = (byte)(((regs.R + 1) & 0x7F) | (regs.R & 0x80));
|
||||
Tact += 1;
|
||||
|
||||
|
||||
IFF1 = false;
|
||||
IFF2 = false; // proof?
|
||||
HALTED = false;
|
||||
|
||||
// M2
|
||||
WRMEM(regs.SP, (byte)(regs.PC >> 8)); // M2: 3T write PCH; SP--
|
||||
regs.SP--;
|
||||
Tact += 3;
|
||||
|
||||
// M3
|
||||
WRMEM(regs.SP, (byte)regs.PC); // M3: 3T write PCL
|
||||
Tact += 3;
|
||||
|
||||
if (IM == 0) // IM 0: execute instruction taken from BUS with timing T+2???
|
||||
{
|
||||
regs.MW = 0x0038; // workaround: just execute #FF
|
||||
}
|
||||
else if (IM == 1) // IM 1: execute #FF with timing T+2 (11+2=13T)
|
||||
{
|
||||
regs.MW = 0x0038;
|
||||
}
|
||||
else // IM 2: VH=reg.I; VL=BUS; PC=[V]
|
||||
{
|
||||
// M4
|
||||
ushort adr = (ushort)((regs.IR & 0xFF00) | BUS);
|
||||
regs.MW = RDMEM(adr); // M4: 3T read VL
|
||||
Tact += 3;
|
||||
|
||||
// M5
|
||||
regs.MW += (ushort)(RDMEM(++adr) * 0x100); // M5: 3T read VH, PC=V
|
||||
Tact += 3;
|
||||
}
|
||||
regs.PC = regs.MW;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* Copyright 2001, 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
using ZXMAK2.Engine.Cpu.Processor;
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu.Tools
|
||||
{
|
||||
public class TimingTool(Z80Cpu cpu, Func<ushort, byte> reader)
|
||||
{
|
||||
private readonly Z80Cpu _cpu = cpu;
|
||||
private readonly Func<ushort, byte> _memRead = reader;
|
||||
|
||||
public string GetTimingString(int addr)
|
||||
{
|
||||
var t = GetTiming(addr);
|
||||
return t.HasValue ? string.Format("{0}T", t) : "N/A";
|
||||
}
|
||||
|
||||
public int? GetTiming(int addr)
|
||||
{
|
||||
if (_memRead == null || _cpu == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var tableNumber = 0;
|
||||
var perfTime = 0;
|
||||
var offset = 0;
|
||||
var opCode = -1;
|
||||
while (true)
|
||||
{
|
||||
opCode = _memRead((ushort)(addr + offset));
|
||||
var res = s_tables[tableNumber][opCode];
|
||||
if (res < 0xF0)
|
||||
{
|
||||
return res + perfTime;
|
||||
}
|
||||
tableNumber = res - 0xF0;
|
||||
if (tableNumber >= 5)
|
||||
{
|
||||
break;
|
||||
}
|
||||
perfTime += 4;
|
||||
offset++;
|
||||
if (tableNumber == 4)
|
||||
{
|
||||
offset++;
|
||||
}
|
||||
if (perfTime > 1500000)
|
||||
{
|
||||
System.Console.Error.WriteLine("TimingTool.GetTiming: {0}T reached!", perfTime);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
switch (opCode)
|
||||
{
|
||||
case 0x10:
|
||||
return DjnzTime(perfTime, 0x08, 0x0D);
|
||||
|
||||
case 0xB1:
|
||||
case 0xB2:
|
||||
case 0xB9:
|
||||
case 0xBA:
|
||||
return DjnzTime(perfTime, 0x0C, 0x11);
|
||||
|
||||
case 0x20: return FlagTime(perfTime, 0x07, 0x0C, 0x40);
|
||||
case 0x28: return FlagTime(perfTime, 0x0C, 0x07, 0x40);
|
||||
case 0x30: return FlagTime(perfTime, 0x07, 0x0C, 0x01);
|
||||
case 0x38: return FlagTime(perfTime, 0x0C, 0x07, 0x01);
|
||||
case 0xC0: return FlagTime(perfTime, 0x05, 0x0B, 0x40);
|
||||
case 0xC8: return FlagTime(perfTime, 0x0B, 0x05, 0x40);
|
||||
case 0xD0: return FlagTime(perfTime, 0x05, 0x0B, 0x01);
|
||||
case 0xD8: return FlagTime(perfTime, 0x0B, 0x05, 0x01);
|
||||
case 0xE0: return FlagTime(perfTime, 0x05, 0x0B, 0x04);
|
||||
case 0xE8: return FlagTime(perfTime, 0x0B, 0x05, 0x04);
|
||||
case 0xF0: return FlagTime(perfTime, 0x05, 0x0B, 0x80);
|
||||
case 0xF8: return FlagTime(perfTime, 0x0B, 0x05, 0x80);
|
||||
case 0xC4: return FlagTime(perfTime, 0x0A, 0x11, 0x40);
|
||||
case 0xCC: return FlagTime(perfTime, 0x11, 0x0A, 0x40);
|
||||
case 0xD4: return FlagTime(perfTime, 0x0A, 0x11, 0x01);
|
||||
case 0xDC: return FlagTime(perfTime, 0x11, 0x0A, 0x01);
|
||||
case 0xE4: return FlagTime(perfTime, 0x0A, 0x11, 0x04);
|
||||
case 0xEC: return FlagTime(perfTime, 0x11, 0x0A, 0x04);
|
||||
case 0xF4: return FlagTime(perfTime, 0x0A, 0x11, 0x80);
|
||||
case 0xFC: return FlagTime(perfTime, 0x11, 0x0A, 0x80);
|
||||
|
||||
case 0xB0:
|
||||
case 0xB8:
|
||||
return RepTime(perfTime, 0x0C, 0x11);
|
||||
|
||||
case 0xB3:
|
||||
case 0xBB:
|
||||
return RepFlagTime(perfTime, 0x0C, 0x0C, 0x11);
|
||||
}
|
||||
System.Console.Error.WriteLine(
|
||||
"TimingTool.GetTiming: unexpected opCode #{0:2X}",
|
||||
opCode);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
#region Private
|
||||
|
||||
private int DjnzTime(int timeBase, int timeEnd, int timeNoEnd)
|
||||
{
|
||||
var ifTime = _cpu.regs.B == 0x01 ? timeEnd : timeNoEnd;
|
||||
return timeBase + ifTime;
|
||||
}
|
||||
|
||||
private int FlagTime(int timeBase, int timeOn, int timeOff, int flagMask)
|
||||
{
|
||||
var ifTime = (_cpu.regs.F & flagMask) != 0 ? timeOn : timeOff;
|
||||
return timeBase + ifTime;
|
||||
}
|
||||
|
||||
private int RepTime(int perfTime, int timeEnd, int timeNoEnd)
|
||||
{
|
||||
var ifTime = _cpu.regs.BC == 0x0001 ? timeEnd : timeNoEnd;
|
||||
return perfTime + ifTime;
|
||||
}
|
||||
|
||||
private int RepFlagTime(int timeEnd, int timeOn, int timeOff, int perfTime)
|
||||
{
|
||||
if (_cpu.regs.BC == 0x0001)
|
||||
{
|
||||
return perfTime + timeEnd;
|
||||
}
|
||||
var data = _memRead(_cpu.regs.HL);
|
||||
var ifTime = _cpu.regs.F == data ? timeOn : timeOff;
|
||||
return perfTime + ifTime;
|
||||
}
|
||||
|
||||
#endregion Private
|
||||
|
||||
|
||||
#region Tables
|
||||
|
||||
private static readonly int[] s_tableDirect =
|
||||
[
|
||||
0x04,0x0A,0x07,0x06,0x04,0x04,0x07,0x04, //;00h-07h
|
||||
0x04,0x0B,0x07,0x06,0x04,0x04,0x07,0x04, //;08h-0Fh
|
||||
0xFF,0x0A,0x07,0x06,0x04,0x04,0x07,0x04, //;10h-17h
|
||||
0x0C,0x0B,0x07,0x06,0x04,0x04,0x07,0x04, //;18h-1Fh
|
||||
0xFF,0x0A,0x10,0x06,0x04,0x04,0x07,0x04, //;20h-27h
|
||||
0xFF,0x0B,0x10,0x06,0x04,0x04,0x07,0x04, //;28h-2Fh
|
||||
0xFF,0x0A,0x0D,0x06,0x0B,0x0B,0x0A,0x04, //;30h-37h
|
||||
0xFF,0x0B,0x0D,0x06,0x04,0x04,0x07,0x04, //;38h-3Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;40h-47h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;48h-4Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;50h-57h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;58h-5Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;60h-67h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;68h-6Fh
|
||||
0x07,0x07,0x07,0x07,0x07,0x07,0x04,0x07, //;70h-77h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;78h-7Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;80h-87h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;88h-8Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;90h-97h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;98h-9Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;A0h-A7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;A8h-AFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;B0h-B7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;B8h-BFh
|
||||
0xFF,0x0A,0x0A,0x0A,0xFF,0x0B,0x07,0x0B, //;C0h-C7h
|
||||
0xFF,0x0A,0x0A,0xF1,0xFF,0x11,0x07,0x0B, //;C8h-CFh
|
||||
0xFF,0x0A,0x0A,0x0B,0xFF,0x0B,0x07,0x0B, //;D0h-D7h
|
||||
0xFF,0x04,0x0A,0x0B,0xFF,0xF3,0x07,0x0B, //;D8h-DFh
|
||||
0xFF,0x0A,0x0A,0x13,0xFF,0x0B,0x07,0x0B, //;E0h-E7h
|
||||
0xFF,0x04,0x0A,0x04,0xFF,0xF2,0x07,0x0B, //;E8h-EFh
|
||||
0xFF,0x0A,0x0A,0x04,0xFF,0x0B,0x07,0x0B, //;F0h-F7h
|
||||
0xFF,0x06,0x0A,0x04,0xFF,0xF3,0x07,0x0B //;F8h-FFh
|
||||
];
|
||||
|
||||
private static readonly int[] s_tableCb =
|
||||
[
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;00h-07h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;08h-0Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;10h-17h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;18h-1Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;20h-27h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;28h-2Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;30h-37h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;38h-3Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x08,0x04, // ;40h-47h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x08,0x04, // ;48h-4Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x08,0x04, // ;50h-57h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x08,0x04, // ;58h-5Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x08,0x04, // ;60h-67h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x08,0x04, // ;68h-6Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x08,0x04, // ;70h-77h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x08,0x04, // ;78h-7Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;80h-87h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;88h-8Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;90h-97h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;98h-9Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;A0h-A7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;A8h-AFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;B0h-B7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;B8h-BFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;C0h-C7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;C8h-CFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;D0h-D7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;D8h-DFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;E0h-E7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;E8h-EFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;F0h-F7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04 // ;F8h-FFh
|
||||
];
|
||||
|
||||
private static readonly int[] s_tableEd =
|
||||
[
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;00h-07h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;08h-0Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;10h-17h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;18h-1Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;20h-27h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;28h-2Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;30h-37h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;38h-3Fh
|
||||
0x08,0x08,0x0B,0x10,0x04,0x0A,0x04,0x05, // ;40h-47h
|
||||
0x08,0x08,0x0B,0x10,0x04,0x0A,0x04,0x05, // ;48h-4Fh
|
||||
0x08,0x08,0x0B,0x10,0x04,0x0A,0x04,0x05, // ;50h-57h
|
||||
0x08,0x08,0x0B,0x10,0x04,0x0A,0x04,0x05, // ;58h-5Fh
|
||||
0x08,0x08,0x0B,0x10,0x04,0x0A,0x04,0x0E, // ;60h-67h
|
||||
0x08,0x08,0x0B,0x10,0x04,0x0A,0x04,0x0E, // ;68h-6Fh
|
||||
0x08,0x08,0x0B,0x10,0x04,0x0A,0x04,0x04, // ;70h-77h
|
||||
0x08,0x08,0x0B,0x10,0x04,0x0A,0x04,0x04, // ;78h-7Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;80h-87h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;88h-8Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;90h-97h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;98h-9Fh
|
||||
0x0C,0x0C,0x0C,0x0C,0x04,0x04,0x04,0x04, // ;A0h-A7h
|
||||
0x0C,0x0C,0x0C,0x0C,0x04,0x04,0x04,0x04, // ;A8h-AFh
|
||||
0xFF,0xFF,0xFF,0xFF,0x04,0x04,0x04,0x04, // ;B0h-B7h
|
||||
0xFF,0xFF,0xFF,0xFF,0x04,0x04,0x04,0x04, // ;B8h-BFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;C0h-C7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;C8h-CFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;D0h-D7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;D8h-DFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;E0h-E7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;E8h-EFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;F0h-F7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04 // ;F8h-FFh
|
||||
];
|
||||
|
||||
|
||||
private static readonly int[] s_tableDdFd =
|
||||
[
|
||||
0x04,0x0A,0x07,0x06,0x04,0x04,0x07,0x04, // ;00h-07h
|
||||
0x04,0x0B,0x07,0x06,0x04,0x04,0x07,0x04, // ;08h-0Fh
|
||||
0xFF,0x0A,0x07,0x06,0x04,0x04,0x07,0x04, // ;10h-17h
|
||||
0x0C,0x0B,0x07,0x06,0x04,0x04,0x07,0x04, // ;18h-1Fh
|
||||
0xFF,0x0A,0x10,0x06,0x04,0x04,0x07,0x04, // ;20h-27h
|
||||
0xFF,0x0B,0x10,0x06,0x04,0x04,0x07,0x04, // ;28h-2Fh
|
||||
0xFF,0x0A,0x0D,0x06,0x13,0x13,0x0F,0x04, // ;30h-37h
|
||||
0xFF,0x0B,0x0D,0x06,0x04,0x04,0x0F,0x04, // ;38h-3Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;40h-47h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;48h-4Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;50h-57h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;58h-5Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;60h-67h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;68h-6Fh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x04,0x0F, // ;70h-77h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;78h-7Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;80h-87h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;88h-8Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;90h-97h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;98h-9Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;A0h-A7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;A8h-AFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;B0h-B7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;B8h-BFh
|
||||
0xFF,0x0A,0x0A,0x0A,0xFF,0x0B,0x0F,0x0B, // ;C0h-C7h
|
||||
0xFF,0x0A,0x0A,0xF4,0xFF,0x11,0x0F,0x0B, // ;C8h-CFh
|
||||
0xFF,0x0A,0x0A,0x0B,0xFF,0x0B,0x0F,0x0B, // ;D0h-D7h
|
||||
0xFF,0x04,0x0A,0x0B,0xFF,0xF3,0x0F,0x0B, // ;D8h-DFh
|
||||
0xFF,0x0A,0x0A,0x13,0xFF,0x0B,0x0F,0x0B, // ;E0h-E7h
|
||||
0xFF,0x04,0x0A,0x04,0xFF,0xF2,0x0F,0x0B, // ;E8h-EFh
|
||||
0xFF,0x0A,0x0A,0x04,0xFF,0x0B,0x0F,0x0B, // ;F0h-F7h
|
||||
0xFF,0x06,0x0A,0x04,0xFF,0xF3,0x0F,0x0B // ;F8h-FFh
|
||||
];
|
||||
|
||||
private static readonly int[] s_tableDdCbFdCb =
|
||||
[
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;00h-07h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;08h-0Fh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;10h-17h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;18h-1Fh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;20h-27h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;28h-2Fh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;30h-37h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;38h-3Fh
|
||||
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // ;40h-47h
|
||||
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // ;48h-4Fh
|
||||
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // ;50h-57h
|
||||
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // ;58h-5Fh
|
||||
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // ;60h-67h
|
||||
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // ;68h-6Fh
|
||||
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // ;70h-77h
|
||||
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // ;78h-7Fh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;80h-87h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;88h-8Fh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;90h-97h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;98h-9Fh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;A0h-A7h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;A8h-AFh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;B0h-B7h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;B8h-BFh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;C0h-C7h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;C8h-CFh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;D0h-D7h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;D8h-DFh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;E0h-E7h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;E8h-EFh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;F0h-F7h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F // ;F8h-FFh
|
||||
];
|
||||
|
||||
private static readonly int[][] s_tables =
|
||||
[
|
||||
s_tableDirect,
|
||||
s_tableCb,
|
||||
s_tableEd,
|
||||
s_tableDdFd,
|
||||
s_tableDdCbFdCb,
|
||||
];
|
||||
|
||||
#endregion Tables
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// CpuTrace — ring buffer storing last N executed instructions with register snapshots
|
||||
// Used for crash/corruption analysis in the WC emulator
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace InflateDebugger
|
||||
{
|
||||
/// <summary>Snapshot of CPU state at one instruction</summary>
|
||||
public struct TraceEntry
|
||||
{
|
||||
public long Tact;
|
||||
public ushort PC;
|
||||
public byte Opcode; // first byte of the instruction
|
||||
public ushort AF, BC, DE, HL;
|
||||
public ushort AF2, BC2, DE2, HL2; // shadow regs
|
||||
public ushort IX, IY, SP;
|
||||
public byte Win0, Win1, Win2, Win3; // page window mapping
|
||||
|
||||
// Optional: what was written (0xFFFF = no write)
|
||||
public ushort WriteAddr;
|
||||
public byte WriteVal;
|
||||
|
||||
// WC API trap info (0xFF = not a WC call)
|
||||
public byte WcFuncNum;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat("[{0,10}] PC={1:X4} op={2:X2}", Tact, PC, Opcode);
|
||||
sb.AppendFormat(" AF={0:X4} BC={1:X4} DE={2:X4} HL={3:X4}", AF, BC, DE, HL);
|
||||
sb.AppendFormat(" IX={0:X4} SP={1:X4}", IX, SP);
|
||||
sb.AppendFormat(" W[{0:X2},{1:X2},{2:X2},{3:X2}]", Win0, Win1, Win2, Win3);
|
||||
if (WcFuncNum != 0xFF)
|
||||
sb.AppendFormat(" WC_CALL=0x{0:X2}", WcFuncNum);
|
||||
if (WriteAddr != 0xFFFF)
|
||||
sb.AppendFormat(" WR[{0:X4}]={1:X2}", WriteAddr, WriteVal);
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Fixed-size ring buffer of CPU trace entries</summary>
|
||||
public class CpuTrace
|
||||
{
|
||||
private readonly TraceEntry[] _buf;
|
||||
private readonly int _capacity;
|
||||
private int _head; // next write position
|
||||
private int _count; // total entries stored (capped at capacity)
|
||||
|
||||
public CpuTrace(int capacity = 200)
|
||||
{
|
||||
_capacity = capacity;
|
||||
_buf = new TraceEntry[capacity];
|
||||
_head = 0;
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
public int Count => _count;
|
||||
public int Capacity => _capacity;
|
||||
|
||||
/// <summary>Record a new trace entry (overwrites oldest when full)</summary>
|
||||
public void Record(ref TraceEntry entry)
|
||||
{
|
||||
_buf[_head] = entry;
|
||||
_head = (_head + 1) % _capacity;
|
||||
if (_count < _capacity) _count++;
|
||||
}
|
||||
|
||||
/// <summary>Get entry by age: 0 = most recent, 1 = one before, etc.</summary>
|
||||
public TraceEntry Get(int age)
|
||||
{
|
||||
if (age < 0 || age >= _count)
|
||||
throw new IndexOutOfRangeException($"age={age}, count={_count}");
|
||||
int idx = (_head - 1 - age + _capacity * 2) % _capacity;
|
||||
return _buf[idx];
|
||||
}
|
||||
|
||||
/// <summary>Dump the last N entries (most recent first) to a string</summary>
|
||||
public string Dump(int maxEntries = 0)
|
||||
{
|
||||
if (maxEntries <= 0) maxEntries = _count;
|
||||
int n = Math.Min(maxEntries, _count);
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"=== CPU Trace (last {n} of {_count}) ===");
|
||||
// Print oldest-first for readability
|
||||
for (int i = n - 1; i >= 0; i--)
|
||||
sb.AppendLine(Get(i).ToString());
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>Dump trace to file</summary>
|
||||
public void DumpToFile(string path, int maxEntries = 0)
|
||||
{
|
||||
System.IO.File.WriteAllText(path, Dump(maxEntries));
|
||||
}
|
||||
|
||||
/// <summary>Clear all entries</summary>
|
||||
public void Clear()
|
||||
{
|
||||
_head = 0;
|
||||
_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RootNamespace>InflateDebugger</RootNamespace>
|
||||
<Nullable>disable</Nullable>
|
||||
<NoWarn>CS8625;CS8618;CS8602;CS8604;CS8600;CS8601</NoWarn>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,674 @@
|
||||
// InflateDebugger — Z80 emulator for TSConfig inflate + WC plugin debugging
|
||||
//
|
||||
// MODE 1 — inflate (default):
|
||||
// Loads VGZ file into emulated TSConfig memory pages (as tape loader would),
|
||||
// loads inflate.bin into Win3, sets up registers as inflate_call.s would,
|
||||
// and runs the Z80 CPU with debug port monitoring and watchdog.
|
||||
//
|
||||
// Usage: dotnet run [--vgz <path>] [--ref <path>] [--inflate <path>]
|
||||
// [--dump-on-hang] [--max-cycles <N>] [--watchdog <N>]
|
||||
//
|
||||
// MODE 2 — plugin:
|
||||
// Loads a WMF plugin binary, emulates WC kernel API (P8000 dispatch),
|
||||
// emulates disk I/O from test_data directory, runs the full plugin lifecycle
|
||||
// with CPU trace, page protection, and WC call logging.
|
||||
//
|
||||
// Usage: dotnet run --mode plugin --wmf <path> [--file <vgz>]
|
||||
// [--max-cycles <N>] [--verbose]
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ZXMAK2.Engine.Cpu;
|
||||
using ZXMAK2.Engine.Cpu.Processor;
|
||||
|
||||
namespace InflateDebugger
|
||||
{
|
||||
class Program
|
||||
{
|
||||
// ── TSConfig page assignments (matching real WC plugin) ──
|
||||
const byte SRC_START_PAGE = 0xA1; // TAP pages for VGZ source
|
||||
const byte DST_START_PAGE = 0x20; // megabuffer for decompressed output
|
||||
const byte INFLATE_PAGE = 0x6C; // inflate code page (base 0x67 + 5)
|
||||
const byte PAGE_DECODE = 0xEA; // decode working buffer page
|
||||
const byte WC_BASE_PAGE = 0x67; // WC plugin base page
|
||||
|
||||
// ── Ports ──
|
||||
const ushort DBG_STATUS = 0xFFAF;
|
||||
const ushort DBG_SRC = 0xFEAF;
|
||||
const ushort DBG_DST = 0xFDAF;
|
||||
|
||||
// ── Defaults ──
|
||||
const long DEFAULT_MAX_CYCLES = 500_000_000; // 500M cycles max
|
||||
const long DEFAULT_WATCHDOG = 200_000; // 200K cycles without dbg port = hang
|
||||
|
||||
// ── State ──
|
||||
static TsMemory mem = new();
|
||||
static CpuUnit cpu = new();
|
||||
|
||||
static long lastDbgCycle = 0;
|
||||
static long watchdogLimit = DEFAULT_WATCHDOG;
|
||||
static long maxCycles = DEFAULT_MAX_CYCLES;
|
||||
static bool dumpOnHang = true;
|
||||
static bool finished = false;
|
||||
static byte lastDbgStatus = 0;
|
||||
static byte lastDbgSrc = 0;
|
||||
static byte lastDbgDst = 0;
|
||||
static int srcPagesLoaded = 0;
|
||||
static string? refPath = null;
|
||||
static string dumpDir = "";
|
||||
|
||||
// Track which pages were written as destination
|
||||
static HashSet<int> dstPagesWritten = new();
|
||||
|
||||
static int Main(string[] args)
|
||||
{
|
||||
string basePath = FindProjectRoot();
|
||||
|
||||
// ── Detect mode ──
|
||||
string mode = "inflate"; // default
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
if (args[i] == "--mode" && i + 1 < args.Length)
|
||||
{
|
||||
mode = args[++i].ToLowerInvariant();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return mode switch
|
||||
{
|
||||
"plugin" => RunPluginMode(args, basePath),
|
||||
_ => RunInflateMode(args, basePath)
|
||||
};
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Plugin mode — runs full WMF plugin with WC API emulation
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
static int RunPluginMode(string[] args, string basePath)
|
||||
{
|
||||
string? wmfPath = null;
|
||||
string? testFile = null;
|
||||
string? diskRoot = Path.Combine(basePath, "Debuger", "test_data");
|
||||
long maxCyclesP = 2_000_000_000;
|
||||
bool verbose = true;
|
||||
bool verbosePages = false;
|
||||
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
switch (args[i])
|
||||
{
|
||||
case "--wmf" when i + 1 < args.Length:
|
||||
wmfPath = args[++i]; break;
|
||||
case "--file" when i + 1 < args.Length:
|
||||
testFile = args[++i]; break;
|
||||
case "--disk-root" when i + 1 < args.Length:
|
||||
diskRoot = args[++i]; break;
|
||||
case "--max-cycles" when i + 1 < args.Length:
|
||||
maxCyclesP = long.Parse(args[++i]); break;
|
||||
case "--verbose":
|
||||
verbose = true; break;
|
||||
case "--quiet":
|
||||
verbose = false; break;
|
||||
case "--verbose-pages":
|
||||
verbosePages = true; break;
|
||||
}
|
||||
}
|
||||
|
||||
// Default WMF: look in DiskRef/WC/VGMPLAY.WMF or variants/
|
||||
if (wmfPath == null)
|
||||
{
|
||||
wmfPath = Path.Combine(basePath, @"DiskRef\WC\VGMPLAY.WMF");
|
||||
if (!File.Exists(wmfPath))
|
||||
wmfPath = Path.Combine(basePath, @"variants\VGMPLAY_v1_b8.WMF");
|
||||
}
|
||||
|
||||
// Default test file: first .vgz in test_data
|
||||
if (testFile == null && Directory.Exists(diskRoot!))
|
||||
{
|
||||
var firstVgz = Directory.GetFiles(diskRoot!, "*.vgz")
|
||||
.OrderBy(f => f).FirstOrDefault();
|
||||
if (firstVgz != null)
|
||||
testFile = Path.GetFileName(firstVgz);
|
||||
}
|
||||
|
||||
var runner = new PluginRunner
|
||||
{
|
||||
MaxCycles = maxCyclesP,
|
||||
VerboseWcCalls = verbose,
|
||||
VerbosePageSwitch = verbosePages,
|
||||
DumpOnExit = true,
|
||||
DumpDir = Path.Combine(basePath, @"Debuger\dumps"),
|
||||
DiskRootPath = diskRoot!,
|
||||
TestFileName = testFile
|
||||
};
|
||||
|
||||
return runner.Run(wmfPath!);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Inflate mode — original inflate test harness
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
static int RunInflateMode(string[] args, string basePath)
|
||||
{
|
||||
string vgzPath = Path.Combine(basePath,
|
||||
@"DiskRef\Music\OPL2\Arcade\_\Act-Fancer_Cybernetick_Hyper_Weapon_(Arcade)\02 Sci-Vax (BGM 1).vgz");
|
||||
string inflatePath = Path.Combine(basePath, @"src_sdcc\build\inflate.bin");
|
||||
refPath = Path.Combine(basePath, @"Debuger\ref.vgm");
|
||||
dumpDir = Path.Combine(basePath, @"Debuger\dumps");
|
||||
|
||||
// Parse args
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
switch (args[i])
|
||||
{
|
||||
case "--vgz" when i + 1 < args.Length: vgzPath = args[++i]; break;
|
||||
case "--ref" when i + 1 < args.Length: refPath = args[++i]; break;
|
||||
case "--inflate" when i + 1 < args.Length: inflatePath = args[++i]; break;
|
||||
case "--max-cycles" when i + 1 < args.Length: maxCycles = long.Parse(args[++i]); break;
|
||||
case "--watchdog" when i + 1 < args.Length: watchdogLimit = long.Parse(args[++i]); break;
|
||||
case "--dump-on-hang": dumpOnHang = true; break;
|
||||
case "--no-dump": dumpOnHang = false; break;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("═══════════════════════════════════════════════════");
|
||||
Console.WriteLine(" TSConfig Inflate Debugger");
|
||||
Console.WriteLine("═══════════════════════════════════════════════════");
|
||||
|
||||
// ─── 1. Load VGZ file ───
|
||||
if (!File.Exists(vgzPath))
|
||||
{
|
||||
Console.Error.WriteLine($"VGZ file not found: {vgzPath}");
|
||||
return 1;
|
||||
}
|
||||
byte[] vgzData = File.ReadAllBytes(vgzPath);
|
||||
Console.WriteLine($"VGZ file: {vgzPath}");
|
||||
Console.WriteLine($"VGZ size: {vgzData.Length} bytes");
|
||||
|
||||
// ─── 2. Load inflate.bin ───
|
||||
if (!File.Exists(inflatePath))
|
||||
{
|
||||
Console.Error.WriteLine($"inflate.bin not found: {inflatePath}");
|
||||
return 1;
|
||||
}
|
||||
byte[] inflateData = File.ReadAllBytes(inflatePath);
|
||||
Console.WriteLine($"inflate.bin: {inflateData.Length} bytes");
|
||||
|
||||
// ─── 3. Set up TSConfig memory ───
|
||||
SetupMemory(vgzData, inflateData);
|
||||
|
||||
// ─── 4. Set up CPU ───
|
||||
SetupCpu();
|
||||
|
||||
// ─── 5. Run ───
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("─── Execution Start ─────────────────────────────");
|
||||
Console.WriteLine();
|
||||
|
||||
int exitCode = RunCpu();
|
||||
|
||||
// ─── 6. Post-run analysis ───
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("─── Post-Run Analysis ───────────────────────────");
|
||||
AnalyzeResults();
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
static string FindProjectRoot()
|
||||
{
|
||||
// Walk up from current dir or exe dir looking for Debuger folder
|
||||
string? dir = AppDomain.CurrentDomain.BaseDirectory;
|
||||
for (int i = 0; i < 10 && dir != null; i++)
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(dir, "Debuger")) &&
|
||||
Directory.Exists(Path.Combine(dir, "src_sdcc")))
|
||||
return dir;
|
||||
dir = Path.GetDirectoryName(dir);
|
||||
}
|
||||
// Try current directory
|
||||
dir = Environment.CurrentDirectory;
|
||||
for (int i = 0; i < 10 && dir != null; i++)
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(dir, "Debuger")) &&
|
||||
Directory.Exists(Path.Combine(dir, "src_sdcc")))
|
||||
return dir;
|
||||
dir = Path.GetDirectoryName(dir);
|
||||
}
|
||||
return @"D:\zx_projects\OplPlug";
|
||||
}
|
||||
|
||||
static void SetupMemory(byte[] vgzData, byte[] inflateData)
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("─── Memory Setup ────────────────────────────────");
|
||||
|
||||
// Load VGZ into TAP pages (0xA1+) — as WC tape loader would
|
||||
srcPagesLoaded = mem.LoadToPages(vgzData, SRC_START_PAGE);
|
||||
Console.WriteLine($"VGZ loaded to pages 0x{SRC_START_PAGE:X2}–0x{SRC_START_PAGE + srcPagesLoaded - 1:X2} ({srcPagesLoaded} pages)");
|
||||
|
||||
// Load inflate.bin into inflate page (0x6C)
|
||||
// inflate.bin is padded to fill code + RAM tables within one 16KB page
|
||||
if (inflateData.Length > TsMemory.PageSize)
|
||||
{
|
||||
Console.WriteLine($"WARNING: inflate.bin ({inflateData.Length}) > 16KB, truncating");
|
||||
}
|
||||
Array.Copy(inflateData, 0, mem.GetPage(INFLATE_PAGE), 0,
|
||||
Math.Min(inflateData.Length, TsMemory.PageSize));
|
||||
Console.WriteLine($"inflate.bin loaded to page 0x{INFLATE_PAGE:X2}");
|
||||
|
||||
// Set up initial window mapping:
|
||||
// Win0 = source page 0xA1 (set by inflate entry code)
|
||||
// Win1 = don't care yet (inflate sets it)
|
||||
// Win2 = WC base page 0x67 (current plugin page — inflate will remap to PAGE_DECODE)
|
||||
// Win3 = inflate page 0x6C (set by inflate_call.s before JP 0xC000)
|
||||
mem.SetWindow(0, SRC_START_PAGE); // Will be set by inflate entry
|
||||
mem.SetWindow(1, 0x00); // Don't care — inflate sets Win1
|
||||
mem.SetWindow(2, WC_BASE_PAGE); // Current Win2 = plugin base
|
||||
mem.SetWindow(3, INFLATE_PAGE); // inflate code
|
||||
|
||||
// Set up WC system variables at 0x6000–0x6003
|
||||
// These are in the page mapped to Win1 at offset 0x2000
|
||||
// Actually in the real system, Win1 maps some WC page.
|
||||
// We need 0x6000–0x6003 readable. Let's put a known page in Win1
|
||||
// that has these values. In WC, Win1 = page at sys var area.
|
||||
// For simplicity: map Win1 to a scratch page, write sys vars there
|
||||
byte sysVarPage = 0xFE; // use a spare page for sys vars
|
||||
mem.SetWindow(1, sysVarPage);
|
||||
// 0x6000 = offset 0x2000 in Win1
|
||||
// Write the page values as WC would have them:
|
||||
mem.GetPage(sysVarPage)[0x2000] = SRC_START_PAGE; // 0x6000 = Win0 page
|
||||
mem.GetPage(sysVarPage)[0x2001] = sysVarPage; // 0x6001 = Win1 page
|
||||
mem.GetPage(sysVarPage)[0x2002] = WC_BASE_PAGE; // 0x6002 = Win2 page (plugin base)
|
||||
mem.GetPage(sysVarPage)[0x2003] = INFLATE_PAGE; // 0x6003 = Win3 page
|
||||
|
||||
Console.WriteLine($"Windows: W0=0x{SRC_START_PAGE:X2} W1=0x{sysVarPage:X2} W2=0x{WC_BASE_PAGE:X2} W3=0x{INFLATE_PAGE:X2}");
|
||||
Console.WriteLine($"Sys vars @0x6000: [{SRC_START_PAGE:X2} {sysVarPage:X2} {WC_BASE_PAGE:X2} {INFLATE_PAGE:X2}]");
|
||||
|
||||
// Display first bytes of inflate at 0xC000
|
||||
Console.Write("inflate @0xC000: ");
|
||||
for (int i = 0; i < 16; i++)
|
||||
Console.Write($"{mem.Read((ushort)(0xC000 + i)):X2} ");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
static void SetupCpu()
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("─── CPU Setup ───────────────────────────────────");
|
||||
|
||||
// Wire CPU bus to TSConfig memory
|
||||
cpu.RDMEM = (addr) => mem.Read(addr);
|
||||
cpu.RDMEM_M1 = (addr) => mem.Read(addr);
|
||||
cpu.WRMEM = (addr, val) => mem.Write(addr, val);
|
||||
cpu.RDPORT = (port) => 0xFF; // unused reads return 0xFF
|
||||
cpu.WRPORT = HandlePortWrite;
|
||||
cpu.RDNOMREQ = (addr) => { };
|
||||
cpu.WRNOMREQ = (addr) => { };
|
||||
cpu.RESET = () => { };
|
||||
cpu.NMIACK_M1 = () => { };
|
||||
cpu.INTACK_M1 = () => { };
|
||||
cpu.SCANSIG = () => { };
|
||||
|
||||
// No interrupts during inflate
|
||||
cpu.IFF1 = false;
|
||||
cpu.IFF2 = false;
|
||||
cpu.INT = false;
|
||||
cpu.NMI = false;
|
||||
cpu.RST = false;
|
||||
cpu.IM = 1;
|
||||
|
||||
// Set up registers as inflate_call.s would before JP 0xC000:
|
||||
// A = src start page (0xA1)
|
||||
// E = dst start page (0x20)
|
||||
// D = saved Win 2 page (0x67 — the plugin base page)
|
||||
// SP = 0xFFF0
|
||||
// Return address pushed on stack
|
||||
cpu.regs.A = SRC_START_PAGE; // 0xA1
|
||||
cpu.regs.E = DST_START_PAGE; // 0x20
|
||||
cpu.regs.D = WC_BASE_PAGE; // 0x67 — saved pg2
|
||||
cpu.regs.SP = 0xFFF0;
|
||||
cpu.regs.PC = 0xC000;
|
||||
|
||||
// Push a sentinel return address (0x0000) so RET goes to addr 0,
|
||||
// which we detect as "inflate returned"
|
||||
cpu.regs.SP -= 2;
|
||||
mem.Write(cpu.regs.SP, 0x00); // low byte
|
||||
mem.Write((ushort)(cpu.regs.SP + 1), 0x00); // high byte
|
||||
|
||||
cpu.Tact = 0;
|
||||
cpu.FX = CpuModeIndex.None;
|
||||
cpu.XFX = CpuModeEx.None;
|
||||
cpu.HALTED = false;
|
||||
|
||||
Console.WriteLine($"PC=0x{cpu.regs.PC:X4} SP=0x{cpu.regs.SP:X4}");
|
||||
Console.WriteLine($"A=0x{cpu.regs.A:X2} D=0x{cpu.regs.D:X2} E=0x{cpu.regs.E:X2}");
|
||||
Console.WriteLine($"Return sentinel at [0x{cpu.regs.SP:X4}] = 0x0000");
|
||||
}
|
||||
|
||||
static void HandlePortWrite(ushort port, byte value)
|
||||
{
|
||||
// TSConfig page ports
|
||||
if (mem.HandlePort(port, value))
|
||||
{
|
||||
// Log page changes (optional verbose)
|
||||
return;
|
||||
}
|
||||
|
||||
// Debug ports
|
||||
switch (port)
|
||||
{
|
||||
case DBG_STATUS:
|
||||
lastDbgStatus = value;
|
||||
lastDbgCycle = cpu.Tact;
|
||||
PrintDbgStatus(value);
|
||||
break;
|
||||
|
||||
case DBG_SRC:
|
||||
lastDbgSrc = value;
|
||||
lastDbgCycle = cpu.Tact;
|
||||
break;
|
||||
|
||||
case DBG_DST:
|
||||
lastDbgDst = value;
|
||||
lastDbgCycle = cpu.Tact;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly Dictionary<byte, string> StatusNames = new()
|
||||
{
|
||||
[0x0E] = "inflate entry",
|
||||
[0x0F] = "stores done",
|
||||
[0x10] = "Win0+Win2 remapped",
|
||||
[0x11] = "Win2→PAGE_DECODE",
|
||||
[0x12] = "gzip header OK",
|
||||
[0x14] = "blocktype0 (stored)",
|
||||
[0x15] = "blocktype1 (static)",
|
||||
[0x16] = "blocktype2 (dynamic)",
|
||||
[0x17] = "Huffman tables built → decode loop",
|
||||
[0x20] = "page flush (bt0)",
|
||||
[0x21] = "page flush (decode)",
|
||||
[0x30] = "EXIT OK",
|
||||
[0x31] = "FAIL",
|
||||
[0xA6] = "bt2: HLIT/HDIST/HCLEN read",
|
||||
[0xA7] = "bt2: makeHuffmanTable(19) done",
|
||||
[0xA8] = "bt2: decodeProtoHuffman done",
|
||||
[0xA9] = "bt2: code_length_orders read → mht",
|
||||
[0xB1] = "mht: count loop done",
|
||||
[0xB2] = "mht: start codes done",
|
||||
[0xB3] = "mht: code assignment done",
|
||||
[0xBF] = "decodeProtoHuffman entry",
|
||||
[0xC0] = "dph: loop iteration",
|
||||
[0xC1] = "dph: leaf found",
|
||||
[0xC2] = "dph: special code",
|
||||
};
|
||||
|
||||
static void PrintDbgStatus(byte code)
|
||||
{
|
||||
string name = StatusNames.TryGetValue(code, out var n) ? n : "???";
|
||||
string extra = "";
|
||||
// For codes that also emit SRC/DST, show them
|
||||
if (code == 0xC1 || code == 0xC2)
|
||||
extra = $" FEAF=0x{lastDbgSrc:X2}";
|
||||
else if (code == 0x10 || code == 0x20 || code == 0x21 || code == 0x30)
|
||||
extra = $" src_pg=0x{lastDbgSrc:X2} dst_pg=0x{lastDbgDst:X2}";
|
||||
|
||||
Console.ForegroundColor = code switch
|
||||
{
|
||||
0x30 => ConsoleColor.Green,
|
||||
0x31 => ConsoleColor.Red,
|
||||
>= 0xB0 and <= 0xBF => ConsoleColor.DarkGray, // mht internals
|
||||
>= 0xC0 and <= 0xCF => ConsoleColor.DarkCyan, // dph internals
|
||||
_ => ConsoleColor.Yellow
|
||||
};
|
||||
Console.WriteLine($" [{cpu.Tact,10}] DBG 0x{code:X2}: {name}{extra}");
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
static int RunCpu()
|
||||
{
|
||||
long cycleCount = 0;
|
||||
|
||||
while (!finished)
|
||||
{
|
||||
// ── Watchdog: execution outside Win3 ──
|
||||
ushort pc = cpu.regs.PC;
|
||||
if (pc < 0xC000)
|
||||
{
|
||||
if (pc == 0x0000)
|
||||
{
|
||||
// Sentinel return address — inflate returned!
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
Console.WriteLine($"\n ✓ Inflate returned at cycle {cpu.Tact}, A=0x{cpu.regs.A:X2}");
|
||||
Console.ResetColor();
|
||||
if (cpu.regs.A == 0)
|
||||
Console.WriteLine(" Result: SUCCESS (A=0, no carry)");
|
||||
else
|
||||
Console.WriteLine($" Result: FAIL (A=0x{cpu.regs.A:X2})");
|
||||
|
||||
finished = true;
|
||||
DumpState("return");
|
||||
return cpu.regs.A == 0 ? 0 : 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"\n ✗ RUNAWAY: PC=0x{pc:X4} at cycle {cpu.Tact}");
|
||||
Console.WriteLine($" Last DBG: 0x{lastDbgStatus:X2} at cycle {lastDbgCycle}");
|
||||
Console.ResetColor();
|
||||
DumpState("runaway");
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Watchdog: cycles without debug port activity ──
|
||||
if (cpu.Tact - lastDbgCycle > watchdogLimit && lastDbgCycle > 0)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"\n ✗ WATCHDOG: {cpu.Tact - lastDbgCycle} cycles since last debug output");
|
||||
Console.WriteLine($" Last DBG: 0x{lastDbgStatus:X2} at cycle {lastDbgCycle}");
|
||||
Console.WriteLine($" PC=0x{cpu.regs.PC:X4} SP=0x{cpu.regs.SP:X4}");
|
||||
Console.ResetColor();
|
||||
PrintCpuState();
|
||||
if (dumpOnHang)
|
||||
DumpState("watchdog");
|
||||
return 4;
|
||||
}
|
||||
|
||||
// ── Watchdog: max total cycles ──
|
||||
if (cpu.Tact > maxCycles)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"\n ✗ MAX CYCLES ({maxCycles}) exceeded");
|
||||
Console.ResetColor();
|
||||
DumpState("maxcycles");
|
||||
return 5;
|
||||
}
|
||||
|
||||
// ── HALT detection ──
|
||||
if (cpu.HALTED)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"\n ✗ HALT at PC=0x{cpu.regs.PC:X4}, cycle {cpu.Tact}");
|
||||
Console.ResetColor();
|
||||
DumpState("halt");
|
||||
return 6;
|
||||
}
|
||||
|
||||
// ── Execute one instruction ──
|
||||
cpu.ExecCycle();
|
||||
cycleCount++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void PrintCpuState()
|
||||
{
|
||||
var r = cpu.regs;
|
||||
Console.WriteLine($" AF={r.AF:X4} BC={r.BC:X4} DE={r.DE:X4} HL={r.HL:X4}");
|
||||
Console.WriteLine($" AF'={r._AF:X4} BC'={r._BC:X4} DE'={r._DE:X4} HL'={r._HL:X4}");
|
||||
Console.WriteLine($" IX={r.IX:X4} IY={r.IY:X4} SP={r.SP:X4} PC={r.PC:X4}");
|
||||
Console.WriteLine($" Win0=pg{mem.GetWindow(0):X2} Win1=pg{mem.GetWindow(1):X2} Win2=pg{mem.GetWindow(2):X2} Win3=pg{mem.GetWindow(3):X2}");
|
||||
|
||||
// Disassemble a few instructions around PC
|
||||
Console.Write(" Code @PC: ");
|
||||
for (int i = 0; i < 8; i++)
|
||||
Console.Write($"{mem.Read((ushort)(r.PC + i)):X2} ");
|
||||
Console.WriteLine();
|
||||
|
||||
// Stack dump
|
||||
Console.Write(" Stack: ");
|
||||
for (int i = 0; i < 8; i++)
|
||||
Console.Write($"{mem.Read((ushort)(r.SP + i)):X2} ");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
static void DumpState(string reason)
|
||||
{
|
||||
if (!dumpOnHang) return;
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(dumpDir);
|
||||
|
||||
// Dump inflate page (Win3)
|
||||
string inflFile = Path.Combine(dumpDir, $"{reason}_inflate_page_{INFLATE_PAGE:X2}.bin");
|
||||
File.WriteAllBytes(inflFile, mem.GetPage(INFLATE_PAGE));
|
||||
Console.WriteLine($" Dumped: {inflFile}");
|
||||
|
||||
// Dump decode buffer page
|
||||
string decFile = Path.Combine(dumpDir, $"{reason}_decode_page_{PAGE_DECODE:X2}.bin");
|
||||
File.WriteAllBytes(decFile, mem.GetPage(PAGE_DECODE));
|
||||
Console.WriteLine($" Dumped: {decFile}");
|
||||
|
||||
// Dump destination pages
|
||||
for (int p = DST_START_PAGE; p < DST_START_PAGE + 16; p++)
|
||||
{
|
||||
byte[] pg = mem.GetPage(p);
|
||||
bool allZero = true;
|
||||
for (int i = 0; i < pg.Length && allZero; i++)
|
||||
if (pg[i] != 0) allZero = false;
|
||||
if (!allZero)
|
||||
{
|
||||
string dstFile = Path.Combine(dumpDir, $"{reason}_dst_page_{p:X2}.bin");
|
||||
File.WriteAllBytes(dstFile, pg);
|
||||
}
|
||||
}
|
||||
Console.WriteLine($" Dumped destination pages (0x{DST_START_PAGE:X2}+)");
|
||||
|
||||
// Dump Win3 area (the whole mapped page, including runtime modifications)
|
||||
string win3File = Path.Combine(dumpDir, $"{reason}_win3_mapped.bin");
|
||||
File.WriteAllBytes(win3File, mem.GetPage(mem.GetWindow(3)));
|
||||
Console.WriteLine($" Dumped: {win3File} (Win3=page 0x{mem.GetWindow(3):X2})");
|
||||
|
||||
// CPU state dump
|
||||
string stateFile = Path.Combine(dumpDir, $"{reason}_cpu_state.txt");
|
||||
using (var sw = new StreamWriter(stateFile))
|
||||
{
|
||||
var r = cpu.regs;
|
||||
sw.WriteLine($"Reason: {reason}");
|
||||
sw.WriteLine($"Tact: {cpu.Tact}");
|
||||
sw.WriteLine($"AF={r.AF:X4} BC={r.BC:X4} DE={r.DE:X4} HL={r.HL:X4}");
|
||||
sw.WriteLine($"AF'={r._AF:X4} BC'={r._BC:X4} DE'={r._DE:X4} HL'={r._HL:X4}");
|
||||
sw.WriteLine($"IX={r.IX:X4} IY={r.IY:X4} SP={r.SP:X4} PC={r.PC:X4}");
|
||||
sw.WriteLine($"Win0=0x{mem.GetWindow(0):X2} Win1=0x{mem.GetWindow(1):X2} Win2=0x{mem.GetWindow(2):X2} Win3=0x{mem.GetWindow(3):X2}");
|
||||
sw.WriteLine($"Last DBG status=0x{lastDbgStatus:X2} src=0x{lastDbgSrc:X2} dst=0x{lastDbgDst:X2}");
|
||||
sw.WriteLine($"Last DBG at cycle {lastDbgCycle}");
|
||||
sw.WriteLine();
|
||||
|
||||
// Disassemble around PC
|
||||
sw.Write("Code @PC: ");
|
||||
for (int i = 0; i < 32; i++)
|
||||
sw.Write($"{mem.Read((ushort)(r.PC + i)):X2} ");
|
||||
sw.WriteLine();
|
||||
|
||||
sw.Write("Stack: ");
|
||||
for (int i = 0; i < 16; i++)
|
||||
sw.Write($"{mem.Read((ushort)(r.SP + i)):X2} ");
|
||||
sw.WriteLine();
|
||||
}
|
||||
Console.WriteLine($" Dumped: {stateFile}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($" Dump error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
static void AnalyzeResults()
|
||||
{
|
||||
if (refPath == null || !File.Exists(refPath))
|
||||
{
|
||||
Console.WriteLine("No ref.vgm found, skipping comparison.");
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] refData = File.ReadAllBytes(refPath);
|
||||
Console.WriteLine($"ref.vgm: {refData.Length} bytes");
|
||||
|
||||
// Collect decompressed output from destination pages
|
||||
int refPages = (refData.Length + TsMemory.PageSize - 1) / TsMemory.PageSize;
|
||||
byte[] output = mem.ReadPages(DST_START_PAGE, refPages);
|
||||
|
||||
// Compare
|
||||
int firstDiff = -1;
|
||||
int matchBytes = 0;
|
||||
int compareLen = Math.Min(refData.Length, output.Length);
|
||||
|
||||
for (int i = 0; i < compareLen; i++)
|
||||
{
|
||||
if (output[i] == refData[i])
|
||||
matchBytes++;
|
||||
else if (firstDiff < 0)
|
||||
firstDiff = i;
|
||||
}
|
||||
|
||||
if (firstDiff < 0 && matchBytes == refData.Length)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
Console.WriteLine($" ✓ PERFECT MATCH: {matchBytes} bytes identical to ref.vgm");
|
||||
Console.ResetColor();
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($" ✗ MISMATCH at offset 0x{firstDiff:X}");
|
||||
Console.WriteLine($" Matched: {matchBytes}/{refData.Length} bytes ({100.0 * matchBytes / refData.Length:F1}%)");
|
||||
Console.ResetColor();
|
||||
|
||||
if (firstDiff >= 0)
|
||||
{
|
||||
Console.Write(" ref: ");
|
||||
for (int i = Math.Max(0, firstDiff - 4); i < Math.Min(refData.Length, firstDiff + 12); i++)
|
||||
Console.Write(i == firstDiff ? $"[{refData[i]:X2}]" : $" {refData[i]:X2} ");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.Write(" got: ");
|
||||
for (int i = Math.Max(0, firstDiff - 4); i < Math.Min(output.Length, firstDiff + 12); i++)
|
||||
Console.Write(i == firstDiff ? $"[{output[i]:X2}]" : $" {output[i]:X2} ");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
// Save output for analysis
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(dumpDir);
|
||||
string outFile = Path.Combine(dumpDir, "output.vgm");
|
||||
byte[] trimmed = new byte[refData.Length];
|
||||
Array.Copy(output, trimmed, Math.Min(output.Length, trimmed.Length));
|
||||
File.WriteAllBytes(outFile, trimmed);
|
||||
Console.WriteLine($" Saved decompressed output: {outFile}");
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
// TsMemory — TSConfig 256-page × 16KB paged memory with page protection
|
||||
// Enhanced: write protection, dirty tracking, violation logging
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace InflateDebugger
|
||||
{
|
||||
public class TsMemory
|
||||
{
|
||||
public const int PageSize = 0x4000; // 16KB
|
||||
public const int TotalPages = 256; // 4 MB
|
||||
|
||||
// Physical memory: 256 pages × 16KB
|
||||
private readonly byte[][] _pages;
|
||||
|
||||
// Window → physical page mapping
|
||||
// Win0 = 0x0000–0x3FFF, Win1 = 0x4000–0x7FFF,
|
||||
// Win2 = 0x8000–0xBFFF, Win3 = 0xC000–0xFFFF
|
||||
private readonly byte[] _winPage = new byte[4];
|
||||
|
||||
// TSConfig page ports
|
||||
public const ushort PORT_W0 = 0x10AF;
|
||||
public const ushort PORT_W1 = 0x11AF;
|
||||
public const ushort PORT_W2 = 0x12AF;
|
||||
public const ushort PORT_W3 = 0x13AF;
|
||||
|
||||
// ── Page protection ──────────────────────────────────────────
|
||||
|
||||
/// <summary>Set of physical page numbers that are write-protected</summary>
|
||||
private readonly HashSet<int> _protectedPages = new();
|
||||
|
||||
/// <summary>Pages that have been written to (dirty tracking)</summary>
|
||||
private readonly HashSet<int> _dirtyPages = new();
|
||||
|
||||
/// <summary>Protection violation log</summary>
|
||||
public List<ProtectionViolation> Violations { get; } = new();
|
||||
|
||||
/// <summary>Max violations to log before stopping (0 = unlimited)</summary>
|
||||
public int MaxViolations { get; set; } = 100;
|
||||
|
||||
/// <summary>If true, throw on protection violation</summary>
|
||||
public bool ThrowOnViolation { get; set; } = false;
|
||||
|
||||
/// <summary>Callback invoked on every write. Args: z80addr, page, offset, value</summary>
|
||||
public Action<ushort, byte, int, byte> OnWrite { get; set; }
|
||||
|
||||
/// <summary>Callback invoked on protection violation</summary>
|
||||
public Action<ProtectionViolation> OnViolation { get; set; }
|
||||
|
||||
public TsMemory()
|
||||
{
|
||||
_pages = new byte[TotalPages][];
|
||||
for (int i = 0; i < TotalPages; i++)
|
||||
_pages[i] = new byte[PageSize];
|
||||
}
|
||||
|
||||
// ── Page protection API ──────────────────────────────────────
|
||||
|
||||
public void ProtectPage(int page) => _protectedPages.Add(page);
|
||||
public void UnprotectPage(int page) => _protectedPages.Remove(page);
|
||||
public void ProtectRange(int startPage, int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++) _protectedPages.Add(startPage + i);
|
||||
}
|
||||
public void UnprotectRange(int startPage, int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++) _protectedPages.Remove(startPage + i);
|
||||
}
|
||||
public bool IsProtected(int page) => _protectedPages.Contains(page);
|
||||
public void ClearProtection() => _protectedPages.Clear();
|
||||
|
||||
public IReadOnlyCollection<int> DirtyPages => _dirtyPages;
|
||||
public void ClearDirtyTracking() => _dirtyPages.Clear();
|
||||
|
||||
// ── Window management ────────────────────────────────────────
|
||||
|
||||
/// <summary>Set window mapping (0–3) to physical page</summary>
|
||||
public void SetWindow(int win, byte page)
|
||||
{
|
||||
_winPage[win] = page;
|
||||
}
|
||||
|
||||
public byte GetWindow(int win) => _winPage[win];
|
||||
|
||||
// ── Memory access ────────────────────────────────────────────
|
||||
|
||||
/// <summary>Read byte at 16-bit Z80 address through page windows</summary>
|
||||
public byte Read(ushort addr)
|
||||
{
|
||||
int win = addr >> 14; // 0–3
|
||||
int offset = addr & 0x3FFF;
|
||||
return _pages[_winPage[win]][offset];
|
||||
}
|
||||
|
||||
/// <summary>Write byte at 16-bit Z80 address (with protection check)</summary>
|
||||
public void Write(ushort addr, byte val)
|
||||
{
|
||||
int win = addr >> 14;
|
||||
int offset = addr & 0x3FFF;
|
||||
byte page = _winPage[win];
|
||||
|
||||
_dirtyPages.Add(page);
|
||||
OnWrite?.Invoke(addr, page, offset, val);
|
||||
|
||||
if (_protectedPages.Contains(page))
|
||||
{
|
||||
var v = new ProtectionViolation
|
||||
{
|
||||
Z80Addr = addr,
|
||||
PhysPage = page,
|
||||
Offset = offset,
|
||||
Value = val,
|
||||
OldValue = _pages[page][offset],
|
||||
Window = win
|
||||
};
|
||||
if (Violations.Count < MaxViolations || MaxViolations == 0)
|
||||
Violations.Add(v);
|
||||
OnViolation?.Invoke(v);
|
||||
if (ThrowOnViolation)
|
||||
throw new ProtectionViolationException(v);
|
||||
}
|
||||
|
||||
_pages[page][offset] = val;
|
||||
}
|
||||
|
||||
/// <summary>Direct write bypassing protection (for initial data loading)</summary>
|
||||
public void WriteUnprotected(ushort addr, byte val)
|
||||
{
|
||||
int win = addr >> 14;
|
||||
int offset = addr & 0x3FFF;
|
||||
_pages[_winPage[win]][offset] = val;
|
||||
}
|
||||
|
||||
/// <summary>Direct access to a physical page</summary>
|
||||
public byte[] GetPage(int page) => _pages[page];
|
||||
|
||||
/// <summary>Load data into physical pages starting at startPage</summary>
|
||||
public int LoadToPages(byte[] data, int startPage)
|
||||
{
|
||||
int pagesNeeded = (data.Length + PageSize - 1) / PageSize;
|
||||
for (int p = 0; p < pagesNeeded; p++)
|
||||
{
|
||||
int srcOff = p * PageSize;
|
||||
int len = Math.Min(PageSize, data.Length - srcOff);
|
||||
Array.Copy(data, srcOff, _pages[startPage + p], 0, len);
|
||||
}
|
||||
return pagesNeeded;
|
||||
}
|
||||
|
||||
/// <summary>Handle OUT to TSConfig page port</summary>
|
||||
public bool HandlePort(ushort port, byte value)
|
||||
{
|
||||
switch (port)
|
||||
{
|
||||
case PORT_W0: SetWindow(0, value); return true;
|
||||
case PORT_W1: SetWindow(1, value); return true;
|
||||
case PORT_W2: SetWindow(2, value); return true;
|
||||
case PORT_W3: SetWindow(3, value); return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Read all output pages (destination) into a flat array</summary>
|
||||
public byte[] ReadPages(int startPage, int count)
|
||||
{
|
||||
byte[] result = new byte[count * PageSize];
|
||||
for (int p = 0; p < count; p++)
|
||||
Array.Copy(_pages[startPage + p], 0, result, p * PageSize, PageSize);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>Update WC system variable area (0x6000–0x600E)</summary>
|
||||
public void UpdateSysVars()
|
||||
{
|
||||
byte sysPage = _winPage[1];
|
||||
_pages[sysPage][0x2000] = _winPage[0]; // 0x6000 = PG0
|
||||
_pages[sysPage][0x2001] = _winPage[1]; // 0x6001 = PG4
|
||||
_pages[sysPage][0x2002] = _winPage[2]; // 0x6002 = PG8
|
||||
_pages[sysPage][0x2003] = _winPage[3]; // 0x6003 = PGC
|
||||
}
|
||||
|
||||
/// <summary>Dump 16 bytes around an address for debugging</summary>
|
||||
public string HexDump(ushort addr, int before = 8, int after = 8)
|
||||
{
|
||||
int start = Math.Max(0, addr - before);
|
||||
int end = Math.Min(0xFFFF, addr + after);
|
||||
var sb = new System.Text.StringBuilder();
|
||||
for (int a = start; a <= end; a++)
|
||||
{
|
||||
if (a == addr) sb.Append('[');
|
||||
sb.AppendFormat("{0:X2}", Read((ushort)a));
|
||||
if (a == addr) sb.Append(']');
|
||||
else sb.Append(' ');
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
// ── Protection violation types ───────────────────────────────────
|
||||
|
||||
public struct ProtectionViolation
|
||||
{
|
||||
public ushort Z80Addr;
|
||||
public byte PhysPage;
|
||||
public int Offset;
|
||||
public byte Value;
|
||||
public byte OldValue;
|
||||
public int Window;
|
||||
|
||||
public override string ToString() =>
|
||||
$"PROTECT VIOLATION: [{Z80Addr:X4}] pg={PhysPage:X2} off={Offset:X4} " +
|
||||
$"old={OldValue:X2} new={Value:X2} win={Window}";
|
||||
}
|
||||
|
||||
public class ProtectionViolationException : Exception
|
||||
{
|
||||
public ProtectionViolation Violation { get; }
|
||||
public ProtectionViolationException(ProtectionViolation v)
|
||||
: base(v.ToString()) { Violation = v; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,779 @@
|
||||
// WcKernel — WC (Wild Commander) API emulator for the C# debugger
|
||||
// Traps CALL 0x6006 and emulates function dispatch based on A register
|
||||
//
|
||||
// Emulated functions (used by VGM player):
|
||||
// 0x00 = MNGC_PL — plugin page → Win3
|
||||
// 0x01 = PRWOW — draw window (stub)
|
||||
// 0x02 = RRESB — restore window (stub)
|
||||
// 0x05 = GADRW — get screen address
|
||||
// 0x0E = TURBOPL — CPU speed (stub)
|
||||
// 0x0F = GEDPL — restore display (stub)
|
||||
// 0x30 = LOAD512 — load sectors from file → memory
|
||||
// 0x41 = MNGCVPL — video page → Win3
|
||||
// 0x4E = MNG0_PL — plugin page → Win0
|
||||
// 0x4F = MNG8_PL — plugin page → Win2
|
||||
// + keyboard stubs, file stubs, etc.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using ZXMAK2.Engine.Cpu;
|
||||
using ZXMAK2.Engine.Cpu.Processor;
|
||||
|
||||
namespace InflateDebugger
|
||||
{
|
||||
public class WcKernel
|
||||
{
|
||||
// ── WC Constants ─────────────────────────────────────────────
|
||||
|
||||
public const ushort WC_ENTRY = 0x6006;
|
||||
|
||||
// Physical page bases
|
||||
public const byte PLGPG = 0x61; // plugin page base (page 0 = 0x61)
|
||||
public const byte TVBPG = 0x20; // megabuffer / video page base
|
||||
public const byte MTXPG = 0x00; // main text page
|
||||
|
||||
// System variable addresses (in Win1 = 0x4000–0x7FFF)
|
||||
public const ushort SYS_PG0 = 0x6000;
|
||||
public const ushort SYS_PG4 = 0x6001;
|
||||
public const ushort SYS_PG8 = 0x6002;
|
||||
public const ushort SYS_PGC = 0x6003;
|
||||
public const ushort SYS_ABT = 0x6004;
|
||||
public const ushort SYS_ENT = 0x6005;
|
||||
public const ushort SYS_TMN = 0x6009;
|
||||
public const ushort SYS_CNFV = 0x600D;
|
||||
public const ushort SYS_HEI = 0x600E;
|
||||
|
||||
// Function numbers (A register values for CALL 0x6006)
|
||||
public const byte FN_MNGC_PL = 0x00;
|
||||
public const byte FN_PRWOW = 0x01;
|
||||
public const byte FN_RRESB = 0x02;
|
||||
public const byte FN_PRSRW = 0x03;
|
||||
public const byte FN_TXTPR = 0x04;
|
||||
public const byte FN_GADRW = 0x05;
|
||||
public const byte FN_CURSOR = 0x06;
|
||||
public const byte FN_CURSER = 0x07;
|
||||
public const byte FN_YN = 0x08;
|
||||
public const byte FN_ISTR = 0x09;
|
||||
public const byte FN_NORK = 0x0A;
|
||||
public const byte FN_MEZZ = 0x0B;
|
||||
public const byte FN_SCRLWOW = 0x0C;
|
||||
public const byte FN_PRSRW_A = 0x0D;
|
||||
public const byte FN_TURBOPL = 0x0E;
|
||||
public const byte FN_GEDPL = 0x0F;
|
||||
|
||||
// Keyboard: 0x10–0x2F
|
||||
public const byte FN_KEY_SPACE = 0x10;
|
||||
public const byte FN_KEY_UP = 0x11;
|
||||
public const byte FN_KEY_DOWN = 0x12;
|
||||
public const byte FN_KEY_LEFT = 0x13;
|
||||
public const byte FN_KEY_RIGHT = 0x14;
|
||||
public const byte FN_KEY_TAB = 0x15;
|
||||
public const byte FN_KEY_ENTER = 0x16;
|
||||
public const byte FN_KEY_ESC = 0x17;
|
||||
public const byte FN_KEY_BSPC = 0x18;
|
||||
public const byte FN_KEY_PGUP = 0x19;
|
||||
public const byte FN_KEY_PGDN = 0x1A;
|
||||
public const byte FN_KEY_HOME = 0x1B;
|
||||
public const byte FN_KEY_END = 0x1C;
|
||||
public const byte FN_KBSCN = 0x1D;
|
||||
// F1–F10: 0x1E–0x27, ALT=0x28, SHIFT=0x29, CTRL=0x2A
|
||||
// DEL=0x2B, CAPS=0x2C, ANY=0x2D, WAITREL=0x2E, WAITANY=0x2F
|
||||
|
||||
public const byte FN_LOAD512 = 0x30;
|
||||
public const byte FN_SAVE512 = 0x31;
|
||||
public const byte FN_GIPAGPL = 0x32;
|
||||
public const byte FN_TENTRY = 0x33;
|
||||
public const byte FN_CHTOSEP = 0x34;
|
||||
public const byte FN_TMRKDFL = 0x35;
|
||||
public const byte FN_ADIR = 0x36;
|
||||
public const byte FN_STREAM = 0x37;
|
||||
public const byte FN_FINDNEXT = 0x38;
|
||||
public const byte FN_FENTRY = 0x39;
|
||||
public const byte FN_LOADNONE = 0x3A;
|
||||
public const byte FN_GFILE = 0x3B;
|
||||
public const byte FN_GDIR = 0x3C;
|
||||
public const byte FN_MKFILE = 0x3D;
|
||||
public const byte FN_MKDIR = 0x3E;
|
||||
public const byte FN_RENAME = 0x3F;
|
||||
public const byte FN_DELFL = 0x40;
|
||||
|
||||
public const byte FN_MNGCVPL = 0x41;
|
||||
public const byte FN_MNGV_PL = 0x42;
|
||||
public const byte FN_GVMOD = 0x43;
|
||||
public const byte FN_GYOFF = 0x44;
|
||||
public const byte FN_GXOFF = 0x45;
|
||||
public const byte FN_GVTM = 0x46;
|
||||
public const byte FN_GVTL = 0x47;
|
||||
public const byte FN_GVSGP = 0x48;
|
||||
public const byte FN_MNG0VPL = 0x49;
|
||||
public const byte FN_MNG8VPL = 0x4A;
|
||||
public const byte FN_DMAPL = 0x4B;
|
||||
public const byte FN_PRM_PL = 0x4C;
|
||||
public const byte FN_INT_PL = 0x4D;
|
||||
public const byte FN_MNG0_PL = 0x4E;
|
||||
public const byte FN_MNG8_PL = 0x4F;
|
||||
|
||||
public const byte FN_STRSET = 0x50;
|
||||
public const byte FN_LOAD256 = 0x51;
|
||||
public const byte FN_INT_PL_H = 0x52;
|
||||
public const byte FN_RES_JUMP = 0x53;
|
||||
public const byte FN_RES_CALL = 0x54;
|
||||
public const byte FN_KEY_INS = 0x55;
|
||||
|
||||
// ── State ─────────────────────────────────────────────────────
|
||||
|
||||
private readonly TsMemory _mem;
|
||||
private CpuUnit _cpu;
|
||||
|
||||
// Plugin page offset within the 64-page plugin area (typically 0)
|
||||
public byte PPGP { get; set; } = 0;
|
||||
|
||||
// Emulated file I/O state
|
||||
private byte[] _fileData; // loaded file data (whole VGM/VGZ)
|
||||
private int _filePos; // current file read position (byte offset)
|
||||
public uint FileSize => _fileData != null ? (uint)_fileData.Length : 0;
|
||||
|
||||
// Disk root directory (test_data path)
|
||||
public string DiskRootPath { get; set; }
|
||||
|
||||
// WC call log
|
||||
public List<WcCallRecord> CallLog { get; } = new();
|
||||
public bool LogCalls { get; set; } = true;
|
||||
public int MaxCallLog { get; set; } = 10000;
|
||||
|
||||
// Saved Win0 page (P8000 saves/restores it on each WC call)
|
||||
private byte _savedWin0Page;
|
||||
|
||||
// Screen height (for gadrw calculations)
|
||||
public byte ScreenHeight { get; set; } = 36; // 90×36 (TextMode=2)
|
||||
public byte ScreenWidth { get; set; } = 90;
|
||||
|
||||
// WC system page (mapped to Win1 permanently)
|
||||
public byte SysVarPage { get; set; } = 0x05;
|
||||
|
||||
// Callbacks for logging
|
||||
public Action<string> Log { get; set; }
|
||||
|
||||
// ── Constructor ───────────────────────────────────────────────
|
||||
|
||||
public WcKernel(TsMemory mem, CpuUnit cpu)
|
||||
{
|
||||
_mem = mem;
|
||||
_cpu = cpu;
|
||||
}
|
||||
|
||||
// ── File I/O emulation ────────────────────────────────────────
|
||||
|
||||
/// <summary>Load a file for emulated file I/O (simulates WC file open + stream)</summary>
|
||||
public void SetFileData(byte[] data)
|
||||
{
|
||||
_fileData = data;
|
||||
_filePos = 0;
|
||||
}
|
||||
|
||||
/// <summary>Load a file from disk root by name</summary>
|
||||
public bool OpenFile(string name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(DiskRootPath)) return false;
|
||||
string path = Path.Combine(DiskRootPath, name);
|
||||
if (!File.Exists(path)) return false;
|
||||
_fileData = File.ReadAllBytes(path);
|
||||
_filePos = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── Setup system variables ────────────────────────────────────
|
||||
|
||||
public void InitSysVars()
|
||||
{
|
||||
var pg = _mem.GetPage(SysVarPage);
|
||||
|
||||
// PG0–PGC (0x6000–0x6003 = offset 0x2000 in Win1)
|
||||
pg[0x2000] = _mem.GetWindow(0);
|
||||
pg[0x2001] = _mem.GetWindow(1);
|
||||
pg[0x2002] = _mem.GetWindow(2);
|
||||
pg[0x2003] = _mem.GetWindow(3);
|
||||
|
||||
// ABT / ENT = 0
|
||||
pg[0x2004] = 0;
|
||||
pg[0x2005] = 0;
|
||||
|
||||
// JP FUN instruction at 0x6006 (we trap this, but put real bytes for reads)
|
||||
pg[0x2006] = 0xC3; // JP
|
||||
pg[0x2007] = 0x00;
|
||||
pg[0x2008] = 0x00;
|
||||
|
||||
// TMN timer
|
||||
pg[0x2009] = 0;
|
||||
pg[0x200A] = 0;
|
||||
|
||||
// CNFv = 7 (VDAC2/FT812 — TSConfig default)
|
||||
pg[0x200D] = 7;
|
||||
|
||||
// HEI = screen height
|
||||
pg[0x200E] = ScreenHeight;
|
||||
}
|
||||
|
||||
// ── Update PGC in sys vars after page switch ──────────────────
|
||||
|
||||
private void UpdateSysPGC()
|
||||
{
|
||||
_mem.GetPage(SysVarPage)[0x2003] = _mem.GetWindow(3);
|
||||
}
|
||||
private void UpdateSysPG0()
|
||||
{
|
||||
_mem.GetPage(SysVarPage)[0x2000] = _mem.GetWindow(0);
|
||||
}
|
||||
private void UpdateSysPG8()
|
||||
{
|
||||
_mem.GetPage(SysVarPage)[0x2002] = _mem.GetWindow(2);
|
||||
}
|
||||
|
||||
// ── Main trap handler ─────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Check if CPU is about to execute CALL 0x6006 and intercept it.
|
||||
/// Call this BEFORE ExecCycle() when PC == address of CALL 0x6006.
|
||||
/// Returns true if the call was intercepted.
|
||||
/// </summary>
|
||||
public bool TryIntercept()
|
||||
{
|
||||
ushort pc = _cpu.regs.PC;
|
||||
|
||||
// Check: is the instruction at PC a CALL 0x6006?
|
||||
// CALL nn = CD lo hi → CD 06 60
|
||||
if (_mem.Read(pc) == 0xCD &&
|
||||
_mem.Read((ushort)(pc + 1)) == 0x06 &&
|
||||
_mem.Read((ushort)(pc + 2)) == 0x60)
|
||||
{
|
||||
byte funcNum = _cpu.regs.A;
|
||||
ushort retAddr = (ushort)(pc + 3);
|
||||
|
||||
// Log the call
|
||||
if (LogCalls && CallLog.Count < MaxCallLog)
|
||||
{
|
||||
CallLog.Add(new WcCallRecord
|
||||
{
|
||||
Tact = _cpu.Tact,
|
||||
PC = pc,
|
||||
FuncNum = funcNum,
|
||||
AF = _cpu.regs.AF,
|
||||
BC = _cpu.regs.BC,
|
||||
DE = _cpu.regs.DE,
|
||||
HL = _cpu.regs.HL,
|
||||
AF2 = _cpu.regs._AF,
|
||||
IX = _cpu.regs.IX,
|
||||
SP = _cpu.regs.SP
|
||||
});
|
||||
}
|
||||
|
||||
// Emulate P8000 dispatch: save Win0, set Win3=TXT
|
||||
_savedWin0Page = _mem.GetWindow(0);
|
||||
|
||||
// Execute the function
|
||||
DispatchFunction(funcNum);
|
||||
|
||||
// P8000 epilogue: restore Win0 only.
|
||||
// Do NOT reset Win3 — page management functions (MNGCPL, MNGCVPL)
|
||||
// explicitly set Win3 and the caller expects it to persist.
|
||||
_mem.SetWindow(0, _savedWin0Page);
|
||||
// Update sys vars to reflect actual window state
|
||||
UpdateSysPGC();
|
||||
UpdateSysPG0();
|
||||
|
||||
// Set PC past the CALL instruction
|
||||
_cpu.regs.PC = retAddr;
|
||||
|
||||
// Advance Tact (approximate: a typical WC call takes ~100-500 T-states)
|
||||
_cpu.Tact += 100;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ── Function dispatcher ───────────────────────────────────────
|
||||
|
||||
private void DispatchFunction(byte funcNum)
|
||||
{
|
||||
switch (funcNum)
|
||||
{
|
||||
// ── Page management ──
|
||||
case FN_MNGC_PL: Fn_MngcPl(); break;
|
||||
case FN_MNG0_PL: Fn_Mng0Pl(); break;
|
||||
case FN_MNG8_PL: Fn_Mng8Pl(); break;
|
||||
case FN_MNGCVPL: Fn_Mngcvpl(); break;
|
||||
case FN_MNG0VPL: Fn_Mng0vpl(); break;
|
||||
case FN_MNG8VPL: Fn_Mng8vpl(); break;
|
||||
|
||||
// ── UI (stubs) ──
|
||||
case FN_GEDPL: Fn_Gedpl(); break;
|
||||
case FN_PRWOW: Fn_Prwow(); break;
|
||||
case FN_RRESB: Fn_Rresb(); break;
|
||||
case FN_GADRW: Fn_Gadrw(); break;
|
||||
case FN_PRSRW: Fn_Stub("PRSRW"); break;
|
||||
case FN_PRSRW_A: Fn_Stub("PRSRW_A");break;
|
||||
case FN_TXTPR: Fn_Stub("TXTPR"); break;
|
||||
case FN_SCRLWOW: Fn_Stub("SCRLWOW"); break;
|
||||
case FN_CURSOR: Fn_Stub("CURSOR"); break;
|
||||
case FN_CURSER: Fn_Stub("CURSER"); break;
|
||||
case FN_YN: Fn_Stub("YN"); break;
|
||||
case FN_ISTR: Fn_Stub("ISTR"); break;
|
||||
case FN_NORK: Fn_Stub("NORK"); break;
|
||||
case FN_MEZZ: Fn_Stub("MEZZ"); break;
|
||||
|
||||
// ── Turbo / misc ──
|
||||
case FN_TURBOPL: Fn_Turbopl(); break;
|
||||
case FN_PRM_PL: Fn_PrmPl(); break;
|
||||
case FN_INT_PL: Fn_Stub("INT_PL"); break;
|
||||
case FN_INT_PL_H: Fn_Stub("INT_PL_H");break;
|
||||
case FN_STRSET: Fn_Strset(); break;
|
||||
case FN_DMAPL: Fn_Stub("DMAPL"); break;
|
||||
|
||||
// ── Keyboard (all return 0 = not pressed) ──
|
||||
case >= FN_KEY_SPACE and <= 0x2F:
|
||||
Fn_KeyStub(funcNum);
|
||||
break;
|
||||
|
||||
// ── File I/O ──
|
||||
case FN_LOAD512: Fn_Load512(); break;
|
||||
case FN_LOAD256: Fn_Load256(); break;
|
||||
case FN_SAVE512: Fn_Stub("SAVE512"); break;
|
||||
case FN_GIPAGPL: Fn_Gipagpl(); break;
|
||||
case FN_STREAM: Fn_Stream(); break;
|
||||
case FN_FENTRY: Fn_Fentry(); break;
|
||||
case FN_GFILE: Fn_Gfile(); break;
|
||||
case FN_ADIR: Fn_Stub("ADIR"); break;
|
||||
case FN_FINDNEXT: Fn_Stub("FINDNEXT");break;
|
||||
case FN_TENTRY: Fn_Stub("TENTRY"); break;
|
||||
case FN_GDIR: Fn_Stub("GDIR"); break;
|
||||
case FN_LOADNONE: Fn_Loadnone(); break;
|
||||
|
||||
// ── Graphics (stubs) ──
|
||||
case FN_MNGV_PL: Fn_Stub("MNGV_PL"); break;
|
||||
case FN_GVMOD: Fn_Stub("GVMOD"); break;
|
||||
case FN_GYOFF: Fn_Stub("GYOFF"); break;
|
||||
case FN_GXOFF: Fn_Stub("GXOFF"); break;
|
||||
|
||||
// ── Resident calls ──
|
||||
case FN_RES_JUMP: Fn_Stub("RES_JUMP"); break;
|
||||
case FN_RES_CALL: Fn_Stub("RES_CALL"); break;
|
||||
|
||||
default:
|
||||
Log?.Invoke($" WC UNKNOWN function 0x{funcNum:X2} at tact={_cpu.Tact}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Page management functions
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
/// <summary>0x00 MNGC_PL: plugin page A' → Win3. phys = A' + PLGPG + PPGP</summary>
|
||||
private void Fn_MngcPl()
|
||||
{
|
||||
byte pageArg = (byte)(_cpu.regs._AF >> 8); // A' register
|
||||
|
||||
if (pageArg == 0xFE)
|
||||
{
|
||||
// 0xFE = first text screen
|
||||
_mem.SetWindow(3, MTXPG);
|
||||
}
|
||||
else if (pageArg == 0xFF)
|
||||
{
|
||||
// 0xFF = font page
|
||||
_mem.SetWindow(3, 0x01);
|
||||
}
|
||||
else if (pageArg < 64)
|
||||
{
|
||||
byte phys = (byte)(pageArg + PLGPG + PPGP);
|
||||
_mem.SetWindow(3, phys);
|
||||
}
|
||||
// else >= 64: NOP (return without changing)
|
||||
|
||||
UpdateSysPGC();
|
||||
}
|
||||
|
||||
/// <summary>0x4E MNG0_PL: plugin page A' → Win0. phys = A' + PLGPG + PPGP</summary>
|
||||
private void Fn_Mng0Pl()
|
||||
{
|
||||
byte pageArg = (byte)(_cpu.regs._AF >> 8);
|
||||
if (pageArg < 64)
|
||||
{
|
||||
byte phys = (byte)(pageArg + PLGPG + PPGP);
|
||||
_mem.SetWindow(0, phys);
|
||||
UpdateSysPG0();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>0x4F MNG8_PL: plugin page A' → Win2. phys = A' + PLGPG + PPGP</summary>
|
||||
private void Fn_Mng8Pl()
|
||||
{
|
||||
byte pageArg = (byte)(_cpu.regs._AF >> 8);
|
||||
if (pageArg < 64)
|
||||
{
|
||||
byte phys = (byte)(pageArg + PLGPG + PPGP);
|
||||
_mem.SetWindow(2, phys);
|
||||
UpdateSysPG8();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>0x41 MNGCVPL: video page A' → Win3. phys = A' + TVBPG(0x20)</summary>
|
||||
private void Fn_Mngcvpl()
|
||||
{
|
||||
byte vpage = (byte)(_cpu.regs._AF >> 8);
|
||||
if (vpage < 64)
|
||||
{
|
||||
byte phys = (byte)(vpage + TVBPG);
|
||||
_mem.SetWindow(3, phys);
|
||||
}
|
||||
UpdateSysPGC();
|
||||
}
|
||||
|
||||
/// <summary>0x49 MNG0VPL: video page A' → Win0</summary>
|
||||
private void Fn_Mng0vpl()
|
||||
{
|
||||
byte vpage = (byte)(_cpu.regs._AF >> 8);
|
||||
if (vpage < 64)
|
||||
{
|
||||
byte phys = (byte)(vpage + TVBPG);
|
||||
_mem.SetWindow(0, phys);
|
||||
UpdateSysPG0();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>0x4A MNG8VPL: video page A' → Win2</summary>
|
||||
private void Fn_Mng8vpl()
|
||||
{
|
||||
byte vpage = (byte)(_cpu.regs._AF >> 8);
|
||||
if (vpage < 64)
|
||||
{
|
||||
byte phys = (byte)(vpage + TVBPG);
|
||||
_mem.SetWindow(2, phys);
|
||||
UpdateSysPG8();
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// UI functions (mostly stubs for text-mode UI)
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
/// <summary>0x0F GEDPL: restore WC display (stub — sets Win3 to TXT)</summary>
|
||||
private void Fn_Gedpl()
|
||||
{
|
||||
// Real WC: restores palette, attributes, text mode
|
||||
// Also trashes 0x1000–0x1447 in Win0 — we emulate that
|
||||
_mem.SetWindow(3, MTXPG);
|
||||
UpdateSysPGC();
|
||||
Log?.Invoke(" WC GEDPL (stub)");
|
||||
}
|
||||
|
||||
/// <summary>0x01 PRWOW: draw window (stub)</summary>
|
||||
private void Fn_Prwow()
|
||||
{
|
||||
// IX = win ptr (from HL in sdcccall)
|
||||
_mem.SetWindow(3, MTXPG);
|
||||
UpdateSysPGC();
|
||||
}
|
||||
|
||||
/// <summary>0x02 RRESB: restore window background (stub)</summary>
|
||||
private void Fn_Rresb()
|
||||
{
|
||||
_mem.SetWindow(3, MTXPG);
|
||||
UpdateSysPGC();
|
||||
}
|
||||
|
||||
/// <summary>0x05 GADRW: get screen address for (y,x) in window</summary>
|
||||
private void Fn_Gadrw()
|
||||
{
|
||||
// In wc_api.s: IX=win, D=y, E=x
|
||||
// Returns DE = screen address
|
||||
// Text screen at 0xC000, each row = ScreenWidth bytes
|
||||
// Window content starts at win.x + win.y * ScreenWidth + 0xC000
|
||||
// We compute: addr = 0xC000 + (win_y + y) * ScreenWidth + (win_x + x)
|
||||
|
||||
ushort winPtr = _cpu.regs.IX;
|
||||
byte wy = _mem.Read((ushort)(winPtr + 3)); // win.y at +3
|
||||
byte wx = _mem.Read((ushort)(winPtr + 2)); // win.x at +2
|
||||
byte y = _cpu.regs.D;
|
||||
byte x = _cpu.regs.E;
|
||||
|
||||
ushort addr = (ushort)(0xC000 + (wy + y) * ScreenWidth + (wx + x));
|
||||
_cpu.regs.DE = addr;
|
||||
|
||||
// Also need to set Win3 to TXT
|
||||
_mem.SetWindow(3, MTXPG);
|
||||
UpdateSysPGC();
|
||||
}
|
||||
|
||||
/// <summary>0x0E TURBOPL: set CPU/AY speed via SYSCONF port</summary>
|
||||
private void Fn_Turbopl()
|
||||
{
|
||||
// B = mode (0=CPU, 1=AY, 0xFF=restore), C = param
|
||||
byte mode = (byte)(_cpu.regs.BC >> 8); // B
|
||||
byte param = (byte)(_cpu.regs.BC & 0xFF); // C
|
||||
|
||||
if (mode == 0x00) // WC_TURBO_CPU: set CPU frequency
|
||||
{
|
||||
// param: 0=3.5, 1=7, 2=14, 3=28
|
||||
// Write turbo bits to SYSCONF port so PluginRunner picks it up
|
||||
byte sysconf = (byte)(param & 0x03);
|
||||
_cpu.WRPORT(0x20AF, sysconf);
|
||||
}
|
||||
// mode 1 (AY freq) — not relevant for emulation
|
||||
// mode 0xFF (restore) — restore to 3.5 MHz
|
||||
else if (mode == 0xFF)
|
||||
{
|
||||
_cpu.WRPORT(0x20AF, 0x00); // restore to 3.5 MHz
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>0x4C PRM_PL: get INI parameter (stub — return 0xFF = not set)</summary>
|
||||
private void Fn_PrmPl()
|
||||
{
|
||||
_cpu.regs.A = 0xFF;
|
||||
}
|
||||
|
||||
/// <summary>0x50 STRSET: memset(ptr, len, char)</summary>
|
||||
private void Fn_Strset()
|
||||
{
|
||||
// In wc_api.s: HL=dst, DE=len, A'=char
|
||||
// We actually don't need real WC for this, but the plugin calls it
|
||||
ushort dst = _cpu.regs.HL;
|
||||
ushort len = _cpu.regs.DE;
|
||||
byte ch = (byte)(_cpu.regs._AF >> 8);
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
_mem.Write((ushort)(dst + i), ch);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Keyboard stubs (all return A=0 = not pressed)
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
private void Fn_KeyStub(byte funcNum)
|
||||
{
|
||||
_cpu.regs.A = 0; // not pressed
|
||||
// Clear Z flag by default (A=0 → Z=1 → key not pressed)
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// File I/O emulation
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
/// <summary>0x30 LOAD512: load blocks×512 bytes from file → memory at dest</summary>
|
||||
private void Fn_Load512()
|
||||
{
|
||||
// wc_api.s _wc_load512: HL=dest, B=blocks (from stack via frame pointer)
|
||||
// WC API #0x30: HL=dest, B=blocks → A=0 ok / 0x0F EOF
|
||||
// Returns: A = status, HL = next address after loaded data
|
||||
|
||||
ushort loadDest = _cpu.regs.HL;
|
||||
byte blocks = _cpu.regs.B;
|
||||
int bytesToLoad = blocks * 512;
|
||||
|
||||
if (_fileData == null)
|
||||
{
|
||||
Log?.Invoke(" WC LOAD512: no file data!");
|
||||
_cpu.regs.A = 0xFF; // error
|
||||
return;
|
||||
}
|
||||
|
||||
int remaining = _fileData.Length - _filePos;
|
||||
int actual = Math.Min(bytesToLoad, remaining);
|
||||
bool isEof = (actual < bytesToLoad) || (_filePos + actual >= _fileData.Length);
|
||||
|
||||
// Write data into memory at loadDest (using Win3 map)
|
||||
for (int i = 0; i < actual; i++)
|
||||
{
|
||||
_mem.Write((ushort)(loadDest + i), _fileData[_filePos + i]);
|
||||
}
|
||||
_filePos += actual;
|
||||
|
||||
// Update HL to point past the loaded data
|
||||
_cpu.regs.HL = (ushort)(loadDest + actual);
|
||||
|
||||
_cpu.regs.A = isEof ? (byte)0x0F : (byte)0; // 0x0F = WC_EOF
|
||||
|
||||
Log?.Invoke($" LOAD512: dest=0x{loadDest:X4} blocks={blocks} bytes={actual} eof={isEof} filePos={_filePos}/{_fileData.Length}");
|
||||
}
|
||||
|
||||
/// <summary>0x51 LOAD256: load blocks×256 bytes</summary>
|
||||
private void Fn_Load256()
|
||||
{
|
||||
// Same pattern as LOAD512 but 256-byte blocks: HL=dest, B=blocks
|
||||
ushort loadDest = _cpu.regs.HL;
|
||||
byte blocks = _cpu.regs.B;
|
||||
int bytesToLoad = blocks * 256;
|
||||
|
||||
if (_fileData == null) { _cpu.regs.A = 0xFF; return; }
|
||||
|
||||
int remaining = _fileData.Length - _filePos;
|
||||
int actual = Math.Min(bytesToLoad, remaining);
|
||||
bool isEof = actual < bytesToLoad || _filePos + actual >= _fileData.Length;
|
||||
|
||||
for (int i = 0; i < actual; i++)
|
||||
_mem.Write((ushort)(loadDest + i), _fileData[_filePos + i]);
|
||||
_filePos += actual;
|
||||
|
||||
_cpu.regs.HL = (ushort)(loadDest + actual);
|
||||
_cpu.regs.A = isEof ? (byte)0x0F : (byte)0;
|
||||
|
||||
Log?.Invoke($" LOAD256: dest=0x{loadDest:X4} blocks={blocks} bytes={actual} eof={isEof}");
|
||||
}
|
||||
|
||||
/// <summary>0x32 GIPAGPL: seek file to beginning</summary>
|
||||
private void Fn_Gipagpl()
|
||||
{
|
||||
_filePos = 0;
|
||||
}
|
||||
|
||||
/// <summary>0x37 STREAM: open/switch file stream (stub — just log)</summary>
|
||||
private void Fn_Stream()
|
||||
{
|
||||
// D = mode (0xFF=root, 0xFE=clone, 0xFD=wcdir, 0/1=stream#)
|
||||
Log?.Invoke($" WC STREAM mode=0x{_cpu.regs.D:X2}");
|
||||
}
|
||||
|
||||
/// <summary>0x39 FENTRY: find file by name</summary>
|
||||
private void Fn_Fentry()
|
||||
{
|
||||
// HL = addr of [flag_byte][name_string][0]
|
||||
ushort namePtr = _cpu.regs.HL;
|
||||
byte flag = _mem.Read(namePtr);
|
||||
namePtr++;
|
||||
|
||||
// Read filename
|
||||
var sb = new StringBuilder();
|
||||
for (int i = 0; i < 255; i++)
|
||||
{
|
||||
byte ch = _mem.Read((ushort)(namePtr + i));
|
||||
if (ch == 0) break;
|
||||
sb.Append((char)ch);
|
||||
}
|
||||
string fileName = sb.ToString();
|
||||
Log?.Invoke($" WC FENTRY: \"{fileName}\" flag=0x{flag:X2}");
|
||||
|
||||
// Try to find the file on disk root
|
||||
if (!string.IsNullOrEmpty(DiskRootPath))
|
||||
{
|
||||
string path = Path.Combine(DiskRootPath, fileName);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
_cpu.regs.A = 1; // found (non-zero)
|
||||
// Set file size in DE:HL
|
||||
long fsize = new FileInfo(path).Length;
|
||||
_cpu.regs.HL = (ushort)(fsize & 0xFFFF);
|
||||
_cpu.regs.DE = (ushort)((fsize >> 16) & 0xFFFF);
|
||||
|
||||
// Store path for subsequent GFILE + LOAD512
|
||||
_pendingFileName = path;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_cpu.regs.A = 0; // not found
|
||||
}
|
||||
|
||||
private string _pendingFileName;
|
||||
|
||||
/// <summary>0x3B GFILE: open found file for reading</summary>
|
||||
private void Fn_Gfile()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_pendingFileName) && File.Exists(_pendingFileName))
|
||||
{
|
||||
_fileData = File.ReadAllBytes(_pendingFileName);
|
||||
_filePos = 0;
|
||||
Log?.Invoke($" WC GFILE: opened \"{_pendingFileName}\" ({_fileData.Length} bytes)");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log?.Invoke(" WC GFILE: no pending file");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>0x3A LOADNONE: skip N sectors in stream</summary>
|
||||
private void Fn_Loadnone()
|
||||
{
|
||||
byte sectors = (byte)(_cpu.regs._AF >> 8);
|
||||
int skip = sectors * 512;
|
||||
if (_fileData != null)
|
||||
_filePos = Math.Min(_filePos + skip, _fileData.Length);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Stub for unimplemented functions
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
private void Fn_Stub(string name)
|
||||
{
|
||||
// Most stubs just need Win3 = TXT (already done in P8000 epilogue)
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Call log
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
public string DumpCallLog(int last = 50)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"=== WC Call Log (last {last} of {CallLog.Count}) ===");
|
||||
int start = Math.Max(0, CallLog.Count - last);
|
||||
for (int i = start; i < CallLog.Count; i++)
|
||||
{
|
||||
var c = CallLog[i];
|
||||
sb.AppendFormat("[{0,10}] PC={1:X4} FN=0x{2:X2} ({3}) AF={4:X4} BC={5:X4} DE={6:X4} HL={7:X4} IX={8:X4}",
|
||||
c.Tact, c.PC, c.FuncNum, GetFuncName(c.FuncNum),
|
||||
c.AF, c.BC, c.DE, c.HL, c.IX);
|
||||
sb.AppendLine();
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string GetFuncName(byte fn) => fn switch
|
||||
{
|
||||
FN_MNGC_PL => "MNGC_PL",
|
||||
FN_PRWOW => "PRWOW",
|
||||
FN_RRESB => "RRESB",
|
||||
FN_PRSRW => "PRSRW",
|
||||
FN_TXTPR => "TXTPR",
|
||||
FN_GADRW => "GADRW",
|
||||
FN_TURBOPL => "TURBOPL",
|
||||
FN_GEDPL => "GEDPL",
|
||||
FN_LOAD512 => "LOAD512",
|
||||
FN_LOAD256 => "LOAD256",
|
||||
FN_GIPAGPL => "GIPAGPL",
|
||||
FN_STREAM => "STREAM",
|
||||
FN_FENTRY => "FENTRY",
|
||||
FN_GFILE => "GFILE",
|
||||
FN_MNGCVPL => "MNGCVPL",
|
||||
FN_MNG0_PL => "MNG0_PL",
|
||||
FN_MNG8_PL => "MNG8_PL",
|
||||
FN_MNG0VPL => "MNG0VPL",
|
||||
FN_MNG8VPL => "MNG8VPL",
|
||||
FN_STRSET => "STRSET",
|
||||
FN_PRM_PL => "PRM_PL",
|
||||
FN_ADIR => "ADIR",
|
||||
FN_LOADNONE=> "LOADNONE",
|
||||
FN_SAVE512 => "SAVE512",
|
||||
FN_DMAPL => "DMAPL",
|
||||
>= FN_KEY_SPACE and <= 0x2F => $"KEY_{fn - FN_KEY_SPACE:X2}",
|
||||
_ => $"?{fn:X2}"
|
||||
};
|
||||
}
|
||||
|
||||
// ── WC call record ────────────────────────────────────────────────
|
||||
|
||||
public struct WcCallRecord
|
||||
{
|
||||
public long Tact;
|
||||
public ushort PC;
|
||||
public byte FuncNum;
|
||||
public ushort AF, BC, DE, HL;
|
||||
public ushort AF2;
|
||||
public ushort IX, SP;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user