Merge pull request #681 from vadosnaprimer/gpgx32

old gpgx is back!
This commit is contained in:
feos 2016-08-13 22:49:04 +03:00 committed by GitHub
commit 8721d0ef83
213 changed files with 109774 additions and 1294 deletions

View File

@ -802,6 +802,10 @@ namespace BizHawk.Client.Common
nextEmulator = new Octoshock(nextComm, null, null, rom.FileData, GetCoreSettings<Octoshock>(), GetCoreSyncSettings<Octoshock>());
nextEmulator.CoreComm.RomStatusDetails = "PSX etc.";
break;
case "GEN":
// discard "Genplus-gx64", auto-added due to implementing IEmulator
core = CoreInventory.Instance["GEN", "Genplus-gx"];
break;
}
if (core != null)

View File

@ -881,6 +881,46 @@
</Compile>
<Compile Include="Consoles\Sega\gpgx\GPGXControlConverter.cs" />
<Compile Include="Consoles\Sega\gpgx\LibGPGX.cs" />
<Compile Include="Consoles\Sega\gpgx64\GenDbgHlp.cs" />
<Compile Include="Consoles\Sega\gpgx64\GPGX.cs" />
<Compile Include="Consoles\Sega\gpgx64\GPGX.ICodeDataLogger.cs">
<DependentUpon>GPGX.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\gpgx64\GPGX.IDebuggable.cs">
<DependentUpon>GPGX.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\gpgx64\GPGX.IDisassembler.cs">
<DependentUpon>GPGX.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\gpgx64\GPGX.IDriveLight.cs">
<DependentUpon>GPGX.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\gpgx64\GPGX.IEmulator.cs">
<DependentUpon>GPGX.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\gpgx64\GPGX.IInputPollable.cs">
<DependentUpon>GPGX.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\gpgx64\GPGX.IMemoryDomains.cs">
<DependentUpon>GPGX.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\gpgx64\GPGX.ISaveRam.cs">
<DependentUpon>GPGX.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\gpgx64\GPGX.ISettable.cs">
<DependentUpon>GPGX.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\gpgx64\GPGX.IStatable.cs">
<DependentUpon>GPGX.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\gpgx64\GPGX.ITraceable.cs">
<DependentUpon>GPGX.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\gpgx64\GPGX.IVideoProvider.cs">
<DependentUpon>GPGX.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\gpgx64\GPGXControlConverter.cs" />
<Compile Include="Consoles\Sega\gpgx64\LibGPGX.cs" />
<Compile Include="Consoles\Sega\Saturn\FilePiping.cs" />
<Compile Include="Consoles\Sega\Saturn\LibYabause.cs" />
<Compile Include="Consoles\Sega\Saturn\Yabause.cs" />

View File

@ -10,8 +10,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public void SetCDL(CodeDataLog cdl)
{
CDL = cdl;
if (cdl == null) Core.gpgx_set_cd_callback(null);
else Core.gpgx_set_cd_callback(CDCallback);
if (cdl == null) LibGPGX.gpgx_set_cd_callback(null);
else LibGPGX.gpgx_set_cd_callback(CDCallback);
}
public void NewCDL(CodeDataLog cdl)

View File

@ -10,9 +10,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
{
LibGPGX.RegisterInfo[] regs = new LibGPGX.RegisterInfo[Core.gpgx_getmaxnumregs()];
LibGPGX.RegisterInfo[] regs = new LibGPGX.RegisterInfo[LibGPGX.gpgx_getmaxnumregs()];
int n = Core.gpgx_getregs(regs);
int n = LibGPGX.gpgx_getregs(regs);
if (n > regs.Length)
throw new InvalidOperationException("A buffer overrun has occured!");
var ret = new Dictionary<string, RegisterValue>();
@ -63,7 +63,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
private void RefreshMemCallbacks()
{
Core.gpgx_set_mem_callback(
LibGPGX.gpgx_set_mem_callback(
MemoryCallbacks.HasReads ? ReadCallback : null,
MemoryCallbacks.HasWrites ? WriteCallback : null,
MemoryCallbacks.HasExecutes ? ExecCallback : null);
@ -71,7 +71,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
private void KillMemCallbacks()
{
Core.gpgx_set_mem_callback(null, null, null);
LibGPGX.gpgx_set_mem_callback(null, null, null);
}
}
}

View File

@ -29,26 +29,26 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
public void FrameAdvance(bool render, bool rendersound = true)
{
if (Controller["Reset"])
Core.gpgx_reset(false);
LibGPGX.gpgx_reset(false);
if (Controller["Power"])
Core.gpgx_reset(true);
LibGPGX.gpgx_reset(true);
// this shouldn't be needed, as nothing has changed
// if (!Core.gpgx_get_control(input, inputsize))
// throw new Exception("gpgx_get_control() failed!");
// do we really have to get each time? nothing has changed
if (!LibGPGX.gpgx_get_control(input, inputsize))
throw new Exception("gpgx_get_control() failed!");
ControlConverter.ScreenWidth = vwidth;
ControlConverter.ScreenHeight = vheight;
ControlConverter.Convert(Controller, input);
if (!Core.gpgx_put_control(input, inputsize))
if (!LibGPGX.gpgx_put_control(input, inputsize))
throw new Exception("gpgx_put_control() failed!");
IsLagFrame = true;
Frame++;
_drivelight = false;
Core.gpgx_advance();
LibGPGX.gpgx_advance();
UpdateVideo();
update_audio();
@ -89,10 +89,16 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
if (!disposed)
{
if (Elf != null)
Elf.Dispose();
if (AttachedCore != this)
throw new Exception();
if (SaveRamModified)
_disposedSaveRam = CloneSaveRam();
KillMemCallbacks();
if (CD != null)
{
CD.Dispose();
}
AttachedCore = null;
disposed = true;
}
}

View File

@ -17,7 +17,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
IntPtr area = IntPtr.Zero;
int size = 0;
IntPtr pname = Core.gpgx_get_memdom(i, ref area, ref size);
IntPtr pname = LibGPGX.gpgx_get_memdom(i, ref area, ref size);
if (area == IntPtr.Zero || pname == IntPtr.Zero || size == 0)
continue;
string name = Marshal.PtrToStringAnsi(pname);
@ -26,17 +26,17 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
// vram pokes need to go through hook which invalidates cached tiles
byte* p = (byte*)area;
mm.Add(new MemoryDomainDelegate(name, size, MemoryDomain.Endian.Unknown,
delegate(long addr)
delegate (long addr)
{
if (addr < 0 || addr >= 65536)
throw new ArgumentOutOfRangeException();
return p[addr ^ 1];
},
delegate(long addr, byte val)
delegate (long addr, byte val)
{
if (addr < 0 || addr >= 65536)
throw new ArgumentOutOfRangeException();
Core.gpgx_poke_vram(((int)addr) ^ 1, val);
LibGPGX.gpgx_poke_vram(((int)addr) ^ 1, val);
},
wordSize: 2));
}
@ -44,6 +44,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
else
{
// TODO: are the Z80 domains really Swap16 in the core? Check this
//var byteSize = name.Contains("Z80") ? 1 : 2;
mm.Add(MemoryDomain.FromIntPtrSwap16(name, size,
MemoryDomain.Endian.Big, area, name != "MD CART" && name != "CD BOOT ROM"));
}
@ -54,14 +55,14 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
var a = (uint)addr;
if (a >= 0x1000000)
throw new ArgumentOutOfRangeException();
return Core.gpgx_peek_m68k_bus(a);
return LibGPGX.gpgx_peek_m68k_bus(a);
},
delegate (long addr, byte val)
{
var a = (uint)addr;
if (a >= 0x1000000)
throw new ArgumentOutOfRangeException();
Core.gpgx_write_m68k_bus(a, val);
LibGPGX.gpgx_write_m68k_bus(a, val);
}, 2);
mm.Add(m68Bus);
@ -72,14 +73,14 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
var a = (uint)addr;
if (a >= 0x1000000)
throw new ArgumentOutOfRangeException();
return Core.gpgx_peek_s68k_bus(a);
return LibGPGX.gpgx_peek_s68k_bus(a);
},
delegate (long addr, byte val)
{
var a = (uint)addr;
if (a >= 0x1000000)
throw new ArgumentOutOfRangeException();
Core.gpgx_write_s68k_bus(a, val);
LibGPGX.gpgx_write_s68k_bus(a, val);
}, 2);
if (IsSegaCD)

View File

@ -11,30 +11,69 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
public byte[] CloneSaveRam()
{
int size = 0;
IntPtr area = Core.gpgx_get_sram(ref size);
if (size == 0 || area == IntPtr.Zero)
return new byte[0];
if (disposed)
{
if (_disposedSaveRam != null)
{
return (byte[])_disposedSaveRam.Clone();
}
byte[] ret = new byte[size];
Marshal.Copy(area, ret, 0, size);
return ret;
return new byte[0];
}
else
{
int size = 0;
IntPtr area = IntPtr.Zero;
LibGPGX.gpgx_get_sram(ref area, ref size);
if (size <= 0 || area == IntPtr.Zero)
return new byte[0];
LibGPGX.gpgx_sram_prepread();
byte[] ret = new byte[size];
Marshal.Copy(area, ret, 0, size);
return ret;
}
}
public void StoreSaveRam(byte[] data)
{
if (!Core.gpgx_put_sram(data, data.Length))
throw new Exception("Core rejected saveram");
if (disposed)
{
throw new ObjectDisposedException(typeof(GPGX).ToString());
}
else
{
int size = 0;
IntPtr area = IntPtr.Zero;
LibGPGX.gpgx_get_sram(ref area, ref size);
if (size <= 0 || area == IntPtr.Zero)
return;
if (size != data.Length)
throw new Exception("Unexpected saveram size");
Marshal.Copy(data, 0, area, size);
LibGPGX.gpgx_sram_commitwrite();
}
}
public bool SaveRamModified
{
get
{
int size = 0;
IntPtr area = Core.gpgx_get_sram(ref size);
return size > 0 && area != IntPtr.Zero;
if (disposed)
{
return _disposedSaveRam != null;
}
else
{
int size = 0;
IntPtr area = IntPtr.Zero;
LibGPGX.gpgx_get_sram(ref area, ref size);
return size > 0 && area != IntPtr.Zero;
}
}
}
private byte[] _disposedSaveRam = null;
}
}

View File

@ -23,7 +23,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
bool ret = GPGXSettings.NeedsReboot(_settings, o);
_settings = o;
Core.gpgx_set_draw_mask(_settings.GetDrawMask());
LibGPGX.gpgx_set_draw_mask(_settings.GetDrawMask());
return ret;
}
@ -80,7 +80,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
private bool _PadScreen320;
[DisplayName("Pad screen to 320")]
[Description("When using 1:1 aspect ratio, enable to make screen width constant (320) between game modes")]
[Description("Set to True to pads the screen out to be 320 when in 256 wide video modes")]
[DefaultValue(false)]
public bool PadScreen320 { get { return _PadScreen320; } set { _PadScreen320 = value; } }

View File

@ -29,42 +29,51 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
LoadStateBinary(new BinaryReader(new MemoryStream(state)));
}
public byte[] SaveStateBinary()
{
var ms = new MemoryStream(_savebuff2, true);
var bw = new BinaryWriter(ms);
SaveStateBinary(bw);
bw.Flush();
ms.Close();
return _savebuff2;
}
public void LoadStateBinary(BinaryReader reader)
{
Elf.LoadStateBinary(reader);
int newlen = reader.ReadInt32();
if (newlen != _savebuff.Length)
{
throw new Exception("Unexpected state size");
}
reader.Read(_savebuff, 0, _savebuff.Length);
if (!LibGPGX.gpgx_state_load(_savebuff, _savebuff.Length))
{
throw new Exception("gpgx_state_load() returned false");
}
// other variables
Frame = reader.ReadInt32();
LagCount = reader.ReadInt32();
IsLagFrame = reader.ReadBoolean();
// any managed pointers that we sent to the core need to be resent now!
Core.gpgx_set_input_callback(InputCallback);
RefreshMemCallbacks();
Core.gpgx_set_cdd_callback(cd_callback_handle);
Core.gpgx_invalidate_pattern_cache();
UpdateVideo();
}
public void SaveStateBinary(BinaryWriter writer)
{
Elf.SaveStateBinary(writer);
if (!LibGPGX.gpgx_state_save(_savebuff, _savebuff.Length))
throw new Exception("gpgx_state_save() returned false");
writer.Write(_savebuff.Length);
writer.Write(_savebuff);
// other variables
writer.Write(Frame);
writer.Write(LagCount);
writer.Write(IsLagFrame);
}
public byte[] SaveStateBinary()
{
var ms = new MemoryStream();
var bw = new BinaryWriter(ms);
SaveStateBinary(bw);
bw.Flush();
ms.Close();
return ms.ToArray();
}
private void InitStateBuffers()
{
}
private byte[] _savebuff;
private byte[] _savebuff2;
}
}

View File

@ -40,7 +40,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
int gppitch, gpwidth, gpheight;
IntPtr src = IntPtr.Zero;
Core.gpgx_get_video(out gpwidth, out gpheight, out gppitch, ref src);
LibGPGX.gpgx_get_video(out gpwidth, out gpheight, out gppitch, ref src);
vwidth = gpwidth;
vheight = gpheight;

View File

@ -1,404 +1,403 @@
using System;
using System.Runtime.InteropServices;
using BizHawk.Emulation.Common;
using BizHawk.Common;
using System.IO;
using BizHawk.Emulation.Common.BizInvoke;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
[CoreAttributes(
"Genplus-gx",
"",
isPorted: true,
isReleased: true,
portedVersion: "r874",
portedUrl: "https://code.google.com/p/genplus-gx/",
singleInstance: false
)]
public partial class GPGX : IEmulator, ISyncSoundProvider, IVideoProvider, ISaveRam, IStatable, IRegionable,
IInputPollable, IDebuggable, IDriveLight, ICodeDataLogger, IDisassemblable
{
LibGPGX Core;
ElfRunner Elf;
DiscSystem.Disc CD;
DiscSystem.DiscSectorReader DiscSectorReader;
byte[] romfile;
bool disposed = false;
LibGPGX.load_archive_cb LoadCallback = null;
LibGPGX.InputData input = new LibGPGX.InputData();
public enum ControlType
{
None,
OnePlayer,
Normal,
Xea1p,
Activator,
Teamplayer,
Wayplay,
Mouse
};
[CoreConstructor("GEN")]
public GPGX(CoreComm comm, byte[] file, object Settings, object SyncSettings)
: this(comm, file, null, Settings, SyncSettings)
{
}
public GPGX(CoreComm comm, byte[] rom, DiscSystem.Disc CD, object Settings, object SyncSettings)
{
ServiceProvider = new BasicServiceProvider(this);
// this can influence some things internally (autodetect romtype, etc)
string romextension = "GEN";
// three or six button?
// http://www.sega-16.com/forum/showthread.php?4398-Forgotten-Worlds-giving-you-GAME-OVER-immediately-Fix-inside&highlight=forgotten%20worlds
//hack, don't use
if (rom != null && rom.Length > 32 * 1024 * 1024)
{
throw new InvalidOperationException("ROM too big! Did you try to load a CD as a ROM?");
}
try
{
Elf = new ElfRunner(Path.Combine(comm.CoreFileProvider.DllPath(), "gpgx.elf"), 8 * 1024 * 1024, 36 * 1024 * 1024, 4 * 1024 * 1024);
if (Elf.ShouldMonitor)
Core = BizInvoker.GetInvoker<LibGPGX>(Elf, Elf);
else
Core = BizInvoker.GetInvoker<LibGPGX>(Elf);
_syncSettings = (GPGXSyncSettings)SyncSettings ?? new GPGXSyncSettings();
_settings = (GPGXSettings)Settings ?? new GPGXSettings();
CoreComm = comm;
LoadCallback = new LibGPGX.load_archive_cb(load_archive);
this.romfile = rom;
this.CD = CD;
if (CD != null)
{
this.DiscSectorReader = new DiscSystem.DiscSectorReader(CD);
cd_callback_handle = new LibGPGX.cd_read_cb(CDRead);
Core.gpgx_set_cdd_callback(cd_callback_handle);
}
LibGPGX.INPUT_SYSTEM system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_NONE;
LibGPGX.INPUT_SYSTEM system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_NONE;
switch (_syncSettings.ControlType)
{
case ControlType.None:
default:
break;
case ControlType.Activator:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_ACTIVATOR;
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_ACTIVATOR;
break;
case ControlType.Normal:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
break;
case ControlType.OnePlayer:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
break;
case ControlType.Xea1p:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_XE_A1P;
break;
case ControlType.Teamplayer:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_TEAMPLAYER;
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_TEAMPLAYER;
break;
case ControlType.Wayplay:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_WAYPLAY;
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_WAYPLAY;
break;
case ControlType.Mouse:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
// seems like mouse in port 1 would be supported, but not both at the same time
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_MOUSE;
break;
}
if (!Core.gpgx_init(romextension, LoadCallback, _syncSettings.UseSixButton, system_a, system_b, _syncSettings.Region, _settings.GetNativeSettings()))
throw new Exception("gpgx_init() failed");
{
int fpsnum = 60;
int fpsden = 1;
Core.gpgx_get_fps(ref fpsnum, ref fpsden);
CoreComm.VsyncNum = fpsnum;
CoreComm.VsyncDen = fpsden;
Region = CoreComm.VsyncRate > 55 ? DisplayType.NTSC : DisplayType.PAL;
}
// compute state size
InitStateBuffers();
SetControllerDefinition();
// pull the default video size from the core
UpdateVideoInitial();
SetMemoryDomains();
InputCallback = new LibGPGX.input_cb(input_callback);
Core.gpgx_set_input_callback(InputCallback);
if (CD != null)
DriveLightEnabled = true;
// process the non-init settings now
PutSettings(_settings);
//TODO - this hits performance, we need to make it controllable
CDCallback = new LibGPGX.CDCallback(CDCallbackProc);
InitMemCallbacks();
KillMemCallbacks();
Tracer = new GPGXTraceBuffer(this, MemoryDomains, this);
(ServiceProvider as BasicServiceProvider).Register<ITraceable>(Tracer);
Elf.Seal();
}
catch
{
Dispose();
throw;
}
}
/// <summary>
/// core callback for file loading
/// </summary>
/// <param name="filename">string identifying file to be loaded</param>
/// <param name="buffer">buffer to load file to</param>
/// <param name="maxsize">maximum length buffer can hold</param>
/// <returns>actual size loaded, or 0 on failure</returns>
int load_archive(string filename, IntPtr buffer, int maxsize)
{
byte[] srcdata = null;
if (buffer == IntPtr.Zero)
{
Console.WriteLine("Couldn't satisfy firmware request {0} because buffer == NULL", filename);
return 0;
}
if (filename == "PRIMARY_ROM")
{
if (romfile == null)
{
Console.WriteLine("Couldn't satisfy firmware request PRIMARY_ROM because none was provided.");
return 0;
}
srcdata = romfile;
}
else if (filename == "PRIMARY_CD" || filename == "SECONDARY_CD")
{
if (filename == "PRIMARY_CD" && romfile != null)
{
Console.WriteLine("Declined to satisfy firmware request PRIMARY_CD because PRIMARY_ROM was provided.");
return 0;
}
else
{
if (CD == null)
{
Console.WriteLine("Couldn't satisfy firmware request {0} because none was provided.", filename);
return 0;
}
srcdata = GetCDData();
if (srcdata.Length != maxsize)
{
Console.WriteLine("Couldn't satisfy firmware request {0} because of struct size.", filename);
return 0;
}
}
}
else
{
// use fromtend firmware interface
string firmwareID = null;
switch (filename)
{
case "CD_BIOS_EU": firmwareID = "CD_BIOS_EU"; break;
case "CD_BIOS_JP": firmwareID = "CD_BIOS_JP"; break;
case "CD_BIOS_US": firmwareID = "CD_BIOS_US"; break;
default:
break;
}
if (firmwareID != null)
{
// this path will be the most common PEBKAC error, so be a bit more vocal about the problem
srcdata = CoreComm.CoreFileProvider.GetFirmware("GEN", firmwareID, false, "GPGX firmwares are usually required.");
if (srcdata == null)
{
Console.WriteLine("Frontend couldn't satisfy firmware request GEN:{0}", firmwareID);
return 0;
}
}
else
{
Console.WriteLine("Unrecognized firmware request {0}", filename);
return 0;
}
}
if (srcdata != null)
{
if (srcdata.Length > maxsize)
{
Console.WriteLine("Couldn't satisfy firmware request {0} because {1} > {2}", filename, srcdata.Length, maxsize);
return 0;
}
else
{
Marshal.Copy(srcdata, 0, buffer, srcdata.Length);
Console.WriteLine("Firmware request {0} satisfied at size {1}", filename, srcdata.Length);
return srcdata.Length;
}
}
else
{
throw new Exception();
//Console.WriteLine("Couldn't satisfy firmware request {0} for unknown reasons", filename);
//return 0;
}
}
void CDRead(int lba, IntPtr dest, bool audio)
{
if (audio)
{
byte[] data = new byte[2352];
if (lba < CD.Session1.LeadoutLBA)
{
DiscSectorReader.ReadLBA_2352(lba, data, 0);
}
else
{
// audio seems to read slightly past the end of disks; probably innoculous
// just send back 0s.
// Console.WriteLine("!!{0} >= {1}", lba, CD.LBACount);
}
Marshal.Copy(data, 0, dest, 2352);
}
else
{
byte[] data = new byte[2048];
DiscSectorReader.ReadLBA_2048(lba, data, 0);
Marshal.Copy(data, 0, dest, 2048);
_drivelight = true;
}
}
LibGPGX.cd_read_cb cd_callback_handle;
unsafe byte[] GetCDData()
{
LibGPGX.CDData ret = new LibGPGX.CDData();
int size = Marshal.SizeOf(ret);
var ses = CD.Session1;
int ntrack = ses.InformationTrackCount;
// bet you a dollar this is all wrong
//zero 07-jul-2015 - throws a dollar in the pile, since he probably messed it up worse
for (int i = 0; i < LibGPGX.CD_MAX_TRACKS; i++)
{
if (i < ntrack)
{
ret.tracks[i].start = ses.Tracks[i + 1].LBA;
ret.tracks[i].end = ses.Tracks[i + 2].LBA;
if (i == ntrack - 1)
{
ret.end = ret.tracks[i].end;
ret.last = ntrack;
}
}
else
{
ret.tracks[i].start = 0;
ret.tracks[i].end = 0;
}
}
byte[] retdata = new byte[size];
fixed (byte* p = &retdata[0])
{
Marshal.StructureToPtr(ret, (IntPtr)p, false);
}
return retdata;
}
/// <summary>
/// size of native input struct
/// </summary>
int inputsize;
GPGXControlConverter ControlConverter;
private void SetControllerDefinition()
{
inputsize = Marshal.SizeOf(typeof(LibGPGX.InputData));
if (!Core.gpgx_get_control(input, inputsize))
throw new Exception("gpgx_get_control() failed");
ControlConverter = new GPGXControlConverter(input);
ControllerDefinition = ControlConverter.ControllerDef;
}
public LibGPGX.INPUT_DEVICE[] GetDevices()
{
return (LibGPGX.INPUT_DEVICE[])input.dev.Clone();
}
public bool IsSegaCD { get { return CD != null; } }
public void UpdateVDPViewContext(LibGPGX.VDPView view)
{
Core.gpgx_get_vdp_view(view);
Core.gpgx_flush_vram(); // fully regenerate internal caches as needed
}
short[] samples = new short[4096];
int nsamp = 0;
public void GetSamples(out short[] samples, out int nsamp)
{
nsamp = this.nsamp;
samples = this.samples;
this.nsamp = 0;
}
public void DiscardSamples()
{
this.nsamp = 0;
}
void update_audio()
{
IntPtr src = IntPtr.Zero;
Core.gpgx_get_audio(ref nsamp, ref src);
if (src != IntPtr.Zero)
{
Marshal.Copy(src, samples, 0, nsamp * 2);
}
}
public DisplayType Region { get; private set; }
}
}
using System;
using System.Runtime.InteropServices;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
[CoreAttributes(
"Genplus-gx",
"",
isPorted: true,
isReleased: true,
portedVersion: "r874",
portedUrl: "https://code.google.com/p/genplus-gx/",
singleInstance: true
)]
public partial class GPGX : IEmulator, ISyncSoundProvider, IVideoProvider, ISaveRam, IStatable, IRegionable,
IInputPollable, IDebuggable, IDriveLight, ICodeDataLogger, IDisassemblable
{
static GPGX AttachedCore = null;
DiscSystem.Disc CD;
DiscSystem.DiscSectorReader DiscSectorReader;
byte[] romfile;
bool disposed = false;
LibGPGX.load_archive_cb LoadCallback = null;
LibGPGX.InputData input = new LibGPGX.InputData();
public enum ControlType
{
None,
OnePlayer,
Normal,
Xea1p,
Activator,
Teamplayer,
Wayplay,
Mouse
};
[CoreConstructor("GEN")]
public GPGX(CoreComm comm, byte[] file, object Settings, object SyncSettings)
: this(comm, file, null, Settings, SyncSettings)
{
}
public GPGX(CoreComm comm, byte[] rom, DiscSystem.Disc CD, object Settings, object SyncSettings)
{
ServiceProvider = new BasicServiceProvider(this);
// this can influence some things internally
string romextension = "GEN";
// three or six button?
// http://www.sega-16.com/forum/showthread.php?4398-Forgotten-Worlds-giving-you-GAME-OVER-immediately-Fix-inside&highlight=forgotten%20worlds
//hack, don't use
if (rom != null && rom.Length > 32 * 1024 * 1024)
{
throw new InvalidOperationException("ROM too big! Did you try to load a CD as a ROM?");
}
try
{
_syncSettings = (GPGXSyncSettings)SyncSettings ?? new GPGXSyncSettings();
_settings = (GPGXSettings)Settings ?? new GPGXSettings();
CoreComm = comm;
if (AttachedCore != null)
{
AttachedCore.Dispose();
AttachedCore = null;
}
AttachedCore = this;
LoadCallback = new LibGPGX.load_archive_cb(load_archive);
this.romfile = rom;
this.CD = CD;
this.DiscSectorReader = new DiscSystem.DiscSectorReader(CD);
LibGPGX.INPUT_SYSTEM system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_NONE;
LibGPGX.INPUT_SYSTEM system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_NONE;
switch (_syncSettings.ControlType)
{
case ControlType.None:
default:
break;
case ControlType.Activator:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_ACTIVATOR;
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_ACTIVATOR;
break;
case ControlType.Normal:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
break;
case ControlType.OnePlayer:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
break;
case ControlType.Xea1p:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_XE_A1P;
break;
case ControlType.Teamplayer:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_TEAMPLAYER;
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_TEAMPLAYER;
break;
case ControlType.Wayplay:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_WAYPLAY;
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_WAYPLAY;
break;
case ControlType.Mouse:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
// seems like mouse in port 1 would be supported, but not both at the same time
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_MOUSE;
break;
}
if (!LibGPGX.gpgx_init(romextension, LoadCallback, _syncSettings.UseSixButton, system_a, system_b, _syncSettings.Region, _settings.GetNativeSettings()))
throw new Exception("gpgx_init() failed");
{
int fpsnum = 60;
int fpsden = 1;
LibGPGX.gpgx_get_fps(ref fpsnum, ref fpsden);
CoreComm.VsyncNum = fpsnum;
CoreComm.VsyncDen = fpsden;
Region = CoreComm.VsyncRate > 55 ? DisplayType.NTSC : DisplayType.PAL;
}
// compute state size
{
byte[] tmp = new byte[LibGPGX.gpgx_state_max_size()];
int size = LibGPGX.gpgx_state_size(tmp, tmp.Length);
if (size <= 0)
throw new Exception("Couldn't Determine GPGX internal state size!");
_savebuff = new byte[size];
_savebuff2 = new byte[_savebuff.Length + 13];
Console.WriteLine("GPGX Internal State Size: {0}", size);
}
SetControllerDefinition();
// pull the default video size from the core
UpdateVideoInitial();
SetMemoryDomains();
InputCallback = new LibGPGX.input_cb(input_callback);
LibGPGX.gpgx_set_input_callback(InputCallback);
if (CD != null)
DriveLightEnabled = true;
// process the non-init settings now
PutSettings(_settings);
//TODO - this hits performance, we need to make it controllable
CDCallback = new LibGPGX.CDCallback(CDCallbackProc);
InitMemCallbacks();
KillMemCallbacks();
Tracer = new GPGXTraceBuffer(this, MemoryDomains, this);
(ServiceProvider as BasicServiceProvider).Register<ITraceable>(Tracer);
}
catch
{
Dispose();
throw;
}
}
/// <summary>
/// core callback for file loading
/// </summary>
/// <param name="filename">string identifying file to be loaded</param>
/// <param name="buffer">buffer to load file to</param>
/// <param name="maxsize">maximum length buffer can hold</param>
/// <returns>actual size loaded, or 0 on failure</returns>
int load_archive(string filename, IntPtr buffer, int maxsize)
{
byte[] srcdata = null;
if (buffer == IntPtr.Zero)
{
Console.WriteLine("Couldn't satisfy firmware request {0} because buffer == NULL", filename);
return 0;
}
if (filename == "PRIMARY_ROM")
{
if (romfile == null)
{
Console.WriteLine("Couldn't satisfy firmware request PRIMARY_ROM because none was provided.");
return 0;
}
srcdata = romfile;
}
else if (filename == "PRIMARY_CD" || filename == "SECONDARY_CD")
{
if (filename == "PRIMARY_CD" && romfile != null)
{
Console.WriteLine("Declined to satisfy firmware request PRIMARY_CD because PRIMARY_ROM was provided.");
return 0;
}
else
{
if (CD == null)
{
Console.WriteLine("Couldn't satisfy firmware request {0} because none was provided.", filename);
return 0;
}
srcdata = GetCDData();
if (srcdata.Length != maxsize)
{
Console.WriteLine("Couldn't satisfy firmware request {0} because of struct size.", filename);
return 0;
}
}
}
else
{
// use fromtend firmware interface
string firmwareID = null;
switch (filename)
{
case "CD_BIOS_EU": firmwareID = "CD_BIOS_EU"; break;
case "CD_BIOS_JP": firmwareID = "CD_BIOS_JP"; break;
case "CD_BIOS_US": firmwareID = "CD_BIOS_US"; break;
default:
break;
}
if (firmwareID != null)
{
// this path will be the most common PEBKAC error, so be a bit more vocal about the problem
srcdata = CoreComm.CoreFileProvider.GetFirmware("GEN", firmwareID, false, "GPGX firmwares are usually required.");
if (srcdata == null)
{
Console.WriteLine("Frontend couldn't satisfy firmware request GEN:{0}", firmwareID);
return 0;
}
}
else
{
Console.WriteLine("Unrecognized firmware request {0}", filename);
return 0;
}
}
if (srcdata != null)
{
if (srcdata.Length > maxsize)
{
Console.WriteLine("Couldn't satisfy firmware request {0} because {1} > {2}", filename, srcdata.Length, maxsize);
return 0;
}
else
{
Marshal.Copy(srcdata, 0, buffer, srcdata.Length);
Console.WriteLine("Firmware request {0} satisfied at size {1}", filename, srcdata.Length);
return srcdata.Length;
}
}
else
{
throw new Exception();
//Console.WriteLine("Couldn't satisfy firmware request {0} for unknown reasons", filename);
//return 0;
}
}
void CDRead(int lba, IntPtr dest, bool audio)
{
if (audio)
{
byte[] data = new byte[2352];
if (lba < CD.Session1.LeadoutLBA)
{
DiscSectorReader.ReadLBA_2352(lba, data, 0);
}
else
{
// audio seems to read slightly past the end of disks; probably innoculous
// just send back 0s.
// Console.WriteLine("!!{0} >= {1}", lba, CD.LBACount);
}
Marshal.Copy(data, 0, dest, 2352);
}
else
{
byte[] data = new byte[2048];
DiscSectorReader.ReadLBA_2048(lba, data, 0);
Marshal.Copy(data, 0, dest, 2048);
_drivelight = true;
}
}
LibGPGX.cd_read_cb cd_callback_handle;
unsafe byte[] GetCDData()
{
LibGPGX.CDData ret = new LibGPGX.CDData();
int size = Marshal.SizeOf(ret);
ret.readcallback = cd_callback_handle = new LibGPGX.cd_read_cb(CDRead);
var ses = CD.Session1;
int ntrack = ses.InformationTrackCount;
// bet you a dollar this is all wrong
//zero 07-jul-2015 - throws a dollar in the pile, since he probably messed it up worse
for (int i = 0; i < LibGPGX.CD_MAX_TRACKS; i++)
{
if (i < ntrack)
{
ret.tracks[i].start = ses.Tracks[i + 1].LBA;
ret.tracks[i].end = ses.Tracks[i + 2].LBA;
if (i == ntrack - 1)
{
ret.end = ret.tracks[i].end;
ret.last = ntrack;
}
}
else
{
ret.tracks[i].start = 0;
ret.tracks[i].end = 0;
}
}
byte[] retdata = new byte[size];
fixed (byte* p = &retdata[0])
{
Marshal.StructureToPtr(ret, (IntPtr)p, false);
}
return retdata;
}
/// <summary>
/// size of native input struct
/// </summary>
int inputsize;
GPGXControlConverter ControlConverter;
private void SetControllerDefinition()
{
inputsize = Marshal.SizeOf(typeof(LibGPGX.InputData));
if (!LibGPGX.gpgx_get_control(input, inputsize))
throw new Exception("gpgx_get_control() failed");
ControlConverter = new GPGXControlConverter(input);
ControllerDefinition = ControlConverter.ControllerDef;
}
public LibGPGX.INPUT_DEVICE[] GetDevices()
{
return (LibGPGX.INPUT_DEVICE[])input.dev.Clone();
}
public bool IsSegaCD { get { return CD != null; } }
public void UpdateVDPViewContext(LibGPGX.VDPView view)
{
LibGPGX.gpgx_get_vdp_view(view);
LibGPGX.gpgx_flush_vram(); // fully regenerate internal caches as needed
}
short[] samples = new short[4096];
int nsamp = 0;
public void GetSamples(out short[] samples, out int nsamp)
{
nsamp = this.nsamp;
samples = this.samples;
this.nsamp = 0;
}
public void DiscardSamples()
{
this.nsamp = 0;
}
void update_audio()
{
IntPtr src = IntPtr.Zero;
LibGPGX.gpgx_get_audio(ref nsamp, ref src);
if (src != IntPtr.Zero)
{
Marshal.Copy(src, samples, 0, nsamp * 2);
}
}
public DisplayType Region { get; private set; }
}
}

View File

@ -1,264 +1,264 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
public class GPGXControlConverter
{
// this isn't all done
struct CName
{
public string Name;
public LibGPGX.INPUT_KEYS Key;
public CName(string Name, LibGPGX.INPUT_KEYS Key)
{
this.Name = Name;
this.Key = Key;
}
}
static CName[] Genesis3 =
{
new CName("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
new CName("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN),
new CName("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT),
new CName("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT),
new CName("A", LibGPGX.INPUT_KEYS.INPUT_A),
new CName("B", LibGPGX.INPUT_KEYS.INPUT_B),
new CName("C", LibGPGX.INPUT_KEYS.INPUT_C),
new CName("Start", LibGPGX.INPUT_KEYS.INPUT_START),
};
static CName[] Genesis6 =
{
new CName("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
new CName("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN),
new CName("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT),
new CName("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT),
new CName("A", LibGPGX.INPUT_KEYS.INPUT_A),
new CName("B", LibGPGX.INPUT_KEYS.INPUT_B),
new CName("C", LibGPGX.INPUT_KEYS.INPUT_C),
new CName("Start", LibGPGX.INPUT_KEYS.INPUT_START),
new CName("X", LibGPGX.INPUT_KEYS.INPUT_X),
new CName("Y", LibGPGX.INPUT_KEYS.INPUT_Y),
new CName("Z", LibGPGX.INPUT_KEYS.INPUT_Z),
new CName("Mode", LibGPGX.INPUT_KEYS.INPUT_MODE),
};
static CName[] Mouse =
{
new CName("Mouse Left", LibGPGX.INPUT_KEYS.INPUT_MOUSE_LEFT),
new CName("Mouse Center", LibGPGX.INPUT_KEYS.INPUT_MOUSE_CENTER),
new CName("Mouse Right", LibGPGX.INPUT_KEYS.INPUT_MOUSE_RIGHT),
new CName("Mouse Start", LibGPGX.INPUT_KEYS.INPUT_MOUSE_START),
};
static CName[] Lightgun =
{
new CName("Lightgun Trigger", LibGPGX.INPUT_KEYS.INPUT_MENACER_TRIGGER),
new CName("Lightgun Start", LibGPGX.INPUT_KEYS.INPUT_MENACER_START),
};
static CName[] Activator =
{
new CName("1L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_1L),
new CName("1U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_1U),
new CName("2L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_2L),
new CName("2U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_2U),
new CName("3L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_3L),
new CName("3U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_3U),
new CName("4L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_4L),
new CName("4U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_4U),
new CName("5L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_5L),
new CName("5U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_5U),
new CName("6L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_6L),
new CName("6U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_6U),
new CName("7L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_7L),
new CName("7U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_7U),
new CName("8L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_8L),
new CName("8U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_8U),
};
static CName[] XEA1P =
{
new CName("XE A", LibGPGX.INPUT_KEYS.INPUT_XE_A),
new CName("XE B", LibGPGX.INPUT_KEYS.INPUT_XE_B),
new CName("XE C", LibGPGX.INPUT_KEYS.INPUT_XE_C),
new CName("XE D", LibGPGX.INPUT_KEYS.INPUT_XE_D),
new CName("XE Start", LibGPGX.INPUT_KEYS.INPUT_XE_START),
new CName("XE Select", LibGPGX.INPUT_KEYS.INPUT_XE_SELECT),
new CName("XE E1", LibGPGX.INPUT_KEYS.INPUT_XE_E1),
new CName("XE E2", LibGPGX.INPUT_KEYS.INPUT_XE_E2),
};
static ControllerDefinition.FloatRange MouseRange = new ControllerDefinition.FloatRange(-256, 0, 255);
// lightgun needs to be transformed to match the current screen resolution
static ControllerDefinition.FloatRange LightgunRange = new ControllerDefinition.FloatRange(0, 5000, 10000);
static ControllerDefinition.FloatRange XEA1PRange = new ControllerDefinition.FloatRange(-128, 0, 127);
LibGPGX.InputData target = null;
IController source = null;
List<Action> Converts = new List<Action>();
public ControllerDefinition ControllerDef { get; private set; }
void AddToController(int idx, int player, IEnumerable<CName> Buttons)
{
foreach (var Button in Buttons)
{
string Name = string.Format("P{0} {1}", player, Button.Name);
ControllerDef.BoolButtons.Add(Name);
var ButtonFlag = Button.Key;
Converts.Add(delegate()
{
if (source.IsPressed(Name))
target.pad[idx] |= ButtonFlag;
});
}
}
void DoMouseAnalog(int idx, int player)
{
string NX = string.Format("P{0} Mouse X", player);
string NY = string.Format("P{0} Mouse Y", player);
ControllerDef.FloatControls.Add(NX);
ControllerDef.FloatControls.Add(NY);
ControllerDef.FloatRanges.Add(MouseRange);
ControllerDef.FloatRanges.Add(MouseRange);
Converts.Add(delegate()
{
target.analog[(2 * idx) + 0] = (short)source.GetFloat(NX);
target.analog[(2 * idx) + 1] = (short)source.GetFloat(NY);
});
}
void DoLightgunAnalog(int idx, int player)
{
string NX = string.Format("P{0} Lightgun X", player);
string NY = string.Format("P{0} Lightgun Y", player);
ControllerDef.FloatControls.Add(NX);
ControllerDef.FloatControls.Add(NY);
ControllerDef.FloatRanges.Add(LightgunRange);
ControllerDef.FloatRanges.Add(LightgunRange);
Converts.Add(delegate()
{
target.analog[(2 * idx) + 0] = (short)(source.GetFloat(NX) / 10000.0f * (ScreenWidth - 1));
target.analog[(2 * idx) + 1] = (short)(source.GetFloat(NY) / 10000.0f * (ScreenHeight - 1));
});
}
void DoXEA1PAnalog(int idx, int player)
{
string NX = string.Format("P{0} Stick X", player);
string NY = string.Format("P{0} Stick Y", player);
string NZ = string.Format("P{0} Stick Z", player);
ControllerDef.FloatControls.Add(NX);
ControllerDef.FloatControls.Add(NY);
ControllerDef.FloatControls.Add(NZ);
ControllerDef.FloatRanges.Add(XEA1PRange);
ControllerDef.FloatRanges.Add(XEA1PRange);
ControllerDef.FloatRanges.Add(XEA1PRange);
Converts.Add(delegate()
{
target.analog[(2 * idx) + 0] = (short)(source.GetFloat(NX));
target.analog[(2 * idx) + 1] = (short)(source.GetFloat(NY));
// +2 is correct in how gpgx internally does this
target.analog[(2 * idx) + 2] = (short)(source.GetFloat(NZ));
});
}
public GPGXControlConverter(LibGPGX.InputData input)
{
Console.WriteLine("Genesis Controller report:");
foreach (var e in input.system)
Console.WriteLine("S:{0}", e);
foreach (var e in input.dev)
Console.WriteLine("D:{0}", e);
int player = 1;
ControllerDef = new ControllerDefinition();
ControllerDef.BoolButtons.Add("Power");
ControllerDef.BoolButtons.Add("Reset");
for (int i = 0; i < LibGPGX.MAX_DEVICES; i++)
{
switch (input.dev[i])
{
case LibGPGX.INPUT_DEVICE.DEVICE_PAD3B:
AddToController(i, player, Genesis3);
player++;
break;
case LibGPGX.INPUT_DEVICE.DEVICE_PAD6B:
AddToController(i, player, Genesis6);
player++;
break;
case LibGPGX.INPUT_DEVICE.DEVICE_MOUSE:
AddToController(i, player, Mouse);
DoMouseAnalog(i, player);
player++;
break;
case LibGPGX.INPUT_DEVICE.DEVICE_NONE:
break;
case LibGPGX.INPUT_DEVICE.DEVICE_LIGHTGUN:
// supports menacers and justifiers
AddToController(i, player, Lightgun);
DoLightgunAnalog(i, player);
player++;
break;
case LibGPGX.INPUT_DEVICE.DEVICE_PAD2B:
case LibGPGX.INPUT_DEVICE.DEVICE_PADDLE:
case LibGPGX.INPUT_DEVICE.DEVICE_SPORTSPAD:
case LibGPGX.INPUT_DEVICE.DEVICE_TEREBI:
throw new Exception("Master System only device? Something went wrong.");
case LibGPGX.INPUT_DEVICE.DEVICE_ACTIVATOR:
AddToController(i, player, Activator);
player++;
break;
case LibGPGX.INPUT_DEVICE.DEVICE_XE_A1P:
AddToController(i, player, XEA1P);
DoXEA1PAnalog(i, player);
player++;
break;
case LibGPGX.INPUT_DEVICE.DEVICE_PICO:
// PICO isn't finished on the unmanaged side either
throw new Exception("Sega PICO not implemented yet!");
default:
throw new Exception("Unknown Genesis control device! Something went wrong.");
}
}
ControllerDef.Name = "GPGX Genesis Controller";
}
public void Convert(IController source, LibGPGX.InputData target)
{
this.source = source;
this.target = target;
target.ClearAllBools();
foreach (var f in Converts)
f();
this.source = null;
this.target = null;
}
/// <summary>
/// must be set for proper lightgun operation
/// </summary>
public int ScreenWidth { get; set; }
/// <summary>
/// must be set for proper lightgun operation
/// </summary>
public int ScreenHeight { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
public class GPGXControlConverter
{
// this isn't all done
struct CName
{
public string Name;
public LibGPGX.INPUT_KEYS Key;
public CName(string Name, LibGPGX.INPUT_KEYS Key)
{
this.Name = Name;
this.Key = Key;
}
}
static CName[] Genesis3 =
{
new CName("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
new CName("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN),
new CName("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT),
new CName("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT),
new CName("A", LibGPGX.INPUT_KEYS.INPUT_A),
new CName("B", LibGPGX.INPUT_KEYS.INPUT_B),
new CName("C", LibGPGX.INPUT_KEYS.INPUT_C),
new CName("Start", LibGPGX.INPUT_KEYS.INPUT_START),
};
static CName[] Genesis6 =
{
new CName("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
new CName("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN),
new CName("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT),
new CName("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT),
new CName("A", LibGPGX.INPUT_KEYS.INPUT_A),
new CName("B", LibGPGX.INPUT_KEYS.INPUT_B),
new CName("C", LibGPGX.INPUT_KEYS.INPUT_C),
new CName("Start", LibGPGX.INPUT_KEYS.INPUT_START),
new CName("X", LibGPGX.INPUT_KEYS.INPUT_X),
new CName("Y", LibGPGX.INPUT_KEYS.INPUT_Y),
new CName("Z", LibGPGX.INPUT_KEYS.INPUT_Z),
new CName("Mode", LibGPGX.INPUT_KEYS.INPUT_MODE),
};
static CName[] Mouse =
{
new CName("Mouse Left", LibGPGX.INPUT_KEYS.INPUT_MOUSE_LEFT),
new CName("Mouse Center", LibGPGX.INPUT_KEYS.INPUT_MOUSE_CENTER),
new CName("Mouse Right", LibGPGX.INPUT_KEYS.INPUT_MOUSE_RIGHT),
new CName("Mouse Start", LibGPGX.INPUT_KEYS.INPUT_MOUSE_START),
};
static CName[] Lightgun =
{
new CName("Lightgun Trigger", LibGPGX.INPUT_KEYS.INPUT_MENACER_TRIGGER),
new CName("Lightgun Start", LibGPGX.INPUT_KEYS.INPUT_MENACER_START),
};
static CName[] Activator =
{
new CName("1L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_1L),
new CName("1U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_1U),
new CName("2L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_2L),
new CName("2U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_2U),
new CName("3L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_3L),
new CName("3U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_3U),
new CName("4L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_4L),
new CName("4U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_4U),
new CName("5L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_5L),
new CName("5U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_5U),
new CName("6L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_6L),
new CName("6U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_6U),
new CName("7L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_7L),
new CName("7U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_7U),
new CName("8L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_8L),
new CName("8U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_8U),
};
static CName[] XEA1P =
{
new CName("XE A", LibGPGX.INPUT_KEYS.INPUT_XE_A),
new CName("XE B", LibGPGX.INPUT_KEYS.INPUT_XE_B),
new CName("XE C", LibGPGX.INPUT_KEYS.INPUT_XE_C),
new CName("XE D", LibGPGX.INPUT_KEYS.INPUT_XE_D),
new CName("XE Start", LibGPGX.INPUT_KEYS.INPUT_XE_START),
new CName("XE Select", LibGPGX.INPUT_KEYS.INPUT_XE_SELECT),
new CName("XE E1", LibGPGX.INPUT_KEYS.INPUT_XE_E1),
new CName("XE E2", LibGPGX.INPUT_KEYS.INPUT_XE_E2),
};
static ControllerDefinition.FloatRange MouseRange = new ControllerDefinition.FloatRange(-256, 0, 255);
// lightgun needs to be transformed to match the current screen resolution
static ControllerDefinition.FloatRange LightgunRange = new ControllerDefinition.FloatRange(0, 5000, 10000);
static ControllerDefinition.FloatRange XEA1PRange = new ControllerDefinition.FloatRange(-128, 0, 127);
LibGPGX.InputData target = null;
IController source = null;
List<Action> Converts = new List<Action>();
public ControllerDefinition ControllerDef { get; private set; }
void AddToController(int idx, int player, IEnumerable<CName> Buttons)
{
foreach (var Button in Buttons)
{
string Name = string.Format("P{0} {1}", player, Button.Name);
ControllerDef.BoolButtons.Add(Name);
var ButtonFlag = Button.Key;
Converts.Add(delegate()
{
if (source.IsPressed(Name))
target.pad[idx] |= ButtonFlag;
});
}
}
void DoMouseAnalog(int idx, int player)
{
string NX = string.Format("P{0} Mouse X", player);
string NY = string.Format("P{0} Mouse Y", player);
ControllerDef.FloatControls.Add(NX);
ControllerDef.FloatControls.Add(NY);
ControllerDef.FloatRanges.Add(MouseRange);
ControllerDef.FloatRanges.Add(MouseRange);
Converts.Add(delegate()
{
target.analog[(2 * idx) + 0] = (short)source.GetFloat(NX);
target.analog[(2 * idx) + 1] = (short)source.GetFloat(NY);
});
}
void DoLightgunAnalog(int idx, int player)
{
string NX = string.Format("P{0} Lightgun X", player);
string NY = string.Format("P{0} Lightgun Y", player);
ControllerDef.FloatControls.Add(NX);
ControllerDef.FloatControls.Add(NY);
ControllerDef.FloatRanges.Add(LightgunRange);
ControllerDef.FloatRanges.Add(LightgunRange);
Converts.Add(delegate()
{
target.analog[(2 * idx) + 0] = (short)(source.GetFloat(NX) / 10000.0f * (ScreenWidth - 1));
target.analog[(2 * idx) + 1] = (short)(source.GetFloat(NY) / 10000.0f * (ScreenHeight - 1));
});
}
void DoXEA1PAnalog(int idx, int player)
{
string NX = string.Format("P{0} Stick X", player);
string NY = string.Format("P{0} Stick Y", player);
string NZ = string.Format("P{0} Stick Z", player);
ControllerDef.FloatControls.Add(NX);
ControllerDef.FloatControls.Add(NY);
ControllerDef.FloatControls.Add(NZ);
ControllerDef.FloatRanges.Add(XEA1PRange);
ControllerDef.FloatRanges.Add(XEA1PRange);
ControllerDef.FloatRanges.Add(XEA1PRange);
Converts.Add(delegate()
{
target.analog[(2 * idx) + 0] = (short)(source.GetFloat(NX));
target.analog[(2 * idx) + 1] = (short)(source.GetFloat(NY));
// +2 is correct in how gpgx internally does this
target.analog[(2 * idx) + 2] = (short)(source.GetFloat(NZ));
});
}
public GPGXControlConverter(LibGPGX.InputData input)
{
Console.WriteLine("Genesis Controller report:");
foreach (var e in input.system)
Console.WriteLine("S:{0}", e);
foreach (var e in input.dev)
Console.WriteLine("D:{0}", e);
int player = 1;
ControllerDef = new ControllerDefinition();
ControllerDef.BoolButtons.Add("Power");
ControllerDef.BoolButtons.Add("Reset");
for (int i = 0; i < LibGPGX.MAX_DEVICES; i++)
{
switch (input.dev[i])
{
case LibGPGX.INPUT_DEVICE.DEVICE_PAD3B:
AddToController(i, player, Genesis3);
player++;
break;
case LibGPGX.INPUT_DEVICE.DEVICE_PAD6B:
AddToController(i, player, Genesis6);
player++;
break;
case LibGPGX.INPUT_DEVICE.DEVICE_MOUSE:
AddToController(i, player, Mouse);
DoMouseAnalog(i, player);
player++;
break;
case LibGPGX.INPUT_DEVICE.DEVICE_NONE:
break;
case LibGPGX.INPUT_DEVICE.DEVICE_LIGHTGUN:
// supports menacers and justifiers
AddToController(i, player, Lightgun);
DoLightgunAnalog(i, player);
player++;
break;
case LibGPGX.INPUT_DEVICE.DEVICE_PAD2B:
case LibGPGX.INPUT_DEVICE.DEVICE_PADDLE:
case LibGPGX.INPUT_DEVICE.DEVICE_SPORTSPAD:
case LibGPGX.INPUT_DEVICE.DEVICE_TEREBI:
throw new Exception("Master System only device? Something went wrong.");
case LibGPGX.INPUT_DEVICE.DEVICE_ACTIVATOR:
AddToController(i, player, Activator);
player++;
break;
case LibGPGX.INPUT_DEVICE.DEVICE_XE_A1P:
AddToController(i, player, XEA1P);
DoXEA1PAnalog(i, player);
player++;
break;
case LibGPGX.INPUT_DEVICE.DEVICE_PICO:
// PICO isn't finished on the unmanaged side either
throw new Exception("Sega PICO not implemented yet!");
default:
throw new Exception("Unknown Genesis control device! Something went wrong.");
}
}
ControllerDef.Name = "GPGX Genesis Controller";
}
public void Convert(IController source, LibGPGX.InputData target)
{
this.source = source;
this.target = target;
target.ClearAllBools();
foreach (var f in Converts)
f();
this.source = null;
this.target = null;
}
/// <summary>
/// must be set for proper lightgun operation
/// </summary>
public int ScreenWidth { get; set; }
/// <summary>
/// must be set for proper lightgun operation
/// </summary>
public int ScreenHeight { get; set; }
}
}

View File

@ -1,214 +1,214 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
/*
* how to use:
* 0) get https://code.google.com/p/pdbparse/
* 1) set modulename to the name of the dll file.
* 2) set symbolname to the name of a file that you produced by executing the following command:
* pdb_print_gvars.py [module pdb file] 0x00000000 > [output file]
* 3) set start to an address (relative to the beginning of the dll) to start scanning
* 4) set length to the byte length of the scan area
* 5) instantiate a GenDbWind, and use it to control the scanner while you manipulate the dll into various configurations.
*
* ideas for modification:
* 1) unhardcode config parameters and allow modifying them through the interface
* 2) read section sizes and positions from the dll itself instead of the start\length params
* 3) support an ignore list of symbols
*/
public class GenDbgHlp : IDisposable
{
private static class Win32
{
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
}
// config
const string modulename = "libgenplusgx.dll";
const string symbolname = @"D:\encodes\bizhawksrc\genplus-gx\libretro\msvc\Debug\vars.txt";
const int start = 0x0c7d8000 - 0x0c540000;
const int length = 0x01082000;
bool disposed = false;
public void Dispose()
{
if (!disposed)
{
Win32.FreeLibrary(DllBase);
DllBase = IntPtr.Zero;
disposed = true;
}
}
IntPtr DllBase;
List<Symbol> SymbolsByAddr = new List<Symbol>();
Dictionary<string, Symbol> SymbolsByName = new Dictionary<string, Symbol>();
byte[][] data = new byte[10][];
public void SaveState(int statenum)
{
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
if (data[statenum] == null)
data[statenum] = new byte[length];
Marshal.Copy(DllBase + start, data[statenum], 0, length);
Console.WriteLine("State {0} saved", statenum);
}
unsafe public void Cmp(int statex, int statey)
{
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
List<Tuple<int, int>> bads = new List<Tuple<int, int>>();
byte[] x = data[statex];
byte[] y = data[statey];
if (x == null || y == null)
{
Console.WriteLine("Missing State!");
return;
}
bool inrange = false;
int startsec = 0;
fixed (byte* p0 = &x[0])
fixed (byte* p1 = &y[0])
{
for (int i = 0; i < length; i++)
{
if (!inrange)
{
if (p0[i] != p1[i])
{
startsec = i;
inrange = true;
}
}
else
{
if (p0[i] == p1[i])
{
bads.Add(new Tuple<int, int>(startsec, i));
inrange = false;
}
}
}
}
if (inrange)
bads.Add(new Tuple<int, int>(startsec, length));
for (int i = 0; i < bads.Count; i++)
{
IntPtr addr = (IntPtr)(bads[i].Item1 + start);
int len = bads[i].Item2 - bads[i].Item1;
var ss = Find(addr, len);
Console.WriteLine("0x{0:X8}[0x{1}]", (int)addr, len);
foreach (var sym in ss)
Console.WriteLine(sym);
Console.WriteLine();
}
if (bads.Count == 0)
Console.WriteLine("Clean!");
}
public GenDbgHlp()
{
using (StreamReader sr = new StreamReader(symbolname))
{
string line;
while ((line = sr.ReadLine()) != null)
{
Symbol sym = Symbol.FromString(line);
SymbolsByAddr.Add(sym);
SymbolsByName.Add(sym.name, sym);
}
SymbolsByAddr.Sort();
}
DllBase = Win32.LoadLibrary(modulename);
if (DllBase == IntPtr.Zero)
throw new Exception();
}
public List<Symbol> Find(IntPtr addr, int length)
{
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
Symbol min = new Symbol { addr = addr };
Symbol max = new Symbol { addr = addr + length };
int minidx = SymbolsByAddr.BinarySearch(min);
if (minidx < 0)
{
minidx = ~minidx;
// inexact matches return the first larger value, so find the next smallset one
if (minidx > 0)
minidx--;
}
int maxidx = SymbolsByAddr.BinarySearch(max);
if (maxidx < 0)
{
maxidx = ~maxidx;
if (maxidx > 0)
maxidx--;
}
return SymbolsByAddr.GetRange(minidx, maxidx - minidx + 1);
}
public struct Symbol : IComparable<Symbol>
{
public IntPtr addr;
public string section;
public string name;
public static Symbol FromString(string s)
{
string[] ss = s.Split(',');
if (ss.Length != 4)
throw new Exception();
if (!ss[1].StartsWith("0x"))
throw new Exception();
Symbol ret = new Symbol
{
addr = (IntPtr)int.Parse(ss[1].Substring(2), System.Globalization.NumberStyles.AllowHexSpecifier),
section = ss[3],
name = ss[0]
};
return ret;
}
public int CompareTo(Symbol other)
{
return (int)this.addr - (int)other.addr;
}
public override string ToString()
{
return string.Format("0x{0:X8} {1} ({2})", (int)addr, name, section);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
/*
* how to use:
* 0) get https://code.google.com/p/pdbparse/
* 1) set modulename to the name of the dll file.
* 2) set symbolname to the name of a file that you produced by executing the following command:
* pdb_print_gvars.py [module pdb file] 0x00000000 > [output file]
* 3) set start to an address (relative to the beginning of the dll) to start scanning
* 4) set length to the byte length of the scan area
* 5) instantiate a GenDbWind, and use it to control the scanner while you manipulate the dll into various configurations.
*
* ideas for modification:
* 1) unhardcode config parameters and allow modifying them through the interface
* 2) read section sizes and positions from the dll itself instead of the start\length params
* 3) support an ignore list of symbols
*/
public class GenDbgHlp : IDisposable
{
private static class Win32
{
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
}
// config
const string modulename = "libgenplusgx.dll";
const string symbolname = @"D:\encodes\bizhawksrc\genplus-gx\libretro\msvc\Debug\vars.txt";
const int start = 0x0c7d8000 - 0x0c540000;
const int length = 0x01082000;
bool disposed = false;
public void Dispose()
{
if (!disposed)
{
Win32.FreeLibrary(DllBase);
DllBase = IntPtr.Zero;
disposed = true;
}
}
IntPtr DllBase;
List<Symbol> SymbolsByAddr = new List<Symbol>();
Dictionary<string, Symbol> SymbolsByName = new Dictionary<string, Symbol>();
byte[][] data = new byte[10][];
public void SaveState(int statenum)
{
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
if (data[statenum] == null)
data[statenum] = new byte[length];
Marshal.Copy(DllBase + start, data[statenum], 0, length);
Console.WriteLine("State {0} saved", statenum);
}
unsafe public void Cmp(int statex, int statey)
{
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
List<Tuple<int, int>> bads = new List<Tuple<int, int>>();
byte[] x = data[statex];
byte[] y = data[statey];
if (x == null || y == null)
{
Console.WriteLine("Missing State!");
return;
}
bool inrange = false;
int startsec = 0;
fixed (byte* p0 = &x[0])
fixed (byte* p1 = &y[0])
{
for (int i = 0; i < length; i++)
{
if (!inrange)
{
if (p0[i] != p1[i])
{
startsec = i;
inrange = true;
}
}
else
{
if (p0[i] == p1[i])
{
bads.Add(new Tuple<int, int>(startsec, i));
inrange = false;
}
}
}
}
if (inrange)
bads.Add(new Tuple<int, int>(startsec, length));
for (int i = 0; i < bads.Count; i++)
{
IntPtr addr = (IntPtr)(bads[i].Item1 + start);
int len = bads[i].Item2 - bads[i].Item1;
var ss = Find(addr, len);
Console.WriteLine("0x{0:X8}[0x{1}]", (int)addr, len);
foreach (var sym in ss)
Console.WriteLine(sym);
Console.WriteLine();
}
if (bads.Count == 0)
Console.WriteLine("Clean!");
}
public GenDbgHlp()
{
using (StreamReader sr = new StreamReader(symbolname))
{
string line;
while ((line = sr.ReadLine()) != null)
{
Symbol sym = Symbol.FromString(line);
SymbolsByAddr.Add(sym);
SymbolsByName.Add(sym.name, sym);
}
SymbolsByAddr.Sort();
}
DllBase = Win32.LoadLibrary(modulename);
if (DllBase == IntPtr.Zero)
throw new Exception();
}
public List<Symbol> Find(IntPtr addr, int length)
{
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
Symbol min = new Symbol { addr = addr };
Symbol max = new Symbol { addr = addr + length };
int minidx = SymbolsByAddr.BinarySearch(min);
if (minidx < 0)
{
minidx = ~minidx;
// inexact matches return the first larger value, so find the next smallset one
if (minidx > 0)
minidx--;
}
int maxidx = SymbolsByAddr.BinarySearch(max);
if (maxidx < 0)
{
maxidx = ~maxidx;
if (maxidx > 0)
maxidx--;
}
return SymbolsByAddr.GetRange(minidx, maxidx - minidx + 1);
}
public struct Symbol : IComparable<Symbol>
{
public IntPtr addr;
public string section;
public string name;
public static Symbol FromString(string s)
{
string[] ss = s.Split(',');
if (ss.Length != 4)
throw new Exception();
if (!ss[1].StartsWith("0x"))
throw new Exception();
Symbol ret = new Symbol
{
addr = (IntPtr)int.Parse(ss[1].Substring(2), System.Globalization.NumberStyles.AllowHexSpecifier),
section = ss[3],
name = ss[0]
};
return ret;
}
public int CompareTo(Symbol other)
{
return (int)this.addr - (int)other.addr;
}
public override string ToString()
{
return string.Format("0x{0:X8} {1} ({2})", (int)addr, name, section);
}
}
}
}

View File

@ -1,354 +1,355 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using BizHawk.Emulation.Common.BizInvoke;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
public abstract class LibGPGX
{
public const string DllName = "libgenplusgx.dll";
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_get_video(out int w, out int h, out int pitch, ref IntPtr buffer);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_get_audio(ref int n, ref IntPtr buffer);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int load_archive_cb(string filename, IntPtr buffer, int maxsize);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_advance();
public enum Region : int
{
Autodetect = 0,
USA = 1,
Europe = 2,
Japan_NTSC = 3,
Japan_PAL = 4
}
[StructLayout(LayoutKind.Sequential)]
public class InitSettings
{
public enum FilterType : byte
{
None = 0,
LowPass = 1,
ThreeBand = 2
}
public FilterType Filter;
public ushort LowPassRange;
public short LowFreq;
public short HighFreq;
public short LowGain;
public short MidGain;
public short HighGain;
public uint BackdropColor;
}
[BizImport(CallingConvention.Cdecl, Compatibility=true)]
public abstract bool gpgx_init(string feromextension, load_archive_cb feload_archive_cb, bool sixbutton, INPUT_SYSTEM system_a, INPUT_SYSTEM system_b, Region region, [In]InitSettings settings);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_get_fps(ref int num, ref int den);
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
public abstract bool gpgx_get_control([Out]InputData dest, int bytes);
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
public abstract bool gpgx_put_control([In]InputData src, int bytes);
[BizImport(CallingConvention.Cdecl)]
public abstract IntPtr gpgx_get_sram(ref int size);
[BizImport(CallingConvention.Cdecl)]
public abstract bool gpgx_put_sram(byte[] data, int size);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_clear_sram();
public const int MIN_MEM_DOMAIN = 0;
public const int MAX_MEM_DOMAIN = 13;
[BizImport(CallingConvention.Cdecl)]
// apparently, if you use built in string marshalling, the interop will assume that
// the unmanaged char pointer was allocated in hglobal and try to free it that way
public abstract IntPtr gpgx_get_memdom(int which, ref IntPtr area, ref int size);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_reset(bool hard);
public const int MAX_DEVICES = 8;
public enum INPUT_SYSTEM : byte
{
SYSTEM_NONE = 0, // unconnected port
SYSTEM_MD_GAMEPAD = 1, // single 3-buttons or 6-buttons Control Pad
SYSTEM_MOUSE = 2, // Sega Mouse
SYSTEM_MENACER = 3, // Sega Menacer -- port B only
SYSTEM_JUSTIFIER = 4, // Konami Justifiers -- port B only
SYSTEM_XE_A1P = 5, // XE-A1P analog controller -- port A only
SYSTEM_ACTIVATOR = 6, // Sega Activator
SYSTEM_MS_GAMEPAD = 7, // single 2-buttons Control Pad -- Master System
SYSTEM_LIGHTPHASER = 8, // Sega Light Phaser -- Master System
SYSTEM_PADDLE = 9, // Sega Paddle Control -- Master System
SYSTEM_SPORTSPAD = 10, // Sega Sports Pad -- Master System
SYSTEM_TEAMPLAYER = 11, // Multi Tap -- Sega TeamPlayer
SYSTEM_WAYPLAY = 12, // Multi Tap -- EA 4-Way Play -- use both ports
};
public enum INPUT_DEVICE : byte
{
DEVICE_NONE = 0xff, // unconnected device = fixed ID for Team Player)
DEVICE_PAD3B = 0x00, // 3-buttons Control Pad = fixed ID for Team Player)
DEVICE_PAD6B = 0x01, // 6-buttons Control Pad = fixed ID for Team Player)
DEVICE_PAD2B = 0x02, // 2-buttons Control Pad
DEVICE_MOUSE = 0x03, // Sega Mouse
DEVICE_LIGHTGUN = 0x04, // Sega Light Phaser, Menacer or Konami Justifiers
DEVICE_PADDLE = 0x05, // Sega Paddle Control
DEVICE_SPORTSPAD = 0x06,// Sega Sports Pad
DEVICE_PICO = 0x07, // PICO tablet
DEVICE_TEREBI = 0x08, // Terebi Oekaki tablet
DEVICE_XE_A1P = 0x09, // XE-A1P analog controller
DEVICE_ACTIVATOR = 0x0a,// Activator
};
public enum CDLog_AddrType
{
MDCART, RAM68k, RAMZ80, SRAM,
};
[Flags]
public enum CDLog_Flags
{
Exec68k = 0x01,
Data68k = 0x04,
ExecZ80First = 0x08,
ExecZ80Operand = 0x10,
DataZ80 = 0x20,
DMASource = 0x40,
};
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void input_cb();
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_set_input_callback(input_cb cb);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void mem_cb(uint addr);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void CDCallback(int addr, CDLog_AddrType addrtype, CDLog_Flags flags);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_set_mem_callback(mem_cb read, mem_cb write, mem_cb exec);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_set_cd_callback(CDCallback cd);
/// <summary>
/// not every flag is valid for every device!
/// </summary>
[Flags]
public enum INPUT_KEYS : ushort
{
/* Default Input bitmasks */
INPUT_MODE = 0x0800,
INPUT_X = 0x0400,
INPUT_Y = 0x0200,
INPUT_Z = 0x0100,
INPUT_START = 0x0080,
INPUT_A = 0x0040,
INPUT_C = 0x0020,
INPUT_B = 0x0010,
INPUT_RIGHT = 0x0008,
INPUT_LEFT = 0x0004,
INPUT_DOWN = 0x0002,
INPUT_UP = 0x0001,
/* Master System specific bitmasks */
INPUT_BUTTON2 = 0x0020,
INPUT_BUTTON1 = 0x0010,
/* Mega Mouse specific bitmask */
INPUT_MOUSE_START = 0x0080,
INPUT_MOUSE_CENTER = 0x0040,
INPUT_MOUSE_RIGHT = 0x0020,
INPUT_MOUSE_LEFT = 0x0010,
/* Pico hardware specific bitmask */
INPUT_PICO_PEN = 0x0080,
INPUT_PICO_RED = 0x0010,
/* XE-1AP specific bitmask */
INPUT_XE_E1 = 0x0800,
INPUT_XE_E2 = 0x0400,
INPUT_XE_START = 0x0200,
INPUT_XE_SELECT = 0x0100,
INPUT_XE_A = 0x0080,
INPUT_XE_B = 0x0040,
INPUT_XE_C = 0x0020,
INPUT_XE_D = 0x0010,
/* Activator specific bitmasks */
INPUT_ACTIVATOR_8U = 0x8000,
INPUT_ACTIVATOR_8L = 0x4000,
INPUT_ACTIVATOR_7U = 0x2000,
INPUT_ACTIVATOR_7L = 0x1000,
INPUT_ACTIVATOR_6U = 0x0800,
INPUT_ACTIVATOR_6L = 0x0400,
INPUT_ACTIVATOR_5U = 0x0200,
INPUT_ACTIVATOR_5L = 0x0100,
INPUT_ACTIVATOR_4U = 0x0080,
INPUT_ACTIVATOR_4L = 0x0040,
INPUT_ACTIVATOR_3U = 0x0020,
INPUT_ACTIVATOR_3L = 0x0010,
INPUT_ACTIVATOR_2U = 0x0008,
INPUT_ACTIVATOR_2L = 0x0004,
INPUT_ACTIVATOR_1U = 0x0002,
INPUT_ACTIVATOR_1L = 0x0001,
/* Menacer */
INPUT_MENACER_TRIGGER = 0x0040,
INPUT_MENACER_START = 0x0080,
};
[StructLayout(LayoutKind.Sequential)]
public class InputData
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public readonly INPUT_SYSTEM[] system = new INPUT_SYSTEM[2];
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES)]
public readonly INPUT_DEVICE[] dev = new INPUT_DEVICE[MAX_DEVICES];
/// <summary>
/// digital inputs
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES)]
public readonly INPUT_KEYS[] pad = new INPUT_KEYS[MAX_DEVICES];
/// <summary>
/// analog (x/y)
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES * 2)]
public readonly short[] analog = new short[MAX_DEVICES * 2];
/// <summary>
/// gun horizontal offset
/// </summary>
public int x_offset;
/// <summary>
/// gun vertical offset
/// </summary>
public int y_offset;
public void ClearAllBools()
{
for (int i = 0; i < pad.Length; i++)
pad[i] = 0;
}
}
public const int CD_MAX_TRACKS = 100;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void cd_read_cb(int lba, IntPtr dest, bool audio);
[StructLayout(LayoutKind.Sequential)]
public struct CDTrack
{
public int start;
public int end;
}
[StructLayout(LayoutKind.Sequential)]
public class CDData
{
public int end;
public int last;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CD_MAX_TRACKS)]
public readonly CDTrack[] tracks = new CDTrack[CD_MAX_TRACKS];
}
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_set_cdd_callback(cd_read_cb cddcb);
[StructLayout(LayoutKind.Sequential)]
public struct VDPNameTable
{
public int Width; // in cells
public int Height; // in cells
public int Baseaddr;
}
[StructLayout(LayoutKind.Sequential)]
public class VDPView
{
public IntPtr VRAM;
public IntPtr PatternCache;
public IntPtr ColorCache;
public VDPNameTable NTA;
public VDPNameTable NTB;
public VDPNameTable NTW;
}
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
public abstract void gpgx_get_vdp_view([Out] VDPView view);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_poke_vram(int addr, byte value);
/// <summary>
/// regenerate whatever portions of the bg pattern cache are currently dirty.
/// </summary>
[BizImport(CallingConvention.Cdecl)] // the core will handle this itself; you only need to call this when using the cache for your own purposes
public abstract void gpgx_flush_vram();
/// <summary>
/// mark the bg pattern cache as dirty
/// </summary>
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_invalidate_pattern_cache();
[StructLayout(LayoutKind.Sequential)]
public struct RegisterInfo
{
public int Value;
public IntPtr Name;
}
[BizImport(CallingConvention.Cdecl)]
public abstract int gpgx_getmaxnumregs();
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
public abstract int gpgx_getregs([Out] RegisterInfo[] regs);
[Flags]
public enum DrawMask : int
{
BGA = 1,
BGB = 2,
BGW = 4,
Obj = 8,
Backdrop = 16
}
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_set_draw_mask(DrawMask mask);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_write_m68k_bus(uint addr, byte data);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_write_s68k_bus(uint addr, byte data);
[BizImport(CallingConvention.Cdecl)]
public abstract byte gpgx_peek_m68k_bus(uint addr);
[BizImport(CallingConvention.Cdecl)]
public abstract byte gpgx_peek_s68k_bus(uint addr);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
public static class LibGPGX
{
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gpgx_get_video(out int w, out int h, out int pitch, ref IntPtr buffer);
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gpgx_get_audio(ref int n, ref IntPtr buffer);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int load_archive_cb(string filename, IntPtr buffer, int maxsize);
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gpgx_advance();
public enum Region : int
{
Autodetect = 0,
USA = 1,
Europe = 2,
Japan_NTSC = 3,
Japan_PAL = 4
}
[StructLayout(LayoutKind.Sequential)]
public class InitSettings
{
public enum FilterType : byte
{
None = 0,
LowPass = 1,
ThreeBand = 2
}
public FilterType Filter;
public ushort LowPassRange;
public short LowFreq;
public short HighFreq;
public short LowGain;
public short MidGain;
public short HighGain;
public uint BackdropColor;
}
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool gpgx_init(string feromextension, load_archive_cb feload_archive_cb, bool sixbutton, INPUT_SYSTEM system_a, INPUT_SYSTEM system_b, Region region, [In]InitSettings settings);
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gpgx_get_fps(ref int num, ref int den);
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int gpgx_state_max_size();
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int gpgx_state_size(byte[] dest, int size);
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool gpgx_state_save(byte[] dest, int size);
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool gpgx_state_load(byte[] src, int size);
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool gpgx_get_control([Out]InputData dest, int bytes);
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool gpgx_put_control([In]InputData src, int bytes);
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gpgx_get_sram(ref IntPtr area, ref int size);
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gpgx_clear_sram();
public const int MIN_MEM_DOMAIN = 0;
public const int MAX_MEM_DOMAIN = 13;
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
// apparently, if you use built in string marshalling, the interop will assume that
// the unmanaged char pointer was allocated in hglobal and try to free it that way
public static extern IntPtr gpgx_get_memdom(int which, ref IntPtr area, ref int size);
// call this before reading sram returned by gpgx_get_sram()
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gpgx_sram_prepread();
// call this after writing sram returned by gpgx_get_sram()
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gpgx_sram_commitwrite();
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gpgx_reset(bool hard);
public const int MAX_DEVICES = 8;
public enum INPUT_SYSTEM : byte
{
SYSTEM_NONE = 0, // unconnected port
SYSTEM_MD_GAMEPAD = 1, // single 3-buttons or 6-buttons Control Pad
SYSTEM_MOUSE = 2, // Sega Mouse
SYSTEM_MENACER = 3, // Sega Menacer -- port B only
SYSTEM_JUSTIFIER = 4, // Konami Justifiers -- port B only
SYSTEM_XE_A1P = 5, // XE-A1P analog controller -- port A only
SYSTEM_ACTIVATOR = 6, // Sega Activator
SYSTEM_MS_GAMEPAD = 7, // single 2-buttons Control Pad -- Master System
SYSTEM_LIGHTPHASER = 8, // Sega Light Phaser -- Master System
SYSTEM_PADDLE = 9, // Sega Paddle Control -- Master System
SYSTEM_SPORTSPAD = 10, // Sega Sports Pad -- Master System
SYSTEM_TEAMPLAYER = 11, // Multi Tap -- Sega TeamPlayer
SYSTEM_WAYPLAY = 12, // Multi Tap -- EA 4-Way Play -- use both ports
};
public enum INPUT_DEVICE : byte
{
DEVICE_NONE = 0xff, // unconnected device = fixed ID for Team Player)
DEVICE_PAD3B = 0x00, // 3-buttons Control Pad = fixed ID for Team Player)
DEVICE_PAD6B = 0x01, // 6-buttons Control Pad = fixed ID for Team Player)
DEVICE_PAD2B = 0x02, // 2-buttons Control Pad
DEVICE_MOUSE = 0x03, // Sega Mouse
DEVICE_LIGHTGUN = 0x04, // Sega Light Phaser, Menacer or Konami Justifiers
DEVICE_PADDLE = 0x05, // Sega Paddle Control
DEVICE_SPORTSPAD = 0x06,// Sega Sports Pad
DEVICE_PICO = 0x07, // PICO tablet
DEVICE_TEREBI = 0x08, // Terebi Oekaki tablet
DEVICE_XE_A1P = 0x09, // XE-A1P analog controller
DEVICE_ACTIVATOR = 0x0a,// Activator
};
public enum CDLog_AddrType
{
MDCART, RAM68k, RAMZ80, SRAM,
};
[Flags]
public enum CDLog_Flags
{
Exec68k = 0x01,
Data68k = 0x04,
ExecZ80First = 0x08,
ExecZ80Operand = 0x10,
DataZ80 = 0x20,
DMASource = 0x40,
};
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void input_cb();
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gpgx_set_input_callback(input_cb cb);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void mem_cb(uint addr);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void CDCallback(int addr, CDLog_AddrType addrtype, CDLog_Flags flags);
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gpgx_set_mem_callback(mem_cb read, mem_cb write, mem_cb exec);
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gpgx_set_cd_callback(CDCallback cd);
/// <summary>
/// not every flag is valid for every device!
/// </summary>
[Flags]
public enum INPUT_KEYS : ushort
{
/* Default Input bitmasks */
INPUT_MODE = 0x0800,
INPUT_X = 0x0400,
INPUT_Y = 0x0200,
INPUT_Z = 0x0100,
INPUT_START = 0x0080,
INPUT_A = 0x0040,
INPUT_C = 0x0020,
INPUT_B = 0x0010,
INPUT_RIGHT = 0x0008,
INPUT_LEFT = 0x0004,
INPUT_DOWN = 0x0002,
INPUT_UP = 0x0001,
/* Master System specific bitmasks */
INPUT_BUTTON2 = 0x0020,
INPUT_BUTTON1 = 0x0010,
/* Mega Mouse specific bitmask */
INPUT_MOUSE_START = 0x0080,
INPUT_MOUSE_CENTER = 0x0040,
INPUT_MOUSE_RIGHT = 0x0020,
INPUT_MOUSE_LEFT = 0x0010,
/* Pico hardware specific bitmask */
INPUT_PICO_PEN = 0x0080,
INPUT_PICO_RED = 0x0010,
/* XE-1AP specific bitmask */
INPUT_XE_E1 = 0x0800,
INPUT_XE_E2 = 0x0400,
INPUT_XE_START = 0x0200,
INPUT_XE_SELECT = 0x0100,
INPUT_XE_A = 0x0080,
INPUT_XE_B = 0x0040,
INPUT_XE_C = 0x0020,
INPUT_XE_D = 0x0010,
/* Activator specific bitmasks */
INPUT_ACTIVATOR_8U = 0x8000,
INPUT_ACTIVATOR_8L = 0x4000,
INPUT_ACTIVATOR_7U = 0x2000,
INPUT_ACTIVATOR_7L = 0x1000,
INPUT_ACTIVATOR_6U = 0x0800,
INPUT_ACTIVATOR_6L = 0x0400,
INPUT_ACTIVATOR_5U = 0x0200,
INPUT_ACTIVATOR_5L = 0x0100,
INPUT_ACTIVATOR_4U = 0x0080,
INPUT_ACTIVATOR_4L = 0x0040,
INPUT_ACTIVATOR_3U = 0x0020,
INPUT_ACTIVATOR_3L = 0x0010,
INPUT_ACTIVATOR_2U = 0x0008,
INPUT_ACTIVATOR_2L = 0x0004,
INPUT_ACTIVATOR_1U = 0x0002,
INPUT_ACTIVATOR_1L = 0x0001,
/* Menacer */
INPUT_MENACER_TRIGGER = 0x0040,
INPUT_MENACER_START = 0x0080,
};
[StructLayout(LayoutKind.Sequential)]
public class InputData
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public readonly INPUT_SYSTEM[] system = new INPUT_SYSTEM[2];
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES)]
public readonly INPUT_DEVICE[] dev = new INPUT_DEVICE[MAX_DEVICES];
/// <summary>
/// digital inputs
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES)]
public readonly INPUT_KEYS[] pad = new INPUT_KEYS[MAX_DEVICES];
/// <summary>
/// analog (x/y)
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES * 2)]
public readonly short[] analog = new short[MAX_DEVICES * 2];
/// <summary>
/// gun horizontal offset
/// </summary>
public int x_offset;
/// <summary>
/// gun vertical offset
/// </summary>
public int y_offset;
public void ClearAllBools()
{
for (int i = 0; i < pad.Length; i++)
pad[i] = 0;
}
}
public const int CD_MAX_TRACKS = 100;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void cd_read_cb(int lba, IntPtr dest, bool audio);
[StructLayout(LayoutKind.Sequential)]
public struct CDTrack
{
public int start;
public int end;
}
[StructLayout(LayoutKind.Sequential)]
public class CDData
{
public int end;
public int last;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CD_MAX_TRACKS)]
public readonly CDTrack[] tracks = new CDTrack[CD_MAX_TRACKS];
public cd_read_cb readcallback;
}
[StructLayout(LayoutKind.Sequential)]
public struct VDPNameTable
{
public int Width; // in cells
public int Height; // in cells
public int Baseaddr;
}
[StructLayout(LayoutKind.Sequential)]
public class VDPView
{
public IntPtr VRAM;
public IntPtr PatternCache;
public IntPtr ColorCache;
public VDPNameTable NTA;
public VDPNameTable NTB;
public VDPNameTable NTW;
}
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gpgx_get_vdp_view([Out] VDPView view);
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gpgx_poke_vram(int addr, byte value);
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gpgx_flush_vram();
[StructLayout(LayoutKind.Sequential)]
public struct RegisterInfo
{
public int Value;
public IntPtr Name;
}
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int gpgx_getmaxnumregs();
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int gpgx_getregs([Out] RegisterInfo[] regs);
[Flags]
public enum DrawMask : int
{
BGA = 1,
BGB = 2,
BGW = 4,
Obj = 8,
Backdrop = 16
}
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gpgx_set_draw_mask(DrawMask mask);
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gpgx_write_m68k_bus(uint addr, byte data);
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gpgx_write_s68k_bus(uint addr, byte data);
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern byte gpgx_peek_m68k_bus(uint addr);
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern byte gpgx_peek_s68k_bus(uint addr);
}
}

View File

@ -0,0 +1,53 @@
using System;
using System.IO;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
{
public partial class GPGX : ICodeDataLogger
{
public void SetCDL(CodeDataLog cdl)
{
CDL = cdl;
if (cdl == null) Core.gpgx_set_cd_callback(null);
else Core.gpgx_set_cd_callback(CDCallback);
}
public void NewCDL(CodeDataLog cdl)
{
cdl["MD CART"] = new byte[MemoryDomains["MD CART"].Size];
cdl["68K RAM"] = new byte[MemoryDomains["68K RAM"].Size];
cdl["Z80 RAM"] = new byte[MemoryDomains["Z80 RAM"].Size];
if (MemoryDomains.Has("SRAM"))
cdl["SRAM"] = new byte[MemoryDomains["SRAM"].Size];
cdl.SubType = "GEN";
cdl.SubVer = 0;
}
// TODO: we have Disassembling now
// not supported
public void DisassembleCDL(Stream s, CodeDataLog cdl) { }
private CodeDataLog CDL;
private void CDCallbackProc(int addr, LibGPGX.CDLog_AddrType addrtype, LibGPGX.CDLog_Flags flags)
{
//TODO - hard reset makes CDL go nuts.
if (CDL == null) return;
if (!CDL.Active) return;
string key;
switch (addrtype)
{
case LibGPGX.CDLog_AddrType.MDCART: key = "MD CART"; break;
case LibGPGX.CDLog_AddrType.RAM68k: key = "68K RAM"; break;
case LibGPGX.CDLog_AddrType.RAMZ80: key = "Z80 RAM"; break;
case LibGPGX.CDLog_AddrType.SRAM: key = "SRAM"; break;
default: throw new InvalidOperationException("Lagrangian earwax incident");
}
CDL[key][addr] |= (byte)flags;
}
}
}

View File

@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
{
public partial class GPGX : IDebuggable
{
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
{
LibGPGX.RegisterInfo[] regs = new LibGPGX.RegisterInfo[Core.gpgx_getmaxnumregs()];
int n = Core.gpgx_getregs(regs);
if (n > regs.Length)
throw new InvalidOperationException("A buffer overrun has occured!");
var ret = new Dictionary<string, RegisterValue>();
for (int i = 0; i < n; i++)
{
// el hacko
string name = Marshal.PtrToStringAnsi(regs[i].Name);
byte size = 32;
if (name.Contains("68K SR") || name.StartsWith("Z80"))
size = 16;
ret[name] = new RegisterValue((ulong)regs[i].Value, size);
}
return ret;
}
[FeatureNotImplemented]
public void SetCpuRegister(string register, int value)
{
throw new NotImplementedException();
}
public IMemoryCallbackSystem MemoryCallbacks
{
get { return _memoryCallbacks; }
}
public bool CanStep(StepType type) { return false; }
[FeatureNotImplemented]
public void Step(StepType type) { throw new NotImplementedException(); }
private readonly MemoryCallbackSystem _memoryCallbacks = new MemoryCallbackSystem();
private LibGPGX.mem_cb ExecCallback;
private LibGPGX.mem_cb ReadCallback;
private LibGPGX.mem_cb WriteCallback;
private LibGPGX.CDCallback CDCallback;
private void InitMemCallbacks()
{
ExecCallback = new LibGPGX.mem_cb(a => MemoryCallbacks.CallExecutes(a));
ReadCallback = new LibGPGX.mem_cb(a => MemoryCallbacks.CallReads(a));
WriteCallback = new LibGPGX.mem_cb(a => MemoryCallbacks.CallWrites(a));
_memoryCallbacks.ActiveChanged += RefreshMemCallbacks;
}
private void RefreshMemCallbacks()
{
Core.gpgx_set_mem_callback(
MemoryCallbacks.HasReads ? ReadCallback : null,
MemoryCallbacks.HasWrites ? WriteCallback : null,
MemoryCallbacks.HasExecutes ? ExecCallback : null);
}
private void KillMemCallbacks()
{
Core.gpgx_set_mem_callback(null, null, null);
}
}
}

View File

@ -0,0 +1,45 @@
using System.Collections.Generic;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components.M68000;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
{
public partial class GPGX : IDisassemblable
{
public string Cpu
{
get
{
return "M68000";
}
set
{
}
}
public string PCRegisterName
{
get { return "M68K PC"; }
}
public IEnumerable<string> AvailableCpus
{
get { yield return "M68000"; }
}
public string Disassemble(MemoryDomain m, uint addr, out int length)
{
_disassemblerInstance.ReadWord = (a) => (short)m.PeekUshort(a, m.EndianType == MemoryDomain.Endian.Big);
_disassemblerInstance.ReadByte = (a) => (sbyte)m.PeekByte(a);
_disassemblerInstance.ReadLong = (a) => (int)m.PeekUint(a, m.EndianType == MemoryDomain.Endian.Big);
var info = _disassemblerInstance.Disassemble((int)addr);
length = info.Length;
return string.Format("{0:X4} {1,-7} {2}", info.RawBytes.Substring(0, 4), info.Mnemonic, info.Args);
}
// TODO: refactor MC6800's disassembler to be a static call
private MC68000 _disassemblerInstance = new MC68000();
}
}

View File

@ -0,0 +1,12 @@
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
{
public partial class GPGX : IDriveLight
{
public bool DriveLightEnabled { get; private set; }
public bool DriveLightOn { get; private set; }
private bool _drivelight;
}
}

View File

@ -0,0 +1,100 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
{
public partial class GPGX : IEmulator
{
public IEmulatorServiceProvider ServiceProvider { get; private set; }
public ISoundProvider SoundProvider { get { return null; } }
public ISyncSoundProvider SyncSoundProvider
{
get { return this; }
}
public bool StartAsyncSound()
{
return false;
}
public void EndAsyncSound() { }
public ControllerDefinition ControllerDefinition { get; private set; }
public IController Controller { get; set; }
// TODO: use render and rendersound
public void FrameAdvance(bool render, bool rendersound = true)
{
if (Controller["Reset"])
Core.gpgx_reset(false);
if (Controller["Power"])
Core.gpgx_reset(true);
// this shouldn't be needed, as nothing has changed
// if (!Core.gpgx_get_control(input, inputsize))
// throw new Exception("gpgx_get_control() failed!");
ControlConverter.ScreenWidth = vwidth;
ControlConverter.ScreenHeight = vheight;
ControlConverter.Convert(Controller, input);
if (!Core.gpgx_put_control(input, inputsize))
throw new Exception("gpgx_put_control() failed!");
IsLagFrame = true;
Frame++;
_drivelight = false;
Core.gpgx_advance();
UpdateVideo();
update_audio();
if (IsLagFrame)
LagCount++;
if (CD != null)
DriveLightOn = _drivelight;
}
public int Frame { get; private set; }
public string SystemId
{
get { return "GEN"; }
}
public bool DeterministicEmulation
{
get { return true; }
}
public string BoardName
{
get { return null; }
}
public void ResetCounters()
{
Frame = 0;
IsLagFrame = false;
LagCount = 0;
}
public CoreComm CoreComm { get; private set; }
public void Dispose()
{
if (!disposed)
{
if (Elf != null)
Elf.Dispose();
if (CD != null)
CD.Dispose();
disposed = true;
}
}
}
}

View File

@ -0,0 +1,23 @@
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
{
public partial class GPGX : IInputPollable
{
public int LagCount { get; set; }
public bool IsLagFrame { get; set; }
public IInputCallbackSystem InputCallbacks { get { return _inputCallbacks; } }
private LibGPGX.input_cb InputCallback = null;
private readonly InputCallbackSystem _inputCallbacks = new InputCallbackSystem();
private void input_callback()
{
InputCallbacks.Call();
IsLagFrame = false;
}
}
}

View File

@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
{
public partial class GPGX
{
private IMemoryDomains MemoryDomains;
private unsafe void SetMemoryDomains()
{
var mm = new List<MemoryDomain>();
for (int i = LibGPGX.MIN_MEM_DOMAIN; i <= LibGPGX.MAX_MEM_DOMAIN; i++)
{
IntPtr area = IntPtr.Zero;
int size = 0;
IntPtr pname = Core.gpgx_get_memdom(i, ref area, ref size);
if (area == IntPtr.Zero || pname == IntPtr.Zero || size == 0)
continue;
string name = Marshal.PtrToStringAnsi(pname);
if (name == "VRAM")
{
// vram pokes need to go through hook which invalidates cached tiles
byte* p = (byte*)area;
mm.Add(new MemoryDomainDelegate(name, size, MemoryDomain.Endian.Unknown,
delegate(long addr)
{
if (addr < 0 || addr >= 65536)
throw new ArgumentOutOfRangeException();
return p[addr ^ 1];
},
delegate(long addr, byte val)
{
if (addr < 0 || addr >= 65536)
throw new ArgumentOutOfRangeException();
Core.gpgx_poke_vram(((int)addr) ^ 1, val);
},
wordSize: 2));
}
else
{
// TODO: are the Z80 domains really Swap16 in the core? Check this
mm.Add(MemoryDomain.FromIntPtrSwap16(name, size,
MemoryDomain.Endian.Big, area, name != "MD CART" && name != "CD BOOT ROM"));
}
}
var m68Bus = new MemoryDomainDelegate("M68K BUS", 0x1000000, MemoryDomain.Endian.Big,
delegate (long addr)
{
var a = (uint)addr;
if (a >= 0x1000000)
throw new ArgumentOutOfRangeException();
return Core.gpgx_peek_m68k_bus(a);
},
delegate (long addr, byte val)
{
var a = (uint)addr;
if (a >= 0x1000000)
throw new ArgumentOutOfRangeException();
Core.gpgx_write_m68k_bus(a, val);
}, 2);
mm.Add(m68Bus);
var s68Bus = new MemoryDomainDelegate("S68K BUS", 0x1000000, MemoryDomain.Endian.Big,
delegate (long addr)
{
var a = (uint)addr;
if (a >= 0x1000000)
throw new ArgumentOutOfRangeException();
return Core.gpgx_peek_s68k_bus(a);
},
delegate (long addr, byte val)
{
var a = (uint)addr;
if (a >= 0x1000000)
throw new ArgumentOutOfRangeException();
Core.gpgx_write_s68k_bus(a, val);
}, 2);
if (IsSegaCD)
{
mm.Add(s68Bus);
}
MemoryDomains = new MemoryDomainList(mm);
MemoryDomains.SystemBus = m68Bus;
(ServiceProvider as BasicServiceProvider).Register<IMemoryDomains>(MemoryDomains);
}
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.IO;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using System.Runtime.InteropServices;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
{
public partial class GPGX : ISaveRam
{
public byte[] CloneSaveRam()
{
int size = 0;
IntPtr area = Core.gpgx_get_sram(ref size);
if (size == 0 || area == IntPtr.Zero)
return new byte[0];
byte[] ret = new byte[size];
Marshal.Copy(area, ret, 0, size);
return ret;
}
public void StoreSaveRam(byte[] data)
{
if (!Core.gpgx_put_sram(data, data.Length))
throw new Exception("Core rejected saveram");
}
public bool SaveRamModified
{
get
{
int size = 0;
IntPtr area = Core.gpgx_get_sram(ref size);
return size > 0 && area != IntPtr.Zero;
}
}
}
}

View File

@ -0,0 +1,210 @@
using System.ComponentModel;
using BizHawk.Common;
using BizHawk.Emulation.Common;
using Newtonsoft.Json;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
{
public partial class GPGX : ISettable<GPGX.GPGXSettings, GPGX.GPGXSyncSettings>
{
public GPGXSettings GetSettings()
{
return _settings.Clone();
}
public GPGXSyncSettings GetSyncSettings()
{
return _syncSettings.Clone();
}
public bool PutSettings(GPGXSettings o)
{
bool ret = GPGXSettings.NeedsReboot(_settings, o);
_settings = o;
Core.gpgx_set_draw_mask(_settings.GetDrawMask());
return ret;
}
public bool PutSyncSettings(GPGXSyncSettings o)
{
bool ret = GPGXSyncSettings.NeedsReboot(_syncSettings, o);
_syncSettings = o;
return ret;
}
private GPGXSyncSettings _syncSettings;
private GPGXSettings _settings;
public class GPGXSettings
{
[DeepEqualsIgnore]
[JsonIgnore]
private bool _DrawBGA;
[DisplayName("Background Layer A")]
[Description("True to draw BG layer A")]
[DefaultValue(true)]
public bool DrawBGA { get { return _DrawBGA; } set { _DrawBGA = value; } }
[DeepEqualsIgnore]
[JsonIgnore]
private bool _DrawBGB;
[DisplayName("Background Layer B")]
[Description("True to draw BG layer B")]
[DefaultValue(true)]
public bool DrawBGB { get { return _DrawBGB; } set { _DrawBGB = value; } }
[DeepEqualsIgnore]
[JsonIgnore]
private bool _DrawBGW;
[DisplayName("Background Layer W")]
[Description("True to draw BG layer W")]
[DefaultValue(true)]
public bool DrawBGW { get { return _DrawBGW; } set { _DrawBGW = value; } }
[DeepEqualsIgnore]
[JsonIgnore]
private bool _DrawObj;
[DisplayName("Sprite Layer")]
[Description("True to draw sprite layer")]
[DefaultValue(true)]
public bool DrawObj { get { return _DrawObj; } set { _DrawObj = value; } }
[DeepEqualsIgnore]
[JsonIgnore]
private bool _PadScreen320;
[DisplayName("Pad screen to 320")]
[Description("When using 1:1 aspect ratio, enable to make screen width constant (320) between game modes")]
[DefaultValue(false)]
public bool PadScreen320 { get { return _PadScreen320; } set { _PadScreen320 = value; } }
[DisplayName("Audio Filter")]
[DefaultValue(LibGPGX.InitSettings.FilterType.LowPass)]
public LibGPGX.InitSettings.FilterType Filter { get; set; }
[DisplayName("Low Pass Range")]
[Description("Only active when filter type is lowpass")]
[DefaultValue((ushort)39321)]
public ushort LowPassRange { get; set; }
[DisplayName("Three band low cutoff")]
[Description("Only active when filter type is three band")]
[DefaultValue((short)880)]
public short LowFreq { get; set; }
[DisplayName("Three band high cutoff")]
[Description("Only active when filter type is three band")]
[DefaultValue((short)5000)]
public short HighFreq { get; set; }
[DisplayName("Three band low gain")]
[Description("Only active when filter type is three band")]
[DefaultValue((short)1)]
public short LowGain { get; set; }
[DisplayName("Three band mid gain")]
[Description("Only active when filter type is three band")]
[DefaultValue((short)1)]
public short MidGain { get; set; }
[DisplayName("Three band high gain")]
[Description("Only active when filter type is three band")]
[DefaultValue((short)1)]
public short HighGain { get; set; }
[DeepEqualsIgnore]
[JsonIgnore]
private bool _Backdrop;
[DisplayName("Use custom backdrop color")]
[Description("Filler when layers are off")]
[DefaultValue((bool)false)]
public bool Backdrop { get { return _Backdrop; } set { _Backdrop = value; } }
[DisplayName("Custom backdrop color")]
[Description("Magic pink (0xffff00ff) by default")]
[DefaultValue((uint)0xffff00ff)]
public uint BackdropColor { get; set; }
public GPGXSettings()
{
SettingsUtil.SetDefaultValues(this);
}
public GPGXSettings Clone()
{
return (GPGXSettings)MemberwiseClone();
}
public LibGPGX.DrawMask GetDrawMask()
{
LibGPGX.DrawMask ret = 0;
if (DrawBGA) ret |= LibGPGX.DrawMask.BGA;
if (DrawBGB) ret |= LibGPGX.DrawMask.BGB;
if (DrawBGW) ret |= LibGPGX.DrawMask.BGW;
if (DrawObj) ret |= LibGPGX.DrawMask.Obj;
if (Backdrop) ret |= LibGPGX.DrawMask.Backdrop;
return ret;
}
public static bool NeedsReboot(GPGXSettings x, GPGXSettings y)
{
return !DeepEquality.DeepEquals(x, y);
}
public LibGPGX.InitSettings GetNativeSettings()
{
return new LibGPGX.InitSettings
{
Filter = Filter,
LowPassRange = LowPassRange,
LowFreq = LowFreq,
HighFreq = HighFreq,
LowGain = LowGain,
MidGain = MidGain,
HighGain = HighGain,
BackdropColor = BackdropColor
};
}
}
public class GPGXSyncSettings
{
[DisplayName("Use Six Button Controllers")]
[Description("Controls the type of any attached normal controllers; six button controllers are used if true, otherwise three button controllers. Some games don't work correctly with six button controllers. Not relevant if other controller types are connected.")]
[DefaultValue(true)]
public bool UseSixButton { get; set; }
[DisplayName("Control Type")]
[Description("Sets the type of controls that are plugged into the console. Some games will automatically load with a different control type.")]
[DefaultValue(ControlType.Normal)]
public ControlType ControlType { get; set; }
[DisplayName("Autodetect Region")]
[Description("Sets the region of the emulated console. Many games can run on multiple regions and will behave differently on different ones. Some games may require a particular region.")]
[DefaultValue(LibGPGX.Region.Autodetect)]
public LibGPGX.Region Region { get; set; }
public GPGXSyncSettings()
{
SettingsUtil.SetDefaultValues(this);
}
public GPGXSyncSettings Clone()
{
return (GPGXSyncSettings)MemberwiseClone();
}
public static bool NeedsReboot(GPGXSyncSettings x, GPGXSyncSettings y)
{
return !DeepEquality.DeepEquals(x, y);
}
}
}
}

View File

@ -0,0 +1,70 @@
using System;
using System.IO;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
{
public partial class GPGX : IStatable
{
public bool BinarySaveStatesPreferred
{
get { return true; }
}
public void SaveStateText(TextWriter writer)
{
var temp = SaveStateBinary();
temp.SaveAsHexFast(writer);
// write extra copy of stuff we don't use
writer.WriteLine("Frame {0}", Frame);
}
public void LoadStateText(TextReader reader)
{
string hex = reader.ReadLine();
byte[] state = new byte[hex.Length / 2];
state.ReadFromHexFast(hex);
LoadStateBinary(new BinaryReader(new MemoryStream(state)));
}
public void LoadStateBinary(BinaryReader reader)
{
Elf.LoadStateBinary(reader);
// other variables
Frame = reader.ReadInt32();
LagCount = reader.ReadInt32();
IsLagFrame = reader.ReadBoolean();
// any managed pointers that we sent to the core need to be resent now!
Core.gpgx_set_input_callback(InputCallback);
RefreshMemCallbacks();
Core.gpgx_set_cdd_callback(cd_callback_handle);
Core.gpgx_invalidate_pattern_cache();
UpdateVideo();
}
public void SaveStateBinary(BinaryWriter writer)
{
Elf.SaveStateBinary(writer);
// other variables
writer.Write(Frame);
writer.Write(LagCount);
writer.Write(IsLagFrame);
}
public byte[] SaveStateBinary()
{
var ms = new MemoryStream();
var bw = new BinaryWriter(ms);
SaveStateBinary(bw);
bw.Flush();
ms.Close();
return ms.ToArray();
}
private void InitStateBuffers()
{
}
}
}

View File

@ -0,0 +1,66 @@
using BizHawk.Emulation.Common;
using System;
using System.Collections.Generic;
using System.Text;
using BizHawk.Common.NumberExtensions;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
{
public partial class GPGX
{
private readonly ITraceable Tracer;
public class GPGXTraceBuffer : CallbackBasedTraceBuffer
{
public GPGXTraceBuffer(IDebuggable debuggableCore, IMemoryDomains memoryDomains, IDisassemblable disassembler)
: base(debuggableCore, memoryDomains, disassembler)
{
Header = "M68K: PC, machine code, mnemonic, arguments, registers (D0-D7, A0-A7, SR, USP, status flags)";
}
public override void TraceFromCallback()
{
var regs = DebuggableCore.GetCpuFlagsAndRegisters();
uint pc = (uint)regs["M68K PC"].Value;
var length = 0;
var disasm = Disassembler.Disassemble(MemoryDomains.SystemBus, pc, out length);
var traceInfo = new TraceInfo
{
Disassembly = string.Format("{0:X6}: {1}", pc, disasm)
};
var sb = new StringBuilder();
foreach (var r in regs)
{
if (r.Key.StartsWith("M68K")) // drop Z80 regs until it has its own debugger/tracer
{
if (r.Key != "M68K SP" && r.Key != "M68K ISP" && // copies of a7
r.Key != "M68K PC" && // already present in every line start
r.Key != "M68K IR") // copy of last opcode, already shown in raw bytes
{
sb.Append(
string.Format("{0}:{1} ",
r.Key.Replace("M68K", string.Empty).Trim(),
r.Value.Value.ToHexString(r.Value.BitSize / 4)));
}
}
}
var sr = regs["M68K SR"].Value;
sb.Append(
string.Format("{0}{1}{2}{3}{4}",
(sr & 16) > 0 ? "X" : "x",
(sr & 8) > 0 ? "N" : "n",
(sr & 4) > 0 ? "Z" : "z",
(sr & 2) > 0 ? "V" : "v",
(sr & 1) > 0 ? "C" : "c"));
traceInfo.RegisterInfo = sb.ToString().Trim();
Buffer.Add(traceInfo);
}
}
}
}

View File

@ -0,0 +1,77 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
{
public partial class GPGX : IVideoProvider
{
public int[] GetVideoBuffer() { return vidbuff; }
public int VirtualWidth { get { return 320; } }
public int VirtualHeight { get { return 224; } }
public int BufferWidth { get { return vwidth; } }
public int BufferHeight { get { return vheight; } }
public int BackgroundColor { get { return unchecked((int)0xff000000); } }
private int[] vidbuff = new int[0];
private int vwidth;
private int vheight;
private void UpdateVideoInitial()
{
// hack: you should call update_video() here, but that gives you 256x192 on frame 0
// and we know that we only use GPGX to emulate genesis games that will always be 320x224 immediately afterwards
// so instead, just assume a 320x224 size now; if that happens to be wrong, it'll be fixed soon enough.
vwidth = 320;
vheight = 224;
vidbuff = new int[vwidth * vheight];
for (int i = 0; i < vidbuff.Length; i++)
vidbuff[i] = unchecked((int)0xff000000);
}
private unsafe void UpdateVideo()
{
int gppitch, gpwidth, gpheight;
IntPtr src = IntPtr.Zero;
Core.gpgx_get_video(out gpwidth, out gpheight, out gppitch, ref src);
vwidth = gpwidth;
vheight = gpheight;
if (_settings.PadScreen320 && vwidth == 256)
vwidth = 320;
int xpad = (vwidth - gpwidth) / 2;
int xpad2 = vwidth - gpwidth - xpad;
if (vidbuff.Length < vwidth * vheight)
vidbuff = new int[vwidth * vheight];
int rinc = (gppitch / 4) - gpwidth;
fixed (int* pdst_ = &vidbuff[0])
{
int* pdst = pdst_;
int* psrc = (int*)src;
for (int j = 0; j < gpheight; j++)
{
for (int i = 0; i < xpad; i++)
*pdst++ = unchecked((int)0xff000000);
for (int i = 0; i < gpwidth; i++)
*pdst++ = *psrc++;// | unchecked((int)0xff000000);
for (int i = 0; i < xpad2; i++)
*pdst++ = unchecked((int)0xff000000);
psrc += rinc;
}
}
}
}
}

View File

@ -0,0 +1,404 @@
using System;
using System.Runtime.InteropServices;
using BizHawk.Emulation.Common;
using BizHawk.Common;
using System.IO;
using BizHawk.Emulation.Common.BizInvoke;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
{
[CoreAttributes(
"Genplus-gx64",
"",
isPorted: true,
isReleased: true,
portedVersion: "r874",
portedUrl: "https://code.google.com/p/genplus-gx/",
singleInstance: false
)]
public partial class GPGX : IEmulator, ISyncSoundProvider, IVideoProvider, ISaveRam, IStatable, IRegionable,
IInputPollable, IDebuggable, IDriveLight, ICodeDataLogger, IDisassemblable
{
LibGPGX Core;
ElfRunner Elf;
DiscSystem.Disc CD;
DiscSystem.DiscSectorReader DiscSectorReader;
byte[] romfile;
bool disposed = false;
LibGPGX.load_archive_cb LoadCallback = null;
LibGPGX.InputData input = new LibGPGX.InputData();
public enum ControlType
{
None,
OnePlayer,
Normal,
Xea1p,
Activator,
Teamplayer,
Wayplay,
Mouse
};
[CoreConstructor("GEN")]
public GPGX(CoreComm comm, byte[] file, object Settings, object SyncSettings)
: this(comm, file, null, Settings, SyncSettings)
{
}
public GPGX(CoreComm comm, byte[] rom, DiscSystem.Disc CD, object Settings, object SyncSettings)
{
ServiceProvider = new BasicServiceProvider(this);
// this can influence some things internally (autodetect romtype, etc)
string romextension = "GEN";
// three or six button?
// http://www.sega-16.com/forum/showthread.php?4398-Forgotten-Worlds-giving-you-GAME-OVER-immediately-Fix-inside&highlight=forgotten%20worlds
//hack, don't use
if (rom != null && rom.Length > 32 * 1024 * 1024)
{
throw new InvalidOperationException("ROM too big! Did you try to load a CD as a ROM?");
}
try
{
Elf = new ElfRunner(Path.Combine(comm.CoreFileProvider.DllPath(), "gpgx.elf"), 8 * 1024 * 1024, 36 * 1024 * 1024, 4 * 1024 * 1024);
if (Elf.ShouldMonitor)
Core = BizInvoker.GetInvoker<LibGPGX>(Elf, Elf);
else
Core = BizInvoker.GetInvoker<LibGPGX>(Elf);
_syncSettings = (GPGXSyncSettings)SyncSettings ?? new GPGXSyncSettings();
_settings = (GPGXSettings)Settings ?? new GPGXSettings();
CoreComm = comm;
LoadCallback = new LibGPGX.load_archive_cb(load_archive);
this.romfile = rom;
this.CD = CD;
if (CD != null)
{
this.DiscSectorReader = new DiscSystem.DiscSectorReader(CD);
cd_callback_handle = new LibGPGX.cd_read_cb(CDRead);
Core.gpgx_set_cdd_callback(cd_callback_handle);
}
LibGPGX.INPUT_SYSTEM system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_NONE;
LibGPGX.INPUT_SYSTEM system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_NONE;
switch (_syncSettings.ControlType)
{
case ControlType.None:
default:
break;
case ControlType.Activator:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_ACTIVATOR;
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_ACTIVATOR;
break;
case ControlType.Normal:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
break;
case ControlType.OnePlayer:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
break;
case ControlType.Xea1p:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_XE_A1P;
break;
case ControlType.Teamplayer:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_TEAMPLAYER;
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_TEAMPLAYER;
break;
case ControlType.Wayplay:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_WAYPLAY;
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_WAYPLAY;
break;
case ControlType.Mouse:
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
// seems like mouse in port 1 would be supported, but not both at the same time
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_MOUSE;
break;
}
if (!Core.gpgx_init(romextension, LoadCallback, _syncSettings.UseSixButton, system_a, system_b, _syncSettings.Region, _settings.GetNativeSettings()))
throw new Exception("gpgx_init() failed");
{
int fpsnum = 60;
int fpsden = 1;
Core.gpgx_get_fps(ref fpsnum, ref fpsden);
CoreComm.VsyncNum = fpsnum;
CoreComm.VsyncDen = fpsden;
Region = CoreComm.VsyncRate > 55 ? DisplayType.NTSC : DisplayType.PAL;
}
// compute state size
InitStateBuffers();
SetControllerDefinition();
// pull the default video size from the core
UpdateVideoInitial();
SetMemoryDomains();
InputCallback = new LibGPGX.input_cb(input_callback);
Core.gpgx_set_input_callback(InputCallback);
if (CD != null)
DriveLightEnabled = true;
// process the non-init settings now
PutSettings(_settings);
//TODO - this hits performance, we need to make it controllable
CDCallback = new LibGPGX.CDCallback(CDCallbackProc);
InitMemCallbacks();
KillMemCallbacks();
Tracer = new GPGXTraceBuffer(this, MemoryDomains, this);
(ServiceProvider as BasicServiceProvider).Register<ITraceable>(Tracer);
Elf.Seal();
}
catch
{
Dispose();
throw;
}
}
/// <summary>
/// core callback for file loading
/// </summary>
/// <param name="filename">string identifying file to be loaded</param>
/// <param name="buffer">buffer to load file to</param>
/// <param name="maxsize">maximum length buffer can hold</param>
/// <returns>actual size loaded, or 0 on failure</returns>
int load_archive(string filename, IntPtr buffer, int maxsize)
{
byte[] srcdata = null;
if (buffer == IntPtr.Zero)
{
Console.WriteLine("Couldn't satisfy firmware request {0} because buffer == NULL", filename);
return 0;
}
if (filename == "PRIMARY_ROM")
{
if (romfile == null)
{
Console.WriteLine("Couldn't satisfy firmware request PRIMARY_ROM because none was provided.");
return 0;
}
srcdata = romfile;
}
else if (filename == "PRIMARY_CD" || filename == "SECONDARY_CD")
{
if (filename == "PRIMARY_CD" && romfile != null)
{
Console.WriteLine("Declined to satisfy firmware request PRIMARY_CD because PRIMARY_ROM was provided.");
return 0;
}
else
{
if (CD == null)
{
Console.WriteLine("Couldn't satisfy firmware request {0} because none was provided.", filename);
return 0;
}
srcdata = GetCDData();
if (srcdata.Length != maxsize)
{
Console.WriteLine("Couldn't satisfy firmware request {0} because of struct size.", filename);
return 0;
}
}
}
else
{
// use fromtend firmware interface
string firmwareID = null;
switch (filename)
{
case "CD_BIOS_EU": firmwareID = "CD_BIOS_EU"; break;
case "CD_BIOS_JP": firmwareID = "CD_BIOS_JP"; break;
case "CD_BIOS_US": firmwareID = "CD_BIOS_US"; break;
default:
break;
}
if (firmwareID != null)
{
// this path will be the most common PEBKAC error, so be a bit more vocal about the problem
srcdata = CoreComm.CoreFileProvider.GetFirmware("GEN", firmwareID, false, "GPGX firmwares are usually required.");
if (srcdata == null)
{
Console.WriteLine("Frontend couldn't satisfy firmware request GEN:{0}", firmwareID);
return 0;
}
}
else
{
Console.WriteLine("Unrecognized firmware request {0}", filename);
return 0;
}
}
if (srcdata != null)
{
if (srcdata.Length > maxsize)
{
Console.WriteLine("Couldn't satisfy firmware request {0} because {1} > {2}", filename, srcdata.Length, maxsize);
return 0;
}
else
{
Marshal.Copy(srcdata, 0, buffer, srcdata.Length);
Console.WriteLine("Firmware request {0} satisfied at size {1}", filename, srcdata.Length);
return srcdata.Length;
}
}
else
{
throw new Exception();
//Console.WriteLine("Couldn't satisfy firmware request {0} for unknown reasons", filename);
//return 0;
}
}
void CDRead(int lba, IntPtr dest, bool audio)
{
if (audio)
{
byte[] data = new byte[2352];
if (lba < CD.Session1.LeadoutLBA)
{
DiscSectorReader.ReadLBA_2352(lba, data, 0);
}
else
{
// audio seems to read slightly past the end of disks; probably innoculous
// just send back 0s.
// Console.WriteLine("!!{0} >= {1}", lba, CD.LBACount);
}
Marshal.Copy(data, 0, dest, 2352);
}
else
{
byte[] data = new byte[2048];
DiscSectorReader.ReadLBA_2048(lba, data, 0);
Marshal.Copy(data, 0, dest, 2048);
_drivelight = true;
}
}
LibGPGX.cd_read_cb cd_callback_handle;
unsafe byte[] GetCDData()
{
LibGPGX.CDData ret = new LibGPGX.CDData();
int size = Marshal.SizeOf(ret);
var ses = CD.Session1;
int ntrack = ses.InformationTrackCount;
// bet you a dollar this is all wrong
//zero 07-jul-2015 - throws a dollar in the pile, since he probably messed it up worse
for (int i = 0; i < LibGPGX.CD_MAX_TRACKS; i++)
{
if (i < ntrack)
{
ret.tracks[i].start = ses.Tracks[i + 1].LBA;
ret.tracks[i].end = ses.Tracks[i + 2].LBA;
if (i == ntrack - 1)
{
ret.end = ret.tracks[i].end;
ret.last = ntrack;
}
}
else
{
ret.tracks[i].start = 0;
ret.tracks[i].end = 0;
}
}
byte[] retdata = new byte[size];
fixed (byte* p = &retdata[0])
{
Marshal.StructureToPtr(ret, (IntPtr)p, false);
}
return retdata;
}
/// <summary>
/// size of native input struct
/// </summary>
int inputsize;
GPGXControlConverter ControlConverter;
private void SetControllerDefinition()
{
inputsize = Marshal.SizeOf(typeof(LibGPGX.InputData));
if (!Core.gpgx_get_control(input, inputsize))
throw new Exception("gpgx_get_control() failed");
ControlConverter = new GPGXControlConverter(input);
ControllerDefinition = ControlConverter.ControllerDef;
}
public LibGPGX.INPUT_DEVICE[] GetDevices()
{
return (LibGPGX.INPUT_DEVICE[])input.dev.Clone();
}
public bool IsSegaCD { get { return CD != null; } }
public void UpdateVDPViewContext(LibGPGX.VDPView view)
{
Core.gpgx_get_vdp_view(view);
Core.gpgx_flush_vram(); // fully regenerate internal caches as needed
}
short[] samples = new short[4096];
int nsamp = 0;
public void GetSamples(out short[] samples, out int nsamp)
{
nsamp = this.nsamp;
samples = this.samples;
this.nsamp = 0;
}
public void DiscardSamples()
{
this.nsamp = 0;
}
void update_audio()
{
IntPtr src = IntPtr.Zero;
Core.gpgx_get_audio(ref nsamp, ref src);
if (src != IntPtr.Zero)
{
Marshal.Copy(src, samples, 0, nsamp * 2);
}
}
public DisplayType Region { get; private set; }
}
}

View File

@ -0,0 +1,264 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
{
public class GPGXControlConverter
{
// this isn't all done
struct CName
{
public string Name;
public LibGPGX.INPUT_KEYS Key;
public CName(string Name, LibGPGX.INPUT_KEYS Key)
{
this.Name = Name;
this.Key = Key;
}
}
static CName[] Genesis3 =
{
new CName("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
new CName("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN),
new CName("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT),
new CName("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT),
new CName("A", LibGPGX.INPUT_KEYS.INPUT_A),
new CName("B", LibGPGX.INPUT_KEYS.INPUT_B),
new CName("C", LibGPGX.INPUT_KEYS.INPUT_C),
new CName("Start", LibGPGX.INPUT_KEYS.INPUT_START),
};
static CName[] Genesis6 =
{
new CName("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
new CName("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN),
new CName("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT),
new CName("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT),
new CName("A", LibGPGX.INPUT_KEYS.INPUT_A),
new CName("B", LibGPGX.INPUT_KEYS.INPUT_B),
new CName("C", LibGPGX.INPUT_KEYS.INPUT_C),
new CName("Start", LibGPGX.INPUT_KEYS.INPUT_START),
new CName("X", LibGPGX.INPUT_KEYS.INPUT_X),
new CName("Y", LibGPGX.INPUT_KEYS.INPUT_Y),
new CName("Z", LibGPGX.INPUT_KEYS.INPUT_Z),
new CName("Mode", LibGPGX.INPUT_KEYS.INPUT_MODE),
};
static CName[] Mouse =
{
new CName("Mouse Left", LibGPGX.INPUT_KEYS.INPUT_MOUSE_LEFT),
new CName("Mouse Center", LibGPGX.INPUT_KEYS.INPUT_MOUSE_CENTER),
new CName("Mouse Right", LibGPGX.INPUT_KEYS.INPUT_MOUSE_RIGHT),
new CName("Mouse Start", LibGPGX.INPUT_KEYS.INPUT_MOUSE_START),
};
static CName[] Lightgun =
{
new CName("Lightgun Trigger", LibGPGX.INPUT_KEYS.INPUT_MENACER_TRIGGER),
new CName("Lightgun Start", LibGPGX.INPUT_KEYS.INPUT_MENACER_START),
};
static CName[] Activator =
{
new CName("1L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_1L),
new CName("1U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_1U),
new CName("2L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_2L),
new CName("2U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_2U),
new CName("3L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_3L),
new CName("3U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_3U),
new CName("4L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_4L),
new CName("4U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_4U),
new CName("5L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_5L),
new CName("5U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_5U),
new CName("6L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_6L),
new CName("6U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_6U),
new CName("7L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_7L),
new CName("7U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_7U),
new CName("8L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_8L),
new CName("8U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_8U),
};
static CName[] XEA1P =
{
new CName("XE A", LibGPGX.INPUT_KEYS.INPUT_XE_A),
new CName("XE B", LibGPGX.INPUT_KEYS.INPUT_XE_B),
new CName("XE C", LibGPGX.INPUT_KEYS.INPUT_XE_C),
new CName("XE D", LibGPGX.INPUT_KEYS.INPUT_XE_D),
new CName("XE Start", LibGPGX.INPUT_KEYS.INPUT_XE_START),
new CName("XE Select", LibGPGX.INPUT_KEYS.INPUT_XE_SELECT),
new CName("XE E1", LibGPGX.INPUT_KEYS.INPUT_XE_E1),
new CName("XE E2", LibGPGX.INPUT_KEYS.INPUT_XE_E2),
};
static ControllerDefinition.FloatRange MouseRange = new ControllerDefinition.FloatRange(-256, 0, 255);
// lightgun needs to be transformed to match the current screen resolution
static ControllerDefinition.FloatRange LightgunRange = new ControllerDefinition.FloatRange(0, 5000, 10000);
static ControllerDefinition.FloatRange XEA1PRange = new ControllerDefinition.FloatRange(-128, 0, 127);
LibGPGX.InputData target = null;
IController source = null;
List<Action> Converts = new List<Action>();
public ControllerDefinition ControllerDef { get; private set; }
void AddToController(int idx, int player, IEnumerable<CName> Buttons)
{
foreach (var Button in Buttons)
{
string Name = string.Format("P{0} {1}", player, Button.Name);
ControllerDef.BoolButtons.Add(Name);
var ButtonFlag = Button.Key;
Converts.Add(delegate()
{
if (source.IsPressed(Name))
target.pad[idx] |= ButtonFlag;
});
}
}
void DoMouseAnalog(int idx, int player)
{
string NX = string.Format("P{0} Mouse X", player);
string NY = string.Format("P{0} Mouse Y", player);
ControllerDef.FloatControls.Add(NX);
ControllerDef.FloatControls.Add(NY);
ControllerDef.FloatRanges.Add(MouseRange);
ControllerDef.FloatRanges.Add(MouseRange);
Converts.Add(delegate()
{
target.analog[(2 * idx) + 0] = (short)source.GetFloat(NX);
target.analog[(2 * idx) + 1] = (short)source.GetFloat(NY);
});
}
void DoLightgunAnalog(int idx, int player)
{
string NX = string.Format("P{0} Lightgun X", player);
string NY = string.Format("P{0} Lightgun Y", player);
ControllerDef.FloatControls.Add(NX);
ControllerDef.FloatControls.Add(NY);
ControllerDef.FloatRanges.Add(LightgunRange);
ControllerDef.FloatRanges.Add(LightgunRange);
Converts.Add(delegate()
{
target.analog[(2 * idx) + 0] = (short)(source.GetFloat(NX) / 10000.0f * (ScreenWidth - 1));
target.analog[(2 * idx) + 1] = (short)(source.GetFloat(NY) / 10000.0f * (ScreenHeight - 1));
});
}
void DoXEA1PAnalog(int idx, int player)
{
string NX = string.Format("P{0} Stick X", player);
string NY = string.Format("P{0} Stick Y", player);
string NZ = string.Format("P{0} Stick Z", player);
ControllerDef.FloatControls.Add(NX);
ControllerDef.FloatControls.Add(NY);
ControllerDef.FloatControls.Add(NZ);
ControllerDef.FloatRanges.Add(XEA1PRange);
ControllerDef.FloatRanges.Add(XEA1PRange);
ControllerDef.FloatRanges.Add(XEA1PRange);
Converts.Add(delegate()
{
target.analog[(2 * idx) + 0] = (short)(source.GetFloat(NX));
target.analog[(2 * idx) + 1] = (short)(source.GetFloat(NY));
// +2 is correct in how gpgx internally does this
target.analog[(2 * idx) + 2] = (short)(source.GetFloat(NZ));
});
}
public GPGXControlConverter(LibGPGX.InputData input)
{
Console.WriteLine("Genesis Controller report:");
foreach (var e in input.system)
Console.WriteLine("S:{0}", e);
foreach (var e in input.dev)
Console.WriteLine("D:{0}", e);
int player = 1;
ControllerDef = new ControllerDefinition();
ControllerDef.BoolButtons.Add("Power");
ControllerDef.BoolButtons.Add("Reset");
for (int i = 0; i < LibGPGX.MAX_DEVICES; i++)
{
switch (input.dev[i])
{
case LibGPGX.INPUT_DEVICE.DEVICE_PAD3B:
AddToController(i, player, Genesis3);
player++;
break;
case LibGPGX.INPUT_DEVICE.DEVICE_PAD6B:
AddToController(i, player, Genesis6);
player++;
break;
case LibGPGX.INPUT_DEVICE.DEVICE_MOUSE:
AddToController(i, player, Mouse);
DoMouseAnalog(i, player);
player++;
break;
case LibGPGX.INPUT_DEVICE.DEVICE_NONE:
break;
case LibGPGX.INPUT_DEVICE.DEVICE_LIGHTGUN:
// supports menacers and justifiers
AddToController(i, player, Lightgun);
DoLightgunAnalog(i, player);
player++;
break;
case LibGPGX.INPUT_DEVICE.DEVICE_PAD2B:
case LibGPGX.INPUT_DEVICE.DEVICE_PADDLE:
case LibGPGX.INPUT_DEVICE.DEVICE_SPORTSPAD:
case LibGPGX.INPUT_DEVICE.DEVICE_TEREBI:
throw new Exception("Master System only device? Something went wrong.");
case LibGPGX.INPUT_DEVICE.DEVICE_ACTIVATOR:
AddToController(i, player, Activator);
player++;
break;
case LibGPGX.INPUT_DEVICE.DEVICE_XE_A1P:
AddToController(i, player, XEA1P);
DoXEA1PAnalog(i, player);
player++;
break;
case LibGPGX.INPUT_DEVICE.DEVICE_PICO:
// PICO isn't finished on the unmanaged side either
throw new Exception("Sega PICO not implemented yet!");
default:
throw new Exception("Unknown Genesis control device! Something went wrong.");
}
}
ControllerDef.Name = "GPGX Genesis Controller";
}
public void Convert(IController source, LibGPGX.InputData target)
{
this.source = source;
this.target = target;
target.ClearAllBools();
foreach (var f in Converts)
f();
this.source = null;
this.target = null;
}
/// <summary>
/// must be set for proper lightgun operation
/// </summary>
public int ScreenWidth { get; set; }
/// <summary>
/// must be set for proper lightgun operation
/// </summary>
public int ScreenHeight { get; set; }
}
}

View File

@ -0,0 +1,214 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
{
/*
* how to use:
* 0) get https://code.google.com/p/pdbparse/
* 1) set modulename to the name of the dll file.
* 2) set symbolname to the name of a file that you produced by executing the following command:
* pdb_print_gvars.py [module pdb file] 0x00000000 > [output file]
* 3) set start to an address (relative to the beginning of the dll) to start scanning
* 4) set length to the byte length of the scan area
* 5) instantiate a GenDbWind, and use it to control the scanner while you manipulate the dll into various configurations.
*
* ideas for modification:
* 1) unhardcode config parameters and allow modifying them through the interface
* 2) read section sizes and positions from the dll itself instead of the start\length params
* 3) support an ignore list of symbols
*/
public class GenDbgHlp : IDisposable
{
private static class Win32
{
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
}
// config
const string modulename = "libgenplusgx.dll";
const string symbolname = @"D:\encodes\bizhawksrc\genplus-gx\libretro\msvc\Debug\vars.txt";
const int start = 0x0c7d8000 - 0x0c540000;
const int length = 0x01082000;
bool disposed = false;
public void Dispose()
{
if (!disposed)
{
Win32.FreeLibrary(DllBase);
DllBase = IntPtr.Zero;
disposed = true;
}
}
IntPtr DllBase;
List<Symbol> SymbolsByAddr = new List<Symbol>();
Dictionary<string, Symbol> SymbolsByName = new Dictionary<string, Symbol>();
byte[][] data = new byte[10][];
public void SaveState(int statenum)
{
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
if (data[statenum] == null)
data[statenum] = new byte[length];
Marshal.Copy(DllBase + start, data[statenum], 0, length);
Console.WriteLine("State {0} saved", statenum);
}
unsafe public void Cmp(int statex, int statey)
{
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
List<Tuple<int, int>> bads = new List<Tuple<int, int>>();
byte[] x = data[statex];
byte[] y = data[statey];
if (x == null || y == null)
{
Console.WriteLine("Missing State!");
return;
}
bool inrange = false;
int startsec = 0;
fixed (byte* p0 = &x[0])
fixed (byte* p1 = &y[0])
{
for (int i = 0; i < length; i++)
{
if (!inrange)
{
if (p0[i] != p1[i])
{
startsec = i;
inrange = true;
}
}
else
{
if (p0[i] == p1[i])
{
bads.Add(new Tuple<int, int>(startsec, i));
inrange = false;
}
}
}
}
if (inrange)
bads.Add(new Tuple<int, int>(startsec, length));
for (int i = 0; i < bads.Count; i++)
{
IntPtr addr = (IntPtr)(bads[i].Item1 + start);
int len = bads[i].Item2 - bads[i].Item1;
var ss = Find(addr, len);
Console.WriteLine("0x{0:X8}[0x{1}]", (int)addr, len);
foreach (var sym in ss)
Console.WriteLine(sym);
Console.WriteLine();
}
if (bads.Count == 0)
Console.WriteLine("Clean!");
}
public GenDbgHlp()
{
using (StreamReader sr = new StreamReader(symbolname))
{
string line;
while ((line = sr.ReadLine()) != null)
{
Symbol sym = Symbol.FromString(line);
SymbolsByAddr.Add(sym);
SymbolsByName.Add(sym.name, sym);
}
SymbolsByAddr.Sort();
}
DllBase = Win32.LoadLibrary(modulename);
if (DllBase == IntPtr.Zero)
throw new Exception();
}
public List<Symbol> Find(IntPtr addr, int length)
{
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
Symbol min = new Symbol { addr = addr };
Symbol max = new Symbol { addr = addr + length };
int minidx = SymbolsByAddr.BinarySearch(min);
if (minidx < 0)
{
minidx = ~minidx;
// inexact matches return the first larger value, so find the next smallset one
if (minidx > 0)
minidx--;
}
int maxidx = SymbolsByAddr.BinarySearch(max);
if (maxidx < 0)
{
maxidx = ~maxidx;
if (maxidx > 0)
maxidx--;
}
return SymbolsByAddr.GetRange(minidx, maxidx - minidx + 1);
}
public struct Symbol : IComparable<Symbol>
{
public IntPtr addr;
public string section;
public string name;
public static Symbol FromString(string s)
{
string[] ss = s.Split(',');
if (ss.Length != 4)
throw new Exception();
if (!ss[1].StartsWith("0x"))
throw new Exception();
Symbol ret = new Symbol
{
addr = (IntPtr)int.Parse(ss[1].Substring(2), System.Globalization.NumberStyles.AllowHexSpecifier),
section = ss[3],
name = ss[0]
};
return ret;
}
public int CompareTo(Symbol other)
{
return (int)this.addr - (int)other.addr;
}
public override string ToString()
{
return string.Format("0x{0:X8} {1} ({2})", (int)addr, name, section);
}
}
}
}

View File

@ -0,0 +1,354 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using BizHawk.Emulation.Common.BizInvoke;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
{
public abstract class LibGPGX
{
public const string DllName = "libgenplusgx.dll";
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_get_video(out int w, out int h, out int pitch, ref IntPtr buffer);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_get_audio(ref int n, ref IntPtr buffer);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int load_archive_cb(string filename, IntPtr buffer, int maxsize);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_advance();
public enum Region : int
{
Autodetect = 0,
USA = 1,
Europe = 2,
Japan_NTSC = 3,
Japan_PAL = 4
}
[StructLayout(LayoutKind.Sequential)]
public class InitSettings
{
public enum FilterType : byte
{
None = 0,
LowPass = 1,
ThreeBand = 2
}
public FilterType Filter;
public ushort LowPassRange;
public short LowFreq;
public short HighFreq;
public short LowGain;
public short MidGain;
public short HighGain;
public uint BackdropColor;
}
[BizImport(CallingConvention.Cdecl, Compatibility=true)]
public abstract bool gpgx_init(string feromextension, load_archive_cb feload_archive_cb, bool sixbutton, INPUT_SYSTEM system_a, INPUT_SYSTEM system_b, Region region, [In]InitSettings settings);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_get_fps(ref int num, ref int den);
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
public abstract bool gpgx_get_control([Out]InputData dest, int bytes);
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
public abstract bool gpgx_put_control([In]InputData src, int bytes);
[BizImport(CallingConvention.Cdecl)]
public abstract IntPtr gpgx_get_sram(ref int size);
[BizImport(CallingConvention.Cdecl)]
public abstract bool gpgx_put_sram(byte[] data, int size);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_clear_sram();
public const int MIN_MEM_DOMAIN = 0;
public const int MAX_MEM_DOMAIN = 13;
[BizImport(CallingConvention.Cdecl)]
// apparently, if you use built in string marshalling, the interop will assume that
// the unmanaged char pointer was allocated in hglobal and try to free it that way
public abstract IntPtr gpgx_get_memdom(int which, ref IntPtr area, ref int size);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_reset(bool hard);
public const int MAX_DEVICES = 8;
public enum INPUT_SYSTEM : byte
{
SYSTEM_NONE = 0, // unconnected port
SYSTEM_MD_GAMEPAD = 1, // single 3-buttons or 6-buttons Control Pad
SYSTEM_MOUSE = 2, // Sega Mouse
SYSTEM_MENACER = 3, // Sega Menacer -- port B only
SYSTEM_JUSTIFIER = 4, // Konami Justifiers -- port B only
SYSTEM_XE_A1P = 5, // XE-A1P analog controller -- port A only
SYSTEM_ACTIVATOR = 6, // Sega Activator
SYSTEM_MS_GAMEPAD = 7, // single 2-buttons Control Pad -- Master System
SYSTEM_LIGHTPHASER = 8, // Sega Light Phaser -- Master System
SYSTEM_PADDLE = 9, // Sega Paddle Control -- Master System
SYSTEM_SPORTSPAD = 10, // Sega Sports Pad -- Master System
SYSTEM_TEAMPLAYER = 11, // Multi Tap -- Sega TeamPlayer
SYSTEM_WAYPLAY = 12, // Multi Tap -- EA 4-Way Play -- use both ports
};
public enum INPUT_DEVICE : byte
{
DEVICE_NONE = 0xff, // unconnected device = fixed ID for Team Player)
DEVICE_PAD3B = 0x00, // 3-buttons Control Pad = fixed ID for Team Player)
DEVICE_PAD6B = 0x01, // 6-buttons Control Pad = fixed ID for Team Player)
DEVICE_PAD2B = 0x02, // 2-buttons Control Pad
DEVICE_MOUSE = 0x03, // Sega Mouse
DEVICE_LIGHTGUN = 0x04, // Sega Light Phaser, Menacer or Konami Justifiers
DEVICE_PADDLE = 0x05, // Sega Paddle Control
DEVICE_SPORTSPAD = 0x06,// Sega Sports Pad
DEVICE_PICO = 0x07, // PICO tablet
DEVICE_TEREBI = 0x08, // Terebi Oekaki tablet
DEVICE_XE_A1P = 0x09, // XE-A1P analog controller
DEVICE_ACTIVATOR = 0x0a,// Activator
};
public enum CDLog_AddrType
{
MDCART, RAM68k, RAMZ80, SRAM,
};
[Flags]
public enum CDLog_Flags
{
Exec68k = 0x01,
Data68k = 0x04,
ExecZ80First = 0x08,
ExecZ80Operand = 0x10,
DataZ80 = 0x20,
DMASource = 0x40,
};
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void input_cb();
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_set_input_callback(input_cb cb);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void mem_cb(uint addr);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void CDCallback(int addr, CDLog_AddrType addrtype, CDLog_Flags flags);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_set_mem_callback(mem_cb read, mem_cb write, mem_cb exec);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_set_cd_callback(CDCallback cd);
/// <summary>
/// not every flag is valid for every device!
/// </summary>
[Flags]
public enum INPUT_KEYS : ushort
{
/* Default Input bitmasks */
INPUT_MODE = 0x0800,
INPUT_X = 0x0400,
INPUT_Y = 0x0200,
INPUT_Z = 0x0100,
INPUT_START = 0x0080,
INPUT_A = 0x0040,
INPUT_C = 0x0020,
INPUT_B = 0x0010,
INPUT_RIGHT = 0x0008,
INPUT_LEFT = 0x0004,
INPUT_DOWN = 0x0002,
INPUT_UP = 0x0001,
/* Master System specific bitmasks */
INPUT_BUTTON2 = 0x0020,
INPUT_BUTTON1 = 0x0010,
/* Mega Mouse specific bitmask */
INPUT_MOUSE_START = 0x0080,
INPUT_MOUSE_CENTER = 0x0040,
INPUT_MOUSE_RIGHT = 0x0020,
INPUT_MOUSE_LEFT = 0x0010,
/* Pico hardware specific bitmask */
INPUT_PICO_PEN = 0x0080,
INPUT_PICO_RED = 0x0010,
/* XE-1AP specific bitmask */
INPUT_XE_E1 = 0x0800,
INPUT_XE_E2 = 0x0400,
INPUT_XE_START = 0x0200,
INPUT_XE_SELECT = 0x0100,
INPUT_XE_A = 0x0080,
INPUT_XE_B = 0x0040,
INPUT_XE_C = 0x0020,
INPUT_XE_D = 0x0010,
/* Activator specific bitmasks */
INPUT_ACTIVATOR_8U = 0x8000,
INPUT_ACTIVATOR_8L = 0x4000,
INPUT_ACTIVATOR_7U = 0x2000,
INPUT_ACTIVATOR_7L = 0x1000,
INPUT_ACTIVATOR_6U = 0x0800,
INPUT_ACTIVATOR_6L = 0x0400,
INPUT_ACTIVATOR_5U = 0x0200,
INPUT_ACTIVATOR_5L = 0x0100,
INPUT_ACTIVATOR_4U = 0x0080,
INPUT_ACTIVATOR_4L = 0x0040,
INPUT_ACTIVATOR_3U = 0x0020,
INPUT_ACTIVATOR_3L = 0x0010,
INPUT_ACTIVATOR_2U = 0x0008,
INPUT_ACTIVATOR_2L = 0x0004,
INPUT_ACTIVATOR_1U = 0x0002,
INPUT_ACTIVATOR_1L = 0x0001,
/* Menacer */
INPUT_MENACER_TRIGGER = 0x0040,
INPUT_MENACER_START = 0x0080,
};
[StructLayout(LayoutKind.Sequential)]
public class InputData
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public readonly INPUT_SYSTEM[] system = new INPUT_SYSTEM[2];
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES)]
public readonly INPUT_DEVICE[] dev = new INPUT_DEVICE[MAX_DEVICES];
/// <summary>
/// digital inputs
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES)]
public readonly INPUT_KEYS[] pad = new INPUT_KEYS[MAX_DEVICES];
/// <summary>
/// analog (x/y)
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES * 2)]
public readonly short[] analog = new short[MAX_DEVICES * 2];
/// <summary>
/// gun horizontal offset
/// </summary>
public int x_offset;
/// <summary>
/// gun vertical offset
/// </summary>
public int y_offset;
public void ClearAllBools()
{
for (int i = 0; i < pad.Length; i++)
pad[i] = 0;
}
}
public const int CD_MAX_TRACKS = 100;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void cd_read_cb(int lba, IntPtr dest, bool audio);
[StructLayout(LayoutKind.Sequential)]
public struct CDTrack
{
public int start;
public int end;
}
[StructLayout(LayoutKind.Sequential)]
public class CDData
{
public int end;
public int last;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CD_MAX_TRACKS)]
public readonly CDTrack[] tracks = new CDTrack[CD_MAX_TRACKS];
}
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_set_cdd_callback(cd_read_cb cddcb);
[StructLayout(LayoutKind.Sequential)]
public struct VDPNameTable
{
public int Width; // in cells
public int Height; // in cells
public int Baseaddr;
}
[StructLayout(LayoutKind.Sequential)]
public class VDPView
{
public IntPtr VRAM;
public IntPtr PatternCache;
public IntPtr ColorCache;
public VDPNameTable NTA;
public VDPNameTable NTB;
public VDPNameTable NTW;
}
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
public abstract void gpgx_get_vdp_view([Out] VDPView view);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_poke_vram(int addr, byte value);
/// <summary>
/// regenerate whatever portions of the bg pattern cache are currently dirty.
/// </summary>
[BizImport(CallingConvention.Cdecl)] // the core will handle this itself; you only need to call this when using the cache for your own purposes
public abstract void gpgx_flush_vram();
/// <summary>
/// mark the bg pattern cache as dirty
/// </summary>
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_invalidate_pattern_cache();
[StructLayout(LayoutKind.Sequential)]
public struct RegisterInfo
{
public int Value;
public IntPtr Name;
}
[BizImport(CallingConvention.Cdecl)]
public abstract int gpgx_getmaxnumregs();
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
public abstract int gpgx_getregs([Out] RegisterInfo[] regs);
[Flags]
public enum DrawMask : int
{
BGA = 1,
BGB = 2,
BGW = 4,
Obj = 8,
Backdrop = 16
}
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_set_draw_mask(DrawMask mask);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_write_m68k_bus(uint addr, byte data);
[BizImport(CallingConvention.Cdecl)]
public abstract void gpgx_write_s68k_bus(uint addr, byte data);
[BizImport(CallingConvention.Cdecl)]
public abstract byte gpgx_peek_m68k_bus(uint addr);
[BizImport(CallingConvention.Cdecl)]
public abstract byte gpgx_peek_s68k_bus(uint addr);
}
}

1023
genplus-gx32/HISTORY.txt Normal file

File diff suppressed because it is too large Load Diff

621
genplus-gx32/LICENSE.txt Normal file
View File

@ -0,0 +1,621 @@
Unless otherwise explicitly stated, all code in Genesis Plus GX is released
under the following license:
Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles MacDonald
Some portions copyright Nicola Salmoria and the MAME team
All rights reserved.
Copyright (c) 2007-2013 Eke-Eke
All rights reserved.
Redistribution and use of this code or any derivative works are permitted
provided that the following conditions are met:
* Redistributions may not be sold, nor may they be used in a commercial
product or activity.
* Redistributions that are modified from the original source must include the
complete source code, including the source code for all components used by a
binary built from the modified sources. However, as a special exception, the
source code distributed need not include anything that is normally distributed
(in either source or binary form) with the major components (compiler, kernel,
and so on) of the operating system on which the executable runs, unless that
component itself accompanies the executable.
* Redistributions must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------------------------
TREMOR library is distributed under the following license:
Copyright (c) 2002, Xiph.org Foundation
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the Xiph.org Foundation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------------------------
NTSC Filter and Blip Buffer libraries are distributed under the
terms of the GNU Lesser General Public License (LGPL)
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
----------------------------------------------------------------------------------------
Gamecube & Wii ports are linked with LIBASND library and includes code distributed under
the following license:
Copyright (c) 2008 Hermes <www.entuwii.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
- The names of the contributors may not be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------------------------

148
genplus-gx32/Makefile.gc Normal file
View File

@ -0,0 +1,148 @@
#---------------------------------------------------------------------------------
# Clear the implicit built in rules
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPPC)),)
$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=<path to>devkitPPC)
endif
include $(DEVKITPPC)/gamecube_rules
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
#---------------------------------------------------------------------------------
TARGET := genplus_cube
BUILD := build_cube
SOURCES := core core/m68k core/z80 core/sound core/tremor core/ntsc core/input_hw core/cd_hw core/cart_hw core/cart_hw/svp \
gx gx/utils gx/gui gx/fileio gx/images gx/sounds
INCLUDES := core core/m68k core/z80 core/sound core/tremor core/ntsc core/input_hw core/cd_hw core/cart_hw core/cart_hw/svp \
gx gx/utils gx/gui gx/fileio gx/images gx/sounds \
$(BUILD)
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
CFLAGS = -O3 -fomit-frame-pointer --param large-function-growth=800 --param inline-unit-growth=200 -Wall -Winline -Wno-strict-aliasing $(MACHDEP) $(INCLUDE) -DUSE_LIBTREMOR -DDISABLE_MANY_OGG_OPEN_FILES -DUSE_16BPP_RENDERING -DALT_RENDERER
CXXFLAGS = $(CFLAGS)
LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map
#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
LIBS := -lpng -lfat -liso9660 -lasnd -logc -lm -lz
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
#---------------------------------------------------------------------------------
# automatically build a list of object files for our project
#---------------------------------------------------------------------------------
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
PNGFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.png)))
PCMFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.pcm)))
OGGFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.ogg)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
export LD := $(CC)
else
export LD := $(CXX)
endif
export OFILES := $(addsuffix .o,$(BINFILES)) \
$(PNGFILES:.png=.png.o) $(PCMFILES:.pcm=.pcm.o) $(OGGFILES:.ogg=.ogg.o) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \
$(sFILES:.s=.o) $(SFILES:.S=.o)
#---------------------------------------------------------------------------------
# build a list of include paths
#---------------------------------------------------------------------------------
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD) \
-I$(LIBOGC_INC) -I$(PORTLIBS)/include
#---------------------------------------------------------------------------------
# build a list of library paths
#---------------------------------------------------------------------------------
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \
-L$(LIBOGC_LIB)
export OUTPUT := $(CURDIR)/$(TARGET)
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile.gc
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).dol
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT).dol: $(OUTPUT).elf
$(OUTPUT).elf: $(OFILES)
#---------------------------------------------------------------------------------
# This rule links in binary data with the various extension
#---------------------------------------------------------------------------------
%.jpg.o : %.jpg
@echo $(notdir $<)
$(bin2o)
%.png.o : %.png
@echo $(notdir $<)
$(bin2o)
%.pcm.o : %.pcm
@echo $(notdir $<)
$(bin2o)
%.ogg.o : %.ogg
@echo $(notdir $<)
$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------

View File

@ -0,0 +1,235 @@
DEBUG = 0
LOGSOUND = 0
FRONTEND_SUPPORTS_RGB565 = 1
GENPLUS_SRC_DIR := core
LIBRETRO_DIR := libretro
ifeq ($(platform),)
platform = unix
ifeq ($(shell uname -a),)
platform = win
else ifneq ($(findstring MINGW,$(shell uname -a)),)
platform = win
else ifneq ($(findstring Darwin,$(shell uname -a)),)
platform = osx
else ifneq ($(findstring win,$(shell uname -a)),)
platform = win
endif
endif
# system platform
system_platform = unix
ifeq ($(shell uname -a),)
EXE_EXT = .exe
system_platform = win
else ifneq ($(findstring Darwin,$(shell uname -a)),)
system_platform = osx
else ifneq ($(findstring MINGW,$(shell uname -a)),)
system_platform = win
endif
TARGET_NAME := genesis_plus_gx
ifeq ($(platform), unix)
TARGET := $(TARGET_NAME)_libretro.so
fpic := -fPIC
SHARED := -shared -Wl,--version-script=libretro/link.T -Wl,--no-undefined -lz
ENDIANNESS_DEFINES := -DLSB_FIRST
PLATFORM_DEFINES := -DHAVE_ZLIB
else ifeq ($(platform), osx)
TARGET := $(TARGET_NAME)_libretro.dylib
fpic := -fPIC
SHARED := -dynamiclib -lz
ENDIANNESS_DEFINES := -DLSB_FIRST
PLATFORM_DEFINES := -DHAVE_ZLIB
else ifeq ($(platform), ios)
TARGET := $(TARGET_NAME)_libretro_ios.dylib
fpic := -fPIC
SHARED := -dynamiclib -lz
ENDIANNESS_DEFINES := -DLSB_FIRST
PLATFORM_DEFINES := -DHAVE_ZLIB
CC = clang -arch armv7 -isysroot $(IOSSDK)
else ifeq ($(platform), qnx)
TARGET := $(TARGET_NAME)_libretro_qnx.so
fpic := -fPIC
SHARED := -lm -shared -Wl,--version-script=libretro/link.T -Wl,--no-undefined -lz
ENDIANNESS_DEFINES := -DLSB_FIRST
PLATFORM_DEFINES := -DHAVE_ZLIB
CC = qcc -Vgcc_ntoarmv7le
AR = qcc -Vgcc_ntoarmv7le
PLATFORM_DEFINES := -D__BLACKBERRY_QNX__ -marm -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=softfp
else ifeq ($(platform), ps3)
TARGET := $(TARGET_NAME)_libretro_ps3.a
CC = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-gcc.exe
AR = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-ar.exe
PLATFORM_DEFINES := -D__CELLOS_LV2 -DALT_RENDER
STATIC_LINKING = 1
else ifeq ($(platform), sncps3)
TARGET := $(TARGET_NAME)_libretro_ps3.a
CC = $(CELL_SDK)/host-win32/sn/bin/ps3ppusnc.exe
AR = $(CELL_SDK)/host-win32/sn/bin/ps3snarl.exe
PLATFORM_DEFINES := -D__CELLOS_LV2 -DALT_RENDER
STATIC_LINKING = 1
else ifeq ($(platform), psl1ght)
TARGET := $(TARGET_NAME)_libretro_psl1ght.a$(EXE_EXT)
CC = $(PS3DEV)/ppu/bin/ppu-gcc$(EXE_EXT)
AR = $(PS3DEV)/ppu/bin/ppu-ar$(EXE_EXT)
PLATFORM_DEFINES := -D__CELLOS_LV2 -DALT_RENDER
STATIC_LINKING = 1
else ifeq ($(platform), psp1)
TARGET := $(TARGET_NAME)_libretro_psp1.a$(EXE_EXT)
CC = psp-gcc$(EXE_EXT)
AR = psp-ar$(EXE_EXT)
ENDIANNESS_DEFINES := -DLSB_FIRST
PLATFORM_DEFINES := -DPSP
CFLAGS += -G0
STATIC_LINKING = 1
else ifeq ($(platform), xenon)
TARGET := $(TARGET_NAME)_libretro_xenon360.a
CC = xenon-gcc$(EXE_EXT)
AR = xenon-ar$(EXE_EXT)
PLATFORM_DEFINES := -D__LIBXENON__ -DALT_RENDER
STATIC_LINKING = 1
else ifeq ($(platform), ngc)
TARGET := $(TARGET_NAME)_libretro_ngc.a
CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT)
AR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT)
PLATFORM_DEFINES := -DGEKKO -DHW_DOL -mrvl -mcpu=750 -meabi -mhard-float -DALT_RENDER
STATIC_LINKING = 1
else ifeq ($(platform), wii)
TARGET := $(TARGET_NAME)_libretro_wii.a
CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT)
AR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT)
PLATFORM_DEFINES := -DGEKKO -DHW_RVL -mrvl -mcpu=750 -meabi -mhard-float -DALT_RENDER
STATIC_LINKING = 1
else
TARGET := $(TARGET_NAME)_libretro.dll
CC = gcc
SHARED := -shared -static-libgcc -static-libstdc++ -Wl,--version-script=libretro/link.T -Wl,--no-undefined -lz
ENDIANNESS_DEFINES := -DLSB_FIRST
PLATFORM_DEFINES := -DHAVE_ZLIB
endif
ifeq ($(DEBUG), 1)
CFLAGS += -O0 -g
else
ifeq ($(platform),qnx)
CFLAGS += -Os -DNDEBUG
else
CFLAGS += -O3 -DNDEBUG
endif
endif
LIBRETRO_SRC := $(GENPLUS_SRC_DIR)/genesis.c \
$(GENPLUS_SRC_DIR)/vdp_ctrl.c \
$(GENPLUS_SRC_DIR)/vdp_render.c \
$(GENPLUS_SRC_DIR)/system.c \
$(GENPLUS_SRC_DIR)/io_ctrl.c \
$(GENPLUS_SRC_DIR)/loadrom.c \
$(GENPLUS_SRC_DIR)/mem68k.c \
$(GENPLUS_SRC_DIR)/state.c \
$(GENPLUS_SRC_DIR)/memz80.c \
$(GENPLUS_SRC_DIR)/membnk.c \
$(GENPLUS_SRC_DIR)/input_hw/activator.c \
$(GENPLUS_SRC_DIR)/input_hw/gamepad.c \
$(GENPLUS_SRC_DIR)/input_hw/input.c \
$(GENPLUS_SRC_DIR)/input_hw/lightgun.c \
$(GENPLUS_SRC_DIR)/input_hw/mouse.c \
$(GENPLUS_SRC_DIR)/input_hw/paddle.c \
$(GENPLUS_SRC_DIR)/input_hw/sportspad.c \
$(GENPLUS_SRC_DIR)/input_hw/teamplayer.c \
$(GENPLUS_SRC_DIR)/input_hw/xe_a1p.c \
$(GENPLUS_SRC_DIR)/input_hw/terebi_oekaki.c \
$(GENPLUS_SRC_DIR)/cd_hw/cd_cart.c \
$(GENPLUS_SRC_DIR)/cd_hw/cdc.c \
$(GENPLUS_SRC_DIR)/cd_hw/cdd.c \
$(GENPLUS_SRC_DIR)/cd_hw/gfx.c \
$(GENPLUS_SRC_DIR)/cd_hw/pcm.c \
$(GENPLUS_SRC_DIR)/cd_hw/scd.c \
$(GENPLUS_SRC_DIR)/cart_hw/areplay.c \
$(GENPLUS_SRC_DIR)/cart_hw/md_cart.c \
$(GENPLUS_SRC_DIR)/cart_hw/sms_cart.c \
$(GENPLUS_SRC_DIR)/cart_hw/eeprom_93c.c \
$(GENPLUS_SRC_DIR)/cart_hw/eeprom_i2c.c \
$(GENPLUS_SRC_DIR)/cart_hw/eeprom_spi.c \
$(GENPLUS_SRC_DIR)/cart_hw/ggenie.c \
$(GENPLUS_SRC_DIR)/cart_hw/sram.c \
$(GENPLUS_SRC_DIR)/cart_hw/svp/ssp16.c \
$(GENPLUS_SRC_DIR)/cart_hw/svp/svp.c \
$(GENPLUS_SRC_DIR)/ntsc/md_ntsc.c \
$(GENPLUS_SRC_DIR)/ntsc/sms_ntsc.c \
$(GENPLUS_SRC_DIR)/sound/eq.c \
$(GENPLUS_SRC_DIR)/sound/sound.c \
$(GENPLUS_SRC_DIR)/sound/ym2612.c \
$(GENPLUS_SRC_DIR)/sound/ym2413.c \
$(GENPLUS_SRC_DIR)/sound/sn76489.c \
$(GENPLUS_SRC_DIR)/sound/blip_buf.c \
$(GENPLUS_SRC_DIR)/z80/z80.c \
$(GENPLUS_SRC_DIR)/m68k/m68kcpu.c \
$(GENPLUS_SRC_DIR)/m68k/s68kcpu.c \
$(LIBRETRO_DIR)/libretro.c
LIBRETRO_OBJ := $(LIBRETRO_SRC:.c=.o)
ifeq ($(LOGSOUND), 1)
LIBRETRO_CFLAGS := -DLOGSOUND
endif
DEFINES :=
CFLAGS += $(fpic) $(DEFINES) $(CODE_DEFINES)
ifeq ($(FRONTEND_SUPPORTS_RGB565), 1)
# if you have a new frontend that supports RGB565
BPP_DEFINES = -DUSE_16BPP_RENDERING -DFRONTEND_SUPPORTS_RGB565
else
BPP_DEFINES = -DUSE_15BPP_RENDERING
endif
LIBRETRO_CFLAGS += -I$(GENPLUS_SRC_DIR) \
-I$(GENPLUS_SRC_DIR)/sound \
-I$(GENPLUS_SRC_DIR)/input_hw \
-I$(GENPLUS_SRC_DIR)/cart_hw \
-I$(GENPLUS_SRC_DIR)/cd_hw \
-I$(GENPLUS_SRC_DIR)/cart_hw/svp \
-I$(GENPLUS_SRC_DIR)/m68k \
-I$(GENPLUS_SRC_DIR)/z80 \
-I$(GENPLUS_SRC_DIR)/ntsc \
-I$(LIBRETRO_DIR) \
$(BPP_DEFINES) \
$(ENDIANNESS_DEFINES) \
$(PLATFORM_DEFINES) \
-D__LIBRETRO__
ifeq ($(platform), qnx)
LIBRETRO_CFLAGS += -D__inline__=inline
else
LIBRETRO_CFLAGS += -DINLINE="static inline"
endif
LIBRETRO_LIBS := -lm
all: $(TARGET)
%.o: %.c
$(CC) -o $@ -c $< $(CFLAGS) $(LIBRETRO_CFLAGS)
$(TARGET): $(LIBRETRO_OBJ)
ifeq ($(STATIC_LINKING), 1)
$(AR) rcs $@ $(LIBRETRO_OBJ)
else
$(CC) -o $(TARGET) $(fpic) $(LIBRETRO_OBJ) $(LIBRETRO_LIBS) $(SHARED)
endif
clean-objs:
rm -f $(LIBRETRO_OBJ)
clean:
rm -f $(LIBRETRO_OBJ)
rm -f $(TARGET)
.PHONY: clean clean-objs

148
genplus-gx32/Makefile.wii Normal file
View File

@ -0,0 +1,148 @@
#---------------------------------------------------------------------------------
# Clear the implicit built in rules
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPPC)),)
$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=<path to>devkitPPC)
endif
include $(DEVKITPPC)/wii_rules
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
#---------------------------------------------------------------------------------
TARGET := genplus_wii
BUILD := build_wii
SOURCES := core core/m68k core/z80 core/sound core/tremor core/ntsc core/input_hw core/cd_hw core/cart_hw core/cart_hw/svp \
gx gx/utils gx/gui gx/fileio gx/images gx/sounds
INCLUDES := core core/m68k core/z80 core/sound core/tremor core/ntsc core/input_hw core/cd_hw core/cart_hw core/cart_hw/svp \
gx gx/utils gx/gui gx/fileio gx/images gx/sounds \
$(BUILD)
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
CFLAGS = -O3 -fomit-frame-pointer --param large-function-growth=800 --param inline-unit-growth=200 -Wall -Winline -Wno-strict-aliasing $(MACHDEP) $(INCLUDE) -DUSE_LIBTREMOR -DUSE_16BPP_RENDERING -DALT_RENDERER -DHW_RVL
CXXFLAGS = $(CFLAGS)
LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map
#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
LIBS := -lpng -ldi -lfat -liso9660 -lasnd -lwiiuse -lbte -logc -lm -lz
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
#---------------------------------------------------------------------------------
# automatically build a list of object files for our project
#---------------------------------------------------------------------------------
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
PNGFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.png)))
PCMFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.pcm)))
OGGFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.ogg)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
export LD := $(CC)
else
export LD := $(CXX)
endif
export OFILES := $(addsuffix .o,$(BINFILES)) \
$(PNGFILES:.png=.png.o) $(PCMFILES:.pcm=.pcm.o) $(OGGFILES:.ogg=.ogg.o) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \
$(sFILES:.s=.o) $(SFILES:.S=.o)
#---------------------------------------------------------------------------------
# build a list of include paths
#---------------------------------------------------------------------------------
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD) \
-I$(LIBOGC_INC) -I$(PORTLIBS)/include
#---------------------------------------------------------------------------------
# build a list of library paths
#---------------------------------------------------------------------------------
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \
-L$(LIBOGC_LIB)
export OUTPUT := $(CURDIR)/$(TARGET)
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile.wii
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).dol
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT).dol: $(OUTPUT).elf
$(OUTPUT).elf: $(OFILES)
#---------------------------------------------------------------------------------
# This rule links in binary data with the various extension
#---------------------------------------------------------------------------------
%.jpg.o : %.jpg
@echo $(notdir $<)
$(bin2o)
%.png.o : %.png
@echo $(notdir $<)
$(bin2o)
%.pcm.o : %.pcm
@echo $(notdir $<)
$(bin2o)
%.ogg.o : %.ogg
@echo $(notdir $<)
$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------

View File

@ -0,0 +1,29 @@
#ifndef CALLBACKS_H
#define CALLBACKS_H
#include "types.h"
typedef void (*CDCallback)(int32 addr, int32 addrtype, int32 flags);
extern void (*biz_execcb)(unsigned addr);
extern void (*biz_readcb)(unsigned addr);
extern void (*biz_writecb)(unsigned addr);
extern CDCallback biz_cdcallback;
extern unsigned biz_lastpc;
enum eCDLog_AddrType
{
eCDLog_AddrType_MDCART, eCDLog_AddrType_RAM68k, eCDLog_AddrType_RAMZ80, eCDLog_AddrType_SRAM,
};
enum eCDLog_Flags
{
eCDLog_Flags_Exec68k = 0x01,
eCDLog_Flags_Data68k = 0x04,
eCDLog_Flags_ExecZ80First = 0x08,
eCDLog_Flags_ExecZ80Operand = 0x10,
eCDLog_Flags_DataZ80 = 0x20,
eCDLog_Flags_DMASource = 0x40
};
#endif

View File

@ -0,0 +1,706 @@
#include <stddef.h>
#include <stdarg.h>
#include <stdio.h>
#include "callbacks.h"
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
#include "shared.h"
#include "libretro.h"
#include "state.h"
#include "genesis.h"
#include "md_ntsc.h"
#include "sms_ntsc.h"
#include "eeprom_i2c.h"
char GG_ROM[256] = "GG_ROM"; // game genie rom
char AR_ROM[256] = "AR_ROM"; // actin replay rom
char SK_ROM[256] = "SK_ROM"; // sanic and knuckles
char SK_UPMEM[256] = "SK_UPMEM"; // sanic and knuckles
char GG_BIOS[256] = "GG_BIOS"; // game gear bootrom
char CD_BIOS_EU[256] = "CD_BIOS_EU"; // cd bioses
char CD_BIOS_US[256] = "CD_BIOS_US";
char CD_BIOS_JP[256] = "CD_BIOS_JP";
char MS_BIOS_US[256] = "MS_BIOS_US"; // master system bioses
char MS_BIOS_EU[256] = "MS_BIOS_EU";
char MS_BIOS_JP[256] = "MS_BIOS_JP";
char romextension[4];
static uint32_t bitmap_data_[1024 * 512];
static int16 soundbuffer[4096];
static int nsamples;
int cinterface_render_bga = 1;
int cinterface_render_bgb = 1;
int cinterface_render_bgw = 1;
int cinterface_render_obj = 1;
uint8 cinterface_custom_backdrop = 0;
uint32 cinterface_custom_backdrop_color = 0xffff00ff; // pink
extern uint8 border;
#define GPGX_EX __declspec(dllexport)
static int vwidth;
static int vheight;
static uint8_t brm_format[0x40] =
{
0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x00,0x00,0x00,0x00,0x40,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x53,0x45,0x47,0x41,0x5f,0x43,0x44,0x5f,0x52,0x4f,0x4d,0x00,0x01,0x00,0x00,0x00,
0x52,0x41,0x4d,0x5f,0x43,0x41,0x52,0x54,0x52,0x49,0x44,0x47,0x45,0x5f,0x5f,0x5f
};
extern void zap(void);
void (*biz_execcb)(unsigned addr) = NULL;
void (*biz_readcb)(unsigned addr) = NULL;
void (*biz_writecb)(unsigned addr) = NULL;
CDCallback biz_cdcallback = NULL;
unsigned biz_lastpc = 0;
static void update_viewport(void)
{
vwidth = bitmap.viewport.w + (bitmap.viewport.x * 2);
vheight = bitmap.viewport.h + (bitmap.viewport.y * 2);
if (config.ntsc)
{
if (reg[12] & 1)
vwidth = MD_NTSC_OUT_WIDTH(vwidth);
else
vwidth = SMS_NTSC_OUT_WIDTH(vwidth);
}
if (config.render && interlaced)
{
vheight = vheight * 2;
}
}
GPGX_EX void gpgx_get_video(int *w, int *h, int *pitch, void **buffer)
{
if (w)
*w = vwidth;
if (h)
*h = vheight;
if (pitch)
*pitch = bitmap.pitch;
if (buffer)
*buffer = bitmap.data;
}
GPGX_EX void gpgx_get_audio(int *n, void **buffer)
{
if (n)
*n = nsamples;
if (buffer)
*buffer = soundbuffer;
}
// this is most certainly wrong for interlacing
GPGX_EX void gpgx_get_fps(int *num, int *den)
{
if (vdp_pal)
{
if (num)
*num = 53203424;
if (den)
*den = 3420 * 313;
}
else
{
if (num)
*num = 53693175;
if (den)
*den = 3420 * 262;
}
}
GPGX_EX int gpgx_state_max_size(void)
{
// original state size, plus 64K sram or 16K ebram, plus 8K ibram or seeprom control structures
return STATE_SIZE + (64 + 8) * 1024;
}
GPGX_EX int gpgx_state_size(void *dest, int size)
{
int actual = 0;
if (size < gpgx_state_max_size())
return -1;
actual = state_save((unsigned char*) dest);
if (actual > size)
// fixme!
return -1;
return actual;
}
GPGX_EX int gpgx_state_save(void *dest, int size)
{
return state_save((unsigned char*) dest) == size;
}
GPGX_EX int gpgx_state_load(void *src, int size)
{
if (!size)
return 0;
if (state_load((unsigned char *) src) == size)
{
update_viewport();
return 1;
}
else
return 0;
}
void osd_input_update(void)
{
}
void (*input_callback_cb)(void);
void real_input_callback(void)
{
if (input_callback_cb)
input_callback_cb();
}
GPGX_EX void gpgx_set_input_callback(void (*fecb)(void))
{
input_callback_cb = fecb;
}
int (*load_archive_cb)(const char *filename, unsigned char *buffer, int maxsize);
// return 0 on failure, else actual loaded size
// extension, if not null, should be populated with the extension of the file loaded
// (up to 3 chars and null terminator, no more)
int load_archive(const char *filename, unsigned char *buffer, int maxsize, char *extension)
{
if (extension)
memcpy(extension, romextension, 4);
return load_archive_cb(filename, buffer, maxsize);
}
GPGX_EX int gpgx_get_control(t_input *dest, int bytes)
{
if (bytes != sizeof(t_input))
return 0;
memcpy(dest, &input, sizeof(t_input));
return 1;
}
GPGX_EX int gpgx_put_control(t_input *src, int bytes)
{
if (bytes != sizeof(t_input))
return 0;
memcpy(&input, src, sizeof(t_input));
return 1;
}
GPGX_EX void gpgx_advance(void)
{
if (system_hw == SYSTEM_MCD)
system_frame_scd(0);
else if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
system_frame_gen(0);
else
system_frame_sms(0);
if (bitmap.viewport.changed & 1)
{
bitmap.viewport.changed &= ~1;
update_viewport();
}
nsamples = audio_update(soundbuffer);
}
typedef struct
{
uint32 width; // in cells
uint32 height;
uint32 baseaddr;
} nametable_t;
typedef struct
{
uint8 *vram; // 64K vram
uint8 *patterncache; // every pattern, first normal, then hflip, vflip, bothflip
uint32 *colorcache; // 64 colors
nametable_t nta;
nametable_t ntb;
nametable_t ntw;
} vdpview_t;
extern uint8 bg_pattern_cache[];
extern uint32 pixel[];
GPGX_EX void gpgx_get_vdp_view(vdpview_t *view)
{
view->vram = vram;
view->patterncache = bg_pattern_cache;
view->colorcache = pixel + 0x40;
view->nta.width = 1 << (playfield_shift - 1);
view->ntb.width = 1 << (playfield_shift - 1);
view->nta.height = (playfield_row_mask + 1) >> 3;
view->ntb.height = (playfield_row_mask + 1) >> 3;
view->ntw.width = 1 << (5 + (reg[12] & 1));
view->ntw.height = 32;
view->nta.baseaddr = ntab;
view->ntb.baseaddr = ntbb;
view->ntw.baseaddr = ntwb;
}
// internal: computes sram size (no brams)
int saveramsize(void)
{
return sram_get_actual_size();
}
GPGX_EX void gpgx_clear_sram(void)
{
// clear sram
if (sram.on)
memset(sram.sram, 0xff, 0x10000);
if (cdd.loaded)
{
// clear and format bram
memset(scd.bram, 0, 0x2000);
brm_format[0x10] = brm_format[0x12] = brm_format[0x14] = brm_format[0x16] = 0x00;
brm_format[0x11] = brm_format[0x13] = brm_format[0x15] = brm_format[0x17] = (sizeof(scd.bram) / 64) - 3;
memcpy(scd.bram + 0x2000 - 0x40, brm_format, 0x40);
if (scd.cartridge.id)
{
// clear and format ebram
memset(scd.cartridge.area, 0x00, scd.cartridge.mask + 1);
brm_format[0x10] = brm_format[0x12] = brm_format[0x14] = brm_format[0x16] = (((scd.cartridge.mask + 1) / 64) - 3) >> 8;
brm_format[0x11] = brm_format[0x13] = brm_format[0x15] = brm_format[0x17] = (((scd.cartridge.mask + 1) / 64) - 3) & 0xff;
memcpy(scd.cartridge.area + scd.cartridge.mask + 1 - 0x40, brm_format, 0x40);
}
}
}
// a bit hacky:
// in order to present a single memory block to the frontend,
// we copy the bram bits next to the ebram bits
GPGX_EX void gpgx_sram_prepread(void)
{
if (!sram.on && cdd.loaded && scd.cartridge.id)
{
void *dest = scd.cartridge.area + scd.cartridge.mask + 1;
memcpy(dest, scd.bram, 0x2000);
}
}
GPGX_EX void gpgx_sram_commitwrite(void)
{
if (!sram.on && cdd.loaded && scd.cartridge.id)
{
void *src = scd.cartridge.area + scd.cartridge.mask + 1;
memcpy(scd.bram, src, 0x2000);
}
}
GPGX_EX void gpgx_poke_vram(int addr, uint8 val)
{
write_vram_byte(addr, val);
}
GPGX_EX void gpgx_flush_vram(void)
{
flush_vram_cache();
}
GPGX_EX const char* gpgx_get_memdom(int which, void **area, int *size)
{
if (!area || !size)
return NULL;
switch (which)
{
case 0:
*area = work_ram;
*size = 0x10000;
return "68K RAM";
case 1:
*area = zram;
*size = 0x2000;
return "Z80 RAM";
case 2:
if (!cdd.loaded)
{
*area = ext.md_cart.rom;
*size = ext.md_cart.romsize;
return "MD CART";
}
else if (scd.cartridge.id)
{
*area = scd.cartridge.area;
*size = scd.cartridge.mask + 1;
return "EBRAM";
}
else return NULL;
case 3:
if (cdd.loaded)
{
*area = scd.bootrom;
*size = 0x20000;
return "CD BOOT ROM";
}
else return NULL;
case 4:
if (cdd.loaded)
{
*area = scd.prg_ram;
*size = 0x80000;
return "CD PRG RAM";
}
else return NULL;
case 5:
if (cdd.loaded)
{
*area = scd.word_ram[0];
*size = 0x20000;
return "CD WORD RAM[0] (1M)";
}
else return NULL;
case 6:
if (cdd.loaded)
{
*area = scd.word_ram[1];
*size = 0x20000;
return "CD WORD RAM[1] (1M)";
}
else return NULL;
case 7:
if (cdd.loaded)
{
*area = scd.word_ram_2M;
*size = 0x40000;
return "CD WORD RAM (2M)";
}
else return NULL;
case 8:
if (cdd.loaded)
{
*area = scd.bram;
*size = 0x2000;
return "CD BRAM";
}
else return NULL;
case 9:
*area = boot_rom;
*size = 0x800;
return "BOOT ROM";
default:
return NULL;
case 10:
if (sram.on)
{
*area = sram.sram;
*size = saveramsize();
return "SRAM";
}
else return NULL;
case 11:
*area = cram;
*size = 128;
return "CRAM";
case 12:
*area = vsram;
*size = 128;
return "VSRAM";
case 13:
*area = vram;
*size = 65536;
return "VRAM";
}
}
GPGX_EX void gpgx_write_m68k_bus(unsigned addr, unsigned data)
{
unsigned char *base = m68k.memory_map[addr >> 16 & 0xff].base;
if (base)
base[addr & 0xffff ^ 1] = data;
}
GPGX_EX void gpgx_write_s68k_bus(unsigned addr, unsigned data)
{
unsigned char *base = s68k.memory_map[addr >> 16 & 0xff].base;
if (base)
base[addr & 0xffff ^ 1] = data;
}
GPGX_EX unsigned gpgx_peek_m68k_bus(unsigned addr)
{
unsigned char *base = m68k.memory_map[addr >> 16 & 0xff].base;
if (base)
return base[addr & 0xffff ^ 1];
else
return 0xff;
}
GPGX_EX unsigned gpgx_peek_s68k_bus(unsigned addr)
{
unsigned char *base = s68k.memory_map[addr >> 16 & 0xff].base;
if (base)
return base[addr & 0xffff ^ 1];
else
return 0xff;
}
GPGX_EX void gpgx_get_sram(void **area, int *size)
{
if (!area || !size)
return;
if (sram.on)
{
*area = sram.sram;
*size = saveramsize();
}
else if (scd.cartridge.id)
{
*area = scd.cartridge.area;
*size = scd.cartridge.mask + 1 + 0x2000;
}
else if (cdd.loaded)
{
*area = scd.bram;
*size = 0x2000;
}
else
{
if (area)
*area = NULL;
if (size)
*size = 0;
}
}
struct InitSettings
{
uint8_t Filter;
uint16_t LowPassRange;
int16_t LowFreq;
int16_t HighFreq;
int16_t LowGain;
int16_t MidGain;
int16_t HighGain;
uint32_t BackdropColor;
};
GPGX_EX int gpgx_init(const char *feromextension, int (*feload_archive_cb)(const char *filename, unsigned char *buffer, int maxsize), int sixbutton, char system_a, char system_b, int region, struct InitSettings *settings)
{
zap();
memset(&bitmap, 0, sizeof(bitmap));
memset(bitmap_data_, 0, sizeof(bitmap_data_));
strncpy(romextension, feromextension, 3);
romextension[3] = 0;
load_archive_cb = feload_archive_cb;
bitmap.width = 1024;
bitmap.height = 512;
bitmap.pitch = 1024 * 4;
bitmap.data = (uint8_t *)bitmap_data_;
/* sound options */
config.psg_preamp = 150;
config.fm_preamp= 100;
config.hq_fm = 1; /* high-quality resampling */
config.psgBoostNoise = 1;
config.filter = settings->Filter; //0; /* no filter */
config.lp_range = settings->LowPassRange; //0x9999; /* 0.6 in 16.16 fixed point */
config.low_freq = settings->LowFreq; //880;
config.high_freq = settings->HighFreq; //5000;
config.lg = settings->LowGain; //1.0;
config.mg = settings->MidGain; //1.0;
config.hg = settings->HighGain; //1.0;
config.dac_bits = 14; /* MAX DEPTH */
config.ym2413= 2; /* AUTO */
config.mono = 0; /* STEREO output */
/* system options */
config.system = 0; /* AUTO */
config.region_detect = region; // see loadrom.c
config.vdp_mode = 0; /* AUTO */
config.master_clock = 0; /* AUTO */
config.force_dtack = 0;
config.addr_error = 1;
config.bios = 0;
config.lock_on = 0;
/* video options */
config.overscan = 0;
config.gg_extra = 0;
config.ntsc = 0;
config.render = 0;
// set overall input system type
// usual is MD GAMEPAD or NONE
// TEAMPLAYER, WAYPLAY, ACTIVATOR, XEA1P, MOUSE need to be specified
// everything else is auto or master system only
// XEA1P is port 1 only
// WAYPLAY is both ports at same time only
input.system[0] = system_a;
input.system[1] = system_b;
cinterface_custom_backdrop_color = settings->BackdropColor;
// apparently, the only part of config.input used is the padtype identifier,
// and that's used only for choosing pad type when system_md
{
int i;
for (i = 0; i < MAX_INPUTS; i++)
config.input[i].padtype = sixbutton ? DEVICE_PAD6B : DEVICE_PAD3B;
}
if (!load_rom("PRIMARY_ROM"))
return 0;
audio_init(44100, 0);
system_init();
system_reset();
update_viewport();
gpgx_clear_sram();
return 1;
}
GPGX_EX void gpgx_reset(int hard)
{
if (hard)
system_reset();
else
gen_reset(0);
}
GPGX_EX void gpgx_set_mem_callback(void (*read)(unsigned), void (*write)(unsigned), void (*exec)(unsigned))
{
biz_readcb = read;
biz_writecb = write;
biz_execcb = exec;
}
GPGX_EX void gpgx_set_cd_callback(CDCallback cdcallback)
{
biz_cdcallback = cdcallback;
}
GPGX_EX void gpgx_set_draw_mask(int mask)
{
cinterface_render_bga = !!(mask & 1);
cinterface_render_bgb = !!(mask & 2);
cinterface_render_bgw = !!(mask & 4);
cinterface_render_obj = !!(mask & 8);
cinterface_custom_backdrop = !!(mask & 16);
if (cinterface_custom_backdrop)
color_update_m5(0, 0);
else
color_update_m5(0x00, *(uint16 *)&cram[border << 1]);
}
typedef struct
{
unsigned int value;
const char *name;
} gpregister_t;
GPGX_EX int gpgx_getmaxnumregs(void)
{
return 57;
}
GPGX_EX int gpgx_getregs(gpregister_t *regs)
{
int ret = 0;
// 22
#define MAKEREG(x) regs->name = "M68K " #x; regs->value = m68k_get_reg(M68K_REG_##x); regs++; ret++;
MAKEREG(D0);
MAKEREG(D1);
MAKEREG(D2);
MAKEREG(D3);
MAKEREG(D4);
MAKEREG(D5);
MAKEREG(D6);
MAKEREG(D7);
MAKEREG(A0);
MAKEREG(A1);
MAKEREG(A2);
MAKEREG(A3);
MAKEREG(A4);
MAKEREG(A5);
MAKEREG(A6);
MAKEREG(A7);
MAKEREG(PC);
MAKEREG(SR);
MAKEREG(SP);
MAKEREG(USP);
MAKEREG(ISP);
MAKEREG(IR);
#undef MAKEREG
(regs-6)->value = biz_lastpc; // during read/write callbacks, PC runs away due to prefetch. restore it.
// 13
#define MAKEREG(x) regs->name = "Z80 " #x; regs->value = Z80.x.d; regs++; ret++;
MAKEREG(pc);
MAKEREG(sp);
MAKEREG(af);
MAKEREG(bc);
MAKEREG(de);
MAKEREG(hl);
MAKEREG(ix);
MAKEREG(iy);
MAKEREG(wz);
MAKEREG(af2);
MAKEREG(bc2);
MAKEREG(de2);
MAKEREG(hl2);
#undef MAKEREG
// 22
if (system_hw == SYSTEM_MCD)
{
#define MAKEREG(x) regs->name = "S68K " #x; regs->value = s68k_get_reg(M68K_REG_##x); regs++; ret++;
MAKEREG(D0);
MAKEREG(D1);
MAKEREG(D2);
MAKEREG(D3);
MAKEREG(D4);
MAKEREG(D5);
MAKEREG(D6);
MAKEREG(D7);
MAKEREG(A0);
MAKEREG(A1);
MAKEREG(A2);
MAKEREG(A3);
MAKEREG(A4);
MAKEREG(A5);
MAKEREG(A6);
MAKEREG(A7);
MAKEREG(PC);
MAKEREG(SR);
MAKEREG(SP);
MAKEREG(USP);
MAKEREG(ISP);
MAKEREG(IR);
#undef MAKEREG
}
return ret;
}

View File

@ -0,0 +1,589 @@
#include "shared.h"
#include "eeprom_93c.h"
#include "eq.h"
extern struct
{
uint8 enabled;
uint8 status;
uint8 *rom;
uint8 *ram;
uint16 regs[13];
uint16 old[4];
uint16 data[4];
uint32 addr[4];
} action_replay;
typedef struct
{
uint8 address_bits; // number of bits needed to address memory: 7, 8 or 16 //
uint16 size_mask; // depends on the max size of the memory (in bytes) //
uint16 pagewrite_mask; // depends on the maximal number of bytes that can be written in a single write cycle //
uint32 sda_in_adr; // 68000 memory address mapped to SDA_IN //
uint32 sda_out_adr; // 68000 memory address mapped to SDA_OUT //
uint32 scl_adr; // 68000 memory address mapped to SCL //
uint8 sda_in_bit; // bit offset for SDA_IN //
uint8 sda_out_bit; // bit offset for SDA_OUT //
uint8 scl_bit; // bit offset for SCL //
} T_CONFIG_I2C;
typedef enum
{
STAND_BY = 0,
WAIT_STOP,
GET_SLAVE_ADR,
GET_WORD_ADR_7BITS,
GET_WORD_ADR_HIGH,
GET_WORD_ADR_LOW,
WRITE_DATA,
READ_DATA
} T_STATE_I2C;
typedef struct
{
uint8 sda; // current /SDA line state //
uint8 scl; // current /SCL line state //
uint8 old_sda; // previous /SDA line state //
uint8 old_scl; // previous /SCL line state //
uint8 cycles; // current operation cycle number (0-9) //
uint8 rw; // operation type (1:READ, 0:WRITE) //
uint16 slave_mask; // device address (shifted by the memory address width)//
uint16 word_address; // memory address //
T_STATE_I2C state; // current operation state //
T_CONFIG_I2C config; // EEPROM characteristics for this game //
} T_EEPROM_I2C;
extern T_EEPROM_I2C eeprom_i2c;
typedef enum
{
STANDBY,
GET_OPCODE_,//
GET_ADDRESS,
WRITE_BYTE,
READ_BYTE
} T_STATE_SPI;
typedef struct
{
uint8 cs; // !CS line state //
uint8 clk; // SCLK line state //
uint8 out; // SO line state //
uint8 status; // status register //
uint8 opcode; // 8-bit opcode //
uint8 buffer; // 8-bit data buffer //
uint16 addr; // 16-bit address //
uint32 cycles; // current operation cycle //
T_STATE_SPI state; // current operation state //
} T_EEPROM_SPI;
extern T_EEPROM_SPI spi_eeprom;
extern struct
{
uint8 enabled;
uint8 *rom;
uint16 regs[0x20];
uint16 old[6];
uint16 data[6];
uint32 addr[6];
} ggenie;
extern struct
{
uint8 State;
uint8 Counter;
} activator[2];
extern uint8 pad_index;
extern struct
{
uint8 State;
uint8 Port;
} lightgun;
extern struct
{
uint8 State;
uint8 Counter;
uint8 Wait;
uint8 Port;
} mouse;
extern struct
{
uint8 State;
} paddle[2];
extern struct
{
uint8 State;
uint8 Counter;
} sportspad[2];
extern struct
{
uint8 State;
uint8 Counter;
uint8 Table[12];
} teamplayer[2];
extern struct
{
uint8 axis;
uint8 busy;
} tablet;
extern struct
{
uint8 State;
uint8 Counter;
uint8 Latency;
} xe_a1p[2];
typedef struct
{
// Configuration //
int PreAmp[4][2]; // stereo channels pre-amplification ratio (%) //
int NoiseFeedback;
int SRWidth;
// PSG registers: //
int Registers[8]; // Tone, vol x4 //
int LatchedRegister;
int NoiseShiftRegister;
int NoiseFreq; // Noise channel signal generator frequency //
// Output calculation variables //
int ToneFreqVals[4]; // Frequency register values (counters) //
int ToneFreqPos[4]; // Frequency channel flip-flops //
int Channel[4][2]; // current amplitude of each (stereo) channel //
int ChanOut[4][2]; // current output value of each (stereo) channel //
// Internal M-clock counter //
unsigned long clocks;
} SN76489_Context;
extern SN76489_Context SN76489;
extern int fm_buffer[1080 * 2];
extern int fm_last[2];
extern int *fm_ptr;
// Cycle-accurate FM samples //
extern uint32 fm_cycles_ratio;
extern uint32 fm_cycles_start;
extern uint32 fm_cycles_count;
// YM chip function pointers //
extern void (*YM_Reset)(void);
extern void (*YM_Update)(int *buffer, int length);
extern void (*YM_Write)(unsigned int a, unsigned int v);
typedef struct
{
UINT32 ar; // attack rate: AR<<2 //
UINT32 dr; // decay rate: DR<<2 //
UINT32 rr; // release rate:RR<<2 //
UINT8 KSR; // key scale rate //
UINT8 ksl; // keyscale level //
UINT8 ksr; // key scale rate: kcode>>KSR //
UINT8 mul; // multiple: mul_tab[ML] //
// Phase Generator //
UINT32 phase; // frequency counter //
UINT32 freq; // frequency counter step //
UINT8 fb_shift; // feedback shift value //
INT32 op1_out[2]; // slot1 output for feedback //
// Envelope Generator //
UINT8 eg_type; // percussive/nonpercussive mode //
UINT8 state; // phase type //
UINT32 TL; // total level: TL << 2 //
INT32 TLL; // adjusted now TL //
INT32 volume; // envelope counter //
UINT32 sl; // sustain level: sl_tab[SL] //
UINT8 eg_sh_dp; // (dump state) //
UINT8 eg_sel_dp; // (dump state) //
UINT8 eg_sh_ar; // (attack state) //
UINT8 eg_sel_ar; // (attack state) //
UINT8 eg_sh_dr; // (decay state) //
UINT8 eg_sel_dr; // (decay state) //
UINT8 eg_sh_rr; // (release state for non-perc.) //
UINT8 eg_sel_rr; // (release state for non-perc.) //
UINT8 eg_sh_rs; // (release state for perc.mode) //
UINT8 eg_sel_rs; // (release state for perc.mode) //
UINT32 key; // 0 = KEY OFF, >0 = KEY ON //
// LFO //
UINT32 AMmask; // LFO Amplitude Modulation enable mask //
UINT8 vib; // LFO Phase Modulation enable flag (active high)//
// waveform select //
unsigned int wavetable;
} YM2413_OPLL_SLOT;
typedef struct
{
YM2413_OPLL_SLOT SLOT[2];
// phase generator state //
UINT32 block_fnum; // block+fnum //
UINT32 fc; // Freq. freqement base //
UINT32 ksl_base; // KeyScaleLevel Base step //
UINT8 kcode; // key code (for key scaling) //
UINT8 sus; // sus on/off (release speed in percussive mode) //
} YM2413_OPLL_CH;
// chip state //
typedef struct {
YM2413_OPLL_CH P_CH[9]; // OPLL chips have 9 channels //
UINT8 instvol_r[9]; // instrument/volume (or volume/volume in percussive mode) //
UINT32 eg_cnt; // global envelope generator counter //
UINT32 eg_timer; // global envelope generator counter works at frequency = chipclock/72 //
UINT32 eg_timer_add; // step of eg_timer //
UINT32 eg_timer_overflow; // envelope generator timer overlfows every 1 sample (on real chip) //
UINT8 rhythm; // Rhythm mode //
// LFO //
UINT32 lfo_am_cnt;
UINT32 lfo_am_inc;
UINT32 lfo_pm_cnt;
UINT32 lfo_pm_inc;
UINT32 noise_rng; // 23 bit noise shift register //
UINT32 noise_p; // current noise 'phase' //
UINT32 noise_f; // current noise period //
// instrument settings //
//
//0-user instrument
//1-15 - fixed instruments
//16 -bass drum settings
//17,18 - other percussion instruments
//
UINT8 inst_tab[19][8];
UINT32 fn_tab[1024]; // fnumber->increment counter //
UINT8 address; // address register //
UINT8 status; // status flag //
double clock; // master clock (Hz) //
int rate; // sampling rate (Hz) //
} YM2413;
extern YM2413 ym2413;
// struct describing a single operator (SLOT) //
typedef struct
{
INT32 *DT; // detune :dt_tab[DT] //
UINT8 KSR; // key scale rate :3-KSR //
UINT32 ar; // attack rate //
UINT32 d1r; // decay rate //
UINT32 d2r; // sustain rate //
UINT32 rr; // release rate //
UINT8 ksr; // key scale rate :kcode>>(3-KSR) //
UINT32 mul; // multiple :ML_TABLE[ML] //
// Phase Generator //
UINT32 phase; // phase counter //
INT32 Incr; // phase step //
// Envelope Generator //
UINT8 state; // phase type //
UINT32 tl; // total level: TL << 3 //
INT32 volume; // envelope counter //
UINT32 sl; // sustain level:sl_table[SL] //
UINT32 vol_out; // current output from EG circuit (without AM from LFO) //
UINT8 eg_sh_ar; // (attack state) //
UINT8 eg_sel_ar; // (attack state) //
UINT8 eg_sh_d1r; // (decay state) //
UINT8 eg_sel_d1r; // (decay state) //
UINT8 eg_sh_d2r; // (sustain state) //
UINT8 eg_sel_d2r; // (sustain state) //
UINT8 eg_sh_rr; // (release state) //
UINT8 eg_sel_rr; // (release state) //
UINT8 ssg; // SSG-EG waveform //
UINT8 ssgn; // SSG-EG negated output //
UINT8 key; // 0=last key was KEY OFF, 1=KEY ON //
// LFO //
UINT32 AMmask; // AM enable flag //
} FM_SLOT;
typedef struct
{
FM_SLOT SLOT[4]; // four SLOTs (operators) //
UINT8 ALGO; // algorithm //
UINT8 FB; // feedback shift //
INT32 op1_out[2]; // op1 output for feedback //
INT32 *connect1; // SLOT1 output pointer //
INT32 *connect3; // SLOT3 output pointer //
INT32 *connect2; // SLOT2 output pointer //
INT32 *connect4; // SLOT4 output pointer //
INT32 *mem_connect; // where to put the delayed sample (MEM) //
INT32 mem_value; // delayed sample (MEM) value //
INT32 pms; // channel PMS //
UINT8 ams; // channel AMS //
UINT32 fc; // fnum,blk //
UINT8 kcode; // key code //
UINT32 block_fnum; // blk/fnum value (for LFO PM calculations) //
} FM_CH;
typedef struct
{
UINT16 address; // address register //
UINT8 status; // status flag //
UINT32 mode; // mode CSM / 3SLOT //
UINT8 fn_h; // freq latch //
INT32 TA; // timer a value //
INT32 TAL; // timer a base //
INT32 TAC; // timer a counter //
INT32 TB; // timer b value //
INT32 TBL; // timer b base //
INT32 TBC; // timer b counter //
INT32 dt_tab[8][32]; // DeTune table //
} FM_ST;
// OPN unit //
// OPN 3slot struct //
typedef struct
{
UINT32 fc[3]; // fnum3,blk3: calculated //
UINT8 fn_h; // freq3 latch //
UINT8 kcode[3]; // key code //
UINT32 block_fnum[3]; // current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) //
UINT8 key_csm; // CSM mode Key-ON flag //
} FM_3SLOT;
// OPN/A/B common state //
typedef struct
{
FM_ST ST; // general state //
FM_3SLOT SL3; // 3 slot mode state //
unsigned int pan[6*2]; // fm channels output masks (0xffffffff = enable) //
// EG //
UINT32 eg_cnt; // global envelope generator counter //
UINT32 eg_timer; // global envelope generator counter works at frequency = chipclock/144/3 //
// LFO //
UINT8 lfo_cnt; // current LFO phase (out of 128) //
UINT32 lfo_timer; // current LFO phase runs at LFO frequency //
UINT32 lfo_timer_overflow; // LFO timer overflows every N samples (depends on LFO frequency) //
UINT32 LFO_AM; // current LFO AM step //
UINT32 LFO_PM; // current LFO PM step //
} FM_OPN;
// YM2612 chip //
typedef struct
{
FM_CH CH[6]; // channel state //
UINT8 dacen; // DAC mode //
INT32 dacout; // DAC output //
FM_OPN OPN; // OPN state //
} YM2612;
extern YM2612 ym2612;
// current chip state //
extern INT32 m2,c1,c2; // Phase Modulation input for operators 2,3,4 //
extern INT32 mem; // one sample delay memory //
extern INT32 out_fm[8]; // outputs of working channels //
extern UINT32 bitmask; // working channels output bitmasking (DAC quantization) //
extern uint8 tmss[4]; // TMSS security register //
extern uint8 rom_region;
extern uint8 pause_b;
extern EQSTATE eq;
extern int16 llp,rrp;
#define Z(a) memset((a), 0, sizeof(*(a)))
#define Y(a) memset((a), 0, sizeof((a)))
void zap(void)
{
Z(&config);
Z(&eeprom_93c);
Z(&ext);
Y(boot_rom);
Y(work_ram);
Y(zram);
Z(&zbank);
Z(&zstate);
Z(&pico_current);
Z(&input);
memset(old_system, -1, sizeof(old_system));
Y(io_reg);
Z(&region_code);
Z(&rominfo);
Z(&romtype);
Z(&m68k);
Z(&s68k);
Y(zbank_memory_map);
Z(&sram); // NB: sram.sram is not allocated
Z(&svp);
Z(&bitmap);
Z(&snd);
Z(&mcycles_vdp);
Z(&system_hw);
Z(&system_bios);
Z(&system_clock);
Y(reg);
Y(sat);
Y(vram);
Y(cram);
Y(vsram);
Z(&hint_pending);
Z(&vint_pending);
Z(&status);
Z(&dma_length);
Z(&ntab);
Z(&ntbb);
Z(&ntwb);
Z(&satb);
Z(&hscb);
Y(bg_name_dirty);
Y(bg_name_list);
Z(&bg_list_index);
Z(&hscroll_mask);
Z(&playfield_shift);
Z(&playfield_col_mask);
Z(&playfield_row_mask);
Z(&odd_frame);
Z(&im2_flag);
Z(&interlaced);
Z(&vdp_pal);
Z(&v_counter);
Z(&vc_max);
Z(&vscroll);
Z(&lines_per_frame);
Z(&max_sprite_pixels);
Z(&fifo_write_cnt);
Z(&fifo_slots);
Z(&hvc_latch);
Z(&hctab);
Z(&vdp_68k_data_w);
Z(&vdp_z80_data_w);
Z(&vdp_68k_data_r);
Z(&vdp_z80_data_r);
Z(&spr_col);
Z(&Z80);
Y(z80_readmap);
Y(z80_writemap);
Z(&z80_writemem);
Z(&z80_readmem);
Z(&z80_writeport);
Z(&z80_readport);
//=======
Z(&action_replay);
Z(&eeprom_i2c);
Z(&spi_eeprom);
Z(&ggenie);
Y(activator);
Y(gamepad);
Z(&pad_index);
Z(&lightgun);
Z(&mouse);
Y(paddle);
Y(sportspad);
Y(teamplayer);
Z(&tablet);
Y(xe_a1p);
Z(&SN76489);
Y(fm_buffer);
Y(fm_last);
Z(&fm_ptr);
Z(&fm_cycles_ratio);
Z(&fm_cycles_start);
Z(&fm_cycles_count);
Z(&YM_Reset);
Z(&YM_Update);
Z(&YM_Write);
Z(&ym2413);
Z(&ym2612);
Z(&m2);
Z(&c1);
Z(&c2);
Z(&mem);
Y(out_fm);
Z(&bitmask);
Y(tmss);
Z(&rom_region);
Z(&pause_b);
Z(&eq);
Z(&llp);
Z(&rrp);
}

View File

@ -0,0 +1,319 @@
/****************************************************************************
* Genesis Plus
* Action Replay / Pro Action Replay emulation
*
* Copyright (C) 2009-2011 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
#define TYPE_PRO1 0x12
#define TYPE_PRO2 0x22
struct
{
uint8 enabled;
uint8 status;
uint8 *rom;
uint8 *ram;
uint16 regs[13];
uint16 old[4];
uint16 data[4];
uint32 addr[4];
} action_replay;
static void ar_write_regs(uint32 address, uint32 data);
static void ar_write_regs_2(uint32 address, uint32 data);
static void ar_write_ram_8(uint32 address, uint32 data);
void areplay_init(void)
{
int size;
FILE *f;
memset(&action_replay,0,sizeof(action_replay));
/* store Action replay ROM (max. 128k) & RAM (64k) above cartridge ROM + SRAM area */
if (cart.romsize > 0x810000) return;
action_replay.rom = cart.rom + 0x810000;
action_replay.ram = cart.rom + 0x830000;
/* Open Action Replay ROM */
f = fopen(AR_ROM,"rb");
if (f == NULL) return;
/* ROM size */
fseek(f, 0, SEEK_END);
size = ftell(f);
fseek(f, 0, SEEK_SET);
/* detect Action Replay board type */
switch (size)
{
case 0x8000:
{
/* normal Action Replay (32K) */
action_replay.enabled = TYPE_AR;
/* internal registers mapped at $010000-$01ffff */
m68k.memory_map[0x01].write16 = ar_write_regs;
break;
}
case 0x10000:
case 0x20000:
{
/* read Stack Pointer */
uint8 sp[4];
fread(&sp, 4, 1, f);
fseek(f, 0, SEEK_SET);
/* Detect board version */
if (sp[1] == 0x42)
{
/* PRO Action Replay 1 (64/128K) */
action_replay.enabled = TYPE_PRO1;
/* internal registers mapped at $010000-$01ffff */
m68k.memory_map[0x01].write16 = ar_write_regs;
}
else if (sp[1] == 0x60)
{
/* PRO Action Replay 2 (64K) */
action_replay.enabled = TYPE_PRO2;
/* internal registers mapped at $100000-$10ffff */
m68k.memory_map[0x10].write16 = ar_write_regs_2;
}
/* internal RAM (64k), mapped at $420000-$42ffff or $600000-$60ffff */
if (action_replay.enabled)
{
m68k.memory_map[sp[1]].base = action_replay.ram;
m68k.memory_map[sp[1]].read8 = NULL;
m68k.memory_map[sp[1]].read16 = NULL;
m68k.memory_map[sp[1]].write8 = ar_write_ram_8;
m68k.memory_map[sp[1]].write16 = NULL;
}
break;
}
default:
{
break;
}
}
if (action_replay.enabled)
{
/* Load ROM */
int i = 0;
for (i=0; i<size; i+=0x1000)
{
fread(action_replay.rom + i, 0x1000, 1, f);
}
#ifdef LSB_FIRST
for (i= 0; i<size; i+=2)
{
/* Byteswap ROM */
uint8 temp = action_replay.rom[i];
action_replay.rom[i] = action_replay.rom[i+1];
action_replay.rom[i+1] = temp;
}
#endif
}
/* Close ROM file */
fclose(f);
}
void areplay_shutdown(void)
{
/* clear existing patches */
areplay_set_status(AR_SWITCH_OFF);
/* disable device by default */
action_replay.enabled = 0;
}
void areplay_reset(int hard)
{
if (action_replay.enabled)
{
if (hard || (action_replay.status == AR_SWITCH_TRAINER))
{
/* reset internal registers */
memset(action_replay.regs, 0, sizeof(action_replay.regs));
memset(action_replay.old, 0, sizeof(action_replay.old));
memset(action_replay.data, 0, sizeof(action_replay.data));
memset(action_replay.addr, 0, sizeof(action_replay.addr));
/* by default, internal ROM is mapped at $000000-$00FFFF */
m68k.memory_map[0].base = action_replay.rom;
/* reset internal RAM on power-on */
if (hard)
{
memset(action_replay.ram,0xff,0x10000);
}
}
}
}
int areplay_get_status(void)
{
if (action_replay.enabled)
{
return action_replay.status;
}
return -1;
}
void areplay_set_status(int status)
{
if (action_replay.enabled)
{
/* no Trainer mode for normal Action Replay */
if ((action_replay.enabled == TYPE_AR) && (status == AR_SWITCH_TRAINER))
{
status = AR_SWITCH_OFF;
}
/* check status changes */
switch (status)
{
case AR_SWITCH_OFF:
case AR_SWITCH_TRAINER:
{
/* check that patches were previously enabled */
if (action_replay.status == AR_SWITCH_ON)
{
/* restore original data */
*(uint16 *)(cart.rom + action_replay.addr[0]) = action_replay.old[0];
*(uint16 *)(cart.rom + action_replay.addr[1]) = action_replay.old[1];
*(uint16 *)(cart.rom + action_replay.addr[2]) = action_replay.old[2];
*(uint16 *)(cart.rom + action_replay.addr[3]) = action_replay.old[3];
}
break;
}
case AR_SWITCH_ON:
{
/* check that patches were previously disabled */
if (action_replay.status != AR_SWITCH_ON)
{
/* decode patch data */
action_replay.data[0] = action_replay.regs[0];
action_replay.data[1] = action_replay.regs[4];
action_replay.data[2] = action_replay.regs[7];
action_replay.data[3] = action_replay.regs[10];
/* decode patch address ($000000-$7fffff) */
action_replay.addr[0] = (action_replay.regs[1] | ((action_replay.regs[2] & 0x3f00) << 8)) << 1;
action_replay.addr[1] = (action_replay.regs[5] | ((action_replay.regs[6] & 0x3f00) << 8)) << 1;
action_replay.addr[2] = (action_replay.regs[8] | ((action_replay.regs[9] & 0x3f00) << 8)) << 1;
action_replay.addr[3] = (action_replay.regs[11] | ((action_replay.regs[12] & 0x3f00) << 8)) << 1;
/* save original data */
action_replay.old[0] = *(uint16 *)(cart.rom + action_replay.addr[0]);
action_replay.old[1] = *(uint16 *)(cart.rom + action_replay.addr[1]);
action_replay.old[2] = *(uint16 *)(cart.rom + action_replay.addr[2]);
action_replay.old[3] = *(uint16 *)(cart.rom + action_replay.addr[3]);
/* patch new data */
*(uint16 *)(cart.rom + action_replay.addr[0]) = action_replay.data[0];
*(uint16 *)(cart.rom + action_replay.addr[1]) = action_replay.data[1];
*(uint16 *)(cart.rom + action_replay.addr[2]) = action_replay.data[2];
*(uint16 *)(cart.rom + action_replay.addr[3]) = action_replay.data[3];
}
break;
}
default:
{
return;
}
}
/* update status */
action_replay.status = status;
}
}
static void ar_write_regs(uint32 address, uint32 data)
{
/* register offset */
int offset = (address & 0xffff) >> 1;
if (offset > 12)
{
m68k_unused_16_w(address,data);
return;
}
/* update internal register */
action_replay.regs[offset] = data;
/* MODE register */
if (action_replay.regs[3] == 0xffff)
{
/* check switch status */
if (action_replay.status == AR_SWITCH_ON)
{
/* reset existing patches */
areplay_set_status(AR_SWITCH_OFF);
areplay_set_status(AR_SWITCH_ON);
}
/* enable Cartridge ROM */
m68k.memory_map[0].base = cart.rom;
}
}
static void ar_write_regs_2(uint32 address, uint32 data)
{
/* enable Cartridge ROM */
if (((address & 0xff) == 0x78) && (data == 0xffff))
{
m68k.memory_map[0].base = cart.rom;
}
}
static void ar_write_ram_8(uint32 address, uint32 data)
{
/* byte writes are handled as word writes, with LSB duplicated in MSB (/LWR is not used) */
*(uint16 *)(action_replay.ram + (address & 0xfffe)) = (data | (data << 8));
}

View File

@ -0,0 +1,52 @@
/****************************************************************************
* Genesis Plus
* DATEL Action Replay / Pro Action Replay emulation
*
* Copyright (C) 2009-2011 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _AREPLAY_H_
#define _AREPLAY_H_
#define AR_SWITCH_OFF (0)
#define AR_SWITCH_ON (1)
#define AR_SWITCH_TRAINER (2)
extern void areplay_init(void);
extern void areplay_shutdown(void);
extern void areplay_reset(int hard);
extern void areplay_set_status(int status);
extern int areplay_get_status(void);
#endif

View File

@ -0,0 +1,249 @@
/****************************************************************************
* Genesis Plus
* Microwire Serial EEPROM (93C46 only) support
*
* Copyright (C) 2011 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
#include "eeprom_93c.h"
/* fixed board implementation */
#define BIT_DATA (0)
#define BIT_CLK (1)
#define BIT_CS (2)
T_EEPROM_93C eeprom_93c;
void eeprom_93c_init()
{
/* default eeprom state */
memset(&eeprom_93c, 0, sizeof(T_EEPROM_93C));
eeprom_93c.data = 1;
eeprom_93c.state = WAIT_START93;
sram.custom = 3;
}
void eeprom_93c_write(unsigned char data)
{
/* Make sure CS is HIGH */
if (data & (1 << BIT_CS))
{
/* Data latched on CLK postive edge */
if ((data & (1 << BIT_CLK)) && !eeprom_93c.clk)
{
/* Current EEPROM state */
switch (eeprom_93c.state)
{
case WAIT_START93:
{
/* Wait for START bit */
if (data & (1 << BIT_DATA))
{
eeprom_93c.opcode = 0;
eeprom_93c.cycles = 0;
eeprom_93c.state = GET_OPCODE93;
}
break;
}
case GET_OPCODE93:
{
/* 8-bit buffer (opcode + address) */
eeprom_93c.opcode |= ((data >> BIT_DATA) & 1) << (7 - eeprom_93c.cycles);
eeprom_93c.cycles++;
if (eeprom_93c.cycles == 8)
{
/* Decode instruction */
switch ((eeprom_93c.opcode >> 6) & 3)
{
case 1:
{
/* WRITE */
eeprom_93c.buffer = 0;
eeprom_93c.cycles = 0;
eeprom_93c.state = WRITE_WORD93;
break;
}
case 2:
{
/* READ */
eeprom_93c.buffer = *(uint16 *)(sram.sram + ((eeprom_93c.opcode & 0x3F) << 1));
eeprom_93c.cycles = 0;
eeprom_93c.state = READ_WORD93;
/* Force DATA OUT */
eeprom_93c.data = 0;
break;
}
case 3:
{
/* ERASE */
if (eeprom_93c.we)
{
*(uint16 *)(sram.sram + ((eeprom_93c.opcode & 0x3F) << 1)) = 0xFFFF;
}
/* wait for next command */
eeprom_93c.state = WAIT_STANDBY93;
break;
}
default:
{
/* special command */
switch ((eeprom_93c.opcode >> 4) & 3)
{
case 1:
{
/* WRITE ALL */
eeprom_93c.buffer = 0;
eeprom_93c.cycles = 0;
eeprom_93c.state = WRITE_WORD93;
break;
}
case 2:
{
/* ERASE ALL */
if (eeprom_93c.we)
{
memset(sram.sram, 0xFF, 128);
}
/* wait for next command */
eeprom_93c.state = WAIT_STANDBY93;
break;
}
default:
{
/* WRITE ENABLE/DISABLE */
eeprom_93c.we = (eeprom_93c.opcode >> 4) & 1;
/* wait for next command */
eeprom_93c.state = WAIT_STANDBY93;
break;
}
}
break;
}
}
}
break;
}
case WRITE_WORD93:
{
/* 16-bit data buffer */
eeprom_93c.buffer |= ((data >> BIT_DATA) & 1) << (15 - eeprom_93c.cycles);
eeprom_93c.cycles++;
if (eeprom_93c.cycles == 16)
{
/* check EEPROM write protection */
if (eeprom_93c.we)
{
if (eeprom_93c.opcode & 0x40)
{
/* write one word */
*(uint16 *)(sram.sram + ((eeprom_93c.opcode & 0x3F) << 1)) = eeprom_93c.buffer;
}
else
{
/* write 64 words */
int i;
for (i=0; i<64; i++)
{
*(uint16 *)(sram.sram + (i << 1)) = eeprom_93c.buffer;
}
}
}
/* wait for next command */
eeprom_93c.state = WAIT_STANDBY93;
}
break;
}
case READ_WORD93:
{
/* set DATA OUT */
eeprom_93c.data = ((eeprom_93c.buffer >> (15 - eeprom_93c.cycles)) & 1);
eeprom_93c.cycles++;
if (eeprom_93c.cycles == 16)
{
/* read next word (93C46B) */
eeprom_93c.opcode++;
eeprom_93c.cycles = 0;
eeprom_93c.buffer = *(uint16 *)(sram.sram + ((eeprom_93c.opcode & 0x3F) << 1));
}
break;
}
default:
{
/* wait for STANDBY mode */
break;
}
}
}
}
else
{
/* CS HIGH->LOW transition */
if (eeprom_93c.cs)
{
/* standby mode */
eeprom_93c.data = 1;
eeprom_93c.state = WAIT_START93;
}
}
/* Update input lines */
eeprom_93c.cs = (data >> BIT_CS) & 1;
eeprom_93c.clk = (data >> BIT_CLK) & 1;
}
unsigned char eeprom_93c_read(void)
{
return ((eeprom_93c.cs << BIT_CS) | (eeprom_93c.data << BIT_DATA) | (1 << BIT_CLK));
}

View File

@ -0,0 +1,72 @@
/****************************************************************************
* Genesis Plus
* Microwire Serial EEPROM (93C46 only) support
*
* Copyright (C) 2011 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _EEPROM_93C_H_
#define _EEPROM_93C_H_
typedef enum
{
WAIT_STANDBY93,
WAIT_START93,
GET_OPCODE93,
WRITE_WORD93,
READ_WORD93
} T_STATE_93C;
typedef struct
{
uint8 enabled; /* 1: chip enabled */
uint8 cs; /* CHIP SELECT line state */
uint8 clk; /* CLK line state */
uint8 data; /* DATA OUT line state */
uint8 cycles; /* current operation cycle */
uint8 we; /* 1: write enabled */
uint8 opcode; /* 8-bit opcode + address */
uint16 buffer; /* 16-bit data buffer */
T_STATE_93C state; /* current operation state */
} T_EEPROM_93C;
/* global variables */
extern T_EEPROM_93C eeprom_93c;
/* Function prototypes */
extern void eeprom_93c_init();
extern void eeprom_93c_write(unsigned char data);
extern unsigned char eeprom_93c_read(void);
#endif

View File

@ -0,0 +1,583 @@
/****************************************************************************
* Genesis Plus
* I2C Serial EEPROM (24Cxx) support
*
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
#include "eeprom_i2c.h"
#define GAME_CNT 28
/* this defines the type of EEPROM inside the game cartridge as Backup RAM
*
* Here are some notes from 8BitWizard (http://www.spritesmind.net/_GenDev/forum):
*
* Mode 1 (7-bit) - the chip takes a single byte with a 7-bit memory address and a R/W bit (24C01)
* Mode 2 (8-bit) - the chip takes a 7-bit device address and R/W bit followed by an 8-bit memory address;
* the device address may contain up to three more memory address bits (24C01 - 24C16).
* You can also string eight 24C01, four 24C02, two 24C08, or various combinations, set their address config lines correctly,
* and the result appears exactly the same as a 24C16
* Mode 3 (16-bit) - the chip takes a 7-bit device address and R/W bit followed by a 16-bit memory address (24C32 and larger)
*
* Also, while most 24Cxx are addressed at 200000-2FFFFF, I have found two different ways of mapping the control lines.
* EA uses SDA on D7 (read/write) and SCL on D6 (write only), and I have found boards using different mapping (I think Accolade)
* which uses D1-read=SDA, D0-write=SDA, D1-write=SCL. Accolade also has a custom-chip mapper which may even use a third method.
*/
typedef struct
{
char game_id[16];
uint16 chk;
T_CONFIG_I2C config;
} T_GAME_ENTRY;
static const T_GAME_ENTRY database[GAME_CNT] =
{
/* ACCLAIM mappers */
/* 24C02 (old mapper) */
{{"T-081326" }, 0, {8, 0xFF, 0xFF, 0x200001, 0x200001, 0x200001, 0, 1, 1}}, /* NBA Jam (UE) */
{{"T-81033" }, 0, {8, 0xFF, 0xFF, 0x200001, 0x200001, 0x200001, 0, 1, 1}}, /* NBA Jam (J) */
/* 24C02 */
{{"T-081276" }, 0, {8, 0xFF, 0xFF, 0x200001, 0x200001, 0x200000, 0, 0, 0}}, /* NFL Quarterback Club */
/* 24C04 */
{{"T-81406" }, 0, {8, 0x1FF, 0x1FF, 0x200001, 0x200001, 0x200000, 0, 0, 0}}, /* NBA Jam TE */
/* 24C16 */
{{"T-081586" }, 0, {8, 0x7FF, 0x7FF, 0x200001, 0x200001, 0x200000, 0, 0, 0}}, /* NFL Quarterback Club '96 */
/* 24C65 */
{{"T-81576" }, 0, {16, 0x1FFF, 0x1FFF, 0x200001, 0x200001, 0x200000, 0, 0, 0}}, /* College Slam */
{{"T-81476" }, 0, {16, 0x1FFF, 0x1FFF, 0x200001, 0x200001, 0x200000, 0, 0, 0}}, /* Frank Thomas Big Hurt Baseball */
/* EA mapper (X24C01 only) */
{{"T-50176" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 7, 7, 6}}, /* Rings of Power */
{{"T-50396" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 7, 7, 6}}, /* NHLPA Hockey 93 */
{{"T-50446" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 7, 7, 6}}, /* John Madden Football 93 */
{{"T-50516" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 7, 7, 6}}, /* John Madden Football 93 (Championship Ed.) */
{{"T-50606" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 7, 7, 6}}, /* Bill Walsh College Football */
/* SEGA mapper (X24C01 only) */
{{"T-12046" }, 0xAD23, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Megaman - The Wily Wars */
{{"T-12053" }, 0xEA80, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Rockman Mega World [Alt] */
{{"MK-1215" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Evander 'Real Deal' Holyfield's Boxing */
{{"MK-1228" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Greatest Heavyweights of the Ring (U) */
{{"G-5538" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Greatest Heavyweights of the Ring (J) */
{{"PR-1993" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Greatest Heavyweights of the Ring (E) */
{{"G-4060" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Wonderboy in Monster World */
{{"00001211-00"}, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Sports Talk Baseball */
{{"00004076-00"}, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Honoo no Toukyuuji Dodge Danpei */
{{"G-4524" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Ninja Burai Densetsu */
{{"00054503-00"}, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Game Toshokan */
/* CODEMASTERS mapper */
/* 24C08 */
{{"T-120106" }, 0, {8, 0x3FF, 0x3FF, 0x300000, 0x380001, 0x300000, 0, 7, 1}}, /* Brian Lara Cricket */
{{"00000000-00"}, 0xCEE0, {8, 0x3FF, 0x3FF, 0x300000, 0x380001, 0x300000, 0, 7, 1}}, /* Micro Machines Military */
/* 24C16 */
{{"T-120096" }, 0, {8, 0x7FF, 0x7FF, 0x300000, 0x380001, 0x300000, 0, 7, 1}}, /* Micro Machines 2 - Turbo Tournament */
{{"00000000-00"}, 0x2C41, {8, 0x7FF, 0x7FF, 0x300000, 0x380001, 0x300000, 0, 7, 1}}, /* Micro Machines Turbo Tournament 96 */
/* 24C65 */
{{"T-120146-50"}, 0, {16, 0x1FFF, 0x1FFF, 0x300000, 0x380001, 0x300000, 0, 7, 1}} /* Brian Lara Cricket 96, Shane Warne Cricket */
};
T_EEPROM_I2C eeprom_i2c;
static unsigned int eeprom_i2c_read_byte(unsigned int address);
static unsigned int eeprom_i2c_read_word(unsigned int address);
static void eeprom_i2c_write_byte(unsigned int address, unsigned int data);
static void eeprom_i2c_write_word(unsigned int address, unsigned int data);
void eeprom_i2c_init()
{
int i = 0;
/* initialize eeprom */
memset(&eeprom_i2c, 0, sizeof(T_EEPROM_I2C));
eeprom_i2c.sda = eeprom_i2c.old_sda = 1;
eeprom_i2c.scl = eeprom_i2c.old_scl = 1;
eeprom_i2c.state = STAND_BY;
/* no eeprom by default */
sram.custom = 0;
/* look into game database */
while (i<GAME_CNT)
{
if (strstr(rominfo.product,database[i].game_id) != NULL)
{
/* additional check (Micro Machines, Rockman Mega World) */
if ((database[i].chk == 0x0000) || (database[i].chk == rominfo.realchecksum))
{
sram.custom = 1;
sram.on = 1;
memcpy(&eeprom_i2c.config, &database[i].config, sizeof(T_CONFIG_I2C));
i = GAME_CNT;
}
}
i++;
}
/* Game not found in database but ROM header indicates it uses serial EEPROM */
if (!sram.custom && sram.detected)
{
if ((READ_BYTE(cart.rom,0x1b2) == 0xe8) || ((sram.end - sram.start) < 2))
{
/* set SEGA mapper as default */
sram.custom = 1;
memcpy(&eeprom_i2c.config, &database[9].config, sizeof(T_CONFIG_I2C));
}
}
/* initialize m68k bus handlers */
if (sram.custom)
{
m68k.memory_map[eeprom_i2c.config.sda_out_adr >> 16].read8 = eeprom_i2c_read_byte;
m68k.memory_map[eeprom_i2c.config.sda_out_adr >> 16].read16 = eeprom_i2c_read_word;
m68k.memory_map[eeprom_i2c.config.sda_in_adr >> 16].read8 = eeprom_i2c_read_byte;
m68k.memory_map[eeprom_i2c.config.sda_in_adr >> 16].read16 = eeprom_i2c_read_word;
m68k.memory_map[eeprom_i2c.config.scl_adr >> 16].write8 = eeprom_i2c_write_byte;
m68k.memory_map[eeprom_i2c.config.scl_adr >> 16].write16 = eeprom_i2c_write_word;
zbank_memory_map[eeprom_i2c.config.sda_out_adr >> 16].read = eeprom_i2c_read_byte;
zbank_memory_map[eeprom_i2c.config.sda_in_adr >> 16].read = eeprom_i2c_read_byte;
zbank_memory_map[eeprom_i2c.config.scl_adr >> 16].write = eeprom_i2c_write_byte;
}
}
INLINE void Detect_START()
{
if (eeprom_i2c.old_scl && eeprom_i2c.scl)
{
if (eeprom_i2c.old_sda && !eeprom_i2c.sda)
{
eeprom_i2c.cycles = 0;
eeprom_i2c.slave_mask = 0;
if (eeprom_i2c.config.address_bits == 7)
{
eeprom_i2c.word_address = 0;
eeprom_i2c.state = GET_WORD_ADR_7BITS;
}
else eeprom_i2c.state = GET_SLAVE_ADR;
}
}
}
INLINE void Detect_STOP()
{
if (eeprom_i2c.old_scl && eeprom_i2c.scl)
{
if (!eeprom_i2c.old_sda && eeprom_i2c.sda)
{
eeprom_i2c.state = STAND_BY;
}
}
}
static void eeprom_i2c_update(void)
{
/* EEPROM current state */
switch (eeprom_i2c.state)
{
/* Standby Mode */
case STAND_BY:
{
Detect_START();
Detect_STOP();
break;
}
/* Suspended Mode */
case WAIT_STOP:
{
Detect_STOP();
break;
}
/* Get Word Address 7 bits: MODE-1 only (24C01)
* and R/W bit
*/
case GET_WORD_ADR_7BITS:
{
Detect_START();
Detect_STOP();
/* look for SCL LOW to HIGH transition */
if (!eeprom_i2c.old_scl && eeprom_i2c.scl)
{
if (eeprom_i2c.cycles == 0) eeprom_i2c.cycles ++;
}
/* look for SCL HIGH to LOW transition */
if (eeprom_i2c.old_scl && !eeprom_i2c.scl && (eeprom_i2c.cycles > 0))
{
if (eeprom_i2c.cycles < 8)
{
eeprom_i2c.word_address |= (eeprom_i2c.old_sda << (7 - eeprom_i2c.cycles));
}
else if (eeprom_i2c.cycles == 8)
{
eeprom_i2c.rw = eeprom_i2c.old_sda;
}
else
{ /* ACK CYCLE */
eeprom_i2c.cycles = 0;
eeprom_i2c.word_address &= eeprom_i2c.config.size_mask;
eeprom_i2c.state = eeprom_i2c.rw ? READ_DATA : WRITE_DATA;
}
eeprom_i2c.cycles ++;
}
break;
}
/* Get Slave Address (3bits) : MODE-2 & MODE-3 only (24C01 - 24C512) (0-3bits, depending on the array size)
* or/and Word Address MSB: MODE-2 only (24C04 - 24C16) (0-3bits, depending on the array size)
* and R/W bit
*/
case GET_SLAVE_ADR:
{
Detect_START();
Detect_STOP();
/* look for SCL LOW to HIGH transition */
if (!eeprom_i2c.old_scl && eeprom_i2c.scl)
{
if (eeprom_i2c.cycles == 0) eeprom_i2c.cycles ++;
}
/* look for SCL HIGH to LOW transition */
if (eeprom_i2c.old_scl && !eeprom_i2c.scl && (eeprom_i2c.cycles > 0))
{
if ((eeprom_i2c.cycles > 4) && (eeprom_i2c.cycles <8))
{
if ((eeprom_i2c.config.address_bits == 16) ||
(eeprom_i2c.config.size_mask < (1 << (15 - eeprom_i2c.cycles))))
{
/* this is a SLAVE ADDRESS bit */
eeprom_i2c.slave_mask |= (eeprom_i2c.old_sda << (7 - eeprom_i2c.cycles));
}
else
{
/* this is a WORD ADDRESS high bit */
if (eeprom_i2c.old_sda) eeprom_i2c.word_address |= (1 << (15 - eeprom_i2c.cycles));
else eeprom_i2c.word_address &= ~(1 << (15 - eeprom_i2c.cycles));
}
}
else if (eeprom_i2c.cycles == 8) eeprom_i2c.rw = eeprom_i2c.old_sda;
else if (eeprom_i2c.cycles > 8)
{
/* ACK CYCLE */
eeprom_i2c.cycles = 0;
if (eeprom_i2c.config.address_bits == 16)
{
/* two ADDRESS bytes */
eeprom_i2c.state = eeprom_i2c.rw ? READ_DATA : GET_WORD_ADR_HIGH;
eeprom_i2c.slave_mask <<= 16;
}
else
{
/* one ADDRESS byte */
eeprom_i2c.state = eeprom_i2c.rw ? READ_DATA : GET_WORD_ADR_LOW;
eeprom_i2c.slave_mask <<= 8;
}
}
eeprom_i2c.cycles ++;
}
break;
}
/* Get Word Address MSB (4-8bits depending on the array size)
* MODE-3 only (24C32 - 24C512)
*/
case GET_WORD_ADR_HIGH:
{
Detect_START();
Detect_STOP();
/* look for SCL HIGH to LOW transition */
if (eeprom_i2c.old_scl && !eeprom_i2c.scl)
{
if (eeprom_i2c.cycles < 9)
{
if ((eeprom_i2c.config.size_mask + 1) < (1 << (17 - eeprom_i2c.cycles)))
{
/* ignored bit: slave mask should be right-shifted by one */
eeprom_i2c.slave_mask >>= 1;
}
else
{
/* this is a WORD ADDRESS high bit */
if (eeprom_i2c.old_sda) eeprom_i2c.word_address |= (1 << (16 - eeprom_i2c.cycles));
else eeprom_i2c.word_address &= ~(1 << (16 - eeprom_i2c.cycles));
}
eeprom_i2c.cycles ++;
}
else
{
/* ACK CYCLE */
eeprom_i2c.cycles = 1;
eeprom_i2c.state = GET_WORD_ADR_LOW;
}
}
break;
}
/* Get Word Address LSB: 7bits (24C01) or 8bits (24C02-24C512)
* MODE-2 and MODE-3 only (24C01 - 24C512)
*/
case GET_WORD_ADR_LOW:
{
Detect_START();
Detect_STOP();
/* look for SCL HIGH to LOW transition */
if (eeprom_i2c.old_scl && !eeprom_i2c.scl)
{
if (eeprom_i2c.cycles < 9)
{
if ((eeprom_i2c.config.size_mask + 1) < (1 << (9 - eeprom_i2c.cycles)))
{
/* ignored bit (X24C01): slave mask should be right-shifted by one */
eeprom_i2c.slave_mask >>= 1;
}
else
{
/* this is a WORD ADDRESS high bit */
if (eeprom_i2c.old_sda) eeprom_i2c.word_address |= (1 << (8 - eeprom_i2c.cycles));
else eeprom_i2c.word_address &= ~(1 << (8 - eeprom_i2c.cycles));
}
eeprom_i2c.cycles ++;
}
else
{
/* ACK CYCLE */
eeprom_i2c.cycles = 1;
eeprom_i2c.word_address &= eeprom_i2c.config.size_mask;
eeprom_i2c.state = WRITE_DATA;
}
}
break;
}
/*
* Read Cycle
*/
case READ_DATA:
{
Detect_START();
Detect_STOP();
/* look for SCL HIGH to LOW transition */
if (eeprom_i2c.old_scl && !eeprom_i2c.scl)
{
if (eeprom_i2c.cycles < 9) eeprom_i2c.cycles ++;
else
{
eeprom_i2c.cycles = 1;
/* ACK not received */
if (eeprom_i2c.old_sda) eeprom_i2c.state = WAIT_STOP;
}
}
break;
}
/*
* Write Cycle
*/
case WRITE_DATA:
{
Detect_START();
Detect_STOP();
/* look for SCL HIGH to LOW transition */
if (eeprom_i2c.old_scl && !eeprom_i2c.scl)
{
if (eeprom_i2c.cycles < 9)
{
/* Write DATA bits (max 64kBytes) */
uint16 sram_address = (eeprom_i2c.slave_mask | eeprom_i2c.word_address) & 0xFFFF;
if (eeprom_i2c.old_sda) sram.sram[sram_address] |= (1 << (8 - eeprom_i2c.cycles));
else sram.sram[sram_address] &= ~(1 << (8 - eeprom_i2c.cycles));
if (eeprom_i2c.cycles == 8)
{
/* WORD ADDRESS is incremented (roll up at maximum pagesize) */
eeprom_i2c.word_address = (eeprom_i2c.word_address & (0xFFFF - eeprom_i2c.config.pagewrite_mask)) |
((eeprom_i2c.word_address + 1) & eeprom_i2c.config.pagewrite_mask);
}
eeprom_i2c.cycles ++;
}
else eeprom_i2c.cycles = 1; /* ACK cycle */
}
break;
}
}
eeprom_i2c.old_scl = eeprom_i2c.scl;
eeprom_i2c.old_sda = eeprom_i2c.sda;
}
static unsigned char eeprom_i2c_out(void)
{
uint8 sda_out = eeprom_i2c.sda;
/* EEPROM state */
switch (eeprom_i2c.state)
{
case READ_DATA:
{
if (eeprom_i2c.cycles < 9)
{
/* Return DATA bits (max 64kBytes) */
uint16 sram_address = (eeprom_i2c.slave_mask | eeprom_i2c.word_address) & 0xffff;
sda_out = (sram.sram[sram_address] >> (8 - eeprom_i2c.cycles)) & 1;
if (eeprom_i2c.cycles == 8)
{
/* WORD ADDRESS is incremented (roll up at maximum array size) */
eeprom_i2c.word_address ++;
eeprom_i2c.word_address &= eeprom_i2c.config.size_mask;
}
}
break;
}
case GET_WORD_ADR_7BITS:
case GET_SLAVE_ADR:
case GET_WORD_ADR_HIGH:
case GET_WORD_ADR_LOW:
case WRITE_DATA:
{
if (eeprom_i2c.cycles == 9) sda_out = 0;
break;
}
default:
{
break;
}
}
return (sda_out << eeprom_i2c.config.sda_out_bit);
}
static unsigned int eeprom_i2c_read_byte(unsigned int address)
{
if (address == eeprom_i2c.config.sda_out_adr)
{
return eeprom_i2c_out();
}
return READ_BYTE(cart.rom, address);
}
static unsigned int eeprom_i2c_read_word(unsigned int address)
{
if (address == eeprom_i2c.config.sda_out_adr)
{
return (eeprom_i2c_out() << 8);
}
if (address == (eeprom_i2c.config.sda_out_adr ^ 1))
{
return eeprom_i2c_out();
}
return *(uint16 *)(cart.rom + address);
}
static void eeprom_i2c_write_byte(unsigned int address, unsigned int data)
{
int do_update = 0;
if (address == eeprom_i2c.config.sda_in_adr)
{
eeprom_i2c.sda = (data >> eeprom_i2c.config.sda_in_bit) & 1;
do_update = 1;
}
if (address == eeprom_i2c.config.scl_adr)
{
eeprom_i2c.scl = (data >> eeprom_i2c.config.scl_bit) & 1;
do_update = 1;
}
if (do_update)
{
eeprom_i2c_update();
return;
}
m68k_unused_8_w(address, data);
}
static void eeprom_i2c_write_word(unsigned int address, unsigned int data)
{
int do_update = 0;
if (address == eeprom_i2c.config.sda_in_adr)
{
eeprom_i2c.sda = (data >> (8 + eeprom_i2c.config.sda_in_bit)) & 1;
do_update = 1;
}
else if (address == (eeprom_i2c.config.sda_in_adr ^1))
{
eeprom_i2c.sda = (data >> eeprom_i2c.config.sda_in_bit) & 1;
do_update = 1;
}
if (address == eeprom_i2c.config.scl_adr)
{
eeprom_i2c.scl = (data >> (8 + eeprom_i2c.config.scl_bit)) & 1;
do_update = 1;
}
else if (address == (eeprom_i2c.config.scl_adr ^1))
{
eeprom_i2c.scl = (data >> eeprom_i2c.config.scl_bit) & 1;
do_update = 1;
}
if (do_update)
{
eeprom_i2c_update();
return;
}
m68k_unused_16_w(address, data);
}

View File

@ -0,0 +1,86 @@
/****************************************************************************
* Genesis Plus
* I2C Serial EEPROM (24Cxx) support
*
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _EEPROM_I2C_H_
#define _EEPROM_I2C_H_
typedef struct
{
uint8 address_bits; /* number of bits needed to address memory: 7, 8 or 16 */
uint16 size_mask; /* depends on the max size of the memory (in bytes) */
uint16 pagewrite_mask; /* depends on the maximal number of bytes that can be written in a single write cycle */
uint32 sda_in_adr; /* 68000 memory address mapped to SDA_IN */
uint32 sda_out_adr; /* 68000 memory address mapped to SDA_OUT */
uint32 scl_adr; /* 68000 memory address mapped to SCL */
uint8 sda_in_bit; /* bit offset for SDA_IN */
uint8 sda_out_bit; /* bit offset for SDA_OUT */
uint8 scl_bit; /* bit offset for SCL */
} T_CONFIG_I2C;
typedef enum
{
STAND_BY = 0,
WAIT_STOP,
GET_SLAVE_ADR,
GET_WORD_ADR_7BITS,
GET_WORD_ADR_HIGH,
GET_WORD_ADR_LOW,
WRITE_DATA,
READ_DATA
} T_STATE_I2C;
typedef struct
{
uint8 sda; /* current /SDA line state */
uint8 scl; /* current /SCL line state */
uint8 old_sda; /* previous /SDA line state */
uint8 old_scl; /* previous /SCL line state */
uint8 cycles; /* current operation cycle number (0-9) */
uint8 rw; /* operation type (1:READ, 0:WRITE) */
uint16 slave_mask; /* device address (shifted by the memory address width)*/
uint16 word_address; /* memory address */
T_STATE_I2C state; /* current operation state */
T_CONFIG_I2C config; /* EEPROM characteristics for this game */
} T_EEPROM_I2C;
extern T_EEPROM_I2C eeprom_i2c;
/* Function prototypes */
extern void eeprom_i2c_init();
#endif

View File

@ -0,0 +1,337 @@
/****************************************************************************
* Genesis Plus
* SPI Serial EEPROM (25xxx/95xxx) support
*
* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
#include "eeprom_spi.h"
/* max supported size 64KB (25x512/95x512) */
#define SIZE_MASK 0xffff
#define PAGE_MASK 0x7f
/* hard-coded board implementation (!WP pin not used) */
#define BIT_DATA (0)
#define BIT_CLK (1)
#define BIT_HOLD (2)
#define BIT_CS (3)
T_EEPROM_SPI spi_eeprom;
void eeprom_spi_init()
{
/* reset eeprom state */
memset(&spi_eeprom, 0, sizeof(T_EEPROM_SPI));
spi_eeprom.out = 1;
spi_eeprom.state = GET_OPCODE;
/* enable backup RAM */
sram.custom = 2;
sram.on = 1;
}
void eeprom_spi_write(unsigned char data)
{
/* Make sure !HOLD is high */
if (data & (1 << BIT_HOLD))
{
/* Check !CS state */
if (data & (1 << BIT_CS))
{
/* !CS high -> end of current operation */
spi_eeprom.cycles = 0;
spi_eeprom.out = 1;
spi_eeprom.opcode = 0;
spi_eeprom.state = GET_OPCODE;
}
else
{
/* !CS low -> process current operation */
switch (spi_eeprom.state)
{
case GET_OPCODE:
{
/* latch data on CLK positive edge */
if ((data & (1 << BIT_CLK)) && !spi_eeprom.clk)
{
/* 8-bit opcode buffer */
spi_eeprom.opcode |= ((data >> BIT_DATA) & 1);
spi_eeprom.cycles++;
/* last bit ? */
if (spi_eeprom.cycles == 8)
{
/* reset cycles count */
spi_eeprom.cycles = 0;
/* Decode instruction */
switch (spi_eeprom.opcode)
{
case 0x01:
{
/* WRITE STATUS */
spi_eeprom.buffer = 0;
spi_eeprom.state = WRITE_BYTE;
break;
}
case 0x02:
{
/* WRITE BYTE */
spi_eeprom.addr = 0;
spi_eeprom.state = GET_ADDRESS;
break;
}
case 0x03:
{
/* READ BYTE */
spi_eeprom.addr = 0;
spi_eeprom.state = GET_ADDRESS;
break;
}
case 0x04:
{
/* WRITE DISABLE */
spi_eeprom.status &= ~0x02;
spi_eeprom.state = STANDBY;
break;
}
case 0x05:
{
/* READ STATUS */
spi_eeprom.buffer = spi_eeprom.status;
spi_eeprom.state = READ_BYTE;
break;
}
case 0x06:
{
/* WRITE ENABLE */
spi_eeprom.status |= 0x02;
spi_eeprom.state = STANDBY;
break;
}
default:
{
/* specific instructions (not supported) */
spi_eeprom.state = STANDBY;
break;
}
}
}
else
{
/* shift opcode value */
spi_eeprom.opcode = spi_eeprom.opcode << 1;
}
}
break;
}
case GET_ADDRESS:
{
/* latch data on CLK positive edge */
if ((data & (1 << BIT_CLK)) && !spi_eeprom.clk)
{
/* 16-bit address */
spi_eeprom.addr |= ((data >> BIT_DATA) & 1);
spi_eeprom.cycles++;
/* last bit ? */
if (spi_eeprom.cycles == 16)
{
/* reset cycles count */
spi_eeprom.cycles = 0;
/* mask unused address bits */
spi_eeprom.addr &= SIZE_MASK;
/* operation type */
if (spi_eeprom.opcode & 0x01)
{
/* READ operation */
spi_eeprom.buffer = sram.sram[spi_eeprom.addr];
spi_eeprom.state = READ_BYTE;
}
else
{
/* WRITE operation */
spi_eeprom.buffer = 0;
spi_eeprom.state = WRITE_BYTE;
}
}
else
{
/* shift address value */
spi_eeprom.addr = spi_eeprom.addr << 1;
}
}
break;
}
case WRITE_BYTE:
{
/* latch data on CLK positive edge */
if ((data & (1 << BIT_CLK)) && !spi_eeprom.clk)
{
/* 8-bit data buffer */
spi_eeprom.buffer |= ((data >> BIT_DATA) & 1);
spi_eeprom.cycles++;
/* last bit ? */
if (spi_eeprom.cycles == 8)
{
/* reset cycles count */
spi_eeprom.cycles = 0;
/* write data to destination */
if (spi_eeprom.opcode & 0x01)
{
/* update status register */
spi_eeprom.status = (spi_eeprom.status & 0x02) | (spi_eeprom.buffer & 0x0c);
/* wait for operation end */
spi_eeprom.state = STANDBY;
}
else
{
/* Memory Array (write-protected) */
if (spi_eeprom.status & 2)
{
/* check array protection bits (BP0, BP1) */
switch ((spi_eeprom.status >> 2) & 0x03)
{
case 0x01:
{
/* $C000-$FFFF (sector #3) is protected */
if (spi_eeprom.addr < 0xC000)
{
sram.sram[spi_eeprom.addr] = spi_eeprom.buffer;
}
break;
}
case 0x02:
{
/* $8000-$FFFF (sectors #2 and #3) is protected */
if (spi_eeprom.addr < 0x8000)
{
sram.sram[spi_eeprom.addr] = spi_eeprom.buffer;
}
break;
}
case 0x03:
{
/* $0000-$FFFF (all sectors) is protected */
break;
}
default:
{
/* no sectors protected */
sram.sram[spi_eeprom.addr] = spi_eeprom.buffer;
break;
}
}
}
/* reset data buffer */
spi_eeprom.buffer = 0;
/* increase array address (sequential writes are limited within the same page) */
spi_eeprom.addr = (spi_eeprom.addr & ~PAGE_MASK) | ((spi_eeprom.addr + 1) & PAGE_MASK);
}
}
else
{
/* shift data buffer value */
spi_eeprom.buffer = spi_eeprom.buffer << 1;
}
}
break;
}
case READ_BYTE:
{
/* output data on CLK positive edge */
if ((data & (1 << BIT_CLK)) && !spi_eeprom.clk)
{
/* read out bits */
spi_eeprom.out = (spi_eeprom.buffer >> (7 - spi_eeprom.cycles)) & 1;
spi_eeprom.cycles++;
/* last bit ? */
if (spi_eeprom.cycles == 8)
{
/* reset cycles count */
spi_eeprom.cycles = 0;
/* read from memory array ? */
if (spi_eeprom.opcode == 0x03)
{
/* read next array byte */
spi_eeprom.addr = (spi_eeprom.addr + 1) & SIZE_MASK;
spi_eeprom.buffer = sram.sram[spi_eeprom.addr];
}
}
}
break;
}
default:
{
/* wait for !CS low->high transition */
break;
}
}
}
}
/* update input lines */
spi_eeprom.cs = (data >> BIT_CS) & 1;
spi_eeprom.clk = (data >> BIT_CLK) & 1;
}
unsigned int eeprom_spi_read(unsigned int address)
{
return (spi_eeprom.out << BIT_DATA);
}

View File

@ -0,0 +1,71 @@
/****************************************************************************
* Genesis Plus
* SPI Serial EEPROM (25XX512 only) support
*
* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _EEPROM_SPI_H_
#define _EEPROM_SPI_H_
typedef enum
{
STANDBY,
GET_OPCODE,
GET_ADDRESS,
WRITE_BYTE,
READ_BYTE
} T_STATE_SPI;
typedef struct
{
uint8 cs; /* !CS line state */
uint8 clk; /* SCLK line state */
uint8 out; /* SO line state */
uint8 status; /* status register */
uint8 opcode; /* 8-bit opcode */
uint8 buffer; /* 8-bit data buffer */
uint16 addr; /* 16-bit address */
uint32 cycles; /* current operation cycle */
T_STATE_SPI state; /* current operation state */
} T_EEPROM_SPI;
extern T_EEPROM_SPI spi_eeprom;
/* Function prototypes */
extern void eeprom_spi_init();
extern void eeprom_spi_write(unsigned char data);
extern unsigned int eeprom_spi_read(unsigned int address);
#endif

View File

@ -0,0 +1,283 @@
/****************************************************************************
* Genesis Plus
* Game Genie Hardware emulation
*
* Copyright (C) 2009-2011 Eke-Eke (Genesis Plus GX)
*
* Based on documentation from Charles McDonald
* (http://cgfm2.emuviews.com/txt/genie.txt)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
struct
{
uint8 enabled;
uint8 *rom;
uint16 regs[0x20];
uint16 old[6];
uint16 data[6];
uint32 addr[6];
} ggenie;
static unsigned int ggenie_read_byte(unsigned int address);
static unsigned int ggenie_read_word(unsigned int address);
static void ggenie_write_byte(unsigned int address, unsigned int data);
static void ggenie_write_word(unsigned int address, unsigned int data);
static void ggenie_write_regs(unsigned int offset, unsigned int data);
void ggenie_init(void)
{
int i;
FILE *f;
memset(&ggenie,0,sizeof(ggenie));
/* Store Game Genie ROM (32k) above cartridge ROM + SRAM area */
if (cart.romsize > 0x810000) return;
ggenie.rom = cart.rom + 0x810000;
/* Open Game Genie ROM file */
f = fopen(GG_ROM,"rb");
if (f == NULL) return;
/* Load ROM */
for (i=0; i<0x8000; i+=0x1000)
{
fread(ggenie.rom + i, 0x1000, 1, f);
}
/* Close ROM file */
fclose(f);
#ifdef LSB_FIRST
for (i=0; i<0x8000; i+=2)
{
/* Byteswap ROM */
uint8 temp = ggenie.rom[i];
ggenie.rom[i] = ggenie.rom[i+1];
ggenie.rom[i+1] = temp;
}
#endif
/* $0000-$7fff mirrored into $8000-$ffff */
memcpy(ggenie.rom + 0x8000, ggenie.rom, 0x8000);
/* set flag */
ggenie.enabled = 1;
}
void ggenie_shutdown(void)
{
if (ggenie.enabled)
{
ggenie_switch(0);
ggenie.enabled = 0;
}
}
void ggenie_reset(int hard)
{
if (ggenie.enabled)
{
if (hard)
{
/* clear codes */
ggenie_switch(0);
/* reset internal state */
memset(ggenie.regs,0,sizeof(ggenie.regs));
memset(ggenie.old,0,sizeof(ggenie.old));
memset(ggenie.data,0,sizeof(ggenie.data));
memset(ggenie.addr,0,sizeof(ggenie.addr));
}
/* Game Genie ROM is mapped at $000000-$007fff */
m68k.memory_map[0].base = ggenie.rom;
/* Internal registers are mapped at $000000-$00001f */
m68k.memory_map[0].write8 = ggenie_write_byte;
m68k.memory_map[0].write16 = ggenie_write_word;
/* Disable registers reads */
m68k.memory_map[0].read16 = NULL;
}
}
void ggenie_switch(int enable)
{
int i;
if (enable)
{
/* enable cheats */
for (i=0; i<6; i++)
{
/* patch is enabled ? */
if (ggenie.regs[0] & (1 << i))
{
/* save old value and patch ROM if enabled */
ggenie.old[i] = *(uint16 *)(cart.rom + ggenie.addr[i]);
*(uint16 *)(cart.rom + ggenie.addr[i]) = ggenie.data[i];
}
}
}
else
{
/* disable cheats in reversed order in case the same address is used by multiple patches */
for (i=5; i>=0; i--)
{
/* patch is enabled ? */
if (ggenie.regs[0] & (1 << i))
{
/* restore original ROM value */
*(uint16 *)(cart.rom + ggenie.addr[i]) = ggenie.old[i];
}
}
}
}
static unsigned int ggenie_read_byte(unsigned int address)
{
unsigned int data = ggenie.regs[(address >> 1) & 0x1f];
return ((address & 1) ? (data & 0xff) : ((data >> 8) & 0xff));
}
static unsigned int ggenie_read_word(unsigned int address)
{
return ggenie.regs[(address >> 1) & 0x1f];
}
static void ggenie_write_byte(unsigned int address, unsigned int data)
{
/* Register offset */
uint8 offset = (address >> 1) & 0x1f;
/* /LWR and /UWR are used to decode writes */
if (address & 1)
{
data = (ggenie.regs[offset] & 0xff00) | (data & 0xff);
}
else
{
data = (ggenie.regs[offset] & 0x00ff) | ((data & 0xff) << 8);
}
/* Update internal register */
ggenie_write_regs(offset,data);
}
static void ggenie_write_word(unsigned int address, unsigned int data)
{
/* Register offset */
uint8 offset = (address >> 1) & 0x1f;
/* Write internal register (full WORD) */
ggenie_write_regs(offset,data);
}
static void ggenie_write_regs(unsigned int offset, unsigned int data)
{
/* update internal register */
ggenie.regs[offset] = data;
/* Mode Register */
if (offset == 0)
{
/* MODE bit */
if (data & 0x400)
{
/* $0000-$7ffff reads mapped to Cartridge ROM */
m68k.memory_map[0].base = cart.rom;
m68k.memory_map[0].read8 = NULL;
m68k.memory_map[0].read16 = NULL;
}
else
{
/* $0000-$7ffff reads mapped to Game Genie ROM */
m68k.memory_map[0].base = ggenie.rom;
m68k.memory_map[0].read8 = NULL;
m68k.memory_map[0].read16 = NULL;
/* READ_ENABLE bit */
if (data & 0x200)
{
/* $0000-$7ffff reads mapped to Game Genie Registers */
/* code doing this should execute in RAM so we don't need to modify base address */
m68k.memory_map[0].read8 = ggenie_read_byte;
m68k.memory_map[0].read16 = ggenie_read_word;
}
}
/* LOCK bit */
if (data & 0x100)
{
/* decode patch address (ROM area only)*/
/* note: Charles's doc is wrong, first register holds bits 23-16 of patch address */
ggenie.addr[0] = ((ggenie.regs[2] & 0x3f) << 16) | ggenie.regs[3];
ggenie.addr[1] = ((ggenie.regs[5] & 0x3f) << 16) | ggenie.regs[6];
ggenie.addr[2] = ((ggenie.regs[8] & 0x3f) << 16) | ggenie.regs[9];
ggenie.addr[3] = ((ggenie.regs[11] & 0x3f) << 16) | ggenie.regs[12];
ggenie.addr[4] = ((ggenie.regs[14] & 0x3f) << 16) | ggenie.regs[15];
ggenie.addr[5] = ((ggenie.regs[17] & 0x3f) << 16) | ggenie.regs[18];
/* decode patch data */
ggenie.data[0] = ggenie.regs[4];
ggenie.data[1] = ggenie.regs[7];
ggenie.data[2] = ggenie.regs[10];
ggenie.data[3] = ggenie.regs[13];
ggenie.data[4] = ggenie.regs[16];
ggenie.data[5] = ggenie.regs[19];
/* disable internal registers */
m68k.memory_map[0].write8 = m68k_unused_8_w;
m68k.memory_map[0].write16 = m68k_unused_16_w;
/* patch ROM when GG program exits (LOCK bit set) */
/* this is done here to handle patched program reads faster & more easily */
/* on real HW, address decoding would be done on each reads */
ggenie_switch(1);
}
else
{
m68k.memory_map[0].write8 = ggenie_write_byte;
m68k.memory_map[0].write16 = ggenie_write_word;
}
}
/* RESET register */
else if (offset == 1)
{
ggenie.regs[1] |= 1;
}
}

View File

@ -0,0 +1,51 @@
/****************************************************************************
* Genesis Plus
* Game Genie Hardware emulation
*
* Copyright (C) 2009-2011 Eke-Eke (Genesis Plus GX)
*
* Based on documentation from Charles McDonald
* (http://cgfm2.emuviews.com/txt/genie.txt)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _GGENIE_H_
#define _GGENIE_H_
/* Function prototypes */
extern void ggenie_init(void);
extern void ggenie_shutdown(void);
extern void ggenie_reset(int hard);
extern void ggenie_switch(int enable);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,90 @@
/****************************************************************************
* Genesis Plus
* Mega Drive cartridge hardware support
*
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
*
* Most cartridge protections were initially documented by Haze
* (http://haze.mameworld.info/)
*
* Realtec mapper was documented by TascoDeluxe
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _MD_CART_H_
#define _MD_CART_H_
#define cart ext.md_cart
/* Lock-On cartridge type */
#define TYPE_GG 0x01 /* Game Genie */
#define TYPE_AR 0x02 /* (Pro) Action Replay */
#define TYPE_SK 0x03 /* Sonic & Knuckles */
/* Special hardware (0x01 & 0x02 reserved for Master System 3-D glasses & Terebi Oekaki) */
#define HW_J_CART 0x04
#define HW_LOCK_ON 0x08
/* Cartridge extra hardware */
typedef struct
{
uint8 regs[4]; /* internal registers (R/W) */
uint32 mask[4]; /* registers address mask */
uint32 addr[4]; /* registers address */
uint16 realtec; /* realtec mapper */
uint16 bankshift; /* cartridge with bankshift mecanism reseted on software reset */
unsigned int (*time_r)(unsigned int address); /* !TIME signal ($a130xx) read handler */
void (*time_w)(unsigned int address, unsigned int data); /* !TIME signal ($a130xx) write handler */
unsigned int (*regs_r)(unsigned int address); /* cart hardware registers read handler */
void (*regs_w)(unsigned int address, unsigned int data); /* cart hardware registers write handler */
} cart_hw_t;
/* Cartridge type */
typedef struct
{
uint8 rom[MAXROMSIZE]; /* ROM area */
uint8 *base; /* ROM base (saved for OS/Cartridge ROM swap) */
uint32 romsize; /* ROM size */
uint32 mask; /* ROM mask */
uint8 special; /* Lock-On, J-Cart or SMS 3-D glasses hardware */
cart_hw_t hw; /* Extra mapping hardware */
} md_cart_t;
/* Function prototypes */
extern void md_cart_init(void);
extern void md_cart_reset(int hard_reset);
extern int md_cart_context_save(uint8 *state);
extern int md_cart_context_load(uint8 *state);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,56 @@
/****************************************************************************
* Genesis Plus
* SG-1000, Master System & Game Gear cartridge hardware support
*
* Copyright (C) 2007-2012 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _SMS_CART_H_
#define _SMS_CART_H_
/* Special hardware */
#define HW_3D_GLASSES 0x01
#define HW_TEREBI_OEKAKI 0x02
/* Function prototypes */
extern void sms_cart_init(void);
extern void sms_cart_reset(void);
extern void sms_cart_switch(uint8 mode);
extern int sms_cart_region_detect(void);
extern int sms_cart_context_save(uint8 *state);
extern int sms_cart_context_load(uint8 *state);
#endif

View File

@ -0,0 +1,297 @@
/***************************************************************************************
* Genesis Plus
* Backup RAM support
*
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
#include "eeprom_i2c.h"
#include "eeprom_spi.h"
#include "eeprom_93c.h"
T_SRAM sram;
/****************************************************************************
* A quick guide to external RAM on the Genesis
*
* The external RAM definition is held at offset 0x1b0 of the ROM header.
*
* 1B0h: dc.b 'RA', %1x1yz000, %abc00000
* 1B4h: dc.l RAM start address
* 1B8h: dc.l RAM end address
* x 1 for BACKUP (not volatile), 0 for volatile RAM
* yz 10 if even address only
* 11 if odd address only
* 00 if both even and odd address
* 01 others (serial EEPROM, RAM with 4-bit data bus, etc)
* abc 001 if SRAM
* 010 if EEPROM (serial or parallel)
* other values unused
*
* Assuming max. 64k backup RAM throughout
****************************************************************************/
void sram_init()
{
memset(&sram, 0, sizeof (T_SRAM));
/* backup RAM data is stored above cartridge ROM area, at $800000-$80FFFF (max. 64K) */
if (cart.romsize > 0x800000) return;
sram.sram = cart.rom + 0x800000;
/* initialize Backup RAM */
memset(sram.sram, 0xFF, 0x10000);
//sram.crc = crc32(0, sram.sram, 0x10000);
/* retrieve informations from header */
if ((READ_BYTE(cart.rom,0x1b0) == 0x52) && (READ_BYTE(cart.rom,0x1b1) == 0x41))
{
/* backup RAM detected */
sram.detected = 1;
/* enable backup RAM */
sram.on = 1;
/* retrieve backup RAM start & end addresses */
sram.start = READ_WORD_LONG(cart.rom, 0x1b4);
sram.end = READ_WORD_LONG(cart.rom, 0x1b8);
/* autodetect games with wrong header infos */
if (strstr(rominfo.product,"T-26013") != NULL)
{
/* Psy-O-Blade (wrong header) */
sram.start = 0x200001;
sram.end = 0x203fff;
}
/* fixe games indicating internal RAM as volatile external RAM (Feng Kuang Tao Hua Yuan) */
else if (sram.start == 0xff0000)
{
/* backup RAM should be disabled */
sram.on = 0;
}
/* fixe other bad header informations */
else if ((sram.start > sram.end) || ((sram.end - sram.start) >= 0x10000))
{
sram.end = sram.start + 0xffff;
}
}
else
{
/* autodetect games with missing header infos */
if (strstr(rominfo.product,"T-50086") != NULL)
{
/* PGA Tour Golf */
sram.on = 1;
sram.start = 0x200001;
sram.end = 0x203fff;
}
else if (strstr(rominfo.product,"ACLD007") != NULL)
{
/* Winter Challenge */
sram.on = 1;
sram.start = 0x200001;
sram.end = 0x200fff;
}
else if (strstr(rominfo.product,"T-50286") != NULL)
{
/* Buck Rogers - Countdown to Doomsday */
sram.on = 1;
sram.start = 0x200001;
sram.end = 0x203fff;
}
else if (((rominfo.realchecksum == 0xaeaa) || (rominfo.realchecksum == 0x8dba)) &&
(rominfo.checksum == 0x8104))
{
/* Xin Qigai Wangzi (use uncommon area) */
sram.on = 1;
sram.start = 0x400001;
sram.end = 0x40ffff;
}
else if ((strstr(rominfo.ROMType,"SF") != NULL) && (strstr(rominfo.product,"001") != NULL))
{
/* SF-001 */
sram.on = 1;
if (rominfo.checksum == 0x3e08)
{
/* last revision (use bankswitching) */
sram.start = 0x3c0001;
sram.end = 0x3cffff;
}
else
{
/* older revisions (use uncommon area) */
sram.start = 0x400001;
sram.end = 0x40ffff;
}
}
else if ((strstr(rominfo.ROMType,"SF") != NULL) && (strstr(rominfo.product,"004") != NULL))
{
/* SF-004 (use bankswitching) */
sram.on = 1;
sram.start = 0x200001;
sram.end = 0x203fff;
}
else if (strstr(rominfo.international,"SONIC & KNUCKLES") != NULL)
{
/* Sonic 3 & Knuckles combined ROM */
if (cart.romsize == 0x400000)
{
/* Sonic & Knuckle does not have backup RAM but can access FRAM from Sonic 3 cartridge */
sram.on = 1;
sram.start = 0x200001;
sram.end = 0x203fff;
}
}
/* auto-detect games which need disabled backup RAM */
else if (strstr(rominfo.product,"T-113016") != NULL)
{
/* Pugsy (does not have backup RAM but tries writing outside ROM area as copy protection) */
sram.on = 0;
}
else if (strstr(rominfo.international,"SONIC THE HEDGEHOG 2") != NULL)
{
/* Sonic the Hedgehog 2 (does not have backup RAM) */
/* this prevents backup RAM from being mapped in place of mirrored ROM when using S&K LOCK-ON feature */
sram.on = 0;
}
// by default, enable backup RAM for ROM smaller than 2MB
/*
else if (cart.romsize <= 0x200000)
{
// 64KB static RAM mapped to $200000-$20ffff
sram.start = 0x200000;
sram.end = 0x20ffff;
sram.on = 1;
}
*/
}
}
unsigned int sram_read_byte(unsigned int address)
{
return sram.sram[address & 0xffff];
}
unsigned int sram_read_word(unsigned int address)
{
address &= 0xfffe;
return (sram.sram[address + 1] | (sram.sram[address] << 8));
}
void sram_write_byte(unsigned int address, unsigned int data)
{
sram.sram[address & 0xffff] = data;
}
void sram_write_word(unsigned int address, unsigned int data)
{
address &= 0xfffe;
sram.sram[address] = data >> 8;
sram.sram[address + 1] = data & 0xff;
}
// the variables in SRAM_T are all part of "configuration", so we don't have to save those.
// the only thing that needs to be saved is the SRAM itself and the SEEPROM struct (if applicable)
int sram_context_save(uint8 *state)
{
int bufferptr = 0;
if (!sram.on)
return 0;
save_param(sram.sram, sram_get_actual_size());
switch (sram.custom)
{
case 1:
save_param(&eeprom_i2c, sizeof(eeprom_i2c));
break;
case 2:
save_param(&spi_eeprom, sizeof(spi_eeprom));
break;
case 3:
save_param(&eeprom_93c, sizeof(eeprom_93c));
break;
}
return bufferptr;
}
int sram_context_load(uint8 *state)
{
int bufferptr = 0;
if (!sram.on)
return 0;
load_param(sram.sram, sram_get_actual_size());
switch (sram.custom)
{
case 1:
load_param(&eeprom_i2c, sizeof(eeprom_i2c));
break;
case 2:
load_param(&spi_eeprom, sizeof(spi_eeprom));
break;
case 3:
load_param(&eeprom_93c, sizeof(eeprom_93c));
break;
}
return bufferptr;
}
int sram_get_actual_size()
{
if (!sram.on)
return 0;
switch (sram.custom)
{
case 0: // plain bus access saveram
break;
case 1: // i2c
return eeprom_i2c.config.size_mask + 1;
case 2: // spi
return 0x10000; // it doesn't appear to mask anything internally
case 3: // 93c
return 0x10000; // SMS only and i don't have time to look into it
default:
return 0x10000; // who knows
}
// figure size for plain bus access saverams
{
int startaddr = sram.start / 8192;
int endaddr = sram.end / 8192 + 1;
int size = (endaddr - startaddr) * 8192;
return size;
}
}

View File

@ -0,0 +1,67 @@
/***************************************************************************************
* Genesis Plus
* Backup RAM support
*
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _SRAM_H_
#define _SRAM_H_
typedef struct
{
uint8 detected;
uint8 on;
uint8 custom;
uint32 start;
uint32 end;
//uint32 crc;
uint8 *sram;
} T_SRAM;
/* Function prototypes */
extern void sram_init();
extern unsigned int sram_read_byte(unsigned int address);
extern unsigned int sram_read_word(unsigned int address);
extern void sram_write_byte(unsigned int address, unsigned int data);
extern void sram_write_word(unsigned int address, unsigned int data);
extern int sram_context_save(uint8 *state);
extern int sram_context_load(uint8 *state);
extern int sram_get_actual_size();
/* global variables */
extern T_SRAM sram;
#endif

View File

@ -0,0 +1,68 @@
vscroll: 1 (0); 209 (26) - alternates every 4 frames
vram range for patterns: 0000-999f (low scr 0000-395f,72e0-999f; high 3980-999f)
name table address: c000
seen DMAs (in order):
[300002-3026c3]->[0020-26e1] len 4961
[3026c2-303943]->[26e0-3961] len 2369
[303942-306003]->[72e0-99a1] len 4961
---
[306002-3086c3]->[3980-6041] len 4961
[3086c2-309943]->[6040-72c1] len 2369
[309942-30c003]->[72e0-99a2] len 4961
tile arrangement:
000: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
001: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
002: 001 003 005 007 009 00b 00d 00f 011 013 015 017 019 01b 01d 01f 021 023 025 027 029 02b 02d 02f 031 033 035 037 039 03b 03d 03f
003: 002 004 006 008 00a 00c 00e 010 012 014 016 018 01a 01c 01e 020 022 024 026 028 02a 02c 02e 030 032 034 036 038 03a 03c 03e 040
004: 041 043 045 047 049 04b 04d 04f 051 053 055 057 059 05b 05d 05f 061 063 065 067 069 06b 06d 06f 071 073 075 077 079 07b 07d 07f
005: 042 044 046 048 04a 04c 04e 050 052 054 056 058 05a 05c 05e 060 062 064 066 068 06a 06c 06e 070 072 074 076 078 07a 07c 07e 080
006: 081 083 085 087 089 08b 08d 08f 091 093 095 097 099 09b 09d 09f 0a1 0a3 0a5 0a7 0a9 0ab 0ad 0af 0b1 0b3 0b5 0b7 0b9 0bb 0bd 0bf
007: 082 084 086 088 08a 08c 08e 090 092 094 096 098 09a 09c 09e 0a0 0a2 0a4 0a6 0a8 0aa 0ac 0ae 0b0 0b2 0b4 0b6 0b8 0ba 0bc 0be 0c0
008: 0c1 0c3 0c5 0c7 0c9 0cb 0cd 0cf 0d1 0d3 0d5 0d7 0d9 0db 0dd 0df 0e1 0e3 0e5 0e7 0e9 0eb 0ed 0ef 0f1 0f3 0f5 0f7 0f9 0fb 0fd 0ff
009: 0c2 0c4 0c6 0c8 0ca 0cc 0ce 0d0 0d2 0d4 0d6 0d8 0da 0dc 0de 0e0 0e2 0e4 0e6 0e8 0ea 0ec 0ee 0f0 0f2 0f4 0f6 0f8 0fa 0fc 0fe 100
010: 101 103 105 107 109 10b 10d 10f 111 113 115 117 119 11b 11d 11f 121 123 125 127 129 12b 12d 12f 131 133 135 137 139 13b 13d 13f
011: 102 104 106 108 10a 10c 10e 110 112 114 116 118 11a 11c 11e 120 122 124 126 128 12a 12c 12e 130 132 134 136 138 13a 13c 13e 140
012: 141 143 145 147 149 14b 14d 14f 151 153 155 157 159 15b 15d 15f 161 163 165 167 169 16b 16d 16f 171 173 175 177 179 17b 17d 17f
013: 142 144 146 148 14a 14c 14e 150 152 154 156 158 15a 15c 15e 160 162 164 166 168 16a 16c 16e 170 172 174 176 178 17a 17c 17e 180
014: 181 183 185 187 189 18b 18d 18f 191 193 195 197 199 19b 19d 19f 1a1 1a3 1a5 1a7 1a9 1ab 1ad 1af 1b1 1b3 1b5 1b7 1b9 1bb 1bd 1bf
015: 182 184 186 188 18a 18c 18e 190 192 194 196 198 19a 19c 19e 1a0 1a2 1a4 1a6 1a8 1aa 1ac 1ae 1b0 1b2 1b4 1b6 1b8 1ba 1bc 1be 1c0
016: 1c1 1c3 1c5 1c7 1c9 397 399 39b 39d 39f 3a1 3a3 3a5 3a7 3a9 3ab 3ad 3af 3b1 3b3 3b5 3b7 3b9 3bb 3bd 3bf 3c1 3c3 3c5 3c7 3c9 3cb
017: 1c2 1c4 1c6 1c8 1ca 398 39a 39c 39e 3a0 3a2 3a4 3a6 3a8 3aa 3ac 3ae 3b0 3b2 3b4 3b6 3b8 3ba 3bc 3be 3c0 3c2 3c4 3c6 3c8 3ca 3cc
018: 3cd 3cf 3d1 3d3 3d5 3d7 3d9 3db 3dd 3df 3e1 3e3 3e5 3e7 3e9 3eb 3ed 3ef 3f1 3f3 3f5 3f7 3f9 3fb 3fd 3ff 401 403 405 407 409 40b
019: 3ce 3d0 3d2 3d4 3d6 3d8 3da 3dc 3de 3e0 3e2 3e4 3e6 3e8 3ea 3ec 3ee 3f0 3f2 3f4 3f6 3f8 3fa 3fc 3fe 400 402 404 406 408 40a 40c
020: 40d 40f 411 413 415 417 419 41b 41d 41f 421 423 425 427 429 42b 42d 42f 431 433 435 437 439 43b 43d 43f 441 443 445 447 449 44b
021: 40e 410 412 414 416 418 41a 41c 41e 420 422 424 426 428 42a 42c 42e 430 432 434 436 438 43a 43c 43e 440 442 444 446 448 44a 44c
022: 44d 44f 451 453 455 457 459 45b 45d 45f 461 463 465 467 469 46b 46d 46f 471 473 475 477 479 47b 47d 47f 481 483 485 487 489 48b
023: 44e 450 452 454 456 458 45a 45c 45e 460 462 464 466 468 46a 46c 46e 470 472 474 476 478 47a 47c 47e 480 482 484 486 488 48a 48c
024: 48d 48f 491 493 495 497 499 49b 49d 49f 4a1 4a3 4a5 4a7 4a9 4ab 4ad 4af 4b1 4b3 4b5 4b7 4b9 4bb 4bd 4bf 4c1 4c3 4c5 4c7 4c9 4cb
025: 48e 490 492 494 496 498 49a 49c 49e 4a0 4a2 4a4 4a6 4a8 4aa 4ac 4ae 4b0 4b2 4b4 4b6 4b8 4ba 4bc 4be 4c0 4c2 4c4 4c6 4c8 4ca 4cc
026: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
027: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
028: 1cc 1ce 1d0 1d2 1d4 1d6 1d8 1da 1dc 1de 1e0 1e2 1e4 1e6 1e8 1ea 1ec 1ee 1f0 1f2 1f4 1f6 1f8 1fa 1fc 1fe 200 202 204 206 208 20a
029: 1cd 1cf 1d1 1d3 1d5 1d7 1d9 1db 1dd 1df 1e1 1e3 1e5 1e7 1e9 1eb 1ed 1ef 1f1 1f3 1f5 1f7 1f9 1fb 1fd 1ff 201 203 205 207 209 20b
030: 20c 20e 210 212 214 216 218 21a 21c 21e 220 222 224 226 228 22a 22c 22e 230 232 234 236 238 23a 23c 23e 240 242 244 246 248 24a
031: 20d 20f 211 213 215 217 219 21b 21d 21f 221 223 225 227 229 22b 22d 22f 231 233 235 237 239 23b 23d 23f 241 243 245 247 249 24b
032: 24c 24e 250 252 254 256 258 25a 25c 25e 260 262 264 266 268 26a 26c 26e 270 272 274 276 278 27a 27c 27e 280 282 284 286 288 28a
033: 24d 24f 251 253 255 257 259 25b 25d 25f 261 263 265 267 269 26b 26d 26f 271 273 275 277 279 27b 27d 27f 281 283 285 287 289 28b
034: 28c 28e 290 292 294 296 298 29a 29c 29e 2a0 2a2 2a4 2a6 2a8 2aa 2ac 2ae 2b0 2b2 2b4 2b6 2b8 2ba 2bc 2be 2c0 2c2 2c4 2c6 2c8 2ca
035: 28d 28f 291 293 295 297 299 29b 29d 29f 2a1 2a3 2a5 2a7 2a9 2ab 2ad 2af 2b1 2b3 2b5 2b7 2b9 2bb 2bd 2bf 2c1 2c3 2c5 2c7 2c9 2cb
036: 2cc 2ce 2d0 2d2 2d4 2d6 2d8 2da 2dc 2de 2e0 2e2 2e4 2e6 2e8 2ea 2ec 2ee 2f0 2f2 2f4 2f6 2f8 2fa 2fc 2fe 300 302 304 306 308 30a
037: 2cd 2cf 2d1 2d3 2d5 2d7 2d9 2db 2dd 2df 2e1 2e3 2e5 2e7 2e9 2eb 2ed 2ef 2f1 2f3 2f5 2f7 2f9 2fb 2fd 2ff 301 303 305 307 309 30b
038: 30c 30e 310 312 314 316 318 31a 31c 31e 320 322 324 326 328 32a 32c 32e 330 332 334 336 338 33a 33c 33e 340 342 344 346 348 34a
039: 30d 30f 311 313 315 317 319 31b 31d 31f 321 323 325 327 329 32b 32d 32f 331 333 335 337 339 33b 33d 33f 341 343 345 347 349 34b
040: 34c 34e 350 352 354 356 358 35a 35c 35e 360 362 364 366 368 36a 36c 36e 370 372 374 376 378 37a 37c 37e 380 382 384 386 388 38a
041: 34d 34f 351 353 355 357 359 35b 35d 35f 361 363 365 367 369 36b 36d 36f 371 373 375 377 379 37b 37d 37f 381 383 385 387 389 38b
042: 38c 38e 390 392 394 397 399 39b 39d 39f 3a1 3a3 3a5 3a7 3a9 3ab 3ad 3af 3b1 3b3 3b5 3b7 3b9 3bb 3bd 3bf 3c1 3c3 3c5 3c7 3c9 3cb
043: 38d 38f 391 393 395 398 39a 39c 39e 3a0 3a2 3a4 3a6 3a8 3aa 3ac 3ae 3b0 3b2 3b4 3b6 3b8 3ba 3bc 3be 3c0 3c2 3c4 3c6 3c8 3ca 3cc
044: 3cd 3cf 3d1 3d3 3d5 3d7 3d9 3db 3dd 3df 3e1 3e3 3e5 3e7 3e9 3eb 3ed 3ef 3f1 3f3 3f5 3f7 3f9 3fb 3fd 3ff 401 403 405 407 409 40b
045: 3ce 3d0 3d2 3d4 3d6 3d8 3da 3dc 3de 3e0 3e2 3e4 3e6 3e8 3ea 3ec 3ee 3f0 3f2 3f4 3f6 3f8 3fa 3fc 3fe 400 402 404 406 408 40a 40c
046: 40d 40f 411 413 415 417 419 41b 41d 41f 421 423 425 427 429 42b 42d 42f 431 433 435 437 439 43b 43d 43f 441 443 445 447 449 44b
047: 40e 410 412 414 416 418 41a 41c 41e 420 422 424 426 428 42a 42c 42e 430 432 434 436 438 43a 43c 43e 440 442 444 446 448 44a 44c
048: 44d 44f 451 453 455 457 459 45b 45d 45f 461 463 465 467 469 46b 46d 46f 471 473 475 477 479 47b 47d 47f 481 483 485 487 489 48b
049: 44e 450 452 454 456 458 45a 45c 45e 460 462 464 466 468 46a 46c 46e 470 472 474 476 478 47a 47c 47e 480 482 484 486 488 48a 48c
050: 48d 48f 491 493 495 497 499 49b 49d 49f 4a1 4a3 4a5 4a7 4a9 4ab 4ad 4af 4b1 4b3 4b5 4b7 4b9 4bb 4bd 4bf 4c1 4c3 4c5 4c7 4c9 4cb
051: 48e 490 492 494 496 498 49a 49c 49e 4a0 4a2 4a4 4a6 4a8 4aa 4ac 4ae 4b0 4b2 4b4 4b6 4b8 4ba 4bc 4be 4c0 4c2 4c4 4c6 4c8 4ca 4cc
052: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
053: 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,79 @@
/*
basic, incomplete SSP160x (SSP1601?) interpreter
with SVP memory controller emu
(c) Copyright 2008, Grazvydas "notaz" Ignotas
Free for non-commercial use.
For commercial use, separate licencing terms must be obtained.
Modified for Genesis Plus GX (Eke-Eke): added BIG ENDIAN support, fixed addr/code inversion
*/
#ifndef _SSP16_H_
#define _SSP16_H_
/* emulation event logging (from Picodrive) */
#ifdef LOG_SVP
#define EL_SVP 0x00004000 /* SVP stuff */
#define EL_ANOMALY 0x80000000 /* some unexpected conditions (during emulation) */
#define elprintf(w,f,...) error("%d(%d): " f "\n",frame_count,v_counter,##__VA_ARGS__);
#endif
/* register names */
enum {
SSP_GR0, SSP_X, SSP_Y, SSP_A,
SSP_ST, SSP_STACK, SSP_PC, SSP_P,
SSP_PM0, SSP_PM1, SSP_PM2, SSP_XST,
SSP_PM4, SSP_gr13, SSP_PMC, SSP_AL
};
typedef union
{
unsigned int v;
struct {
#ifdef LSB_FIRST
unsigned short l;
unsigned short h;
#else
unsigned short h;
unsigned short l;
#endif
} byte;
} ssp_reg_t;
typedef struct
{
union {
unsigned short RAM[256*2]; /* 2 internal RAM banks */
struct {
unsigned short RAM0[256];
unsigned short RAM1[256];
} bank;
} mem;
ssp_reg_t gr[16]; /* general registers */
union {
unsigned char r[8]; /* BANK pointers */
struct {
unsigned char r0[4];
unsigned char r1[4];
} bank;
} ptr;
unsigned short stack[6];
unsigned int pmac[2][6]; /* read/write modes/addrs for PM0-PM5 */
#define SSP_PMC_HAVE_ADDR 0x0001 /* address written to PMAC, waiting for mode */
#define SSP_PMC_SET 0x0002 /* PMAC is set */
#define SSP_HANG 0x1000 /* 68000 hangs SVP */
#define SSP_WAIT_PM0 0x2000 /* bit1 in PM0 */
#define SSP_WAIT_30FE06 0x4000 /* ssp tight loops on 30FE08 to become non-zero */
#define SSP_WAIT_30FE08 0x8000 /* same for 30FE06 */
#define SSP_WAIT_MASK 0xf000
unsigned int emu_status;
unsigned int pad[30];
} ssp1601_t;
void ssp1601_reset(ssp1601_t *ssp);
void ssp1601_run(int cycles);
#endif

View File

@ -0,0 +1,49 @@
/*
basic, incomplete SSP160x (SSP1601?) interpreter
with SVP memory controller emu
(c) Copyright 2008, Grazvydas "notaz" Ignotas
Free for non-commercial use.
For commercial use, separate licencing terms must be obtained.
Modified for Genesis Plus GX (Eke-Eke): added BIG ENDIAN support, fixed addr/code inversion
*/
#include "shared.h"
svp_t *svp = NULL;
void svp_init(void)
{
svp = (void *) ((char *)cart.rom + 0x200000);
memset(svp, 0, sizeof(*svp));
}
void svp_reset(void)
{
memcpy(svp->iram_rom + 0x800, cart.rom + 0x800, 0x20000 - 0x800);
ssp1601_reset(&svp->ssp1601);
}
void svp_write_dram(uint32 address, uint32 data)
{
*(uint16 *)(svp->dram + (address & 0x1fffe)) = data;
if ((address == 0x30fe06) && data) svp->ssp1601.emu_status &= ~SSP_WAIT_30FE06;
if ((address == 0x30fe08) && data) svp->ssp1601.emu_status &= ~SSP_WAIT_30FE08;
}
uint32 svp_read_cell_1(uint32 address)
{
address >>= 1;
address = (address & 0x7001) | ((address & 0x3e) << 6) | ((address & 0xfc0) >> 5);
return *(uint16 *)(svp->dram + (address & 0x1fffe));
}
uint32 svp_read_cell_2(uint32 address)
{
address >>= 1;
address = (address & 0x7801) | ((address & 0x1e) << 6) | ((address & 0x7e0) >> 4);
return *(uint16 *)(svp->dram + (address & 0x1fffe));
}

View File

@ -0,0 +1,33 @@
/*
basic, incomplete SSP160x (SSP1601?) interpreter
with SVP memory controller emu
(c) Copyright 2008, Grazvydas "notaz" Ignotas
Free for non-commercial use.
For commercial use, separate licencing terms must be obtained.
Modified for Genesis Plus GX (Eke-Eke): added BIG ENDIAN support, fixed addr/code inversion
*/
#ifndef _SVP_H_
#define _SVP_H_
#include "shared.h"
#include "ssp16.h"
typedef struct {
unsigned char iram_rom[0x20000]; /* IRAM (0-0x7ff) and program ROM (0x800-0x1ffff) */
unsigned char dram[0x20000];
ssp1601_t ssp1601;
} svp_t;
extern svp_t *svp;
extern void svp_init(void);
extern void svp_reset(void);
extern void svp_write_dram(uint32 address, uint32 data);
extern uint32 svp_read_cell_1(uint32 address);
extern uint32 svp_read_cell_2(uint32 address);
#endif

View File

@ -0,0 +1,524 @@
-------------------------------------------------------------------------------
notaz's SVP doc
$Id: svpdoc.txt 349 2008-02-04 23:13:59Z notaz $
Copyright 2008, Grazvydas Ignotas (notaz)
-------------------------------------------------------------------------------
If you use this, please credit me in your work or it's documentation.
Tasco Deluxe should also be credited for his pioneering work on the subject.
Thanks.
Use monospace font and disable word wrap when reading this document.
-------------------------------------------------------------------------------
Table of Contents
-------------------------------------------------------------------------------
0. Introduction
1. Overview
2. The SSP160x DSP
2.1. General registers
2.2. External registers
2.3. Pointer registers
2.4. The instruction set
3. Memory map
4. Other notes
-------------------------------------------------------------------------------
0. Introduction
-------------------------------------------------------------------------------
This document is an attempt to provide technical information needed to
emulate Sega's SVP chip. It is based on reverse engineering Virtua Racing
game and on various internet sources. None of information provided here
was verified on the real hardware, so some things are likely to be
inaccurate.
The following information sources were used while writing this document
and emulator implementation:
[1] SVP Reference Guide (annotated) and SVP Register Guide (annotated)
by Tasco Deluxe < tasco.deluxe @ gmail.com >
http://www.sharemation.com/TascoDLX/SVP%20Reference%20Guide%202007.02.11.txt
http://www.sharemation.com/TascoDLX/SVP%20Register%20Guide%202007.02.11.txt
[2] SSP1610 disassembler
written by Pierpaolo Prazzoli, MAME source code.
http://mamedev.org/
[3] SSP1601 DSP datasheet
http://notaz.gp2x.de/docs/SSP1601.pdf
[4] DSP page (with code samples) in Samsung Semiconductor website from 1997
retrieved from Internet Archive: The Wayback Machine
http://web.archive.org/web/19970607052826/www.sec.samsung.com/Products/dsp/dspcore.htm
[5] Sega's SVP Chip: The Road not Taken?
Ken Horowitz, Sega-16
http://sega-16.com/feature_page.php?id=37&title=Sega's%20SVP%20Chip:%20The%20Road%20not%20Taken?
-------------------------------------------------------------------------------
1. Overview
-------------------------------------------------------------------------------
The only game released with SVP chip was Virtua Racing. There are at least 4
versions of the game: USA, Jap and 2 different Eur revisions. Three of them
share identical SSP160x code, one of the Eur revisions has some differences.
From the software developer's point of view, the game cartridge contains
at least:
* Samsung SSP160x 16-bit DSP core, which includes [3]:
* Two independent high-speed RAM banks, accessed in single clock cycle,
256 words each.
* 16 x 16 bit multiply unit.
* 32-bit ALU, status register.
* Hardware stack of 6 levels.
* 128KB of DRAM.
* 2KB of IRAM (instruction RAM).
* Memory controller with address mapping capability.
* 2MB of game ROM.
[5] claims there is also "2 Channels PWM" in the cartridge, but it's either
not used or not there at all.
Various sources claim that SSP160x is SSP1601 which is likely to be true,
because the code doesn't seem to use any SSP1605+ features.
-------------------------------------------------------------------------------
2. The SSP160x DSP
-------------------------------------------------------------------------------
SSP160x is 16-bit DSP, capable of performing multiplication + addition in
single clock cycle [3]. It has 8 general, 8 external and 8 pointer registers.
There is a status register which has operation control bits and condition
flags. Condition flags are set/cleared during ALU (arithmetic, logic)
operations. It also has 6-level hardware stack and 2 internal RAM banks
RAM0 and RAM1, 256 words each.
The device is only capable of addressing 16-bit words, so all addresses refer
to words (16bit value in ROM, accessed by 68k through address 0x84 would be
accessed by SSP160x using address 0x42).
[3] mentions interrupt pins, but interrupts don't seem to be used by SVP code
(actually there are functions which look like interrupt handler routines, but
they don't seem to do anything important).
2.1. General registers
----------------------
There are 8 general registers: -, X, Y, A, ST, STACK, PC and P ([2] [4]).
Size is given in bits.
2.1.1. "-"
Constant register with all bits set (0xffff). Also used for programming
external registers (blind reads/writes, see 2.2).
size: 16
2.1.2. "X"
Generic register. Also acts as a multiplier 1 for P register.
size: 16
2.1.3. "Y"
Generic register. Also acts as a multiplier 2 for P register.
size: 16
2.1.4. "A"
Accumulator. Stores the result of all ALU (but not multiply) operations,
status register is updated according to this. When directly accessed,
only upper word is read/written. Low word can be accessed by using AL
(see 2.2.8).
size: 32
2.1.5. "ST"
STatus register. Bits 0-9 are CONTROL, other are FLAG [2]. Only some of
them are actually used by SVP.
Bits: fedc ba98 7654 3210
210 - RPL "Loop size". If non-zero, makes (rX+) and (rX-) respectively
modulo-increment and modulo-decrement (see 2.3). The value
shows which power of 2 to use, i.e. 4 means modulo by 16.
43 - RB Unknown. Not used by SVP code.
5 - ST5 Affects behavior of external registers. See 2.2.
6 - ST6 Affects behavior of external registers. See 2.2.
According to [3] (5,6) bits correspond to hardware pins.
7 - IE Interrupt enable? Not used by SVP code.
8 - OP Saturated value? Not used by SVP code.
9 - MACS MAC shift? Not used by SVP code.
a - GPI_0 Interrupt 0 enable/status? Not used by SVP code.
b - GPI_1 Interrupt 1 enable/status? Not used by SVP code.
c - L L flag. Similar to carry? Not used by SVP code.
d - Z Zero flag. Set after ALU operations, when all 32 accumulator
bits become zero.
e - OV Overflow flag. Not used by SVP code.
f - N Negative flag. Set after ALU operations, when bit31 in
accumulator is 1.
size: 16
2.1.6. "STACK"
Hardware stack of 6 levels [3]. Values are "pushed" by directly writing to
it, or by "call" instruction. "Pop" is performed by directly reading the
register or by "ret" instruction.
size: 16
2.1.7. "PC"
Program Counter. Can be written directly to perform a jump. It is not clear
if it is possible to read it (SVP code never does).
size: 16
2.1.8. "P"
multiply Product - multiplication result register.
Always contains 32-bit multiplication result of X, Y and 2 (P = X * Y * 2).
X and Y are sign-extended before performing the multiplication.
size: 32
2.2. External registers
-----------------------
The external registers, as the name says, are external to SSP160x, they are
hooked to memory controller in SVP, so by accessing them we actually program
the memory controller. They act as programmable memory access registers or
external status registers [1]. Some of them can act as both, depending on how
ST5 ans ST6 bits are set in status register. After a register is programmed,
accessing it causes reads/writes from/to external memory (see section 3 for
the memory map). The access may also cause some additional effects, like
incremental of address, associated with accessed register.
In this document and my emu, instead of using names EXT0-EXT7
from [4] I used different names for these registers. Those names are from
Tasco Deluxe's [1] doc.
All these registers can be blind-accessed (as said in [1]) by performing
(ld -, PMx) or (ld PMx, -). This programs them to access memory (except PMC,
where the effect is different).
All registers are 16-bit.
2.2.1. "PM0"
If ST5 or ST6 is set, acts as Programmable Memory access register
(see 2.2.7). Else it acts as status of XST (2.2.4). It is also mapped
to a15004 on 68k side:
???????? ??????10
0: set, when SSP160x has written something to XST
(cleared when 015004 is read by 68k)
1: set, when 68k has written something to a15000 or a15002
(cleared on PM0 read by SSP160x)
Note that this is likely to be incorrect, but such behavior is OK for
emulation to work.
2.2.2. "PM1"
Programmable Memory access register. Only accessed with ST bits set by
SVP code.
2.2.3. "PM2"
Same as PM1.
2.2.4. "XST"
If ST5 or ST6 is set, acts as Programmable Memory access register
(only used by memory test code). Else it acts as eXternal STatus
register, which is also mapped to a15000 and a15002 on 68k side.
Affects PM0 when written to.
2.2.5. "PM4"
Programmable Memory access register. Not affected by ST5 and ST6 bits,
always stays in PMAR mode.
2.2.6. "EXT5"
Not used by SVP, so not covered by this document.
2.2.7. "PMC"
Programmable Memory access Control. It is set using 2 16bit writes, first
address, then mode word. After setting PMAC, PMx should be blind accessed
using (ld -, PMx) or (ld PMx, -) to program it for reading or writing
external memory respectively. Every PMx register can be programmed to
access it's own memory location with it's own mode. Registers are programmed
separately for reading and writing.
Reading PMC register also shifts it's state (from "waiting for address" to
"waiting for mode" and back). Reads always return address word related to
last PMx register accessed, or last address word written to PMC (whichever
event happened last before PMC read).
The address word contains bits 0-15 of the memory word-address.
The mode word format is as follows:
dsnnnv?? ???aaaaa
a: bits 16-20 of memory word-address.
n: auto-increment value. If set, after every access of PMx, word-address
value related to it will be incremented by (words):
1 - 1 5 - 16
2 - 2 6 - 32
3 - 4 7 - 128
4 - 8
d: make auto-increment negative - decrement by count listed above.
s: special-increment mode. If current address is even (when accessing
programmed PMx), increment it by 1. Else, increment by 32. It is not
clear what happens if d and n bits are also set (never done by SVP).
v: over-write mode when writing, unknown when reading (not used).
Over-write mode splits the word being written into 4 half-bytes and
only writes those half-bytes, which are not zero.
When auto-increment is performed, it affects all 21 address bits.
2.2.8. "AL"
This register acts more like a general register.
If this register is blind-accessed, it is "dummy programmed", i.e. nothing
happens and PMC is reset to "waiting for address" state.
In all other cases, it is Accumulator Low, 16 least significant bits of
accumulator. Normally reading acc (ld X, A) you get 16 most significant
bits, so this allows you access the low word of 32bit accumulator.
2.3. Pointer registers
----------------------
There are 8 8-bit pointer registers rX, which are internal to SSP160x and are
used to access internal RAM banks RAM0 and RAM1, or program memory indirectly.
r0-r3 (ri) point to RAM0, r4-r7 (rj) point to RAM1. Each bank has 256 words of
RAM, so 8bit registers can fully address them. The registers can be accessed
directly, or 2 indirection levels can be used [ (rX), ((rX)) ]. They work
similar to * and ** operators in C, only they use different types of memory
and ((rX)) also performs post-increment. First indirection level (rX) accesses
a word in RAMx, second accesses program memory at address read from (rX), and
increments value in (rX).
Only r0,r1,r2,r4,r5,r6 can be directly modified (ldi r0, 5), or by using
modifiers. 3 modifiers can be applied when using first indirection level
(optional):
+ : post-increment (ld a, (r0+) ). Increment register value after operation.
Can be made modulo-increment by setting RPL bits in status register
(see 2.1.5).
- : post-decrement. Also can be made modulo-decrement by using RPL bits in ST.
+!: post-increment, unaffected by RPL (probably).
These are only used on 1st indirection level, so things like ( ld a, ((r0+)) )
and (ld X, r6-) are probably invalid.
r3 and r7 are special and can not be changed (at least Samsung samples [4] and
SVP code never do). They are fixed to the start of their RAM banks. (They are
probably changeable for ssp1605+, Samsung's old DSP page claims that).
1 of these 4 modifiers must be used on these registers (short form direct
addressing? [2]):
|00: RAMx[0] The very first word in the RAM bank.
|01: RAMx[1] Second word
|10: RAMx[2] ...
|11: RAMx[3]
2.4. The instruction set
------------------------
The Samsung SSP16 series assembler uses right-to-left notation ([2] [4]):
ld X, Y
means value from Y should be copied to X.
Size of every instruction is word, some have extension words for immediate
values. When writing an interpreter, 7 most significant bits are usually
enough to determine which opcode it is.
encoding bits are marked as:
rrrr - general or external register, in order specified in 2.1 and 2.2
(0 is '-', 1 'X', ..., 8 is 'PM0', ..., 0xf is 'AL')
dddd - same as above, as destination operand
ssss - same as above, as source operand
jpp - pointer register index, 0-7
j - specifies RAM bank, i.e. RAM0 or RAM1
i* - immediate value bits
a* - offset in internal RAM bank
mm - modifier for pointer register, depending on register:
r0-r2,r4-r6 r3,r7 examples
0: (none) |00 ld a, (r0) cmp a, (r7|00)
1: +! |01 ld (r0+!), a ld (r7|01), a
2: - |10 add a, (r0-)
3: + |11
cccc - encodes condition, only 3 used by SVP, see check_cond() below
ooo - operation to perform
Operation is written in C-style pseudo-code, where:
program_memory[X] - access program memory at address X
RAMj[X] - access internal RAM bank j=0,1 (RAM0 or RAM1), word
offset X
RIJ[X] - pointer register rX, X=0-7
pr_modif_read(m,X) - read pointer register rX, applying modifier m:
if register is r3 or r7, return value m
else switch on value m:
0: return rX;
1: tmp = rX; rX++; return tmp; // rX+!
2: tmp = rX; modulo_decrement(rX); return tmp; // rX-
3: tmp = rX; modulo_increment(rX); return tmp; // rX+
the modulo value used (if used at all) depends on ST
RPL bits (see 2.1.5)
check_cond(c,f) - checks if a flag matches f bit:
switch (c) {
case 0: return true;
case 5: return (Z == f) ? true : false; // check Z flag
case 7: return (N == f) ? true : false; // check N flag
} // other conditions are possible, but they are not used
update_flags() - update ST flags according to last ALU operation.
sign_extend(X) - sign extend 16bit value X to 32bits.
next_op_address() - address of instruction after current instruction.
2.4.1. ALU instructions
All of these instructions update flags, which are set according to full 32bit
accumulator. The SVP code only checks N and Z flags, so it is not known when
exactly OV and L flags are set. Operations are performed on full A, so
(andi A, 0) would clear all 32 bits of A.
They share the same addressing modes. The exact arithmetic operation is
determined by 3 most significant (ooo) bits:
001 - sub - subtract (OP -=)
011 - cmp - compare (OP -, flags are updated according to result)
100 - add - add (OP +=)
101 - and - binary AND (OP &=)
110 - or - binary OR (OP |=)
111 - eor - exclusive OR (OP ^=)
syntax encoding operation
OP A, s ooo0 0000 0000 rrrr A OP r << 16;
OP A, (ri) ooo0 001j 0000 mmpp A OP RAMj[pr_modif_read(m,jpp)] << 16;
OP A, adr ooo0 011j aaaa aaaa A OP RAMj[a] << 16;
OPi A, imm ooo0 1000 0000 0000 A OP i << 16;
iiii iiii iiii iiii
op A, ((ri)) ooo0 101j 0000 mmpp tmp = pr_modif_read(m,jpp);
A OP program_memory[RAMj[tmp]] << 16;
RAMj[tmp]++;
op A, ri ooo1 001j 0000 00pp A OP RIJ[jpp] << 16;
OPi simm ooo1 1000 iiii iiii A OP i << 16;
There is also "perform operation on accumulator" instruction:
syntax encoding operation
mod cond, op 1001 000f cccc 0ooo if (check_cond(c,f)) switch(o) {
case 2: A >>= 1; break; // arithmetic shift
case 3: A <<= 1; break;
case 6: A = -A; break; // negate A
case 7: A = abs(A); break; // absolute val.
} // other operations are possible, but
// they are not used by SVP.
2.4.2. Load (move) instructions
These instructions never affect flags (even ld A).
If destination is A, and source is 16bit, only upper word is transfered (same
thing happens on opposite). If dest. is A, and source is P, whole 32bit value
is transfered. It is not clear if P can be destination operand (probably not,
no code ever does this).
Writing to STACK pushes a value there, reading pops. It is not known what
happens on overflow/underflow (never happens in SVP code).
ld -, - is used as a nop.
syntax encoding operation
ld d, s 0000 0000 dddd ssss d = s;
ld d, (ri) 0000 001j dddd mmpp d = RAMj[pr_modif_read(m,jpp)];
ld (ri), s 0000 010j ssss mmpp RAMj[pr_modif_read(m,jpp)] = s;
ldi d, imm 0000 1000 dddd 0000 d = i;
iiii iiii iiii iiii
ld d, ((ri)) 0000 101j dddd mmpp tmp = pr_modif_read(m,jpp);
d = program_memory[RAMj[tmp]];
RAMj[tmp]++;
ldi (ri), imm 0000 110l 0000 mmpp RAMj[pr_modif_read(m,jpp)] = i;
iiii iiii iiii iiii
ld adr, a 0000 111j aaaa aaaa RAMj[a] = A;
ld d, ri 0001 001j dddd 00pp d = RIJ[jpp];
ld ri, s 0001 010j ssss 00pp RIJ[jpp] = s;
ldi ri, simm 0001 1jpp iiii iiii RIJ[jpp] = i;
ld d, (a) 0100 1010 dddd 0000 d = program_memory[A[31:16]];
// read a word from program memory. Offset
// is the upper word in A.
2.4.3. Program control instructions
Only 3 instructions: call, ret (alias of ld PC, STACK) and branch. Indirect
jumps can be performed by simply writing to PC.
syntax encoding operation
call cond, addr 0100 100f cccc 0000 if (check_cond(c,f)) {
aaaa aaaa aaaa aaaa STACK = next_op_address(); PC = a;
}
bra cond, addr 0100 110f cccc 0000 if (check_cond(c,f)) PC = a;
aaaa aaaa aaaa aaaa
ret 0000 0000 0110 0101 PC = STACK; // same as ld PC, STACK
2.4.4. Multiply-accumulate instructions
Not sure if (ri) and (rj) really get loaded into X and Y, but multiplication
result surely is loaded into P. There is probably optional 3rd operand (1, 0;
encoded by bit16, default 1), but it's not used by SVP code.
syntax encoding operation
mld (rj), (ri) 1011 0111 nnjj mmii A = 0; update_flags();
X = RAM0[pr_modif_read(m,0ii)];
Y = RAM1[pr_modif_read(m,1jj)];
P = sign_extend(X) * sign_extend(Y) * 2
mpya (rj), (ri) 1001 0111 nnjj mmii A += P; update_flags();
X = RAM0[pr_modif_read(m,0ii)];
Y = RAM1[pr_modif_read(m,1jj)];
P = sign_extend(X) * sign_extend(Y) * 2
mpys (rj), (ri) 0011 0111 nnjj mmii A -= P; update_flags();
X = RAM0[pr_modif_read(m,0ii)];
Y = RAM1[pr_modif_read(m,1jj)];
P = sign_extend(X) * sign_extend(Y) * 2
-------------------------------------------------------------------------------
3. Memory map
-------------------------------------------------------------------------------
The SSp160x can access it's own program memory, and external memory through EXT
registers (see 2.2). Program memory is read-execute-only, the size of this
space is 64K words (this is how much 16bit PC can address):
byte address word address name
0- 7ff 0- 3ff IRAM
800-1ffff 400-ffff ROM
There were reports that SVP has internal ROM, but fortunately they were wrong.
The location 800-1ffff is mapped from the same location in the 2MB game ROM.
The IRAM is read-only (as SSP160x doesn't have any means of writing to it's
program memory), but it can be changed through external memory space, as it's
also mapped there.
The external memory space seems to match the one visible by 68k, with some
differences:
68k space SVP space word address name
0-1fffff 0-1fffff 0- fffff game ROM
300000-31ffff 300000-31ffff 180000-18ffff DRAM
? 390000-3907ff 1c8000-1c83ff IRAM
390000-39ffff ? ? "cell arrange" 1
3a0000-3affff ? ? "cell arrange" 2
a15000-a15009 n/a n/a Status/control registers
The external memory can be read/written by SSP160x (except game ROM, which can
only be read).
"cell arrange" 1 and 2 are similar to the one used in SegaCD, they map
300000-30ffff location to 390000-39ffff and 3a0000-3affff, where linear image
written to 300000 can be read as VDP patterns at 390000. Virtua Racing doesn't
seem to use this feature, it is only used by memory test code.
Here is the list of status/control registers (16bit size):
a15000 - w/r command/result register. Visible as XST for SSP160x (2.2.4).
a15002 - mirror of the above.
a15004 - status of command/result register (see 2.2.1).
a15006 - possibly halts the SVP. Before doing DMA from DRAM, 68k code writes
0xa, and after it's finished, writes 0. This is probably done to
prevent SVP accessing DRAM and avoid bus clashes.
a15008 - possibly causes an interrupt. There is (unused?) code which writes
0, 1, and again 0 in sequence.
-------------------------------------------------------------------------------
4. Other notes
-------------------------------------------------------------------------------
The game has arcade-style memory self-check mode, which can be accessed by
pressing _all_ buttons (including directions) on 3-button controller. There was
probably some loopback plug for this.
SVP seems to have DMA latency issue similar to one in Sega CD, as the code
always sets DMA source address value larger by 2, then intended for copy.
This is even true for DMAs from ROM, as it's probably hooked through SVP's
memory controller.
The entry point for the code seems to be at address 0x800 (word 0x400) in ROM,
but it is not clear where the address is fetched from when the system powers
up. The memory test code also sets up "ld PC, .." opcodes at 0x7f4, 0x7f8 and
0x7fc, which jump to some routines, possibly interrupt handlers. This means
that mentioned addresses might be built-in interrupt vectors.
The SVP code doesn't seem to be timing sensitive, so it can be emulated without
knowing timing of the instructions or even how fast the chip is clocked.
Overclocking doesn't have any effect, underclocking causes slowdowns. Running
10-12M instructions/sec (or possibly less) is sufficient.

View File

@ -0,0 +1,270 @@
/***************************************************************************************
* Genesis Plus
* CD compatible ROM/RAM cartridge support
*
* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
/*--------------------------------------------------------------------------*/
/* backup RAM cartridge (max. 512KB) */
/*--------------------------------------------------------------------------*/
static unsigned int cart_ram_read_byte(unsigned int address)
{
/* LSB only */
if (address & 1)
{
return scd.cartridge.area[(address >> 1) & scd.cartridge.mask];
}
return 0xff;
}
static unsigned int cart_ram_read_word(unsigned int address)
{
return (scd.cartridge.area[(address >> 1) & scd.cartridge.mask] | 0xff00);
}
static void cart_ram_write_byte(unsigned int address, unsigned int data)
{
/* LSB only */
if (address & 1)
{
scd.cartridge.area[(address >> 1) & scd.cartridge.mask] = data;
}
}
static void cart_ram_write_word(unsigned int address, unsigned int data)
{
scd.cartridge.area[(address >> 1) & scd.cartridge.mask] = data & 0xff;
}
/*--------------------------------------------------------------------------*/
/* backup RAM cartridge ID */
/*--------------------------------------------------------------------------*/
static unsigned int cart_id_read_byte(unsigned int address)
{
/* LSB only */
if (address & 1)
{
return scd.cartridge.id;
}
return 0xff;
}
static unsigned int cart_id_read_word(unsigned int address)
{
return (scd.cartridge.id | 0xff00);
}
/*--------------------------------------------------------------------------*/
/* backup RAM cartridge write protection */
/*--------------------------------------------------------------------------*/
static unsigned int cart_prot_read_byte(unsigned int address)
{
/* LSB only */
if (address & 1)
{
return scd.cartridge.prot;
}
return 0xff;
}
static unsigned int cart_prot_read_word(unsigned int address)
{
return (scd.cartridge.prot | 0xff00);
}
static void cart_prot_write_byte(unsigned int address, unsigned int data)
{
/* LSB only */
if (address & 1)
{
int i;
if (data & 1)
{
/* cartridge is write enabled */
for (i=0x60; i<0x70; i++)
{
m68k.memory_map[i].write8 = cart_ram_write_byte;
m68k.memory_map[i].write16 = cart_ram_write_word;
zbank_memory_map[i].write = cart_ram_write_byte;
}
}
else
{
/* cartridge is write protected */
for (i=0x60; i<0x70; i++)
{
m68k.memory_map[i].write8 = m68k_unused_8_w;
m68k.memory_map[i].write16 = m68k_unused_16_w;
zbank_memory_map[i].write = zbank_unused_w;
}
}
scd.cartridge.prot = data;
}
}
static void cart_prot_write_word(unsigned int address, unsigned int data)
{
int i;
if (data & 1)
{
/* cartridge is write enabled */
for (i=0x60; i<0x70; i++)
{
m68k.memory_map[i].write8 = cart_ram_write_byte;
m68k.memory_map[i].write16 = cart_ram_write_word;
zbank_memory_map[i].write = cart_ram_write_byte;
}
}
else
{
/* cartridge is write protected */
for (i=0x60; i<0x70; i++)
{
m68k.memory_map[i].write8 = m68k_unused_8_w;
m68k.memory_map[i].write16 = m68k_unused_16_w;
zbank_memory_map[i].write = zbank_unused_w;
}
}
scd.cartridge.prot = data & 0xff;
}
/*--------------------------------------------------------------------------*/
/* ROM/RAM cartridge initialization */
/*--------------------------------------------------------------------------*/
void cd_cart_init(void)
{
int i;
/* System boot mode */
if (scd.cartridge.boot)
{
/* disable backup RAM cartridge when booting from cartridge (Mode 1) */
scd.cartridge.id = 0;
}
else
{
/* enable 512K backup RAM cartridge when booting from CD (Mode 2) */
//scd.cartridge.id = 6;
//scd.cartridge.id = 4; // use 128K instead, which is the size of a real ebram cart
// bizhawk doesn't need the extra space because it gives each game its own anyway
scd.cartridge.id = 1; // 16K to be size-frugal
}
/* RAM cartridge enabled ? */
if (scd.cartridge.id)
{
/* disable cartridge backup memory */
memset(&sram, 0, sizeof (T_SRAM));
/* clear backup RAM */
memset(scd.cartridge.area, 0x00, sizeof(scd.cartridge.area));
/* backup RAM size mask */
scd.cartridge.mask = (1 << (scd.cartridge.id + 13)) - 1;
/* enable RAM cartridge write access */
scd.cartridge.prot = 1;
/* RAM cartridge ID register (read-only) */
for (i=0x40; i<0x60; i++)
{
m68k.memory_map[i].base = NULL;
m68k.memory_map[i].read8 = cart_id_read_byte;
m68k.memory_map[i].read16 = cart_id_read_word;
m68k.memory_map[i].write8 = m68k_unused_8_w;
m68k.memory_map[i].write16 = m68k_unused_16_w;
zbank_memory_map[i].read = cart_id_read_byte;
zbank_memory_map[i].write = zbank_unused_w;
}
/* RAM cartridge memory */
for (i=0x60; i<0x70; i++)
{
m68k.memory_map[i].base = NULL;
m68k.memory_map[i].read8 = cart_ram_read_byte;
m68k.memory_map[i].read16 = cart_ram_read_word;
m68k.memory_map[i].write8 = cart_ram_write_byte;
m68k.memory_map[i].write16 = cart_ram_write_word;
zbank_memory_map[i].read = cart_ram_read_byte;
zbank_memory_map[i].write = cart_ram_write_byte;
}
/* RAM cartridge write protection register */
for (i=0x70; i<0x80; i++)
{
m68k.memory_map[i].base = NULL;
m68k.memory_map[i].read8 = cart_prot_read_byte;
m68k.memory_map[i].read16 = cart_prot_read_word;
m68k.memory_map[i].write8 = cart_prot_write_byte;
m68k.memory_map[i].write16 = cart_prot_write_word;
zbank_memory_map[i].read = cart_prot_read_byte;
zbank_memory_map[i].write = cart_prot_write_byte;
}
}
else
{
/* initialize ROM cartridge */
md_cart_init();
/* when booting from CD, cartridge is mapped to $400000-$7FFFFF */
if (!scd.cartridge.boot)
{
for (i=0; i<0x40; i++)
{
m68k.memory_map[i+0x40].base = m68k.memory_map[i].base;
m68k.memory_map[i+0x40].read8 = m68k.memory_map[i].read8;
m68k.memory_map[i+0x40].read16 = m68k.memory_map[i].read16;
m68k.memory_map[i+0x40].write8 = m68k.memory_map[i].write8;
m68k.memory_map[i+0x40].write16 = m68k.memory_map[i].write16;
zbank_memory_map[i+0x40].read = zbank_memory_map[i].read;
zbank_memory_map[i+0x40].write = zbank_memory_map[i].write;
}
}
}
}

View File

@ -0,0 +1,51 @@
/***************************************************************************************
* Genesis Plus
* CD compatible ROM/RAM cartridge support
*
* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
/* CD compatible ROM/RAM cartridge */
typedef struct
{
uint8 area[0x810000]; /* cartridge ROM/RAM area (max. 8MB + 64KB backup) */
uint8 boot; /* cartridge boot mode (0x00: boot from CD with ROM/RAM cartridge enabled, 0x40: boot from ROM cartridge with CD enabled) */
uint8 id; /* RAM cartridge ID (related to RAM size, 0 if disabled) */
uint8 prot; /* RAM cartridge write protection */
uint32 mask; /* RAM cartridge size mask */
} cd_cart_t;
/* Function prototypes */
extern void cd_cart_init(void);

View File

@ -0,0 +1,705 @@
/***************************************************************************************
* Genesis Plus
* CD data controller (LC89510 compatible)
*
* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
/* IFSTAT register bitmasks */
#define BIT_DTEI 0x40
#define BIT_DECI 0x20
#define BIT_DTBSY 0x08
#define BIT_DTEN 0x02
/* IFCTRL register bitmasks */
#define BIT_DTEIEN 0x40
#define BIT_DECIEN 0x20
#define BIT_DOUTEN 0x02
/* CTRL0 register bitmasks */
#define BIT_DECEN 0x80
#define BIT_E01RQ 0x20
#define BIT_AUTORQ 0x10
#define BIT_WRRQ 0x04
/* CTRL1 register bitmasks */
#define BIT_MODRQ 0x08
#define BIT_FORMRQ 0x04
#define BIT_SHDREN 0x01
/* CTRL2 register bitmask */
#define BIT_VALST 0x80
/* TODO: figure exact DMA transfer rate */
#define DMA_BYTES_PER_LINE 512
void cdc_init(void)
{
memset(&cdc, 0, sizeof(cdc_t));
}
void cdc_reset(void)
{
/* reset CDC register index */
scd.regs[0x04>>1].byte.l = 0x00;
/* reset CDC registers */
cdc.ifstat = 0xff;
cdc.ifctrl = 0x00;
cdc.ctrl[0] = 0x00;
cdc.ctrl[1] = 0x00;
cdc.stat[0] = 0x00;
cdc.stat[1] = 0x00;
cdc.stat[2] = 0x00;
cdc.stat[3] = 0x80;
cdc.head[0][0] = 0x00;
cdc.head[0][1] = 0x00;
cdc.head[0][2] = 0x00;
cdc.head[0][3] = 0x01;
cdc.head[1][0] = 0x00;
cdc.head[1][1] = 0x00;
cdc.head[1][2] = 0x00;
cdc.head[1][3] = 0x00;
/* reset CDC cycle counter */
cdc.cycles = 0;
/* DMA transfer disabled */
cdc.dma_w = 0;
/* clear any pending IRQ */
if (scd.pending & (1 << 5))
{
/* clear any pending interrupt level 5 */
scd.pending &= ~(1 << 5);
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
}
int cdc_context_save(uint8 *state)
{
uint8 tmp8;
int bufferptr = 0;
if (cdc.dma_w == pcm_ram_dma_w)
{
tmp8 = 1;
}
else if (cdc.dma_w == prg_ram_dma_w)
{
tmp8 = 2;
}
else if (cdc.dma_w == word_ram_0_dma_w)
{
tmp8 = 3;
}
else if (cdc.dma_w == word_ram_1_dma_w)
{
tmp8 = 4;
}
else if (cdc.dma_w == word_ram_2M_dma_w)
{
tmp8 = 5;
}
else
{
tmp8 = 0;
}
save_param(&cdc, sizeof(cdc));
save_param(&tmp8, 1);
return bufferptr;
}
int cdc_context_load(uint8 *state)
{
uint8 tmp8;
int bufferptr = 0;
load_param(&cdc, sizeof(cdc));
load_param(&tmp8, 1);
switch (tmp8)
{
case 1:
cdc.dma_w = pcm_ram_dma_w;
break;
case 2:
cdc.dma_w = prg_ram_dma_w;
break;
case 3:
cdc.dma_w = word_ram_0_dma_w;
break;
case 4:
cdc.dma_w = word_ram_1_dma_w;
break;
case 5:
cdc.dma_w = word_ram_2M_dma_w;
break;
default:
cdc.dma_w = 0;
break;
}
return bufferptr;
}
void cdc_dma_update(void)
{
/* maximal transfer length */
int length = DMA_BYTES_PER_LINE;
/* end of DMA transfer ? */
if (cdc.dbc.w < DMA_BYTES_PER_LINE)
{
/* transfer remaining words using 16-bit DMA */
cdc.dma_w((cdc.dbc.w + 1) >> 1);
/* reset data byte counter (DBCH bits 4-7 should be set to 1) */
cdc.dbc.w = 0xf000;
/* clear !DTEN and !DTBSY */
cdc.ifstat |= (BIT_DTBSY | BIT_DTEN);
/* pending Data Transfer End interrupt */
cdc.ifstat &= ~BIT_DTEI;
/* Data Transfer End interrupt enabled ? */
if (cdc.ifctrl & BIT_DTEIEN)
{
/* pending level 5 interrupt */
scd.pending |= (1 << 5);
/* level 5 interrupt enabled ? */
if (scd.regs[0x32>>1].byte.l & 0x20)
{
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
}
/* clear DSR bit & set EDT bit (CD register $04) */
scd.regs[0x04>>1].byte.h = (scd.regs[0x04>>1].byte.h & 0x07) | 0x80;
/* SUB-CPU idle on register $04 polling ? */
if (s68k.stopped & (1<<0x04))
{
/* sync SUB-CPU with CDC */
s68k.cycles = scd.cycles;
/* restart SUB-CPU */
s68k.stopped = 0;
#ifdef LOG_SCD
error("s68k started from %d cycles\n", s68k.cycles);
#endif
}
/* disable DMA transfer */
cdc.dma_w = 0;
}
else
{
/* transfer all words using 16-bit DMA */
cdc.dma_w(DMA_BYTES_PER_LINE >> 1);
/* decrement data byte counter */
cdc.dbc.w -= length;
}
}
int cdc_decoder_update(uint32 header)
{
/* data decoding enabled ? */
if (cdc.ctrl[0] & BIT_DECEN)
{
/* update HEAD registers */
*(uint32 *)(cdc.head[0]) = header;
/* set !VALST */
cdc.stat[3] = 0x00;
/* pending decoder interrupt */
cdc.ifstat &= ~BIT_DECI;
/* decoder interrupt enabled ? */
if (cdc.ifctrl & BIT_DECIEN)
{
/* pending level 5 interrupt */
scd.pending |= (1 << 5);
/* level 5 interrupt enabled ? */
if (scd.regs[0x32>>1].byte.l & 0x20)
{
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
}
/* buffer RAM write enabled ? */
if (cdc.ctrl[0] & BIT_WRRQ)
{
uint16 offset;
/* increment block pointer */
cdc.pt.w += 2352;
/* increment write address */
cdc.wa.w += 2352;
/* CDC buffer address */
offset = cdc.pt.w & 0x3fff;
/* write CDD block header (4 bytes) */
*(uint32 *)(cdc.ram + offset) = header;
/* write CDD block data (2048 bytes) */
cdd_read_data(cdc.ram + 4 + offset);
/* take care of buffer overrun */
if (offset > (0x4000 - 2048 - 4))
{
/* data should be written at the start of buffer */
memcpy(cdc.ram, cdc.ram + 0x4000, offset + 2048 + 4 - 0x4000);
}
/* read next data block */
return 1;
}
}
/* keep decoding same data block if Buffer Write is disabled */
return 0;
}
void cdc_reg_w(unsigned char data)
{
#ifdef LOG_CDC
error("CDC register %X write 0x%04x (%X)\n", scd.regs[0x04>>1].byte.l & 0x0F, data, s68k.pc);
#endif
switch (scd.regs[0x04>>1].byte.l & 0x0F)
{
case 0x01: /* IFCTRL */
{
/* pending interrupts ? */
if (((data & BIT_DTEIEN) && !(cdc.ifstat & BIT_DTEI)) ||
((data & BIT_DECIEN) && !(cdc.ifstat & BIT_DECI)))
{
/* pending level 5 interrupt */
scd.pending |= (1 << 5);
/* level 5 interrupt enabled ? */
if (scd.regs[0x32>>1].byte.l & 0x20)
{
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
}
else if (scd.pending & (1 << 5))
{
/* clear pending level 5 interrupts */
scd.pending &= ~(1 << 5);
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
/* abort any data transfer if data output is disabled */
if (!(data & BIT_DOUTEN))
{
/* clear !DTBSY and !DTEN */
cdc.ifstat |= (BIT_DTBSY | BIT_DTEN);
}
cdc.ifctrl = data;
scd.regs[0x04>>1].byte.l = 0x02;
break;
}
case 0x02: /* DBCL */
cdc.dbc.byte.l = data;
scd.regs[0x04>>1].byte.l = 0x03;
break;
case 0x03: /* DBCH */
cdc.dbc.byte.h = data;
scd.regs[0x04>>1].byte.l = 0x04;
break;
case 0x04: /* DACL */
cdc.dac.byte.l = data;
scd.regs[0x04>>1].byte.l = 0x05;
break;
case 0x05: /* DACH */
cdc.dac.byte.h = data;
scd.regs[0x04>>1].byte.l = 0x06;
break;
case 0x06: /* DTRG */
{
/* start data transfer if data output is enabled */
if (cdc.ifctrl & BIT_DOUTEN)
{
/* set !DTBSY */
cdc.ifstat &= ~BIT_DTBSY;
/* clear DBCH bits 4-7 */
cdc.dbc.byte.h &= 0x0f;
/* clear EDT & DSR bits (SCD register $04) */
scd.regs[0x04>>1].byte.h &= 0x07;
/* setup data transfer destination */
switch (scd.regs[0x04>>1].byte.h & 0x07)
{
case 2: /* MAIN-CPU host read */
case 3: /* SUB-CPU host read */
{
/* set !DTEN */
cdc.ifstat &= ~BIT_DTEN;
/* set DSR bit (register $04) */
scd.regs[0x04>>1].byte.h |= 0x40;
break;
}
case 4: /* PCM RAM DMA */
{
cdc.dma_w = pcm_ram_dma_w;
break;
}
case 5: /* PRG-RAM DMA */
{
cdc.dma_w = prg_ram_dma_w;
break;
}
case 7: /* WORD-RAM DMA */
{
/* check memory mode */
if (scd.regs[0x02 >> 1].byte.l & 0x04)
{
/* 1M mode */
if (scd.regs[0x02 >> 1].byte.l & 0x01)
{
/* Word-RAM bank 0 is assigned to SUB-CPU */
cdc.dma_w = word_ram_0_dma_w;
}
else
{
/* Word-RAM bank 1 is assigned to SUB-CPU */
cdc.dma_w = word_ram_1_dma_w;
}
}
else
{
/* 2M mode */
if (scd.regs[0x02 >> 1].byte.l & 0x02)
{
/* only process DMA if Word-RAM is assigned to SUB-CPU */
cdc.dma_w = word_ram_2M_dma_w;
}
}
break;
}
default: /* invalid */
{
#ifdef LOG_CDC
error("invalid CDC tranfer destination (%d)\n", scd.regs[0x04>>1].byte.h & 0x07);
#endif
break;
}
}
}
scd.regs[0x04>>1].byte.l = 0x07;
break;
}
case 0x07: /* DTACK */
{
/* clear pending data transfer end interrupt */
cdc.ifstat |= BIT_DTEI;
/* clear DBCH bits 4-7 */
cdc.dbc.byte.h &= 0x0f;
#if 0
/* no pending decoder interrupt ? */
if ((cdc.ifstat | BIT_DECI) || !(cdc.ifctrl & BIT_DECIEN))
{
/* clear pending level 5 interrupt */
scd.pending &= ~(1 << 5);
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
#endif
scd.regs[0x04>>1].byte.l = 0x08;
break;
}
case 0x08: /* WAL */
cdc.wa.byte.l = data;
scd.regs[0x04>>1].byte.l = 0x09;
break;
case 0x09: /* WAH */
cdc.wa.byte.h = data;
scd.regs[0x04>>1].byte.l = 0x0a;
break;
case 0x0a: /* CTRL0 */
{
/* set CRCOK bit only if decoding is enabled */
cdc.stat[0] = data & BIT_DECEN;
/* update decoding mode */
if (data & BIT_AUTORQ)
{
/* set MODE bit according to CTRL1 register & clear FORM bit */
cdc.stat[2] = cdc.ctrl[1] & BIT_MODRQ;
}
else
{
/* set MODE & FORM bits according to CTRL1 register */
cdc.stat[2] = cdc.ctrl[1] & (BIT_MODRQ | BIT_FORMRQ);
}
cdc.ctrl[0] = data;
scd.regs[0x04>>1].byte.l = 0x0b;
break;
}
case 0x0b: /* CTRL1 */
{
/* update decoding mode */
if (cdc.ctrl[0] & BIT_AUTORQ)
{
/* set MODE bit according to CTRL1 register & clear FORM bit */
cdc.stat[2] = data & BIT_MODRQ;
}
else
{
/* set MODE & FORM bits according to CTRL1 register */
cdc.stat[2] = data & (BIT_MODRQ | BIT_FORMRQ);
}
cdc.ctrl[1] = data;
scd.regs[0x04>>1].byte.l = 0x0c;
break;
}
case 0x0c: /* PTL */
cdc.pt.byte.l = data;
scd.regs[0x04>>1].byte.l = 0x0d;
break;
case 0x0d: /* PTH */
cdc.pt.byte.h = data;
scd.regs[0x04>>1].byte.l = 0x0e;
break;
case 0x0e: /* CTRL2 (unused) */
scd.regs[0x04>>1].byte.l = 0x0f;
break;
case 0x0f: /* RESET */
cdc_reset();
break;
default: /* by default, SBOUT is not used */
break;
}
}
unsigned char cdc_reg_r(void)
{
switch (scd.regs[0x04>>1].byte.l & 0x0F)
{
case 0x01: /* IFSTAT */
scd.regs[0x04>>1].byte.l = 0x02;
return cdc.ifstat;
case 0x02: /* DBCL */
scd.regs[0x04>>1].byte.l = 0x03;
return cdc.dbc.byte.l;
case 0x03: /* DBCH */
scd.regs[0x04>>1].byte.l = 0x04;
return cdc.dbc.byte.h;
case 0x04: /* HEAD0 */
scd.regs[0x04>>1].byte.l = 0x05;
return cdc.head[cdc.ctrl[1] & BIT_SHDREN][0];
case 0x05: /* HEAD1 */
scd.regs[0x04>>1].byte.l = 0x06;
return cdc.head[cdc.ctrl[1] & BIT_SHDREN][1];
case 0x06: /* HEAD2 */
scd.regs[0x04>>1].byte.l = 0x07;
return cdc.head[cdc.ctrl[1] & BIT_SHDREN][2];
case 0x07: /* HEAD3 */
scd.regs[0x04>>1].byte.l = 0x08;
return cdc.head[cdc.ctrl[1] & BIT_SHDREN][3];
case 0x08: /* PTL */
scd.regs[0x04>>1].byte.l = 0x09;
return cdc.pt.byte.l;
case 0x09: /* PTH */
scd.regs[0x04>>1].byte.l = 0x0a;
return cdc.pt.byte.h;
case 0x0a: /* WAL */
scd.regs[0x04>>1].byte.l = 0x0b;
return cdc.wa.byte.l;
case 0x0b: /* WAH */
scd.regs[0x04>>1].byte.l = 0x0c;
return cdc.wa.byte.h;
case 0x0c: /* STAT0 */
scd.regs[0x04>>1].byte.l = 0x0d;
return cdc.stat[0];
case 0x0d: /* STAT1 (always return 0) */
scd.regs[0x04>>1].byte.l = 0x0e;
return 0x00;
case 0x0e: /* STAT2 */
scd.regs[0x04>>1].byte.l = 0x0f;
return cdc.stat[2];
case 0x0f: /* STAT3 */
{
uint8 data = cdc.stat[3];
/* clear !VALST (note: this is not 100% correct but BIOS do not seem to care) */
cdc.stat[3] = BIT_VALST;
/* clear pending decoder interrupt */
cdc.ifstat |= BIT_DECI;
#if 0
/* no pending data transfer end interrupt */
if ((cdc.ifstat | BIT_DTEI) || !(cdc.ifctrl & BIT_DTEIEN))
{
/* clear pending level 5 interrupt */
scd.pending &= ~(1 << 5);
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
#endif
scd.regs[0x04>>1].byte.l = 0x00;
return data;
}
default: /* by default, COMIN is always empty */
return 0xff;
}
}
unsigned short cdc_host_r(void)
{
/* check if data is available */
if (!(cdc.ifstat & BIT_DTEN))
{
/* read data word from CDC RAM buffer */
uint16 data = *(uint16 *)(cdc.ram + (cdc.dac.w & 0x3ffe));
#ifdef LSB_FIRST
/* source data is stored in big endian format */
data = ((data >> 8) | (data << 8)) & 0xffff;
#endif
#ifdef LOG_CDC
error("CDC host read 0x%04x -> 0x%04x (dbc=0x%x) (%X)\n", cdc.dac.w, data, cdc.dbc.w, s68k.pc);
#endif
/* increment data address counter */
cdc.dac.w += 2;
/* decrement data byte counter */
cdc.dbc.w -= 2;
/* end of transfer ? */
if ((int16)cdc.dbc.w <= 0)
{
/* reset data byte counter (DBCH bits 4-7 should be set to 1) */
cdc.dbc.w = 0xf000;
/* clear !DTEN and !DTBSY */
cdc.ifstat |= (BIT_DTBSY | BIT_DTEN);
/* pending Data Transfer End interrupt */
cdc.ifstat &= ~BIT_DTEI;
/* Data Transfer End interrupt enabled ? */
if (cdc.ifctrl & BIT_DTEIEN)
{
/* pending level 5 interrupt */
scd.pending |= (1 << 5);
/* level 5 interrupt enabled ? */
if (scd.regs[0x32>>1].byte.l & 0x20)
{
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
}
/* clear DSR bit & set EDT bit (SCD register $04) */
scd.regs[0x04>>1].byte.h = (scd.regs[0x04>>1].byte.h & 0x07) | 0x80;
}
return data;
}
#ifdef LOG_CDC
error("error reading CDC host (data transfer disabled)\n");
#endif
return 0xffff;
}

View File

@ -0,0 +1,71 @@
/***************************************************************************************
* Genesis Plus
* CD data controller (LC89510 compatible)
*
* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _HW_CDC_
#define _HW_CDC_
#define cdc scd.cdc_hw
/* CDC hardware */
typedef struct
{
uint8 ifstat;
uint8 ifctrl;
reg16_t dbc;
reg16_t dac;
reg16_t pt;
reg16_t wa;
uint8 ctrl[2];
uint8 head[2][4];
uint8 stat[4];
int cycles;
void (*dma_w)(unsigned int words); /* DMA transfer callback */
uint8 ram[0x4000 + 2352]; /* 16K external RAM (with one block overhead to handle buffer overrun) */
} cdc_t;
/* Function prototypes */
extern void cdc_init(void);
extern void cdc_reset(void);
extern int cdc_context_save(uint8 *state);
extern int cdc_context_load(uint8 *state);
extern void cdc_dma_update(void);
extern int cdc_decoder_update(uint32 header);
extern void cdc_reg_w(unsigned char data);
extern unsigned char cdc_reg_r(void);
extern unsigned short cdc_host_r(void);
#endif

View File

@ -0,0 +1,865 @@
/***************************************************************************************
* Genesis Plus
* CD drive processor & CD-DA fader
*
* Copyright (C) 2012-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
/* BCD conversion lookup tables */
static const uint8 lut_BCD_8[100] =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
};
static const uint16 lut_BCD_16[100] =
{
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009,
0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x0108, 0x0109,
0x0200, 0x0201, 0x0202, 0x0203, 0x0204, 0x0205, 0x0206, 0x0207, 0x0208, 0x0209,
0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, 0x0308, 0x0309,
0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409,
0x0500, 0x0501, 0x0502, 0x0503, 0x0504, 0x0505, 0x0506, 0x0507, 0x0508, 0x0509,
0x0600, 0x0601, 0x0602, 0x0603, 0x0604, 0x0605, 0x0606, 0x0607, 0x0608, 0x0609,
0x0700, 0x0701, 0x0702, 0x0703, 0x0704, 0x0705, 0x0706, 0x0707, 0x0708, 0x0709,
0x0800, 0x0801, 0x0802, 0x0803, 0x0804, 0x0805, 0x0806, 0x0807, 0x0808, 0x0809,
0x0900, 0x0901, 0x0902, 0x0903, 0x0904, 0x0905, 0x0906, 0x0907, 0x0908, 0x0909,
};
/* pre-build TOC */
static const uint16 toc_snatcher[21] =
{
56014, 495, 10120, 20555, 1580, 5417, 12502, 16090, 6553, 9681,
8148, 20228, 8622, 6142, 5858, 1287, 7424, 3535, 31697, 2485,
31380
};
static const uint16 toc_lunar[52] =
{
5422, 1057, 7932, 5401, 6380, 6592, 5862, 5937, 5478, 5870,
6673, 6613, 6429, 4996, 4977, 5657, 3720, 5892, 3140, 3263,
6351, 5187, 3249, 1464, 1596, 1750, 1751, 6599, 4578, 5205,
1550, 1827, 2328, 1346, 1569, 1613, 7199, 4928, 1656, 2549,
1875, 3901, 1850, 2399, 2028, 1724, 4889, 14551, 1184, 2132,
685, 3167
};
static const uint32 toc_shadow[15] =
{
10226, 70054, 11100, 12532, 12444, 11923, 10059, 10167, 10138, 13792,
11637, 2547, 2521, 3856, 900
};
static const uint32 toc_dungeon[13] =
{
2250, 22950, 16350, 24900, 13875, 19950, 13800, 15375, 17400, 17100,
3325, 6825, 25275
};
static const uint32 toc_ffight[26] =
{
11994, 9742, 10136, 9685, 9553, 14588, 9430, 8721, 9975, 9764,
9704, 12796, 585, 754, 951, 624, 9047, 1068, 817, 9191, 1024,
14562, 10320, 8627, 3795, 3047
};
static const uint32 toc_ffightj[29] =
{
11994, 9752, 10119, 9690, 9567, 14575, 9431, 8731, 9965, 9763,
9716, 12791, 579, 751, 958, 630, 9050, 1052, 825, 9193, 1026,
14553, 9834, 10542, 1699, 1792, 1781, 3783, 3052
};
/* supported WAVE file header (16-bit stereo samples @44.1kHz) */
static const unsigned char waveHeader[32] =
{
0x57,0x41,0x56,0x45,0x66,0x6d,0x74,0x20,0x10,0x00,0x00,0x00,0x01,0x00,0x02,0x00,
0x44,0xac,0x00,0x00,0x10,0xb1,0x02,0x00,0x04,0x00,0x10,0x00,0x64,0x61,0x74,0x61
};
static blip_t* blip[2];
// FRONTEND INTERFACE
void (*cdd_readcallback)(int lba, void *dest, int audio);
typedef struct
{
toc_t toc;
void (*cdd_readcallback)(int lba, void *dest, int audio);
} frontendcd_t;
int cdd_load(const char *key, char *header)
{
frontendcd_t fecd;
char data[2048];
int startoffs;
int bytes = sizeof(frontendcd_t);
if (load_archive(key, (unsigned char *)&fecd, bytes, NULL) != bytes)
return 0;
// look for valid header
fecd.cdd_readcallback(0, data, 0);
if (memcmp("SEGADISCSYSTEM", data, 14) == 0)
startoffs = 0;
else if (memcmp("SEGADISCSYSTEM", data + 16, 14) == 0)
startoffs = 16;
else
return 0;
// copy security block
memcpy(header, data + startoffs, 0x210);
// copy disk information
cdd_readcallback = fecd.cdd_readcallback;
memcpy(&cdd.toc, &fecd.toc, sizeof(toc_t));
cdd.loaded = 1;
return 1;
}
void cdd_init(blip_t* left, blip_t* right)
{
/* CD-DA is running by default at 44100 Hz */
/* Audio stream is resampled to desired rate using Blip Buffer */
blip[0] = left;
blip[1] = right;
blip_set_rates(left, 44100, snd.sample_rate);
blip_set_rates(right, 44100, snd.sample_rate);
}
void cdd_reset(void)
{
/* reset cycle counter */
cdd.cycles = 0;
/* reset drive access latency */
cdd.latency = 0;
/* reset track index */
cdd.index = 0;
/* reset logical block address */
cdd.lba = 0;
// reset audio subblock position
cdd.sampleOffset = 0;
// reset audio read position
cdd.sampleLba = 0;
/* reset status */
cdd.status = cdd.loaded ? CD_STOP : NO_DISC;
/* reset CD-DA fader (full volume) */
cdd.volume = 0x400;
/* clear CD-DA output */
cdd.audio[0] = cdd.audio[1] = 0;
}
int cdd_context_save(uint8 *state)
{
int bufferptr = 0;
save_param(&cdd.cycles, sizeof(cdd.cycles));
save_param(&cdd.latency, sizeof(cdd.latency));
save_param(&cdd.index, sizeof(cdd.index));
save_param(&cdd.lba, sizeof(cdd.lba));
save_param(&cdd.scanOffset, sizeof(cdd.scanOffset));
save_param(&cdd.volume, sizeof(cdd.volume));
save_param(&cdd.status, sizeof(cdd.status));
save_param(&cdd.sampleOffset, sizeof(cdd.sampleOffset));
save_param(&cdd.sampleLba, sizeof(cdd.sampleLba));
return bufferptr;
}
int cdd_context_load(uint8 *state)
{
int lba;
int bufferptr = 0;
load_param(&cdd.cycles, sizeof(cdd.cycles));
load_param(&cdd.latency, sizeof(cdd.latency));
load_param(&cdd.index, sizeof(cdd.index));
load_param(&cdd.lba, sizeof(cdd.lba));
load_param(&cdd.scanOffset, sizeof(cdd.scanOffset));
load_param(&cdd.volume, sizeof(cdd.volume));
load_param(&cdd.status, sizeof(cdd.status));
load_param(&cdd.sampleOffset, sizeof(cdd.sampleOffset));
load_param(&cdd.sampleLba, sizeof(cdd.sampleLba));
return bufferptr;
}
void cdd_unload(void)
{
cdd.loaded = 0;
cdd_readcallback = NULL;
/* reset TOC */
memset(&cdd.toc, 0x00, sizeof(cdd.toc));
}
void cdd_read_data(uint8 *dst)
{
/* only read DATA track sectors */
if ((cdd.lba >= 0) && (cdd.lba < cdd.toc.tracks[0].end))
{
cdd_readcallback(cdd.lba, dst, 0);
}
}
void cdd_read_audio(unsigned int samples)
{
// previous audio outputs //
int16 l = cdd.audio[0];
int16 r = cdd.audio[1];
// get number of internal clocks (samples) needed //
samples = blip_clocks_needed(blip[0], samples);
// audio track playing ? //
if (!scd.regs[0x36>>1].byte.h)
{
int i, mul, delta;
// current CD-DA fader volume //
int curVol = cdd.volume;
// CD-DA fader volume setup (0-1024) //
int endVol = scd.regs[0x34>>1].w >> 4;
// read samples from current block //
{
#ifdef LSB_FIRST
int16 *ptr = (int16 *) (cdc.ram);
#else
uint8 *ptr = cdc.ram;
#endif
{
char scratch[2352];
// copy the end of current sector
int nsampreq = samples;
unsigned char *dest = cdc.ram;
cdd_readcallback(cdd.sampleLba, scratch, 1);
memcpy(cdc.ram, scratch + cdd.sampleOffset * 4, 2352 - cdd.sampleOffset * 4);
cdd.sampleLba++;
nsampreq -= 588 - cdd.sampleOffset;
dest += 2352 - cdd.sampleOffset * 4;
cdd.sampleOffset = 0;
// fill full sectors
while (nsampreq >= 588)
{
cdd_readcallback(cdd.sampleLba, scratch, 1);
memcpy(dest, scratch, 2352);
cdd.sampleLba++;
nsampreq -= 588;
dest += 2352;
}
// do last partial sector
if (nsampreq > 0)
{
cdd_readcallback(cdd.sampleLba, scratch, 1);
memcpy(dest, scratch, nsampreq * 4);
cdd.sampleOffset = nsampreq;
dest += nsampreq * 4;
nsampreq = 0;
}
//printf("samples: %i\n", samples);
//memset(cdc.ram, 0, samples * 4);
//fread(cdc.ram, 1, samples * 4, cdd.toc.tracks[cdd.index].fd);
}
// process 16-bit (little-endian) stereo samples //
for (i=0; i<samples; i++)
{
// CD-DA fader multiplier (cf. LC7883 datasheet) //
// (MIN) 0,1,2,3,4,8,12,16,20...,1020,1024 (MAX) //
mul = (curVol & 0x7fc) ? (curVol & 0x7fc) : (curVol & 0x03);
// left channel //
#ifdef LSB_FIRST
delta = ((ptr[0] * mul) / 1024) - l;
ptr++;
#else
delta = (((int16)((ptr[0] + ptr[1]*256)) * mul) / 1024) - l;
ptr += 2;
#endif
l += delta;
blip_add_delta_fast(blip[0], i, delta);
// right channel //
#ifdef LSB_FIRST
delta = ((ptr[0] * mul) / 1024) - r;
ptr++;
#else
delta = (((int16)((ptr[0] + ptr[1]*256)) * mul) / 1024) - r;
ptr += 2;
#endif
r += delta;
blip_add_delta_fast(blip[1], i, delta);
// update CD-DA fader volume (one step/sample) //
if (curVol < endVol)
{
// fade-in //
curVol++;
}
else if (curVol > endVol)
{
// fade-out //
curVol--;
}
else if (!curVol)
{
// audio will remain muted until next setup //
break;
}
}
}
// save current CD-DA fader volume //
cdd.volume = curVol;
// save last audio output for next frame //
cdd.audio[0] = l;
cdd.audio[1] = r;
}
else
{
// no audio output //
if (l) blip_add_delta_fast(blip[0], 0, -l);
if (r) blip_add_delta_fast(blip[1], 0, -r);
// save audio output for next frame //
cdd.audio[0] = 0;
cdd.audio[1] = 0;
}
// end of Blip Buffer timeframe //
blip_end_frame(blip[0], samples);
blip_end_frame(blip[1], samples);
}
void cdd_update(void)
{
#ifdef LOG_CDD
error("LBA = %d (track n°%d)(latency=%d)\n", cdd.lba, cdd.index, cdd.latency);
#endif
/* seeking disc */
if (cdd.status == CD_SEEK)
{
/* drive latency */
if (cdd.latency > 0)
{
cdd.latency--;
return;
}
/* drive is ready */
cdd.status = CD_READY;
}
/* reading disc */
else if (cdd.status == CD_PLAY)
{
/* drive latency */
if (cdd.latency > 0)
{
cdd.latency--;
return;
}
/* track type */
if (!cdd.index)
{
/* DATA sector header (CD-ROM Mode 1) */
uint8 header[4];
uint32 msf = cdd.lba + 150;
header[0] = lut_BCD_8[(msf / 75) / 60];
header[1] = lut_BCD_8[(msf / 75) % 60];
header[2] = lut_BCD_8[(msf % 75)];
header[3] = 0x01;
/* data track sector read is controlled by CDC */
cdd.lba += cdc_decoder_update(*(uint32 *)(header));
}
else if (cdd.index < cdd.toc.last)
{
/* check against audio track start index */
if (cdd.lba >= cdd.toc.tracks[cdd.index].start)
{
/* audio track playing */
// if it wasn't before, set the audio start position
if (scd.regs[0x36>>1].byte.h)
{
cdd.sampleLba = cdd.lba + 1;
cdd.sampleOffset = 0;
}
scd.regs[0x36>>1].byte.h = 0x00;
}
/* audio blocks are still sent to CDC as well as CD DAC/Fader */
cdc_decoder_update(0);
/* next audio block is automatically read */
cdd.lba++;
}
else
{
/* end of disc */
cdd.status = CD_END;
return;
}
/* check end of current track */
if (cdd.lba >= cdd.toc.tracks[cdd.index].end)
{
/* play next track */
cdd.index++;
/* PAUSE between tracks */
scd.regs[0x36>>1].byte.h = 0x01;
}
}
/* scanning disc */
else if (cdd.status == CD_SCAN)
{
/* fast-forward or fast-rewind */
cdd.lba += cdd.scanOffset;
cdd.sampleLba += cdd.scanOffset;
/* check current track limits */
if (cdd.lba >= cdd.toc.tracks[cdd.index].end)
{
/* next track */
cdd.index++;
/* skip directly to track start position */
cdd.lba = cdd.toc.tracks[cdd.index].start;
/* AUDIO track playing ? */
if (cdd.status == CD_PLAY)
{
scd.regs[0x36>>1].byte.h = 0x00;
// set audio start point
cdd.sampleLba = cdd.lba;
cdd.sampleOffset = 0;
}
}
else if (cdd.lba < cdd.toc.tracks[cdd.index].start)
{
/* previous track */
cdd.index--;
/* skip directly to track end position */
cdd.lba = cdd.toc.tracks[cdd.index].end;
}
/* check disc limits */
if (cdd.index < 0)
{
cdd.index = 0;
cdd.lba = 0;
}
else if (cdd.index >= cdd.toc.last)
{
/* no AUDIO track playing */
scd.regs[0x36>>1].byte.h = 0x01;
/* end of disc */
cdd.index = cdd.toc.last;
cdd.lba = cdd.toc.end;
cdd.status = CD_END;
return;
}
}
}
void cdd_process(void)
{
/* Process CDD command */
switch (scd.regs[0x42>>1].byte.h & 0x0f)
{
case 0x00: /* Drive Status */
{
/* RS1-RS8 normally unchanged */
scd.regs[0x38>>1].byte.h = cdd.status;
/* unless RS1 indicated invalid track infos */
if (scd.regs[0x38>>1].byte.l == 0x0f)
{
/* and SEEK has ended */
if (cdd.status != CD_SEEK)
{
/* then return valid track infos, e.g current track number in RS2-RS3 (fixes Lunar - The Silver Star) */
scd.regs[0x38>>1].byte.l = 0x02;
scd.regs[0x3a>>1].w = (cdd.index < cdd.toc.last) ? lut_BCD_16[cdd.index + 1] : 0x0A0A;
}
}
break;
}
case 0x01: /* Stop Drive */
{
/* update status */
cdd.status = cdd.loaded ? CD_STOP : NO_DISC;
/* no audio track playing */
scd.regs[0x36>>1].byte.h = 0x01;
/* RS1-RS8 ignored, expects 0x0 ("no disc" ?) in RS0 once */
scd.regs[0x38>>1].w = 0x0000;
scd.regs[0x3a>>1].w = 0x0000;
scd.regs[0x3c>>1].w = 0x0000;
scd.regs[0x3e>>1].w = 0x0000;
scd.regs[0x40>>1].w = 0x000f;
return;
}
case 0x02: /* Read TOC */
{
/* Infos automatically retrieved by CDD processor from Q-Channel */
/* commands 0x00-0x02 (current block) and 0x03-0x05 (Lead-In) */
switch (scd.regs[0x44>>1].byte.l)
{
case 0x00: /* Current Absolute Time (MM:SS:FF) */
{
int lba = cdd.lba + 150;
scd.regs[0x38>>1].w = cdd.status << 8;
scd.regs[0x3a>>1].w = lut_BCD_16[(lba/75)/60];
scd.regs[0x3c>>1].w = lut_BCD_16[(lba/75)%60];
scd.regs[0x3e>>1].w = lut_BCD_16[(lba%75)];
scd.regs[0x40>>1].byte.h = cdd.index ? 0x00 : 0x04; /* Current block flags in RS8 (bit0 = mute status, bit1: pre-emphasis status, bit2: track type) */
break;
}
case 0x01: /* Current Track Relative Time (MM:SS:FF) */
{
int lba = cdd.lba - cdd.toc.tracks[cdd.index].start;
scd.regs[0x38>>1].w = (cdd.status << 8) | 0x01;
scd.regs[0x3a>>1].w = lut_BCD_16[(lba/75)/60];
scd.regs[0x3c>>1].w = lut_BCD_16[(lba/75)%60];
scd.regs[0x3e>>1].w = lut_BCD_16[(lba%75)];
scd.regs[0x40>>1].byte.h = cdd.index ? 0x00 : 0x04; /* Current block flags in RS8 (bit0 = mute status, bit1: pre-emphasis status, bit2: track type) */
break;
}
case 0x02: /* Current Track Number */
{
scd.regs[0x38>>1].w = (cdd.status << 8) | 0x02;
scd.regs[0x3a>>1].w = (cdd.index < cdd.toc.last) ? lut_BCD_16[cdd.index + 1] : 0x0A0A;
scd.regs[0x3c>>1].w = 0x0000;
scd.regs[0x3e>>1].w = 0x0000; /* Disk Control Code (?) in RS6 */
scd.regs[0x40>>1].byte.h = 0x00;
break;
}
case 0x03: /* Total length (MM:SS:FF) */
{
int lba = cdd.toc.end + 150;
scd.regs[0x38>>1].w = (cdd.status << 8) | 0x03;
scd.regs[0x3a>>1].w = lut_BCD_16[(lba/75)/60];
scd.regs[0x3c>>1].w = lut_BCD_16[(lba/75)%60];
scd.regs[0x3e>>1].w = lut_BCD_16[(lba%75)];
scd.regs[0x40>>1].byte.h = 0x00;
break;
}
case 0x04: /* First & Last Track Numbers */
{
scd.regs[0x38>>1].w = (cdd.status << 8) | 0x04;
scd.regs[0x3a>>1].w = 0x0001;
scd.regs[0x3c>>1].w = lut_BCD_16[cdd.toc.last];
scd.regs[0x3e>>1].w = 0x0000; /* Drive Version (?) in RS6-RS7 */
scd.regs[0x40>>1].byte.h = 0x00; /* Lead-In flags in RS8 (bit0 = mute status, bit1: pre-emphasis status, bit2: track type) */
break;
}
case 0x05: /* Track Start Time (MM:SS:FF) */
{
int track = scd.regs[0x46>>1].byte.h * 10 + scd.regs[0x46>>1].byte.l;
int lba = cdd.toc.tracks[track-1].start + 150;
scd.regs[0x38>>1].w = (cdd.status << 8) | 0x05;
scd.regs[0x3a>>1].w = lut_BCD_16[(lba/75)/60];
scd.regs[0x3c>>1].w = lut_BCD_16[(lba/75)%60];
scd.regs[0x3e>>1].w = lut_BCD_16[(lba%75)];
scd.regs[0x40>>1].byte.h = track % 10; /* Track Number (low digit) */
if (track == 1)
{
/* RS6 bit 3 is set for the first (DATA) track */
scd.regs[0x3e>>1].byte.h |= 0x08;
}
break;
}
default:
{
#ifdef LOG_ERROR
error("Unknown CDD Command %02X (%X)\n", scd.regs[0x44>>1].byte.l, s68k.pc);
#endif
return;
}
}
break;
}
case 0x03: /* Play */
{
/* reset track index */
int index = 0;
/* new LBA position */
int lba = ((scd.regs[0x44>>1].byte.h * 10 + scd.regs[0x44>>1].byte.l) * 60 +
(scd.regs[0x46>>1].byte.h * 10 + scd.regs[0x46>>1].byte.l)) * 75 +
(scd.regs[0x48>>1].byte.h * 10 + scd.regs[0x48>>1].byte.l) - 150;
/* CD drive latency */
if (!cdd.latency)
{
/* Fixes a few games hanging during intro because they expect data to be read with some delay */
/* Radical Rex needs at least one interrupt delay */
/* Wolf Team games (Anet Futatabi, Cobra Command, Road Avenger & Time Gal) need at least 6 interrupts delay */
/* Space Adventure Cobra (2nd morgue scene) needs at least 13 interrupts delay (incl. seek time, so 6 is OK) */
/* Jeopardy & ESPN Sunday Night NFL are picky about this as well: 10 interrupts delay (+ seek time) seems OK */
cdd.latency = 10;
}
/* CD drive seek time */
/* max. seek time = 1.5 s = 1.5 x 75 = 112.5 CDD interrupts (rounded to 120) for 270000 sectors max on disc. */
/* Note: This is only a rough approximation since, on real hardware, seek time is much likely not linear and */
/* latency much larger than above value, but this model works fine for Sonic CD (track 26 playback needs to */
/* be enough delayed to start in sync with intro sequence, as compared with real hardware recording). */
if (lba > cdd.lba)
{
cdd.latency += (((lba - cdd.lba) * 120) / 270000);
}
else
{
cdd.latency += (((cdd.lba - lba) * 120) / 270000);
}
/* update current LBA */
cdd.lba = lba;
/* get track index */
while ((cdd.toc.tracks[index].end <= lba) && (index < cdd.toc.last)) index++;
/* update current track index */
cdd.index = index;
/* no audio track playing (yet) */
scd.regs[0x36>>1].byte.h = 0x01;
/* update status */
cdd.status = CD_PLAY;
/* return track index in RS2-RS3 */
scd.regs[0x38>>1].w = (CD_PLAY << 8) | 0x02;
scd.regs[0x3a>>1].w = (cdd.index < cdd.toc.last) ? lut_BCD_16[index + 1] : 0x0A0A;
scd.regs[0x3c>>1].w = 0x0000;
scd.regs[0x3e>>1].w = 0x0000;
scd.regs[0x40>>1].byte.h = 0x00;
break;
}
case 0x04: /* Seek */
{
/* reset track index */
int index = 0;
/* new LBA position */
int lba = ((scd.regs[0x44>>1].byte.h * 10 + scd.regs[0x44>>1].byte.l) * 60 +
(scd.regs[0x46>>1].byte.h * 10 + scd.regs[0x46>>1].byte.l)) * 75 +
(scd.regs[0x48>>1].byte.h * 10 + scd.regs[0x48>>1].byte.l) - 150;
/* CD drive seek time */
/* We are using similar linear model as above, although still not exactly accurate, */
/* it works fine for Switch/Panic! intro (Switch needs at least 30 interrupts while */
/* seeking from 00:05:63 to 24:03:19, Panic! when seeking from 00:05:60 to 24:06:07) */
if (lba > cdd.lba)
{
cdd.latency = ((lba - cdd.lba) * 120) / 270000;
}
else
{
cdd.latency = ((cdd.lba - lba) * 120) / 270000;
}
/* update current LBA */
cdd.lba = lba;
/* get current track index */
while ((cdd.toc.tracks[index].end <= lba) && (index < cdd.toc.last)) index++;
/* update current track index */
cdd.index = index;
/* no audio track playing */
scd.regs[0x36>>1].byte.h = 0x01;
/* update status */
cdd.status = CD_SEEK;
/* unknown RS1-RS8 values (returning 0xF in RS1 invalidates track infos in RS2-RS8 and fixes Final Fight CD intro when seek time is emulated) */
scd.regs[0x38>>1].w = (CD_SEEK << 8) | 0x0f;
scd.regs[0x3a>>1].w = 0x0000;
scd.regs[0x3c>>1].w = 0x0000;
scd.regs[0x3e>>1].w = 0x0000;
scd.regs[0x40>>1].w = ~(CD_SEEK + 0xf) & 0x0f;
return;
}
case 0x06: /* Pause */
{
/* no audio track playing */
scd.regs[0x36>>1].byte.h = 0x01;
/* update status (RS1-RS8 unchanged) */
cdd.status = scd.regs[0x38>>1].byte.h = CD_READY;
break;
}
case 0x07: /* Resume */
{
/* update status (RS1-RS8 unchanged) */
cdd.status = scd.regs[0x38>>1].byte.h = CD_PLAY;
break;
}
case 0x08: /* Forward Scan */
{
/* reset scanning direction / speed */
cdd.scanOffset = CD_SCAN_SPEED;
/* update status (RS1-RS8 unchanged) */
cdd.status = scd.regs[0x38>>1].byte.h = CD_SCAN;
break;
}
case 0x09: /* Rewind Scan */
{
/* reset scanning direction / speed */
cdd.scanOffset = -CD_SCAN_SPEED;
/* update status (RS1-RS8 unchanged) */
cdd.status = scd.regs[0x38>>1].byte.h = CD_SCAN;
break;
}
case 0x0a: /* N-Track Jump Control ? (usually sent before CD_SEEK or CD_PLAY commands) */
{
/* TC3 corresponds to seek direction (00=forward, FF=reverse) */
/* TC4-TC7 are related to seek length (4x4 bits i.e parameter values are between -65535 and +65535) */
/* Maybe related to number of auto-sequenced track jumps/moves for CD DSP (cf. CXD2500BQ datasheet) */
/* also see US Patent nr. 5222054 for a detailled description of seeking operation using Track Jump */
/* no audio track playing */
scd.regs[0x36>>1].byte.h = 0x01;
/* update status (RS1-RS8 unchanged) */
cdd.status = scd.regs[0x38>>1].byte.h = CD_READY;
break;
}
case 0x0c: /* Close Tray */
{
/* no audio track playing */
scd.regs[0x36>>1].byte.h = 0x01;
/* update status */
cdd.status = cdd.loaded ? CD_STOP : NO_DISC;
/* RS1-RS8 ignored, expects 0x0 ("no disc" ?) in RS0 once */
scd.regs[0x38>>1].w = 0x0000;
scd.regs[0x3a>>1].w = 0x0000;
scd.regs[0x3c>>1].w = 0x0000;
scd.regs[0x3e>>1].w = 0x0000;
scd.regs[0x40>>1].w = 0x000f;
#ifdef CD_TRAY_CALLBACK
CD_TRAY_CALLBACK
#endif
return;
}
case 0x0d: /* Open Tray */
{
/* no audio track playing */
scd.regs[0x36>>1].byte.h = 0x01;
/* update status (RS1-RS8 ignored) */
cdd.status = CD_OPEN;
scd.regs[0x38>>1].w = CD_OPEN << 8;
scd.regs[0x3a>>1].w = 0x0000;
scd.regs[0x3c>>1].w = 0x0000;
scd.regs[0x3e>>1].w = 0x0000;
scd.regs[0x40>>1].w = ~CD_OPEN & 0x0f;
#ifdef CD_TRAY_CALLBACK
CD_TRAY_CALLBACK
#endif
return;
}
default: /* Unknown command */
#ifdef LOG_CDD
error("Unknown CDD Command !!!\n");
#endif
scd.regs[0x38>>1].byte.h = cdd.status;
break;
}
/* only compute checksum when necessary */
scd.regs[0x40>>1].byte.l = ~(scd.regs[0x38>>1].byte.h + scd.regs[0x38>>1].byte.l +
scd.regs[0x3a>>1].byte.h + scd.regs[0x3a>>1].byte.l +
scd.regs[0x3c>>1].byte.h + scd.regs[0x3c>>1].byte.l +
scd.regs[0x3e>>1].byte.h + scd.regs[0x3e>>1].byte.l +
scd.regs[0x40>>1].byte.h) & 0x0f;
}

View File

@ -0,0 +1,108 @@
/***************************************************************************************
* Genesis Plus
* CD drive processor & CD-DA fader
*
* Copyright (C) 2012-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _HW_CDD_
#define _HW_CDD_
#include "blip_buf.h"
#ifdef USE_LIBTREMOR
#include "tremor/ivorbisfile.h"
#endif
#define cdd scd.cdd_hw
/* CDD status */
#define NO_DISC 0x00
#define CD_PLAY 0x01
#define CD_SEEK 0x02
#define CD_SCAN 0x03
#define CD_READY 0x04
#define CD_OPEN 0x05 /* similar to 0x0E ? */
#define CD_STOP 0x09
#define CD_END 0x0C
/* CD blocks scanning speed */
#define CD_SCAN_SPEED 30
#define CD_MAX_TRACKS 100
/* CD track */
typedef struct
{
int start;
int end;
} track_t;
/* CD TOC */
typedef struct
{
int end;
int last;
track_t tracks[CD_MAX_TRACKS];
} toc_t;
/* CDD hardware */
typedef struct
{
uint32 cycles;
uint32 latency;
int loaded;
int index;
int lba;
int scanOffset;
int volume;
int sampleOffset;
int sampleLba;
uint8 status;
toc_t toc;
int16 audio[2];
} cdd_t;
/* Function prototypes */
extern void cdd_init(blip_t* left, blip_t* right);
extern void cdd_reset(void);
extern int cdd_context_save(uint8 *state);
extern int cdd_context_load(uint8 *state);
extern int cdd_load(const char *key, char *header);
extern void cdd_unload(void);
extern void cdd_read_data(uint8 *dst);
extern void cdd_read_audio(unsigned int samples);
extern void cdd_update(void);
extern void cdd_process(void);
#endif

View File

@ -0,0 +1,729 @@
/***************************************************************************************
* Genesis Plus
* CD graphics processor
*
* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
/***************************************************************/
/* WORD-RAM DMA interfaces (1M & 2M modes) */
/***************************************************************/
void word_ram_0_dma_w(unsigned int words)
{
uint16 data;
/* CDC buffer source address */
uint16 src_index = cdc.dac.w & 0x3ffe;
/* WORD-RAM destination address*/
uint32 dst_index = (scd.regs[0x0a>>1].w << 3) & 0x1fffe;
/* update DMA destination address */
scd.regs[0x0a>>1].w += (words >> 2);
/* update DMA source address */
cdc.dac.w += (words << 1);
/* DMA transfer */
while (words--)
{
/* read 16-bit word from CDC buffer */
data = *(uint16 *)(cdc.ram + src_index);
#ifdef LSB_FIRST
/* source data is stored in big endian format */
data = ((data >> 8) | (data << 8)) & 0xffff;
#endif
/* write 16-bit word to WORD-RAM */
*(uint16 *)(scd.word_ram[0] + dst_index) = data ;
/* increment CDC buffer source address */
src_index = (src_index + 2) & 0x3ffe;
/* increment WORD-RAM destination address */
dst_index = (dst_index + 2) & 0x1fffe;
}
}
void word_ram_1_dma_w(unsigned int words)
{
uint16 data;
/* CDC buffer source address */
uint16 src_index = cdc.dac.w & 0x3ffe;
/* WORD-RAM destination address*/
uint32 dst_index = ((scd.regs[0x0a>>1].w << 3) & 0x1fffe);
/* update DMA destination address */
scd.regs[0x0a>>1].w += (words >> 2);
/* update DMA source address */
cdc.dac.w += (words << 1);
/* DMA transfer */
while (words--)
{
/* read 16-bit word from CDC buffer */
data = *(uint16 *)(cdc.ram + src_index);
#ifdef LSB_FIRST
/* source data is stored in big endian format */
data = ((data >> 8) | (data << 8)) & 0xffff;
#endif
/* write 16-bit word to WORD-RAM */
*(uint16 *)(scd.word_ram[1] + dst_index) = data ;
/* increment CDC buffer source address */
src_index = (src_index + 2) & 0x3ffe;
/* increment WORD-RAM destination address */
dst_index = (dst_index + 2) & 0x1fffe;
}
}
void word_ram_2M_dma_w(unsigned int words)
{
uint16 data;
/* CDC buffer source address */
uint16 src_index = cdc.dac.w & 0x3ffe;
/* WORD-RAM destination address*/
uint32 dst_index = (scd.regs[0x0a>>1].w << 3) & 0x3fffe;
/* update DMA destination address */
scd.regs[0x0a>>1].w += (words >> 2);
/* update DMA source address */
cdc.dac.w += (words << 1);
/* DMA transfer */
while (words--)
{
/* read 16-bit word from CDC buffer */
data = *(uint16 *)(cdc.ram + src_index);
#ifdef LSB_FIRST
/* source data is stored in big endian format */
data = ((data >> 8) | (data << 8)) & 0xffff;
#endif
/* write 16-bit word to WORD-RAM */
*(uint16 *)(scd.word_ram_2M + dst_index) = data ;
/* increment CDC buffer source address */
src_index = (src_index + 2) & 0x3ffe;
/* increment WORD-RAM destination address */
dst_index = (dst_index + 2) & 0x3fffe;
}
}
/***************************************************************/
/* WORD-RAM 0 & 1 DOT image SUB-CPU interface (1M Mode) */
/***************************************************************/
unsigned int dot_ram_0_read16(unsigned int address)
{
uint8 data = READ_BYTE(scd.word_ram[0], (address >> 1) & 0x1ffff);
return ((data & 0x0f) | ((data << 4) & 0xf00));
}
unsigned int dot_ram_1_read16(unsigned int address)
{
uint8 data = READ_BYTE(scd.word_ram[1], (address >> 1) & 0x1ffff);
return ((data & 0x0f) | ((data << 4) & 0xf00));
}
void dot_ram_0_write16(unsigned int address, unsigned int data)
{
uint8 prev;
address = (address >> 1) & 0x1ffff;
prev = READ_BYTE(scd.word_ram[0], address);
data = (data & 0x0f) | ((data >> 4) & 0xf0);
data = gfx.lut_prio[(scd.regs[0x02>>1].w >> 3) & 0x03][prev][data];
WRITE_BYTE(scd.word_ram[0], address, data);
}
void dot_ram_1_write16(unsigned int address, unsigned int data)
{
uint8 prev;
address = (address >> 1) & 0x1ffff;
prev = READ_BYTE(scd.word_ram[1], address);
data = (data & 0x0f) | ((data >> 4) & 0xf0);
data = gfx.lut_prio[(scd.regs[0x02>>1].w >> 3) & 0x03][prev][data];
WRITE_BYTE(scd.word_ram[1], address, data);
}
unsigned int dot_ram_0_read8(unsigned int address)
{
uint8 data = READ_BYTE(scd.word_ram[0], (address >> 1) & 0x1ffff);
if (address & 1)
{
return (data & 0x0f);
}
return (data >> 4);
}
unsigned int dot_ram_1_read8(unsigned int address)
{
uint8 data = READ_BYTE(scd.word_ram[1], (address >> 1) & 0x1ffff);
if (address & 1)
{
return (data & 0x0f);
}
return (data >> 4);
}
void dot_ram_0_write8(unsigned int address, unsigned int data)
{
uint8 prev = READ_BYTE(scd.word_ram[0], (address >> 1) & 0x1ffff);
if (address & 1)
{
data = (prev & 0xf0) | (data & 0x0f);
}
else
{
data = (prev & 0x0f) | (data << 4);
}
data = gfx.lut_prio[(scd.regs[0x02>>1].w >> 3) & 0x03][prev][data];
WRITE_BYTE(scd.word_ram[0], (address >> 1) & 0x1ffff, data);
}
void dot_ram_1_write8(unsigned int address, unsigned int data)
{
uint8 prev = READ_BYTE(scd.word_ram[1], (address >> 1) & 0x1ffff);
if (address & 1)
{
data = (prev & 0xf0) | (data & 0x0f);
}
else
{
data = (prev & 0x0f) | (data << 4);
}
data = gfx.lut_prio[(scd.regs[0x02>>1].w >> 3) & 0x03][prev][data];
WRITE_BYTE(scd.word_ram[1], (address >> 1) & 0x1ffff, data);
}
/***************************************************************/
/* WORD-RAM 0 & 1 CELL image MAIN-CPU interface (1M Mode) */
/***************************************************************/
unsigned int cell_ram_0_read16(unsigned int address)
{
address = gfx.lut_offset[(address >> 2) & 0x7fff] | (address & 0x10002);
return *(uint16 *)(scd.word_ram[0] + address);
}
unsigned int cell_ram_1_read16(unsigned int address)
{
address = gfx.lut_offset[(address >> 2) & 0x7fff] | (address & 0x10002);
return *(uint16 *)(scd.word_ram[1] + address);
}
void cell_ram_0_write16(unsigned int address, unsigned int data)
{
address = gfx.lut_offset[(address >> 2) & 0x7fff] | (address & 0x10002);
*(uint16 *)(scd.word_ram[0] + address) = data;
}
void cell_ram_1_write16(unsigned int address, unsigned int data)
{
address = gfx.lut_offset[(address >> 2) & 0x7fff] | (address & 0x10002);
*(uint16 *)(scd.word_ram[1] + address) = data;
}
unsigned int cell_ram_0_read8(unsigned int address)
{
address = gfx.lut_offset[(address >> 2) & 0x7fff] | (address & 0x10003);
return READ_BYTE(scd.word_ram[0], address);
}
unsigned int cell_ram_1_read8(unsigned int address)
{
address = gfx.lut_offset[(address >> 2) & 0x7fff] | (address & 0x10003);
return READ_BYTE(scd.word_ram[1], address);
}
void cell_ram_0_write8(unsigned int address, unsigned int data)
{
address = gfx.lut_offset[(address >> 2) & 0x7fff] | (address & 0x10003);
WRITE_BYTE(scd.word_ram[0], address, data);
}
void cell_ram_1_write8(unsigned int address, unsigned int data)
{
address = gfx.lut_offset[(address >> 2) & 0x7fff] | (address & 0x10003);
WRITE_BYTE(scd.word_ram[1], address, data);
}
/***************************************************************/
/* Rotation / Scaling operation (2M Mode) */
/***************************************************************/
void gfx_init(void)
{
int i, j;
uint16 offset;
uint8 mask, row, col, temp;
memset(&gfx, 0, sizeof(gfx_t));
/* Initialize cell image lookup table */
/* $220000-$22FFFF corresponds to $200000-$20FFFF */
for (i=0; i<0x4000; i++)
{
offset = (i & 0x07) << 8; /* cell vline (0-7) */
offset = offset | (((i >> 8) & 0x3f) << 2); /* cell x offset (0-63) */
offset = offset | (((i >> 3) & 0x1f) << 11); /* cell y offset (0-31) */
gfx.lut_offset[i] = offset;
}
/* $230000-$237FFF corresponds to $210000-$217FFF */
for (i=0x4000; i<0x6000; i++)
{
offset = (i & 0x07) << 8; /* cell vline (0-7) */
offset = offset | (((i >> 7) & 0x3f) << 2); /* cell x offset (0-63) */
offset = offset | (((i >> 3) & 0x0f) << 11); /* cell y offset (0-15) */
gfx.lut_offset[i] = offset;
}
/* $238000-$23BFFF corresponds to $218000-$21BFFF */
for (i=0x6000; i<0x7000; i++)
{
offset = (i & 0x07) << 8; /* cell vline (0-7) */
offset = offset | (((i >> 6) & 0x3f) << 2); /* cell x offset (0-63) */
offset = offset | (((i >> 3) & 0x07) << 11); /* cell y offset (0-7) */
gfx.lut_offset[i] = offset | 0x8000;
}
/* $23C000-$23DFFF corresponds to $21C000-$21DFFF */
for (i=0x7000; i<0x7800; i++)
{
offset = (i & 0x07) << 8; /* cell vline (0-7) */
offset = offset | (((i >> 5) & 0x3f) << 2); /* cell x offset (0-63) */
offset = offset | (((i >> 3) & 0x03) << 11); /* cell y offset (0-3) */
gfx.lut_offset[i] = offset | 0xc000;
}
/* $23E000-$23FFFF corresponds to $21E000-$21FFFF */
for (i=0x7800; i<0x8000; i++)
{
offset = (i & 0x07) << 8; /* cell vline (0-7) */
offset = offset | (((i >> 5) & 0x3f) << 2); /* cell x offset (0-63) */
offset = offset | (((i >> 3) & 0x03) << 11); /* cell y offset (0-3) */
gfx.lut_offset[i] = offset | 0xe000;
}
/* Initialize priority modes lookup table */
for (i=0; i<0x100; i++)
{
for (j=0; j<0x100; j++)
{
/* normal */
gfx.lut_prio[0][i][j] = j;
/* underwrite */
gfx.lut_prio[1][i][j] = ((i & 0x0f) ? (i & 0x0f) : (j & 0x0f)) | ((i & 0xf0) ? (i & 0xf0) : (j & 0xf0));
/* overwrite */
gfx.lut_prio[2][i][j] = ((j & 0x0f) ? (j & 0x0f) : (i & 0x0f)) | ((j & 0xf0) ? (j & 0xf0) : (i & 0xf0));
/* invalid */
gfx.lut_prio[3][i][j] = i;
}
}
/* Initialize cell lookup table */
/* table entry = yyxxshrr (8 bits) */
/* with: yy = cell row (0-3) */
/* xx = cell column (0-3) */
/* s = stamp size (0=16x16, 1=32x32) */
/* hrr = HFLIP & ROTATION bits */
for (i=0; i<0x100; i++)
{
/* one stamp = 2x2 cells (16x16) or 4x4 cells (32x32) */
mask = (i & 8) ? 3 : 1;
row = (i >> 6) & mask;
col = (i >> 4) & mask;
if (i & 4) { col = col ^ mask; } /* HFLIP (always first) */
if (i & 2) { col = col ^ mask; row = row ^ mask; } /* ROLL1 */
if (i & 1) { temp = col; col = row ^ mask; row = temp; } /* ROLL0 */
/* cell offset (0-3 or 0-15) */
gfx.lut_cell[i] = row + col * (mask + 1);
}
/* Initialize pixel lookup table */
/* table entry = yyyxxxhrr (9 bits) */
/* with: yyy = pixel row (0-7) */
/* xxx = pixel column (0-7) */
/* hrr = HFLIP & ROTATION bits */
for (i=0; i<0x200; i++)
{
/* one cell = 8x8 pixels */
row = (i >> 6) & 7;
col = (i >> 3) & 7;
if (i & 4) { col = col ^ 7; } /* HFLIP (always first) */
if (i & 2) { col = col ^ 7; row = row ^ 7; } /* ROLL1 */
if (i & 1) { temp = col; col = row ^ 7; row = temp; } /* ROLL0 */
/* pixel offset (0-63) */
gfx.lut_pixel[i] = col + row * 8;
}
}
void gfx_reset(void)
{
/* Reset cycle counter */
gfx.cycles = 0;
}
int gfx_context_save(uint8 *state)
{
uint32 tmp32;
int bufferptr = 0;
save_param(&gfx.cycles, sizeof(gfx.cycles));
save_param(&gfx.cyclesPerLine, sizeof(gfx.cyclesPerLine));
save_param(&gfx.dotMask, sizeof(gfx.dotMask));
save_param(&gfx.stampShift, sizeof(gfx.stampShift));
save_param(&gfx.mapShift, sizeof(gfx.mapShift));
save_param(&gfx.bufferOffset, sizeof(gfx.bufferOffset));
save_param(&gfx.bufferStart, sizeof(gfx.bufferStart));
tmp32 = (uint8 *)(gfx.tracePtr) - scd.word_ram_2M;
save_param(&tmp32, 4);
tmp32 = (uint8 *)(gfx.mapPtr) - scd.word_ram_2M;
save_param(&tmp32, 4);
return bufferptr;
}
int gfx_context_load(uint8 *state)
{
uint32 tmp32;
int bufferptr = 0;
load_param(&gfx.cycles, sizeof(gfx.cycles));
load_param(&gfx.cyclesPerLine, sizeof(gfx.cyclesPerLine));
load_param(&gfx.dotMask, sizeof(gfx.dotMask));
load_param(&gfx.stampShift, sizeof(gfx.stampShift));
load_param(&gfx.mapShift, sizeof(gfx.mapShift));
load_param(&gfx.bufferOffset, sizeof(gfx.bufferOffset));
load_param(&gfx.bufferStart, sizeof(gfx.bufferStart));
load_param(&tmp32, 4);
gfx.tracePtr = (uint16 *)(scd.word_ram_2M + tmp32);
load_param(&tmp32, 4);
gfx.mapPtr = (uint16 *)(scd.word_ram_2M + tmp32);
return bufferptr;
}
INLINE void gfx_render(uint32 bufferIndex, uint32 width)
{
uint8 pixel_in, pixel_out;
uint16 stamp_data;
uint32 stamp_index;
/* pixel map start position for current line (13.3 format converted to 13.11) */
uint32 xpos = *gfx.tracePtr++ << 8;
uint32 ypos = *gfx.tracePtr++ << 8;
/* pixel map offset values for current line (5.11 format) */
uint32 xoffset = (int16) *gfx.tracePtr++;
uint32 yoffset = (int16) *gfx.tracePtr++;
/* process all dots */
while (width--)
{
/* check if stamp map is repeated */
if (scd.regs[0x58>>1].byte.l & 0x01)
{
/* stamp map range */
xpos &= gfx.dotMask;
ypos &= gfx.dotMask;
}
else
{
/* 24-bit range */
xpos &= 0xffffff;
ypos &= 0xffffff;
}
/* check if pixel is outside stamp map */
if ((xpos | ypos) & ~gfx.dotMask)
{
/* force pixel output to 0 */
pixel_out = 0x00;
}
else
{
/* read stamp map table data */
stamp_data = gfx.mapPtr[(xpos >> gfx.stampShift) | ((ypos >> gfx.stampShift) << gfx.mapShift)];
/* stamp generator base index */
/* sss ssssssss ccyyyxxx (16x16) or sss sssssscc ccyyyxxx (32x32) */
/* with: s = stamp number (1 stamp = 16x16 or 32x32 pixels) */
/* c = cell offset (0-3 for 16x16, 0-15 for 32x32) */
/* yyy = line offset (0-7) */
/* xxx = pixel offset (0-7) */
stamp_index = (stamp_data & 0x7ff) << 8;
if (stamp_index)
{
/* extract HFLIP & ROTATION bits */
stamp_data = (stamp_data >> 13) & 7;
/* cell offset (0-3 or 0-15) */
/* table entry = yyxxshrr (8 bits) */
/* with: yy = cell row (0-3) = (ypos >> (11 + 3)) & 3 */
/* xx = cell column (0-3) = (xpos >> (11 + 3)) & 3 */
/* s = stamp size (0=16x16, 1=32x32) */
/* hrr = HFLIP & ROTATION bits */
stamp_index |= gfx.lut_cell[stamp_data | ((scd.regs[0x58>>1].byte.l & 0x02) << 2 ) | ((ypos >> 8) & 0xc0) | ((xpos >> 10) & 0x30)] << 6;
/* pixel offset (0-63) */
/* table entry = yyyxxxhrr (9 bits) */
/* with: yyy = pixel row (0-7) = (ypos >> 11) & 7 */
/* xxx = pixel column (0-7) = (xpos >> 11) & 7 */
/* hrr = HFLIP & ROTATION bits */
stamp_index |= gfx.lut_pixel[stamp_data | ((xpos >> 8) & 0x38) | ((ypos >> 5) & 0x1c0)];
/* read pixel pair (2 pixels/byte) */
pixel_out = READ_BYTE(scd.word_ram_2M, stamp_index >> 1);
/* extract left or rigth pixel */
if (stamp_index & 1)
{
pixel_out &= 0x0f;
}
else
{
pixel_out >>= 4;
}
}
else
{
/* stamp 0 is not used: force pixel output to 0 */
pixel_out = 0x00;
}
}
/* read out paired pixel data */
pixel_in = READ_BYTE(scd.word_ram_2M, bufferIndex >> 1);
/* update left or rigth pixel */
if (bufferIndex & 1)
{
pixel_out |= (pixel_in & 0xf0);
}
else
{
pixel_out = (pixel_out << 4) | (pixel_in & 0x0f);
}
/* priority mode write */
pixel_out = gfx.lut_prio[(scd.regs[0x02>>1].w >> 3) & 0x03][pixel_in][pixel_out];
/* write data to image buffer */
WRITE_BYTE(scd.word_ram_2M, bufferIndex >> 1, pixel_out);
/* check current pixel position */
if ((bufferIndex & 7) != 7)
{
/* next pixel */
bufferIndex++;
}
else
{
/* next cell: increment image buffer offset by one column (minus 7 pixels) */
bufferIndex += gfx.bufferOffset;
}
/* increment pixel position */
xpos += xoffset;
ypos += yoffset;
}
}
void gfx_start(unsigned int base, int cycles)
{
/* make sure 2M mode is enabled */
if (!(scd.regs[0x02>>1].byte.l & 0x04))
{
uint32 mask;
/* trace vector pointer */
gfx.tracePtr = (uint16 *)(scd.word_ram_2M + ((base << 2) & 0x3fff8));
/* stamps & stamp map size */
switch ((scd.regs[0x58>>1].byte.l >> 1) & 0x03)
{
case 0:
gfx.dotMask = 0x07ffff; /* 256x256 dots/map */
gfx.stampShift = 11 + 4; /* 16x16 dots/stamps */
gfx.mapShift = 4; /* 16x16 stamps/map */
mask = 0x3fe00; /* 512 bytes/table */
break;
case 1:
gfx.dotMask = 0x07ffff; /* 256x256 dots/map */
gfx.stampShift = 11 + 5; /* 32x32 dots/stamps */
gfx.mapShift = 3; /* 8x8 stamps/map */
mask = 0x3ff80; /* 128 bytes/table */
break;
case 2:
gfx.dotMask = 0x7fffff; /* 4096*4096 dots/map */
gfx.stampShift = 11 + 4; /* 16x16 dots/stamps */
gfx.mapShift = 8; /* 256x256 stamps/map */
mask = 0x20000; /* 131072 bytes/table */
break;
case 3:
gfx.dotMask = 0x7fffff; /* 4096*4096 dots/map */
gfx.stampShift = 11 + 5; /* 32x32 dots/stamps */
gfx.mapShift = 7; /* 128x128 stamps/map */
mask = 0x38000; /* 32768 bytes/table */
break;
}
/* stamp map table base address */
gfx.mapPtr = (uint16 *)(scd.word_ram_2M + ((scd.regs[0x5a>>1].w << 2) & mask));
/* image buffer column offset (64 pixels/cell, minus 7 pixels to restart at cell beginning) */
gfx.bufferOffset = (((scd.regs[0x5c>>1].byte.l & 0x1f) + 1) << 6) - 7;
/* image buffer start index in dot units (2 pixels/byte) */
gfx.bufferStart = (scd.regs[0x5e>>1].w << 3) & 0x7ffc0;
/* add image buffer horizontal dot offset */
gfx.bufferStart += (scd.regs[0x60>>1].byte.l & 0x3f);
/* reset GFX chip cycle counter */
gfx.cycles = cycles;
/* update GFX chip timings (see AC3:Thunderhawk / Thunderstrike) */
gfx.cyclesPerLine = 4 * 5 * scd.regs[0x62>>1].w;
/* start graphics operation */
scd.regs[0x58>>1].byte.h = 0x80;
}
}
void gfx_update(int cycles)
{
/* synchronize GFX chip with SUB-CPU */
cycles -= gfx.cycles;
/* make sure SUB-CPU is ahead */
if (cycles > 0)
{
/* number of lines to process */
unsigned int lines = (cycles + gfx.cyclesPerLine - 1) / gfx.cyclesPerLine;
/* check against remaining lines */
if (lines < scd.regs[0x64>>1].byte.l)
{
/* update Vdot remaining size */
scd.regs[0x64>>1].byte.l -= lines;
/* increment cycle counter */
gfx.cycles += lines * gfx.cyclesPerLine;
}
else
{
/* process remaining lines */
lines = scd.regs[0x64>>1].byte.l;
/* clear Vdot remaining size */
scd.regs[0x64>>1].byte.l = 0;
/* end of graphics operation */
scd.regs[0x58>>1].byte.h = 0;
/* SUB-CPU idle on register $58 polling ? */
if (s68k.stopped & (1<<0x08))
{
/* sync SUB-CPU with GFX chip */
s68k.cycles = scd.cycles;
/* restart SUB-CPU */
s68k.stopped = 0;
#ifdef LOG_SCD
error("s68k started from %d cycles\n", s68k.cycles);
#endif
}
/* level 1 interrupt enabled ? */
if (scd.regs[0x32>>1].byte.l & 0x02)
{
/* trigger level 1 interrupt */
scd.pending |= (1 << 1);
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
}
/* render lines */
while (lines--)
{
/* process dots to image buffer */
gfx_render(gfx.bufferStart, scd.regs[0x62>>1].w);
/* increment image buffer start index for next line (8 pixels/line) */
gfx.bufferStart += 8;
}
}
}

View File

@ -0,0 +1,116 @@
/***************************************************************************************
* Genesis Plus
* CD graphics processor
*
* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _CD_GFX_
#define _CD_GFX_
#define gfx scd.gfx_hw
typedef struct
{
uint32 cycles; /* current cycles count for graphics operation */
uint32 cyclesPerLine; /* current graphics operation timings */
uint32 dotMask; /* stamp map size mask */
uint16 *tracePtr; /* trace vector pointer */
uint16 *mapPtr; /* stamp map table base address */
uint8 stampShift; /* stamp pixel shift value (related to stamp size) */
uint8 mapShift; /* stamp map table shift value (related to stamp map size) */
uint16 bufferOffset; /* image buffer column offset */
uint32 bufferStart; /* image buffer start index */
uint16 lut_offset[0x8000]; /* Cell Image -> WORD-RAM offset lookup table (1M Mode) */
uint8 lut_prio[4][0x100][0x100]; /* WORD-RAM data writes priority lookup table */
uint8 lut_pixel[0x200]; /* Graphics operation dot offset lookup table */
uint8 lut_cell[0x100]; /* Graphics operation stamp offset lookup table */
} gfx_t;
/***************************************************************/
/* WORD-RAM DMA interfaces (1M & 2M modes) */
/***************************************************************/
extern void word_ram_0_dma_w(unsigned int words);
extern void word_ram_1_dma_w(unsigned int words);
extern void word_ram_2M_dma_w(unsigned int words);
/***************************************************************/
/* WORD-RAM 0 & 1 CPU interfaces (1M mode) */
/***************************************************************/
extern unsigned int word_ram_0_read16(unsigned int address);
extern unsigned int word_ram_1_read16(unsigned int address);
extern void word_ram_0_write16(unsigned int address, unsigned int data);
extern void word_ram_1_write16(unsigned int address, unsigned int data);
extern unsigned int word_ram_0_read8(unsigned int address);
extern unsigned int word_ram_1_read8(unsigned int address);
extern void word_ram_0_write8(unsigned int address, unsigned int data);
extern void word_ram_1_write8(unsigned int address, unsigned int data);
/***************************************************************/
/* WORD-RAM 0 & 1 DOT image SUB-CPU interface (1M mode) */
/***************************************************************/
extern unsigned int dot_ram_0_read16(unsigned int address);
extern unsigned int dot_ram_1_read16(unsigned int address);
extern void dot_ram_0_write16(unsigned int address, unsigned int data);
extern void dot_ram_1_write16(unsigned int address, unsigned int data);
extern unsigned int dot_ram_0_read8(unsigned int address);
extern unsigned int dot_ram_1_read8(unsigned int address);
extern void dot_ram_0_write8(unsigned int address, unsigned int data);
extern void dot_ram_1_write8(unsigned int address, unsigned int data);
/***************************************************************/
/* WORD-RAM 0 & 1 CELL image MAIN-CPU interface (1M mode) */
/***************************************************************/
extern unsigned int cell_ram_0_read16(unsigned int address);
extern unsigned int cell_ram_1_read16(unsigned int address);
extern void cell_ram_0_write16(unsigned int address, unsigned int data);
extern void cell_ram_1_write16(unsigned int address, unsigned int data);
extern unsigned int cell_ram_0_read8(unsigned int address);
extern unsigned int cell_ram_1_read8(unsigned int address);
extern void cell_ram_0_write8(unsigned int address, unsigned int data);
extern void cell_ram_1_write8(unsigned int address, unsigned int data);
/***************************************************************/
/* Rotation / Scaling operation (2M mode) */
/***************************************************************/
extern void gfx_init(void);
extern void gfx_reset(void);
extern int gfx_context_save(uint8 *state);
extern int gfx_context_load(uint8 *state);
extern void gfx_start(unsigned int base, int cycles);
extern void gfx_update(int cycles);
#endif

View File

@ -0,0 +1,442 @@
/***************************************************************************************
* Genesis Plus
* PCM sound chip (315-5476A) (RF5C164 compatible)
*
* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
#define PCM_SCYCLES_RATIO (384 * 4)
#define pcm scd.pcm_hw
static blip_t* blip[2];
void pcm_init(blip_t* left, blip_t* right)
{
/* number of SCD master clocks run per second */
double mclk = snd.frame_rate ? (SCYCLES_PER_LINE * (vdp_pal ? 313 : 262) * snd.frame_rate) : SCD_CLOCK;
/* PCM chips is running at original rate and is synchronized with SUB-CPU */
/* Chip output is resampled to desired rate using Blip Buffer. */
blip[0] = left;
blip[1] = right;
blip_set_rates(left, mclk / PCM_SCYCLES_RATIO, snd.sample_rate);
blip_set_rates(right, mclk / PCM_SCYCLES_RATIO, snd.sample_rate);
}
void pcm_reset(void)
{
/* reset chip & clear external RAM */
memset(&pcm, 0, sizeof(pcm_t));
/* reset default bank */
pcm.bank = pcm.ram;
/* reset channels stereo panning */
pcm.chan[0].pan = 0xff;
pcm.chan[1].pan = 0xff;
pcm.chan[2].pan = 0xff;
pcm.chan[3].pan = 0xff;
pcm.chan[4].pan = 0xff;
pcm.chan[5].pan = 0xff;
pcm.chan[6].pan = 0xff;
pcm.chan[7].pan = 0xff;
/* reset master clocks counter */
pcm.cycles = 0;
/* clear blip buffers */
blip_clear(blip[0]);
blip_clear(blip[1]);
}
int pcm_context_save(uint8 *state)
{
uint8 tmp8;
int bufferptr = 0;
tmp8 = (pcm.bank - pcm.ram) >> 12;
save_param(pcm.chan, sizeof(pcm.chan));
save_param(pcm.out, sizeof(pcm.out));
save_param(&tmp8, 1);
save_param(&pcm.enabled, sizeof(pcm.enabled));
save_param(&pcm.status, sizeof(pcm.status));
save_param(&pcm.index, sizeof(pcm.index));
save_param(pcm.ram, sizeof(pcm.ram));
return bufferptr;
}
int pcm_context_load(uint8 *state)
{
uint8 tmp8;
int bufferptr = 0;
load_param(pcm.chan, sizeof(pcm.chan));
load_param(pcm.out, sizeof(pcm.out));
load_param(&tmp8, 1);
pcm.bank = &pcm.ram[(tmp8 & 0x0f) << 12];
load_param(&pcm.enabled, sizeof(pcm.enabled));
load_param(&pcm.status, sizeof(pcm.status));
load_param(&pcm.index, sizeof(pcm.index));
load_param(pcm.ram, sizeof(pcm.ram));
return bufferptr;
}
void pcm_run(unsigned int length)
{
#ifdef LOG_PCM
error("[%d][%d]run %d PCM samples (from %d)\n", v_counter, s68k.cycles, length, pcm.cycles);
#endif
/* check if PCM chip is running */
if (pcm.enabled)
{
int i, j, l, r;
/* generate PCM samples */
for (i=0; i<length; i++)
{
/* clear output */
l = r = 0;
/* run eight PCM channels */
for (j=0; j<8; j++)
{
/* check if channel is enabled */
if (pcm.status & (1 << j))
{
/* read from current WAVE RAM address */
short data = pcm.ram[(pcm.chan[j].addr >> 11) & 0xffff];
/* loop data ? */
if (data == 0xff)
{
/* reset WAVE RAM address */
pcm.chan[j].addr = pcm.chan[j].ls.w << 11;
/* read again from WAVE RAM address */
data = pcm.ram[pcm.chan[j].ls.w];
}
else
{
/* increment WAVE RAM address */
pcm.chan[j].addr += pcm.chan[j].fd.w;
}
/* infinite loop should not output any data */
if (data != 0xff)
{
/* check sign bit (output centered around 0) */
if (data & 0x80)
{
/* PCM data is positive */
data = data & 0x7f;
}
else
{
/* PCM data is negative */
data = -(data & 0x7f);
}
/* multiply PCM data with ENV & stereo PAN data then add to L/R outputs (14.5 fixed point) */
l += ((data * pcm.chan[j].env * (pcm.chan[j].pan & 0x0F)) >> 5);
r += ((data * pcm.chan[j].env * (pcm.chan[j].pan >> 4)) >> 5);
}
}
}
/* limiter */
if (l < -32768) l = -32768;
else if (l > 32767) l = 32767;
if (r < -32768) r = -32768;
else if (r > 32767) r = 32767;
/* check if PCM left output changed */
if (pcm.out[0] != l)
{
blip_add_delta_fast(blip[0], i, l-pcm.out[0]);
pcm.out[0] = l;
}
/* check if PCM right output changed */
if (pcm.out[1] != r)
{
blip_add_delta_fast(blip[1], i, r-pcm.out[1]);
pcm.out[1] = r;
}
}
}
else
{
/* check if PCM left output changed */
if (pcm.out[0])
{
blip_add_delta_fast(blip[0], 0, -pcm.out[0]);
pcm.out[0] = 0;
}
/* check if PCM right output changed */
if (pcm.out[1])
{
blip_add_delta_fast(blip[1], 0, -pcm.out[1]);
pcm.out[1] = 0;
}
}
/* end of blip buffer frame */
blip_end_frame(blip[0], length);
blip_end_frame(blip[1], length);
/* update PCM master clock counter */
pcm.cycles += length * PCM_SCYCLES_RATIO;
}
void pcm_update(unsigned int samples)
{
/* get number of internal clocks (samples) needed */
unsigned int clocks = blip_clocks_needed(blip[0], samples);
/* run PCM chip */
if (clocks > 0)
{
pcm_run(clocks);
}
/* reset PCM master clocks counter */
pcm.cycles = 0;
}
void pcm_write(unsigned int address, unsigned char data)
{
/* synchronize PCM chip with SUB-CPU */
int clocks = s68k.cycles - pcm.cycles;
if (clocks > 0)
{
/* number of internal clocks (samples) to run */
clocks = (clocks + PCM_SCYCLES_RATIO - 1) / PCM_SCYCLES_RATIO;
pcm_run(clocks);
}
#ifdef LOG_PCM
error("[%d][%d]PCM write %x -> 0x%02x (%X)\n", v_counter, s68k.cycles, address, data, s68k.pc);
#endif
/* external RAM is mapped to $1000-$1FFF */
if (address >= 0x1000)
{
/* 4K bank access */
pcm.bank[address & 0xfff] = data;
return;
}
/* internal area si mapped to $0000-$0FFF */
switch (address)
{
case 0x00: /* ENV register */
{
/* update channel ENV multiplier */
pcm.chan[pcm.index].env = data;
return;
}
case 0x01: /* PAN register */
{
/* update channel stereo panning value */
pcm.chan[pcm.index].pan = data;
return;
}
case 0x02: /* FD register (LSB) */
{
/* update channel WAVE RAM address increment LSB */
pcm.chan[pcm.index].fd.byte.l = data;
return;
}
case 0x03: /* FD register (MSB) */
{
/* update channel WAVE RAM address increment MSB */
pcm.chan[pcm.index].fd.byte.h = data;
return;
}
case 0x04: /* LS register (LSB) */
{
/* update channel WAVE RAM loop address LSB */
pcm.chan[pcm.index].ls.byte.l = data;
return;
}
case 0x05: /* LS register (MSB) */
{
/* update channel WAVE RAM loop address MSB */
pcm.chan[pcm.index].ls.byte.h = data;
return;
}
case 0x06: /* ST register */
{
/* update channel WAVE RAM start address (16.11 fixed point) */
pcm.chan[pcm.index].st = data << (8 + 11);
/* reload WAVE RAM address if channel is OFF */
if (!(pcm.status & (1 << pcm.index)))
{
pcm.chan[pcm.index].addr = pcm.chan[pcm.index].st;
}
return;
}
case 0x07: /* CTRL register */
{
if (data & 0x40)
{
/* channel selection (0-7) */
pcm.index = data & 0x07;
}
else
{
/* external RAM bank selection (16 x 4K) */
pcm.bank = &pcm.ram[(data & 0x0f) << 12];
}
/* update PCM chip status (bit 7) */
pcm.enabled = data & 0x80;
return;
}
case 0x08: /* ON/OFF register */
{
/* update PCM channels status */
pcm.status = ~data;
/* reload WAVE RAM address pointers when channels are OFF */
if (data & 0x01) pcm.chan[0].addr = pcm.chan[0].st;
if (data & 0x02) pcm.chan[1].addr = pcm.chan[1].st;
if (data & 0x04) pcm.chan[2].addr = pcm.chan[2].st;
if (data & 0x08) pcm.chan[3].addr = pcm.chan[3].st;
if (data & 0x10) pcm.chan[4].addr = pcm.chan[4].st;
if (data & 0x20) pcm.chan[5].addr = pcm.chan[5].st;
if (data & 0x40) pcm.chan[6].addr = pcm.chan[6].st;
if (data & 0x80) pcm.chan[7].addr = pcm.chan[7].st;
return;
}
default:
{
/* illegal access */
return;
}
}
}
unsigned char pcm_read(unsigned int address)
{
/* synchronize PCM chip with SUB-CPU */
int clocks = s68k.cycles - pcm.cycles;
if (clocks > 0)
{
/* number of internal clocks (samples) to run */
clocks = (clocks + PCM_SCYCLES_RATIO - 1) / PCM_SCYCLES_RATIO;
pcm_run(clocks);
}
#ifdef LOG_PCM
error("[%d][%d]PCM read (%X)\n", v_counter, s68k.cycles, address, s68k.pc);
#endif
/* external RAM (TODO: verify if possible to read, some docs claim it's not !) */
if (address >= 0x1000)
{
/* 4K bank access */
return pcm.bank[address & 0xfff];
}
/* read WAVE RAM address pointers */
if ((address >= 0x10) && (address < 0x20))
{
int index = (address >> 1) & 0x07;
if (address & 1)
{
return (pcm.chan[index].addr >> (11 + 8)) & 0xff;
}
else
{
return (pcm.chan[index].addr >> 11) & 0xff;
}
}
/* illegal access */
return 0xff;
}
void pcm_ram_dma_w(unsigned int words)
{
uint16 data;
/* CDC buffer source address */
uint16 src_index = cdc.dac.w & 0x3ffe;
/* PCM-RAM destination address*/
uint16 dst_index = (scd.regs[0x0a>>1].w << 2) & 0xffe;
/* update DMA destination address */
scd.regs[0x0a>>1].w += (words >> 1);
/* update DMA source address */
cdc.dac.w += (words << 1);
/* DMA transfer */
while (words--)
{
/* read 16-bit word from CDC buffer */
data = *(uint16 *)(cdc.ram + src_index);
/* write 16-bit word to PCM RAM (endianness does not matter since PCM RAM is always accessed as byte)*/
*(uint16 *)(pcm.bank + dst_index) = data ;
/* increment CDC buffer source address */
src_index = (src_index + 2) & 0x3ffe;
/* increment PCM-RAM destination address */
dst_index = (dst_index + 2) & 0xffe;
}
}

View File

@ -0,0 +1,77 @@
/***************************************************************************************
* Genesis Plus
* PCM sound chip (315-5476A) (RF5C164 compatible)
*
* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _CD_PCM_
#define _CD_PCM_
#include "blip_buf.h"
/* PCM channel */
typedef struct
{
uint32 addr; /* current Wave RAM address (16.11 fixed point) */
uint32 st; /* Wave RAM start address (16.11 fixed point) */
reg16_t ls; /* Wave RAM loop address ($0000-$ffff) */
reg16_t fd; /* Wave RAM address increment (5.11 fixed point) */
uint8 env; /* enveloppe multiplier */
uint8 pan; /* stereo panning */
} chan_t;
/* PCM sound chip */
typedef struct
{
chan_t chan[8]; /* PCM channels 1-8 */
int16 out[2]; /* previous PCM stereo output */
uint8 *bank; /* external RAM bank pointer */
uint8 enabled; /* PCM chip ON/OFF status */
uint8 status; /* channels ON/OFF status */
uint8 index; /* current channel index */
uint8 ram[0x10000]; /* 64k external RAM */
uint32 cycles;
} pcm_t;
/* Function prototypes */
extern void pcm_init(blip_t* left, blip_t* right);
extern void pcm_reset(void);
extern int pcm_context_save(uint8 *state);
extern int pcm_context_load(uint8 *state);
extern void pcm_update(unsigned int samples);
extern void pcm_write(unsigned int address, unsigned char data);
extern unsigned char pcm_read(unsigned int address);
extern void pcm_ram_dma_w(unsigned int words);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,90 @@
/***************************************************************************************
* Genesis Plus
* Mega CD / Sega CD hardware
*
* Copyright (C) 2012-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _HW_SCD_
#define _HW_SCD_
#include "cdd.h"
#include "cdc.h"
#include "gfx.h"
#include "pcm.h"
#include "cd_cart.h"
#define scd ext.cd_hw
/* 5000000 SCD clocks/s = ~3184 clocks/line with a Master Clock of 53.693175 MHz */
/* This would be slightly (~30 clocks) more on PAL systems because of the slower */
/* Master Clock (53.203424 MHz) but not enough to really care about since clocks */
/* are not running in sync anyway. */
#define SCD_CLOCK 50000000
#define SCYCLES_PER_LINE 3184
/* Timer & Stopwatch clocks divider */
#define TIMERS_SCYCLES_RATIO (384 * 4)
/* CD hardware */
typedef struct
{
cd_cart_t cartridge; /* ROM/RAM Cartridge */
uint8 bootrom[0x20000]; /* 128K internal BOOT ROM */
uint8 prg_ram[0x80000]; /* 512K PRG-RAM */
uint8 word_ram[2][0x20000]; /* 2 x 128K Word RAM (1M mode) */
uint8 word_ram_2M[0x40000]; /* 256K Word RAM (2M mode) */
uint8 bram[0x2000]; /* 8K Backup RAM */
reg16_t regs[0x100]; /* 256 x 16-bit ASIC registers */
uint32 cycles; /* Master clock counter */
int32 stopwatch; /* Stopwatch counter */
int32 timer; /* Timer counter */
uint8 pending; /* Pending interrupts */
uint8 dmna; /* Pending DMNA write status */
gfx_t gfx_hw; /* Graphics processor */
cdc_t cdc_hw; /* CD data controller */
cdd_t cdd_hw; /* CD drive processor */
pcm_t pcm_hw; /* PCM chip */
} cd_hw_t;
/* Function prototypes */
extern void scd_init(void);
extern void scd_reset(int hard);
extern void scd_update(unsigned int cycles);
extern void scd_end_frame(unsigned int cycles);
extern int scd_context_load(uint8 *state);
extern int scd_context_save(uint8 *state);
extern int scd_68k_irq_ack(int level);
extern void prg_ram_dma_w(unsigned int words);
#endif

540
genplus-gx32/core/genesis.c Normal file
View File

@ -0,0 +1,540 @@
/***************************************************************************************
* Genesis Plus
* Internal Hardware & Bus controllers
*
* Support for SG-1000, Mark-III, Master System, Game Gear, Mega Drive & Mega CD hardware
*
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code)
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
external_t ext; /* External Hardware (Cartridge, CD unit, ...) */
uint8 boot_rom[0x800]; /* Genesis BOOT ROM */
uint8 work_ram[0x10000]; /* 68K RAM */
uint8 zram[0x2000]; /* Z80 RAM */
uint32 zbank; /* Z80 bank window address */
uint8 zstate; /* Z80 bus state (d0 = BUSACK, d1 = /RESET) */
uint8 pico_current; /* PICO current page */
uint8 tmss[4]; /* TMSS security register */
/*--------------------------------------------------------------------------*/
/* Init, reset, shutdown functions */
/*--------------------------------------------------------------------------*/
void gen_init(void)
{
int i;
/* initialize Z80 */
z80_init(0,z80_irq_callback);
/* 8-bit / 16-bit modes */
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{
/* initialize main 68k */
m68k_init();
m68k.aerr_enabled = config.addr_error;
/* initialize main 68k memory map */
/* $800000-$DFFFFF : illegal access by default */
for (i=0x80; i<0xe0; i++)
{
m68k.memory_map[i].base = work_ram; /* for VDP DMA */
m68k.memory_map[i].read8 = m68k_lockup_r_8;
m68k.memory_map[i].read16 = m68k_lockup_r_16;
m68k.memory_map[i].write8 = m68k_lockup_w_8;
m68k.memory_map[i].write16 = m68k_lockup_w_16;
zbank_memory_map[i].read = zbank_lockup_r;
zbank_memory_map[i].write = zbank_lockup_w;
}
/* $C0xxxx, $C8xxxx, $D0xxxx, $D8xxxx : VDP ports */
for (i=0xc0; i<0xe0; i+=8)
{
m68k.memory_map[i].read8 = vdp_read_byte;
m68k.memory_map[i].read16 = vdp_read_word;
m68k.memory_map[i].write8 = vdp_write_byte;
m68k.memory_map[i].write16 = vdp_write_word;
zbank_memory_map[i].read = zbank_read_vdp;
zbank_memory_map[i].write = zbank_write_vdp;
}
/* $E00000-$FFFFFF : Work RAM (64k) */
for (i=0xe0; i<0x100; i++)
{
m68k.memory_map[i].base = work_ram;
m68k.memory_map[i].read8 = NULL;
m68k.memory_map[i].read16 = NULL;
m68k.memory_map[i].write8 = NULL;
m68k.memory_map[i].write16 = NULL;
/* Z80 can ONLY write to 68k RAM, not read it */
zbank_memory_map[i].read = zbank_unused_r;
zbank_memory_map[i].write = NULL;
}
if (system_hw == SYSTEM_PICO)
{
/* additional registers mapped to $800000-$80FFFF */
m68k.memory_map[0x80].read8 = pico_read_byte;
m68k.memory_map[0x80].read16 = pico_read_word;
m68k.memory_map[0x80].write8 = m68k_unused_8_w;
m68k.memory_map[0x80].write16 = m68k_unused_16_w;
/* there is no I/O area (Notaz) */
m68k.memory_map[0xa1].read8 = m68k_read_bus_8;
m68k.memory_map[0xa1].read16 = m68k_read_bus_16;
m68k.memory_map[0xa1].write8 = m68k_unused_8_w;
m68k.memory_map[0xa1].write16 = m68k_unused_16_w;
/* initialize page index (closed) */
pico_current = 0;
}
else
{
/* $A10000-$A1FFFF : I/O & Control registers */
m68k.memory_map[0xa1].read8 = ctrl_io_read_byte;
m68k.memory_map[0xa1].read16 = ctrl_io_read_word;
m68k.memory_map[0xa1].write8 = ctrl_io_write_byte;
m68k.memory_map[0xa1].write16 = ctrl_io_write_word;
zbank_memory_map[0xa1].read = zbank_read_ctrl_io;
zbank_memory_map[0xa1].write = zbank_write_ctrl_io;
/* initialize Z80 memory map */
/* $0000-$3FFF is mapped to Z80 RAM (8K mirrored) */
/* $4000-$FFFF is mapped to hardware but Z80 PC should never point there */
for (i=0; i<64; i++)
{
z80_readmap[i] = &zram[(i & 7) << 10];
}
/* initialize Z80 memory handlers */
z80_writemem = z80_memory_w;
z80_readmem = z80_memory_r;
/* initialize Z80 port handlers */
z80_writeport = z80_unused_port_w;
z80_readport = z80_unused_port_r;
}
/* $000000-$7FFFFF : external hardware area */
if (system_hw == SYSTEM_MCD)
{
/* initialize SUB-CPU */
s68k_init();
/* initialize CD hardware */
scd_init();
}
else
{
/* Cartridge hardware */
md_cart_init();
}
}
else
{
/* initialize cartridge hardware & Z80 memory handlers */
sms_cart_init();
/* initialize Z80 ports handlers */
switch (system_hw)
{
/* Master System compatibility mode */
case SYSTEM_PBC:
{
z80_writeport = z80_md_port_w;
z80_readport = z80_md_port_r;
break;
}
/* Game Gear hardware */
case SYSTEM_GG:
case SYSTEM_GGMS:
{
/* initialize cartridge hardware & Z80 memory handlers */
sms_cart_init();
/* initialize Z80 ports handlers */
z80_writeport = z80_gg_port_w;
z80_readport = z80_gg_port_r;
break;
}
/* Master SYstem hardware */
case SYSTEM_SMS:
case SYSTEM_SMS2:
{
z80_writeport = z80_ms_port_w;
z80_readport = z80_ms_port_r;
break;
}
/* Mark-III hardware */
case SYSTEM_MARKIII:
{
z80_writeport = z80_m3_port_w;
z80_readport = z80_m3_port_r;
break;
}
/* SG-1000 hardware */
case SYSTEM_SG:
{
z80_writeport = z80_sg_port_w;
z80_readport = z80_sg_port_r;
break;
}
}
}
}
void gen_reset(int hard_reset)
{
/* System Reset */
if (hard_reset)
{
/* clear RAM (TODO: use random bit patterns for all systems, like on real hardware) */
memset(work_ram, 0x00, sizeof (work_ram));
memset(zram, 0x00, sizeof (zram));
}
else
{
/* reset YM2612 (on hard reset, this is done by sound_reset) */
fm_reset(0);
}
/* 68k & Z80 could be anywhere in VDP frame (Bonkers, Eternal Champions, X-Men 2) */
m68k.cycles = Z80.cycles = 0; //(uint32)((MCYCLES_PER_LINE * lines_per_frame) * ((double)rand() / (double)RAND_MAX));
/* 68k cycles should be a multiple of 7 */
m68k.cycles = (m68k.cycles / 7) * 7;
/* Z80 cycles should be a multiple of 15 */
Z80.cycles = (Z80.cycles / 15) * 15;
/* 8-bit / 16-bit modes */
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{
if (system_hw == SYSTEM_MCD)
{
/* FRES is only asserted on Power ON */
if (hard_reset)
{
/* reset CD hardware */
scd_reset(1);
}
}
/* reset MD cartridge hardware */
md_cart_reset(hard_reset);
/* Z80 bus is released & Z80 is reseted */
m68k.memory_map[0xa0].read8 = m68k_read_bus_8;
m68k.memory_map[0xa0].read16 = m68k_read_bus_16;
m68k.memory_map[0xa0].write8 = m68k_unused_8_w;
m68k.memory_map[0xa0].write16 = m68k_unused_16_w;
zstate = 0;
/* assume default bank is $000000-$007FFF */
zbank = 0;
/* TMSS support */
if ((config.bios & 1) && (system_hw == SYSTEM_MD) && hard_reset)
{
int i;
/* clear TMSS register */
memset(tmss, 0x00, sizeof(tmss));
/* VDP access is locked by default */
for (i=0xc0; i<0xe0; i+=8)
{
m68k.memory_map[i].read8 = m68k_lockup_r_8;
m68k.memory_map[i].read16 = m68k_lockup_r_16;
m68k.memory_map[i].write8 = m68k_lockup_w_8;
m68k.memory_map[i].write16 = m68k_lockup_w_16;
zbank_memory_map[i].read = zbank_lockup_r;
zbank_memory_map[i].write = zbank_lockup_w;
}
/* check if BOOT ROM is loaded */
if (system_bios & SYSTEM_MD)
{
/* save default cartridge slot mapping */
cart.base = m68k.memory_map[0].base;
/* BOOT ROM is mapped at $000000-$0007FF */
m68k.memory_map[0].base = boot_rom;
}
}
/* reset MAIN-CPU */
m68k_pulse_reset();
}
else
{
/* RAM state at power-on is undefined on some systems */
if ((system_hw == SYSTEM_MARKIII) || ((system_hw & SYSTEM_SMS) && (region_code == REGION_JAPAN_NTSC)))
{
/* some korean games rely on RAM to be initialized with values different from $00 or $ff */
memset(work_ram, 0xf0, sizeof(work_ram));
}
/* reset cartridge hardware */
sms_cart_reset();
/* halt 68k (/VRES is forced low) */
m68k_pulse_halt();
}
/* reset Z80 */
z80_reset();
/* some Z80 registers need to be initialized on Power ON */
if (hard_reset)
{
/* Power Base Converter specific */
if (system_hw == SYSTEM_PBC)
{
/* startup code logic (verified on real hardware): */
/* 21 01 E1 : LD HL, $E101
25 -- -- : DEC H
F9 -- -- : LD SP,HL
C7 -- -- : RST $00
01 01 -- : LD BC, $xx01
*/
Z80.hl.w.l = 0xE001;
Z80.sp.w.l = 0xDFFF;
Z80.r = 4;
}
/* Master System specific (when BIOS is disabled) */
else if ((system_hw & SYSTEM_SMS) && (!(config.bios & 1) || !(system_bios & SYSTEM_SMS)))
{
/* usually done by BIOS & required by some SMS games that don't initialize SP */
Z80.sp.w.l = 0xDFFF;
}
}
}
/*-----------------------------------------------------------------------*/
/* OS ROM / TMSS register control functions (Genesis mode) */
/*-----------------------------------------------------------------------*/
void gen_tmss_w(unsigned int offset, unsigned int data)
{
int i;
/* write TMSS register */
WRITE_WORD(tmss, offset, data);
/* VDP requires "SEGA" value to be written in TMSS register */
if (memcmp((char *)tmss, "SEGA", 4) == 0)
{
for (i=0xc0; i<0xe0; i+=8)
{
m68k.memory_map[i].read8 = vdp_read_byte;
m68k.memory_map[i].read16 = vdp_read_word;
m68k.memory_map[i].write8 = vdp_write_byte;
m68k.memory_map[i].write16 = vdp_write_word;
zbank_memory_map[i].read = zbank_read_vdp;
zbank_memory_map[i].write = zbank_write_vdp;
}
}
else
{
for (i=0xc0; i<0xe0; i+=8)
{
m68k.memory_map[i].read8 = m68k_lockup_r_8;
m68k.memory_map[i].read16 = m68k_lockup_r_16;
m68k.memory_map[i].write8 = m68k_lockup_w_8;
m68k.memory_map[i].write16 = m68k_lockup_w_16;
zbank_memory_map[i].read = zbank_lockup_r;
zbank_memory_map[i].write = zbank_lockup_w;
}
}
}
void gen_bankswitch_w(unsigned int data)
{
/* check if BOOT ROM is loaded */
if (system_bios & SYSTEM_MD)
{
if (data & 1)
{
/* enable cartridge ROM */
m68k.memory_map[0].base = cart.base;
}
else
{
/* enable internal BOOT ROM */
m68k.memory_map[0].base = boot_rom;
}
}
}
unsigned int gen_bankswitch_r(void)
{
/* check if BOOT ROM is loaded */
if (system_bios & SYSTEM_MD)
{
return (m68k.memory_map[0].base == cart.base);
}
return 0xff;
}
/*-----------------------------------------------------------------------*/
/* Z80 Bus controller chip functions (Genesis mode) */
/* ----------------------------------------------------------------------*/
void gen_zbusreq_w(unsigned int data, unsigned int cycles)
{
if (data) /* !ZBUSREQ asserted */
{
/* check if Z80 is going to be stopped */
if (zstate == 1)
{
/* resynchronize with 68k */
z80_run(cycles);
/* enable 68k access to Z80 bus */
m68k.memory_map[0xa0].read8 = z80_read_byte;
m68k.memory_map[0xa0].read16 = z80_read_word;
m68k.memory_map[0xa0].write8 = z80_write_byte;
m68k.memory_map[0xa0].write16 = z80_write_word;
}
/* update Z80 bus status */
zstate |= 2;
}
else /* !ZBUSREQ released */
{
/* check if Z80 is going to be restarted */
if (zstate == 3)
{
/* resynchronize with 68k */
Z80.cycles = cycles;
/* disable 68k access to Z80 bus */
m68k.memory_map[0xa0].read8 = m68k_read_bus_8;
m68k.memory_map[0xa0].read16 = m68k_read_bus_16;
m68k.memory_map[0xa0].write8 = m68k_unused_8_w;
m68k.memory_map[0xa0].write16 = m68k_unused_16_w;
}
/* update Z80 bus status */
zstate &= 1;
}
}
void gen_zreset_w(unsigned int data, unsigned int cycles)
{
if (data) /* !ZRESET released */
{
/* check if Z80 is going to be restarted */
if (zstate == 0)
{
/* resynchronize with 68k */
Z80.cycles = cycles;
/* reset Z80 & YM2612 */
z80_reset();
fm_reset(cycles);
}
/* check if 68k access to Z80 bus is granted */
else if (zstate == 2)
{
/* enable 68k access to Z80 bus */
m68k.memory_map[0xa0].read8 = z80_read_byte;
m68k.memory_map[0xa0].read16 = z80_read_word;
m68k.memory_map[0xa0].write8 = z80_write_byte;
m68k.memory_map[0xa0].write16 = z80_write_word;
/* reset Z80 & YM2612 */
z80_reset();
fm_reset(cycles);
}
/* update Z80 bus status */
zstate |= 1;
}
else /* !ZRESET asserted */
{
/* check if Z80 is going to be stopped */
if (zstate == 1)
{
/* resynchronize with 68k */
z80_run(cycles);
}
/* check if 68k had access to Z80 bus */
else if (zstate == 3)
{
/* disable 68k access to Z80 bus */
m68k.memory_map[0xa0].read8 = m68k_read_bus_8;
m68k.memory_map[0xa0].read16 = m68k_read_bus_16;
m68k.memory_map[0xa0].write8 = m68k_unused_8_w;
m68k.memory_map[0xa0].write16 = m68k_unused_16_w;
}
/* stop YM2612 */
fm_reset(cycles);
/* update Z80 bus status */
zstate &= 2;
}
}
void gen_zbank_w (unsigned int data)
{
zbank = ((zbank >> 1) | ((data & 1) << 23)) & 0xFF8000;
}
/*-----------------------------------------------------------------------*/
/* Z80 interrupt callback */
/* ----------------------------------------------------------------------*/
int z80_irq_callback (int param)
{
return -1;
}

View File

@ -0,0 +1,77 @@
/***************************************************************************************
* Genesis Plus
* Internal hardware & Bus controllers
*
* Support for SG-1000, Mark-III, Master System, Game Gear, Mega Drive & Mega CD hardware
*
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code)
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _GENESIS_H_
#define _GENESIS_H_
#include "md_cart.h"
#include "sms_cart.h"
#include "scd.h"
/* External Hardware */
typedef union
{
md_cart_t md_cart;
cd_hw_t cd_hw;
} external_t;
/* Global variables */
extern external_t ext;
extern uint8 boot_rom[0x800];
extern uint8 work_ram[0x10000];
extern uint8 zram[0x2000];
extern uint32 zbank;
extern uint8 zstate;
extern uint8 pico_current;
/* Function prototypes */
extern void gen_init(void);
extern void gen_reset(int hard_reset);
extern void gen_tmss_w(unsigned int offset, unsigned int data);
extern void gen_bankswitch_w(unsigned int data);
extern unsigned int gen_bankswitch_r(void);
extern void gen_zbusreq_w(unsigned int state, unsigned int cycles);
extern void gen_zreset_w(unsigned int state, unsigned int cycles);
extern void gen_zbank_w(unsigned int state);
extern int z80_irq_callback(int param);
#endif /* _GEN_H_ */

652
genplus-gx32/core/hvc.h Normal file
View File

@ -0,0 +1,652 @@
/***************************************************************************************
* Genesis Plus
* HV Counters
*
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
/*
NTSC, 256x192
-------------
Lines Description
192 Active display
24 Bottom border
3 Bottom blanking
3 Vertical blanking
13 Top blanking
27 Top border
V counter values
00-DA, D5-FF
NTSC, 256x224
-------------
Lines Description
224 Active display
8 Bottom border
3 Bottom blanking
3 Vertical blanking
13 Top blanking
11 Top border
V counter values
00-EA, E5-FF
NTSC, 256x240
-------------
This mode does not work on NTSC machines. All 30 rows of the name table are
displayed, there is no border, blanking, or retrace period, and the next
frame starts after the 30th row. The display rolls continuously though it
can be stabilized by adjusting the vertical hold.
V counter values
00-FF, 00-06
PAL, 256x192
------------
Lines Description
192 Active display
48 Bottom border
3 Bottom blanking
3 Vertical blanking
13 Top blanking
54 Top border
V counter values
00-F2, BA-FF
PAL, 256x224
------------
Lines Description
224 Active display
32 Bottom border
3 Bottom blanking
3 Vertical blanking
13 Top blanking
38 Top border
V counter values
00-FF, 00-02, CA-FF
PAL, 256x240
------------
Lines Description
240 Active display
24 Bottom border
3 Bottom blanking
3 Vertical blanking
13 Top blanking
30 Top border
V counter values
00-FF, 00-0A, D2-FF
Here are some details about what the different screen areas look like,
useful if you are emulating overscan or if you want to have a 'virtual'
vertical hold control in your emulator.
Active display - Where the display generated by the VDP goes.
Bottom border - Filled with border color from VDP register #7.
Bottom blanking - Filled with a light black color. (like display was blanked)
Vertical sync - Filled with a pure black color. (like display was turned off)
Top blanking - Filled with a light black color. (like display was blanked)
Top border - Filled with the border color from VDP register #7.
*/
#ifndef _HVC_H_
#define _HVC_H_
/***************************************************************/
/* */
/* H-counter timings in H32 & H40 modes (starts from HINT) */
/* */
/* There are normally 3420 Master Clock counts per raster line */
/* with 342 dots/line in H32 mode & 420 dots/line in H40 mode. */
/* */
/* in H32 mode, dot clock is divided from MCLK (MCLK/10) */
/* in H40 mode, dot clock is divided from EDCLK (EDCLK/2) */
/* */
/* EDCLK (external dot clock ?) is generated outside the VDP: */
/* When HSYNC is low, EDCLK varies between MCLK/10 and MCLK/8, */
/* otherwise it is fixed to MCLK/8. */
/* */
/* Notes: */
/* (1) VDP register 12 bit 7 enables use of EDCLK when set */
/* (2) VDP register 12 bit 5 forces HSYNC high when set */
/* (3) H32 or H40 mode is selected with VDP register $0C bit 0 */
/* and can be set independently from above settings */
/* */
/* On real hardware, non-standard timings can be obtained by */
/* modifying those settings (for example, dot clock can be set */
/* to MCLK/8 in both modes if HSYNC output is disabled and if */
/* EDCLK input is enabled in H32 mode / disabled in H40 mode), */
/* resulting in slightly different H-counter and VDP timings. */
/* */
/* Genesis Plus GX timings always assume standard settings i.e */
/* HSYNC output always enabled and EDCLK input enabled in H40 */
/* mode / disabled in H32 mode. */
/* */
/***************************************************************/
static const uint8 cycle2hc32[3420] =
{
/* end of active display (14 pixels -> 140 Mcycles) , H interrupt triggered, Vcounter increment */
0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x88, 0x88, 0x88, 0x88,
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
0x89, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,
0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
/* right border (14 pixels -> 140 Mcycles) */
0x8c, 0x8c, 0x8c, 0x8c,
0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,
0x8d, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,
0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
/* right blanking (9 pixels -> 90 Mcycles), VDP status HBLANK flag set */
0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
0xe9, 0xe9, 0xe9, 0xe9,
0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9,
0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea,
0xea, 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec,
0xec, 0xec,
/* horizontal sync (26 pixels -> 260 Mcycles) */
0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed,
0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef,
0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1,
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3,
0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4,
0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5,
0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9,
0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
/* left blanking (24 pixels -> 240 Mcycles) */
0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd,
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
/* V interrupt triggered (MD mode) */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05,
0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
/* left border (13 pixels -> 130 Mcycles) , VDP status HBLANK flag cleared */
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09,
0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
/* remaining active display (252 pixels -> 2520 Mcycles) */
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d,
0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,
0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19,
0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
0x1a, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b,
0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1d,
0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d,
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21,
0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25,
0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29, 0x29, 0x29,
0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d,
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31,
0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32,
0x32, 0x32, 0x32, 0x32, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35,
0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37,
0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39,
0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
0x3a, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3d,
0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43,
0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45,
0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46,
0x46, 0x46, 0x46, 0x46, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47,
0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48,
0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49,
0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
0x4a, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b,
0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4d,
0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d,
0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e,
0x4e, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f,
0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51, 0x51, 0x51,
0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51,
0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52,
0x52, 0x52, 0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53,
0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54,
0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x55,
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56,
0x56, 0x56, 0x56, 0x56, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x59,
0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59,
0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a,
0x5a, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b,
0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
0x5e, 0x5e, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62,
0x62, 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64,
0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x65,
0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67,
0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68,
0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a,
0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b,
0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c,
0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d,
0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d,
0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e,
0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f,
0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70,
0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x71,
0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71,
0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73,
0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74,
0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75,
0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75,
0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76,
0x76, 0x76, 0x76, 0x76, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79,
0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a,
0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c,
0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7d,
0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d,
0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e,
0x7e, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81,
0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
0x82, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84
};
static const uint8 cycle2hc40[3420] =
{
/* end of active display (16 pixels -> 128 Mcycles) , HINT triggered , Vcounter increment */
0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,
0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,
0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,
/* right border (14 pixels -> 112 Mcycles) */
0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,
0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,
0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,
0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
/* right blanking (9 pixels -> 72 Mcycles) , VDP status HBLANK flag set */
0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6,
/* horizontal sync (32 pixels -> 313 Mcycles) */
0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6,
0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8,
0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9,
0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea,
0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xeb,
0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec,
0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef,
0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef,
0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3,
0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4,
0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4,
0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
0xf6,
/* left blanking (32 pixels -> 259 Mcycles) */
0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8,
0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9,
0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa,
0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb,
0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd,
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
/* Vertical Interrupt triggered */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05,
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06,
/* left border (13 pixels -> 104 Mcycles) , VDP status HBLANK flag cleared */
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09,
0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a,
0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b,
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c,
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
/* remaining active display (304 pixels -> 2432 Mcycles) */
0x0d, 0x0d, 0x0d, 0x0d,
0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e,
0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f,
0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12,
0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,
0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14,
0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,
0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16,
0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17,
0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19,
0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, 0x1a,
0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1b,
0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c,
0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1d,
0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1e,
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f,
0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21,
0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, 0x22,
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23,
0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24,
0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25,
0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, 0x26,
0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, 0x27,
0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28,
0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29, 0x29, 0x29,
0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b,
0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2c,
0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d,
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e,
0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f,
0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31,
0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x32,
0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x33, 0x33, 0x33, 0x33,
0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x34, 0x34, 0x34, 0x34,
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35,
0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37, 0x37, 0x37,
0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, 0x38,
0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39,
0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x3a, 0x3a, 0x3a, 0x3a,
0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3b,
0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c,
0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3d,
0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3e,
0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f,
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x40, 0x40, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43, 0x43,
0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x44, 0x44, 0x44, 0x44,
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45,
0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x46, 0x46, 0x46, 0x46,
0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x47, 0x47, 0x47, 0x47,
0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x48,
0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49,
0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4a,
0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4b,
0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, 0x4c, 0x4c,
0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4d,
0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, 0x4e,
0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f, 0x4f, 0x4f,
0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50,
0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51, 0x51, 0x51,
0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x52,
0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x53, 0x53, 0x53, 0x53,
0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54, 0x54, 0x54,
0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x55,
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x56, 0x56, 0x56, 0x56,
0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x57, 0x57, 0x57, 0x57,
0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x58,
0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x59,
0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5a,
0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5b,
0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5e, 0x5e,
0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x5f,
0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x60,
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x62,
0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x64, 0x64, 0x64, 0x64,
0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x65,
0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67,
0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x68,
0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69, 0x69,
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a,
0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b,
0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6c, 0x6c, 0x6c, 0x6c,
0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d,
0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6e,
0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f,
0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x70,
0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x71,
0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x72, 0x72, 0x72, 0x72,
0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73, 0x73,
0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x74,
0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75,
0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x76, 0x76, 0x76, 0x76,
0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x77, 0x77, 0x77, 0x77,
0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x78,
0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79,
0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a,
0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7b,
0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7c,
0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7d,
0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7e,
0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f,
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81,
0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83, 0x83,
0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x84, 0x84, 0x84, 0x84,
0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85,
0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x86,
0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x87, 0x87, 0x87, 0x87,
0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x88, 0x88, 0x88, 0x88,
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x89,
0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a,
0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b,
0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8c,
0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d, 0x8d, 0x8d,
0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e,
0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f,
0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92,
0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93,
0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94,
0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x95,
0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x96, 0x96, 0x96, 0x96,
0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97,
0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, 0x98,
0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99,
0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a, 0x9a,
0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b,
0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9c,
0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d,
0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e,
0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, 0x9f, 0x9f,
0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0xa0, 0xa0, 0xa0, 0xa0,
0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1,
0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2,
0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4,
0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4};
#endif /* _HVC_H_ */

View File

@ -0,0 +1,134 @@
/***************************************************************************************
* Genesis Plus
* Sega Activator support
*
* Copyright (C) 2011-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
struct
{
uint8 State;
uint8 Counter;
} activator[2];
void activator_reset(int index)
{
activator[index].State = 0x40;
activator[index].Counter = 0;
}
INLINE unsigned char activator_read(int index)
{
/* IR sensors 1-16 data (active low) */
uint16 data = ~input.pad[index << 2];
/* D1 = D0 (data is ready) */
uint8 temp = (activator[index].State & 0x01) << 1;
switch (activator[index].Counter)
{
case 0: /* x x x x 0 1 0 0 */
temp |= 0x04;
break;
case 1: /* x x l1 l2 l3 l4 1 1 */
temp |= ((data << 2) & 0x3C);
break;
case 2: /* x x l5 l6 l7 l8 0 0 */
temp |= ((data >> 2) & 0x3C);
break;
case 3: /* x x h1 h2 h3 h4 1 1 */
temp |= ((data >> 6) & 0x3C);
break;
case 4: /* x x h5 h6 h7 h8 0 0 */
temp |= ((data >> 10) & 0x3C);
break;
}
return temp;
}
INLINE void activator_write(int index, unsigned char data, unsigned char mask)
{
/* update bits set as output only */
data = (activator[index].State & ~mask) | (data & mask);
/* TH transitions */
if ((activator[index].State ^ data) & 0x40)
{
/* reset sequence cycle */
activator[index].Counter = 0;
}
else
{
/* D0 transitions */
if ((activator[index].State ^ data) & 0x01)
{
/* increment sequence cycle */
if (activator[index].Counter < 4)
{
activator[index].Counter++;
}
}
}
/* update internal state */
activator[index].State = data;
}
unsigned char activator_1_read(void)
{
return activator_read(0);
}
unsigned char activator_2_read(void)
{
return activator_read(1);
}
void activator_1_write(unsigned char data, unsigned char mask)
{
activator_write(0, data, mask);
}
void activator_2_write(unsigned char data, unsigned char mask)
{
activator_write(1, data, mask);
}

View File

@ -0,0 +1,49 @@
/***************************************************************************************
* Genesis Plus
* Sega Activator support
*
* Copyright (C) 2011-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _ACTIVATOR_H_
#define _ACTIVATOR_H_
/* Function prototypes */
extern void activator_reset(int index);
extern unsigned char activator_1_read(void);
extern unsigned char activator_2_read(void);
extern void activator_1_write(unsigned char data, unsigned char mask);
extern void activator_2_write(unsigned char data, unsigned char mask);
#endif

View File

@ -0,0 +1,234 @@
/***************************************************************************************
* Genesis Plus
* 3-Buttons & 6-Buttons pad support
* Support for J-CART & 4-Way Play adapters
*
* Copyright (C) 2007-2011 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
#include "gamepad.h"
uint8 pad_index;
void gamepad_reset(int port)
{
/* default state (Gouketsuji Ichizoku / Power Instinct, Samurai Spirits / Samurai Shodown) */
gamepad[port].State = 0x40;
gamepad[port].Counter = 0;
gamepad[port].Timeout = 0;
/* reset pad index (4-WayPlay) */
pad_index = 0;
}
void gamepad_refresh(int port)
{
/* 6-buttons pad */
if (gamepad[port].Timeout++ > 25)
{
gamepad[port].Counter = 0;
gamepad[port].Timeout = 0;
}
}
INLINE unsigned char gamepad_read(int port)
{
/* bit 7 is latched, returns current TH state */
unsigned int data = (gamepad[port].State & 0x40) | 0x3F;
/* pad value */
unsigned int val = input.pad[port];
/* get current step (TH state) */
unsigned int step = gamepad[port].Counter | ((data >> 6) & 1);
switch (step)
{
case 1: /*** First High ***/
case 3: /*** Second High ***/
case 5: /*** Third High ***/
{
/* TH = 1 : ?1CBRLDU */
data &= ~(val & 0x3F);
break;
}
case 0: /*** First low ***/
case 2: /*** Second low ***/
{
/* TH = 0 : ?0SA00DU */
data &= ~(val & 0x03);
data &= ~((val >> 2) & 0x30);
data &= ~0x0C;
break;
}
/* 6buttons specific (taken from gen-hw.txt) */
/* A 6-button gamepad allows the extra buttons to be read based on how */
/* many times TH is switched from 1 to 0 (and not 0 to 1). Observe the */
/* following sequence */
/*
TH = 1 : ?1CBRLDU 3-button pad return value
TH = 0 : ?0SA00DU 3-button pad return value
TH = 1 : ?1CBRLDU 3-button pad return value
TH = 0 : ?0SA0000 D3-0 are forced to '0'
TH = 1 : ?1CBMXYZ Extra buttons returned in D3-0
TH = 0 : ?0SA1111 D3-0 are forced to '1'
*/
case 4: /*** Third Low ***/
{
/* TH = 0 : ?0SA0000 D3-0 are forced to '0'*/
data &= ~((val >> 2) & 0x30);
data &= ~0x0F;
break;
}
case 6: /*** Fourth Low ***/
{
/* TH = 0 : ?0SA1111 D3-0 are forced to '1'*/
data &= ~((val >> 2) & 0x30);
break;
}
case 7: /*** Fourth High ***/
{
/* TH = 1 : ?1CBMXYZ Extra buttons returned in D3-0*/
data &= ~(val & 0x30);
data &= ~((val >> 8) & 0x0F);
break;
}
}
return data;
}
INLINE void gamepad_write(int port, unsigned char data, unsigned char mask)
{
/* update bits set as output only */
data = (gamepad[port].State & ~mask) | (data & mask);
if (input.dev[port] == DEVICE_PAD6B)
{
/* TH=0 to TH=1 transition */
if (!(gamepad[port].State & 0x40) && (data & 0x40))
{
gamepad[port].Counter = (gamepad[port].Counter + 2) & 6;
gamepad[port].Timeout = 0;
}
}
/* update internal state */
gamepad[port].State = data;
}
/*--------------------------------------------------------------------------*/
/* Default ports handlers */
/*--------------------------------------------------------------------------*/
unsigned char gamepad_1_read(void)
{
return gamepad_read(0);
}
unsigned char gamepad_2_read(void)
{
return gamepad_read(4);
}
void gamepad_1_write(unsigned char data, unsigned char mask)
{
gamepad_write(0, data, mask);
}
void gamepad_2_write(unsigned char data, unsigned char mask)
{
gamepad_write(4, data, mask);
}
/*--------------------------------------------------------------------------*/
/* 4-WayPlay ports handler */
/*--------------------------------------------------------------------------*/
unsigned char wayplay_1_read(void)
{
if (pad_index < 4)
{
return gamepad_read(pad_index);
}
/* multitap detection */
return 0x70;
}
unsigned char wayplay_2_read(void)
{
return 0x7F;
}
void wayplay_1_write(unsigned char data, unsigned char mask)
{
if (pad_index < 4)
{
gamepad_write(pad_index, data, mask);
}
}
void wayplay_2_write(unsigned char data, unsigned char mask)
{
if ((mask & 0x70) == 0x70)
{
pad_index = (data & 0x70) >> 4;
}
}
/*--------------------------------------------------------------------------*/
/* J-Cart memory handlers */
/*--------------------------------------------------------------------------*/
unsigned int jcart_read(unsigned int address)
{
/* TH2 output read is fixed to zero (fixes Micro Machines 2) */
return ((gamepad_read(5) & 0x7F) | ((gamepad_read(6) & 0x3F) << 8));
}
void jcart_write(unsigned int address, unsigned int data)
{
gamepad_write(5, (data & 1) << 6, 0x40);
gamepad_write(6, (data & 1) << 6, 0x40);
return;
}

View File

@ -0,0 +1,57 @@
/***************************************************************************************
* Genesis Plus
* 3-Buttons & 6-Buttons pad support
* Support for J-CART & 4-Way Play adapters
*
* Copyright (C) 2007-2011 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _GAMEPAD_H_
#define _GAMEPAD_H_
/* Function prototypes */
extern void gamepad_reset(int port);
extern void gamepad_refresh(int port);
extern unsigned char gamepad_1_read(void);
extern unsigned char gamepad_2_read(void);
extern void gamepad_1_write(unsigned char data, unsigned char mask);
extern void gamepad_2_write(unsigned char data, unsigned char mask);
extern unsigned char wayplay_1_read(void);
extern unsigned char wayplay_2_read(void);
extern void wayplay_1_write(unsigned char data, unsigned char mask);
extern void wayplay_2_write(unsigned char data, unsigned char mask);
extern unsigned int jcart_read(unsigned int address);
extern void jcart_write(unsigned int address, unsigned int data);
#endif

View File

@ -0,0 +1,374 @@
/***************************************************************************************
* Genesis Plus
* Input peripherals support
*
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code)
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
#include "gamepad.h"
#include "lightgun.h"
#include "mouse.h"
#include "activator.h"
#include "xe_a1p.h"
#include "teamplayer.h"
#include "paddle.h"
#include "sportspad.h"
#include "terebi_oekaki.h"
t_input input;
int old_system[2] = {-1,-1};
void input_init(void)
{
int i;
int player = 0;
for (i=0; i<MAX_DEVICES; i++)
{
input.dev[i] = NO_DEVICE;
input.pad[i] = 0;
}
/* PICO tablet */
if (system_hw == SYSTEM_PICO)
{
input.dev[0] = DEVICE_PICO;
return;
}
/* Terebi Oekaki tablet */
if (cart.special & HW_TEREBI_OEKAKI)
{
input.dev[0] = DEVICE_TEREBI;
return;
}
switch (input.system[0])
{
case SYSTEM_MS_GAMEPAD:
{
input.dev[0] = DEVICE_PAD2B;
player++;
break;
}
case SYSTEM_MD_GAMEPAD:
{
input.dev[0] = config.input[player].padtype;
player++;
break;
}
case SYSTEM_MOUSE:
{
input.dev[0] = DEVICE_MOUSE;
player++;
break;
}
case SYSTEM_ACTIVATOR:
{
input.dev[0] = DEVICE_ACTIVATOR;
player++;
break;
}
case SYSTEM_XE_A1P:
{
input.dev[0] = DEVICE_XE_A1P;
player++;
break;
}
case SYSTEM_WAYPLAY:
{
for (i=0; i< 4; i++)
{
if (player < MAX_INPUTS)
{
input.dev[i] = config.input[player].padtype;
player++;
}
}
break;
}
case SYSTEM_TEAMPLAYER:
{
for (i=0; i<4; i++)
{
if (player < MAX_INPUTS)
{
input.dev[i] = config.input[player].padtype;
player++;
}
}
teamplayer_init(0);
break;
}
case SYSTEM_LIGHTPHASER:
{
input.dev[0] = DEVICE_LIGHTGUN;
player++;
break;
}
case SYSTEM_PADDLE:
{
input.dev[0] = DEVICE_PADDLE;
player++;
break;
}
case SYSTEM_SPORTSPAD:
{
input.dev[0] = DEVICE_SPORTSPAD;
player++;
break;
}
}
if (player == MAX_INPUTS)
{
return;
}
switch (input.system[1])
{
case SYSTEM_MS_GAMEPAD:
{
input.dev[4] = DEVICE_PAD2B;
player++;
break;
}
case SYSTEM_MD_GAMEPAD:
{
input.dev[4] = config.input[player].padtype;
player++;
break;
}
case SYSTEM_MOUSE:
{
input.dev[4] = DEVICE_MOUSE;
player++;
break;
}
case SYSTEM_ACTIVATOR:
{
input.dev[4] = DEVICE_ACTIVATOR;
player++;
break;
}
case SYSTEM_XE_A1P:
{
input.dev[4] = DEVICE_XE_A1P;
player++;
break;
}
case SYSTEM_MENACER:
{
input.dev[4] = DEVICE_LIGHTGUN;
player++;
break;
}
case SYSTEM_JUSTIFIER:
{
for (i=4; i<6; i++)
{
if (player < MAX_INPUTS)
{
input.dev[i] = DEVICE_LIGHTGUN;
player++;
}
}
break;
}
case SYSTEM_TEAMPLAYER:
{
for (i=4; i<8; i++)
{
if (player < MAX_INPUTS)
{
input.dev[i] = config.input[player].padtype;
player++;
}
}
teamplayer_init(1);
break;
}
case SYSTEM_LIGHTPHASER:
{
input.dev[4] = DEVICE_LIGHTGUN;
player++;
break;
}
case SYSTEM_PADDLE:
{
input.dev[4] = DEVICE_PADDLE;
player++;
break;
}
case SYSTEM_SPORTSPAD:
{
input.dev[4] = DEVICE_SPORTSPAD;
player++;
break;
}
}
/* J-CART */
if (cart.special & HW_J_CART)
{
/* two additional gamepads */
for (i=5; i<7; i++)
{
if (player < MAX_INPUTS)
{
input.dev[i] = config.input[player].padtype;
player ++;
}
}
}
}
void input_reset(void)
{
/* Reset input devices */
int i;
for (i=0; i<MAX_DEVICES; i++)
{
switch (input.dev[i])
{
case DEVICE_PAD2B:
case DEVICE_PAD3B:
case DEVICE_PAD6B:
{
gamepad_reset(i);
break;
}
case DEVICE_LIGHTGUN:
{
lightgun_reset(i);
break;
}
case DEVICE_MOUSE:
{
mouse_reset(i);
break;
}
case DEVICE_ACTIVATOR:
{
activator_reset(i >> 2);
break;
}
case DEVICE_XE_A1P:
{
xe_a1p_reset(i);
break;
}
case DEVICE_PADDLE:
{
paddle_reset(i);
break;
}
case DEVICE_SPORTSPAD:
{
sportspad_reset(i);
break;
}
case DEVICE_TEREBI:
{
terebi_oekaki_reset();
break;
}
default:
{
break;
}
}
}
/* Team Player */
for (i=0; i<2; i++)
{
if (input.system[i] == SYSTEM_TEAMPLAYER)
{
teamplayer_reset(i);
}
}
}
void input_refresh(void)
{
int i;
for (i=0; i<MAX_DEVICES; i++)
{
switch (input.dev[i])
{
case DEVICE_PAD6B:
{
gamepad_refresh(i);
break;
}
case DEVICE_LIGHTGUN:
{
lightgun_refresh(i);
break;
}
}
}
}

View File

@ -0,0 +1,156 @@
/***************************************************************************************
* Genesis Plus
* Input peripherals support
*
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code)
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _INPUT_H_
#define _INPUT_H_
/* Max. number of devices */
#define MAX_DEVICES (8)
/* Ports configuration */
#define NO_SYSTEM (0) /* unconnected port*/
#define SYSTEM_MD_GAMEPAD (1) /* single 3-buttons or 6-buttons Control Pad */
#define SYSTEM_MOUSE (2) /* Sega Mouse */
#define SYSTEM_MENACER (3) /* Sega Menacer (port B only) */
#define SYSTEM_JUSTIFIER (4) /* Konami Justifiers (port B only) */
#define SYSTEM_XE_A1P (5) /* XE-A1P analog controller (port A only) */
#define SYSTEM_ACTIVATOR (6) /* Sega Activator */
#define SYSTEM_MS_GAMEPAD (7) /* single 2-buttons Control Pad (Master System) */
#define SYSTEM_LIGHTPHASER (8) /* Sega Light Phaser (Master System) */
#define SYSTEM_PADDLE (9) /* Sega Paddle Control (Master System) */
#define SYSTEM_SPORTSPAD (10) /* Sega Sports Pad (Master System) */
#define SYSTEM_TEAMPLAYER (11) /* Multi Tap -- Sega TeamPlayer */
#define SYSTEM_WAYPLAY (12) /* Multi Tap -- EA 4-Way Play (use both ports) */
/* Device type */
#define NO_DEVICE (0xff) /* unconnected device (fixed ID for Team Player) */
#define DEVICE_PAD3B (0x00) /* 3-buttons Control Pad (fixed ID for Team Player)*/
#define DEVICE_PAD6B (0x01) /* 6-buttons Control Pad (fixed ID for Team Player) */
#define DEVICE_PAD2B (0x02) /* 2-buttons Control Pad */
#define DEVICE_MOUSE (0x03) /* Sega Mouse */
#define DEVICE_LIGHTGUN (0x04) /* Sega Light Phaser, Menacer or Konami Justifiers */
#define DEVICE_PADDLE (0x05) /* Sega Paddle Control */
#define DEVICE_SPORTSPAD (0x06) /* Sega Sports Pad */
#define DEVICE_PICO (0x07) /* PICO tablet */
#define DEVICE_TEREBI (0x08) /* Terebi Oekaki tablet */
#define DEVICE_XE_A1P (0x09) /* XE-A1P analog controller */
#define DEVICE_ACTIVATOR (0x0a) /* Activator */
/* Default Input bitmasks */
#define INPUT_MODE (0x0800)
#define INPUT_X (0x0400)
#define INPUT_Y (0x0200)
#define INPUT_Z (0x0100)
#define INPUT_START (0x0080)
#define INPUT_A (0x0040)
#define INPUT_C (0x0020)
#define INPUT_B (0x0010)
#define INPUT_RIGHT (0x0008)
#define INPUT_LEFT (0x0004)
#define INPUT_DOWN (0x0002)
#define INPUT_UP (0x0001)
/* Master System specific bitmasks */
#define INPUT_BUTTON2 (0x0020)
#define INPUT_BUTTON1 (0x0010)
/* Mega Mouse specific bitmask */
#define INPUT_MOUSE_CENTER (0x0040)
#define INPUT_MOUSE_RIGHT (0x0020)
#define INPUT_MOUSE_LEFT (0x0010)
/* Pico hardware specific bitmask */
#define INPUT_PICO_PEN (0x0080)
#define INPUT_PICO_RED (0x0010)
/* XE-1AP specific bitmask */
#define INPUT_XE_E1 (0x0800)
#define INPUT_XE_E2 (0x0400)
#define INPUT_XE_START (0x0200)
#define INPUT_XE_SELECT (0x0100)
#define INPUT_XE_A (0x0080)
#define INPUT_XE_B (0x0040)
#define INPUT_XE_C (0x0020)
#define INPUT_XE_D (0x0010)
/* Activator specific bitmasks */
#define INPUT_ACTIVATOR_8U (0x8000)
#define INPUT_ACTIVATOR_8L (0x4000)
#define INPUT_ACTIVATOR_7U (0x2000)
#define INPUT_ACTIVATOR_7L (0x1000)
#define INPUT_ACTIVATOR_6U (0x0800)
#define INPUT_ACTIVATOR_6L (0x0400)
#define INPUT_ACTIVATOR_5U (0x0200)
#define INPUT_ACTIVATOR_5L (0x0100)
#define INPUT_ACTIVATOR_4U (0x0080)
#define INPUT_ACTIVATOR_4L (0x0040)
#define INPUT_ACTIVATOR_3U (0x0020)
#define INPUT_ACTIVATOR_3L (0x0010)
#define INPUT_ACTIVATOR_2U (0x0008)
#define INPUT_ACTIVATOR_2L (0x0004)
#define INPUT_ACTIVATOR_1U (0x0002)
#define INPUT_ACTIVATOR_1L (0x0001)
typedef struct
{
uint8 system[2]; /* can be one of the SYSTEM_* values */
uint8 dev[MAX_DEVICES]; /* can be one of the DEVICE_* values */
uint16 pad[MAX_DEVICES]; /* digital inputs (any of INPUT_* values) */
int16 analog[MAX_DEVICES][2]; /* analog inputs (x/y) */
int x_offset; /* gun horizontal offset */
int y_offset; /* gun vertical offset */
} t_input;
struct
{
uint8 State;
uint8 Counter;
uint8 Timeout;
} gamepad[MAX_DEVICES];
/* Global variables */
extern t_input input;
extern int old_system[2];
/* Function prototypes */
extern void input_init(void);
extern void input_reset(void);
extern void input_refresh(void);
#endif

View File

@ -0,0 +1,265 @@
/***************************************************************************************
* Genesis Plus
* Sega Light Phaser, Menacer & Konami Justifiers support
*
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
/************************************************************************************/
/* */
/* H-counter values returned in H40 & H32 modes */
/* */
/* Inside VDP, dot counter register is 9-bit, with only upper 8 bits being returned */
/* */
/* The number of dots per raster line is 342 in H32 mode and 420 in H40 mode */
/* */
/************************************************************************************/
static const uint8 hc_256[171] =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93,
0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
};
static const uint8 hc_320[210] =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6,
0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED,
0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD,
0xFE, 0xFF
};
struct
{
uint8 State;
uint8 Port;
} lightgun;
void lightgun_reset(int port)
{
input.analog[port][0] = bitmap.viewport.w / 2;
input.analog[port][1] = bitmap.viewport.h / 2;
lightgun.State = 0x40;
lightgun.Port = 4;
}
void lightgun_refresh(int port)
{
/* Check that lightgun is enabled */
if (port == lightgun.Port)
{
/* screen Y position */
int y = input.analog[port][1] + input.y_offset;
/* check if active line falls within current gun Y position */
if ((y == v_counter) && (y < bitmap.viewport.h))
{
/* HL enabled ? */
if (io_reg[5] & 0x80)
{
/* screen X position */
int x = input.analog[port][0];
/* Sega Menacer specific */
if (input.system[1] == SYSTEM_MENACER)
{
/* raw position is scaled up by games */
if (system_hw == SYSTEM_MCD)
{
x = (x * 304) / 320;
}
else
{
x = (x * 289) / 320;
}
}
/* External Interrupt enabled ? */
if (reg[11] & 0x08)
{
m68k_update_irq(2);
}
/* HVC latch enabled ? */
if (reg[0] & 0x02)
{
/* line accurate V-Counter value */
hvc_latch = 0x10000 | (y << 8);
}
else
{
/* HACK: force HVC latch even when disabled (a few games does not lock HV Counter but instead applies larger offset value) */
hvc_latch = 0x20000 | (y << 8);
}
/* pixel accurate H-Counter value */
if (reg[12] & 1)
{
hvc_latch |= hc_320[((x / 2) + input.x_offset) % 210];
}
else
{
hvc_latch |= hc_256[((x / 2) + input.x_offset) % 171];
}
}
}
else if (hvc_latch & 0x20000)
{
/* HVC should be free-running on other lines when latch is disabled (fixes "Gunfight - 3 in 1" randomization) */
hvc_latch = 0;
}
}
}
/*--------------------------------------------------------------------------*/
/* Sega Phaser */
/*--------------------------------------------------------------------------*/
INLINE unsigned char phaser_read(int port)
{
/* TL returns TRIGGER (INPUT_A) button status (active low) */
unsigned char temp = ~((input.pad[port] >> 2) & 0x10);
/* Check that TH is set as an input */
if (io_reg[0x0F] & (0x02 << (port >> 1)))
{
/* Get current X position (phaser is only used in MS compatiblity mode) */
int hcounter = hctab[(Z80.cycles + SMS_CYCLE_OFFSET) % MCYCLES_PER_LINE];
/* Compare with gun position */
int dx = input.analog[port][0] - (hcounter << 1);
int dy = input.analog[port][1] - (v_counter);
/* Check if current pixel is within lightgun spot ? */
if ((abs(dy) <= 5) && (abs(dx) <= 60))
{
/* set TH low */
temp &= ~0x40;
/* prevents multiple latch at each port read */
if (lightgun.State)
{
/* latch estimated HC value */
hvc_latch = 0x10000 | (input.x_offset + (input.analog[port][0] >> 1));
lightgun.State = 0;
}
else
{
lightgun.State = 1;
}
}
}
return temp & 0x7F;
}
unsigned char phaser_1_read(void)
{
return phaser_read(0);
}
unsigned char phaser_2_read(void)
{
return phaser_read(4);
}
/*--------------------------------------------------------------------------*/
/* Sega Menacer */
/*--------------------------------------------------------------------------*/
unsigned char menacer_read(void)
{
/* D0=??? (INPUT_B), D1=TRIGGER (INPUT_A), D2=??? (INPUT_C), D3= START (INPUT_START) (active high) */
/* TL & TR pins always return 0 (normally set as output) */
/* TH always return 1 (0 on active pixel but button acquisition is always done during VBLANK) */
unsigned data = input.pad[4] >> 4;
return ((data & 0x09) | ((data >> 1) & 0x02) | ((data << 1) & 0x04) | 0x40);
}
/*--------------------------------------------------------------------------*/
/* Konami Justifiers */
/*--------------------------------------------------------------------------*/
unsigned char justifier_read(void)
{
/* Gun detection */
if (lightgun.State & 0x40)
{
return 0x30;
}
/* Return TRIGGER (INPUT_A) & START (INPUT_START) button status in D0-D1 (active low) */
/* TL & TR pins should always return 1 (normally set as output) */
/* LEFT & RIGHT pins should always return 0 */
return (((~input.pad[lightgun.Port] >> 6) & 0x03) | 0x70);
}
void justifier_write(unsigned char data, unsigned char mask)
{
/* update bits set as output only, other bits are cleared (fixes Lethal Enforcers 2) */
data &= mask;
/* gun index */
lightgun.Port = 4 + ((data >> 5) & 1);
/* update internal state */
lightgun.State = data;
}

View File

@ -0,0 +1,51 @@
/***************************************************************************************
* Genesis Plus
* Sega Light Phaser, Menacer & Konami Justifiers support
*
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _LIGHTGUN_H_
#define _LIGHTGUN_H_
/* Input devices port handlers */
extern void lightgun_reset(int index);
extern void lightgun_refresh(int port);
extern unsigned char phaser_1_read(void);
extern unsigned char phaser_2_read(void);
extern unsigned char menacer_read(void);
extern unsigned char justifier_read(void);
extern void justifier_write(unsigned char data, unsigned char mask);
#endif

View File

@ -0,0 +1,159 @@
/***************************************************************************************
* Genesis Plus
* Sega Mouse support
*
* Copyright (C) 2007-2011 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
struct
{
uint8 State;
uint8 Counter;
uint8 Wait;
uint8 Port;
} mouse;
void mouse_reset(int port)
{
input.analog[port][0] = 0;
input.analog[port][1] = 0;
mouse.State = 0x60;
mouse.Counter = 0;
mouse.Wait = 0;
mouse.Port = port;
}
unsigned char mouse_read()
{
unsigned int temp = 0x00;
int x = input.analog[mouse.Port][0];
int y = input.analog[mouse.Port][1];
switch (mouse.Counter)
{
case 0: /* initial */
temp = 0x00;
break;
case 1: /* xxxx1011 */
temp = 0x0B;
break;
case 2: /* xxxx1111 */
temp = 0x0F;
break;
case 3: /* xxxx1111 */
temp = 0x0F;
break;
case 4: /* Axis sign & overflow (not emulated) bits */
temp |= (x < 0);
temp |= (y < 0) << 1;
/*
temp |= (abs(x) > 255) << 2;
temp |= (abs(y) > 255) << 3;
*/
break;
case 5: /* START, A, B, C buttons state (active high) */
temp = (input.pad[mouse.Port] >> 4) & 0x0F;
break;
case 6: /* X Axis MSB */
temp = (x >> 4) & 0x0F;
break;
case 7: /* X Axis LSB */
temp = (x & 0x0F);
break;
case 8: /* Y Axis MSB */
temp = (y >> 4) & 0x0F;
break;
case 9: /* Y Axis LSB */
temp = (y & 0x0F);
break;
}
/* TL = busy status */
if (mouse.Wait)
{
/* wait before ACK, fix some buggy mouse routine (Cannon Fodder, Shangai 2, Wack World,...) */
mouse.Wait = 0;
/* TL = !TR */
temp |= (~mouse.State & 0x20) >> 1;
}
else
{
/* TL = TR (data is ready) */
temp |= (mouse.State & 0x20) >> 1;
}
return temp;
}
void mouse_write(unsigned char data, unsigned char mask)
{
/* update bits set as output only */
data = (mouse.State & ~mask) | (data & mask);
/* TH transition */
if ((mouse.State ^ data) & 0x40)
{
/* start (TH=0) or stop (TH=1) acquisition */
mouse.Counter = 1 - ((data & 0x40) >> 6);
}
/* TR transition */
if ((mouse.State ^ data) & 0x20)
{
/* acquisition in progress */
if ((mouse.Counter > 0) && (mouse.Counter < 10))
{
/* increment phase */
mouse.Counter++;
}
/* TL handshake latency */
mouse.Wait = 1;
}
/* update internal state */
mouse.State = data;
}

View File

@ -0,0 +1,47 @@
/***************************************************************************************
* Genesis Plus
* Sega Mouse support
*
* Copyright (C) 2007-2011 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _MOUSE_H_
#define _MOUSE_H_
/* Function prototypes */
extern void mouse_reset(int port);
extern unsigned char mouse_read(void);
extern void mouse_write(unsigned char data, unsigned char mask);
#endif

View File

@ -0,0 +1,111 @@
/***************************************************************************************
* Genesis Plus
* Sega Paddle Control support
*
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
struct
{
uint8 State;
} paddle[2];
void paddle_reset(int index)
{
input.analog[index][0] = 128;
paddle[index>>2].State = 0x40;
}
INLINE unsigned char paddle_read(int port)
{
/* FIRE button status (active low) */
unsigned char temp = ~(input.pad[port] & 0x10);
/* Pad index */
int index = port >> 2;
/* Clear low bits */
temp &= 0x70;
/* Japanese model: automatic flip-flop */
if (region_code < REGION_USA)
{
paddle[index].State ^= 0x40;
}
if (paddle[index].State & 0x40)
{
/* return higher bits */
temp |= (input.analog[port][0] >> 4) & 0x0F;
}
else
{
/* return lower bits */
temp |= input.analog[port][0] & 0x0F;
/* set TR low */
temp &= ~0x20;
}
return temp;
}
INLINE void paddle_write(int index, unsigned char data, unsigned char mask)
{
/* update bits set as output only */
paddle[index].State = (paddle[index].State & ~mask) | (data & mask);
}
unsigned char paddle_1_read(void)
{
return paddle_read(0);
}
unsigned char paddle_2_read(void)
{
return paddle_read(4);
}
void paddle_1_write(unsigned char data, unsigned char mask)
{
paddle_write(0, data, mask);
}
void paddle_2_write(unsigned char data, unsigned char mask)
{
paddle_write(1, data, mask);
}

View File

@ -0,0 +1,49 @@
/***************************************************************************************
* Genesis Plus
* Sega Paddle Control support
*
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _PADDLE_H_
#define _PADDLE_H_
/* Function prototypes */
extern void paddle_reset(int index);
extern unsigned char paddle_1_read(void);
extern unsigned char paddle_2_read(void);
extern void paddle_1_write(unsigned char data, unsigned char mask);
extern void paddle_2_write(unsigned char data, unsigned char mask);
#endif

View File

@ -0,0 +1,134 @@
/***************************************************************************************
* Genesis Plus
* Sega Sports Pad support
*
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
struct
{
uint8 State;
uint8 Counter;
} sportspad[2];
void sportspad_reset(int index)
{
input.analog[index][0] = 128;
input.analog[index][1] = 128;
sportspad[index>>2].State = 0x40;
sportspad[index>>2].Counter = 0;
}
INLINE unsigned char sportspad_read(int port)
{
/* Buttons 1(B) & 2(C) status (active low) */
unsigned char temp = ~(input.pad[port] & 0x30);
/* Pad index */
int index = port >> 2;
/* Clear low bits */
temp &= 0x70;
/* Detect current state */
switch (sportspad[index].Counter & 3)
{
case 1:
{
/* X position high bits */
temp |= (input.analog[port][0] >> 4) & 0x0F;
break;
}
case 2:
{
/* X position low bits */
temp |= input.analog[port][0] & 0x0F;
break;
}
case 3:
{
/* Y position high bits */
temp |= (input.analog[port][1] >> 4) & 0x0F;
break;
}
default:
{
/* Y position low bits */
temp |= input.analog[port][1] & 0x0F;
break;
}
}
return temp;
}
INLINE void sportspad_write(int index, unsigned char data, unsigned char mask)
{
/* update bits set as output only */
data = (sportspad[index].State & ~mask) | (data & mask);
/* check TH transitions */
if ((data ^ sportspad[index].State) & 0x40)
{
sportspad[index].Counter++;
}
/* update internal state */
sportspad[index].State = data;
}
unsigned char sportspad_1_read(void)
{
return sportspad_read(0);
}
unsigned char sportspad_2_read(void)
{
return sportspad_read(4);
}
void sportspad_1_write(unsigned char data, unsigned char mask)
{
sportspad_write(0, data, mask);
}
void sportspad_2_write(unsigned char data, unsigned char mask)
{
sportspad_write(1, data, mask);
}

View File

@ -0,0 +1,49 @@
/***************************************************************************************
* Genesis Plus
* Sega Sports Pad support
*
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _SPORTSPAD_H_
#define _SPORTSPAD_H_
/* Function prototypes */
extern void sportspad_reset(int index);
extern unsigned char sportspad_1_read(void);
extern unsigned char sportspad_2_read(void);
extern void sportspad_1_write(unsigned char data, unsigned char mask);
extern void sportspad_2_write(unsigned char data, unsigned char mask);
#endif

View File

@ -0,0 +1,177 @@
/***************************************************************************************
* Genesis Plus
* Team Player support
*
* Copyright (C) 2007-2014 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
struct
{
uint8 State;
uint8 Counter;
uint8 Table[12];
} teamplayer[2];
void teamplayer_init(int port)
{
int i,padnum;
int index = 0;
/* this table determines which gamepad input should be returned during acquisition sequence
index = teamplayer read table index: 0=1st read, 1=2nd read, ...
table = high bits are pad index, low bits are pad input shift: 0=RLDU, 4=SABC, 8=MXYZ
*/
for (i=0; i<4; i++)
{
padnum = (4 * port) + i;
if (input.dev[padnum] == DEVICE_PAD3B)
{
padnum = padnum << 4;
teamplayer[port].Table[index++] = padnum;
teamplayer[port].Table[index++] = padnum | 4;
}
else
{
padnum = padnum << 4;
teamplayer[port].Table[index++] = padnum;
teamplayer[port].Table[index++] = padnum | 4;
teamplayer[port].Table[index++] = padnum | 8;
}
}
}
void teamplayer_reset(int port)
{
teamplayer[port].State = 0x60; /* TH = 1, TR = 1 */
teamplayer[port].Counter = 0;
}
INLINE unsigned int teamplayer_read(int port)
{
unsigned int counter = teamplayer[port].Counter;
/* acquisition sequence */
switch (counter)
{
case 0: /* initial state: xxx0011 */
{
/* TL should match TR */
return ((teamplayer[port].State & 0x20) >> 1) | 0x03;
}
case 1: /* start request: xxx1111 */
{
/* TL should match TR */
return ((teamplayer[port].State & 0x20) >> 1) | 0x0F;
}
case 2:
case 3: /* ack request: xxx0000 */
{
/* TL should match TR */
return ((teamplayer[port].State & 0x20) >> 1);
}
case 4:
case 5:
case 6:
case 7: /* PAD type: xxx0000 (3B), xxx0001 (6B) or xxx1111 (NC)*/
{
unsigned int retval = input.dev[(port << 2) + (counter - 4)];
/* TL should match TR */
return (((teamplayer[port].State & 0x20) >> 1) | retval);
}
default: /* PAD status: xxxRLDU -> xxxSACB -> xxxMXYZ */
{
unsigned int retval = 0x0F;
/* SEGA teamplayer returns successively PAD1 -> PAD2 -> PAD3 -> PAD4 inputs */
unsigned int padnum = teamplayer[port].Table[counter - 8] >> 4;
/* Each PAD inputs is obtained through 2 or 3 sequential reads: RLDU -> SACB -> MXYZ */
retval &= ~(input.pad[padnum] >> (teamplayer[port].Table[counter - 8] & 0x0F));
/* TL should match TR */
return (((teamplayer[port].State & 0x20) >> 1) | retval);
}
}
}
INLINE void teamplayer_write(int port, unsigned char data, unsigned char mask)
{
/* update bits set as output only */
unsigned int state = (teamplayer[port].State & ~mask) | (data & mask);
/* check if TH is HIGH */
if (state & 0x40)
{
/* reset counter */
teamplayer[port].Counter = 0;
}
/* TH & TR handshaking */
else if ((teamplayer[port].State ^ state) & 0x60)
{
/* increment counter */
teamplayer[port].Counter++;
}
/* update internal state */
teamplayer[port].State = state;
}
unsigned char teamplayer_1_read(void)
{
return teamplayer_read(0);
}
unsigned char teamplayer_2_read(void)
{
return teamplayer_read(1);
}
void teamplayer_1_write(unsigned char data, unsigned char mask)
{
teamplayer_write(0, data, mask);
}
void teamplayer_2_write(unsigned char data, unsigned char mask)
{
teamplayer_write(1, data, mask);
}

View File

@ -0,0 +1,50 @@
/***************************************************************************************
* Genesis Plus
* Team Player support
*
* Copyright (C) 2007-2014 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _TEAMPLAYER_H_
#define _TEAMPLAYER_H_
/* Function prototypes */
extern void teamplayer_init(int port);
extern void teamplayer_reset(int port);
extern unsigned char teamplayer_1_read(void);
extern unsigned char teamplayer_2_read(void);
extern void teamplayer_1_write(unsigned char data, unsigned char mask);
extern void teamplayer_2_write(unsigned char data, unsigned char mask);
#endif

View File

@ -0,0 +1,77 @@
/***************************************************************************************
* Genesis Plus
* Terebi Oekaki graphic board support
*
* Copyright (C) 2011 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
struct
{
uint8 axis;
uint8 busy;
} tablet;
void terebi_oekaki_reset(void)
{
input.analog[0][0] = 128;
input.analog[0][1] = 128;
tablet.axis = 1;
tablet.busy = 1;
}
unsigned short terebi_oekaki_read(void)
{
uint16 data = (tablet.busy << 15) | input.analog[0][tablet.axis];
if (!(input.pad[0] & INPUT_B))
{
data |= 0x100;
}
/* clear BUSY flag */
tablet.busy = 0;
return data;
}
void terebi_oekaki_write(unsigned char data)
{
/* X (1) or Y (0) axis */
tablet.axis = (data & 1) ^ 1;
/* set BUSY flag */
tablet.busy = 1;
}

View File

@ -0,0 +1,47 @@
/***************************************************************************************
* Genesis Plus
* Terebi Oekaki graphic board support
*
* Copyright (C) 2011 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _TEREBI_H_
#define _TEREBI_H_
/* Function prototypes */
extern void terebi_oekaki_reset(void);
extern unsigned short terebi_oekaki_read(void);
extern void terebi_oekaki_write(unsigned char data);
#endif

View File

@ -0,0 +1,182 @@
/***************************************************************************************
* Genesis Plus
* XE-A1P analog controller support
*
* Copyright (C) 2011-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
struct
{
uint8 State;
uint8 Counter;
uint8 Latency;
} xe_a1p[2];
void xe_a1p_reset(int index)
{
input.analog[index][0] = 128;
input.analog[index][1] = 128;
input.analog[index+1][0] = 128;
index >>= 2;
xe_a1p[index].State = 0x40;
xe_a1p[index].Counter = 0;
xe_a1p[index].Latency = 0;
}
INLINE unsigned char xe_a1p_read(int index)
{
unsigned int temp = 0x40;
unsigned int port = index << 2;
/* Left Stick X & Y analog values (bidirectional) */
int x = input.analog[port][0];
int y = input.analog[port][1];
/* Right Stick X or Y value (unidirectional) */
int z = input.analog[port+1][0];
/* Buttons status (active low) */
uint16 pad = ~input.pad[port];
/* Current internal cycle (0-7) */
unsigned int cycle = xe_a1p[index].Counter & 7;
/* Current 4-bit data cycle */
/* There are eight internal data cycle for each 5 acquisition sequence */
/* First 4 return the same 4-bit data, next 4 return next 4-bit data */
switch (xe_a1p[index].Counter >> 2)
{
case 0:
temp |= ((pad >> 8) & 0x0F); /* E1 E2 Start Select */
break;
case 1:
temp |= ((pad >> 4) & 0x0F); /* A B C D */
break;
case 2:
temp |= ((x >> 4) & 0x0F);
break;
case 3:
temp |= ((y >> 4) & 0x0F);
break;
case 4:
break;
case 5:
temp |= ((z >> 4) & 0x0F);
break;
case 6:
temp |= (x & 0x0F);
break;
case 7:
temp |= (y & 0x0F);
break;
case 8:
break;
case 9:
temp |= (z & 0x0F);
break;
}
/* TL indicates which part of data is returned (0=1st part, 1=2nd part) */
temp |= ((cycle & 4) << 2);
/* TR indicates if data is ready (0=ready, 1=not ready) */
/* Fastest One input routine actually expects this bit to switch between 0 & 1 */
/* so we make the first read of a data cycle return 1 then 0 for remaining reads */
temp |= (!(cycle & 3) << 5);
/* Automatically increment data cycle on each read (within current acquisition sequence) */
cycle = (cycle + 1) & 7;
/* Update internal cycle counter */
xe_a1p[index].Counter = (xe_a1p[index].Counter & ~7) | cycle;
/* Update internal latency on each read */
xe_a1p[index].Latency++;
return temp;
}
INLINE void xe_a1p_write(int index, unsigned char data, unsigned char mask)
{
/* update bits set as output only */
data = (xe_a1p[index].State & ~mask) | (data & mask);
/* look for TH 1->0 transitions */
if (!(data & 0x40) && (xe_a1p[index].State & 0x40))
{
/* reset acquisition cycle */
xe_a1p[index].Latency = xe_a1p[index].Counter = 0;
}
else
{
/* some games immediately write new data to TH */
/* so we make sure first sequence has actually been handled */
if (xe_a1p[index].Latency > 2)
{
/* next acquisition sequence */
xe_a1p[index].Counter = (xe_a1p[index].Counter & ~7) + 8;
/* 5 sequence max with 8 cycles each */
if (xe_a1p[index].Counter > 32)
{
xe_a1p[index].Counter = 32;
}
}
}
/* update internal state */
xe_a1p[index].State = data;
}
unsigned char xe_a1p_1_read(void)
{
return xe_a1p_read(0);
}
unsigned char xe_a1p_2_read(void)
{
return xe_a1p_read(1);
}
void xe_a1p_1_write(unsigned char data, unsigned char mask)
{
xe_a1p_write(0, data, mask);
}
void xe_a1p_2_write(unsigned char data, unsigned char mask)
{
xe_a1p_write(1, data, mask);
}

View File

@ -0,0 +1,49 @@
/***************************************************************************************
* Genesis Plus
* XE-A1P analog controller support
*
* Copyright (C) 2011-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _XE_A1PH_
#define _XE_A1PH_
/* Function prototypes */
extern void xe_a1p_reset(int index);
extern unsigned char xe_a1p_1_read(void);
extern unsigned char xe_a1p_2_read(void);
extern void xe_a1p_1_write(unsigned char data, unsigned char mask);
extern void xe_a1p_2_write(unsigned char data, unsigned char mask);
#endif

595
genplus-gx32/core/io_ctrl.c Normal file
View File

@ -0,0 +1,595 @@
/***************************************************************************************
* Genesis Plus
* I/O controller (Genesis & Master System modes)
*
* Support for Master System (315-5216, 315-5237 & 315-5297), Game Gear & Mega Drive I/O chips
*
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code)
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
#include "gamepad.h"
#include "lightgun.h"
#include "mouse.h"
#include "activator.h"
#include "xe_a1p.h"
#include "teamplayer.h"
#include "paddle.h"
#include "sportspad.h"
uint8 io_reg[0x10];
uint8 region_code = REGION_USA;
static struct port_t
{
void (*data_w)(unsigned char data, unsigned char mask);
unsigned char (*data_r)(void);
} port[3];
static void dummy_write(unsigned char data, unsigned char mask)
{
}
static unsigned char dummy_read(void)
{
return 0x7F;
}
/*****************************************************************************
* I/O chip initialization *
* *
*****************************************************************************/
void io_init(void)
{
/* Initialize connected peripherals */
input_init();
/* Initialize IO Ports handlers & connected peripherals */
switch (input.system[0])
{
case SYSTEM_MS_GAMEPAD:
{
port[0].data_w = dummy_write;
port[0].data_r = gamepad_1_read;
break;
}
case SYSTEM_MD_GAMEPAD:
{
port[0].data_w = gamepad_1_write;
port[0].data_r = gamepad_1_read;
break;
}
case SYSTEM_MOUSE:
{
port[0].data_w = mouse_write;
port[0].data_r = mouse_read;
break;
}
case SYSTEM_ACTIVATOR:
{
port[0].data_w = activator_1_write;
port[0].data_r = activator_1_read;
break;
}
case SYSTEM_XE_A1P:
{
port[0].data_w = xe_a1p_1_write;
port[0].data_r = xe_a1p_1_read;
break;
}
case SYSTEM_WAYPLAY:
{
port[0].data_w = wayplay_1_write;
port[0].data_r = wayplay_1_read;
break;
}
case SYSTEM_TEAMPLAYER:
{
port[0].data_w = teamplayer_1_write;
port[0].data_r = teamplayer_1_read;
break;
}
case SYSTEM_LIGHTPHASER:
{
port[0].data_w = dummy_write;
port[0].data_r = phaser_1_read;
break;
}
case SYSTEM_PADDLE:
{
port[0].data_w = paddle_1_write;
port[0].data_r = paddle_1_read;
break;
}
case SYSTEM_SPORTSPAD:
{
port[0].data_w = sportspad_1_write;
port[0].data_r = sportspad_1_read;
break;
}
default:
{
port[0].data_w = dummy_write;
port[0].data_r = dummy_read;
break;
}
}
switch (input.system[1])
{
case SYSTEM_MS_GAMEPAD:
{
port[1].data_w = dummy_write;
port[1].data_r = gamepad_2_read;
break;
}
case SYSTEM_MD_GAMEPAD:
{
port[1].data_w = gamepad_2_write;
port[1].data_r = gamepad_2_read;
break;
}
case SYSTEM_MOUSE:
{
port[1].data_w = mouse_write;
port[1].data_r = mouse_read;
break;
}
case SYSTEM_XE_A1P:
{
port[1].data_w = xe_a1p_2_write;
port[1].data_r = xe_a1p_2_read;
break;
}
case SYSTEM_ACTIVATOR:
{
port[1].data_w = activator_2_write;
port[1].data_r = activator_2_read;
break;
}
case SYSTEM_MENACER:
{
port[1].data_w = dummy_write;
port[1].data_r = menacer_read;
break;
}
case SYSTEM_JUSTIFIER:
{
port[1].data_w = justifier_write;
port[1].data_r = justifier_read;
break;
}
case SYSTEM_WAYPLAY:
{
port[1].data_w = wayplay_2_write;
port[1].data_r = wayplay_2_read;
break;
}
case SYSTEM_TEAMPLAYER:
{
port[1].data_w = teamplayer_2_write;
port[1].data_r = teamplayer_2_read;
break;
}
case SYSTEM_LIGHTPHASER:
{
port[1].data_w = dummy_write;
port[1].data_r = phaser_2_read;
break;
}
case SYSTEM_PADDLE:
{
port[1].data_w = paddle_2_write;
port[1].data_r = paddle_2_read;
break;
}
case SYSTEM_SPORTSPAD:
{
port[1].data_w = sportspad_2_write;
port[1].data_r = sportspad_2_read;
break;
}
default:
{
port[1].data_w = dummy_write;
port[1].data_r = dummy_read;
break;
}
}
/* External Port (unconnected) */
port[2].data_w = dummy_write;
port[2].data_r = dummy_read;
}
void io_reset(void)
{
/* Reset I/O registers */
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
{
io_reg[0x00] = region_code | (config.bios & 1);
io_reg[0x01] = 0x00;
io_reg[0x02] = 0x00;
io_reg[0x03] = 0x00;
io_reg[0x04] = 0x00;
io_reg[0x05] = 0x00;
io_reg[0x06] = 0x00;
io_reg[0x07] = 0xFF;
io_reg[0x08] = 0x00;
io_reg[0x09] = 0x00;
io_reg[0x0A] = 0xFF;
io_reg[0x0B] = 0x00;
io_reg[0x0C] = 0x00;
io_reg[0x0D] = 0xFB;
io_reg[0x0E] = 0x00;
io_reg[0x0F] = 0x00;
/* CD unit detection */
if (system_hw != SYSTEM_MCD)
{
io_reg[0x00] |= 0x20;
}
}
else
{
/* Game Gear specific registers */
io_reg[0x00] = 0x80 | (region_code >> 1);
io_reg[0x01] = 0x00;
io_reg[0x02] = 0xFF;
io_reg[0x03] = 0x00;
io_reg[0x04] = 0xFF;
io_reg[0x05] = 0x00;
io_reg[0x06] = 0xFF;
/* initial !RESET input */
io_reg[0x0D] = IO_RESET_HI;
/* default !CONT input */
if (system_hw != SYSTEM_PBC)
{
io_reg[0x0D] |= IO_CONT1_HI;
}
/* Control registers */
io_reg[0x0E] = 0x00;
io_reg[0x0F] = 0xFF;
}
/* Reset connected peripherals */
input_reset();
}
/*****************************************************************************
* I/O ports access from 68k (Genesis mode) *
* *
*****************************************************************************/
void io_68k_write(unsigned int offset, unsigned int data)
{
switch (offset)
{
case 0x01: /* Port A Data */
case 0x02: /* Port B Data */
case 0x03: /* Port C Data */
{
io_reg[offset] = data;
port[offset-1].data_w(data, io_reg[offset + 3]);
return;
}
case 0x04: /* Port A Ctrl */
case 0x05: /* Port B Ctrl */
case 0x06: /* Port C Ctrl */
{
if (data != io_reg[offset])
{
io_reg[offset] = data;
port[offset-4].data_w(io_reg[offset-3], data);
}
return;
}
case 0x07: /* Port A TxData */
case 0x0A: /* Port B TxData */
case 0x0D: /* Port C TxData */
{
io_reg[offset] = data;
return;
}
case 0x09: /* Port A S-Ctrl */
case 0x0C: /* Port B S-Ctrl */
case 0x0F: /* Port C S-Ctrl */
{
io_reg[offset] = data & 0xF8;
return;
}
default: /* Read-only ports */
{
return;
}
}
}
unsigned int io_68k_read(unsigned int offset)
{
switch(offset)
{
case 0x01: /* Port A Data */
case 0x02: /* Port B Data */
case 0x03: /* Port C Data */
{
unsigned int mask, data;
real_input_callback();
mask = 0x80 | io_reg[offset + 3];
data = port[offset-1].data_r();
return (io_reg[offset] & mask) | (data & ~mask);
}
default: /* return register value */
{
return io_reg[offset];
}
}
}
/*****************************************************************************
* I/O ports access from Z80 *
* *
*****************************************************************************/
void io_z80_write(unsigned int offset, unsigned int data, unsigned int cycles)
{
if (offset)
{
/* I/O Control register */
if (region_code & REGION_USA)
{
/*
Bit Function
--------------
D7 : Port B TH pin output level (1=high, 0=low)
D6 : Port B TR pin output level (1=high, 0=low)
D5 : Port A TH pin output level (1=high, 0=low)
D4 : Port A TR pin output level (1=high, 0=low)
D3 : Port B TH pin direction (1=input, 0=output)
D2 : Port B TR pin direction (1=input, 0=output)
D1 : Port A TH pin direction (1=input, 0=output)
D0 : Port A TR pin direction (1=input, 0=output)
*/
/* Send TR/TH state to connected peripherals */
port[0].data_w((data << 1) & 0x60, (~io_reg[0x0F] << 5) & 0x60);
port[1].data_w((data >> 1) & 0x60, (~io_reg[0x0F] << 3) & 0x60);
/* Check for TH low-to-high transitions on both ports */
if ((!(io_reg[0x0F] & 0x80) && (data & 0x80)) ||
(!(io_reg[0x0F] & 0x20) && (data & 0x20)))
{
/* Latch new HVC */
hvc_latch = hctab[cycles % MCYCLES_PER_LINE] | 0x10000;
}
/* Update I/O Control register */
io_reg[0x0F] = data;
}
else
{
/* TH output is fixed to 0 & TR is always an input on japanese hardware */
io_reg[0x0F] = (data | 0x05) & 0x5F;
/* Port $DD bits D4-D5 return D0-D2 (cf. http://www2.odn.ne.jp/~haf09260/Sms/EnrSms.htm) */
io_reg[0x0D] = ((data & 0x01) << 4) | ((data & 0x04) << 3);
}
}
else
{
/* Update Memory Control register */
io_reg[0x0E] = data;
/* Switch cartridge & BIOS ROM */
sms_cart_switch(~data);
}
}
unsigned int io_z80_read(unsigned int offset)
{
unsigned int data, ctrl;
real_input_callback();
/* Read port A & port B input data */
data = (port[0].data_r()) | (port[1].data_r() << 8);
/* I/O control register value */
ctrl = io_reg[0x0F];
/* I/O ports */
if (offset)
{
/*
Bit Function
--------------
D7 : Port B TH pin input
D6 : Port A TH pin input
D5 : CONT input (0 on Mega Drive hardware, 1 otherwise)
D4 : RESET button (1: default, 0: pressed, only on Master System hardware)
D3 : Port B TR pin input
D2 : Port B TL pin input
D1 : Port B Right pin input
D0 : Port B Left pin input
*/
data = ((data >> 10) & 0x0F) | (data & 0x40) | ((data >> 7) & 0x80) | io_reg[0x0D];
/* clear !RESET input */
io_reg[0x0D] |= IO_RESET_HI;
/* Adjust port B TH state if configured as output */
if (!(ctrl & 0x08))
{
data &= ~0x80;
data |= (ctrl & 0x80);
}
/* Adjust port A TH state if configured as output */
if (!(ctrl & 0x02))
{
data &= ~0x40;
data |= ((ctrl & 0x20) << 1);
}
/* Adjust port B TR state if configured as output */
if (!(ctrl & 0x04))
{
data &= ~0x08;
data |= ((ctrl & 0x40) >> 3);
}
}
else
{
/*
Bit Function
--------------
D7 : Port B Down pin input
D6 : Port B Up pin input
D5 : Port A TR pin input
D4 : Port A TL pin input
D3 : Port A Right pin input
D2 : Port A Left pin input
D1 : Port A Down pin input
D0 : Port A Up pin input
*/
data = (data & 0x3F) | ((data >> 2) & 0xC0);
/* Adjust port A TR state if configured as output */
if (!(ctrl & 0x01))
{
data &= ~0x20;
data |= ((ctrl & 0x10) << 1);
}
}
return data;
}
/*****************************************************************************
* Game Gear communication ports access *
* *
*****************************************************************************/
void io_gg_write(unsigned int offset, unsigned int data)
{
switch (offset)
{
case 1: /* Parallel data register */
io_reg[1] = data;
return;
case 2: /* Data direction register and NMI enable */
io_reg[2] = data;
return;
case 3: /* Transmit data buffer */
io_reg[3] = data;
return;
case 5: /* Serial control (bits 0-2 are read-only) */
io_reg[5] = data & 0xF8;
return;
case 6: /* PSG Stereo output control */
io_reg[6] = data;
SN76489_Config(Z80.cycles, config.psg_preamp, config.psgBoostNoise, data);
return;
default: /* Read-only */
return;
}
}
unsigned int io_gg_read(unsigned int offset)
{
switch (offset)
{
case 0: /* Mode Register */
return (io_reg[0] & ~(input.pad[0] & INPUT_START));
case 1: /* Parallel data register (not connected) */
return ((io_reg[1] & ~(io_reg[2] & 0x7F)) | (io_reg[2] & 0x7F));
case 2: /* Data direction register and NMI enable */
return io_reg[2];
case 3: /* Transmit data buffer */
return io_reg[3];
case 4: /* Receive data buffer */
return io_reg[4];
case 5: /* Serial control */
return io_reg[5];
default: /* Write-Only */
return 0xFF;
}
}

View File

@ -0,0 +1,68 @@
/***************************************************************************************
* Genesis Plus
* I/O controller
*
* Support for Master System (315-5216, 315-5237 & 315-5297), Game Gear & Mega Drive I/O chips
*
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code)
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _IO_CTRL_H_
#define _IO_CTRL_H_
#define IO_RESET_HI 0x10
#define IO_CONT1_HI 0x20
#define REGION_JAPAN_NTSC 0x00
#define REGION_JAPAN_PAL 0x40
#define REGION_USA 0x80
#define REGION_EUROPE 0xC0
/* Global variables */
extern uint8 io_reg[0x10];
extern uint8 region_code;
/* Function prototypes */
extern void io_init(void);
extern void io_reset(void);
extern void io_68k_write(unsigned int offset, unsigned int data);
extern unsigned int io_68k_read(unsigned int offset);
extern void io_z80_write(unsigned int offset, unsigned int data, unsigned int cycles);
extern unsigned int io_z80_read(unsigned int offset);
extern void io_gg_write(unsigned int offset, unsigned int data);
extern unsigned int io_gg_read(unsigned int offset);
#endif /* _IO_CTRL_H_ */

1139
genplus-gx32/core/loadrom.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,75 @@
/***************************************************************************************
* Genesis Plus
* ROM Loading Support
*
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code)
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#ifndef _LOADROM_H_
#define _LOADROM_H_
#define MAXROMSIZE (32*1024*1024)
typedef struct
{
char consoletype[18]; /* Genesis or Mega Drive */
char copyright[18]; /* Copyright message */
char domestic[50]; /* Domestic name of ROM */
char international[50]; /* International name of ROM */
char ROMType[4]; /* Boot ROM (BR), Educational (AL) or Game (GM) program */
char product[14]; /* Product serial number */
unsigned short checksum; /* ROM Checksum (header) */
unsigned short realchecksum; /* ROM Checksum (calculated) */
unsigned int romstart; /* ROM start address */
unsigned int romend; /* ROM end address */
char country[18]; /* Country flag */
uint16 peripherals; /* Supported peripherals */
} ROMINFO;
/* Global variables */
extern ROMINFO rominfo;
extern uint8 romtype;
/* Function prototypes */
extern int load_bios(void);
extern int load_rom(const char *filename);
extern void get_region(char *romheader);
extern char *get_company(void);
extern char *get_peripheral(int index);
extern void getrominfo(char *romheader);
#endif /* _LOADROM_H_ */

View File

@ -0,0 +1,389 @@
#ifndef M68K__HEADER
#define M68K__HEADER
/* ======================================================================== */
/* ========================= LICENSING & COPYRIGHT ======================== */
/* ======================================================================== */
/*
* MUSASHI
* Version 3.32
*
* A portable Motorola M680x0 processor emulation engine.
* Copyright Karl Stenerud. All rights reserved.
*
* This code may be freely used for non-commercial purposes as long as this
* copyright notice remains unaltered in the source code and any binary files
* containing this code in compiled form.
*
* All other licensing terms must be negotiated with the author
* (Karl Stenerud).
*
* The latest version of this code can be obtained at:
* http://kstenerud.cjb.net
*/
/* Modified by Eke-Eke for Genesis Plus GX:
- removed unused stuff to reduce memory usage / optimize execution (multiple CPU types support, NMI support, ...)
- moved stuff to compile statically in a single object file
- implemented support for global cycle count (shared by 68k & Z80 CPU)
- added support for interrupt latency (Sesame's Street Counting Cafe, Fatal Rewind)
- added proper cycle use on reset
- added cycle accurate timings for MUL/DIV instructions (thanks to Jorge Cwik !)
- fixed undocumented flags for DIV instructions (Blood Shot)
- added MAIN-CPU & SUB-CPU support for Mega CD emulation
*/
/* ======================================================================== */
/* ================================ INCLUDES ============================== */
/* ======================================================================== */
#include <setjmp.h>
#include "macros.h"
/* ======================================================================== */
/* ==================== ARCHITECTURE-DEPENDANT DEFINES ==================== */
/* ======================================================================== */
/* Check for > 32bit sizes */
#if UINT_MAX > 0xffffffff
#define M68K_INT_GT_32_BIT 1
#else
#define M68K_INT_GT_32_BIT 0
#endif
/* Data types used in this emulation core */
#undef sint8
#undef sint16
#undef sint32
#undef sint64
#undef uint8
#undef uint16
#undef uint32
#undef uint64
#undef sint
#undef uint
#define sint8 signed char /* ASG: changed from char to signed char */
#define sint16 signed short
#define sint32 signed int /* AWJ: changed from long to int */
#define uint8 unsigned char
#define uint16 unsigned short
#define uint32 unsigned int /* AWJ: changed from long to int */
/* signed and unsigned int must be at least 32 bits wide */
#define sint signed int
#define uint unsigned int
#if M68K_USE_64_BIT
#define sint64 signed long long
#define uint64 unsigned long long
#else
#define sint64 sint32
#define uint64 uint32
#endif /* M68K_USE_64_BIT */
/* Allow for architectures that don't have 8-bit sizes */
/*#if UCHAR_MAX == 0xff*/
#define MAKE_INT_8(A) (sint8)(A)
/*#else
#undef sint8
#define sint8 signed int
#undef uint8
#define uint8 unsigned int
INLINE sint MAKE_INT_8(uint value)
{
return (value & 0x80) ? value | ~0xff : value & 0xff;
}*/
/*#endif *//* UCHAR_MAX == 0xff */
/* Allow for architectures that don't have 16-bit sizes */
/*#if USHRT_MAX == 0xffff*/
#define MAKE_INT_16(A) (sint16)(A)
/*#else
#undef sint16
#define sint16 signed int
#undef uint16
#define uint16 unsigned int
INLINE sint MAKE_INT_16(uint value)
{
return (value & 0x8000) ? value | ~0xffff : value & 0xffff;
}*/
/*#endif *//* USHRT_MAX == 0xffff */
/* Allow for architectures that don't have 32-bit sizes */
/*#if UINT_MAX == 0xffffffff*/
#define MAKE_INT_32(A) (sint32)(A)
/*#else
#undef sint32
#define sint32 signed int
#undef uint32
#define uint32 unsigned int
INLINE sint MAKE_INT_32(uint value)
{
return (value & 0x80000000) ? value | ~0xffffffff : value & 0xffffffff;
}*/
/*#endif *//* UINT_MAX == 0xffffffff */
/* ======================================================================== */
/* ============================ GENERAL DEFINES =========================== */
/* ======================================================================== */
/* There are 7 levels of interrupt to the 68K.
* A transition from < 7 to 7 will cause a non-maskable interrupt (NMI).
*/
#define M68K_IRQ_NONE 0
#define M68K_IRQ_1 1
#define M68K_IRQ_2 2
#define M68K_IRQ_3 3
#define M68K_IRQ_4 4
#define M68K_IRQ_5 5
#define M68K_IRQ_6 6
#define M68K_IRQ_7 7
/* Special interrupt acknowledge values.
* Use these as special returns from the interrupt acknowledge callback
* (specified later in this header).
*/
/* Causes an interrupt autovector (0x18 + interrupt level) to be taken.
* This happens in a real 68K if VPA or AVEC is asserted during an interrupt
* acknowledge cycle instead of DTACK.
*/
#define M68K_INT_ACK_AUTOVECTOR 0xffffffff
/* Causes the spurious interrupt vector (0x18) to be taken
* This happens in a real 68K if BERR is asserted during the interrupt
* acknowledge cycle (i.e. no devices responded to the acknowledge).
*/
#define M68K_INT_ACK_SPURIOUS 0xfffffffe
/* Registers used by m68k_get_reg() and m68k_set_reg() */
typedef enum
{
/* Real registers */
M68K_REG_D0, /* Data registers */
M68K_REG_D1,
M68K_REG_D2,
M68K_REG_D3,
M68K_REG_D4,
M68K_REG_D5,
M68K_REG_D6,
M68K_REG_D7,
M68K_REG_A0, /* Address registers */
M68K_REG_A1,
M68K_REG_A2,
M68K_REG_A3,
M68K_REG_A4,
M68K_REG_A5,
M68K_REG_A6,
M68K_REG_A7,
M68K_REG_PC, /* Program Counter */
M68K_REG_SR, /* Status Register */
M68K_REG_SP, /* The current Stack Pointer (located in A7) */
M68K_REG_USP, /* User Stack Pointer */
M68K_REG_ISP, /* Interrupt Stack Pointer */
#if M68K_EMULATE_PREFETCH
/* Assumed registers */
/* These are cheat registers which emulate the 1-longword prefetch
* present in the 68000 and 68010.
*/
M68K_REG_PREF_ADDR, /* Last prefetch address */
M68K_REG_PREF_DATA, /* Last prefetch data */
#endif
/* Convenience registers */
M68K_REG_IR /* Instruction register */
} m68k_register_t;
/* 68k memory map structure */
typedef struct
{
unsigned char *base; /* memory-based access (ROM, RAM) */
unsigned int (*read8)(unsigned int address); /* I/O byte read access */
unsigned int (*read16)(unsigned int address); /* I/O word read access */
void (*write8)(unsigned int address, unsigned int data); /* I/O byte write access */
void (*write16)(unsigned int address, unsigned int data); /* I/O word write access */
} cpu_memory_map;
/* 68k idle loop detection */
typedef struct
{
uint pc;
uint cycle;
uint detected;
} cpu_idle_t;
typedef struct
{
cpu_memory_map memory_map[256]; /* memory mapping */
cpu_idle_t poll; /* polling detection */ // 0x1400
uint cycles; /* current master cycle count */
uint cycle_end; /* aimed master cycle count for current execution frame */
uint dar[16]; /* Data and Address Registers */
uint pc; /* Program Counter */
uint sp[5]; /* User and Interrupt Stack Pointers */
uint ir; /* Instruction Register */
uint t1_flag; /* Trace 1 */
uint s_flag; /* Supervisor */
uint x_flag; /* Extend */
uint n_flag; /* Negative */
uint not_z_flag; /* Zero, inverted for speedups */
uint v_flag; /* Overflow */
uint c_flag; /* Carry */
uint int_mask; /* I0-I2 */
uint int_level; /* State of interrupt pins IPL0-IPL2 -- ASG: changed from ints_pending */
uint stopped; /* Stopped state */
uint pref_addr; /* Last prefetch address */
uint pref_data; /* Data in the prefetch queue */
uint instr_mode; /* Stores whether we are in instruction mode or group 0/1 exception mode */
uint run_mode; /* Stores whether we are processing a reset, bus error, address error, or something else */
uint aerr_enabled; /* Enables/deisables address error checks at runtime */
jmp_buf aerr_trap; /* Address error jump */
uint aerr_address; /* Address error location */
uint aerr_write_mode; /* Address error write mode */
uint aerr_fc; /* Address error FC code */
uint tracing; /* Tracing enable flag */
uint address_space; /* Current FC code */
/* Callbacks to host */
int (*int_ack_callback)(int int_line); /* Interrupt Acknowledge */
void (*reset_instr_callback)(void); /* Called when a RESET instruction is encountered */
int (*tas_instr_callback)(void); /* Called when a TAS instruction is encountered, allows / disallows writeback */
void (*set_fc_callback)(unsigned int new_fc); /* Called when the CPU function code changes */
} m68ki_cpu_core;
/* CPU cores */
extern m68ki_cpu_core m68k;
extern m68ki_cpu_core s68k;
/* ======================================================================== */
/* ============================== CALLBACKS =============================== */
/* ======================================================================== */
/* These functions allow you to set callbacks to the host when specific events
* occur. Note that you must enable the corresponding value in m68kconf.h
* in order for these to do anything useful.
* Note: I have defined default callbacks which are used if you have enabled
* the corresponding #define in m68kconf.h but either haven't assigned a
* callback or have assigned a callback of NULL.
*/
#if M68K_EMULATE_INT_ACK == OPT_ON
/* Set the callback for an interrupt acknowledge.
* You must enable M68K_EMULATE_INT_ACK in m68kconf.h.
* The CPU will call the callback with the interrupt level being acknowledged.
* The host program must return either a vector from 0x02-0xff, or one of the
* special interrupt acknowledge values specified earlier in this header.
* If this is not implemented, the CPU will always assume an autovectored
* interrupt, and will automatically clear the interrupt request when it
* services the interrupt.
* Default behavior: return M68K_INT_ACK_AUTOVECTOR.
*/
void m68k_set_int_ack_callback(int (*callback)(int int_level));
#endif
#if M68K_EMULATE_RESET == OPT_ON
/* Set the callback for the RESET instruction.
* You must enable M68K_EMULATE_RESET in m68kconf.h.
* The CPU calls this callback every time it encounters a RESET instruction.
* Default behavior: do nothing.
*/
void m68k_set_reset_instr_callback(void (*callback)(void));
#endif
#if M68K_TAS_HAS_CALLBACK == OPT_ON
/* Set the callback for the TAS instruction.
* You must enable M68K_TAS_HAS_CALLBACK in m68kconf.h.
* The CPU calls this callback every time it encounters a TAS instruction.
* Default behavior: return 1, allow writeback.
*/
void m68k_set_tas_instr_callback(int (*callback)(void));
#endif
#if M68K_EMULATE_FC == OPT_ON
/* Set the callback for CPU function code changes.
* You must enable M68K_EMULATE_FC in m68kconf.h.
* The CPU calls this callback with the function code before every memory
* access to set the CPU's function code according to what kind of memory
* access it is (supervisor/user, program/data and such).
* Default behavior: do nothing.
*/
void m68k_set_fc_callback(void (*callback)(unsigned int new_fc));
#endif
/* ======================================================================== */
/* ====================== FUNCTIONS TO ACCESS THE CPU ===================== */
/* ======================================================================== */
/* Do whatever initialisations the core requires. Should be called
* at least once at init time.
*/
extern void m68k_init(void);
extern void s68k_init(void);
/* Pulse the RESET pin on the CPU.
* You *MUST* reset the CPU at least once to initialize the emulation
*/
extern void m68k_pulse_reset(void);
extern void s68k_pulse_reset(void);
/* Run until given cycle count is reached */
extern void m68k_run(unsigned int cycles);
extern void s68k_run(unsigned int cycles);
/* Set the IPL0-IPL2 pins on the CPU (IRQ).
* A transition from < 7 to 7 will cause a non-maskable interrupt (NMI).
* Setting IRQ to 0 will clear an interrupt request.
*/
extern void m68k_set_irq(unsigned int int_level);
extern void m68k_set_irq_delay(unsigned int int_level);
extern void m68k_update_irq(unsigned int mask);
extern void s68k_update_irq(unsigned int mask);
/* Halt the CPU as if you pulsed the HALT pin. */
extern void m68k_pulse_halt(void);
extern void m68k_clear_halt(void);
extern void s68k_pulse_halt(void);
extern void s68k_clear_halt(void);
/* Peek at the internals of a CPU context. This can either be a context
* retrieved using m68k_get_context() or the currently running context.
* If context is NULL, the currently running CPU context will be used.
*/
extern unsigned int m68k_get_reg(m68k_register_t reg);
extern unsigned int s68k_get_reg(m68k_register_t reg);
/* Poke values into the internals of the currently running CPU context */
extern void m68k_set_reg(m68k_register_t reg, unsigned int value);
extern void s68k_set_reg(m68k_register_t reg, unsigned int value);
/* ======================================================================== */
/* ============================== END OF FILE ============================= */
/* ======================================================================== */
#endif /* M68K__HEADER */

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