This commit is contained in:
nog5 2018-09-14 18:38:25 +02:00
commit ab78d3ff0d
59 changed files with 3376 additions and 946 deletions

4
.gitmodules vendored
View File

@ -11,3 +11,7 @@
path = waterbox/snes9x
url = https://github.com/TASVideos/snes9x.git
branch = bizsnes
[submodule "mgba"]
path = mgba
url = https://github.com/TASVideos/mgba.git
branch = bizhawk-0.6

View File

@ -140,7 +140,7 @@ namespace BizHawk.Client.Common
//handling of initial .. was removed (Path.GetFullPath can handle it)
//handling of file:// or file:\\ was removed (can Path.GetFullPath handle it? not sure)
// all pad paths default to EXE
// all bad paths default to EXE
return GetExeDirectoryAbsolute();
}
@ -461,5 +461,19 @@ namespace BizHawk.Client.Common
return entry;
}
/// <summary>
/// Puts the currently configured temp path into the environment for use as actual temp directory
/// </summary>
public static void RefreshTempPath()
{
if (Global.Config.PathEntries.TempFilesFragment != "")
{
//TODO - BUG - needs to route through PathManager.MakeAbsolutePath or something similar, but how?
string target = Global.Config.PathEntries.TempFilesFragment;
BizHawk.Common.TempFileManager.HelperSetTempPath(target);
}
}
}
}

View File

@ -37,6 +37,7 @@ namespace BizHawk.Client.Common
{
PathEntries.ResolveWithDefaults();
HotkeyBindings.ResolveWithDefaults();
PathManager.RefreshTempPath();
}
// Core preference for generic file extension, key: file extension, value: a systemID or empty if no preference

View File

@ -165,6 +165,8 @@ namespace BizHawk.Client.Common
public string GlobalRomFragment => Global.Config.PathEntries["Global", "ROM"].Path;
public string TempFilesFragment => Global.Config.PathEntries["Global", "Temp Files"].Path;
// this one is special
public string GlobalBaseFragment => Global.Config.PathEntries["Global", "Base"].Path;
@ -184,6 +186,7 @@ namespace BizHawk.Client.Common
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "TAStudio states", Path = Path.Combine(".", "Movies", "TAStudio states"), Ordinal = 12 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Multi-Disk Bundles", Path = Path.Combine(".", ""), Ordinal = 13 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "External Tools", Path = Path.Combine(".", "ExternalTools"), Ordinal = 14 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Temp Files", Path = "", Ordinal = 15 },
new PathEntry { System = "INTV", SystemDisplayName = "Intellivision", Type = "Base", Path = Path.Combine(".", "Intellivision"), Ordinal = 0 },
new PathEntry { System = "INTV", SystemDisplayName = "Intellivision", Type = "ROM", Path = ".", Ordinal = 1 },

View File

@ -133,10 +133,10 @@ namespace BizHawk.Client.Common
var input = Movie.GetInputState(Global.Emulator.Frame);
// adelikat: TODO: this is likely the source of frame 0 TAStudio bugs, I think the intent is to check if the movie is 0 length?
if (Global.Emulator.Frame == 0) // Hacky
{
HandleMovieAfterFrameLoop(); // Frame 0 needs to be handled.
}
//if (Global.Emulator.Frame == 0) // Hacky
//{
// HandleMovieAfterFrameLoop(); // Frame 0 needs to be handled.
//}
if (input == null)
{
@ -327,6 +327,10 @@ namespace BizHawk.Client.Common
{
HandlePlaybackEnd();
}
else if (Movie.IsPlaying && !Movie.IsFinished)
{
LatchInputFromLog();
}
}
public bool HandleMovieLoadState(string path)
@ -456,6 +460,7 @@ namespace BizHawk.Client.Common
else
{
Movie.StartNewPlayback();
LatchInputFromLog();
}
}

View File

@ -281,9 +281,35 @@ namespace BizHawk.Client.Common
["Space"] = '_'
},
["ZXSpectrum"] = new Dictionary<string, char>
{
["Return"] = 'e',
["Space"] = 's',
{
["Caps Shift"] = '^',
["Caps Lock"] = 'L',
["Period"] = '_',
["Symbol Shift"] = 'v',
["Semi-Colon"] = ';',
["Quote"] = '"',
["Comma"] = ',',
["True Video"] = 'T',
["Inv Video"] = 'I',
["Break"] = 'B',
["Delete"] = 'D',
["Graph"] = 'G',
["Extend Mode"] = 'M',
["Edit"] = 'E',
["Play Tape"] = 'P',
["Stop Tape"] = 'S',
["RTZ Tape"] = 'r',
["Record Tape"] = 'R',
["Insert Next Tape"] = '>',
["Insert Previous Tape"] = '<',
["Next Tape Block"] = ']',
["Prev Tape Block"] = '[',
["Get Tape Status"] = 'S',
["Insert Next Disk"] = '}',
["Insert Previous Disk"] = '{',
["Get Disk Status"] = 's',
["Return"] = 'e',
["Space"] = '-',
["Up Cursor"] = 'u',
["Down Cursor"] = 'd',
["Left Cursor"] = 'l',

View File

@ -75,7 +75,7 @@ namespace BizHawk.Client.Common
_mDisk = disk;
if (disk)
{
var path = TempFileCleaner.GetTempFilename("movieOnDisk");
var path = TempFileManager.GetTempFilename("movieOnDisk");
stream = new FileStream(path, FileMode.Create, System.Security.AccessControl.FileSystemRights.FullControl, FileShare.None, 4 * 1024, FileOptions.DeleteOnClose);
}
else

View File

@ -27,7 +27,7 @@ namespace BizHawk.Client.Common
_mCapacity = capacity;
if (onDisk)
{
var path = TempFileCleaner.GetTempFilename("rewindbuf");
var path = TempFileManager.GetTempFilename("rewindbuf");
// I checked the DeleteOnClose operation to make sure it cleans up when the process is aborted, and it seems to.
// Otherwise we would have a more complex tempfile management problem here.

View File

@ -21,6 +21,14 @@ namespace BizHawk.Client.Common
/// </summary>
public static SeparatorWatch Instance => new SeparatorWatch();
public static SeparatorWatch NewSeparatorWatch(string description)
{
return new SeparatorWatch
{
Notes = description
};
}
/// <summary>
/// Get the appropriate DisplayType
/// </summary>
@ -45,10 +53,10 @@ namespace BizHawk.Client.Common
/// </summary>
public override int Previous => 0;
/// <summary>
/// Ignore that stuff
/// </summary>
public override string ValueString => "";
/// <summary>
/// Ignore that stuff
/// </summary>
public override string ValueString => Notes; //"";
/// <summary>
/// Ignore that stuff
@ -62,13 +70,26 @@ namespace BizHawk.Client.Common
/// <returns>A well formatted string representation</returns>
public override string ToDisplayString()
{
return "----";
if (Notes == null || Notes == "")
return "----";
else
return Notes;
}
/// <summary>
/// Ignore that stuff
/// <summary>
/// Transforms the current instance into a string
/// </summary>
public override bool Poke(string value)
/// <returns>A <see cref="string"/> representation of the current <see cref="Watch"/></returns>
public override string ToString()
{
return $"0\tS\t_\t1\t\t{Notes.Trim('\r', '\n')}";
//return $"{(Domain == null && Address == 0 ? "0" : Address.ToHexString((Domain?.Size ?? 0xFF - 1).NumHexDigits()))}\t{SizeAsChar}\t{TypeAsChar}\t{Convert.ToInt32(BigEndian)}\t{Domain?.Name}\t{Notes.Trim('\r', '\n')}";
}
/// <summary>
/// Ignore that stuff
/// </summary>
public override bool Poke(string value)
{
return false;
}
@ -95,6 +116,8 @@ namespace BizHawk.Client.Common
/// </summary>
public override void Update()
{
}
//AddressString = (Notes == null || Notes == "") ? "" : Notes;
}
}
}

