ZXHawk: debug only zx-state snapshot export

This commit is contained in:
Asnivor 2018-09-10 10:05:49 +01:00
parent e3736bdaa7
commit 16d68ea813
19 changed files with 989 additions and 23 deletions

View File

@ -389,7 +389,9 @@
this.ZXSpectrumPokeMemoryMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ZXSpectrumMediaMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ZXSpectrumTapesSubMenu = new System.Windows.Forms.ToolStripMenuItem();
this.zxt1ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ZXSpectrumDisksSubMenu = new System.Windows.Forms.ToolStripMenuItem();
this.zxt2ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.Atari7800HawkCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.MainStatusBar = new StatusStripEx();
this.DumpStatusButton = new System.Windows.Forms.ToolStripDropDownButton();
@ -462,8 +464,7 @@
this.ShowMenuContextMenuSeparator = new System.Windows.Forms.ToolStripSeparator();
this.ShowMenuContextMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.timerMouseIdle = new System.Windows.Forms.Timer(this.components);
this.zxt1ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.zxt2ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ZXSpectrumExportSnapshotMenuItemMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.MainformMenu.SuspendLayout();
this.MainStatusBar.SuspendLayout();
this.MainFormContextMenu.SuspendLayout();
@ -3454,7 +3455,8 @@
//
this.ZXSpectrumMediaMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.ZXSpectrumTapesSubMenu,
this.ZXSpectrumDisksSubMenu});
this.ZXSpectrumDisksSubMenu,
this.ZXSpectrumExportSnapshotMenuItemMenuItem});
this.ZXSpectrumMediaMenuItem.Name = "ZXSpectrumMediaMenuItem";
this.ZXSpectrumMediaMenuItem.Size = new System.Drawing.Size(201, 22);
this.ZXSpectrumMediaMenuItem.Text = "Media";
@ -3465,19 +3467,31 @@
this.ZXSpectrumTapesSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.zxt1ToolStripMenuItem});
this.ZXSpectrumTapesSubMenu.Name = "ZXSpectrumTapesSubMenu";
this.ZXSpectrumTapesSubMenu.Size = new System.Drawing.Size(152, 22);
this.ZXSpectrumTapesSubMenu.Size = new System.Drawing.Size(159, 22);
this.ZXSpectrumTapesSubMenu.Text = "Tapes";
this.ZXSpectrumTapesSubMenu.DropDownOpened += new System.EventHandler(this.ZXSpectrumTapesSubMenu_DropDownOpened);
//
// zxt1ToolStripMenuItem
//
this.zxt1ToolStripMenuItem.Name = "zxt1ToolStripMenuItem";
this.zxt1ToolStripMenuItem.Size = new System.Drawing.Size(94, 22);
this.zxt1ToolStripMenuItem.Text = "zxt1";
//
// ZXSpectrumDisksSubMenu
//
this.ZXSpectrumDisksSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.zxt2ToolStripMenuItem});
this.ZXSpectrumDisksSubMenu.Name = "ZXSpectrumDisksSubMenu";
this.ZXSpectrumDisksSubMenu.Size = new System.Drawing.Size(152, 22);
this.ZXSpectrumDisksSubMenu.Size = new System.Drawing.Size(159, 22);
this.ZXSpectrumDisksSubMenu.Text = "Disks";
this.ZXSpectrumDisksSubMenu.DropDownOpened += new System.EventHandler(this.ZXSpectrumDisksSubMenu_DropDownOpened);
//
// zxt2ToolStripMenuItem
//
this.zxt2ToolStripMenuItem.Name = "zxt2ToolStripMenuItem";
this.zxt2ToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.zxt2ToolStripMenuItem.Text = "zxt2";
//
// Atari7800HawkCoreMenuItem
//
this.Atari7800HawkCoreMenuItem.Name = "Atari7800HawkCoreMenuItem";
@ -4104,17 +4118,12 @@
this.timerMouseIdle.Interval = 2000;
this.timerMouseIdle.Tick += new System.EventHandler(this.TimerMouseIdle_Tick);
//
// zxt1ToolStripMenuItem
// ZXSpectrumExportSnapshotMenuItemMenuItem
//
this.zxt1ToolStripMenuItem.Name = "zxt1ToolStripMenuItem";
this.zxt1ToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.zxt1ToolStripMenuItem.Text = "zxt1";
//
// zxt2ToolStripMenuItem
//
this.zxt2ToolStripMenuItem.Name = "zxt2ToolStripMenuItem";
this.zxt2ToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.zxt2ToolStripMenuItem.Text = "zxt2";
this.ZXSpectrumExportSnapshotMenuItemMenuItem.Name = "ZXSpectrumExportSnapshotMenuItemMenuItem";
this.ZXSpectrumExportSnapshotMenuItemMenuItem.Size = new System.Drawing.Size(159, 22);
this.ZXSpectrumExportSnapshotMenuItemMenuItem.Text = "Export Snapshot";
this.ZXSpectrumExportSnapshotMenuItemMenuItem.Click += new System.EventHandler(this.ZXSpectrumExportSnapshotMenuItemMenuItem_Click);
//
// MainForm
//
@ -4591,5 +4600,6 @@
private System.Windows.Forms.ToolStripMenuItem ZXSpectrumDisksSubMenu;
private System.Windows.Forms.ToolStripMenuItem zxt1ToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem zxt2ToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem ZXSpectrumExportSnapshotMenuItemMenuItem;
}
}

