Merge branch 'master' into domainedmemoryhooks

This commit is contained in:
adelikat 2017-10-29 12:38:29 -05:00
commit ebcd237858
253 changed files with 12248 additions and 31242 deletions

View File

@ -743,6 +743,34 @@
"P2 B1": "",
"P2 B2": ""
},
"SMS Paddle Controller": {
"P1 Left": "LeftArrow, J1 POV1L",
"P1 Right": "RightArrow, J1 POV1R",
"P1 B1": "Z, J1 B1, X1 X",
"Reset": "J1 B9, X1 Back",
"Pause": "J1 B10, X1 Start"
},
"SMS Light Phaser Controller": {
"P1 Trigger": "Z, J1 B1, X1 X, WMouse L",
"Reset": "J1 B9, X1 Back",
"Pause": "J1 B10, X1 Start"
},
"SMS Sports Pad Controller": {
"P1 Up": "UpArrow, J1 POV1U",
"P1 Down": "DownArrow, J1 POV1D",
"P1 Left": "LeftArrow, J1 POV1L",
"P1 Right": "RightArrow, J1 POV1R",
"P1 B1": "Z, J1 B1, X1 X",
"P1 B2": "X, J1 B2, X1 A",
"Reset": "J1 B9, X1 Back",
"Pause": "J1 B10, X1 Start",
"P2 Up": "",
"P2 Down": "",
"P2 Left": "",
"P2 Right": "",
"P2 B1": "",
"P2 B2": ""
},
"GG Controller": {
"P1 Up": "UpArrow, J1 POV1U, X1 DpadUp, X1 LStickUp",
"P1 Down": "DownArrow, J1 POV1D, X1 DpadDown, X1 LStickDown",
@ -1567,6 +1595,47 @@
"Mult": 1.0,
"Deadzone": 0.1
}
},
"SMS Paddle Controller": {
"P1 Paddle": {
"Value": "X1 LeftThumbX",
"Mult": 1.0,
"Deadzone": 0.1
}
},
"SMS Light Phaser Controller": {
"P1 X": {
"Value": "WMouse X",
"Mult": 1.0,
"Deadzone": 0.0
},
"P1 Y": {
"Value": "WMouse Y",
"Mult": 1.0,
"Deadzone": 0.0
}
},
"SMS Sports Pad Controller": {
"P1 X": {
"Value": "X1 LeftThumbX",
"Mult": 1.0,
"Deadzone": 0.1
},
"P1 Y": {
"Value": "X1 LeftThumbY",
"Mult": -1.0,
"Deadzone": 0.1
},
"P2 X": {
"Value": "X2 LeftThumbX",
"Mult": 1.0,
"Deadzone": 0.1
},
"P2 Y": {
"Value": "X2 LeftThumbY",
"Mult": -1.0,
"Deadzone": 0.1
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -231,7 +231,7 @@ B19256C6716147A9744F5BD528F14450 Magic Knight Rayearth 2 - Making of Magic Knig
846D48D0F4024C8094117599D0E1EEF1 Magic Knight Rayearth (J) GG SRAM=8192 Japan
E496FF2196C372F4D6111538950D25CA Magical Puzzle Popils (W) (En,Ja) GG Puzzle SRAM=8192 World
3AF0C6DDF5F00A493E1F159FCEDC0933 Magical Taruruuto-kun (J) GG Japan
B0C35BC53AB7C184D34E5624F69AAD24 The Majors Pro Baseball (U) GG Sports;Baseball SRAM=128;GGLink USA
B0C35BC53AB7C184D34E5624F69AAD24 The Majors Pro Baseball (U) GG Sports;Baseball SRAM=128;GGLink;EEPROM USA
A15C5219F766D516D1B8D9A09B9A2BB4 Mappy (J) GG Japan
B83F36FD113A8F75F1A29652ACB641FC Marble Madness (UE) GG Arcade USA;Europe
BA846684A66E90372C3C234955EE28BC Marko's Magic Football (E) (En,Fr,De,Es) GG Europe
@ -461,9 +461,9 @@ A23E89266DDAD3C856E7401D04A49C6C Woody Pop (W) (Rev 1) GG World
13F72ACFEA47587F9AA9F655BF98653C World Class Leader Board Golf (UE) GG Sports;Golf USA;Europe
D95D381C6AFFB8345EE5457655E393D1 World Cup USA 94 (UE) (En,Fr,De,Es,It,Nl,Pt,Sv) GG Sports;Soccer USA;Europe
D8939B64458FAF174CDC1241F777CB59 World Derby (J) GG GGLink Japan
E7EABBFC7A1F1339C4720249AEA92A32 World Series Baseball '95 (U) GG Sports;Baseball SRAM=128;GGLink USA
59359FC38865CFF00C90D6EB148DDC2F World Series Baseball (U) GG Sports;Baseball SRAM=128;GGLink USA
05CAC33029F0CAAC27774504C1AA8597 World Series Baseball (U) (Rev 1) GG Sports;Baseball SRAM=128;GGLink USA
E7EABBFC7A1F1339C4720249AEA92A32 World Series Baseball '95 (U) GG Sports;Baseball SRAM=128;GGLink;EEPROM USA
59359FC38865CFF00C90D6EB148DDC2F World Series Baseball (U) GG Sports;Baseball SRAM=128;GGLink;EEPROM USA
05CAC33029F0CAAC27774504C1AA8597 World Series Baseball (U) (Rev 1) GG Sports;Baseball SRAM=128;GGLink;EEPROM USA
D810E851AD60ED5BA50B6246C2CE12F2 WWF Raw (UE) GG Sports;Wrestling USA;Europe
571AC03B80E3075C699CD583BF8651FD X-Men - Gamemaster's Legacy (UE) GG Marvel USA;Europe
CA15F2BA2507EBD836C42D9D10231EB1 X-Men - Mojo World (UE) GG Marvel USA;Europe

View File

@ -106,7 +106,7 @@ F2535DF9BDC3A84221303FA62D61AD6E Back to the Future Part II (E) SMS Europe
3D24A52E98E6C85D7C059386451CE749 Back to the Future Part III (E) SMS PAL Europe
8A94DED3D95AA46DAE8800B92E66D3EE Baku Baku (B) SMS Brazil
7A5D3B9963E316CB7F73BBDC2A7311C6 Bank Panic (E) SMS Europe
26DF4404950CB8DA47235833C0C101C6 Bart vs. the Space Mutants (E) SMS Europe
26DF4404950CB8DA47235833C0C101C6 Bart vs. the Space Mutants (E) SMS PAL Europe
CBA2EC2940619956359801618E7AAB17 Bart vs. the World (E) SMS Europe
0069B1BD9C5B6B88ACE6324D7E61539F Basketball Nightmare (E) SMS Sports;Basketball Europe
215876A62D3CA48D28E98CD8A2C70A15 Batman Returns (E) SMS Europe

View File

@ -32,9 +32,6 @@ namespace BizHawk.Client.ApiHawk
return CoreSystem.Atari2600;
case "A78":
return CoreSystem.Atari2600;
case "A7800":
return CoreSystem.Atari7800;
case "Coleco":

View File

@ -266,6 +266,12 @@ namespace BizHawk.Client.Common
return Path.Combine(MakeAbsolutePath(pathEntry.Path, game.System), name) + ".SaveRAM";
}
public static string AutoSaveRamPath(GameInfo game)
{
var path = SaveRamPath(game);
return path.Insert(path.Length - 8, ".AutoSaveRAM");
}
public static string RetroSaveRAMDirectory(GameInfo game)
{

View File

@ -8,7 +8,6 @@ using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores;
using BizHawk.Emulation.Cores.Libretro;
using BizHawk.Emulation.Cores.Atari.A7800Hawk;
using BizHawk.Emulation.Cores.Atari.Atari7800;
using BizHawk.Emulation.Cores.Calculators;
using BizHawk.Emulation.Cores.Computers.AppleII;
using BizHawk.Emulation.Cores.Computers.Commodore64;
@ -471,7 +470,7 @@ namespace BizHawk.Client.Common
System = "PSX"
};
}
else if (ext == ".iso" || ext == ".cue" || ext == ".ccd")
else if (ext == ".iso" || ext == ".cue" || ext == ".ccd" || ext == ".mds")
{
if (file.IsArchive)
{
@ -495,7 +494,7 @@ namespace BizHawk.Client.Common
throw new InvalidOperationException("\r\n" + discMountJob.OUT_Log);
}
var disc = discMountJob.OUT_Disc;
var disc = discMountJob.OUT_Disc;
// -----------
// TODO - use more sophisticated IDer
@ -515,7 +514,9 @@ namespace BizHawk.Client.Common
// try to use our wizard methods
game = new GameInfo { Name = Path.GetFileNameWithoutExtension(file.Name), Hash = discHash };
switch (new DiscIdentifier(disc).DetectDiscType())
var dt = new DiscIdentifier(disc).DetectDiscType();
switch (dt)
{
case DiscType.SegaSaturn:
game.System = "SAT";
@ -533,9 +534,23 @@ namespace BizHawk.Client.Common
case DiscType.PCFX:
game.System = "PCFX";
break;
case DiscType.TurboCD:
game.System = "PCECD";
break;
case DiscType.Amiga:
case DiscType.CDi:
case DiscType.Dreamcast:
case DiscType.GameCube:
case DiscType.NeoGeoCD:
case DiscType.Panasonic3DO:
case DiscType.Playdia:
case DiscType.Wii:
// no supported emulator core for these (yet)
game.System = dt.ToString();
throw new NoAvailableCoreException(dt.ToString());
case DiscType.AudioDisc:
case DiscType.TurboCD:
case DiscType.UnknownCDFS:
case DiscType.UnknownFormat:
if (PreferredPlatformIsDefined(ext))
@ -544,7 +559,7 @@ namespace BizHawk.Client.Common
}
else
{
game.System = "PCECD";
game.System = "NULL"; // "PCECD";
}
break;
@ -553,6 +568,9 @@ namespace BizHawk.Client.Common
switch (game.System)
{
case "NULL":
nextEmulator = null;
break;
case "GEN":
var genesis = new GPGX(nextComm, null, new[] { disc }, GetCoreSettings<GPGX>(), GetCoreSyncSettings<GPGX>());
nextEmulator = genesis;
@ -938,19 +956,8 @@ namespace BizHawk.Client.Common
}
break;
case "A78":
var gamedbpath = Path.Combine(PathManager.GetExeDirectoryAbsolute(), "gamedb", "EMU7800.csv");
if (!VersionInfo.DeveloperBuild)
{
nextEmulator = new Atari7800(nextComm, game, rom.RomData, gamedbpath); // Don't expose A7800Hawk in releases yet
}
else
{
nextEmulator = Global.Config.A78_UseEmu7800
? nextEmulator = new Atari7800(nextComm, game, rom.RomData, gamedbpath)
: nextEmulator = new A7800Hawk(nextComm, game, rom.RomData, gamedbpath, GetCoreSettings<A7800Hawk>(), GetCoreSyncSettings<A7800Hawk>());
}
var gamedbpath = Path.Combine(PathManager.GetExeDirectoryAbsolute(), "gamedb", "gamedb_a7800.csv");
nextEmulator = new A7800Hawk(nextComm, game, rom.RomData, gamedbpath, GetCoreSettings<A7800Hawk>(), GetCoreSyncSettings<A7800Hawk>());
break;
case "C64":
var c64 = new C64(nextComm, Enumerable.Repeat(rom.RomData, 1), rom.GameInfo, GetCoreSettings<C64>(), GetCoreSyncSettings<C64>());
@ -1041,7 +1048,14 @@ namespace BizHawk.Client.Common
DoMessageCallback("Failed to load a GB rom in SGB mode. Disabling SGB Mode.");
return LoadRom(path, nextComm, false, recursiveCount + 1);
}
else
// handle exceptions thrown by the new detected systems that bizhawk does not have cores for
else if (ex is NoAvailableCoreException)
{
DoLoadErrorCallback(ex.Message + "\n\n" + ex, system);
}
else
{
DoLoadErrorCallback("A core accepted the rom, but threw an exception while loading it:\n\n" + ex, system);
}

View File

@ -113,7 +113,6 @@ namespace BizHawk.Client.Common
public bool InputConfigAutoTab = true;
public bool ShowLogWindow = false;
public bool BackupSavestates = true;
public bool BackupSaveram = true;
public bool SaveScreenshotWithStates = true;
public int BigScreenshotSize = 128 * 1024;
public bool NoLowResLargeScreenshotWithStates = false;
@ -134,6 +133,21 @@ namespace BizHawk.Client.Common
public string Update_IgnoreVersion = "";
public bool CDLAutoSave = true, CDLAutoStart = true;
/// <summary>
/// Makes a .bak file before any saveram-writing operation (could be extended to make timestamped backups)
/// </summary>
public bool BackupSaveram = true;
/// <summary>
/// Whether to make AutoSave files at periodic intervals
/// </summary>
public bool AutosaveSaveRAM;
/// <summary>
/// Intervals at which to make AutoSave files
/// </summary>
public int FlushSaveRamFrames;
//check CurrentDomain_AssemblyResolve if you change the defaults or name of this key
public bool UseNLua = true; // Whether or not to use a good, reliable, memory-leak-free lua interface that is slower than the original luainterface
@ -542,7 +556,6 @@ namespace BizHawk.Client.Common
public bool NES_InQuickNES = true;
public bool SNES_InSnes9x = true;
public bool GBA_UsemGBA = true;
public bool A78_UseEmu7800 = true;
public bool SGB_UseBsnes = false;
public bool CoreForcingViaGameDB = true;
public string LibretroCore;

View File

@ -4,6 +4,7 @@ using System.ComponentModel;
using NLua;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions;
using BizHawk.Common.BufferExtensions;
namespace BizHawk.Client.Common
{
@ -108,6 +109,37 @@ namespace BizHawk.Client.Common
return false;
}
[LuaMethod("hash_region", "Returns a hash as a string of a region of memory, starting from addr, through count bytes. If the domain is unspecified, it uses the current region.")]
public string HashRegion(int addr, int count, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
// checks
if (addr < 0 || addr >= d.Size)
{
string error = $"Address {addr} is outside the bounds of domain {d.Name}";
Log(error);
throw new ArgumentOutOfRangeException(error);
}
if (addr + count > d.Size)
{
string error = $"Address {addr} + count {count} is outside the bounds of domain {d.Name}";
Log(error);
throw new ArgumentOutOfRangeException(error);
}
byte[] data = new byte[count];
for (int i = 0; i < count; i++)
{
data[i] = d.PeekByte(addr + i);
}
using (var hasher = System.Security.Cryptography.SHA256.Create())
{
return hasher.ComputeHash(data).BytesToHexString();
}
}
#endregion
#region Common Special and Legacy Methods

View File

@ -8,7 +8,6 @@ using BizHawk.Emulation.Cores.Nintendo.SNES9X;
using BizHawk.Emulation.Cores.Nintendo.SNES;
using BizHawk.Emulation.Cores.Nintendo.GBA;
using BizHawk.Emulation.Cores.Atari.A7800Hawk;
using BizHawk.Emulation.Cores.Atari.Atari7800;
namespace BizHawk.Client.Common
{
@ -465,7 +464,6 @@ namespace BizHawk.Client.Common
public bool? PreviousNES_InQuickNES { get; set; }
public bool? PreviousSNES_InSnes9x { get; set; }
public bool? PreviousGBA_UsemGBA { get; set; }
public bool? PreviousA78_UseEmu7800 { get; set; }
public void QueueNewMovie(IMovie movie, bool record, IEmulator emulator)
{
@ -533,22 +531,6 @@ namespace BizHawk.Client.Common
Global.Config.GBA_UsemGBA = false;
}
}
else if (!record && emulator.SystemId == "A78") // meh, copy pasta one more time, last time, I promise
{
var atari7800HawkName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(A7800Hawk), typeof(CoreAttribute))).CoreName;
var emu7800HawkName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(Atari7800), typeof(CoreAttribute))).CoreName;
if (movie.Core == atari7800HawkName)
{
PreviousA78_UseEmu7800 = Global.Config.A78_UseEmu7800;
Global.Config.A78_UseEmu7800 = true;
}
else if (movie.Core == emu7800HawkName)
{
PreviousA78_UseEmu7800 = Global.Config.A78_UseEmu7800;
Global.Config.A78_UseEmu7800 = false;
}
}
if (record) // This is a hack really, we need to set the movie to its propert state so that it will be considered active later
{

View File

@ -20,7 +20,6 @@ namespace BizHawk.Client.Common
bool? PreviousNES_InQuickNES { get; set; }
bool? PreviousSNES_InSnes9x { get; set; }
bool? PreviousGBA_UsemGBA { get; set; }
bool? PreviousA78_UseEmu7800 { get; set; }
void HandleMovieOnFrameLoop();
void HandleMovieAfterFrameLoop();

View File

@ -514,14 +514,14 @@ namespace BizHawk.Client.Common
Log?.Dispose();
Log = branch.InputLog.Clone();
////_changes = true;
_lagLog.FromLagLog(branch.LagLog);
// if there are branch states, they will be loaded anyway
// but if there's none, or only *after* divergent point, don't invalidate the entire movie anymore
if (divergentPoint.HasValue)
{
_stateManager.Invalidate(divergentPoint.Value);
_lagLog.FromLagLog(branch.LagLog); // don't truncate LagLog if the branch's one is shorter, but input is the same
}
else
{
@ -530,8 +530,7 @@ namespace BizHawk.Client.Common
_stateManager.LoadBranch(Branches.IndexOf(branch));
_stateManager.SetState(branch.Frame, branch.CoreData);
////ChangeLog = branch.ChangeLog;
if (BindMarkersToInput) // pretty critical not to erase them
{
Markers = branch.Markers;

View File

@ -284,29 +284,61 @@ namespace BizHawk.Client.Common
case WatchSize.Byte:
return (byte?)_val;
case WatchSize.Word:
if (addr == _watch.Address)
if (_watch.BigEndian)
{
if (addr == _watch.Address)
{
return (byte)(_val & 0xFF);
}
return (byte)(_val >> 8);
}
return (byte)(_val & 0xFF);
case WatchSize.DWord:
if (addr == _watch.Address)
else
{
if (addr == _watch.Address)
{
return (byte)(_val >> 8);
}
return (byte)(_val & 0xFF);
}
case WatchSize.DWord:
if (_watch.BigEndian)
{
if (addr == _watch.Address)
{
return (byte)((_val >> 24) & 0xFF);
}
if (addr == _watch.Address + 1)
{
return (byte)((_val >> 16) & 0xFF);
}
if (addr == _watch.Address + 2)
{
return (byte)((_val >> 8) & 0xFF);
}
return (byte)(_val & 0xFF);
}
else
{
if (addr == _watch.Address)
{
return (byte)(_val & 0xFF);
}
if (addr == _watch.Address + 1)
{
return (byte)((_val >> 8) & 0xFF);
}
if (addr == _watch.Address + 2)
{
return (byte)((_val >> 16) & 0xFF);
}
return (byte)((_val >> 24) & 0xFF);
}
if (addr == _watch.Address + 1)
{
return (byte)((_val >> 16) & 0xFF);
}
if (addr == _watch.Address + 2)
{
return (byte)((_val >> 8) & 0xFF);
}
return (byte)(_val & 0xFF);
}
}

View File

@ -111,7 +111,7 @@ namespace BizHawk.Client.DiscoHawk
foreach (string str in files)
{
string ext = Path.GetExtension(str).ToUpper();
if(!ext.In(new string[]{".CUE",".ISO",".CCD"}))
if(!ext.In(new string[]{".CUE",".ISO",".CCD", ".MDS"}))
{
return new List<string>();
}

View File

@ -112,7 +112,7 @@ namespace BizHawk.Client.EmuHawk
}
else
{
cmdRom = arg;
cmdRom = args[i];
}
}
}

View File

@ -822,6 +822,12 @@
<Compile Include="tools\GB\GBGPUView.Designer.cs">
<DependentUpon>GBGPUView.cs</DependentUpon>
</Compile>
<Compile Include="tools\GB\GBPrinterView.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="tools\GB\GBPrinterView.Designer.cs">
<DependentUpon>GBPrinterView.cs</DependentUpon>
</Compile>
<Compile Include="tools\Genesis\GenDbgWind.cs">
<SubType>Form</SubType>
</Compile>
@ -1167,7 +1173,6 @@
</Compile>
<Compile Include="tools\VirtualPads\schema\A26Schema.cs" />
<Compile Include="tools\VirtualPads\schema\A78Schema.cs" />
<Compile Include="tools\VirtualPads\schema\A7800HawkSchema.cs" />
<Compile Include="tools\VirtualPads\schema\AppleIISchema.cs" />
<Compile Include="tools\VirtualPads\schema\C64Schema.cs" />
<Compile Include="tools\VirtualPads\schema\ColecoSchema.cs" />
@ -1317,7 +1322,7 @@
<EmbeddedResource Include="config\A7800\A7800ControllerSettings.resx">
<DependentUpon>A7800ControllerSettings.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="config\A7800\A7800FilterSettings.resx">
<EmbeddedResource Include="config\A7800\A7800FilterSettings.resx">
<DependentUpon>A7800FilterSettings.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="config\INTV\IntvControllerSettings.resx">
@ -1490,6 +1495,9 @@
<EmbeddedResource Include="tools\GB\GBGPUView.resx">
<DependentUpon>GBGPUView.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="tools\GB\GBPrinterView.resx">
<DependentUpon>GBPrinterView.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="tools\Genesis\GenDbgWind.resx">
<DependentUpon>GenDbgWind.cs</DependentUpon>
</EmbeddedResource>
@ -2085,7 +2093,6 @@
<None Include="images\checkbox.png" />
<None Include="images\Erase.png" />
<None Include="images\bsnes.png" />
<None Include="images\emu7800.png" />
<None Include="images\genplus.png" />
<None Include="images\gambatte.png" />
<None Include="images\dual.png" />

View File

@ -586,6 +586,13 @@ namespace BizHawk.Client.EmuHawk
};
var filterProgram = UpdateSourceInternal(job);
//this only happens when we're forcing the client to size itself with autoload and the core says 0x0....
//we need some other more sensible client size.
if (filterProgram == null)
{
return new Size(256, 192);
}
var size = filterProgram.Filters[filterProgram.Filters.Count - 1].FindOutput().SurfaceFormat.Size;
return size;

View File

@ -3,7 +3,6 @@
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions;
using BizHawk.Emulation.Cores.Atari.Atari7800;
using BizHawk.Emulation.Cores.Nintendo.GBA;
using BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES;
using BizHawk.Emulation.Cores.Nintendo.SNES;
@ -36,10 +35,6 @@ namespace BizHawk.Client.EmuHawk.CoreExtensions
{
return Properties.Resources.bsnes;
}
else if (core is Atari7800)
{
return Properties.Resources.emu7800;
}
else if (core is GPGX)
{
return Properties.Resources.genplus;

View File

@ -51,11 +51,11 @@ namespace BizHawk.Client.EmuHawk
return new[]
{
".NES", ".FDS", ".UNF", ".SMS", ".GG", ".SG", ".GB", ".GBC", ".GBA", ".PCE", ".SGX", ".BIN", ".SMD", ".GEN", ".MD", ".SMC", ".SFC", ".A26", ".A78", ".LNX", ".COL", ".ROM", ".M3U", ".CUE", ".CCD", ".SGB", ".Z64", ".V64", ".N64", ".WS", ".WSC", ".XML", ".DSK", ".DO", ".PO", ".PSF", ".MINIPSF", ".NSF",
".EXE", ".PRG", ".D64", "*G64", ".CRT", ".TAP", ".32X"
".EXE", ".PRG", ".D64", "*G64", ".CRT", ".TAP", ".32X", ".MDS"
};
}
return new[] { ".NES", ".FDS", ".UNF", ".SMS", ".GG", ".SG", ".GB", ".GBC", ".GBA", ".PCE", ".SGX", ".BIN", ".SMD", ".GEN", ".MD", ".SMC", ".SFC", ".A26", ".A78", ".LNX", ".COL", ".ROM", ".M3U", ".CUE", ".CCD", ".SGB", ".Z64", ".V64", ".N64", ".WS", ".WSC", ".XML", ".DSK", ".DO", ".PO", ".PSF", ".MINIPSF", ".NSF", ".32X" };
return new[] { ".NES", ".FDS", ".UNF", ".SMS", ".GG", ".SG", ".GB", ".GBC", ".GBA", ".PCE", ".SGX", ".BIN", ".SMD", ".GEN", ".MD", ".SMC", ".SFC", ".A26", ".A78", ".LNX", ".COL", ".ROM", ".M3U", ".CUE", ".CCD", ".SGB", ".Z64", ".V64", ".N64", ".WS", ".WSC", ".XML", ".DSK", ".DO", ".PO", ".PSF", ".MINIPSF", ".NSF", ".32X", ".MDS" };
}
}

View File

@ -73,7 +73,7 @@
this.LoadCurrentSlotMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.SaveRAMSubMenu = new System.Windows.Forms.ToolStripMenuItem();
this.FlushSaveRAMMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator();
this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator();
this.MovieSubMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ReadonlyMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator15 = new System.Windows.Forms.ToolStripSeparator();
@ -192,8 +192,6 @@
this.GbaCoreSubMenu = new System.Windows.Forms.ToolStripMenuItem();
this.VbaNextCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.MgbaCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.Atari7800CoreSubMenu = new System.Windows.Forms.ToolStripMenuItem();
this.Emu7800CoreMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.Atari7800HawkCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.SGBCoreSubmenu = new System.Windows.Forms.ToolStripMenuItem();
this.SgbBsnesMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@ -238,8 +236,9 @@
this.coreToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.quickNESToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.nesHawkToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator34 = new System.Windows.Forms.ToolStripSeparator();
this.NESPPUViewerMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator34 = new System.Windows.Forms.ToolStripSeparator();
this.toolStripSeparator35 = new System.Windows.Forms.ToolStripSeparator();
this.NESPPUViewerMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.NESNametableViewerMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.NESGameGenieCodesMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.musicRipperToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@ -307,7 +306,8 @@
this.LoadGBInSGBMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator28 = new System.Windows.Forms.ToolStripSeparator();
this.GBGPUViewerMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.GBGameGenieMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.GBPrinterViewerMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.GBGameGenieMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.GBASubMenu = new System.Windows.Forms.ToolStripMenuItem();
this.GBACoreSelectionSubMenu = new System.Windows.Forms.ToolStripMenuItem();
this.GBAmGBAMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@ -329,7 +329,6 @@
this.SnesOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ColecoSubMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ColecoControllerSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator35 = new System.Windows.Forms.ToolStripSeparator();
this.ColecoSkipBiosMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.N64SubMenu = new System.Windows.Forms.ToolStripMenuItem();
this.N64PluginSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@ -443,7 +442,12 @@
this.ShowMenuContextMenuSeparator = new System.Windows.Forms.ToolStripSeparator();
this.ShowMenuContextMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.timerMouseIdle = new System.Windows.Forms.Timer(this.components);
this.MainformMenu.SuspendLayout();
this.SMSControllerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.SMSControllerStandardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.SMSControllerPaddleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.SMSControllerLightPhaserToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.SMSControllerSportsPadToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.MainformMenu.SuspendLayout();
this.MainStatusBar.SuspendLayout();
this.MainFormContextMenu.SuspendLayout();
this.SuspendLayout();
@ -902,26 +906,26 @@
this.LoadCurrentSlotMenuItem.Size = new System.Drawing.Size(178, 22);
this.LoadCurrentSlotMenuItem.Text = "Load Current Slot";
this.LoadCurrentSlotMenuItem.Click += new System.EventHandler(this.LoadCurrentSlotMenuItem_Click);
//
// SaveRAMSubMenu
//
this.SaveRAMSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.FlushSaveRAMMenuItem});
this.SaveRAMSubMenu.Name = "SaveRAMSubMenu";
this.SaveRAMSubMenu.Size = new System.Drawing.Size(159, 22);
this.SaveRAMSubMenu.Text = "Save &RAM";
this.SaveRAMSubMenu.DropDownOpened += new System.EventHandler(this.FlushSaveRAMSubMenu_DropDownOpened);
//
// FlushSaveRAMMenuItem
//
this.FlushSaveRAMMenuItem.Name = "FlushSaveRAMMenuItem";
this.FlushSaveRAMMenuItem.Size = new System.Drawing.Size(156, 22);
this.FlushSaveRAMMenuItem.Text = "&Flush Save Ram";
this.FlushSaveRAMMenuItem.Click += new System.EventHandler(this.FlushSaveRAMMenuItem_Click);
//
// toolStripMenuItem2
//
this.toolStripMenuItem2.Name = "toolStripMenuItem2";
//
// SaveRAMSubMenu
//
this.SaveRAMSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.FlushSaveRAMMenuItem});
this.SaveRAMSubMenu.Name = "SaveRAMSubMenu";
this.SaveRAMSubMenu.Size = new System.Drawing.Size(159, 22);
this.SaveRAMSubMenu.Text = "Save &RAM";
this.SaveRAMSubMenu.DropDownOpened += new System.EventHandler(this.SaveRAMSubMenu_DropDownOpened);
//
// FlushSaveRAMMenuItem
//
this.FlushSaveRAMMenuItem.Name = "FlushSaveRAMMenuItem";
this.FlushSaveRAMMenuItem.Size = new System.Drawing.Size(156, 22);
this.FlushSaveRAMMenuItem.Text = "&Flush Save Ram";
this.FlushSaveRAMMenuItem.Click += new System.EventHandler(this.FlushSaveRAMMenuItem_Click);
// toolStripMenuItem2
//
this.toolStripMenuItem2.Name = "toolStripMenuItem2";
this.toolStripMenuItem2.Size = new System.Drawing.Size(156, 6);
//
// MovieSubMenu
@ -1805,7 +1809,6 @@
this.NesCoreSubMenu,
this.CoreSNESSubMenu,
this.GbaCoreSubMenu,
this.Atari7800CoreSubMenu,
this.SGBCoreSubmenu,
this.GBInSGBMenuItem,
this.toolStripMenuItem16,
@ -1890,29 +1893,11 @@
this.MgbaCoreMenuItem.Text = "mGBA";
this.MgbaCoreMenuItem.Click += new System.EventHandler(this.GbaCorePick_Click);
//
// Atari7800CoreSubMenu
//
this.Atari7800CoreSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.Emu7800CoreMenuItem,
this.Atari7800HawkCoreMenuItem});
this.Atari7800CoreSubMenu.Name = "Atari7800CoreSubMenu";
this.Atari7800CoreSubMenu.Size = new System.Drawing.Size(239, 22);
this.Atari7800CoreSubMenu.Text = "Atari 7800";
this.Atari7800CoreSubMenu.DropDownOpened += new System.EventHandler(this.Atari7800CoreSubMenu_DropDownOpened);
//
// Emu7800CoreMenuItem
//
this.Emu7800CoreMenuItem.Name = "Emu7800CoreMenuItem";
this.Emu7800CoreMenuItem.Size = new System.Drawing.Size(153, 22);
this.Emu7800CoreMenuItem.Text = "EMU7800";
this.Emu7800CoreMenuItem.Click += new System.EventHandler(this.Atari7800CorePick_Click);
//
// Atari7800HawkCoreMenuItem
//
this.Atari7800HawkCoreMenuItem.Name = "Atari7800HawkCoreMenuItem";
this.Atari7800HawkCoreMenuItem.Size = new System.Drawing.Size(153, 22);
this.Atari7800HawkCoreMenuItem.Text = "Atari7800Hawk";
this.Atari7800HawkCoreMenuItem.Click += new System.EventHandler(this.Atari7800CorePick_Click);
//
// SGBCoreSubmenu
//
@ -2495,7 +2480,8 @@
this.SMSSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.SMSregionToolStripMenuItem,
this.SMSdisplayToolStripMenuItem,
this.SMStoolStripMenuItem2,
this.SMSControllerToolStripMenuItem,
this.SMStoolStripMenuItem2,
this.SMSenableBIOSToolStripMenuItem,
this.SMSEnableFMChipMenuItem,
this.SMSOverclockMenuItem,
@ -2562,9 +2548,44 @@
this.SMSdisplayNtscToolStripMenuItem.Text = "NTSC";
this.SMSdisplayNtscToolStripMenuItem.Click += new System.EventHandler(this.SMS_DisplayNTSC_Click);
//
// SMSdisplayPalToolStripMenuItem
// SMSControllerToolStripMenuItem
//
this.SMSdisplayPalToolStripMenuItem.Name = "SMSdisplayPalToolStripMenuItem";
this.SMSControllerToolStripMenuItem.Name = "SMSControllerToolStripMenuItem";
this.SMSControllerToolStripMenuItem.Text = "&Controller Type";
this.SMSControllerToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.SMSControllerStandardToolStripMenuItem,
this.SMSControllerPaddleToolStripMenuItem,
this.SMSControllerLightPhaserToolStripMenuItem,
this.SMSControllerSportsPadToolStripMenuItem});
//
// SMSControllerStandardToolStripMenuItem
//
this.SMSControllerStandardToolStripMenuItem.Name = "SMSControllerStandardToolStripMenuItem";
this.SMSControllerStandardToolStripMenuItem.Text = "Standard";
this.SMSControllerStandardToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerStandardToolStripMenuItem_Click);
//
// SMSControllerPaddleToolStripMenuItem
//
this.SMSControllerPaddleToolStripMenuItem.Name = "SMSControllerPaddleToolStripMenuItem";
this.SMSControllerPaddleToolStripMenuItem.Text = "Paddle";
this.SMSControllerPaddleToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerPaddleToolStripMenuItem_Click);
//
// SMSControllerLightPhaserToolStripMenuItem
//
this.SMSControllerLightPhaserToolStripMenuItem.Name = "SMSControllerLightPhaserToolStripMenuItem";
this.SMSControllerLightPhaserToolStripMenuItem.Text = "Light Phaser";
this.SMSControllerLightPhaserToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerLightPhaserToolStripMenuItem_Click);
//
// SMSControllerSportsPadToolStripMenuItem
//
this.SMSControllerSportsPadToolStripMenuItem.Name = "SMSControllerSportsPadToolStripMenuItem";
this.SMSControllerSportsPadToolStripMenuItem.Text = "Sports Pad";
this.SMSControllerSportsPadToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerSportsPadToolStripMenuItem_Click);
//
// SMSdisplayPalToolStripMenuItem
//
this.SMSdisplayPalToolStripMenuItem.Name = "SMSdisplayPalToolStripMenuItem";
this.SMSdisplayPalToolStripMenuItem.Size = new System.Drawing.Size(104, 22);
this.SMSdisplayPalToolStripMenuItem.Text = "PAL";
this.SMSdisplayPalToolStripMenuItem.Click += new System.EventHandler(this.SMS_DisplayPAL_Click);
@ -2765,7 +2786,8 @@
this.LoadGBInSGBMenuItem,
this.toolStripSeparator28,
this.GBGPUViewerMenuItem,
this.GBGameGenieMenuItem});
this.GBGameGenieMenuItem,
this.GBPrinterViewerMenuItem});
this.GBSubMenu.Name = "GBSubMenu";
this.GBSubMenu.Size = new System.Drawing.Size(34, 19);
this.GBSubMenu.Text = "&GB";
@ -2803,10 +2825,17 @@
this.GBGameGenieMenuItem.Size = new System.Drawing.Size(233, 22);
this.GBGameGenieMenuItem.Text = "&Game Genie Encoder/Decoder";
this.GBGameGenieMenuItem.Click += new System.EventHandler(this.GBGameGenieMenuItem_Click);
//
// GBASubMenu
//
this.GBASubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
//
// GBPrinterViewerMenuItem
//
this.GBPrinterViewerMenuItem.Name = "GBPrinterViewerMenuItem";
this.GBPrinterViewerMenuItem.Size = new System.Drawing.Size(233, 22);
this.GBPrinterViewerMenuItem.Text = "&Printer Viewer";
this.GBPrinterViewerMenuItem.Click += new System.EventHandler(this.GBPrinterViewerMenuItem_Click);
//
// GBASubMenu
//
this.GBASubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.GBACoreSelectionSubMenu,
this.GBAcoresettingsToolStripMenuItem1,
this.toolStripSeparator33,
@ -4176,6 +4205,7 @@
private System.Windows.Forms.ToolStripMenuItem SaveMovieContextMenuItem;
private System.Windows.Forms.ToolStripMenuItem VirtualPadMenuItem;
private System.Windows.Forms.ToolStripMenuItem GBGPUViewerMenuItem;
private System.Windows.Forms.ToolStripMenuItem GBPrinterViewerMenuItem;
private System.Windows.Forms.ToolStripMenuItem AudioThrottleMenuItem;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator27;
private System.Windows.Forms.ToolStripMenuItem VsyncEnabledMenuItem;
@ -4309,6 +4339,7 @@
private System.Windows.Forms.ToolStripMenuItem quickNESToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem nesHawkToolStripMenuItem;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator34;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator35;
private System.Windows.Forms.ToolStripMenuItem GBACoreSelectionSubMenu;
private System.Windows.Forms.ToolStripMenuItem GBAmGBAMenuItem;
private System.Windows.Forms.ToolStripMenuItem GBAVBANextMenuItem;
@ -4358,7 +4389,6 @@
private System.Windows.Forms.ToolStripMenuItem IntvSubMenu;
private System.Windows.Forms.ToolStripMenuItem IntVControllerSettingsMenuItem;
private System.Windows.Forms.ToolStripMenuItem SNESControllerConfigurationMenuItem;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator35;
private System.Windows.Forms.ToolStripMenuItem C64DisksSubMenu;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator36;
private System.Windows.Forms.ToolStripMenuItem sNESToolStripMenuItem;
@ -4373,13 +4403,16 @@
private System.Windows.Forms.ToolStripMenuItem GbaCoreSubMenu;
private System.Windows.Forms.ToolStripMenuItem VbaNextCoreMenuItem;
private System.Windows.Forms.ToolStripMenuItem MgbaCoreMenuItem;
private System.Windows.Forms.ToolStripMenuItem Atari7800CoreSubMenu;
private System.Windows.Forms.ToolStripMenuItem Emu7800CoreMenuItem;
private System.Windows.Forms.ToolStripMenuItem Atari7800HawkCoreMenuItem;
private System.Windows.Forms.ToolStripMenuItem SGBCoreSubmenu;
private System.Windows.Forms.ToolStripMenuItem SgbBsnesMenuItem;
private System.Windows.Forms.ToolStripMenuItem SgbSameBoyMenuItem;
private System.Windows.Forms.ToolStripMenuItem pCFXToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem preferencesToolStripMenuItem3;
private System.Windows.Forms.ToolStripMenuItem SMSControllerToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem SMSControllerStandardToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem SMSControllerPaddleToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem SMSControllerLightPhaserToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem SMSControllerSportsPadToolStripMenuItem;
}
}

View File

@ -6,7 +6,6 @@ using System.Windows.Forms;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions;
using BizHawk.Emulation.Cores.Atari.A7800Hawk;
using BizHawk.Emulation.Cores.Atari.Atari7800;
using BizHawk.Emulation.Cores.Calculators;
using BizHawk.Emulation.Cores.ColecoVision;
using BizHawk.Emulation.Cores.Nintendo.NES;
@ -226,9 +225,9 @@ namespace BizHawk.Client.EmuHawk
}
}
private void FlushSaveRAMSubMenu_DropDownOpened(object sender, EventArgs e)
private void SaveRAMSubMenu_DropDownOpened(object sender, EventArgs e)
{
FlushSaveRAMMenuItem.ShortcutKeyDisplayString = Global.Config.HotkeyBindings["Flush SRAM"].Bindings;
FlushSaveRAMMenuItem.ShortcutKeyDisplayString = Global.Config.HotkeyBindings["Flush SaveRAM"].Bindings;
}
private void MovieSubMenu_DropDownOpened(object sender, EventArgs e)
@ -1187,7 +1186,6 @@ namespace BizHawk.Client.EmuHawk
private void CoresSubMenu_DropDownOpened(object sender, EventArgs e)
{
Atari7800CoreSubMenu.Visible = VersionInfo.DeveloperBuild;
GBInSGBMenuItem.Checked = Global.Config.GB_AsSGB;
allowGameDBCoreOverridesToolStripMenuItem.Checked = Global.Config.CoreForcingViaGameDB;
@ -1240,21 +1238,6 @@ namespace BizHawk.Client.EmuHawk
}
}
private void Atari7800CoreSubMenu_DropDownOpened(object sender, EventArgs e)
{
Emu7800CoreMenuItem.Checked = Global.Config.A78_UseEmu7800;
Atari7800HawkCoreMenuItem.Checked = !Global.Config.A78_UseEmu7800;
}
private void Atari7800CorePick_Click(object sender, EventArgs e)
{
Global.Config.A78_UseEmu7800 ^= true;
if (Emulator.SystemId == "A78")
{
FlagNeedsReboot();
}
}
private void SGBCoreSubmenu_DropDownOpened(object sender, EventArgs e)
{
SgbBsnesMenuItem.Checked = Global.Config.SGB_UseBsnes;
@ -1762,6 +1745,10 @@ namespace BizHawk.Client.EmuHawk
SMSdisplayNtscToolStripMenuItem.Checked = ss.DisplayType == "NTSC";
SMSdisplayPalToolStripMenuItem.Checked = ss.DisplayType == "PAL";
SMSdisplayAutoToolStripMenuItem.Checked = ss.DisplayType == "Auto";
SMSControllerStandardToolStripMenuItem.Checked = ss.ControllerType == "Standard";
SMSControllerPaddleToolStripMenuItem.Checked = ss.ControllerType == "Paddle";
SMSControllerLightPhaserToolStripMenuItem.Checked = ss.ControllerType == "Light Phaser";
SMSControllerSportsPadToolStripMenuItem.Checked = ss.ControllerType == "Sports Pad";
SMSenableBIOSToolStripMenuItem.Checked = ss.UseBIOS;
SMSEnableFMChipMenuItem.Checked = ss.EnableFM;
SMSOverclockMenuItem.Checked = ss.AllowOverlock;
@ -1775,9 +1762,11 @@ namespace BizHawk.Client.EmuHawk
SMSEnableFMChipMenuItem.Visible =
SMSFix3DGameDisplayToolStripMenuItem.Visible =
SMSenableBIOSToolStripMenuItem.Visible =
SMSDisplayOverscanMenuItem.Visible =
Global.Game.System == "SMS";
SMSDisplayOverscanMenuItem.Visible =
Global.Game.System == "SMS" || Global.Game.System == "SG";
SMSOverclockMenuItem.Visible =
SMSForceStereoMenuItem.Visible =
SMSdisplayToolStripMenuItem.Visible =
@ -1914,6 +1903,34 @@ namespace BizHawk.Client.EmuHawk
GlobalWin.Tools.Load<SmsVDPViewer>();
}
private void SMSControllerStandardToolStripMenuItem_Click(object sender, EventArgs e)
{
var s = ((SMS)Emulator).GetSyncSettings();
s.ControllerType = "Standard";
PutCoreSyncSettings(s);
}
private void SMSControllerPaddleToolStripMenuItem_Click(object sender, EventArgs e)
{
var s = ((SMS)Emulator).GetSyncSettings();
s.ControllerType = "Paddle";
PutCoreSyncSettings(s);
}
private void SMSControllerLightPhaserToolStripMenuItem_Click(object sender, EventArgs e)
{
var s = ((SMS)Emulator).GetSyncSettings();
s.ControllerType = "Light Phaser";
PutCoreSyncSettings(s);
}
private void SMSControllerSportsPadToolStripMenuItem_Click(object sender, EventArgs e)
{
var s = ((SMS)Emulator).GetSyncSettings();
s.ControllerType = "Sports Pad";
PutCoreSyncSettings(s);
}
#endregion
#region TI83
@ -2033,6 +2050,11 @@ namespace BizHawk.Client.EmuHawk
GlobalWin.Tools.LoadGameGenieEc();
}
private void GBPrinterViewerMenuItem_Click(object sender, EventArgs e)
{
GlobalWin.Tools.Load<GBPrinterView>();
}
#endregion
#region GBA

View File

@ -34,6 +34,7 @@ using BizHawk.Emulation.Cores.Nintendo.SNES9X;
using BizHawk.Emulation.Cores.Consoles.SNK;
using BizHawk.Emulation.Cores.Consoles.Sega.PicoDrive;
using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy;
using BizHawk.Emulation.Cores.Atari.A7800Hawk;
namespace BizHawk.Client.EmuHawk
{
@ -273,7 +274,8 @@ namespace BizHawk.Client.EmuHawk
if (argParse.cmdRom != null)
{
// Commandline should always override auto-load
LoadRom(argParse.cmdRom, new LoadRomArgs { OpenAdvanced = new OpenAdvanced_OpenRom() });
var ioa = OpenAdvancedSerializer.ParseWithLegacy(argParse.cmdRom);
LoadRom(argParse.cmdRom, new LoadRomArgs { OpenAdvanced = ioa });
if (Global.Game == null)
{
MessageBox.Show("Failed to load " + argParse.cmdRom + " specified on commandline");
@ -404,7 +406,7 @@ namespace BizHawk.Client.EmuHawk
{
PauseEmulator();
}
// start dumping, if appropriate
if (argParse.cmdDumpType != null && argParse.cmdDumpName != null)
{
@ -1414,6 +1416,8 @@ namespace BizHawk.Client.EmuHawk
public PresentationPanel PresentationPanel { get; }
//countdown for saveram autoflushing
public int AutoFlushSaveRamIn { get; set; }
#endregion
#region Private methods
@ -1566,6 +1570,16 @@ namespace BizHawk.Client.EmuHawk
{
try // zero says: this is sort of sketchy... but this is no time for rearchitecting
{
if (Global.Config.AutosaveSaveRAM)
{
var saveram = new FileInfo(PathManager.SaveRamPath(Global.Game));
var autosave = new FileInfo(PathManager.AutoSaveRamPath(Global.Game));
if (autosave.Exists && autosave.LastWriteTime > saveram.LastWriteTime)
{
GlobalWin.OSD.AddMessage("AutoSaveRAM is newer than last saved SaveRAM");
}
}
byte[] sram;
// GBA meteor core might not know how big the saveram ought to be, so just send it the whole file
@ -1595,47 +1609,66 @@ namespace BizHawk.Client.EmuHawk
}
Emulator.AsSaveRam().StoreSaveRam(sram);
AutoFlushSaveRamIn = Global.Config.FlushSaveRamFrames;
}
catch (IOException)
{
GlobalWin.OSD.AddMessage("An error occurred while loading Sram");
}
}
}
}
public void FlushSaveRAM()
public void FlushSaveRAM(bool autosave = false)
{
if (Emulator.HasSaveRam())
{
var path = PathManager.SaveRamPath(Global.Game);
var f = new FileInfo(path);
if (f.Directory != null && !f.Directory.Exists)
string path;
if (autosave)
{
f.Directory.Create();
path = PathManager.AutoSaveRamPath(Global.Game);
AutoFlushSaveRamIn = Global.Config.FlushSaveRamFrames;
}
else
{
path = PathManager.SaveRamPath(Global.Game);
}
var file = new FileInfo(path);
var newPath = path + ".new";
var newFile = new FileInfo(newPath);
var backupPath = path + ".bak";
var backupFile = new FileInfo(backupPath);
if (file.Directory != null && !file.Directory.Exists)
{
file.Directory.Create();
}
// Make backup first
if (Global.Config.BackupSaveram && f.Exists)
{
var backup = path + ".bak";
var backupFile = new FileInfo(backup);
if (backupFile.Exists)
{
backupFile.Delete();
}
f.CopyTo(backup);
}
var writer = new BinaryWriter(new FileStream(path, FileMode.Create, FileAccess.Write));
var writer = new BinaryWriter(new FileStream(newPath, FileMode.Create, FileAccess.Write));
var saveram = Emulator.AsSaveRam().CloneSaveRam();
if (saveram != null)
{
writer.Write(saveram, 0, saveram.Length);
}
writer.Close();
if (file.Exists)
{
if (Global.Config.BackupSaveram)
{
if (backupFile.Exists)
{
backupFile.Delete();
}
file.MoveTo(backupPath);
}
else
{
file.Delete();
}
}
newFile.MoveTo(path);
}
}
@ -1725,8 +1758,11 @@ namespace BizHawk.Client.EmuHawk
case "A26":
AtariSubMenu.Visible = true;
break;
case "A7800":
A7800SubMenu.Visible = true;
case "A78":
if (Emulator is A7800Hawk)
{
A7800SubMenu.Visible = true;
}
break;
case "PSX":
PSXSubMenu.Visible = true;
@ -2041,13 +2077,13 @@ namespace BizHawk.Client.EmuHawk
if (VersionInfo.DeveloperBuild)
{
return FormatFilter(
"Rom Files", "*.nes;*.fds;*.unf;*.sms;*.gg;*.sg;*.pce;*.sgx;*.bin;*.smd;*.rom;*.a26;*.a78;*.lnx;*.m3u;*.cue;*.ccd;*.exe;*.gb;*.gbc;*.gba;*.gen;*.md;*.32x;*.col;*.int;*.smc;*.sfc;*.prg;*.d64;*.g64;*.crt;*.tap;*.sgb;*.xml;*.z64;*.v64;*.n64;*.ws;*.wsc;*.dsk;*.do;*.po;*.vb;*.ngp;*.ngc;*.psf;*.minipsf;*.nsf;%ARCH%",
"Rom Files", "*.nes;*.fds;*.unf;*.sms;*.gg;*.sg;*.pce;*.sgx;*.bin;*.smd;*.rom;*.a26;*.a78;*.lnx;*.m3u;*.cue;*.ccd;*.mds;*.exe;*.gb;*.gbc;*.gba;*.gen;*.md;*.32x;*.col;*.int;*.smc;*.sfc;*.prg;*.d64;*.g64;*.crt;*.tap;*.sgb;*.xml;*.z64;*.v64;*.n64;*.ws;*.wsc;*.dsk;*.do;*.po;*.vb;*.ngp;*.ngc;*.psf;*.minipsf;*.nsf;%ARCH%",
"Music Files", "*.psf;*.minipsf;*.sid;*.nsf",
"Disc Images", "*.cue;*.ccd;*.m3u",
"Disc Images", "*.cue;*.ccd;*.mds;*.m3u",
"NES", "*.nes;*.fds;*.unf;*.nsf;%ARCH%",
"Super NES", "*.smc;*.sfc;*.xml;%ARCH%",
"Master System", "*.sms;*.gg;*.sg;%ARCH%",
"PC Engine", "*.pce;*.sgx;*.cue;*.ccd;%ARCH%",
"PC Engine", "*.pce;*.sgx;*.cue;*.ccd;*.mds;%ARCH%",
"TI-83", "*.rom;%ARCH%",
"Archive Files", "%ARCH%",
"Savestate", "*.state",
@ -2059,7 +2095,7 @@ namespace BizHawk.Client.EmuHawk
"Gameboy Advance", "*.gba;%ARCH%",
"Colecovision", "*.col;%ARCH%",
"Intellivision", "*.int;*.bin;*.rom;%ARCH%",
"PlayStation", "*.cue;*.ccd;*.m3u",
"PlayStation", "*.cue;*.ccd;*.mds;*.m3u",
"PSX Executables (experimental)", "*.exe",
"PSF Playstation Sound File", "*.psf;*.minipsf",
"Commodore 64", "*.prg; *.d64, *.g64; *.crt; *.tap;%ARCH%",
@ -2073,17 +2109,17 @@ namespace BizHawk.Client.EmuHawk
}
return FormatFilter(
"Rom Files", "*.nes;*.fds;*.unf;*.sms;*.gg;*.sg;*.gb;*.gbc;*.gba;*.pce;*.sgx;*.bin;*.smd;*.gen;*.md;*.32x;*.smc;*.sfc;*.a26;*.a78;*.lnx;*.col;*.int;*.rom;*.m3u;*.cue;*.ccd;*.sgb;*.z64;*.v64;*.n64;*.ws;*.wsc;*.xml;*.dsk;*.do;*.po;*.psf;*.ngp;*.ngc;*.prg;*.d64;*.g64;*.minipsf;*.nsf;%ARCH%",
"Disc Images", "*.cue;*.ccd;*.m3u",
"Rom Files", "*.nes;*.fds;*.unf;*.sms;*.gg;*.sg;*.gb;*.gbc;*.gba;*.pce;*.sgx;*.bin;*.smd;*.gen;*.md;*.32x;*.smc;*.sfc;*.a26;*.a78;*.lnx;*.col;*.int;*.rom;*.m3u;*.cue;*.ccd;*.mds;*.sgb;*.z64;*.v64;*.n64;*.ws;*.wsc;*.xml;*.dsk;*.do;*.po;*.psf;*.ngp;*.ngc;*.prg;*.d64;*.g64;*.minipsf;*.nsf;%ARCH%",
"Disc Images", "*.cue;*.ccd;*.mds;*.m3u",
"NES", "*.nes;*.fds;*.unf;*.nsf;%ARCH%",
"Super NES", "*.smc;*.sfc;*.xml;%ARCH%",
"PlayStation", "*.cue;*.ccd;*.m3u",
"PlayStation", "*.cue;*.ccd;*.mds;*.m3u",
"PSF Playstation Sound File", "*.psf;*.minipsf",
"Nintendo 64", "*.z64;*.v64;*.n64",
"Gameboy", "*.gb;*.gbc;*.sgb;%ARCH%",
"Gameboy Advance", "*.gba;%ARCH%",
"Master System", "*.sms;*.gg;*.sg;%ARCH%",
"PC Engine", "*.pce;*.sgx;*.cue;*.ccd;%ARCH%",
"PC Engine", "*.pce;*.sgx;*.cue;*.ccd;*.mds;%ARCH%",
"Atari 2600", "*.a26;%ARCH%",
"Atari 7800", "*.a78;%ARCH%",
"Atari Lynx", "*.lnx;%ARCH%",
@ -2902,6 +2938,13 @@ namespace BizHawk.Client.EmuHawk
Global.MovieSession.HandleMovieOnFrameLoop();
if (Global.Config.AutosaveSaveRAM)
{
if (AutoFlushSaveRamIn-- <= 0)
{
FlushSaveRAM(true);
}
}
// why not skip audio if the user doesnt want sound
bool renderSound = (Global.Config.SoundEnabled && !IsTurboing) || (_currAviWriter?.UsesAudio ?? false);
if (!renderSound)
@ -3540,6 +3583,12 @@ namespace BizHawk.Client.EmuHawk
}
}
if (ioa is OpenAdvanced_OpenRom)
{
var ioa_openrom = (OpenAdvanced_OpenRom)ioa;
path = ioa_openrom.Path;
}
CoreFileProvider.SyncCoreCommInputSignals(nextComm);
var result = loader.LoadRom(path, nextComm);
@ -3616,9 +3665,16 @@ namespace BizHawk.Client.EmuHawk
JumpLists.AddRecentItem(loaderName, ioa.DisplayName);
// Don't load Save Ram if a movie is being loaded
if (!Global.MovieSession.MovieIsQueued && File.Exists(PathManager.SaveRamPath(loader.Game)))
if (!Global.MovieSession.MovieIsQueued)
{
LoadSaveRam();
if (File.Exists(PathManager.SaveRamPath(loader.Game)))
{
LoadSaveRam();
}
else if (Global.Config.AutosaveSaveRAM && File.Exists(PathManager.AutoSaveRamPath(loader.Game)))
{
GlobalWin.OSD.AddMessage("AutoSaveRAM found, but SaveRAM was not saved");
}
}
GlobalWin.Tools.Restart();

View File

@ -489,17 +489,7 @@ namespace BizHawk.Client.EmuHawk.Properties {
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap emu7800 {
get {
object obj = ResourceManager.GetObject("emu7800", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>

View File

@ -205,9 +205,6 @@
<data name="calculator" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\images\calculator.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="emu7800" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\images\emu7800.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="ts_v_piano_16_green_blue" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\images\tastudio\ts_v_piano_16_green_blue.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
@ -1560,4 +1557,4 @@
<data name="NGPController" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\images\ControllerImages\NGPController.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>
</root>

View File

@ -48,6 +48,17 @@
this.EnableContextMenuCheckbox = new System.Windows.Forms.CheckBox();
this.PauseWhenMenuActivatedCheckbox = new System.Windows.Forms.CheckBox();
this.tabPage3 = new System.Windows.Forms.TabPage();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.AutosaveSRAMtextBox = new System.Windows.Forms.NumericUpDown();
this.AutosaveSRAMradioButton1 = new System.Windows.Forms.RadioButton();
this.label8 = new System.Windows.Forms.Label();
this.AutosaveSRAMradioButton2 = new System.Windows.Forms.RadioButton();
this.AutosaveSRAMradioButton3 = new System.Windows.Forms.RadioButton();
this.AutosaveSRAMCheckbox = new System.Windows.Forms.CheckBox();
this.panel1 = new System.Windows.Forms.Panel();
this.label7 = new System.Windows.Forms.Label();
this.LuaInterfaceRadio = new System.Windows.Forms.RadioButton();
this.NLuaRadio = new System.Windows.Forms.RadioButton();
this.label6 = new System.Windows.Forms.Label();
this.cbMoviesInAWE = new System.Windows.Forms.CheckBox();
this.label5 = new System.Windows.Forms.Label();
@ -56,20 +67,18 @@
this.label12 = new System.Windows.Forms.Label();
this.label13 = new System.Windows.Forms.Label();
this.FrameAdvSkipLagCheckbox = new System.Windows.Forms.CheckBox();
this.label9 = new System.Windows.Forms.Label();
this.label10 = new System.Windows.Forms.Label();
this.BackupSRamCheckbox = new System.Windows.Forms.CheckBox();
this.label4 = new System.Windows.Forms.Label();
this.LogWindowAsConsoleCheckbox = new System.Windows.Forms.CheckBox();
this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
this.panel1 = new System.Windows.Forms.Panel();
this.NLuaRadio = new System.Windows.Forms.RadioButton();
this.LuaInterfaceRadio = new System.Windows.Forms.RadioButton();
this.label7 = new System.Windows.Forms.Label();
this.label9 = new System.Windows.Forms.Label();
this.label10 = new System.Windows.Forms.Label();
this.tabControl1.SuspendLayout();
this.tabPage1.SuspendLayout();
this.groupBox1.SuspendLayout();
this.tabPage3.SuspendLayout();
this.groupBox2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.AutosaveSRAMtextBox)).BeginInit();
this.panel1.SuspendLayout();
this.SuspendLayout();
//
@ -123,7 +132,7 @@
this.tabPage1.Location = new System.Drawing.Point(4, 22);
this.tabPage1.Name = "tabPage1";
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
this.tabPage1.Size = new System.Drawing.Size(386, 368);
this.tabPage1.Size = new System.Drawing.Size(386, 376);
this.tabPage1.TabIndex = 0;
this.tabPage1.Text = "General";
this.tabPage1.UseVisualStyleBackColor = true;
@ -270,6 +279,8 @@
//
// tabPage3
//
this.tabPage3.Controls.Add(this.groupBox2);
this.tabPage3.Controls.Add(this.AutosaveSRAMCheckbox);
this.tabPage3.Controls.Add(this.panel1);
this.tabPage3.Controls.Add(this.label6);
this.tabPage3.Controls.Add(this.cbMoviesInAWE);
@ -279,8 +290,6 @@
this.tabPage3.Controls.Add(this.label12);
this.tabPage3.Controls.Add(this.label13);
this.tabPage3.Controls.Add(this.FrameAdvSkipLagCheckbox);
this.tabPage3.Controls.Add(this.label9);
this.tabPage3.Controls.Add(this.label10);
this.tabPage3.Controls.Add(this.BackupSRamCheckbox);
this.tabPage3.Controls.Add(this.label4);
this.tabPage3.Controls.Add(this.LogWindowAsConsoleCheckbox);
@ -291,6 +300,128 @@
this.tabPage3.Text = "Advanced";
this.tabPage3.UseVisualStyleBackColor = true;
//
// groupBox2
//
this.groupBox2.Controls.Add(this.label10);
this.groupBox2.Controls.Add(this.label9);
this.groupBox2.Controls.Add(this.AutosaveSRAMtextBox);
this.groupBox2.Controls.Add(this.AutosaveSRAMradioButton1);
this.groupBox2.Controls.Add(this.label8);
this.groupBox2.Controls.Add(this.AutosaveSRAMradioButton2);
this.groupBox2.Controls.Add(this.AutosaveSRAMradioButton3);
this.groupBox2.Location = new System.Drawing.Point(27, 59);
this.groupBox2.Margin = new System.Windows.Forms.Padding(0);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(265, 60);
this.groupBox2.TabIndex = 27;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "AutoSaveRAM";
//
// AutosaveSRAMtextBox
//
this.AutosaveSRAMtextBox.Location = new System.Drawing.Point(151, 33);
this.AutosaveSRAMtextBox.Maximum = new decimal(new int[] {
100000,
0,
0,
0});
this.AutosaveSRAMtextBox.Name = "AutosaveSRAMtextBox";
this.AutosaveSRAMtextBox.Size = new System.Drawing.Size(50, 20);
this.AutosaveSRAMtextBox.TabIndex = 27;
//
// AutosaveSRAMradioButton1
//
this.AutosaveSRAMradioButton1.AutoSize = true;
this.AutosaveSRAMradioButton1.Location = new System.Drawing.Point(48, 33);
this.AutosaveSRAMradioButton1.Name = "AutosaveSRAMradioButton1";
this.AutosaveSRAMradioButton1.Size = new System.Drawing.Size(36, 17);
this.AutosaveSRAMradioButton1.TabIndex = 22;
this.AutosaveSRAMradioButton1.TabStop = true;
this.AutosaveSRAMradioButton1.Text = "5s";
this.AutosaveSRAMradioButton1.UseVisualStyleBackColor = true;
//
// label8
//
this.label8.AutoSize = true;
this.label8.Location = new System.Drawing.Point(202, 35);
this.label8.Name = "label8";
this.label8.Size = new System.Drawing.Size(12, 13);
this.label8.TabIndex = 26;
this.label8.Text = "s";
//
// AutosaveSRAMradioButton2
//
this.AutosaveSRAMradioButton2.AutoSize = true;
this.AutosaveSRAMradioButton2.Location = new System.Drawing.Point(90, 34);
this.AutosaveSRAMradioButton2.Name = "AutosaveSRAMradioButton2";
this.AutosaveSRAMradioButton2.Size = new System.Drawing.Size(39, 17);
this.AutosaveSRAMradioButton2.TabIndex = 23;
this.AutosaveSRAMradioButton2.TabStop = true;
this.AutosaveSRAMradioButton2.Text = "5m";
this.AutosaveSRAMradioButton2.UseVisualStyleBackColor = true;
//
// AutosaveSRAMradioButton3
//
this.AutosaveSRAMradioButton3.AutoSize = true;
this.AutosaveSRAMradioButton3.Location = new System.Drawing.Point(131, 35);
this.AutosaveSRAMradioButton3.Name = "AutosaveSRAMradioButton3";
this.AutosaveSRAMradioButton3.Size = new System.Drawing.Size(14, 13);
this.AutosaveSRAMradioButton3.TabIndex = 24;
this.AutosaveSRAMradioButton3.TabStop = true;
this.AutosaveSRAMradioButton3.UseVisualStyleBackColor = true;
this.AutosaveSRAMradioButton3.CheckedChanged += new System.EventHandler(this.AutosaveSRAMradioButton3_CheckedChanged);
//
// AutosaveSRAMCheckbox
//
this.AutosaveSRAMCheckbox.AutoSize = true;
this.AutosaveSRAMCheckbox.Location = new System.Drawing.Point(6, 62);
this.AutosaveSRAMCheckbox.Name = "AutosaveSRAMCheckbox";
this.AutosaveSRAMCheckbox.Size = new System.Drawing.Size(15, 14);
this.AutosaveSRAMCheckbox.TabIndex = 21;
this.AutosaveSRAMCheckbox.UseVisualStyleBackColor = true;
this.AutosaveSRAMCheckbox.CheckedChanged += new System.EventHandler(this.AutosaveSRAMCheckbox_CheckedChanged);
//
// panel1
//
this.panel1.Controls.Add(this.label7);
this.panel1.Controls.Add(this.LuaInterfaceRadio);
this.panel1.Controls.Add(this.NLuaRadio);
this.panel1.Location = new System.Drawing.Point(6, 312);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(377, 61);
this.panel1.TabIndex = 20;
//
// label7
//
this.label7.AutoSize = true;
this.label7.Location = new System.Drawing.Point(3, 1);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(50, 13);
this.label7.TabIndex = 2;
this.label7.Text = "Lua Core";
//
// LuaInterfaceRadio
//
this.LuaInterfaceRadio.AutoSize = true;
this.LuaInterfaceRadio.Location = new System.Drawing.Point(4, 36);
this.LuaInterfaceRadio.Name = "LuaInterfaceRadio";
this.LuaInterfaceRadio.Size = new System.Drawing.Size(338, 17);
this.LuaInterfaceRadio.TabIndex = 1;
this.LuaInterfaceRadio.TabStop = true;
this.LuaInterfaceRadio.Text = "Lua+LuaInterface - Faster but memory leaks, use at your own risk!";
this.LuaInterfaceRadio.UseVisualStyleBackColor = true;
//
// NLuaRadio
//
this.NLuaRadio.AutoSize = true;
this.NLuaRadio.Location = new System.Drawing.Point(4, 17);
this.NLuaRadio.Name = "NLuaRadio";
this.NLuaRadio.Size = new System.Drawing.Size(194, 17);
this.NLuaRadio.TabIndex = 0;
this.NLuaRadio.TabStop = true;
this.NLuaRadio.Text = "NLua+KopiLua - Reliable but slower";
this.NLuaRadio.UseVisualStyleBackColor = true;
//
// label6
//
this.label6.AutoSize = true;
@ -335,7 +466,7 @@
// LuaDuringTurboCheckbox
//
this.LuaDuringTurboCheckbox.AutoSize = true;
this.LuaDuringTurboCheckbox.Location = new System.Drawing.Point(6, 174);
this.LuaDuringTurboCheckbox.Location = new System.Drawing.Point(6, 178);
this.LuaDuringTurboCheckbox.Name = "LuaDuringTurboCheckbox";
this.LuaDuringTurboCheckbox.Size = new System.Drawing.Size(166, 17);
this.LuaDuringTurboCheckbox.TabIndex = 15;
@ -345,7 +476,7 @@
// label12
//
this.label12.AutoSize = true;
this.label12.Location = new System.Drawing.Point(24, 149);
this.label12.Location = new System.Drawing.Point(24, 162);
this.label12.Name = "label12";
this.label12.Size = new System.Drawing.Size(231, 13);
this.label12.TabIndex = 14;
@ -354,7 +485,7 @@
// label13
//
this.label13.AutoSize = true;
this.label13.Location = new System.Drawing.Point(24, 136);
this.label13.Location = new System.Drawing.Point(24, 149);
this.label13.Name = "label13";
this.label13.Size = new System.Drawing.Size(268, 13);
this.label13.TabIndex = 13;
@ -363,45 +494,27 @@
// FrameAdvSkipLagCheckbox
//
this.FrameAdvSkipLagCheckbox.AutoSize = true;
this.FrameAdvSkipLagCheckbox.Location = new System.Drawing.Point(6, 116);
this.FrameAdvSkipLagCheckbox.Location = new System.Drawing.Point(6, 129);
this.FrameAdvSkipLagCheckbox.Name = "FrameAdvSkipLagCheckbox";
this.FrameAdvSkipLagCheckbox.Size = new System.Drawing.Size(241, 17);
this.FrameAdvSkipLagCheckbox.TabIndex = 12;
this.FrameAdvSkipLagCheckbox.Text = "Frame advance button skips non-input frames";
this.FrameAdvSkipLagCheckbox.UseVisualStyleBackColor = true;
//
// label9
//
this.label9.AutoSize = true;
this.label9.Location = new System.Drawing.Point(24, 94);
this.label9.Name = "label9";
this.label9.Size = new System.Drawing.Size(99, 13);
this.label9.TabIndex = 11;
this.label9.Text = "before overwriting it";
//
// label10
//
this.label10.AutoSize = true;
this.label10.Location = new System.Drawing.Point(24, 81);
this.label10.Name = "label10";
this.label10.Size = new System.Drawing.Size(277, 13);
this.label10.TabIndex = 10;
this.label10.Text = "When set, the client will make a backup copy of saveram";
//
// BackupSRamCheckbox
//
this.BackupSRamCheckbox.AutoSize = true;
this.BackupSRamCheckbox.Location = new System.Drawing.Point(6, 61);
this.BackupSRamCheckbox.Location = new System.Drawing.Point(6, 39);
this.BackupSRamCheckbox.Name = "BackupSRamCheckbox";
this.BackupSRamCheckbox.Size = new System.Drawing.Size(108, 17);
this.BackupSRamCheckbox.Size = new System.Drawing.Size(203, 17);
this.BackupSRamCheckbox.TabIndex = 9;
this.BackupSRamCheckbox.Text = "Backup Saveram";
this.BackupSRamCheckbox.Text = "Backup SaveRAM to .SaveRAM.bak";
this.BackupSRamCheckbox.UseVisualStyleBackColor = true;
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(24, 37);
this.label4.Location = new System.Drawing.Point(24, 23);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(234, 13);
this.label4.TabIndex = 2;
@ -410,53 +523,30 @@
// LogWindowAsConsoleCheckbox
//
this.LogWindowAsConsoleCheckbox.AutoSize = true;
this.LogWindowAsConsoleCheckbox.Location = new System.Drawing.Point(6, 17);
this.LogWindowAsConsoleCheckbox.Location = new System.Drawing.Point(6, 3);
this.LogWindowAsConsoleCheckbox.Name = "LogWindowAsConsoleCheckbox";
this.LogWindowAsConsoleCheckbox.Size = new System.Drawing.Size(233, 17);
this.LogWindowAsConsoleCheckbox.TabIndex = 1;
this.LogWindowAsConsoleCheckbox.Text = "Create the log window as a console window";
this.LogWindowAsConsoleCheckbox.UseVisualStyleBackColor = true;
//
// panel1
// label9
//
this.panel1.Controls.Add(this.label7);
this.panel1.Controls.Add(this.LuaInterfaceRadio);
this.panel1.Controls.Add(this.NLuaRadio);
this.panel1.Location = new System.Drawing.Point(6, 312);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(377, 61);
this.panel1.TabIndex = 20;
this.label9.AutoSize = true;
this.label9.Location = new System.Drawing.Point(6, 16);
this.label9.Name = "label9";
this.label9.Size = new System.Drawing.Size(225, 13);
this.label9.TabIndex = 28;
this.label9.Text = "Save SaveRAM to .AutoSaveRAM.SaveRAM";
//
// NLuaRadio
// label10
//
this.NLuaRadio.AutoSize = true;
this.NLuaRadio.Location = new System.Drawing.Point(4, 17);
this.NLuaRadio.Name = "NLuaRadio";
this.NLuaRadio.Size = new System.Drawing.Size(194, 17);
this.NLuaRadio.TabIndex = 0;
this.NLuaRadio.TabStop = true;
this.NLuaRadio.Text = "NLua+KopiLua - Reliable but slower";
this.NLuaRadio.UseVisualStyleBackColor = true;
//
// LuaInterfaceRadio
//
this.LuaInterfaceRadio.AutoSize = true;
this.LuaInterfaceRadio.Location = new System.Drawing.Point(4, 36);
this.LuaInterfaceRadio.Name = "LuaInterfaceRadio";
this.LuaInterfaceRadio.Size = new System.Drawing.Size(338, 17);
this.LuaInterfaceRadio.TabIndex = 1;
this.LuaInterfaceRadio.TabStop = true;
this.LuaInterfaceRadio.Text = "Lua+LuaInterface - Faster but memory leaks, use at your own risk!";
this.LuaInterfaceRadio.UseVisualStyleBackColor = true;
//
// label7
//
this.label7.AutoSize = true;
this.label7.Location = new System.Drawing.Point(3, 1);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(50, 13);
this.label7.TabIndex = 2;
this.label7.Text = "Lua Core";
this.label10.AutoSize = true;
this.label10.Location = new System.Drawing.Point(9, 34);
this.label10.Name = "label10";
this.label10.Size = new System.Drawing.Size(33, 13);
this.label10.TabIndex = 29;
this.label10.Text = "every";
//
// EmuHawkOptions
//
@ -480,6 +570,9 @@
this.groupBox1.PerformLayout();
this.tabPage3.ResumeLayout(false);
this.tabPage3.PerformLayout();
this.groupBox2.ResumeLayout(false);
this.groupBox2.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.AutosaveSRAMtextBox)).EndInit();
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.ResumeLayout(false);
@ -508,8 +601,6 @@
private System.Windows.Forms.Label label4;
private System.Windows.Forms.ToolTip toolTip1;
private System.Windows.Forms.CheckBox BackupSRamCheckbox;
private System.Windows.Forms.Label label9;
private System.Windows.Forms.Label label10;
private System.Windows.Forms.CheckBox FrameAdvSkipLagCheckbox;
private System.Windows.Forms.Label label12;
private System.Windows.Forms.Label label13;
@ -525,5 +616,14 @@
private System.Windows.Forms.Label label7;
private System.Windows.Forms.RadioButton LuaInterfaceRadio;
private System.Windows.Forms.RadioButton NLuaRadio;
private System.Windows.Forms.CheckBox AutosaveSRAMCheckbox;
private System.Windows.Forms.Label label8;
private System.Windows.Forms.RadioButton AutosaveSRAMradioButton3;
private System.Windows.Forms.RadioButton AutosaveSRAMradioButton2;
private System.Windows.Forms.RadioButton AutosaveSRAMradioButton1;
private System.Windows.Forms.GroupBox groupBox2;
private System.Windows.Forms.NumericUpDown AutosaveSRAMtextBox;
private System.Windows.Forms.Label label10;
private System.Windows.Forms.Label label9;
}
}

View File

@ -7,6 +7,34 @@ namespace BizHawk.Client.EmuHawk
{
public partial class EmuHawkOptions : Form
{
public int AutosaveSaveRAMSeconds {
get {
if (AutosaveSRAMradioButton1.Checked)
return 5;
if (AutosaveSRAMradioButton2.Checked)
return 5 * 60;
return (int)AutosaveSRAMtextBox.Value;
}
set {
switch (value)
{
case 5:
AutosaveSRAMradioButton1.Checked = true;
AutosaveSRAMtextBox.Enabled = false;
break;
case 5 * 60:
AutosaveSRAMradioButton2.Checked = true;
AutosaveSRAMtextBox.Enabled = false;
break;
default:
AutosaveSRAMradioButton3.Checked = true;
AutosaveSRAMtextBox.Enabled = true;
break;
}
AutosaveSRAMtextBox.Value = value;
}
}
public EmuHawkOptions()
{
InitializeComponent();
@ -25,6 +53,9 @@ namespace BizHawk.Client.EmuHawk
SingleInstanceModeCheckbox.Checked = Global.Config.SingleInstanceMode;
BackupSRamCheckbox.Checked = Global.Config.BackupSaveram;
AutosaveSRAMCheckbox.Checked = Global.Config.AutosaveSaveRAM;
groupBox2.Enabled = AutosaveSRAMCheckbox.Checked;
AutosaveSaveRAMSeconds = Global.Config.FlushSaveRamFrames / 60;
FrameAdvSkipLagCheckbox.Checked = Global.Config.SkipLagFrame;
LogWindowAsConsoleCheckbox.Checked = Global.Config.WIN32_CONSOLE;
LuaDuringTurboCheckbox.Checked = Global.Config.RunLuaDuringTurbo;
@ -56,6 +87,10 @@ namespace BizHawk.Client.EmuHawk
Global.Config.SingleInstanceMode = SingleInstanceModeCheckbox.Checked;
Global.Config.BackupSaveram = BackupSRamCheckbox.Checked;
Global.Config.AutosaveSaveRAM = AutosaveSRAMCheckbox.Checked;
Global.Config.FlushSaveRamFrames = AutosaveSaveRAMSeconds * 60;
if (GlobalWin.MainForm.AutoFlushSaveRamIn > Global.Config.FlushSaveRamFrames)
GlobalWin.MainForm.AutoFlushSaveRamIn = Global.Config.FlushSaveRamFrames;
Global.Config.SkipLagFrame = FrameAdvSkipLagCheckbox.Checked;
Global.Config.WIN32_CONSOLE = LogWindowAsConsoleCheckbox.Checked;
Global.Config.RunLuaDuringTurbo = LuaDuringTurboCheckbox.Checked;
@ -77,5 +112,15 @@ namespace BizHawk.Client.EmuHawk
DialogResult = DialogResult.Cancel;
GlobalWin.OSD.AddMessage("Customizing aborted.");
}
private void AutosaveSRAMCheckbox_CheckedChanged(object sender, EventArgs e)
{
groupBox2.Enabled = AutosaveSRAMCheckbox.Checked;
}
private void AutosaveSRAMradioButton3_CheckedChanged(object sender, EventArgs e)
{
AutosaveSRAMtextBox.Enabled = AutosaveSRAMradioButton3.Checked;
}
}
}

View File

@ -28,220 +28,87 @@
/// </summary>
private void InitializeComponent()
{
this.OK = new System.Windows.Forms.Button();
this.Cancel = new System.Windows.Forms.Button();
this.trackBar1 = new System.Windows.Forms.TrackBar();
this.trackBar2 = new System.Windows.Forms.TrackBar();
this.trackBar3 = new System.Windows.Forms.TrackBar();
this.trackBar4 = new System.Windows.Forms.TrackBar();
this.trackBar5 = new System.Windows.Forms.TrackBar();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label();
this.label5 = new System.Windows.Forms.Label();
this.label6 = new System.Windows.Forms.Label();
this.label7 = new System.Windows.Forms.Label();
this.label8 = new System.Windows.Forms.Label();
this.label9 = new System.Windows.Forms.Label();
this.label10 = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.trackBar1)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.trackBar2)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.trackBar3)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.trackBar4)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.trackBar5)).BeginInit();
this.SuspendLayout();
//
// OK
//
this.OK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.OK.Location = new System.Drawing.Point(41, 262);
this.OK.Name = "OK";
this.OK.Size = new System.Drawing.Size(75, 23);
this.OK.TabIndex = 0;
this.OK.Text = "&OK";
this.OK.UseVisualStyleBackColor = true;
this.OK.Click += new System.EventHandler(this.Ok_Click);
//
// Cancel
//
this.Cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.Cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.Cancel.Location = new System.Drawing.Point(122, 262);
this.Cancel.Name = "Cancel";
this.Cancel.Size = new System.Drawing.Size(75, 23);
this.Cancel.TabIndex = 1;
this.Cancel.Text = "&Cancel";
this.Cancel.UseVisualStyleBackColor = true;
this.Cancel.Click += new System.EventHandler(this.Cancel_Click);
//
// trackBar1
//
this.trackBar1.Location = new System.Drawing.Point(12, 12);
this.trackBar1.Name = "trackBar1";
this.trackBar1.Size = new System.Drawing.Size(104, 45);
this.trackBar1.TabIndex = 2;
this.trackBar1.ValueChanged += new System.EventHandler(this.TrackBar1_ValueChanged);
//
// trackBar2
//
this.trackBar2.Location = new System.Drawing.Point(12, 60);
this.trackBar2.Name = "trackBar2";
this.trackBar2.Size = new System.Drawing.Size(104, 45);
this.trackBar2.TabIndex = 3;
this.trackBar2.ValueChanged += new System.EventHandler(this.TrackBar2_ValueChanged);
//
// trackBar3
//
this.trackBar3.Location = new System.Drawing.Point(12, 108);
this.trackBar3.Name = "trackBar3";
this.trackBar3.Size = new System.Drawing.Size(104, 45);
this.trackBar3.TabIndex = 4;
this.trackBar3.ValueChanged += new System.EventHandler(this.TrackBar3_ValueChanged);
//
// trackBar4
//
this.trackBar4.Location = new System.Drawing.Point(12, 156);
this.trackBar4.Name = "trackBar4";
this.trackBar4.Size = new System.Drawing.Size(104, 45);
this.trackBar4.TabIndex = 5;
this.trackBar4.ValueChanged += new System.EventHandler(this.TrackBar4_ValueChanged);
//
// trackBar5
//
this.trackBar5.Location = new System.Drawing.Point(12, 204);
this.trackBar5.Name = "trackBar5";
this.trackBar5.Size = new System.Drawing.Size(104, 45);
this.trackBar5.TabIndex = 6;
this.trackBar5.ValueChanged += new System.EventHandler(this.TrackBar5_ValueChanged);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(122, 12);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(50, 13);
this.label1.TabIndex = 7;
this.label1.Text = "Square 1";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(122, 60);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(50, 13);
this.label2.TabIndex = 8;
this.label2.Text = "Square 2";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(122, 108);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(45, 13);
this.label3.TabIndex = 9;
this.label3.Text = "Triangle";
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(122, 156);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(34, 13);
this.label4.TabIndex = 10;
this.label4.Text = "Noise";
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(122, 204);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(31, 13);
this.label5.TabIndex = 11;
this.label5.Text = "DMC";
//
// label6
//
this.label6.AutoSize = true;
this.label6.Location = new System.Drawing.Point(122, 25);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(13, 13);
this.label6.TabIndex = 12;
this.label6.Text = "0";
//
// label7
//
this.label7.AutoSize = true;
this.label7.Location = new System.Drawing.Point(122, 73);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(13, 13);
this.label7.TabIndex = 13;
this.label7.Text = "0";
//
// label8
//
this.label8.AutoSize = true;
this.label8.Location = new System.Drawing.Point(122, 121);
this.label8.Name = "label8";
this.label8.Size = new System.Drawing.Size(13, 13);
this.label8.TabIndex = 14;
this.label8.Text = "0";
//
// label9
//
this.label9.AutoSize = true;
this.label9.Location = new System.Drawing.Point(122, 169);
this.label9.Name = "label9";
this.label9.Size = new System.Drawing.Size(13, 13);
this.label9.TabIndex = 15;
this.label9.Text = "0";
//
// label10
//
this.label10.AutoSize = true;
this.label10.Location = new System.Drawing.Point(122, 217);
this.label10.Name = "label10";
this.label10.Size = new System.Drawing.Size(13, 13);
this.label10.TabIndex = 16;
this.label10.Text = "0";
//
// NESSoundConfig
//
this.AcceptButton = this.OK;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.Cancel;
this.ClientSize = new System.Drawing.Size(209, 297);
this.Controls.Add(this.label10);
this.Controls.Add(this.label9);
this.Controls.Add(this.label8);
this.Controls.Add(this.label7);
this.Controls.Add(this.label6);
this.Controls.Add(this.label5);
this.Controls.Add(this.label4);
this.Controls.Add(this.label3);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.trackBar5);
this.Controls.Add(this.trackBar4);
this.Controls.Add(this.trackBar3);
this.Controls.Add(this.trackBar2);
this.Controls.Add(this.trackBar1);
this.Controls.Add(this.Cancel);
this.Controls.Add(this.OK);
this.MaximizeBox = false;
this.Name = "NESSoundConfig";
this.ShowIcon = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "NES Sound Channels";
this.Load += new System.EventHandler(this.NESSoundConfig_Load);
((System.ComponentModel.ISupportInitialize)(this.trackBar1)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.trackBar2)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.trackBar3)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.trackBar4)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.trackBar5)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
this.OK = new System.Windows.Forms.Button();
this.Cancel = new System.Windows.Forms.Button();
this.trackBar1 = new System.Windows.Forms.TrackBar();
this.label1 = new System.Windows.Forms.Label();
this.label6 = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.trackBar1)).BeginInit();
this.SuspendLayout();
//
// OK
//
this.OK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.OK.Location = new System.Drawing.Point(41, 117);
this.OK.Name = "OK";
this.OK.Size = new System.Drawing.Size(75, 23);
this.OK.TabIndex = 0;
this.OK.Text = "&OK";
this.OK.UseVisualStyleBackColor = true;
this.OK.Click += new System.EventHandler(this.Ok_Click);
//
// Cancel
//
this.Cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.Cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.Cancel.Location = new System.Drawing.Point(122, 117);
this.Cancel.Name = "Cancel";
this.Cancel.Size = new System.Drawing.Size(75, 23);
this.Cancel.TabIndex = 1;
this.Cancel.Text = "&Cancel";
this.Cancel.UseVisualStyleBackColor = true;
this.Cancel.Click += new System.EventHandler(this.Cancel_Click);
//
// trackBar1
//
this.trackBar1.LargeChange = 1;
this.trackBar1.Location = new System.Drawing.Point(12, 12);
this.trackBar1.Minimum = 1;
this.trackBar1.Name = "trackBar1";
this.trackBar1.Size = new System.Drawing.Size(104, 45);
this.trackBar1.TabIndex = 2;
this.trackBar1.Value = 1;
this.trackBar1.ValueChanged += new System.EventHandler(this.TrackBar1_ValueChanged);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(122, 12);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(67, 13);
this.label1.TabIndex = 7;
this.label1.Text = "APU Volume";
//
// label6
//
this.label6.AutoSize = true;
this.label6.Location = new System.Drawing.Point(122, 25);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(13, 13);
this.label6.TabIndex = 12;
this.label6.Text = "0";
//
// NESSoundConfig
//
this.AcceptButton = this.OK;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.Cancel;
this.ClientSize = new System.Drawing.Size(209, 152);
this.Controls.Add(this.label6);
this.Controls.Add(this.label1);
this.Controls.Add(this.trackBar1);
this.Controls.Add(this.Cancel);
this.Controls.Add(this.OK);
this.MaximizeBox = false;
this.Name = "NESSoundConfig";
this.ShowIcon = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "NES Sound Channels";
this.Load += new System.EventHandler(this.NESSoundConfig_Load);
((System.ComponentModel.ISupportInitialize)(this.trackBar1)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
@ -250,19 +117,7 @@
private System.Windows.Forms.Button OK;
private System.Windows.Forms.Button Cancel;
private System.Windows.Forms.TrackBar trackBar1;
private System.Windows.Forms.TrackBar trackBar2;
private System.Windows.Forms.TrackBar trackBar3;
private System.Windows.Forms.TrackBar trackBar4;
private System.Windows.Forms.TrackBar trackBar5;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.Label label6;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.Label label8;
private System.Windows.Forms.Label label9;
private System.Windows.Forms.Label label10;
}
}

View File

@ -38,11 +38,7 @@ namespace BizHawk.Client.EmuHawk
// get baseline maxes from a default config object
var d = new NES.NESSettings();
trackBar1.Maximum = d.Square1;
trackBar2.Maximum = d.Square2;
trackBar3.Maximum = d.Triangle;
trackBar4.Maximum = d.Noise;
trackBar5.Maximum = d.DMC;
trackBar1.Minimum = d.APU_vol;
}
private void NESSoundConfig_Load(object sender, EventArgs e)
@ -50,11 +46,7 @@ namespace BizHawk.Client.EmuHawk
_oldSettings = NES.GetSettings();
_settings = _oldSettings.Clone();
trackBar1.Value = _settings.Square1;
trackBar2.Value = _settings.Square2;
trackBar3.Value = _settings.Triangle;
trackBar4.Value = _settings.Noise;
trackBar5.Value = _settings.DMC;
trackBar1.Value = _settings.APU_vol;
}
private void Ok_Click(object sender, EventArgs e)
@ -72,35 +64,7 @@ namespace BizHawk.Client.EmuHawk
private void TrackBar1_ValueChanged(object sender, EventArgs e)
{
label6.Text = trackBar1.Value.ToString();
_settings.Square1 = trackBar1.Value;
NES.PutSettings(_settings);
}
private void TrackBar2_ValueChanged(object sender, EventArgs e)
{
label7.Text = trackBar2.Value.ToString();
_settings.Square2 = trackBar2.Value;
NES.PutSettings(_settings);
}
private void TrackBar3_ValueChanged(object sender, EventArgs e)
{
label8.Text = trackBar3.Value.ToString();
_settings.Triangle = trackBar3.Value;
NES.PutSettings(_settings);
}
private void TrackBar4_ValueChanged(object sender, EventArgs e)
{
label9.Text = trackBar4.Value.ToString();
_settings.Noise = trackBar4.Value;
NES.PutSettings(_settings);
}
private void TrackBar5_ValueChanged(object sender, EventArgs e)
{
label10.Text = trackBar5.Value.ToString();
_settings.DMC = trackBar5.Value;
_settings.APU_vol = trackBar1.Value;
NES.PutSettings(_settings);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 856 B

View File

@ -0,0 +1,148 @@
namespace BizHawk.Client.EmuHawk
{
partial class GBPrinterView
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(GBPrinterView));
this.paperView = new BmpView();
this.label1 = new System.Windows.Forms.Label();
this.paperScroll = new System.Windows.Forms.VScrollBar();
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.saveImageToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.editToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.copyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.menuStrip1.SuspendLayout();
this.SuspendLayout();
//
// menuStrip1
//
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.fileToolStripMenuItem,
this.editToolStripMenuItem});
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
this.menuStrip1.Name = "menuStrip1";
this.menuStrip1.Size = new System.Drawing.Size(336, 24);
this.menuStrip1.TabIndex = 2;
this.menuStrip1.Text = "menuStrip1";
//
// fileToolStripMenuItem
//
this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.saveImageToolStripMenuItem});
this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
this.fileToolStripMenuItem.Text = "&File";
//
// saveImageToolStripMenuItem
//
this.saveImageToolStripMenuItem.Name = "saveImageToolStripMenuItem";
this.saveImageToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.S)));
this.saveImageToolStripMenuItem.Size = new System.Drawing.Size(183, 22);
this.saveImageToolStripMenuItem.Text = "&Save Image...";
this.saveImageToolStripMenuItem.Click += new System.EventHandler(this.saveImageToolStripMenuItem_Click);
//
// editToolStripMenuItem
//
this.editToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.copyToolStripMenuItem});
this.editToolStripMenuItem.Name = "editToolStripMenuItem";
this.editToolStripMenuItem.Size = new System.Drawing.Size(39, 20);
this.editToolStripMenuItem.Text = "&Edit";
//
// copyToolStripMenuItem
//
this.copyToolStripMenuItem.Name = "copyToolStripMenuItem";
this.copyToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.C)));
this.copyToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.copyToolStripMenuItem.Text = "&Copy";
this.copyToolStripMenuItem.Click += new System.EventHandler(this.copyToolStripMenuItem_Click);
//
// paperView
//
this.paperView.Name = "paperView";
this.paperView.Location = new System.Drawing.Point(0, 48);
this.paperView.Size = new System.Drawing.Size(320, 320);
this.paperView.BackColor = System.Drawing.Color.Black;
this.paperView.TabIndex = 0;
//
// label1
//
this.label1.Name = "label1";
this.label1.Location = new System.Drawing.Point(0, 24);
this.label1.Size = new System.Drawing.Size(336, 24);
this.label1.Text = "Note: the printer is only connected while this window is open.";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// paperScroll
//
this.paperScroll.Name = "paperScroll";
this.paperScroll.Location = new System.Drawing.Point(320, 48);
this.paperScroll.Size = new System.Drawing.Size(16, 320);
this.paperScroll.Minimum = 0;
this.paperScroll.SmallChange = 8;
this.paperScroll.LargeChange = 160;
this.paperScroll.ValueChanged += PaperScroll_ValueChanged;
//
// GBPrinterView
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoSize = true;
this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.ClientSize = new System.Drawing.Size(336, 358);
this.Controls.Add(this.menuStrip1);
this.Controls.Add(this.paperView);
this.Controls.Add(this.label1);
this.Controls.Add(this.paperScroll);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MainMenuStrip = this.menuStrip1;
this.MaximizeBox = false;
this.Name = "GBPrinterView";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Printer Viewer";
this.menuStrip1.ResumeLayout(false);
this.menuStrip1.PerformLayout();
this.FormClosed += GBPrinterView_FormClosed;
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.MenuStrip menuStrip1;
private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem saveImageToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem editToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem copyToolStripMenuItem;
private BmpView paperView;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.VScrollBar paperScroll;
}
}

View File

@ -0,0 +1,215 @@
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Windows.Forms;
using BizHawk.Common.NumberExtensions;
using BizHawk.Client.Common;
using BizHawk.Emulation.Cores.Nintendo.Gameboy;
using BizHawk.Client.EmuHawk.WinFormExtensions;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy;
using BizHawk.Common;
namespace BizHawk.Client.EmuHawk
{
public partial class GBPrinterView : Form, IToolFormAutoConfig
{
const int PaperWidth = 160;
// the bg color
private static readonly uint PaperColor = (uint)Color.AntiqueWhite.ToArgb();
private ColorMatrix PaperAdjustment;
[RequiredService]
public IGameboyCommon Gb { get; private set; }
// If we've connected the printer yet
bool connected = false;
// the entire bitmap
Bitmap printerHistory;
public GBPrinterView()
{
InitializeComponent();
// adjust the color of the printed output to be more papery
PaperAdjustment = new ColorMatrix();
PaperAdjustment.Matrix00 = (0xFA - 0x10) / 255F;
PaperAdjustment.Matrix40 = 0x10 / 255F;
PaperAdjustment.Matrix11 = (0xEB - 0x10) / 255F;
PaperAdjustment.Matrix41 = 0x10 / 255F;
PaperAdjustment.Matrix22 = (0xD7 - 0x18) / 255F;
PaperAdjustment.Matrix42 = 0x18 / 255F;
paperView.ChangeBitmapSize(PaperWidth, PaperWidth);
ClearPaper();
}
private void GBPrinterView_FormClosed(object sender, FormClosedEventArgs e)
{
if (Gb != null)
{
Gb.SetPrinterCallback(null);
}
}
public bool UpdateBefore => false;
public bool AskSaveChanges() => true;
public void FastUpdate()
{
}
public void NewUpdate(ToolFormUpdateType type)
{
}
public void Restart()
{
// Really, there's not necessarilly a reason to clear it at all,
// since the paper would still be there,
// but it just seems right to get a blank slate on reset.
ClearPaper();
connected = false;
}
public void UpdateValues()
{
// Automatically connect once the game is running
if (!connected)
{
Gb.SetPrinterCallback(OnPrint);
connected = true;
}
}
/// <summary>
/// The printer callback that . See PrinterCallback for details.
/// </summary>
void OnPrint(IntPtr image, byte height, byte topMargin, byte bottomMargin, byte exposure)
{
// In this implementation:
// the bottom margin and top margin are just white lines at the top and bottom
// exposure is ignored
// The page received image
Bitmap page = new Bitmap(PaperWidth, height);
var bmp = page.LockBits(new Rectangle(0, 0, PaperWidth, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < PaperWidth; x++)
{
uint pixel;
unsafe
{
// Pixel of the image; it's just sent from the core as a big bitmap that's 160xheight
pixel = *(uint*)(image + (x + y * PaperWidth) * sizeof(uint));
}
SetPixel(bmp, x, y, pixel);
}
}
page.UnlockBits(bmp);
// add it to the bottom of the history
int oldHeight = printerHistory.Height;
ResizeHistory(printerHistory.Height + page.Height + topMargin + bottomMargin);
using (var g = Graphics.FromImage(printerHistory))
{
// Make it brown
ImageAttributes a = new ImageAttributes();
a.SetColorMatrix(PaperAdjustment);
g.DrawImage(page, new Rectangle(0, oldHeight + topMargin, page.Width, page.Height), 0F, 0F, page.Width, page.Height, GraphicsUnit.Pixel, a);
g.Flush();
}
RefreshView();
}
/// <summary>
/// Set a 2x pixel
/// </summary>
/// <param name="bmp">The bitmap data to draw to</param>
/// <param name="x">X position</param>
/// <param name="y">Y position</param>
/// <param name="c">The ARGB color to set that pixel to</param>
unsafe void SetPixel(BitmapData bmp, int x, int y, uint c)
{
uint* pixel = (uint*)(bmp.Scan0 + x * 4 + y * bmp.Stride);
*pixel = c;
}
void ClearPaper()
{
ResizeHistory(8);
RefreshView();
}
void ResizeHistory(int height)
{
// copy to a new image of height
var newHistory = new Bitmap(PaperWidth, height);
using (var g = Graphics.FromImage(newHistory))
{
g.Clear(Color.FromArgb((int)PaperColor));
if (printerHistory != null)
g.DrawImage(printerHistory, Point.Empty);
g.Flush();
}
if (printerHistory != null)
printerHistory.Dispose();
printerHistory = newHistory;
// Update scrollbar, viewport is a square
paperScroll.Maximum = Math.Max(0, height);
}
void RefreshView()
{
using (Graphics g = Graphics.FromImage(paperView.BMP))
{
g.Clear(Color.FromArgb((int)PaperColor));
g.DrawImage(printerHistory, new Point(0, -paperScroll.Value));
g.Flush();
}
paperView.Refresh();
}
private void saveImageToolStripMenuItem_Click(object sender, EventArgs e)
{
// slight hack to use the nice SaveFile() feature of a BmpView
BmpView toSave = new BmpView();
toSave.ChangeBitmapSize(printerHistory.Size);
using (var g = Graphics.FromImage(toSave.BMP))
{
g.DrawImage(printerHistory, Point.Empty);
g.Flush();
}
toSave.SaveFile();
}
private void copyToolStripMenuItem_Click(object sender, EventArgs e)
{
Clipboard.SetImage(printerHistory);
}
private void PaperScroll_ValueChanged(object sender, System.EventArgs e)
{
RefreshView();
}
}
}

View File

@ -0,0 +1,280 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAQAICAAAAEAIACoEAAARgAAACAgAAABAAgAqAgAAO4QAAAQEAAAAQAgAGgEAACWGQAAEBAAAAEA
CABoBQAA/h0AACgAAAAgAAAAQAAAAAEAIAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAB7e3tAfHx8v3d3d/9tbW3/aWlp/2lpaf9paWn/aWlp/2lpaf9paWn/aWlp/2lpaf9paWn/aWlp/2lp
af9paWn/aWlp/2lpaf9oaGj/ZmZm/3BwcP+EhIT/jo6Ov4+Pj0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAHt7e0B6enq/fX19/4GBgf+Dg4P/g4OD/4ODg/+Dg4P/g4OD/4KCgv+BgYH/fn5+/3x8
fP98fHz/fn5+/4GBgf+CgoL/goKC/3x8fP9ycnL/dXV1/4WFhf+MjIzPiIiIcISEhDB/f38QAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAd3d3QHl5eb+JiYn/qKio/7i4uP+3t7f/t7e3/7e3t/+3t7f/tra2/7Gx
sf+oqKj/o6Oj/6SkpP+pqan/s7Oz/7a2tv+0tLT/paWl/4iIiP9+fn7/h4eH/4mJie+FhYXPgoKCj4SE
hDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB3d3dAeHh4v4+Pj/+7u7v/0dHR/8/Pz//Pz8//0NDQ/9HR
0f/Q0ND/ycnJ/729vf+3t7f/uLi4/729vv/IyMr/zs7P/83Nzv+9vb3/np6e/46Ojv+NjY3/ioqK/4KC
gv9+fn6/f39/QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHd3d0B4eHi/jo6O/7q6uv/Ozs7/ysrK/8rK
yv/Ozs7/0NDQ/9DQ0P/Kysr/vb29/7e3t/+3t7f/urq8/8LCx//IyM3/zMzO/8TExf+ysrL/o6Oj/5iY
mP+MjIz/fn5+/3Z2dr93d3dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAd3d3QHZ2dr+JiYn/ra2t/7e3
t/+lpaX/p6en/7y8vP/Jycn/zs7O/8zMzP/Dw8L/urq9/7Kyu/+oqLz/nZ3C/6Cgxf+xscf/ubnD/7i4
vP+vr7D/oKCh/5OTlP+JiYr/hYWFz4aGhnCKioowj4+PEAAAAAAAAAAAAAAAAAAAAAB3d3dAdXV1v4CA
gP+VlZX/ioqK/2BgYP9mZmb/mpqa/7u7u//Jycn/z8/O/8zMzP/AwMj/qqrE/4iIv/9ZWbn/Vla3/39/
uP+cnLn/rq66/7GxtP+kpKf/n5+i/6Skpf+hoaHvkpKSz4uLi4+KioowAAAAAAAAAAAAAAAAAAAAAHNz
c0B0dHS/dHR0/3R0dP9gYGD/NjY2/zs7O/9wcHD/m5ub/7y8vP/Ozs7/0NDQ/8PDzv+mpsn/eHjD/zg4
u/8xMbf/ZGS1/4SEtf+Rkbf/lJS1/4yMrf+UlK7/ra22/62trv+Wlpb/ioqKv4uLi0AAAAAAAAAAAAAA
AAAAAAAAb29vQHBwcL9jY2P/S0tL/zY2Nv8mJib/KCgo/zw8PP9nZ2f/qKio/8rKyv/Pz87/wsLO/6am
yv94eMj/ODjJ/zAwxf9hYb7/cXG4/19fsv9YWLH/WVmz/3Jyt/+jo73/sLCy/5eXmP+Kioq/i4uLQAAA
AAAAAAAAAAAAAAAAAABvb29AcHBwv2VlZf9QUFD/PDw8/ygoKP8pKSn/Pz8//2pqav+pqan/ysrK/87O
zv/Gxs//sLDM/42NzP9cXND/VFTN/3d3w/90dLv/S0u0/zo6tf9AQL3/YWHC/5+fwv+xsbX/l5eZ/4qK
ir+Li4tAAAAAAAAAAAAAAAAAAAAAAHNzc0B1dXW/enp6/4WFhf9wcHD/PDw8/z8/P/94eHj/o6Oj/7+/
v//Ozs7/0NDQ/83N0P/Gxs//uLjP/6Sk0f+ens3/paXD/42Nvv9VVb3/OzvB/0BAzP9hYc7/n5/H/7Gx
tf+Xl5n/ioqKv4uLi0AAAAAAAAAAAAAAAAAAAAAAd3d3QHh4eL+Hh4f/pqam/5ubm/9nZ2f/ampq/6Oj
o//ExMT/zMzM/9DQ0P/Q0ND/0dHR/9HR0f/OztH/ysrS/8bGzv/Cwsf/p6fE/3Z2xf9fX8r/YmLT/3t7
0/+oqMj/srK1/5iYmf+Kioq/i4uLQAAAAAAAAAAAAAAAAAAAAAB3d3dAeHh4v4yMjP+0tLT/vb29/6io
qP+pqan/v7+//8zMzP/Pz8//0NDQ/9DQ0P/R0dH/0dHR/9DQ0f/OztH/zc3Q/8zMzf/Bwcz/r6/N/6am
z/+oqNP/r6/Q/7u7x/+0tLX/mJiZ/4qKir+Li4tAAAAAAAAAAAAAAAAAAAAAAHd3d0B4eHi/jo6O/7q6
uv/Nzc3/yMjI/8fHx//Ly8v/zc3N/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/87Ozv/Ozs7/zs7O/8zM
zv/Kys7/ycnP/8nJz//IyMz/xMTG/7W1tf+ZmZn/ioqKv4uLi0AAAAAAAAAAAAAAAAAAAAAAd3d3QHh4
eL+Ojo7/uLi4/8zMzP/IyMj/xsbG/8fHx//IyMj/yMjI/8jIyP/IyMj/yMjI/8jIyP/IyMj/yMjI/8jI
yP/IyMj/yMjI/8fHyP/Hx8n/x8fJ/8bGyP/ExMT/tbW1/5mZmf+Kioq/i4uLQAAAAAAAAAAAAAAAAAAA
AAB3d3dAdnZ2v4aGhv+jo6P/r6+v/6qqqv+nqKj/pqio/6WpqP+lqaj/pamo/6WpqP+lqaj/pamo/6Wp
qP+lqaj/pamo/6WpqP+mqaj/pqio/6eoqP+oqKj/rKys/7Kysv+rq6v/lZWV/4qKir+Li4tAAAAAAAAA
AAAAAAAAAAAAAHNzc0B0dHS/dnZ2/3t7e/94eXn/b3Bw/2lsbP9nbm3/ZW9t/2Vvbf9lb23/ZW9t/2Vv
bf9lb23/ZW9t/2Vvbf9lb23/ZW9t/2Zvbf9obmz/am1s/2xtbP94eHj/j4+P/5eXl/+Pj4//ioqKv4uL
i0AAAAAAAAAAAAAAAAAAAAAAb29vQHFxcb9sbGz/Y2Nj/11eXv9bXV3/UWNf/0FtY/84c2b/NnNm/zZz
Zv82c2b/NnNm/zZzZv82c2b/NnNm/zZzZv82c2b/O3Bl/0RrYv9OZV//Vl5c/2VkZf95eHn/hYWF/4mJ
if+Kioq/i4uLQAAAAAAAAAAAAAAAAAAAAABra2tAbW1tv2dnZ/9bW1v/XV9e/2xzcf9eioD/NKWM/x2z
kv8atJP/GLWT/xi1k/8YtZP/GLWT/xi1k/8YtZP/GLWT/xi1k/8krZD/PZ6J/1OOgf9oe3f/cXBx/25u
bv90dHT/hISE/4qKir+Li4tAAAAAAAAAAAAAAAAAAAAAAGtra0BsbGy/ZWVl/1dXV/9cX1//dH17/2Se
kf8swaD/DdSp/wnWqv8H16r/B9eq/wfXqv8H16r/B9eq/wfXqv8H16r/B9eq/xfNpv83uZ3/VaOS/3GK
hf93d3f/aWlp/2xsbP+BgYH/jIyMv4uLi0AAAAAAAAAAAAAAAAAAAAAAa2trQGxsbL9lZWX/V1dX/1xf
Xv90fXv/Y5+R/yjDof8J1qr/BNir/wLZq/8C2av/Atmr/wLZq/8C2av/Atmr/wLZq/8C2av/E8+m/zS6
nf9To5L/cIqE/3d3d/9paWn/bGxs/4GBgf+MjIy/i4uLQAAAAAAAAAAAAAAAAAAAAABra2tAbGxsv2Vl
Zf9XV1f/XF9e/3R9e/9in5L/JsOi/wbXqv8C2av/ANus/wDbrP8A26z/ANus/wDbrP8A26z/ANus/wDb
rP8R0Kf/M7ud/1Kkkv9wioT/d3d3/2lpaf9sbGz/gYGB/4yMjL+Li4tAAAAAAAAAAAAAAAAAAAAAAGtr
a0BsbGy/ZWVl/1dXV/9cX17/dH17/2Kfkv8mw6L/Bteq/wLZq/8A26z/ANus/wDbrP8A26z/ANus/wDb
rP8A26z/ANus/xHQp/8zu53/UqSS/3CKhP93d3f/aWlp/2xsbP+BgYH/jIyMv4uLi0AAAAAAAAAAAAAA
AAAAAAAAa2trQGxsbL9lZWX/V1dX/1xfXv90fXv/Yp+S/ybDov8G16r/Atmr/wDbrP8A26z/ANus/wDb
rP8A26z/ANus/wDbrP8A26z/EdCn/zO7nf9SpJL/cIqE/3d3d/9paWn/bGxs/4GBgf+MjIy/i4uLQAAA
AAAAAAAAAAAAAAAAAABra2tAbGxsv2VlZf9XV1f/XF9e/3R9e/9in5L/JsOi/wbXqv8C2av/ANus/wDb
rP8A26z/ANus/wDbrP8A26z/ANus/wDbrP8R0Kf/M7ud/1Kkkv9wioT/d3d3/2lpaf9sbGz/gYGB/4yM
jL+Li4tAAAAAAAAAAAAAAAAAAAAAAGtra0BsbGy/ZWVl/1dXV/9cX17/dH17/2Ofkf8ow6H/Cdaq/wTY
q/8C2qv/Atqr/wLaq/8C2qv/Atqr/wLaq/8C2qv/Atqr/xPPpv80up3/U6OS/3CKhP93d3f/aWlp/2xs
bP+BgYH/jIyMv4uLi0AAAAAAAAAAAAAAAAAAAAAAa2trQGxsbL9lZWX/V1dX/1xfX/90fXv/ZJ6R/yzB
oP8N1Kn/Cdaq/wfYqv8H2Kr/B9iq/wfYqv8H2Kr/B9iq/wfYqv8H2Kr/F82m/ze5nf9Vo5L/cYqE/3d3
d/9paWn/bGxs/4GBgf+MjIy/i4uLQAAAAAAAAAAAAAAAAAAAAABra2tAbW1tv2dnZ/9bW1v/XWBf/210
cv9fi4H/NaaN/x60k/8btpT/GbeU/xm3lP8Zt5T/GbeU/xm3lP8Zt5T/GbeU/xm3lP8lr5H/Pp+K/1SP
gv9pfHj/cnFx/25ubv90dHT/hISE/4qKir+Li4tAAAAAAAAAAAAAAAAAAAAAAG9vb0BxcXG/bGxs/2Rk
ZP9fYGD/XWBg/1RmYv9EcGb/O3Zp/zl2af85d2n/OXdp/zl3af85d2n/OXdp/zl3af85d2n/OXdp/z5z
aP9HbWX/UWdi/1lhX/9nZ2f/enp6/4WFhf+JiYn/ioqKv4uLi0AAAAAAAAAAAAAAAAAAAAAAc3NzQHV1
db9xcXH/aGho/2BgYP9XWFj/UVRU/09WVf9NV1X/TVdV/01XVf9NV1X/TVdV/01XVf9NV1X/TVdV/01X
Vf9NV1X/TldV/1BWVP9SVVT/VFVU/2FhYf95eXn/hoaG74iIiM+Li4uPioqKMAAAAAAAAAAAAAAAAAAA
AAB3d3dAeXl5v3R0dP9oaGj/YGBg/1paWv9XWFj/VlhY/1VZWP9VWVj/VVlY/1VZWP9VWVj/VVlY/1VZ
WP9VWVj/VVlY/1VZWP9WWVj/VlhY/1dYWP9YWFj/X19f/2xsbP91dXXPfX19cIqKijCPj48QAAAAAAAA
AAAAAAAAAAAAAHt7e0B8fHy/dXV1/2hoaP9gYGD/XFxc/1paWv9aWlr/Wlpa/1paWv9aWlr/Wlpa/1pa
Wv9aWlr/Wlpa/1paWv9aWlr/Wlpa/1paWv9aWlr/Wlpa/1paWv9eXl7/ZmZm/2lpab9ra2tAAAAAAAAA
AAAAAAAA8AAAP/AAAD/wAAAP8AAAD/AAAA/wAAAP8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AA
AAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AA
AAPwAAAD8AAAD/AAAA8oAAAAIAAAAEAAAAABAAgAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAA/zMz
M/82Njb/ODg4/z57bv87fW//OH9v/zd/cP9DQ0P/SEhI/01NTf9MX1v/T15b/1FRUf9XV1f/VFlY/1dY
WP9TXFr/UF5b/1RcWv9YWln/Wlpa/1ldXP9aXFz/XFxc/11eXv9KYVz/TGBc/11lY/9UbWf/XWhm/1Jt
aP9HdWv/RnZs/1dzbf9Zcm3/THpw/0t8cf9gYGD/YGJi/2JiYv9hZGP/YWVk/2FmZP9kZGT/ZmZm/2Nq
af9ibmv/aGho/2pqav9pbGv/bGxs/25ubv9jcm//ZHNv/29xcf9tcnH/bnNy/2R6dv9ofHf/aHx4/2h/
ev9sf3v/b357/3BwcP9ycnL/cXd2/3R0dP92dnb/cnl3/3N8ev90fHr/eHh4/3p6ev95fHv/en18/3x8
fP9+fn7/NoBw/zuFdf88hHT/Q4Bz/0CCdP9Whnz/Uol9/2qAfP9sgHz/Skq5/0tLvP9PT77/UVG//1pa
uP9jY7T/Z2e1/2pqtv9mZrn/YWG+/2Rkv/9vb7n/bW28/3FxvP99fbj/SkrD/05Owv9RUcf/UlLI/25u
wf9pacr/dXXD/3l5xP9wcMr/cXHO/3p6yf87m4b/PpiE/zych/8crY7/HqyN/x2vj/8ero//K6SK/yym
i/8mqIv/IKuN/yepjP8hrY7/OLKX/zS2mf84spj/M7ud/zW6nf8wvp//Lr+g/1Sdjf9Ymov/WJqM/1if
kP9piIL/aoiB/2qMhf9To5H/V6CQ/1Kkkv8ewp//D8yj/wvOpP8OzaT/DM6k/w7OpP8Yx6H/EM2j/xbN
pv8Wzqb/Gcyl/wbXqv8P0qj/CNaq/wPZq/8E2Kv/Btiq/wDarP8R0aj/gICA/4KCgv+DhIT/hISE/4aG
hv+IiIj/ioqK/4yMjP+Ojo7/j4+Q/5CQkP+SkpL/lJSU/5aWl/+YmJj/mpqa/5ubnf+cnJ3/np6i/5yc
pP+Tk6//n5+p/4qKsf+Kirf/gYG5/5WVv/+amrr/nqKh/6Ghof+jpKT/pKSk/6ampv+lpan/pqau/6mp
qf+rq67/ra2t/6KisP+lpbL/qamz/66usv+mprn/pKS+/7CwsP+1tbX/srK4/7e3uv+xsb7/t7e+/7i4
uP+9vb7/hobA/4ODxv+KisD/h4fP/5aWw/+amsH/nJzD/5iYxv+QkM3/mprM/6Kixf+oqMT/o6PJ/6Wl
y/+goM7/qqrN/7KywP+zs8X/u7vA/76+wf+6usX/vb3H/7e3yf+wsM3/u7vL/7+/yv++vs3/oKDQ/7Gx
0P+/wMD/wMHB/8TExf/Bwcr/wMDN/8bGzf/Jycn/y8vN/83Nzf/BwdD/ycnQ/83N0P/Q0ND/AAAA/wBM
TExJSURDQ0NDQ0FBQUFBOUFCQ0A0RKOoqaelo6QAAExMTE2ipqmpqaqqqaimpKWnqKmno0lJo6enpqOl
pQAASUlJoqq+xs3Nzc3NxMC+vb/FxsazqqWnqKalo6KiAABISEmirs30+Pj4+PTz087O0PL0886+r6qo
paJNTaOoAEhISKOvzfP09Pj6/vjy09DS6e716M2+r6qmoqKlp6kASEhITaq/xL7BzvT6+PTn0cza2+DR
z8W/r6qpqKipqABERERJpKmlQ0SvzfT5+Org12Nj1bzLyLW1tLOvq6ipAENDQ0NBNBkJCjOvzvj97+Ft
Wlhfurm5uLbHwbCsqagAQEBBNDAQCAEDDUnB9Pn24nBpZmBkXlxdZbzIsqyoqQBBQUE0MBgJAgMNS8T0
+fvs3W9rbGNbV1pq2ce0rKmoAEREQ0RJSDANDUOx0/j+/fvx8N7c1WBnaW7ZybOsqakARERITaiwq0lM
s9P4+v7+/fz77+vf1m5v2N/Js6yoqABISEmircTNwcHT+Pr+/v7+/vr59+7k4/Dk5sqzrKmpAEhISKKu
zfPz8/T5+P36+vr6/vr59/f1+/bozb6tqKgASEhJoq7G1PPy8vP08/Pz8/Pz8/Py8+ry6NTNs6ypqQBI
SERMp7G/v769vb29vb29vb29vb29vr7BxMCwqqioAENDRERMTaJKRz9VVVVVVVVVPVU+Pz9GSqSsrqyq
qakAQEBAQDMxLS4jJVJQUE9PT09PT1BRJCMvN0mlqKioqAA0NDQzLSYqNVNzfHV2dHZ0dnR2fXhyVDo4
QUmjqKmpADMzMzEsGSo7h3+VlJSbk5uTm5OUj4CFiUJAQU2nqakAMzMzMSwZKz6Ig6GfnZ2dnZ2dnZyZ
go6LRTRBTaepqQAzMzMwKBcrPY2Em5+goKCgoKCgn5iBjotFNEBNp6mpADMzMzEsGSs9jYSbnaCgoKCg
oKCemIGOi0U0QU2nqakAMzMzMCgYKz2NhJudnaCgoKCgoJ+YgY6LRTRATaepqQAzMzMxLBkrPYiDm52g
oKCgoKCgnpiBjotFNEFNp6mpADMzMzAoGCtViIShn52dnZ2dnZ2cmYKMi0U0QE2nqakAMzMzMSwZKzyH
f5WUk5GRkZGRkZaPgIWJRTRDoqepqQA0NDQzLScqNlNzfHV3dnZ2dnZ2fXlyVDs5QUijp6ipAEFBQUAz
LCccHSEFB05OTk5OTk4GBCAfHjJEoqaoqakAQ0NDQTQwJhcTDBsaGhoaGhoaGhoLDBMWLEOipqioqABJ
SUlIQTEoFRQQDw8PDw8PDw8PDw8QEBUoNEiipqmpAElJSUhBMCYYFBQUFBQUFBQUFBQUFBQUFSYtM0FM
p6jwAAA/8AAAP/AAAA/wAAAP8AAAD/AAAA/wAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AA
AAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AA
AAPwAAAP8AAADygAAAAQAAAAIAAAAAEAIAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHx8
fP9paWn/aWlp/2lpaf9paWn/aWlp/2lpaf9paWn/aWlp/2ZmZv+Pj4//AAAAAAAAAAAAAAAAAAAAAAAA
AAB5eXn/09PT/9LS0v/S0tL/z8/P/7e3t/+5ubn/0tLS/83Nzf+BgYH/i4uL/4ODg/8AAAAAAAAAAAAA
AAAAAAAAeHh4/9DQ0P/Gxsb/0NDQ/9HR0f+4uLj/tra2/8PDzP/Ozs//t7e3/5aWlv90dHT/AAAAAAAA
AAAAAAAAAAAAAHZ2dv+QkJD/IyMj/6ysrP/Q0ND/0tLR/5iYx/8YGK//gICy/7i4vf+goKX/uLi4/4uL
i/8AAAAAAAAAAAAAAABubm7/JCQk/xwcHP8lJSX/xsbG/9HR0f+YmMn/GBjS/3h4vv83N6z/Q0O6/76+
w/+Li4v/AAAAAAAAAAAAAAAAeHh4/62trf8lJSX/u7u7/9DQ0P/R0dH/0dHR/8bG0/+6ur//OjrC/0JC
2v++vsT/i4uL/wAAAAAAAAAAAAAAAHh4eP/R0dH/xsbG/9DQ0P/R0dH/0dHR/9HR0f/R0dH/0dHR/8rK
0v/Ly9P/w8PD/4uLi/8AAAAAAAAAAAAAAAB5eXn/zc3N/8bGxv/Gxsb/xsbG/8bGxv/Gxsb/xsbG/8bG
xv/Gxsb/x8fH/8PDw/+Li4v/AAAAAAAAAAAAAAAAc3Nz/2NjY/9NTk7/RlNQ/0VTUP9FU1D/RVNQ/0VT
UP9FU1D/SlFP/09PT/+Ojo7/i4uL/wAAAAAAAAAAAAAAAGxsbP9RUVH/gY2K/xLSqP8K1qr/Ctaq/wrW
qv8K1qr/Ctaq/0mvmf9/fn//YmJi/4yMjP8AAAAAAAAAAAAAAABsbGz/UFBQ/4CNiv8J1qr/ANus/wDb
rP8A26z/ANus/wDbrP9EsZn/f35+/2JiYv+MjIz/AAAAAAAAAAAAAAAAbGxs/1BQUP+AjYr/Cdaq/wDb
rP8A26z/ANus/wDbrP8A26z/RLGZ/39+fv9iYmL/jIyM/wAAAAAAAAAAAAAAAGxsbP9QUFD/gI2K/wnW
qv8A26z/ANus/wDbrP8A26z/ANus/0Sxmf9/fn7/YmJi/4yMjP8AAAAAAAAAAAAAAABsbGz/UVFR/4GN
iv8S0qj/Cteq/wrXqv8K16r/Cteq/wrXqv9Jr5n/f35+/2JiYv+MjIz/AAAAAAAAAAAAAAAAc3Nz/2Vl
Zf9RUlL/SldU/0lXVP9JV1T/SVdU/0lXVP9JV1T/TlRT/1NTU/+Pj4//i4uL/wAAAAAAAAAAAAAAAHx8
fP9iYmL/Wlpa/1paWv9aWlr/Wlpa/1paWv9aWlr/Wlpa/1paWv9bW1v/ampq/wAAAAAAAAAAwAcAAMAD
AADAAwAAwAEAAMABAADAAQAAwAEAAMABAADAAQAAwAEAAMABAADAAQAAwAEAAMABAADAAQAAwAMAACgA
AAAQAAAAIAAAAAEACAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAcHBz/IyMj/yQkJP8lJSX/TU5O/09P
T/9KUU//RVNQ/0ZTUP9OVFP/SVdU/0pXVP9QUFD/UVFR/1FSUv9TU1P/Wlpa/1tbW/9iYmL/Y2Nj/2Vl
Zf9mZmb/aWlp/2pqav9sbGz/bm5u/3Nzc/90dHT/dnZ2/3h4eP95eXn/fHx8/39+fv8YGK//Nzes/0ND
uv94eL7/GBjS/zo6wv9CQtr/Sa+Z/0Sxmf8J1qr/Ctaq/wDbrP8S0qj/gYGB/4ODg/+AjYr/gY2K/4uL
i/+MjIz/jo6O/4+Pj/+QkJD/lpaW/4CAsv+goKX/rKys/62trf+2trb/t7e3/7i4uP+5ubn/u7u7/7i4
vf+6ur//mJjH/5iYyf++vsP/vr7E/8PDw//Gxsb/x8fH/8PDzP/Nzc3/zs7P/8/Pz//GxtP/ysrS/8vL
0//Q0ND/0dHR/9LS0f/T09P//////wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/VVUfFhYWFhYWFxYVNVVVVVVV
HlRTU009P1NLLjIvVVVVVR1RSFFSPjxKTD03G1VVVVUcNgE6UVNDIThBOT4yVVVVGQIAA0hSRCUkIiNF
MlVVVR07A0BRUlJOQiYnRjJVVVUdUkhRUlJSUlJPUEcyVVVVHktISEhISEhISElHMlVVVRoTBAgHBwcH
BwYFNDJVVVUYDTEtKysrKysoIBIzVVVVGAwwKiwsLCwsKSASM1VVVRgMMCosLCwsLCkgEjNVVVUYDDAq
LCwsLCwpIBIzVVVVGA0xLSsrKysrKCASM1VVVRoUDgsKCgoKCgkPNTJVVVUfEhAQEBAQEBAQERdVVcAH
AADAAwAAwAMAAMABAADAAQAAwAEAAMABAADAAQAAwAEAAMABAADAAQAAwAEAAMABAADAAQAAwAEAAMAD
AAA=
</value>
</data>
</root>

View File

@ -526,7 +526,16 @@ namespace BizHawk.Client.EmuHawk
{
if (_addr + j + DataSize <= _domain.Size)
{
rowStr.AppendFormat(_digitFormatString, MakeValue(_addr + j));
int t_val = 0;
int t_next = 0;
for (int k = 0; k < DataSize; k++)
{
t_next = MakeValue(1, _addr + j + k);
t_val += (t_next << ((DataSize - k - 1) * 8));
}
rowStr.AppendFormat(_digitFormatString, t_val);
}
else
{
@ -569,7 +578,7 @@ namespace BizHawk.Client.EmuHawk
{
if (Global.CheatList.IsActive(_domain, address))
{
return Global.CheatList.GetCheatValue(_domain, address, (WatchSize)DataSize ).Value;
return Global.CheatList.GetCheatValue(_domain, address, (WatchSize)dataSize ).Value;
}
switch (dataSize)

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
@ -289,5 +290,74 @@ namespace BizHawk.Client.EmuHawk
}
}
}
public class TastudioBranchInfo
{
public string Id { get; set; }
public int Frame { get; set; }
public string Text { get; set; }
}
[LuaMethod("getbranches", "Returns a list of the current tastudio branches. Each entry will have the Id, Frame, and Text properties of the branch")]
public LuaTable GetBranches()
{
var table = Lua.NewTable();
if (Engaged())
{
var branches = Tastudio.CurrentTasMovie.Branches.Select(b => new
{
Id = b.UniqueIdentifier.ToString(),
Frame = b.Frame,
Text = b.UserText
})
.ToList();
for (int i = 0; i < branches.Count; i++)
{
table[i] = branches[i];
}
}
return table;
}
[LuaMethod("getbranchinput", "Gets the controller state of the given frame with the given branch identifier")]
public LuaTable GetBranchInput(string branchId, int frame)
{
var table = Lua.NewTable();
if (Engaged())
{
if (Tastudio.CurrentTasMovie.Branches.Any(b => b.UniqueIdentifier.ToString() == branchId))
{
var branch = Tastudio.CurrentTasMovie.Branches.First(b => b.UniqueIdentifier.ToString() == branchId);
if (frame < branch.InputLog.Count)
{
var input = branch.InputLog[frame];
var adapter = new Bk2ControllerAdapter
{
Definition = Global.MovieSession.MovieControllerAdapter.Definition
};
adapter.SetControllersAsMnemonic(input);
foreach (var button in adapter.Definition.BoolButtons)
{
table[button] = adapter.IsPressed(button);
}
foreach (var button in adapter.Definition.FloatControls)
{
table[button] = adapter.GetFloat(button);
}
}
}
}
return table;
}
}
}

View File

@ -55,7 +55,7 @@ namespace BizHawk.Client.EmuHawk
private bool _triggerAutoRestore; // If true, autorestore will be called on mouse up
private bool? _autoRestorePaused = null;
private int? _seekStartFrame = null;
private bool _shouldUnpauseFromRewind = false;
private bool _unpauseAfterSeeking = false;
private ControllerDefinition ControllerType => Global.MovieSession.MovieControllerAdapter.Definition;
@ -111,10 +111,10 @@ namespace BizHawk.Client.EmuHawk
}
Mainform.PauseOnFrame = null;
if (_shouldUnpauseFromRewind)
if (_unpauseAfterSeeking)
{
Mainform.UnpauseEmulator();
_shouldUnpauseFromRewind = false;
_unpauseAfterSeeking = false;
}
if (CurrentTasMovie != null)
@ -762,7 +762,8 @@ namespace BizHawk.Client.EmuHawk
private void TasView_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right && !TasView.IsPointingAtColumnHeader && !_supressContextMenu && TasView.SelectedRows.Any())
if (e.Button == MouseButtons.Right && !TasView.IsPointingAtColumnHeader &&
!_supressContextMenu && TasView.SelectedRows.Any() && !_leftButtonHeld)
{
if (Global.MovieSession.Movie.FrameCount < TasView.SelectedRows.Max())
{

View File

@ -72,11 +72,10 @@ namespace BizHawk.Client.EmuHawk
}
else if (ofd.FileName.EndsWith(".bkm") || ofd.FileName.EndsWith(".bk2")) // todo: proper extention iteration
{
Mainform.StartNewMovie(MovieService.Get(ofd.FileName), false);
var result1 = MessageBox.Show("This is a regular movie, a new project must be created from it, in order to use in TAStudio\nProceed?", "Convert movie", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
if (result1 == DialogResult.OK)
{
Mainform.StartNewMovie(MovieService.Get(ofd.FileName), false);
ConvertCurrentMovieToTasproj();
StartNewMovieWrapper(false);
SetUpColumns();

View File

@ -829,7 +829,7 @@ namespace BizHawk.Client.EmuHawk
if (frame == Emulator.Frame)
return;
_shouldUnpauseFromRewind = fromRewinding && !Mainform.EmulatorPaused;
_unpauseAfterSeeking = (fromRewinding || WasRecording) && !Mainform.EmulatorPaused;
TastudioPlayMode();
KeyValuePair<int, byte[]> closestState = CurrentTasMovie.TasStateManager.GetStateClosestToFrame(frame);
if (closestState.Value != null && (frame < Emulator.Frame || closestState.Key > Emulator.Frame))
@ -871,7 +871,8 @@ namespace BizHawk.Client.EmuHawk
// frame == Emulator.Frame when frame == 0
if (frame > Emulator.Frame)
{
if (Mainform.EmulatorPaused || Mainform.IsSeeking || fromRewinding) // make seek frame keep up with emulation on fast scrolls
// make seek frame keep up with emulation on fast scrolls
if (Mainform.EmulatorPaused || Mainform.IsSeeking || fromRewinding || WasRecording)
{
StartSeeking(frame);
}

View File

@ -1,265 +0,0 @@
using System.Collections.Generic;
using System.Drawing;
using BizHawk.Common.ReflectionExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Atari.A7800Hawk;
namespace BizHawk.Client.EmuHawk
{
[Schema("A7800")]
public class A7800HawkSchema : IVirtualPadSchema
{
private string UnpluggedControllerName => typeof(UnpluggedController).DisplayName();
private string StandardControllerName => typeof(StandardController).DisplayName();
private string ProLineControllerName => typeof(ProLineController).DisplayName();
public IEnumerable<PadSchema> GetPadSchemas(IEmulator core)
{
var A78SyncSettings = ((A7800Hawk)core).GetSyncSettings().Clone();
var port1 = A78SyncSettings.Port1;
var port2 = A78SyncSettings.Port2;
if (port1 == StandardControllerName)
{
yield return JoystickController(1);
}
if (port2 == StandardControllerName)
{
yield return JoystickController(2);
}
if (port1 == ProLineControllerName)
{
yield return ProLineController(1);
}
if (port2 == ProLineControllerName)
{
yield return ProLineController(2);
}
}
private static PadSchema ProLineController(int controller)
{
return new PadSchema
{
DisplayName = "Player " + controller,
IsConsole = false,
DefaultSize = new Size(174, 74),
MaxSize = new Size(174, 74),
Buttons = new[]
{
new PadSchema.ButtonSchema
{
Name = "P" + controller + " Up",
DisplayName = "",
Icon = Properties.Resources.BlueUp,
Location = new Point(23, 15),
Type = PadSchema.PadInputType.Boolean
},
new PadSchema.ButtonSchema
{
Name = "P" + controller + " Down",
DisplayName = "",
Icon = Properties.Resources.BlueDown,
Location = new Point(23, 36),
Type = PadSchema.PadInputType.Boolean
},
new PadSchema.ButtonSchema
{
Name = "P" + controller + " Left",
DisplayName = "",
Icon = Properties.Resources.Back,
Location = new Point(2, 24),
Type = PadSchema.PadInputType.Boolean
},
new PadSchema.ButtonSchema
{
Name = "P" + controller + " Right",
DisplayName = "",
Icon = Properties.Resources.Forward,
Location = new Point(44, 24),
Type = PadSchema.PadInputType.Boolean
},
new PadSchema.ButtonSchema
{
Name = "P" + controller + " Trigger",
DisplayName = "1",
Location = new Point(120, 24),
Type = PadSchema.PadInputType.Boolean
},
new PadSchema.ButtonSchema
{
Name = "P" + controller + " Trigger 2",
DisplayName = "2",
Location = new Point(145, 24),
Type = PadSchema.PadInputType.Boolean
}
}
};
}
private static PadSchema JoystickController(int controller)
{
return new PadSchema
{
DisplayName = "Player " + controller,
IsConsole = false,
DefaultSize = new Size(174, 74),
MaxSize = new Size(174, 74),
Buttons = new[]
{
new PadSchema.ButtonSchema
{
Name = "P" + controller + " Up",
DisplayName = "",
Icon = Properties.Resources.BlueUp,
Location = new Point(23, 15),
Type = PadSchema.PadInputType.Boolean
},
new PadSchema.ButtonSchema
{
Name = "P" + controller + " Down",
DisplayName = "",
Icon = Properties.Resources.BlueDown,
Location = new Point(23, 36),
Type = PadSchema.PadInputType.Boolean
},
new PadSchema.ButtonSchema
{
Name = "P" + controller + " Left",
DisplayName = "",
Icon = Properties.Resources.Back,
Location = new Point(2, 24),
Type = PadSchema.PadInputType.Boolean
},
new PadSchema.ButtonSchema
{
Name = "P" + controller + " Right",
DisplayName = "",
Icon = Properties.Resources.Forward,
Location = new Point(44, 24),
Type = PadSchema.PadInputType.Boolean
},
new PadSchema.ButtonSchema
{
Name = "P" + controller + " Trigger",
DisplayName = "1",
Location = new Point(120, 24),
Type = PadSchema.PadInputType.Boolean
}
}
};
}
private static PadSchema PaddleController(int controller)
{
return new PadSchema
{
DisplayName = "Player " + controller,
IsConsole = false,
DefaultSize = new Size(250, 74),
Buttons = new[]
{
new PadSchema.ButtonSchema
{
Name = "P" + controller + " Paddle",
DisplayName = "Paddle",
Location = new Point(23, 15),
Type = PadSchema.PadInputType.FloatSingle
},
new PadSchema.ButtonSchema
{
Name = "P" + controller + " Trigger",
DisplayName = "1",
Location = new Point(12, 90),
Type = PadSchema.PadInputType.Boolean
}
}
};
}
private static PadSchema LightGunController(int controller)
{
return new PadSchema
{
DisplayName = "Light Gun",
IsConsole = false,
DefaultSize = new Size(356, 290),
MaxSize = new Size(356, 290),
Buttons = new[]
{
new PadSchema.ButtonSchema
{
Name = "P" + controller + " VPos",
Location = new Point(14, 17),
Type = PadSchema.PadInputType.TargetedPair,
TargetSize = new Size(256, 240),
SecondaryNames = new[]
{
"P" + controller + " HPos",
}
},
new PadSchema.ButtonSchema
{
Name = "P" + controller + " Trigger",
DisplayName = "Trigger",
Location = new Point(284, 17),
Type = PadSchema.PadInputType.Boolean
}
}
};
}
private static PadSchema ConsoleButtons()
{
return new PadSchema
{
DisplayName = "Console",
IsConsole = true,
DefaultSize = new Size(215, 50),
Buttons = new[]
{
new PadSchema.ButtonSchema
{
Name = "Select",
DisplayName = "Select",
Location = new Point(10, 15),
Type = PadSchema.PadInputType.Boolean
},
new PadSchema.ButtonSchema
{
Name = "Reset",
DisplayName = "Reset",
Location = new Point(60, 15),
Type = PadSchema.PadInputType.Boolean
},
new PadSchema.ButtonSchema
{
Name = "Power",
DisplayName = "Power",
Location = new Point(108, 15),
Type = PadSchema.PadInputType.Boolean
},
new PadSchema.ButtonSchema
{
Name = "Pause",
DisplayName = "Pause",
Location = new Point(158, 15),
Type = PadSchema.PadInputType.Boolean
},
new PadSchema.ButtonSchema
{
Name = "BW",
DisplayName = "BW",
Location = new Point(158, 15),
Type = PadSchema.PadInputType.Boolean
}
}
};
}
}
}

View File

@ -2,7 +2,9 @@
using System.Drawing;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Atari.Atari7800;
using BizHawk.Common.ReflectionExtensions;
using BizHawk.Emulation.Cores.Atari.A7800Hawk;
namespace BizHawk.Client.EmuHawk
{
@ -11,33 +13,42 @@ namespace BizHawk.Client.EmuHawk
{
public IEnumerable<PadSchema> GetPadSchemas(IEmulator core)
{
switch (((Atari7800)core).ControlAdapter.ControlType.Name)
return Atari7800HawkSchema.GetPadSchemas((A7800Hawk)core);
}
}
internal static class Atari7800HawkSchema
{
private static string UnpluggedControllerName => typeof(UnpluggedController).DisplayName();
private static string StandardControllerName => typeof(StandardController).DisplayName();
private static string ProLineControllerName => typeof(ProLineController).DisplayName();
public static IEnumerable<PadSchema> GetPadSchemas(A7800Hawk core)
{
var A78SyncSettings = core.GetSyncSettings().Clone();
var port1 = A78SyncSettings.Port1;
var port2 = A78SyncSettings.Port2;
if (port1 == StandardControllerName)
{
case "Atari 7800 Joystick Controller":
yield return JoystickController(1);
yield return JoystickController(2);
break;
case "Atari 7800 Paddle Controller":
yield return PaddleController(1);
yield return PaddleController(2);
break;
case "Atari 7800 Keypad Controller":
break;
case "Atari 7800 Driving Controller":
break;
case "Atari 7800 Booster Grip Controller":
break;
case "Atari 7800 ProLine Joystick Controller":
yield return ProLineController(1);
yield return ProLineController(2);
break;
case "Atari 7800 Light Gun Controller":
yield return LightGunController(1);
yield return LightGunController(2);
break;
yield return JoystickController(1);
}
if (port2 == StandardControllerName)
{
yield return JoystickController(2);
}
if (port1 == ProLineControllerName)
{
yield return ProLineController(1);
}
if (port2 == ProLineControllerName)
{
yield return ProLineController(2);
}
yield return ConsoleButtons();
}
private static PadSchema ProLineController(int controller)

View File

@ -91,6 +91,7 @@
<Compile Include="Interfaces\IEmulatorServiceProvider.cs" />
<Compile Include="Interfaces\Services\IBoardInfo.cs" />
<Compile Include="Interfaces\Services\ICreateGameDBEntries.cs" />
<Compile Include="Interfaces\Services\ICycleTiming.cs" />
<Compile Include="Interfaces\Services\ISoundProvider.cs" />
<Compile Include="Interfaces\Services\ICodeDataLogger.cs" />
<Compile Include="Interfaces\Services\IDebuggable.cs" />

View File

@ -20,6 +20,20 @@ namespace BizHawk.Emulation.Common
}
}
public class NoAvailableCoreException : Exception
{
public NoAvailableCoreException()
: base("System is currently NOT emulated")
{
}
public NoAvailableCoreException(string message)
: base ("System is currently NOT emulated: " + message)
{
}
}
public class CGBNotSupportedException : Exception
{
public CGBNotSupportedException()

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BizHawk.Emulation.Common
{
public interface ICycleTiming
{
/// <summary>
/// Total elapsed emulation time relative to <see cref="ClockRate"/>
/// </summary>
long CycleCount { get; }
/// <summary>
/// Clock Rate in hz for <see cref="CycleCount"/>
/// </summary>
double ClockRate { get; }
}
}

View File

@ -63,9 +63,6 @@
<Reference Include="ELFSharp">
<HintPath>..\References\ELFSharp.dll</HintPath>
</Reference>
<Reference Include="EMU7800">
<HintPath>..\References\EMU7800.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\References\Newtonsoft.Json.dll</HintPath>
</Reference>
@ -368,30 +365,10 @@
</Compile>
<Compile Include="Consoles\Atari\A7800Hawk\M6532.cs" />
<Compile Include="Consoles\Atari\A7800Hawk\Maria.cs" />
<Compile Include="Consoles\Atari\A7800Hawk\Pokey.cs" />
<Compile Include="Consoles\Atari\A7800Hawk\TIA_Sound\Tia.Audio.cs" />
<Compile Include="Consoles\Atari\A7800Hawk\TIA_Sound\TIA.cs" />
<Compile Include="Consoles\Atari\A7800Hawk\TIA_Sound\Tia.ISoundProvider.cs" />
<Compile Include="Consoles\Atari\A7800Hawk\TIA_Sound\Tia.SyncState.cs" />
<Compile Include="Consoles\Atari\7800\Atari7800.cs" />
<Compile Include="Consoles\Atari\7800\Atari7800.IDebuggable.cs">
<DependentUpon>Atari7800.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Atari\7800\Atari7800.IEmulator.cs">
<DependentUpon>Atari7800.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Atari\7800\Atari7800.IInputPollable.cs">
<DependentUpon>Atari7800.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Atari\7800\Atari7800.IMemoryDomains.cs">
<DependentUpon>Atari7800.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Atari\7800\Atari7800.IStatable.cs">
<DependentUpon>Atari7800.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Atari\7800\Atari7800Control.cs" />
<Compile Include="Consoles\Atari\7800\Atari7800.ISaveRam.cs">
<DependentUpon>Atari7800.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Atari\lynx\LibLynx.cs" />
<Compile Include="Consoles\Atari\lynx\Lynx.cs" />
<Compile Include="Consoles\Atari\lynx\Lynx.IInputPollable.cs">
@ -486,6 +463,9 @@
<Compile Include="Consoles\Nintendo\Gameboy\Gambatte.IEmulator.cs">
<DependentUpon>Gambatte.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\Gameboy\Gambatte.ILinkable.cs">
<DependentUpon>Gambatte.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\Gameboy\Gambatte.IMemoryDomains.cs">
<DependentUpon>Gambatte.cs</DependentUpon>
</Compile>
@ -535,6 +515,7 @@
<Compile Include="Consoles\Nintendo\Gameboy\GambatteLink.IVideoProvider.cs">
<DependentUpon>GambatteLink.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\Gameboy\GambattePrinter.cs" />
<Compile Include="Consoles\Nintendo\Gameboy\GamebatteLink.ISoundProvider.cs">
<DependentUpon>GambatteLink.cs</DependentUpon>
</Compile>
@ -771,6 +752,7 @@
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\Mapper049.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\Mapper052.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\Mapper074.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\Mapper114.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\Mapper115.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\Mapper121.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\Mapper123.cs" />
@ -899,6 +881,9 @@
<Compile Include="Consoles\Nintendo\NES\NES.IDriveLight.cs">
<DependentUpon>NES.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\NES\NES.ICodeDataLogger.cs">
<DependentUpon>NES.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\NES\NES.IInputPollable.cs">
<DependentUpon>NES.cs</DependentUpon>
</Compile>
@ -1090,6 +1075,8 @@
<Compile Include="Consoles\Sega\Saturn\LibSaturnus.cs" />
<Compile Include="Consoles\Sega\Saturn\Saturnus.cs" />
<Compile Include="Consoles\Sega\Saturn\SaturnusControllerDeck.cs" />
<Compile Include="Consoles\Sega\SMS\EEPROM.93c46.cs" />
<Compile Include="Consoles\Sega\SMS\MemoryMap.EEPROM.cs" />
<Compile Include="Consoles\Sega\SMS\SMS.cs" />
<Compile Include="Consoles\Sega\SMS\SMS.ICodeDataLogger.cs">
<DependentUpon>SMS.cs</DependentUpon>
@ -1205,12 +1192,14 @@
<Compile Include="CPUs\Z80-GB\Registers.cs" />
<Compile Include="CPUs\Z80-GB\Tables.cs" />
<Compile Include="CPUs\Z80-GB\Z80.cs" />
<Compile Include="CPUs\Z80\Disassembler.cs" />
<Compile Include="CPUs\Z80\Execute.cs" />
<Compile Include="CPUs\Z80\Interrupts.cs" />
<Compile Include="CPUs\Z80\Registers.cs" />
<Compile Include="CPUs\Z80\Tables.cs" />
<Compile Include="CPUs\Z80\Z80A.cs" />
<Compile Include="CPUs\Z80A\NewDisassembler.cs" />
<Compile Include="CPUs\Z80A\Execute.cs" />
<Compile Include="CPUs\Z80A\Interrupts.cs" />
<Compile Include="CPUs\Z80A\Registers.cs" />
<Compile Include="CPUs\Z80A\Operations.cs" />
<Compile Include="CPUs\Z80A\Tables_Direct.cs" />
<Compile Include="CPUs\Z80A\Tables_Indirect.cs" />
<Compile Include="CPUs\Z80A\Z80A.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>

File diff suppressed because it is too large Load Diff

View File

@ -1,55 +0,0 @@
using System;
namespace BizHawk.Emulation.Cores.Components.Z80
{
public partial class Z80A
{
private bool iff1;
public bool IFF1 { get { return iff1; } set { iff1 = value; } }
private bool iff2;
public bool IFF2 { get { return iff2; } set { iff2 = value; } }
private bool interrupt;
public bool Interrupt { get { return interrupt; } set { interrupt = value; } }
private bool nonMaskableInterrupt;
public bool NonMaskableInterrupt
{
get { return nonMaskableInterrupt; }
set { if (value && !nonMaskableInterrupt) NonMaskableInterruptPending = true; nonMaskableInterrupt = value; }
}
private bool nonMaskableInterruptPending;
public bool NonMaskableInterruptPending { get { return nonMaskableInterruptPending; } set { nonMaskableInterruptPending = value; } }
private int interruptMode;
public int InterruptMode
{
get { return interruptMode; }
set { if (value < 0 || value > 2) throw new ArgumentOutOfRangeException(); interruptMode = value; }
}
private bool halted;
public bool Halted { get { return halted; } set { halted = value; } }
public Action IRQCallback = delegate() { };
public Action NMICallback = delegate() { };
private void ResetInterrupts()
{
IFF1 = false;
IFF2 = false;
Interrupt = false;
NonMaskableInterrupt = false;
NonMaskableInterruptPending = false;
InterruptMode = 1;
Halted = false;
}
private void Halt()
{
Halted = true;
}
}
}

View File

@ -1,270 +0,0 @@
using System.Runtime.InteropServices;
using System;
namespace BizHawk.Emulation.Cores.Components.Z80
{
public partial class Z80A
{
[StructLayout(LayoutKind.Explicit)]
[Serializable()]
public struct RegisterPair
{
[FieldOffset(0)]
public ushort Word;
[FieldOffset(0)]
public byte Low;
[FieldOffset(1)]
public byte High;
public RegisterPair(ushort value)
{
Word = value;
Low = (byte)(Word);
High = (byte)(Word >> 8);
}
public static implicit operator ushort(RegisterPair rp)
{
return rp.Word;
}
public static implicit operator RegisterPair(ushort value)
{
return new RegisterPair(value);
}
}
private bool RegFlagC
{
get { return (RegAF.Low & 0x01) != 0; }
set { RegAF.Low = (byte)((RegAF.Low & ~0x01) | (value ? 0x01 : 0x00)); }
}
private bool RegFlagN
{
get { return (RegAF.Low & 0x02) != 0; }
set { RegAF.Low = (byte)((RegAF.Low & ~0x02) | (value ? 0x02 : 0x00)); }
}
private bool RegFlagP
{
get { return (RegAF.Low & 0x04) != 0; }
set { RegAF.Low = (byte)((RegAF.Low & ~0x04) | (value ? 0x04 : 0x00)); }
}
private bool RegFlag3
{
get { return (RegAF.Low & 0x08) != 0; }
set { RegAF.Low = (byte)((RegAF.Low & ~0x08) | (value ? 0x08 : 0x00)); }
}
private bool RegFlagH
{
get { return (RegAF.Low & 0x10) != 0; }
set { RegAF.Low = (byte)((RegAF.Low & ~0x10) | (value ? 0x10 : 0x00)); }
}
private bool RegFlag5
{
get { return (RegAF.Low & 0x20) != 0; }
set { RegAF.Low = (byte)((RegAF.Low & ~0x20) | (value ? 0x20 : 0x00)); }
}
private bool RegFlagZ
{
get { return (RegAF.Low & 0x40) != 0; }
set { RegAF.Low = (byte)((RegAF.Low & ~0x40) | (value ? 0x40 : 0x00)); }
}
private bool RegFlagS
{
get { return (RegAF.Low & 0x80) != 0; }
set { RegAF.Low = (byte)((RegAF.Low & ~0x80) | (value ? 0x80 : 0x00)); }
}
private RegisterPair RegAF;
private RegisterPair RegBC;
private RegisterPair RegDE;
private RegisterPair RegHL;
private RegisterPair RegWZ;
private RegisterPair RegAltAF; // Shadow for A and F
private RegisterPair RegAltBC; // Shadow for B and C
private RegisterPair RegAltDE; // Shadow for D and E
private RegisterPair RegAltHL; // Shadow for H and L
// NOTE: There is no AltWZ register (despite it being shown on various block diagrams)
private byte RegI; // I (interrupt vector)
private byte RegR; // R (memory refresh)
private RegisterPair RegIX; // IX (index register x)
private RegisterPair RegIY; // IY (index register y)
private RegisterPair RegSP; // SP (stack pointer)
private RegisterPair RegPC; // PC (program counter)
private void ResetRegisters()
{
// Clear main registers
RegAF = 0; RegBC = 0; RegDE = 0; RegHL = 0; RegWZ = 0;
// Clear alternate registers
RegAltAF = 0; RegAltBC = 0; RegAltDE = 0; RegAltHL = 0;
// Clear special purpose registers
RegI = 0; RegR = 0;
RegIX.Word = 0; RegIY.Word = 0;
RegSP.Word = 0; RegPC.Word = 0;
}
public byte RegisterA
{
get { return RegAF.High; }
set { RegAF.High = value; }
}
public byte RegisterF
{
get { return RegAF.Low; }
set { RegAF.Low = value; }
}
public ushort RegisterAF
{
get { return RegAF.Word; }
set { RegAF.Word = value; }
}
public byte RegisterB
{
get { return RegBC.High; }
set { RegBC.High = value; }
}
public byte RegisterC
{
get { return RegBC.Low; }
set { RegBC.Low = value; }
}
public ushort RegisterBC
{
get { return RegBC.Word; }
set { RegBC.Word = value; }
}
public byte RegisterD
{
get { return RegDE.High; }
set { RegDE.High = value; }
}
public byte RegisterE
{
get { return RegDE.Low; }
set { RegDE.Low = value; }
}
public ushort RegisterDE
{
get { return RegDE.Word; }
set { RegDE.Word = value; }
}
public byte RegisterH
{
get { return RegHL.High; }
set { RegHL.High = value; }
}
public byte RegisterL
{
get { return RegHL.Low; }
set { RegHL.Low = value; }
}
public ushort RegisterHL
{
get { return RegHL.Word; }
set { RegHL.Word = value; }
}
public byte RegisterW
{
get { return RegWZ.High; }
set { RegWZ.High = value; }
}
public byte RegisterZ
{
get { return RegWZ.Low; }
set { RegWZ.Low = value; }
}
public ushort RegisterWZ
{
get { return RegWZ.Word; }
set { RegWZ.Word = value; }
}
public ushort RegisterPC
{
get { return RegPC.Word; }
set { RegPC.Word = value; }
}
public ushort RegisterSP
{
get { return RegSP.Word; }
set { RegSP.Word = value; }
}
public ushort RegisterIX
{
get { return RegIX.Word; }
set { RegIX.Word = value; }
}
public ushort RegisterIY
{
get { return RegIY.Word; }
set { RegIY.Word = value; }
}
public byte RegisterI
{
get { return RegI; }
set { RegI = value; }
}
public byte RegisterR
{
get { return RegR; }
set { RegR = value; }
}
public ushort RegisterShadowAF
{
get { return RegAltAF.Word; }
set { RegAltAF.Word = value; }
}
public ushort RegisterShadowBC
{
get { return RegAltBC.Word; }
set { RegAltBC.Word = value; }
}
public ushort RegisterShadowDE
{
get { return RegAltDE.Word; }
set { RegAltDE.Word = value; }
}
public ushort RegisterShadowHL
{
get { return RegAltHL.Word; }
set { RegAltHL.Word = value; }
}
}
}

View File

@ -1,331 +0,0 @@
namespace BizHawk.Emulation.Cores.Components.Z80
{
public partial class Z80A
{
private void InitialiseTables()
{
InitTableInc();
InitTableDec();
InitTableParity();
InitTableALU();
InitTableRotShift();
InitTableHalfBorrow();
InitTableHalfCarry();
InitTableNeg();
InitTableDaa();
}
private byte[] TableInc;
private void InitTableInc()
{
TableInc = new byte[256];
for (int i = 0; i < 256; ++i)
TableInc[i] = FlagByte(false, false, i == 0x80, UndocumentedX(i), (i & 0xF) == 0x0, UndocumentedY(i), i == 0, i > 127);
}
private byte[] TableDec;
private void InitTableDec()
{
TableDec = new byte[256];
for (int i = 0; i < 256; ++i)
TableDec[i] = FlagByte(false, true, i == 0x7F, UndocumentedX(i), (i & 0xF) == 0xF, UndocumentedY(i), i == 0, i > 127);
}
private bool[] TableParity;
private void InitTableParity()
{
TableParity = new bool[256];
for (int i = 0; i < 256; ++i)
{
int Bits = 0;
for (int j = 0; j < 8; ++j)
{
Bits += (i >> j) & 1;
}
TableParity[i] = (Bits & 1) == 0;
}
}
private ushort[, , ,] TableALU;
private void InitTableALU()
{
TableALU = new ushort[8, 256, 256, 2]; // Class, OP1, OP2, Carry
for (int i = 0; i < 8; ++i)
{
for (int op1 = 0; op1 < 256; ++op1)
{
for (int op2 = 0; op2 < 256; ++op2)
{
for (int c = 0; c < 2; ++c)
{
int ac = (i == 1 || i == 3) ? c : 0;
bool S = false;
bool Z = false;
bool C = false;
bool H = false;
bool N = false;
bool P = false;
byte result_b = 0;
int result_si = 0;
int result_ui = 0;
// Fetch result
switch (i)
{
case 0:
case 1:
result_si = (sbyte)op1 + (sbyte)op2 + ac;
result_ui = op1 + op2 + ac;
break;
case 2:
case 3:
case 7:
result_si = (sbyte)op1 - (sbyte)op2 - ac;
result_ui = op1 - op2 - ac;
break;
case 4:
result_si = op1 & op2;
break;
case 5:
result_si = op1 ^ op2;
break;
case 6:
result_si = op1 | op2;
break;
}
result_b = (byte)result_si;
// Parity/Carry
switch (i)
{
case 0:
case 1:
case 2:
case 3:
case 7:
P = result_si < -128 || result_si > 127;
C = result_ui < 0 || result_ui > 255;
break;
case 4:
case 5:
case 6:
P = TableParity[result_b];
C = false;
break;
}
// Subtraction
N = i == 2 || i == 3 || i == 7;
// Half carry
switch (i)
{
case 0:
case 1:
H = ((op1 & 0xF) + (op2 & 0xF) + (ac & 0xF)) > 0xF;
break;
case 2:
case 3:
case 7:
H = ((op1 & 0xF) - (op2 & 0xF) - (ac & 0xF)) < 0x0;
break;
case 4:
H = true;
break;
case 5:
case 6:
H = false;
break;
}
// Undocumented
byte UndocumentedFlags = (byte)(result_b & 0x28);
if (i == 7) UndocumentedFlags = (byte)(op2 & 0x28);
S = result_b > 127;
Z = result_b == 0;
if (i == 7) result_b = (byte)op1;
TableALU[i, op1, op2, c] = (ushort)(
result_b * 256 +
((C ? 0x01 : 0) + (N ? 0x02 : 0) + (P ? 0x04 : 0) + (H ? 0x10 : 0) + (Z ? 0x40 : 0) + (S ? 0x80 : 0)) +
(UndocumentedFlags));
}
}
}
}
}
private bool[,] TableHalfBorrow;
private void InitTableHalfBorrow()
{
TableHalfBorrow = new bool[256, 256];
for (int i = 0; i < 256; i++)
{
for (int j = 0; j < 256; j++)
{
TableHalfBorrow[i, j] = ((i & 0xF) - (j & 0xF)) < 0;
}
}
}
private bool[,] TableHalfCarry;
private void InitTableHalfCarry()
{
TableHalfCarry = new bool[256, 256];
for (int i = 0; i < 256; i++)
{
for (int j = 0; j < 256; j++)
{
TableHalfCarry[i, j] = ((i & 0xF) + (j & 0xF)) > 0xF;
}
}
}
private ushort[, ,] TableRotShift;
private void InitTableRotShift()
{
TableRotShift = new ushort[2, 8, 65536]; // All, operation, AF
for (int all = 0; all < 2; all++)
{
for (int y = 0; y < 8; ++y)
{
for (int af = 0; af < 65536; af++)
{
byte Old = (byte)(af >> 8);
bool OldCarry = (af & 0x01) != 0;
ushort newAf = (ushort)(af & ~(0x13)); // Clear HALF-CARRY, SUBTRACT and CARRY flags
byte New = Old;
if ((y & 1) == 0)
{
if ((Old & 0x80) != 0) ++newAf;
New <<= 1;
if ((y & 0x04) == 0)
{
if (((y & 0x02) == 0) ? ((newAf & 0x01) != 0) : OldCarry) New |= 0x01;
}
else
{
if ((y & 0x02) != 0) New |= 0x01;
}
}
else
{
if ((Old & 0x01) != 0) ++newAf;
New >>= 1;
if ((y & 0x04) == 0)
{
if (((y & 0x02) == 0) ? ((newAf & 0x01) != 0) : OldCarry) New |= 0x80;
}
else
{
if ((y & 0x02) == 0) New |= (byte)(Old & 0x80);
}
}
newAf &= 0xFF;
newAf |= (ushort)(New * 256);
if (all == 1)
{
newAf &= unchecked((ushort)~0xC4); // Clear S, Z, P
if (New > 127) newAf |= 0x80;
if (New == 0) newAf |= 0x40;
if (TableParity[New]) newAf |= 0x04;
}
TableRotShift[all, y, af] = (ushort)((newAf & ~0x28) | ((newAf >> 8) & 0x28));
}
}
}
}
private ushort[] TableNeg;
private void InitTableNeg()
{
TableNeg = new ushort[65536];
for (int af = 0; af < 65536; af++)
{
ushort raf = 0;
byte b = (byte)(af >> 8);
byte a = (byte)-b;
raf |= (ushort)(a * 256);
raf |= FlagByte(b != 0x00, true, b == 0x80, UndocumentedX(a), TableHalfCarry[a, b], UndocumentedY(a), a == 0, a > 127);
TableNeg[af] = raf;
}
}
private ushort[] TableDaa;
private void InitTableDaa()
{
TableDaa = new ushort[65536];
for (int af = 0; af < 65536; ++af)
{
byte a = (byte)(af >> 8);
byte tmp = a;
if (IsN(af))
{
if (IsH(af) || ((a & 0x0F) > 0x09)) tmp -= 0x06;
if (IsC(af) || a > 0x99) tmp -= 0x60;
}
else
{
if (IsH(af) || ((a & 0x0F) > 0x09)) tmp += 0x06;
if (IsC(af) || a > 0x99) tmp += 0x60;
}
TableDaa[af] = (ushort)((tmp * 256) + FlagByte(IsC(af) || a > 0x99, IsN(af), TableParity[tmp], UndocumentedX(tmp), ((a ^ tmp) & 0x10) != 0, UndocumentedY(tmp), tmp == 0, tmp > 127));
}
}
private byte FlagByte(bool C, bool N, bool P, bool X, bool H, bool Y, bool Z, bool S)
{
return (byte)(
(C ? 0x01 : 0) +
(N ? 0x02 : 0) +
(P ? 0x04 : 0) +
(X ? 0x08 : 0) +
(H ? 0x10 : 0) +
(Y ? 0x20 : 0) +
(Z ? 0x40 : 0) +
(S ? 0x80 : 0)
);
}
private bool UndocumentedX(int value)
{
return (value & 0x08) != 0;
}
private bool UndocumentedY(int value)
{
return (value & 0x20) != 0;
}
private bool IsC(int value) { return (value & 0x01) != 0; }
private bool IsN(int value) { return (value & 0x02) != 0; }
private bool IsP(int value) { return (value & 0x04) != 0; }
private bool IsX(int value) { return (value & 0x08) != 0; }
private bool IsH(int value) { return (value & 0x10) != 0; }
private bool IsY(int value) { return (value & 0x20) != 0; }
private bool IsZ(int value) { return (value & 0x40) != 0; }
private bool IsS(int value) { return (value & 0x80) != 0; }
}
}

View File

@ -1,142 +0,0 @@
using System;
using System.Globalization;
using System.IO;
using BizHawk.Common;
using BizHawk.Emulation.Common;
// This Z80 emulator is a modified version of Ben Ryves 'Brazil' emulator.
// It is MIT licensed.
// for WZ register details, see: http://www.grimware.org/lib/exe/fetch.php/documentations/devices/z80/z80.memptr.eng.txt
namespace BizHawk.Emulation.Cores.Components.Z80
{
public sealed partial class Z80A
{
public Z80A()
{
InitialiseTables();
Reset();
}
public void Reset()
{
ResetRegisters();
ResetInterrupts();
PendingCycles = 0;
ExpectedExecutedCycles = 0;
TotalExecutedCycles = 0;
}
public void SoftReset()
{
ResetRegisters();
ResetInterrupts();
}
// Memory Access
public Func<ushort, bool, byte> FetchMemory;
public Func<ushort, byte> ReadMemory;
public Action<ushort, byte> WriteMemory;
public byte ReadMemoryWrapper(ushort addr)
{
if (MemoryCallbacks != null)
{
MemoryCallbacks.CallReads(addr, "System Bus");
}
return ReadMemory(addr);
}
public byte FetchFirstMemoryWrapper(ushort addr)
{
if (MemoryCallbacks != null)
{
MemoryCallbacks.CallReads(addr, "System Bus");
}
if (FetchMemory != null)
{
return FetchMemory(addr, true);
}
return ReadMemory(addr);
}
public byte FetchMemoryWrapper(ushort addr)
{
if (MemoryCallbacks != null)
{
MemoryCallbacks.CallReads(addr, "System Bus");
}
if (FetchMemory != null)
{
return FetchMemory(addr, false);
}
return ReadMemory(addr);
}
public void WriteMemoryWrapper(ushort addr, byte value)
{
if (MemoryCallbacks != null)
{
MemoryCallbacks.CallWrites(addr, "System Bus");
}
WriteMemory(addr, value);
}
public IMemoryCallbackSystem MemoryCallbacks { get; set; }
// Utility function, not used by core
public ushort ReadWord(ushort addr)
{
ushort value = ReadMemory(addr++);
value |= (ushort)(ReadMemory(addr) << 8);
return value;
}
// Hardware I/O Port Access
public Func<ushort, byte> ReadHardware;
public Action<ushort, byte> WriteHardware;
// State Save/Load
public void SyncState(Serializer ser)
{
ser.BeginSection("Z80");
ser.Sync("AF", ref RegAF.Word);
ser.Sync("BC", ref RegBC.Word);
ser.Sync("DE", ref RegDE.Word);
ser.Sync("HL", ref RegHL.Word);
ser.Sync("WZ", ref RegWZ.Word);
ser.Sync("ShadowAF", ref RegAltAF.Word);
ser.Sync("ShadowBC", ref RegAltBC.Word);
ser.Sync("ShadowDE", ref RegAltDE.Word);
ser.Sync("ShadowHL", ref RegAltHL.Word);
ser.Sync("I", ref RegI);
ser.Sync("R", ref RegR);
ser.Sync("IX", ref RegIX.Word);
ser.Sync("IY", ref RegIY.Word);
ser.Sync("SP", ref RegSP.Word);
ser.Sync("PC", ref RegPC.Word);
ser.Sync("IRQ", ref interrupt);
ser.Sync("NMI", ref nonMaskableInterrupt);
ser.Sync("NMIPending", ref nonMaskableInterruptPending);
ser.Sync("IM", ref interruptMode);
ser.Sync("IFF1", ref iff1);
ser.Sync("IFF2", ref iff2);
ser.Sync("Halted", ref halted);
ser.Sync("ExecutedCycles", ref totalExecutedCycles);
ser.Sync("PendingCycles", ref pendingCycles);
ser.Sync("EI_pending", ref EI_pending);
ser.EndSection();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,123 @@
using System;
namespace BizHawk.Emulation.Cores.Components.Z80A
{
public partial class Z80A
{
private bool iff1;
public bool IFF1 { get { return iff1; } set { iff1 = value; } }
private bool iff2;
public bool IFF2 { get { return iff2; } set { iff2 = value; } }
private bool nonMaskableInterrupt;
public bool NonMaskableInterrupt
{
get { return nonMaskableInterrupt; }
set { if (value && !nonMaskableInterrupt) NonMaskableInterruptPending = true; nonMaskableInterrupt = value; }
}
private bool nonMaskableInterruptPending;
public bool NonMaskableInterruptPending { get { return nonMaskableInterruptPending; } set { nonMaskableInterruptPending = value; } }
private int interruptMode;
public int InterruptMode
{
get { return interruptMode; }
set { if (value < 0 || value > 2) throw new ArgumentOutOfRangeException(); interruptMode = value; }
}
public Action IRQCallback = delegate () { };
public Action NMICallback = delegate () { };
private void NMI_()
{
cur_instr = new ushort[]
{IDLE,
DEC16, SPl, SPh,
WR, SPl, SPh, PCh,
IDLE,
DEC16, SPl, SPh,
WR, SPl, SPh, PCl,
IDLE,
ASGN, PCl, 0x66,
ASGN, PCh, 0,
IDLE,
OP };
}
// Mode 0 interrupts only take effect if a CALL or RST is on the data bus
// Otherwise operation just continues as normal
// For now assume a NOP is on the data bus, in which case no stack operations occur
//NOTE: TODO: When a CALL is present on the data bus, adjust WZ accordingly
private void INTERRUPT_0(ushort src)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, ALU, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
OP };
}
// Just jump to $0038
private void INTERRUPT_1()
{
cur_instr = new ushort[]
{DEC16, SPl, SPh,
IDLE,
WR, SPl, SPh, PCh,
IDLE,
DEC16, SPl, SPh,
IDLE,
WR, SPl, SPh, PCl,
IDLE,
ASGN, PCl, 0x38,
IDLE,
ASGN, PCh, 0,
IDLE,
OP };
}
// Interrupt mode 2 uses the I vector combined with a byte on the data bus
// Again for now we assume only a 0 on the data bus and jump to (0xI00)
private void INTERRUPT_2(ushort src)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
DEC16, SPl, SPh,
WR, SPl, SPh, PCh,
IDLE,
DEC16, SPl, SPh,
WR, SPl, SPh, PCl,
IDLE,
ASGN, PCl, 0,
TR, PCh, I,
IDLE,
IDLE,
RD, Z, PCl, PCh,
INC16, PCl, PCh,
IDLE,
RD, W, PCl, PCh,
IDLE,
TR16, PCl, PCh, Z, W,
OP };
}
private static ushort[] INT_vectors = new ushort[] {0x40, 0x48, 0x50, 0x58, 0x60};
private void ResetInterrupts()
{
IFF1 = false;
IFF2 = false;
NonMaskableInterrupt = false;
NonMaskableInterruptPending = false;
FlagI = false;
InterruptMode = 1;
}
}
}

View File

@ -1,114 +1,27 @@
//http://www.zophar.net/fileuploads/2/10819kouzv/z80undoc.html
//TODO: ex. (IX+00h) could be turned into (IX)
//usage:
//VgMuseum.Z80.Disassembler disasm = new Disassembler();
//ushort pc = RegPC.Word;
//string str = disasm.Disassemble(() => ReadMemory(pc++));
//Console.WriteLine(str);
//please note that however much youre tempted to, timings can't be put in a table here because they depend on how the instruction executes at runtime
using System;
using System;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Components.Z80
namespace BizHawk.Emulation.Cores.Components.Z80A
{
public class Disassembler : IDisassemblable
public sealed partial class Z80A : IDisassemblable
{
readonly static sbyte[,] opcodeSizes = new sbyte[7, 256];
public static void GenerateOpcodeSizes()
{
Disassembler disasm = new Disassembler();
for (int i = 0; i < 256; i++)
{
int pc = 0;
byte[] opcode = { (byte)i, 0, 0, 0 };
disasm.Disassemble(() => opcode[pc++]);
opcodeSizes[0, i] = (sbyte)pc;
}
opcodeSizes[0, 0xCB] = -1;
opcodeSizes[0, 0xED] = -2;
opcodeSizes[0, 0xDD] = -3;
opcodeSizes[0, 0xFD] = -4;
for (int i = 0; i < 256; i++)
{
int pc = 0;
byte[] opcode = { 0xCB, (byte)i, 0, 0, 0 };
disasm.Disassemble(() => opcode[pc++]);
opcodeSizes[1, i] = (sbyte)pc;
}
for (int i = 0; i < 256; i++)
{
int pc = 0;
byte[] opcode = { 0xED, (byte)i, 0, 0, 0 };
disasm.Disassemble(() => opcode[pc++]);
opcodeSizes[2, i] = (sbyte)pc;
}
for (int i = 0; i < 256; i++)
{
int pc = 0;
byte[] opcode = { 0xDD, (byte)i, 0, 0, 0 };
disasm.Disassemble(() => opcode[pc++]);
opcodeSizes[3, i] = (sbyte)pc;
}
opcodeSizes[3, 0xCB] = -5;
opcodeSizes[3, 0xED] = -2;
for (int i = 0; i < 256; i++)
{
int pc = 0;
byte[] opcode = { 0xFD, (byte)i, 0, 0, 0 };
disasm.Disassemble(() => opcode[pc++]);
opcodeSizes[4, i] = (sbyte)pc;
}
opcodeSizes[3, 0xCB] = -6;
opcodeSizes[3, 0xED] = -2;
for (int i = 0; i < 256; i++)
{
int pc = 0;
byte[] opcode = { 0xDD, 0xCB, (byte)i, 0, 0, 0 };
disasm.Disassemble(() => opcode[pc++]);
opcodeSizes[5, i] = (sbyte)pc;
}
for (int i = 0; i < 256; i++)
{
int pc = 0;
byte[] opcode = { 0xFD, 0xCB, (byte)i, 0, 0, 0 };
disasm.Disassemble(() => opcode[pc++]);
opcodeSizes[6, i] = (sbyte)pc;
}
}
static string Result(string format, Func<byte> read)
static string Result(string format, Func<ushort, byte> read, ref ushort addr)
{
//d immediately succeeds the opcode
//n immediate succeeds the opcode and the displacement (if present)
//nn immediately succeeds the opcode and the displacement (if present)
if (format.IndexOf("nn") != -1)
{
byte B = read();
byte C = read();
byte B = read(addr++);
byte C = read(addr++);
format = format.Replace("nn", string.Format("{0:X4}h", B + C * 256));
}
if (format.IndexOf("n") != -1)
{
byte B = read();
byte B = read(addr++);
format = format.Replace("n", string.Format("{0:X2}h", B));
}
@ -116,7 +29,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80
if (format.IndexOf("d") != -1)
{
byte B = read();
byte B = read(addr++);
bool neg = ((B & 0x80) != 0);
char sign = neg ? '-' : '+';
int val = neg ? 256 - B : B;
@ -480,46 +393,49 @@ namespace BizHawk.Emulation.Cores.Components.Z80
"NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", "NOP", //0x100
};
string DisassembleInternal(Func<byte> read)
public string Disassemble(ushort addr, Func<ushort, byte> read, out ushort size)
{
byte A = read();
ushort start_addr = addr;
ushort extra_inc = 0;
byte A = read(addr++);
string format;
switch (A)
{
case 0xCB:
A = read();
A = read(addr++);
format = mnemonicsCB[A];
break;
case 0xDD:
A = read();
A = read(addr++);
switch (A)
{
case 0xCB: format = mnemonicsDDCB[A]; break;
case 0xCB: format = mnemonicsDDCB[read((ushort)(addr + 1))]; extra_inc = 1; break;
case 0xED: format = mnemonicsED[A]; break;
default: format = mnemonicsDD[A]; break;
}
break;
case 0xED:
A = read();
A = read(addr++);
format = mnemonicsED[A];
break;
case 0xFD:
A = read();
A = read(addr++);
switch (A)
{
case 0xCB: format = mnemonicsFDCB[A]; break;
case 0xCB: format = mnemonicsFDCB[read((ushort)(addr + 1))]; extra_inc = 1; break;
case 0xED: format = mnemonicsED[A]; break;
default: format = mnemonicsFD[A]; break;
}
break;
default: format = mnemonics[A]; break;
}
return format;
}
string temp = Result(format, read, ref addr);
public string Disassemble(Func<byte> read)
{
return Result(DisassembleInternal(read), read);
addr += extra_inc;
size = (ushort)(addr - start_addr);
return temp;
}
#region IDisassemblable
@ -543,7 +459,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80
public string Disassemble(MemoryDomain m, uint addr, out int length)
{
int loc = (int)addr;
string ret = Disassemble(() => m.PeekByte(loc++));
ushort unused = 0;
string ret = Disassemble((ushort) addr, a => m.PeekByte(a), out unused);
length = loc - (int)addr;
return ret;
}

View File

@ -0,0 +1,742 @@
using BizHawk.Common.NumberExtensions;
using System;
namespace BizHawk.Emulation.Cores.Components.Z80A
{
public partial class Z80A
{
public void Read_Func(ushort dest, ushort src_l, ushort src_h)
{
Regs[dest] = ReadMemory((ushort)(Regs[src_l] | (Regs[src_h]) << 8));
}
public void I_Read_Func(ushort dest, ushort src_l, ushort src_h, ushort inc)
{
Regs[dest] = ReadMemory((ushort)((Regs[src_l] | (Regs[src_h] << 8)) + inc));
}
public void Write_Func(ushort dest_l, ushort dest_h, ushort src)
{
WriteMemory((ushort)(Regs[dest_l] | (Regs[dest_h] << 8)), (byte)Regs[src]);
}
public void I_Write_Func(ushort dest_l, ushort dest_h, ushort inc, ushort src)
{
WriteMemory((ushort)((Regs[dest_l] | (Regs[dest_h] << 8)) + inc), (byte)Regs[src]);
}
public void OUT_Func(ushort dest, ushort src)
{
WriteHardware(Regs[dest], (byte)(Regs[src]));
}
public void IN_Func(ushort dest, ushort src)
{
Regs[dest] = ReadHardware(Regs[src]);
}
public void TR_Func(ushort dest, ushort src)
{
Regs[dest] = Regs[src];
}
public void TR16_Func(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
{
Regs[dest_l] = Regs[src_l];
Regs[dest_h] = Regs[src_h];
}
public void ADD16_Func(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
{
int Reg16_d = Regs[dest_l] | (Regs[dest_h] << 8);
int Reg16_s = Regs[src_l] | (Regs[src_h] << 8);
int temp = Reg16_d + Reg16_s;
FlagC = temp.Bit(16);
FlagH = ((Reg16_d & 0xFFF) + (Reg16_s & 0xFFF)) > 0xFFF;
FlagN = false;
Flag3 = (temp & 0x0800) != 0;
Flag5 = (temp & 0x2000) != 0;
Regs[dest_l] = (ushort)(temp & 0xFF);
Regs[dest_h] = (ushort)((temp & 0xFF00) >> 8);
}
public void ADD8_Func(ushort dest, ushort src)
{
int Reg16_d = Regs[dest];
Reg16_d += Regs[src];
FlagC = Reg16_d.Bit(8);
FlagZ = (Reg16_d & 0xFF) == 0;
ushort ans = (ushort)(Reg16_d & 0xFF);
// redo for half carry flag
Reg16_d = Regs[dest] & 0xF;
Reg16_d += (Regs[src] & 0xF);
FlagH = Reg16_d.Bit(4);
FlagN = false;
Flag3 = (ans & 0x08) != 0;
Flag5 = (ans & 0x20) != 0;
FlagP = (Regs[dest].Bit(7) == Regs[src].Bit(7)) && (Regs[dest].Bit(7) != ans.Bit(7));
FlagS = ans > 127;
Regs[dest] = ans;
}
public void SUB8_Func(ushort dest, ushort src)
{
int Reg16_d = Regs[dest];
Reg16_d -= Regs[src];
FlagC = Reg16_d.Bit(8);
FlagZ = (Reg16_d & 0xFF) == 0;
ushort ans = (ushort)(Reg16_d & 0xFF);
// redo for half carry flag
Reg16_d = Regs[dest] & 0xF;
Reg16_d -= (Regs[src] & 0xF);
FlagH = Reg16_d.Bit(4);
FlagN = true;
Flag3 = (ans & 0x08) != 0;
Flag5 = (ans & 0x20) != 0;
FlagP = (Regs[dest].Bit(7) != Regs[src].Bit(7)) && (Regs[dest].Bit(7) != ans.Bit(7));
FlagS = ans > 127;
Regs[dest] = ans;
}
public void BIT_Func(ushort bit, ushort src)
{
FlagZ = !Regs[src].Bit(bit);
FlagP = FlagZ; // special case
FlagH = true;
FlagN = false;
FlagS = ((bit == 7) && Regs[src].Bit(bit));
Flag5 = Regs[src].Bit(5);
Flag3 = Regs[src].Bit(3);
}
// When doing I* + n bit tests, flags 3 and 5 come from I* + n
// This cooresponds to the high byte of WZ
// This is the same for the (HL) bit tests, except that WZ were not assigned to before the test occurs
public void I_BIT_Func(ushort bit, ushort src)
{
FlagZ = !Regs[src].Bit(bit);
FlagP = FlagZ; // special case
FlagH = true;
FlagN = false;
FlagS = ((bit == 7) && Regs[src].Bit(bit));
Flag5 = Regs[W].Bit(5);
Flag3 = Regs[W].Bit(3);
}
public void SET_Func(ushort bit, ushort src)
{
Regs[src] |= (ushort)(1 << bit);
}
public void RES_Func(ushort bit, ushort src)
{
Regs[src] &= (ushort)(0xFF - (1 << bit));
}
public void ASGN_Func(ushort src, ushort val)
{
Regs[src] = val;
}
public void SLL_Func(ushort src)
{
FlagC = Regs[src].Bit(7);
Regs[src] = (ushort)(((Regs[src] << 1) & 0xFF) | 0x1);
FlagS = Regs[src].Bit(7);
FlagZ = Regs[src] == 0;
FlagP = TableParity[Regs[src]];
Flag3 = (Regs[src] & 0x08) != 0;
Flag5 = (Regs[src] & 0x20) != 0;
FlagH = false;
FlagN = false;
}
public void SLA_Func(ushort src)
{
FlagC = Regs[src].Bit(7);
Regs[src] = (ushort)((Regs[src] << 1) & 0xFF);
FlagS = Regs[src].Bit(7);
FlagZ = Regs[src] == 0;
FlagP = TableParity[Regs[src]];
Flag3 = (Regs[src] & 0x08) != 0;
Flag5 = (Regs[src] & 0x20) != 0;
FlagH = false;
FlagN = false;
}
public void SRA_Func(ushort src)
{
FlagC = Regs[src].Bit(0);
ushort temp = (ushort)(Regs[src] & 0x80); // MSB doesn't change in this operation
Regs[src] = (ushort)((Regs[src] >> 1) | temp);
FlagS = Regs[src].Bit(7);
FlagZ = Regs[src] == 0;
FlagP = TableParity[Regs[src]];
Flag3 = (Regs[src] & 0x08) != 0;
Flag5 = (Regs[src] & 0x20) != 0;
FlagH = false;
FlagN = false;
}
public void SRL_Func(ushort src)
{
FlagC = Regs[src].Bit(0) ? true : false;
Regs[src] = (ushort)(Regs[src] >> 1);
FlagS = Regs[src].Bit(7);
FlagZ = Regs[src] == 0;
FlagP = TableParity[Regs[src]];
Flag3 = (Regs[src] & 0x08) != 0;
Flag5 = (Regs[src] & 0x20) != 0;
FlagH = false;
FlagN = false;
}
public void CPL_Func(ushort src)
{
Regs[src] = (ushort)((~Regs[src]) & 0xFF);
FlagH = true;
FlagN = true;
Flag3 = (Regs[src] & 0x08) != 0;
Flag5 = (Regs[src] & 0x20) != 0;
}
public void CCF_Func(ushort src)
{
FlagH = FlagC;
FlagC = !FlagC;
FlagN = false;
Flag3 = (Regs[src] & 0x08) != 0;
Flag5 = (Regs[src] & 0x20) != 0;
}
public void SCF_Func(ushort src)
{
FlagC = true;
FlagH = false;
FlagN = false;
Flag3 = (Regs[src] & 0x08) != 0;
Flag5 = (Regs[src] & 0x20) != 0;
}
public void AND8_Func(ushort dest, ushort src)
{
Regs[dest] = (ushort)(Regs[dest] & Regs[src]);
FlagZ = Regs[dest] == 0;
FlagC = false;
FlagH = true;
FlagN = false;
Flag3 = (Regs[dest] & 0x08) != 0;
Flag5 = (Regs[dest] & 0x20) != 0;
FlagS = Regs[dest] > 127;
FlagP = TableParity[Regs[dest]];
}
public void OR8_Func(ushort dest, ushort src)
{
Regs[dest] = (ushort)(Regs[dest] | Regs[src]);
FlagZ = Regs[dest] == 0;
FlagC = false;
FlagH = false;
FlagN = false;
Flag3 = (Regs[dest] & 0x08) != 0;
Flag5 = (Regs[dest] & 0x20) != 0;
FlagS = Regs[dest] > 127;
FlagP = TableParity[Regs[dest]];
}
public void XOR8_Func(ushort dest, ushort src)
{
Regs[dest] = (ushort)((Regs[dest] ^ Regs[src]));
FlagZ = Regs[dest] == 0;
FlagC = false;
FlagH = false;
FlagN = false;
Flag3 = (Regs[dest] & 0x08) != 0;
Flag5 = (Regs[dest] & 0x20) != 0;
FlagS = Regs[dest] > 127;
FlagP = TableParity[Regs[dest]];
}
public void CP8_Func(ushort dest, ushort src)
{
int Reg16_d = Regs[dest];
Reg16_d -= Regs[src];
FlagC = Reg16_d.Bit(8);
FlagZ = (Reg16_d & 0xFF) == 0;
ushort ans = (ushort)(Reg16_d & 0xFF);
// redo for half carry flag
Reg16_d = Regs[dest] & 0xF;
Reg16_d -= (Regs[src] & 0xF);
FlagH = Reg16_d.Bit(4);
FlagN = true;
Flag3 = (Regs[src] & 0x08) != 0;
Flag5 = (Regs[src] & 0x20) != 0;
FlagP = (Regs[dest].Bit(7) != Regs[src].Bit(7)) && (Regs[dest].Bit(7) != ans.Bit(7));
FlagS = ans > 127;
}
public void RRC_Func(ushort src)
{
bool imm = src == Aim;
if (imm) { src = A; }
FlagC = Regs[src].Bit(0);
Regs[src] = (ushort)((FlagC ? 0x80 : 0) | (Regs[src] >> 1));
if (!imm)
{
FlagS = Regs[src].Bit(7);
FlagZ = Regs[src] == 0;
FlagP = TableParity[Regs[src]];
}
Flag3 = (Regs[src] & 0x08) != 0;
Flag5 = (Regs[src] & 0x20) != 0;
FlagH = false;
FlagN = false;
}
public void RR_Func(ushort src)
{
bool imm = src == Aim;
if (imm) { src = A; }
ushort c = (ushort)(FlagC ? 0x80 : 0);
FlagC = Regs[src].Bit(0);
Regs[src] = (ushort)(c | (Regs[src] >> 1));
if (!imm)
{
FlagS = Regs[src].Bit(7);
FlagZ = Regs[src] == 0;
FlagP = TableParity[Regs[src]];
}
Flag3 = (Regs[src] & 0x08) != 0;
Flag5 = (Regs[src] & 0x20) != 0;
FlagH = false;
FlagN = false;
}
public void RLC_Func(ushort src)
{
bool imm = src == Aim;
if (imm) { src = A; }
ushort c = (ushort)(Regs[src].Bit(7) ? 1 : 0);
FlagC = Regs[src].Bit(7);
Regs[src] = (ushort)(((Regs[src] << 1) & 0xFF) | c);
if (!imm)
{
FlagS = Regs[src].Bit(7);
FlagZ = Regs[src] == 0;
FlagP = TableParity[Regs[src]];
}
Flag3 = (Regs[src] & 0x08) != 0;
Flag5 = (Regs[src] & 0x20) != 0;
FlagH = false;
FlagN = false;
}
public void RL_Func(ushort src)
{
bool imm = src == Aim;
if (imm) { src = A; }
ushort c = (ushort)(FlagC ? 1 : 0);
FlagC = Regs[src].Bit(7);
Regs[src] = (ushort)(((Regs[src] << 1) & 0xFF) | c);
if (!imm)
{
FlagS = Regs[src].Bit(7);
FlagZ = Regs[src] == 0;
FlagP = TableParity[Regs[src]];
}
Flag3 = (Regs[src] & 0x08) != 0;
Flag5 = (Regs[src] & 0x20) != 0;
FlagH = false;
FlagN = false;
}
public void INC8_Func(ushort src)
{
int Reg16_d = Regs[src];
Reg16_d += 1;
FlagZ = (Reg16_d & 0xFF) == 0;
ushort ans = (ushort)(Reg16_d & 0xFF);
// redo for half carry flag
Reg16_d = Regs[src] & 0xF;
Reg16_d += 1;
FlagH = Reg16_d.Bit(4);
FlagN = false;
Regs[src] = ans;
FlagS = Regs[src].Bit(7);
FlagP = Regs[src] == 0x80;
Flag3 = (Regs[src] & 0x08) != 0;
Flag5 = (Regs[src] & 0x20) != 0;
}
public void DEC8_Func(ushort src)
{
int Reg16_d = Regs[src];
Reg16_d -= 1;
FlagZ = (Reg16_d & 0xFF) == 0;
ushort ans = (ushort)(Reg16_d & 0xFF);
// redo for half carry flag
Reg16_d = Regs[src] & 0xF;
Reg16_d -= 1;
FlagH = Reg16_d.Bit(4);
FlagN = true;
Regs[src] = ans;
FlagS = Regs[src].Bit(7);
FlagP = Regs[src] == 0x7F;
Flag3 = (Regs[src] & 0x08) != 0;
Flag5 = (Regs[src] & 0x20) != 0;
}
public void INC16_Func(ushort src_l, ushort src_h)
{
int Reg16_d = Regs[src_l] | (Regs[src_h] << 8);
Reg16_d += 1;
Regs[src_l] = (ushort)(Reg16_d & 0xFF);
Regs[src_h] = (ushort)((Reg16_d & 0xFF00) >> 8);
}
public void DEC16_Func(ushort src_l, ushort src_h)
{
int Reg16_d = Regs[src_l] | (Regs[src_h] << 8);
Reg16_d -= 1;
Regs[src_l] = (ushort)(Reg16_d & 0xFF);
Regs[src_h] = (ushort)((Reg16_d & 0xFF00) >> 8);
}
public void ADC8_Func(ushort dest, ushort src)
{
int Reg16_d = Regs[dest];
int c = FlagC ? 1 : 0;
Reg16_d += (Regs[src] + c);
FlagC = Reg16_d.Bit(8);
FlagZ = (Reg16_d & 0xFF) == 0;
ushort ans = (ushort)(Reg16_d & 0xFF);
// redo for half carry flag
Reg16_d = Regs[dest] & 0xF;
Reg16_d += ((Regs[src] & 0xF) + c);
FlagH = Reg16_d.Bit(4);
FlagN = false;
Flag3 = (ans & 0x08) != 0;
Flag5 = (ans & 0x20) != 0;
FlagP = (Regs[dest].Bit(7) == Regs[src].Bit(7)) && (Regs[dest].Bit(7) != ans.Bit(7));
FlagS = ans > 127;
Regs[dest] = ans;
}
public void SBC8_Func(ushort dest, ushort src)
{
int Reg16_d = Regs[dest];
int c = FlagC ? 1 : 0;
Reg16_d -= (Regs[src] + c);
FlagC = Reg16_d.Bit(8);
FlagZ = (Reg16_d & 0xFF) == 0;
ushort ans = (ushort)(Reg16_d & 0xFF);
// redo for half carry flag
Reg16_d = Regs[dest] & 0xF;
Reg16_d -= ((Regs[src] & 0xF) + c);
FlagH = Reg16_d.Bit(4);
FlagN = true;
Flag3 = (ans & 0x08) != 0;
Flag5 = (ans & 0x20) != 0;
FlagP = (Regs[dest].Bit(7) != Regs[src].Bit(7)) && (Regs[dest].Bit(7) != ans.Bit(7));
FlagS = ans > 127;
Regs[dest] = ans;
}
public void DA_Func(ushort src)
{
byte a = (byte)Regs[src];
byte temp = a;
if (FlagN)
{
if (FlagH || ((a & 0x0F) > 0x09)) { temp -= 0x06; }
if (FlagC || a > 0x99) { temp -= 0x60; }
}
else
{
if (FlagH || ((a & 0x0F) > 0x09)) { temp += 0x06; }
if (FlagC || a > 0x99) { temp += 0x60; }
}
temp &= 0xFF;
FlagC = FlagC || a > 0x99;
FlagZ = temp == 0;
FlagH = ((a ^ temp) & 0x10) != 0;
FlagP = TableParity[temp];
FlagS = temp > 127;
Flag3 = (temp & 0x08) != 0;
Flag5 = (temp & 0x20) != 0;
Regs[src] = temp;
}
// used for signed operations
public void ADDS_Func(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
{
int Reg16_d = Regs[dest_l];
int Reg16_s = Regs[src_l];
Reg16_d += Reg16_s;
ushort temp = 0;
// since this is signed addition, calculate the high byte carry appropriately
// note that flags are unaffected by this operation
if (Reg16_s.Bit(7))
{
if (((Reg16_d & 0xFF) >= Regs[dest_l]))
{
temp = 0xFF;
}
else
{
temp = 0;
}
}
else
{
temp = (ushort)(Reg16_d.Bit(8) ? 1 : 0);
}
ushort ans_l = (ushort)(Reg16_d & 0xFF);
Regs[dest_l] = ans_l;
Regs[dest_h] += temp;
Regs[dest_h] &= 0xFF;
}
public void EXCH_16_Func(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
{
ushort temp = Regs[dest_l];
Regs[dest_l] = Regs[src_l];
Regs[src_l] = temp;
temp = Regs[dest_h];
Regs[dest_h] = Regs[src_h];
Regs[src_h] = temp;
}
public void SBC_16_Func(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
{
int Reg16_d = Regs[dest_l] | (Regs[dest_h] << 8);
int Reg16_s = Regs[src_l] | (Regs[src_h] << 8);
int c = FlagC ? 1 : 0;
int ans = Reg16_d - Reg16_s - c;
FlagN = true;
FlagC = ans.Bit(16);
FlagP = (Reg16_d.Bit(15) != Reg16_s.Bit(15)) && (Reg16_d.Bit(15) != ans.Bit(15));
FlagS = (ushort)(ans & 0xFFFF) > 32767;
FlagZ = (ans & 0xFFFF) == 0;
Flag3 = (ans & 0x0800) != 0;
Flag5 = (ans & 0x2000) != 0;
// redo for half carry flag
Reg16_d &= 0xFFF;
Reg16_d -= ((Reg16_s & 0xFFF) + c);
FlagH = Reg16_d.Bit(12);
Regs[dest_l] = (ushort)(ans & 0xFF);
Regs[dest_h] = (ushort)((ans >> 8) & 0xFF);
}
public void ADC_16_Func(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
{
int Reg16_d = Regs[dest_l] | (Regs[dest_h] << 8);
int Reg16_s = Regs[src_l] | (Regs[src_h] << 8);
int ans = Reg16_d + Reg16_s + (FlagC ? 1 : 0);
FlagH = ((Reg16_d & 0xFFF) + (Reg16_s & 0xFFF) + (FlagC ? 1 : 0)) > 0xFFF;
FlagN = false;
FlagC = ans.Bit(16);
FlagP = (Reg16_d.Bit(15) == Reg16_s.Bit(15)) && (Reg16_d.Bit(15) != ans.Bit(15));
FlagS = (ans & 0xFFFF) > 32767;
FlagZ = (ans & 0xFFFF) == 0;
Flag3 = (ans & 0x0800) != 0;
Flag5 = (ans & 0x2000) != 0;
Regs[dest_l] = (ushort)(ans & 0xFF);
Regs[dest_h] = (ushort)((ans >> 8) & 0xFF);
}
public void NEG_8_Func(ushort src)
{
int Reg16_d = 0;
Reg16_d -= Regs[src];
FlagC = Regs[src] != 0x0;
FlagZ = (Reg16_d & 0xFF) == 0;
FlagP = Regs[src] == 0x80;
FlagS = (Reg16_d & 0xFF) > 127;
ushort ans = (ushort)(Reg16_d & 0xFF);
// redo for half carry flag
Reg16_d = 0;
Reg16_d -= (Regs[src] & 0xF);
FlagH = Reg16_d.Bit(4);
Regs[src] = ans;
FlagN = true;
Flag3 = (ans & 0x08) != 0;
Flag5 = (ans & 0x20) != 0;
}
public void RRD_Func(ushort dest, ushort src)
{
ushort temp1 = Regs[src];
ushort temp2 = Regs[dest];
Regs[dest] = (ushort)(((temp1 & 0x0F) << 4) + ((temp2 & 0xF0) >> 4));
Regs[src] = (ushort)((temp1 & 0xF0) + (temp2 & 0x0F));
temp1 = Regs[src];
FlagS = temp1 > 127;
FlagZ = temp1 == 0;
FlagH = false;
FlagP = TableParity[temp1];
FlagN = false;
Flag3 = (temp1 & 0x08) != 0;
Flag5 = (temp1 & 0x20) != 0;
}
public void RLD_Func(ushort dest, ushort src)
{
ushort temp1 = Regs[src];
ushort temp2 = Regs[dest];
Regs[dest] = (ushort)((temp1 & 0x0F) + ((temp2 & 0x0F) << 4));
Regs[src] = (ushort)((temp1 & 0xF0) + ((temp2 & 0xF0) >> 4));
temp1 = Regs[src];
FlagS = temp1 > 127;
FlagZ = temp1 == 0;
FlagH = false;
FlagP = TableParity[temp1];
FlagN = false;
Flag3 = (temp1 & 0x08) != 0;
Flag5 = (temp1 & 0x20) != 0;
}
// sets flags for LD/R
public void SET_FL_LD_Func()
{
FlagP = (Regs[C] | (Regs[B] << 8)) != 0;
FlagH = false;
FlagN = false;
Flag5 = ((Regs[ALU] + Regs[A]) & 0x02) != 0;
Flag3 = ((Regs[ALU] + Regs[A]) & 0x08) != 0;
}
// set flags for CP/R
public void SET_FL_CP_Func()
{
int Reg8_d = Regs[A];
int Reg8_s = Regs[ALU];
// get half carry flag
byte temp = (byte)((Reg8_d & 0xF) - (Reg8_s & 0xF));
FlagH = temp.Bit(4);
temp = (byte)(Reg8_d - Reg8_s);
FlagN = true;
FlagZ = temp == 0;
FlagS = temp > 127;
FlagP = (Regs[C] | (Regs[B] << 8)) != 0;
temp = (byte)(Reg8_d - Reg8_s - (FlagH ? 1 : 0));
Flag5 = (temp & 0x02) != 0;
Flag3 = (temp & 0x08) != 0;
}
// set flags for LD A, I/R
public void SET_FL_IR_Func(ushort dest)
{
if (dest == A)
{
FlagN = false;
FlagH = false;
FlagZ = Regs[A] == 0;
FlagS = Regs[A] > 127;
FlagP = iff2;
Flag5 = (Regs[A] & 0x02) != 0;
Flag3 = (Regs[A] & 0x08) != 0;
}
}
}
}

View File

@ -0,0 +1,8 @@
TODO:
Mode 0 and 2 interrupts
Check T-cycle level memory access timing
Check R register
new tests for WZ Registers
Memory refresh - IR is pushed onto the address bus at instruction start, does anything need this?
Data Bus - For mode zero and 2 interrupts, need a system that uses it to test

View File

@ -0,0 +1,132 @@
using System.Runtime.InteropServices;
using System;
namespace BizHawk.Emulation.Cores.Components.Z80A
{
public partial class Z80A
{
// registers
// note these are not constants. When shadows are used, they will be changed accordingly
public ushort PCl = 0;
public ushort PCh = 1;
public ushort SPl = 2;
public ushort SPh = 3;
public ushort A = 4;
public ushort F = 5;
public ushort B = 6;
public ushort C = 7;
public ushort D = 8;
public ushort E = 9;
public ushort H = 10;
public ushort L = 11;
public ushort W = 12;
public ushort Z = 13;
public ushort Aim = 14; // use this indicator for RLCA etc., since the Z flag is reset on those
public ushort Ixl = 15;
public ushort Ixh = 16;
public ushort Iyl = 17;
public ushort Iyh = 18;
public ushort Int = 19;
public ushort R = 20;
public ushort I = 21;
public ushort ZERO = 22; // it is convenient to have a register that is always zero, to reuse instructions
public ushort ALU = 23; // This will be temporary arthimatic storage
// shadow registers
public ushort A_s = 24;
public ushort F_s = 25;
public ushort B_s = 26;
public ushort C_s = 27;
public ushort D_s = 28;
public ushort E_s = 29;
public ushort H_s = 30;
public ushort L_s = 31;
public ushort[] Regs = new ushort[36];
public bool FlagI;
public bool FlagC
{
get { return (Regs[5] & 0x01) != 0; }
set { Regs[5] = (ushort)((Regs[5] & ~0x01) | (value ? 0x01 : 0x00)); }
}
public bool FlagN
{
get { return (Regs[5] & 0x02) != 0; }
set { Regs[5] = (ushort)((Regs[5] & ~0x02) | (value ? 0x02 : 0x00)); }
}
public bool FlagP
{
get { return (Regs[5] & 0x04) != 0; }
set { Regs[5] = (ushort)((Regs[5] & ~0x04) | (value ? 0x04 : 0x00)); }
}
public bool Flag3
{
get { return (Regs[5] & 0x08) != 0; }
set { Regs[5] = (ushort)((Regs[5] & ~0x08) | (value ? 0x08 : 0x00)); }
}
public bool FlagH
{
get { return (Regs[5] & 0x10) != 0; }
set { Regs[5] = (ushort)((Regs[5] & ~0x10) | (value ? 0x10 : 0x00)); }
}
public bool Flag5
{
get { return (Regs[5] & 0x20) != 0; }
set { Regs[5] = (ushort)((Regs[5] & ~0x20) | (value ? 0x20 : 0x00)); }
}
public bool FlagZ
{
get { return (Regs[5] & 0x40) != 0; }
set { Regs[5] = (ushort)((Regs[5] & ~0x40) | (value ? 0x40 : 0x00)); }
}
public bool FlagS
{
get { return (Regs[5] & 0x80) != 0; }
set { Regs[5] = (ushort)((Regs[5] & ~0x80) | (value ? 0x80 : 0x00)); }
}
public ushort RegPC
{
get { return (ushort)(Regs[0] | (Regs[1] << 8)); }
set
{
Regs[0] = (ushort)(value & 0xFF);
Regs[1] = (ushort)((value >> 8) & 0xFF);
}
}
private void ResetRegisters()
{
for (int i=0; i < 36; i++)
{
Regs[i] = 0;
}
}
private bool[] TableParity;
private void InitTableParity()
{
TableParity = new bool[256];
for (int i = 0; i < 256; ++i)
{
int Bits = 0;
for (int j = 0; j < 8; ++j)
{
Bits += (i >> j) & 1;
}
TableParity[i] = (Bits & 1) == 0;
}
}
}
}

View File

@ -0,0 +1,572 @@
using System;
namespace BizHawk.Emulation.Cores.Components.Z80A
{
public partial class Z80A
{
// this contains the vectors of instrcution operations
// NOTE: This list is NOT confirmed accurate for each individual cycle
private void NOP_()
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
OP };
}
// NOTE: In a real Z80, this operation just flips a switch to choose between 2 registers
// but it's simpler to emulate just by exchanging the register with it's shadow
private void EXCH_()
{
cur_instr = new ushort[]
{EXCH,
IDLE,
IDLE,
OP };
}
private void EXX_()
{
cur_instr = new ushort[]
{EXX,
IDLE,
IDLE,
OP };
}
// this exchanges 2 16 bit registers
private void EXCH_16_(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{EXCH_16, dest_l, dest_h, src_l, src_h,
IDLE,
IDLE,
OP };
}
private void INC_16(ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
INC16, src_l, src_h,
IDLE,
OP };
}
private void DEC_16(ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
DEC16, src_l, src_h,
IDLE,
IDLE,
OP };
}
// this is done in two steps technically, but the flags don't work out using existing funcitons
// so let's use a different function since it's an internal operation anyway
private void ADD_16(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
TR16, Z, W, dest_l, dest_h,
INC16, Z, W,
IDLE,
IDLE,
ADD16, dest_l, dest_h, src_l, src_h,
IDLE,
IDLE,
OP };
}
private void REG_OP(ushort operation, ushort dest, ushort src)
{
cur_instr = new ushort[]
{operation, dest, src,
IDLE,
IDLE,
OP };
}
// Operations using the I and R registers take one T-cycle longer
private void REG_OP_IR(ushort operation, ushort dest, ushort src)
{
cur_instr = new ushort[]
{operation, dest, src,
IDLE,
IDLE,
SET_FL_IR, dest,
OP };
}
// note: do not use DEC here since no flags are affected by this operation
private void DJNZ_()
{
if ((Regs[B] - 1) != 0)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
ASGN, B, (ushort)((Regs[B] - 1) & 0xFF),
IDLE,
RD, Z, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
ASGN, W, 0,
IDLE,
ADDS, PCl, PCh, Z, W,
TR16, Z, W, PCl, PCh,
OP };
}
else
{
cur_instr = new ushort[]
{IDLE,
ASGN, B, (ushort)((Regs[B] - 1) & 0xFF),
IDLE,
RD, ALU, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
OP };
}
}
private void HALT_()
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
HALT };
}
private void JR_COND(bool cond)
{
if (cond)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, Z, PCl, PCh,
INC16, PCl, PCh,
IDLE,
IDLE,
ASGN, W, 0,
IDLE,
ADDS, PCl, PCh, Z, W,
TR16, Z, W, PCl, PCh,
IDLE,
OP };
}
else
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
RD, ALU, PCl, PCh,
IDLE,
INC16, PCl, PCh,
OP };
}
}
private void JP_COND(bool cond)
{
if (cond)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, Z, PCl, PCh,
INC16, PCl, PCh,
RD, W, PCl, PCh,
IDLE,
INC16, PCl, PCh,
TR16, PCl, PCh, Z, W,
IDLE,
OP };
}
else
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, Z, PCl, PCh,
INC16, PCl, PCh,
IDLE,
RD, W, PCl, PCh,
INC16, PCl, PCh,
IDLE,
IDLE,
OP };
}
}
private void RET_()
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, Z, SPl, SPh,
INC16, SPl, SPh,
IDLE,
IDLE,
RD, W, SPl, SPh,
INC16, SPl, SPh,
TR16, PCl, PCh, Z, W,
OP };
}
private void RETI_()
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, Z, SPl, SPh,
INC16, SPl, SPh,
IDLE,
IDLE,
RD, W, SPl, SPh,
INC16, SPl, SPh,
TR16, PCl, PCh, Z, W,
OP };
}
private void RETN_()
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, Z, SPl, SPh,
INC16, SPl, SPh,
IDLE,
RD, W, SPl, SPh,
INC16, SPl, SPh,
EI_RETN,
TR16, PCl, PCh, Z, W,
OP };
}
private void RET_COND(bool cond)
{
if (cond)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, Z, SPl, SPh,
INC16, SPl, SPh,
IDLE,
IDLE,
RD, W, SPl, SPh,
INC16, SPl, SPh,
IDLE,
TR16, PCl, PCh, Z, W,
OP };
}
else
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
IDLE,
OP };
}
}
private void CALL_COND(bool cond)
{
if (cond)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, Z, PCl, PCh,
INC16, PCl, PCh,
IDLE,
RD, W, PCl, PCh,
INC16, PCl, PCh,
IDLE,
DEC16, SPl, SPh,
IDLE,
WR, SPl, SPh, PCh,
DEC16, SPl, SPh,
WR, SPl, SPh, PCl,
IDLE,
TR, PCl, Z,
TR, PCh, W,
OP };
}
else
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, Z, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
RD, W, PCl, PCh,
IDLE,
INC16, PCl, PCh,
OP };
}
}
private void INT_OP(ushort operation, ushort src)
{
cur_instr = new ushort[]
{operation, src,
IDLE,
IDLE,
OP };
}
private void BIT_OP(ushort operation, ushort bit, ushort src)
{
cur_instr = new ushort[]
{operation, bit, src,
IDLE,
IDLE,
OP };
}
private void PUSH_(ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
DEC16, SPl, SPh,
IDLE,
WR, SPl, SPh, src_h,
IDLE,
DEC16, SPl, SPh,
IDLE,
WR, SPl, SPh, src_l,
IDLE,
OP };
}
private void POP_(ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
RD, src_l, SPl, SPh,
IDLE,
INC16, SPl, SPh,
IDLE,
RD, src_h, SPl, SPh,
IDLE,
INC16, SPl, SPh,
IDLE,
OP };
}
private void RST_(ushort n)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
DEC16, SPl, SPh,
WR, SPl, SPh, PCh,
DEC16, SPl, SPh,
WR, SPl, SPh, PCl,
IDLE,
ASGN, Z, n,
ASGN, W, 0,
TR16, PCl, PCh, Z, W,
OP };
}
private void PREFIX_(ushort src)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
PREFIX, src};
}
private void PREFETCH_(ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{TR16, Z, W, src_l, src_h,
ADDS, Z, W, ALU, ZERO,
IDLE,
PREFIX, IXYprefetch };
}
private void DI_()
{
cur_instr = new ushort[]
{DI,
IDLE,
IDLE,
OP };
}
private void EI_()
{
cur_instr = new ushort[]
{EI,
IDLE,
IDLE,
OP };
}
private void JP_16(ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{TR, PCl, src_l,
IDLE,
TR, PCh, src_h,
OP };
}
private void LD_SP_16(ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
TR, SPl, src_l,
TR, SPh, src_h,
IDLE,
OP };
}
private void OUT_()
{
cur_instr = new ushort[]
{IDLE,
RD, ALU, PCl, PCh,
IDLE,
INC16, PCl, PCh,
TR, W, A,
OUT, ALU, A,
TR, Z, ALU,
INC16, Z, ALU,
IDLE,
IDLE,
OP};
}
private void OUT_REG_(ushort dest, ushort src)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
OUT, dest, src,
IDLE,
TR16, Z, W, C, B,
INC16, Z, W,
IDLE,
OP};
}
private void IN_()
{
cur_instr = new ushort[]
{IDLE,
RD, ALU, PCl, PCh,
IDLE,
INC16, PCl, PCh,
TR, W, A,
IN, A, ALU,
TR, Z, ALU,
INC16, Z, W,
IDLE,
IDLE,
OP};
}
private void IN_REG_(ushort dest, ushort src)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IN, dest, src,
IDLE,
TR16, Z, W, C, B,
INC16, Z, W,
IDLE,
OP};
}
private void REG_OP_16_(ushort op, ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
TR16, Z, W, dest_l, dest_h,
INC16, Z, W,
IDLE,
IDLE,
op, dest_l, dest_h, src_l, src_h,
IDLE,
IDLE,
OP};
}
private void INT_MODE_(ushort src)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
INT_MODE, src,
OP };
}
private void RRD_()
{
cur_instr = new ushort[]
{IDLE,
IDLE,
TR16, Z, W, L, H,
IDLE,
RD, ALU, Z, W,
IDLE,
RRD, ALU, A,
IDLE,
WR, Z, W, ALU,
IDLE,
INC16, Z, W,
IDLE,
IDLE,
OP };
}
private void RLD_()
{
cur_instr = new ushort[]
{IDLE,
IDLE,
TR16, Z, W, L, H,
IDLE,
RD, ALU, Z, W,
IDLE,
RLD, ALU, A,
IDLE,
WR, Z, W, ALU,
IDLE,
INC16, Z, W,
IDLE,
IDLE,
OP };
}
}
}

View File

@ -0,0 +1,478 @@
namespace BizHawk.Emulation.Cores.Components.Z80A
{
public partial class Z80A
{
private void INT_OP_IND(ushort operation, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
RD, ALU, src_l, src_h,
IDLE,
operation, ALU,
IDLE,
WR, src_l, src_h, ALU,
IDLE,
IDLE,
OP };
}
private void BIT_OP_IND(ushort operation, ushort bit, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
RD, ALU, src_l, src_h,
IDLE,
operation, bit, ALU,
IDLE,
WR, src_l, src_h, ALU,
IDLE,
IDLE,
OP };
}
// Note that this operation uses I_BIT, same as indexed BIT.
// This is where the strange behaviour in Flag bits 3 and 5 come from.
// normally WZ contain I* + n when doing I_BIT ops, but here we use that code path
// even though WZ is not assigned to, letting it's value from other operations show through
private void BIT_TE_IND(ushort operation, ushort bit, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
RD, ALU, src_l, src_h,
IDLE,
I_BIT, bit, ALU,
IDLE,
OP };
}
private void REG_OP_IND_INC(ushort operation, ushort dest, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, ALU, src_l, src_h,
IDLE,
operation, dest, ALU,
INC16, src_l, src_h,
OP };
}
private void REG_OP_IND(ushort operation, ushort dest, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
TR16, Z, W, src_l, src_h,
RD, ALU, Z, W,
INC16, Z, W,
operation, dest, ALU,
OP };
}
private void LD_16_IND_nn(ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
RD, Z, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
RD, W, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
WR, Z, W, src_l,
IDLE,
INC16, Z, W,
IDLE,
WR, Z, W, src_h,
IDLE,
OP };
}
private void LD_IND_16_nn(ushort dest_l, ushort dest_h)
{
cur_instr = new ushort[]
{IDLE,
RD, Z, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
RD, W, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
RD, dest_l, Z, W,
IDLE,
INC16, Z, W,
IDLE,
RD, dest_h, Z, W,
IDLE,
OP };
}
private void LD_8_IND_nn(ushort src)
{
cur_instr = new ushort[]
{IDLE,
RD, Z, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
RD, W, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
WR, Z, W, src,
INC16, Z, W,
TR, W, A,
OP };
}
private void LD_IND_8_nn(ushort dest)
{
cur_instr = new ushort[]
{IDLE,
RD, Z, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
RD, W, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
RD, dest, Z, W,
IDLE,
INC16, Z, W,
OP };
}
private void LD_8_IND(ushort dest_l, ushort dest_h, ushort src)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
TR16, Z, W, dest_l, dest_h,
WR, Z, W, src,
INC16, Z, W,
TR, W, A,
OP };
}
private void LD_8_IND_IND(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
RD, ALU, src_l, src_h,
IDLE,
INC16, src_l, src_h,
IDLE,
WR, dest_l, dest_h, ALU,
IDLE,
OP };
}
private void LD_IND_8_INC(ushort dest, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
RD, dest, src_l, src_h,
IDLE,
INC16, src_l, src_h,
OP };
}
private void LD_IND_8_DEC(ushort dest, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
RD, dest, src_l, src_h,
IDLE,
DEC16, src_l, src_h,
IDLE,
OP };
}
private void LD_IND_16(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
RD, dest_l, src_l, src_h,
IDLE,
INC16, src_l, src_h,
RD, dest_h, src_l, src_h,
IDLE,
INC16, src_l, src_h,
OP };
}
private void INC_8_IND(ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
RD, ALU, src_l, src_h,
IDLE,
INC8, ALU,
IDLE,
WR, src_l, src_h, ALU,
IDLE,
IDLE,
OP };
}
private void DEC_8_IND(ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
RD, ALU, src_l, src_h,
IDLE,
DEC8, ALU,
IDLE,
WR, src_l, src_h, ALU,
IDLE,
IDLE,
OP };
}
// NOTE: WZ implied for the wollowing 3 functions
private void I_INT_OP(ushort operation, ushort dest)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, ALU, Z, W,
IDLE,
operation, ALU,
IDLE,
WR, Z, W, ALU,
IDLE,
TR, dest, ALU,
IDLE,
OP };
}
private void I_BIT_OP(ushort operation, ushort bit, ushort dest)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, ALU, Z, W,
IDLE,
operation, bit, ALU,
IDLE,
WR, Z, W, ALU,
IDLE,
TR, dest, ALU,
IDLE,
OP };
}
private void I_BIT_TE(ushort bit)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
RD, ALU, Z, W,
IDLE,
I_BIT, bit, ALU,
IDLE,
OP };
}
private void I_OP_n(ushort operation, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, ALU, PCl, PCh,
INC16, PCl, PCh,
IDLE,
TR16, Z, W, src_l, src_h,
IDLE,
ADDS, Z, W, ALU, ZERO,
IDLE,
RD, ALU, Z, W,
IDLE,
IDLE,
operation, ALU,
IDLE,
IDLE,
IDLE,
WR, Z, W, ALU,
IDLE,
OP };
}
private void I_OP_n_n(ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, ALU, PCl, PCh,
INC16, PCl, PCh,
IDLE,
TR16, Z, W, src_l, src_h,
IDLE,
ADDS, Z, W, ALU, ZERO,
IDLE,
RD, ALU, PCl, PCh,
INC16, PCl, PCh,
IDLE,
WR, Z, W, ALU,
IDLE,
OP };
}
private void I_REG_OP_IND_n(ushort operation, ushort dest, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, ALU, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
TR16, Z, W, src_l, src_h,
IDLE,
ADDS, Z, W, ALU, ZERO,
IDLE,
RD, ALU, Z, W,
IDLE,
operation, dest, ALU,
IDLE,
OP };
}
private void I_LD_8_IND_n(ushort dest_l, ushort dest_h, ushort src)
{
cur_instr = new ushort[]
{IDLE,
RD, ALU, PCl, PCh,
IDLE,
INC16, PCl, PCh,
IDLE,
TR16, Z, W, dest_l, dest_h,
IDLE,
ADDS, Z, W, ALU, ZERO,
IDLE,
WR, Z, W, src,
IDLE,
IDLE,
IDLE,
IDLE,
OP };
}
private void LD_OP_R(ushort operation, ushort repeat_instr)
{
cur_instr = new ushort[]
{RD, ALU, L, H,
IDLE,
WR, E, D, ALU,
IDLE,
operation, L, H,
IDLE,
operation, E, D,
IDLE,
DEC16, C, B,
SET_FL_LD,
IDLE,
OP_R, 0, operation, repeat_instr };
}
private void CP_OP_R(ushort operation, ushort repeat_instr)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, ALU, L, H,
operation, L, H,
IDLE,
IDLE,
DEC16, C, B,
SET_FL_CP,
IDLE,
operation, Z, W,
IDLE,
OP_R, 1, operation, repeat_instr };
}
private void IN_OP_R(ushort operation, ushort repeat_instr)
{
cur_instr = new ushort[]
{IN, ALU, C,
IDLE,
WR, L, H, ALU,
IDLE,
operation, L, H,
IDLE,
TR16, Z, W, C, B,
operation, Z, W,
IDLE,
DEC8, B,
IDLE,
OP_R, 2, operation, repeat_instr };
}
private void OUT_OP_R(ushort operation, ushort repeat_instr)
{
cur_instr = new ushort[]
{RD, ALU, L, H,
IDLE,
OUT, C, ALU,
IDLE,
IDLE,
operation, L, H,
DEC8, B,
IDLE,
TR16, Z, W, C, B,
operation, Z, W,
IDLE,
OP_R, 3, operation, repeat_instr };
}
// this is an indirect change of a a 16 bit register with memory
private void EXCH_16_IND_(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h)
{
cur_instr = new ushort[]
{IDLE,
IDLE,
RD, Z, dest_l, dest_h,
IDLE,
IDLE,
I_RD, W, dest_l, dest_h, 1,
IDLE,
IDLE,
WR, dest_l, dest_h, src_l,
IDLE,
IDLE,
I_WR, dest_l, dest_h, 1, src_h,
IDLE,
IDLE,
TR16, src_l, src_h, Z, W,
IDLE,
IDLE,
IDLE,
OP };
}
}
}

View File

@ -0,0 +1,687 @@
using System;
using System.Globalization;
using System.IO;
using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Common.NumberExtensions;
// Z80A CPU
namespace BizHawk.Emulation.Cores.Components.Z80A
{
public sealed partial class Z80A
{
// operations that can take place in an instruction
public const ushort IDLE = 0;
public const ushort OP = 1;
public const ushort OP_R = 2; // used for repeating operations
public const ushort HALT = 3;
public const ushort RD = 4;
public const ushort WR = 5;
public const ushort I_RD = 6;
public const ushort I_WR = 7;
public const ushort TR = 8;
public const ushort TR16 = 9;
public const ushort ADD16 = 10;
public const ushort ADD8 = 11;
public const ushort SUB8 = 12;
public const ushort ADC8 = 13;
public const ushort SBC8 = 14;
public const ushort SBC16 = 15;
public const ushort ADC16 = 16;
public const ushort INC16 = 17;
public const ushort INC8 = 18;
public const ushort DEC16 = 19;
public const ushort DEC8 = 20;
public const ushort RLC = 21;
public const ushort RL = 22;
public const ushort RRC = 23;
public const ushort RR = 24;
public const ushort CPL = 25;
public const ushort DA = 26;
public const ushort SCF = 27;
public const ushort CCF = 28;
public const ushort AND8 = 29;
public const ushort XOR8 = 30;
public const ushort OR8 = 31;
public const ushort CP8 = 32;
public const ushort SLA = 33;
public const ushort SRA = 34;
public const ushort SRL = 35;
public const ushort SLL = 36;
public const ushort BIT = 37;
public const ushort RES = 38;
public const ushort SET = 39;
public const ushort EI = 40;
public const ushort DI = 41;
public const ushort EXCH = 42;
public const ushort EXX = 43;
public const ushort EXCH_16 = 44;
public const ushort PREFIX = 45;
public const ushort PREFETCH = 46;
public const ushort ASGN = 47;
public const ushort ADDS = 48; // signed 16 bit operation used in 2 instructions
public const ushort INT_MODE = 49;
public const ushort EI_RETN = 50;
public const ushort EI_RETI = 51; // reti has no delay in interrupt enable
public const ushort OUT = 52;
public const ushort IN = 53;
public const ushort NEG = 54;
public const ushort RRD = 55;
public const ushort RLD = 56;
public const ushort SET_FL_LD = 57;
public const ushort SET_FL_CP = 58;
public const ushort SET_FL_IR = 59;
public const ushort I_BIT = 60;
public const ushort HL_BIT = 61;
public byte temp_R;
public Z80A()
{
Reset();
InitTableParity();
}
public void Reset()
{
ResetRegisters();
ResetInterrupts();
TotalExecutedCycles = 0;
cur_instr = new ushort[] { OP };
NO_prefix = true;
}
public IMemoryCallbackSystem MemoryCallbacks { get; set; }
// Memory Access
public Func<ushort, byte> FetchMemory;
public Func<ushort, byte> ReadMemory;
public Action<ushort, byte> WriteMemory;
public Func<ushort, byte> PeekMemory;
public Func<ushort, byte> DummyReadMemory;
// Hardware I/O Port Access
public Func<ushort, byte> ReadHardware;
public Action<ushort, byte> WriteHardware;
//this only calls when the first byte of an instruction is fetched.
public Action<ushort> OnExecFetch;
public void UnregisterMemoryMapper()
{
ReadMemory = null;
WriteMemory = null;
PeekMemory = null;
DummyReadMemory = null;
ReadHardware = null;
WriteHardware = null;
}
public void SetCallbacks
(
Func<ushort, byte> ReadMemory,
Func<ushort, byte> DummyReadMemory,
Func<ushort, byte> PeekMemory,
Action<ushort, byte> WriteMemory,
Func<ushort, byte> ReadHardware,
Action<ushort, byte> WriteHardware
)
{
this.ReadMemory = ReadMemory;
this.DummyReadMemory = DummyReadMemory;
this.PeekMemory = PeekMemory;
this.WriteMemory = WriteMemory;
this.ReadHardware = ReadHardware;
this.WriteHardware = WriteHardware;
}
// Execute instructions
public void ExecuteOne()
{
if (Regs[A] > 255) { Console.WriteLine(RegPC); }
switch (cur_instr[instr_pntr++])
{
case IDLE:
// do nothing
break;
case OP:
// Read the opcode of the next instruction
if (EI_pending > 0)
{
EI_pending--;
if (EI_pending == 0) { IFF1 = IFF2 = true; }
}
// Process interrupt requests.
if (nonMaskableInterruptPending)
{
nonMaskableInterruptPending = false;
if (TraceCallback != null)
{
TraceCallback(new TraceInfo{Disassembly = "====NMI====", RegisterInfo = ""});
}
iff2 = iff1;
iff1 = false;
NMI_();
NMICallback();
}
else if (iff1 && FlagI)
{
iff1 = iff2 = false;
EI_pending = 0;
if (TraceCallback != null)
{
TraceCallback(new TraceInfo{Disassembly = "====IRQ====", RegisterInfo = ""});
}
switch (interruptMode)
{
case 0:
// Requires something to be pushed onto the data bus
// we'll assume it's a zero for now
INTERRUPT_0(0);
break;
case 1:
INTERRUPT_1();
break;
case 2:
// Low byte of interrupt vector comes from data bus
// We'll assume it's zero for now
INTERRUPT_2(0);
break;
}
IRQCallback();
}
else
{
if (OnExecFetch != null) OnExecFetch(RegPC);
if (TraceCallback != null) TraceCallback(State());
FetchInstruction(FetchMemory(RegPC++));
}
instr_pntr = 0;
temp_R = (byte)(Regs[R] & 0x7F);
temp_R++;
temp_R &= 0x7F;
Regs[R] = (byte)((Regs[R] & 0x80) | temp_R);
break;
case OP_R:
// determine if we repeat based on what operation we are doing
// single execution versions also come here, but never repeat
ushort temp1 = cur_instr[instr_pntr++];
ushort temp2 = cur_instr[instr_pntr++];
ushort temp3 = cur_instr[instr_pntr++];
bool repeat = false;
int Reg16_d = Regs[C] | (Regs[B] << 8);
switch (temp1)
{
case 0:
repeat = Reg16_d != 0;
break;
case 1:
repeat = (Reg16_d != 0) && !FlagZ;
break;
case 2:
repeat = Regs[B] != 0;
break;
case 3:
repeat = Regs[B] != 0;
break;
}
// if we repeat, we do a 5 cycle refresh which decrements PC by 2
// if we don't repeat, continue on as a normal opcode fetch
if (repeat && temp3 > 0)
{
cur_instr = new ushort[]
{IDLE,
DEC16, PCl, PCh,
IDLE,
DEC16, PCl, PCh,
OP };
// adjust WZ register accordingly
switch (temp1)
{
case 0:
// TEST: PC before or after the instruction?
Regs[Z] = Regs[PCl];
Regs[W] = Regs[PCh];
INC16_Func(Z, W);
break;
case 1:
// TEST: PC before or after the instruction?
Regs[Z] = Regs[PCl];
Regs[W] = Regs[PCh];
INC16_Func(Z, W);
break;
case 2:
// Nothing
break;
case 3:
// Nothing
break;
}
}
else
{
// Interrupts can occur at this point, so process them accordingly
// Read the opcode of the next instruction
if (EI_pending > 0)
{
EI_pending--;
if (EI_pending == 0) { IFF1 = IFF2 = true; }
}
// Process interrupt requests.
if (nonMaskableInterruptPending)
{
nonMaskableInterruptPending = false;
if (TraceCallback != null)
{
TraceCallback(new TraceInfo{Disassembly = "====NMI====", RegisterInfo = ""});
}
iff2 = iff1;
iff1 = false;
NMI_();
NMICallback();
}
else if (iff1 && FlagI)
{
iff1 = iff2 = false;
EI_pending = 0;
if (TraceCallback != null)
{
TraceCallback(new TraceInfo{Disassembly = "====IRQ====", RegisterInfo = ""});
}
switch (interruptMode)
{
case 0:
// Requires something to be pushed onto the data bus
// we'll assume it's a zero for now
INTERRUPT_0(0);
break;
case 1:
INTERRUPT_1();
break;
case 2:
// Low byte of interrupt vector comes from data bus
// We'll assume it's zero for now
INTERRUPT_2(0);
break;
}
IRQCallback();
}
else
{
if (OnExecFetch != null) OnExecFetch(RegPC);
if (TraceCallback != null) TraceCallback(State());
FetchInstruction(FetchMemory(RegPC++));
}
temp_R = (byte)(Regs[R] & 0x7F);
temp_R++;
temp_R &= 0x7F;
Regs[R] = (byte)((Regs[R] & 0x80) | temp_R);
}
instr_pntr = 0;
break;
case HALT:
halted = true;
if (EI_pending > 0)
{
EI_pending--;
if (EI_pending == 0) { IFF1 = IFF2 = true; }
}
// Process interrupt requests.
if (nonMaskableInterruptPending)
{
nonMaskableInterruptPending = false;
if (TraceCallback != null)
{
TraceCallback(new TraceInfo{Disassembly = "====NMI====", RegisterInfo = ""});
}
iff2 = iff1;
iff1 = false;
NMI_();
NMICallback();
halted = false;
}
else if (iff1 && FlagI)
{
iff1 = iff2 = false;
EI_pending = 0;
if (TraceCallback != null)
{
TraceCallback(new TraceInfo{Disassembly = "====IRQ====", RegisterInfo = ""});
}
switch (interruptMode)
{
case 0:
// Requires something to be pushed onto the data bus
// we'll assume it's a zero for now
INTERRUPT_0(0);
break;
case 1:
INTERRUPT_1();
break;
case 2:
// Low byte of interrupt vector comes from data bus
// We'll assume it's zero for now
INTERRUPT_2(0);
break;
}
IRQCallback();
halted = false;
}
else
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
HALT };
}
temp_R = (byte)(Regs[R] & 0x7F);
temp_R++;
temp_R &= 0x7F;
Regs[R] = (byte)((Regs[R] & 0x80) | temp_R);
instr_pntr = 0;
break;
case RD:
Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case WR:
Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case I_RD:
I_Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case I_WR:
I_Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case TR:
TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case TR16:
TR16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case ADD16:
ADD16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case ADD8:
ADD8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case SUB8:
SUB8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case ADC8:
ADC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case ADC16:
ADC_16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case SBC8:
SBC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case SBC16:
SBC_16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case INC16:
INC16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case INC8:
INC8_Func(cur_instr[instr_pntr++]);
break;
case DEC16:
DEC16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case DEC8:
DEC8_Func(cur_instr[instr_pntr++]);
break;
case RLC:
RLC_Func(cur_instr[instr_pntr++]);
break;
case RL:
RL_Func(cur_instr[instr_pntr++]);
break;
case RRC:
RRC_Func(cur_instr[instr_pntr++]);
break;
case RR:
RR_Func(cur_instr[instr_pntr++]);
break;
case CPL:
CPL_Func(cur_instr[instr_pntr++]);
break;
case DA:
DA_Func(cur_instr[instr_pntr++]);
break;
case SCF:
SCF_Func(cur_instr[instr_pntr++]);
break;
case CCF:
CCF_Func(cur_instr[instr_pntr++]);
break;
case AND8:
AND8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case XOR8:
XOR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case OR8:
OR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case CP8:
CP8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case SLA:
SLA_Func(cur_instr[instr_pntr++]);
break;
case SRA:
SRA_Func(cur_instr[instr_pntr++]);
break;
case SRL:
SRL_Func(cur_instr[instr_pntr++]);
break;
case SLL:
SLL_Func(cur_instr[instr_pntr++]);
break;
case BIT:
BIT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case I_BIT:
I_BIT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case RES:
RES_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case SET:
SET_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case EI:
EI_pending = 2;
break;
case DI:
IFF1 = IFF2 = false;
break;
case EXCH:
EXCH_16_Func(F_s, A_s, F, A);
break;
case EXX:
EXCH_16_Func(C_s, B_s, C, B);
EXCH_16_Func(E_s, D_s, E, D);
EXCH_16_Func(L_s, H_s, L, H);
break;
case EXCH_16:
EXCH_16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case PREFIX:
ushort prefix_src = cur_instr[instr_pntr++];
NO_prefix = false;
if (prefix_src == CBpre) { CB_prefix = true; }
if (prefix_src == EXTDpre) { EXTD_prefix = true; }
if (prefix_src == IXpre) { IX_prefix = true; }
if (prefix_src == IYpre) { IY_prefix = true; }
if (prefix_src == IXCBpre) { IXCB_prefix = true; IXCB_prefetch = true; }
if (prefix_src == IYCBpre) { IYCB_prefix = true; IYCB_prefetch = true; }
FetchInstruction(FetchMemory(RegPC++));
instr_pntr = 0;
// only the first prefix in a double prefix increases R, although I don't know how / why
if (prefix_src < 4)
{
temp_R = (byte)(Regs[R] & 0x7F);
temp_R++;
temp_R &= 0x7F;
Regs[R] = (byte)((Regs[R] & 0x80) | temp_R);
}
break;
case ASGN:
ASGN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case ADDS:
ADDS_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case EI_RETI:
// NOTE: This is needed for systems using multiple interrupt sources, it triggers the next interrupt
// Not currently implemented here
iff1 = iff2;
break;
case EI_RETN:
iff1 = iff2;
break;
case OUT:
OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case IN:
IN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case NEG:
NEG_8_Func(cur_instr[instr_pntr++]);
break;
case INT_MODE:
interruptMode = cur_instr[instr_pntr++];
break;
case RRD:
RRD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case RLD:
RLD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
break;
case SET_FL_LD:
SET_FL_LD_Func();
break;
case SET_FL_CP:
SET_FL_CP_Func();
break;
case SET_FL_IR:
SET_FL_IR_Func(cur_instr[instr_pntr++]);
break;
}
totalExecutedCycles++;
}
// tracer stuff
public Action<TraceInfo> TraceCallback;
public string TraceHeader
{
get { return "Z80A: PC, machine code, mnemonic, operands, registers (AF, BC, DE, HL, IX, IY, SP, Cy), flags (CNP3H5ZS)"; }
}
public TraceInfo State(bool disassemble = true)
{
ushort bytes_read = 0;
string disasm = disassemble ? Disassemble(RegPC, ReadMemory, out bytes_read) : "---";
string byte_code = null;
for (ushort i = 0; i < bytes_read; i++)
{
byte_code += ReadMemory((ushort)(RegPC + i)).ToHexString(2);
if (i < (bytes_read - 1))
{
byte_code += " ";
}
}
return new TraceInfo
{
Disassembly = string.Format(
"{0:X4}: {1} {2}",
RegPC,
byte_code.PadRight(12),
disasm.PadRight(26)),
RegisterInfo = string.Format(
"AF:{0:X4} BC:{1:X4} DE:{2:X4} HL:{3:X4} IX:{4:X4} IY:{5:X4} SP:{6:X4} Cy:{7} {8}{9}{10}{11}{12}{13}{14}{15}{16}",
(Regs[A] << 8) + Regs[F],
(Regs[B] << 8) + Regs[C],
(Regs[D] << 8) + Regs[E],
(Regs[H] << 8) + Regs[L],
(Regs[Ixh] << 8) + Regs[Ixl],
(Regs[Iyh] << 8) + Regs[Iyl],
Regs[SPl] | (Regs[SPh] << 8),
TotalExecutedCycles,
FlagC ? "C" : "c",
FlagN ? "N" : "n",
FlagP ? "P" : "p",
Flag3 ? "3" : "-",
FlagH ? "H" : "h",
Flag5 ? "5" : "-",
FlagZ ? "Z" : "z",
FlagS ? "S" : "s",
FlagI ? "E" : "e")
};
}
// State Save/Load
public void SyncState(Serializer ser)
{
ser.BeginSection("Z80A");
ser.Sync("Regs", ref Regs, false);
ser.Sync("NMI", ref nonMaskableInterrupt);
ser.Sync("NMIPending", ref nonMaskableInterruptPending);
ser.Sync("IM", ref interruptMode);
ser.Sync("IFF1", ref iff1);
ser.Sync("IFF2", ref iff2);
ser.Sync("Halted", ref halted);
ser.Sync("ExecutedCycles", ref totalExecutedCycles);
ser.Sync("EI_pending", ref EI_pending);
ser.Sync("instruction_pointer", ref instr_pntr);
ser.Sync("current instruction", ref cur_instr, false);
ser.Sync("opcode", ref opcode);
ser.Sync("FlagI", ref FlagI);
ser.Sync("NO Preifx", ref NO_prefix);
ser.Sync("CB Preifx", ref CB_prefix);
ser.Sync("IX_prefix", ref IX_prefix);
ser.Sync("IY_prefix", ref IY_prefix);
ser.Sync("IXCB_prefix", ref IXCB_prefix);
ser.Sync("IYCB_prefix", ref IYCB_prefix);
ser.Sync("EXTD_prefix", ref EXTD_prefix);
ser.Sync("IXCB_prefetch", ref IXCB_prefetch);
ser.Sync("IYCB_prefetch", ref IYCB_prefetch);
ser.Sync("PF", ref PF);
ser.EndSection();
}
}
}

View File

@ -12,36 +12,36 @@ namespace BizHawk.Emulation.Cores.Calculators
{
return new Dictionary<string, RegisterValue>
{
["A"] = _cpu.RegisterA,
["AF"] = _cpu.RegisterAF,
["B"] = _cpu.RegisterB,
["BC"] = _cpu.RegisterBC,
["C"] = _cpu.RegisterC,
["D"] = _cpu.RegisterD,
["DE"] = _cpu.RegisterDE,
["E"] = _cpu.RegisterE,
["F"] = _cpu.RegisterF,
["H"] = _cpu.RegisterH,
["HL"] = _cpu.RegisterHL,
["I"] = _cpu.RegisterI,
["IX"] = _cpu.RegisterIX,
["IY"] = _cpu.RegisterIY,
["L"] = _cpu.RegisterL,
["PC"] = _cpu.RegisterPC,
["R"] = _cpu.RegisterR,
["Shadow AF"] = _cpu.RegisterShadowAF,
["Shadow BC"] = _cpu.RegisterShadowBC,
["Shadow DE"] = _cpu.RegisterShadowDE,
["Shadow HL"] = _cpu.RegisterShadowHL,
["SP"] = _cpu.RegisterSP,
["Flag C"] = _cpu.RegisterF.Bit(0),
["Flag N"] = _cpu.RegisterF.Bit(1),
["Flag P/V"] = _cpu.RegisterF.Bit(2),
["Flag 3rd"] = _cpu.RegisterF.Bit(3),
["Flag H"] = _cpu.RegisterF.Bit(4),
["Flag 5th"] = _cpu.RegisterF.Bit(5),
["Flag Z"] = _cpu.RegisterF.Bit(6),
["Flag S"] = _cpu.RegisterF.Bit(7)
["A"] = _cpu.Regs[_cpu.A],
["AF"] = _cpu.Regs[_cpu.F] + (_cpu.Regs[_cpu.A] << 8),
["B"] = _cpu.Regs[_cpu.B],
["BC"] = _cpu.Regs[_cpu.C] + (_cpu.Regs[_cpu.B] << 8),
["C"] = _cpu.Regs[_cpu.C],
["D"] = _cpu.Regs[_cpu.D],
["DE"] = _cpu.Regs[_cpu.E] + (_cpu.Regs[_cpu.D] << 8),
["E"] = _cpu.Regs[_cpu.E],
["F"] = _cpu.Regs[_cpu.F],
["H"] = _cpu.Regs[_cpu.H],
["HL"] = _cpu.Regs[_cpu.L] + (_cpu.Regs[_cpu.H] << 8),
["I"] = _cpu.Regs[_cpu.I],
["IX"] = _cpu.Regs[_cpu.Ixl] + (_cpu.Regs[_cpu.Ixh] << 8),
["IY"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8),
["L"] = _cpu.Regs[_cpu.L],
["PC"] = _cpu.Regs[_cpu.PCl] + (_cpu.Regs[_cpu.PCh] << 8),
["R"] = _cpu.Regs[_cpu.R],
["Shadow AF"] = _cpu.Regs[_cpu.F_s] + (_cpu.Regs[_cpu.A_s] << 8),
["Shadow BC"] = _cpu.Regs[_cpu.C_s] + (_cpu.Regs[_cpu.B_s] << 8),
["Shadow DE"] = _cpu.Regs[_cpu.E_s] + (_cpu.Regs[_cpu.D_s] << 8),
["Shadow HL"] = _cpu.Regs[_cpu.L_s] + (_cpu.Regs[_cpu.H_s] << 8),
["SP"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8),
["Flag C"] = _cpu.FlagC,
["Flag N"] = _cpu.FlagN,
["Flag P/V"] = _cpu.FlagP,
["Flag 3rd"] = _cpu.Flag3,
["Flag H"] = _cpu.FlagH,
["Flag 5th"] = _cpu.Flag5,
["Flag Z"] = _cpu.FlagZ,
["Flag S"] = _cpu.FlagS
};
}
@ -49,73 +49,85 @@ namespace BizHawk.Emulation.Cores.Calculators
{
switch (register)
{
default:
throw new InvalidOperationException();
case "A":
_cpu.RegisterA = (byte)value;
break;
case "AF":
_cpu.RegisterAF = (byte)value;
break;
case "B":
_cpu.RegisterB = (byte)value;
break;
case "BC":
_cpu.RegisterBC = (byte)value;
break;
case "C":
_cpu.RegisterC = (byte)value;
break;
case "D":
_cpu.RegisterD = (byte)value;
break;
case "DE":
_cpu.RegisterDE = (byte)value;
break;
case "E":
_cpu.RegisterE = (byte)value;
break;
case "F":
_cpu.RegisterF = (byte)value;
break;
case "H":
_cpu.RegisterH = (byte)value;
break;
case "HL":
_cpu.RegisterHL = (byte)value;
break;
case "I":
_cpu.RegisterI = (byte)value;
break;
case "IX":
_cpu.RegisterIX = (byte)value;
break;
case "IY":
_cpu.RegisterIY = (byte)value;
break;
case "L":
_cpu.RegisterL = (byte)value;
break;
case "PC":
_cpu.RegisterPC = (ushort)value;
break;
case "R":
_cpu.RegisterR = (byte)value;
break;
case "Shadow AF":
_cpu.RegisterShadowAF = (byte)value;
break;
case "Shadow BC":
_cpu.RegisterShadowBC = (byte)value;
break;
case "Shadow DE":
_cpu.RegisterShadowDE = (byte)value;
break;
case "Shadow HL":
_cpu.RegisterShadowHL = (byte)value;
break;
case "SP":
_cpu.RegisterSP = (byte)value;
default:
throw new InvalidOperationException();
case "A":
_cpu.Regs[_cpu.A] = (ushort)value;
break;
case "AF":
_cpu.Regs[_cpu.F] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.A] = (ushort)(value & 0xFF00);
break;
case "B":
_cpu.Regs[_cpu.B] = (ushort)value;
break;
case "BC":
_cpu.Regs[_cpu.C] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.B] = (ushort)(value & 0xFF00);
break;
case "C":
_cpu.Regs[_cpu.C] = (ushort)value;
break;
case "D":
_cpu.Regs[_cpu.D] = (ushort)value;
break;
case "DE":
_cpu.Regs[_cpu.E] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.D] = (ushort)(value & 0xFF00);
break;
case "E":
_cpu.Regs[_cpu.E] = (ushort)value;
break;
case "F":
_cpu.Regs[_cpu.F] = (ushort)value;
break;
case "H":
_cpu.Regs[_cpu.H] = (ushort)value;
break;
case "HL":
_cpu.Regs[_cpu.L] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.H] = (ushort)(value & 0xFF00);
break;
case "I":
_cpu.Regs[_cpu.I] = (ushort)value;
break;
case "IX":
_cpu.Regs[_cpu.Ixl] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.Ixh] = (ushort)(value & 0xFF00);
break;
case "IY":
_cpu.Regs[_cpu.Iyl] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.Iyh] = (ushort)(value & 0xFF00);
break;
case "L":
_cpu.Regs[_cpu.L] = (ushort)value;
break;
case "PC":
_cpu.Regs[_cpu.PCl] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.PCh] = (ushort)(value & 0xFF00);
break;
case "R":
_cpu.Regs[_cpu.R] = (ushort)value;
break;
case "Shadow AF":
_cpu.Regs[_cpu.F_s] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.A_s] = (ushort)(value & 0xFF00);
break;
case "Shadow BC":
_cpu.Regs[_cpu.C_s] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.B_s] = (ushort)(value & 0xFF00);
break;
case "Shadow DE":
_cpu.Regs[_cpu.E_s] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.D_s] = (ushort)(value & 0xFF00);
break;
case "Shadow HL":
_cpu.Regs[_cpu.L_s] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.H_s] = (ushort)(value & 0xFF00);
break;
case "SP":
_cpu.Regs[_cpu.SPl] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.SPh] = (ushort)(value & 0xFF00);
break;
}
}

View File

@ -13,21 +13,42 @@ namespace BizHawk.Emulation.Cores.Calculators
_controller = controller;
_lagged = true;
_cpu.Debug = _tracer.Enabled;
if (_cpu.Debug && _cpu.Logger == null) // TODO, lets not do this on each frame. But lets refactor CoreComm/CoreComm first
if (_tracer.Enabled)
{
_cpu.Logger = s => _tracer.Put(s);
_cpu.TraceCallback = s => _tracer.Put(s);
}
else
{
_cpu.TraceCallback = null;
}
// I eyeballed this speed
for (int i = 0; i < 5; i++)
{
_onPressed = controller.IsPressed("ON");
_onPressed = controller.IsPressed("ON");
// and this was derived from other emus
_cpu.ExecuteCycles(10000);
_cpu.Interrupt = true;
if (_onPressed && ON_key_int_EN && !ON_key_int)
{
ON_key_int = true;
_cpu.FlagI = true;
}
// see: http://wikiti.brandonw.net/index.php?title=83:Ports:04
// for timer interrupt frequency
// CPU frequency is 6MHz
for (int i = 0; i < 100000; i++)
{
_cpu.ExecuteOne();
TIM_count++;
if (TIM_count >= TIM_hit)
{
TIM_count = 0;
if (TIM_1_int_EN)
{
TIM_1_int = true;
_cpu.FlagI = true;
}
}
}
Frame++;

View File

@ -7,55 +7,52 @@ namespace BizHawk.Emulation.Cores.Calculators
{
public partial class TI83 : IStatable
{
private byte[] _stateBuffer;
public bool BinarySaveStatesPreferred
{
get { return true; }
}
public bool BinarySaveStatesPreferred => false;
public void SaveStateText(TextWriter writer)
{
SyncState(new Serializer(writer));
}
public void LoadStateText(TextReader reader)
{
SyncState(new Serializer(reader));
}
public void SaveStateBinary(BinaryWriter bw)
{
SyncState(Serializer.CreateBinaryWriter(bw));
SyncState(new Serializer(bw));
}
public void LoadStateBinary(BinaryReader br)
{
SyncState(Serializer.CreateBinaryReader(br));
}
public void SaveStateText(TextWriter tw)
{
SyncState(Serializer.CreateTextWriter(tw));
}
public void LoadStateText(TextReader tr)
{
SyncState(Serializer.CreateTextReader(tr));
SyncState(new Serializer(br));
}
public byte[] SaveStateBinary()
{
if (_stateBuffer == null)
{
var stream = new MemoryStream();
var writer = new BinaryWriter(stream);
SaveStateBinary(writer);
_stateBuffer = stream.ToArray();
writer.Close();
return _stateBuffer;
}
else
{
var stream = new MemoryStream(_stateBuffer);
var writer = new BinaryWriter(stream);
SaveStateBinary(writer);
writer.Close();
return _stateBuffer;
}
MemoryStream ms = new MemoryStream();
BinaryWriter bw = new BinaryWriter(ms);
SaveStateBinary(bw);
bw.Flush();
return ms.ToArray();
}
private void SyncState(Serializer ser)
{
ser.BeginSection("TI83");
byte[] core = null;
if (ser.IsWriter)
{
var ms = new MemoryStream();
ms.Close();
core = ms.ToArray();
}
_cpu.SyncState(ser);
ser.BeginSection("TI83");
ser.Sync("RAM", ref _ram, false);
ser.Sync("romPageLow3Bits", ref _romPageLow3Bits);
ser.Sync("romPageHighBit", ref _romPageHighBit);
@ -72,6 +69,15 @@ namespace BizHawk.Emulation.Cores.Calculators
ser.Sync("Frame", ref _frame);
ser.Sync("LagCount", ref _lagCount);
ser.Sync("IsLag", ref _isLag);
ser.Sync("ON_key_int", ref ON_key_int);
ser.Sync("ON_key_int_EN", ref ON_key_int_EN);
ser.Sync("TIM_1_int", ref TIM_1_int);
ser.Sync("TIM_1_int_EN", ref TIM_1_int_EN);
ser.Sync("TIM_frq", ref TIM_frq);
ser.Sync("TIM_mult", ref TIM_mult);
ser.Sync("TIM_count", ref TIM_count);
ser.Sync("TIM_hit", ref TIM_hit);
ser.EndSection();
if (ser.IsReader)

View File

@ -2,7 +2,7 @@ using System;
using System.Globalization;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components.Z80;
using BizHawk.Emulation.Cores.Components.Z80A;
// http://www.ticalc.org/pub/text/calcinfo/
namespace BizHawk.Emulation.Cores.Calculators
@ -23,6 +23,7 @@ namespace BizHawk.Emulation.Cores.Calculators
PutSettings((TI83Settings)settings ?? new TI83Settings());
CoreComm = comm;
_cpu.FetchMemory = ReadMemory;
_cpu.ReadMemory = ReadMemory;
_cpu.WriteMemory = WriteMemory;
_cpu.ReadHardware = ReadHardware;
@ -34,21 +35,13 @@ namespace BizHawk.Emulation.Cores.Calculators
_rom = rom;
LinkPort = new TI83LinkPort(this);
// different calculators (different revisions?) have different initPC. we track this in the game database by rom hash
// if( *(unsigned long *)(m_pRom + 0x6ce) == 0x04D3163E ) m_Regs.PC.W = 0x6ce; //KNOWN
// else if( *(unsigned long *)(m_pRom + 0x6f6) == 0x04D3163E ) m_Regs.PC.W = 0x6f6; //UNKNOWN
if (game["initPC"])
{
_startPC = ushort.Parse(game.OptionValue("initPC"), NumberStyles.HexNumber);
}
HardReset();
SetupMemoryDomains();
_tracer = new TraceBuffer { Header = _cpu.TraceHeader };
ser.Register<ITraceable>(_tracer);
ser.Register<IDisassemblable>(new Disassembler());
ser.Register<IDisassemblable>(_cpu);
}
private readonly TraceBuffer _tracer;
@ -57,8 +50,6 @@ namespace BizHawk.Emulation.Cores.Calculators
private readonly byte[] _rom;
// configuration
private readonly ushort _startPC;
private IController _controller;
private byte[] _ram;
@ -75,6 +66,10 @@ namespace BizHawk.Emulation.Cores.Calculators
private bool _cursorMoved;
private int _frame;
public bool ON_key_int, ON_key_int_EN;
public bool TIM_1_int, TIM_1_int_EN;
public int TIM_frq, TIM_mult, TIM_count, TIM_hit;
// Link Cable
public TI83LinkPort LinkPort { get; }
@ -151,7 +146,7 @@ namespace BizHawk.Emulation.Cores.Calculators
if (LinkActive)
{
// Prevent rom calls from disturbing link port activity
if (LinkActive && _cpu.RegisterPC < 0x4000)
if (LinkActive && _cpu.RegPC < 0x4000)
{
return;
}
@ -169,7 +164,60 @@ namespace BizHawk.Emulation.Cores.Calculators
_romPageLow3Bits = value & 0x7;
break;
case 3: // PORT_STATUS
_maskOn = (byte)(value & 1);
// controls ON key interrupts
if ((value & 0x1) == 0)
{
ON_key_int = false;
ON_key_int_EN = false;
}
else
{
ON_key_int_EN = true;
}
// controls first timer interrupts
if ((value & 0x2) == 0)
{
TIM_1_int = false;
TIM_1_int_EN = false;
}
else
{
TIM_1_int_EN = true;
}
// controls second timer, not yet implemented and unclear how to differentiate
if ((value & 0x4) == 0)
{
}
else
{
}
// controls low power mode, not yet implemeneted
if ((value & 0x8) == 0)
{
}
else
{
}
break;
case 4: // PORT_INTCTRL
// controls ON key interrupts
TIM_frq = value & 6;
TIM_mult = ((value & 0x10) == 0x10) ? 1800 : 1620;
TIM_hit = (int)Math.Floor((double)TIM_mult / (3 + TIM_frq * 2));
TIM_hit = (int)Math.Floor((double)6000000 / TIM_hit);
// Bit 0 is some form of memory mapping
// Bit 5 controls reset
// Bit 6-7 controls battery power compare (not implemented, will always return full power)
break;
case 16: // PORT_DISPCTRL
////Console.WriteLine("write PORT_DISPCTRL {0}",value);
@ -198,22 +246,23 @@ namespace BizHawk.Emulation.Cores.Calculators
{
// Console.WriteLine("read PORT_STATUS");
// Bits:
// 0 - Set if ON key is down and ON key is trapped
// 0 - Set if ON key Interrupt generated
// 1 - Update things (keyboard etc)
// 2 - Unknown, but used
// 3 - Set if ON key is up
// 4-7 - Unknown
////if (onPressed && maskOn) ret |= 1;
////if (!onPressed) ret |= 0x8;
return (byte)((_controller.IsPressed("ON") ? _maskOn : 8) | (LinkActive ? 0 : 2));
return (byte)((_controller.IsPressed("ON") ? 0 : 8) |
(TIM_1_int ? 2 : 0) |
(ON_key_int ? 1 : 0));
}
case 4: // PORT_INTCTRL
////Console.WriteLine("read PORT_INTCTRL");
return 0xFF;
// returns mirror of link port
return (byte)((_romPageHighBit << 4) | (LinkState << 2) | LinkOutput);
case 16: // PORT_DISPCTRL
////Console.WriteLine("read DISPCTRL");
// Console.WriteLine("read DISPCTRL");
break;
case 17: // PORT_DISPDATA
@ -428,13 +477,13 @@ namespace BizHawk.Emulation.Cores.Calculators
private void IRQCallback()
{
// Console.WriteLine("IRQ with vec {0} and cpu.InterruptMode {1}", cpu.RegisterI, cpu.InterruptMode);
_cpu.Interrupt = false;
//Console.WriteLine("IRQ with vec {0} and cpu.InterruptMode {1}", _cpu.Regs[_cpu.I], _cpu.InterruptMode);
_cpu.FlagI = false;
}
private void NMICallback()
{
Console.WriteLine("NMI");
//Console.WriteLine("NMI");
_cpu.NonMaskableInterrupt = false;
}
@ -447,7 +496,7 @@ namespace BizHawk.Emulation.Cores.Calculators
_ram[i] = 0xFF;
}
_cpu.RegisterPC = _startPC;
_cpu.RegPC = 0;
_cpu.IFF1 = false;
_cpu.IFF2 = false;
@ -463,4 +512,4 @@ namespace BizHawk.Emulation.Cores.Calculators
_displayX = _displayY = 0;
}
}
}
}

View File

@ -21,7 +21,6 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
private IController _controller;
private int _frame;
private int _lastAddress;
private bool _frameStartPending = true;
private bool _leftDifficultySwitchPressed;
private bool _rightDifficultySwitchPressed;
@ -34,6 +33,9 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
internal byte[] Rom { get; }
internal int DistinctAccessCount { get; private set; }
// keeps track of tia cycles, 3 cycles per CPU cycle
private int cyc_counter;
private static MapperBase SetMultiCartMapper(int romLength, int gameTotal)
{
switch (romLength / gameTotal)
@ -319,17 +321,10 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
_m6532 = new M6532(this);
// Set up the system state here. for instance..
// Read from the reset vector for where to start
Cpu.PC = (ushort)(ReadMemory(0x1FFC) + (ReadMemory(0x1FFD) << 8)); // set the initial PC
HardReset();
// Show mapper class on romstatusdetails
CoreComm.RomStatusDetails = $"{this._game.Name}\r\nSHA1:{Rom.HashSHA1()}\r\nMD5:{Rom.HashMD5()}\r\nMapper Impl \"{_mapper.GetType()}\"";
// as it turns out, the stack pointer cannot be set to 0 for some games as they do not initilize it themselves.
// some documentation seems to indicate it should beset to FD, but currently there is no documentation of the 6532
// executing a reset sequence at power on, but it's needed so let's hard code it for now
Cpu.S = 0xFD;
}
private bool _pal;
@ -349,96 +344,28 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
};
_tia.Reset();
_m6532 = new M6532(this);
Cpu.PC = (ushort)(ReadMemory(0x1FFC) + (ReadMemory(0x1FFD) << 8)); // set the initial PC
// as it turns out, the stack pointer cannot be set to 0 for some games as they do not initilize it themselves.
// some documentation seems to indicate it should beset to FD, but currently there is no documentation of the 6532
// executing a reset sequence at power on, but it's needed so let's hard code it for now
Cpu.S = 0xFD;
SetupMemoryDomains();
}
private void VFrameAdvance() // advance up to 500 lines looking for end of video frame
// after vsync falling edge, continues to end of next line
{
bool frameend = false;
_tia.FrameEndCallBack = (n) => frameend = true;
for (int i = 0; i < 500 && !frameend; i++)
{
ScanlineAdvance();
}
_tia.FrameEndCallBack = null;
}
private void StartFrameCond()
{
if (_frameStartPending)
{
_frame++;
_islag = true;
if (_controller.IsPressed("Power"))
{
HardReset();
}
if (_controller.IsPressed("Toggle Left Difficulty") && !_leftDifficultySwitchHeld)
{
_leftDifficultySwitchPressed ^= true;
_leftDifficultySwitchHeld = true;
}
else if (!_controller.IsPressed("Toggle Left Difficulty"))
{
_leftDifficultySwitchHeld = false;
}
if (_controller.IsPressed("Toggle Right Difficulty") && !_rightDifficultySwitchHeld)
{
_rightDifficultySwitchPressed ^= true;
_rightDifficultySwitchHeld = true;
}
else if (!_controller.IsPressed("Toggle Right Difficulty"))
{
_rightDifficultySwitchHeld = false;
}
_tia.BeginAudioFrame();
_frameStartPending = false;
}
}
private void FinishFrameCond()
{
if (_tia.LineCount >= _tia.NominalNumScanlines)
{
_tia.CompleteAudioFrame();
if (_islag)
{
_lagcount++;
}
_tia.LineCount = 0;
_frameStartPending = true;
}
cyc_counter = 0;
}
private void Cycle()
{
_tia.Execute(1);
_tia.Execute(1);
_tia.Execute(1);
_m6532.Timer.Tick();
if (Tracer.Enabled)
_tia.Execute();
cyc_counter++;
if (cyc_counter == 3)
{
Tracer.Put(Cpu.TraceState());
}
_m6532.Timer.Tick();
if (Tracer.Enabled)
{
Tracer.Put(Cpu.TraceState());
}
Cpu.ExecuteOne();
_mapper.ClockCpu();
Cpu.ExecuteOne();
_mapper.ClockCpu();
cyc_counter = 0;
}
}
internal byte ReadControls1(bool peek)

View File

@ -60,97 +60,18 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
public bool CanStep(StepType type)
{
switch (type)
{
case StepType.Into:
case StepType.Out:
case StepType.Over:
return true;
default:
return false;
}
return false;
}
[FeatureNotImplemented]
public void Step(StepType type)
{
switch (type)
{
case StepType.Into:
StepInto();
break;
case StepType.Out:
StepOut();
break;
case StepType.Over:
StepOver();
break;
}
throw new NotImplementedException();
}
public int TotalExecutedCycles => Cpu.TotalExecutedCycles;
private void StepInto()
{
do
{
CycleAdvance();
} while (!Cpu.AtStart);
}
private void StepOver()
{
var instruction = Cpu.PeekMemory(Cpu.PC);
if (instruction == JSR)
{
var destination = Cpu.PC + opsize[JSR];
while (Cpu.PC != destination)
{
StepInto();
}
}
else
{
StepInto();
}
}
private void StepOut()
{
var instruction = Cpu.PeekMemory(Cpu.PC);
JSRCount = instruction == JSR ? 1 : 0;
var bailOutFrame = Frame + 1;
while (true)
{
StepInto();
var instr = Cpu.PeekMemory(Cpu.PC);
if (instr == JSR)
{
JSRCount++;
}
else if (instr == RTS && JSRCount <= 0)
{
StepInto();
JSRCount = 0;
break;
}
else if (instr == RTS)
{
JSRCount--;
}
else // Emergency bail out logic
{
if (Frame == bailOutFrame)
{
break;
}
}
}
}
private int JSRCount = 0;
private const byte JSR = 0x20;
@ -212,22 +133,6 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
#region Currently Unused Debug hooks
private void ScanlineAdvance()
{
StartFrameCond();
int currentLine = _tia.LineCount;
while (_tia.LineCount == currentLine)
Cycle();
FinishFrameCond();
}
private void CycleAdvance()
{
StartFrameCond();
Cycle();
FinishFrameCond();
}
private int CurrentScanLine
{
get { return _tia.LineCount; }

View File

@ -12,18 +12,53 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
{
_controller = controller;
StartFrameCond();
while (_tia.LineCount < _tia.NominalNumScanlines)
_frame++;
_islag = true;
// Handle all the console controls here
if (_controller.IsPressed("Power"))
{
HardReset();
}
if (_controller.IsPressed("Toggle Left Difficulty") && !_leftDifficultySwitchHeld)
{
_leftDifficultySwitchPressed ^= true;
_leftDifficultySwitchHeld = true;
}
else if (!_controller.IsPressed("Toggle Left Difficulty"))
{
_leftDifficultySwitchHeld = false;
}
if (_controller.IsPressed("Toggle Right Difficulty") && !_rightDifficultySwitchHeld)
{
_rightDifficultySwitchPressed ^= true;
_rightDifficultySwitchHeld = true;
}
else if (!_controller.IsPressed("Toggle Right Difficulty"))
{
_rightDifficultySwitchHeld = false;
}
while (!_tia.New_Frame)
{
Cycle();
}
_tia.New_Frame = false;
if (rendersound == false)
{
_tia.AudioClocks = 0; // we need this here since the async sound provider won't check in this case
}
FinishFrameCond();
if (_islag)
{
_lagcount++;
}
_tia.LineCount = 0;
}
public int Frame => _frame;

View File

@ -46,7 +46,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
ser.Sync("Lag", ref _lagcount);
ser.Sync("Frame", ref _frame);
ser.Sync("IsLag", ref _islag);
ser.Sync("frameStartPending", ref _frameStartPending);
ser.Sync("cyc_counter", ref cyc_counter);
ser.Sync("leftDifficultySwitchPressed", ref _leftDifficultySwitchPressed);
ser.Sync("rightDifficultySwitchPressed", ref _rightDifficultySwitchPressed);
ser.Sync("leftDifficultySwitchHeld", ref _leftDifficultySwitchHeld);

View File

@ -102,18 +102,25 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
// give the emu a minimal of input\output connections so it doesn't crash
var comm = new CoreComm(null, null);
// here we advance past start up irregularities to see how long a frame is based on calls to Vsync
// we run 72 frames, then run 270 scanlines worth of cycles.
// if we don't hit a new frame, we can be pretty confident we are in PAL
using (Atari2600 emu = new Atari2600(new CoreComm(null, null), newgame, rom, null, null))
{
List<int> framecounts = new List<int>();
emu._tia.FrameEndCallBack = (i) => framecounts.Add(i);
for (int i = 0; i < 71; i++) // run for 71 * 262 lines, since we're in NTSC mode
for (int i = 0; i < 72; i++)
{
emu.FrameAdvance(NullController.Instance, false, false);
}
int numpal = framecounts.Count((i) => i > 287);
bool pal = numpal >= 25;
Console.WriteLine("PAL Detection: {0} lines, {1}", numpal, pal);
for (int i = 0; i < 61560; i++)
{
emu.Cycle();
}
bool pal = !emu._tia.New_Frame;
Console.WriteLine("PAL Detection: {0}", pal);
return pal;
}
}

View File

@ -35,6 +35,9 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
_spf = _vsyncNum / (double)_vsyncDen > 55.0 ? 735 : 882;
}
// indicates to the core where a new frame is starting
public bool New_Frame = false;
private const int BackColor = unchecked((int)0xff000000);
private const int ScreenWidth = 160;
private const int MaxScreenHeight = 312;
@ -161,11 +164,6 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
/// </summary>
public int LineCount { get; set; }
/// <summary>
/// Gets or sets a callback that is called at the end of a video frame. used internally
/// </summary>
public Action<int> FrameEndCallBack { private get; set; }
public void Reset()
{
_hsyncCnt = 0;
@ -229,11 +227,9 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
}
// Execute TIA cycles
public void Execute(int cycles)
public void Execute()
{
// Still ignoring cycles...
// delay vblank latch
// Handle all of the Latch delays that occur in the TIA
if (_vblankDelay > 0)
{
_vblankDelay++;
@ -244,7 +240,6 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
}
}
// delay latch to new playfield register
if (_pf0Updater)
{
_pf0DelayClock++;
@ -275,7 +270,6 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
}
}
// delay latch to missile enable
if (_enam0Delay > 0)
{
_enam0Delay++;
@ -296,7 +290,6 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
}
}
// delay latch to ball enable
if (_enambDelay > 0)
{
_enambDelay++;
@ -307,7 +300,6 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
}
}
// delay latch to player graphics registers
if (_prg0Delay > 0)
{
_prg0Delay++;
@ -333,7 +325,6 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
}
}
// HMP write delay
if (_hmp0Delay > 0)
{
_hmp0Delay++;
@ -773,9 +764,12 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
// do the audio sampling
if (_hsyncCnt == 36 || _hsyncCnt == 148)
{
LocalAudioCycles[AudioClocks] += (short)(AUD[0].Cycle() / 2);
LocalAudioCycles[AudioClocks] += (short)(AUD[1].Cycle() / 2);
AudioClocks++;
if (AudioClocks < 2000)
{
LocalAudioCycles[AudioClocks] += (short)(AUD[0].Cycle() / 2);
LocalAudioCycles[AudioClocks] += (short)(AUD[1].Cycle() / 2);
AudioClocks++;
}
}
// Increment the hsync counter
@ -796,7 +790,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
int topLine = _pal ? _core.Settings.PALTopLine : _core.Settings.NTSCTopLine;
int bottomLine = _pal ? _core.Settings.PALBottomLine : _core.Settings.NTSCBottomLine;
// if vsync occured unexpectedly early, black out the remainer
// if vsync occured unexpectedly early, black out the remainder
for (; validlines < bottomLine; validlines++)
{
for (int i = 0; i < 160; i++)
@ -972,7 +966,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
// write to frame buffer
OutputFrame(_currentScanLine);
FrameEndCallBack?.Invoke(_currentScanLine);
New_Frame = true;
// Clear all from last frame
_currentScanLine = 0;
@ -1337,17 +1331,5 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
{
AUDC, AUDF, AUDV
}
private int _frameStartCycles, _frameEndCycles;
public void BeginAudioFrame()
{
_frameStartCycles = _core.Cpu.TotalExecutedCycles;
}
public void CompleteAudioFrame()
{
_frameEndCycles = _core.Cpu.TotalExecutedCycles;
}
}
}

View File

@ -62,10 +62,8 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
ser.Sync("vblankEnabled", ref _vblankEnabled);
ser.Sync("vsyncEnabled", ref _vsyncEnabled);
ser.Sync("CurrentScanLine", ref _currentScanLine);
ser.Sync("scanlinebuffer", ref _scanlinebuffer, false);
ser.Sync("AudioClocks", ref AudioClocks);
ser.Sync("FrameStartCycles", ref _frameStartCycles);
ser.Sync("FrameEndCycles", ref _frameEndCycles);
ser.Sync("New_Frame", ref New_Frame);
ser.BeginSection("Player0");
_player0.SyncState(ser);

View File

@ -1,76 +0,0 @@
using System;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.Atari7800
{
public partial class Atari7800 : IDebuggable
{
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
{
return new Dictionary<string, RegisterValue>
{
["A"] = _theMachine.CPU.A,
["P"] = _theMachine.CPU.P,
["PC"] = _theMachine.CPU.PC,
["S"] = _theMachine.CPU.S,
["X"] = _theMachine.CPU.X,
["Y"] = _theMachine.CPU.Y,
["Flag B"] = _theMachine.CPU.fB,
["Flag C"] = _theMachine.CPU.fC,
["Flag D"] = _theMachine.CPU.fD,
["Flag I"] = _theMachine.CPU.fI,
["Flag N"] = _theMachine.CPU.fN,
["Flag V"] = _theMachine.CPU.fV,
["Flag Z"] = _theMachine.CPU.fZ
};
}
public void SetCpuRegister(string register, int value)
{
switch (register)
{
default:
throw new InvalidOperationException();
case "A":
_theMachine.CPU.A = (byte)value;
break;
case "P":
_theMachine.CPU.P = (byte)value;
break;
case "PC":
_theMachine.CPU.PC = (ushort)value;
break;
case "S":
_theMachine.CPU.S = (byte)value;
break;
case "X":
_theMachine.CPU.X = (byte)value;
break;
case "Y":
_theMachine.CPU.Y = (byte)value;
break;
}
}
public IMemoryCallbackSystem MemoryCallbacks
{
[FeatureNotImplemented]
get { throw new NotImplementedException(); }
}
public bool CanStep(StepType type)
{
return false;
}
[FeatureNotImplemented]
public void Step(StepType type)
{
throw new NotImplementedException();
}
public int TotalExecutedCycles => (int)_theMachine.CPU.Clock;
}
}

View File

@ -1,59 +0,0 @@
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.Atari7800
{
public partial class Atari7800 : IEmulator
{
public IEmulatorServiceProvider ServiceProvider { get; }
public ControllerDefinition ControllerDefinition { get; private set; }
public void FrameAdvance(IController controller, bool render, bool rendersound)
{
_frame++;
if (controller.IsPressed("Power"))
{
// it seems that theMachine.Reset() doesn't clear ram, etc
// this should leave hsram intact but clear most other things
HardReset();
}
ControlAdapter.Convert(controller, _theMachine.InputState);
_theMachine.ComputeNextFrame(_avProvider.Framebuffer);
_islag = _theMachine.InputState.Lagged;
if (_islag)
{
_lagcount++;
}
_avProvider.FillFrameBuffer();
}
public int Frame => _frame;
public string SystemId => "A78"; // TODO 2600?
public bool DeterministicEmulation { get; set; }
public void ResetCounters()
{
_frame = 0;
_lagcount = 0;
_islag = false;
}
public CoreComm CoreComm { get; }
public void Dispose()
{
if (_avProvider != null)
{
_avProvider.Dispose();
_avProvider = null;
}
}
}
}

View File

@ -1,24 +0,0 @@
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.Atari7800
{
public partial class Atari7800 : IInputPollable
{
public int LagCount
{
get { return _lagcount; }
set { _lagcount = value; }
}
public bool IsLagFrame
{
get { return _islag; }
set { _islag = value; }
}
public IInputCallbackSystem InputCallbacks { get; } = new InputCallbackSystem();
private bool _islag = true;
private int _lagcount;
}
}

View File

@ -1,93 +0,0 @@
using System;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
using EMU7800.Core;
namespace BizHawk.Emulation.Cores.Atari.Atari7800
{
public partial class Atari7800
{
private List<MemoryDomain> _MemoryDomains;
private IMemoryDomains MemoryDomains;
public void SetupMemoryDomains(HSC7800 hsc7800)
{
// reset memory domains
if (_MemoryDomains == null)
{
_MemoryDomains = new List<MemoryDomain>();
if (_theMachine is Machine7800)
{
_MemoryDomains.Add(new MemoryDomainDelegate(
"RAM", 0x1000, MemoryDomain.Endian.Unknown,
delegate(long addr)
{
if (addr < 0 || addr >= 0x1000)
{
throw new ArgumentOutOfRangeException();
}
if (addr < 0x800)
{
return ((Machine7800)_theMachine).RAM1[(ushort)addr];
}
return ((Machine7800)_theMachine).RAM2[(ushort)addr];
},
delegate(long addr, byte val)
{
if (addr < 0 || addr >= 0x1000)
{
throw new ArgumentOutOfRangeException();
}
else if (addr < 0x800)
{
((Machine7800)_theMachine).RAM1[(ushort)(addr & 0x800)] = val;
}
else
{
((Machine7800)_theMachine).RAM2[(ushort)addr] = val;
}
}, 1));
_MemoryDomains.Add(new MemoryDomainByteArray(
"BIOS ROM", MemoryDomain.Endian.Unknown,
_bios, false, 1));
if (hsc7800 != null)
{
_MemoryDomains.Add(new MemoryDomainByteArray(
"HSC ROM", MemoryDomain.Endian.Unknown, _hsbios, false, 1));
_MemoryDomains.Add(new MemoryDomainByteArray(
"HSC RAM", MemoryDomain.Endian.Unknown, _hsram, true, 1));
}
_MemoryDomains.Add(new MemoryDomainDelegate(
"System Bus", 65536, MemoryDomain.Endian.Unknown,
delegate(long addr)
{
if (addr < 0 || addr >= 0x10000)
throw new ArgumentOutOfRangeException();
return _theMachine.Mem[(ushort)addr];
},
delegate(long addr, byte val)
{
if (addr < 0 || addr >= 0x10000)
throw new ArgumentOutOfRangeException();
_theMachine.Mem[(ushort)addr] = val;
}, 1));
}
else // todo 2600?
{
}
MemoryDomains = new MemoryDomainList(_MemoryDomains);
(ServiceProvider as BasicServiceProvider).Register<IMemoryDomains>(MemoryDomains);
}
}
}
}

View File

@ -1,22 +0,0 @@
using System;
using BizHawk.Emulation.Common;
using EMU7800.Core;
namespace BizHawk.Emulation.Cores.Atari.Atari7800
{
public partial class Atari7800 : ISaveRam
{
public byte[] CloneSaveRam()
{
return (byte[])_hsram.Clone();
}
public void StoreSaveRam(byte[] data)
{
Buffer.BlockCopy(data, 0, _hsram, 0, data.Length);
}
public bool SaveRamModified => _gameInfo.MachineType == MachineType.A7800PAL
|| _gameInfo.MachineType == MachineType.A7800NTSC;
}
}

View File

@ -1,66 +0,0 @@
using System.IO;
using BizHawk.Common;
using BizHawk.Emulation.Common;
using EMU7800.Core;
namespace BizHawk.Emulation.Cores.Atari.Atari7800
{
public partial class Atari7800 : IStatable
{
public bool BinarySaveStatesPreferred => true;
public void SaveStateText(TextWriter writer)
{
SyncState(new Serializer(writer));
}
public void LoadStateText(TextReader reader)
{
SyncState(new Serializer(reader));
}
public void SaveStateBinary(BinaryWriter bw)
{
SyncState(new Serializer(bw));
}
public void LoadStateBinary(BinaryReader br)
{
SyncState(new Serializer(br));
}
public byte[] SaveStateBinary()
{
MemoryStream ms = new MemoryStream();
BinaryWriter bw = new BinaryWriter(ms);
SaveStateBinary(bw);
bw.Flush();
return ms.ToArray();
}
private void SyncState(Serializer ser)
{
byte[] core = null;
if (ser.IsWriter)
{
var ms = new MemoryStream();
_theMachine.Serialize(new BinaryWriter(ms));
ms.Close();
core = ms.ToArray();
}
ser.BeginSection("Atari7800");
ser.Sync("core", ref core, false);
ser.Sync("Lag", ref _lagcount);
ser.Sync("Frame", ref _frame);
ser.Sync("IsLag", ref _islag);
ser.EndSection();
if (ser.IsReader)
{
_theMachine = MachineBase.Deserialize(new BinaryReader(new MemoryStream(core, false)));
_avProvider.ConnectToMachine(_theMachine, _gameInfo);
}
}
}
}

View File

@ -1,301 +0,0 @@
using System;
using System.IO;
using BizHawk.Emulation.Common;
using EMU7800.Core;
using EMU7800.Win;
namespace BizHawk.Emulation.Cores.Atari.Atari7800
{
[Core(
"EMU7800",
"",
isPorted: true,
isReleased: true,
portedVersion: "v1.5",
portedUrl: "http://emu7800.sourceforge.net/")]
[ServiceNotApplicable(typeof(ISettable<,>), typeof(IDriveLight))]
public partial class Atari7800 : IEmulator, ISaveRam, IDebuggable, IStatable, IInputPollable, IRegionable
{
// TODO:
// some things don't work when you try to plug in a 2600 game
static Atari7800()
{
// add alpha bits to palette tables
for (int i = 0; i < TIATables.NTSCPalette.Length; i++)
{
TIATables.NTSCPalette[i] |= unchecked((int)0xff000000);
}
for (int i = 0; i < TIATables.PALPalette.Length; i++)
{
TIATables.PALPalette[i] |= unchecked((int)0xff000000);
}
for (int i = 0; i < MariaTables.NTSCPalette.Length; i++)
{
MariaTables.NTSCPalette[i] |= unchecked((int)0xff000000);
}
for (int i = 0; i < MariaTables.PALPalette.Length; i++)
{
MariaTables.PALPalette[i] |= unchecked((int)0xff000000);
}
}
public Atari7800(CoreComm comm, GameInfo game, byte[] rom, string gameDbFn)
{
var ser = new BasicServiceProvider(this);
ser.Register<IVideoProvider>(_avProvider);
ser.Register<ISoundProvider>(_avProvider);
ServiceProvider = ser;
CoreComm = comm;
byte[] highscoreBios = comm.CoreFileProvider.GetFirmware("A78", "Bios_HSC", false, "Some functions may not work without the high score BIOS.");
byte[] palBios = comm.CoreFileProvider.GetFirmware("A78", "Bios_PAL", false, "The game will not run if the correct region BIOS is not available.");
byte[] ntscBios = comm.CoreFileProvider.GetFirmware("A78", "Bios_NTSC", false, "The game will not run if the correct region BIOS is not available.");
if (GameProgramLibrary.EMU7800DB == null)
{
GameProgramLibrary.EMU7800DB = new GameProgramLibrary(new StreamReader(gameDbFn));
}
if (rom.Length % 1024 == 128)
{
Console.WriteLine("Trimming 128 byte .a78 header...");
byte[] newrom = new byte[rom.Length - 128];
Buffer.BlockCopy(rom, 128, newrom, 0, newrom.Length);
rom = newrom;
}
_gameInfo = GameProgramLibrary.EMU7800DB.TryRecognizeRom(rom);
CoreComm.RomStatusDetails = _gameInfo.ToString();
Console.WriteLine("Rom Determiniation from 7800DB:");
Console.WriteLine(_gameInfo.ToString());
_rom = rom;
_hsbios = highscoreBios;
_bios = _gameInfo.MachineType == MachineType.A7800PAL ? palBios : ntscBios;
_pal = _gameInfo.MachineType == MachineType.A7800PAL || _gameInfo.MachineType == MachineType.A2600PAL;
if (_bios == null)
{
throw new MissingFirmwareException("The BIOS corresponding to the region of the game you loaded is required to run Atari 7800 games.");
}
HardReset();
}
public DisplayType Region => _pal ? DisplayType.PAL : DisplayType.NTSC;
public Atari7800Control ControlAdapter { get; private set; }
private readonly byte[] _rom;
private readonly byte[] _hsbios;
private readonly byte[] _bios;
private readonly GameProgram _gameInfo;
private readonly byte[] _hsram = new byte[2048];
private readonly bool _pal;
private Cart _cart;
private MachineBase _theMachine;
private int _frame = 0;
private class ConsoleLogger : ILogger
{
public void WriteLine(string format, params object[] args)
{
Console.WriteLine(format, args);
}
public void WriteLine(object value)
{
Console.WriteLine(value);
}
public void Write(string format, params object[] args)
{
Console.Write(format, args);
}
public void Write(object value)
{
Console.Write(value);
}
}
private void HardReset()
{
_cart = Cart.Create(_rom, _gameInfo.CartType);
ILogger logger = new ConsoleLogger();
HSC7800 hsc7800 = null;
if (_hsbios != null)
{
hsc7800 = new HSC7800(_hsbios, _hsram);
}
Bios7800 bios7800 = new Bios7800(_bios);
_theMachine = MachineBase.Create(
_gameInfo.MachineType,
_cart,
bios7800,
hsc7800,
_gameInfo.LController,
_gameInfo.RController,
logger);
_theMachine.Reset();
_theMachine.InputState.InputPollCallback = InputCallbacks.Call;
ControlAdapter = new Atari7800Control(_theMachine);
ControllerDefinition = ControlAdapter.ControlType;
_avProvider.ConnectToMachine(_theMachine, _gameInfo);
SetupMemoryDomains(hsc7800);
}
#region audio\video
private MyAVProvider _avProvider = new MyAVProvider();
private class MyAVProvider : IVideoProvider, ISoundProvider, IDisposable
{
// to sync exactly with audio as this emulator creates and times it, the frame rate should be exactly 60:1 or 50:1
private int _frameHz;
public FrameBuffer Framebuffer { get; private set; }
public void ConnectToMachine(MachineBase m, GameProgram g)
{
_frameHz = m.FrameHZ;
Framebuffer = m.CreateFrameBuffer();
BufferWidth = Framebuffer.VisiblePitch;
BufferHeight = Framebuffer.Scanlines;
_vidbuffer = new int[BufferWidth * BufferHeight];
uint newsamplerate = (uint)m.SoundSampleFrequency;
if (newsamplerate != _samplerate)
{
// really shouldn't happen (after init), but if it does, we're ready
_resampler?.Dispose();
_resampler = new SpeexResampler((SpeexResampler.Quality)3, newsamplerate, 44100, newsamplerate, 44100, null, null);
_samplerate = newsamplerate;
_dcfilter = new DCFilter(256);
}
if (g.MachineType == MachineType.A2600PAL)
{
_palette = TIATables.PALPalette;
}
else if (g.MachineType == MachineType.A7800PAL)
{
_palette = MariaTables.PALPalette;
}
else if (g.MachineType == MachineType.A2600NTSC)
{
_palette = TIATables.NTSCPalette;
}
else
{
_palette = MariaTables.NTSCPalette;
}
}
private uint _samplerate;
private int[] _vidbuffer;
private SpeexResampler _resampler;
private DCFilter _dcfilter;
private int[] _palette;
public void FillFrameBuffer()
{
unsafe
{
fixed (byte* src_ = Framebuffer.VideoBuffer)
fixed (int* dst_ = _vidbuffer)
fixed (int* pal = _palette)
{
byte* src = src_;
int* dst = dst_;
for (int i = 0; i < _vidbuffer.Length; i++)
{
*dst++ = pal[*src++];
}
}
}
}
public int[] GetVideoBuffer()
{
return _vidbuffer;
}
public int VirtualWidth => 275;
public int VirtualHeight => BufferHeight;
public int BufferWidth { get; private set; }
public int BufferHeight { get; private set; }
public int BackgroundColor => unchecked((int)0xff000000);
public int VsyncNumerator => _frameHz;
public int VsyncDenominator => 1;
#region ISoundProvider
public bool CanProvideAsync => false;
public void GetSamplesSync(out short[] samples, out int nsamp)
{
int nsampin = Framebuffer.SoundBufferByteLength;
unsafe
{
fixed (byte* src = Framebuffer.SoundBuffer)
{
for (int i = 0; i < nsampin; i++)
{
// the buffer values don't really get very large at all,
// so this doesn't overflow
short s = (short)(src[i] * 200);
_resampler.EnqueueSample(s, s);
}
}
}
_resampler.GetSamplesSync(out samples, out nsamp);
_dcfilter.PushThroughSamples(samples, nsamp * 2);
}
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
public void DiscardSamples()
{
_resampler?.DiscardSamples();
}
#endregion
public void Dispose()
{
if (_resampler != null)
{
_resampler.Dispose();
_resampler = null;
}
}
}
#endregion
}
}

View File

@ -1,411 +0,0 @@
using System;
using EMU7800.Core;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.Atari7800
{
public class Atari7800Control
{
private static readonly ControllerDefinition Joystick = new ControllerDefinition
{
Name = "Atari 7800 Joystick Controller",
BoolButtons =
{
// hard reset, not passed to EMU7800
"Power",
// on the console
"Reset",
"Select",
"BW", // should be "Color"??
"Left Difficulty", // better not put P# on these as they might not correspond to player numbers
"Right Difficulty",
"Pause",
// ports
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Trigger",
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Trigger"
}
};
private static readonly ControllerDefinition Paddles = new ControllerDefinition
{
Name = "Atari 7800 Paddle Controller",
BoolButtons =
{
// hard reset, not passed to EMU7800
"Power",
// on the console
"Reset",
"Select",
"BW", // should be "Color"??
"Left Difficulty", // better not put P# on these as they might not correspond to player numbers
"Right Difficulty",
// ports
"P1 Trigger",
"P2 Trigger",
"P3 Trigger",
"P4 Trigger"
},
FloatControls = // should be in [0..700000]
{
"P1 Paddle",
"P2 Paddle",
"P3 Paddle",
"P4 Paddle"
},
FloatRanges =
{
// what is the center point supposed to be here?
new[] { 0.0f, 0.0f, 700000.0f },
new[] { 0.0f, 0.0f, 700000.0f },
new[] { 0.0f, 0.0f, 700000.0f },
new[] { 0.0f, 0.0f, 700000.0f }
}
};
private static readonly ControllerDefinition Keypad = new ControllerDefinition
{
Name = "Atari 7800 Keypad Controller",
BoolButtons =
{
// hard reset, not passed to EMU7800
"Power",
// on the console
"Reset",
"Select",
"BW", // should be "Color"??
"Toggle Left Difficulty", // better not put P# on these as they might not correspond to player numbers
"Toggle Right Difficulty",
// ports
"P1 Keypad1", "P1 Keypad2", "P1 Keypad3",
"P1 Keypad4", "P1 Keypad5", "P1 Keypad6",
"P1 Keypad7", "P1 Keypad8", "P1 Keypad9",
"P1 KeypadA", "P1 Keypad0", "P1 KeypadP",
"P2 Keypad1", "P2 Keypad2", "P2 Keypad3",
"P2 Keypad4", "P2 Keypad5", "P2 Keypad6",
"P2 Keypad7", "P2 Keypad8", "P2 Keypad9",
"P2 KeypadA", "P2 Keypad0", "P2 KeypadP",
"P3 Keypad1", "P3 Keypad2", "P3 Keypad3",
"P3 Keypad4", "P3 Keypad5", "P3 Keypad6",
"P3 Keypad7", "P3 Keypad8", "P3 Keypad9",
"P3 KeypadA", "P3 Keypad0", "P3 KeypadP",
"P4 Keypad1", "P4 Keypad2", "P4 Keypad3",
"P4 Keypad4", "P4 Keypad5", "P4 Keypad6",
"P4 Keypad7", "P4 Keypad8", "P4 Keypad9",
"P4 KeypadA", "P4 Keypad0", "P4 KeypadP"
}
};
private static readonly ControllerDefinition Driving = new ControllerDefinition
{
Name = "Atari 7800 Driving Controller",
BoolButtons =
{
// hard reset, not passed to EMU7800
"Power",
// on the console
"Reset",
"Select",
"BW", // should be "Color"??
"Toggle Left Difficulty", // better not put P# on these as they might not correspond to player numbers
"Toggle Right Difficulty",
// ports
"P1 Trigger",
"P2 Trigger"
},
FloatControls = // should be in [0..3]
{
"P1 Driving",
"P2 Driving"
},
FloatRanges =
{
new[] { 0.0f, 0.0f, 3.0f },
new[] { 0.0f, 0.0f, 3.0f },
new[] { 0.0f, 0.0f, 3.0f }
}
};
private static readonly ControllerDefinition BoosterGrip = new ControllerDefinition
{
Name = "Atari 7800 Booster Grip Controller",
BoolButtons =
{
// hard reset, not passed to EMU7800
"Power",
// on the console
"Reset",
"Select",
"BW", // should be "Color"??
"Toggle Left Difficulty", // better not put P# on these as they might not correspond to player numbers
"Toggle Right Difficulty",
// ports
// NB: as referenced by the emu, p1t2 = p1t2, p1t3 = p2t2, p2t2 = p3t2, p2t3 = p4t2
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Trigger", "P1 Trigger 2", "P1 Trigger 3",
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Trigger", "P2 Trigger 2", "P2 Trigger 3"
}
};
private static readonly ControllerDefinition ProLineJoystick = new ControllerDefinition
{
Name = "Atari 7800 ProLine Joystick Controller",
BoolButtons =
{
// hard reset, not passed to EMU7800
"Power",
// on the console
"Reset",
"Select",
"BW", // should be "Color"??
"Toggle Left Difficulty", // better not put P# on these as they might not correspond to player numbers
"Toggle Right Difficulty",
"Pause",
// ports
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Trigger", "P1 Trigger 2",
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Trigger", "P2 Trigger 2"
}
};
private static readonly ControllerDefinition Lightgun = new ControllerDefinition
{
Name = "Atari 7800 Light Gun Controller",
BoolButtons =
{
// hard reset, not passed to EMU7800
"Power",
// on the console
"Reset",
"Select",
"Pause",
"Left Difficulty", // better not put P# on these as they might not correspond to player numbers
"Right Difficulty",
// ports
"P1 Trigger",
"P2 Trigger"
},
FloatControls = // vpos should be actual scanline number. hpos should be in [0..319]??
{
"P1 VPos", "P1 HPos",
"P2 VPos", "P2 HPos"
},
FloatRanges =
{
// how many scanlines are there again??
new[] { 0.0f, 0.0f, 240.0f },
new[] { 0.0f, 0.0f, 319.0f },
new[] { 0.0f, 0.0f, 240.0f },
new[] { 0.0f, 0.0f, 319.0f }
}
};
private struct ControlAdapter
{
public readonly ControllerDefinition Type;
public readonly Controller Left;
public readonly Controller Right;
public readonly Action<IController, InputState> Convert;
public ControlAdapter(ControllerDefinition type, Controller left, Controller right, Action<IController, InputState> convert)
{
Type = type;
Left = left;
Right = right;
Convert = convert;
}
}
private static readonly ControlAdapter[] Adapters =
{
new ControlAdapter(Joystick, Controller.Joystick, Controller.Joystick, ConvertJoystick),
new ControlAdapter(Paddles, Controller.Paddles, Controller.Paddles, ConvertPaddles),
new ControlAdapter(Keypad, Controller.Keypad, Controller.Keypad, ConvertKeypad),
new ControlAdapter(Driving, Controller.Driving, Controller.Driving, ConvertDriving),
new ControlAdapter(BoosterGrip, Controller.BoosterGrip, Controller.BoosterGrip, ConvertBoosterGrip),
new ControlAdapter(ProLineJoystick, Controller.ProLineJoystick, Controller.ProLineJoystick, ConvertProLineJoystick),
new ControlAdapter(Lightgun, Controller.Lightgun, Controller.Lightgun, ConvertLightgun),
};
private static void ConvertConsoleButtons(IController c, InputState s)
{
s.RaiseInput(0, MachineInput.Reset, c.IsPressed("Reset"));
s.RaiseInput(0, MachineInput.Select, c.IsPressed("Select"));
s.RaiseInput(0, MachineInput.Color, c.IsPressed("BW"));
if (c.IsPressed("Toggle Left Difficulty"))
{
s.RaiseInput(0, MachineInput.LeftDifficulty, c.IsPressed("Toggle Left Difficulty"));
}
if (c.IsPressed("Toggle Right Difficulty"))
{
s.RaiseInput(0, MachineInput.RightDifficulty, c.IsPressed("Toggle Right Difficulty"));
}
}
private static void ConvertConsoleButtons7800(IController c, InputState s)
{
s.RaiseInput(0, MachineInput.Reset, c.IsPressed("Reset"));
s.RaiseInput(0, MachineInput.Select, c.IsPressed("Select"));
s.RaiseInput(0, MachineInput.Color, c.IsPressed("Pause"));
if (c.IsPressed("Toggle Left Difficulty"))
{
s.RaiseInput(0, MachineInput.LeftDifficulty, c.IsPressed("Toggle Left Difficulty"));
}
if (c.IsPressed("Toggle Right Difficulty"))
{
s.RaiseInput(0, MachineInput.RightDifficulty, c.IsPressed("Toggle Right Difficulty"));
}
}
private static void ConvertDirections(IController c, InputState s, int p)
{
string ps = $"P{p + 1} ";
s.RaiseInput(p, MachineInput.Up, c.IsPressed(ps + "Up"));
s.RaiseInput(p, MachineInput.Down, c.IsPressed(ps + "Down"));
s.RaiseInput(p, MachineInput.Left, c.IsPressed(ps + "Left"));
s.RaiseInput(p, MachineInput.Right, c.IsPressed(ps + "Right"));
}
private static void ConvertTrigger(IController c, InputState s, int p)
{
string ps = $"P{p + 1} ";
s.RaiseInput(p, MachineInput.Fire, c.IsPressed(ps + "Trigger"));
}
private static void ConvertJoystick(IController c, InputState s)
{
s.ClearControllerInput();
ConvertConsoleButtons(c, s);
ConvertDirections(c, s, 0);
ConvertDirections(c, s, 1);
ConvertTrigger(c, s, 0);
ConvertTrigger(c, s, 1);
}
private static void ConvertPaddles(IController c, InputState s)
{
s.ClearControllerInput();
ConvertConsoleButtons(c, s);
for (int i = 0; i < 4; i++)
{
string ps = $"P{i + 1} ";
ConvertTrigger(c, s, i);
s.RaisePaddleInput(i, 700000, (int)c.GetFloat(ps + "Trigger"));
}
}
private static void ConvertKeypad(IController c, InputState s)
{
s.ClearControllerInput();
ConvertConsoleButtons(c, s);
for (int i = 0; i < 4; i++)
{
string ps = $"P{i + 1} ";
s.RaiseInput(i, MachineInput.NumPad1, c.IsPressed(ps + "Keypad1"));
s.RaiseInput(i, MachineInput.NumPad2, c.IsPressed(ps + "Keypad2"));
s.RaiseInput(i, MachineInput.NumPad3, c.IsPressed(ps + "Keypad3"));
s.RaiseInput(i, MachineInput.NumPad4, c.IsPressed(ps + "Keypad4"));
s.RaiseInput(i, MachineInput.NumPad5, c.IsPressed(ps + "Keypad5"));
s.RaiseInput(i, MachineInput.NumPad6, c.IsPressed(ps + "Keypad6"));
s.RaiseInput(i, MachineInput.NumPad7, c.IsPressed(ps + "Keypad7"));
s.RaiseInput(i, MachineInput.NumPad8, c.IsPressed(ps + "Keypad8"));
s.RaiseInput(i, MachineInput.NumPad9, c.IsPressed(ps + "Keypad9"));
s.RaiseInput(i, MachineInput.NumPadMult, c.IsPressed(ps + "KeypadA"));
s.RaiseInput(i, MachineInput.NumPad0, c.IsPressed(ps + "Keypad0"));
s.RaiseInput(i, MachineInput.NumPadHash, c.IsPressed(ps + "KeypadP"));
}
}
private static readonly MachineInput[] Drvlut =
{
MachineInput.Driving0,
MachineInput.Driving1,
MachineInput.Driving2,
MachineInput.Driving3
};
private static void ConvertDriving(IController c, InputState s)
{
s.ClearControllerInput();
ConvertConsoleButtons(c, s);
ConvertTrigger(c, s, 0);
ConvertTrigger(c, s, 1);
s.RaiseInput(0, Drvlut[(int)c.GetFloat("P1 Driving")], true);
s.RaiseInput(1, Drvlut[(int)c.GetFloat("P2 Driving")], true);
}
private static void ConvertBoosterGrip(IController c, InputState s)
{
s.ClearControllerInput();
ConvertConsoleButtons(c, s);
ConvertDirections(c, s, 0);
ConvertDirections(c, s, 1);
// weird mapping is intentional
s.RaiseInput(0, MachineInput.Fire, c.IsPressed("P1 Trigger"));
s.RaiseInput(0, MachineInput.Fire2, c.IsPressed("P1 Trigger 2"));
s.RaiseInput(1, MachineInput.Fire2, c.IsPressed("P1 Trigger 3"));
s.RaiseInput(1, MachineInput.Fire, c.IsPressed("P2 Trigger"));
s.RaiseInput(2, MachineInput.Fire2, c.IsPressed("P2 Trigger 2"));
s.RaiseInput(3, MachineInput.Fire2, c.IsPressed("P2 Trigger 3"));
}
private static void ConvertProLineJoystick(IController c, InputState s)
{
s.ClearControllerInput();
ConvertConsoleButtons7800(c, s);
ConvertDirections(c, s, 0);
ConvertDirections(c, s, 1);
s.RaiseInput(0, MachineInput.Fire, c.IsPressed("P1 Trigger"));
s.RaiseInput(0, MachineInput.Fire2, c.IsPressed("P1 Trigger 2"));
s.RaiseInput(1, MachineInput.Fire, c.IsPressed("P2 Trigger"));
s.RaiseInput(1, MachineInput.Fire2, c.IsPressed("P2 Trigger 2"));
}
private static void ConvertLightgun(IController c, InputState s)
{
s.ClearControllerInput();
ConvertConsoleButtons7800(c, s);
ConvertTrigger(c, s, 0);
ConvertTrigger(c, s, 1);
s.RaiseLightgunPos(0, (int)c.GetFloat("P1 VPos"), (int)c.GetFloat("P1 HPos"));
s.RaiseLightgunPos(1, (int)c.GetFloat("P2 VPos"), (int)c.GetFloat("P2 HPos"));
}
public Action<IController, InputState> Convert { get; private set; }
public ControllerDefinition ControlType { get; private set; }
public Atari7800Control(MachineBase mac)
{
var l = mac.InputState.LeftControllerJack;
var r = mac.InputState.RightControllerJack;
foreach (var a in Adapters)
{
if (a.Left == l && a.Right == r)
{
Convert = a.Convert;
ControlType = a.Type;
return;
}
}
throw new Exception($"Couldn't connect Atari 7800 controls \"{l}\" and \"{r}\"");
}
}
}

View File

@ -5,7 +5,7 @@ using System.Collections.Generic;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
public partial class A7800Hawk : IEmulator, IVideoProvider
public partial class A7800Hawk : IEmulator, IVideoProvider, ISoundProvider
{
public IEmulatorServiceProvider ServiceProvider { get; }
@ -76,7 +76,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
GetControllerState(controller);
GetConsoleState(controller);
maria.RunFrame();
if (_islag)
@ -160,15 +160,25 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
if (tia._hsyncCnt == 113 || tia._hsyncCnt == 340)
{
tia.Execute(0);
// even though its clocked seperately, we sample the Pokey here
if (is_pokey) { pokey.sample(); }
}
// tick the m6532 timer, which is still active although not recommended to use
// also it runs off of the cpu cycle timer
// similarly tick the pokey if it is in use
if (cpu_cycle== 4)
{
m6532.Timer.Tick();
}
// the pokey chip ticks at the nominal clock rate (same as maria)
if (is_pokey)
{
pokey.Tick();
}
if (cpu_cycle <= (2 + (slow_access ? 1 : 0)))
{
cpu_is_haltable = true;
@ -284,7 +294,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
public int Frame => _frame;
public string SystemId => "A7800";
public string SystemId => "A78";
public bool DeterministicEmulation { get; set; }
@ -344,5 +354,53 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
};
#endregion
#region Sound provider
private int _spf;
public bool CanProvideAsync => false;
public void SetSyncMode(SyncSoundMode mode)
{
if (mode != SyncSoundMode.Sync)
{
throw new InvalidOperationException("Only Sync mode is supported.");
}
}
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
public void GetSamplesSync(out short[] samples, out int nsamp)
{
short[] ret = new short[_spf * 2];
nsamp = _spf;
tia.GetSamples(ret);
if (is_pokey)
{
short[] ret2 = new short[_spf * 2];
pokey.GetSamples(ret2);
for (int i = 0; i < _spf * 2; i ++)
{
ret[i] += ret2[i];
}
}
samples = ret;
}
public void GetSamplesAsync(short[] samples)
{
throw new NotSupportedException("Async is not available");
}
public void DiscardSamples()
{
tia.AudioClocks = 0;
}
#endregion
}
}

View File

@ -62,6 +62,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
ser.Sync("A7800_control_register", ref A7800_control_register);
ser.Sync("_isPAL", ref _isPAL);
ser.Sync("_spf", ref _spf);
ser.Sync("Maria_regs", ref Maria_regs, false);
ser.Sync("RAM", ref RAM, false);
@ -80,7 +81,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
ser.Sync("small flag", ref small_flag);
ser.Sync("pal kara", ref PAL_Kara);
ser.Sync("Cart RAM", ref cart_RAM);
ser.Sync("pokey", ref pokey);
ser.Sync("is_pokey", ref is_pokey);
ser.Sync("left_toggle", ref left_toggle);
ser.Sync("right_toggle", ref right_toggle);
ser.Sync("left_was_pressed", ref left_was_pressed);

View File

@ -13,8 +13,8 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
isPorted: false,
isReleased: true)]
[ServiceNotApplicable(typeof(ISettable<,>), typeof(IDriveLight))]
public partial class A7800Hawk : IEmulator, ISaveRam, IDebuggable, IStatable, IInputPollable, IRegionable,
ISettable<A7800Hawk.A7800Settings, A7800Hawk.A7800SyncSettings>
public partial class A7800Hawk : IEmulator, ISaveRam, IDebuggable, IStatable, IInputPollable,
IRegionable, IBoardInfo, ISettable<A7800Hawk.A7800Settings, A7800Hawk.A7800SyncSettings>
{
// this register selects between 2600 and 7800 mode in the A7800
// however, we already have a 2600 emulator so this core will only be loading A7800 games
@ -40,7 +40,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
public bool small_flag = false;
public bool PAL_Kara = false;
public int cart_RAM = 0;
public bool pokey = false;
public bool is_pokey = false;
private readonly ITraceable _tracer;
@ -49,6 +49,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
public bool _isPAL;
public M6532 m6532;
public TIA tia;
public Pokey pokey;
public A7800Hawk(CoreComm comm, GameInfo game, byte[] rom, string gameDbFn, object settings, object syncSettings)
{
@ -57,6 +58,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
maria = new Maria();
tia = new TIA();
m6532 = new M6532();
pokey = new Pokey();
cpu = new MOS6502X
{
@ -131,7 +133,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
if (dict.ContainsKey("Pokey"))
{
bool.TryParse(dict["Pokey"], out pokey);
bool.TryParse(dict["Pokey"], out is_pokey);
}
// some games will not function with the high score bios
@ -222,18 +224,22 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
maria.Core = this;
m6532.Core = this;
tia.Core = this;
pokey.Core = this;
ser.Register<IVideoProvider>(this);
ser.Register<ISoundProvider>(tia);
ser.Register<ISoundProvider>(this);
ServiceProvider = ser;
_tracer = new TraceBuffer { Header = cpu.TraceHeader };
ser.Register<ITraceable>(_tracer);
SetupMemoryDomains();
HardReset();
ser.Register<IDisassemblable>(cpu);
HardReset();
}
public string BoardName => mapper.GetType().Name.Replace("Mapper", "");
public DisplayType Region => _isPAL ? DisplayType.PAL : DisplayType.NTSC;
private readonly A7800HawkControllerDeck _controllerDeck;
@ -248,6 +254,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
maria.Reset();
m6532.Reset();
pokey.Reset();
Maria_regs = new byte[0x20];
RAM = new byte[0x1000];
@ -255,6 +262,8 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
cpu_cycle = 0;
_vidbuffer = new int[VirtualWidth * VirtualHeight];
_spf = (_frameHz > 55) ? 740 : 880;
}
private void ExecFetch(ushort addr)

View File

@ -31,6 +31,10 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
return Core.RAM[0x800 + addr & 0x7FF];
}
}
else if (addr < 0x8000 && Core.is_pokey)
{
return Core.pokey.ReadReg(addr & 0xF);
}
else
{
// cartridge and other OPSYS
@ -75,8 +79,12 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
Core.RAM[0x800 + addr & 0x7FF] = value;
}
}
else
else if (addr < 0x8000 && Core.is_pokey)
{
Core.pokey.WriteReg(addr & 0xF, value);
}
else
{
// cartridge and other OPSYS
}
}

View File

@ -55,7 +55,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
}
else
{
if (Core.cart_RAM == 0 && !Core.pokey)
if (Core.cart_RAM == 0 && !Core.is_pokey)
{
// return bank 6
int temp_addr = addr - 0x4000;
@ -80,11 +80,11 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
else if (Core.cart_RAM > 0)
{
// return RAM
if (Core.cart_RAM==8 && addr >= 0x6000)
if (Core.cart_RAM == 8 && addr >= 0x6000)
{
return RAM[addr - 0x6000];
}
else if (Core.cart_RAM==16)
else if (Core.cart_RAM == 16)
{
return RAM[addr - 0x4000];
}
@ -92,12 +92,15 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
// this would coorespond to reading from 0x4000-0x5FFF with only 8k of RAM
// Let's just return FF for now
return 0xFF;
return 0xFF;
}
}
else if (Core.is_pokey)
{
return Core.pokey.ReadReg(addr & 0xF);
}
else
{
// pokey
return 0xFF;
}
}
@ -138,9 +141,9 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
bank = (byte)(value & (Core.small_flag ? 0x3 : 0x7));
}
else if (Core.pokey)
else if (Core.is_pokey)
{
Core.pokey.WriteReg(addr & 0xF, value);
}
else if (Core.cart_RAM > 0)
{

View File

@ -0,0 +1,316 @@
using System;
using BizHawk.Emulation.Common;
using BizHawk.Common.NumberExtensions;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
// emualtes pokey sound chip
// note: A7800 implementation is used only for sound
// potentiometers, keyboard, and IRQs are not used in this context
/*
* Regs 0,2,4,6: Frequency control (divider = value + 1)
* Regs 1,3,5,7: Channel control (Bits 0-3 = volume) (bits 4 - 7 control clocking)
* Reg 8: Control register
*
* Reg A: Random number generator
*
* The registers are write only, except for the RNG none of the things that would return reads are connected
* for now return FF
*/
public class Pokey
{
public A7800Hawk Core { get; set; }
public readonly short[] LocalAudioCycles = new short[2000];
public int AudioClocks;
// state variables
public byte[] Regs = new byte[16];
public int poly4, poly5, poly9, poly17;
public int[] ch_div = new int[4];
public int[] inc_ch = new int[4];
public bool[] ch_out = new bool[4];
public bool[] ch_src = new bool[4];
public int[] ch_vol = new int[4];
public bool high_pass_1;
public bool high_pass_2;
// these are derived values and do not need to be save-stated
public bool[] clock_ch = new bool[4];
public int bit_xor;
public Pokey()
{
}
public void sample()
{
LocalAudioCycles[AudioClocks] += (short)(ch_vol[0] + ch_vol[1] + ch_vol[2] + ch_vol[3]);
AudioClocks++;
}
public void GetSamples(short[] samples)
{
if (AudioClocks > 0)
{
var samples31Khz = new short[AudioClocks]; // mono
for (int i = 0; i < AudioClocks; i++)
{
samples31Khz[i] = LocalAudioCycles[i];
LocalAudioCycles[i] = 0;
}
// convert from 31khz to 44khz
for (var i = 0; i < samples.Length / 2; i++)
{
samples[i * 2] = samples31Khz[(int)(((double)samples31Khz.Length / (double)(samples.Length / 2)) * i)];
samples[(i * 2) + 1] = samples[i * 2];
}
}
AudioClocks = 0;
}
public byte ReadReg(int reg)
{
byte ret = 0xFF;
if (reg==0xA)
{
ret = (byte)(poly17 >> 9);
}
return ret;
}
public void WriteReg(int reg, byte value)
{
Regs[reg] = value;
// this condition resets poly counters and holds them in place
if ((Regs[0xF] & 3) == 0)
{
poly4 = 0xF;
poly5 = 0x1F;
poly17 = 0x1FFFF;
}
}
public void Tick()
{
// clock the 4-5-(9 or 17) bit poly counters
// NOTE: These might not be the exact poly implementation, I just picked a maximal one from wikipedia
// poly 4 and 5 are known to result in:
// poly4 output: 000011101100101
// poly5 output: 1101001100000111001000101011110
if ((Regs[0xF] & 3) != 0)
{
bit_xor = ((poly4) ^ (poly4 >> 1)) & 1;
poly4 = (poly4 >> 1) | (bit_xor << 3);
bit_xor = ((poly5 >> 2) ^ poly5) & 1;
poly5 = (poly5 >> 1) | (bit_xor << 4);
if (Regs[8].Bit(7))
{
// clock only 9 bits of the 17 bit poly
poly9 = poly17 >> 8;
bit_xor = ((poly9 >> 4) ^ poly9) & 1;
poly9 = (poly9 >> 1) | (bit_xor << 8);
poly17 = (poly17 & 0xFF) | (poly9 << 8);
}
else
{
// clock the whole 17 bit poly
bit_xor = ((poly17 >> 3) ^ poly17) & 1;
poly17 = (poly17 >> 1) | (bit_xor << 16);
}
}
clock_ch[0] = clock_ch[1] = clock_ch[2] = clock_ch[3] = false;
// now that we have the poly counters, check which channels to clock
if (Regs[8].Bit(6))
{
clock_ch[0] = true;
clock_ch[2] = true;
}
else
{
inc_ch[0]++;
inc_ch[2]++;
if (Regs[8].Bit(0))
{
if (inc_ch[0] >= 114) { inc_ch[0] = 0; clock_ch[0] = true; }
if (inc_ch[2] >= 114) { inc_ch[2] = 0; clock_ch[2] = true; }
}
else
{
if (inc_ch[0] >= 28) { inc_ch[0] = 0; clock_ch[0] = true; }
if (inc_ch[2] >= 28) { inc_ch[2] = 0; clock_ch[2] = true; }
}
}
if (Regs[8].Bit(4))
{
if (clock_ch[0]) { clock_ch[1] = true; }
}
else
{
inc_ch[1]++;
if (Regs[8].Bit(0))
{
if (inc_ch[1] >= 114) { inc_ch[1] = 0; clock_ch[1] = true; }
}
else
{
if (inc_ch[1] >= 28) { inc_ch[1] = 0; clock_ch[1] = true; }
}
}
if (Regs[8].Bit(3))
{
if (clock_ch[2]) { clock_ch[3] = true; }
}
else
{
inc_ch[3]++;
if (Regs[8].Bit(0))
{
if (inc_ch[3] >= 114) { inc_ch[3] = 0; clock_ch[3] = true; }
}
else
{
if (inc_ch[3] >= 28) { inc_ch[3] = 0; clock_ch[3] = true; }
}
}
// first update the high pass filter latch
if (clock_ch[2] && Regs[8].Bit(2)) { high_pass_1 = ch_out[0]; }
if (clock_ch[3] && Regs[8].Bit(1)) { high_pass_2 = ch_out[1]; }
// now we know what channels to clock, execute the cycles
for (int i = 0; i < 4; i++) {
if (clock_ch[i])
{
ch_div[i]++;
if (ch_div[i] >= (Regs[i * 2] + 1))
{
ch_div[i] = 0;
// select the next source based on the channel control register
if (Regs[i * 2 + 1].Bit(4))
{
// forced output always on (used with volume modulation)
ch_out[i] = true;
}
else if ((Regs[i * 2 + 1] & 0xF0) == 0)
{
// 17 bit poly then 5 bit poly
if (ch_src[i])
{
ch_out[i] = poly5.Bit(4);
}
else
{
ch_out[i] = poly5.Bit(16);
}
}
else if (((Regs[i * 2 + 1] & 0xF0) == 0x20) || ((Regs[i * 2 + 1] & 0xF0) == 0x60))
{
// 5 bit poly
if (ch_src[i])
{
ch_out[i] = poly5.Bit(4);
}
}
else if ((Regs[i * 2 + 1] & 0xF0) == 0x40)
{
// 4 bit poly then 5 bit poly
if (ch_src[i])
{
ch_out[i] = poly5.Bit(4);
}
else
{
ch_out[i] = poly4.Bit(3);
}
}
else if ((Regs[i * 2 + 1] & 0xF0) == 0x80)
{
// 17 bit poly
if (ch_src[i])
{
ch_out[i] = poly17.Bit(16);
}
}
else if ((Regs[i * 2 + 1] & 0xF0) == 0xA0)
{
// tone
if (ch_src[i])
{
ch_out[i] = !ch_out[i];
}
}
else if ((Regs[i * 2 + 1] & 0xF0) == 0xC0)
{
// 4 bit poly
if (ch_src[i])
{
ch_out[i] = poly4.Bit(3);
}
}
ch_src[i] = !ch_src[i];
// for channels 1 and 2, an optional high pass filter exists
// the filter is just a flip flop and xor combo
if ((i == 0 && Regs[8].Bit(2)) || (i == 1 && Regs[8].Bit(1)))
{
if (i == 0) { ch_vol[0] = (ch_out[0] ^ high_pass_1) ? (Regs[1] & 0xF) : 0; }
if (i == 1) { ch_vol[1] = (ch_out[1] ^ high_pass_2) ? (Regs[3] & 0xF) : 0; }
}
else
{
ch_vol[i] = (ch_out[i] ? (Regs[i * 2 + 1] & 0xF) : 0) * 70;
}
}
}
}
}
public void Reset()
{
Regs = new byte[16];
poly4 = 0xF;
poly5 = 0x1F;
poly17 = 0x1FFFF;
}
public void SyncState(Serializer ser)
{
ser.BeginSection("Pokey");
ser.Sync("Regs", ref Regs, false);
ser.Sync("poly4", ref poly4);
ser.Sync("poly5", ref poly5);
ser.Sync("poly9", ref poly9);
ser.Sync("poly17", ref poly17);
ser.Sync("ch_div", ref ch_div, false);
ser.Sync("inc_ch", ref inc_ch, false);
ser.Sync("ch_out", ref ch_out, false);
ser.Sync("ch_src", ref ch_src, false);
ser.Sync("ch_vol", ref ch_vol, false);
ser.Sync("high_pass_1", ref high_pass_1);
ser.Sync("high_pass_2", ref high_pass_2);
ser.EndSection();
}
}
}

View File

@ -6,20 +6,17 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
// Emulates the TIA
public partial class TIA : ISoundProvider
public partial class TIA
{
public A7800Hawk Core { get; set; }
public byte BusState;
private bool _doTicks;
private int _spf;
public int AudioClocks; // not savestated
public int _hsyncCnt;
private int _capChargeStart;
private bool _capCharging;
public int AudioClocks; // not savestated
private readonly Audio[] AUD = { new Audio(), new Audio() };
@ -33,7 +30,6 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
_capChargeStart = 0;
_capCharging = false;
AudioClocks = 0;
_spf = (Core._frameHz > 55) ? 740 : 880;
_doTicks = false;
}
@ -45,11 +41,32 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
AudioClocks++;
}
public void GetSamples(short[] samples)
{
if (AudioClocks > 0)
{
var samples31Khz = new short[AudioClocks]; // mono
for (int i = 0; i < AudioClocks; i++)
{
samples31Khz[i] = LocalAudioCycles[i];
LocalAudioCycles[i] = 0;
}
// convert from 31khz to 44khz
for (var i = 0; i < samples.Length / 2; i++)
{
samples[i * 2] = samples31Khz[(int)(((double)samples31Khz.Length / (double)(samples.Length / 2)) * i)];
samples[(i * 2) + 1] = samples[i * 2];
}
}
AudioClocks = 0;
}
public byte ReadMemory(ushort addr, bool peek)
{
var maskedAddr = (ushort)(addr & 0x000F);
byte coll = 0;
int mask = 0;
if (maskedAddr == 0x00) // CXM0P
{

View File

@ -1,63 +0,0 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
public partial class TIA : ISoundProvider
{
public bool CanProvideAsync => false;
public void SetSyncMode(SyncSoundMode mode)
{
if (mode != SyncSoundMode.Sync)
{
throw new InvalidOperationException("Only Sync mode is supported.");
}
}
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
public void GetSamplesSync(out short[] samples, out int nsamp)
{
short[] ret = new short[_spf * 2];
nsamp = _spf;
GetSamples(ret);
samples = ret;
}
public void GetSamplesAsync(short[] samples)
{
throw new NotSupportedException("Async is not available");
}
public void DiscardSamples()
{
AudioClocks = 0;
}
// Exposing this as GetSamplesAsync would allow this to provide async sound
// However, it does nothing special for async sound so I don't see a point
private void GetSamples(short[] samples)
{
if (AudioClocks > 0)
{
var samples31Khz = new short[AudioClocks]; // mono
for (int i = 0; i < AudioClocks; i++)
{
samples31Khz[i] = LocalAudioCycles[i];
LocalAudioCycles[i] = 0;
}
// convert from 31khz to 44khz
for (var i = 0; i < samples.Length / 2; i++)
{
samples[i * 2] = samples31Khz[(int)(((double)samples31Khz.Length / (double)(samples.Length / 2)) * i)];
samples[(i * 2) + 1] = samples[i * 2];
}
}
AudioClocks = 0;
}
}
}

View File

@ -14,7 +14,6 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
ser.Sync("Bus_State", ref BusState);
ser.Sync("Ticks", ref _doTicks);
ser.Sync("_spf", ref _spf);
// some of these things weren't in the state because they weren't needed if
// states were always taken at frame boundaries

View File

@ -22,10 +22,6 @@ namespace BizHawk.Emulation.Cores.Atari.Lynx
_ser.Serialize(writer, s);
// write extra copy of stuff we don't use
writer.WriteLine();
writer.WriteLine("Frame {0}", Frame);
////Console.WriteLine(BizHawk.Common.BufferExtensions.BufferExtensions.HashSHA1(SaveStateBinary()));
}

View File

@ -30,6 +30,10 @@ namespace BizHawk.Emulation.Cores.ColecoVision
Name = "ColecoVision Basic Controller",
BoolButtons = Port1.Definition.BoolButtons
.Concat(Port2.Definition.BoolButtons)
.Concat(new[]
{
"Power", "Reset"
})
.ToList()
};

View File

@ -38,7 +38,7 @@ namespace BizHawk.Emulation.Cores.ColecoVision
public byte Read(IController c, bool left_mode, int wheel)
{
return 0; // needs checking
return 0x7F; // needs checking
}
public ControllerDefinition Definition { get; }

View File

@ -12,36 +12,36 @@ namespace BizHawk.Emulation.Cores.ColecoVision
{
return new Dictionary<string, RegisterValue>
{
["A"] = _cpu.RegisterA,
["AF"] = _cpu.RegisterAF,
["B"] = _cpu.RegisterB,
["BC"] = _cpu.RegisterBC,
["C"] = _cpu.RegisterC,
["D"] = _cpu.RegisterD,
["DE"] = _cpu.RegisterDE,
["E"] = _cpu.RegisterE,
["F"] = _cpu.RegisterF,
["H"] = _cpu.RegisterH,
["HL"] = _cpu.RegisterHL,
["I"] = _cpu.RegisterI,
["IX"] = _cpu.RegisterIX,
["IY"] = _cpu.RegisterIY,
["L"] = _cpu.RegisterL,
["PC"] = _cpu.RegisterPC,
["R"] = _cpu.RegisterR,
["Shadow AF"] = _cpu.RegisterShadowAF,
["Shadow BC"] = _cpu.RegisterShadowBC,
["Shadow DE"] = _cpu.RegisterShadowDE,
["Shadow HL"] = _cpu.RegisterShadowHL,
["SP"] = _cpu.RegisterSP,
["Flag C"] = _cpu.RegisterF.Bit(0),
["Flag N"] = _cpu.RegisterF.Bit(1),
["Flag P/V"] = _cpu.RegisterF.Bit(2),
["Flag 3rd"] = _cpu.RegisterF.Bit(3),
["Flag H"] = _cpu.RegisterF.Bit(4),
["Flag 5th"] = _cpu.RegisterF.Bit(5),
["Flag Z"] = _cpu.RegisterF.Bit(6),
["Flag S"] = _cpu.RegisterF.Bit(7)
["A"] = _cpu.Regs[_cpu.A],
["AF"] = _cpu.Regs[_cpu.F] + (_cpu.Regs[_cpu.A] << 8),
["B"] = _cpu.Regs[_cpu.B],
["BC"] = _cpu.Regs[_cpu.C] + (_cpu.Regs[_cpu.B] << 8),
["C"] = _cpu.Regs[_cpu.C],
["D"] = _cpu.Regs[_cpu.D],
["DE"] = _cpu.Regs[_cpu.E] + (_cpu.Regs[_cpu.D] << 8),
["E"] = _cpu.Regs[_cpu.E],
["F"] = _cpu.Regs[_cpu.F],
["H"] = _cpu.Regs[_cpu.H],
["HL"] = _cpu.Regs[_cpu.L] + (_cpu.Regs[_cpu.H] << 8),
["I"] = _cpu.Regs[_cpu.I],
["IX"] = _cpu.Regs[_cpu.Ixl] + (_cpu.Regs[_cpu.Ixh] << 8),
["IY"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8),
["L"] = _cpu.Regs[_cpu.L],
["PC"] = _cpu.Regs[_cpu.PCl] + (_cpu.Regs[_cpu.PCh] << 8),
["R"] = _cpu.Regs[_cpu.R],
["Shadow AF"] = _cpu.Regs[_cpu.F_s] + (_cpu.Regs[_cpu.A_s] << 8),
["Shadow BC"] = _cpu.Regs[_cpu.C_s] + (_cpu.Regs[_cpu.B_s] << 8),
["Shadow DE"] = _cpu.Regs[_cpu.E_s] + (_cpu.Regs[_cpu.D_s] << 8),
["Shadow HL"] = _cpu.Regs[_cpu.L_s] + (_cpu.Regs[_cpu.H_s] << 8),
["SP"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8),
["Flag C"] = _cpu.FlagC,
["Flag N"] = _cpu.FlagN,
["Flag P/V"] = _cpu.FlagP,
["Flag 3rd"] = _cpu.Flag3,
["Flag H"] = _cpu.FlagH,
["Flag 5th"] = _cpu.Flag5,
["Flag Z"] = _cpu.FlagZ,
["Flag S"] = _cpu.FlagS
};
}
@ -52,70 +52,82 @@ namespace BizHawk.Emulation.Cores.ColecoVision
default:
throw new InvalidOperationException();
case "A":
_cpu.RegisterA = (byte)value;
_cpu.Regs[_cpu.A] = (ushort)value;
break;
case "AF":
_cpu.RegisterAF = (byte)value;
_cpu.Regs[_cpu.F] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.A] = (ushort)(value & 0xFF00);
break;
case "B":
_cpu.RegisterB = (byte)value;
_cpu.Regs[_cpu.B] = (ushort)value;
break;
case "BC":
_cpu.RegisterBC = (byte)value;
_cpu.Regs[_cpu.C] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.B] = (ushort)(value & 0xFF00);
break;
case "C":
_cpu.RegisterC = (byte)value;
_cpu.Regs[_cpu.C] = (ushort)value;
break;
case "D":
_cpu.RegisterD = (byte)value;
_cpu.Regs[_cpu.D] = (ushort)value;
break;
case "DE":
_cpu.RegisterDE = (byte)value;
_cpu.Regs[_cpu.E] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.D] = (ushort)(value & 0xFF00);
break;
case "E":
_cpu.RegisterE = (byte)value;
_cpu.Regs[_cpu.E] = (ushort)value;
break;
case "F":
_cpu.RegisterF = (byte)value;
_cpu.Regs[_cpu.F] = (ushort)value;
break;
case "H":
_cpu.RegisterH = (byte)value;
_cpu.Regs[_cpu.H] = (ushort)value;
break;
case "HL":
_cpu.RegisterHL = (byte)value;
_cpu.Regs[_cpu.L] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.H] = (ushort)(value & 0xFF00);
break;
case "I":
_cpu.RegisterI = (byte)value;
_cpu.Regs[_cpu.I] = (ushort)value;
break;
case "IX":
_cpu.RegisterIX = (byte)value;
_cpu.Regs[_cpu.Ixl] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.Ixh] = (ushort)(value & 0xFF00);
break;
case "IY":
_cpu.RegisterIY = (byte)value;
_cpu.Regs[_cpu.Iyl] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.Iyh] = (ushort)(value & 0xFF00);
break;
case "L":
_cpu.RegisterL = (byte)value;
_cpu.Regs[_cpu.L] = (ushort)value;
break;
case "PC":
_cpu.RegisterPC = (ushort)value;
_cpu.Regs[_cpu.PCl] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.PCh] = (ushort)(value & 0xFF00);
break;
case "R":
_cpu.RegisterR = (byte)value;
_cpu.Regs[_cpu.R] = (ushort)value;
break;
case "Shadow AF":
_cpu.RegisterShadowAF = (byte)value;
_cpu.Regs[_cpu.F_s] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.A_s] = (ushort)(value & 0xFF00);
break;
case "Shadow BC":
_cpu.RegisterShadowBC = (byte)value;
_cpu.Regs[_cpu.C_s] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.B_s] = (ushort)(value & 0xFF00);
break;
case "Shadow DE":
_cpu.RegisterShadowDE = (byte)value;
_cpu.Regs[_cpu.E_s] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.D_s] = (ushort)(value & 0xFF00);
break;
case "Shadow HL":
_cpu.RegisterShadowHL = (byte)value;
_cpu.Regs[_cpu.L_s] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.H_s] = (ushort)(value & 0xFF00);
break;
case "SP":
_cpu.RegisterSP = (byte)value;
_cpu.Regs[_cpu.SPl] = (ushort)(value & 0xFF);
_cpu.Regs[_cpu.SPh] = (ushort)(value & 0xFF00);
break;
}
}

View File

@ -12,16 +12,30 @@ namespace BizHawk.Emulation.Cores.ColecoVision
public void FrameAdvance(IController controller, bool render, bool renderSound)
{
_controller = controller;
_cpu.Debug = _tracer.Enabled;
// NOTE: Need to research differences between reset and power cycle
if (_controller.IsPressed("Power"))
{
HardReset();
}
if (_controller.IsPressed("Reset"))
{
SoftReset();
}
_frame++;
_isLag = true;
PSG.BeginFrame(_cpu.TotalExecutedCycles);
if (_cpu.Debug && _cpu.Logger == null) // TODO, lets not do this on each frame. But lets refactor CoreComm/CoreComm first
if (_tracer.Enabled)
{
_cpu.Logger = (s) => _tracer.Put(s);
_cpu.TraceCallback = s => _tracer.Put(s);
}
else
{
_cpu.TraceCallback = null;
}
byte tempRet1 = ControllerDeck.ReadPort1(controller, true, true);
byte tempRet2 = ControllerDeck.ReadPort2(controller, true, true);

View File

@ -7,53 +7,52 @@ namespace BizHawk.Emulation.Cores.ColecoVision
{
public partial class ColecoVision : IStatable
{
public bool BinarySaveStatesPreferred => false;
public bool BinarySaveStatesPreferred
{
get { return true; }
}
public void SaveStateText(TextWriter writer)
{
SyncState(new Serializer(writer));
}
public void LoadStateText(TextReader reader)
{
SyncState(new Serializer(reader));
}
public void SaveStateBinary(BinaryWriter bw)
{
SyncState(Serializer.CreateBinaryWriter(bw));
SyncState(new Serializer(bw));
}
public void LoadStateBinary(BinaryReader br)
{
SyncState(Serializer.CreateBinaryReader(br));
}
public void SaveStateText(TextWriter tw)
{
SyncState(Serializer.CreateTextWriter(tw));
}
public void LoadStateText(TextReader tr)
{
SyncState(Serializer.CreateTextReader(tr));
SyncState(new Serializer(br));
}
public byte[] SaveStateBinary()
{
if (_stateBuffer == null)
{
var stream = new MemoryStream();
var writer = new BinaryWriter(stream);
SaveStateBinary(writer);
_stateBuffer = stream.ToArray();
writer.Close();
return _stateBuffer;
}
else
{
var stream = new MemoryStream(_stateBuffer);
var writer = new BinaryWriter(stream);
SaveStateBinary(writer);
writer.Close();
return _stateBuffer;
}
MemoryStream ms = new MemoryStream();
BinaryWriter bw = new BinaryWriter(ms);
SaveStateBinary(bw);
bw.Flush();
return ms.ToArray();
}
private void SyncState(Serializer ser)
{
ser.BeginSection("Coleco");
byte[] core = null;
if (ser.IsWriter)
{
var ms = new MemoryStream();
ms.Close();
core = ms.ToArray();
}
_cpu.SyncState(ser);
ser.BeginSection("Coleco");
_vdp.SyncState(ser);
PSG.SyncState(ser);
ser.Sync("RAM", ref _ram, false);

View File

@ -1,6 +1,6 @@
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components;
using BizHawk.Emulation.Cores.Components.Z80;
using BizHawk.Emulation.Cores.Components.Z80A;
namespace BizHawk.Emulation.Cores.ColecoVision
{
@ -24,6 +24,7 @@ namespace BizHawk.Emulation.Cores.ColecoVision
_cpu = new Z80A
{
FetchMemory = ReadMemory,
ReadMemory = ReadMemory,
WriteMemory = WriteMemory,
ReadHardware = ReadPort,
@ -53,7 +54,7 @@ namespace BizHawk.Emulation.Cores.ColecoVision
SetupMemoryDomains();
_tracer.Header = _cpu.TraceHeader;
ser.Register<IDisassemblable>(new Disassembler());
ser.Register<IDisassemblable>(_cpu);
ser.Register<ITraceable>(_tracer);
}
@ -223,5 +224,17 @@ namespace BizHawk.Emulation.Cores.ColecoVision
////Console.WriteLine("Unhandled write at {0:X4}:{1:X2}", addr, value);
}
private void HardReset()
{
PSG.Reset();
_cpu.Reset();
}
private void SoftReset()
{
PSG.Reset();
_cpu.Reset();
}
}
}
}

View File

@ -2,7 +2,7 @@
using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components.Z80;
using BizHawk.Emulation.Cores.Components.Z80A;
namespace BizHawk.Emulation.Cores.ColecoVision
{
@ -54,14 +54,17 @@ namespace BizHawk.Emulation.Cores.ColecoVision
Cpu.NonMaskableInterrupt = true;
}
Cpu.ExecuteCycles(228);
Cpu.Interrupt = false;
for (int i = 0; i < 228; i++)
{
Cpu.ExecuteOne();
}
Cpu.FlagI = false;
if (Int_pending && scanLine==50)
{
if (EnableInterrupts)
{
Cpu.Interrupt = true;
Cpu.FlagI = true;
Int_pending = false;
}
}

View File

@ -22,10 +22,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
ser.Serialize(writer, s);
// write extra copy of stuff we don't use
writer.WriteLine();
writer.WriteLine("Frame {0}", Frame);
//Console.WriteLine(BizHawk.Common.BufferExtensions.BufferExtensions.HashSHA1(SaveStateBinary()));
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
public partial class Gameboy : ILinkable
{
public bool LinkConnected { get; private set; }
}
}

View File

@ -14,10 +14,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
var s = SaveState();
ser.Serialize(writer, s);
// write extra copy of stuff we don't use
writer.WriteLine();
writer.WriteLine("Frame {0}", Frame);
}
public void LoadStateText(TextReader reader)

View File

@ -19,7 +19,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
[ServiceNotApplicable(typeof(IDriveLight), typeof(IDriveLight))]
public partial class Gameboy : IEmulator, IVideoProvider, ISoundProvider, ISaveRam, IStatable, IInputPollable, ICodeDataLogger,
IBoardInfo, IDebuggable, ISettable<Gameboy.GambatteSettings, Gameboy.GambatteSyncSettings>,
IGameboyCommon
IGameboyCommon, ICycleTiming, ILinkable
{
[CoreConstructor("GB", "GBC")]
public Gameboy(CoreComm comm, GameInfo game, byte[] file, object settings, object syncSettings, bool deterministic)
@ -253,7 +253,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
/// number of extra cycles we overran in the last frame
/// </summary>
private uint frameOverflow = 0;
public ulong CycleCount => _cycleCount;
public long CycleCount => (long)_cycleCount;
public double ClockRate => TICKSPERSECOND;
#endregion
@ -496,6 +497,32 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
}
}
GambattePrinter printer;
/// <summary>
/// set up Printer callback
/// </summary>
public void SetPrinterCallback(PrinterCallback callback)
{
// Copying SetScanlineCallback for this check, I assume this is still a bug somewhere
if (GambatteState == IntPtr.Zero)
{
return; // not sure how this is being reached. tried the debugger...
}
if (callback != null)
{
printer = new GambattePrinter(this, callback);
LinkConnected = true;
}
else
{
LinkConnected = false;
printer.Disconnect();
printer = null;
}
}
LibGambatte.ScanlineCallback scanlinecb;
ScanlineCallback endofframecallback;

View File

@ -27,10 +27,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
cablediscosignal = _cablediscosignal
};
ser.Serialize(writer, s);
// write extra copy of stuff we don't use
// is this needed anymore??
writer.WriteLine();
writer.WriteLine("Frame {0}", Frame);
}
public void LoadStateText(TextReader reader)

View File

@ -0,0 +1,314 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
/// <summary>
/// Emulate the gameboy printer in managed code
/// </summary>
public class GambattePrinter
{
// A loose c->c# port of SameBoy's printer code
enum CommandState : byte
{
GB_PRINTER_COMMAND_MAGIC1,
GB_PRINTER_COMMAND_MAGIC2,
GB_PRINTER_COMMAND_ID,
GB_PRINTER_COMMAND_COMPRESSION,
GB_PRINTER_COMMAND_LENGTH_LOW,
GB_PRINTER_COMMAND_LENGTH_HIGH,
GB_PRINTER_COMMAND_DATA,
GB_PRINTER_COMMAND_CHECKSUM_LOW,
GB_PRINTER_COMMAND_CHECKSUM_HIGH,
GB_PRINTER_COMMAND_ACTIVE,
GB_PRINTER_COMMAND_STATUS,
}
enum CommandID : byte
{
GB_PRINTER_INIT_COMMAND = 1,
GB_PRINTER_START_COMMAND = 2,
GB_PRINTER_DATA_COMMAND = 4,
GB_PRINTER_NOP_COMMAND = 0xF,
}
const int GB_PRINTER_MAX_COMMAND_LENGTH = 0x280;
const int GB_PRINTER_DATA_SIZE = 0x280;
const ushort SerialIRQAddress = 0x58;
Gameboy gb;
PrinterCallback callback;
LibGambatte.LinkCallback linkCallback;
CommandState command_state;
CommandID command_id;
bool compression;
ushort length_left;
byte[] command_data = new byte[GB_PRINTER_MAX_COMMAND_LENGTH];
ushort command_length;
ushort checksum;
byte status;
byte[] image = new byte[160 * 200];
ushort image_offset;
byte compression_run_lenth;
bool compression_run_is_compressed;
public GambattePrinter(Gameboy gb, PrinterCallback callback)
{
this.gb = gb;
this.callback = callback;
linkCallback = OnSerial;
LibGambatte.gambatte_setlinkcallback(gb.GambatteState, linkCallback);
// connect the cable
LibGambatte.gambatte_linkstatus(gb.GambatteState, 259);
}
public void Disconnect()
{
if (gb.GambatteState != IntPtr.Zero)
LibGambatte.gambatte_setlinkcallback(gb.GambatteState, null);
}
void OnSerial()
{
if (LibGambatte.gambatte_linkstatus(gb.GambatteState, 256) != 0) // ClockTrigger
{
LibGambatte.gambatte_linkstatus(gb.GambatteState, 257); // ack
byte output = HandleSerial((byte)LibGambatte.gambatte_linkstatus(gb.GambatteState, 258)); // GetOut
LibGambatte.gambatte_linkstatus(gb.GambatteState, output); // ShiftIn
}
}
byte HandleSerial(byte byte_received)
{
byte byte_to_send = 0;
switch (command_state)
{
case CommandState.GB_PRINTER_COMMAND_MAGIC1:
if (byte_received != 0x88)
{
return byte_to_send;
}
status &= 254;
command_length = 0;
checksum = 0;
break;
case CommandState.GB_PRINTER_COMMAND_MAGIC2:
if (byte_received != 0x33)
{
if (byte_received != 0x88)
{
command_state = CommandState.GB_PRINTER_COMMAND_MAGIC1;
}
return byte_to_send;
}
break;
case CommandState.GB_PRINTER_COMMAND_ID:
command_id = (CommandID)(byte_received & 0xF);
break;
case CommandState.GB_PRINTER_COMMAND_COMPRESSION:
compression = (byte_received & 1) != 0;
break;
case CommandState.GB_PRINTER_COMMAND_LENGTH_LOW:
length_left = byte_received;
break;
case CommandState.GB_PRINTER_COMMAND_LENGTH_HIGH:
length_left |= (ushort)((byte_received & 3) << 8);
break;
case CommandState.GB_PRINTER_COMMAND_DATA:
if (command_length != GB_PRINTER_MAX_COMMAND_LENGTH)
{
if (compression)
{
if (compression_run_lenth == 0)
{
compression_run_is_compressed = (byte_received & 0x80) != 0;
compression_run_lenth = (byte)((byte_received & 0x7F) + 1 + (compression_run_is_compressed ? 1 : 0));
}
else if (compression_run_is_compressed)
{
while (compression_run_lenth > 0)
{
command_data[command_length++] = byte_received;
compression_run_lenth--;
if (command_length == GB_PRINTER_MAX_COMMAND_LENGTH)
{
compression_run_lenth = 0;
}
}
}
else
{
command_data[command_length++] = byte_received;
compression_run_lenth--;
}
}
else
{
command_data[command_length++] = byte_received;
}
}
length_left--;
break;
case CommandState.GB_PRINTER_COMMAND_CHECKSUM_LOW:
checksum ^= byte_received;
break;
case CommandState.GB_PRINTER_COMMAND_CHECKSUM_HIGH:
checksum ^= (ushort)(byte_received << 8);
if (checksum != 0)
{
status |= 1; /* Checksum error*/
command_state = CommandState.GB_PRINTER_COMMAND_MAGIC1;
return byte_to_send;
}
break;
case CommandState.GB_PRINTER_COMMAND_ACTIVE:
byte_to_send = 0x81;
break;
case CommandState.GB_PRINTER_COMMAND_STATUS:
if (((int)command_id & 0xF) == (byte)CommandID.GB_PRINTER_INIT_COMMAND)
{
/* Games expect INIT commands to return 0? */
byte_to_send = 0;
}
else
{
byte_to_send = status;
}
/* Printing is done instantly, but let the game recieve a 6 (Printing) status at least once, for compatibility */
if (status == 6)
{
status = 4; /* Done */
}
command_state = CommandState.GB_PRINTER_COMMAND_MAGIC1;
HandleCommand();
return byte_to_send;
}
if (command_state >= CommandState.GB_PRINTER_COMMAND_ID && command_state < CommandState.GB_PRINTER_COMMAND_CHECKSUM_LOW)
{
checksum += byte_received;
}
if (command_state != CommandState.GB_PRINTER_COMMAND_DATA)
{
command_state++;
}
if (command_state == CommandState.GB_PRINTER_COMMAND_DATA)
{
if (length_left == 0)
{
command_state++;
}
}
return byte_to_send;
}
void HandleCommand()
{
switch (command_id)
{
case CommandID.GB_PRINTER_INIT_COMMAND:
status = 0;
image_offset = 0;
break;
case CommandID.GB_PRINTER_START_COMMAND:
if (command_length == 4)
{
status = 6; /* Printing */
uint[] outputImage = new uint[image_offset];
int palette = command_data[2];
uint[] colors = new uint[] {
0xFFFFFFFFU,
0xFFAAAAAAU,
0xFF555555U,
0xFF000000U
};
for (int i = 0; i < image_offset; i++)
{
outputImage[i] = colors[(palette >> (image[i] * 2)) & 3];
}
if (callback != null)
{
// The native-friendly callback almost seems silly now :P
unsafe
{
fixed (uint* imagePtr = outputImage)
{
callback((IntPtr)imagePtr, (byte)(image_offset / 160),
(byte)(command_data[1] >> 4), (byte)(command_data[1] & 7),
(byte)(command_data[3] & 0x7F));
}
}
}
image_offset = 0;
}
break;
case CommandID.GB_PRINTER_DATA_COMMAND:
if (command_length == GB_PRINTER_DATA_SIZE)
{
image_offset %= (ushort)image.Length;
status = 8; /* Received 0x280 bytes */
int data_index = 0;
for (int row = 2; row > 0; row--)
{
for (int tile_x = 0; tile_x < 160 / 8; tile_x++)
{
for (int y = 0; y < 8; y++, data_index += 2)
{
for (int x_pixel = 0; x_pixel < 8; x_pixel++)
{
image[image_offset + tile_x * 8 + x_pixel + y * 160] =
(byte)((command_data[data_index] >> 7) | ((command_data[data_index + 1] >> 7) << 1));
command_data[data_index] <<= 1;
command_data[data_index + 1] <<= 1;
}
}
}
image_offset += 8 * 160;
}
}
break;
default:
break;
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More