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;
}
}
@ -4369,7 +4374,6 @@ namespace BizHawk.Client.EmuHawk
GenericCoreConfig.DoDialog(this, "PC-FX Settings");
}
private bool Rewind(ref bool runFrame, long currentTimestamp, out bool returnToRecording)
{
var isRewinding = false;

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>
<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())
{
return attr.SupportedSystems.Contains(Global.Emulator.SystemId);
}
// 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;
return true;
if (attr?.SupportedSystems != null && attr.SupportedSystems.Any())
{
// supported systems are available
supported = attr.SupportedSystems.Contains(Global.Emulator.SystemId);
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

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

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

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.