View File

@ -2564,6 +2564,32 @@ namespace BizHawk.Client.EmuHawk
}
}
private void ZXSpectrumExportSnapshotMenuItemMenuItem_Click(object sender, EventArgs e)
{
SaveFileDialog zxSnapExpDialog = new SaveFileDialog();
zxSnapExpDialog.RestoreDirectory = true;
zxSnapExpDialog.Title = "EXPERIMENTAL - Export 3rd party snapshot formats";
zxSnapExpDialog.DefaultExt = "szx";
zxSnapExpDialog.Filter = "ZX-State files (*.szx)|*.szx";
zxSnapExpDialog.SupportMultiDottedExtensions = true;
try
{
var res = zxSnapExpDialog.ShowDialog();
if (res == DialogResult.OK)
{
var speccy = (ZXSpectrum)Emulator;
var snap = speccy.GetSZXSnapshot();
File.WriteAllBytes(zxSnapExpDialog.FileName, snap);
//File.WriteAllText(zxSnapExpDialog.FileName, snap);
}
}
catch (Exception ex)
{
var ee = ex;
}
}
#endregion
#region Help

View File

@ -1834,6 +1834,11 @@ namespace BizHawk.Client.EmuHawk
break;
case "ZXSpectrum":
zXSpectrumToolStripMenuItem.Visible = true;
#if DEBUG
ZXSpectrumExportSnapshotMenuItemMenuItem.Visible = true;
#else
ZXSpectrumExportSnapshotMenuItemMenuItem.Visible = false;
#endif
break;
}
}
@ -4368,7 +4373,6 @@ namespace BizHawk.Client.EmuHawk
{
GenericCoreConfig.DoDialog(this, "PC-FX Settings");
}
private bool Rewind(ref bool runFrame, long currentTimestamp, out bool returnToRecording)
{

View File

@ -293,6 +293,8 @@
<Compile Include="Computers\SinclairSpectrum\Media\Disk\DiskType.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\MediaConverter.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\MediaConverterType.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\Snapshot\SZX\SZX.Objects.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\Snapshot\SZX\SZX.Methods.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\Tape\CSW\CswConverter.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\Tape\PZX\PzxConverter.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\Tape\TapeCommand.cs" />

View File

@ -7,6 +7,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A
public long TotalExecutedCycles;
private int EI_pending;
// ZXHawk needs to be able to read this for zx-state snapshot export
public int EIPending { get { return EI_pending; } }
public const ushort CBpre = 0;
public const ushort EXTDpre = 1;

View File

@ -19,6 +19,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// Activates a register
/// </summary>
int SelectedRegister { get; set; }
int[] ExportRegisters();
/// <summary>
/// Writes to the PSG

View File

@ -167,6 +167,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
}
}
/// <summary>
/// Used for snapshot generation
/// </summary>
/// <returns></returns>
public int[] ExportRegisters()
{
return _registers;
}
#endregion
#region Public Methods
@ -176,6 +185,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// </summary>
public void Reset()
{
for (int i = 0; i < 16; i++)
{
if (i == 6)
_registers[i] = 0xff;
else
_registers[i] = 0;
}
/*
_noiseVal = 0x0FFFF;
_outABC = 0;

View File

@ -389,6 +389,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
// fetch instruction without incrementing pc
//_cpu.FetchInstruction(_cpu.FetchMemory(firstByte));
}
#endregion

View File

@ -34,7 +34,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// Signs that the shadow screen is now displaying
/// Note: normal screen memory in RAM5 is not altered, the ULA just outputs Screen1 instead (RAM7)
/// </summary>
protected bool SHADOWPaged;
public bool SHADOWPaged;
/// <summary>
/// Index of the current RAM page
@ -66,22 +66,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <summary>
/// High bit of the ROM selection (in normal paging mode)
/// </summary>
protected bool ROMhigh = false;
public bool ROMhigh = false;
/// <summary>
/// Low bit of the ROM selection (in normal paging mode)
/// </summary>
protected bool ROMlow = false;
public bool ROMlow = false;
/// <summary>
/// Signs that the +2a/+3 special paging mode is activated
/// </summary>
protected bool SpecialPagingMode;
public bool SpecialPagingMode;
/// <summary>
/// Index of the current special paging mode (0-3)
/// </summary>
protected int PagingConfiguration;
public int PagingConfiguration;
/// <summary>
/// The last byte that was read after contended cycles

View File

@ -17,6 +17,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
set { LastULAOutByte = value; }
}
public byte Last7ffd;
public byte LastFe;
public byte Last1ffd;
/// <summary>
/// Reads a byte of data from a specified port address
/// </summary>

View File

@ -96,6 +96,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// but it is only partially decoded so it actually responds to any port with bits 1 and 15 reset
if (portBits[1] == false && portBits[15] == false)
{
Last7ffd = value;
// if paging is disabled then all writes to this port are ignored until the next reboot
if (!PagingDisabled)
{
@ -136,6 +138,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// Only even addresses address the ULA
if (lowBitReset)
{
LastFe = value;
// store the last OUT byte
LastULAOutByte = value;

View File

@ -77,6 +77,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// port 0x7ffd - hardware should only respond when bits 1 & 15 are reset and bit 14 is set
if (!portBits[1] && !portBits[15] && portBits[14])
{
Last7ffd = value;
if (!PagingDisabled)
{
// bits 0, 1, 2 select the RAM page
@ -97,6 +99,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// port 0x1ffd - hardware should only respond when bits 1, 13, 14 & 15 are reset and bit 12 is set
if (!portBits[1] && portBits[12] && !portBits[13] && !portBits[14] && !portBits[15])
{
Last1ffd = value;
if (!PagingDisabled)
{
if (!bits[0])
@ -134,6 +138,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// Only even addresses address the ULA
if (lowBitReset)
{
LastFe = value;
// store the last OUT byte
LastULAOutByte = value;

View File

@ -68,6 +68,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="value"></param>
public override void WritePort(ushort port, byte value)
{
if (port == 0x7ffd)
Last7ffd = value;
else if (port == 0x1ffd)
Last1ffd = value;
else if ((port & 0x01) == 0)
LastFe = value;
// get a BitArray of the port
BitArray portBits = new BitArray(BitConverter.GetBytes(port));
// get a BitArray of the value byte

View File

@ -71,6 +71,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
if ((port & 0x0001) != 0)
return;
LastFe = value;
// store the last OUT byte
LastULAOutByte = value;

View File

@ -1,6 +1,7 @@
using System;
using System.IO;
using System.IO.Compression;
using System.Runtime.InteropServices;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
@ -89,11 +90,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="buf"></param>
/// <param name="offsetIndex"></param>
/// <returns></returns>
public static int GetInt32(byte[] buf, int offsetIndex)
public static int GetInt32(byte[] buf, int offsetIndex)
{
return buf[offsetIndex] | buf[offsetIndex + 1] << 8 | buf[offsetIndex + 2] << 16 | buf[offsetIndex + 3] << 24;
}
/// <summary>
/// Returns an int32 from a byte array based on offset
/// </summary>
/// <param name="buf"></param>
/// <param name="offsetIndex"></param>
/// <returns></returns>
public static uint GetUInt32(byte[] buf, int offsetIndex)
{
return (uint)(buf[offsetIndex] | buf[offsetIndex + 1] << 8 | buf[offsetIndex + 2] << 16 | buf[offsetIndex + 3] << 24);
}
/// <summary>
/// Returns an uint16 from a byte array based on offset
/// </summary>
@ -148,6 +160,29 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
ds.Read(destBuffer, 0, destBuffer.Length);
}
public static byte[] SerializeRaw(object obj)
{
int rSize = Marshal.SizeOf(obj);
IntPtr buff = Marshal.AllocHGlobal(rSize);
Marshal.StructureToPtr(obj, buff, false);
byte[] rData = new byte[rSize];
Marshal.Copy(buff, rData, 0, rSize);
return rData;
}
public static T DeserializeRaw<T>(byte[] rData, int pos)
{
int rSize = Marshal.SizeOf(typeof(T));
if (rSize > rData.Length - pos)
throw new Exception();
IntPtr buff = Marshal.AllocHGlobal(rSize);
Marshal.Copy(rData, pos, buff, rSize);
T rObj = (T)Marshal.PtrToStructure(buff, typeof(T));
Marshal.FreeHGlobal(buff);
return rObj;
}
#endregion
}
}

View File

@ -0,0 +1,425 @@
using BizHawk.Common.NumberExtensions;
using BizHawk.Emulation.Cores.Components.Z80A;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// SZX Methods
/// Based on the work done by ArjunNair in ZERO spectrum emulator: https://github.com/ArjunNair/Zero-Emulator/blob/master/Ziggy/Peripherals/SZXFile.cs
/// </summary>
public partial class SZX
{
private SpectrumBase _machine;
private Z80A _cpu => _machine.CPU;
private SZX(SpectrumBase machine)
{
_machine = machine;
}
/// <summary>
/// Exports state information to a byte array in ZX-State format
/// </summary>
/// <param name="machine"></param>
/// <returns></returns>
public static byte[] ExportSZX(SpectrumBase machine)
{
var s = new SZX(machine);
byte[] result = null;
using (MemoryStream ms = new MemoryStream())
{
using (BinaryWriter r = new BinaryWriter(ms))
{
// temp buffer
byte[] buff;
// working block
ZXSTBLOCK block = new ZXSTBLOCK();
// header
ZXSTHEADER header = new ZXSTHEADER();
header.dwMagic = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("ZXST"), 0);
header.chMajorVersion = 1;
header.chMinorVersion = 4;
header.chFlags = 0;
switch (s._machine.Spectrum.MachineType)
{
case MachineType.ZXSpectrum16: header.chMachineId = (int)MachineIdentifier.ZXSTMID_16K; break;
case MachineType.ZXSpectrum48: header.chMachineId = (int)MachineIdentifier.ZXSTMID_48K; break;
case MachineType.ZXSpectrum128: header.chMachineId = (int)MachineIdentifier.ZXSTMID_128K; break;
case MachineType.ZXSpectrum128Plus2: header.chMachineId = (int)MachineIdentifier.ZXSTMID_PLUS2; break;
case MachineType.ZXSpectrum128Plus2a: header.chMachineId = (int)MachineIdentifier.ZXSTMID_PLUS2A; break;
case MachineType.ZXSpectrum128Plus3: header.chMachineId = (int)MachineIdentifier.ZXSTMID_PLUS3; break;
}
buff = MediaConverter.SerializeRaw(header);
r.Write(buff);
// ZXSTCREATOR
var bStruct = s.GetZXSTCREATOR();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("CRTR"), 0);
block.dwSize = (uint)Marshal.SizeOf(bStruct);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(bStruct);
r.Write(buff);
// ZXSTZ80REGS
var cStruct = s.GetZXSTZ80REGS();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("Z80R"), 0);
block.dwSize = (uint)Marshal.SizeOf(cStruct);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(cStruct);
r.Write(buff);
// ZXSTSPECREGS
var dStruct = s.GetZXSTSPECREGS();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("SPCR"), 0);
block.dwSize = (uint)Marshal.SizeOf(dStruct);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(dStruct);
r.Write(buff);
// ZXSTKEYBOARD
var eStruct = s.GetZXSTKEYBOARD();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("KEYB"), 0);
block.dwSize = (uint)Marshal.SizeOf(eStruct);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(eStruct);
r.Write(buff);
// ZXSTJOYSTICK
var fStruct = s.GetZXSTJOYSTICK();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("JOY\0"), 0);
block.dwSize = (uint)Marshal.SizeOf(fStruct);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(fStruct);
r.Write(buff);
// ZXSTAYBLOCK
if (s._machine.Spectrum.MachineType != MachineType.ZXSpectrum16 && s._machine.Spectrum.MachineType != MachineType.ZXSpectrum48)
{
var gStruct = s.GetZXSTAYBLOCK();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("AY\0\0"), 0);
block.dwSize = (uint)Marshal.SizeOf(gStruct);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(gStruct);
r.Write(buff);
}
// ZXSTRAMPAGE
switch (s._machine.Spectrum.MachineType)
{
// For 16k Spectrums, only page 5 (0x4000 - 0x7fff) is saved.
case MachineType.ZXSpectrum16:
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
var rp16 = s.GetZXSTRAMPAGE(5, s._machine.RAM0);
block.dwSize = (uint)Marshal.SizeOf(rp16);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(rp16);
r.Write(buff);
break;
// For 48k Spectrums and Timex TS/TC models, pages 5, 2 (0x8000 - 0xbfff) and 0 (0xc000 - 0xffff) are saved.
case MachineType.ZXSpectrum48:
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
var rp48_0 = s.GetZXSTRAMPAGE(5, s._machine.RAM0);
block.dwSize = (uint)Marshal.SizeOf(rp48_0);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(rp48_0);
r.Write(buff);
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
var rp48_1 = s.GetZXSTRAMPAGE(5, s._machine.RAM1);
block.dwSize = (uint)Marshal.SizeOf(rp48_1);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(rp48_1);
r.Write(buff);
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
var rp48_2 = s.GetZXSTRAMPAGE(5, s._machine.RAM2);
block.dwSize = (uint)Marshal.SizeOf(rp48_2);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(rp48_2);
r.Write(buff);
break;
// For 128k Spectrums and the Pentagon 128, all pages (0-7) are saved.
case MachineType.ZXSpectrum128:
case MachineType.ZXSpectrum128Plus2:
case MachineType.ZXSpectrum128Plus2a:
case MachineType.ZXSpectrum128Plus3:
List<byte[]> rams = new List<byte[]>
{
s._machine.RAM0, s._machine.RAM1, s._machine.RAM2, s._machine.RAM3,
s._machine.RAM4, s._machine.RAM5, s._machine.RAM6, s._machine.RAM7
};
for (byte i = 0; i < 8; i++)
{
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("RAMP"), 0);
var rp = s.GetZXSTRAMPAGE(i, rams[i]);
block.dwSize = (uint)Marshal.SizeOf(rp);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(rp);
r.Write(buff);
}
break;
}
/*
// ZXSTPLUS3
if (s._machine.Spectrum.MachineType == MachineType.ZXSpectrum128Plus3)
{
var iStruct = s.GetZXSTPLUS3();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("+3\0\0"), 0);
block.dwSize = (uint)Marshal.SizeOf(iStruct);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(iStruct);
r.Write(buff);
// ZXSTDSKFILE
if (s._machine.diskImages.Count() > 0)
{
var jStruct = s.GetZXSTDSKFILE();
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("DSK\0"), 0);
block.dwSize = (uint)Marshal.SizeOf(jStruct);
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(jStruct);
r.Write(buff);
}
}
// ZXSTTAPE
if (s._machine.tapeImages.Count() > 0)
{
var hStruct = s.GetZXSTTAPE();
var tapeData = s._machine.tapeImages[s._machine.TapeMediaIndex];
block.dwId = MediaConverter.GetUInt32(Encoding.UTF8.GetBytes("TAPE"), 0);
block.dwSize = (uint)Marshal.SizeOf(hStruct) + (uint)tapeData.Length;
buff = MediaConverter.SerializeRaw(block);
r.Write(buff);
buff = MediaConverter.SerializeRaw(hStruct);
r.Write(buff);
buff = MediaConverter.SerializeRaw(tapeData);
r.Write(buff);
char[] terminator = "\0".ToCharArray();
r.Write(terminator);
}
*/
}
result = ms.ToArray();
}
return result;
}
private ZXSTRAMPAGE GetZXSTRAMPAGE(byte page, byte[] RAM)
{
var s = new ZXSTRAMPAGE();
s.wFlags = 0; // uncompressed only at the moment
s.chPageNo = page;
s.ramPage = RAM;
return s;
}
private ZXSTCREATOR GetZXSTCREATOR()
{
var s = new ZXSTCREATOR();
var str = "BIZHAWK EMULATOR".ToCharArray();
s.szCreator = new char[32];
for (int i = 0; i < str.Length; i++)
s.szCreator[i] = str[i];
s.chMajorVersion = 1;
s.chMinorVersion = 4;
return s;
}
private ZXSTZ80REGS GetZXSTZ80REGS()
{
var s = new ZXSTZ80REGS();
s.AF = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["AF"].Value;
s.BC = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["BC"].Value;
s.DE = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["DE"].Value;
s.HL = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["HL"].Value;
s.AF1 = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["Shadow AF"].Value;
s.BC1 = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["Shadow BC"].Value;
s.DE1 = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["Shadow DE"].Value;
s.HL1 = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["Shadow HL"].Value;
s.IX = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["IX"].Value;
s.IY = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["IY"].Value;
s.SP = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["SP"].Value;
s.PC = (ushort)_machine.Spectrum.GetCpuFlagsAndRegisters()["PC"].Value;
s.I = (byte)_machine.CPU.Regs[_machine.CPU.I];
s.R = (byte)_machine.CPU.Regs[_machine.CPU.R];
s.IFF1 = (byte)(_machine.CPU.IFF1 ? 1 : 0);
s.IFF2 = (byte)(_machine.CPU.IFF2 ? 1 : 0);
s.IM = (byte)_machine.CPU.InterruptMode;
s.dwCyclesStart = (uint)(_machine.CurrentFrameCycle + _machine.ULADevice.InterruptStartTime);
s.wMemPtr = (ushort)(_machine.CPU.Regs[_machine.CPU.Z] + (_machine.CPU.Regs[_machine.CPU.W] << 8));
//s.chHoldIntReqCycles = ?
if (_machine.CPU.EIPending > 0)
{
s.chFlags |= ZXSTZF_EILAST;
}
else if (_machine.CPU.halted)
{
s.chFlags |= ZXSTZF_HALTED;
}
return s;
}
private ZXSTSPECREGS GetZXSTSPECREGS()
{
var s = new ZXSTSPECREGS();
s.chBorder = _machine.ULADevice.BorderColor > 7 ? (byte)0 : (byte)_machine.ULADevice.BorderColor;
s.chFe = _machine.LastFe;
byte x7ffd = (byte)_machine.RAMPaged;
byte x1ffd = 0;
switch (_machine.Spectrum.MachineType)
{
case MachineType.ZXSpectrum16:
case MachineType.ZXSpectrum48:
s.ch7ffd = 0;
s.unionPage = 0;
break;
case MachineType.ZXSpectrum128:
case MachineType.ZXSpectrum128Plus2:
// 7FFD
if (_machine._ROMpaged == 1)
x7ffd |= 0x10;
if (_machine.SHADOWPaged)
x7ffd |= 0x08;
if (_machine.PagingDisabled)
x7ffd |= 0x20;
break;
case MachineType.ZXSpectrum128Plus2a:
case MachineType.ZXSpectrum128Plus3:
if (_machine.UPDDiskDevice.FDD_FLAG_MOTOR)
x1ffd |= 0x08;
if (_machine.SpecialPagingMode)
{
x1ffd |= 0x01;
switch (_machine.PagingConfiguration)
{
case 1:
x1ffd |= 0x02;
break;
case 2:
x1ffd |= 0x04;
break;
case 3:
x1ffd |= 0x02;
x1ffd |= 0x04;
break;
}
}
else
{
if (_machine.ROMhigh)
x1ffd |= 0x04;
}
if (_machine.ROMlow)
x7ffd |= 0x10;
if (_machine.SHADOWPaged)
x7ffd |= 0x08;
if (_machine.PagingDisabled)
x7ffd |= 0x20;
break;
}
s.ch7ffd = x7ffd;
s.unionPage = x1ffd;
return s;
}
private ZXSTKEYBOARD GetZXSTKEYBOARD()
{
var s = new ZXSTKEYBOARD();
s.dwFlags = 0; //no issue 2 emulation
s.chKeyboardJoystick |= (byte)JoystickTypes.ZXSTKJT_NONE;
return s;
}
private ZXSTJOYSTICK GetZXSTJOYSTICK()
{
var s = new ZXSTJOYSTICK();
s.dwFlags = 0; //depreciated
s.chTypePlayer1 |= (byte)JoystickTypes.ZXSTKJT_KEMPSTON;
s.chTypePlayer2 |= (byte)JoystickTypes.ZXSTKJT_SINCLAIR1;
return s;
}
private ZXSTAYBLOCK GetZXSTAYBLOCK()
{
var s = new ZXSTAYBLOCK();
s.cFlags = 0; // no external units
s.chCurrentRegister = (byte)_machine.AYDevice.SelectedRegister;
var regs = _machine.AYDevice.ExportRegisters();
s.chAyRegs = new byte[16];
for (int i = 0; i < 16; i++)
{
s.chAyRegs[i] = (byte)regs[i];
}
return s;
}
private ZXSTTAPE GetZXSTTAPE()
{
var s = new ZXSTTAPE();
s.wFlags |= (int)CassetteRecorderState.ZXSTTP_EMBEDDED;
s.wCurrentBlockNo = (ushort)_machine.TapeDevice.CurrentDataBlockIndex;
s.dwCompressedSize = _machine.tapeImages[_machine.TapeDevice.CurrentDataBlockIndex].Length;
s.dwUncompressedSize = _machine.tapeImages[_machine.TapeDevice.CurrentDataBlockIndex].Length;
char[] ext = "tzx".ToCharArray();
s.szFileExtension = new char[16];
for (int f = 1; f < ext.Length; f++)
{
s.szFileExtension[f - 1] = ext[f];
}
return s;
}
private ZXSTPLUS3 GetZXSTPLUS3()
{
var s = new ZXSTPLUS3();
s.chNumDrives = 1;
s.fMotorOn = _machine.UPDDiskDevice.FDD_FLAG_MOTOR ? (byte)1 : (byte)0;
return s;
}
private ZXSTDSKFILE GetZXSTDSKFILE()
{
var s = new ZXSTDSKFILE();
s.wFlags = 0;
s.chDriveNum = 0;
s.dwUncompressedSize = 0;
return s;
}
}
}

