Add OPL4 playback and analyzer support
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
src_sdcc/build
|
||||
Debuger/obj
|
||||
Debuger/bin
|
||||
Debuger/*.vgz
|
||||
Debuger/*.vgm
|
||||
Debuger/dumps
|
||||
**/*.img
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"C_Cpp.errorSquiggles": "disabled"
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu
|
||||
{
|
||||
/// <summary>
|
||||
/// Represent masks for the Z80 flags register
|
||||
/// </summary>
|
||||
public static class CpuFlags
|
||||
{
|
||||
#region Flags
|
||||
|
||||
/// <summary>
|
||||
/// Sign Flag
|
||||
/// [Set if the 2-complement value is negative (copy of MSB)]
|
||||
/// </summary>
|
||||
public const byte S = 0x80;
|
||||
|
||||
/// <summary>
|
||||
/// Zero Flag
|
||||
/// [Set if the value is zero]
|
||||
/// </summary>
|
||||
public const byte Z = 0x40;
|
||||
|
||||
/// <summary>
|
||||
/// Undocumented flag F5
|
||||
/// </summary>
|
||||
public const byte F5 = 0x20;
|
||||
|
||||
/// <summary>
|
||||
/// Half Carry Flag
|
||||
/// [Carry from bit 3 to bit 4]
|
||||
/// </summary>
|
||||
public const byte H = 0x10;
|
||||
|
||||
/// <summary>
|
||||
/// Undocumented flag F3
|
||||
/// </summary>
|
||||
public const byte F3 = 0x08;
|
||||
|
||||
/// <summary>
|
||||
/// P/V - Parity/Overflow Flag
|
||||
/// [Parity set if even number of bits set.
|
||||
/// Overflow set if the 2-complement result does not fit in the register]
|
||||
/// </summary>
|
||||
public const byte P = 0x04;
|
||||
|
||||
/// <summary>
|
||||
/// Add/Subtract Flag
|
||||
/// [Set if the last operation was a subtraction]
|
||||
/// </summary>
|
||||
public const byte N = 0x02;
|
||||
|
||||
/// <summary>
|
||||
/// Carry Flag
|
||||
/// [Set if the result did not fit in the register]
|
||||
/// </summary>
|
||||
public const byte C = 0x01;
|
||||
|
||||
#endregion Flags
|
||||
|
||||
|
||||
#region Inverted Masks
|
||||
|
||||
public const byte NotS = S ^ 0xFF;
|
||||
public const byte NotZ = Z ^ 0xFF;
|
||||
public const byte NotF5 = F5 ^ 0xFF;
|
||||
public const byte NotH = H ^ 0xFF;
|
||||
public const byte NotF3 = F3 ^ 0xFF;
|
||||
public const byte NotP = P ^ 0xFF;
|
||||
public const byte NotN = N ^ 0xFF;
|
||||
public const byte NotC = C ^ 0xFF;
|
||||
|
||||
#endregion Inverted Masks
|
||||
|
||||
|
||||
#region Internal flag sets (used by engine)
|
||||
|
||||
internal const byte F3F5 = F3 | F5;
|
||||
internal const byte SF3F5 = S | F3 | F5;
|
||||
internal const byte PCF3F5 = P | C | F3 | F5;
|
||||
internal const byte HNCF3F5 = H | N | C | F3 | F5;
|
||||
internal const byte HPNF3F5 = H | P | N | F3 | F5;
|
||||
internal const byte HC = H | C;
|
||||
internal const byte SZP = S | Z | P;
|
||||
internal const byte SZPC = S | Z | P | C;
|
||||
internal const byte HN = H | N;
|
||||
|
||||
internal const byte NotF3F5 = F3F5 ^ 0xFF;
|
||||
internal const byte NotSF3F5 = SF3F5 ^ 0xFF;
|
||||
internal const byte NotPCF3F5 = PCF3F5 ^ 0xFF;
|
||||
internal const byte NotHNCF3F5 = HNCF3F5 ^ 0xFF;
|
||||
internal const byte NotHPNF3F5 = HPNF3F5 ^ 0xFF;
|
||||
internal const byte NotHC = HC ^ 0xFF;
|
||||
internal const byte NotSZP = SZP ^ 0xFF;
|
||||
internal const byte NotSZPC = SZPC ^ 0xFF;
|
||||
internal const byte NotHN = HN ^ 0xFF;
|
||||
|
||||
#endregion Internal flag sets (used by engine)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu
|
||||
{
|
||||
public enum CpuModeEx
|
||||
{
|
||||
None = 0,
|
||||
Cb,
|
||||
Ed,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu
|
||||
{
|
||||
public enum CpuModeIndex
|
||||
{
|
||||
None = 0,
|
||||
Ix,
|
||||
Iy,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Description: Z80 CPU Emulator [registers part]
|
||||
* Date: 18.03.2007
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using ZXMAK2.Engine.Cpu.Processor;
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public sealed class CpuRegs
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public ushort AF = 0;
|
||||
[FieldOffset(2)]
|
||||
public ushort BC = 0;
|
||||
[FieldOffset(4)]
|
||||
public ushort DE = 0;
|
||||
[FieldOffset(6)]
|
||||
public ushort HL = 0;
|
||||
[FieldOffset(8)]
|
||||
public ushort _AF = 0;
|
||||
[FieldOffset(10)]
|
||||
public ushort _BC = 0;
|
||||
[FieldOffset(12)]
|
||||
public ushort _DE = 0;
|
||||
[FieldOffset(14)]
|
||||
public ushort _HL = 0;
|
||||
[FieldOffset(16)]
|
||||
public ushort IX = 0;
|
||||
[FieldOffset(18)]
|
||||
public ushort IY = 0;
|
||||
[FieldOffset(20)]
|
||||
public ushort IR = 0;
|
||||
[FieldOffset(22)]
|
||||
public ushort PC = 0;
|
||||
[FieldOffset(24)]
|
||||
public ushort SP = 0;
|
||||
[FieldOffset(26)]
|
||||
public ushort MW = 0; // MEMPTR
|
||||
[FieldOffset(28)]
|
||||
public byte Q = 0; // TODO: Q flags
|
||||
|
||||
|
||||
[FieldOffset(1)]
|
||||
public byte A;
|
||||
[FieldOffset(0)]
|
||||
public byte F;
|
||||
[FieldOffset(3)]
|
||||
public byte B;
|
||||
[FieldOffset(2)]
|
||||
public byte C;
|
||||
[FieldOffset(5)]
|
||||
public byte D;
|
||||
[FieldOffset(4)]
|
||||
public byte E;
|
||||
[FieldOffset(7)]
|
||||
public byte H;
|
||||
[FieldOffset(6)]
|
||||
public byte L;
|
||||
[FieldOffset(17)]
|
||||
public byte XH;
|
||||
[FieldOffset(16)]
|
||||
public byte XL;
|
||||
[FieldOffset(19)]
|
||||
public byte YH;
|
||||
[FieldOffset(18)]
|
||||
public byte YL;
|
||||
[FieldOffset(21)]
|
||||
public byte I;
|
||||
[FieldOffset(20)]
|
||||
public byte R;
|
||||
|
||||
[FieldOffset(27)]
|
||||
public byte MH;
|
||||
[FieldOffset(26)]
|
||||
public byte ML;
|
||||
|
||||
[FieldOffset(23)]
|
||||
public byte PCH = 0;
|
||||
[FieldOffset(22)]
|
||||
public byte PCL = 0;
|
||||
|
||||
|
||||
#region Access Lamda Generators
|
||||
|
||||
// Access field from an object context
|
||||
// makes one native instruction lesser
|
||||
|
||||
internal Func<byte> CreateRegGetter(int r)
|
||||
{
|
||||
return r switch
|
||||
{
|
||||
CpuRegId.B => () => B,
|
||||
CpuRegId.C => () => C,
|
||||
CpuRegId.D => () => D,
|
||||
CpuRegId.E => () => E,
|
||||
CpuRegId.H => () => H,
|
||||
CpuRegId.L => () => L,
|
||||
CpuRegId.A => () => A,
|
||||
CpuRegId.F => () => F,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(r)),
|
||||
};
|
||||
}
|
||||
|
||||
internal Action<byte> CreateRegSetter(int r)
|
||||
{
|
||||
return r switch
|
||||
{
|
||||
CpuRegId.B => arg => B = arg,
|
||||
CpuRegId.C => arg => C = arg,
|
||||
CpuRegId.D => arg => D = arg,
|
||||
CpuRegId.E => arg => E = arg,
|
||||
CpuRegId.H => arg => H = arg,
|
||||
CpuRegId.L => arg => L = arg,
|
||||
CpuRegId.A => arg => A = arg,
|
||||
CpuRegId.F => arg => F = arg,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(r)),
|
||||
};
|
||||
}
|
||||
|
||||
internal Func<ushort> CreatePairGetter(int rr)
|
||||
{
|
||||
return rr switch
|
||||
{
|
||||
CpuRegId.Bc => () => BC,
|
||||
CpuRegId.De => () => DE,
|
||||
CpuRegId.Hl => () => HL,
|
||||
CpuRegId.Sp => () => SP,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(rr)),
|
||||
};
|
||||
}
|
||||
|
||||
internal Action<ushort> CreatePairSetter(int rr)
|
||||
{
|
||||
return rr switch
|
||||
{
|
||||
CpuRegId.Bc => arg => BC = arg,
|
||||
CpuRegId.De => arg => DE = arg,
|
||||
CpuRegId.Hl => arg => HL = arg,
|
||||
CpuRegId.Sp => arg => SP = arg,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(rr)),
|
||||
};
|
||||
}
|
||||
|
||||
#endregion Access Lamda Generators
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu
|
||||
{
|
||||
public enum CpuType
|
||||
{
|
||||
ZILOG_NMOS = 0,
|
||||
ZILOG_CMOS = 1,
|
||||
NEC_NMOS = 2, // NEC
|
||||
ST_CMOS = 3, // Z84?
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
using ZXMAK2.Engine.Cpu.Processor;
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu
|
||||
{
|
||||
public sealed class CpuUnit : Z80Cpu
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu.Processor
|
||||
{
|
||||
internal static class CpuRegId
|
||||
{
|
||||
public const int Bc = 0;
|
||||
public const int De = 1;
|
||||
public const int Hl = 2;
|
||||
public const int Sp = 3;
|
||||
|
||||
public const int B = 0;
|
||||
public const int C = 1;
|
||||
public const int D = 2;
|
||||
public const int E = 3;
|
||||
public const int H = 4;
|
||||
public const int L = 5;
|
||||
public const int F = 6;
|
||||
public const int A = 7;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,681 @@
|
||||
/*
|
||||
* Copyright 2015 Alex Makeev
|
||||
* Copyright 2009 SMT (ALU tables)
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --------------------------------------------------------------------
|
||||
* Most of the tables based on the source code of UnrealSpeccy emulator
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu.Processor
|
||||
{
|
||||
public static class CpuTables
|
||||
{
|
||||
#region .ctor
|
||||
|
||||
static CpuTables()
|
||||
{
|
||||
Logf = CreateLogf();
|
||||
Adcf = CreateAdcf();
|
||||
|
||||
// === make_rot === credits to SMT (Unreal)
|
||||
Rlcaf = new byte[0x100];
|
||||
Rrcaf = new byte[0x100];
|
||||
for (var i = 0; i < 0x100; i++)
|
||||
{
|
||||
Rlcaf[i] = (byte)(Rlcf[i] & 0x3B); // rra,rla uses same tables
|
||||
}
|
||||
for (var i = 0; i < 0x100; i++)
|
||||
{
|
||||
Rrcaf[i] = (byte)(Rrcf[i] & 0x3B);
|
||||
}
|
||||
|
||||
// === make_sbc === credits to SMT (Unreal)
|
||||
Sbcf = new byte[0x20000];
|
||||
Cpf = new byte[0x10000];
|
||||
Cpf8b = new byte[0x10000];
|
||||
for (var c = 0; c < 2; c++)
|
||||
{
|
||||
for (var x = 0; x < 0x100; x++)
|
||||
{
|
||||
for (var y = 0; y < 0x100; y++)
|
||||
{
|
||||
var res = x - y - c;
|
||||
var fl = res & CpuFlags.SF3F5;
|
||||
if ((res & 0xFF) == 0) fl |= CpuFlags.Z;
|
||||
if ((res & 0x10000) != 0) fl |= CpuFlags.C;
|
||||
var r = (sbyte)x - (sbyte)y - c;
|
||||
if (r >= 0x80 || r < -0x80) fl |= CpuFlags.P;
|
||||
if ((((x & 0x0F) - (res & 0x0F) - c) & 0x10) != 0) fl |= CpuFlags.H;
|
||||
fl |= CpuFlags.N;
|
||||
Sbcf[c * 0x10000 + x * 0x100 + y] = (byte)fl;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < 0x10000; i++)
|
||||
{
|
||||
Cpf[i] = (byte)((Sbcf[i] & CpuFlags.NotF3F5) | (i & CpuFlags.F3F5));
|
||||
var val = (byte)((i >> 8) - (i & 0xFF) - ((Sbcf[i] & CpuFlags.H) >> 4));
|
||||
Cpf8b[i] = (byte)((Sbcf[i] & CpuFlags.NotPCF3F5) + (val & CpuFlags.F3) + ((val << 4) & CpuFlags.F5));
|
||||
}
|
||||
|
||||
Parity = new byte[256];
|
||||
for (var i = 0; i < Parity.Length; i++)
|
||||
{
|
||||
// Bit Twiddling Hacks: Compute parity in parallel, 8-bit
|
||||
var p = (0x6996 >> ((i ^ (i >> 4)) & 0xf)) & 1;
|
||||
Parity[i] = p == 0 ? CpuFlags.P : (byte)0;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] CreateLogf()
|
||||
{
|
||||
// === make_log === credits to SMT (Unreal)
|
||||
var log_f = new byte[0x100];
|
||||
for (var x = 0; x < 0x100; x++)
|
||||
{
|
||||
var fl = x & CpuFlags.SF3F5;
|
||||
var p = CpuFlags.P;
|
||||
for (var i = 0x80; i != 0; i /= 2)
|
||||
{
|
||||
if ((x & i) == i) p ^= CpuFlags.P;
|
||||
}
|
||||
log_f[x] = (byte)(fl | p);
|
||||
}
|
||||
log_f[0] |= CpuFlags.Z;
|
||||
return log_f;
|
||||
}
|
||||
|
||||
private static byte[] CreateAdcf()
|
||||
{
|
||||
// === make_adc === credits to SMT (Unreal)
|
||||
var adcf = new byte[0x20000];
|
||||
for (var c = 0; c < 2; c++)
|
||||
{
|
||||
for (var x = 0; x < 0x100; x++)
|
||||
{
|
||||
for (var y = 0; y < 0x100; y++)
|
||||
{
|
||||
uint res = (uint)(x + y + c);
|
||||
byte fl = 0;
|
||||
if ((res & 0xFF) == 0) fl |= CpuFlags.Z;
|
||||
fl |= (byte)(res & CpuFlags.SF3F5);
|
||||
if (res >= 0x100) fl |= CpuFlags.C;
|
||||
if ((((x & 0x0F) + (y & 0x0F) + c) & 0x10) != 0) fl |= CpuFlags.H;
|
||||
int ri = (sbyte)x + (sbyte)y + c;
|
||||
if (ri >= 0x80 || ri <= -0x81) fl |= CpuFlags.P;
|
||||
adcf[c * 0x10000 + x * 0x100 + y] = fl;
|
||||
}
|
||||
}
|
||||
}
|
||||
return adcf;
|
||||
}
|
||||
|
||||
#endregion .ctor
|
||||
|
||||
|
||||
public static readonly byte[] Logf;
|
||||
public static readonly byte[] Adcf; // flags for adc and add
|
||||
public static readonly byte[] Rlcaf; // flags for RLCA
|
||||
public static readonly byte[] Rrcaf; // flags for RRCA
|
||||
public static readonly byte[] Sbcf; // flags for sub and sbc
|
||||
public static readonly byte[] Cpf; // flags for cp
|
||||
public static readonly byte[] Cpf8b; // flags for cpi(r)/cpd(r)
|
||||
public static readonly byte[] Parity; // parity calculator
|
||||
|
||||
|
||||
public static readonly byte[] Rlcf =
|
||||
// credits to SMT (Unreal)
|
||||
[ // for rlc r. may be for rlca (0x3B mask)
|
||||
0x44,0x00,0x00,0x04,0x08,0x0c,0x0c,0x08
|
||||
,0x00,0x04,0x04,0x00,0x0c,0x08,0x08,0x0c
|
||||
,0x20,0x24,0x24,0x20,0x2c,0x28,0x28,0x2c
|
||||
,0x24,0x20,0x20,0x24,0x28,0x2c,0x2c,0x28
|
||||
,0x00,0x04,0x04,0x00,0x0c,0x08,0x08,0x0c
|
||||
,0x04,0x00,0x00,0x04,0x08,0x0c,0x0c,0x08
|
||||
,0x24,0x20,0x20,0x24,0x28,0x2c,0x2c,0x28
|
||||
,0x20,0x24,0x24,0x20,0x2c,0x28,0x28,0x2c
|
||||
,0x80,0x84,0x84,0x80,0x8c,0x88,0x88,0x8c
|
||||
,0x84,0x80,0x80,0x84,0x88,0x8c,0x8c,0x88
|
||||
,0xa4,0xa0,0xa0,0xa4,0xa8,0xac,0xac,0xa8
|
||||
,0xa0,0xa4,0xa4,0xa0,0xac,0xa8,0xa8,0xac
|
||||
,0x84,0x80,0x80,0x84,0x88,0x8c,0x8c,0x88
|
||||
,0x80,0x84,0x84,0x80,0x8c,0x88,0x88,0x8c
|
||||
,0xa0,0xa4,0xa4,0xa0,0xac,0xa8,0xa8,0xac
|
||||
,0xa4,0xa0,0xa0,0xa4,0xa8,0xac,0xac,0xa8
|
||||
,0x01,0x05,0x05,0x01,0x0d,0x09,0x09,0x0d
|
||||
,0x05,0x01,0x01,0x05,0x09,0x0d,0x0d,0x09
|
||||
,0x25,0x21,0x21,0x25,0x29,0x2d,0x2d,0x29
|
||||
,0x21,0x25,0x25,0x21,0x2d,0x29,0x29,0x2d
|
||||
,0x05,0x01,0x01,0x05,0x09,0x0d,0x0d,0x09
|
||||
,0x01,0x05,0x05,0x01,0x0d,0x09,0x09,0x0d
|
||||
,0x21,0x25,0x25,0x21,0x2d,0x29,0x29,0x2d
|
||||
,0x25,0x21,0x21,0x25,0x29,0x2d,0x2d,0x29
|
||||
,0x85,0x81,0x81,0x85,0x89,0x8d,0x8d,0x89
|
||||
,0x81,0x85,0x85,0x81,0x8d,0x89,0x89,0x8d
|
||||
,0xa1,0xa5,0xa5,0xa1,0xad,0xa9,0xa9,0xad
|
||||
,0xa5,0xa1,0xa1,0xa5,0xa9,0xad,0xad,0xa9
|
||||
,0x81,0x85,0x85,0x81,0x8d,0x89,0x89,0x8d
|
||||
,0x85,0x81,0x81,0x85,0x89,0x8d,0x8d,0x89
|
||||
,0xa5,0xa1,0xa1,0xa5,0xa9,0xad,0xad,0xa9
|
||||
,0xa1,0xa5,0xa5,0xa1,0xad,0xa9,0xa9,0xad
|
||||
];
|
||||
|
||||
public static readonly byte[] Rrcf =
|
||||
// credits to SMT (Unreal)
|
||||
[ // rrc r. may be for rlca (0x3B mask)
|
||||
0x44,0x81,0x00,0x85,0x00,0x85,0x04,0x81
|
||||
,0x00,0x85,0x04,0x81,0x04,0x81,0x00,0x85
|
||||
,0x08,0x8d,0x0c,0x89,0x0c,0x89,0x08,0x8d
|
||||
,0x0c,0x89,0x08,0x8d,0x08,0x8d,0x0c,0x89
|
||||
,0x00,0x85,0x04,0x81,0x04,0x81,0x00,0x85
|
||||
,0x04,0x81,0x00,0x85,0x00,0x85,0x04,0x81
|
||||
,0x0c,0x89,0x08,0x8d,0x08,0x8d,0x0c,0x89
|
||||
,0x08,0x8d,0x0c,0x89,0x0c,0x89,0x08,0x8d
|
||||
,0x20,0xa5,0x24,0xa1,0x24,0xa1,0x20,0xa5
|
||||
,0x24,0xa1,0x20,0xa5,0x20,0xa5,0x24,0xa1
|
||||
,0x2c,0xa9,0x28,0xad,0x28,0xad,0x2c,0xa9
|
||||
,0x28,0xad,0x2c,0xa9,0x2c,0xa9,0x28,0xad
|
||||
,0x24,0xa1,0x20,0xa5,0x20,0xa5,0x24,0xa1
|
||||
,0x20,0xa5,0x24,0xa1,0x24,0xa1,0x20,0xa5
|
||||
,0x28,0xad,0x2c,0xa9,0x2c,0xa9,0x28,0xad
|
||||
,0x2c,0xa9,0x28,0xad,0x28,0xad,0x2c,0xa9
|
||||
,0x00,0x85,0x04,0x81,0x04,0x81,0x00,0x85
|
||||
,0x04,0x81,0x00,0x85,0x00,0x85,0x04,0x81
|
||||
,0x0c,0x89,0x08,0x8d,0x08,0x8d,0x0c,0x89
|
||||
,0x08,0x8d,0x0c,0x89,0x0c,0x89,0x08,0x8d
|
||||
,0x04,0x81,0x00,0x85,0x00,0x85,0x04,0x81
|
||||
,0x00,0x85,0x04,0x81,0x04,0x81,0x00,0x85
|
||||
,0x08,0x8d,0x0c,0x89,0x0c,0x89,0x08,0x8d
|
||||
,0x0c,0x89,0x08,0x8d,0x08,0x8d,0x0c,0x89
|
||||
,0x24,0xa1,0x20,0xa5,0x20,0xa5,0x24,0xa1
|
||||
,0x20,0xa5,0x24,0xa1,0x24,0xa1,0x20,0xa5
|
||||
,0x28,0xad,0x2c,0xa9,0x2c,0xa9,0x28,0xad
|
||||
,0x2c,0xa9,0x28,0xad,0x28,0xad,0x2c,0xa9
|
||||
,0x20,0xa5,0x24,0xa1,0x24,0xa1,0x20,0xa5
|
||||
,0x24,0xa1,0x20,0xa5,0x20,0xa5,0x24,0xa1
|
||||
,0x2c,0xa9,0x28,0xad,0x28,0xad,0x2c,0xa9
|
||||
,0x28,0xad,0x2c,0xa9,0x2c,0xa9,0x28,0xad
|
||||
];
|
||||
|
||||
public static readonly byte[] Incf =
|
||||
// credits to SMT (Unreal)
|
||||
[
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08
|
||||
,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x10
|
||||
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08
|
||||
,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x30
|
||||
,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x28
|
||||
,0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x30
|
||||
,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x28
|
||||
,0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x10
|
||||
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08
|
||||
,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x10
|
||||
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08
|
||||
,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x30
|
||||
,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x28
|
||||
,0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x30
|
||||
,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x28
|
||||
,0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x94
|
||||
,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x88
|
||||
,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x90
|
||||
,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x88
|
||||
,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0xb0
|
||||
,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa8
|
||||
,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xb0
|
||||
,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa8
|
||||
,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0x90
|
||||
,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x88
|
||||
,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x90
|
||||
,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x88
|
||||
,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0xb0
|
||||
,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa8
|
||||
,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xb0
|
||||
,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa8
|
||||
,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0x50
|
||||
];
|
||||
|
||||
public static readonly byte[] Decf =
|
||||
// credits to SMT (Unreal)
|
||||
[
|
||||
0xba,0x42,0x02,0x02,0x02,0x02,0x02,0x02
|
||||
,0x02,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a
|
||||
,0x1a,0x02,0x02,0x02,0x02,0x02,0x02,0x02
|
||||
,0x02,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a
|
||||
,0x1a,0x22,0x22,0x22,0x22,0x22,0x22,0x22
|
||||
,0x22,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a
|
||||
,0x3a,0x22,0x22,0x22,0x22,0x22,0x22,0x22
|
||||
,0x22,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a
|
||||
,0x3a,0x02,0x02,0x02,0x02,0x02,0x02,0x02
|
||||
,0x02,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a
|
||||
,0x1a,0x02,0x02,0x02,0x02,0x02,0x02,0x02
|
||||
,0x02,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a
|
||||
,0x1a,0x22,0x22,0x22,0x22,0x22,0x22,0x22
|
||||
,0x22,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a
|
||||
,0x3a,0x22,0x22,0x22,0x22,0x22,0x22,0x22
|
||||
,0x22,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a
|
||||
,0x3e,0x82,0x82,0x82,0x82,0x82,0x82,0x82
|
||||
,0x82,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a
|
||||
,0x9a,0x82,0x82,0x82,0x82,0x82,0x82,0x82
|
||||
,0x82,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a
|
||||
,0x9a,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2
|
||||
,0xa2,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa
|
||||
,0xba,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2
|
||||
,0xa2,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa
|
||||
,0xba,0x82,0x82,0x82,0x82,0x82,0x82,0x82
|
||||
,0x82,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a
|
||||
,0x9a,0x82,0x82,0x82,0x82,0x82,0x82,0x82
|
||||
,0x82,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a
|
||||
,0x9a,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2
|
||||
,0xa2,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa
|
||||
,0xba,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2
|
||||
,0xa2,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa
|
||||
];
|
||||
|
||||
public static readonly ushort[] Daaf =
|
||||
// credits to SMT (converted from Unreal)
|
||||
[
|
||||
0x0044,0x0100,0x0200,0x0304,0x0400,0x0504,0x0604,0x0700,0x0808,0x090C,
|
||||
0x1010,0x1114,0x1214,0x1310,0x1414,0x1510,0x1000,0x1104,0x1204,0x1300,
|
||||
0x1404,0x1500,0x1600,0x1704,0x180C,0x1908,0x2030,0x2134,0x2234,0x2330,
|
||||
0x2434,0x2530,0x2020,0x2124,0x2224,0x2320,0x2424,0x2520,0x2620,0x2724,
|
||||
0x282C,0x2928,0x3034,0x3130,0x3230,0x3334,0x3430,0x3534,0x3024,0x3120,
|
||||
0x3220,0x3324,0x3420,0x3524,0x3624,0x3720,0x3828,0x392C,0x4010,0x4114,
|
||||
0x4214,0x4310,0x4414,0x4510,0x4000,0x4104,0x4204,0x4300,0x4404,0x4500,
|
||||
0x4600,0x4704,0x480C,0x4908,0x5014,0x5110,0x5210,0x5314,0x5410,0x5514,
|
||||
0x5004,0x5100,0x5200,0x5304,0x5400,0x5504,0x5604,0x5700,0x5808,0x590C,
|
||||
0x6034,0x6130,0x6230,0x6334,0x6430,0x6534,0x6024,0x6120,0x6220,0x6324,
|
||||
0x6420,0x6524,0x6624,0x6720,0x6828,0x692C,0x7030,0x7134,0x7234,0x7330,
|
||||
0x7434,0x7530,0x7020,0x7124,0x7224,0x7320,0x7424,0x7520,0x7620,0x7724,
|
||||
0x782C,0x7928,0x8090,0x8194,0x8294,0x8390,0x8494,0x8590,0x8080,0x8184,
|
||||
0x8284,0x8380,0x8484,0x8580,0x8680,0x8784,0x888C,0x8988,0x9094,0x9190,
|
||||
0x9290,0x9394,0x9490,0x9594,0x9084,0x9180,0x9280,0x9384,0x9480,0x9584,
|
||||
0x9684,0x9780,0x9888,0x998C,0x0055,0x0111,0x0211,0x0315,0x0411,0x0515,
|
||||
0x0045,0x0101,0x0201,0x0305,0x0401,0x0505,0x0605,0x0701,0x0809,0x090D,
|
||||
0x1011,0x1115,0x1215,0x1311,0x1415,0x1511,0x1001,0x1105,0x1205,0x1301,
|
||||
0x1405,0x1501,0x1601,0x1705,0x180D,0x1909,0x2031,0x2135,0x2235,0x2331,
|
||||
0x2435,0x2531,0x2021,0x2125,0x2225,0x2321,0x2425,0x2521,0x2621,0x2725,
|
||||
0x282D,0x2929,0x3035,0x3131,0x3231,0x3335,0x3431,0x3535,0x3025,0x3121,
|
||||
0x3221,0x3325,0x3421,0x3525,0x3625,0x3721,0x3829,0x392D,0x4011,0x4115,
|
||||
0x4215,0x4311,0x4415,0x4511,0x4001,0x4105,0x4205,0x4301,0x4405,0x4501,
|
||||
0x4601,0x4705,0x480D,0x4909,0x5015,0x5111,0x5211,0x5315,0x5411,0x5515,
|
||||
0x5005,0x5101,0x5201,0x5305,0x5401,0x5505,0x5605,0x5701,0x5809,0x590D,
|
||||
0x6035,0x6131,0x6231,0x6335,0x6431,0x6535,0x6025,0x6121,0x6221,0x6325,
|
||||
0x6421,0x6525,0x6625,0x6721,0x6829,0x692D,0x7031,0x7135,0x7235,0x7331,
|
||||
0x7435,0x7531,0x7021,0x7125,0x7225,0x7321,0x7425,0x7521,0x7621,0x7725,
|
||||
0x782D,0x7929,0x8091,0x8195,0x8295,0x8391,0x8495,0x8591,0x8081,0x8185,
|
||||
0x8285,0x8381,0x8485,0x8581,0x8681,0x8785,0x888D,0x8989,0x9095,0x9191,
|
||||
0x9291,0x9395,0x9491,0x9595,0x9085,0x9181,0x9281,0x9385,0x9481,0x9585,
|
||||
0x9685,0x9781,0x9889,0x998D,0xA0B5,0xA1B1,0xA2B1,0xA3B5,0xA4B1,0xA5B5,
|
||||
0xA0A5,0xA1A1,0xA2A1,0xA3A5,0xA4A1,0xA5A5,0xA6A5,0xA7A1,0xA8A9,0xA9AD,
|
||||
0xB0B1,0xB1B5,0xB2B5,0xB3B1,0xB4B5,0xB5B1,0xB0A1,0xB1A5,0xB2A5,0xB3A1,
|
||||
0xB4A5,0xB5A1,0xB6A1,0xB7A5,0xB8AD,0xB9A9,0xC095,0xC191,0xC291,0xC395,
|
||||
0xC491,0xC595,0xC085,0xC181,0xC281,0xC385,0xC481,0xC585,0xC685,0xC781,
|
||||
0xC889,0xC98D,0xD091,0xD195,0xD295,0xD391,0xD495,0xD591,0xD081,0xD185,
|
||||
0xD285,0xD381,0xD485,0xD581,0xD681,0xD785,0xD88D,0xD989,0xE0B1,0xE1B5,
|
||||
0xE2B5,0xE3B1,0xE4B5,0xE5B1,0xE0A1,0xE1A5,0xE2A5,0xE3A1,0xE4A5,0xE5A1,
|
||||
0xE6A1,0xE7A5,0xE8AD,0xE9A9,0xF0B5,0xF1B1,0xF2B1,0xF3B5,0xF4B1,0xF5B5,
|
||||
0xF0A5,0xF1A1,0xF2A1,0xF3A5,0xF4A1,0xF5A5,0xF6A5,0xF7A1,0xF8A9,0xF9AD,
|
||||
0x0055,0x0111,0x0211,0x0315,0x0411,0x0515,0x0045,0x0101,0x0201,0x0305,
|
||||
0x0401,0x0505,0x0605,0x0701,0x0809,0x090D,0x1011,0x1115,0x1215,0x1311,
|
||||
0x1415,0x1511,0x1001,0x1105,0x1205,0x1301,0x1405,0x1501,0x1601,0x1705,
|
||||
0x180D,0x1909,0x2031,0x2135,0x2235,0x2331,0x2435,0x2531,0x2021,0x2125,
|
||||
0x2225,0x2321,0x2425,0x2521,0x2621,0x2725,0x282D,0x2929,0x3035,0x3131,
|
||||
0x3231,0x3335,0x3431,0x3535,0x3025,0x3121,0x3221,0x3325,0x3421,0x3525,
|
||||
0x3625,0x3721,0x3829,0x392D,0x4011,0x4115,0x4215,0x4311,0x4415,0x4511,
|
||||
0x4001,0x4105,0x4205,0x4301,0x4405,0x4501,0x4601,0x4705,0x480D,0x4909,
|
||||
0x5015,0x5111,0x5211,0x5315,0x5411,0x5515,0x5005,0x5101,0x5201,0x5305,
|
||||
0x5401,0x5505,0x5605,0x5701,0x5809,0x590D,0x6035,0x6131,0x6231,0x6335,
|
||||
0x6431,0x6535,0x0046,0x0102,0x0202,0x0306,0x0402,0x0506,0x0606,0x0702,
|
||||
0x080A,0x090E,0x0402,0x0506,0x0606,0x0702,0x080A,0x090E,0x1002,0x1106,
|
||||
0x1206,0x1302,0x1406,0x1502,0x1602,0x1706,0x180E,0x190A,0x1406,0x1502,
|
||||
0x1602,0x1706,0x180E,0x190A,0x2022,0x2126,0x2226,0x2322,0x2426,0x2522,
|
||||
0x2622,0x2726,0x282E,0x292A,0x2426,0x2522,0x2622,0x2726,0x282E,0x292A,
|
||||
0x3026,0x3122,0x3222,0x3326,0x3422,0x3526,0x3626,0x3722,0x382A,0x392E,
|
||||
0x3422,0x3526,0x3626,0x3722,0x382A,0x392E,0x4002,0x4106,0x4206,0x4302,
|
||||
0x4406,0x4502,0x4602,0x4706,0x480E,0x490A,0x4406,0x4502,0x4602,0x4706,
|
||||
0x480E,0x490A,0x5006,0x5102,0x5202,0x5306,0x5402,0x5506,0x5606,0x5702,
|
||||
0x580A,0x590E,0x5402,0x5506,0x5606,0x5702,0x580A,0x590E,0x6026,0x6122,
|
||||
0x6222,0x6326,0x6422,0x6526,0x6626,0x6722,0x682A,0x692E,0x6422,0x6526,
|
||||
0x6626,0x6722,0x682A,0x692E,0x7022,0x7126,0x7226,0x7322,0x7426,0x7522,
|
||||
0x7622,0x7726,0x782E,0x792A,0x7426,0x7522,0x7622,0x7726,0x782E,0x792A,
|
||||
0x8082,0x8186,0x8286,0x8382,0x8486,0x8582,0x8682,0x8786,0x888E,0x898A,
|
||||
0x8486,0x8582,0x8682,0x8786,0x888E,0x898A,0x9086,0x9182,0x9282,0x9386,
|
||||
0x9482,0x9586,0x9686,0x9782,0x988A,0x998E,0x3423,0x3527,0x3627,0x3723,
|
||||
0x382B,0x392F,0x4003,0x4107,0x4207,0x4303,0x4407,0x4503,0x4603,0x4707,
|
||||
0x480F,0x490B,0x4407,0x4503,0x4603,0x4707,0x480F,0x490B,0x5007,0x5103,
|
||||
0x5203,0x5307,0x5403,0x5507,0x5607,0x5703,0x580B,0x590F,0x5403,0x5507,
|
||||
0x5607,0x5703,0x580B,0x590F,0x6027,0x6123,0x6223,0x6327,0x6423,0x6527,
|
||||
0x6627,0x6723,0x682B,0x692F,0x6423,0x6527,0x6627,0x6723,0x682B,0x692F,
|
||||
0x7023,0x7127,0x7227,0x7323,0x7427,0x7523,0x7623,0x7727,0x782F,0x792B,
|
||||
0x7427,0x7523,0x7623,0x7727,0x782F,0x792B,0x8083,0x8187,0x8287,0x8383,
|
||||
0x8487,0x8583,0x8683,0x8787,0x888F,0x898B,0x8487,0x8583,0x8683,0x8787,
|
||||
0x888F,0x898B,0x9087,0x9183,0x9283,0x9387,0x9483,0x9587,0x9687,0x9783,
|
||||
0x988B,0x998F,0x9483,0x9587,0x9687,0x9783,0x988B,0x998F,0xA0A7,0xA1A3,
|
||||
0xA2A3,0xA3A7,0xA4A3,0xA5A7,0xA6A7,0xA7A3,0xA8AB,0xA9AF,0xA4A3,0xA5A7,
|
||||
0xA6A7,0xA7A3,0xA8AB,0xA9AF,0xB0A3,0xB1A7,0xB2A7,0xB3A3,0xB4A7,0xB5A3,
|
||||
0xB6A3,0xB7A7,0xB8AF,0xB9AB,0xB4A7,0xB5A3,0xB6A3,0xB7A7,0xB8AF,0xB9AB,
|
||||
0xC087,0xC183,0xC283,0xC387,0xC483,0xC587,0xC687,0xC783,0xC88B,0xC98F,
|
||||
0xC483,0xC587,0xC687,0xC783,0xC88B,0xC98F,0xD083,0xD187,0xD287,0xD383,
|
||||
0xD487,0xD583,0xD683,0xD787,0xD88F,0xD98B,0xD487,0xD583,0xD683,0xD787,
|
||||
0xD88F,0xD98B,0xE0A3,0xE1A7,0xE2A7,0xE3A3,0xE4A7,0xE5A3,0xE6A3,0xE7A7,
|
||||
0xE8AF,0xE9AB,0xE4A7,0xE5A3,0xE6A3,0xE7A7,0xE8AF,0xE9AB,0xF0A7,0xF1A3,
|
||||
0xF2A3,0xF3A7,0xF4A3,0xF5A7,0xF6A7,0xF7A3,0xF8AB,0xF9AF,0xF4A3,0xF5A7,
|
||||
0xF6A7,0xF7A3,0xF8AB,0xF9AF,0x0047,0x0103,0x0203,0x0307,0x0403,0x0507,
|
||||
0x0607,0x0703,0x080B,0x090F,0x0403,0x0507,0x0607,0x0703,0x080B,0x090F,
|
||||
0x1003,0x1107,0x1207,0x1303,0x1407,0x1503,0x1603,0x1707,0x180F,0x190B,
|
||||
0x1407,0x1503,0x1603,0x1707,0x180F,0x190B,0x2023,0x2127,0x2227,0x2323,
|
||||
0x2427,0x2523,0x2623,0x2727,0x282F,0x292B,0x2427,0x2523,0x2623,0x2727,
|
||||
0x282F,0x292B,0x3027,0x3123,0x3223,0x3327,0x3423,0x3527,0x3627,0x3723,
|
||||
0x382B,0x392F,0x3423,0x3527,0x3627,0x3723,0x382B,0x392F,0x4003,0x4107,
|
||||
0x4207,0x4303,0x4407,0x4503,0x4603,0x4707,0x480F,0x490B,0x4407,0x4503,
|
||||
0x4603,0x4707,0x480F,0x490B,0x5007,0x5103,0x5203,0x5307,0x5403,0x5507,
|
||||
0x5607,0x5703,0x580B,0x590F,0x5403,0x5507,0x5607,0x5703,0x580B,0x590F,
|
||||
0x6027,0x6123,0x6223,0x6327,0x6423,0x6527,0x6627,0x6723,0x682B,0x692F,
|
||||
0x6423,0x6527,0x6627,0x6723,0x682B,0x692F,0x7023,0x7127,0x7227,0x7323,
|
||||
0x7427,0x7523,0x7623,0x7727,0x782F,0x792B,0x7427,0x7523,0x7623,0x7727,
|
||||
0x782F,0x792B,0x8083,0x8187,0x8287,0x8383,0x8487,0x8583,0x8683,0x8787,
|
||||
0x888F,0x898B,0x8487,0x8583,0x8683,0x8787,0x888F,0x898B,0x9087,0x9183,
|
||||
0x9283,0x9387,0x9483,0x9587,0x9687,0x9783,0x988B,0x998F,0x9483,0x9587,
|
||||
0x9687,0x9783,0x988B,0x998F,0x0604,0x0700,0x0808,0x090C,0x0A0C,0x0B08,
|
||||
0x0C0C,0x0D08,0x0E08,0x0F0C,0x1010,0x1114,0x1214,0x1310,0x1414,0x1510,
|
||||
0x1600,0x1704,0x180C,0x1908,0x1A08,0x1B0C,0x1C08,0x1D0C,0x1E0C,0x1F08,
|
||||
0x2030,0x2134,0x2234,0x2330,0x2434,0x2530,0x2620,0x2724,0x282C,0x2928,
|
||||
0x2A28,0x2B2C,0x2C28,0x2D2C,0x2E2C,0x2F28,0x3034,0x3130,0x3230,0x3334,
|
||||
0x3430,0x3534,0x3624,0x3720,0x3828,0x392C,0x3A2C,0x3B28,0x3C2C,0x3D28,
|
||||
0x3E28,0x3F2C,0x4010,0x4114,0x4214,0x4310,0x4414,0x4510,0x4600,0x4704,
|
||||
0x480C,0x4908,0x4A08,0x4B0C,0x4C08,0x4D0C,0x4E0C,0x4F08,0x5014,0x5110,
|
||||
0x5210,0x5314,0x5410,0x5514,0x5604,0x5700,0x5808,0x590C,0x5A0C,0x5B08,
|
||||
0x5C0C,0x5D08,0x5E08,0x5F0C,0x6034,0x6130,0x6230,0x6334,0x6430,0x6534,
|
||||
0x6624,0x6720,0x6828,0x692C,0x6A2C,0x6B28,0x6C2C,0x6D28,0x6E28,0x6F2C,
|
||||
0x7030,0x7134,0x7234,0x7330,0x7434,0x7530,0x7620,0x7724,0x782C,0x7928,
|
||||
0x7A28,0x7B2C,0x7C28,0x7D2C,0x7E2C,0x7F28,0x8090,0x8194,0x8294,0x8390,
|
||||
0x8494,0x8590,0x8680,0x8784,0x888C,0x8988,0x8A88,0x8B8C,0x8C88,0x8D8C,
|
||||
0x8E8C,0x8F88,0x9094,0x9190,0x9290,0x9394,0x9490,0x9594,0x9684,0x9780,
|
||||
0x9888,0x998C,0x9A8C,0x9B88,0x9C8C,0x9D88,0x9E88,0x9F8C,0x0055,0x0111,
|
||||
0x0211,0x0315,0x0411,0x0515,0x0605,0x0701,0x0809,0x090D,0x0A0D,0x0B09,
|
||||
0x0C0D,0x0D09,0x0E09,0x0F0D,0x1011,0x1115,0x1215,0x1311,0x1415,0x1511,
|
||||
0x1601,0x1705,0x180D,0x1909,0x1A09,0x1B0D,0x1C09,0x1D0D,0x1E0D,0x1F09,
|
||||
0x2031,0x2135,0x2235,0x2331,0x2435,0x2531,0x2621,0x2725,0x282D,0x2929,
|
||||
0x2A29,0x2B2D,0x2C29,0x2D2D,0x2E2D,0x2F29,0x3035,0x3131,0x3231,0x3335,
|
||||
0x3431,0x3535,0x3625,0x3721,0x3829,0x392D,0x3A2D,0x3B29,0x3C2D,0x3D29,
|
||||
0x3E29,0x3F2D,0x4011,0x4115,0x4215,0x4311,0x4415,0x4511,0x4601,0x4705,
|
||||
0x480D,0x4909,0x4A09,0x4B0D,0x4C09,0x4D0D,0x4E0D,0x4F09,0x5015,0x5111,
|
||||
0x5211,0x5315,0x5411,0x5515,0x5605,0x5701,0x5809,0x590D,0x5A0D,0x5B09,
|
||||
0x5C0D,0x5D09,0x5E09,0x5F0D,0x6035,0x6131,0x6231,0x6335,0x6431,0x6535,
|
||||
0x6625,0x6721,0x6829,0x692D,0x6A2D,0x6B29,0x6C2D,0x6D29,0x6E29,0x6F2D,
|
||||
0x7031,0x7135,0x7235,0x7331,0x7435,0x7531,0x7621,0x7725,0x782D,0x7929,
|
||||
0x7A29,0x7B2D,0x7C29,0x7D2D,0x7E2D,0x7F29,0x8091,0x8195,0x8295,0x8391,
|
||||
0x8495,0x8591,0x8681,0x8785,0x888D,0x8989,0x8A89,0x8B8D,0x8C89,0x8D8D,
|
||||
0x8E8D,0x8F89,0x9095,0x9191,0x9291,0x9395,0x9491,0x9595,0x9685,0x9781,
|
||||
0x9889,0x998D,0x9A8D,0x9B89,0x9C8D,0x9D89,0x9E89,0x9F8D,0xA0B5,0xA1B1,
|
||||
0xA2B1,0xA3B5,0xA4B1,0xA5B5,0xA6A5,0xA7A1,0xA8A9,0xA9AD,0xAAAD,0xABA9,
|
||||
0xACAD,0xADA9,0xAEA9,0xAFAD,0xB0B1,0xB1B5,0xB2B5,0xB3B1,0xB4B5,0xB5B1,
|
||||
0xB6A1,0xB7A5,0xB8AD,0xB9A9,0xBAA9,0xBBAD,0xBCA9,0xBDAD,0xBEAD,0xBFA9,
|
||||
0xC095,0xC191,0xC291,0xC395,0xC491,0xC595,0xC685,0xC781,0xC889,0xC98D,
|
||||
0xCA8D,0xCB89,0xCC8D,0xCD89,0xCE89,0xCF8D,0xD091,0xD195,0xD295,0xD391,
|
||||
0xD495,0xD591,0xD681,0xD785,0xD88D,0xD989,0xDA89,0xDB8D,0xDC89,0xDD8D,
|
||||
0xDE8D,0xDF89,0xE0B1,0xE1B5,0xE2B5,0xE3B1,0xE4B5,0xE5B1,0xE6A1,0xE7A5,
|
||||
0xE8AD,0xE9A9,0xEAA9,0xEBAD,0xECA9,0xEDAD,0xEEAD,0xEFA9,0xF0B5,0xF1B1,
|
||||
0xF2B1,0xF3B5,0xF4B1,0xF5B5,0xF6A5,0xF7A1,0xF8A9,0xF9AD,0xFAAD,0xFBA9,
|
||||
0xFCAD,0xFDA9,0xFEA9,0xFFAD,0x0055,0x0111,0x0211,0x0315,0x0411,0x0515,
|
||||
0x0605,0x0701,0x0809,0x090D,0x0A0D,0x0B09,0x0C0D,0x0D09,0x0E09,0x0F0D,
|
||||
0x1011,0x1115,0x1215,0x1311,0x1415,0x1511,0x1601,0x1705,0x180D,0x1909,
|
||||
0x1A09,0x1B0D,0x1C09,0x1D0D,0x1E0D,0x1F09,0x2031,0x2135,0x2235,0x2331,
|
||||
0x2435,0x2531,0x2621,0x2725,0x282D,0x2929,0x2A29,0x2B2D,0x2C29,0x2D2D,
|
||||
0x2E2D,0x2F29,0x3035,0x3131,0x3231,0x3335,0x3431,0x3535,0x3625,0x3721,
|
||||
0x3829,0x392D,0x3A2D,0x3B29,0x3C2D,0x3D29,0x3E29,0x3F2D,0x4011,0x4115,
|
||||
0x4215,0x4311,0x4415,0x4511,0x4601,0x4705,0x480D,0x4909,0x4A09,0x4B0D,
|
||||
0x4C09,0x4D0D,0x4E0D,0x4F09,0x5015,0x5111,0x5211,0x5315,0x5411,0x5515,
|
||||
0x5605,0x5701,0x5809,0x590D,0x5A0D,0x5B09,0x5C0D,0x5D09,0x5E09,0x5F0D,
|
||||
0x6035,0x6131,0x6231,0x6335,0x6431,0x6535,0xFABE,0xFBBA,0xFCBE,0xFDBA,
|
||||
0xFEBA,0xFFBE,0x0046,0x0102,0x0202,0x0306,0x0402,0x0506,0x0606,0x0702,
|
||||
0x080A,0x090E,0x0A1E,0x0B1A,0x0C1E,0x0D1A,0x0E1A,0x0F1E,0x1002,0x1106,
|
||||
0x1206,0x1302,0x1406,0x1502,0x1602,0x1706,0x180E,0x190A,0x1A1A,0x1B1E,
|
||||
0x1C1A,0x1D1E,0x1E1E,0x1F1A,0x2022,0x2126,0x2226,0x2322,0x2426,0x2522,
|
||||
0x2622,0x2726,0x282E,0x292A,0x2A3A,0x2B3E,0x2C3A,0x2D3E,0x2E3E,0x2F3A,
|
||||
0x3026,0x3122,0x3222,0x3326,0x3422,0x3526,0x3626,0x3722,0x382A,0x392E,
|
||||
0x3A3E,0x3B3A,0x3C3E,0x3D3A,0x3E3A,0x3F3E,0x4002,0x4106,0x4206,0x4302,
|
||||
0x4406,0x4502,0x4602,0x4706,0x480E,0x490A,0x4A1A,0x4B1E,0x4C1A,0x4D1E,
|
||||
0x4E1E,0x4F1A,0x5006,0x5102,0x5202,0x5306,0x5402,0x5506,0x5606,0x5702,
|
||||
0x580A,0x590E,0x5A1E,0x5B1A,0x5C1E,0x5D1A,0x5E1A,0x5F1E,0x6026,0x6122,
|
||||
0x6222,0x6326,0x6422,0x6526,0x6626,0x6722,0x682A,0x692E,0x6A3E,0x6B3A,
|
||||
0x6C3E,0x6D3A,0x6E3A,0x6F3E,0x7022,0x7126,0x7226,0x7322,0x7426,0x7522,
|
||||
0x7622,0x7726,0x782E,0x792A,0x7A3A,0x7B3E,0x7C3A,0x7D3E,0x7E3E,0x7F3A,
|
||||
0x8082,0x8186,0x8286,0x8382,0x8486,0x8582,0x8682,0x8786,0x888E,0x898A,
|
||||
0x8A9A,0x8B9E,0x8C9A,0x8D9E,0x8E9E,0x8F9A,0x9086,0x9182,0x9282,0x9386,
|
||||
0x3423,0x3527,0x3627,0x3723,0x382B,0x392F,0x3A3F,0x3B3B,0x3C3F,0x3D3B,
|
||||
0x3E3B,0x3F3F,0x4003,0x4107,0x4207,0x4303,0x4407,0x4503,0x4603,0x4707,
|
||||
0x480F,0x490B,0x4A1B,0x4B1F,0x4C1B,0x4D1F,0x4E1F,0x4F1B,0x5007,0x5103,
|
||||
0x5203,0x5307,0x5403,0x5507,0x5607,0x5703,0x580B,0x590F,0x5A1F,0x5B1B,
|
||||
0x5C1F,0x5D1B,0x5E1B,0x5F1F,0x6027,0x6123,0x6223,0x6327,0x6423,0x6527,
|
||||
0x6627,0x6723,0x682B,0x692F,0x6A3F,0x6B3B,0x6C3F,0x6D3B,0x6E3B,0x6F3F,
|
||||
0x7023,0x7127,0x7227,0x7323,0x7427,0x7523,0x7623,0x7727,0x782F,0x792B,
|
||||
0x7A3B,0x7B3F,0x7C3B,0x7D3F,0x7E3F,0x7F3B,0x8083,0x8187,0x8287,0x8383,
|
||||
0x8487,0x8583,0x8683,0x8787,0x888F,0x898B,0x8A9B,0x8B9F,0x8C9B,0x8D9F,
|
||||
0x8E9F,0x8F9B,0x9087,0x9183,0x9283,0x9387,0x9483,0x9587,0x9687,0x9783,
|
||||
0x988B,0x998F,0x9A9F,0x9B9B,0x9C9F,0x9D9B,0x9E9B,0x9F9F,0xA0A7,0xA1A3,
|
||||
0xA2A3,0xA3A7,0xA4A3,0xA5A7,0xA6A7,0xA7A3,0xA8AB,0xA9AF,0xAABF,0xABBB,
|
||||
0xACBF,0xADBB,0xAEBB,0xAFBF,0xB0A3,0xB1A7,0xB2A7,0xB3A3,0xB4A7,0xB5A3,
|
||||
0xB6A3,0xB7A7,0xB8AF,0xB9AB,0xBABB,0xBBBF,0xBCBB,0xBDBF,0xBEBF,0xBFBB,
|
||||
0xC087,0xC183,0xC283,0xC387,0xC483,0xC587,0xC687,0xC783,0xC88B,0xC98F,
|
||||
0xCA9F,0xCB9B,0xCC9F,0xCD9B,0xCE9B,0xCF9F,0xD083,0xD187,0xD287,0xD383,
|
||||
0xD487,0xD583,0xD683,0xD787,0xD88F,0xD98B,0xDA9B,0xDB9F,0xDC9B,0xDD9F,
|
||||
0xDE9F,0xDF9B,0xE0A3,0xE1A7,0xE2A7,0xE3A3,0xE4A7,0xE5A3,0xE6A3,0xE7A7,
|
||||
0xE8AF,0xE9AB,0xEABB,0xEBBF,0xECBB,0xEDBF,0xEEBF,0xEFBB,0xF0A7,0xF1A3,
|
||||
0xF2A3,0xF3A7,0xF4A3,0xF5A7,0xF6A7,0xF7A3,0xF8AB,0xF9AF,0xFABF,0xFBBB,
|
||||
0xFCBF,0xFDBB,0xFEBB,0xFFBF,0x0047,0x0103,0x0203,0x0307,0x0403,0x0507,
|
||||
0x0607,0x0703,0x080B,0x090F,0x0A1F,0x0B1B,0x0C1F,0x0D1B,0x0E1B,0x0F1F,
|
||||
0x1003,0x1107,0x1207,0x1303,0x1407,0x1503,0x1603,0x1707,0x180F,0x190B,
|
||||
0x1A1B,0x1B1F,0x1C1B,0x1D1F,0x1E1F,0x1F1B,0x2023,0x2127,0x2227,0x2323,
|
||||
0x2427,0x2523,0x2623,0x2727,0x282F,0x292B,0x2A3B,0x2B3F,0x2C3B,0x2D3F,
|
||||
0x2E3F,0x2F3B,0x3027,0x3123,0x3223,0x3327,0x3423,0x3527,0x3627,0x3723,
|
||||
0x382B,0x392F,0x3A3F,0x3B3B,0x3C3F,0x3D3B,0x3E3B,0x3F3F,0x4003,0x4107,
|
||||
0x4207,0x4303,0x4407,0x4503,0x4603,0x4707,0x480F,0x490B,0x4A1B,0x4B1F,
|
||||
0x4C1B,0x4D1F,0x4E1F,0x4F1B,0x5007,0x5103,0x5203,0x5307,0x5403,0x5507,
|
||||
0x5607,0x5703,0x580B,0x590F,0x5A1F,0x5B1B,0x5C1F,0x5D1B,0x5E1B,0x5F1F,
|
||||
0x6027,0x6123,0x6223,0x6327,0x6423,0x6527,0x6627,0x6723,0x682B,0x692F,
|
||||
0x6A3F,0x6B3B,0x6C3F,0x6D3B,0x6E3B,0x6F3F,0x7023,0x7127,0x7227,0x7323,
|
||||
0x7427,0x7523,0x7623,0x7727,0x782F,0x792B,0x7A3B,0x7B3F,0x7C3B,0x7D3F,
|
||||
0x7E3F,0x7F3B,0x8083,0x8187,0x8287,0x8383,0x8487,0x8583,0x8683,0x8787,
|
||||
0x888F,0x898B,0x8A9B,0x8B9F,0x8C9B,0x8D9F,0x8E9F,0x8F9B,0x9087,0x9183,
|
||||
0x9283,0x9387,0x9483,0x9587,0x9687,0x9783,0x988B,0x998F,
|
||||
];
|
||||
|
||||
public static readonly byte[] Rl0 =
|
||||
[
|
||||
0x44,0x00,0x00,0x04,0x08,0x0c,0x0c,0x08
|
||||
,0x00,0x04,0x04,0x00,0x0c,0x08,0x08,0x0c
|
||||
,0x20,0x24,0x24,0x20,0x2c,0x28,0x28,0x2c
|
||||
,0x24,0x20,0x20,0x24,0x28,0x2c,0x2c,0x28
|
||||
,0x00,0x04,0x04,0x00,0x0c,0x08,0x08,0x0c
|
||||
,0x04,0x00,0x00,0x04,0x08,0x0c,0x0c,0x08
|
||||
,0x24,0x20,0x20,0x24,0x28,0x2c,0x2c,0x28
|
||||
,0x20,0x24,0x24,0x20,0x2c,0x28,0x28,0x2c
|
||||
,0x80,0x84,0x84,0x80,0x8c,0x88,0x88,0x8c
|
||||
,0x84,0x80,0x80,0x84,0x88,0x8c,0x8c,0x88
|
||||
,0xa4,0xa0,0xa0,0xa4,0xa8,0xac,0xac,0xa8
|
||||
,0xa0,0xa4,0xa4,0xa0,0xac,0xa8,0xa8,0xac
|
||||
,0x84,0x80,0x80,0x84,0x88,0x8c,0x8c,0x88
|
||||
,0x80,0x84,0x84,0x80,0x8c,0x88,0x88,0x8c
|
||||
,0xa0,0xa4,0xa4,0xa0,0xac,0xa8,0xa8,0xac
|
||||
,0xa4,0xa0,0xa0,0xa4,0xa8,0xac,0xac,0xa8
|
||||
,0x45,0x01,0x01,0x05,0x09,0x0d,0x0d,0x09
|
||||
,0x01,0x05,0x05,0x01,0x0d,0x09,0x09,0x0d
|
||||
,0x21,0x25,0x25,0x21,0x2d,0x29,0x29,0x2d
|
||||
,0x25,0x21,0x21,0x25,0x29,0x2d,0x2d,0x29
|
||||
,0x01,0x05,0x05,0x01,0x0d,0x09,0x09,0x0d
|
||||
,0x05,0x01,0x01,0x05,0x09,0x0d,0x0d,0x09
|
||||
,0x25,0x21,0x21,0x25,0x29,0x2d,0x2d,0x29
|
||||
,0x21,0x25,0x25,0x21,0x2d,0x29,0x29,0x2d
|
||||
,0x81,0x85,0x85,0x81,0x8d,0x89,0x89,0x8d
|
||||
,0x85,0x81,0x81,0x85,0x89,0x8d,0x8d,0x89
|
||||
,0xa5,0xa1,0xa1,0xa5,0xa9,0xad,0xad,0xa9
|
||||
,0xa1,0xa5,0xa5,0xa1,0xad,0xa9,0xa9,0xad
|
||||
,0x85,0x81,0x81,0x85,0x89,0x8d,0x8d,0x89
|
||||
,0x81,0x85,0x85,0x81,0x8d,0x89,0x89,0x8d
|
||||
,0xa1,0xa5,0xa5,0xa1,0xad,0xa9,0xa9,0xad
|
||||
,0xa5,0xa1,0xa1,0xa5,0xa9,0xad,0xad,0xa9
|
||||
];
|
||||
|
||||
public static readonly byte[] Rl1 =
|
||||
[
|
||||
0x00,0x04,0x04,0x00,0x0c,0x08,0x08,0x0c
|
||||
,0x04,0x00,0x00,0x04,0x08,0x0c,0x0c,0x08
|
||||
,0x24,0x20,0x20,0x24,0x28,0x2c,0x2c,0x28
|
||||
,0x20,0x24,0x24,0x20,0x2c,0x28,0x28,0x2c
|
||||
,0x04,0x00,0x00,0x04,0x08,0x0c,0x0c,0x08
|
||||
,0x00,0x04,0x04,0x00,0x0c,0x08,0x08,0x0c
|
||||
,0x20,0x24,0x24,0x20,0x2c,0x28,0x28,0x2c
|
||||
,0x24,0x20,0x20,0x24,0x28,0x2c,0x2c,0x28
|
||||
,0x84,0x80,0x80,0x84,0x88,0x8c,0x8c,0x88
|
||||
,0x80,0x84,0x84,0x80,0x8c,0x88,0x88,0x8c
|
||||
,0xa0,0xa4,0xa4,0xa0,0xac,0xa8,0xa8,0xac
|
||||
,0xa4,0xa0,0xa0,0xa4,0xa8,0xac,0xac,0xa8
|
||||
,0x80,0x84,0x84,0x80,0x8c,0x88,0x88,0x8c
|
||||
,0x84,0x80,0x80,0x84,0x88,0x8c,0x8c,0x88
|
||||
,0xa4,0xa0,0xa0,0xa4,0xa8,0xac,0xac,0xa8
|
||||
,0xa0,0xa4,0xa4,0xa0,0xac,0xa8,0xa8,0xac
|
||||
,0x01,0x05,0x05,0x01,0x0d,0x09,0x09,0x0d
|
||||
,0x05,0x01,0x01,0x05,0x09,0x0d,0x0d,0x09
|
||||
,0x25,0x21,0x21,0x25,0x29,0x2d,0x2d,0x29
|
||||
,0x21,0x25,0x25,0x21,0x2d,0x29,0x29,0x2d
|
||||
,0x05,0x01,0x01,0x05,0x09,0x0d,0x0d,0x09
|
||||
,0x01,0x05,0x05,0x01,0x0d,0x09,0x09,0x0d
|
||||
,0x21,0x25,0x25,0x21,0x2d,0x29,0x29,0x2d
|
||||
,0x25,0x21,0x21,0x25,0x29,0x2d,0x2d,0x29
|
||||
,0x85,0x81,0x81,0x85,0x89,0x8d,0x8d,0x89
|
||||
,0x81,0x85,0x85,0x81,0x8d,0x89,0x89,0x8d
|
||||
,0xa1,0xa5,0xa5,0xa1,0xad,0xa9,0xa9,0xad
|
||||
,0xa5,0xa1,0xa1,0xa5,0xa9,0xad,0xad,0xa9
|
||||
,0x81,0x85,0x85,0x81,0x8d,0x89,0x89,0x8d
|
||||
,0x85,0x81,0x81,0x85,0x89,0x8d,0x8d,0x89
|
||||
,0xa5,0xa1,0xa1,0xa5,0xa9,0xad,0xad,0xa9
|
||||
,0xa1,0xa5,0xa5,0xa1,0xad,0xa9,0xa9,0xad
|
||||
];
|
||||
|
||||
public static readonly byte[] Rr0 =
|
||||
[
|
||||
0x44,0x45,0x00,0x01,0x00,0x01,0x04,0x05
|
||||
,0x00,0x01,0x04,0x05,0x04,0x05,0x00,0x01
|
||||
,0x08,0x09,0x0c,0x0d,0x0c,0x0d,0x08,0x09
|
||||
,0x0c,0x0d,0x08,0x09,0x08,0x09,0x0c,0x0d
|
||||
,0x00,0x01,0x04,0x05,0x04,0x05,0x00,0x01
|
||||
,0x04,0x05,0x00,0x01,0x00,0x01,0x04,0x05
|
||||
,0x0c,0x0d,0x08,0x09,0x08,0x09,0x0c,0x0d
|
||||
,0x08,0x09,0x0c,0x0d,0x0c,0x0d,0x08,0x09
|
||||
,0x20,0x21,0x24,0x25,0x24,0x25,0x20,0x21
|
||||
,0x24,0x25,0x20,0x21,0x20,0x21,0x24,0x25
|
||||
,0x2c,0x2d,0x28,0x29,0x28,0x29,0x2c,0x2d
|
||||
,0x28,0x29,0x2c,0x2d,0x2c,0x2d,0x28,0x29
|
||||
,0x24,0x25,0x20,0x21,0x20,0x21,0x24,0x25
|
||||
,0x20,0x21,0x24,0x25,0x24,0x25,0x20,0x21
|
||||
,0x28,0x29,0x2c,0x2d,0x2c,0x2d,0x28,0x29
|
||||
,0x2c,0x2d,0x28,0x29,0x28,0x29,0x2c,0x2d
|
||||
,0x00,0x01,0x04,0x05,0x04,0x05,0x00,0x01
|
||||
,0x04,0x05,0x00,0x01,0x00,0x01,0x04,0x05
|
||||
,0x0c,0x0d,0x08,0x09,0x08,0x09,0x0c,0x0d
|
||||
,0x08,0x09,0x0c,0x0d,0x0c,0x0d,0x08,0x09
|
||||
,0x04,0x05,0x00,0x01,0x00,0x01,0x04,0x05
|
||||
,0x00,0x01,0x04,0x05,0x04,0x05,0x00,0x01
|
||||
,0x08,0x09,0x0c,0x0d,0x0c,0x0d,0x08,0x09
|
||||
,0x0c,0x0d,0x08,0x09,0x08,0x09,0x0c,0x0d
|
||||
,0x24,0x25,0x20,0x21,0x20,0x21,0x24,0x25
|
||||
,0x20,0x21,0x24,0x25,0x24,0x25,0x20,0x21
|
||||
,0x28,0x29,0x2c,0x2d,0x2c,0x2d,0x28,0x29
|
||||
,0x2c,0x2d,0x28,0x29,0x28,0x29,0x2c,0x2d
|
||||
,0x20,0x21,0x24,0x25,0x24,0x25,0x20,0x21
|
||||
,0x24,0x25,0x20,0x21,0x20,0x21,0x24,0x25
|
||||
,0x2c,0x2d,0x28,0x29,0x28,0x29,0x2c,0x2d
|
||||
,0x28,0x29,0x2c,0x2d,0x2c,0x2d,0x28,0x29
|
||||
];
|
||||
|
||||
public static readonly byte[] Rr1 =
|
||||
[
|
||||
0x80,0x81,0x84,0x85,0x84,0x85,0x80,0x81
|
||||
,0x84,0x85,0x80,0x81,0x80,0x81,0x84,0x85
|
||||
,0x8c,0x8d,0x88,0x89,0x88,0x89,0x8c,0x8d
|
||||
,0x88,0x89,0x8c,0x8d,0x8c,0x8d,0x88,0x89
|
||||
,0x84,0x85,0x80,0x81,0x80,0x81,0x84,0x85
|
||||
,0x80,0x81,0x84,0x85,0x84,0x85,0x80,0x81
|
||||
,0x88,0x89,0x8c,0x8d,0x8c,0x8d,0x88,0x89
|
||||
,0x8c,0x8d,0x88,0x89,0x88,0x89,0x8c,0x8d
|
||||
,0xa4,0xa5,0xa0,0xa1,0xa0,0xa1,0xa4,0xa5
|
||||
,0xa0,0xa1,0xa4,0xa5,0xa4,0xa5,0xa0,0xa1
|
||||
,0xa8,0xa9,0xac,0xad,0xac,0xad,0xa8,0xa9
|
||||
,0xac,0xad,0xa8,0xa9,0xa8,0xa9,0xac,0xad
|
||||
,0xa0,0xa1,0xa4,0xa5,0xa4,0xa5,0xa0,0xa1
|
||||
,0xa4,0xa5,0xa0,0xa1,0xa0,0xa1,0xa4,0xa5
|
||||
,0xac,0xad,0xa8,0xa9,0xa8,0xa9,0xac,0xad
|
||||
,0xa8,0xa9,0xac,0xad,0xac,0xad,0xa8,0xa9
|
||||
,0x84,0x85,0x80,0x81,0x80,0x81,0x84,0x85
|
||||
,0x80,0x81,0x84,0x85,0x84,0x85,0x80,0x81
|
||||
,0x88,0x89,0x8c,0x8d,0x8c,0x8d,0x88,0x89
|
||||
,0x8c,0x8d,0x88,0x89,0x88,0x89,0x8c,0x8d
|
||||
,0x80,0x81,0x84,0x85,0x84,0x85,0x80,0x81
|
||||
,0x84,0x85,0x80,0x81,0x80,0x81,0x84,0x85
|
||||
,0x8c,0x8d,0x88,0x89,0x88,0x89,0x8c,0x8d
|
||||
,0x88,0x89,0x8c,0x8d,0x8c,0x8d,0x88,0x89
|
||||
,0xa0,0xa1,0xa4,0xa5,0xa4,0xa5,0xa0,0xa1
|
||||
,0xa4,0xa5,0xa0,0xa1,0xa0,0xa1,0xa4,0xa5
|
||||
,0xac,0xad,0xa8,0xa9,0xa8,0xa9,0xac,0xad
|
||||
,0xa8,0xa9,0xac,0xad,0xac,0xad,0xa8,0xa9
|
||||
,0xa4,0xa5,0xa0,0xa1,0xa0,0xa1,0xa4,0xa5
|
||||
,0xa0,0xa1,0xa4,0xa5,0xa4,0xa5,0xa0,0xa1
|
||||
,0xa8,0xa9,0xac,0xad,0xac,0xad,0xa8,0xa9
|
||||
,0xac,0xad,0xa8,0xa9,0xa8,0xa9,0xac,0xad
|
||||
];
|
||||
|
||||
public static readonly byte[] Sraf =
|
||||
[
|
||||
0x44,0x45,0x00,0x01,0x00,0x01,0x04,0x05
|
||||
,0x00,0x01,0x04,0x05,0x04,0x05,0x00,0x01
|
||||
,0x08,0x09,0x0c,0x0d,0x0c,0x0d,0x08,0x09
|
||||
,0x0c,0x0d,0x08,0x09,0x08,0x09,0x0c,0x0d
|
||||
,0x00,0x01,0x04,0x05,0x04,0x05,0x00,0x01
|
||||
,0x04,0x05,0x00,0x01,0x00,0x01,0x04,0x05
|
||||
,0x0c,0x0d,0x08,0x09,0x08,0x09,0x0c,0x0d
|
||||
,0x08,0x09,0x0c,0x0d,0x0c,0x0d,0x08,0x09
|
||||
,0x20,0x21,0x24,0x25,0x24,0x25,0x20,0x21
|
||||
,0x24,0x25,0x20,0x21,0x20,0x21,0x24,0x25
|
||||
,0x2c,0x2d,0x28,0x29,0x28,0x29,0x2c,0x2d
|
||||
,0x28,0x29,0x2c,0x2d,0x2c,0x2d,0x28,0x29
|
||||
,0x24,0x25,0x20,0x21,0x20,0x21,0x24,0x25
|
||||
,0x20,0x21,0x24,0x25,0x24,0x25,0x20,0x21
|
||||
,0x28,0x29,0x2c,0x2d,0x2c,0x2d,0x28,0x29
|
||||
,0x2c,0x2d,0x28,0x29,0x28,0x29,0x2c,0x2d
|
||||
,0x84,0x85,0x80,0x81,0x80,0x81,0x84,0x85
|
||||
,0x80,0x81,0x84,0x85,0x84,0x85,0x80,0x81
|
||||
,0x88,0x89,0x8c,0x8d,0x8c,0x8d,0x88,0x89
|
||||
,0x8c,0x8d,0x88,0x89,0x88,0x89,0x8c,0x8d
|
||||
,0x80,0x81,0x84,0x85,0x84,0x85,0x80,0x81
|
||||
,0x84,0x85,0x80,0x81,0x80,0x81,0x84,0x85
|
||||
,0x8c,0x8d,0x88,0x89,0x88,0x89,0x8c,0x8d
|
||||
,0x88,0x89,0x8c,0x8d,0x8c,0x8d,0x88,0x89
|
||||
,0xa0,0xa1,0xa4,0xa5,0xa4,0xa5,0xa0,0xa1
|
||||
,0xa4,0xa5,0xa0,0xa1,0xa0,0xa1,0xa4,0xa5
|
||||
,0xac,0xad,0xa8,0xa9,0xa8,0xa9,0xac,0xad
|
||||
,0xa8,0xa9,0xac,0xad,0xac,0xad,0xa8,0xa9
|
||||
,0xa4,0xa5,0xa0,0xa1,0xa0,0xa1,0xa4,0xa5
|
||||
,0xa0,0xa1,0xa4,0xa5,0xa4,0xa5,0xa0,0xa1
|
||||
,0xa8,0xa9,0xac,0xad,0xac,0xad,0xa8,0xa9
|
||||
,0xac,0xad,0xa8,0xa9,0xa8,0xa9,0xac,0xad
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Description: Z80 CPU Emulator [ALU part]
|
||||
* Date: 13.04.2007
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu.Processor
|
||||
{
|
||||
public partial class Z80Cpu
|
||||
{
|
||||
#region ALU
|
||||
|
||||
private void ALU_ADDR(byte src)
|
||||
{
|
||||
regs.F = CpuTables.Adcf[regs.A + src * 0x100];
|
||||
regs.A += src;
|
||||
}
|
||||
|
||||
private void ALU_ADCR(byte src)
|
||||
{
|
||||
var carry = regs.F & CpuFlags.C;
|
||||
regs.F = CpuTables.Adcf[regs.A + src * 0x100 + 0x10000 * carry];
|
||||
regs.A += (byte)(src + carry);
|
||||
}
|
||||
|
||||
private void ALU_SUBR(byte src)
|
||||
{
|
||||
regs.F = CpuTables.Sbcf[regs.A * 0x100 + src];
|
||||
regs.A -= src;
|
||||
}
|
||||
|
||||
private void ALU_SBCR(byte src)
|
||||
{
|
||||
var carry = regs.F & CpuFlags.C;
|
||||
regs.F = CpuTables.Sbcf[regs.A * 0x100 + src + 0x10000 * carry];
|
||||
regs.A -= (byte)(src + carry);
|
||||
}
|
||||
|
||||
private void ALU_ANDR(byte src)
|
||||
{
|
||||
regs.A &= src;
|
||||
regs.F = (byte)(CpuTables.Logf[regs.A] | CpuFlags.H);
|
||||
}
|
||||
|
||||
private void ALU_XORR(byte src)
|
||||
{
|
||||
regs.A ^= src;
|
||||
regs.F = CpuTables.Logf[regs.A];
|
||||
}
|
||||
|
||||
private void ALU_ORR(byte src)
|
||||
{
|
||||
regs.A |= src;
|
||||
regs.F = CpuTables.Logf[regs.A];
|
||||
}
|
||||
|
||||
private void ALU_CPR(byte src)
|
||||
{
|
||||
regs.F = CpuTables.Cpf[regs.A * 0x100 + src];
|
||||
}
|
||||
|
||||
private byte ALU_INCR(byte x)
|
||||
{
|
||||
regs.F = (byte)(CpuTables.Incf[x] | (regs.F & CpuFlags.C));
|
||||
x++;
|
||||
return x;
|
||||
}
|
||||
|
||||
private byte ALU_DECR(byte x)
|
||||
{
|
||||
regs.F = (byte)(CpuTables.Decf[x] | (regs.F & CpuFlags.C));
|
||||
x--;
|
||||
return x;
|
||||
}
|
||||
|
||||
private ushort ALU_ADDHLRR(ushort rhl, ushort rde)
|
||||
{
|
||||
regs.F = (byte)(regs.F & CpuFlags.NotHNCF3F5);
|
||||
regs.F |= (byte)((((rhl & 0x0FFF) + (rde & 0x0FFF)) >> 8) & CpuFlags.H);
|
||||
uint res = (uint)((rhl & 0xFFFF) + (rde & 0xFFFF));
|
||||
|
||||
if ((res & 0x10000) != 0) regs.F |= CpuFlags.C;
|
||||
regs.F |= (byte)((res >> 8) & CpuFlags.F3F5);
|
||||
return (ushort)res;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ALU #CB
|
||||
|
||||
private byte ALU_RLC(int x)
|
||||
{
|
||||
regs.F = CpuTables.Rlcf[x];
|
||||
x <<= 1;
|
||||
if ((x & 0x100) != 0) x |= 0x01;
|
||||
return (byte)x;
|
||||
}
|
||||
|
||||
private byte ALU_RRC(int x)
|
||||
{
|
||||
regs.F = CpuTables.Rrcf[x];
|
||||
if ((x & 0x01) != 0) x = (x >> 1) | 0x80;
|
||||
else x >>= 1;
|
||||
return (byte)x;
|
||||
}
|
||||
|
||||
private byte ALU_RL(int x)
|
||||
{
|
||||
if ((regs.F & CpuFlags.C) != 0)
|
||||
{
|
||||
regs.F = CpuTables.Rl1[x];
|
||||
x <<= 1;
|
||||
x++;
|
||||
}
|
||||
else
|
||||
{
|
||||
regs.F = CpuTables.Rl0[x];
|
||||
x <<= 1;
|
||||
}
|
||||
return (byte)x;
|
||||
}
|
||||
|
||||
private byte ALU_RR(int x)
|
||||
{
|
||||
if ((regs.F & CpuFlags.C) != 0)
|
||||
{
|
||||
regs.F = CpuTables.Rr1[x];
|
||||
x >>= 1;
|
||||
x += 0x80;
|
||||
}
|
||||
else
|
||||
{
|
||||
regs.F = CpuTables.Rr0[x];
|
||||
x >>= 1;
|
||||
}
|
||||
return (byte)x;
|
||||
}
|
||||
|
||||
private byte ALU_SLA(int x)
|
||||
{
|
||||
regs.F = CpuTables.Rl0[x];
|
||||
x <<= 1;
|
||||
return (byte)x;
|
||||
}
|
||||
|
||||
private byte ALU_SRA(int x)
|
||||
{
|
||||
regs.F = CpuTables.Sraf[x];
|
||||
x = (x >> 1) + (x & 0x80);
|
||||
return (byte)x;
|
||||
}
|
||||
|
||||
private byte ALU_SLL(int x)
|
||||
{
|
||||
regs.F = CpuTables.Rl1[x];
|
||||
x <<= 1;
|
||||
x++;
|
||||
return (byte)x;
|
||||
}
|
||||
|
||||
private byte ALU_SRL(int x)
|
||||
{
|
||||
regs.F = CpuTables.Rr0[x];
|
||||
x >>= 1;
|
||||
return (byte)x;
|
||||
}
|
||||
|
||||
private void ALU_BIT(byte src, int bit)
|
||||
{
|
||||
regs.F = (byte)(CpuTables.Logf[src & (1 << bit)] | CpuFlags.H | (regs.F & CpuFlags.C) | (src & CpuFlags.F3F5));
|
||||
}
|
||||
|
||||
private void ALU_BITMEM(byte src, int bit)
|
||||
{
|
||||
regs.F = (byte)(CpuTables.Logf[src & (1 << bit)] | CpuFlags.H | (regs.F & CpuFlags.C));
|
||||
regs.F = (byte)((regs.F & CpuFlags.NotF3F5) | (regs.MH & CpuFlags.F3F5));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,722 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Description: Z80 CPU Emulator [direct opcode part]
|
||||
* Date: 13.04.2007
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu.Processor
|
||||
{
|
||||
public partial class Z80Cpu
|
||||
{
|
||||
#region direct/DD/FD
|
||||
|
||||
private void INA_NN_(byte cmd) // IN A,(N) [11T]
|
||||
{
|
||||
// 11T (4, 3, 4)
|
||||
|
||||
regs.MW = RDMEM(regs.PC++); Tact += 3;
|
||||
regs.MW += (ushort)(regs.A << 8);
|
||||
|
||||
regs.A = RDPORT(regs.MW); Tact += 4;
|
||||
regs.MW++;
|
||||
}
|
||||
|
||||
private void OUT_NN_A(byte cmd) // OUT (N),A [11T]+
|
||||
{
|
||||
// 11T (4, 3, 4)
|
||||
|
||||
regs.MW = RDMEM(regs.PC++); Tact += 3;
|
||||
regs.MW += (ushort)(regs.A << 8);
|
||||
|
||||
WRPORT(regs.MW, regs.A); Tact += 4;
|
||||
regs.ML++;
|
||||
}
|
||||
|
||||
private void DI(byte cmd)
|
||||
{
|
||||
IFF1 = false;
|
||||
IFF2 = false;
|
||||
}
|
||||
|
||||
private void EI(byte cmd)
|
||||
{
|
||||
IFF1 = true;
|
||||
IFF2 = true;
|
||||
BINT = true;
|
||||
}
|
||||
|
||||
private void LDSPHL(byte cmd) // LD SP,HL
|
||||
{
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
regs.SP = regs.HL;
|
||||
}
|
||||
|
||||
private void EX_SP_HL(byte cmd) // EX (SP),HL
|
||||
{
|
||||
// 19T (4, 3, 4, 3, 5)
|
||||
|
||||
ushort tmpsp = regs.SP;
|
||||
regs.MW = RDMEM(tmpsp); Tact += 3;
|
||||
tmpsp++;
|
||||
|
||||
regs.MW += (ushort)(RDMEM(tmpsp) * 0x100); Tact += 3;
|
||||
RDNOMREQ(tmpsp); Tact += 1;
|
||||
|
||||
WRMEM(tmpsp, regs.H); Tact += 3;
|
||||
tmpsp--;
|
||||
|
||||
WRMEM(tmpsp, regs.L); Tact += 3;
|
||||
WRNOMREQ(tmpsp); Tact++;
|
||||
WRNOMREQ(tmpsp); Tact++;
|
||||
regs.HL = regs.MW;
|
||||
}
|
||||
|
||||
private void JP_HL_(byte cmd) // JP (HL)
|
||||
{
|
||||
regs.PC = regs.HL;
|
||||
}
|
||||
|
||||
private void EXDEHL(byte cmd) // EX DE,HL
|
||||
{
|
||||
(regs.DE, regs.HL) = (regs.HL, regs.DE);
|
||||
}
|
||||
|
||||
private void EXAFAF(byte cmd) // EX AF,AF'
|
||||
{
|
||||
(regs._AF, regs.AF) = (regs.AF, regs._AF);
|
||||
}
|
||||
|
||||
private void EXX(byte cmd) // EXX
|
||||
{
|
||||
(regs._BC, regs.BC) = (regs.BC, regs._BC);
|
||||
(regs._DE, regs.DE) = (regs.DE, regs._DE);
|
||||
(regs._HL, regs.HL) = (regs.HL, regs._HL);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region logical
|
||||
|
||||
private void RLCA(byte cmd)
|
||||
{
|
||||
regs.F = (byte)(CpuTables.Rlcaf[regs.A] | (regs.F & CpuFlags.SZP));
|
||||
int x = regs.A;
|
||||
x <<= 1;
|
||||
if ((x & 0x100) != 0) x |= 0x01;
|
||||
regs.A = (byte)x;
|
||||
}
|
||||
|
||||
private void RRCA(byte cmd)
|
||||
{
|
||||
regs.F = (byte)(CpuTables.Rrcaf[regs.A] | (regs.F & CpuFlags.SZP));
|
||||
int x = regs.A;
|
||||
if ((x & 0x01) != 0) x = (x >> 1) | 0x80;
|
||||
else x >>= 1;
|
||||
regs.A = (byte)x;
|
||||
}
|
||||
|
||||
private void RLA(byte cmd)
|
||||
{
|
||||
var carry = (regs.F & CpuFlags.C) != 0;
|
||||
regs.F = (byte)(CpuTables.Rlcaf[regs.A] | (regs.F & CpuFlags.SZP)); // use same table with rlca
|
||||
regs.A = (byte)(regs.A << 1);
|
||||
if (carry) regs.A |= 0x01;
|
||||
}
|
||||
|
||||
private void RRA(byte cmd)
|
||||
{
|
||||
var carry = (regs.F & CpuFlags.C) != 0;
|
||||
regs.F = (byte)(CpuTables.Rrcaf[regs.A] | (regs.F & CpuFlags.SZP)); // use same table with rrca
|
||||
regs.A = (byte)(regs.A >> 1);
|
||||
if (carry) regs.A |= 0x80;
|
||||
}
|
||||
|
||||
private void DAA(byte cmd)
|
||||
{
|
||||
regs.AF = CpuTables.Daaf[regs.A + 0x100 * ((regs.F & 3) + ((regs.F >> 2) & 4))];
|
||||
}
|
||||
|
||||
private void CPL(byte cmd)
|
||||
{
|
||||
regs.A = (byte)~regs.A;
|
||||
regs.F = (byte)((regs.F & CpuFlags.NotF3F5) | CpuFlags.HN | (regs.A & CpuFlags.F3F5));
|
||||
}
|
||||
|
||||
private void SCF_ZILOG(byte cmd)
|
||||
{
|
||||
regs.F = (byte)((regs.F & CpuFlags.SZP) |
|
||||
(((regs.F ^ regs.Q) | regs.A) & CpuFlags.F3F5) |
|
||||
CpuFlags.C);
|
||||
}
|
||||
|
||||
private void SCF_ST_CMOS(byte cmd)
|
||||
{
|
||||
regs.F = (byte)((regs.F & CpuFlags.SZP) |
|
||||
((((regs.F ^ regs.Q) & CpuFlags.F5) | regs.A) & CpuFlags.F3F5) |
|
||||
CpuFlags.C);
|
||||
}
|
||||
|
||||
private void SCF_NEC_NMOS(byte cmd)
|
||||
{
|
||||
regs.F = (byte)((regs.F & CpuFlags.SZP) |
|
||||
(regs.A & CpuFlags.F3F5) |
|
||||
CpuFlags.C);
|
||||
}
|
||||
|
||||
private void CCF_ZILOG(byte cmd)
|
||||
{
|
||||
regs.F = (byte)(((regs.F & CpuFlags.SZPC) ^ CpuFlags.C) |
|
||||
(((regs.F ^ regs.Q) | regs.A) & CpuFlags.F3F5) |
|
||||
((regs.F & CpuFlags.C) << 4));
|
||||
}
|
||||
|
||||
private void CCF_ST_CMOS(byte cmd)
|
||||
{
|
||||
regs.F = (byte)(((regs.F & CpuFlags.SZPC) ^ CpuFlags.C) |
|
||||
((((regs.F ^ regs.Q) & CpuFlags.F5) | regs.A) & CpuFlags.F3F5) |
|
||||
((regs.F & CpuFlags.C) << 4));
|
||||
}
|
||||
|
||||
private void CCF_NEC_NMOS(byte cmd)
|
||||
{
|
||||
regs.F = (byte)(((regs.F & CpuFlags.SZPC) ^ CpuFlags.C) |
|
||||
(regs.A & CpuFlags.F3F5) |
|
||||
((regs.F & CpuFlags.C) << 4));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region jmp/call/ret/jr
|
||||
|
||||
private static readonly byte[] s_conds =
|
||||
[
|
||||
CpuFlags.Z,
|
||||
CpuFlags.C,
|
||||
CpuFlags.P,
|
||||
CpuFlags.S
|
||||
];
|
||||
|
||||
private void DJNZ(byte cmd) // DJNZ nn
|
||||
{
|
||||
// B==0 => 8T (5, 3)
|
||||
// B!=0 => 13T (5, 3, 5)
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
int drel = (sbyte)RDMEM(regs.PC); Tact += 3;
|
||||
regs.PC++;
|
||||
|
||||
if (--regs.B != 0)
|
||||
{
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
regs.MW = (ushort)(regs.PC + drel);
|
||||
regs.PC = regs.MW;
|
||||
}
|
||||
}
|
||||
|
||||
private void JRNN(byte cmd) // JR nn
|
||||
{
|
||||
// 12T (4, 3, 5)
|
||||
|
||||
int drel = (sbyte)RDMEM(regs.PC); Tact += 3;
|
||||
regs.PC++;
|
||||
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
regs.MW = (ushort)(regs.PC + drel);
|
||||
regs.PC = regs.MW;
|
||||
}
|
||||
|
||||
private void JRXNN(byte cmd) // JR x,nn
|
||||
{
|
||||
// false => 7T (4, 3)
|
||||
// true => 12 (4, 3, 5)
|
||||
var cond = (cmd & 0x18) >> 3;
|
||||
var mask = s_conds[cond >> 1];
|
||||
var f = regs.AF & mask;
|
||||
if ((cond & 1) != 0) f ^= mask;
|
||||
|
||||
int drel = (sbyte)RDMEM(regs.PC); Tact += 3;
|
||||
regs.PC++;
|
||||
|
||||
if (f == 0)
|
||||
{
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
regs.MW = (ushort)(regs.PC + drel);
|
||||
regs.PC = regs.MW;
|
||||
}
|
||||
}
|
||||
|
||||
private void CALLNNNN(byte cmd) // CALL
|
||||
{
|
||||
// 17T (4, 3, 4, 3, 3)
|
||||
|
||||
regs.MW = RDMEM(regs.PC); Tact += 3;
|
||||
regs.PC++;
|
||||
|
||||
regs.MW += (ushort)(RDMEM(regs.PC) * 0x100); Tact += 3;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
regs.PC++;
|
||||
regs.SP--;
|
||||
|
||||
WRMEM(regs.SP, (byte)(regs.PC >> 8)); Tact += 3;
|
||||
regs.SP--;
|
||||
|
||||
WRMEM(regs.SP, (byte)regs.PC); Tact += 3;
|
||||
regs.PC = regs.MW;
|
||||
}
|
||||
|
||||
private void CALLXNNNN(byte cmd) // CALL x,#nn
|
||||
{
|
||||
// false => 10T (4, 3, 3)
|
||||
// true => 17T (4, 3, 4, 3, 3)
|
||||
var cond = (cmd & 0x38) >> 3;
|
||||
var mask = s_conds[cond >> 1];
|
||||
var f = regs.AF & mask;
|
||||
if ((cond & 1) != 0) f ^= mask;
|
||||
|
||||
regs.MW = RDMEM(regs.PC); Tact += 3;
|
||||
regs.PC++;
|
||||
|
||||
regs.MW += (ushort)(RDMEM(regs.PC) * 0x100); Tact += 3;
|
||||
if (f == 0)
|
||||
{
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
}
|
||||
regs.PC++;
|
||||
|
||||
if (f == 0)
|
||||
{
|
||||
regs.SP--;
|
||||
|
||||
WRMEM(regs.SP, (byte)(regs.PC >> 8)); Tact += 3;
|
||||
regs.SP--;
|
||||
|
||||
WRMEM(regs.SP, (byte)regs.PC); Tact += 3;
|
||||
regs.PC = regs.MW;
|
||||
}
|
||||
}
|
||||
|
||||
private void RET(byte cmd) // RET
|
||||
{
|
||||
// 10T (4, 3, 3)
|
||||
|
||||
regs.MW = RDMEM(regs.SP); Tact += 3;
|
||||
regs.SP++;
|
||||
|
||||
regs.MW += (ushort)(RDMEM(regs.SP) * 0x100); Tact += 3;
|
||||
regs.SP++;
|
||||
regs.PC = regs.MW;
|
||||
}
|
||||
|
||||
private void RETX(byte cmd) // RET x
|
||||
{
|
||||
// false => 5T (5)
|
||||
// true => 11T (5, 3, 3)
|
||||
var cond = (cmd & 0x38) >> 3;
|
||||
var mask = s_conds[cond >> 1];
|
||||
var f = regs.AF & mask;
|
||||
if ((cond & 1) != 0) f ^= mask;
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
if (f == 0)
|
||||
{
|
||||
regs.MW = RDMEM(regs.SP); Tact += 3;
|
||||
regs.SP++;
|
||||
|
||||
regs.MW += (ushort)(RDMEM(regs.SP) * 0x100); Tact += 3;
|
||||
regs.SP++;
|
||||
regs.PC = regs.MW;
|
||||
}
|
||||
}
|
||||
|
||||
private void JPNNNN(byte cmd) // JP nnnn
|
||||
{
|
||||
// 10T (4, 3, 3)
|
||||
|
||||
regs.MW = RDMEM(regs.PC); Tact += 3;
|
||||
regs.PC++;
|
||||
|
||||
regs.MW += (ushort)(RDMEM(regs.PC) * 0x100); Tact += 3;
|
||||
regs.PC = regs.MW;
|
||||
}
|
||||
|
||||
private void JPXNN(byte cmd) // JP x,#nn ???
|
||||
{
|
||||
// 10T (4, 3, 3)
|
||||
var cond = (cmd & 0x38) >> 3;
|
||||
var mask = s_conds[cond >> 1];
|
||||
var f = regs.AF & mask;
|
||||
if ((cond & 1) != 0) f ^= mask;
|
||||
|
||||
regs.MW = RDMEM(regs.PC); Tact += 3;
|
||||
regs.PC++;
|
||||
|
||||
regs.MW += (ushort)(RDMEM(regs.PC) * 0x100); Tact += 3;
|
||||
regs.PC++;
|
||||
|
||||
if (f == 0)
|
||||
regs.PC = regs.MW;
|
||||
}
|
||||
|
||||
private void RSTNN(byte cmd) // RST #nn ?TIME?
|
||||
{
|
||||
// 11T (5, 3, 3)
|
||||
var rst = (ushort)(cmd & 0x38);
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
regs.SP--;
|
||||
|
||||
WRMEM(regs.SP, (byte)(regs.PC >> 8)); Tact += 3;
|
||||
regs.SP--;
|
||||
|
||||
WRMEM(regs.SP, (byte)regs.PC); Tact += 3;
|
||||
regs.MW = rst;
|
||||
regs.PC = regs.MW;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region push/pop
|
||||
|
||||
private void PUSHRR(byte cmd) // PUSH RR ?TIME?
|
||||
{
|
||||
// 11T (5, 3, 3)
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
RDNOMREQ(regs.IR); Tact += 1;
|
||||
ushort val = rr == CpuRegId.Sp ? regs.AF : _pairGetters[rr]();
|
||||
|
||||
regs.SP--;
|
||||
WRMEM(regs.SP, (byte)(val >> 8)); Tact += 3;
|
||||
|
||||
regs.SP--;
|
||||
WRMEM(regs.SP, (byte)val); Tact += 3;
|
||||
}
|
||||
|
||||
private void POPRR(byte cmd) // POP RR
|
||||
{
|
||||
// 10T (4, 3, 3)
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
var val = (ushort)RDMEM(regs.SP);
|
||||
regs.SP++;
|
||||
Tact += 3;
|
||||
|
||||
val |= (ushort)(RDMEM(regs.SP) << 8);
|
||||
regs.SP++;
|
||||
if (rr == CpuRegId.Sp) regs.AF = val;
|
||||
else _pairSetters[rr](val);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ALU
|
||||
|
||||
private void ALUAN(byte cmd)
|
||||
{
|
||||
// 7T (4, 3)
|
||||
var op = (cmd & 0x38) >> 3;
|
||||
|
||||
var val = RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
_alualg[op](val);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void ALUAR(byte cmd) // ADD/ADC/SUB/SBC/AND/XOR/OR/CP A,R
|
||||
{
|
||||
var r = cmd & 0x07;
|
||||
var op = (cmd & 0x38) >> 3;
|
||||
|
||||
_alualg[op](_regGetters[r]());
|
||||
}
|
||||
|
||||
private void ALUA_HL_(byte cmd) // ADD/ADC/SUB/SBC/AND/XOR/OR/CP A,(HL)
|
||||
{
|
||||
// 7T (4, 3)
|
||||
var op = (cmd & 0x38) >> 3;
|
||||
|
||||
var val = RDMEM(regs.HL);
|
||||
_alualg[op](val);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void ADDHLRR(byte cmd) // ADD HL,RR
|
||||
{
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
regs.MW = (ushort)(regs.HL + 1);
|
||||
regs.HL = ALU_ADDHLRR(regs.HL, _pairGetters[(cmd & 0x30) >> 4]());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region loads
|
||||
|
||||
private void LDA_RR_(byte cmd) // LD A,(RR)
|
||||
{
|
||||
// 7T (4, 3)
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
var rrValue = _pairGetters[rr]();
|
||||
regs.A = RDMEM(rrValue);
|
||||
regs.MW = (ushort)(rrValue + 1);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void LDA_NN_(byte cmd) // LD A,(nnnn)
|
||||
{
|
||||
// 13T (4, 3, 3, 3)
|
||||
|
||||
ushort adr = RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
adr += (ushort)(RDMEM(regs.PC) * 0x100);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
regs.A = RDMEM(adr);
|
||||
regs.MW = (ushort)(adr + 1);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void LDHL_NN_(byte cmd) // LD HL,(nnnn)
|
||||
{
|
||||
// 16T (4, 3, 3, 3, 3)
|
||||
|
||||
var adr = (ushort)RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
adr += (ushort)(RDMEM(regs.PC) * 0x100);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
var val = (ushort)RDMEM(adr);
|
||||
regs.MW = (ushort)(adr + 1);
|
||||
Tact += 3;
|
||||
|
||||
val += (ushort)(RDMEM(regs.MW) * 0x100);
|
||||
regs.HL = val;
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void LD_RR_A(byte cmd) // LD (RR),A
|
||||
{
|
||||
// 7T (4, 3)
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
var rrValue = _pairGetters[rr]();
|
||||
WRMEM(rrValue, regs.A);
|
||||
regs.MH = regs.A;
|
||||
regs.ML = (byte)(rrValue + 1);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void LD_NN_A(byte cmd) // LD (nnnn),A
|
||||
{
|
||||
// 13T (4, 3, 3, 3)
|
||||
|
||||
ushort adr = RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
adr += (ushort)(RDMEM(regs.PC) * 0x100);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
WRMEM(adr, regs.A);
|
||||
regs.MH = regs.A;
|
||||
regs.ML = (byte)(adr + 1);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void LD_NN_HL(byte cmd) // LD (nnnn),HL
|
||||
{
|
||||
// 16T (4, 3, 3, 3, 3)
|
||||
|
||||
ushort adr = RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
adr += (ushort)(RDMEM(regs.PC) * 0x100);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
WRMEM(adr, regs.L);
|
||||
regs.MW = (ushort)(adr + 1);
|
||||
Tact += 3;
|
||||
|
||||
WRMEM(regs.MW, regs.H);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void LDRRNNNN(byte cmd) // LD RR,nnnn
|
||||
{
|
||||
// 10T (4, 3, 3)
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
ushort val = RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
val |= (ushort)(RDMEM(regs.PC) << 8);
|
||||
regs.PC++;
|
||||
_pairSetters[rr](val);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void LDRNN(byte cmd) // LD R,nn
|
||||
{
|
||||
// 7T (4, 3)
|
||||
var r = (cmd & 0x38) >> 3;
|
||||
|
||||
_regSetters[r](RDMEM(regs.PC));
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void LD_HL_NN(byte cmd) // LD (HL),nn
|
||||
{
|
||||
// 10T (4, 3, 3)
|
||||
|
||||
var val = RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
WRMEM(regs.HL, val);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void LDRdRs(byte cmd) // LD R1,R2
|
||||
{
|
||||
var rsrc = cmd & 0x07;
|
||||
var rdst = (cmd & 0x38) >> 3;
|
||||
_regSetters[rdst](_regGetters[rsrc]());
|
||||
}
|
||||
|
||||
private void LD_HL_R(byte cmd) // LD (HL),R
|
||||
{
|
||||
// 7T (4, 3)
|
||||
var r = cmd & 0x07;
|
||||
|
||||
WRMEM(regs.HL, _regGetters[r]());
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void LDR_HL_(byte cmd) // LD R,(HL)
|
||||
{
|
||||
// 7T (4, 3)
|
||||
var r = (cmd & 0x38) >> 3;
|
||||
|
||||
_regSetters[r](RDMEM(regs.HL));
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INC/DEC
|
||||
|
||||
private void DECRR(byte cmd) // DEC RR
|
||||
{
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
_pairSetters[rr]((ushort)(_pairGetters[rr]() - 1));
|
||||
}
|
||||
|
||||
private void INCRR(byte cmd) // INC RR
|
||||
{
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
_pairSetters[rr]((ushort)(_pairGetters[rr]() + 1));
|
||||
}
|
||||
|
||||
private void DECR(byte cmd) // DEC R
|
||||
{
|
||||
var r = (cmd & 0x38) >> 3;
|
||||
|
||||
_regSetters[r](ALU_DECR(_regGetters[r]()));
|
||||
}
|
||||
|
||||
private void INCR(byte cmd) // INC R
|
||||
{
|
||||
var r = (cmd & 0x38) >> 3;
|
||||
|
||||
_regSetters[r](ALU_INCR(_regGetters[r]()));
|
||||
}
|
||||
|
||||
private void DEC_HL_(byte cmd) // DEC (HL)
|
||||
{
|
||||
// 11T (4, 4, 3)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val = ALU_DECR(val);
|
||||
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void INC_HL_(byte cmd) // INC (HL)
|
||||
{
|
||||
// 11T (4, 4, 3)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val = ALU_INCR(val);
|
||||
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void HALT(byte cmd)
|
||||
{
|
||||
HALTED = true;
|
||||
regs.PC--; // workaround for Z80 snapshot halt issue + comfortable debugging
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Description: Z80 CPU Emulator [CB prefixed opcodes part]
|
||||
* Date: 13.04.2007
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu.Processor
|
||||
{
|
||||
public partial class Z80Cpu
|
||||
{
|
||||
private void CB_RLC(byte cmd) // RLC r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
_regSetters[r](ALU_RLC(_regGetters[r]()));
|
||||
}
|
||||
|
||||
private void CB_RRC(byte cmd) // RRC r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
_regSetters[r](ALU_RRC(_regGetters[r]()));
|
||||
}
|
||||
|
||||
private void CB_RL(byte cmd) // RL r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
_regSetters[r](ALU_RL(_regGetters[r]()));
|
||||
}
|
||||
|
||||
private void CB_RR(byte cmd) // RR r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
_regSetters[r](ALU_RR(_regGetters[r]()));
|
||||
}
|
||||
|
||||
private void CB_SLA(byte cmd) // SLA r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
_regSetters[r](ALU_SLA(_regGetters[r]()));
|
||||
}
|
||||
|
||||
private void CB_SRA(byte cmd) // SRA r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
_regSetters[r](ALU_SRA(_regGetters[r]()));
|
||||
}
|
||||
|
||||
private void CB_SLL(byte cmd) // *SLL r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
_regSetters[r](ALU_SLL(_regGetters[r]()));
|
||||
}
|
||||
|
||||
private void CB_SRL(byte cmd) // SRL r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
_regSetters[r](ALU_SRL(_regGetters[r]()));
|
||||
}
|
||||
|
||||
private void CB_BIT(byte cmd) // BIT r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
var b = (cmd & 0x38) >> 3;
|
||||
|
||||
ALU_BIT(_regGetters[r](), b);
|
||||
}
|
||||
|
||||
private void CB_RES(byte cmd) // RES r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
var m = ~(1 << ((cmd & 0x38) >> 3));
|
||||
|
||||
_regSetters[r]((byte)(_regGetters[r]() & m));
|
||||
}
|
||||
|
||||
private void CB_SET(byte cmd) // SET r
|
||||
{
|
||||
var r = cmd & 7;
|
||||
var m = 1 << ((cmd & 0x38) >> 3);
|
||||
|
||||
_regSetters[r]((byte)(_regGetters[r]() | m));
|
||||
}
|
||||
|
||||
private void CB_RLCHL(byte cmd) // RLC (HL)
|
||||
{
|
||||
// 15T (4, 4, 4, 3)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val = ALU_RLC(val);
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void CB_RRCHL(byte cmd) // RRC (HL)
|
||||
{
|
||||
// 15T (4, 4, 4, 3)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val = ALU_RRC(val);
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void CB_RLHL(byte cmd) // RL (HL)
|
||||
{
|
||||
// 15T (4, 4, 4, 3)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val = ALU_RL(val);
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void CB_RRHL(byte cmd) // RR (HL) [15T]
|
||||
{
|
||||
// 15T (4, 4, 4, 3)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val = ALU_RR(val);
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void CB_SLAHL(byte cmd) // SLA (HL) [15T]
|
||||
{
|
||||
// 15T (4, 4, 4, 3)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val = ALU_SLA(val);
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void CB_SRAHL(byte cmd) // SRA (HL) [15T]
|
||||
{
|
||||
// 15T (4, 4, 4, 3)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val = ALU_SRA(val);
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void CB_SLLHL(byte cmd) // *SLL (HL) [15T]
|
||||
{
|
||||
// 15T (4, 4, 4, 3)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val = ALU_SLL(val);
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void CB_SRLHL(byte cmd) // SRL (HL)
|
||||
{
|
||||
// 15T (4, 4, 4, 3)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val = ALU_SRL(val);
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void CB_BITHL(byte cmd) // BIT (HL) [12T]
|
||||
{
|
||||
// 12T (4, 4, 4)
|
||||
var b = (cmd & 0x38) >> 3;
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 4;
|
||||
ALU_BITMEM(val, b);
|
||||
}
|
||||
|
||||
private void CB_RESHL(byte cmd) // RES (HL) [15T]
|
||||
{
|
||||
// 15 (4, 4, 4, 3)
|
||||
var m = (byte)~(1 << ((cmd & 0x38) >> 3));
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val &= m;
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void CB_SETHL(byte cmd) // SET (HL) [15T]
|
||||
{
|
||||
// 15T (4, 4, 4, 3)
|
||||
var m = (byte)(1 << ((cmd & 0x38) >> 3));
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
val |= m;
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,880 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Description: Z80 CPU Emulator [ED prefixed opcode part]
|
||||
* Date: 13.04.2007
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
namespace ZXMAK2.Engine.Cpu.Processor
|
||||
{
|
||||
public partial class Z80Cpu
|
||||
{
|
||||
private void ED_LDI(byte cmd)
|
||||
{
|
||||
// 16T (4, 4, 3, 5)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
|
||||
WRMEM(regs.DE, val); Tact += 3;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
|
||||
regs.HL++;
|
||||
regs.DE++;
|
||||
regs.BC--;
|
||||
val += regs.A;
|
||||
|
||||
regs.F = (byte)((regs.F & CpuFlags.NotHPNF3F5) |
|
||||
(val & CpuFlags.F3) | ((val << 4) & CpuFlags.F5));
|
||||
if (regs.BC != 0) regs.F |= CpuFlags.P;
|
||||
}
|
||||
|
||||
private void ED_CPI(byte cmd)
|
||||
{
|
||||
// 16T (4, 4, 3, 5)
|
||||
|
||||
var cf = regs.F & CpuFlags.C;
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
|
||||
regs.HL++;
|
||||
regs.F = (byte)(CpuTables.Cpf8b[regs.A * 0x100 + val] + cf);
|
||||
if (--regs.BC != 0) regs.F |= CpuFlags.P;
|
||||
regs.MW++;
|
||||
}
|
||||
|
||||
private void ED_INI(byte cmd) // INI [16T]
|
||||
{
|
||||
// 16T (4, 5, 3, 4)
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
var val = RDPORT(regs.BC); Tact += 3;
|
||||
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
|
||||
regs.MW = (ushort)(regs.BC + 1);
|
||||
regs.HL++;
|
||||
regs.B--;
|
||||
|
||||
//FUSE
|
||||
byte flgtmp = (byte)(val + regs.C + 1);
|
||||
regs.F = (byte)(CpuTables.Logf[regs.B] & CpuFlags.NotP);
|
||||
if ((CpuTables.Logf[(flgtmp & 0x07) ^ regs.B] & CpuFlags.P) != 0) regs.F |= CpuFlags.P;
|
||||
if (flgtmp < val) regs.F |= CpuFlags.HC;
|
||||
if ((val & 0x80) != 0) regs.F |= CpuFlags.N;
|
||||
|
||||
Tact++; //?? really?
|
||||
}
|
||||
|
||||
private void ED_OUTI(byte cmd) // OUTI [16T]
|
||||
{
|
||||
// 16 (4, 5, 3, 4)
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
regs.B--;
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
|
||||
WRPORT(regs.BC, val); Tact += 3;
|
||||
|
||||
regs.MW = (ushort)(regs.BC + 1);
|
||||
regs.HL++;
|
||||
|
||||
//FUSE
|
||||
byte flgtmp = (byte)(val + regs.L);
|
||||
regs.F = (byte)(CpuTables.Logf[regs.B] & CpuFlags.NotP);
|
||||
if ((CpuTables.Logf[(flgtmp & 0x07) ^ regs.B] & CpuFlags.P) != 0) regs.F |= CpuFlags.P;
|
||||
if (flgtmp < val) regs.F |= CpuFlags.HC;
|
||||
if ((val & 0x80) != 0) regs.F |= CpuFlags.N;
|
||||
|
||||
Tact++; //?? really?
|
||||
}
|
||||
|
||||
private void ED_LDD(byte cmd)
|
||||
{
|
||||
// 16T (4, 4, 3, 5)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
|
||||
WRMEM(regs.DE, val); Tact += 3;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
|
||||
regs.HL--;
|
||||
regs.DE--;
|
||||
regs.BC--;
|
||||
val += regs.A;
|
||||
|
||||
regs.F = (byte)((regs.F & CpuFlags.NotHPNF3F5) |
|
||||
(val & CpuFlags.F3) | ((val << 4) & CpuFlags.F5));
|
||||
if (regs.BC != 0) regs.F |= CpuFlags.P;
|
||||
}
|
||||
|
||||
private void ED_CPD(byte cmd)
|
||||
{
|
||||
// 16T (4, 4, 3, 5)
|
||||
|
||||
var cf = regs.F & CpuFlags.C;
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
|
||||
regs.HL--;
|
||||
regs.BC--;
|
||||
regs.MW--;
|
||||
regs.F = (byte)(CpuTables.Cpf8b[regs.A * 0x100 + val] + cf);
|
||||
if (regs.BC != 0) regs.F |= CpuFlags.P;
|
||||
}
|
||||
|
||||
private void ED_IND(byte cmd) // IND [16T]
|
||||
{
|
||||
// 16T (4, 5, 3, 4)
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
var val = RDPORT(regs.BC); Tact += 3;
|
||||
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
|
||||
regs.MW = (ushort)(regs.BC - 1);
|
||||
regs.HL--;
|
||||
regs.B--;
|
||||
|
||||
//FUSE
|
||||
byte flgtmp = (byte)(val + regs.C - 1);
|
||||
regs.F = (byte)(CpuTables.Logf[regs.B] & CpuFlags.NotP);
|
||||
if ((CpuTables.Logf[(flgtmp & 0x07) ^ regs.B] & CpuFlags.P) != 0) regs.F |= CpuFlags.P;
|
||||
if (flgtmp < val) regs.F |= CpuFlags.HC;
|
||||
if ((val & 0x80) != 0) regs.F |= CpuFlags.N;
|
||||
|
||||
Tact++; // ?? really?
|
||||
}
|
||||
|
||||
private void ED_OUTD(byte cmd) // OUTD [16T]
|
||||
{
|
||||
// 16T (4, 5, 3, 4)
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
regs.B--;
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
|
||||
WRPORT(regs.BC, val); Tact += 3;
|
||||
|
||||
regs.MW = (ushort)(regs.BC - 1);
|
||||
regs.HL--;
|
||||
|
||||
//FUSE
|
||||
byte flgtmp = (byte)(val + regs.L);
|
||||
regs.F = (byte)(CpuTables.Logf[regs.B] & CpuFlags.NotP);
|
||||
if ((CpuTables.Logf[(flgtmp & 0x07) ^ regs.B] & CpuFlags.P) != 0) regs.F |= CpuFlags.P;
|
||||
if (flgtmp < val) regs.F |= CpuFlags.HC;
|
||||
if ((val & 0x80) != 0) regs.F |= CpuFlags.N;
|
||||
|
||||
Tact++; // ?? really?
|
||||
}
|
||||
|
||||
private void ED_LDIR(byte cmd)
|
||||
{
|
||||
//BC==0 => 16T (4, 4, 3, 5)
|
||||
//BC!=0 => 21T (4, 4, 3, 5, 5)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
|
||||
WRMEM(regs.DE, val); Tact += 3;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
|
||||
regs.BC--;
|
||||
val += regs.A;
|
||||
|
||||
regs.F &= CpuFlags.NotHPNF3F5;
|
||||
if (regs.BC != 0)
|
||||
{
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
regs.PC--;
|
||||
regs.MW = regs.PC;
|
||||
regs.PC--;
|
||||
|
||||
// YF = PCi.13; XF = PCi.11
|
||||
regs.F |= (byte)(regs.PCH & CpuFlags.F3F5);
|
||||
regs.F |= CpuFlags.P;
|
||||
}
|
||||
else
|
||||
{
|
||||
// YF = PCi.13; XF = PCi.11
|
||||
regs.F |= (byte)((val & CpuFlags.F3) | ((val << 4) & CpuFlags.F5));
|
||||
}
|
||||
regs.HL++;
|
||||
regs.DE++;
|
||||
}
|
||||
|
||||
private void ED_CPIR(byte cmd)
|
||||
{
|
||||
//BC==0 => 16T (4, 4, 3, 5)
|
||||
//BC!=0 => 21T (4, 4, 3, 5, 5)
|
||||
|
||||
regs.MW++;
|
||||
var cf = regs.F & CpuFlags.C;
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
|
||||
regs.BC--;
|
||||
regs.F = (byte)(CpuTables.Cpf8b[regs.A * 0x100 + val] + cf);
|
||||
|
||||
if (regs.BC != 0)
|
||||
{
|
||||
regs.F |= CpuFlags.P;
|
||||
if ((regs.F & CpuFlags.Z) == 0)
|
||||
{
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
regs.PC--;
|
||||
regs.MW = regs.PC;
|
||||
regs.PC--;
|
||||
|
||||
// YF = PCi.13; XF = PCi.11
|
||||
regs.F &= CpuFlags.NotF3F5;
|
||||
regs.F |= (byte)(regs.PCH & CpuFlags.F3F5);
|
||||
}
|
||||
}
|
||||
regs.HL++;
|
||||
}
|
||||
|
||||
private void ED_INIR(byte cmd) // INIR [16T/21T]
|
||||
{
|
||||
// B==0 => 16T (4, 5, 3, 4)
|
||||
// B!=0 => 21T (4, 5, 3, 4, 5)
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
var val = RDPORT(regs.BC); Tact += 3;
|
||||
|
||||
var nf = (byte)((val >> 6) & CpuFlags.N);
|
||||
var t = val + ((regs.C + 1) & 0xff);
|
||||
var hcf = t > 255 ? CpuFlags.HC : 0;
|
||||
|
||||
WRMEM(regs.HL++, val); Tact += 3;
|
||||
regs.MW = (ushort)(regs.BC + 1);
|
||||
|
||||
regs.B--; Tact++;
|
||||
var pf = CpuTables.Parity[(t & 7) ^ regs.B];
|
||||
|
||||
if (regs.B != 0)
|
||||
{
|
||||
WRNOMREQ(regs.HL); Tact++;
|
||||
WRNOMREQ(regs.HL); Tact++;
|
||||
WRNOMREQ(regs.HL); Tact++;
|
||||
WRNOMREQ(regs.HL); Tact++;
|
||||
WRNOMREQ(regs.HL); Tact++;
|
||||
regs.PC -= 2;
|
||||
|
||||
regs.F = (byte)(
|
||||
/* ZF = 0 */
|
||||
(regs.B & CpuFlags.S) | /* SF = Bo.7 */
|
||||
(regs.PCH & CpuFlags.F3F5) | /* YF = PCi.13; XF = PCi.11 */
|
||||
nf); /* NF = IO.7 */
|
||||
if (hcf != 0)
|
||||
{
|
||||
regs.F |= CpuFlags.C;
|
||||
if ((val & 0x80) != 0)
|
||||
{
|
||||
// PF = PF ^ Parity((B - 1) & 0x7) ^ 1;
|
||||
// HF = (B & 0x0F) == 0x00;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[(regs.B - 1) & 7] ^ CpuFlags.P);
|
||||
if ((regs.B & 0x0f) == 0x00)
|
||||
regs.F |= CpuFlags.H;
|
||||
}
|
||||
else
|
||||
{
|
||||
// PF = PF ^ Parity((B + 1) & 0x7) ^ 1;
|
||||
// HF = (B & 0x0F) == 0x0F;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[(regs.B + 1) & 7] ^ CpuFlags.P);
|
||||
if ((regs.B & 0x0f) == 0x0f)
|
||||
regs.F |= CpuFlags.H;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// PF = PF ^ Parity(B & 0x7) ^ 1;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[regs.B & 7] ^ CpuFlags.P);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
regs.F = (byte)(CpuFlags.Z | /* ZF = 1; SF, YF, XF = 0 */
|
||||
hcf | /* HF, CF = T > 255 */
|
||||
pf | /* PF = ((T & 7) ^ Bo).parity */
|
||||
nf); /* NF = IO.7 */
|
||||
}
|
||||
}
|
||||
|
||||
private void ED_OTIR(byte cmd) // OTIR [16T/21T]
|
||||
{
|
||||
// B==0 => 16T (4, 5, 3, 4)
|
||||
// B!=0 => 21T (4, 5, 3, 4, 5)
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
var val = RDMEM(regs.HL++); Tact += 3;
|
||||
regs.MW = (ushort)(regs.BC + 1);
|
||||
|
||||
var nf = (byte)((val >> 6) & CpuFlags.N);
|
||||
var t = val + regs.L;
|
||||
var hcf = t > 255 ? CpuFlags.HC : 0;
|
||||
|
||||
regs.B--;
|
||||
var pf = CpuTables.Parity[(t & 7) ^ regs.B];
|
||||
|
||||
WRPORT(regs.BC, val); Tact += 3;
|
||||
Tact++;
|
||||
|
||||
if (regs.B != 0)
|
||||
{
|
||||
RDNOMREQ(regs.BC); Tact++;
|
||||
RDNOMREQ(regs.BC); Tact++;
|
||||
RDNOMREQ(regs.BC); Tact++;
|
||||
RDNOMREQ(regs.BC); Tact++;
|
||||
RDNOMREQ(regs.BC); Tact++;
|
||||
regs.PC -= 2;
|
||||
|
||||
regs.F = (byte)(
|
||||
/* ZF = 0 */
|
||||
(regs.B & CpuFlags.S) | /* SF = Bo.7 */
|
||||
(regs.PCH & CpuFlags.F3F5) |/* YF = PCi.13; XF = PCi.11 */
|
||||
nf); /* NF = IO.7 */
|
||||
if (hcf != 0)
|
||||
{
|
||||
regs.F |= CpuFlags.C;
|
||||
if ((val & 0x80) != 0)
|
||||
{
|
||||
// PF = PF ^ Parity((B - 1) & 0x7) ^ 1;
|
||||
// HF = (B & 0x0F) == 0x00;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[(regs.B - 1) & 7] ^ CpuFlags.P);
|
||||
if ((regs.B & 0x0f) == 0x00)
|
||||
regs.F |= CpuFlags.H;
|
||||
}
|
||||
else
|
||||
{
|
||||
// PF = PF ^ Parity((B + 1) & 0x7) ^ 1;
|
||||
// HF = (B & 0x0F) == 0x0F;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[(regs.B + 1) & 7] ^ CpuFlags.P);
|
||||
if ((regs.B & 0x0f) == 0x0f)
|
||||
regs.F |= CpuFlags.H;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// PF = PF ^ Parity(B & 0x7) ^ 1;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[regs.B & 7] ^ CpuFlags.P);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
regs.F = (byte)(CpuFlags.Z | /* ZF = 1; SF, YF, XF = 0 */
|
||||
hcf | /* HF, CF = T > 255 */
|
||||
pf | /* PF = ((T & 7) ^ Bo).parity */
|
||||
nf); /* NF = IO.7 */
|
||||
}
|
||||
}
|
||||
|
||||
private void ED_LDDR(byte cmd)
|
||||
{
|
||||
//BC==0 => 16T (4, 4, 3, 5)
|
||||
//BC!=0 => 21T (4, 4, 3, 5, 5)
|
||||
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
|
||||
WRMEM(regs.DE, val); Tact += 3;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
|
||||
regs.BC--;
|
||||
val += regs.A;
|
||||
|
||||
regs.F &= CpuFlags.NotHPNF3F5;
|
||||
if (regs.BC != 0)
|
||||
{
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
WRNOMREQ(regs.DE); Tact++;
|
||||
regs.PC--;
|
||||
regs.MW = regs.PC;
|
||||
regs.PC--;
|
||||
|
||||
// YF = PCi.13; XF = PCi.11
|
||||
regs.F |= (byte)(regs.PCH & CpuFlags.F3F5);
|
||||
regs.F |= CpuFlags.P;
|
||||
}
|
||||
else
|
||||
{
|
||||
// YF = PCi.13; XF = PCi.11
|
||||
regs.F |= (byte)((val & CpuFlags.F3) | ((val << 4) & CpuFlags.F5));
|
||||
}
|
||||
regs.HL--;
|
||||
regs.DE--;
|
||||
}
|
||||
|
||||
private void ED_CPDR(byte cmd)
|
||||
{
|
||||
// BC==0 => 16T (4, 4, 3, 5)
|
||||
// BC!=0 => 21T (4, 4, 3, 5, 5)
|
||||
|
||||
regs.MW--;
|
||||
var cf = regs.F & CpuFlags.C;
|
||||
var val = RDMEM(regs.HL); Tact += 3;
|
||||
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
regs.BC--;
|
||||
regs.F = (byte)(CpuTables.Cpf8b[regs.A * 0x100 + val] + cf);
|
||||
|
||||
if (regs.BC != 0)
|
||||
{
|
||||
regs.F |= CpuFlags.P;
|
||||
if ((regs.F & CpuFlags.Z) == 0)
|
||||
{
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
regs.PC--;
|
||||
regs.MW = regs.PC;
|
||||
regs.PC--;
|
||||
|
||||
// YF = PCi.13; XF = PCi.11
|
||||
regs.F &= CpuFlags.NotF3F5;
|
||||
regs.F |= (byte)(regs.PCH & CpuFlags.F3F5);
|
||||
}
|
||||
}
|
||||
regs.HL--;
|
||||
}
|
||||
|
||||
private void ED_INDR(byte cmd) // INDR [16T/21T]
|
||||
{
|
||||
// B==0 => 16 (4, 5, 3, 4)
|
||||
// B!=0 => 21 (4, 5, 3, 4, 5)
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
var val = RDPORT(regs.BC); Tact += 3;
|
||||
|
||||
var nf = (byte)((val >> 6) & CpuFlags.N);
|
||||
var t = val + ((regs.C - 1) & 0xff);
|
||||
var hcf = t > 255 ? CpuFlags.HC : 0;
|
||||
|
||||
WRMEM(regs.HL--, val); Tact += 3;
|
||||
regs.MW = (ushort)(regs.BC - 1);
|
||||
|
||||
regs.B--; Tact++;
|
||||
var pf = CpuTables.Parity[(t & 7) ^ regs.B];
|
||||
|
||||
if (regs.B != 0)
|
||||
{
|
||||
WRNOMREQ(regs.HL); Tact++;
|
||||
WRNOMREQ(regs.HL); Tact++;
|
||||
WRNOMREQ(regs.HL); Tact++;
|
||||
WRNOMREQ(regs.HL); Tact++;
|
||||
WRNOMREQ(regs.HL); Tact++;
|
||||
regs.PC -= 2;
|
||||
|
||||
regs.F = (byte)(
|
||||
/* ZF = 0 */
|
||||
(regs.B & CpuFlags.S) | /* SF = Bo.7 */
|
||||
(regs.PCH & CpuFlags.F3F5) |/* YF = PCi.13; XF = PCi.11 */
|
||||
nf); /* NF = IO.7 */
|
||||
if (hcf != 0)
|
||||
{
|
||||
regs.F |= CpuFlags.C;
|
||||
if ((val & 0x80) != 0)
|
||||
{
|
||||
// PF = PF ^ Parity((B - 1) & 0x7) ^ 1;
|
||||
// HF = (B & 0x0F) == 0x00;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[(regs.B - 1) & 7] ^ CpuFlags.P);
|
||||
if ((regs.B & 0x0f) == 0x00)
|
||||
regs.F |= CpuFlags.H;
|
||||
}
|
||||
else
|
||||
{
|
||||
// PF = PF ^ Parity((B + 1) & 0x7) ^ 1;
|
||||
// HF = (B & 0x0F) == 0x0F;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[(regs.B + 1) & 7] ^ CpuFlags.P);
|
||||
if ((regs.B & 0x0f) == 0x0f)
|
||||
regs.F |= CpuFlags.H;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// PF = PF ^ Parity(B & 0x7) ^ 1;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[regs.B & 7] ^ CpuFlags.P);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
regs.F = (byte)(CpuFlags.Z | /* ZF = 1; SF, YF, XF = 0 */
|
||||
hcf | /* HF, CF = T > 255 */
|
||||
pf | /* PF = ((T & 7) ^ Bo).parity */
|
||||
nf); /* NF = IO.7 */
|
||||
}
|
||||
}
|
||||
|
||||
private void ED_OTDR(byte cmd) //OTDR [16T/21T]
|
||||
{
|
||||
// B==0 => 16T (4, 5, 3, 4)
|
||||
// B!=0 => 21T (4, 5, 3, 4, 5)
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
var val = RDMEM(regs.HL--); Tact += 3;
|
||||
regs.MW = (ushort)(regs.BC - 1);
|
||||
|
||||
var nf = (byte)((val >> 6) & CpuFlags.N);
|
||||
var t = val + regs.L;
|
||||
var hcf = t > 255 ? CpuFlags.HC : 0;
|
||||
|
||||
regs.B--;
|
||||
var pf = CpuTables.Parity[(t & 7) ^ regs.B];
|
||||
|
||||
WRPORT(regs.BC, val); Tact += 3;
|
||||
Tact++;
|
||||
|
||||
if (regs.B != 0)
|
||||
{
|
||||
RDNOMREQ(regs.BC); Tact++;
|
||||
RDNOMREQ(regs.BC); Tact++;
|
||||
RDNOMREQ(regs.BC); Tact++;
|
||||
RDNOMREQ(regs.BC); Tact++;
|
||||
RDNOMREQ(regs.BC); Tact++;
|
||||
regs.PC -= 2;
|
||||
|
||||
regs.F = (byte)(
|
||||
/* ZF = 0 */
|
||||
(regs.B & CpuFlags.S) | /* SF = Bo.7 */
|
||||
(regs.PCH & CpuFlags.F3F5) |/* YF = PCi.13; XF = PCi.11 */
|
||||
nf); /* NF = IO.7 */
|
||||
if (hcf != 0)
|
||||
{
|
||||
regs.F |= CpuFlags.C;
|
||||
if ((val & 0x80) != 0)
|
||||
{
|
||||
// PF = PF ^ Parity((B - 1) & 0x7) ^ 1;
|
||||
// HF = (B & 0x0F) == 0x00;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[(regs.B - 1) & 7] ^ CpuFlags.P);
|
||||
if ((regs.B & 0x0f) == 0x00)
|
||||
regs.F |= CpuFlags.H;
|
||||
}
|
||||
else
|
||||
{
|
||||
// PF = PF ^ Parity((B + 1) & 0x7) ^ 1;
|
||||
// HF = (B & 0x0F) == 0x0F;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[(regs.B + 1) & 7] ^ CpuFlags.P);
|
||||
if ((regs.B & 0x0f) == 0x0f)
|
||||
regs.F |= CpuFlags.H;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// PF = PF ^ Parity(B & 0x7) ^ 1;
|
||||
regs.F |= (byte)(pf ^ CpuTables.Parity[regs.B & 7] ^ CpuFlags.P);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
regs.F = (byte)(CpuFlags.Z | /* ZF = 1; SF, YF, XF = 0 */
|
||||
hcf | /* HF, CF = T > 255 */
|
||||
pf | /* PF = ((T & 7) ^ Bo).parity */
|
||||
nf); /* NF = IO.7 */
|
||||
}
|
||||
}
|
||||
|
||||
private void ED_INRC(byte cmd) // in R,(c) [12T]
|
||||
{
|
||||
// 12T (4, 4, 4)
|
||||
var r = (cmd & 0x38) >> 3;
|
||||
|
||||
regs.MW = regs.BC;
|
||||
var pval = RDPORT(regs.BC);
|
||||
regs.MW++;
|
||||
if (r != CpuRegId.F)
|
||||
_regSetters[r](pval);
|
||||
regs.F = (byte)(CpuTables.Logf[pval] | (regs.F & CpuFlags.C));
|
||||
Tact += 4;
|
||||
}
|
||||
|
||||
private void ED_OUTCR_NMOS(byte cmd) // out (c),R [12T]
|
||||
{
|
||||
// 12T (4, 4, 4)
|
||||
var r = (cmd & 0x38) >> 3;
|
||||
|
||||
regs.MW = regs.BC;
|
||||
if (r != CpuRegId.F)
|
||||
WRPORT(regs.BC, _regGetters[r]());
|
||||
else
|
||||
WRPORT(regs.BC, 0x00); // 0 for Z80 and 0xFF for Z84
|
||||
regs.MW++;
|
||||
Tact += 4;
|
||||
}
|
||||
|
||||
private void ED_OUTCR_CMOS(byte cmd) // out (c),R [12T]
|
||||
{
|
||||
// 12T (4, 4, 4)
|
||||
var r = (cmd & 0x38) >> 3;
|
||||
|
||||
regs.MW = regs.BC;
|
||||
if (r != CpuRegId.F)
|
||||
WRPORT(regs.BC, _regGetters[r]());
|
||||
else
|
||||
WRPORT(regs.BC, 0xFF); // 0 for Z80 and 0xFF for Z84
|
||||
regs.MW++;
|
||||
Tact += 4;
|
||||
}
|
||||
|
||||
private void ED_ADCHLRR(byte cmd) // adc hl,RR
|
||||
{
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
regs.MW = (ushort)(regs.HL + 1);
|
||||
byte fl = (byte)((((regs.HL & 0x0FFF) + (_pairGetters[rr]() & 0x0FFF) + (regs.F & CpuFlags.C)) >> 8) & CpuFlags.H);
|
||||
uint tmp = (uint)((regs.HL & 0xFFFF) + (_pairGetters[rr]() & 0xFFFF) + (regs.F & CpuFlags.C)); // AF???
|
||||
if ((tmp & 0x10000) != 0) fl |= CpuFlags.C;
|
||||
if ((tmp & 0xFFFF) == 0) fl |= CpuFlags.Z;
|
||||
int ri = (int)(short)regs.HL + (int)(short)_pairGetters[rr]() + (int)(regs.F & CpuFlags.C);
|
||||
if (ri < -0x8000 || ri >= 0x8000) fl |= CpuFlags.P;
|
||||
regs.HL = (ushort)tmp;
|
||||
regs.F = (byte)(fl | (regs.H & CpuFlags.SF3F5));
|
||||
}
|
||||
|
||||
private void ED_SBCHLRR(byte cmd) // sbc hl,RR
|
||||
{
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
RDNOMREQ(regs.IR); Tact += 1;
|
||||
RDNOMREQ(regs.IR); Tact += 1;
|
||||
RDNOMREQ(regs.IR); Tact += 1;
|
||||
RDNOMREQ(regs.IR); Tact += 1;
|
||||
RDNOMREQ(regs.IR); Tact += 1;
|
||||
RDNOMREQ(regs.IR); Tact += 1;
|
||||
RDNOMREQ(regs.IR); Tact += 1;
|
||||
|
||||
regs.MW = (ushort)(regs.HL + 1);
|
||||
byte fl = CpuFlags.N;
|
||||
fl |= (byte)((((regs.HL & 0x0FFF) - (_pairGetters[rr]() & 0x0FFF) - (regs.F & CpuFlags.C)) >> 8) & CpuFlags.H);
|
||||
uint tmp = (uint)((regs.HL & 0xFFFF) - (_pairGetters[rr]() & 0xFFFF) - (regs.F & CpuFlags.C)); // AF???
|
||||
if ((tmp & 0x10000) != 0) fl |= CpuFlags.C;
|
||||
if ((tmp & 0xFFFF) == 0) fl |= CpuFlags.Z;
|
||||
int ri = (int)(short)regs.HL - (int)(short)_pairGetters[rr]() - (int)(regs.F & CpuFlags.C);
|
||||
if (ri < -0x8000 || ri >= 0x8000) fl |= CpuFlags.P;
|
||||
regs.HL = (ushort)tmp;
|
||||
regs.F = (byte)(fl | (regs.H & CpuFlags.SF3F5));
|
||||
}
|
||||
|
||||
private void ED_LDRR_NN_(byte cmd) // ld RR,(NN)
|
||||
{
|
||||
// 20T (4, 4, 3, 3, 3, 3)
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
var adr = (ushort)RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
adr += (ushort)(RDMEM(regs.PC) * 0x100);
|
||||
regs.PC++;
|
||||
regs.MW = (ushort)(adr + 1);
|
||||
Tact += 3;
|
||||
|
||||
var val = (ushort)RDMEM(adr);
|
||||
Tact += 3;
|
||||
|
||||
val += (ushort)(RDMEM(regs.MW) * 0x100);
|
||||
_pairSetters[rr](val);
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void ED_LD_NN_RR(byte cmd) // ld (NN),RR
|
||||
{
|
||||
// 20 (4, 4, 3, 3, 3, 3)
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
var adr = (ushort)RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
adr += (ushort)(RDMEM(regs.PC) * 0x100);
|
||||
regs.PC++;
|
||||
regs.MW = (ushort)(adr + 1);
|
||||
var val = _pairGetters[rr]();
|
||||
Tact += 3;
|
||||
|
||||
WRMEM(adr, (byte)val);
|
||||
Tact += 3;
|
||||
|
||||
WRMEM(regs.MW, (byte)(val >> 8));
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void ED_RETN(byte cmd) // reti/retn
|
||||
{
|
||||
// 14T (4, 4, 3, 3)
|
||||
|
||||
IFF1 = IFF2;
|
||||
var adr = (ushort)RDMEM(regs.SP);
|
||||
Tact += 3;
|
||||
|
||||
adr += (ushort)(RDMEM(++regs.SP) * 0x100);
|
||||
++regs.SP;
|
||||
regs.PC = adr;
|
||||
regs.MW = adr;
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void ED_IM(byte cmd) // im X
|
||||
{
|
||||
var mode = (byte)((cmd & 0x18) >> 3);
|
||||
if (mode < 2) mode = 1;
|
||||
mode--;
|
||||
|
||||
IM = mode;
|
||||
}
|
||||
|
||||
private void ED_LDXRA(byte cmd) // ld I/R,a
|
||||
{
|
||||
var ir = (cmd & 0x08) == 0;
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
if (ir) // I
|
||||
regs.I = regs.A;
|
||||
else
|
||||
regs.R = regs.A;
|
||||
}
|
||||
|
||||
private void ED_LDAXR(byte cmd) // ld a,I/R
|
||||
{
|
||||
var ir = (cmd & 0x08) == 0;
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
|
||||
if (ir) // I
|
||||
regs.A = regs.I;
|
||||
else
|
||||
regs.A = regs.R;
|
||||
|
||||
regs.F = (byte)(((regs.F & CpuFlags.C) | CpuTables.Logf[regs.A]) & CpuFlags.NotP);
|
||||
switch (_type)
|
||||
{
|
||||
case CpuType.ZILOG_NMOS:
|
||||
case CpuType.NEC_NMOS:
|
||||
case CpuType.ST_CMOS:
|
||||
SCANSIG?.Invoke();
|
||||
if (!(INT && IFF1) && IFF2)
|
||||
{
|
||||
regs.F |= CpuFlags.P;
|
||||
}
|
||||
break;
|
||||
case CpuType.ZILOG_CMOS:
|
||||
if (IFF2)
|
||||
{
|
||||
regs.F |= CpuFlags.P;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private void ED_RRD(byte cmd) // RRD
|
||||
{
|
||||
// 18T (4, 4, 3, 4, 3)
|
||||
|
||||
var tmp = RDMEM(regs.HL); Tact += 3;
|
||||
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
|
||||
regs.MW = (ushort)(regs.HL + 1);
|
||||
var val = (byte)((regs.A << 4) | (tmp >> 4));
|
||||
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
regs.A = (byte)((regs.A & 0xF0) | (tmp & 0x0F));
|
||||
regs.F = (byte)(CpuTables.Logf[regs.A] | (regs.F & CpuFlags.C));
|
||||
}
|
||||
|
||||
private void ED_RLD(byte cmd) // RLD
|
||||
{
|
||||
// 18T (4, 4, 3, 4, 3)
|
||||
|
||||
var tmp = RDMEM(regs.HL); Tact += 3;
|
||||
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
RDNOMREQ(regs.HL); Tact++;
|
||||
|
||||
regs.MW = (ushort)(regs.HL + 1);
|
||||
var val = (byte)((regs.A & 0x0F) | (tmp << 4));
|
||||
|
||||
WRMEM(regs.HL, val); Tact += 3;
|
||||
regs.A = (byte)((regs.A & 0xF0) | (tmp >> 4));
|
||||
regs.F = (byte)(CpuTables.Logf[regs.A] | (regs.F & CpuFlags.C));
|
||||
}
|
||||
|
||||
private void ED_NEG(byte cmd) // NEG
|
||||
{
|
||||
regs.F = CpuTables.Sbcf[regs.A];
|
||||
regs.A = (byte)-regs.A;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Description: Z80 CPU Emulator [DD/FD prefixed opcode part]
|
||||
* Date: 13.04.2007
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu.Processor
|
||||
{
|
||||
public partial class Z80Cpu
|
||||
{
|
||||
#region FXxx ops...
|
||||
|
||||
private void FX_LDSPHL(byte cmd) // LD SP,IX
|
||||
{
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.SP = regs.IX;
|
||||
else
|
||||
regs.SP = regs.IY;
|
||||
}
|
||||
|
||||
private void FX_EX_SP_HL(byte cmd) // EX (SP),IX
|
||||
{
|
||||
// 23T (4, 4, 3, 4, 3, 5)
|
||||
|
||||
var tmpsp = regs.SP;
|
||||
regs.MW = RDMEM(tmpsp); Tact += 3;
|
||||
tmpsp++;
|
||||
|
||||
regs.MW += (ushort)(RDMEM(tmpsp) * 0x100); Tact += 3;
|
||||
RDNOMREQ(tmpsp); Tact++;
|
||||
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
{
|
||||
WRMEM(tmpsp, regs.XH); Tact += 3;
|
||||
tmpsp--;
|
||||
|
||||
WRMEM(tmpsp, regs.XL); Tact += 3;
|
||||
WRNOMREQ(tmpsp); Tact++;
|
||||
WRNOMREQ(tmpsp); Tact++;
|
||||
regs.IX = regs.MW;
|
||||
}
|
||||
else
|
||||
{
|
||||
WRMEM(tmpsp, regs.YH); Tact += 3;
|
||||
tmpsp--;
|
||||
|
||||
WRMEM(tmpsp, regs.YL); Tact += 3;
|
||||
WRNOMREQ(tmpsp); Tact++;
|
||||
WRNOMREQ(tmpsp); Tact++;
|
||||
regs.IY = regs.MW;
|
||||
}
|
||||
}
|
||||
|
||||
private void FX_JP_HL_(byte cmd) // JP (IX)
|
||||
{
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.PC = regs.IX;
|
||||
else
|
||||
regs.PC = regs.IY;
|
||||
}
|
||||
|
||||
private void FX_PUSHIX(byte cmd) // PUSH IX
|
||||
{
|
||||
// 15 (4, 5, 3, 3)
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
var val = FX == CpuModeIndex.Ix ? regs.IX : regs.IY;
|
||||
regs.SP--;
|
||||
|
||||
WRMEM(regs.SP, (byte)(val >> 8)); Tact += 3;
|
||||
regs.SP--;
|
||||
|
||||
WRMEM(regs.SP, (byte)val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_POPIX(byte cmd) // POP IX
|
||||
{
|
||||
// 14T (4, 4, 3, 3)
|
||||
|
||||
var val = (ushort)RDMEM(regs.SP);
|
||||
regs.SP++;
|
||||
Tact += 3;
|
||||
|
||||
val |= (ushort)(RDMEM(regs.SP) << 8);
|
||||
regs.SP++;
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.IX = val;
|
||||
else
|
||||
regs.IY = val;
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_ALUAXH(byte cmd) // ADD/ADC/SUB/SBC/AND/XOR/OR/CP A,XH
|
||||
{
|
||||
byte val;
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
val = (byte)(regs.IX >> 8);
|
||||
else
|
||||
val = (byte)(regs.IY >> 8);
|
||||
_alualg[(cmd & 0x38) >> 3](val);
|
||||
}
|
||||
|
||||
private void FX_ALUAXL(byte cmd) // ADD/ADC/SUB/SBC/AND/XOR/OR/CP A,XL
|
||||
{
|
||||
byte val;
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
val = (byte)regs.IX;
|
||||
else
|
||||
val = (byte)regs.IY;
|
||||
_alualg[(cmd & 0x38) >> 3](val);
|
||||
}
|
||||
|
||||
private void FX_ALUA_IX_(byte cmd) // ADD/ADC/SUB/SBC/AND/XOR/OR/CP A,(IX)
|
||||
{
|
||||
// 19T (4, 4, 3, 5, 3)
|
||||
var op = (cmd & 0x38) >> 3;
|
||||
|
||||
int drel = (sbyte)RDMEM(regs.PC); Tact += 3;
|
||||
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
|
||||
regs.PC++;
|
||||
regs.MW = FX == CpuModeIndex.Ix ? regs.IX : regs.IY;
|
||||
regs.MW = (ushort)(regs.MW + drel);
|
||||
|
||||
var val = RDMEM(regs.MW); Tact += 3;
|
||||
_alualg[op](val);
|
||||
}
|
||||
|
||||
private void FX_ADDIXRR(byte cmd) // ADD IX,RR
|
||||
{
|
||||
var rr = (cmd & 0x30) >> 4;
|
||||
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
var rde = rr switch
|
||||
{
|
||||
0 => regs.BC,
|
||||
1 => regs.DE,
|
||||
2 => FX == CpuModeIndex.Ix ? regs.IX : regs.IY,
|
||||
3 => regs.SP,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(cmd), "(cmd & 0x30) >> 4 is out of range"),
|
||||
};
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
{
|
||||
regs.MW = (ushort)(regs.IX + 1);
|
||||
regs.IX = ALU_ADDHLRR(regs.IX, rde);
|
||||
}
|
||||
else
|
||||
{
|
||||
regs.MW = (ushort)(regs.IY + 1);
|
||||
regs.IY = ALU_ADDHLRR(regs.IY, rde);
|
||||
}
|
||||
}
|
||||
|
||||
private void FX_DECIX(byte cmd) // DEC IX
|
||||
{
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.IX--;
|
||||
else
|
||||
regs.IY--;
|
||||
}
|
||||
|
||||
private void FX_INCIX(byte cmd) // INC IX
|
||||
{
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
RDNOMREQ(regs.IR); Tact++;
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.IX++;
|
||||
else
|
||||
regs.IY++;
|
||||
}
|
||||
|
||||
private void FX_LDIX_N_(byte cmd) // LD IX,(nnnn)
|
||||
{
|
||||
// 20 (4, 4, 3, 3, 3, 3)
|
||||
|
||||
var adr = (ushort)RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
adr += (ushort)(RDMEM(regs.PC) * 0x100);
|
||||
regs.PC++;
|
||||
regs.MW = (ushort)(adr + 1);
|
||||
Tact += 3;
|
||||
|
||||
var val = (ushort)RDMEM(adr);
|
||||
Tact += 3;
|
||||
|
||||
val += (ushort)(RDMEM(regs.MW) * 0x100);
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.IX = val;
|
||||
else
|
||||
regs.IY = val;
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_LD_NN_IX(byte cmd) // LD (nnnn),IX
|
||||
{
|
||||
// 20 (4, 4, 3, 3, 3, 3)
|
||||
|
||||
var hl = FX == CpuModeIndex.Ix ? regs.IX : regs.IY;
|
||||
var adr = (ushort)RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
adr += (ushort)(RDMEM(regs.PC) * 0x100);
|
||||
regs.PC++;
|
||||
regs.MW = (ushort)(adr + 1);
|
||||
Tact += 3;
|
||||
|
||||
WRMEM(adr, (byte)hl);
|
||||
Tact += 3;
|
||||
|
||||
WRMEM(regs.MW, (byte)(hl >> 8));
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_LDIXNNNN(byte cmd) // LD IX,nnnn
|
||||
{
|
||||
// 14 (4, 4, 3, 3)
|
||||
|
||||
var val = (ushort)RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
|
||||
val |= (ushort)(RDMEM(regs.PC) << 8);
|
||||
regs.PC++;
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.IX = val;
|
||||
else
|
||||
regs.IY = val;
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_DEC_IX_(byte cmd) // DEC (IX)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
|
||||
int drel = (sbyte)RDMEM(regs.PC); Tact += 3;
|
||||
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
|
||||
regs.PC++;
|
||||
regs.MW = FX == CpuModeIndex.Ix ? regs.IX : regs.IY;
|
||||
regs.MW = (ushort)(regs.MW + drel);
|
||||
|
||||
var val = RDMEM(regs.MW); Tact += 3;
|
||||
RDNOMREQ(regs.MW); Tact++;
|
||||
val = ALU_DECR(val);
|
||||
WRMEM(regs.MW, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_INC_IX_(byte cmd) // INC (IX)
|
||||
{
|
||||
//23T (4, 4, 3, 5, 4, 3)
|
||||
|
||||
int drel = (sbyte)RDMEM(regs.PC); Tact += 3;
|
||||
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
|
||||
regs.PC++;
|
||||
regs.MW = FX == CpuModeIndex.Ix ? regs.IX : regs.IY;
|
||||
regs.MW = (ushort)(regs.MW + drel);
|
||||
|
||||
var val = RDMEM(regs.MW); Tact += 3;
|
||||
RDNOMREQ(regs.MW); Tact++;
|
||||
val = ALU_INCR(val);
|
||||
WRMEM(regs.MW, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_LD_IX_NN(byte cmd) // LD (IX),nn
|
||||
{
|
||||
// 19 (4, 4, 3, 5, 3)
|
||||
|
||||
int drel = (sbyte)RDMEM(regs.PC); Tact += 3;
|
||||
regs.PC++;
|
||||
|
||||
var val = RDMEM(regs.PC); Tact += 3;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
regs.PC++;
|
||||
regs.MW = FX == CpuModeIndex.Ix ? regs.IX : regs.IY;
|
||||
regs.MW = (ushort)(regs.MW + drel);
|
||||
WRMEM(regs.MW, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_LD_IX_R(byte cmd) // LD (IX),R
|
||||
{
|
||||
// 19T (4, 4, 3, 5, 3)
|
||||
var r = cmd & 0x07;
|
||||
|
||||
int drel = (sbyte)RDMEM(regs.PC); Tact += 3;
|
||||
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
|
||||
regs.PC++;
|
||||
regs.MW = FX == CpuModeIndex.Ix ? regs.IX : regs.IY;
|
||||
regs.MW = (ushort)(regs.MW + drel);
|
||||
WRMEM(regs.MW, _regGetters[r]()); Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_LDR_IX_(byte cmd) // LD R,(IX)
|
||||
{
|
||||
// 19T (4, 4, 3, 5, 3)
|
||||
var r = (cmd & 0x38) >> 3;
|
||||
|
||||
int drel = (sbyte)RDMEM(regs.PC); Tact += 3;
|
||||
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
|
||||
regs.PC++;
|
||||
regs.MW = FX == CpuModeIndex.Ix ? regs.IX : regs.IY;
|
||||
regs.MW = (ushort)(regs.MW + drel);
|
||||
|
||||
_regSetters[r](RDMEM(regs.MW)); Tact += 3;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region RdRs...
|
||||
|
||||
private void FX_LDHL(byte cmd)
|
||||
{
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.XH = regs.XL;
|
||||
else
|
||||
regs.YH = regs.YL;
|
||||
}
|
||||
|
||||
private void FX_LDLH(byte cmd)
|
||||
{
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.XL = regs.XH;
|
||||
else
|
||||
regs.YL = regs.YH;
|
||||
}
|
||||
|
||||
private void FX_LDRL(byte cmd)
|
||||
{
|
||||
var r = (cmd & 0x38) >> 3;
|
||||
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
_regSetters[r](regs.XL);
|
||||
else
|
||||
_regSetters[r](regs.YL);
|
||||
}
|
||||
|
||||
private void FX_LDRH(byte cmd)
|
||||
{
|
||||
var r = (cmd & 0x38) >> 3;
|
||||
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
_regSetters[r](regs.XH);
|
||||
else
|
||||
_regSetters[r](regs.YH);
|
||||
}
|
||||
|
||||
private void FX_LDLR(byte cmd)
|
||||
{
|
||||
var r = cmd & 0x07;
|
||||
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.XL = _regGetters[r]();
|
||||
else
|
||||
regs.YL = _regGetters[r]();
|
||||
}
|
||||
|
||||
private void FX_LDHR(byte cmd)
|
||||
{
|
||||
var r = cmd & 0x07;
|
||||
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.XH = _regGetters[r]();
|
||||
else
|
||||
regs.YH = _regGetters[r]();
|
||||
}
|
||||
|
||||
private void FX_LDLNN(byte cmd) // LD XL,nn
|
||||
{
|
||||
// 11T (4, 4, 3)
|
||||
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.XL = RDMEM(regs.PC);
|
||||
else
|
||||
regs.YL = RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_LDHNN(byte cmd) // LD XH,nn
|
||||
{
|
||||
// 11T (4, 4, 3)
|
||||
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.XH = RDMEM(regs.PC);
|
||||
else
|
||||
regs.YH = RDMEM(regs.PC);
|
||||
regs.PC++;
|
||||
Tact += 3;
|
||||
}
|
||||
|
||||
private void FX_INCL(byte cmd) // INC XL
|
||||
{
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.XL = ALU_INCR(regs.XL);
|
||||
else
|
||||
regs.YL = ALU_INCR(regs.YL);
|
||||
}
|
||||
|
||||
private void FX_INCH(byte cmd) // INC XH
|
||||
{
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.XH = ALU_INCR(regs.XH);
|
||||
else
|
||||
regs.YH = ALU_INCR(regs.YH);
|
||||
}
|
||||
|
||||
private void FX_DECL(byte cmd) // DEC XL
|
||||
{
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.XL = ALU_DECR(regs.XL);
|
||||
else
|
||||
regs.YL = ALU_DECR(regs.YL);
|
||||
}
|
||||
|
||||
private void FX_DECH(byte cmd) // DEC XH
|
||||
{
|
||||
if (FX == CpuModeIndex.Ix)
|
||||
regs.XH = ALU_DECR(regs.XH);
|
||||
else
|
||||
regs.YH = ALU_DECR(regs.YH);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright 2007, 2011, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Description: Z80 CPU Emulator [FXCB prefixed opcodes part]
|
||||
* Date: 25.09.2011
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu.Processor
|
||||
{
|
||||
public partial class Z80Cpu
|
||||
{
|
||||
private void FXCB_RLC(byte cmd, ushort adr) // *RLC r
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var r = cmd & 7;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_RLC(val);
|
||||
_regSetters[r](val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_RRC(byte cmd, ushort adr) // *RRC r
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var r = cmd & 7;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_RRC(val);
|
||||
_regSetters[r](val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_RL(byte cmd, ushort adr) // *RL r
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var r = cmd & 7;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_RL(val);
|
||||
_regSetters[r](val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_RR(byte cmd, ushort adr) // *RR r
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var r = cmd & 7;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_RR(val);
|
||||
_regSetters[r](val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_SLA(byte cmd, ushort adr) // *SLA r
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var r = cmd & 7;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_SLA(val);
|
||||
_regSetters[r](val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_SRA(byte cmd, ushort adr) // *SRA r
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var r = cmd & 7;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_SRA(val);
|
||||
_regSetters[r](val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_SLL(byte cmd, ushort adr) // **SLL r
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var r = cmd & 7;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_SLL(val);
|
||||
_regSetters[r](val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_SRL(byte cmd, ushort adr) // *SRL r
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var r = cmd & 7;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_SRL(val);
|
||||
_regSetters[r](val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_RES(byte cmd, ushort adr) // *RES b,r,(IX+drel)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var m = (byte)~(1 << ((cmd & 0x38) >> 3));
|
||||
var r = cmd & 7;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val &= m;
|
||||
_regSetters[r](val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_SET(byte cmd, ushort adr) // *SET b,r,(IX+drel)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var m = (byte)(1 << ((cmd & 0x38) >> 3));
|
||||
var r = cmd & 7;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val |= m;
|
||||
_regSetters[r](val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_RLCIX(byte cmd, ushort adr) // RLC (IX+) [23T]
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_RLC(val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_RRCIX(byte cmd, ushort adr) // RRC (IX+)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_RRC(val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_RLIX(byte cmd, ushort adr) // RL (IX+)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_RL(val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_RRIX(byte cmd, ushort adr) // RR (IX+)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_RR(val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_SLAIX(byte cmd, ushort adr) // SLA (IX+)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_SLA(val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_SRAIX(byte cmd, ushort adr) // SRA (IX+)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_SRA(val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_SLLIX(byte cmd, ushort adr) // *SLL (IX+)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_SLL(val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_SRLIX(byte cmd, ushort adr) // SRL (IX+)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val = ALU_SRL(val);
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_BITIX(byte cmd, ushort adr) // BIT (IX+)
|
||||
{
|
||||
// 20T (4, 4, 3, 5, 4)
|
||||
var b = (cmd & 0x38) >> 3;
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
ALU_BITMEM(val, b);
|
||||
}
|
||||
|
||||
private void FXCB_RESIX(byte cmd, ushort adr) // RES (IX+)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var m = (byte)~(1 << ((cmd & 0x38) >> 3));
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val &= m;
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
|
||||
private void FXCB_SETIX(byte cmd, ushort adr) // SET (IX+)
|
||||
{
|
||||
// 23T (4, 4, 3, 5, 4, 3)
|
||||
var m = (byte)(1 << ((cmd & 0x38) >> 3));
|
||||
|
||||
var val = RDMEM(adr); Tact += 3;
|
||||
RDNOMREQ(adr); Tact++;
|
||||
val |= m;
|
||||
WRMEM(adr, val); Tact += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu.Processor
|
||||
{
|
||||
public partial class Z80Cpu
|
||||
{
|
||||
private readonly Action<byte>[] _opcodes;
|
||||
private readonly Action<byte>[] _opcodesFx;
|
||||
private readonly Action<byte>[] _opcodesEd;
|
||||
private readonly Action<byte>[] _opcodesCb;
|
||||
private readonly Action<byte, ushort>[] _opcodesFxCb;
|
||||
private readonly Action<byte>[] _alualg;
|
||||
private readonly Func<ushort>[] _pairGetters;
|
||||
private readonly Action<ushort>[] _pairSetters;
|
||||
private readonly Func<byte>[] _regGetters;
|
||||
private readonly Action<byte>[] _regSetters;
|
||||
|
||||
|
||||
|
||||
private Action<byte>[] CreateOpcodes()
|
||||
{
|
||||
Action<byte> scf;
|
||||
Action<byte> ccf;
|
||||
switch (_type)
|
||||
{
|
||||
case CpuType.ZILOG_NMOS:
|
||||
case CpuType.ZILOG_CMOS:
|
||||
scf = SCF_ZILOG;
|
||||
ccf = CCF_ZILOG;
|
||||
break;
|
||||
case CpuType.NEC_NMOS:
|
||||
scf = SCF_NEC_NMOS;
|
||||
ccf = CCF_NEC_NMOS;
|
||||
break;
|
||||
case CpuType.ST_CMOS:
|
||||
scf = SCF_ST_CMOS;
|
||||
ccf = CCF_ST_CMOS;
|
||||
break;
|
||||
default: throw new NotImplementedException();
|
||||
}
|
||||
|
||||
var opcodes = new Action<byte>[256]
|
||||
{
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
null, LDRRNNNN, LD_RR_A, INCRR, INCR, DECR, LDRNN, RLCA, EXAFAF, ADDHLRR, LDA_RR_, DECRR, INCR, DECR, LDRNN, RRCA, // 00..0F
|
||||
DJNZ, LDRRNNNN, LD_RR_A, INCRR, INCR, DECR, LDRNN, RLA, JRNN, ADDHLRR, LDA_RR_, DECRR, INCR, DECR, LDRNN, RRA, // 10..1F
|
||||
JRXNN, LDRRNNNN, LD_NN_HL, INCRR, INCR, DECR, LDRNN, DAA, JRXNN, ADDHLRR, LDHL_NN_, DECRR, INCR, DECR, LDRNN, CPL, // 20..2F
|
||||
JRXNN, LDRRNNNN, LD_NN_A, INCRR, INC_HL_, DEC_HL_, LD_HL_NN, scf, JRXNN, ADDHLRR, LDA_NN_, DECRR, INCR, DECR, LDRNN, ccf, // 30..3F
|
||||
|
||||
null, LDRdRs, LDRdRs, LDRdRs, LDRdRs, LDRdRs, LDR_HL_, LDRdRs, LDRdRs, null, LDRdRs, LDRdRs, LDRdRs, LDRdRs, LDR_HL_, LDRdRs, // 40..4F
|
||||
LDRdRs, LDRdRs, null, LDRdRs, LDRdRs, LDRdRs, LDR_HL_, LDRdRs, LDRdRs, LDRdRs, LDRdRs, null, LDRdRs, LDRdRs, LDR_HL_, LDRdRs, // 50..5F
|
||||
LDRdRs, LDRdRs, LDRdRs, LDRdRs, null, LDRdRs, LDR_HL_, LDRdRs, LDRdRs, LDRdRs, LDRdRs, LDRdRs, LDRdRs, null, LDR_HL_, LDRdRs, // 60..6F
|
||||
LD_HL_R, LD_HL_R, LD_HL_R, LD_HL_R, LD_HL_R, LD_HL_R, HALT, LD_HL_R, LDRdRs, LDRdRs, LDRdRs, LDRdRs, LDRdRs, LDRdRs, LDR_HL_, null, // 70..7F
|
||||
|
||||
ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUA_HL_, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUA_HL_, ALUAR, // 80..8F
|
||||
ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUA_HL_, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUA_HL_, ALUAR, // 90..9F
|
||||
ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUA_HL_, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUA_HL_, ALUAR, // A0..AF
|
||||
ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUA_HL_, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, ALUA_HL_, ALUAR, // B0..BF
|
||||
|
||||
RETX, POPRR, JPXNN, JPNNNN, CALLXNNNN, PUSHRR, ALUAN, RSTNN, RETX, RET, JPXNN, null, CALLXNNNN, CALLNNNN, ALUAN, RSTNN, // C0..CF
|
||||
RETX, POPRR, JPXNN, OUT_NN_A, CALLXNNNN, PUSHRR, ALUAN, RSTNN, RETX, EXX, JPXNN, INA_NN_, CALLXNNNN, null, ALUAN, RSTNN, // D0..DF
|
||||
RETX, POPRR, JPXNN, EX_SP_HL, CALLXNNNN, PUSHRR, ALUAN, RSTNN, RETX, JP_HL_, JPXNN, EXDEHL, CALLXNNNN, null, ALUAN, RSTNN, // E0..EF
|
||||
RETX, POPRR, JPXNN, DI, CALLXNNNN, PUSHRR, ALUAN, RSTNN, RETX, LDSPHL, JPXNN, EI, CALLXNNNN, null, ALUAN, RSTNN, // F0..FF
|
||||
};
|
||||
return opcodes;
|
||||
// patch opcodes with optimized version
|
||||
//for (var cmd = 0; cmd < 0x100; cmd++)
|
||||
//{
|
||||
// opcodes[cmd] =
|
||||
// EmitOpcodeLdRegReg(cmd) ??
|
||||
// EmitOpcodeJrXdisp(cmd) ??
|
||||
// opcodes[cmd];
|
||||
//}
|
||||
//return opcodes;
|
||||
}
|
||||
|
||||
private Action<byte>[] CreateOpcodesFx()
|
||||
{
|
||||
Action<byte> scf;
|
||||
Action<byte> ccf;
|
||||
switch (_type)
|
||||
{
|
||||
case CpuType.ZILOG_NMOS:
|
||||
case CpuType.ZILOG_CMOS:
|
||||
scf = SCF_ZILOG;
|
||||
ccf = CCF_ZILOG;
|
||||
break;
|
||||
case CpuType.NEC_NMOS:
|
||||
scf = SCF_NEC_NMOS;
|
||||
ccf = CCF_NEC_NMOS;
|
||||
break;
|
||||
case CpuType.ST_CMOS:
|
||||
scf = SCF_ST_CMOS;
|
||||
ccf = CCF_ST_CMOS;
|
||||
break;
|
||||
default: throw new NotImplementedException();
|
||||
}
|
||||
|
||||
return
|
||||
[
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
null, LDRRNNNN, LD_RR_A, INCRR, INCR, DECR, LDRNN, RLCA, EXAFAF, FX_ADDIXRR, LDA_RR_, DECRR, INCR, DECR, LDRNN, RRCA, // 00..0F
|
||||
DJNZ, LDRRNNNN, LD_RR_A, INCRR, INCR, DECR, LDRNN, RLA, JRNN, FX_ADDIXRR, LDA_RR_, DECRR, INCR, DECR, LDRNN, RRA, // 10..1F
|
||||
JRXNN, FX_LDIXNNNN, FX_LD_NN_IX, FX_INCIX, FX_INCH, FX_DECH, FX_LDHNN, DAA, JRXNN, FX_ADDIXRR, FX_LDIX_N_, FX_DECIX, FX_INCL, FX_DECL, FX_LDLNN, CPL, // 20..2F
|
||||
JRXNN, LDRRNNNN, LD_NN_A, INCRR, FX_INC_IX_, FX_DEC_IX_, FX_LD_IX_NN, scf, JRXNN, FX_ADDIXRR, LDA_NN_, DECRR, INCR, DECR, LDRNN, ccf, // 30..3F
|
||||
|
||||
null, LDRdRs, LDRdRs, LDRdRs, FX_LDRH, FX_LDRL, FX_LDR_IX_, LDRdRs, LDRdRs, null, LDRdRs, LDRdRs, FX_LDRH, FX_LDRL, FX_LDR_IX_, LDRdRs, // 40..4F
|
||||
LDRdRs, LDRdRs, null, LDRdRs, FX_LDRH, FX_LDRL, FX_LDR_IX_, LDRdRs, LDRdRs, LDRdRs, LDRdRs, null, FX_LDRH, FX_LDRL, FX_LDR_IX_, LDRdRs, // 50..5F
|
||||
FX_LDHR, FX_LDHR, FX_LDHR, FX_LDHR, null, FX_LDHL, FX_LDR_IX_, FX_LDHR, FX_LDLR, FX_LDLR, FX_LDLR, FX_LDLR, FX_LDLH, null, FX_LDR_IX_, FX_LDLR,// 60..6F
|
||||
FX_LD_IX_R, FX_LD_IX_R, FX_LD_IX_R, FX_LD_IX_R, FX_LD_IX_R, FX_LD_IX_R, HALT, FX_LD_IX_R, LDRdRs, LDRdRs, LDRdRs, LDRdRs, FX_LDRH, FX_LDRL, FX_LDR_IX_, null, // 70..7F
|
||||
|
||||
ALUAR, ALUAR, ALUAR, ALUAR, FX_ALUAXH, FX_ALUAXL, FX_ALUA_IX_, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, FX_ALUAXH, FX_ALUAXL, FX_ALUA_IX_, ALUAR, // 80..8F
|
||||
ALUAR, ALUAR, ALUAR, ALUAR, FX_ALUAXH, FX_ALUAXL, FX_ALUA_IX_, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, FX_ALUAXH, FX_ALUAXL, FX_ALUA_IX_, ALUAR, // 90..9F
|
||||
ALUAR, ALUAR, ALUAR, ALUAR, FX_ALUAXH, FX_ALUAXL, FX_ALUA_IX_, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, FX_ALUAXH, FX_ALUAXL, FX_ALUA_IX_, ALUAR, // A0..AF
|
||||
ALUAR, ALUAR, ALUAR, ALUAR, FX_ALUAXH, FX_ALUAXL, FX_ALUA_IX_, ALUAR, ALUAR, ALUAR, ALUAR, ALUAR, FX_ALUAXH, FX_ALUAXL, FX_ALUA_IX_, ALUAR, // B0..BF
|
||||
|
||||
RETX, POPRR, JPXNN, JPNNNN, CALLXNNNN, PUSHRR, ALUAN, RSTNN, RETX, RET, JPXNN, null, CALLXNNNN, CALLNNNN, ALUAN, RSTNN, // C0..CF
|
||||
RETX, POPRR, JPXNN, OUT_NN_A, CALLXNNNN, PUSHRR, ALUAN, RSTNN, RETX, EXX, JPXNN, INA_NN_, CALLXNNNN, null, ALUAN, RSTNN, // D0..DF
|
||||
RETX, FX_POPIX, JPXNN, FX_EX_SP_HL, CALLXNNNN, FX_PUSHIX, ALUAN, RSTNN, RETX, FX_JP_HL_, JPXNN, EXDEHL, CALLXNNNN, null, ALUAN, RSTNN, // E0..EF
|
||||
RETX, POPRR, JPXNN, DI, CALLXNNNN, PUSHRR, ALUAN, RSTNN, RETX, FX_LDSPHL, JPXNN, EI, CALLXNNNN, null, ALUAN, RSTNN, // F0..FF
|
||||
];
|
||||
}
|
||||
|
||||
private Action<byte>[] CreateOpcodesEd()
|
||||
{
|
||||
Action<byte> ed_outcr = _type switch
|
||||
{
|
||||
CpuType.ZILOG_NMOS or CpuType.NEC_NMOS => ED_OUTCR_NMOS,
|
||||
CpuType.ST_CMOS or CpuType.ZILOG_CMOS => ED_OUTCR_CMOS,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
return
|
||||
[
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, // 00..0F
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, // 10..1F
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, // 20..2F
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, // 30..3F
|
||||
ED_INRC, ed_outcr, ED_SBCHLRR, ED_LD_NN_RR, ED_NEG, ED_RETN, ED_IM, ED_LDXRA, ED_INRC, ed_outcr, ED_ADCHLRR, ED_LDRR_NN_, ED_NEG, ED_RETN, ED_IM, ED_LDXRA, // 40..4F
|
||||
ED_INRC, ed_outcr, ED_SBCHLRR, ED_LD_NN_RR, ED_NEG, ED_RETN, ED_IM, ED_LDAXR, ED_INRC, ed_outcr, ED_ADCHLRR, ED_LDRR_NN_, ED_NEG, ED_RETN, ED_IM, ED_LDAXR, // 50..5F
|
||||
ED_INRC, ed_outcr, ED_SBCHLRR, ED_LD_NN_RR, ED_NEG, ED_RETN, ED_IM, ED_RRD, ED_INRC, ed_outcr, ED_ADCHLRR, ED_LDRR_NN_, ED_NEG, ED_RETN, ED_IM, ED_RLD, // 60..6F
|
||||
ED_INRC, ed_outcr, ED_SBCHLRR, ED_LD_NN_RR, ED_NEG, ED_RETN, ED_IM, null, ED_INRC, ed_outcr, ED_ADCHLRR, ED_LDRR_NN_, ED_NEG, ED_RETN, ED_IM, null, // 70..7F
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, // 80..8F
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, // 90..9F
|
||||
ED_LDI, ED_CPI, ED_INI, ED_OUTI, null, null, null, null, ED_LDD, ED_CPD, ED_IND, ED_OUTD, null, null, null, null, // A0..AF
|
||||
ED_LDIR, ED_CPIR, ED_INIR, ED_OTIR, null, null, null, null, ED_LDDR, ED_CPDR, ED_INDR, ED_OTDR, null, null, null, null, // B0..BF
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, // C0..CF
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, // D0..DF
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, // E0..EF
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null // F0..FF
|
||||
];
|
||||
}
|
||||
|
||||
private Action<byte>[] CreateOpcodesCb()
|
||||
{
|
||||
return
|
||||
[
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
CB_RLC, CB_RLC, CB_RLC, CB_RLC, CB_RLC, CB_RLC, CB_RLCHL, CB_RLC, CB_RRC, CB_RRC, CB_RRC, CB_RRC, CB_RRC, CB_RRC, CB_RRCHL, CB_RRC, // 00..0F
|
||||
CB_RL, CB_RL, CB_RL, CB_RL, CB_RL, CB_RL, CB_RLHL, CB_RL, CB_RR, CB_RR, CB_RR, CB_RR, CB_RR, CB_RR, CB_RRHL, CB_RR, // 10..1F
|
||||
CB_SLA, CB_SLA, CB_SLA, CB_SLA, CB_SLA, CB_SLA, CB_SLAHL, CB_SLA, CB_SRA, CB_SRA, CB_SRA, CB_SRA, CB_SRA, CB_SRA, CB_SRAHL, CB_SRA, // 20..2F
|
||||
CB_SLL, CB_SLL, CB_SLL, CB_SLL, CB_SLL, CB_SLL, CB_SLLHL, CB_SLL, CB_SRL, CB_SRL, CB_SRL, CB_SRL, CB_SRL, CB_SRL, CB_SRLHL, CB_SRL, // 30..3F
|
||||
CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BITHL, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BITHL, CB_BIT, // 40..4F
|
||||
CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BITHL, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BITHL, CB_BIT, // 50..5F
|
||||
CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BITHL, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BITHL, CB_BIT, // 60..6F
|
||||
CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BITHL, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BIT, CB_BITHL, CB_BIT, // 70..7F
|
||||
CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RESHL, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RESHL, CB_RES, // 80..8F
|
||||
CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RESHL, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RESHL, CB_RES, // 90..9F
|
||||
CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RESHL, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RESHL, CB_RES, // A0..AF
|
||||
CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RESHL, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RES, CB_RESHL, CB_RES, // B0..BF
|
||||
CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SETHL, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SETHL, CB_SET, // C0..CF
|
||||
CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SETHL, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SETHL, CB_SET, // D0..DF
|
||||
CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SETHL, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SETHL, CB_SET, // E0..EF
|
||||
CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SETHL, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SET, CB_SETHL, CB_SET, // F0..FF
|
||||
];
|
||||
}
|
||||
|
||||
private Action<byte, ushort>[] CreateOpcodesFxCb()
|
||||
{
|
||||
return
|
||||
[
|
||||
FXCB_RLC, FXCB_RLC, FXCB_RLC, FXCB_RLC, FXCB_RLC, FXCB_RLC, FXCB_RLCIX, FXCB_RLC, FXCB_RRC, FXCB_RRC, FXCB_RRC, FXCB_RRC, FXCB_RRC, FXCB_RRC, FXCB_RRCIX, FXCB_RRC, // 00..0F
|
||||
FXCB_RL, FXCB_RL, FXCB_RL, FXCB_RL, FXCB_RL, FXCB_RL, FXCB_RLIX, FXCB_RL, FXCB_RR, FXCB_RR, FXCB_RR, FXCB_RR, FXCB_RR, FXCB_RR, FXCB_RRIX, FXCB_RR, // 10..1F
|
||||
FXCB_SLA, FXCB_SLA, FXCB_SLA, FXCB_SLA, FXCB_SLA, FXCB_SLA, FXCB_SLAIX, FXCB_SLA, FXCB_SRA, FXCB_SRA, FXCB_SRA, FXCB_SRA, FXCB_SRA, FXCB_SRA, FXCB_SRAIX, FXCB_SRA, // 20..2F
|
||||
FXCB_SLL, FXCB_SLL, FXCB_SLL, FXCB_SLL, FXCB_SLL, FXCB_SLL, FXCB_SLLIX, FXCB_SLL, FXCB_SRL, FXCB_SRL, FXCB_SRL, FXCB_SRL, FXCB_SRL, FXCB_SRL, FXCB_SRLIX, FXCB_SRL, // 30..3F
|
||||
FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, // 40..4F
|
||||
FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, // 50..5F
|
||||
FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, // 60..6F
|
||||
FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, FXCB_BITIX, // 70..7F
|
||||
FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RESIX, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RESIX, FXCB_RES, // 80..8F
|
||||
FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RESIX, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RESIX, FXCB_RES, // 90..9F
|
||||
FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RESIX, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RESIX, FXCB_RES, // A0..AF
|
||||
FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RESIX, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RES, FXCB_RESIX, FXCB_RES, // B0..BF
|
||||
FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SETIX, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SETIX, FXCB_SET, // C0..CF
|
||||
FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SETIX, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SETIX, FXCB_SET, // D0..DF
|
||||
FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SETIX, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SETIX, FXCB_SET, // E0..EF
|
||||
FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SETIX, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SET, FXCB_SETIX, FXCB_SET, // F0..FF
|
||||
];
|
||||
}
|
||||
|
||||
private Action<byte>[] CreateAluAlg()
|
||||
{
|
||||
return
|
||||
[
|
||||
ALU_ADDR, ALU_ADCR, ALU_SUBR, ALU_SBCR,
|
||||
ALU_ANDR, ALU_XORR, ALU_ORR, ALU_CPR,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region Optimized emitters
|
||||
|
||||
//private Action<byte> EmitOpcodeLdRegReg(int cmd)
|
||||
//{
|
||||
// if (cmd < 0x40 || cmd >= 0x80)
|
||||
// {
|
||||
// return null;
|
||||
// }
|
||||
// var rsrc = cmd & 0x07;
|
||||
// var rdst = (cmd & 0x38) >> 3;
|
||||
// if (rsrc == rdst)
|
||||
// {
|
||||
// return rsrc == 6 ? HALT : (Action<byte>)null;
|
||||
// }
|
||||
// var getter = rsrc != 6 ? regs.CreateRegGetter(rsrc) :
|
||||
// new Func<byte>(() =>
|
||||
// {
|
||||
// // LD R,(HL)
|
||||
// // 7T (4, 3)
|
||||
// var value = RDMEM(regs.HL); Tact += 3;
|
||||
// return value;
|
||||
// });
|
||||
// var setter = rdst != 6 ? regs.CreateRegSetter(rdst) :
|
||||
// new Action<byte>(arg =>
|
||||
// {
|
||||
// // LD (HL),R
|
||||
// // 7T (4, 3)
|
||||
// WRMEM(regs.HL, arg); Tact += 3;
|
||||
// });
|
||||
// return arg => setter(getter());
|
||||
//}
|
||||
|
||||
//private Action<byte> EmitOpcodeJrXdisp(int cmd)
|
||||
//{
|
||||
// if (cmd < 0x20 || cmd >= 0x40 || (cmd & 7) != 0)
|
||||
// {
|
||||
// return null;
|
||||
// }
|
||||
// // JR x,disp
|
||||
// // false => 7T (4, 3)
|
||||
// // true => 12 (4, 3, 5)
|
||||
// var cond = (cmd & 0x18) >> 3;
|
||||
// var mask = s_conds[cond >> 1];
|
||||
// var xor = (cond & 1) == 0 ? -1 : 0;
|
||||
// return new Action<byte>(arg =>
|
||||
// {
|
||||
// int drel = (sbyte)RDMEM(regs.PC); Tact += 3;
|
||||
// regs.PC++;
|
||||
// var f = (regs.AF ^ xor) & mask;
|
||||
// if (f == 0)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// RDNOMREQ(regs.PC); Tact++;
|
||||
// RDNOMREQ(regs.PC); Tact++;
|
||||
// RDNOMREQ(regs.PC); Tact++;
|
||||
// RDNOMREQ(regs.PC); Tact++;
|
||||
// RDNOMREQ(regs.PC); Tact++;
|
||||
// regs.MW = (ushort)(regs.PC + drel);
|
||||
// regs.PC = regs.MW;
|
||||
// });
|
||||
//}
|
||||
|
||||
#endregion Optimized emitters
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
* Copyright 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Description: Z80 CPU Emulator
|
||||
* Date: 13.04.2007
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu.Processor
|
||||
{
|
||||
public partial class Z80Cpu
|
||||
{
|
||||
public readonly CpuRegs regs = new();
|
||||
public int RzxCounter;
|
||||
public long Tact;
|
||||
public bool HALTED;
|
||||
public bool IFF1;
|
||||
public bool IFF2;
|
||||
public byte IM;
|
||||
public bool BINT; // last opcode was EI or DD/FD prefix (to prevent INT handling)
|
||||
public CpuModeIndex FX;
|
||||
public CpuModeEx XFX;
|
||||
public ushort LPC; // last opcode PC
|
||||
|
||||
public bool INT;
|
||||
public bool NMI;
|
||||
public bool RST;
|
||||
public byte BUS = 0xFF; // state of free data bus
|
||||
|
||||
public Action RESET;
|
||||
public Action NMIACK_M1;
|
||||
public Action INTACK_M1;
|
||||
public Func<ushort, byte> RDMEM_M1;
|
||||
public Func<ushort, byte> RDMEM;
|
||||
public Action<ushort, byte> WRMEM;
|
||||
public Func<ushort, byte> RDPORT;
|
||||
public Action<ushort, byte> WRPORT;
|
||||
public Action<ushort> RDNOMREQ;
|
||||
public Action<ushort> WRNOMREQ;
|
||||
public Action SCANSIG;
|
||||
|
||||
private CpuType _type = CpuType.NEC_NMOS;
|
||||
public CpuType Type
|
||||
{
|
||||
get { return _type; }
|
||||
set { _type = value; InitOpcodeTables(); }
|
||||
}
|
||||
|
||||
public Z80Cpu()
|
||||
{
|
||||
_pairGetters = [.. Enumerable.Range(0, 4).Select(regs.CreatePairGetter)];
|
||||
_pairSetters = [.. Enumerable.Range(0, 4).Select(regs.CreatePairSetter)];
|
||||
_regGetters = [.. Enumerable.Range(0, 8).Select(regs.CreateRegGetter)];
|
||||
_regSetters = [.. Enumerable.Range(0, 8).Select(regs.CreateRegSetter)];
|
||||
_alualg = CreateAluAlg();
|
||||
|
||||
_opcodes = new Action<byte>[256];
|
||||
_opcodesFx = new Action<byte>[256];
|
||||
_opcodesEd = new Action<byte>[256];
|
||||
_opcodesCb = new Action<byte>[256];
|
||||
_opcodesFxCb = new Action<byte, ushort>[256];
|
||||
InitOpcodeTables();
|
||||
|
||||
regs.AF = 0xFF;
|
||||
regs.BC = 0xFF;
|
||||
regs.DE = 0xFF;
|
||||
regs.HL = 0xFF;
|
||||
regs._AF = 0xFF;
|
||||
regs._BC = 0xFF;
|
||||
regs._DE = 0xFF;
|
||||
regs._HL = 0xFF;
|
||||
regs.IX = 0xFF;
|
||||
regs.IY = 0xFF;
|
||||
regs.IR = 0xFF;
|
||||
regs.PC = 0xFF;
|
||||
regs.SP = 0xFF;
|
||||
regs.MW = 0xFF;
|
||||
}
|
||||
|
||||
private void InitOpcodeTables()
|
||||
{
|
||||
InitOpcodeTable(_opcodes, CreateOpcodes());
|
||||
InitOpcodeTable(_opcodesFx, CreateOpcodesFx());
|
||||
InitOpcodeTable(_opcodesEd, CreateOpcodesEd());
|
||||
InitOpcodeTable(_opcodesCb, CreateOpcodesCb());
|
||||
InitOpcodeTable(_opcodesFxCb, CreateOpcodesFxCb());
|
||||
}
|
||||
|
||||
private static void InitOpcodeTable<T>(T[] opcodes, T[] actions)
|
||||
{
|
||||
if (opcodes.Length != actions.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(opcodes));
|
||||
|
||||
for (var i = 0; i < opcodes.Length; i++)
|
||||
{
|
||||
opcodes[i] = actions[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void ExecCycle()
|
||||
{
|
||||
byte cmd = 0;
|
||||
if (XFX == CpuModeEx.None && FX == CpuModeIndex.None)
|
||||
{
|
||||
if (ProcessSignals())
|
||||
return;
|
||||
LPC = regs.PC;
|
||||
cmd = RDMEM_M1(LPC);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ProcessSignals())
|
||||
return;
|
||||
cmd = RDMEM(regs.PC);
|
||||
}
|
||||
Tact += 3;
|
||||
regs.PC++;
|
||||
if (XFX == CpuModeEx.Cb)
|
||||
{
|
||||
BINT = false;
|
||||
if (FX != CpuModeIndex.None)
|
||||
{
|
||||
// elapsed T: 4, 4, 3
|
||||
// will be T: 4, 4, 3, 5
|
||||
int drel = (sbyte)cmd;
|
||||
|
||||
regs.MW = FX == CpuModeIndex.Ix ? (ushort)(regs.IX + drel) : (ushort)(regs.IY + drel);
|
||||
cmd = RDMEM(regs.PC); Tact += 3;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
RDNOMREQ(regs.PC); Tact++;
|
||||
|
||||
regs.PC++;
|
||||
_opcodesFxCb[cmd](cmd, regs.MW);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Refresh();
|
||||
regs.R = (byte)(((regs.R + 1) & 0x7F) | (regs.R & 0x80));
|
||||
Tact += 1;
|
||||
RzxCounter++;
|
||||
|
||||
_opcodesCb[cmd](cmd);
|
||||
}
|
||||
XFX = CpuModeEx.None;
|
||||
FX = CpuModeIndex.None;
|
||||
}
|
||||
else if (XFX == CpuModeEx.Ed)
|
||||
{
|
||||
//Refresh();
|
||||
regs.R = (byte)(((regs.R + 1) & 0x7F) | (regs.R & 0x80));
|
||||
Tact += 1;
|
||||
RzxCounter++;
|
||||
|
||||
BINT = false;
|
||||
_opcodesEd[cmd]?.Invoke(cmd);
|
||||
|
||||
XFX = CpuModeEx.None;
|
||||
FX = CpuModeIndex.None;
|
||||
}
|
||||
else if (cmd == 0xDD)
|
||||
{
|
||||
//Refresh();
|
||||
regs.R = (byte)(((regs.R + 1) & 0x7F) | (regs.R & 0x80));
|
||||
Tact += 1;
|
||||
RzxCounter++;
|
||||
|
||||
FX = CpuModeIndex.Ix;
|
||||
BINT = true;
|
||||
}
|
||||
else if (cmd == 0xFD)
|
||||
{
|
||||
//Refresh();
|
||||
regs.R = (byte)(((regs.R + 1) & 0x7F) | (regs.R & 0x80));
|
||||
Tact += 1;
|
||||
RzxCounter++;
|
||||
|
||||
FX = CpuModeIndex.Iy;
|
||||
BINT = true;
|
||||
}
|
||||
else if (cmd == 0xCB)
|
||||
{
|
||||
//Refresh();
|
||||
regs.R = (byte)(((regs.R + 1) & 0x7F) | (regs.R & 0x80));
|
||||
Tact += 1;
|
||||
RzxCounter++;
|
||||
|
||||
XFX = CpuModeEx.Cb;
|
||||
BINT = true;
|
||||
}
|
||||
else if (cmd == 0xED)
|
||||
{
|
||||
//Refresh();
|
||||
regs.R = (byte)(((regs.R + 1) & 0x7F) | (regs.R & 0x80));
|
||||
Tact += 1;
|
||||
RzxCounter++;
|
||||
|
||||
XFX = CpuModeEx.Ed;
|
||||
BINT = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Refresh();
|
||||
regs.R = (byte)(((regs.R + 1) & 0x7F) | (regs.R & 0x80));
|
||||
Tact += 1;
|
||||
RzxCounter++;
|
||||
|
||||
BINT = false;
|
||||
(FX == CpuModeIndex.None ? _opcodes[cmd] : _opcodesFx[cmd])?.Invoke(cmd);
|
||||
FX = CpuModeIndex.None;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ProcessSignals()
|
||||
{
|
||||
if (RST) // RESET
|
||||
{
|
||||
// 3T
|
||||
RESET();
|
||||
//Refresh(); //+1T
|
||||
regs.R = (byte)(((regs.R + 1) & 0x7F) | (regs.R & 0x80));
|
||||
Tact += 1;
|
||||
RzxCounter++;
|
||||
|
||||
|
||||
FX = CpuModeIndex.None;
|
||||
XFX = CpuModeEx.None;
|
||||
HALTED = false;
|
||||
|
||||
IFF1 = false;
|
||||
IFF2 = false;
|
||||
regs.PC = 0;
|
||||
regs.IR = 0;
|
||||
IM = 0;
|
||||
//regs.SP = 0xFFFF;
|
||||
//regs.AF = 0xFFFF;
|
||||
|
||||
Tact += 2; // total should be 3T?
|
||||
return true;
|
||||
}
|
||||
else if (NMI)
|
||||
{
|
||||
// 11T (5, 3, 3)
|
||||
|
||||
if (HALTED) // workaround for Z80 snapshot halt issue + comfortable debugging
|
||||
regs.PC++;
|
||||
|
||||
// M1
|
||||
NMIACK_M1();
|
||||
Tact += 4;
|
||||
//Refresh();
|
||||
regs.R = (byte)(((regs.R + 1) & 0x7F) | (regs.R & 0x80));
|
||||
Tact += 1;
|
||||
RzxCounter++;
|
||||
|
||||
|
||||
IFF2 = IFF1;
|
||||
IFF1 = false;
|
||||
HALTED = false;
|
||||
regs.SP--;
|
||||
|
||||
// M2
|
||||
WRMEM(regs.SP, (byte)(regs.PC >> 8));
|
||||
Tact += 3;
|
||||
regs.SP--;
|
||||
|
||||
// M3
|
||||
WRMEM(regs.SP, (byte)regs.PC);
|
||||
regs.PC = 0x0066;
|
||||
Tact += 3;
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (INT && (!BINT) && IFF1)
|
||||
{
|
||||
// http://www.z80.info/interrup.htm
|
||||
// IM0: 13T (7,3,3) [RST]
|
||||
// IM1: 13T (7,3,3)
|
||||
// IM2: 19T (7,3,3,3,3)
|
||||
|
||||
if (HALTED) // workaround for Z80 snapshot halt issue + comfortable debugging
|
||||
regs.PC++;
|
||||
|
||||
|
||||
INTACK_M1();
|
||||
// M1: 7T = interrupt acknowledgement; SP--
|
||||
regs.SP--;
|
||||
//if (HALTED) ??
|
||||
// Tact += 2;
|
||||
Tact += 4 + 2;
|
||||
//Refresh();
|
||||
//RzxCounter--; // fix because INTAK should not be calculated
|
||||
regs.R = (byte)(((regs.R + 1) & 0x7F) | (regs.R & 0x80));
|
||||
Tact += 1;
|
||||
|
||||
|
||||
IFF1 = false;
|
||||
IFF2 = false; // proof?
|
||||
HALTED = false;
|
||||
|
||||
// M2
|
||||
WRMEM(regs.SP, (byte)(regs.PC >> 8)); // M2: 3T write PCH; SP--
|
||||
regs.SP--;
|
||||
Tact += 3;
|
||||
|
||||
// M3
|
||||
WRMEM(regs.SP, (byte)regs.PC); // M3: 3T write PCL
|
||||
Tact += 3;
|
||||
|
||||
if (IM == 0) // IM 0: execute instruction taken from BUS with timing T+2???
|
||||
{
|
||||
regs.MW = 0x0038; // workaround: just execute #FF
|
||||
}
|
||||
else if (IM == 1) // IM 1: execute #FF with timing T+2 (11+2=13T)
|
||||
{
|
||||
regs.MW = 0x0038;
|
||||
}
|
||||
else // IM 2: VH=reg.I; VL=BUS; PC=[V]
|
||||
{
|
||||
// M4
|
||||
ushort adr = (ushort)((regs.IR & 0xFF00) | BUS);
|
||||
regs.MW = RDMEM(adr); // M4: 3T read VL
|
||||
Tact += 3;
|
||||
|
||||
// M5
|
||||
regs.MW += (ushort)(RDMEM(++adr) * 0x100); // M5: 3T read VH, PC=V
|
||||
Tact += 3;
|
||||
}
|
||||
regs.PC = regs.MW;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* Copyright 2001, 2007, 2015 Alex Makeev
|
||||
*
|
||||
* This file is part of ZXMAK2 (ZX Spectrum virtual machine).
|
||||
*
|
||||
* ZXMAK2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ZXMAK2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ZXMAK2. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
using System;
|
||||
using ZXMAK2.Engine.Cpu.Processor;
|
||||
|
||||
|
||||
namespace ZXMAK2.Engine.Cpu.Tools
|
||||
{
|
||||
public class TimingTool(Z80Cpu cpu, Func<ushort, byte> reader)
|
||||
{
|
||||
private readonly Z80Cpu _cpu = cpu;
|
||||
private readonly Func<ushort, byte> _memRead = reader;
|
||||
|
||||
public string GetTimingString(int addr)
|
||||
{
|
||||
var t = GetTiming(addr);
|
||||
return t.HasValue ? string.Format("{0}T", t) : "N/A";
|
||||
}
|
||||
|
||||
public int? GetTiming(int addr)
|
||||
{
|
||||
if (_memRead == null || _cpu == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var tableNumber = 0;
|
||||
var perfTime = 0;
|
||||
var offset = 0;
|
||||
var opCode = -1;
|
||||
while (true)
|
||||
{
|
||||
opCode = _memRead((ushort)(addr + offset));
|
||||
var res = s_tables[tableNumber][opCode];
|
||||
if (res < 0xF0)
|
||||
{
|
||||
return res + perfTime;
|
||||
}
|
||||
tableNumber = res - 0xF0;
|
||||
if (tableNumber >= 5)
|
||||
{
|
||||
break;
|
||||
}
|
||||
perfTime += 4;
|
||||
offset++;
|
||||
if (tableNumber == 4)
|
||||
{
|
||||
offset++;
|
||||
}
|
||||
if (perfTime > 1500000)
|
||||
{
|
||||
System.Console.Error.WriteLine("TimingTool.GetTiming: {0}T reached!", perfTime);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
switch (opCode)
|
||||
{
|
||||
case 0x10:
|
||||
return DjnzTime(perfTime, 0x08, 0x0D);
|
||||
|
||||
case 0xB1:
|
||||
case 0xB2:
|
||||
case 0xB9:
|
||||
case 0xBA:
|
||||
return DjnzTime(perfTime, 0x0C, 0x11);
|
||||
|
||||
case 0x20: return FlagTime(perfTime, 0x07, 0x0C, 0x40);
|
||||
case 0x28: return FlagTime(perfTime, 0x0C, 0x07, 0x40);
|
||||
case 0x30: return FlagTime(perfTime, 0x07, 0x0C, 0x01);
|
||||
case 0x38: return FlagTime(perfTime, 0x0C, 0x07, 0x01);
|
||||
case 0xC0: return FlagTime(perfTime, 0x05, 0x0B, 0x40);
|
||||
case 0xC8: return FlagTime(perfTime, 0x0B, 0x05, 0x40);
|
||||
case 0xD0: return FlagTime(perfTime, 0x05, 0x0B, 0x01);
|
||||
case 0xD8: return FlagTime(perfTime, 0x0B, 0x05, 0x01);
|
||||
case 0xE0: return FlagTime(perfTime, 0x05, 0x0B, 0x04);
|
||||
case 0xE8: return FlagTime(perfTime, 0x0B, 0x05, 0x04);
|
||||
case 0xF0: return FlagTime(perfTime, 0x05, 0x0B, 0x80);
|
||||
case 0xF8: return FlagTime(perfTime, 0x0B, 0x05, 0x80);
|
||||
case 0xC4: return FlagTime(perfTime, 0x0A, 0x11, 0x40);
|
||||
case 0xCC: return FlagTime(perfTime, 0x11, 0x0A, 0x40);
|
||||
case 0xD4: return FlagTime(perfTime, 0x0A, 0x11, 0x01);
|
||||
case 0xDC: return FlagTime(perfTime, 0x11, 0x0A, 0x01);
|
||||
case 0xE4: return FlagTime(perfTime, 0x0A, 0x11, 0x04);
|
||||
case 0xEC: return FlagTime(perfTime, 0x11, 0x0A, 0x04);
|
||||
case 0xF4: return FlagTime(perfTime, 0x0A, 0x11, 0x80);
|
||||
case 0xFC: return FlagTime(perfTime, 0x11, 0x0A, 0x80);
|
||||
|
||||
case 0xB0:
|
||||
case 0xB8:
|
||||
return RepTime(perfTime, 0x0C, 0x11);
|
||||
|
||||
case 0xB3:
|
||||
case 0xBB:
|
||||
return RepFlagTime(perfTime, 0x0C, 0x0C, 0x11);
|
||||
}
|
||||
System.Console.Error.WriteLine(
|
||||
"TimingTool.GetTiming: unexpected opCode #{0:2X}",
|
||||
opCode);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
#region Private
|
||||
|
||||
private int DjnzTime(int timeBase, int timeEnd, int timeNoEnd)
|
||||
{
|
||||
var ifTime = _cpu.regs.B == 0x01 ? timeEnd : timeNoEnd;
|
||||
return timeBase + ifTime;
|
||||
}
|
||||
|
||||
private int FlagTime(int timeBase, int timeOn, int timeOff, int flagMask)
|
||||
{
|
||||
var ifTime = (_cpu.regs.F & flagMask) != 0 ? timeOn : timeOff;
|
||||
return timeBase + ifTime;
|
||||
}
|
||||
|
||||
private int RepTime(int perfTime, int timeEnd, int timeNoEnd)
|
||||
{
|
||||
var ifTime = _cpu.regs.BC == 0x0001 ? timeEnd : timeNoEnd;
|
||||
return perfTime + ifTime;
|
||||
}
|
||||
|
||||
private int RepFlagTime(int timeEnd, int timeOn, int timeOff, int perfTime)
|
||||
{
|
||||
if (_cpu.regs.BC == 0x0001)
|
||||
{
|
||||
return perfTime + timeEnd;
|
||||
}
|
||||
var data = _memRead(_cpu.regs.HL);
|
||||
var ifTime = _cpu.regs.F == data ? timeOn : timeOff;
|
||||
return perfTime + ifTime;
|
||||
}
|
||||
|
||||
#endregion Private
|
||||
|
||||
|
||||
#region Tables
|
||||
|
||||
private static readonly int[] s_tableDirect =
|
||||
[
|
||||
0x04,0x0A,0x07,0x06,0x04,0x04,0x07,0x04, //;00h-07h
|
||||
0x04,0x0B,0x07,0x06,0x04,0x04,0x07,0x04, //;08h-0Fh
|
||||
0xFF,0x0A,0x07,0x06,0x04,0x04,0x07,0x04, //;10h-17h
|
||||
0x0C,0x0B,0x07,0x06,0x04,0x04,0x07,0x04, //;18h-1Fh
|
||||
0xFF,0x0A,0x10,0x06,0x04,0x04,0x07,0x04, //;20h-27h
|
||||
0xFF,0x0B,0x10,0x06,0x04,0x04,0x07,0x04, //;28h-2Fh
|
||||
0xFF,0x0A,0x0D,0x06,0x0B,0x0B,0x0A,0x04, //;30h-37h
|
||||
0xFF,0x0B,0x0D,0x06,0x04,0x04,0x07,0x04, //;38h-3Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;40h-47h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;48h-4Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;50h-57h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;58h-5Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;60h-67h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;68h-6Fh
|
||||
0x07,0x07,0x07,0x07,0x07,0x07,0x04,0x07, //;70h-77h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;78h-7Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;80h-87h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;88h-8Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;90h-97h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;98h-9Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;A0h-A7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;A8h-AFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;B0h-B7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x07,0x04, //;B8h-BFh
|
||||
0xFF,0x0A,0x0A,0x0A,0xFF,0x0B,0x07,0x0B, //;C0h-C7h
|
||||
0xFF,0x0A,0x0A,0xF1,0xFF,0x11,0x07,0x0B, //;C8h-CFh
|
||||
0xFF,0x0A,0x0A,0x0B,0xFF,0x0B,0x07,0x0B, //;D0h-D7h
|
||||
0xFF,0x04,0x0A,0x0B,0xFF,0xF3,0x07,0x0B, //;D8h-DFh
|
||||
0xFF,0x0A,0x0A,0x13,0xFF,0x0B,0x07,0x0B, //;E0h-E7h
|
||||
0xFF,0x04,0x0A,0x04,0xFF,0xF2,0x07,0x0B, //;E8h-EFh
|
||||
0xFF,0x0A,0x0A,0x04,0xFF,0x0B,0x07,0x0B, //;F0h-F7h
|
||||
0xFF,0x06,0x0A,0x04,0xFF,0xF3,0x07,0x0B //;F8h-FFh
|
||||
];
|
||||
|
||||
private static readonly int[] s_tableCb =
|
||||
[
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;00h-07h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;08h-0Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;10h-17h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;18h-1Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;20h-27h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;28h-2Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;30h-37h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;38h-3Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x08,0x04, // ;40h-47h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x08,0x04, // ;48h-4Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x08,0x04, // ;50h-57h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x08,0x04, // ;58h-5Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x08,0x04, // ;60h-67h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x08,0x04, // ;68h-6Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x08,0x04, // ;70h-77h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x08,0x04, // ;78h-7Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;80h-87h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;88h-8Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;90h-97h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;98h-9Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;A0h-A7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;A8h-AFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;B0h-B7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;B8h-BFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;C0h-C7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;C8h-CFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;D0h-D7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;D8h-DFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;E0h-E7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;E8h-EFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04, // ;F0h-F7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0B,0x04 // ;F8h-FFh
|
||||
];
|
||||
|
||||
private static readonly int[] s_tableEd =
|
||||
[
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;00h-07h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;08h-0Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;10h-17h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;18h-1Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;20h-27h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;28h-2Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;30h-37h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;38h-3Fh
|
||||
0x08,0x08,0x0B,0x10,0x04,0x0A,0x04,0x05, // ;40h-47h
|
||||
0x08,0x08,0x0B,0x10,0x04,0x0A,0x04,0x05, // ;48h-4Fh
|
||||
0x08,0x08,0x0B,0x10,0x04,0x0A,0x04,0x05, // ;50h-57h
|
||||
0x08,0x08,0x0B,0x10,0x04,0x0A,0x04,0x05, // ;58h-5Fh
|
||||
0x08,0x08,0x0B,0x10,0x04,0x0A,0x04,0x0E, // ;60h-67h
|
||||
0x08,0x08,0x0B,0x10,0x04,0x0A,0x04,0x0E, // ;68h-6Fh
|
||||
0x08,0x08,0x0B,0x10,0x04,0x0A,0x04,0x04, // ;70h-77h
|
||||
0x08,0x08,0x0B,0x10,0x04,0x0A,0x04,0x04, // ;78h-7Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;80h-87h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;88h-8Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;90h-97h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;98h-9Fh
|
||||
0x0C,0x0C,0x0C,0x0C,0x04,0x04,0x04,0x04, // ;A0h-A7h
|
||||
0x0C,0x0C,0x0C,0x0C,0x04,0x04,0x04,0x04, // ;A8h-AFh
|
||||
0xFF,0xFF,0xFF,0xFF,0x04,0x04,0x04,0x04, // ;B0h-B7h
|
||||
0xFF,0xFF,0xFF,0xFF,0x04,0x04,0x04,0x04, // ;B8h-BFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;C0h-C7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;C8h-CFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;D0h-D7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;D8h-DFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;E0h-E7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;E8h-EFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, // ;F0h-F7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04 // ;F8h-FFh
|
||||
];
|
||||
|
||||
|
||||
private static readonly int[] s_tableDdFd =
|
||||
[
|
||||
0x04,0x0A,0x07,0x06,0x04,0x04,0x07,0x04, // ;00h-07h
|
||||
0x04,0x0B,0x07,0x06,0x04,0x04,0x07,0x04, // ;08h-0Fh
|
||||
0xFF,0x0A,0x07,0x06,0x04,0x04,0x07,0x04, // ;10h-17h
|
||||
0x0C,0x0B,0x07,0x06,0x04,0x04,0x07,0x04, // ;18h-1Fh
|
||||
0xFF,0x0A,0x10,0x06,0x04,0x04,0x07,0x04, // ;20h-27h
|
||||
0xFF,0x0B,0x10,0x06,0x04,0x04,0x07,0x04, // ;28h-2Fh
|
||||
0xFF,0x0A,0x0D,0x06,0x13,0x13,0x0F,0x04, // ;30h-37h
|
||||
0xFF,0x0B,0x0D,0x06,0x04,0x04,0x0F,0x04, // ;38h-3Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;40h-47h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;48h-4Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;50h-57h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;58h-5Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;60h-67h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;68h-6Fh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x04,0x0F, // ;70h-77h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;78h-7Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;80h-87h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;88h-8Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;90h-97h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;98h-9Fh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;A0h-A7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;A8h-AFh
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;B0h-B7h
|
||||
0x04,0x04,0x04,0x04,0x04,0x04,0x0F,0x04, // ;B8h-BFh
|
||||
0xFF,0x0A,0x0A,0x0A,0xFF,0x0B,0x0F,0x0B, // ;C0h-C7h
|
||||
0xFF,0x0A,0x0A,0xF4,0xFF,0x11,0x0F,0x0B, // ;C8h-CFh
|
||||
0xFF,0x0A,0x0A,0x0B,0xFF,0x0B,0x0F,0x0B, // ;D0h-D7h
|
||||
0xFF,0x04,0x0A,0x0B,0xFF,0xF3,0x0F,0x0B, // ;D8h-DFh
|
||||
0xFF,0x0A,0x0A,0x13,0xFF,0x0B,0x0F,0x0B, // ;E0h-E7h
|
||||
0xFF,0x04,0x0A,0x04,0xFF,0xF2,0x0F,0x0B, // ;E8h-EFh
|
||||
0xFF,0x0A,0x0A,0x04,0xFF,0x0B,0x0F,0x0B, // ;F0h-F7h
|
||||
0xFF,0x06,0x0A,0x04,0xFF,0xF3,0x0F,0x0B // ;F8h-FFh
|
||||
];
|
||||
|
||||
private static readonly int[] s_tableDdCbFdCb =
|
||||
[
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;00h-07h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;08h-0Fh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;10h-17h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;18h-1Fh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;20h-27h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;28h-2Fh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;30h-37h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;38h-3Fh
|
||||
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // ;40h-47h
|
||||
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // ;48h-4Fh
|
||||
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // ;50h-57h
|
||||
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // ;58h-5Fh
|
||||
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // ;60h-67h
|
||||
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // ;68h-6Fh
|
||||
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // ;70h-77h
|
||||
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // ;78h-7Fh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;80h-87h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;88h-8Fh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;90h-97h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;98h-9Fh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;A0h-A7h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;A8h-AFh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;B0h-B7h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;B8h-BFh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;C0h-C7h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;C8h-CFh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;D0h-D7h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;D8h-DFh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;E0h-E7h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;E8h-EFh
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, // ;F0h-F7h
|
||||
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F // ;F8h-FFh
|
||||
];
|
||||
|
||||
private static readonly int[][] s_tables =
|
||||
[
|
||||
s_tableDirect,
|
||||
s_tableCb,
|
||||
s_tableEd,
|
||||
s_tableDdFd,
|
||||
s_tableDdCbFdCb,
|
||||
];
|
||||
|
||||
#endregion Tables
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// CpuTrace — ring buffer storing last N executed instructions with register snapshots
|
||||
// Used for crash/corruption analysis in the WC emulator
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace InflateDebugger
|
||||
{
|
||||
/// <summary>Snapshot of CPU state at one instruction</summary>
|
||||
public struct TraceEntry
|
||||
{
|
||||
public long Tact;
|
||||
public ushort PC;
|
||||
public byte Opcode; // first byte of the instruction
|
||||
public ushort AF, BC, DE, HL;
|
||||
public ushort AF2, BC2, DE2, HL2; // shadow regs
|
||||
public ushort IX, IY, SP;
|
||||
public byte Win0, Win1, Win2, Win3; // page window mapping
|
||||
|
||||
// Optional: what was written (0xFFFF = no write)
|
||||
public ushort WriteAddr;
|
||||
public byte WriteVal;
|
||||
|
||||
// WC API trap info (0xFF = not a WC call)
|
||||
public byte WcFuncNum;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat("[{0,10}] PC={1:X4} op={2:X2}", Tact, PC, Opcode);
|
||||
sb.AppendFormat(" AF={0:X4} BC={1:X4} DE={2:X4} HL={3:X4}", AF, BC, DE, HL);
|
||||
sb.AppendFormat(" IX={0:X4} SP={1:X4}", IX, SP);
|
||||
sb.AppendFormat(" W[{0:X2},{1:X2},{2:X2},{3:X2}]", Win0, Win1, Win2, Win3);
|
||||
if (WcFuncNum != 0xFF)
|
||||
sb.AppendFormat(" WC_CALL=0x{0:X2}", WcFuncNum);
|
||||
if (WriteAddr != 0xFFFF)
|
||||
sb.AppendFormat(" WR[{0:X4}]={1:X2}", WriteAddr, WriteVal);
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Fixed-size ring buffer of CPU trace entries</summary>
|
||||
public class CpuTrace
|
||||
{
|
||||
private readonly TraceEntry[] _buf;
|
||||
private readonly int _capacity;
|
||||
private int _head; // next write position
|
||||
private int _count; // total entries stored (capped at capacity)
|
||||
|
||||
public CpuTrace(int capacity = 200)
|
||||
{
|
||||
_capacity = capacity;
|
||||
_buf = new TraceEntry[capacity];
|
||||
_head = 0;
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
public int Count => _count;
|
||||
public int Capacity => _capacity;
|
||||
|
||||
/// <summary>Record a new trace entry (overwrites oldest when full)</summary>
|
||||
public void Record(ref TraceEntry entry)
|
||||
{
|
||||
_buf[_head] = entry;
|
||||
_head = (_head + 1) % _capacity;
|
||||
if (_count < _capacity) _count++;
|
||||
}
|
||||
|
||||
/// <summary>Get entry by age: 0 = most recent, 1 = one before, etc.</summary>
|
||||
public TraceEntry Get(int age)
|
||||
{
|
||||
if (age < 0 || age >= _count)
|
||||
throw new IndexOutOfRangeException($"age={age}, count={_count}");
|
||||
int idx = (_head - 1 - age + _capacity * 2) % _capacity;
|
||||
return _buf[idx];
|
||||
}
|
||||
|
||||
/// <summary>Dump the last N entries (most recent first) to a string</summary>
|
||||
public string Dump(int maxEntries = 0)
|
||||
{
|
||||
if (maxEntries <= 0) maxEntries = _count;
|
||||
int n = Math.Min(maxEntries, _count);
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"=== CPU Trace (last {n} of {_count}) ===");
|
||||
// Print oldest-first for readability
|
||||
for (int i = n - 1; i >= 0; i--)
|
||||
sb.AppendLine(Get(i).ToString());
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>Dump trace to file</summary>
|
||||
public void DumpToFile(string path, int maxEntries = 0)
|
||||
{
|
||||
System.IO.File.WriteAllText(path, Dump(maxEntries));
|
||||
}
|
||||
|
||||
/// <summary>Clear all entries</summary>
|
||||
public void Clear()
|
||||
{
|
||||
_head = 0;
|
||||
_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RootNamespace>InflateDebugger</RootNamespace>
|
||||
<Nullable>disable</Nullable>
|
||||
<NoWarn>CS8625;CS8618;CS8602;CS8604;CS8600;CS8601</NoWarn>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,674 @@
|
||||
// InflateDebugger — Z80 emulator for TSConfig inflate + WC plugin debugging
|
||||
//
|
||||
// MODE 1 — inflate (default):
|
||||
// Loads VGZ file into emulated TSConfig memory pages (as tape loader would),
|
||||
// loads inflate.bin into Win3, sets up registers as inflate_call.s would,
|
||||
// and runs the Z80 CPU with debug port monitoring and watchdog.
|
||||
//
|
||||
// Usage: dotnet run [--vgz <path>] [--ref <path>] [--inflate <path>]
|
||||
// [--dump-on-hang] [--max-cycles <N>] [--watchdog <N>]
|
||||
//
|
||||
// MODE 2 — plugin:
|
||||
// Loads a WMF plugin binary, emulates WC kernel API (P8000 dispatch),
|
||||
// emulates disk I/O from test_data directory, runs the full plugin lifecycle
|
||||
// with CPU trace, page protection, and WC call logging.
|
||||
//
|
||||
// Usage: dotnet run --mode plugin --wmf <path> [--file <vgz>]
|
||||
// [--max-cycles <N>] [--verbose]
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ZXMAK2.Engine.Cpu;
|
||||
using ZXMAK2.Engine.Cpu.Processor;
|
||||
|
||||
namespace InflateDebugger
|
||||
{
|
||||
class Program
|
||||
{
|
||||
// ── TSConfig page assignments (matching real WC plugin) ──
|
||||
const byte SRC_START_PAGE = 0xA1; // TAP pages for VGZ source
|
||||
const byte DST_START_PAGE = 0x20; // megabuffer for decompressed output
|
||||
const byte INFLATE_PAGE = 0x6C; // inflate code page (base 0x67 + 5)
|
||||
const byte PAGE_DECODE = 0xEA; // decode working buffer page
|
||||
const byte WC_BASE_PAGE = 0x67; // WC plugin base page
|
||||
|
||||
// ── Ports ──
|
||||
const ushort DBG_STATUS = 0xFFAF;
|
||||
const ushort DBG_SRC = 0xFEAF;
|
||||
const ushort DBG_DST = 0xFDAF;
|
||||
|
||||
// ── Defaults ──
|
||||
const long DEFAULT_MAX_CYCLES = 500_000_000; // 500M cycles max
|
||||
const long DEFAULT_WATCHDOG = 200_000; // 200K cycles without dbg port = hang
|
||||
|
||||
// ── State ──
|
||||
static TsMemory mem = new();
|
||||
static CpuUnit cpu = new();
|
||||
|
||||
static long lastDbgCycle = 0;
|
||||
static long watchdogLimit = DEFAULT_WATCHDOG;
|
||||
static long maxCycles = DEFAULT_MAX_CYCLES;
|
||||
static bool dumpOnHang = true;
|
||||
static bool finished = false;
|
||||
static byte lastDbgStatus = 0;
|
||||
static byte lastDbgSrc = 0;
|
||||
static byte lastDbgDst = 0;
|
||||
static int srcPagesLoaded = 0;
|
||||
static string? refPath = null;
|
||||
static string dumpDir = "";
|
||||
|
||||
// Track which pages were written as destination
|
||||
static HashSet<int> dstPagesWritten = new();
|
||||
|
||||
static int Main(string[] args)
|
||||
{
|
||||
string basePath = FindProjectRoot();
|
||||
|
||||
// ── Detect mode ──
|
||||
string mode = "inflate"; // default
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
if (args[i] == "--mode" && i + 1 < args.Length)
|
||||
{
|
||||
mode = args[++i].ToLowerInvariant();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return mode switch
|
||||
{
|
||||
"plugin" => RunPluginMode(args, basePath),
|
||||
_ => RunInflateMode(args, basePath)
|
||||
};
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Plugin mode — runs full WMF plugin with WC API emulation
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
static int RunPluginMode(string[] args, string basePath)
|
||||
{
|
||||
string? wmfPath = null;
|
||||
string? testFile = null;
|
||||
string? diskRoot = Path.Combine(basePath, "Debuger", "test_data");
|
||||
long maxCyclesP = 2_000_000_000;
|
||||
bool verbose = true;
|
||||
bool verbosePages = false;
|
||||
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
switch (args[i])
|
||||
{
|
||||
case "--wmf" when i + 1 < args.Length:
|
||||
wmfPath = args[++i]; break;
|
||||
case "--file" when i + 1 < args.Length:
|
||||
testFile = args[++i]; break;
|
||||
case "--disk-root" when i + 1 < args.Length:
|
||||
diskRoot = args[++i]; break;
|
||||
case "--max-cycles" when i + 1 < args.Length:
|
||||
maxCyclesP = long.Parse(args[++i]); break;
|
||||
case "--verbose":
|
||||
verbose = true; break;
|
||||
case "--quiet":
|
||||
verbose = false; break;
|
||||
case "--verbose-pages":
|
||||
verbosePages = true; break;
|
||||
}
|
||||
}
|
||||
|
||||
// Default WMF: look in DiskRef/WC/VGMPLAY.WMF or variants/
|
||||
if (wmfPath == null)
|
||||
{
|
||||
wmfPath = Path.Combine(basePath, @"DiskRef\WC\VGMPLAY.WMF");
|
||||
if (!File.Exists(wmfPath))
|
||||
wmfPath = Path.Combine(basePath, @"variants\VGMPLAY_v1_b8.WMF");
|
||||
}
|
||||
|
||||
// Default test file: first .vgz in test_data
|
||||
if (testFile == null && Directory.Exists(diskRoot!))
|
||||
{
|
||||
var firstVgz = Directory.GetFiles(diskRoot!, "*.vgz")
|
||||
.OrderBy(f => f).FirstOrDefault();
|
||||
if (firstVgz != null)
|
||||
testFile = Path.GetFileName(firstVgz);
|
||||
}
|
||||
|
||||
var runner = new PluginRunner
|
||||
{
|
||||
MaxCycles = maxCyclesP,
|
||||
VerboseWcCalls = verbose,
|
||||
VerbosePageSwitch = verbosePages,
|
||||
DumpOnExit = true,
|
||||
DumpDir = Path.Combine(basePath, @"Debuger\dumps"),
|
||||
DiskRootPath = diskRoot!,
|
||||
TestFileName = testFile
|
||||
};
|
||||
|
||||
return runner.Run(wmfPath!);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Inflate mode — original inflate test harness
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
static int RunInflateMode(string[] args, string basePath)
|
||||
{
|
||||
string vgzPath = Path.Combine(basePath,
|
||||
@"DiskRef\Music\OPL2\Arcade\_\Act-Fancer_Cybernetick_Hyper_Weapon_(Arcade)\02 Sci-Vax (BGM 1).vgz");
|
||||
string inflatePath = Path.Combine(basePath, @"src_sdcc\build\inflate.bin");
|
||||
refPath = Path.Combine(basePath, @"Debuger\ref.vgm");
|
||||
dumpDir = Path.Combine(basePath, @"Debuger\dumps");
|
||||
|
||||
// Parse args
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
switch (args[i])
|
||||
{
|
||||
case "--vgz" when i + 1 < args.Length: vgzPath = args[++i]; break;
|
||||
case "--ref" when i + 1 < args.Length: refPath = args[++i]; break;
|
||||
case "--inflate" when i + 1 < args.Length: inflatePath = args[++i]; break;
|
||||
case "--max-cycles" when i + 1 < args.Length: maxCycles = long.Parse(args[++i]); break;
|
||||
case "--watchdog" when i + 1 < args.Length: watchdogLimit = long.Parse(args[++i]); break;
|
||||
case "--dump-on-hang": dumpOnHang = true; break;
|
||||
case "--no-dump": dumpOnHang = false; break;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("═══════════════════════════════════════════════════");
|
||||
Console.WriteLine(" TSConfig Inflate Debugger");
|
||||
Console.WriteLine("═══════════════════════════════════════════════════");
|
||||
|
||||
// ─── 1. Load VGZ file ───
|
||||
if (!File.Exists(vgzPath))
|
||||
{
|
||||
Console.Error.WriteLine($"VGZ file not found: {vgzPath}");
|
||||
return 1;
|
||||
}
|
||||
byte[] vgzData = File.ReadAllBytes(vgzPath);
|
||||
Console.WriteLine($"VGZ file: {vgzPath}");
|
||||
Console.WriteLine($"VGZ size: {vgzData.Length} bytes");
|
||||
|
||||
// ─── 2. Load inflate.bin ───
|
||||
if (!File.Exists(inflatePath))
|
||||
{
|
||||
Console.Error.WriteLine($"inflate.bin not found: {inflatePath}");
|
||||
return 1;
|
||||
}
|
||||
byte[] inflateData = File.ReadAllBytes(inflatePath);
|
||||
Console.WriteLine($"inflate.bin: {inflateData.Length} bytes");
|
||||
|
||||
// ─── 3. Set up TSConfig memory ───
|
||||
SetupMemory(vgzData, inflateData);
|
||||
|
||||
// ─── 4. Set up CPU ───
|
||||
SetupCpu();
|
||||
|
||||
// ─── 5. Run ───
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("─── Execution Start ─────────────────────────────");
|
||||
Console.WriteLine();
|
||||
|
||||
int exitCode = RunCpu();
|
||||
|
||||
// ─── 6. Post-run analysis ───
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("─── Post-Run Analysis ───────────────────────────");
|
||||
AnalyzeResults();
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
static string FindProjectRoot()
|
||||
{
|
||||
// Walk up from current dir or exe dir looking for Debuger folder
|
||||
string? dir = AppDomain.CurrentDomain.BaseDirectory;
|
||||
for (int i = 0; i < 10 && dir != null; i++)
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(dir, "Debuger")) &&
|
||||
Directory.Exists(Path.Combine(dir, "src_sdcc")))
|
||||
return dir;
|
||||
dir = Path.GetDirectoryName(dir);
|
||||
}
|
||||
// Try current directory
|
||||
dir = Environment.CurrentDirectory;
|
||||
for (int i = 0; i < 10 && dir != null; i++)
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(dir, "Debuger")) &&
|
||||
Directory.Exists(Path.Combine(dir, "src_sdcc")))
|
||||
return dir;
|
||||
dir = Path.GetDirectoryName(dir);
|
||||
}
|
||||
return @"D:\zx_projects\OplPlug";
|
||||
}
|
||||
|
||||
static void SetupMemory(byte[] vgzData, byte[] inflateData)
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("─── Memory Setup ────────────────────────────────");
|
||||
|
||||
// Load VGZ into TAP pages (0xA1+) — as WC tape loader would
|
||||
srcPagesLoaded = mem.LoadToPages(vgzData, SRC_START_PAGE);
|
||||
Console.WriteLine($"VGZ loaded to pages 0x{SRC_START_PAGE:X2}–0x{SRC_START_PAGE + srcPagesLoaded - 1:X2} ({srcPagesLoaded} pages)");
|
||||
|
||||
// Load inflate.bin into inflate page (0x6C)
|
||||
// inflate.bin is padded to fill code + RAM tables within one 16KB page
|
||||
if (inflateData.Length > TsMemory.PageSize)
|
||||
{
|
||||
Console.WriteLine($"WARNING: inflate.bin ({inflateData.Length}) > 16KB, truncating");
|
||||
}
|
||||
Array.Copy(inflateData, 0, mem.GetPage(INFLATE_PAGE), 0,
|
||||
Math.Min(inflateData.Length, TsMemory.PageSize));
|
||||
Console.WriteLine($"inflate.bin loaded to page 0x{INFLATE_PAGE:X2}");
|
||||
|
||||
// Set up initial window mapping:
|
||||
// Win0 = source page 0xA1 (set by inflate entry code)
|
||||
// Win1 = don't care yet (inflate sets it)
|
||||
// Win2 = WC base page 0x67 (current plugin page — inflate will remap to PAGE_DECODE)
|
||||
// Win3 = inflate page 0x6C (set by inflate_call.s before JP 0xC000)
|
||||
mem.SetWindow(0, SRC_START_PAGE); // Will be set by inflate entry
|
||||
mem.SetWindow(1, 0x00); // Don't care — inflate sets Win1
|
||||
mem.SetWindow(2, WC_BASE_PAGE); // Current Win2 = plugin base
|
||||
mem.SetWindow(3, INFLATE_PAGE); // inflate code
|
||||
|
||||
// Set up WC system variables at 0x6000–0x6003
|
||||
// These are in the page mapped to Win1 at offset 0x2000
|
||||
// Actually in the real system, Win1 maps some WC page.
|
||||
// We need 0x6000–0x6003 readable. Let's put a known page in Win1
|
||||
// that has these values. In WC, Win1 = page at sys var area.
|
||||
// For simplicity: map Win1 to a scratch page, write sys vars there
|
||||
byte sysVarPage = 0xFE; // use a spare page for sys vars
|
||||
mem.SetWindow(1, sysVarPage);
|
||||
// 0x6000 = offset 0x2000 in Win1
|
||||
// Write the page values as WC would have them:
|
||||
mem.GetPage(sysVarPage)[0x2000] = SRC_START_PAGE; // 0x6000 = Win0 page
|
||||
mem.GetPage(sysVarPage)[0x2001] = sysVarPage; // 0x6001 = Win1 page
|
||||
mem.GetPage(sysVarPage)[0x2002] = WC_BASE_PAGE; // 0x6002 = Win2 page (plugin base)
|
||||
mem.GetPage(sysVarPage)[0x2003] = INFLATE_PAGE; // 0x6003 = Win3 page
|
||||
|
||||
Console.WriteLine($"Windows: W0=0x{SRC_START_PAGE:X2} W1=0x{sysVarPage:X2} W2=0x{WC_BASE_PAGE:X2} W3=0x{INFLATE_PAGE:X2}");
|
||||
Console.WriteLine($"Sys vars @0x6000: [{SRC_START_PAGE:X2} {sysVarPage:X2} {WC_BASE_PAGE:X2} {INFLATE_PAGE:X2}]");
|
||||
|
||||
// Display first bytes of inflate at 0xC000
|
||||
Console.Write("inflate @0xC000: ");
|
||||
for (int i = 0; i < 16; i++)
|
||||
Console.Write($"{mem.Read((ushort)(0xC000 + i)):X2} ");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
static void SetupCpu()
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("─── CPU Setup ───────────────────────────────────");
|
||||
|
||||
// Wire CPU bus to TSConfig memory
|
||||
cpu.RDMEM = (addr) => mem.Read(addr);
|
||||
cpu.RDMEM_M1 = (addr) => mem.Read(addr);
|
||||
cpu.WRMEM = (addr, val) => mem.Write(addr, val);
|
||||
cpu.RDPORT = (port) => 0xFF; // unused reads return 0xFF
|
||||
cpu.WRPORT = HandlePortWrite;
|
||||
cpu.RDNOMREQ = (addr) => { };
|
||||
cpu.WRNOMREQ = (addr) => { };
|
||||
cpu.RESET = () => { };
|
||||
cpu.NMIACK_M1 = () => { };
|
||||
cpu.INTACK_M1 = () => { };
|
||||
cpu.SCANSIG = () => { };
|
||||
|
||||
// No interrupts during inflate
|
||||
cpu.IFF1 = false;
|
||||
cpu.IFF2 = false;
|
||||
cpu.INT = false;
|
||||
cpu.NMI = false;
|
||||
cpu.RST = false;
|
||||
cpu.IM = 1;
|
||||
|
||||
// Set up registers as inflate_call.s would before JP 0xC000:
|
||||
// A = src start page (0xA1)
|
||||
// E = dst start page (0x20)
|
||||
// D = saved Win 2 page (0x67 — the plugin base page)
|
||||
// SP = 0xFFF0
|
||||
// Return address pushed on stack
|
||||
cpu.regs.A = SRC_START_PAGE; // 0xA1
|
||||
cpu.regs.E = DST_START_PAGE; // 0x20
|
||||
cpu.regs.D = WC_BASE_PAGE; // 0x67 — saved pg2
|
||||
cpu.regs.SP = 0xFFF0;
|
||||
cpu.regs.PC = 0xC000;
|
||||
|
||||
// Push a sentinel return address (0x0000) so RET goes to addr 0,
|
||||
// which we detect as "inflate returned"
|
||||
cpu.regs.SP -= 2;
|
||||
mem.Write(cpu.regs.SP, 0x00); // low byte
|
||||
mem.Write((ushort)(cpu.regs.SP + 1), 0x00); // high byte
|
||||
|
||||
cpu.Tact = 0;
|
||||
cpu.FX = CpuModeIndex.None;
|
||||
cpu.XFX = CpuModeEx.None;
|
||||
cpu.HALTED = false;
|
||||
|
||||
Console.WriteLine($"PC=0x{cpu.regs.PC:X4} SP=0x{cpu.regs.SP:X4}");
|
||||
Console.WriteLine($"A=0x{cpu.regs.A:X2} D=0x{cpu.regs.D:X2} E=0x{cpu.regs.E:X2}");
|
||||
Console.WriteLine($"Return sentinel at [0x{cpu.regs.SP:X4}] = 0x0000");
|
||||
}
|
||||
|
||||
static void HandlePortWrite(ushort port, byte value)
|
||||
{
|
||||
// TSConfig page ports
|
||||
if (mem.HandlePort(port, value))
|
||||
{
|
||||
// Log page changes (optional verbose)
|
||||
return;
|
||||
}
|
||||
|
||||
// Debug ports
|
||||
switch (port)
|
||||
{
|
||||
case DBG_STATUS:
|
||||
lastDbgStatus = value;
|
||||
lastDbgCycle = cpu.Tact;
|
||||
PrintDbgStatus(value);
|
||||
break;
|
||||
|
||||
case DBG_SRC:
|
||||
lastDbgSrc = value;
|
||||
lastDbgCycle = cpu.Tact;
|
||||
break;
|
||||
|
||||
case DBG_DST:
|
||||
lastDbgDst = value;
|
||||
lastDbgCycle = cpu.Tact;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly Dictionary<byte, string> StatusNames = new()
|
||||
{
|
||||
[0x0E] = "inflate entry",
|
||||
[0x0F] = "stores done",
|
||||
[0x10] = "Win0+Win2 remapped",
|
||||
[0x11] = "Win2→PAGE_DECODE",
|
||||
[0x12] = "gzip header OK",
|
||||
[0x14] = "blocktype0 (stored)",
|
||||
[0x15] = "blocktype1 (static)",
|
||||
[0x16] = "blocktype2 (dynamic)",
|
||||
[0x17] = "Huffman tables built → decode loop",
|
||||
[0x20] = "page flush (bt0)",
|
||||
[0x21] = "page flush (decode)",
|
||||
[0x30] = "EXIT OK",
|
||||
[0x31] = "FAIL",
|
||||
[0xA6] = "bt2: HLIT/HDIST/HCLEN read",
|
||||
[0xA7] = "bt2: makeHuffmanTable(19) done",
|
||||
[0xA8] = "bt2: decodeProtoHuffman done",
|
||||
[0xA9] = "bt2: code_length_orders read → mht",
|
||||
[0xB1] = "mht: count loop done",
|
||||
[0xB2] = "mht: start codes done",
|
||||
[0xB3] = "mht: code assignment done",
|
||||
[0xBF] = "decodeProtoHuffman entry",
|
||||
[0xC0] = "dph: loop iteration",
|
||||
[0xC1] = "dph: leaf found",
|
||||
[0xC2] = "dph: special code",
|
||||
};
|
||||
|
||||
static void PrintDbgStatus(byte code)
|
||||
{
|
||||
string name = StatusNames.TryGetValue(code, out var n) ? n : "???";
|
||||
string extra = "";
|
||||
// For codes that also emit SRC/DST, show them
|
||||
if (code == 0xC1 || code == 0xC2)
|
||||
extra = $" FEAF=0x{lastDbgSrc:X2}";
|
||||
else if (code == 0x10 || code == 0x20 || code == 0x21 || code == 0x30)
|
||||
extra = $" src_pg=0x{lastDbgSrc:X2} dst_pg=0x{lastDbgDst:X2}";
|
||||
|
||||
Console.ForegroundColor = code switch
|
||||
{
|
||||
0x30 => ConsoleColor.Green,
|
||||
0x31 => ConsoleColor.Red,
|
||||
>= 0xB0 and <= 0xBF => ConsoleColor.DarkGray, // mht internals
|
||||
>= 0xC0 and <= 0xCF => ConsoleColor.DarkCyan, // dph internals
|
||||
_ => ConsoleColor.Yellow
|
||||
};
|
||||
Console.WriteLine($" [{cpu.Tact,10}] DBG 0x{code:X2}: {name}{extra}");
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
static int RunCpu()
|
||||
{
|
||||
long cycleCount = 0;
|
||||
|
||||
while (!finished)
|
||||
{
|
||||
// ── Watchdog: execution outside Win3 ──
|
||||
ushort pc = cpu.regs.PC;
|
||||
if (pc < 0xC000)
|
||||
{
|
||||
if (pc == 0x0000)
|
||||
{
|
||||
// Sentinel return address — inflate returned!
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
Console.WriteLine($"\n ✓ Inflate returned at cycle {cpu.Tact}, A=0x{cpu.regs.A:X2}");
|
||||
Console.ResetColor();
|
||||
if (cpu.regs.A == 0)
|
||||
Console.WriteLine(" Result: SUCCESS (A=0, no carry)");
|
||||
else
|
||||
Console.WriteLine($" Result: FAIL (A=0x{cpu.regs.A:X2})");
|
||||
|
||||
finished = true;
|
||||
DumpState("return");
|
||||
return cpu.regs.A == 0 ? 0 : 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"\n ✗ RUNAWAY: PC=0x{pc:X4} at cycle {cpu.Tact}");
|
||||
Console.WriteLine($" Last DBG: 0x{lastDbgStatus:X2} at cycle {lastDbgCycle}");
|
||||
Console.ResetColor();
|
||||
DumpState("runaway");
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Watchdog: cycles without debug port activity ──
|
||||
if (cpu.Tact - lastDbgCycle > watchdogLimit && lastDbgCycle > 0)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"\n ✗ WATCHDOG: {cpu.Tact - lastDbgCycle} cycles since last debug output");
|
||||
Console.WriteLine($" Last DBG: 0x{lastDbgStatus:X2} at cycle {lastDbgCycle}");
|
||||
Console.WriteLine($" PC=0x{cpu.regs.PC:X4} SP=0x{cpu.regs.SP:X4}");
|
||||
Console.ResetColor();
|
||||
PrintCpuState();
|
||||
if (dumpOnHang)
|
||||
DumpState("watchdog");
|
||||
return 4;
|
||||
}
|
||||
|
||||
// ── Watchdog: max total cycles ──
|
||||
if (cpu.Tact > maxCycles)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"\n ✗ MAX CYCLES ({maxCycles}) exceeded");
|
||||
Console.ResetColor();
|
||||
DumpState("maxcycles");
|
||||
return 5;
|
||||
}
|
||||
|
||||
// ── HALT detection ──
|
||||
if (cpu.HALTED)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"\n ✗ HALT at PC=0x{cpu.regs.PC:X4}, cycle {cpu.Tact}");
|
||||
Console.ResetColor();
|
||||
DumpState("halt");
|
||||
return 6;
|
||||
}
|
||||
|
||||
// ── Execute one instruction ──
|
||||
cpu.ExecCycle();
|
||||
cycleCount++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void PrintCpuState()
|
||||
{
|
||||
var r = cpu.regs;
|
||||
Console.WriteLine($" AF={r.AF:X4} BC={r.BC:X4} DE={r.DE:X4} HL={r.HL:X4}");
|
||||
Console.WriteLine($" AF'={r._AF:X4} BC'={r._BC:X4} DE'={r._DE:X4} HL'={r._HL:X4}");
|
||||
Console.WriteLine($" IX={r.IX:X4} IY={r.IY:X4} SP={r.SP:X4} PC={r.PC:X4}");
|
||||
Console.WriteLine($" Win0=pg{mem.GetWindow(0):X2} Win1=pg{mem.GetWindow(1):X2} Win2=pg{mem.GetWindow(2):X2} Win3=pg{mem.GetWindow(3):X2}");
|
||||
|
||||
// Disassemble a few instructions around PC
|
||||
Console.Write(" Code @PC: ");
|
||||
for (int i = 0; i < 8; i++)
|
||||
Console.Write($"{mem.Read((ushort)(r.PC + i)):X2} ");
|
||||
Console.WriteLine();
|
||||
|
||||
// Stack dump
|
||||
Console.Write(" Stack: ");
|
||||
for (int i = 0; i < 8; i++)
|
||||
Console.Write($"{mem.Read((ushort)(r.SP + i)):X2} ");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
static void DumpState(string reason)
|
||||
{
|
||||
if (!dumpOnHang) return;
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(dumpDir);
|
||||
|
||||
// Dump inflate page (Win3)
|
||||
string inflFile = Path.Combine(dumpDir, $"{reason}_inflate_page_{INFLATE_PAGE:X2}.bin");
|
||||
File.WriteAllBytes(inflFile, mem.GetPage(INFLATE_PAGE));
|
||||
Console.WriteLine($" Dumped: {inflFile}");
|
||||
|
||||
// Dump decode buffer page
|
||||
string decFile = Path.Combine(dumpDir, $"{reason}_decode_page_{PAGE_DECODE:X2}.bin");
|
||||
File.WriteAllBytes(decFile, mem.GetPage(PAGE_DECODE));
|
||||
Console.WriteLine($" Dumped: {decFile}");
|
||||
|
||||
// Dump destination pages
|
||||
for (int p = DST_START_PAGE; p < DST_START_PAGE + 16; p++)
|
||||
{
|
||||
byte[] pg = mem.GetPage(p);
|
||||
bool allZero = true;
|
||||
for (int i = 0; i < pg.Length && allZero; i++)
|
||||
if (pg[i] != 0) allZero = false;
|
||||
if (!allZero)
|
||||
{
|
||||
string dstFile = Path.Combine(dumpDir, $"{reason}_dst_page_{p:X2}.bin");
|
||||
File.WriteAllBytes(dstFile, pg);
|
||||
}
|
||||
}
|
||||
Console.WriteLine($" Dumped destination pages (0x{DST_START_PAGE:X2}+)");
|
||||
|
||||
// Dump Win3 area (the whole mapped page, including runtime modifications)
|
||||
string win3File = Path.Combine(dumpDir, $"{reason}_win3_mapped.bin");
|
||||
File.WriteAllBytes(win3File, mem.GetPage(mem.GetWindow(3)));
|
||||
Console.WriteLine($" Dumped: {win3File} (Win3=page 0x{mem.GetWindow(3):X2})");
|
||||
|
||||
// CPU state dump
|
||||
string stateFile = Path.Combine(dumpDir, $"{reason}_cpu_state.txt");
|
||||
using (var sw = new StreamWriter(stateFile))
|
||||
{
|
||||
var r = cpu.regs;
|
||||
sw.WriteLine($"Reason: {reason}");
|
||||
sw.WriteLine($"Tact: {cpu.Tact}");
|
||||
sw.WriteLine($"AF={r.AF:X4} BC={r.BC:X4} DE={r.DE:X4} HL={r.HL:X4}");
|
||||
sw.WriteLine($"AF'={r._AF:X4} BC'={r._BC:X4} DE'={r._DE:X4} HL'={r._HL:X4}");
|
||||
sw.WriteLine($"IX={r.IX:X4} IY={r.IY:X4} SP={r.SP:X4} PC={r.PC:X4}");
|
||||
sw.WriteLine($"Win0=0x{mem.GetWindow(0):X2} Win1=0x{mem.GetWindow(1):X2} Win2=0x{mem.GetWindow(2):X2} Win3=0x{mem.GetWindow(3):X2}");
|
||||
sw.WriteLine($"Last DBG status=0x{lastDbgStatus:X2} src=0x{lastDbgSrc:X2} dst=0x{lastDbgDst:X2}");
|
||||
sw.WriteLine($"Last DBG at cycle {lastDbgCycle}");
|
||||
sw.WriteLine();
|
||||
|
||||
// Disassemble around PC
|
||||
sw.Write("Code @PC: ");
|
||||
for (int i = 0; i < 32; i++)
|
||||
sw.Write($"{mem.Read((ushort)(r.PC + i)):X2} ");
|
||||
sw.WriteLine();
|
||||
|
||||
sw.Write("Stack: ");
|
||||
for (int i = 0; i < 16; i++)
|
||||
sw.Write($"{mem.Read((ushort)(r.SP + i)):X2} ");
|
||||
sw.WriteLine();
|
||||
}
|
||||
Console.WriteLine($" Dumped: {stateFile}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($" Dump error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
static void AnalyzeResults()
|
||||
{
|
||||
if (refPath == null || !File.Exists(refPath))
|
||||
{
|
||||
Console.WriteLine("No ref.vgm found, skipping comparison.");
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] refData = File.ReadAllBytes(refPath);
|
||||
Console.WriteLine($"ref.vgm: {refData.Length} bytes");
|
||||
|
||||
// Collect decompressed output from destination pages
|
||||
int refPages = (refData.Length + TsMemory.PageSize - 1) / TsMemory.PageSize;
|
||||
byte[] output = mem.ReadPages(DST_START_PAGE, refPages);
|
||||
|
||||
// Compare
|
||||
int firstDiff = -1;
|
||||
int matchBytes = 0;
|
||||
int compareLen = Math.Min(refData.Length, output.Length);
|
||||
|
||||
for (int i = 0; i < compareLen; i++)
|
||||
{
|
||||
if (output[i] == refData[i])
|
||||
matchBytes++;
|
||||
else if (firstDiff < 0)
|
||||
firstDiff = i;
|
||||
}
|
||||
|
||||
if (firstDiff < 0 && matchBytes == refData.Length)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
Console.WriteLine($" ✓ PERFECT MATCH: {matchBytes} bytes identical to ref.vgm");
|
||||
Console.ResetColor();
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($" ✗ MISMATCH at offset 0x{firstDiff:X}");
|
||||
Console.WriteLine($" Matched: {matchBytes}/{refData.Length} bytes ({100.0 * matchBytes / refData.Length:F1}%)");
|
||||
Console.ResetColor();
|
||||
|
||||
if (firstDiff >= 0)
|
||||
{
|
||||
Console.Write(" ref: ");
|
||||
for (int i = Math.Max(0, firstDiff - 4); i < Math.Min(refData.Length, firstDiff + 12); i++)
|
||||
Console.Write(i == firstDiff ? $"[{refData[i]:X2}]" : $" {refData[i]:X2} ");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.Write(" got: ");
|
||||
for (int i = Math.Max(0, firstDiff - 4); i < Math.Min(output.Length, firstDiff + 12); i++)
|
||||
Console.Write(i == firstDiff ? $"[{output[i]:X2}]" : $" {output[i]:X2} ");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
// Save output for analysis
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(dumpDir);
|
||||
string outFile = Path.Combine(dumpDir, "output.vgm");
|
||||
byte[] trimmed = new byte[refData.Length];
|
||||
Array.Copy(output, trimmed, Math.Min(output.Length, trimmed.Length));
|
||||
File.WriteAllBytes(outFile, trimmed);
|
||||
Console.WriteLine($" Saved decompressed output: {outFile}");
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
// TsMemory — TSConfig 256-page × 16KB paged memory with page protection
|
||||
// Enhanced: write protection, dirty tracking, violation logging
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace InflateDebugger
|
||||
{
|
||||
public class TsMemory
|
||||
{
|
||||
public const int PageSize = 0x4000; // 16KB
|
||||
public const int TotalPages = 256; // 4 MB
|
||||
|
||||
// Physical memory: 256 pages × 16KB
|
||||
private readonly byte[][] _pages;
|
||||
|
||||
// Window → physical page mapping
|
||||
// Win0 = 0x0000–0x3FFF, Win1 = 0x4000–0x7FFF,
|
||||
// Win2 = 0x8000–0xBFFF, Win3 = 0xC000–0xFFFF
|
||||
private readonly byte[] _winPage = new byte[4];
|
||||
|
||||
// TSConfig page ports
|
||||
public const ushort PORT_W0 = 0x10AF;
|
||||
public const ushort PORT_W1 = 0x11AF;
|
||||
public const ushort PORT_W2 = 0x12AF;
|
||||
public const ushort PORT_W3 = 0x13AF;
|
||||
|
||||
// ── Page protection ──────────────────────────────────────────
|
||||
|
||||
/// <summary>Set of physical page numbers that are write-protected</summary>
|
||||
private readonly HashSet<int> _protectedPages = new();
|
||||
|
||||
/// <summary>Pages that have been written to (dirty tracking)</summary>
|
||||
private readonly HashSet<int> _dirtyPages = new();
|
||||
|
||||
/// <summary>Protection violation log</summary>
|
||||
public List<ProtectionViolation> Violations { get; } = new();
|
||||
|
||||
/// <summary>Max violations to log before stopping (0 = unlimited)</summary>
|
||||
public int MaxViolations { get; set; } = 100;
|
||||
|
||||
/// <summary>If true, throw on protection violation</summary>
|
||||
public bool ThrowOnViolation { get; set; } = false;
|
||||
|
||||
/// <summary>Callback invoked on every write. Args: z80addr, page, offset, value</summary>
|
||||
public Action<ushort, byte, int, byte> OnWrite { get; set; }
|
||||
|
||||
/// <summary>Callback invoked on protection violation</summary>
|
||||
public Action<ProtectionViolation> OnViolation { get; set; }
|
||||
|
||||
public TsMemory()
|
||||
{
|
||||
_pages = new byte[TotalPages][];
|
||||
for (int i = 0; i < TotalPages; i++)
|
||||
_pages[i] = new byte[PageSize];
|
||||
}
|
||||
|
||||
// ── Page protection API ──────────────────────────────────────
|
||||
|
||||
public void ProtectPage(int page) => _protectedPages.Add(page);
|
||||
public void UnprotectPage(int page) => _protectedPages.Remove(page);
|
||||
public void ProtectRange(int startPage, int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++) _protectedPages.Add(startPage + i);
|
||||
}
|
||||
public void UnprotectRange(int startPage, int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++) _protectedPages.Remove(startPage + i);
|
||||
}
|
||||
public bool IsProtected(int page) => _protectedPages.Contains(page);
|
||||
public void ClearProtection() => _protectedPages.Clear();
|
||||
|
||||
public IReadOnlyCollection<int> DirtyPages => _dirtyPages;
|
||||
public void ClearDirtyTracking() => _dirtyPages.Clear();
|
||||
|
||||
// ── Window management ────────────────────────────────────────
|
||||
|
||||
/// <summary>Set window mapping (0–3) to physical page</summary>
|
||||
public void SetWindow(int win, byte page)
|
||||
{
|
||||
_winPage[win] = page;
|
||||
}
|
||||
|
||||
public byte GetWindow(int win) => _winPage[win];
|
||||
|
||||
// ── Memory access ────────────────────────────────────────────
|
||||
|
||||
/// <summary>Read byte at 16-bit Z80 address through page windows</summary>
|
||||
public byte Read(ushort addr)
|
||||
{
|
||||
int win = addr >> 14; // 0–3
|
||||
int offset = addr & 0x3FFF;
|
||||
return _pages[_winPage[win]][offset];
|
||||
}
|
||||
|
||||
/// <summary>Write byte at 16-bit Z80 address (with protection check)</summary>
|
||||
public void Write(ushort addr, byte val)
|
||||
{
|
||||
int win = addr >> 14;
|
||||
int offset = addr & 0x3FFF;
|
||||
byte page = _winPage[win];
|
||||
|
||||
_dirtyPages.Add(page);
|
||||
OnWrite?.Invoke(addr, page, offset, val);
|
||||
|
||||
if (_protectedPages.Contains(page))
|
||||
{
|
||||
var v = new ProtectionViolation
|
||||
{
|
||||
Z80Addr = addr,
|
||||
PhysPage = page,
|
||||
Offset = offset,
|
||||
Value = val,
|
||||
OldValue = _pages[page][offset],
|
||||
Window = win
|
||||
};
|
||||
if (Violations.Count < MaxViolations || MaxViolations == 0)
|
||||
Violations.Add(v);
|
||||
OnViolation?.Invoke(v);
|
||||
if (ThrowOnViolation)
|
||||
throw new ProtectionViolationException(v);
|
||||
}
|
||||
|
||||
_pages[page][offset] = val;
|
||||
}
|
||||
|
||||
/// <summary>Direct write bypassing protection (for initial data loading)</summary>
|
||||
public void WriteUnprotected(ushort addr, byte val)
|
||||
{
|
||||
int win = addr >> 14;
|
||||
int offset = addr & 0x3FFF;
|
||||
_pages[_winPage[win]][offset] = val;
|
||||
}
|
||||
|
||||
/// <summary>Direct access to a physical page</summary>
|
||||
public byte[] GetPage(int page) => _pages[page];
|
||||
|
||||
/// <summary>Load data into physical pages starting at startPage</summary>
|
||||
public int LoadToPages(byte[] data, int startPage)
|
||||
{
|
||||
int pagesNeeded = (data.Length + PageSize - 1) / PageSize;
|
||||
for (int p = 0; p < pagesNeeded; p++)
|
||||
{
|
||||
int srcOff = p * PageSize;
|
||||
int len = Math.Min(PageSize, data.Length - srcOff);
|
||||
Array.Copy(data, srcOff, _pages[startPage + p], 0, len);
|
||||
}
|
||||
return pagesNeeded;
|
||||
}
|
||||
|
||||
/// <summary>Handle OUT to TSConfig page port</summary>
|
||||
public bool HandlePort(ushort port, byte value)
|
||||
{
|
||||
switch (port)
|
||||
{
|
||||
case PORT_W0: SetWindow(0, value); return true;
|
||||
case PORT_W1: SetWindow(1, value); return true;
|
||||
case PORT_W2: SetWindow(2, value); return true;
|
||||
case PORT_W3: SetWindow(3, value); return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Read all output pages (destination) into a flat array</summary>
|
||||
public byte[] ReadPages(int startPage, int count)
|
||||
{
|
||||
byte[] result = new byte[count * PageSize];
|
||||
for (int p = 0; p < count; p++)
|
||||
Array.Copy(_pages[startPage + p], 0, result, p * PageSize, PageSize);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>Update WC system variable area (0x6000–0x600E)</summary>
|
||||
public void UpdateSysVars()
|
||||
{
|
||||
byte sysPage = _winPage[1];
|
||||
_pages[sysPage][0x2000] = _winPage[0]; // 0x6000 = PG0
|
||||
_pages[sysPage][0x2001] = _winPage[1]; // 0x6001 = PG4
|
||||
_pages[sysPage][0x2002] = _winPage[2]; // 0x6002 = PG8
|
||||
_pages[sysPage][0x2003] = _winPage[3]; // 0x6003 = PGC
|
||||
}
|
||||
|
||||
/// <summary>Dump 16 bytes around an address for debugging</summary>
|
||||
public string HexDump(ushort addr, int before = 8, int after = 8)
|
||||
{
|
||||
int start = Math.Max(0, addr - before);
|
||||
int end = Math.Min(0xFFFF, addr + after);
|
||||
var sb = new System.Text.StringBuilder();
|
||||
for (int a = start; a <= end; a++)
|
||||
{
|
||||
if (a == addr) sb.Append('[');
|
||||
sb.AppendFormat("{0:X2}", Read((ushort)a));
|
||||
if (a == addr) sb.Append(']');
|
||||
else sb.Append(' ');
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
// ── Protection violation types ───────────────────────────────────
|
||||
|
||||
public struct ProtectionViolation
|
||||
{
|
||||
public ushort Z80Addr;
|
||||
public byte PhysPage;
|
||||
public int Offset;
|
||||
public byte Value;
|
||||
public byte OldValue;
|
||||
public int Window;
|
||||
|
||||
public override string ToString() =>
|
||||
$"PROTECT VIOLATION: [{Z80Addr:X4}] pg={PhysPage:X2} off={Offset:X4} " +
|
||||
$"old={OldValue:X2} new={Value:X2} win={Window}";
|
||||
}
|
||||
|
||||
public class ProtectionViolationException : Exception
|
||||
{
|
||||
public ProtectionViolation Violation { get; }
|
||||
public ProtectionViolationException(ProtectionViolation v)
|
||||
: base(v.ToString()) { Violation = v; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,779 @@
|
||||
// WcKernel — WC (Wild Commander) API emulator for the C# debugger
|
||||
// Traps CALL 0x6006 and emulates function dispatch based on A register
|
||||
//
|
||||
// Emulated functions (used by VGM player):
|
||||
// 0x00 = MNGC_PL — plugin page → Win3
|
||||
// 0x01 = PRWOW — draw window (stub)
|
||||
// 0x02 = RRESB — restore window (stub)
|
||||
// 0x05 = GADRW — get screen address
|
||||
// 0x0E = TURBOPL — CPU speed (stub)
|
||||
// 0x0F = GEDPL — restore display (stub)
|
||||
// 0x30 = LOAD512 — load sectors from file → memory
|
||||
// 0x41 = MNGCVPL — video page → Win3
|
||||
// 0x4E = MNG0_PL — plugin page → Win0
|
||||
// 0x4F = MNG8_PL — plugin page → Win2
|
||||
// + keyboard stubs, file stubs, etc.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using ZXMAK2.Engine.Cpu;
|
||||
using ZXMAK2.Engine.Cpu.Processor;
|
||||
|
||||
namespace InflateDebugger
|
||||
{
|
||||
public class WcKernel
|
||||
{
|
||||
// ── WC Constants ─────────────────────────────────────────────
|
||||
|
||||
public const ushort WC_ENTRY = 0x6006;
|
||||
|
||||
// Physical page bases
|
||||
public const byte PLGPG = 0x61; // plugin page base (page 0 = 0x61)
|
||||
public const byte TVBPG = 0x20; // megabuffer / video page base
|
||||
public const byte MTXPG = 0x00; // main text page
|
||||
|
||||
// System variable addresses (in Win1 = 0x4000–0x7FFF)
|
||||
public const ushort SYS_PG0 = 0x6000;
|
||||
public const ushort SYS_PG4 = 0x6001;
|
||||
public const ushort SYS_PG8 = 0x6002;
|
||||
public const ushort SYS_PGC = 0x6003;
|
||||
public const ushort SYS_ABT = 0x6004;
|
||||
public const ushort SYS_ENT = 0x6005;
|
||||
public const ushort SYS_TMN = 0x6009;
|
||||
public const ushort SYS_CNFV = 0x600D;
|
||||
public const ushort SYS_HEI = 0x600E;
|
||||
|
||||
// Function numbers (A register values for CALL 0x6006)
|
||||
public const byte FN_MNGC_PL = 0x00;
|
||||
public const byte FN_PRWOW = 0x01;
|
||||
public const byte FN_RRESB = 0x02;
|
||||
public const byte FN_PRSRW = 0x03;
|
||||
public const byte FN_TXTPR = 0x04;
|
||||
public const byte FN_GADRW = 0x05;
|
||||
public const byte FN_CURSOR = 0x06;
|
||||
public const byte FN_CURSER = 0x07;
|
||||
public const byte FN_YN = 0x08;
|
||||
public const byte FN_ISTR = 0x09;
|
||||
public const byte FN_NORK = 0x0A;
|
||||
public const byte FN_MEZZ = 0x0B;
|
||||
public const byte FN_SCRLWOW = 0x0C;
|
||||
public const byte FN_PRSRW_A = 0x0D;
|
||||
public const byte FN_TURBOPL = 0x0E;
|
||||
public const byte FN_GEDPL = 0x0F;
|
||||
|
||||
// Keyboard: 0x10–0x2F
|
||||
public const byte FN_KEY_SPACE = 0x10;
|
||||
public const byte FN_KEY_UP = 0x11;
|
||||
public const byte FN_KEY_DOWN = 0x12;
|
||||
public const byte FN_KEY_LEFT = 0x13;
|
||||
public const byte FN_KEY_RIGHT = 0x14;
|
||||
public const byte FN_KEY_TAB = 0x15;
|
||||
public const byte FN_KEY_ENTER = 0x16;
|
||||
public const byte FN_KEY_ESC = 0x17;
|
||||
public const byte FN_KEY_BSPC = 0x18;
|
||||
public const byte FN_KEY_PGUP = 0x19;
|
||||
public const byte FN_KEY_PGDN = 0x1A;
|
||||
public const byte FN_KEY_HOME = 0x1B;
|
||||
public const byte FN_KEY_END = 0x1C;
|
||||
public const byte FN_KBSCN = 0x1D;
|
||||
// F1–F10: 0x1E–0x27, ALT=0x28, SHIFT=0x29, CTRL=0x2A
|
||||
// DEL=0x2B, CAPS=0x2C, ANY=0x2D, WAITREL=0x2E, WAITANY=0x2F
|
||||
|
||||
public const byte FN_LOAD512 = 0x30;
|
||||
public const byte FN_SAVE512 = 0x31;
|
||||
public const byte FN_GIPAGPL = 0x32;
|
||||
public const byte FN_TENTRY = 0x33;
|
||||
public const byte FN_CHTOSEP = 0x34;
|
||||
public const byte FN_TMRKDFL = 0x35;
|
||||
public const byte FN_ADIR = 0x36;
|
||||
public const byte FN_STREAM = 0x37;
|
||||
public const byte FN_FINDNEXT = 0x38;
|
||||
public const byte FN_FENTRY = 0x39;
|
||||
public const byte FN_LOADNONE = 0x3A;
|
||||
public const byte FN_GFILE = 0x3B;
|
||||
public const byte FN_GDIR = 0x3C;
|
||||
public const byte FN_MKFILE = 0x3D;
|
||||
public const byte FN_MKDIR = 0x3E;
|
||||
public const byte FN_RENAME = 0x3F;
|
||||
public const byte FN_DELFL = 0x40;
|
||||
|
||||
public const byte FN_MNGCVPL = 0x41;
|
||||
public const byte FN_MNGV_PL = 0x42;
|
||||
public const byte FN_GVMOD = 0x43;
|
||||
public const byte FN_GYOFF = 0x44;
|
||||
public const byte FN_GXOFF = 0x45;
|
||||
public const byte FN_GVTM = 0x46;
|
||||
public const byte FN_GVTL = 0x47;
|
||||
public const byte FN_GVSGP = 0x48;
|
||||
public const byte FN_MNG0VPL = 0x49;
|
||||
public const byte FN_MNG8VPL = 0x4A;
|
||||
public const byte FN_DMAPL = 0x4B;
|
||||
public const byte FN_PRM_PL = 0x4C;
|
||||
public const byte FN_INT_PL = 0x4D;
|
||||
public const byte FN_MNG0_PL = 0x4E;
|
||||
public const byte FN_MNG8_PL = 0x4F;
|
||||
|
||||
public const byte FN_STRSET = 0x50;
|
||||
public const byte FN_LOAD256 = 0x51;
|
||||
public const byte FN_INT_PL_H = 0x52;
|
||||
public const byte FN_RES_JUMP = 0x53;
|
||||
public const byte FN_RES_CALL = 0x54;
|
||||
public const byte FN_KEY_INS = 0x55;
|
||||
|
||||
// ── State ─────────────────────────────────────────────────────
|
||||
|
||||
private readonly TsMemory _mem;
|
||||
private CpuUnit _cpu;
|
||||
|
||||
// Plugin page offset within the 64-page plugin area (typically 0)
|
||||
public byte PPGP { get; set; } = 0;
|
||||
|
||||
// Emulated file I/O state
|
||||
private byte[] _fileData; // loaded file data (whole VGM/VGZ)
|
||||
private int _filePos; // current file read position (byte offset)
|
||||
public uint FileSize => _fileData != null ? (uint)_fileData.Length : 0;
|
||||
|
||||
// Disk root directory (test_data path)
|
||||
public string DiskRootPath { get; set; }
|
||||
|
||||
// WC call log
|
||||
public List<WcCallRecord> CallLog { get; } = new();
|
||||
public bool LogCalls { get; set; } = true;
|
||||
public int MaxCallLog { get; set; } = 10000;
|
||||
|
||||
// Saved Win0 page (P8000 saves/restores it on each WC call)
|
||||
private byte _savedWin0Page;
|
||||
|
||||
// Screen height (for gadrw calculations)
|
||||
public byte ScreenHeight { get; set; } = 36; // 90×36 (TextMode=2)
|
||||
public byte ScreenWidth { get; set; } = 90;
|
||||
|
||||
// WC system page (mapped to Win1 permanently)
|
||||
public byte SysVarPage { get; set; } = 0x05;
|
||||
|
||||
// Callbacks for logging
|
||||
public Action<string> Log { get; set; }
|
||||
|
||||
// ── Constructor ───────────────────────────────────────────────
|
||||
|
||||
public WcKernel(TsMemory mem, CpuUnit cpu)
|
||||
{
|
||||
_mem = mem;
|
||||
_cpu = cpu;
|
||||
}
|
||||
|
||||
// ── File I/O emulation ────────────────────────────────────────
|
||||
|
||||
/// <summary>Load a file for emulated file I/O (simulates WC file open + stream)</summary>
|
||||
public void SetFileData(byte[] data)
|
||||
{
|
||||
_fileData = data;
|
||||
_filePos = 0;
|
||||
}
|
||||
|
||||
/// <summary>Load a file from disk root by name</summary>
|
||||
public bool OpenFile(string name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(DiskRootPath)) return false;
|
||||
string path = Path.Combine(DiskRootPath, name);
|
||||
if (!File.Exists(path)) return false;
|
||||
_fileData = File.ReadAllBytes(path);
|
||||
_filePos = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── Setup system variables ────────────────────────────────────
|
||||
|
||||
public void InitSysVars()
|
||||
{
|
||||
var pg = _mem.GetPage(SysVarPage);
|
||||
|
||||
// PG0–PGC (0x6000–0x6003 = offset 0x2000 in Win1)
|
||||
pg[0x2000] = _mem.GetWindow(0);
|
||||
pg[0x2001] = _mem.GetWindow(1);
|
||||
pg[0x2002] = _mem.GetWindow(2);
|
||||
pg[0x2003] = _mem.GetWindow(3);
|
||||
|
||||
// ABT / ENT = 0
|
||||
pg[0x2004] = 0;
|
||||
pg[0x2005] = 0;
|
||||
|
||||
// JP FUN instruction at 0x6006 (we trap this, but put real bytes for reads)
|
||||
pg[0x2006] = 0xC3; // JP
|
||||
pg[0x2007] = 0x00;
|
||||
pg[0x2008] = 0x00;
|
||||
|
||||
// TMN timer
|
||||
pg[0x2009] = 0;
|
||||
pg[0x200A] = 0;
|
||||
|
||||
// CNFv = 7 (VDAC2/FT812 — TSConfig default)
|
||||
pg[0x200D] = 7;
|
||||
|
||||
// HEI = screen height
|
||||
pg[0x200E] = ScreenHeight;
|
||||
}
|
||||
|
||||
// ── Update PGC in sys vars after page switch ──────────────────
|
||||
|
||||
private void UpdateSysPGC()
|
||||
{
|
||||
_mem.GetPage(SysVarPage)[0x2003] = _mem.GetWindow(3);
|
||||
}
|
||||
private void UpdateSysPG0()
|
||||
{
|
||||
_mem.GetPage(SysVarPage)[0x2000] = _mem.GetWindow(0);
|
||||
}
|
||||
private void UpdateSysPG8()
|
||||
{
|
||||
_mem.GetPage(SysVarPage)[0x2002] = _mem.GetWindow(2);
|
||||
}
|
||||
|
||||
// ── Main trap handler ─────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Check if CPU is about to execute CALL 0x6006 and intercept it.
|
||||
/// Call this BEFORE ExecCycle() when PC == address of CALL 0x6006.
|
||||
/// Returns true if the call was intercepted.
|
||||
/// </summary>
|
||||
public bool TryIntercept()
|
||||
{
|
||||
ushort pc = _cpu.regs.PC;
|
||||
|
||||
// Check: is the instruction at PC a CALL 0x6006?
|
||||
// CALL nn = CD lo hi → CD 06 60
|
||||
if (_mem.Read(pc) == 0xCD &&
|
||||
_mem.Read((ushort)(pc + 1)) == 0x06 &&
|
||||
_mem.Read((ushort)(pc + 2)) == 0x60)
|
||||
{
|
||||
byte funcNum = _cpu.regs.A;
|
||||
ushort retAddr = (ushort)(pc + 3);
|
||||
|
||||
// Log the call
|
||||
if (LogCalls && CallLog.Count < MaxCallLog)
|
||||
{
|
||||
CallLog.Add(new WcCallRecord
|
||||
{
|
||||
Tact = _cpu.Tact,
|
||||
PC = pc,
|
||||
FuncNum = funcNum,
|
||||
AF = _cpu.regs.AF,
|
||||
BC = _cpu.regs.BC,
|
||||
DE = _cpu.regs.DE,
|
||||
HL = _cpu.regs.HL,
|
||||
AF2 = _cpu.regs._AF,
|
||||
IX = _cpu.regs.IX,
|
||||
SP = _cpu.regs.SP
|
||||
});
|
||||
}
|
||||
|
||||
// Emulate P8000 dispatch: save Win0, set Win3=TXT
|
||||
_savedWin0Page = _mem.GetWindow(0);
|
||||
|
||||
// Execute the function
|
||||
DispatchFunction(funcNum);
|
||||
|
||||
// P8000 epilogue: restore Win0 only.
|
||||
// Do NOT reset Win3 — page management functions (MNGCPL, MNGCVPL)
|
||||
// explicitly set Win3 and the caller expects it to persist.
|
||||
_mem.SetWindow(0, _savedWin0Page);
|
||||
// Update sys vars to reflect actual window state
|
||||
UpdateSysPGC();
|
||||
UpdateSysPG0();
|
||||
|
||||
// Set PC past the CALL instruction
|
||||
_cpu.regs.PC = retAddr;
|
||||
|
||||
// Advance Tact (approximate: a typical WC call takes ~100-500 T-states)
|
||||
_cpu.Tact += 100;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ── Function dispatcher ───────────────────────────────────────
|
||||
|
||||
private void DispatchFunction(byte funcNum)
|
||||
{
|
||||
switch (funcNum)
|
||||
{
|
||||
// ── Page management ──
|
||||
case FN_MNGC_PL: Fn_MngcPl(); break;
|
||||
case FN_MNG0_PL: Fn_Mng0Pl(); break;
|
||||
case FN_MNG8_PL: Fn_Mng8Pl(); break;
|
||||
case FN_MNGCVPL: Fn_Mngcvpl(); break;
|
||||
case FN_MNG0VPL: Fn_Mng0vpl(); break;
|
||||
case FN_MNG8VPL: Fn_Mng8vpl(); break;
|
||||
|
||||
// ── UI (stubs) ──
|
||||
case FN_GEDPL: Fn_Gedpl(); break;
|
||||
case FN_PRWOW: Fn_Prwow(); break;
|
||||
case FN_RRESB: Fn_Rresb(); break;
|
||||
case FN_GADRW: Fn_Gadrw(); break;
|
||||
case FN_PRSRW: Fn_Stub("PRSRW"); break;
|
||||
case FN_PRSRW_A: Fn_Stub("PRSRW_A");break;
|
||||
case FN_TXTPR: Fn_Stub("TXTPR"); break;
|
||||
case FN_SCRLWOW: Fn_Stub("SCRLWOW"); break;
|
||||
case FN_CURSOR: Fn_Stub("CURSOR"); break;
|
||||
case FN_CURSER: Fn_Stub("CURSER"); break;
|
||||
case FN_YN: Fn_Stub("YN"); break;
|
||||
case FN_ISTR: Fn_Stub("ISTR"); break;
|
||||
case FN_NORK: Fn_Stub("NORK"); break;
|
||||
case FN_MEZZ: Fn_Stub("MEZZ"); break;
|
||||
|
||||
// ── Turbo / misc ──
|
||||
case FN_TURBOPL: Fn_Turbopl(); break;
|
||||
case FN_PRM_PL: Fn_PrmPl(); break;
|
||||
case FN_INT_PL: Fn_Stub("INT_PL"); break;
|
||||
case FN_INT_PL_H: Fn_Stub("INT_PL_H");break;
|
||||
case FN_STRSET: Fn_Strset(); break;
|
||||
case FN_DMAPL: Fn_Stub("DMAPL"); break;
|
||||
|
||||
// ── Keyboard (all return 0 = not pressed) ──
|
||||
case >= FN_KEY_SPACE and <= 0x2F:
|
||||
Fn_KeyStub(funcNum);
|
||||
break;
|
||||
|
||||
// ── File I/O ──
|
||||
case FN_LOAD512: Fn_Load512(); break;
|
||||
case FN_LOAD256: Fn_Load256(); break;
|
||||
case FN_SAVE512: Fn_Stub("SAVE512"); break;
|
||||
case FN_GIPAGPL: Fn_Gipagpl(); break;
|
||||
case FN_STREAM: Fn_Stream(); break;
|
||||
case FN_FENTRY: Fn_Fentry(); break;
|
||||
case FN_GFILE: Fn_Gfile(); break;
|
||||
case FN_ADIR: Fn_Stub("ADIR"); break;
|
||||
case FN_FINDNEXT: Fn_Stub("FINDNEXT");break;
|
||||
case FN_TENTRY: Fn_Stub("TENTRY"); break;
|
||||
case FN_GDIR: Fn_Stub("GDIR"); break;
|
||||
case FN_LOADNONE: Fn_Loadnone(); break;
|
||||
|
||||
// ── Graphics (stubs) ──
|
||||
case FN_MNGV_PL: Fn_Stub("MNGV_PL"); break;
|
||||
case FN_GVMOD: Fn_Stub("GVMOD"); break;
|
||||
case FN_GYOFF: Fn_Stub("GYOFF"); break;
|
||||
case FN_GXOFF: Fn_Stub("GXOFF"); break;
|
||||
|
||||
// ── Resident calls ──
|
||||
case FN_RES_JUMP: Fn_Stub("RES_JUMP"); break;
|
||||
case FN_RES_CALL: Fn_Stub("RES_CALL"); break;
|
||||
|
||||
default:
|
||||
Log?.Invoke($" WC UNKNOWN function 0x{funcNum:X2} at tact={_cpu.Tact}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Page management functions
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
/// <summary>0x00 MNGC_PL: plugin page A' → Win3. phys = A' + PLGPG + PPGP</summary>
|
||||
private void Fn_MngcPl()
|
||||
{
|
||||
byte pageArg = (byte)(_cpu.regs._AF >> 8); // A' register
|
||||
|
||||
if (pageArg == 0xFE)
|
||||
{
|
||||
// 0xFE = first text screen
|
||||
_mem.SetWindow(3, MTXPG);
|
||||
}
|
||||
else if (pageArg == 0xFF)
|
||||
{
|
||||
// 0xFF = font page
|
||||
_mem.SetWindow(3, 0x01);
|
||||
}
|
||||
else if (pageArg < 64)
|
||||
{
|
||||
byte phys = (byte)(pageArg + PLGPG + PPGP);
|
||||
_mem.SetWindow(3, phys);
|
||||
}
|
||||
// else >= 64: NOP (return without changing)
|
||||
|
||||
UpdateSysPGC();
|
||||
}
|
||||
|
||||
/// <summary>0x4E MNG0_PL: plugin page A' → Win0. phys = A' + PLGPG + PPGP</summary>
|
||||
private void Fn_Mng0Pl()
|
||||
{
|
||||
byte pageArg = (byte)(_cpu.regs._AF >> 8);
|
||||
if (pageArg < 64)
|
||||
{
|
||||
byte phys = (byte)(pageArg + PLGPG + PPGP);
|
||||
_mem.SetWindow(0, phys);
|
||||
UpdateSysPG0();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>0x4F MNG8_PL: plugin page A' → Win2. phys = A' + PLGPG + PPGP</summary>
|
||||
private void Fn_Mng8Pl()
|
||||
{
|
||||
byte pageArg = (byte)(_cpu.regs._AF >> 8);
|
||||
if (pageArg < 64)
|
||||
{
|
||||
byte phys = (byte)(pageArg + PLGPG + PPGP);
|
||||
_mem.SetWindow(2, phys);
|
||||
UpdateSysPG8();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>0x41 MNGCVPL: video page A' → Win3. phys = A' + TVBPG(0x20)</summary>
|
||||
private void Fn_Mngcvpl()
|
||||
{
|
||||
byte vpage = (byte)(_cpu.regs._AF >> 8);
|
||||
if (vpage < 64)
|
||||
{
|
||||
byte phys = (byte)(vpage + TVBPG);
|
||||
_mem.SetWindow(3, phys);
|
||||
}
|
||||
UpdateSysPGC();
|
||||
}
|
||||
|
||||
/// <summary>0x49 MNG0VPL: video page A' → Win0</summary>
|
||||
private void Fn_Mng0vpl()
|
||||
{
|
||||
byte vpage = (byte)(_cpu.regs._AF >> 8);
|
||||
if (vpage < 64)
|
||||
{
|
||||
byte phys = (byte)(vpage + TVBPG);
|
||||
_mem.SetWindow(0, phys);
|
||||
UpdateSysPG0();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>0x4A MNG8VPL: video page A' → Win2</summary>
|
||||
private void Fn_Mng8vpl()
|
||||
{
|
||||
byte vpage = (byte)(_cpu.regs._AF >> 8);
|
||||
if (vpage < 64)
|
||||
{
|
||||
byte phys = (byte)(vpage + TVBPG);
|
||||
_mem.SetWindow(2, phys);
|
||||
UpdateSysPG8();
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// UI functions (mostly stubs for text-mode UI)
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
/// <summary>0x0F GEDPL: restore WC display (stub — sets Win3 to TXT)</summary>
|
||||
private void Fn_Gedpl()
|
||||
{
|
||||
// Real WC: restores palette, attributes, text mode
|
||||
// Also trashes 0x1000–0x1447 in Win0 — we emulate that
|
||||
_mem.SetWindow(3, MTXPG);
|
||||
UpdateSysPGC();
|
||||
Log?.Invoke(" WC GEDPL (stub)");
|
||||
}
|
||||
|
||||
/// <summary>0x01 PRWOW: draw window (stub)</summary>
|
||||
private void Fn_Prwow()
|
||||
{
|
||||
// IX = win ptr (from HL in sdcccall)
|
||||
_mem.SetWindow(3, MTXPG);
|
||||
UpdateSysPGC();
|
||||
}
|
||||
|
||||
/// <summary>0x02 RRESB: restore window background (stub)</summary>
|
||||
private void Fn_Rresb()
|
||||
{
|
||||
_mem.SetWindow(3, MTXPG);
|
||||
UpdateSysPGC();
|
||||
}
|
||||
|
||||
/// <summary>0x05 GADRW: get screen address for (y,x) in window</summary>
|
||||
private void Fn_Gadrw()
|
||||
{
|
||||
// In wc_api.s: IX=win, D=y, E=x
|
||||
// Returns DE = screen address
|
||||
// Text screen at 0xC000, each row = ScreenWidth bytes
|
||||
// Window content starts at win.x + win.y * ScreenWidth + 0xC000
|
||||
// We compute: addr = 0xC000 + (win_y + y) * ScreenWidth + (win_x + x)
|
||||
|
||||
ushort winPtr = _cpu.regs.IX;
|
||||
byte wy = _mem.Read((ushort)(winPtr + 3)); // win.y at +3
|
||||
byte wx = _mem.Read((ushort)(winPtr + 2)); // win.x at +2
|
||||
byte y = _cpu.regs.D;
|
||||
byte x = _cpu.regs.E;
|
||||
|
||||
ushort addr = (ushort)(0xC000 + (wy + y) * ScreenWidth + (wx + x));
|
||||
_cpu.regs.DE = addr;
|
||||
|
||||
// Also need to set Win3 to TXT
|
||||
_mem.SetWindow(3, MTXPG);
|
||||
UpdateSysPGC();
|
||||
}
|
||||
|
||||
/// <summary>0x0E TURBOPL: set CPU/AY speed via SYSCONF port</summary>
|
||||
private void Fn_Turbopl()
|
||||
{
|
||||
// B = mode (0=CPU, 1=AY, 0xFF=restore), C = param
|
||||
byte mode = (byte)(_cpu.regs.BC >> 8); // B
|
||||
byte param = (byte)(_cpu.regs.BC & 0xFF); // C
|
||||
|
||||
if (mode == 0x00) // WC_TURBO_CPU: set CPU frequency
|
||||
{
|
||||
// param: 0=3.5, 1=7, 2=14, 3=28
|
||||
// Write turbo bits to SYSCONF port so PluginRunner picks it up
|
||||
byte sysconf = (byte)(param & 0x03);
|
||||
_cpu.WRPORT(0x20AF, sysconf);
|
||||
}
|
||||
// mode 1 (AY freq) — not relevant for emulation
|
||||
// mode 0xFF (restore) — restore to 3.5 MHz
|
||||
else if (mode == 0xFF)
|
||||
{
|
||||
_cpu.WRPORT(0x20AF, 0x00); // restore to 3.5 MHz
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>0x4C PRM_PL: get INI parameter (stub — return 0xFF = not set)</summary>
|
||||
private void Fn_PrmPl()
|
||||
{
|
||||
_cpu.regs.A = 0xFF;
|
||||
}
|
||||
|
||||
/// <summary>0x50 STRSET: memset(ptr, len, char)</summary>
|
||||
private void Fn_Strset()
|
||||
{
|
||||
// In wc_api.s: HL=dst, DE=len, A'=char
|
||||
// We actually don't need real WC for this, but the plugin calls it
|
||||
ushort dst = _cpu.regs.HL;
|
||||
ushort len = _cpu.regs.DE;
|
||||
byte ch = (byte)(_cpu.regs._AF >> 8);
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
_mem.Write((ushort)(dst + i), ch);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Keyboard stubs (all return A=0 = not pressed)
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
private void Fn_KeyStub(byte funcNum)
|
||||
{
|
||||
_cpu.regs.A = 0; // not pressed
|
||||
// Clear Z flag by default (A=0 → Z=1 → key not pressed)
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// File I/O emulation
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
/// <summary>0x30 LOAD512: load blocks×512 bytes from file → memory at dest</summary>
|
||||
private void Fn_Load512()
|
||||
{
|
||||
// wc_api.s _wc_load512: HL=dest, B=blocks (from stack via frame pointer)
|
||||
// WC API #0x30: HL=dest, B=blocks → A=0 ok / 0x0F EOF
|
||||
// Returns: A = status, HL = next address after loaded data
|
||||
|
||||
ushort loadDest = _cpu.regs.HL;
|
||||
byte blocks = _cpu.regs.B;
|
||||
int bytesToLoad = blocks * 512;
|
||||
|
||||
if (_fileData == null)
|
||||
{
|
||||
Log?.Invoke(" WC LOAD512: no file data!");
|
||||
_cpu.regs.A = 0xFF; // error
|
||||
return;
|
||||
}
|
||||
|
||||
int remaining = _fileData.Length - _filePos;
|
||||
int actual = Math.Min(bytesToLoad, remaining);
|
||||
bool isEof = (actual < bytesToLoad) || (_filePos + actual >= _fileData.Length);
|
||||
|
||||
// Write data into memory at loadDest (using Win3 map)
|
||||
for (int i = 0; i < actual; i++)
|
||||
{
|
||||
_mem.Write((ushort)(loadDest + i), _fileData[_filePos + i]);
|
||||
}
|
||||
_filePos += actual;
|
||||
|
||||
// Update HL to point past the loaded data
|
||||
_cpu.regs.HL = (ushort)(loadDest + actual);
|
||||
|
||||
_cpu.regs.A = isEof ? (byte)0x0F : (byte)0; // 0x0F = WC_EOF
|
||||
|
||||
Log?.Invoke($" LOAD512: dest=0x{loadDest:X4} blocks={blocks} bytes={actual} eof={isEof} filePos={_filePos}/{_fileData.Length}");
|
||||
}
|
||||
|
||||
/// <summary>0x51 LOAD256: load blocks×256 bytes</summary>
|
||||
private void Fn_Load256()
|
||||
{
|
||||
// Same pattern as LOAD512 but 256-byte blocks: HL=dest, B=blocks
|
||||
ushort loadDest = _cpu.regs.HL;
|
||||
byte blocks = _cpu.regs.B;
|
||||
int bytesToLoad = blocks * 256;
|
||||
|
||||
if (_fileData == null) { _cpu.regs.A = 0xFF; return; }
|
||||
|
||||
int remaining = _fileData.Length - _filePos;
|
||||
int actual = Math.Min(bytesToLoad, remaining);
|
||||
bool isEof = actual < bytesToLoad || _filePos + actual >= _fileData.Length;
|
||||
|
||||
for (int i = 0; i < actual; i++)
|
||||
_mem.Write((ushort)(loadDest + i), _fileData[_filePos + i]);
|
||||
_filePos += actual;
|
||||
|
||||
_cpu.regs.HL = (ushort)(loadDest + actual);
|
||||
_cpu.regs.A = isEof ? (byte)0x0F : (byte)0;
|
||||
|
||||
Log?.Invoke($" LOAD256: dest=0x{loadDest:X4} blocks={blocks} bytes={actual} eof={isEof}");
|
||||
}
|
||||
|
||||
/// <summary>0x32 GIPAGPL: seek file to beginning</summary>
|
||||
private void Fn_Gipagpl()
|
||||
{
|
||||
_filePos = 0;
|
||||
}
|
||||
|
||||
/// <summary>0x37 STREAM: open/switch file stream (stub — just log)</summary>
|
||||
private void Fn_Stream()
|
||||
{
|
||||
// D = mode (0xFF=root, 0xFE=clone, 0xFD=wcdir, 0/1=stream#)
|
||||
Log?.Invoke($" WC STREAM mode=0x{_cpu.regs.D:X2}");
|
||||
}
|
||||
|
||||
/// <summary>0x39 FENTRY: find file by name</summary>
|
||||
private void Fn_Fentry()
|
||||
{
|
||||
// HL = addr of [flag_byte][name_string][0]
|
||||
ushort namePtr = _cpu.regs.HL;
|
||||
byte flag = _mem.Read(namePtr);
|
||||
namePtr++;
|
||||
|
||||
// Read filename
|
||||
var sb = new StringBuilder();
|
||||
for (int i = 0; i < 255; i++)
|
||||
{
|
||||
byte ch = _mem.Read((ushort)(namePtr + i));
|
||||
if (ch == 0) break;
|
||||
sb.Append((char)ch);
|
||||
}
|
||||
string fileName = sb.ToString();
|
||||
Log?.Invoke($" WC FENTRY: \"{fileName}\" flag=0x{flag:X2}");
|
||||
|
||||
// Try to find the file on disk root
|
||||
if (!string.IsNullOrEmpty(DiskRootPath))
|
||||
{
|
||||
string path = Path.Combine(DiskRootPath, fileName);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
_cpu.regs.A = 1; // found (non-zero)
|
||||
// Set file size in DE:HL
|
||||
long fsize = new FileInfo(path).Length;
|
||||
_cpu.regs.HL = (ushort)(fsize & 0xFFFF);
|
||||
_cpu.regs.DE = (ushort)((fsize >> 16) & 0xFFFF);
|
||||
|
||||
// Store path for subsequent GFILE + LOAD512
|
||||
_pendingFileName = path;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_cpu.regs.A = 0; // not found
|
||||
}
|
||||
|
||||
private string _pendingFileName;
|
||||
|
||||
/// <summary>0x3B GFILE: open found file for reading</summary>
|
||||
private void Fn_Gfile()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_pendingFileName) && File.Exists(_pendingFileName))
|
||||
{
|
||||
_fileData = File.ReadAllBytes(_pendingFileName);
|
||||
_filePos = 0;
|
||||
Log?.Invoke($" WC GFILE: opened \"{_pendingFileName}\" ({_fileData.Length} bytes)");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log?.Invoke(" WC GFILE: no pending file");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>0x3A LOADNONE: skip N sectors in stream</summary>
|
||||
private void Fn_Loadnone()
|
||||
{
|
||||
byte sectors = (byte)(_cpu.regs._AF >> 8);
|
||||
int skip = sectors * 512;
|
||||
if (_fileData != null)
|
||||
_filePos = Math.Min(_filePos + skip, _fileData.Length);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Stub for unimplemented functions
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
private void Fn_Stub(string name)
|
||||
{
|
||||
// Most stubs just need Win3 = TXT (already done in P8000 epilogue)
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Call log
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
public string DumpCallLog(int last = 50)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"=== WC Call Log (last {last} of {CallLog.Count}) ===");
|
||||
int start = Math.Max(0, CallLog.Count - last);
|
||||
for (int i = start; i < CallLog.Count; i++)
|
||||
{
|
||||
var c = CallLog[i];
|
||||
sb.AppendFormat("[{0,10}] PC={1:X4} FN=0x{2:X2} ({3}) AF={4:X4} BC={5:X4} DE={6:X4} HL={7:X4} IX={8:X4}",
|
||||
c.Tact, c.PC, c.FuncNum, GetFuncName(c.FuncNum),
|
||||
c.AF, c.BC, c.DE, c.HL, c.IX);
|
||||
sb.AppendLine();
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string GetFuncName(byte fn) => fn switch
|
||||
{
|
||||
FN_MNGC_PL => "MNGC_PL",
|
||||
FN_PRWOW => "PRWOW",
|
||||
FN_RRESB => "RRESB",
|
||||
FN_PRSRW => "PRSRW",
|
||||
FN_TXTPR => "TXTPR",
|
||||
FN_GADRW => "GADRW",
|
||||
FN_TURBOPL => "TURBOPL",
|
||||
FN_GEDPL => "GEDPL",
|
||||
FN_LOAD512 => "LOAD512",
|
||||
FN_LOAD256 => "LOAD256",
|
||||
FN_GIPAGPL => "GIPAGPL",
|
||||
FN_STREAM => "STREAM",
|
||||
FN_FENTRY => "FENTRY",
|
||||
FN_GFILE => "GFILE",
|
||||
FN_MNGCVPL => "MNGCVPL",
|
||||
FN_MNG0_PL => "MNG0_PL",
|
||||
FN_MNG8_PL => "MNG8_PL",
|
||||
FN_MNG0VPL => "MNG0VPL",
|
||||
FN_MNG8VPL => "MNG8VPL",
|
||||
FN_STRSET => "STRSET",
|
||||
FN_PRM_PL => "PRM_PL",
|
||||
FN_ADIR => "ADIR",
|
||||
FN_LOADNONE=> "LOADNONE",
|
||||
FN_SAVE512 => "SAVE512",
|
||||
FN_DMAPL => "DMAPL",
|
||||
>= FN_KEY_SPACE and <= 0x2F => $"KEY_{fn - FN_KEY_SPACE:X2}",
|
||||
_ => $"?{fn:X2}"
|
||||
};
|
||||
}
|
||||
|
||||
// ── WC call record ────────────────────────────────────────────────
|
||||
|
||||
public struct WcCallRecord
|
||||
{
|
||||
public long Tact;
|
||||
public ushort PC;
|
||||
public byte FuncNum;
|
||||
public ushort AF, BC, DE, HL;
|
||||
public ushort AF2;
|
||||
public ushort IX, SP;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,36 @@
|
||||
Wild Commander v1.00
|
||||
|
||||
“¯à ¢«¥¨¥:
|
||||
|
||||
‚ । ªâ®à¥ áâப¨:
|
||||
CTRL+SHIFT - ᬥ à ᪫ ¤ª¨ ENG/RUS
|
||||
INSERT - ¯¥à¥ª«î票¥ ०¨¬ INS/OVR
|
||||
CAPS LOCK - ¢ª«î票¥/¢ëª«î票¥ ०¨¬ CAPS
|
||||
|
||||
‚ ¯ ¥«ïå:
|
||||
ALT+F1/F2 - ¢ë¡®à ¤à ©¢¥à ¢ á®®â¢. ¯ ¥«¨
|
||||
TAB - ᬥ ¯ ¥«¨
|
||||
SPACE - ¬ ન஢ âì ä ©«ë/ª â «®£¨ (¤ã¡«¨àã¥âáï ª« ¢¨è¥© INSERT)
|
||||
F2 - ®âªàëâì ¬¥î ¯®«ì§®¢ ⥫ï (¥á«¨ ¡ë« § £à㦥 ¯« £¨). § ¤ ¥âáï ¢ wc.mnu (¢ ¯ ¯ª¥ á ¯« £¨ ¬¨)
|
||||
F4 - । ªâ¨à®¢ âì ä ©« ¯®¤ ªãàá®à®¬ ¢ ⥪á⮢®¬ । ªâ®à¥ (¥á«¨ ¡ë« § £à㦥 ¯« £¨)
|
||||
F5 - ª®¯¨à®¢ âì ®â¬¥ç¥ë¥ ä ©«ë/ª â «®£¨ ¢ ¯à®â¨¢®¯®«®¦ãî ¯ ¥«ì (¢ë¢®¤¨â ¤¨ «®£®¢®¥ ®ª®)
|
||||
F6 - ¯¥à¥¨¬¥®¢ âì ä ©«/ª â «®£ ¯®¤ ªãàá®à®¬ (¢à¥¬ï á®§¤ ¨ï ¥ ¬¥ï¥â)
|
||||
F7 - á®§¤ âì ª â «®£ ¢ ªâ¨¢®© ¯ ¥«¨
|
||||
F8 - 㤠«¨âì ®â¬¥ç¥ë¥ ä ©«ë/ª â «®£¨ (¤ã¡«¨àã¥âáï ª« ¢¨è¥© DEL)
|
||||
F9 - áâனª INI ä ©« ( ¢â®¬ â¨ç¥áª¨ á®§¤ ¥â ¯ ¯ªã WC boot-device, ¥á«¨ â ª®¢®© ¥âã)
|
||||
F10 - ¢ë§®¢ ¬¥î § ¯ã᪠/㤠«¥¨ï ¯« £¨®¢. ¯® ENTER â ª®¢ë¥ § ¯ã᪠îâáï (¥á«¨ ¯®¤¤¥à¦¨¢ îâ § ¯ã᪠¨§ ¬¥î), ¯® DEL 㤠«ïîâáï ¨§ ¯ ¬ïâ¨.
|
||||
|
||||
ctrl+F3 - ¢ª«î票¥/¢ëª«î票¥ á®àâ¨à®¢ª¨ ¯® ¨¬¥¨
|
||||
ctrl+F4 - ¢ª«î票¥/¢ëª«î票¥ á®àâ¨à®¢ª¨ ¯® à áè¨à¥¨î
|
||||
ctrl+F6 - ¢ª«î票¥/¢ëª«î票¥ á®àâ¨à®¢ª¨ ¯® à §¬¥àã
|
||||
ctrl+R - ¯¥à¥ç¨â âì ᮤ¥à¦¨¬®¥ ª â «®£ ¨§ ¯ ¥«¨
|
||||
ctrl+A - ¢ë¤¥«¨âì ¢á¥ í«¥¬¥âë ¢ ª â «®£¥
|
||||
|
||||
* - ¨¢¥àâ¨à®¢ âì ¬ ન஢ªã ¢ ª â «®£¥
|
||||
|
||||
ENTER - ¢®©â¨ ¢ ª â «®£/§ £à㧨âì WMF ¯« £¨/§ ¯ãáâ¨âì SPG/§ ¯ãáâ¨âì $C/¯à®á¬®âà¥âì ä ©« ¯®¤ ªãàá®à®¬
|
||||
SHIFT+ENTER - ¢ë¢®¤ ¬¥î ¢ë¡®à ¢ìà (ä®à¬¨àã¥âáï ¨§ ¢áâ஥ëå ¢ì஢ ¨ § £à㦥ëå ¯« £¨®¢, ¯®¤¤¥à¦¨¢ îé¨å ¤ ®¥ ¬¥î)
|
||||
F3 - ®âªàëâì ä ©« ¯à®á¬®âà (ª ª ⥪áâ/HEX/ª à⨪ã (TGA/SCR/Gigascreen/MGS). § £à㦠¥â ä ©«ë ¤® 1¬¡. …᫨ à áè¨à¥¨¥ ¥ ®¯®§ ®, â® ¡ã¤¥â ¯à¥¤«®¦¥® ¢ë¡à âì ¤¥©á⢨¥
|
||||
|
||||
|
||||
P.S. ¢® ¢à¥¬ï ®¯¥à 権 㤠«¥¨ï/ª®¯¨à®¢ ¨ï ä ©«®¢/ª â «®£®¢ ¬®¦® ¤®áà®ç® ¯à¥à¢ âì â ª®¢ë¥ ¦ ⨥¬ ESC (¬¥î ¯®ï¢¨âáï ᬥ¥ ä ©« )!
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
Wild Commander v1.1␍␍[SETUP]␍CPU_FREQ=2; 0=3.5MHz, 1=7MHz, 2=14MHz, 3=reserved␍SavePaths=1; 0-disabled, 1-enabled (remember dirs in panels)␍SavePosition=1; 0-disabled, 1-enabled (restores cursor position, if enabled)␍TextMode=2; 0=80x25, 1=80x30, 2=90x36 text mode␍Logo=1; 0-disabled, 1-enabled (disables logo header and clock)␍ScreenSaver=9; 0-disabled, 1-9 minutes␍␍[PLUGINS]␍MOUNTER.WMF -memory=128 -cpu=3,5 -cache=off -rs232=115200␍SETIME.WMF; memory:128/512/1024/auto cpu:3,5/7/14 cache:on/off rs232:115200/230400␍RE.WMF␍VGMPLAY.WMF␍TXTEDIT.WMF -highlight=auto; auto,on,off␍FILE_CR.WMF␍TRDisp.wmf␍TRDUMP.WMF␍ROM_PROG.WMF␍WPLAYER.WMF␍GSPLAYER.WMF -midi_chip=2; 1 - first (#FF), 2 - second (#FE)␍VIDEO_PL.WMF␍BMPV.WMF␍ILBMVIEW.WMF␍FTVIEW.WMF␍GIFVIEW.WMF␍MCGSV.WMF␍TAPM.WMF -loading=real -cpu=3,5 -memory=128 -basic=48␍COMPILER.WMF; loading:real/slow/fast cpu:3,5/7/14 memory:128/512/1024/auto basic:48/128␍CLOCK.WMF;PLASMATW.WMF;SCRSAVER.WMF␍␍[LPANEL]␍DRV=1; 0-IDEnemoMaster, 1-SD1zc, 2-SDngs, 3-IDEsmucMaster, 4-IDEsmucSlave, 5-IDEnemoSlave, 6-SD2zc␍INFO=0; file info (0 - disabled, 1 - enabled)␍CSORT=2; sorting (0 - off, 1 - by name, 2 - by ext, 3 - by size)␍␍[RPANEL]␍DRV=1; 0-IDEnemoMaster, 1-SD1zc, 2-SDngs, 3-IDEsmucMaster, 4-IDEsmucSlave, 5-IDEnemoSlave, 6-SD2zc␍INFO=0; file info (0 - disabled, 1 - enabled)␍CSORT=2; sorting (0 - off, 1 - by name, 2 - by ext, 3 - by size)␍␍␍;this file and all plug-ins must be placed into WC folder (in root dir of Boot-Device)␍;¤ ë© ä ©« ¨ ¢á¥ ¯« £¨ë 㦮 à §¬¥áâ¨âì ¢ ¯ ¯ª¥ WC␍;(¢ ª®à¥ § £à㧮箣® ãáâனá⢠, ª®â®à®¥ ¢ë¡¨à ¥âáï ¢ ts-bios'e [SS+F12])␍
|
||||
@@ -0,0 +1 @@
|
||||
W: WDC v1.60␍menu\wdc v1.60.$C␍␍H: Hrust 1.3 (TR-DOS Only!)␍menu\Hrust v1.3.spg␍␍R: RUD 2.42 (TR-DOS Only!)␍menu\RUD v2.42.spg␍
|
||||
@@ -0,0 +1,751 @@
|
||||
WildCommander History.
|
||||
|
||||
v1.10 (03.08.2025):
|
||||
.правлен баг: [файловое ядро] генератор индексов не работал на каталогах и файлах с расширением меньше 3х символов
|
||||
.обновлен плагин: GSPLAYER .добавлено проигрывание цифровых модулей Digital Music Maker (.DMM)
|
||||
.обновлен плагин: ROM_PROG - добавлена возможность полной прошивки 512кб, оптимизация кода и обновление интерфейса
|
||||
.обновлен плагин: File Creator - изменен цвет курсора в юзер меню (теперь не слипается с заголовком окна)
|
||||
|
||||
v1.09 (04.08.2024):
|
||||
.обновлен плагин: TAPM .перемещена область выделенная под загрузку TAP файла (совпадала с буфером RAM диска под SCL образ)
|
||||
.обновлен плагин: MOUNTER .добавлена опция выбора скорости rs232 в INI (115200/230400)
|
||||
|
||||
v1.09 (03.08.2024):
|
||||
.обновлен плагин: GSPLAYER .доработан анализатор MOD файлов
|
||||
.обновлен плагин: TAPM .добавлен метод загрузки REAL (со звуком)
|
||||
|
||||
|
||||
v1.09 (31.07.2024):
|
||||
.в плагинную систему добавлен резидент вызова и резидент перехода (для вызова и перехода кода между паг в плагине, в одном окне)
|
||||
.добавлена возможность передавать параметры запуска в плагины из раздела загрузки таковых (в INI файле)
|
||||
.изменена версия плагинной системы, изменена версия INI файла
|
||||
.обновлен плагин: TAPM .по умолчанию сделана опция загрузки FAST
|
||||
.добавлена поддержка опций из INI файла
|
||||
.обновлен плагин: GSPLAYER .увеличен тайм аут ожидания готовности GS при детекте
|
||||
.добавлена очистка поля имени трека
|
||||
.добавлена поддержка опций из INI файла
|
||||
.обновлен плагин: TXT Editor .добавлена поддержка опций из INI файла
|
||||
.обновлен плагин: MOUNTER .добавлена поддержка опций из INI файла
|
||||
|
||||
v1.07 (19.04.2024):
|
||||
.обновлен плагин: GSPLAYER .в midi плеере сделана нормальная синхронизация таймера, с точностью до 8 долей фрейма
|
||||
.в midi плеере сделан игнор задержки перед кодом конца трека (чтобы срабатывал автопереход на следующий модуль)
|
||||
.в midi плеере исправлен баг с именем трека нулевой длины
|
||||
.в midi плеере исправлен баг с именем трека шире поля имени в окне
|
||||
.в midi плеере исправлен баг в таймере тиков (при добавлении остатка не учитывалось переполнение младших 16 бит)
|
||||
|
||||
v1.07 (17.04.2024):
|
||||
.обновлен плагин: GSPLAYER .в midi плеере подправлен коэффициент скорости проигрывания
|
||||
.доработан однобайтовый прогресс бар
|
||||
|
||||
v1.07 (16.04.2024):
|
||||
.обновлен плагин: GSPLAYER .устранены проблемы с синхронизацией в midi плеере (в ущерб визуализации)
|
||||
.в midi плеере добавлена возможность выбора чипа по F1 (1го либо 2го)
|
||||
.в midi плеере добавлен прогресс бар
|
||||
|
||||
v1.07 (15.04.2024):
|
||||
.правлен баг: неправильно подставлялся режим экрана в меню сетапа (F9), из-за кривых констант
|
||||
.обновлен плагин: WPLAYER .добавлена ассоциация для .ETC файлов (SAA модули)
|
||||
.убрано ограничение на 16кб для TFC файлов
|
||||
.обновлен плагин: GSPLAYER .добавлен тайм-аут с окошком, если при первом использовании плагина гс долго отвечает
|
||||
.ускорена загрузка MOD файлов в память GS (примерно на 27%)
|
||||
.добавлено проигрывание MIDI файлов (.MID) через multisound (ay-rs232/midi)
|
||||
|
||||
v1.06 (07.04.2024):
|
||||
.обновлен плагин: WPLAYER .правлен баг: исправлено проигрывание TFD файлов, был кривой парсинг буфера 48кб, который идет до загрузки данных с FAT
|
||||
.добавлено проигрывание PSM (Pro Sound Maker) модулей
|
||||
|
||||
v1.06 (05.04.2024):
|
||||
.добавлена возможность инициализации yes/no (вместо ok/cancel) в функции YN(8)
|
||||
.немного доработан file info
|
||||
.правлен баг: при включении на прерывания обработчика от плагина (INT_PL (86)), функции печати окна (PRWOW) и стирания окна (RRESB) переключали окно #8000-#BFFF не отключая вызов в плагин из прерываний (который должен быть в #8000-#BFFF!)
|
||||
.изменена версия плагинной системы
|
||||
.обновлен плагин: GSPLAYER .вернул таймер на прерывания
|
||||
.изменения интерфейса
|
||||
.обновлен плагин: TXT Editor
|
||||
.обновлен плагин: WPLAYER (правлен баг: для инитов плееров (которые таковые делают) не включалась пага с музоном после выгребания модуля из буфера)
|
||||
|
||||
v1.05 (30.03.2024):
|
||||
.правлен баг: при открытии каталогов больше 4095 записей не ставился нулевой индекс на 4096 записи (проверялось только превышение буфера данных = 9 паг)
|
||||
.правлен баг: снято ограничение на 80 символов ширины экрана в функции PRIAT
|
||||
.обновлен плагин: TXT&BIN Viewer .в режиме BIN добавлен поиск (ctrl+F), пока что вводить можно только текст
|
||||
.в режиме BIN теперь не отображаются данные за пределами длины файла (кратно 16 байт)
|
||||
.правлен баг: в режиме просмотра текста не отображался текст
|
||||
.обновлен плагин: TXT Editor .ускорен режим печати с подсветкой синтаксиса
|
||||
.добавлено отображение маркировки в режиме печати с подсветкой синтаксиса
|
||||
.в редакторах строки добавлено обновление отображения статусов ins/ovr, caps, eng/rus
|
||||
.добавлен вывод ошибки, если выделенная область не помещается в буфер обмена
|
||||
.добавлено отображение оффсета курсора и области маркировки
|
||||
.теперь при открытии .ASM файлов подсветка включается автоматически (F2 вкл/выкл)
|
||||
.правлен баг: маркировка останавливалась на стыках страниц (не всегда)
|
||||
.правлен баг: копирование не проверяло превышение буфера обмена (16кб)
|
||||
|
||||
v1.04 (25.03.2024):
|
||||
.ускорена сортировка по расширению (ещё на ~27%)
|
||||
.обновлен плагин: TXT&BIN Viewer (в режиме BIN: добавлен вывод оффсета, добавлены клавиши PGUP, PGDN)
|
||||
.обновлен плагин: TAPM (добавлен запуск z80 снапшотов, версий 1,2,3)
|
||||
|
||||
v1.03 (23.03.2024):
|
||||
.обновлен плагин: WPLAYER .увеличен макс размер для PT3/VTX модулей до 24кб, в том числе в режиме TS (общий буфер на оба модуля 40кб)
|
||||
.изменения в интерфейсе
|
||||
.добавлена возможность запуска модулей размером меньше 256 байт
|
||||
.обновлен плагин: GSPLAYER .добавлена пауза (M key)
|
||||
.добавлен анализатор в режиме MOD плеера
|
||||
|
||||
v1.03 (18.03.2024):
|
||||
.в F9 меню теперь подтягивает текущее состояние опции Logo enabled/disabled
|
||||
.ускорена сортировка по расширению (примерно на 20%), а так же убрана зависимость от регистра в расширениях при сортировке
|
||||
.добавлено ожидание отпускания клавиш (USPO), перед выводом окна подтверждения действия, при отработке функции Delete(F8)
|
||||
.обновлен плагин: WPLAYER (добавлено потоковое проигрывание TFD файлов; починено восстановление фонта, по выходу из плагина, в режиме плей листа)
|
||||
.обновлен плагин: GSPLAYER (поддержка нового интерфейса, добавлен таймер при проигрывании mp3 и wav)
|
||||
.обновлен плагин: video_pl (поддержка нового интерфейса)
|
||||
|
||||
v1.02 (10.03.2024):
|
||||
.добавлена возможность отключения шапки с часами в пользу панелей (вынесена отдельная опция в INI файл и редактор по F9)
|
||||
.сделана автоматическая центровка всех окон в соответствии c активным режимом экрана (80x25,80x30,90x36)
|
||||
.мелкие изменения в интерфейсе
|
||||
.в API изменен формат типа окна, а так же добавлена автоцентровка окон (см. доку)
|
||||
.изменена версия плагинной системы
|
||||
.обновлен плагин: MOUNTER (косметические изменения)
|
||||
.обновлен плагин: WPLAYER (добавлена пауза проигрывания музыки по M, косметические изменения)
|
||||
.обновлен плагин: ASM Compiler (косметические изменения)
|
||||
.обновлен плагин: TXT Editor (косметические изменения)
|
||||
.обновлен плагин: TAPM (косметические изменения)
|
||||
.обновлен плагин: TRDUMP (TRDump v0.83 by dr.Max)
|
||||
.обновлен плагин: TRDISP (TRDispatcher v0.43b by dr.Max)
|
||||
|
||||
v1.01 final (30.10.2019):
|
||||
.правлен баг: удалялся только первый отмаркированный каталог
|
||||
.правлен баг: копирование дира не копировало файлы с именами больше 64 символов (гробила проца вывода имени)
|
||||
.обновлен плагин: TXT&BIN Viewer - добавлена поддержка вызова по F10 (просмотр текущего каталога в BIN)
|
||||
|
||||
v1.00 final (20.10.2019):
|
||||
.добавлен сброс буфера PS/2 при запуске коммандера
|
||||
.добавлен вывод ошибок в функции удаления каталога
|
||||
.добавлен вывод имени копируемого файла в функции копирования каталога (в окне с прогресс баром)
|
||||
.добавлена полная поддержка txt режима 90x36, в том числе в плагинную систему (панели теперь рисуются в полную ширину экрана)
|
||||
.изменен заголовок и структура wc.ini (!!!). имена файлов с плагинами теперь можно указывать в формате длинного имени
|
||||
.настройка времени включения скрин сейвера вынесена в INI файл
|
||||
.разделены вызовы F3 и ENTER (соотв. флаг в заголовке плагина)
|
||||
.выкинут встроенный просмотр TXT и BIN файлов
|
||||
.обновлен плагин: TXT Editor - добавлена полная поддержка экрана 90x36 (не только по высоте, но и в ширину)
|
||||
.обновлен плагин: ASM Compiler - поддержка вызова только по ENTER
|
||||
.добавлен плагин: TXT&BIN Viewer
|
||||
|
||||
v1.00 rc9 (10.10.2019):
|
||||
.выкинут встроенный TGA вьювер
|
||||
.добавлены отработки на нажатия ctrl+A и * (маркировать все и инвертирование маркировки)
|
||||
.заблокировано выделение ".." при отсутствии маркировки и нажатии F5/F8
|
||||
.добавлена поддержка 2й SD карты
|
||||
.добавлен вывод ошибок для рекурсивного копирования каталога
|
||||
.исправлен выход по ошибке из копирования при встрече каталогов с включенными атрибутами
|
||||
.обновлен плагин: MOUNTER - добавлена поддержка 2й SD карты, добавлен инит карты в случае если оная возвращает idle state
|
||||
.обновлен плагин: BMPV - добавлен просмотр TGA графики
|
||||
|
||||
v1.00 rc7 (05.10.2019):
|
||||
.добавлено сохранение капслока в коротких именах каталогов и файлов
|
||||
.правлен баг: поиск длинного имени в каталоге выдавал ложное совпадение в редкой ситуации
|
||||
.обновлен плагин: MOUNTER - правлен баг в маунте SCL на RAM-диск (парсер каталога не переваривал файлы 0й длины)
|
||||
|
||||
v1.00 rc6 (03.10.2019):
|
||||
.добавлен скролл бар в панели
|
||||
.добавлено копирование/удаление каталогов со всеми вложениями (пока-что без интерфейса)
|
||||
|
||||
v1.00 rc4 (13.09.2019):
|
||||
.добавлен инит бита вида данных в CMOS-часах
|
||||
.правлен баг: для плагинов типов #13 и #15 не выводился текст напротив подсказки F4
|
||||
.в API добавлена поддержка плагинов #11, #12 (вызов по F2 из коммандера)
|
||||
.в API для функции STREAM добавлен новый функционал (параметр #FD: включение спец потока и позиционирование на папку WC)
|
||||
.в авто-загрузку плагинов добавлен вывод поля F2 (при встрече соотв. плагина)
|
||||
.обновлен плагин: File Creator (FILE_CR) - добавлено F2 меню (читает wc.mnu из папки WC, запускает SPG и $C файлы)
|
||||
.обновлен плагин: TXT Editor - исправлен баг: при сохранении пустого файла, сохранялся файл длиной 0 (генерилась цепочка 4Гб)
|
||||
|
||||
v1.00 rc3 (08.09.2019):
|
||||
.обновлен плагин: TXT Editor .добавлена отработка ESC при редактировании имени файла в Save As... (возврат в редактор)
|
||||
.исправлен баг: при сохранении, в случае когда курсор находился в самом конце, длина файла получалась на 1 байт больше реальной
|
||||
.добавлен плагин: GIFVIEW by Grey (исходники конвертнул для компиляции из самого коммандера, плюс правлено управление и был включен кеш)
|
||||
|
||||
v1.00 rc3 (24.08.2019):
|
||||
.обновлен депакер MegaLZ, thx to spke (распаковка SPG при запуске, увеличение скорости распаковки в 2 раза)
|
||||
.добавлено обнуление SFile и смещений тайловых-плоскостей, в процедуре восстановления экрана (GEDPL)
|
||||
.при ручной загрузке плагина (WMF), если имя плагина совпадает с уже загруженным, то предлагается удалить старый плагин и загрузить новый (упрощает разработку и тестирование плагинов)
|
||||
.изменена версия плагинной системы (добалены новые типы плагинов)
|
||||
.обновлен плагин: TXT Editor .добавлена возможность запуска из F10 меню (создание пустого файла)
|
||||
.добавлена поддержка меню выбора вьюверов (зелёное меню)
|
||||
.добавлено сохранение по CTRL+ALT+S (Save As...)
|
||||
.доработки в обработке ошибок при сохранении по выходу
|
||||
.доработка режима подсветки синтаксиса (F2)
|
||||
.обновлена документация на плагинную систему
|
||||
|
||||
v0.99 (16.12.2018):
|
||||
.обновлен плагин: TXT Editor .повышена скорость работы на 14мгц (врубает кеш)
|
||||
.добавлен режим с подсветкой синтаксиса (бета), переключается по F2
|
||||
.обновлен плагин: ASM Compiler .мелкие оптимизации
|
||||
.добавлен вывод места ошибки при JR offset error (JR/DJNZ)
|
||||
.добавлена поддержка SAVEBIN (учитывает только имя)
|
||||
.обновлен плагин: File Creator (FILE_CR) .добавлено создание пустых текстовых файлов (*.txt)
|
||||
.в окне ввода имени по ESC теперь происходит выход, а не запись файла как было раньше
|
||||
|
||||
v0.99 (24.11.2018):
|
||||
.обновлен плагин: MOUNTER .при маунте TRD/SCL имя образа теперь сохраняется и отображается при следующих маунтах/дизмаунтах
|
||||
.добавлена возможность изменения параметров запуска из меню (задание частоты, лока памяти, режима кеш)
|
||||
[не зависит от параметров в биосе, ресет по F12 все выставит как в биосе...]
|
||||
.добавлен вариант запуска с бутом (qc3.11), не пишется в образ!
|
||||
|
||||
v0.99 (02.06.2018):
|
||||
.изменения в интерфейсе (добавлен 2й тип рамок для рисовалки окон, добавлены тени, широкий курсор), часть интерфейса переведена под новый стиль.
|
||||
.небольшие оптимизации по памяти
|
||||
.выкинут встроенный SCR, GIGASCREEN, MG вьювер (есть в плагине)
|
||||
.изменено меню выбора вьювера (зелёная менюшка), меню формируется динамически в зависимости от плагинов в памяти
|
||||
.обновление API: добавлен 4й и 5й тип плагинов (для вызовов из меню выбора вьювера)
|
||||
.изменена версия плагинной системы
|
||||
.обновлен плагин: WPLAYER (добавлена поддержка модулей от нового Fast Tracker 1.08, косметические изменения)
|
||||
.обновлен плагин: MCGSV (добавлена поддержка меню вызова вьюверов)
|
||||
.обновлены плагины: TXTEDIT, COMPILER (косметические изменения)
|
||||
.добавлен плагин: TRDUMP (TRDump v0.56i by dr.Max)
|
||||
.добавлен плагин: SETIME (SETime v0.1i by dr.Max)
|
||||
|
||||
v0.98 (08.04.2018):
|
||||
.доработки в опросе PS/2 клавиатуры (теперь правильно обрабатываются нажатия SHIFT+Arrows)
|
||||
.добавлена обработка "*","-","+" на Numpad'е
|
||||
.изменена версия плагинной системы
|
||||
.обновлен плагин: TXT Editor .добавлен буфер обмена (16кб), сохраняется при выходе из плагина
|
||||
.добавлена маркировка по SHIFT+RIGHT/DOWN/PGDOWN/END и редактирование маркировки по SHIFT+LEFT/UP
|
||||
.реализован весь основной функционал работы с маркировкой
|
||||
(копирование в буфер обмена по CTRL+"C", вставка из буфера обмена по CTRL+"V", вырезание по CTRL+"X")
|
||||
.обновлен плагин: Compiler .добавлена очистка буфера под код (128кб)
|
||||
|
||||
v0.97 (21.08.2017):
|
||||
.обновлен плагин: Compiler .правлен баг с подсчетом EQUшек из очереди (не ставило стоп токена при генерации очереди)
|
||||
.добавлена поддержка INCL до 64кб (без рекурсии)
|
||||
.увеличена скорость компиляции на 26% за счет включения кеш
|
||||
.увеличено макс. количество операндов с выражениями у команды до 15(16), было 8(9)
|
||||
.добавлена возможность просмотра значений меток (по 'H' после завершения компиляции, запускается просмотр и листает экраны по одной кнопке)
|
||||
.добавлен вывод сообщения при запуске компиляции
|
||||
|
||||
v0.97 (18.08.2017):
|
||||
.обновлен плагин: Compiler (исправлен баг с сохранением OBJ размером более 32кб, правлен баг с декодерами в DS, рефакторинг)
|
||||
.обновлен плагин: TXT Editor (добавлен хелп по F1, добавлен поиск по ctrl+F и ctrl+G, оптимизации по скорости)
|
||||
|
||||
v0.97 (12.08.2017):
|
||||
.обновлен плагин: WPLAYER (увеличен буфер под модуль+плеер с 16кб до 32кб, теперь верно играет большие PT3 треки)
|
||||
.обновлен плагин: TXT Editor (исправлен баг с сохранением файлов размером более 16кб, добавлен функционал)
|
||||
.добавлен плагин: Compiler (компиляет *.ASM сорцы по ENTER, OBJ сохраняет в *.WMF; пока-что заточен под компиляние плагинов)
|
||||
|
||||
v0.97 (31.07.2016):
|
||||
.правлен баг: при вызове ISTR(9) обработчик гробил на входе HL'
|
||||
.добавлена функция в API: ADIR(56)
|
||||
.добавлено управление Caps Lock Led в PS/2 драйвер
|
||||
.добавлен вывод ошибок при переименовании файлов/каталогов
|
||||
.обновление API: добавлена передача номера расширения по которому был запущен плагин
|
||||
.некоторое перераспределение памяти в ядре (вскоре придётся выкидывать встроенный функционал в плагины (mgs вьювер первый в очереди))
|
||||
.изменён детект в IDE драйверах (nemo, smuc)
|
||||
|
||||
v0.96 (05.09.2015-08.03.2016):
|
||||
.добавлен плагин: MCGSV.WMF (Multi Color'n'Gigascreen Viewer)
|
||||
.незначительные исправления в документации на API плагинной системы
|
||||
.добавлено зануление параметров SourceAlign, DestinationAlign, AlignSize при запуске плагинов (функция №13 DMAPL)
|
||||
.в функцию работы с потоками (57 STREAM), добавлена возможность клонирования потока активной панели
|
||||
.добавлена комбинация: SHIFT+F3 (просмотр файла с выбором вьювера), так же работает и SHIFT+ENTER
|
||||
.немного окультурен встроенный TXT-Viewer
|
||||
.в авто-загрузку плагинов добавлен вывод поля F4 (при встрече соотв. плагина)
|
||||
.добавлен плагин: TXT Editor (F4)
|
||||
|
||||
v0.94 (21.08.2015):
|
||||
.правлен баг: вьювер Multi-Giga показывал с битой палитрой
|
||||
.в API добавлена передача имени активного файла плагинам, при запуске по расширению(#00), либо по F4 (#14)
|
||||
.в редакторе строки добавлена отработка CAPS LOCK
|
||||
.расширены возможности функции ISTR(9) из API
|
||||
.вернул функцию API #58
|
||||
|
||||
v0.92 (16.08.2015):
|
||||
.правлен баг: загрузчик INI файла не верно отрабатывал конец файла
|
||||
.параметры в SETUP(F9) теперь берутся из текущих, ранее оные выставлялись как по умолчанию
|
||||
.в блок настройки SETUP(F9) добавлены пункты IDEnemoSlave, IDEsmucMaster и IDEsmucSlave
|
||||
.обновлен плагин: MOUNTER (добавлена поддержка IDEnemoSlave; если обнаружен IDE VideoDAC, то работа с IDEnemo идет без INIR/OTIR; сообщение об ошибке гробило память (был вызов PRIAT после TXTPR))
|
||||
.в INI файле теперь можно указывать DRV=3/4/5 (IDEsmucMaster/IDEsmucSlave/IDEnemoSlave)
|
||||
.добавлена общая поддержка Slave для IDEnemo и IDEsmuc драйверов
|
||||
.оптимизации в IDEsmuc драйвере
|
||||
.добавлен детект IDE VideoDAC в IDEnemo драйвер (теперь будет работать и с внешним контроллером [требует замены сигнала DOS\ на +5в])
|
||||
|
||||
v0.90 (09.08.2015):
|
||||
.правлен баг: не работал запуск SPGv0.2 (требуется полная переработка загрузчика)
|
||||
.добавлен доп. уровень защиты на LOAD512, при загрузке из плагинов в #C000-#FFFF
|
||||
|
||||
v0.89 (08.08.2015):
|
||||
.временно отключена функция API FindNext(58) (пока-что выдает другую структуру данных и не реагирует на A)
|
||||
.вернул SD(NGS) драйвер, замержены все изменения из SD(ZC) драйвера
|
||||
.добавлен драйвер под SMUC с открытыми портами
|
||||
.все драйвера теперь хранятся в пакованном виде
|
||||
|
||||
v0.88 (04.08.2015):
|
||||
.обновление API (добавлены вызовы INS и DEL; добавлена возможность вешать на прерывания функцию из плагина, а так же отключать опрос клавиатуры и обновление часов)
|
||||
.обновлен плагин: VIDEO_PL (переделан детект NGS; убран механизм SKIP FRAMES; сделан буфер в 4 кадра; алг. загрузки сделан блоковым [на SD картах дает хороший прирост])
|
||||
.обновлен плагин: WPLAYER (доработан детект STP и ASC модулей, а так же отключен вывод имени в модулях без заголовка)
|
||||
|
||||
v0.87 (30.07.2015):
|
||||
.незначительные изменения в SD(ZC) драйвере (добавлено ожидание busy после завершения транзакции записи и чтения)
|
||||
.небольшие изменения в API (поддержка #14 типа плагинов)
|
||||
.обновлен плагин: WPLAYER (вернул старое управление в режиме без плей-листа, изменены цвета, правлен косяк с полем Editor)
|
||||
|
||||
v0.86 (13.07.2015):
|
||||
.доработана отработка конца каталога (очень редко могло выводить в конце каталога мусор)
|
||||
.обновлен плагин: ROM_PROG (версия от zboszor)
|
||||
|
||||
v0.85 (13.05.2015):
|
||||
.правлен баг: не отображались заголовки в setup'е
|
||||
.обновлен плагин: TAPM (добавлена возможность выбора скорости запуска TAP'ов: SLOW и FAST)
|
||||
|
||||
v0.84 (10.05.2015):
|
||||
.обновлен плагин: MOUNTER (правлен баг, с приходом инта во время работы с регами NVRAM)
|
||||
.правлен баг: не инитился флаг активной панели, после включения/повреждения страницы с оным (из-за этого не запоминалась позиция курсора, пока не нажмут TAB)
|
||||
|
||||
v0.83 (08.05.2015):
|
||||
.добавлено запоминание активной панели и позиции курсора
|
||||
.в ini файл добавлен новый параметр: CURP (включает хранение позиции курсора вместе с путями)
|
||||
.в setup добавлен соотв. пункт
|
||||
|
||||
v0.82 (08.05.2015):
|
||||
.правлен баг: не работал mc/mgs/scr вьювер
|
||||
.обновлен плагин: MOUNTER (доработано меню, добавлен ресет в Basic128)
|
||||
|
||||
v0.81 (07.05.2015):
|
||||
.в редактор строки добавлена возможность редактирования строк любой длины, с выводом окна курсора на экран
|
||||
.функция переименования(F6) теперь может переименовывать файлы/каталоги длиной до 255 символов
|
||||
.изменено распределение памяти, увеличена область под каталоги (144кб на панель)
|
||||
.сделан новый TXT вьювер (есть поддержка только cp866)
|
||||
.правлен баг: в редакторе строки Delete криво отрабатывал с курсором в крайней правой позиции
|
||||
.правлен баг: функция генерации каталога гробила ячейку #7FFF
|
||||
.обновлен плагин: MOUNTER (добавлен вывод меню после маунта TRD/SCL)
|
||||
|
||||
v0.79 (05.05.2015):
|
||||
.добавлено удаление пустых каталогов
|
||||
.для коротких имен, связанных с длинными, теперь всегда генерируется индекс, дабы не занимать имена...
|
||||
|
||||
v0.78 (04.05.2015):
|
||||
.правлен баг: сохранение ini файла в setup'e портило заголовок (используется общий буфер с fat ядром)
|
||||
|
||||
v0.77 (04.05.2015):
|
||||
.в редактор строки добавлена отработка Delete
|
||||
.добавлено отображение режимов INS/OVR и раскладки ENG/RUS в редакторе строки
|
||||
.исправлена ошибка с буквой "Ё" в генераторе каталога, отбраковывало оную как левый символ уникода и соотв. не выдавало длинное имя
|
||||
.та же ошибка исправлена в поиске имени
|
||||
.добавлен вывод ошибки при создании каталога
|
||||
.при генерации длинного имени, короткое больше не содержит пробелы (не все софты умеют с оными работать)
|
||||
|
||||
v0.76 (04.05.2015):
|
||||
.в редактор строки добавлена поддержка русской раскладки
|
||||
.редактор строки теперь реагирует на Insert (по умочанию стоит режим INS)
|
||||
|
||||
v0.75 (04.05.2015):
|
||||
.добавлена проверка на дупликаты, при создании короткого имени
|
||||
.исправлена генерация индексов
|
||||
.добавлено сообщение о сортировке
|
||||
|
||||
v0.74 (03.05.2015):
|
||||
.правлен баг: в генераторе короткого имени (всегда выставляло атрибут архивный)
|
||||
.доработан генератор каталога, не обрабатывал ситуацию с пустым (корневым) каталогом
|
||||
|
||||
v0.73 (03.05.2015):
|
||||
.коммандер переведен на работу с длинными именами
|
||||
-макс. длина имени файла/каталога 255 символов
|
||||
-при создании файла теперь идет проверка на наличие дупликата, так же добавлен возврат типа ошибки
|
||||
-все функции API, которые выдавали имена в формате [flag(1)],name(1-12),0 - теперь выдают имена до 255 символов!
|
||||
-все функции API, которые принимали имена в формате [flag(1)],name(1-12),0 - теперь принимают имена до 255 символов
|
||||
-функция FENTRY теперь может принимать на вход не просто имя файла, но так же и путь (к примеру #00,"..\dir1\dir2\file.txt",0)
|
||||
-сортировщик переделан под длинные имена
|
||||
-плагин WPLAYER теперь полноценно работает с ayl/m3u плей листами
|
||||
.убрана 58я функция из API (MkFile устаревший)
|
||||
.в API добавлены функции RENAME и FindNext (на место 58й)
|
||||
.временно отключен SD(NGS) драйвер
|
||||
.обновлен плагин: WPLAYER (не отдавал неопознанные файлы)
|
||||
.обновлен плагин: FILE_CR (вызовы 58й функции заменены)
|
||||
|
||||
v0.68 (24.04.2015):
|
||||
.состояние NumLock теперь не влияет на Insert и Delete в панелях
|
||||
.добавлены новые функции в API: DelFl, SCRLWOW
|
||||
.сделано отключение графики при запуске
|
||||
|
||||
v0.67 (23.04.2015):
|
||||
.правлен баг: 54я функция API не работала
|
||||
.обновлен плагин: WPLAYER
|
||||
-работа с плай листами
|
||||
-режимы проигрывания (цикл/на одном файле/останов)
|
||||
-генерация списка проигрывания, если есть маркировка на файлах (их кидает в плей лист, при запуске)
|
||||
-заходы в ayl/m3u (пока-что бесполезные, ибо нету поддержки лонг неймов в API коммандера)
|
||||
|
||||
v0.66 (18.04.2015):
|
||||
.доработан детект в SD драйвере
|
||||
.обновлен SD NGS драйвер (замержены все изменения из SD(ZC) драйвера)
|
||||
.обновлен плагин: WPLAYER, добавлено проигрывание SAA1099 модулей (ETracker)
|
||||
.обновлена документация и описания
|
||||
|
||||
v0.65 (13.04.2015):
|
||||
.добавлен новый функционал в интерфейсную часть API
|
||||
.введена глобальная переменная, нужная для SD драйвера в mounter'е
|
||||
.обновлен плагин: Mounter (заменен SD драйвер, задействованы новые функции API)
|
||||
|
||||
v0.63 (09.04.2015):
|
||||
.правлен баг: в блоке настройки INI файла были перепутаны местами левая и правая панель
|
||||
.правлен баг: пути к панелям гробились при входе в ts-bios, пересекалось с фонтом оного
|
||||
.переписан SD драйвер, теперь должен видеть все карты (MMC в том числе)
|
||||
|
||||
v0.62 (30.07.2014):
|
||||
.увеличен размер буфера под плагины (с 512кб до 1мб)
|
||||
.добавлена сортировка по расширению (ctrl+F4)
|
||||
.добавлена сортировка по размеру (ctrl+F6)
|
||||
.обновлен плагин: TAP Mounter (добавлен запуск SNA48/128)
|
||||
|
||||
v0.61 (27.07.2014):
|
||||
.добавлена сортировка (только по имени, отключается на каталогах больше 510)
|
||||
.в ini файл добавлен новый пунк: CSORT (сортировка)
|
||||
.добавлен вывод версии TSconf в F1 меню
|
||||
.добавлены горячие клавиши: ctrl+F3 - вкл/выкл сортировки в панели; ctrl+R - обновить содержимое панели
|
||||
|
||||
v0.60 (26.07.2014):
|
||||
.временно отрубил мерж при стирании окон
|
||||
.доработана пускалка SPGv0.x
|
||||
.изменено чтение версии TSconf
|
||||
|
||||
v0.59 (25.07.2014):
|
||||
.убрана wide версия из сборки
|
||||
.в ini файл добавлен новый пунк: Text Mode (TXTMD), задает разрешение тхт режима (25x80/30x80/36x90)
|
||||
|
||||
v0.58 (24.07.2014):
|
||||
.добавлено включение кеша в окне с системными функциями (page1)
|
||||
.добавлено чтение версии TSconf, TGA вьювер теперь учитывает IDE Video-DAC
|
||||
|
||||
v0.57 (21.07.2014):
|
||||
.правлен баг: при удалении плагина не обновлялась переменная начала свободного места в буфере плагинов
|
||||
.правлен баг: гробились депакеры запускалки SPG
|
||||
.добавлена возможность прямой загрузки данных в #0000-#3FFF (плагинная система)
|
||||
.добавлена возможность запуска скрин сейвера из F10 меню
|
||||
|
||||
v0.56 (09.12.2013-18.07.2014):
|
||||
.буфер окон перемещен в отдельную страницу
|
||||
.добавлена дефрагментация, при удалении окон в произвольном порядке (окна не должны пересекаться!)
|
||||
.добавлена дефрагментация, при удалении плагинов (теперь удаляется не только заголовок плагина, но и место выделенное под него)
|
||||
.расширен функционал плагинной системы, изменена версия и расширен заголовок (функции MNG0_PL, MNG8_PL, MNG0VPL, MNG8VPL)
|
||||
.добавлен редактор INI файла (F9)
|
||||
|
||||
v0.55 (02.12.2013):
|
||||
.расширен функционал плагинной системы, сменена версия таковой (функции DMAPL, GVtm, GVtl, GVsgp)
|
||||
.увеличен размер графического буфера до 64 страниц (1мб)
|
||||
.исправлен баг: не сбрасывался путь в панели при запуске, если носитель отсутствовал
|
||||
.обновлена документация на плагинную систему
|
||||
|
||||
v0.54 (29.11.2013):
|
||||
.добавлено выставление внутренней частоты по выходу из плагинов (задается в ини)
|
||||
.добавлено отображение пути к активному каталогу
|
||||
.добавлен раздел SETUP в ini файл (можно задать частоту процессора и выбрать режим хранения позиций панелей в ОЗУ)
|
||||
.добавлен плагин: TAP Mounter
|
||||
.добавлен плагин: IFF/ILBM Viewer
|
||||
|
||||
v0.53 (16.11.2013):
|
||||
.правлен баг: убрано обращение к порту #2AAF
|
||||
.правлен баг: запуск SPGv0.x не работал
|
||||
|
||||
v0.50 (24.08.2013):
|
||||
.правлен баг: вешался прогресс бар, на файлах меньше 64 байт
|
||||
.увеличен размер видео буфера, выделяемого плагинам (добавлен 3й видео экран, см. функционал)
|
||||
.добавлены параметры панелей в INI файл (изменена версия такового)
|
||||
.в сборку добавлена версия коммандера 80x25 (WildCommanderWide)
|
||||
|
||||
v0.48 (28.11.2012):
|
||||
.обновлен IDE драйвер (добавлена поддержка DMA, доработан детект)
|
||||
.обновлен SD(ZC) драйвер (добавлена поддержка DMA)
|
||||
.переделан алг. чтения/записи на мульти-секторный (FAT ядро)
|
||||
|
||||
v0.40 (27.11.2012):
|
||||
.доработана пускалка SPGv1.0 (добавлена чистка ZX экрана)
|
||||
.обновлен плагин: BMP Viewer & Sprite Cutter (поддержка разрешений от 1x1 до 512x512, 256c спрайты, ручное изменение размера рамки)
|
||||
|
||||
v0.39 (11.10.2012):
|
||||
.доработано меню работы с плагинами (F10)
|
||||
.обновлен плагин: GS Player (переделан mp3 плеер, подправлен детект NGS)
|
||||
|
||||
v0.38 (03.10.2012):
|
||||
.вынесен опрос Caps Lock в плагины
|
||||
.исправлена ошибка в DMAPL (3я функция)
|
||||
|
||||
v0.36 (28.09.2012):
|
||||
.добавлена отработка клавиш LEFT, RIGHT, HOME, END в F10 меню (меню работы с плагинами)
|
||||
.добавлены новые функции в плагинную систему (MKfile, MKdir, STREAM, DMA)
|
||||
.исправлена проблема с конверсией имен "..", "." в GENTRYX
|
||||
.обновлена документация на плагинную систему
|
||||
|
||||
v0.35 (26.09.2012):
|
||||
.добавлена запускалка SPGv1.0 (позволяет грузить до 256блоков с жатием или без (hrust1/megalz)
|
||||
.расширен функционал плагинной системы
|
||||
.добавлены дополнительные защиты в плагинную систему
|
||||
|
||||
v0.33 (31.08.2012):
|
||||
.правлен баг: при запуске SPG всегда выставлялись 14мгц
|
||||
.обновлен Nemo-IDE драйвер, переделаны функции чтения/записи (в соответствие с докой)
|
||||
.обновлен плагин: TRD Mounter (исправлен Nemo-IDE драйвер)
|
||||
.обновлен плагин: File Creator (правлен баг с 9м сектором в TRD без бута)
|
||||
|
||||
v0.31 (23.07.2012-24.08.2012):
|
||||
.доработан Nemo-IDE драйвер, для корректной работы с IDE-SD переходниками
|
||||
.обновлен плагин: TRD Mounter (обновлен Nemo-IDE драйвер)
|
||||
.обновлен плагин: BMP Viewer & Sprite Cutter (новые размеры рамок)
|
||||
.обновлен плагин: File Creator (исправлено название бута, было "QC3.05.B")
|
||||
|
||||
v0.30 (05.04.2012-21.07.2012):
|
||||
.правлен баг: в File Creator'е не проверялся флаг ошибки при вызове функции создания файла
|
||||
.добавлено игнорирование старших 4х бит в номере кластера (FAT ядро)
|
||||
.добавлены опросы F1-F10 в плагинную систему + мелкие изменения
|
||||
.добавлен плагин: BMP Viewer & Sprite Cutter (Beta версия). понимает только BMP 8bit 320x240!
|
||||
|
||||
v0.29 (03.04.2012):
|
||||
.несколько изменён детект FAT32 (FAT ядро)
|
||||
.TURBOPL теперь меняет 3х-битную частоту AY (плагинная система)
|
||||
.убраны мигания экрана при запуске $C/SPG файлов
|
||||
.реализована функция создания файла - MKFILE (плагинная система)
|
||||
.добавлен новый плагин: File Creator, умеет создавать пустые TRD и TRD с boot'ом (размером 640/800KB)
|
||||
.обновлён плагин: TR-DOS Mounter, исправлены мелкие недочёты
|
||||
|
||||
v0.26 (31.03.2012):
|
||||
.снято ограничение на размер создаваемой цепочки, соотв. теперь можно копировать файлы любых размеров! (FAT ядро)
|
||||
.добавлена функция скипа данных (FAT ядро)
|
||||
.добавлена проверка на выход за пределы FAT. теперь: если нету достаточного свободного места, то будет выдана ошибка! (FAT ядро)
|
||||
.обновлён плагин: Video Player, добавлен фрейм скип, а так же заливка паллитры через DMA (необходимо обновить конфиг!!!)
|
||||
|
||||
v0.25 (29.03.2012):
|
||||
.в ядро ФС добавлена новая функция, необходимая для Video Player'а (FAT ядро)
|
||||
.обновлена плагинная система и версия таковой (необходимо обновить ВСЕ плагины)
|
||||
.добавлен таймер на прерывания (для плагинов)
|
||||
.добавлен новый плагин: Video Player
|
||||
|
||||
v0.24 (18.03.2012):
|
||||
.в GS Player добавлено проигрывание mp3 файлов, а так же доработано проигрывание mod'ов и wav'ов
|
||||
.правлены ошибки в плагинной системе (включение 2го видео экрана не работало)
|
||||
.доработаны переключения режимов и экранов (больше не мигает экраном с мусором)
|
||||
|
||||
v0.23 (14.03.2012):
|
||||
.добавлен новый плагин: GS Player (играет MOD и WAV[8bit mono/stereo 8000/11025/16000/22050])
|
||||
.отключено чтение структуры FSInfo (FAT ядро)
|
||||
.изменён фильтр имен (теперь не добавляет "_" именам состоящим из 1го символа)
|
||||
.флаги ABT и ENT вынесены в фикс. адреса (для возможности опроса из плагинов)
|
||||
|
||||
v0.22 (13.03.2012):
|
||||
.автозагрузка ини файла и плагинов теперь учитывает boot device (который выставляется в ts-bios'e)
|
||||
|
||||
v0.21 (11.03.2012):
|
||||
.изменен порт статуса DMA
|
||||
|
||||
v0.20 (10.03.2012):
|
||||
.добавлен вывод версии конфига (по F1)
|
||||
.переделаны алгоритмы работы с DMA
|
||||
|
||||
v0.19 (09.03.2012):
|
||||
.добавлен новый плагин: SCREEN SAVER by r0bat (SCRSAVER.WMF). он же добавлен в INI файл
|
||||
.добавлена новая функция в плагинную систему (опрос any key). обновлена документация
|
||||
.обновлен TRD Mounter: добавлена индикация виртуальных дисководов, добавлена защита от ресета в момент маунта
|
||||
|
||||
v0.18 (08.03.2012):
|
||||
.записи в порты Yoffset и Xoffset перенесены в обработчик прерываний (теперь txt/hex вьюверы работают нормально в режиме 90x36 символов, даже на 3.5МГц)
|
||||
.исправлены мелкие ошибки в плагинной системе
|
||||
|
||||
v0.16 (07.03.2012):
|
||||
.обновлен vDOS в TR-DOS маунтере (mounter.wmf)
|
||||
.окно автозагрузки теперь можно прочитать, зажав любую клавишу при запуске коммандера
|
||||
.добавлена возможность маркировки по клавише INSERT
|
||||
|
||||
v0.15 (06.03.2012):
|
||||
.правлены мелкие недочеты в автогрузилке плагинов. обновлен TRD Mounter.
|
||||
|
||||
v0.14 (06.03.2012):
|
||||
.добавлена возможность автозагрузки плагинов при запуске WC (нужно разместить wc.ini и все плагины в папке WC [в корне SD карты])
|
||||
.правлены недочеты во время просмотра текста/графики
|
||||
|
||||
v0.13 (05.03.2012):
|
||||
.добавлена возможность выхода по ESC из меню ALT+F1/F2
|
||||
.оптимизированно распределение памяти в WC (теперь пользуются только первые 2.5MB)
|
||||
.в маунтер vDOS добавлена возможность маунта SCL как RAM диска! собственно vDOS теперь поддерживает и RAM диск (один на все вирт диски)
|
||||
|
||||
v0.12 (04.03.2012):
|
||||
.обновлён фонт
|
||||
.добавлен диалог, при удалении плагинов из меню (F10)
|
||||
.добавлен инит нового порта DMA
|
||||
|
||||
v0.11 (03.03.2012):
|
||||
.правлен баг: в плагин передавался неверный размер файла...
|
||||
|
||||
v0.099 (03.03.2012):
|
||||
.в плагинной системе: IX можно не восстанавливать по выходу из плагина
|
||||
.теперь после удаления файла/группы файлов курсор не прыгает в начало каталога
|
||||
|
||||
v0.095 (02.03.2012):
|
||||
.добавлено меню запуска плагинов (F10)
|
||||
.добавлена функция удаления плагина (DEL в меню запуска[F10])
|
||||
.обновлен плагин-маунтилка (mounter.wmf). оный теперь можно вызывать из меню, для маунта RS-232 дисков и дизмаунта вирт дисков...
|
||||
|
||||
v0.091 (22.02.2012):
|
||||
.выставление pg0 и memConf, при выходе, теперь производится из сетапных переменных
|
||||
.добавлен плагин TRD Mounter. маунтит TRD образы с FAT32 на виртуальные диски A-D. (запись/чтение идет прямо в образы)
|
||||
|
||||
v0.089 (18.02.2012):
|
||||
.правлен запуск $C/SPG файлов (под не АТМ пагер)
|
||||
.добавлено чтение #AC, #AD ячеек из NVRAM при выходе из коммандера (#AC = pg0, #AD = memConf)
|
||||
.добавлено отключение основного обработчика прерываний (ибо DI может быть отменён из IDE драйвера)
|
||||
.правлен WP-плагин (оный выводил окно до функции определения модуля)
|
||||
|
||||
v0.085 (14.02.2012):
|
||||
.добавлена запускалка хобета-файлов ($C)
|
||||
.расширен функционал доступный из плагинов + обновлена дока
|
||||
|
||||
v0.081 (12.02.2012):
|
||||
.доработан выход из плагинов (менеджер плагинов) + добавлены защиты
|
||||
.добавлено выставление стека при запуске
|
||||
.обновлен дизайн WP-плагина
|
||||
|
||||
v0.079 (09.02.2012):
|
||||
.доработана плагинная система + правлены баги + обновлены доки
|
||||
.добавлена возможность загрузки до 32 плагинов, объёмом до 512кб (менеджер плагинов)
|
||||
.WP плагин переведён на 3.5МГц (основной цикл), дабы TFC нормально игрались
|
||||
.добавлен новый плагин: ROM writer (прошивает заданный 64Кб блок ПЗУ)
|
||||
.правлен баг: функция поиска партиций находила FAT16 разделы (FAT ядро)
|
||||
.правлены мелкие недочёты (при отмене удаления перечитывался каталог, доделано меню Y/N и т.д.)
|
||||
|
||||
v0.073 (06.02.2012):
|
||||
.в ядро ФС добавлена функция поиска файла/каталога по имени (FAT ядро)
|
||||
.в плагинной системе добавлены новые возможности (запуск после загрузки и по таймеру)
|
||||
.добавлена дока по плагинной системе
|
||||
|
||||
v0.072 (05.02.2012):
|
||||
.временно впилен порт #2A (дабы SPG запускались)
|
||||
.убрана лишняя чистилка из TGA вьювера + незначительно ускорен алгоритм
|
||||
.поменяно управление частотой Z80 (новые порты)
|
||||
|
||||
v0.071 (04.02.2012):
|
||||
.правлен баг: при удалении группы файлов курсор мог оказаться в старом каталоге
|
||||
.добавлено центрирование имени загруженного плагина
|
||||
.расширен функционал ядра ФС (необходимый для vTR-DOS'а)
|
||||
|
||||
v0.068 (03.02.2012):
|
||||
.правлен баг: включенный Num Lock блочил клавиши в панелях (Left, Right, Up, Down, Home, End, pgUP, pgDN на основной клавиатуре)
|
||||
.добавлена возможность отмены во время копирования/удаления нескольких файлов (по ESC)
|
||||
|
||||
v0.066 (01.02.2012):
|
||||
.обновлены алгоритмы работы с DMA + правлены мелкие недочеты
|
||||
|
||||
v0.065 (01.02.2012):
|
||||
.добавлена поддержка дма режима память-память во вьюверах графики
|
||||
.сделано кеширование в функции удаления цепочки (FAT ядро), оная стала быстрее в ~128 раз
|
||||
.правлен баг: END на пустом каталоге (корневом) выкидывал курсор в аут...
|
||||
|
||||
v0.061 (31.01.2012):
|
||||
.добавлена функция удаления файла/группы файлов (F8)
|
||||
|
||||
v0.059 (28.01.2012):
|
||||
.правлен баг: в плагин передавалась не верная длина файла
|
||||
.правлен баг: в штампе времени неверно считалось значение секунд (не делилось на 2)
|
||||
.добавлена проверка заголовка при загрузке плагинов (WMF файлы)
|
||||
.добавлена ассоциация расширения "$C" c вьювером ZX экранов и гигаскринов
|
||||
.сделано запоминание координаты курсора в меню выбора вьювера
|
||||
.обновлён WP-плагин (добавлен прогресс бар и автопереходы [для PT3, VT, PT2, PT1, FLS, STC, STP, FTC, PSC, SQT, ASC, GTR, TFC])
|
||||
|
||||
v0.058 (27.01.2012):
|
||||
.расширен функционал доступный из плагинов
|
||||
.из WP-плагина теперь отрабатывается лавиша ENTER
|
||||
.теперь, если плагин не опознаёт контент файла - то таковой отдаётся вьюверам
|
||||
|
||||
v0.056 (26.01.2012):
|
||||
.добавлен вывод окна после удачной загрузки плагина
|
||||
.незначительно обновлён WP-плагин
|
||||
|
||||
v0.055 (26.01.2012):
|
||||
.небольшие доработки в плагинной системе
|
||||
.добавлена возможность просмотра SCR из Hobeta
|
||||
.обновлён WP-плагин
|
||||
|
||||
v0.052 (25.01.2012):
|
||||
.добавлена плагинная система
|
||||
.сделан плагин на основе WP (проигрывает AY/TS/TFM музыку)
|
||||
.обновлён фонт
|
||||
|
||||
v0.049 (22.01.2012):
|
||||
.драйвера перенесены в FAT ядро
|
||||
.исправлена проблема с каталогами > 2048
|
||||
.добавлена поддержка ALT/WIN/KOI в тхт-вьювере + автодетектор
|
||||
|
||||
v0.048 (21.01.2012):
|
||||
.добавлен SD(NGS) драйвер
|
||||
.добавлен запуск SPG файлов (с меню выбора частоты)
|
||||
|
||||
v0.045 (20.01.2012):
|
||||
.добавлены ассоциации A80, ASM, DIZ, NF0 для тхт-вьювера
|
||||
.курсор в окнах теперь учитывает маску
|
||||
.добавлена маркировка файлов (space)
|
||||
.добавлено диалоговое окно перед копированием (F5) + копирование группы файлов
|
||||
.правлен баг: генерилка каталога не учитывала флаг EOC
|
||||
.вызов функций чтения/записи файла теперь можно делать и при выставленном EOC (FAT ядро)
|
||||
|
||||
v0.041 (18.01.2012):
|
||||
.убраны пересечения с лучом в тхт-вьювере
|
||||
.добавлена функция переименования файла/каталога (F6)
|
||||
|
||||
v0.040 (17.01.2012):
|
||||
.снято ограничение на размер FAT (FAT ядро)
|
||||
.правлен баг: функции F5 и F7 сбрасывали историю позиций курсора
|
||||
.правлен баг: скролл вниз в панелях, на каталогах >512 файлов, блочился раньше времени
|
||||
.добавлен штамп времени и даты при создании файла/каталога (берётся из RTC)
|
||||
.на Left и Right теперь висят функции Home и End
|
||||
|
||||
v0.036 (16.01.2012):
|
||||
.исправлен баг с переключением противоположной панели (курентная теряла драйвер)
|
||||
.добавлена строка с информацией о файле под курсором
|
||||
|
||||
v0.035 (15.01.2012):
|
||||
.значительно ускорен просмотр гигаскрин/мультигигаскрин графики (~ 1.5 раза)
|
||||
.улучшен алг. перехода по ENTER из вьюверов
|
||||
.сделан редактор строки
|
||||
.добавлены часы
|
||||
.добавлена функция создания каталога (F7)
|
||||
|
||||
v0.031 (14.01.2012):
|
||||
.правлен баг: скролл вниз в HEX-вьювере врал одну строчку на стыке страниц
|
||||
.добавлен просмотр .mg2, .mg4, .mg8 файлов (Мультигигаскрин)
|
||||
.добавлена ассоциация расширения .gsc с гигаскрином
|
||||
|
||||
v0.030 (13.01.2012):
|
||||
.правлен баг в HEX-вьювере (не правильно работал скролл вверх)
|
||||
.в HEX-вьювер добавлена обработка конца файла, плюс незначительно подправлен интерфейс
|
||||
.в панелях: ENTER - вход в каталог/просмотр файла(как по F3), SHIFT+ENTER - вызвать меню выбора вьювера
|
||||
|
||||
v0.029 (12.01.2012):
|
||||
.добавлен HEX-вьювер (без обработки конца файла)[F3]
|
||||
.добиты последние глюки в тхт вьювере (не чистился экран в конце файла)
|
||||
.добавлен переход на уровень выше по BACKSPACE (в панелях)
|
||||
|
||||
v0.028 (11.01.2012):
|
||||
.в текст-вьювере сделана правильная обработка длинных строк (длиннее 90 символов)
|
||||
.в текст-вьювер добавлены клавиши pgUP и pgDN (F3)
|
||||
.добавлен просмотр ZX картинок и gigascreen (в 256c)[F3]
|
||||
.добавлен диалог выбора типа просмотра, если расширение не опознано (F3)
|
||||
|
||||
v0.026 (10.01.2012):
|
||||
.доработано поведение pgDN в панелях
|
||||
.добавлены клавиши HOME и END в панелях
|
||||
.добавлена проверка заголовка TGA файлов
|
||||
.добавлена клавиша ENTER во вьюверах (производит переход к след. файлу)
|
||||
.доделан тектсовый вьювер (теперь можно скроллить в оба направления)
|
||||
|
||||
v0.025 (10.01.2012):
|
||||
.добавлена возможность просмотра TGA изображений (в режиме 256с)
|
||||
.добавлен инит палитры при запуске
|
||||
|
||||
v0.023 (09.01.2012):
|
||||
.правлен баг с прогресс баром (оный не правильно работал на больших файлах, плюс гробил ide драйвер)
|
||||
.исправлен баг с входом в каталог на позиции Y=#01FF
|
||||
.доработана фильтрация при загрузке каталога (теперь не кажет метку тома)
|
||||
.изменён размер записи в каталоге [в режиме имён 8.3] (25->32)
|
||||
.увеличен максимальный размер каталога [в режиме имён 8.3] (655->2048)
|
||||
|
||||
v0.022(08.01.2012):
|
||||
.исправлены глюки с HDD (FAT ядро)
|
||||
.правлен глюк с неправильной позицией курсора в панели приёмнике после копирования
|
||||
.добавлено возвращение ошибки из функции генерации цепочки, если размер файла вышел более 6144 кластеров
|
||||
.добавлен прогресс бар
|
||||
.добавлены клавиши pgDN/pgUP в панелях
|
||||
@@ -0,0 +1,45 @@
|
||||
WildCommander todo list:
|
||||
.добавить обновление не активной панели (при выходе из плагинов которые этого требуют, а так же после всех операций удаления/копирования в коммандере)
|
||||
|
||||
.добавить инфо в сетап
|
||||
.SPG Logo
|
||||
|
||||
.мониторинг наличия SD карты на прерываниях (FAT ядро?)
|
||||
|
||||
.пересекаются буферы BUX и WINDC, ситуация проявляется в EXTZEL (сейчас не влияет ни на что!)
|
||||
|
||||
SPGv0.2 loader:
|
||||
.алг. пользует область #5300-#5AFF под буфер, что недопустимо!
|
||||
|
||||
API:
|
||||
.в функции удаления файла: при удалении каталога, добавить проверку того, что DIR пустой
|
||||
.в редакторе строки: в инфо добавить возможность вывода позиции курсора/длины строки (буфера)
|
||||
.в редакторе строки: добавить HOME/END
|
||||
|
||||
FAT ядро:
|
||||
.доработать анализатор имени (определение типа SN/LN, SN с пробелами делать LN)
|
||||
.удаление LN в каталоге затирает только SN (добавить полное удаление)
|
||||
.поисковик не игнорирует пробелы в начале LN (с обеих сторон)
|
||||
.файловый уровень для RS232
|
||||
.файловый уровень для ISO9660
|
||||
|
||||
TXT viewer:
|
||||
.Home/End при просмотре
|
||||
.ручное переключение кодировок(ALT/WIN/KOI)
|
||||
.ansi-viewer
|
||||
|
||||
TXT Editor:
|
||||
.добить инспектор
|
||||
|
||||
ROM_PROG:
|
||||
.сканер ПЗУ
|
||||
|
||||
MOUNTER:
|
||||
.при маунте SCL на RAM диск, генерить имя диска (в образе)
|
||||
.(?)при маунте SCL поверх существуещего RAM диска, выводить диалог на замену
|
||||
.при маунте TRD размером меньше 640кб предупреждать об этом (плюс давать ошибку при попытке записи в область за пределами кластерной цепочки)
|
||||
|
||||
BMPV:
|
||||
.заменить вызов 58й функции
|
||||
.доработать алг
|
||||
.центрирование картинок в TGA-вьювере
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user