Add OPL4 playback and analyzer support

This commit is contained in:
2026-09-15 14:59:29 +03:00
commit 1686838e03
553 changed files with 50524 additions and 0 deletions
+119
View File
@@ -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)
}
}
+31
View File
@@ -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,
}
}
+31
View File
@@ -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,
}
}
+168
View File
@@ -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
}
}
+32
View File
@@ -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?
}
}
+29
View File
@@ -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
{
}
}
+41
View File
@@ -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;
}
}
+681
View File
@@ -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
];
}
}
+199
View File
@@ -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
}
}
+722
View File
@@ -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
}
}
}
+212
View File
@@ -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;
}
}
}
+880
View File
@@ -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;
}
}
}
+485
View File
@@ -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
}
}
+263
View File
@@ -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;
}
}
}
+297
View File
@@ -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
}
}
+353
View File
@@ -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
+347
View File
@@ -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
}
}
+103
View File
@@ -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;
}
}
}
+10
View File
@@ -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
+674
View File
@@ -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 { }
}
}
}
}
+221
View File
@@ -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; }
}
}
+779
View File
@@ -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.