View File

@ -0,0 +1,410 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
/// <summary>
/// Structs, Constants and Enums
/// http://www.spectaculator.com/docs/zx-state/intro.shtml
/// </summary>
public partial class SZX
{
#region ZX-State Header
public enum MachineIdentifier : byte
{
ZXSTMID_16K = 0,
ZXSTMID_48K = 1,
ZXSTMID_128K = 2,
ZXSTMID_PLUS2 = 3,
ZXSTMID_PLUS2A = 4,
ZXSTMID_PLUS3 = 5,
ZXSTMID_PLUS3E = 6,
ZXSTMID_PENTAGON128 = 7,
ZXSTMID_TC2048 = 8,
ZXSTMID_TC2068 = 9,
ZXSTMID_SCORPION = 10,
ZXSTMID_SE = 11,
ZXSTMID_TS2068 = 12,
ZXSTMID_PENTAGON512 = 13,
ZXSTMID_PENTAGON1024 = 14,
ZXSTMID_NTSC48K = 15,
ZXSTMID_128KE = 16
}
/// <summary>
/// If set, the emulated Spectrum uses alternate timings (one cycle later than normal timings). If reset, the emulated Spectrum uses standard timings.
/// This flag is only applicable for the ZXSTMID_16K, ZXSTMID_48K and ZXSTMID_128K models.
/// </summary>
public const int ZXSTMF_ALTERNATETIMINGS = 1;
/// <summary>
/// The zx-state header appears right at the start of a zx-state (.szx) file.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTHEADER
{
public uint dwMagic;
public byte chMajorVersion;
public byte chMinorVersion;
public byte chMachineId;
public byte chFlags;
}
#endregion
#region ZXSTBLOCK Header
/// <summary>
/// Block Header. Each real block starts with this header.
/// </summary>
public struct ZXSTBLOCK
{
public uint dwId;
public uint dwSize;
}
#endregion
#region ZXSTCREATOR
/// <summary>
/// This block identifies the program that created this zx-state file.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTCREATOR
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public char[] szCreator;
public short chMajorVersion;
public short chMinorVersion;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public byte[] chData;
}
#endregion
#region ZXSTZ80REGS
/// <summary>
/// The last instruction executed was an EI instruction or an invalid $DD or $FD prefix.
/// </summary>
public const int ZXSTZF_EILAST = 1;
/// <summary>
/// The last instruction executed was a HALT instruction. The CPU is currently executing NOPs and will continue to do so until the next interrupt occurs.
/// This flag is mutually exclusive with ZXSTZF_EILAST.
/// </summary>
public const int ZXSTZF_HALTED = 2;
/// <summary>
/// Contains the Z80 registers and other internal state values. It does not contain any specific model registers.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTZ80REGS
{
public ushort AF, BC, DE, HL;
public ushort AF1, BC1, DE1, HL1;
public ushort IX, IY, SP, PC;
public byte I;
public byte R;
public byte IFF1, IFF2;
public byte IM;
public uint dwCyclesStart;
public byte chHoldIntReqCycles;
public byte chFlags;
public ushort wMemPtr;
}
#endregion
#region ZXSTSPECREGS
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTSPECREGS
{
public byte chBorder;
public byte ch7ffd;
public byte unionPage;
public byte chFe;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] chReserved;
}
#endregion
#region ZXSTAYBLOCK
/// <summary>
/// Fuller Box emulation
/// </summary>
public const int ZXSTAYF_FULLERBOX = 1;
/// <summary>
/// Melodik Soundbox emulation.
/// This is essentially an AY chip for older Spectrums that uses the same ports as that found in 128k Spectrums
/// </summary>
public const int ZXSTAYF_128AY = 2;
/// <summary>
/// The state of the AY chip found in all 128k Spectrums, Pentagons, Scorpions and Timex machines.
/// This block may also be present for 16k/48k Spectrums if Fuller Box or Melodik emulation is enabled.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTAYBLOCK
{
public byte cFlags;
public byte chCurrentRegister;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] chAyRegs;
}
#endregion
#region ZXSTRAMPAGE
/// <summary>
/// Ram pages are compressed using Zlib
/// </summary>
public const int ZXSTRF_COMPRESSED = 1;
/// <summary>
/// zx-state files will contain a number of 16KB RAM page blocks, depending on the specific Spectrum model.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTRAMPAGE
{
public ushort wFlags;
public byte chPageNo;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4000)]
public byte[] ramPage;
}
#endregion
#region ZXSTKEYBOARD
/// <summary>
/// Keyboard state
/// </summary>
public const int ZXSTKF_ISSUE2 = 1;
/// <summary>
/// Supported joystick types
/// </summary>
public enum JoystickTypes
{
ZXSTKJT_KEMPSTON = 0,
ZXSTKJT_FULLER = 1,
ZXSTKJT_CURSOR = 2,
ZXSTKJT_SINCLAIR1 = 3,
ZXSTKJT_SINCLAIR2 = 4,
ZXSTKJT_SPECTRUMPLUS = 5,
ZXSTKJT_TIMEX1 = 6,
ZXSTKJT_TIMEX2 = 7,
ZXSTKJT_NONE = 8
}
/// <summary>
/// The state of the Spectrum keyboard and any keyboard joystick emulation.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTKEYBOARD
{
public uint dwFlags;
public byte chKeyboardJoystick;
}
#endregion
#region ZXSTJOYSTICK
/// <summary>
/// Joystick setup for both players.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTJOYSTICK
{
public uint dwFlags;
public byte chTypePlayer1;
public byte chTypePlayer2;
}
#endregion
#region ZXSTTAPE
/// <summary>
/// Cassette Recorder state
/// </summary>
public enum CassetteRecorderState
{
ZXSTTP_EMBEDDED = 1,
ZXSTTP_COMPRESSED = 2
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTTAPE
{
public ushort wCurrentBlockNo;
public ushort wFlags;
public int dwUncompressedSize;
public int dwCompressedSize;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public char[] szFileExtension;
}
#endregion
#region ZXSTPLUS3
/// <summary>
/// The number of drives connected to the Spectrum +3 and whether their motors are turned on.
/// Any blocks specifying which disk files are in which drive will follow this one.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTPLUS3
{
public byte chNumDrives;
public byte fMotorOn;
}
#endregion
#region ZXSTDSKFILE
/// <summary>
/// Not implemented. All disk images are currently links to external .dsk or .ipf files
/// </summary>
public const int ZXSTDSKF_COMPRESSED = 1;
/// <summary>
/// Not implemented. All disk images are currently links to external .dsk or .ipf files
/// </summary>
public const int ZXSTDSKF_EMBEDDED = 2;
/// <summary>
/// When a double-sided disk is inserted into a single-sided drive, specifies the side being read from/written to.
/// If set, Side B is the active side, otherwise it is Side A.
/// </summary>
public const int ZXSTDSKF_SIDEB = 3;
/// <summary>
/// Each +3 disk drive that has a disk inserted in it will have one of these blocks.
/// They follow the ZXSTPLUS3 block which identifies the number of drives.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZXSTDSKFILE
{
public ushort wFlags;
public byte chDriveNum;
public int dwUncompressedSize;
}
#endregion
#region Not Yet Implemented
#region ZXSTATASP
#endregion
#region ZXSTATARAM
#endregion
#region ZXSTCF
#endregion
#region ZXSTCFRAM
#endregion
#region ZXSTCOVOX
#endregion
#region ZXSTBETA128
#endregion
#region ZXSTBETADISK
#endregion
#region ZXSTDOCK
#endregion
#region ZXSTGS
#endregion
#region ZXSTGSRAMPAGE
#endregion
#region ZXSTIF1
#endregion
#region ZXSTIF2ROM
#endregion
#region ZXSTMCART
#endregion
#region ZXSTMOUSE
#endregion
#region ZXSTMULTIFACE
#endregion
#region ZXSTOPUS
#endregion
#region ZXSTOPUSDISK
#endregion
#region ZXSTPLUSD
#endregion
#region ZXSTPLUSDDISK
#endregion
#region ZXSTROM
#endregion
#region ZXSTSCLDREGS
#endregion
#region ZXSTSIDE
#endregion
#region ZXSTSPECDRUM
#endregion
#region ZXSTUSPEECH
#endregion
#region ZXSTZXPRINTER
#endregion
#endregion
}
}

View File

@ -62,5 +62,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
return m;
}
public byte[] GetSZXSnapshot()
{
return SZX.ExportSZX(_machine);
//return System.Text.Encoding.Default.GetString(data);
}
}
}

View File

@ -62,7 +62,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
deterministicEmulation = deterministic.Value;
}
switch (SyncSettings.MachineType)
MachineType = SyncSettings.MachineType;
switch (MachineType)
{
case MachineType.ZXSpectrum16:
ControllerDefinition = ZXSpectrumControllerDefinition;
@ -146,6 +148,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
private readonly TraceBuffer _tracer;
public IController _controller;
public SpectrumBase _machine;
public MachineType MachineType;
public List<GameInfo> _gameInfo;