View File

@ -142,7 +142,7 @@ namespace BizHawk.Client.Common
{
default:
case WatchSize.Separator:
return SeparatorWatch.Instance;
return SeparatorWatch.NewSeparatorWatch(note);
case WatchSize.Byte:
return new ByteWatch(domain, address, type, bigEndian, note, (byte)value, (byte)prev, changeCount);
case WatchSize.Word:

View File

@ -76,7 +76,7 @@
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(291, 165);
this.label2.TabIndex = 2;
this.label2.Text = "EMULATES\r\nYOUR\r\nMOM";
this.label2.Text = "EMULATES\r\nY̶̷̼̬̟̘̦̼͙̝̯̦̠̜͙͖̘ͨ̒͂͐ͪ͂̒̄ͯͯ͌ͨͯ̽ͨ́͝ͅO̡̝̞̗̩͖͖̼̹̖̫͍̙̖͓̩ͪ͂̊ͭ͑ͮͤ̀̄͌̑ͨͣͩ̿ͫ͂͟͝͞ͅU̴̮̝͍̜̰̦ͥ̓ͩ̌̎̾ͥͪ̋̾́͠Rͨ͊̉̾҉̢̫͔̗͓͖̫̫̘̖̰͟\r\nMOM";
//
// timer1
//

View File

@ -1840,6 +1840,9 @@
<None Include="config\ControllerImages\GENController.png" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\MoveTop.png" />
<None Include="Resources\MoveBottom.png" />
<None Include="Resources\MoveTop.bmp" />
<None Include="Resources\ZXSpectrumKeyboard.bmp" />
<None Include="images\WSW.png" />
<None Include="images\WNW.png" />

View File

@ -610,18 +610,19 @@ namespace BizHawk.Client.EmuHawk
FilterProgram UpdateSourceInternal(JobInfo job)
{
if (job.chain_outsize.Width == 0 || job.chain_outsize.Height == 0)
{
//this has to be a NOP, because lots of stuff will malfunction on a 0-sized viewport
return null;
}
//no drawing actually happens. it's important not to begin drawing on a control
if (!job.simulate && !job.offscreen)
{
GLManager.Activate(CR_GraphicsControl);
}
if (job.chain_outsize.Width == 0 || job.chain_outsize.Height == 0)
{
//this has to be a NOP, because lots of stuff will malfunction on a 0-sized viewport
UpdateSourceDrawingWork(job); //but we still need to do this, because of vsync
return null;
}
IVideoProvider videoProvider = job.videoProvider;
bool simulate = job.simulate;
Size chain_outsize = job.chain_outsize;

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

@ -115,7 +115,7 @@ namespace BizHawk.Client.EmuHawk
}
}
BizHawk.Common.TempFileCleaner.Start();
BizHawk.Common.TempFileManager.Start();
HawkFile.ArchiveHandlerFactory = new SevenZipSharpArchiveHandler();

View File

@ -900,6 +900,16 @@ namespace BizHawk.Client.EmuHawk.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap MoveBottom {
get {
object obj = ResourceManager.GetObject("MoveBottom", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
@ -930,6 +940,16 @@ namespace BizHawk.Client.EmuHawk.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap MoveTop {
get {
object obj = ResourceManager.GetObject("MoveTop", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>

View File

@ -1560,4 +1560,10 @@
<data name="ZXSpectrumKeyboards" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\config\ControllerImages\ZXSpectrumKeyboards.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>
<data name="MoveBottom" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\MoveBottom.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="MoveTop" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\MoveTop.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -6,14 +6,17 @@ namespace BizHawk.Client.EmuHawk
[AttributeUsage(AttributeTargets.Class)]
public class ToolAttribute : Attribute
{
public ToolAttribute(bool released, string[] supportedSystems)
public ToolAttribute(bool released, string[] supportedSystems, string[] unsupportedCores = null)
{
Released = released;
SupportedSystems = supportedSystems;
UnsupportedCores = unsupportedCores;
}
public bool Released { get; private set; }
public IEnumerable<string> SupportedSystems { get; private set; }
public IEnumerable<string> UnsupportedCores { get; private set; }
}
}

View File

@ -308,6 +308,9 @@ namespace BizHawk.Client.EmuHawk
private void Ok_Click(object sender, EventArgs e)
{
SaveSettings();
PathManager.RefreshTempPath();
GlobalWin.OSD.AddMessage("Path settings saved");
Close();
}

View File

@ -20,7 +20,8 @@ namespace BizHawk.Client.EmuHawk
//Verify all wording in the error reports
[Tool(released: true, supportedSystems: new[] { "GB", "GBA", "GEN", "N64", "NES", "PSX", "SAT", "SMS", "SNES" })]
[Tool(released: true, supportedSystems: new[] { "GB", "GBA", "GEN", "N64", "NES", "PSX", "SAT", "SMS", "SNES" },
unsupportedCores: new[] { "Snes9x" })]
public partial class GameShark : Form, IToolForm, IToolFormAutoConfig
{
#region " Game Genie Dictionary "

View File

@ -391,6 +391,13 @@ namespace BizHawk.Client.EmuHawk
return GlobalWin.MainForm.DesktopLocation.Y;
}
[LuaMethodExample("local incbhver = client.getversion( );")]
[LuaMethod("getversion", "Returns the current stable BizHawk version")]
public static string GetVersion()
{
return VersionInfo.Mainversion;
}
[LuaMethodExample("local nlcliget = client.getavailabletools( );")]
[LuaMethod("getavailabletools", "Returns a list of the tools currently open")]
public LuaTable GetAvailableTools()

View File

@ -1314,7 +1314,7 @@ namespace BizHawk.Client.EmuHawk
ToolStripMenuItem sender = o as ToolStripMenuItem;
foreach (ToolStripMenuItem menuItem in dummyObject.DropDownItems)
{
TasView.AllColumns.Find(c => c.Name == (string)menuItem.Tag).Visible = sender.Checked;
menuItem.Checked ^= true;
}
CurrentTasMovie.FlagChanges();
@ -1342,7 +1342,7 @@ namespace BizHawk.Client.EmuHawk
ToolStripMenuItem sender = o as ToolStripMenuItem;
foreach (ToolStripMenuItem menuItem in dummyObject.DropDownItems)
{
TasView.AllColumns.Find(c => c.Name == (string)menuItem.Tag).Visible = sender.Checked;
menuItem.Checked ^= true;
}
CurrentTasMovie.FlagChanges();

View File

@ -10,6 +10,7 @@ using System.Windows.Forms;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
using BizHawk.Common.ReflectionExtensions;
using BizHawk.Client.EmuHawk.CoreExtensions;
namespace BizHawk.Client.EmuHawk
{
@ -743,13 +744,30 @@ namespace BizHawk.Client.EmuHawk
.OfType<ToolAttribute>()
.FirstOrDefault();
// If no supported systems mentioned assume all
if (attr?.SupportedSystems != null && attr.SupportedSystems.Any())
// start with the assumption that if no supported systems are mentioned and no unsupported cores are mentioned
// then this is available for all
bool supported = true;
if (attr?.SupportedSystems != null && attr.SupportedSystems.Any())
{
return attr.SupportedSystems.Contains(Global.Emulator.SystemId);
}
// supported systems are available
supported = attr.SupportedSystems.Contains(Global.Emulator.SystemId);
return true;
if (supported)
{
// check for a core not supported override
if (attr.UnsupportedCores.Contains(Global.Emulator.DisplayName()))
supported = false;
}
}
else if (attr?.UnsupportedCores != null && attr.UnsupportedCores.Any())
{
// no supported system specified, but unsupported cores are
if (attr.UnsupportedCores.Contains(Global.Emulator.DisplayName()))
supported = false;
}
return supported;
}
// Note: Referencing these properties creates an instance of the tool and persists it. They should be referenced by type if this is not desired
@ -973,7 +991,7 @@ namespace BizHawk.Client.EmuHawk
public void LoadGameGenieEc()
{
if (GlobalWin.Tools.IsAvailable<GameShark>())
{
{
GlobalWin.Tools.Load<GameShark>();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -94,6 +94,14 @@ namespace BizHawk.Client.EmuHawk
get { return SelectedItems.Where(x => !x.IsSeparator); }
}
private IEnumerable<Watch> SelectedSeparators
{
get
{
return SelectedItems.Where(x => x.IsSeparator);
}
}
public IEnumerable<Watch> Watches
{
get
@ -427,6 +435,32 @@ namespace BizHawk.Client.EmuHawk
UpdateValues();
}
else if (SelectedSeparators.Any() && !duplicate)
{
var inputPrompt = new InputPrompt
{
Text = "Edit Separator",
StartLocation = this.ChildPointToScreen(WatchListView),
Message = "Separator Text:",
TextInputType = InputPrompt.InputType.Text
};
var result = inputPrompt.ShowHawkDialog();
if (result == DialogResult.OK)
{
Changes();
for (int i = 0; i < SelectedSeparators.Count(); i++)
{
var sep = SelectedSeparators.ToList()[i];
sep.Notes = inputPrompt.PromptText;
_watches[indexes[i]] = sep;
}
}
UpdateValues();
}
}
private string ComputeDisplayType(Watch w)
@ -627,11 +661,21 @@ namespace BizHawk.Client.EmuHawk
{
text = "";
if (index >= _watches.Count || _watches[index].IsSeparator)
if (index >= _watches.Count)
{
return;
}
if (_watches[index].IsSeparator)
{
if (WatchListView.Columns[column].Name == WatchList.ADDRESS)
{
text = _watches[index].Notes;
}
return;
}
var columnName = WatchListView.Columns[column].Name;
switch (columnName)
@ -733,6 +777,8 @@ namespace BizHawk.Client.EmuHawk
RemoveWatchMenuItem.Enabled =
MoveUpMenuItem.Enabled =
MoveDownMenuItem.Enabled =
MoveTopMenuItem.Enabled =
MoveBottomMenuItem.Enabled =
SelectedIndices.Any();
PokeAddressMenuItem.Enabled =
@ -913,7 +959,67 @@ namespace BizHawk.Client.EmuHawk
WatchListView.ItemCount = _watches.Count;
}
private void SelectAllMenuItem_Click(object sender, EventArgs e)
private void MoveTopMenuItem_Click(object sender, EventArgs e)
{
var indexes = SelectedIndices.ToList();
if (!indexes.Any())
{
return;
}
for (int i = 0; i < indexes.Count; i++)
{
var watch = _watches[indexes[i]];
_watches.RemoveAt(indexes[i]);
_watches.Insert(i, watch);
indexes[i] = i;
}
Changes();
WatchListView.SelectedIndices.Clear();
foreach (var t in indexes)
{
WatchListView.SelectItem(t, true);
}
WatchListView.ItemCount = _watches.Count;
}
private void MoveBottomMenuItem_Click(object sender, EventArgs e)
{
var indices = SelectedIndices.ToList();
if (indices.Count == 0) // || indices.Last() == _watches.Count - 1)
{
return;
}
for (var i = 0; i < indices.Count; i++)
{
var watch = _watches[indices[i] - i];
_watches.RemoveAt(indices[i] - i);
_watches.Insert(_watches.Count, watch);
//_watches.Add(watch);
//indices[i] = (_watches.Count - 1 - indices.Count) + i;
}
var newInd = new List<int>();
for (int i = 0, x = _watches.Count - indices.Count; i < indices.Count; i++, x++)
{
newInd.Add(x);
}
WatchListView.SelectedIndices.Clear();
foreach (var t in newInd)
{
WatchListView.SelectItem(t, true);
}
Changes();
WatchListView.ItemCount = _watches.Count;
}
private void SelectAllMenuItem_Click(object sender, EventArgs e)
{
WatchListView.SelectAll();
}
@ -1066,6 +1172,8 @@ namespace BizHawk.Client.EmuHawk
InsertSeperatorContextMenuItem.Visible =
MoveUpContextMenuItem.Visible =
MoveDownContextMenuItem.Visible =
MoveTopContextMenuItem.Visible =
MoveBottomContextMenuItem.Visible =
indexes.Count > 0;
ReadBreakpointContextMenuItem.Visible =
@ -1231,5 +1339,5 @@ namespace BizHawk.Client.EmuHawk
{
base.GenericDragEnter(sender, e);
}
}
}
}

View File

@ -120,13 +120,66 @@
<metadata name="ListViewContextMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>264, 17</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="MoveTopContextMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
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
AAAAAElFTkSuQmCC
</value>
</data>
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>434, 17</value>
</metadata>
<metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>159, 17</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="newToolStripButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
@ -215,6 +268,59 @@
<metadata name="RamWatchMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<data name="MoveTopMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
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
AAAAAElFTkSuQmCC
</value>
</data>
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAQAEAAAAAAAAAAAAAAAEAAAAA

View File

@ -12,7 +12,7 @@ namespace BizHawk.Common
public InstanceDll(string dllPath)
{
// copy the dll to a temp directory
var path = TempFileCleaner.GetTempFilename(string.Format("{0}", Path.GetFileNameWithoutExtension(dllPath)),".dll",false);
var path = TempFileManager.GetTempFilename(string.Format("{0}", Path.GetFileNameWithoutExtension(dllPath)),".dll",false);
using (var stream = new FileStream(path, FileMode.Create, System.Security.AccessControl.FileSystemRights.FullControl, FileShare.ReadWrite | FileShare.Delete, 4 * 1024, FileOptions.None))
using (var sdll = File.OpenRead(dllPath))
sdll.CopyTo(stream);
@ -32,7 +32,7 @@ namespace BizHawk.Common
var lastError = GetLastError();
throw new InvalidOperationException($"Failed to load plugin {path}, error code: 0x{lastError:X}");
}
var newfname = TempFileCleaner.RenameTempFilenameForDelete(path);
var newfname = TempFileManager.RenameTempFilenameForDelete(path);
File.Move(path, newfname);
}
finally

View File

@ -1,4 +1,6 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
@ -10,7 +12,7 @@ namespace BizHawk.Common
/// Files shouldn't be named that unless they're safe to delete, but notably, they may stil be in use. That won't hurt this component.
/// When they're no longer in use, this component will then be able to delete them.
/// </summary>
public static class TempFileCleaner
public static class TempFileManager
{
// TODO - manage paths other than %temp%, make not static, or allow adding multiple paths to static instance
@ -41,7 +43,7 @@ namespace BizHawk.Common
public static void Start()
{
lock (typeof(TempFileCleaner))
lock (typeof(TempFileManager))
{
if (thread != null)
{
@ -64,27 +66,49 @@ namespace BizHawk.Common
static void ThreadProc()
{
var di = new DirectoryInfo(Path.GetTempPath());
//squirrely logic, trying not to create garbage
HashSet<string> knownTempDirs = new HashSet<string>();
List<DirectoryInfo> dis = new List<DirectoryInfo>();
for (;;)
{
var fis = di.GetFiles("bizdelete-*");
foreach (var fi in fis)
lock (typeof(TempFileManager))
{
knownTempDirs.Add(Path.GetTempPath());
if (dis.Count != knownTempDirs.Count)
dis = knownTempDirs.Select(x => new DirectoryInfo(x)).ToList();
}
foreach(var di in dis)
{
FileInfo[] fis = null;
try
{
// SHUT. UP. THE. EXCEPTIONS.
#if WINDOWS
DeleteFileW(fi.FullName);
#else
fi.Delete();
#endif
fis = di.GetFiles("bizdelete-*");
}
catch
{
}
if(fis != null)
{
foreach (var fi in fis)
{
try
{
// SHUT. UP. THE. EXCEPTIONS.
#if WINDOWS
DeleteFileW(fi.FullName);
#else
fi.Delete();
#endif
}
catch
{
}
// try not to do more than one thing per frame
Thread.Sleep(100);
// try not to do more than one thing per frame
Thread.Sleep(100);
}
}
}
// try not to slam the filesystem too hard, we dont want this to cause any hiccups
@ -97,5 +121,13 @@ namespace BizHawk.Common
}
static Thread thread;
public static void HelperSetTempPath(string path)
{
//yes... this is how we're doing it, for now, until it's proven to be troublesome
Directory.CreateDirectory(path);
Environment.SetEnvironmentVariable("TMP", path);
Environment.SetEnvironmentVariable("TEMP", path);
}
}
}

View File

@ -288,11 +288,15 @@
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum128KPlus2a\ZX128Plus2a.Memory.cs" />
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum128KPlus2a\ZX128Plus2a.Port.cs" />
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum16K\ZX16.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\Disk\CPCExtendedFloppyDisk.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\Disk\CPCFloppyDisk.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\Disk\CPCFormat\CPCExtendedFloppyDisk.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\Disk\CPCFormat\CPCFloppyDisk.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\Disk\DiskType.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\Disk\IPFFormat\IPFFloppyDisk.cs" />
<Compile Include="Computers\SinclairSpectrum\Media\Disk\UDIFormat\UDI1_0FloppyDisk.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

@ -816,6 +816,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
fdd = new CPCFloppyDisk();
found = fdd.ParseDisk(diskData);
break;
case DiskType.IPF:
fdd = new IPFFloppyDisk();
found = fdd.ParseDisk(diskData);
break;
case DiskType.UDI:
fdd = new UDI1_0FloppyDisk();
found = fdd.ParseDisk(diskData);
break;
}
if (found)

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

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -141,6 +142,66 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
diskImages.Add(m);
Spectrum._diskInfo.Add(Spectrum._gameInfo[cnt]);
break;
case SpectrumMediaType.DiskDoubleSided:
// this is a bit tricky. we will attempt to parse the double sided disk image byte array,
// then output two separate image byte arrays
List<byte[]> working = new List<byte[]>();
foreach (DiskType type in Enum.GetValues(typeof(DiskType)))
{
bool found = false;
switch (type)
{
case DiskType.CPCExtended:
found = CPCExtendedFloppyDisk.SplitDoubleSided(m, working);
break;
case DiskType.CPC:
found = CPCFloppyDisk.SplitDoubleSided(m, working);
break;
case DiskType.UDI:
found = UDI1_0FloppyDisk.SplitDoubleSided(m, working);
break;
}
if (found)
{
// add side 1
diskImages.Add(working[0]);
// add side 2
diskImages.Add(working[1]);
Common.GameInfo one = new Common.GameInfo();
Common.GameInfo two = new Common.GameInfo();
var gi = Spectrum._gameInfo[cnt];
for (int i = 0; i < 2; i++)
{
Common.GameInfo work = new Common.GameInfo();
if (i == 0)
{
work = one;
}
else if (i == 1)
{
work = two;
}
work.FirmwareHash = gi.FirmwareHash;
work.Hash = gi.Hash;
work.Name = gi.Name + " (Parsed Side " + (i + 1) + ")";
work.Region = gi.Region;
work.NotInDatabase = gi.NotInDatabase;
work.Status = gi.Status;
work.System = gi.System;
Spectrum._diskInfo.Add(work);
}
}
else
{
}
}
break;
}
cnt++;
@ -192,13 +253,38 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
if (hdr.ToUpper().Contains("EXTENDED CPC DSK") || hdr.ToUpper().Contains("MV - CPC"))
{
// spectrum .dsk disk file
return SpectrumMediaType.Disk;
// check for number of sides
var sides = data[0x31];
if (sides == 1)
return SpectrumMediaType.Disk;
else
return SpectrumMediaType.DiskDoubleSided;
}
if (hdr.ToUpper().StartsWith("FDI"))
{
// spectrum .fdi disk file
return SpectrumMediaType.Disk;
}
if (hdr.ToUpper().StartsWith("CAPS"))
{
// IPF format file
return SpectrumMediaType.Disk;
}
if (hdr.ToUpper().StartsWith("UDI!") && data[0x08] == 0)
{
// UDI v1.0
if (hdr.StartsWith("udi!"))
{
throw new NotSupportedException("ZXHawk currently does not supported UDIv1.0 with compression.");
}
else
{
if (data[0x0A] == 0x01)
return SpectrumMediaType.DiskDoubleSided;
else
return SpectrumMediaType.Disk;
}
}
// tape checking
if (hdr.ToUpper().StartsWith("ZXTAPE!"))
@ -231,6 +317,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
None,
Tape,
Disk
Disk,
DiskDoubleSided
}
}

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,5 +1,7 @@
using System.Text;
using BizHawk.Common;
using System;
using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
@ -53,6 +55,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
throw new System.NotImplementedException(sbm.ToString());
}
if (DiskHeader.NumberOfTracks > 42)
{
StringBuilder sbm = new StringBuilder();
sbm.AppendLine();
sbm.AppendLine();
sbm.AppendLine("The detected disk is an " + DiskHeader.NumberOfTracks + " track disk image.");
sbm.AppendLine("This is currently incompatible with the emulated +3 disk drive (42 tracks).");
sbm.AppendLine("Likely the disk image is an 80 track betadisk or opus image, the drives and controllers for which are not currently emulated in ZXHawk");
throw new System.NotImplementedException(sbm.ToString());
}
for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++)
{
DiskHeader.TrackSizes[i] = data[pos++] * 256;
@ -144,6 +157,88 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
return true;
}
/// <summary>
/// Takes a double-sided disk byte array and converts into 2 single-sided arrays
/// </summary>
/// <param name="data"></param>
/// <param name="results"></param>
/// <returns></returns>
public static bool SplitDoubleSided(byte[] data, List<byte[]> results)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
if (!ident.ToUpper().Contains("EXTENDED CPC DSK"))
{
// incorrect format
return false;
}
byte[] S0 = new byte[data.Length];
byte[] S1 = new byte[data.Length];
// disk info block
Array.Copy(data, 0, S0, 0, 0x100);
Array.Copy(data, 0, S1, 0, 0x100);
// change side number
S0[0x31] = 1;
S1[0x31] = 1;
// extended format can have different track sizes
int[] trkSizes = new int[data[0x30] * data[0x31]];
int pos = 0x34;
for (int i = 0; i < data[0x30] * data[0x31]; i++)
{
trkSizes[i] = data[pos] * 256;
// clear destination trk sizes (will be added later)
S0[pos] = 0;
S1[pos] = 0;
pos++;
}
// start at track info blocks
int mPos = 0x100;
int s0Pos = 0x100;
int s0tCount = 0;
int s1tCount = 0;
int s1Pos = 0x100;
int tCount = 0;
while (tCount < data[0x30] * data[0x31])
{
// which side is this?
var side = data[mPos + 0x11];
if (side == 0)
{
// side 1
Array.Copy(data, mPos, S0, s0Pos, trkSizes[tCount]);
s0Pos += trkSizes[tCount];
// trk size table
S0[0x34 + s0tCount++] = (byte)(trkSizes[tCount] / 256);
}
else if (side == 1)
{
// side 2
Array.Copy(data, mPos, S1, s1Pos, trkSizes[tCount]);
s1Pos += trkSizes[tCount];
// trk size table
S1[0x34 + s1tCount++] = (byte)(trkSizes[tCount] / 256);
}
mPos += trkSizes[tCount++];
}
byte[] s0final = new byte[s0Pos];
byte[] s1final = new byte[s1Pos];
Array.Copy(S0, 0, s0final, 0, s0Pos);
Array.Copy(S1, 0, s1final, 0, s1Pos);
results.Add(s0final);
results.Add(s1final);
return true;
}
/// <summary>
/// State serlialization
/// </summary>

View File

@ -1,5 +1,7 @@
using System.Text;
using BizHawk.Common;
using System;
using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
@ -53,6 +55,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
throw new System.NotImplementedException(sbm.ToString());
}
if (DiskHeader.NumberOfTracks > 42)
{
StringBuilder sbm = new StringBuilder();
sbm.AppendLine();
sbm.AppendLine();
sbm.AppendLine("The detected disk is an " + DiskHeader.NumberOfTracks + " track disk image.");
sbm.AppendLine("This is currently incompatible with the emulated +3 disk drive (42 tracks).");
sbm.AppendLine("Likely the disk image is an 80 track betadisk or opus image, the drives and controllers for which are not currently emulated in ZXHawk");
throw new System.NotImplementedException(sbm.ToString());
}
// standard CPC format all track sizes are the same in the image
for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++)
{
@ -149,6 +162,77 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
return true;
}
/// <summary>
/// Takes a double-sided disk byte array and converts into 2 single-sided arrays
/// </summary>
/// <param name="data"></param>
/// <param name="results"></param>
/// <returns></returns>
public static bool SplitDoubleSided(byte[] data, List<byte[]> results)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
if (!ident.ToUpper().Contains("MV - CPC"))
{
// incorrect format
return false;
}
byte[] S0 = new byte[data.Length];
byte[] S1 = new byte[data.Length];
// disk info block
Array.Copy(data, 0, S0, 0, 0x100);
Array.Copy(data, 0, S1, 0, 0x100);
// change side number
S0[0x31] = 1;
S1[0x31] = 1;
var trkSize = MediaConverter.GetWordValue(data, 0x32);
// start at track info blocks
int mPos = 0x100;
int s0Pos = 0x100;
int s1Pos = 0x100;
var numTrks = data[0x30];
var numSides = data[0x31];
while (mPos < trkSize * data[0x30] * data[0x31])
{
// which side is this?
var side = data[mPos + 0x11];
if (side == 0)
{
// side 1
Array.Copy(data, mPos, S0, s0Pos, trkSize);
s0Pos += trkSize;
}
else if (side == 1)
{
// side 2
Array.Copy(data, mPos, S1, s1Pos, trkSize);
s1Pos += trkSize;
}
else
{
}
mPos += trkSize;
}
byte[] s0final = new byte[s0Pos];
byte[] s1final = new byte[s1Pos];
Array.Copy(S0, 0, s0final, 0, s0Pos);
Array.Copy(S1, 0, s1final, 0, s1Pos);
results.Add(s0final);
results.Add(s1final);
return true;
}
/// <summary>
/// State serlialization
/// </summary>

View File

@ -14,6 +14,21 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <summary>
/// Extended CPCEMU disk format (used in the built-in +3 disk drive)
/// </summary>
CPCExtended
CPCExtended,
/// <summary>
/// Interchangeable Preservation Format
/// </summary>
IPF,
/// <summary>
/// Ultra Disk Image Format (v1.0)
/// </summary>
UDI,
/// <summary>
/// Ultra Disk Image Format (v1.1)
/// </summary>
UDIv1_1
}
}

View File

@ -602,13 +602,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
public byte NumberOfSectors { get; set; }
public byte GAP3Length { get; set; }
public byte FillerByte { get; set; }
public Sector[] Sectors { get; set; }
public virtual Sector[] Sectors { get; set; }
#region UDI
public virtual byte TrackType { get; set; }
public virtual int TLEN { get; set; }
public virtual int CLEN { get { return TLEN / 8 + (TLEN % 8 / 7) / 8; } }
public virtual byte[] TrackData { get; set; }
#endregion
/// <summary>
/// Presents a contiguous byte array of all sector data for this track
/// (including any multiple weak/random data)
/// </summary>
public byte[] TrackSectorData
public virtual byte[] TrackSectorData
{
get
{
@ -626,15 +635,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
public class Sector
{
public byte TrackNumber { get; set; }
public byte SideNumber { get; set; }
public byte SectorID { get; set; }
public byte SectorSize { get; set; }
public byte Status1 { get; set; }
public byte Status2 { get; set; }
public int ActualDataByteLength { get; set; }
public byte[] SectorData { get; set; }
public bool ContainsMultipleWeakSectors { get; set; }
public virtual byte TrackNumber { get; set; }
public virtual byte SideNumber { get; set; }
public virtual byte SectorID { get; set; }
public virtual byte SectorSize { get; set; }
public virtual byte Status1 { get; set; }
public virtual byte Status2 { get; set; }
public virtual int ActualDataByteLength { get; set; }
public virtual byte[] SectorData { get; set; }
public virtual bool ContainsMultipleWeakSectors { get; set; }
public int WeakReadIndex = 0;

View File

@ -0,0 +1,461 @@
using BizHawk.Common;
using BizHawk.Common.NumberExtensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
public class IPFFloppyDisk : FloppyDisk
{
/// <summary>
/// The format type
/// </summary>
public override DiskType DiskFormatType => DiskType.IPF;
/// <summary>
/// Attempts to parse incoming disk data
/// </summary>
/// <param name="diskData"></param>
/// <returns>
/// TRUE: disk parsed
/// FALSE: unable to parse disk
/// </returns>
public override bool ParseDisk(byte[] data)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 16);
if (!ident.ToUpper().Contains("CAPS"))
{
// incorrect format
return false;
}
int pos = 0;
List<IPFBlock> blocks = new List<IPFBlock>();
while (pos < data.Length)
{
try
{
var block = IPFBlock.ParseNextBlock(ref pos, this, data, blocks);
if (block == null)
{
// EOF
break;
}
if (block.RecordType == RecordHeaderType.None)
{
// unknown block
}
blocks.Add(block);
}
catch (Exception ex)
{
}
}
// now process the blocks
var infoBlock = blocks.Where(a => a.RecordType == RecordHeaderType.INFO).FirstOrDefault();
var IMGEblocks = blocks.Where(a => a.RecordType == RecordHeaderType.IMGE).ToList();
var DATAblocks = blocks.Where(a => a.RecordType == RecordHeaderType.DATA).ToList();
DiskHeader.NumberOfTracks = (byte)(IMGEblocks.Count());
DiskHeader.NumberOfSides = (byte)(infoBlock.INFOmaxSide + 1);
DiskTracks = new Track[DiskHeader.NumberOfTracks];
for (int t = 0; t < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; t++)
{
// each imge block represents one track
var img = IMGEblocks[t];
DiskTracks[t] = new Track();
var trk = DiskTracks[t];
var blockCount = img.IMGEblockCount;
var dataBlock = DATAblocks.Where(a => a.DATAdataKey == img.IMGEdataKey).FirstOrDefault();
trk.SideNumber = (byte)img.IMGEside;
trk.TrackNumber = (byte)img.IMGEtrack;
trk.Sectors = new Sector[blockCount];
// process data block descriptors
int p = 0;
for (int d = 0; d < blockCount; d++)
{
var extraDataAreaStart = 32 * blockCount;
trk.Sectors[d] = new Sector();
var sector = trk.Sectors[d];
int dataBits = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
int gapBits = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
int dataBytes;
int gapBytes;
int gapOffset;
int cellType;
if (infoBlock.INFOencoderType == 1)
{
dataBytes = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
gapBytes = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
}
else if (infoBlock.INFOencoderType == 2)
{
gapOffset = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
cellType = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
}
int encoderType = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
int? blockFlags = null;
if (infoBlock.INFOencoderType == 2)
{
blockFlags = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p);
}
p += 4;
int gapDefault = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
int dataOffset = MediaConverter.GetBEInt32(dataBlock.DATAextraDataRaw, p); p += 4;
// gap stream elements
if (infoBlock.INFOencoderType == 2 && gapBits != 0 && blockFlags != null)
{
if (!blockFlags.Value.Bit(1) && !blockFlags.Value.Bit(0))
{
// no gap stream
}
if (!blockFlags.Value.Bit(1) && blockFlags.Value.Bit(0))
{
// Forward gap stream list only
}
if (blockFlags.Value.Bit(1) && !blockFlags.Value.Bit(0))
{
// Backward gap stream list only
}
if (blockFlags.Value.Bit(1) && blockFlags.Value.Bit(0))
{
// Forward and Backward stream lists
}
}
// data stream elements
if (dataBits != 0)
{
var dsLocation = dataOffset;
for (;;)
{
byte dataHead = dataBlock.DATAextraDataRaw[dsLocation++];
if (dataHead == 0)
{
// end of data stream list
break;
}
var sampleSize = ((dataHead & 0xE0) >> 5);
var dataType = dataHead & 0x1F;
byte[] dSize = new byte[sampleSize];
Array.Copy(dataBlock.DATAextraDataRaw, dsLocation, dSize, 0, sampleSize);
var dataSize = MediaConverter.GetBEInt32FromByteArray(dSize);
dsLocation += dSize.Length;
int dataLen;
byte[] dataStream = new byte[0];
if (blockFlags != null && blockFlags.Value.Bit(2))
{
// bits
if (dataType != 5)
{
dataLen = dataSize / 8;
if (dataSize % 8 != 0)
{
// bits left over
}
dataStream = new byte[dataLen];
Array.Copy(dataBlock.DATAextraDataRaw, dsLocation, dataStream, 0, dataLen);
}
}
else
{
// bytes
if (dataType != 5)
{
dataStream = new byte[dataSize];
Array.Copy(dataBlock.DATAextraDataRaw, dsLocation, dataStream, 0, dataSize);
}
}
// dataStream[] now contains the data
switch (dataType)
{
// SYNC
case 1:
break;
// DATA
case 2:
if (dataStream.Length == 7)
{
// ID
// first byte IAM
sector.TrackNumber = dataStream[1];
sector.SideNumber = dataStream[2];
sector.SectorID = dataStream[3];
sector.SectorSize = dataStream[4];
}
else if (dataStream.Length > 255)
{
// DATA
// first byte DAM
if (dataStream[0] == 0xF8)
{
// deleted address mark
//sector.Status1
}
sector.SectorData = new byte[dataStream.Length - 1 - 2];
Array.Copy(dataStream, 1, sector.SectorData, 0, dataStream.Length - 1 - 2);
}
break;
// GAP
case 3:
break;
// RAW
case 4:
break;
// FUZZY
case 5:
break;
default:
break;
}
dsLocation += dataStream.Length;
}
}
}
}
return true;
}
public class IPFBlock
{
public RecordHeaderType RecordType;
public int BlockLength;
public int CRC;
public byte[] RawBlockData;
public int StartPos;
#region INFO
public int INFOmediaType;
public int INFOencoderType;
public int INFOencoderRev;
public int INFOfileKey;
public int INFOfileRev;
public int INFOorigin;
public int INFOminTrack;
public int INFOmaxTrack;
public int INFOminSide;
public int INFOmaxSide;
public int INFOcreationDate;
public int INFOcreationTime;
public int INFOplatform1;
public int INFOplatform2;
public int INFOplatform3;
public int INFOplatform4;
public int INFOdiskNumber;
public int INFOcreatorId;
#endregion
#region IMGE
public int IMGEtrack;
public int IMGEside;
public int IMGEdensity;
public int IMGEsignalType;
public int IMGEtrackBytes;
public int IMGEstartBytePos;
public int IMGEstartBitPos;
public int IMGEdataBits;
public int IMGEgapBits;
public int IMGEtrackBits;
public int IMGEblockCount;
public int IMGEencoderProcess;
public int IMGEtrackFlags;
public int IMGEdataKey;
#endregion
#region DATA
public int DATAlength;
public int DATAbitSize;
public int DATAcrc;
public int DATAdataKey;
public byte[] DATAextraDataRaw;
#endregion
public static IPFBlock ParseNextBlock(ref int startPos, FloppyDisk disk, byte[] data, List<IPFBlock> blockCollection)
{
IPFBlock ipf = new IPFBlock();
ipf.StartPos = startPos;
if (startPos >= data.Length)
{
// EOF
return null;
}
// assume the startPos passed in is actually the start of a new block
// look for record header ident
string ident = Encoding.ASCII.GetString(data, startPos, 4);
startPos += 4;
try
{
ipf.RecordType = (RecordHeaderType)Enum.Parse(typeof(RecordHeaderType), ident);
}
catch
{
ipf.RecordType = RecordHeaderType.None;
}
// setup for actual block size
ipf.BlockLength = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.CRC = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.RawBlockData = new byte[ipf.BlockLength];
Array.Copy(data, ipf.StartPos, ipf.RawBlockData, 0, ipf.BlockLength);
switch (ipf.RecordType)
{
// Nothing to process / unknown
// just move ahead
case RecordHeaderType.CAPS:
case RecordHeaderType.TRCK:
case RecordHeaderType.DUMP:
case RecordHeaderType.CTEI:
case RecordHeaderType.CTEX:
default:
startPos = ipf.StartPos + ipf.BlockLength;
break;
// INFO block
case RecordHeaderType.INFO:
// INFO header is followed immediately by an INFO block
ipf.INFOmediaType = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOencoderType = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOencoderRev = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOfileKey = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOfileRev = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOorigin = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOminTrack = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOmaxTrack = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOminSide = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOmaxSide = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOcreationDate = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOcreationTime = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOplatform1 = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOplatform2 = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOplatform3 = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOplatform4 = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOdiskNumber = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.INFOcreatorId = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
startPos += 12; // reserved
break;
case RecordHeaderType.IMGE:
ipf.IMGEtrack = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEside = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEdensity = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEsignalType = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEtrackBytes = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEstartBytePos = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEstartBitPos = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEdataBits = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEgapBits = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEtrackBits = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEblockCount = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEencoderProcess = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEtrackFlags = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.IMGEdataKey = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
startPos += 12; // reserved
break;
case RecordHeaderType.DATA:
ipf.DATAlength = MediaConverter.GetBEInt32(data, startPos);
if (ipf.DATAlength == 0)
{
ipf.DATAextraDataRaw = new byte[0];
ipf.DATAlength = 0;
}
else
{
ipf.DATAextraDataRaw = new byte[ipf.DATAlength];
}
startPos += 4;
ipf.DATAbitSize = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.DATAcrc = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
ipf.DATAdataKey = MediaConverter.GetBEInt32(data, startPos); startPos += 4;
if (ipf.DATAlength != 0)
{
Array.Copy(data, startPos, ipf.DATAextraDataRaw, 0, ipf.DATAlength);
}
startPos += ipf.DATAlength;
break;
}
return ipf;
}
}
public enum RecordHeaderType
{
None,
CAPS,
DUMP,
DATA,
TRCK,
INFO,
IMGE,
CTEI,
CTEX,
}
/// <summary>
/// State serlialization
/// </summary>
/// <param name="ser"></param>
public override void SyncState(Serializer ser)
{
ser.BeginSection("Plus3FloppyDisk");
ser.Sync("CylinderCount", ref CylinderCount);
ser.Sync("SideCount", ref SideCount);
ser.Sync("BytesPerTrack", ref BytesPerTrack);
ser.Sync("WriteProtected", ref WriteProtected);
ser.SyncEnum("Protection", ref Protection);
ser.Sync("DirtyData", ref DirtyData);
if (DirtyData)
{
}
// sync deterministic track and sector counters
ser.Sync(" _randomCounter", ref _randomCounter);
RandomCounter = _randomCounter;
ser.EndSection();
}
}
}

View File

@ -0,0 +1,217 @@
using BizHawk.Common;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
public class UDI1_0FloppyDisk : FloppyDisk
{
/// <summary>
/// The format type
/// </summary>
public override DiskType DiskFormatType => DiskType.UDI;
/// <summary>
/// Attempts to parse incoming disk data
/// </summary>
/// <param name="diskData"></param>
/// <returns>
/// TRUE: disk parsed
/// FALSE: unable to parse disk
/// </returns>
public override bool ParseDisk(byte[] data)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 4);
if (!ident.StartsWith("UDI!") && !ident.StartsWith("udi!"))
{
// incorrect format
return false;
}
if (data[0x08] != 0)
{
// wrong version
return false;
}
if (ident == "udi!")
{
// cant handle compression yet
return false;
}
DiskHeader.DiskIdent = ident;
DiskHeader.NumberOfTracks = (byte)(data[0x09] + 1);
DiskHeader.NumberOfSides = (byte)(data[0x0A] + 1);
DiskTracks = new Track[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides];
int fileSize = MediaConverter.GetInt32(data, 4); // not including the final 4-byte checksum
// ignore extended header
var extHdrSize = MediaConverter.GetInt32(data, 0x0C);
int pos = 0x10 + extHdrSize;
// process track information
for (int t = 0; t < DiskHeader.NumberOfTracks; t++)
{
DiskTracks[t] = new UDIv1Track();
DiskTracks[t].TrackNumber = (byte)t;
DiskTracks[t].SideNumber = 0;
DiskTracks[t].TrackType = data[pos++];
DiskTracks[t].TLEN = MediaConverter.GetWordValue(data, pos); pos += 2;
DiskTracks[t].TrackData = new byte[DiskTracks[t].TLEN + DiskTracks[t].CLEN];
Array.Copy(data, pos, DiskTracks[t].TrackData, 0, DiskTracks[t].TLEN + DiskTracks[t].CLEN);
pos += DiskTracks[t].TLEN + DiskTracks[t].CLEN;
}
return true;
}
/// <summary>
/// Takes a double-sided disk byte array and converts into 2 single-sided arrays
/// </summary>
/// <param name="data"></param>
/// <param name="results"></param>
/// <returns></returns>
public static bool SplitDoubleSided(byte[] data, List<byte[]> results)
{
// look for standard magic string
string ident = Encoding.ASCII.GetString(data, 0, 4);
if (!ident.StartsWith("UDI!") && !ident.StartsWith("udi!"))
{
// incorrect format
return false;
}
if (data[0x08] != 0)
{
// wrong version
return false;
}
if (ident == "udi!")
{
// cant handle compression yet
return false;
}
byte[] S0 = new byte[data.Length];
byte[] S1 = new byte[data.Length];
// header
var extHdr = MediaConverter.GetInt32(data, 0x0C);
Array.Copy(data, 0, S0, 0, 0x10 + extHdr);
Array.Copy(data, 0, S1, 0, 0x10 + extHdr);
// change side number
S0[0x0A] = 0;
S1[0x0A] = 0;
int pos = 0x10 + extHdr;
int fileSize = MediaConverter.GetInt32(data, 4); // not including the final 4-byte checksum
int s0Pos = pos;
int s1Pos = pos;
// process track information
for (int t = 0; t < (data[0x09] + 1) * 2; t++)
{
var TLEN = MediaConverter.GetWordValue(data, pos + 1);
var CLEN = TLEN / 8 + (TLEN % 8 / 7) / 8;
var blockSize = TLEN + CLEN + 3;
// 2 sided image: side 0 tracks will all have t as an even number
try
{
if (t == 0 || t % 2 == 0)
{
Array.Copy(data, pos, S0, s0Pos, blockSize);
s0Pos += blockSize;
}
else
{
Array.Copy(data, pos, S1, s1Pos, blockSize);
s1Pos += blockSize;
}
}
catch (Exception ex)
{
}
pos += blockSize;
}
// skip checkum bytes for now
byte[] s0final = new byte[s0Pos];
byte[] s1final = new byte[s1Pos];
Array.Copy(S0, 0, s0final, 0, s0Pos);
Array.Copy(S1, 0, s1final, 0, s1Pos);
results.Add(s0final);
results.Add(s1final);
return true;
}
public class UDIv1Track : Track
{
/// <summary>
/// Parse the UDI TrackData byte[] array into sector objects
/// </summary>
public override Sector[] Sectors
{
get
{
List<UDIv1Sector> secs = new List<UDIv1Sector>();
var datas = TrackData.Skip(3).Take(TLEN).ToArray();
var clocks = new BitArray(TrackData.Skip(3 + TLEN).Take(CLEN).ToArray());
return secs.ToArray();
}
}
}
public class UDIv1Sector : Sector
{
}
/// <summary>
/// State serlialization
/// </summary>
/// <param name="ser"></param>
public override void SyncState(Serializer ser)
{
ser.BeginSection("Plus3FloppyDisk");
ser.Sync("CylinderCount", ref CylinderCount);
ser.Sync("SideCount", ref SideCount);
ser.Sync("BytesPerTrack", ref BytesPerTrack);
ser.Sync("WriteProtected", ref WriteProtected);
ser.SyncEnum("Protection", ref Protection);
ser.Sync("DirtyData", ref DirtyData);
if (DirtyData)
{
}
// sync deterministic track and sector counters
ser.Sync(" _randomCounter", ref _randomCounter);
RandomCounter = _randomCounter;
ser.EndSection();
}
}
}

View File

@ -1,6 +1,8 @@
using System;
using System.IO;
using System.IO.Compression;
using System.Runtime.InteropServices;
using System.Linq;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
@ -89,11 +91,69 @@ 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 (in BIG ENDIAN format)
/// </summary>
/// <param name="buf"></param>
/// <param name="offsetIndex"></param>
/// <returns></returns>
public static int GetBEInt32(byte[] buf, int offsetIndex)
{
byte[] b = new byte[4];
Array.Copy(buf, offsetIndex, b, 0, 4);
byte[] buffer = b.Reverse().ToArray();
int pos = 0;
return buffer[pos++] | buffer[pos++] << 8 | buffer[pos++] << 16 | buffer[pos++] << 24;
}
/// <summary>
/// Returns an int32 from a byte array based on the length of the byte array (in BIG ENDIAN format)
/// </summary>
/// <param name="buf"></param>
/// <returns></returns>
public static int GetBEInt32FromByteArray(byte[] buf)
{
byte[] b = buf.Reverse().ToArray();
if (b.Length == 0)
return 0;
int res = b[0];
int pos = 1;
switch (b.Length)
{
case 1:
default:
return res;
case 2:
return res | b[pos] << (8 * pos++);
case 3:
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
case 4:
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
case 5:
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
case 6:
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
case 7:
return res | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++) | b[pos] << (8 * pos++);
}
}
/// <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 +208,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;

View File

@ -2,8 +2,9 @@ using System.IO;
internal static class VersionInfo
{
public const string Mainversion = "2.0.0"; // Use numbers only or the new version notification won't work
public static readonly string RELEASEDATE = "June 25, 2017";
// keep this updated at every major release
public const string Mainversion = "2.3.0"; // Use numbers only or the new version notification won't work
public static readonly string RELEASEDATE = "June 24, 2018";
public static readonly bool DeveloperBuild = true;
public static readonly string HomePage = "http://tasvideos.org/BizHawk.html";

1
mgba Submodule

@ -0,0 +1 @@
Subproject commit db0041340ff260424f38772df4f286fc3907c1c8

Binary file not shown.