octoshock!
This commit is contained in:
parent
de5eaf3c4b
commit
5f6bb8d8fd
|
@ -259,8 +259,7 @@ namespace BizHawk.Client.Common
|
|||
nextEmulator = new PSP(nextComm, file.Name);
|
||||
break;
|
||||
case "PSX":
|
||||
nextEmulator = new Octoshock(nextComm);
|
||||
(nextEmulator as Octoshock).LoadCuePath(file.CanonicalFullPath);
|
||||
nextEmulator = new Octoshock(nextComm, disc);
|
||||
nextEmulator.CoreComm.RomStatusDetails = "PSX etc.";
|
||||
break;
|
||||
case "PCE":
|
||||
|
|
|
@ -533,8 +533,8 @@
|
|||
<Compile Include="Consoles\Sega\SMS\VDP.Tables.cs" />
|
||||
<Compile Include="Consoles\Sony\PSP\PPSSPPDll.cs" />
|
||||
<Compile Include="Consoles\Sony\PSP\PSP.cs" />
|
||||
<Compile Include="Consoles\Sony\PSX\LibMednahawkDll.cs" />
|
||||
<Compile Include="Consoles\Sony\PSX\Octoshock.cs" />
|
||||
<Compile Include="Consoles\Sony\PSX\OctoshockDll.cs" />
|
||||
<Compile Include="Consoles\WonderSwan\BizSwan.cs" />
|
||||
<Compile Include="Consoles\WonderSwan\WonderSwan.cs" />
|
||||
<Compile Include="CoreInventory.cs" />
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
public unsafe static class LibMednahawkDll
|
||||
{
|
||||
public enum eProp : int
|
||||
{
|
||||
GetPtr_FramebufferPointer,
|
||||
GetPtr_FramebufferPitchPixels,
|
||||
GetPtr_FramebufferWidth,
|
||||
GetPtr_FramebufferHeight,
|
||||
SetPtr_FopenCallback,
|
||||
SetPtr_FcloseCallback,
|
||||
SetPtr_FopCallback
|
||||
}
|
||||
|
||||
public enum FOP: int
|
||||
{
|
||||
FOP_fread,
|
||||
FOP_fwrite,
|
||||
FOP_fflush,
|
||||
FOP_fseeko,
|
||||
FOP_ftello,
|
||||
FOP_ferror,
|
||||
FOP_clearerr,
|
||||
FOP_size
|
||||
};
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate IntPtr t_FopenCallback(string fname, string mode);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate int t_FcloseCallback(IntPtr fp);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate long t_FopCallback(int op, IntPtr ptr, long a, long b, IntPtr fp);
|
||||
|
||||
[DllImport("libmednahawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern IntPtr dll_GetPropPtr(eProp prop);
|
||||
|
||||
[DllImport("libmednahawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void dll_SetPropPtr(eProp prop, IntPtr val);
|
||||
|
||||
[DllImport("libmednahawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
public static extern bool dll_Initialize();
|
||||
|
||||
[DllImport("libmednahawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void psx_FrameAdvance();
|
||||
|
||||
[DllImport("libmednahawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
public static extern bool psx_LoadCue(string path);
|
||||
}
|
|
@ -1,4 +1,19 @@
|
|||
using System;
|
||||
//TODO hook up newer file ID stuff, think about how to combine it with the disc ID
|
||||
//TODO not liking the name ShockFramebufferJob
|
||||
//TODO change display manager to not require 0xFF alpha channel set on videoproviders. check gdi+ and opengl! this will get us a speedup in some places
|
||||
//TODO Disc.Structure.Sessions[0].length_aba was 0
|
||||
|
||||
//looks like we can have (in NTSC) framebuffer dimensions like this:
|
||||
//width: 280, 350, 700
|
||||
//height: 240, 480
|
||||
//mednafen's strategy is to put everything in a 320x240 and scale it up 3x to 960x720 by default (which is adequate to contain the largest PSX framebuffer)
|
||||
//heres my strategy.
|
||||
//1. we should have a native output mode, for debugging. but most users wont want it (massively distorted resolutions are common in games)
|
||||
//2. do the right thing:
|
||||
//always double a height of 240, and double a width of 280 or 350. For 280, float content in center screen.
|
||||
//but lets not do this til we're on an upgraded mednafen
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
@ -10,7 +25,7 @@ using BizHawk.Emulation.Common;
|
|||
namespace BizHawk.Emulation.Cores.Sony.PSX
|
||||
{
|
||||
[CoreAttributes(
|
||||
"MednafenPSX",
|
||||
"Octoshock",
|
||||
"Ryphecha",
|
||||
isPorted: true,
|
||||
isReleased: false
|
||||
|
@ -31,41 +46,141 @@ namespace BizHawk.Emulation.Cores.Sony.PSX
|
|||
public bool StartAsyncSound() { return true; }
|
||||
public void EndAsyncSound() { }
|
||||
|
||||
public static bool CheckIsPSX(DiscSystem.Disc disc)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
byte[] buf = new byte[59];
|
||||
disc.ReadLBA_2352_Flat(0x24D8, buf, 0, 59);
|
||||
string sig = System.Text.ASCIIEncoding.ASCII.GetString(buf);
|
||||
|
||||
//this string is considered highly unlikely to exist anywhere besides a psx disc
|
||||
if (sig == " Licensed by Sony Computer Entertainment")
|
||||
ret = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//we can only have one active core at a time, due to the lib being so static.
|
||||
//so we'll track the current one here and detach the previous one whenever a new one is booted up.
|
||||
static Octoshock CurrOctoshockCore;
|
||||
|
||||
IntPtr psx;
|
||||
DiscSystem.Disc disc;
|
||||
DiscInterface discInterface;
|
||||
|
||||
bool disposed = false;
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed) return;
|
||||
disposed = true;
|
||||
|
||||
//BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_audio_sample(null);
|
||||
OctoshockDll.shock_Destroy(psx);
|
||||
psx = IntPtr.Zero;
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
public Octoshock(CoreComm comm)
|
||||
class DiscInterface : IDisposable
|
||||
{
|
||||
public DiscInterface(DiscSystem.Disc disc)
|
||||
{
|
||||
this.Disc = disc;
|
||||
cbReadTOC = ShockDisc_ReadTOC;
|
||||
cbReadLBA = ShockDisc_ReadLBA2448;
|
||||
OctoshockDll.shock_CreateDisc(out OctoshockHandle, IntPtr.Zero, disc.LBACount, cbReadTOC, cbReadLBA, true);
|
||||
}
|
||||
|
||||
OctoshockDll.ShockDisc_ReadTOC cbReadTOC;
|
||||
OctoshockDll.ShockDisc_ReadLBA cbReadLBA;
|
||||
|
||||
public DiscSystem.Disc Disc;
|
||||
public IntPtr OctoshockHandle;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
OctoshockDll.shock_DestroyDisc(OctoshockHandle);
|
||||
OctoshockHandle = IntPtr.Zero;
|
||||
}
|
||||
|
||||
int ShockDisc_ReadTOC(IntPtr opaque, OctoshockDll.ShockTOC* read_target, OctoshockDll.ShockTOCTrack* tracks101)
|
||||
{
|
||||
read_target->disc_type = 1; //hardcoded in octoshock
|
||||
read_target->first_track = (byte)Disc.TOCRaw.FirstRecordedTrackNumber; //i _think_ thats what is meant here
|
||||
read_target->last_track = (byte)Disc.TOCRaw.LastRecordedTrackNumber; //i _think_ thats what is meant here
|
||||
|
||||
tracks101[0].lba = tracks101[0].adr = tracks101[0].control = 0;
|
||||
|
||||
for (int i = 1; i < 100; i++)
|
||||
{
|
||||
var item = Disc.TOCRaw.TOCItems[i];
|
||||
tracks101[i].adr = 1; //not sure what this is
|
||||
tracks101[i].lba = (uint)item.LBATimestamp.Sector;
|
||||
tracks101[i].control = (byte)item.Control;
|
||||
}
|
||||
|
||||
////the lead-out track is to be synthesized
|
||||
tracks101[read_target->last_track + 1].adr = 1;
|
||||
tracks101[read_target->last_track + 1].control = 0;
|
||||
tracks101[read_target->last_track + 1].lba = (uint)Disc.TOCRaw.LeadoutTimestamp.Sector;
|
||||
////laaaame
|
||||
//tracks101[read_target->last_track + 1].lba =
|
||||
// (uint)(
|
||||
// Disc.Structure.Sessions[0].Tracks[read_target->last_track - 1].Start_ABA //AUGH. see comment in Start_ABA
|
||||
// + Disc.Structure.Sessions[0].Tracks[read_target->last_track - 1].LengthInSectors
|
||||
// - 150
|
||||
// );
|
||||
|
||||
//element 100 is to be copied as the lead-out track
|
||||
tracks101[100] = tracks101[read_target->last_track + 1];
|
||||
|
||||
return OctoshockDll.SHOCK_OK;
|
||||
}
|
||||
|
||||
byte[] SectorBuffer = new byte[2352];
|
||||
|
||||
int ShockDisc_ReadLBA2448(IntPtr opaque, int lba, void* dst)
|
||||
{
|
||||
//lets you check subcode generation by logging it and checking against the CCD subcode
|
||||
bool subcodeLog = false;
|
||||
bool readLog = false;
|
||||
|
||||
if (subcodeLog) Console.Write("{0}|", lba);
|
||||
else if (readLog) Console.WriteLine("Read Sector: " + lba);
|
||||
|
||||
Disc.ReadLBA_2352(lba, SectorBuffer, 0);
|
||||
Marshal.Copy(SectorBuffer, 0, new IntPtr(dst), 2352);
|
||||
Disc.ReadLBA_SectorEntry(lba).SubcodeSector.ReadSubcodeDeinterleaved(SectorBuffer, 0);
|
||||
Marshal.Copy(SectorBuffer, 0, new IntPtr((byte*)dst + 2352), 96);
|
||||
|
||||
if (subcodeLog)
|
||||
{
|
||||
for (int i = 0; i < 24; i++)
|
||||
Console.Write("{0:X2}", *((byte*)dst + 2352 + i));
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
|
||||
return OctoshockDll.SHOCK_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Octoshock(CoreComm comm, DiscSystem.Disc disc)
|
||||
{
|
||||
ServiceProvider = new BasicServiceProvider(this);
|
||||
var domains = new List<MemoryDomain>();
|
||||
CoreComm = comm;
|
||||
VirtualWidth = BufferWidth = 256;
|
||||
BufferHeight = 192;
|
||||
|
||||
Attach();
|
||||
|
||||
this.disc = disc;
|
||||
discInterface = new DiscInterface(disc);
|
||||
|
||||
//determine region of the provided disc
|
||||
OctoshockDll.ShockDiscInfo discInfo;
|
||||
OctoshockDll.shock_AnalyzeDisc(discInterface.OctoshockHandle, out discInfo);
|
||||
|
||||
//try to acquire the appropriate firmware
|
||||
string firmwareRegion = "U";
|
||||
if(discInfo.region == OctoshockDll.eRegion.EU) firmwareRegion = "E";
|
||||
if (discInfo.region == OctoshockDll.eRegion.JP) firmwareRegion = "J";
|
||||
byte[] firmware = comm.CoreFileProvider.GetFirmware("PSX", "U", true, "A PSX `" + firmwareRegion + "` region bios file is required");
|
||||
|
||||
//create the instance
|
||||
fixed (byte* pFirmware = firmware)
|
||||
OctoshockDll.shock_Create(out psx, discInfo.region, pFirmware);
|
||||
|
||||
OctoshockDll.shock_OpenTray(psx);
|
||||
OctoshockDll.shock_SetDisc(psx, discInterface.OctoshockHandle);
|
||||
OctoshockDll.shock_CloseTray(psx);
|
||||
OctoshockDll.shock_PowerOn(psx);
|
||||
}
|
||||
|
||||
public IEmulatorServiceProvider ServiceProvider { get; private set; }
|
||||
|
@ -76,146 +191,21 @@ namespace BizHawk.Emulation.Cores.Sony.PSX
|
|||
if (CurrOctoshockCore != null)
|
||||
CurrOctoshockCore.Dispose();
|
||||
CurrOctoshockCore = this;
|
||||
|
||||
//the psx instance cant be created until the desired region is known, which needs a disc, so we need the dll static attached first
|
||||
}
|
||||
|
||||
//note to self: try to make mednafen have file IO callbacks into here: open, close, read, write.
|
||||
//we'll trick mednafen into using a virtual filesystem and track the fake files internally
|
||||
|
||||
public void LoadCuePath(string path)
|
||||
{
|
||||
Attach();
|
||||
|
||||
//note to self:
|
||||
//consider loading a fake cue, which is generated by our Disc class, and converting all reads to the fake bin to reads into the disc class.
|
||||
//thatd be pretty cool.... (may need to add an absolute byte range read method into the disc class, which can traverse the requisite LBAs)...
|
||||
//...but... are there other ideas?
|
||||
LibMednahawkDll.psx_LoadCue(path);
|
||||
}
|
||||
//public void LoadCuePath(string path)
|
||||
//{
|
||||
// Attach();
|
||||
// DiscSystem.Disc.FromCCDPath
|
||||
//}
|
||||
|
||||
|
||||
static Octoshock()
|
||||
{
|
||||
LibMednahawkDll.dll_Initialize();
|
||||
|
||||
FopenCallback = new LibMednahawkDll.t_FopenCallback(FopenCallbackProc);
|
||||
FcloseCallback = new LibMednahawkDll.t_FcloseCallback(FcloseCallbackProc);
|
||||
FopCallback = new LibMednahawkDll.t_FopCallback(FopCallbackProc);
|
||||
LibMednahawkDll.dll_SetPropPtr(LibMednahawkDll.eProp.SetPtr_FopenCallback, Marshal.GetFunctionPointerForDelegate(FopenCallback));
|
||||
LibMednahawkDll.dll_SetPropPtr(LibMednahawkDll.eProp.SetPtr_FcloseCallback, Marshal.GetFunctionPointerForDelegate(FcloseCallback));
|
||||
LibMednahawkDll.dll_SetPropPtr(LibMednahawkDll.eProp.SetPtr_FopCallback, Marshal.GetFunctionPointerForDelegate(FopCallback));
|
||||
}
|
||||
|
||||
static LibMednahawkDll.t_FopenCallback FopenCallback;
|
||||
static LibMednahawkDll.t_FcloseCallback FcloseCallback;
|
||||
static LibMednahawkDll.t_FopCallback FopCallback;
|
||||
|
||||
class VirtualFile : IDisposable
|
||||
{
|
||||
public Stream stream;
|
||||
public int id;
|
||||
public void Dispose()
|
||||
{
|
||||
if(stream != null) stream.Dispose();
|
||||
stream = null;
|
||||
}
|
||||
}
|
||||
|
||||
static Dictionary<int, VirtualFile> VirtualFiles = new Dictionary<int, VirtualFile>();
|
||||
|
||||
static IntPtr FopenCallbackProc(string fname, string mode)
|
||||
{
|
||||
throw new NotImplementedException("Antiquated CoreComm.PSX_FirmwaresPath must be replaced by CoreFileProvider");
|
||||
|
||||
// TODO - this should be using the CoreComm.CoreFileProvider interfaces
|
||||
|
||||
//TODO - probably this should never really fail. but for now, mednafen tries to create a bunch of junk, so just return failure for files which cant be opened
|
||||
/*
|
||||
if (fname.StartsWith("$psx"))
|
||||
{
|
||||
string[] parts = fname.Split('/');
|
||||
if (parts[0] != "$psx") throw new InvalidOperationException("Octoshock using some weird path we dont handle yet");
|
||||
if (parts[1] == "firmware")
|
||||
{
|
||||
//fname = Path.Combine(CurrOctoshockCore.CoreComm.PSX_FirmwaresPath, parts[2]);
|
||||
if (!File.Exists(fname))
|
||||
{
|
||||
System.Windows.Forms.MessageBox.Show("the Octoshock core is referencing a firmware file which could not be found. Please make sure it's in your configured PSX firmwares folder. The referenced filename is: " + parts[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Stream stream = null;
|
||||
if (mode == "rb") { if (File.Exists(fname)) stream = new FileStream(fname, FileMode.Open, FileAccess.Read, FileShare.Read); }
|
||||
else if (mode == "wb") stream = new FileStream(fname, FileMode.Create, FileAccess.Write, FileShare.Read);
|
||||
else throw new InvalidOperationException("unexpected virtual file mode from libmednahawk");
|
||||
|
||||
if (stream == null) return IntPtr.Zero;
|
||||
|
||||
//find a free id. dont use 0 because it looks like an error
|
||||
int id = 1;
|
||||
for (; ; )
|
||||
{
|
||||
RETRY:
|
||||
foreach (var vfid in VirtualFiles.Keys)
|
||||
if (vfid == id)
|
||||
{
|
||||
id++;
|
||||
goto RETRY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
var ret = new VirtualFile();
|
||||
ret.id = id;
|
||||
ret.stream = stream;
|
||||
|
||||
VirtualFiles[ret.id] = ret;
|
||||
return new IntPtr(ret.id);
|
||||
*/
|
||||
}
|
||||
static int FcloseCallbackProc(IntPtr fp)
|
||||
{
|
||||
int id = fp.ToInt32();
|
||||
VirtualFiles[id].stream.Dispose();
|
||||
VirtualFiles.Remove(id);
|
||||
return 0;
|
||||
}
|
||||
static byte[] fiobuf = new byte[10*1024];
|
||||
static long FopCallbackProc(int op, IntPtr ptr, long a, long b, IntPtr fp)
|
||||
{
|
||||
var vf = VirtualFiles[fp.ToInt32()];
|
||||
int amt = (int)(a*b);
|
||||
switch ((LibMednahawkDll.FOP)op)
|
||||
{
|
||||
case LibMednahawkDll.FOP.FOP_clearerr: return 0;
|
||||
case LibMednahawkDll.FOP.FOP_ferror: return 0;
|
||||
case LibMednahawkDll.FOP.FOP_fflush: vf.stream.Flush(); return 0;
|
||||
case LibMednahawkDll.FOP.FOP_fread:
|
||||
{
|
||||
if(fiobuf.Length < amt)
|
||||
fiobuf = new byte[amt];
|
||||
int read = vf.stream.Read(fiobuf, 0, amt);
|
||||
Marshal.Copy(fiobuf, 0, ptr, amt);
|
||||
return read / a;
|
||||
}
|
||||
case LibMednahawkDll.FOP.FOP_fseeko:
|
||||
vf.stream.Seek(a, (SeekOrigin)b);
|
||||
return vf.stream.Position;
|
||||
case LibMednahawkDll.FOP.FOP_ftello:
|
||||
return vf.stream.Position;
|
||||
case LibMednahawkDll.FOP.FOP_fwrite:
|
||||
{
|
||||
if (fiobuf.Length < amt)
|
||||
fiobuf = new byte[amt];
|
||||
Marshal.Copy(fiobuf, 0, ptr, amt);
|
||||
vf.stream.Write(fiobuf, 0, amt);
|
||||
return (int)b;
|
||||
}
|
||||
case LibMednahawkDll.FOP.FOP_size: return vf.stream.Length;
|
||||
default:
|
||||
throw new InvalidOperationException("INESTIMABLE GOPHER");
|
||||
}
|
||||
}
|
||||
|
||||
[FeatureNotImplemented]
|
||||
public void ResetCounters()
|
||||
|
@ -226,32 +216,33 @@ namespace BizHawk.Emulation.Cores.Sony.PSX
|
|||
|
||||
public void FrameAdvance(bool render, bool rendersound)
|
||||
{
|
||||
LibMednahawkDll.psx_FrameAdvance();
|
||||
|
||||
OctoshockDll.shock_Step(psx, OctoshockDll.eShockStep.Frame);
|
||||
|
||||
OctoshockDll.ShockFramebufferJob fb = new OctoshockDll.ShockFramebufferJob();
|
||||
OctoshockDll.shock_GetFramebuffer(psx, ref fb);
|
||||
|
||||
//Console.WriteLine(fb.height);
|
||||
|
||||
if (render == false) return;
|
||||
|
||||
int w = LibMednahawkDll.dll_GetPropPtr(LibMednahawkDll.eProp.GetPtr_FramebufferWidth).ToInt32();
|
||||
int h = LibMednahawkDll.dll_GetPropPtr(LibMednahawkDll.eProp.GetPtr_FramebufferHeight).ToInt32();
|
||||
int p = LibMednahawkDll.dll_GetPropPtr(LibMednahawkDll.eProp.GetPtr_FramebufferPitchPixels).ToInt32();
|
||||
IntPtr iptr = LibMednahawkDll.dll_GetPropPtr(LibMednahawkDll.eProp.GetPtr_FramebufferPointer);
|
||||
void* ptr = iptr.ToPointer();
|
||||
|
||||
|
||||
VirtualWidth = BufferWidth = w;
|
||||
int w = fb.width;
|
||||
int h = fb.height;
|
||||
BufferWidth = w;
|
||||
BufferHeight = h;
|
||||
|
||||
int len = w*h;
|
||||
if (frameBuffer.Length != len)
|
||||
{
|
||||
Console.WriteLine("PSX FB size: {0},{1}", fb.width, fb.height);
|
||||
frameBuffer = new int[len];
|
||||
}
|
||||
|
||||
//todo - we could do the reformatting in the PSX core
|
||||
//better yet, we could send a buffer into the psx core before frame advance to use for outputting video to
|
||||
|
||||
for (int y = 0, i = 0; y < h; y++)
|
||||
for (int x = 0; x < w; x++, i++)
|
||||
{
|
||||
frameBuffer[i] = (int)unchecked(((int*)ptr)[y * p + x] | (int)0xFF000000);
|
||||
}
|
||||
fixed (int* ptr = frameBuffer)
|
||||
{
|
||||
fb.ptr = ptr;
|
||||
OctoshockDll.shock_GetFramebuffer(psx, ref fb);
|
||||
//alpha channel is added in c++, right now. wish we didnt have to do it at all
|
||||
}
|
||||
}
|
||||
|
||||
[FeatureNotImplemented]
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
//API TODO
|
||||
//get rid of the 2048 byte reader
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
public unsafe static class OctoshockDll
|
||||
{
|
||||
public enum eRegion : int
|
||||
{
|
||||
JP = 0,
|
||||
NA = 1,
|
||||
EU = 2,
|
||||
NONE = 3 //TODO - whats the difference between unset, and region unknown?
|
||||
}
|
||||
|
||||
public enum eShockStep
|
||||
{
|
||||
Frame
|
||||
};
|
||||
|
||||
public const int SHOCK_OK = 0;
|
||||
public const int SHOCK_ERROR = -1;
|
||||
public const int SHOCK_NOCANDO = -2;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct ShockDiscInfo
|
||||
{
|
||||
public eRegion region;
|
||||
public unsafe fixed sbyte id[5]; //SCEI, SCEA, SCEE, etc. with null terminator
|
||||
};
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct ShockTOCTrack
|
||||
{
|
||||
public byte adr;
|
||||
public byte control;
|
||||
public uint lba;
|
||||
};
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct ShockTOC
|
||||
{
|
||||
public byte first_track;
|
||||
public byte last_track;
|
||||
public byte disc_type;
|
||||
};
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct ShockFramebufferJob
|
||||
{
|
||||
public int width, height;
|
||||
public void* ptr;
|
||||
};
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate int ShockDisc_ReadTOC(IntPtr opaque, ShockTOC* read_target, ShockTOCTrack* tracks101);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate int ShockDisc_ReadLBA(IntPtr opaque, int lba, void* dst);
|
||||
|
||||
[DllImport("octoshock.dll")]
|
||||
public static extern int shock_CreateDisc(out IntPtr outDisc, IntPtr Opaque, int lbaCount, ShockDisc_ReadTOC ReadTOC, ShockDisc_ReadLBA ReadLBA2448, bool suppliesDeinterleavedSubcode);
|
||||
|
||||
[DllImport("octoshock.dll")]
|
||||
public static extern int shock_DestroyDisc(IntPtr disc);
|
||||
|
||||
[DllImport("octoshock.dll")]
|
||||
public static extern int shock_AnalyzeDisc(IntPtr disc, out ShockDiscInfo info);
|
||||
|
||||
[DllImport("octoshock.dll")]
|
||||
public static extern int shock_Create(out IntPtr psx, eRegion region, void* firmware512k);
|
||||
|
||||
[DllImport("octoshock.dll")]
|
||||
public static extern int shock_Destroy(IntPtr psx);
|
||||
|
||||
[DllImport("octoshock.dll")]
|
||||
public static extern int shock_PowerOn(IntPtr psx);
|
||||
|
||||
[DllImport("octoshock.dll")]
|
||||
public static extern int shock_PowerOff(IntPtr psx);
|
||||
|
||||
[DllImport("octoshock.dll")]
|
||||
public static extern int shock_OpenTray(IntPtr psx);
|
||||
|
||||
[DllImport("octoshock.dll")]
|
||||
public static extern int shock_SetDisc(IntPtr psx, IntPtr disc);
|
||||
|
||||
[DllImport("octoshock.dll")]
|
||||
public static extern int shock_CloseTray(IntPtr psx);
|
||||
|
||||
[DllImport("octoshock.dll")]
|
||||
public static extern int shock_Step(IntPtr psx, eShockStep step);
|
||||
|
||||
[DllImport("octoshock.dll")]
|
||||
public static extern int shock_GetFramebuffer(IntPtr psx, ref ShockFramebufferJob fb);
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,106 @@
|
|||
// TODO/WIP
|
||||
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include "mednafen.h"
|
||||
#include "Stream.h"
|
||||
#include "FileStream.h"
|
||||
|
||||
#include <trio/trio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#define fseeko fseek
|
||||
#define ftello ftell
|
||||
|
||||
FileStream::FileStream(const char *path, const int mode): OpenedMode(mode)
|
||||
{
|
||||
if(!(fp = fopen(path, (mode == FileStream::MODE_WRITE) ? "wb" : "rb")))
|
||||
{
|
||||
ErrnoHolder ene(errno);
|
||||
|
||||
throw(MDFN_Error(ene.Errno(), _("Error opening file %s"), ene.StrError()));
|
||||
}
|
||||
}
|
||||
|
||||
FileStream::~FileStream()
|
||||
{
|
||||
}
|
||||
|
||||
uint64 FileStream::attributes(void)
|
||||
{
|
||||
uint64 ret = ATTRIBUTE_SEEKABLE;
|
||||
|
||||
switch(OpenedMode)
|
||||
{
|
||||
case FileStream::MODE_READ:
|
||||
ret |= ATTRIBUTE_READABLE;
|
||||
break;
|
||||
case FileStream::MODE_WRITE_SAFE:
|
||||
case FileStream::MODE_WRITE:
|
||||
ret |= ATTRIBUTE_WRITEABLE;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64 FileStream::read(void *data, uint64 count, bool error_on_eos)
|
||||
{
|
||||
return fread(data, 1, count, fp);
|
||||
}
|
||||
|
||||
void FileStream::write(const void *data, uint64 count)
|
||||
{
|
||||
fwrite(data, 1, count, fp);
|
||||
}
|
||||
|
||||
void FileStream::seek(int64 offset, int whence)
|
||||
{
|
||||
fseeko(fp, offset, whence);
|
||||
}
|
||||
|
||||
int64 FileStream::tell(void)
|
||||
{
|
||||
return ftello(fp);
|
||||
}
|
||||
|
||||
int64 FileStream::size(void) {
|
||||
struct stat buf;
|
||||
|
||||
fstat(fileno(fp), &buf);
|
||||
|
||||
return(buf.st_size);
|
||||
}
|
||||
|
||||
void FileStream::close(void)
|
||||
{
|
||||
if(!fp)
|
||||
return;
|
||||
|
||||
FILE *tmp = fp;
|
||||
fp = NULL;
|
||||
fclose(tmp);
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
// TODO/WIP
|
||||
|
||||
#ifndef __MDFN_FILESTREAM_H
|
||||
#define __MDFN_FILESTREAM_H
|
||||
|
||||
#include "Stream.h"
|
||||
#include "FileWrapper.h"
|
||||
|
||||
class FileStream : public Stream
|
||||
{
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
MODE_READ = FileWrapper::MODE_READ,
|
||||
MODE_WRITE = FileWrapper::MODE_WRITE,
|
||||
MODE_WRITE_SAFE = FileWrapper::MODE_WRITE_SAFE,
|
||||
};
|
||||
|
||||
FileStream(const char *path, const int mode);
|
||||
virtual ~FileStream();
|
||||
|
||||
virtual uint64 attributes(void);
|
||||
|
||||
virtual uint64 read(void *data, uint64 count, bool error_on_eos = true);
|
||||
virtual void write(const void *data, uint64 count);
|
||||
virtual void seek(int64 offset, int whence);
|
||||
virtual int64 tell(void);
|
||||
virtual int64 size(void);
|
||||
virtual void close(void);
|
||||
|
||||
private:
|
||||
FILE *fp;
|
||||
const int OpenedMode;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,135 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include "mednafen.h"
|
||||
#include "FileWrapper.h"
|
||||
|
||||
#include <trio/trio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
// Some really bad preprocessor abuse follows to handle platforms that don't have fseeko and ftello...and of course
|
||||
// for largefile support on Windows:
|
||||
|
||||
#define fseeko fseek
|
||||
#define ftello ftell
|
||||
|
||||
// For special uses, IE in classes that take a path or a FileWrapper & in the constructor, and the FileWrapper non-pointer member
|
||||
// is in the initialization list for the path constructor but not the constructor with FileWrapper&
|
||||
|
||||
FileWrapper::FileWrapper(const char *path, const int mode, const char *purpose) : OpenedMode(mode)
|
||||
{
|
||||
if(!(fp = fopen(path, (mode == MODE_WRITE) ? "wb" : "rb")))
|
||||
{
|
||||
ErrnoHolder ene(errno);
|
||||
|
||||
throw(MDFN_Error(ene.Errno(), _("Error opening file %s"), ene.StrError()));
|
||||
}
|
||||
}
|
||||
|
||||
FileWrapper::~FileWrapper()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void FileWrapper::close(void)
|
||||
{
|
||||
if(!fp)
|
||||
return;
|
||||
|
||||
FILE *tmp = fp;
|
||||
fp = NULL;
|
||||
fclose(tmp);
|
||||
}
|
||||
|
||||
uint64 FileWrapper::read(void *data, uint64 count, bool error_on_eof)
|
||||
{
|
||||
return fread(data, 1, count, fp);
|
||||
}
|
||||
|
||||
void FileWrapper::flush(void)
|
||||
{
|
||||
fflush(fp);
|
||||
}
|
||||
|
||||
void FileWrapper::write(const void *data, uint64 count)
|
||||
{
|
||||
fwrite(data, 1, count, fp);
|
||||
}
|
||||
|
||||
int FileWrapper::scanf(const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, format);
|
||||
|
||||
ret = trio_vfscanf(fp, format, ap);
|
||||
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void FileWrapper::put_char(int c)
|
||||
{
|
||||
fputc(c, fp);
|
||||
}
|
||||
|
||||
void FileWrapper::put_string(const char *str)
|
||||
{
|
||||
write(str, strlen(str));
|
||||
}
|
||||
|
||||
// We need to decide whether to prohibit NULL characters in output and input strings via std::string.
|
||||
// Yes for correctness, no for potential security issues(though unlikely in context all things considered).
|
||||
void FileWrapper::put_string(const std::string &str)
|
||||
{
|
||||
write(str.data(), str.size());
|
||||
}
|
||||
|
||||
char *FileWrapper::get_line(char *buf_s, int buf_size)
|
||||
{
|
||||
return ::fgets(buf_s, buf_size, fp);
|
||||
}
|
||||
|
||||
|
||||
void FileWrapper::seek(int64 offset, int whence)
|
||||
{
|
||||
fseeko(fp, offset, whence);
|
||||
}
|
||||
|
||||
int64 FileWrapper::size(void)
|
||||
{
|
||||
struct stat buf;
|
||||
|
||||
fstat(fileno(fp), &buf);
|
||||
|
||||
return(buf.st_size);
|
||||
}
|
||||
|
||||
int64 FileWrapper::tell(void)
|
||||
{
|
||||
return ftello(fp);
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
#ifndef __MDFN_FILEWRAPPER_H
|
||||
#define __MDFN_FILEWRAPPER_H
|
||||
|
||||
// A stdio FILE wrapper(with some BSD and POSIXisms, and a little dash of Win32, thrown in for special behaviors)
|
||||
class FileWrapper
|
||||
{
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
MODE_READ = 0,
|
||||
MODE_WRITE,
|
||||
MODE_WRITE_SAFE // Will throw an exception instead of overwriting an existing file.
|
||||
};
|
||||
|
||||
FileWrapper(const char *path, const int mode, const char *purpose = NULL);
|
||||
~FileWrapper();
|
||||
|
||||
uint64 read(void *data, uint64 count, bool error_on_eof = true);
|
||||
|
||||
void write(const void *data, uint64 count);
|
||||
|
||||
int scanf(const char *format, ...) MDFN_FORMATSTR(scanf, 2, 3);
|
||||
|
||||
void put_char(int c);
|
||||
|
||||
void put_string(const char *str);
|
||||
void put_string(const std::string &str);
|
||||
|
||||
char *get_line(char *s, int size); // Same semantics as fgets(), for now
|
||||
|
||||
void seek(int64 offset, int whence);
|
||||
|
||||
int64 tell(void);
|
||||
|
||||
int64 size(void);
|
||||
|
||||
void flush(void);
|
||||
|
||||
void close(void); // Flushes and closes the underlying OS/C lib file. Calling any other method of this class after a call to
|
||||
// this method is illegal(except for the implicit call to the destructor).
|
||||
//
|
||||
// This is necessary since there can be errors when closing a file, and we can't safely throw an
|
||||
// exception from the destructor.
|
||||
//
|
||||
// Manually calling this method isn't strictly necessary, it'll be called from the destructor
|
||||
// automatically, but calling is strongly recommended when the file is opened for writing.
|
||||
private:
|
||||
|
||||
FileWrapper & operator=(const FileWrapper &); // Assignment operator
|
||||
FileWrapper(const FileWrapper &); // Copy constructor
|
||||
|
||||
FILE *fp;
|
||||
const int OpenedMode;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,112 @@
|
|||
// TODO/WIP
|
||||
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "octoshock.h"
|
||||
#include "Stream.h"
|
||||
|
||||
//#include <trio/trio.h>
|
||||
|
||||
Stream::Stream()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Stream::~Stream()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Stream::put_line(const std::string& str)
|
||||
{
|
||||
char l = '\n';
|
||||
|
||||
write(&str[0], str.size());
|
||||
write(&l, sizeof(l));
|
||||
}
|
||||
|
||||
|
||||
void Stream::print_format(const char *format, ...)
|
||||
{
|
||||
//char *str = NULL;
|
||||
//int rc;
|
||||
|
||||
//va_list ap;
|
||||
|
||||
//va_start(ap, format);
|
||||
|
||||
//rc = trio_vasprintf(&str, format, ap);
|
||||
|
||||
//va_end(ap);
|
||||
|
||||
//if(rc < 0)
|
||||
// throw MDFN_Error(0, "Error in trio_vasprintf()");
|
||||
//else
|
||||
//{
|
||||
// try // Bleck
|
||||
// {
|
||||
// write(str, rc);
|
||||
// }
|
||||
// catch(...)
|
||||
// {
|
||||
// free(str);
|
||||
// throw;
|
||||
// }
|
||||
// free(str);
|
||||
//}
|
||||
}
|
||||
|
||||
int Stream::get_line(std::string &str)
|
||||
{
|
||||
uint8 c;
|
||||
|
||||
str.clear(); // or str.resize(0)??
|
||||
|
||||
while(read(&c, sizeof(c), false) > 0)
|
||||
{
|
||||
if(c == '\r' || c == '\n' || c == 0)
|
||||
return(c);
|
||||
|
||||
str.push_back(c);
|
||||
}
|
||||
|
||||
return(str.length() ? 256 : -1);
|
||||
}
|
||||
|
||||
StreamFilter::StreamFilter()
|
||||
{
|
||||
target_stream = NULL;
|
||||
}
|
||||
|
||||
StreamFilter::StreamFilter(Stream *target_arg)
|
||||
{
|
||||
target_stream = target_arg;
|
||||
}
|
||||
|
||||
StreamFilter::~StreamFilter()
|
||||
{
|
||||
if(target_stream)
|
||||
delete target_stream;
|
||||
}
|
||||
|
||||
Stream* StreamFilter::steal(void)
|
||||
{
|
||||
Stream *ret = target_stream;
|
||||
target_stream = NULL;
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
#ifndef __MDFN_STREAM_H
|
||||
#define __MDFN_STREAM_H
|
||||
|
||||
// TODO/WIP
|
||||
|
||||
// TODO?: BufferedStream, no virtual functions, yes inline functions, constructor takes a Stream* argument.
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <stdio.h> // For SEEK_* defines, which we will use in Stream out of FORCE OF HABIT.
|
||||
#include <string>
|
||||
|
||||
#include "octoshock.h"
|
||||
|
||||
class Stream
|
||||
{
|
||||
public:
|
||||
|
||||
Stream();
|
||||
virtual ~Stream();
|
||||
|
||||
enum
|
||||
{
|
||||
ATTRIBUTE_READABLE = 1U << 0,
|
||||
ATTRIBUTE_WRITEABLE = 1U << 1,
|
||||
ATTRIBUTE_SEEKABLE = 1U << 2,
|
||||
ATTRIBUTE_SLOW_SEEK = 1U << 3,
|
||||
ATTRIBUTE_SLOW_SIZE = 1U << 4
|
||||
};
|
||||
virtual uint64 attributes(void) = 0;
|
||||
|
||||
virtual uint8 *map(void) = 0; // Map the entirety of the stream data into the address space of the process, if possible, and return a pointer.
|
||||
// (the returned pointer must be cached, and returned on any subsequent calls to map() without an unmap()
|
||||
// in-between, to facilitate a sort of "feature-testing", to determine if an alternative like "MemoryStream"
|
||||
// should be used).
|
||||
//
|
||||
// If the mapping fails for whatever reason, return NULL rather than throwing an exception.
|
||||
//
|
||||
|
||||
virtual void unmap(void) = 0; // Unmap the stream data from the address space. (Possibly invalidating the pointer returned from map()).
|
||||
// (must automatically be called, if necessary, from the destructor).
|
||||
//
|
||||
// If the data can't be "unmapped" as such because it was never mmap()'d or similar in the first place(such as with MemoryStream),
|
||||
// then this will be a nop.
|
||||
|
||||
virtual uint64 read(void *data, uint64 count, bool error_on_eos = true) = 0;
|
||||
virtual void write(const void *data, uint64 count) = 0;
|
||||
|
||||
virtual void seek(int64 offset, int whence = SEEK_SET) = 0;
|
||||
inline void rewind(void)
|
||||
{
|
||||
seek(0, SEEK_SET);
|
||||
}
|
||||
virtual int64 tell(void) = 0;
|
||||
virtual int64 size(void) = 0;
|
||||
virtual void close(void) = 0; // Flushes(in the case of writeable streams) and closes the stream.
|
||||
// Necessary since this operation can fail(running out of disk space, for instance),
|
||||
// and throw an exception in the destructor would be a Bad Idea(TM).
|
||||
//
|
||||
// Manually calling this function isn't strictly necessary, but recommended when the
|
||||
// stream is writeable; it will be called automatically from the destructor, with any
|
||||
// exceptions thrown caught and logged.
|
||||
|
||||
//
|
||||
// Utility functions(TODO):
|
||||
//
|
||||
INLINE uint8 get_u8(void)
|
||||
{
|
||||
uint8 ret;
|
||||
|
||||
read(&ret, sizeof(ret));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
INLINE void put_u8(uint8 c)
|
||||
{
|
||||
write(&c, sizeof(c));
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
INLINE T get_NE(void)
|
||||
{
|
||||
T ret;
|
||||
|
||||
read(&ret, sizeof(ret));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE void put_NE(T c)
|
||||
{
|
||||
write(&c, sizeof(c));
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
INLINE T get_RE(void)
|
||||
{
|
||||
uint8 tmp[sizeof(T)];
|
||||
T ret = 0;
|
||||
|
||||
read(tmp, sizeof(tmp));
|
||||
|
||||
for(unsigned i = 0; i < sizeof(T); i++)
|
||||
ret |= (T)tmp[i] << (i * 8);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE void put_RE(T c)
|
||||
{
|
||||
uint8 tmp[sizeof(T)];
|
||||
|
||||
for(unsigned i = 0; i < sizeof(T); i++)
|
||||
tmp[i] = ((uint8 *)&c)[sizeof(T) - 1 - i];
|
||||
|
||||
write(tmp, sizeof(tmp));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE T get_LE(void)
|
||||
{
|
||||
#ifdef LSB_FIRST
|
||||
return get_NE<T>();
|
||||
#else
|
||||
return get_RE<T>();
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE void put_LE(T c)
|
||||
{
|
||||
#ifdef LSB_FIRST
|
||||
return put_NE<T>(c);
|
||||
#else
|
||||
return put_RE<T>(c);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE T get_BE(void)
|
||||
{
|
||||
#ifndef LSB_FIRST
|
||||
return get_NE<T>();
|
||||
#else
|
||||
return get_RE<T>();
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE void put_BE(T c)
|
||||
{
|
||||
#ifndef LSB_FIRST
|
||||
return put_NE<T>(c);
|
||||
#else
|
||||
return put_RE<T>(c);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Reads a line into "str", overwriting its contents; returns the line-end char('\n' or '\r' or '\0'), or 256 on EOF and
|
||||
// data has been read into "str", and -1 on EOF when no data has been read into "str".
|
||||
// The line-end char won't be added to "str".
|
||||
// It's up to the caller to handle extraneous empty lines caused by DOS-format text lines(\r\n).
|
||||
// ("str" is passed by reference for the possibility of improved performance by reusing alloced memory for the std::string, though part
|
||||
// of it would be up to the STL implementation).
|
||||
// Implemented as virtual so that a higher-performance version can be implemented if possible(IE with MemoryStream)
|
||||
virtual int get_line(std::string &str);
|
||||
|
||||
virtual void put_line(const std::string& str);
|
||||
|
||||
virtual void print_format(const char *format, ...);
|
||||
|
||||
#if 0
|
||||
int scanf(const char *format, ...) MDFN_FORMATSTR(gnu_scanf, 2, 3);
|
||||
void put_string(const char *str);
|
||||
void put_string(const std::string &str);
|
||||
#endif
|
||||
};
|
||||
|
||||
// StreamFilter takes ownership of the Stream pointer passed, and will delete it in its destructor.
|
||||
class StreamFilter : public Stream
|
||||
{
|
||||
public:
|
||||
|
||||
StreamFilter();
|
||||
StreamFilter(Stream *target_arg);
|
||||
virtual ~StreamFilter();
|
||||
|
||||
virtual uint64 read(void *data, uint64 count, bool error_on_eos = true) = 0;
|
||||
virtual void write(const void *data, uint64 count) = 0;
|
||||
virtual void seek(int64 offset, int whence) = 0;
|
||||
virtual int64 tell(void) = 0;
|
||||
virtual int64 size(void) = 0;
|
||||
virtual void close(void) = 0;
|
||||
|
||||
virtual Stream *steal(void);
|
||||
|
||||
private:
|
||||
Stream *target_stream;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual Studio 2010
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "octoshock", "octoshock.vcxproj", "{5F35CAFC-6208-4FBE-AD17-0E69BA3F70EC}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniclient", "..\test\miniclient\miniclient.vcxproj", "{5A0DAC84-1170-4B1A-B9A9-F566A1D97790}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{5F35CAFC-6208-4FBE-AD17-0E69BA3F70EC}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{5F35CAFC-6208-4FBE-AD17-0E69BA3F70EC}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{5F35CAFC-6208-4FBE-AD17-0E69BA3F70EC}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{5F35CAFC-6208-4FBE-AD17-0E69BA3F70EC}.Release|Win32.Build.0 = Release|Win32
|
||||
{5A0DAC84-1170-4B1A-B9A9-F566A1D97790}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{5A0DAC84-1170-4B1A-B9A9-F566A1D97790}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{5A0DAC84-1170-4B1A-B9A9-F566A1D97790}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{5A0DAC84-1170-4B1A-B9A9-F566A1D97790}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,180 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\cdrom\CDUtility.cpp" />
|
||||
<ClCompile Include="..\cdrom\crc32.cpp" />
|
||||
<ClCompile Include="..\cdrom\galois.cpp" />
|
||||
<ClCompile Include="..\cdrom\l-ec.cpp" />
|
||||
<ClCompile Include="..\cdrom\lec.cpp" />
|
||||
<ClCompile Include="..\cdrom\recover-raw.cpp" />
|
||||
<ClCompile Include="..\emuware\emuware.cpp" />
|
||||
<ClCompile Include="..\endian.cpp" />
|
||||
<ClCompile Include="..\file.cpp" />
|
||||
<ClCompile Include="..\octoshock.cpp" />
|
||||
<ClCompile Include="..\psx\cdc.cpp" />
|
||||
<ClCompile Include="..\psx\cpu.cpp" />
|
||||
<ClCompile Include="..\psx\dis.cpp" />
|
||||
<ClCompile Include="..\psx\dma.cpp" />
|
||||
<ClCompile Include="..\psx\frontio.cpp" />
|
||||
<ClCompile Include="..\psx\gpu.cpp" />
|
||||
<ClCompile Include="..\psx\gte.cpp" />
|
||||
<ClCompile Include="..\psx\input\dualanalog.cpp" />
|
||||
<ClCompile Include="..\psx\input\dualshock.cpp" />
|
||||
<ClCompile Include="..\psx\input\gamepad.cpp" />
|
||||
<ClCompile Include="..\psx\input\guncon.cpp" />
|
||||
<ClCompile Include="..\psx\input\justifier.cpp" />
|
||||
<ClCompile Include="..\psx\input\memcard.cpp" />
|
||||
<ClCompile Include="..\psx\input\mouse.cpp" />
|
||||
<ClCompile Include="..\psx\input\multitap.cpp" />
|
||||
<ClCompile Include="..\psx\input\negcon.cpp" />
|
||||
<ClCompile Include="..\psx\irq.cpp" />
|
||||
<ClCompile Include="..\psx\mdec.cpp" />
|
||||
<ClCompile Include="..\psx\psx.cpp" />
|
||||
<ClCompile Include="..\psx\sio.cpp" />
|
||||
<ClCompile Include="..\psx\spu.cpp" />
|
||||
<ClCompile Include="..\psx\timer.cpp" />
|
||||
<ClCompile Include="..\state.cpp" />
|
||||
<ClCompile Include="..\Stream.cpp" />
|
||||
<ClCompile Include="..\video\Deinterlacer.cpp" />
|
||||
<ClCompile Include="..\video\surface.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\cdrom\CDUtility.h" />
|
||||
<ClInclude Include="..\cdrom\dvdisaster.h" />
|
||||
<ClInclude Include="..\cdrom\lec.h" />
|
||||
<ClInclude Include="..\emuware\emuware.h" />
|
||||
<ClInclude Include="..\emuware\msvc\inttypes.h" />
|
||||
<ClInclude Include="..\emuware\msvc\stdint.h" />
|
||||
<ClInclude Include="..\endian.h" />
|
||||
<ClInclude Include="..\error.h" />
|
||||
<ClInclude Include="..\file.h" />
|
||||
<ClInclude Include="..\git.h" />
|
||||
<ClInclude Include="..\masmem.h" />
|
||||
<ClInclude Include="..\math_ops.h" />
|
||||
<ClInclude Include="..\octoshock.h" />
|
||||
<ClInclude Include="..\psx\cdc.h" />
|
||||
<ClInclude Include="..\psx\cpu.h" />
|
||||
<ClInclude Include="..\psx\dis.h" />
|
||||
<ClInclude Include="..\psx\dma.h" />
|
||||
<ClInclude Include="..\psx\frontio.h" />
|
||||
<ClInclude Include="..\psx\gpu.h" />
|
||||
<ClInclude Include="..\psx\gte.h" />
|
||||
<ClInclude Include="..\psx\input\dualanalog.h" />
|
||||
<ClInclude Include="..\psx\input\dualshock.h" />
|
||||
<ClInclude Include="..\psx\input\gamepad.h" />
|
||||
<ClInclude Include="..\psx\input\guncon.h" />
|
||||
<ClInclude Include="..\psx\input\justifier.h" />
|
||||
<ClInclude Include="..\psx\input\memcard.h" />
|
||||
<ClInclude Include="..\psx\input\mouse.h" />
|
||||
<ClInclude Include="..\psx\input\multitap.h" />
|
||||
<ClInclude Include="..\psx\input\negcon.h" />
|
||||
<ClInclude Include="..\psx\irq.h" />
|
||||
<ClInclude Include="..\psx\mdec.h" />
|
||||
<ClInclude Include="..\psx\psx.h" />
|
||||
<ClInclude Include="..\psx\sio.h" />
|
||||
<ClInclude Include="..\psx\spu.h" />
|
||||
<ClInclude Include="..\psx\timer.h" />
|
||||
<ClInclude Include="..\state.h" />
|
||||
<ClInclude Include="..\Stream.h" />
|
||||
<ClInclude Include="..\video\Deinterlacer.h" />
|
||||
<ClInclude Include="..\video\surface.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\psx\gpu_command_table.inc" />
|
||||
<None Include="..\psx\gpu_line.inc" />
|
||||
<None Include="..\psx\gpu_polygon.inc" />
|
||||
<None Include="..\psx\gpu_sprite.inc" />
|
||||
<None Include="..\psx\spu_fir_table.inc" />
|
||||
<None Include="..\psx\spu_nft.inc" />
|
||||
<None Include="..\psx\spu_reverb.inc" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{5F35CAFC-6208-4FBE-AD17-0E69BA3F70EC}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>octoshock</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(ProjectDir)\..\..\..\output\dll\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(ProjectDir)\..\..\..\output\dll\</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>EW_EXPORT;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;OCTOSHOCK_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>../emuware/msvc;..</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>
|
||||
</PrecompiledHeaderOutputFile>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;EW_EXPORT;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>
|
||||
</PrecompiledHeaderOutputFile>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<AdditionalIncludeDirectories>../emuware/msvc;..</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -0,0 +1,247 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="psx">
|
||||
<UniqueIdentifier>{00f73db4-1182-4bf7-b891-66bf860d3742}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="emuware">
|
||||
<UniqueIdentifier>{f69cc8f2-7480-44d6-9a32-9dca789d2bf6}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="cdrom">
|
||||
<UniqueIdentifier>{57a8e6ec-9225-410d-b38f-ba209abae070}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="psx\input">
|
||||
<UniqueIdentifier>{76abb796-5411-4d33-b3e0-f1f3873f138e}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="emuware\msvc">
|
||||
<UniqueIdentifier>{cb700979-4dce-4b10-8521-3ab71a313271}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="video">
|
||||
<UniqueIdentifier>{d1f71901-17a5-441a-8b4f-f7da34a057c1}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\psx\cdc.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\psx.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\dis.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\gte.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\gpu.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\file.cpp" />
|
||||
<ClCompile Include="..\psx\spu.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Stream.cpp" />
|
||||
<ClCompile Include="..\psx\frontio.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\cpu.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\dma.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\irq.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\mdec.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\sio.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\timer.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\state.cpp" />
|
||||
<ClCompile Include="..\endian.cpp" />
|
||||
<ClCompile Include="..\cdrom\lec.cpp">
|
||||
<Filter>cdrom</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\cdrom\galois.cpp">
|
||||
<Filter>cdrom</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\cdrom\l-ec.cpp">
|
||||
<Filter>cdrom</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\cdrom\crc32.cpp">
|
||||
<Filter>cdrom</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\input\dualshock.cpp">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\input\gamepad.cpp">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\input\guncon.cpp">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\input\justifier.cpp">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\input\memcard.cpp">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\input\mouse.cpp">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\input\multitap.cpp">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\input\negcon.cpp">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\input\dualanalog.cpp">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\octoshock.cpp" />
|
||||
<ClCompile Include="..\emuware\emuware.cpp">
|
||||
<Filter>emuware</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\video\surface.cpp">
|
||||
<Filter>video</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\video\Deinterlacer.cpp">
|
||||
<Filter>video</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\cdrom\CDUtility.cpp">
|
||||
<Filter>cdrom</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\cdrom\recover-raw.cpp">
|
||||
<Filter>cdrom</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\psx\cdc.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\psx.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\emuware\emuware.h">
|
||||
<Filter>emuware</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\dis.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\gte.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\state.h" />
|
||||
<ClInclude Include="..\masmem.h" />
|
||||
<ClInclude Include="..\math_ops.h" />
|
||||
<ClInclude Include="..\psx\gpu.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\git.h" />
|
||||
<ClInclude Include="..\file.h" />
|
||||
<ClInclude Include="..\octoshock.h" />
|
||||
<ClInclude Include="..\psx\spu.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\endian.h" />
|
||||
<ClInclude Include="..\Stream.h" />
|
||||
<ClInclude Include="..\error.h" />
|
||||
<ClInclude Include="..\psx\frontio.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\cpu.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\dma.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\irq.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\mdec.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\sio.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\timer.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\cdrom\CDUtility.h">
|
||||
<Filter>cdrom</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\cdrom\dvdisaster.h">
|
||||
<Filter>cdrom</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\cdrom\lec.h">
|
||||
<Filter>cdrom</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\input\dualanalog.h">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\input\dualshock.h">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\input\gamepad.h">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\input\guncon.h">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\input\justifier.h">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\input\memcard.h">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\input\mouse.h">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\input\multitap.h">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\input\negcon.h">
|
||||
<Filter>psx\input</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\emuware\msvc\inttypes.h">
|
||||
<Filter>emuware\msvc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\emuware\msvc\stdint.h">
|
||||
<Filter>emuware\msvc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\video\surface.h">
|
||||
<Filter>video</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\video\Deinterlacer.h">
|
||||
<Filter>video</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\psx\gpu_command_table.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\gpu_line.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\gpu_polygon.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\gpu_sprite.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\spu_fir_table.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\spu_nft.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\spu_reverb.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,58 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../mednafen.h"
|
||||
#include "CDAccess.h"
|
||||
#include "CDAccess_Image.h"
|
||||
#include "CDAccess_CCD.h"
|
||||
|
||||
#ifdef HAVE_LIBCDIO
|
||||
#include "CDAccess_Physical.h"
|
||||
#endif
|
||||
|
||||
using namespace CDUtility;
|
||||
|
||||
CDAccess::CDAccess()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CDAccess::~CDAccess()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CDAccess *cdaccess_open_image(const char *path, bool image_memcache)
|
||||
{
|
||||
CDAccess *ret = NULL;
|
||||
|
||||
if(strlen(path) >= 4 && !strcasecmp(path + strlen(path) - 4, ".ccd"))
|
||||
ret = new CDAccess_CCD(path, image_memcache);
|
||||
else
|
||||
ret = new CDAccess_Image(path, image_memcache);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
CDAccess *cdaccess_open_phys(const char *devicename)
|
||||
{
|
||||
#ifdef HAVE_LIBCDIO
|
||||
return new CDAccess_Physical(devicename);
|
||||
#else
|
||||
throw MDFN_Error(0, _("Physical CD access support not compiled in."));
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef __MDFN_CDROMFILE_H
|
||||
#define __MDFN_CDROMFILE_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "CDUtility.h"
|
||||
|
||||
class CDAccess
|
||||
{
|
||||
public:
|
||||
|
||||
CDAccess();
|
||||
virtual ~CDAccess();
|
||||
|
||||
virtual void Read_Raw_Sector(uint8 *buf, int32 lba) = 0;
|
||||
|
||||
virtual void Read_TOC(CDUtility::TOC *toc) = 0;
|
||||
|
||||
virtual bool Is_Physical(void) throw() = 0;
|
||||
|
||||
virtual void Eject(bool eject_status) = 0; // Eject a disc if it's physical, otherwise NOP. Returns true on success(or NOP), false on error
|
||||
|
||||
private:
|
||||
CDAccess(const CDAccess&); // No copy constructor.
|
||||
CDAccess& operator=(const CDAccess&); // No assignment operator.
|
||||
};
|
||||
|
||||
CDAccess *cdaccess_open_image(const char *path, bool image_memcache);
|
||||
CDAccess *cdaccess_open_phys(const char *devicename);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,324 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* Subchannel Q CRC Code: Copyright (C) 1998 Andreas Mueller <mueller@daneb.ping.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "octoshock.h"
|
||||
#include "CDUtility.h"
|
||||
#include "dvdisaster.h"
|
||||
#include "lec.h"
|
||||
|
||||
// Kill_LEC_Correct();
|
||||
|
||||
|
||||
namespace CDUtility
|
||||
{
|
||||
|
||||
// lookup table for crc calculation
|
||||
static uint16 subq_crctab[256] =
|
||||
{
|
||||
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
|
||||
0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
|
||||
0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
|
||||
0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
|
||||
0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
|
||||
0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
|
||||
0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
|
||||
0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
|
||||
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
|
||||
0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
|
||||
0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
|
||||
0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
|
||||
0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
|
||||
0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
|
||||
0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
|
||||
0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
|
||||
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
|
||||
0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
|
||||
0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
|
||||
0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
|
||||
0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
|
||||
0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
|
||||
0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
|
||||
0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
|
||||
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
|
||||
0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
|
||||
0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
|
||||
0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
|
||||
0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
|
||||
};
|
||||
|
||||
|
||||
static uint8 scramble_table[2352 - 12];
|
||||
|
||||
static bool CDUtility_Inited = false;
|
||||
|
||||
static void InitScrambleTable(void)
|
||||
{
|
||||
unsigned cv = 1;
|
||||
|
||||
for(unsigned i = 12; i < 2352; i++)
|
||||
{
|
||||
unsigned char z = 0;
|
||||
|
||||
for(int b = 0; b < 8; b++)
|
||||
{
|
||||
z |= (cv & 1) << b;
|
||||
|
||||
int feedback = ((cv >> 1) & 1) ^ (cv & 1);
|
||||
cv = (cv >> 1) | (feedback << 14);
|
||||
}
|
||||
|
||||
scramble_table[i - 12] = z;
|
||||
}
|
||||
|
||||
//for(int i = 0; i < 2352 - 12; i++)
|
||||
// printf("0x%02x, ", scramble_table[i]);
|
||||
}
|
||||
|
||||
void CDUtility_Init(void)
|
||||
{
|
||||
if(!CDUtility_Inited)
|
||||
{
|
||||
Init_LEC_Correct();
|
||||
|
||||
InitScrambleTable();
|
||||
|
||||
CDUtility_Inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
void encode_mode0_sector(uint32 aba, uint8 *sector_data)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
lec_encode_mode0_sector(aba, sector_data);
|
||||
}
|
||||
|
||||
void encode_mode1_sector(uint32 aba, uint8 *sector_data)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
lec_encode_mode1_sector(aba, sector_data);
|
||||
}
|
||||
|
||||
void encode_mode2_sector(uint32 aba, uint8 *sector_data)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
lec_encode_mode2_sector(aba, sector_data);
|
||||
}
|
||||
|
||||
void encode_mode2_form1_sector(uint32 aba, uint8 *sector_data)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
lec_encode_mode2_form1_sector(aba, sector_data);
|
||||
}
|
||||
|
||||
void encode_mode2_form2_sector(uint32 aba, uint8 *sector_data)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
lec_encode_mode2_form2_sector(aba, sector_data);
|
||||
}
|
||||
|
||||
bool edc_check(const uint8 *sector_data, bool xa)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
return(CheckEDC(sector_data, xa));
|
||||
}
|
||||
|
||||
bool edc_lec_check_and_correct(uint8 *sector_data, bool xa)
|
||||
{
|
||||
CDUtility_Init();
|
||||
|
||||
return(ValidateRawSector(sector_data, xa));
|
||||
}
|
||||
|
||||
|
||||
bool subq_check_checksum(const uint8 *SubQBuf)
|
||||
{
|
||||
uint16 crc = 0;
|
||||
uint16 stored_crc = 0;
|
||||
|
||||
stored_crc = SubQBuf[0xA] << 8;
|
||||
stored_crc |= SubQBuf[0xB];
|
||||
|
||||
for(int i = 0; i < 0xA; i++)
|
||||
crc = subq_crctab[(crc >> 8) ^ SubQBuf[i]] ^ (crc << 8);
|
||||
|
||||
crc = ~crc;
|
||||
|
||||
return(crc == stored_crc);
|
||||
}
|
||||
|
||||
void subq_generate_checksum(uint8 *buf)
|
||||
{
|
||||
uint16 crc = 0;
|
||||
|
||||
for(int i = 0; i < 0xA; i++)
|
||||
crc = subq_crctab[(crc >> 8) ^ buf[i]] ^ (crc << 8);
|
||||
|
||||
// Checksum
|
||||
buf[0xa] = ~(crc >> 8);
|
||||
buf[0xb] = ~(crc);
|
||||
}
|
||||
|
||||
void subq_deinterleave(const uint8 *SubPWBuf, uint8 *qbuf)
|
||||
{
|
||||
memset(qbuf, 0, 0xC);
|
||||
|
||||
for(int i = 0; i < 96; i++)
|
||||
{
|
||||
qbuf[i >> 3] |= ((SubPWBuf[i] >> 6) & 0x1) << (7 - (i & 0x7));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Deinterleaves 96 bytes of subchannel P-W data from 96 bytes of interleaved subchannel PW data.
|
||||
void subpw_deinterleave(const uint8 *in_buf, uint8 *out_buf)
|
||||
{
|
||||
assert(in_buf != out_buf);
|
||||
|
||||
memset(out_buf, 0, 96);
|
||||
|
||||
for(unsigned ch = 0; ch < 8; ch++)
|
||||
{
|
||||
for(unsigned i = 0; i < 96; i++)
|
||||
{
|
||||
out_buf[(ch * 12) + (i >> 3)] |= ((in_buf[i] >> (7 - ch)) & 0x1) << (7 - (i & 0x7));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Interleaves 96 bytes of subchannel P-W data from 96 bytes of uninterleaved subchannel PW data.
|
||||
void subpw_interleave(const uint8 *in_buf, uint8 *out_buf)
|
||||
{
|
||||
assert(in_buf != out_buf);
|
||||
|
||||
for(unsigned d = 0; d < 12; d++)
|
||||
{
|
||||
for(unsigned bitpoodle = 0; bitpoodle < 8; bitpoodle++)
|
||||
{
|
||||
uint8 rawb = 0;
|
||||
|
||||
for(unsigned ch = 0; ch < 8; ch++)
|
||||
{
|
||||
rawb |= ((in_buf[ch * 12 + d] >> (7 - bitpoodle)) & 1) << (7 - ch);
|
||||
}
|
||||
out_buf[(d << 3) + bitpoodle] = rawb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTES ON LEADOUT AREA SYNTHESIS
|
||||
//
|
||||
// I'm not trusting that the "control" field for the TOC leadout entry will always be set properly, so | the control fields for the last track entry
|
||||
// and the leadout entry together before extracting the D2 bit. Audio track->data leadout is fairly benign though maybe noisy(especially if we ever implement
|
||||
// data scrambling properly), but data track->audio leadout could break things in an insidious manner for the more accurate drive emulation code).
|
||||
//
|
||||
void subpw_synth_leadout_lba(const TOC& toc, const int32 lba, uint8* SubPWBuf)
|
||||
{
|
||||
uint8 buf[0xC];
|
||||
uint32 lba_relative;
|
||||
uint32 ma, sa, fa;
|
||||
uint32 m, s, f;
|
||||
|
||||
lba_relative = lba - toc.tracks[100].lba;
|
||||
|
||||
f = (lba_relative % 75);
|
||||
s = ((lba_relative / 75) % 60);
|
||||
m = (lba_relative / 75 / 60);
|
||||
|
||||
fa = (lba + 150) % 75;
|
||||
sa = ((lba + 150) / 75) % 60;
|
||||
ma = ((lba + 150) / 75 / 60);
|
||||
|
||||
uint8 adr = 0x1; // Q channel data encodes position
|
||||
uint8 control = (toc.tracks[toc.last_track].control & 0x4) | toc.tracks[100].control;
|
||||
|
||||
memset(buf, 0, 0xC);
|
||||
buf[0] = (adr << 0) | (control << 4);
|
||||
buf[1] = 0xAA;
|
||||
buf[2] = 0x01;
|
||||
|
||||
// Track relative MSF address
|
||||
buf[3] = U8_to_BCD(m);
|
||||
buf[4] = U8_to_BCD(s);
|
||||
buf[5] = U8_to_BCD(f);
|
||||
|
||||
buf[6] = 0; // Zerroooo
|
||||
|
||||
// Absolute MSF address
|
||||
buf[7] = U8_to_BCD(ma);
|
||||
buf[8] = U8_to_BCD(sa);
|
||||
buf[9] = U8_to_BCD(fa);
|
||||
|
||||
subq_generate_checksum(buf);
|
||||
|
||||
for(int i = 0; i < 96; i++)
|
||||
SubPWBuf[i] = (((buf[i >> 3] >> (7 - (i & 0x7))) & 1) ? 0x40 : 0x00) | 0x80;
|
||||
}
|
||||
|
||||
void synth_leadout_sector_lba(const uint8 mode, const TOC& toc, const int32 lba, uint8* out_buf)
|
||||
{
|
||||
memset(out_buf, 0, 2352 + 96);
|
||||
subpw_synth_leadout_lba(toc, lba, out_buf + 2352);
|
||||
|
||||
if((toc.tracks[toc.last_track].control | toc.tracks[100].control) & 0x4)
|
||||
{
|
||||
switch(mode)
|
||||
{
|
||||
default:
|
||||
encode_mode0_sector(LBA_to_ABA(lba), out_buf);
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
encode_mode1_sector(LBA_to_ABA(lba), out_buf);
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
out_buf[18] = 0x20;
|
||||
encode_mode2_form2_sector(LBA_to_ABA(lba), out_buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool subq_extrapolate(const uint8 *subq_input, int32 position_delta, uint8 *subq_output)
|
||||
{
|
||||
assert(subq_check_checksum(subq_input));
|
||||
|
||||
|
||||
subq_generate_checksum(subq_output);
|
||||
}
|
||||
#endif
|
||||
|
||||
void scrambleize_data_sector(uint8 *sector_data)
|
||||
{
|
||||
for(unsigned i = 12; i < 2352; i++)
|
||||
sector_data[i] ^= scramble_table[i - 12];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
#ifndef __MDFN_CDROM_CDUTILITY_H
|
||||
#define __MDFN_CDROM_CDUTILITY_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace CDUtility
|
||||
{
|
||||
// Call once at app startup before creating any threads that could potentially cause re-entrancy to these functions.
|
||||
// It will also be called automatically if needed for the first time a function in this namespace that requires
|
||||
// the initialization function to be called is called, for potential
|
||||
// usage in constructors of statically-declared objects.
|
||||
void CDUtility_Init(void);
|
||||
|
||||
// Quick definitions here:
|
||||
//
|
||||
// ABA - Absolute block address, synonymous to absolute MSF
|
||||
// aba = (m_a * 60 * 75) + (s_a * 75) + f_a
|
||||
//
|
||||
// LBA - Logical block address(related: data CDs are required to have a pregap of 2 seconds, IE 150 frames/sectors)
|
||||
// lba = aba - 150
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
ADR_NOQINFO = 0x00,
|
||||
ADR_CURPOS = 0x01,
|
||||
ADR_MCN = 0x02,
|
||||
ADR_ISRC = 0x03
|
||||
};
|
||||
|
||||
|
||||
struct TOC_Track
|
||||
{
|
||||
uint8 adr;
|
||||
uint8 control;
|
||||
uint32 lba;
|
||||
};
|
||||
|
||||
// SubQ control field flags.
|
||||
enum
|
||||
{
|
||||
SUBQ_CTRLF_PRE = 0x01, // With 50/15us pre-emphasis.
|
||||
SUBQ_CTRLF_DCP = 0x02, // Digital copy permitted.
|
||||
SUBQ_CTRLF_DATA = 0x04, // Data track.
|
||||
SUBQ_CTRLF_4CH = 0x08, // 4-channel CD-DA.
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
DISC_TYPE_CDDA_OR_M1 = 0x00,
|
||||
DISC_TYPE_CD_I = 0x10,
|
||||
DISC_TYPE_CD_XA = 0x20
|
||||
};
|
||||
|
||||
struct TOC
|
||||
{
|
||||
INLINE TOC()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
INLINE void Clear(void)
|
||||
{
|
||||
first_track = last_track = 0;
|
||||
disc_type = 0;
|
||||
|
||||
memset(tracks, 0, sizeof(tracks)); // FIXME if we change TOC_Track to non-POD type.
|
||||
}
|
||||
|
||||
INLINE int FindTrackByLBA(uint32 LBA)
|
||||
{
|
||||
for(int32 track = first_track; track <= (last_track + 1); track++)
|
||||
{
|
||||
if(track == (last_track + 1))
|
||||
{
|
||||
if(LBA < tracks[100].lba)
|
||||
return(track - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(LBA < tracks[track].lba)
|
||||
return(track - 1);
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
uint8 first_track;
|
||||
uint8 last_track;
|
||||
uint8 disc_type;
|
||||
TOC_Track tracks[100 + 1]; // [0] is unused, [100] is for the leadout track.
|
||||
// Also, for convenience, tracks[last_track + 1] will always refer
|
||||
// to the leadout track(even if last_track < 99, IE the leadout track details are duplicated).
|
||||
};
|
||||
|
||||
//
|
||||
// Address conversion functions.
|
||||
//
|
||||
static INLINE uint32 AMSF_to_ABA(int32 m_a, int32 s_a, int32 f_a)
|
||||
{
|
||||
return(f_a + 75 * s_a + 75 * 60 * m_a);
|
||||
}
|
||||
|
||||
static INLINE void ABA_to_AMSF(uint32 aba, uint8 *m_a, uint8 *s_a, uint8 *f_a)
|
||||
{
|
||||
*m_a = aba / 75 / 60;
|
||||
*s_a = (aba - *m_a * 75 * 60) / 75;
|
||||
*f_a = aba - (*m_a * 75 * 60) - (*s_a * 75);
|
||||
}
|
||||
|
||||
static INLINE int32 ABA_to_LBA(uint32 aba)
|
||||
{
|
||||
return(aba - 150);
|
||||
}
|
||||
|
||||
static INLINE uint32 LBA_to_ABA(int32 lba)
|
||||
{
|
||||
return(lba + 150);
|
||||
}
|
||||
|
||||
static INLINE int32 AMSF_to_LBA(uint8 m_a, uint8 s_a, uint8 f_a)
|
||||
{
|
||||
return(ABA_to_LBA(AMSF_to_ABA(m_a, s_a, f_a)));
|
||||
}
|
||||
|
||||
static INLINE void LBA_to_AMSF(int32 lba, uint8 *m_a, uint8 *s_a, uint8 *f_a)
|
||||
{
|
||||
ABA_to_AMSF(LBA_to_ABA(lba), m_a, s_a, f_a);
|
||||
}
|
||||
|
||||
//
|
||||
// BCD conversion functions
|
||||
//
|
||||
static INLINE bool BCD_is_valid(uint8 bcd_number)
|
||||
{
|
||||
if((bcd_number & 0xF0) >= 0xA0)
|
||||
return(false);
|
||||
|
||||
if((bcd_number & 0x0F) >= 0x0A)
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
static INLINE uint8 BCD_to_U8(uint8 bcd_number)
|
||||
{
|
||||
return( ((bcd_number >> 4) * 10) + (bcd_number & 0x0F) );
|
||||
}
|
||||
|
||||
static INLINE uint8 U8_to_BCD(uint8 num)
|
||||
{
|
||||
return( ((num / 10) << 4) + (num % 10) );
|
||||
}
|
||||
|
||||
// should always perform the conversion, even if the bcd number is invalid.
|
||||
static INLINE bool BCD_to_U8_check(uint8 bcd_number, uint8 *out_number)
|
||||
{
|
||||
*out_number = BCD_to_U8(bcd_number);
|
||||
|
||||
if(!BCD_is_valid(bcd_number))
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
//
|
||||
// Sector data encoding functions(to full 2352 bytes raw sector).
|
||||
//
|
||||
// sector_data must be able to contain at least 2352 bytes.
|
||||
void encode_mode0_sector(uint32 aba, uint8 *sector_data);
|
||||
void encode_mode1_sector(uint32 aba, uint8 *sector_data); // 2048 bytes of user data at offset 16
|
||||
void encode_mode2_sector(uint32 aba, uint8 *sector_data); // 2336 bytes of user data at offset 16
|
||||
void encode_mode2_form1_sector(uint32 aba, uint8 *sector_data); // 2048+8 bytes of user data at offset 16
|
||||
void encode_mode2_form2_sector(uint32 aba, uint8 *sector_data); // 2324+8 bytes of user data at offset 16
|
||||
|
||||
|
||||
// out_buf must be able to contain 2352+96 bytes.
|
||||
// "mode" is only used if(toc.tracks[100].control & 0x4)
|
||||
void synth_leadout_sector_lba(const uint8 mode, const TOC& toc, const int32 lba, uint8* out_buf);
|
||||
|
||||
//
|
||||
// User data error detection and correction
|
||||
//
|
||||
|
||||
// Check EDC of a mode 1 or mode 2 form 1 sector.
|
||||
// Returns "true" if checksum is ok(matches).
|
||||
// Returns "false" if checksum mismatch.
|
||||
// sector_data should contain 2352 bytes of raw sector data.
|
||||
bool edc_check(const uint8 *sector_data, bool xa);
|
||||
|
||||
// Check EDC and L-EC data of a mode 1 or mode 2 form 1 sector, and correct bit errors if any exist.
|
||||
// Returns "true" if errors weren't detected, or they were corrected succesfully.
|
||||
// Returns "false" if errors couldn't be corrected.
|
||||
// sector_data should contain 2352 bytes of raw sector data.
|
||||
bool edc_lec_check_and_correct(uint8 *sector_data, bool xa);
|
||||
|
||||
//
|
||||
// Subchannel(Q in particular) functions
|
||||
//
|
||||
|
||||
// Returns false on checksum mismatch, true on match.
|
||||
bool subq_check_checksum(const uint8 *subq_buf);
|
||||
|
||||
// Calculates the checksum of Q subchannel data(not including the checksum bytes of course ;)) from subq_buf, and stores it into the appropriate position
|
||||
// in subq_buf.
|
||||
void subq_generate_checksum(uint8 *subq_buf);
|
||||
|
||||
// Deinterleaves 12 bytes of subchannel Q data from 96 bytes of interleaved subchannel PW data.
|
||||
void subq_deinterleave(const uint8 *subpw_buf, uint8 *subq_buf);
|
||||
|
||||
// Deinterleaves 96 bytes of subchannel P-W data from 96 bytes of interleaved subchannel PW data.
|
||||
void subpw_deinterleave(const uint8 *in_buf, uint8 *out_buf);
|
||||
|
||||
// Interleaves 96 bytes of subchannel P-W data from 96 bytes of uninterleaved subchannel PW data.
|
||||
void subpw_interleave(const uint8 *in_buf, uint8 *out_buf);
|
||||
|
||||
// Extrapolates Q subchannel current position data from subq_input, with frame/sector delta position_delta, and writes to subq_output.
|
||||
// Only valid for ADR_CURPOS.
|
||||
// subq_input must pass subq_check_checksum().
|
||||
// TODO
|
||||
//void subq_extrapolate(const uint8 *subq_input, int32 position_delta, uint8 *subq_output);
|
||||
|
||||
// (De)Scrambles data sector.
|
||||
void scrambleize_data_sector(uint8 *sector_data);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,6 @@
|
|||
#include "../mednafen.h"
|
||||
#include "SimpleFIFO.h"
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
#ifndef __MDFN_SIMPLEFIFO_H
|
||||
#define __MDFN_SIMPLEFIFO_H
|
||||
|
||||
#include <vector>
|
||||
#include <assert.h>
|
||||
|
||||
#include "../math_ops.h"
|
||||
|
||||
template<typename T>
|
||||
class SimpleFIFO
|
||||
{
|
||||
public:
|
||||
|
||||
// Constructor
|
||||
SimpleFIFO(uint32 the_size) // Size should be a power of 2!
|
||||
{
|
||||
data.resize((std::vector<T>::size_type)round_up_pow2(the_size));
|
||||
size = the_size;
|
||||
read_pos = 0;
|
||||
write_pos = 0;
|
||||
in_count = 0;
|
||||
}
|
||||
|
||||
// Destructor
|
||||
INLINE ~SimpleFIFO()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
INLINE void SaveStatePostLoad(void)
|
||||
{
|
||||
read_pos %= data.size();
|
||||
write_pos %= data.size();
|
||||
in_count %= (data.size() + 1);
|
||||
}
|
||||
|
||||
#if 0
|
||||
INLINE int StateAction(StateMem *sm, int load, int data_only, const char* sname)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
std::vector<T> data;
|
||||
uint32 size;
|
||||
|
||||
SFVAR(read_pos),
|
||||
SFVAR(write_pos),
|
||||
SFVAR(in_count),
|
||||
SFEND;
|
||||
}
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, sname);
|
||||
|
||||
if(load)
|
||||
{
|
||||
read_pos %= data.size();
|
||||
write_pos %= data.size();
|
||||
in_count %= (data.size() + 1);
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
INLINE uint32 CanRead(void)
|
||||
{
|
||||
return(in_count);
|
||||
}
|
||||
|
||||
INLINE uint32 CanWrite(void)
|
||||
{
|
||||
return(size - in_count);
|
||||
}
|
||||
|
||||
INLINE T ReadUnit(bool peek = false)
|
||||
{
|
||||
T ret;
|
||||
|
||||
assert(in_count > 0);
|
||||
|
||||
ret = data[read_pos];
|
||||
|
||||
if(!peek)
|
||||
{
|
||||
read_pos = (read_pos + 1) & (data.size() - 1);
|
||||
in_count--;
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
INLINE uint8 ReadByte(bool peek = false)
|
||||
{
|
||||
assert(sizeof(T) == 1);
|
||||
|
||||
return(ReadUnit(peek));
|
||||
}
|
||||
|
||||
INLINE void Write(const T *happy_data, uint32 happy_count)
|
||||
{
|
||||
assert(CanWrite() >= happy_count);
|
||||
|
||||
while(happy_count)
|
||||
{
|
||||
data[write_pos] = *happy_data;
|
||||
|
||||
write_pos = (write_pos + 1) & (data.size() - 1);
|
||||
in_count++;
|
||||
happy_data++;
|
||||
happy_count--;
|
||||
}
|
||||
}
|
||||
|
||||
INLINE void WriteUnit(const T& wr_data)
|
||||
{
|
||||
Write(&wr_data, 1);
|
||||
}
|
||||
|
||||
INLINE void WriteByte(const T& wr_data)
|
||||
{
|
||||
assert(sizeof(T) == 1);
|
||||
Write(&wr_data, 1);
|
||||
}
|
||||
|
||||
|
||||
INLINE void Flush(void)
|
||||
{
|
||||
read_pos = 0;
|
||||
write_pos = 0;
|
||||
in_count = 0;
|
||||
}
|
||||
|
||||
//private:
|
||||
std::vector<T> data;
|
||||
uint32 size;
|
||||
uint32 read_pos; // Read position
|
||||
uint32 write_pos; // Write position
|
||||
uint32 in_count; // Number of units in the FIFO
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,915 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../mednafen.h"
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <trio/trio.h>
|
||||
#include "cdromif.h"
|
||||
#include "CDAccess.h"
|
||||
#include "../general.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace CDUtility;
|
||||
|
||||
enum
|
||||
{
|
||||
// Status/Error messages
|
||||
CDIF_MSG_DONE = 0, // Read -> emu. args: No args.
|
||||
CDIF_MSG_INFO, // Read -> emu. args: str_message
|
||||
CDIF_MSG_FATAL_ERROR, // Read -> emu. args: *TODO ARGS*
|
||||
|
||||
//
|
||||
// Command messages.
|
||||
//
|
||||
CDIF_MSG_DIEDIEDIE, // Emu -> read
|
||||
|
||||
CDIF_MSG_READ_SECTOR, /* Emu -> read
|
||||
args[0] = lba
|
||||
*/
|
||||
|
||||
CDIF_MSG_EJECT, // Emu -> read, args[0]; 0=insert, 1=eject
|
||||
};
|
||||
|
||||
class CDIF_Message
|
||||
{
|
||||
public:
|
||||
|
||||
CDIF_Message();
|
||||
CDIF_Message(unsigned int message_, uint32 arg0 = 0, uint32 arg1 = 0, uint32 arg2 = 0, uint32 arg3 = 0);
|
||||
CDIF_Message(unsigned int message_, const std::string &str);
|
||||
~CDIF_Message();
|
||||
|
||||
unsigned int message;
|
||||
uint32 args[4];
|
||||
void *parg;
|
||||
std::string str_message;
|
||||
};
|
||||
|
||||
class CDIF_Queue
|
||||
{
|
||||
public:
|
||||
|
||||
CDIF_Queue();
|
||||
~CDIF_Queue();
|
||||
|
||||
bool Read(CDIF_Message *message, bool blocking = TRUE);
|
||||
|
||||
void Write(const CDIF_Message &message);
|
||||
|
||||
private:
|
||||
std::queue<CDIF_Message> ze_queue;
|
||||
MDFN_Mutex *ze_mutex;
|
||||
MDFN_Cond *ze_cond;
|
||||
};
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool valid;
|
||||
bool error;
|
||||
uint32 lba;
|
||||
uint8 data[2352 + 96];
|
||||
} CDIF_Sector_Buffer;
|
||||
|
||||
// TODO: prohibit copy constructor
|
||||
class CDIF_MT : public CDIF
|
||||
{
|
||||
public:
|
||||
|
||||
CDIF_MT(CDAccess *cda);
|
||||
virtual ~CDIF_MT();
|
||||
|
||||
virtual void HintReadSector(uint32 lba);
|
||||
virtual bool ReadRawSector(uint8 *buf, uint32 lba);
|
||||
|
||||
// Return true if operation succeeded or it was a NOP(either due to not being implemented, or the current status matches eject_status).
|
||||
// Returns false on failure(usually drive error of some kind; not completely fatal, can try again).
|
||||
virtual bool Eject(bool eject_status);
|
||||
|
||||
// FIXME: Semi-private:
|
||||
int ReadThreadStart(void);
|
||||
|
||||
private:
|
||||
|
||||
CDAccess *disc_cdaccess;
|
||||
|
||||
MDFN_Thread *CDReadThread;
|
||||
|
||||
// Queue for messages to the read thread.
|
||||
CDIF_Queue ReadThreadQueue;
|
||||
|
||||
// Queue for messages to the emu thread.
|
||||
CDIF_Queue EmuThreadQueue;
|
||||
|
||||
|
||||
enum { SBSize = 256 };
|
||||
CDIF_Sector_Buffer SectorBuffers[SBSize];
|
||||
|
||||
uint32 SBWritePos;
|
||||
|
||||
MDFN_Mutex *SBMutex;
|
||||
MDFN_Cond *SBCond;
|
||||
|
||||
|
||||
//
|
||||
// Read-thread-only:
|
||||
//
|
||||
void RT_EjectDisc(bool eject_status, bool skip_actual_eject = false);
|
||||
|
||||
uint32 ra_lba;
|
||||
int ra_count;
|
||||
uint32 last_read_lba;
|
||||
};
|
||||
|
||||
|
||||
// TODO: prohibit copy constructor
|
||||
class CDIF_ST : public CDIF
|
||||
{
|
||||
public:
|
||||
|
||||
CDIF_ST(CDAccess *cda);
|
||||
virtual ~CDIF_ST();
|
||||
|
||||
virtual void HintReadSector(uint32 lba);
|
||||
virtual bool ReadRawSector(uint8 *buf, uint32 lba);
|
||||
virtual bool Eject(bool eject_status);
|
||||
|
||||
private:
|
||||
CDAccess *disc_cdaccess;
|
||||
};
|
||||
|
||||
CDIF::CDIF() : UnrecoverableError(false), is_phys_cache(false), DiscEjected(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CDIF::~CDIF()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
CDIF_Message::CDIF_Message()
|
||||
{
|
||||
message = 0;
|
||||
|
||||
memset(args, 0, sizeof(args));
|
||||
}
|
||||
|
||||
CDIF_Message::CDIF_Message(unsigned int message_, uint32 arg0, uint32 arg1, uint32 arg2, uint32 arg3)
|
||||
{
|
||||
message = message_;
|
||||
args[0] = arg0;
|
||||
args[1] = arg1;
|
||||
args[2] = arg2;
|
||||
args[3] = arg3;
|
||||
}
|
||||
|
||||
CDIF_Message::CDIF_Message(unsigned int message_, const std::string &str)
|
||||
{
|
||||
message = message_;
|
||||
str_message = str;
|
||||
}
|
||||
|
||||
CDIF_Message::~CDIF_Message()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CDIF_Queue::CDIF_Queue()
|
||||
{
|
||||
ze_mutex = MDFND_CreateMutex();
|
||||
ze_cond = MDFND_CreateCond();
|
||||
}
|
||||
|
||||
CDIF_Queue::~CDIF_Queue()
|
||||
{
|
||||
MDFND_DestroyMutex(ze_mutex);
|
||||
MDFND_DestroyCond(ze_cond);
|
||||
}
|
||||
|
||||
// Returns FALSE if message not read, TRUE if it was read. Will always return TRUE if "blocking" is set.
|
||||
// Will throw MDFN_Error if the read message code is CDIF_MSG_FATAL_ERROR
|
||||
bool CDIF_Queue::Read(CDIF_Message *message, bool blocking)
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
MDFND_LockMutex(ze_mutex);
|
||||
|
||||
if(blocking)
|
||||
{
|
||||
while(ze_queue.size() == 0) // while, not just if.
|
||||
{
|
||||
MDFND_WaitCond(ze_cond, ze_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
if(ze_queue.size() == 0)
|
||||
ret = false;
|
||||
else
|
||||
{
|
||||
*message = ze_queue.front();
|
||||
ze_queue.pop();
|
||||
}
|
||||
|
||||
MDFND_UnlockMutex(ze_mutex);
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
if(ret && message->message == CDIF_MSG_FATAL_ERROR)
|
||||
throw MDFN_Error(0, "%s", message->str_message.c_str());
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void CDIF_Queue::Write(const CDIF_Message &message)
|
||||
{
|
||||
MDFND_LockMutex(ze_mutex);
|
||||
|
||||
try
|
||||
{
|
||||
ze_queue.push(message);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
fprintf(stderr, "\n\nCDIF_Message queue push failed!!! (We now return you to your regularly unscheduled lockup)\n\n");
|
||||
}
|
||||
|
||||
MDFND_SignalCond(ze_cond); // Signal while the mutex is held to prevent icky race conditions.
|
||||
|
||||
MDFND_UnlockMutex(ze_mutex);
|
||||
}
|
||||
|
||||
|
||||
void CDIF_MT::RT_EjectDisc(bool eject_status, bool skip_actual_eject)
|
||||
{
|
||||
if(eject_status != DiscEjected)
|
||||
{
|
||||
if(!skip_actual_eject)
|
||||
disc_cdaccess->Eject(eject_status);
|
||||
|
||||
// Set after ->Eject(), since it might throw an exception.
|
||||
DiscEjected = -1; // For if TOC reading fails or there's something horribly wrong with the disc.
|
||||
|
||||
if(!eject_status) // Re-read the TOC
|
||||
{
|
||||
disc_cdaccess->Read_TOC(&disc_toc);
|
||||
|
||||
if(disc_toc.first_track < 1 || disc_toc.last_track > 99 || disc_toc.first_track > disc_toc.last_track)
|
||||
{
|
||||
throw(MDFN_Error(0, _("TOC first(%d)/last(%d) track numbers bad."), disc_toc.first_track, disc_toc.last_track));
|
||||
}
|
||||
}
|
||||
DiscEjected = eject_status;
|
||||
|
||||
SBWritePos = 0;
|
||||
ra_lba = 0;
|
||||
ra_count = 0;
|
||||
last_read_lba = ~0U;
|
||||
memset(SectorBuffers, 0, SBSize * sizeof(CDIF_Sector_Buffer));
|
||||
}
|
||||
}
|
||||
|
||||
struct RTS_Args
|
||||
{
|
||||
CDIF_MT *cdif_ptr;
|
||||
};
|
||||
|
||||
static int ReadThreadStart_C(void *v_arg)
|
||||
{
|
||||
RTS_Args *args = (RTS_Args *)v_arg;
|
||||
|
||||
return args->cdif_ptr->ReadThreadStart();
|
||||
}
|
||||
|
||||
int CDIF_MT::ReadThreadStart()
|
||||
{
|
||||
bool Running = TRUE;
|
||||
|
||||
DiscEjected = true;
|
||||
SBWritePos = 0;
|
||||
ra_lba = 0;
|
||||
ra_count = 0;
|
||||
last_read_lba = ~0U;
|
||||
|
||||
try
|
||||
{
|
||||
RT_EjectDisc(false, true);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
EmuThreadQueue.Write(CDIF_Message(CDIF_MSG_FATAL_ERROR, std::string(e.what())));
|
||||
return(0);
|
||||
}
|
||||
|
||||
is_phys_cache = disc_cdaccess->Is_Physical();
|
||||
|
||||
EmuThreadQueue.Write(CDIF_Message(CDIF_MSG_DONE));
|
||||
|
||||
while(Running)
|
||||
{
|
||||
CDIF_Message msg;
|
||||
|
||||
// Only do a blocking-wait for a message if we don't have any sectors to read-ahead.
|
||||
// MDFN_DispMessage("%d %d %d\n", last_read_lba, ra_lba, ra_count);
|
||||
if(ReadThreadQueue.Read(&msg, ra_count ? FALSE : TRUE))
|
||||
{
|
||||
switch(msg.message)
|
||||
{
|
||||
case CDIF_MSG_DIEDIEDIE:
|
||||
Running = FALSE;
|
||||
break;
|
||||
|
||||
case CDIF_MSG_EJECT:
|
||||
try
|
||||
{
|
||||
RT_EjectDisc(msg.args[0]);
|
||||
EmuThreadQueue.Write(CDIF_Message(CDIF_MSG_DONE));
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
EmuThreadQueue.Write(CDIF_Message(CDIF_MSG_FATAL_ERROR, std::string(e.what())));
|
||||
}
|
||||
break;
|
||||
|
||||
case CDIF_MSG_READ_SECTOR:
|
||||
{
|
||||
static const int max_ra = 16;
|
||||
static const int initial_ra = 1;
|
||||
static const int speedmult_ra = 2;
|
||||
uint32 new_lba = msg.args[0];
|
||||
|
||||
assert((unsigned int)max_ra < (SBSize / 4));
|
||||
|
||||
if(last_read_lba != ~0U && new_lba == (last_read_lba + 1))
|
||||
{
|
||||
int how_far_ahead = ra_lba - new_lba;
|
||||
|
||||
if(how_far_ahead <= max_ra)
|
||||
ra_count = std::min(speedmult_ra, 1 + max_ra - how_far_ahead);
|
||||
else
|
||||
ra_count++;
|
||||
}
|
||||
else if(new_lba != last_read_lba)
|
||||
{
|
||||
ra_lba = new_lba;
|
||||
ra_count = initial_ra;
|
||||
}
|
||||
|
||||
last_read_lba = new_lba;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't read >= the "end" of the disc, silly snake. Slither.
|
||||
if(ra_count && ra_lba == disc_toc.tracks[100].lba)
|
||||
{
|
||||
ra_count = 0;
|
||||
//printf("Ephemeral scarabs: %d!\n", ra_lba);
|
||||
}
|
||||
|
||||
if(ra_count)
|
||||
{
|
||||
uint8 tmpbuf[2352 + 96];
|
||||
bool error_condition = false;
|
||||
|
||||
try
|
||||
{
|
||||
disc_cdaccess->Read_Raw_Sector(tmpbuf, ra_lba);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
MDFN_PrintError(_("Sector %u read error: %s"), ra_lba, e.what());
|
||||
memset(tmpbuf, 0, sizeof(tmpbuf));
|
||||
error_condition = true;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
MDFND_LockMutex(SBMutex);
|
||||
|
||||
SectorBuffers[SBWritePos].lba = ra_lba;
|
||||
memcpy(SectorBuffers[SBWritePos].data, tmpbuf, 2352 + 96);
|
||||
SectorBuffers[SBWritePos].valid = TRUE;
|
||||
SectorBuffers[SBWritePos].error = error_condition;
|
||||
SBWritePos = (SBWritePos + 1) % SBSize;
|
||||
|
||||
MDFND_SignalCond(SBCond);
|
||||
|
||||
MDFND_UnlockMutex(SBMutex);
|
||||
//
|
||||
//
|
||||
|
||||
ra_lba++;
|
||||
ra_count--;
|
||||
}
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
CDIF_MT::CDIF_MT(CDAccess *cda) : disc_cdaccess(cda), CDReadThread(NULL), SBMutex(NULL), SBCond(NULL)
|
||||
{
|
||||
try
|
||||
{
|
||||
CDIF_Message msg;
|
||||
RTS_Args s;
|
||||
|
||||
if(!(SBMutex = MDFND_CreateMutex()))
|
||||
throw MDFN_Error(0, _("Error creating CD read thread mutex."));
|
||||
|
||||
if(!(SBCond = MDFND_CreateCond()))
|
||||
throw MDFN_Error(0, _("Error creating CD read thread condition variable."));
|
||||
|
||||
UnrecoverableError = false;
|
||||
|
||||
s.cdif_ptr = this;
|
||||
|
||||
if(!(CDReadThread = MDFND_CreateThread(ReadThreadStart_C, &s)))
|
||||
throw MDFN_Error(0, _("Error creating CD read thread."));
|
||||
|
||||
EmuThreadQueue.Read(&msg);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
if(CDReadThread)
|
||||
{
|
||||
MDFND_WaitThread(CDReadThread, NULL);
|
||||
CDReadThread = NULL;
|
||||
}
|
||||
|
||||
if(SBMutex)
|
||||
{
|
||||
MDFND_DestroyMutex(SBMutex);
|
||||
SBMutex = NULL;
|
||||
}
|
||||
|
||||
if(SBCond)
|
||||
{
|
||||
MDFND_DestroyCond(SBCond);
|
||||
SBCond = NULL;
|
||||
}
|
||||
|
||||
if(disc_cdaccess)
|
||||
{
|
||||
delete disc_cdaccess;
|
||||
disc_cdaccess = NULL;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CDIF_MT::~CDIF_MT()
|
||||
{
|
||||
bool thread_deaded_failed = false;
|
||||
|
||||
try
|
||||
{
|
||||
ReadThreadQueue.Write(CDIF_Message(CDIF_MSG_DIEDIEDIE));
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
MDFND_PrintError(e.what());
|
||||
thread_deaded_failed = true;
|
||||
}
|
||||
|
||||
if(!thread_deaded_failed)
|
||||
MDFND_WaitThread(CDReadThread, NULL);
|
||||
|
||||
if(SBMutex)
|
||||
{
|
||||
MDFND_DestroyMutex(SBMutex);
|
||||
SBMutex = NULL;
|
||||
}
|
||||
|
||||
if(SBCond)
|
||||
{
|
||||
MDFND_DestroyCond(SBCond);
|
||||
SBCond = NULL;
|
||||
}
|
||||
|
||||
if(disc_cdaccess)
|
||||
{
|
||||
delete disc_cdaccess;
|
||||
disc_cdaccess = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool CDIF::ValidateRawSector(uint8 *buf)
|
||||
{
|
||||
int mode = buf[12 + 3];
|
||||
|
||||
if(mode != 0x1 && mode != 0x2)
|
||||
return(false);
|
||||
|
||||
if(!edc_lec_check_and_correct(buf, mode == 2))
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool CDIF_MT::ReadRawSector(uint8 *buf, uint32 lba)
|
||||
{
|
||||
bool found = FALSE;
|
||||
bool error_condition = false;
|
||||
|
||||
if(UnrecoverableError)
|
||||
{
|
||||
memset(buf, 0, 2352 + 96);
|
||||
return(false);
|
||||
}
|
||||
|
||||
// This shouldn't happen, the emulated-system-specific CDROM emulation code should make sure the emulated program doesn't try
|
||||
// to read past the last "real" sector of the disc.
|
||||
if(lba >= disc_toc.tracks[100].lba)
|
||||
{
|
||||
printf("Attempt to read LBA %d, >= LBA %d\n", lba, disc_toc.tracks[100].lba);
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
ReadThreadQueue.Write(CDIF_Message(CDIF_MSG_READ_SECTOR, lba));
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
MDFND_LockMutex(SBMutex);
|
||||
|
||||
do
|
||||
{
|
||||
for(int i = 0; i < SBSize; i++)
|
||||
{
|
||||
if(SectorBuffers[i].valid && SectorBuffers[i].lba == lba)
|
||||
{
|
||||
error_condition = SectorBuffers[i].error;
|
||||
memcpy(buf, SectorBuffers[i].data, 2352 + 96);
|
||||
found = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(!found)
|
||||
{
|
||||
//int32 swt = MDFND_GetTime();
|
||||
MDFND_WaitCond(SBCond, SBMutex);
|
||||
//printf("SB Waited: %d\n", MDFND_GetTime() - swt);
|
||||
}
|
||||
} while(!found);
|
||||
|
||||
MDFND_UnlockMutex(SBMutex);
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
return(!error_condition);
|
||||
}
|
||||
|
||||
void CDIF_MT::HintReadSector(uint32 lba)
|
||||
{
|
||||
if(UnrecoverableError)
|
||||
return;
|
||||
|
||||
ReadThreadQueue.Write(CDIF_Message(CDIF_MSG_READ_SECTOR, lba));
|
||||
}
|
||||
|
||||
int CDIF::ReadSector(uint8* pBuf, uint32 lba, uint32 nSectors)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if(UnrecoverableError)
|
||||
return(false);
|
||||
|
||||
while(nSectors--)
|
||||
{
|
||||
uint8 tmpbuf[2352 + 96];
|
||||
|
||||
if(!ReadRawSector(tmpbuf, lba))
|
||||
{
|
||||
puts("CDIF Raw Read error");
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
if(!ValidateRawSector(tmpbuf))
|
||||
{
|
||||
MDFN_DispMessage(_("Uncorrectable data at sector %d"), lba);
|
||||
MDFN_PrintError(_("Uncorrectable data at sector %d"), lba);
|
||||
return(false);
|
||||
}
|
||||
|
||||
const int mode = tmpbuf[12 + 3];
|
||||
|
||||
if(!ret)
|
||||
ret = mode;
|
||||
|
||||
if(mode == 1)
|
||||
{
|
||||
memcpy(pBuf, &tmpbuf[12 + 4], 2048);
|
||||
}
|
||||
else if(mode == 2)
|
||||
{
|
||||
memcpy(pBuf, &tmpbuf[12 + 4 + 8], 2048);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("CDIF_ReadSector() invalid sector type at LBA=%u\n", (unsigned int)lba);
|
||||
return(false);
|
||||
}
|
||||
|
||||
pBuf += 2048;
|
||||
lba++;
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
bool CDIF_MT::Eject(bool eject_status)
|
||||
{
|
||||
if(UnrecoverableError)
|
||||
return(false);
|
||||
|
||||
try
|
||||
{
|
||||
CDIF_Message msg;
|
||||
|
||||
ReadThreadQueue.Write(CDIF_Message(CDIF_MSG_EJECT, eject_status));
|
||||
EmuThreadQueue.Read(&msg);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
MDFN_PrintError(_("Error on eject/insert attempt: %s"), e.what());
|
||||
return(false);
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// Single-threaded implementation follows.
|
||||
//
|
||||
//
|
||||
|
||||
CDIF_ST::CDIF_ST(CDAccess *cda) : disc_cdaccess(cda)
|
||||
{
|
||||
//puts("***WARNING USING SINGLE-THREADED CD READER***");
|
||||
|
||||
is_phys_cache = disc_cdaccess->Is_Physical();
|
||||
UnrecoverableError = false;
|
||||
DiscEjected = false;
|
||||
|
||||
disc_cdaccess->Read_TOC(&disc_toc);
|
||||
|
||||
if(disc_toc.first_track < 1 || disc_toc.last_track > 99 || disc_toc.first_track > disc_toc.last_track)
|
||||
{
|
||||
throw(MDFN_Error(0, _("TOC first(%d)/last(%d) track numbers bad."), disc_toc.first_track, disc_toc.last_track));
|
||||
}
|
||||
}
|
||||
|
||||
CDIF_ST::~CDIF_ST()
|
||||
{
|
||||
if(disc_cdaccess)
|
||||
{
|
||||
delete disc_cdaccess;
|
||||
disc_cdaccess = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CDIF_ST::HintReadSector(uint32 lba)
|
||||
{
|
||||
// TODO: disc_cdaccess seek hint? (probably not, would require asynchronousitycamel)
|
||||
}
|
||||
|
||||
bool CDIF_ST::ReadRawSector(uint8 *buf, uint32 lba)
|
||||
{
|
||||
if(UnrecoverableError)
|
||||
{
|
||||
memset(buf, 0, 2352 + 96);
|
||||
return(false);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
disc_cdaccess->Read_Raw_Sector(buf, lba);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
MDFN_PrintError(_("Sector %u read error: %s"), lba, e.what());
|
||||
memset(buf, 0, 2352 + 96);
|
||||
return(false);
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool CDIF_ST::Eject(bool eject_status)
|
||||
{
|
||||
if(UnrecoverableError)
|
||||
return(false);
|
||||
|
||||
try
|
||||
{
|
||||
if(eject_status != DiscEjected)
|
||||
{
|
||||
disc_cdaccess->Eject(eject_status);
|
||||
|
||||
// Set after ->Eject(), since it might throw an exception.
|
||||
DiscEjected = -1; // For if TOC reading fails or there's something horribly wrong with the disc.
|
||||
|
||||
if(!eject_status) // Re-read the TOC
|
||||
{
|
||||
disc_cdaccess->Read_TOC(&disc_toc);
|
||||
|
||||
if(disc_toc.first_track < 1 || disc_toc.last_track > 99 || disc_toc.first_track > disc_toc.last_track)
|
||||
{
|
||||
throw(MDFN_Error(0, _("TOC first(%d)/last(%d) track numbers bad."), disc_toc.first_track, disc_toc.last_track));
|
||||
}
|
||||
}
|
||||
DiscEjected = eject_status;
|
||||
}
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
MDFN_PrintError("%s", e.what());
|
||||
return(false);
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
class CDIF_Stream_Thing : public Stream
|
||||
{
|
||||
public:
|
||||
|
||||
CDIF_Stream_Thing(CDIF *cdintf_arg, uint32 lba_arg, uint32 sector_count_arg);
|
||||
~CDIF_Stream_Thing();
|
||||
|
||||
virtual uint64 attributes(void);
|
||||
virtual uint8 *map(void);
|
||||
virtual void unmap(void);
|
||||
|
||||
virtual uint64 read(void *data, uint64 count, bool error_on_eos = true);
|
||||
virtual void write(const void *data, uint64 count);
|
||||
|
||||
virtual void seek(int64 offset, int whence);
|
||||
virtual int64 tell(void);
|
||||
virtual int64 size(void);
|
||||
virtual void close(void);
|
||||
|
||||
private:
|
||||
CDIF *cdintf;
|
||||
const uint32 start_lba;
|
||||
const uint32 sector_count;
|
||||
int64 position;
|
||||
};
|
||||
|
||||
CDIF_Stream_Thing::CDIF_Stream_Thing(CDIF *cdintf_arg, uint32 start_lba_arg, uint32 sector_count_arg) : cdintf(cdintf_arg), start_lba(start_lba_arg), sector_count(sector_count_arg)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CDIF_Stream_Thing::~CDIF_Stream_Thing()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
uint64 CDIF_Stream_Thing::attributes(void)
|
||||
{
|
||||
return(ATTRIBUTE_READABLE | ATTRIBUTE_SEEKABLE);
|
||||
}
|
||||
|
||||
uint8 *CDIF_Stream_Thing::map(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CDIF_Stream_Thing::unmap(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
uint64 CDIF_Stream_Thing::read(void *data, uint64 count, bool error_on_eos)
|
||||
{
|
||||
if(count > (((uint64)sector_count * 2048) - position))
|
||||
{
|
||||
if(error_on_eos)
|
||||
{
|
||||
throw MDFN_Error(0, "EOF");
|
||||
}
|
||||
|
||||
count = ((uint64)sector_count * 2048) - position;
|
||||
}
|
||||
|
||||
if(!count)
|
||||
return(0);
|
||||
|
||||
for(uint64 rp = position; rp < (position + count); rp = (rp &~ 2047) + 2048)
|
||||
{
|
||||
uint8 buf[2048];
|
||||
|
||||
if(!cdintf->ReadSector(buf, start_lba + (rp / 2048), 1))
|
||||
{
|
||||
throw MDFN_Error(ErrnoHolder(EIO));
|
||||
}
|
||||
|
||||
//::printf("Meow: %08llx -- %08llx\n", count, (rp - position) + std::min<uint64>(2048 - (rp & 2047), count - (rp - position)));
|
||||
memcpy((uint8*)data + (rp - position), buf + (rp & 2047), std::min<uint64>(2048 - (rp & 2047), count - (rp - position)));
|
||||
}
|
||||
|
||||
position += count;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void CDIF_Stream_Thing::write(const void *data, uint64 count)
|
||||
{
|
||||
throw MDFN_Error(ErrnoHolder(EBADF));
|
||||
}
|
||||
|
||||
void CDIF_Stream_Thing::seek(int64 offset, int whence)
|
||||
{
|
||||
int64 new_position;
|
||||
|
||||
switch(whence)
|
||||
{
|
||||
default:
|
||||
throw MDFN_Error(ErrnoHolder(EINVAL));
|
||||
break;
|
||||
|
||||
case SEEK_SET:
|
||||
new_position = offset;
|
||||
break;
|
||||
|
||||
case SEEK_CUR:
|
||||
new_position = position + offset;
|
||||
break;
|
||||
|
||||
case SEEK_END:
|
||||
new_position = ((int64)sector_count * 2048) + offset;
|
||||
break;
|
||||
}
|
||||
|
||||
if(new_position < 0 || new_position > ((int64)sector_count * 2048))
|
||||
throw MDFN_Error(ErrnoHolder(EINVAL));
|
||||
|
||||
position = new_position;
|
||||
}
|
||||
|
||||
int64 CDIF_Stream_Thing::tell(void)
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
int64 CDIF_Stream_Thing::size(void)
|
||||
{
|
||||
return(sector_count * 2048);
|
||||
}
|
||||
|
||||
void CDIF_Stream_Thing::close(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
Stream *CDIF::MakeStream(uint32 lba, uint32 sector_count)
|
||||
{
|
||||
return new CDIF_Stream_Thing(this, lba, sector_count);
|
||||
}
|
||||
|
||||
|
||||
CDIF *CDIF_Open(const char *path, const bool is_device, bool image_memcache)
|
||||
{
|
||||
if(is_device)
|
||||
return new CDIF_MT(cdaccess_open_phys(path));
|
||||
else
|
||||
{
|
||||
CDAccess *cda = cdaccess_open_image(path, image_memcache);
|
||||
|
||||
if(!image_memcache)
|
||||
return new CDIF_MT(cda);
|
||||
else
|
||||
return new CDIF_ST(cda);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __MDFN_CDROM_CDROMIF_H
|
||||
#define __MDFN_CDROM_CDROMIF_H
|
||||
|
||||
#include "CDUtility.h"
|
||||
#include "../Stream.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
typedef CDUtility::TOC CD_TOC;
|
||||
|
||||
class CDIF
|
||||
{
|
||||
public:
|
||||
|
||||
CDIF();
|
||||
virtual ~CDIF();
|
||||
|
||||
inline void ReadTOC(CDUtility::TOC *read_target)
|
||||
{
|
||||
*read_target = disc_toc;
|
||||
}
|
||||
|
||||
virtual void HintReadSector(uint32 lba) = 0;
|
||||
virtual bool ReadRawSector(uint8 *buf, uint32 lba) = 0;
|
||||
|
||||
// Call for mode 1 or mode 2 form 1 only.
|
||||
bool ValidateRawSector(uint8 *buf);
|
||||
|
||||
// Utility/Wrapped functions
|
||||
// Reads mode 1 and mode2 form 1 sectors(2048 bytes per sector returned)
|
||||
// Will return the type(1, 2) of the first sector read to the buffer supplied, 0 on error
|
||||
int ReadSector(uint8* pBuf, uint32 lba, uint32 nSectors);
|
||||
|
||||
// Return true if operation succeeded or it was a NOP(either due to not being implemented, or the current status matches eject_status).
|
||||
// Returns false on failure(usually drive error of some kind; not completely fatal, can try again).
|
||||
virtual bool Eject(bool eject_status) = 0;
|
||||
|
||||
inline bool IsPhysical(void) { return(is_phys_cache); }
|
||||
|
||||
// For Mode 1, or Mode 2 Form 1.
|
||||
// No reference counting or whatever is done, so if you destroy the CDIF object before you destroy the returned Stream, things will go BOOM.
|
||||
Stream *MakeStream(uint32 lba, uint32 sector_count);
|
||||
|
||||
protected:
|
||||
bool UnrecoverableError;
|
||||
bool is_phys_cache;
|
||||
CDUtility::TOC disc_toc;
|
||||
int DiscEjected; // 0 = inserted, 1 = ejected, -1 = DRAGONS ATE THE DISC. NOM NOM NOM.
|
||||
};
|
||||
|
||||
CDIF *CDIF_Open(const char *path, const bool is_device, bool image_memcache);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,130 @@
|
|||
/* dvdisaster: Additional error correction for optical media.
|
||||
* Copyright (C) 2004-2007 Carsten Gnoerlich.
|
||||
* Project home page: http://www.dvdisaster.com
|
||||
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||
*
|
||||
* CRC32 code based upon public domain code by Ross Williams (see notes below)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
||||
* or direct your browser at http://www.gnu.org.
|
||||
*/
|
||||
|
||||
#include "dvdisaster.h"
|
||||
|
||||
/***
|
||||
*** EDC checksum used in CDROM sectors
|
||||
***/
|
||||
|
||||
/*****************************************************************/
|
||||
/* */
|
||||
/* CRC LOOKUP TABLE */
|
||||
/* ================ */
|
||||
/* The following CRC lookup table was generated automagically */
|
||||
/* by the Rocksoft^tm Model CRC Algorithm Table Generation */
|
||||
/* Program V1.0 using the following model parameters: */
|
||||
/* */
|
||||
/* Width : 4 bytes. */
|
||||
/* Poly : 0x8001801BL */
|
||||
/* Reverse : TRUE. */
|
||||
/* */
|
||||
/* For more information on the Rocksoft^tm Model CRC Algorithm, */
|
||||
/* see the document titled "A Painless Guide to CRC Error */
|
||||
/* Detection Algorithms" by Ross Williams */
|
||||
/* (ross@guest.adelaide.edu.au.). This document is likely to be */
|
||||
/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */
|
||||
/* */
|
||||
/*****************************************************************/
|
||||
|
||||
unsigned long edctable[256] =
|
||||
{
|
||||
0x00000000L, 0x90910101L, 0x91210201L, 0x01B00300L,
|
||||
0x92410401L, 0x02D00500L, 0x03600600L, 0x93F10701L,
|
||||
0x94810801L, 0x04100900L, 0x05A00A00L, 0x95310B01L,
|
||||
0x06C00C00L, 0x96510D01L, 0x97E10E01L, 0x07700F00L,
|
||||
0x99011001L, 0x09901100L, 0x08201200L, 0x98B11301L,
|
||||
0x0B401400L, 0x9BD11501L, 0x9A611601L, 0x0AF01700L,
|
||||
0x0D801800L, 0x9D111901L, 0x9CA11A01L, 0x0C301B00L,
|
||||
0x9FC11C01L, 0x0F501D00L, 0x0EE01E00L, 0x9E711F01L,
|
||||
0x82012001L, 0x12902100L, 0x13202200L, 0x83B12301L,
|
||||
0x10402400L, 0x80D12501L, 0x81612601L, 0x11F02700L,
|
||||
0x16802800L, 0x86112901L, 0x87A12A01L, 0x17302B00L,
|
||||
0x84C12C01L, 0x14502D00L, 0x15E02E00L, 0x85712F01L,
|
||||
0x1B003000L, 0x8B913101L, 0x8A213201L, 0x1AB03300L,
|
||||
0x89413401L, 0x19D03500L, 0x18603600L, 0x88F13701L,
|
||||
0x8F813801L, 0x1F103900L, 0x1EA03A00L, 0x8E313B01L,
|
||||
0x1DC03C00L, 0x8D513D01L, 0x8CE13E01L, 0x1C703F00L,
|
||||
0xB4014001L, 0x24904100L, 0x25204200L, 0xB5B14301L,
|
||||
0x26404400L, 0xB6D14501L, 0xB7614601L, 0x27F04700L,
|
||||
0x20804800L, 0xB0114901L, 0xB1A14A01L, 0x21304B00L,
|
||||
0xB2C14C01L, 0x22504D00L, 0x23E04E00L, 0xB3714F01L,
|
||||
0x2D005000L, 0xBD915101L, 0xBC215201L, 0x2CB05300L,
|
||||
0xBF415401L, 0x2FD05500L, 0x2E605600L, 0xBEF15701L,
|
||||
0xB9815801L, 0x29105900L, 0x28A05A00L, 0xB8315B01L,
|
||||
0x2BC05C00L, 0xBB515D01L, 0xBAE15E01L, 0x2A705F00L,
|
||||
0x36006000L, 0xA6916101L, 0xA7216201L, 0x37B06300L,
|
||||
0xA4416401L, 0x34D06500L, 0x35606600L, 0xA5F16701L,
|
||||
0xA2816801L, 0x32106900L, 0x33A06A00L, 0xA3316B01L,
|
||||
0x30C06C00L, 0xA0516D01L, 0xA1E16E01L, 0x31706F00L,
|
||||
0xAF017001L, 0x3F907100L, 0x3E207200L, 0xAEB17301L,
|
||||
0x3D407400L, 0xADD17501L, 0xAC617601L, 0x3CF07700L,
|
||||
0x3B807800L, 0xAB117901L, 0xAAA17A01L, 0x3A307B00L,
|
||||
0xA9C17C01L, 0x39507D00L, 0x38E07E00L, 0xA8717F01L,
|
||||
0xD8018001L, 0x48908100L, 0x49208200L, 0xD9B18301L,
|
||||
0x4A408400L, 0xDAD18501L, 0xDB618601L, 0x4BF08700L,
|
||||
0x4C808800L, 0xDC118901L, 0xDDA18A01L, 0x4D308B00L,
|
||||
0xDEC18C01L, 0x4E508D00L, 0x4FE08E00L, 0xDF718F01L,
|
||||
0x41009000L, 0xD1919101L, 0xD0219201L, 0x40B09300L,
|
||||
0xD3419401L, 0x43D09500L, 0x42609600L, 0xD2F19701L,
|
||||
0xD5819801L, 0x45109900L, 0x44A09A00L, 0xD4319B01L,
|
||||
0x47C09C00L, 0xD7519D01L, 0xD6E19E01L, 0x46709F00L,
|
||||
0x5A00A000L, 0xCA91A101L, 0xCB21A201L, 0x5BB0A300L,
|
||||
0xC841A401L, 0x58D0A500L, 0x5960A600L, 0xC9F1A701L,
|
||||
0xCE81A801L, 0x5E10A900L, 0x5FA0AA00L, 0xCF31AB01L,
|
||||
0x5CC0AC00L, 0xCC51AD01L, 0xCDE1AE01L, 0x5D70AF00L,
|
||||
0xC301B001L, 0x5390B100L, 0x5220B200L, 0xC2B1B301L,
|
||||
0x5140B400L, 0xC1D1B501L, 0xC061B601L, 0x50F0B700L,
|
||||
0x5780B800L, 0xC711B901L, 0xC6A1BA01L, 0x5630BB00L,
|
||||
0xC5C1BC01L, 0x5550BD00L, 0x54E0BE00L, 0xC471BF01L,
|
||||
0x6C00C000L, 0xFC91C101L, 0xFD21C201L, 0x6DB0C300L,
|
||||
0xFE41C401L, 0x6ED0C500L, 0x6F60C600L, 0xFFF1C701L,
|
||||
0xF881C801L, 0x6810C900L, 0x69A0CA00L, 0xF931CB01L,
|
||||
0x6AC0CC00L, 0xFA51CD01L, 0xFBE1CE01L, 0x6B70CF00L,
|
||||
0xF501D001L, 0x6590D100L, 0x6420D200L, 0xF4B1D301L,
|
||||
0x6740D400L, 0xF7D1D501L, 0xF661D601L, 0x66F0D700L,
|
||||
0x6180D800L, 0xF111D901L, 0xF0A1DA01L, 0x6030DB00L,
|
||||
0xF3C1DC01L, 0x6350DD00L, 0x62E0DE00L, 0xF271DF01L,
|
||||
0xEE01E001L, 0x7E90E100L, 0x7F20E200L, 0xEFB1E301L,
|
||||
0x7C40E400L, 0xECD1E501L, 0xED61E601L, 0x7DF0E700L,
|
||||
0x7A80E800L, 0xEA11E901L, 0xEBA1EA01L, 0x7B30EB00L,
|
||||
0xE8C1EC01L, 0x7850ED00L, 0x79E0EE00L, 0xE971EF01L,
|
||||
0x7700F000L, 0xE791F101L, 0xE621F201L, 0x76B0F300L,
|
||||
0xE541F401L, 0x75D0F500L, 0x7460F600L, 0xE4F1F701L,
|
||||
0xE381F801L, 0x7310F900L, 0x72A0FA00L, 0xE231FB01L,
|
||||
0x71C0FC00L, 0xE151FD01L, 0xE0E1FE01L, 0x7070FF00L
|
||||
};
|
||||
|
||||
/*
|
||||
* CDROM EDC calculation
|
||||
*/
|
||||
|
||||
uint32 EDCCrc32(const unsigned char *data, int len)
|
||||
{
|
||||
uint32 crc = 0;
|
||||
|
||||
while(len--)
|
||||
crc = edctable[(crc ^ *data++) & 0xFF] ^ (crc >> 8);
|
||||
|
||||
return crc;
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
/* dvdisaster: Additional error correction for optical media.
|
||||
* Copyright (C) 2004-2007 Carsten Gnoerlich.
|
||||
* Project home page: http://www.dvdisaster.com
|
||||
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
||||
* or direct your browser at http://www.gnu.org.
|
||||
*/
|
||||
|
||||
#ifndef DVDISASTER_H
|
||||
#define DVDISASTER_H
|
||||
|
||||
/* "Dare to be gorgeous and unique.
|
||||
* But don't ever be cryptic or otherwise unfathomable.
|
||||
* Make it unforgettably great."
|
||||
*
|
||||
* From "A Final Note on Style",
|
||||
* Amiga Intuition Reference Manual, 1986, p. 231
|
||||
*/
|
||||
|
||||
/***
|
||||
*** I'm too lazy to mess with #include dependencies.
|
||||
*** Everything #includeable is rolled up herein...
|
||||
*/
|
||||
|
||||
#include "octoshock.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <math.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/***
|
||||
*** dvdisaster.c
|
||||
***/
|
||||
|
||||
void PrepareDeadSector(void);
|
||||
|
||||
void CreateEcc(void);
|
||||
void FixEcc(void);
|
||||
void Verify(void);
|
||||
|
||||
uint32 EDCCrc32(const unsigned char*, int);
|
||||
|
||||
/***
|
||||
*** galois.c
|
||||
***
|
||||
* This is currently the hardcoded GF(2**8).
|
||||
* int32 gives abundant space for the GF.
|
||||
* Squeezing it down to uint8 won't probably gain much,
|
||||
* so we implement this defensively here.
|
||||
*
|
||||
* Note that some performance critical stuff needs to
|
||||
* be #included from galois-inlines.h
|
||||
*/
|
||||
|
||||
/* Galois field parameters for 8bit symbol Reed-Solomon code */
|
||||
|
||||
#define GF_SYMBOLSIZE 8
|
||||
#define GF_FIELDSIZE (1<<GF_SYMBOLSIZE)
|
||||
#define GF_FIELDMAX (GF_FIELDSIZE-1)
|
||||
#define GF_ALPHA0 GF_FIELDMAX
|
||||
|
||||
/* Lookup tables for Galois field arithmetic */
|
||||
|
||||
typedef struct _GaloisTables
|
||||
{ int32 gfGenerator; /* GF generator polynomial */
|
||||
int32 *indexOf; /* log */
|
||||
int32 *alphaTo; /* inverse log */
|
||||
int32 *encAlphaTo; /* inverse log optimized for encoder */
|
||||
} GaloisTables;
|
||||
|
||||
/* Lookup and working tables for the ReedSolomon codecs */
|
||||
|
||||
typedef struct _ReedSolomonTables
|
||||
{ GaloisTables *gfTables;/* from above */
|
||||
int32 *gpoly; /* RS code generator polynomial */
|
||||
int32 fcr; /* first consecutive root of RS generator polynomial */
|
||||
int32 primElem; /* primitive field element */
|
||||
int32 nroots; /* degree of RS generator polynomial */
|
||||
int32 ndata; /* data bytes per ecc block */
|
||||
} ReedSolomonTables;
|
||||
|
||||
GaloisTables* CreateGaloisTables(int32);
|
||||
void FreeGaloisTables(GaloisTables*);
|
||||
|
||||
ReedSolomonTables *CreateReedSolomonTables(GaloisTables*, int32, int32, int);
|
||||
void FreeReedSolomonTables(ReedSolomonTables*);
|
||||
|
||||
/***
|
||||
*** l-ec.c
|
||||
***/
|
||||
|
||||
#define N_P_VECTORS 86 /* 43 16bit p vectors */
|
||||
#define P_VECTOR_SIZE 26 /* using RS(26,24) ECC */
|
||||
|
||||
#define N_Q_VECTORS 52 /* 26 16bit q vectors */
|
||||
#define Q_VECTOR_SIZE 45 /* using RS(45,43) ECC */
|
||||
|
||||
#define P_PADDING 229 /* padding values for */
|
||||
#define Q_PADDING 210 /* shortened RS code */
|
||||
|
||||
int PToByteIndex(int, int);
|
||||
int QToByteIndex(int, int);
|
||||
void ByteIndexToP(int, int*, int*);
|
||||
void ByteIndexToQ(int, int*, int*);
|
||||
|
||||
void GetPVector(unsigned char*, unsigned char*, int);
|
||||
void SetPVector(unsigned char*, unsigned char*, int);
|
||||
void FillPVector(unsigned char*, unsigned char, int);
|
||||
void AndPVector(unsigned char*, unsigned char, int);
|
||||
void OrPVector(unsigned char*, unsigned char, int);
|
||||
|
||||
void GetQVector(unsigned char*, unsigned char*, int);
|
||||
void SetQVector(unsigned char*, unsigned char*, int);
|
||||
void FillQVector(unsigned char*, unsigned char, int);
|
||||
void AndQVector(unsigned char*, unsigned char, int);
|
||||
void OrQVector(unsigned char*, unsigned char, int);
|
||||
|
||||
int DecodePQ(ReedSolomonTables*, unsigned char*, int, int*, int);
|
||||
|
||||
int CountC2Errors(unsigned char*);
|
||||
|
||||
/***
|
||||
*** misc.c
|
||||
***/
|
||||
|
||||
char* sgettext(char*);
|
||||
char* sgettext_utf8(char*);
|
||||
|
||||
int64 uchar_to_int64(unsigned char*);
|
||||
void int64_to_uchar(unsigned char*, int64);
|
||||
|
||||
void CalcSectors(int64, int64*, int*);
|
||||
|
||||
/***
|
||||
*** recover-raw.c
|
||||
***/
|
||||
|
||||
#define CD_RAW_SECTOR_SIZE 2352
|
||||
#define CD_RAW_C2_SECTOR_SIZE (2352+294) /* main channel plus C2 vector */
|
||||
|
||||
int CheckEDC(const unsigned char*, bool);
|
||||
int CheckMSF(unsigned char*, int);
|
||||
|
||||
|
||||
int ValidateRawSector(unsigned char *frame, bool xaMode);
|
||||
bool Init_LEC_Correct(void);
|
||||
void Kill_LEC_Correct(void);
|
||||
|
||||
|
||||
#endif /* DVDISASTER_H */
|
|
@ -0,0 +1,40 @@
|
|||
/* dvdisaster: Additional error correction for optical media.
|
||||
* Copyright (C) 2004-2007 Carsten Gnoerlich.
|
||||
* Project home page: http://www.dvdisaster.com
|
||||
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||
*
|
||||
* The Reed-Solomon error correction draws a lot of inspiration - and even code -
|
||||
* from Phil Karn's excellent Reed-Solomon library: http://www.ka9q.net/code/fec/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
||||
* or direct your browser at http://www.gnu.org.
|
||||
*/
|
||||
|
||||
#include "dvdisaster.h"
|
||||
|
||||
/*
|
||||
* The following routine is performance critical.
|
||||
*/
|
||||
|
||||
static inline int mod_fieldmax(int x)
|
||||
{
|
||||
while (x >= GF_FIELDMAX)
|
||||
{
|
||||
x -= GF_FIELDMAX;
|
||||
x = (x >> GF_SYMBOLSIZE) + (x & GF_FIELDMAX);
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
/* dvdisaster: Additional error correction for optical media.
|
||||
* Copyright (C) 2004-2007 Carsten Gnoerlich.
|
||||
* Project home page: http://www.dvdisaster.com
|
||||
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||
*
|
||||
* The Reed-Solomon error correction draws a lot of inspiration - and even code -
|
||||
* from Phil Karn's excellent Reed-Solomon library: http://www.ka9q.net/code/fec/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
||||
* or direct your browser at http://www.gnu.org.
|
||||
*/
|
||||
|
||||
#include "dvdisaster.h"
|
||||
|
||||
#include "galois-inlines.h"
|
||||
|
||||
/***
|
||||
*** Galois field arithmetic.
|
||||
***
|
||||
* Calculations are done over the extension field GF(2**n).
|
||||
* Be careful not to overgeneralize these arithmetics;
|
||||
* they only work for the case of GF(p**n) with p being prime.
|
||||
*/
|
||||
|
||||
/* Initialize the Galois field tables */
|
||||
|
||||
|
||||
GaloisTables* CreateGaloisTables(int32 gf_generator)
|
||||
{
|
||||
GaloisTables *gt = (GaloisTables *)calloc(1, sizeof(GaloisTables));
|
||||
int32 b,log;
|
||||
|
||||
/* Allocate the tables.
|
||||
The encoder uses a special version of alpha_to which has the mod_fieldmax()
|
||||
folded into the table. */
|
||||
|
||||
gt->gfGenerator = gf_generator;
|
||||
|
||||
gt->indexOf = (int32 *)calloc(GF_FIELDSIZE, sizeof(int32));
|
||||
gt->alphaTo = (int32 *)calloc(GF_FIELDSIZE, sizeof(int32));
|
||||
gt->encAlphaTo = (int32 *)calloc(2*GF_FIELDSIZE, sizeof(int32));
|
||||
|
||||
/* create the log/ilog values */
|
||||
|
||||
for(b=1, log=0; log<GF_FIELDMAX; log++)
|
||||
{ gt->indexOf[b] = log;
|
||||
gt->alphaTo[log] = b;
|
||||
b = b << 1;
|
||||
if(b & GF_FIELDSIZE)
|
||||
b = b ^ gf_generator;
|
||||
}
|
||||
|
||||
if(b!=1)
|
||||
{
|
||||
printf("Failed to create the Galois field log tables!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* we're even closed using infinity (makes things easier) */
|
||||
|
||||
gt->indexOf[0] = GF_ALPHA0; /* log(0) = inf */
|
||||
gt->alphaTo[GF_ALPHA0] = 0; /* and the other way around */
|
||||
|
||||
for(b=0; b<2*GF_FIELDSIZE; b++)
|
||||
gt->encAlphaTo[b] = gt->alphaTo[mod_fieldmax(b)];
|
||||
|
||||
return gt;
|
||||
}
|
||||
|
||||
void FreeGaloisTables(GaloisTables *gt)
|
||||
{
|
||||
if(gt->indexOf) free(gt->indexOf);
|
||||
if(gt->alphaTo) free(gt->alphaTo);
|
||||
if(gt->encAlphaTo) free(gt->encAlphaTo);
|
||||
|
||||
free(gt);
|
||||
}
|
||||
|
||||
/***
|
||||
*** Create the the Reed-Solomon generator polynomial
|
||||
*** and some auxiliary data structures.
|
||||
*/
|
||||
|
||||
ReedSolomonTables *CreateReedSolomonTables(GaloisTables *gt,
|
||||
int32 first_consecutive_root,
|
||||
int32 prim_elem,
|
||||
int nroots_in)
|
||||
{ ReedSolomonTables *rt = (ReedSolomonTables *)calloc(1, sizeof(ReedSolomonTables));
|
||||
int32 i,j,root;
|
||||
|
||||
rt->gfTables = gt;
|
||||
rt->fcr = first_consecutive_root;
|
||||
rt->primElem = prim_elem;
|
||||
rt->nroots = nroots_in;
|
||||
rt->ndata = GF_FIELDMAX - rt->nroots;
|
||||
|
||||
rt->gpoly = (int32 *)calloc((rt->nroots+1), sizeof(int32));
|
||||
|
||||
/* Create the RS code generator polynomial */
|
||||
|
||||
rt->gpoly[0] = 1;
|
||||
|
||||
for(i=0, root=first_consecutive_root*prim_elem; i<rt->nroots; i++, root+=prim_elem)
|
||||
{ rt->gpoly[i+1] = 1;
|
||||
|
||||
/* Multiply gpoly by alpha**(root+x) */
|
||||
|
||||
for(j=i; j>0; j--)
|
||||
{
|
||||
if(rt->gpoly[j] != 0)
|
||||
rt->gpoly[j] = rt->gpoly[j-1] ^ gt->alphaTo[mod_fieldmax(gt->indexOf[rt->gpoly[j]] + root)];
|
||||
else
|
||||
rt->gpoly[j] = rt->gpoly[j-1];
|
||||
}
|
||||
|
||||
rt->gpoly[0] = gt->alphaTo[mod_fieldmax(gt->indexOf[rt->gpoly[0]] + root)];
|
||||
}
|
||||
|
||||
/* Store the polynomials index for faster encoding */
|
||||
|
||||
for(i=0; i<=rt->nroots; i++)
|
||||
rt->gpoly[i] = gt->indexOf[rt->gpoly[i]];
|
||||
|
||||
#if 0
|
||||
/* for the precalculated unrolled loops only */
|
||||
|
||||
for(i=gt->nroots-1; i>0; i--)
|
||||
PrintCLI(
|
||||
" par_idx[((++spk)&%d)] ^= enc_alpha_to[feedback + %3d];\n",
|
||||
nroots-1,gt->gpoly[i]);
|
||||
|
||||
PrintCLI(" par_idx[sp] = enc_alpha_to[feedback + %3d];\n",
|
||||
gt->gpoly[0]);
|
||||
#endif
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
void FreeReedSolomonTables(ReedSolomonTables *rt)
|
||||
{
|
||||
if(rt->gpoly) free(rt->gpoly);
|
||||
|
||||
free(rt);
|
||||
}
|
|
@ -0,0 +1,478 @@
|
|||
/* dvdisaster: Additional error correction for optical media.
|
||||
* Copyright (C) 2004-2007 Carsten Gnoerlich.
|
||||
* Project home page: http://www.dvdisaster.com
|
||||
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||
*
|
||||
* The Reed-Solomon error correction draws a lot of inspiration - and even code -
|
||||
* from Phil Karn's excellent Reed-Solomon library: http://www.ka9q.net/code/fec/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
||||
* or direct your browser at http://www.gnu.org.
|
||||
*/
|
||||
|
||||
#include "dvdisaster.h"
|
||||
|
||||
#include "galois-inlines.h"
|
||||
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
/***
|
||||
*** Mapping between cd frame and parity vectors
|
||||
***/
|
||||
|
||||
/*
|
||||
* Mapping of frame bytes to P/Q Vectors
|
||||
*/
|
||||
|
||||
int PToByteIndex(int p, int i)
|
||||
{ return 12 + p + i*86;
|
||||
}
|
||||
|
||||
void ByteIndexToP(int b, int *p, int *i)
|
||||
{ *p = (b-12)%86;
|
||||
*i = (b-12)/86;
|
||||
}
|
||||
|
||||
int QToByteIndex(int q, int i)
|
||||
{ int offset = 12 + (q & 1);
|
||||
|
||||
if(i == 43) return 2248+q;
|
||||
if(i == 44) return 2300+q;
|
||||
|
||||
q&=~1;
|
||||
return offset + (q*43 + i*88) % 2236;
|
||||
}
|
||||
|
||||
void ByteIndexToQ(int b, int *q, int *i)
|
||||
{ int x,y,offset;
|
||||
|
||||
if(b >= 2300)
|
||||
{ *i = 44;
|
||||
*q = (b-2300);
|
||||
return;
|
||||
}
|
||||
|
||||
if(b >= 2248)
|
||||
{ *i = 43;
|
||||
*q = (b-2248);
|
||||
return;
|
||||
}
|
||||
|
||||
offset = b&1;
|
||||
b = (b-12)/2;
|
||||
x = b/43;
|
||||
y = (b-(x*43))%26;
|
||||
*i = b-(x*43);
|
||||
*q = 2*((x+26-y)%26)+offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* There are 86 vectors of P-parity, yielding a RS(26,24) code.
|
||||
*/
|
||||
|
||||
void GetPVector(unsigned char *frame, unsigned char *data, int n)
|
||||
{ int i;
|
||||
int w_idx = n+12;
|
||||
|
||||
for(i=0; i<26; i++, w_idx+=86)
|
||||
data[i] = frame[w_idx];
|
||||
}
|
||||
|
||||
void SetPVector(unsigned char *frame, unsigned char *data, int n)
|
||||
{ int i;
|
||||
int w_idx = n+12;
|
||||
|
||||
for(i=0; i<26; i++, w_idx+=86)
|
||||
frame[w_idx] = data[i];
|
||||
}
|
||||
|
||||
void FillPVector(unsigned char *frame, unsigned char data, int n)
|
||||
{ int i;
|
||||
int w_idx = n+12;
|
||||
|
||||
for(i=0; i<26; i++, w_idx+=86)
|
||||
frame[w_idx] = data;
|
||||
}
|
||||
|
||||
void OrPVector(unsigned char *frame, unsigned char value, int n)
|
||||
{ int i;
|
||||
int w_idx = n+12;
|
||||
|
||||
for(i=0; i<26; i++, w_idx+=86)
|
||||
frame[w_idx] |= value;
|
||||
}
|
||||
|
||||
void AndPVector(unsigned char *frame, unsigned char value, int n)
|
||||
{ int i;
|
||||
int w_idx = n+12;
|
||||
|
||||
for(i=0; i<26; i++, w_idx+=86)
|
||||
frame[w_idx] &= value;
|
||||
}
|
||||
|
||||
/*
|
||||
* There are 52 vectors of Q-parity, yielding a RS(45,43) code.
|
||||
*/
|
||||
|
||||
void GetQVector(unsigned char *frame, unsigned char *data, int n)
|
||||
{ int offset = 12 + (n & 1);
|
||||
int w_idx = (n&~1) * 43;
|
||||
int i;
|
||||
|
||||
for(i=0; i<43; i++, w_idx+=88)
|
||||
data[i] = frame[(w_idx % 2236) + offset];
|
||||
|
||||
data[43] = frame[2248 + n];
|
||||
data[44] = frame[2300 + n];
|
||||
}
|
||||
|
||||
void SetQVector(unsigned char *frame, unsigned char *data, int n)
|
||||
{ int offset = 12 + (n & 1);
|
||||
int w_idx = (n&~1) * 43;
|
||||
int i;
|
||||
|
||||
for(i=0; i<43; i++, w_idx+=88)
|
||||
frame[(w_idx % 2236) + offset] = data[i];
|
||||
|
||||
frame[2248 + n] = data[43];
|
||||
frame[2300 + n] = data[44];
|
||||
}
|
||||
|
||||
void FillQVector(unsigned char *frame, unsigned char data, int n)
|
||||
{ int offset = 12 + (n & 1);
|
||||
int w_idx = (n&~1) * 43;
|
||||
int i;
|
||||
|
||||
for(i=0; i<43; i++, w_idx+=88)
|
||||
frame[(w_idx % 2236) + offset] = data;
|
||||
|
||||
frame[2248 + n] = data;
|
||||
frame[2300 + n] = data;
|
||||
}
|
||||
|
||||
void OrQVector(unsigned char *frame, unsigned char data, int n)
|
||||
{ int offset = 12 + (n & 1);
|
||||
int w_idx = (n&~1) * 43;
|
||||
int i;
|
||||
|
||||
for(i=0; i<43; i++, w_idx+=88)
|
||||
frame[(w_idx % 2236) + offset] |= data;
|
||||
|
||||
frame[2248 + n] |= data;
|
||||
frame[2300 + n] |= data;
|
||||
}
|
||||
|
||||
void AndQVector(unsigned char *frame, unsigned char data, int n)
|
||||
{ int offset = 12 + (n & 1);
|
||||
int w_idx = (n&~1) * 43;
|
||||
int i;
|
||||
|
||||
for(i=0; i<43; i++, w_idx+=88)
|
||||
frame[(w_idx % 2236) + offset] &= data;
|
||||
|
||||
frame[2248 + n] &= data;
|
||||
frame[2300 + n] &= data;
|
||||
}
|
||||
|
||||
/***
|
||||
*** C2 error counting
|
||||
***/
|
||||
|
||||
int CountC2Errors(unsigned char *frame)
|
||||
{ int i,count = 0;
|
||||
frame += 2352;
|
||||
|
||||
for(i=0; i<294; i++, frame++)
|
||||
{ if(*frame & 0x01) count++;
|
||||
if(*frame & 0x02) count++;
|
||||
if(*frame & 0x04) count++;
|
||||
if(*frame & 0x08) count++;
|
||||
if(*frame & 0x10) count++;
|
||||
if(*frame & 0x20) count++;
|
||||
if(*frame & 0x40) count++;
|
||||
if(*frame & 0x80) count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/***
|
||||
*** L-EC error correction for CD raw data sectors
|
||||
***/
|
||||
|
||||
/*
|
||||
* These could be used from ReedSolomonTables,
|
||||
* but hardcoding them is faster.
|
||||
*/
|
||||
|
||||
#define NROOTS 2
|
||||
#define LEC_FIRST_ROOT 0 //GF_ALPHA0
|
||||
#define LEC_PRIM_ELEM 1
|
||||
#define LEC_PRIMTH_ROOT 1
|
||||
|
||||
/*
|
||||
* Calculate the error syndrome
|
||||
*/
|
||||
|
||||
int DecodePQ(ReedSolomonTables *rt, unsigned char *data, int padding,
|
||||
int *erasure_list, int erasure_count)
|
||||
{ GaloisTables *gt = rt->gfTables;
|
||||
int syndrome[NROOTS];
|
||||
int lambda[NROOTS+1];
|
||||
int omega[NROOTS+1];
|
||||
int b[NROOTS+1];
|
||||
int reg[NROOTS+1];
|
||||
int root[NROOTS];
|
||||
int loc[NROOTS];
|
||||
int syn_error;
|
||||
int deg_lambda,lambda_roots;
|
||||
int deg_omega;
|
||||
int shortened_size = GF_FIELDMAX - padding;
|
||||
int corrected = 0;
|
||||
int i,j,k;
|
||||
int r,el;
|
||||
|
||||
/*** Form the syndromes: Evaluate data(x) at roots of g(x) */
|
||||
|
||||
for(i=0; i<NROOTS; i++)
|
||||
syndrome[i] = data[0];
|
||||
|
||||
for(j=1; j<shortened_size; j++)
|
||||
for(i=0; i<NROOTS; i++)
|
||||
if(syndrome[i] == 0)
|
||||
syndrome[i] = data[j];
|
||||
else syndrome[i] = data[j] ^ gt->alphaTo[mod_fieldmax(gt->indexOf[syndrome[i]]
|
||||
+ (LEC_FIRST_ROOT+i)*LEC_PRIM_ELEM)];
|
||||
|
||||
/*** Convert syndrome to index form, check for nonzero condition. */
|
||||
|
||||
syn_error = 0;
|
||||
for(i=0; i<NROOTS; i++)
|
||||
{ syn_error |= syndrome[i];
|
||||
syndrome[i] = gt->indexOf[syndrome[i]];
|
||||
}
|
||||
|
||||
/*** If the syndrome is zero, everything is fine. */
|
||||
|
||||
if(!syn_error)
|
||||
return 0;
|
||||
|
||||
/*** Initialize lambda to be the erasure locator polynomial */
|
||||
|
||||
lambda[0] = 1;
|
||||
lambda[1] = lambda[2] = 0;
|
||||
|
||||
erasure_list[0] += padding;
|
||||
erasure_list[1] += padding;
|
||||
|
||||
if(erasure_count > 2) /* sanity check */
|
||||
erasure_count = 0;
|
||||
|
||||
if(erasure_count > 0)
|
||||
{ lambda[1] = gt->alphaTo[mod_fieldmax(LEC_PRIM_ELEM*(GF_FIELDMAX-1-erasure_list[0]))];
|
||||
|
||||
for(i=1; i<erasure_count; i++)
|
||||
{ int u = mod_fieldmax(LEC_PRIM_ELEM*(GF_FIELDMAX-1-erasure_list[i]));
|
||||
|
||||
for(j=i+1; j>0; j--)
|
||||
{ int tmp = gt->indexOf[lambda[j-1]];
|
||||
|
||||
if(tmp != GF_ALPHA0)
|
||||
lambda[j] ^= gt->alphaTo[mod_fieldmax(u + tmp)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(i=0; i<NROOTS+1; i++)
|
||||
b[i] = gt->indexOf[lambda[i]];
|
||||
|
||||
/*** Berlekamp-Massey algorithm to determine error+erasure locator polynomial */
|
||||
|
||||
r = erasure_count; /* r is the step number */
|
||||
el = erasure_count;
|
||||
|
||||
/* Compute discrepancy at the r-th step in poly-form */
|
||||
|
||||
while(++r <= NROOTS)
|
||||
{ int discr_r = 0;
|
||||
|
||||
for(i=0; i<r; i++)
|
||||
if((lambda[i] != 0) && (syndrome[r-i-1] != GF_ALPHA0))
|
||||
discr_r ^= gt->alphaTo[mod_fieldmax(gt->indexOf[lambda[i]] + syndrome[r-i-1])];
|
||||
|
||||
discr_r = gt->indexOf[discr_r];
|
||||
|
||||
if(discr_r == GF_ALPHA0)
|
||||
{ /* B(x) = x*B(x) */
|
||||
memmove(b+1, b, NROOTS*sizeof(b[0]));
|
||||
b[0] = GF_ALPHA0;
|
||||
}
|
||||
else
|
||||
{ int t[NROOTS+1];
|
||||
|
||||
/* T(x) = lambda(x) - discr_r*x*b(x) */
|
||||
t[0] = lambda[0];
|
||||
for(i=0; i<NROOTS; i++)
|
||||
{ if(b[i] != GF_ALPHA0)
|
||||
t[i+1] = lambda[i+1] ^ gt->alphaTo[mod_fieldmax(discr_r + b[i])];
|
||||
else t[i+1] = lambda[i+1];
|
||||
}
|
||||
|
||||
if(2*el <= r+erasure_count-1)
|
||||
{ el = r + erasure_count - el;
|
||||
|
||||
/* B(x) <-- inv(discr_r) * lambda(x) */
|
||||
for(i=0; i<=NROOTS; i++)
|
||||
b[i] = (lambda[i] == 0) ? GF_ALPHA0
|
||||
: mod_fieldmax(gt->indexOf[lambda[i]] - discr_r + GF_FIELDMAX);
|
||||
}
|
||||
else
|
||||
{ /* 2 lines below: B(x) <-- x*B(x) */
|
||||
memmove(b+1, b, NROOTS*sizeof(b[0]));
|
||||
b[0] = GF_ALPHA0;
|
||||
}
|
||||
|
||||
memcpy(lambda, t, (NROOTS+1)*sizeof(t[0]));
|
||||
}
|
||||
}
|
||||
|
||||
/*** Convert lambda to index form and compute deg(lambda(x)) */
|
||||
|
||||
deg_lambda = 0;
|
||||
for(i=0; i<NROOTS+1; i++)
|
||||
{ lambda[i] = gt->indexOf[lambda[i]];
|
||||
if(lambda[i] != GF_ALPHA0)
|
||||
deg_lambda = i;
|
||||
}
|
||||
|
||||
/*** Find roots of the error+erasure locator polynomial by Chien search */
|
||||
|
||||
memcpy(reg+1, lambda+1, NROOTS*sizeof(reg[0]));
|
||||
lambda_roots = 0; /* Number of roots of lambda(x) */
|
||||
|
||||
for(i=1, k=LEC_PRIMTH_ROOT-1; i<=GF_FIELDMAX; i++, k=mod_fieldmax(k+LEC_PRIMTH_ROOT))
|
||||
{ int q=1; /* lambda[0] is always 0 */
|
||||
|
||||
for(j=deg_lambda; j>0; j--)
|
||||
{ if(reg[j] != GF_ALPHA0)
|
||||
{ reg[j] = mod_fieldmax(reg[j] + j);
|
||||
q ^= gt->alphaTo[reg[j]];
|
||||
}
|
||||
}
|
||||
|
||||
if(q != 0) continue; /* Not a root */
|
||||
|
||||
/* store root in index-form and the error location number */
|
||||
|
||||
root[lambda_roots] = i;
|
||||
loc[lambda_roots] = k;
|
||||
|
||||
/* If we've already found max possible roots, abort the search to save time */
|
||||
|
||||
if(++lambda_roots == deg_lambda) break;
|
||||
}
|
||||
|
||||
/* deg(lambda) unequal to number of roots => uncorrectable error detected
|
||||
This is not reliable for very small numbers of roots, e.g. nroots = 2 */
|
||||
|
||||
if(deg_lambda != lambda_roots)
|
||||
{ return -1;
|
||||
}
|
||||
|
||||
/* Compute err+eras evaluator poly omega(x) = syn(x)*lambda(x)
|
||||
(modulo x**nroots). in index form. Also find deg(omega). */
|
||||
|
||||
deg_omega = deg_lambda-1;
|
||||
|
||||
for(i=0; i<=deg_omega; i++)
|
||||
{ int tmp = 0;
|
||||
|
||||
for(j=i; j>=0; j--)
|
||||
{ if((syndrome[i - j] != GF_ALPHA0) && (lambda[j] != GF_ALPHA0))
|
||||
tmp ^= gt->alphaTo[mod_fieldmax(syndrome[i - j] + lambda[j])];
|
||||
}
|
||||
|
||||
omega[i] = gt->indexOf[tmp];
|
||||
}
|
||||
|
||||
/* Compute error values in poly-form.
|
||||
num1 = omega(inv(X(l))),
|
||||
num2 = inv(X(l))**(FIRST_ROOT-1) and
|
||||
den = lambda_pr(inv(X(l))) all in poly-form. */
|
||||
|
||||
for(j=lambda_roots-1; j>=0; j--)
|
||||
{ int num1 = 0;
|
||||
int num2;
|
||||
int den;
|
||||
int location = loc[j];
|
||||
|
||||
for(i=deg_omega; i>=0; i--)
|
||||
{ if(omega[i] != GF_ALPHA0)
|
||||
num1 ^= gt->alphaTo[mod_fieldmax(omega[i] + i * root[j])];
|
||||
}
|
||||
|
||||
num2 = gt->alphaTo[mod_fieldmax(root[j] * (LEC_FIRST_ROOT - 1) + GF_FIELDMAX)];
|
||||
den = 0;
|
||||
|
||||
/* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
|
||||
|
||||
for(i=MIN(deg_lambda, NROOTS-1) & ~1; i>=0; i-=2)
|
||||
{ if(lambda[i+1] != GF_ALPHA0)
|
||||
den ^= gt->alphaTo[mod_fieldmax(lambda[i+1] + i * root[j])];
|
||||
}
|
||||
|
||||
/* Apply error to data */
|
||||
|
||||
if(num1 != 0 && location >= padding)
|
||||
{
|
||||
corrected++;
|
||||
data[location-padding] ^= gt->alphaTo[mod_fieldmax(gt->indexOf[num1] + gt->indexOf[num2]
|
||||
+ GF_FIELDMAX - gt->indexOf[den])];
|
||||
|
||||
/* If no erasures were given, at most one error was corrected.
|
||||
Return its position in erasure_list[0]. */
|
||||
|
||||
if(!erasure_count)
|
||||
erasure_list[0] = location-padding;
|
||||
}
|
||||
#if 1
|
||||
else return -3;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*** Form the syndromes: Evaluate data(x) at roots of g(x) */
|
||||
|
||||
for(i=0; i<NROOTS; i++)
|
||||
syndrome[i] = data[0];
|
||||
|
||||
for(j=1; j<shortened_size; j++)
|
||||
for(i=0; i<NROOTS; i++)
|
||||
{ if(syndrome[i] == 0)
|
||||
syndrome[i] = data[j];
|
||||
else syndrome[i] = data[j] ^ gt->alphaTo[mod_fieldmax(gt->indexOf[syndrome[i]]
|
||||
+ (LEC_FIRST_ROOT+i)*LEC_PRIM_ELEM)];
|
||||
}
|
||||
|
||||
/*** Convert syndrome to index form, check for nonzero condition. */
|
||||
#if 1
|
||||
for(i=0; i<NROOTS; i++)
|
||||
if(syndrome[i])
|
||||
return -2;
|
||||
#endif
|
||||
|
||||
return corrected;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,691 @@
|
|||
/* cdrdao - write audio CD-Rs in disc-at-once mode
|
||||
*
|
||||
* Copyright (C) 1998-2002 Andreas Mueller <andreas@daneb.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "lec.h"
|
||||
|
||||
#define GF8_PRIM_POLY 0x11d /* x^8 + x^4 + x^3 + x^2 + 1 */
|
||||
|
||||
#define EDC_POLY 0x8001801b /* (x^16 + x^15 + x^2 + 1) (x^16 + x^2 + x + 1) */
|
||||
|
||||
#define LEC_HEADER_OFFSET 12
|
||||
#define LEC_DATA_OFFSET 16
|
||||
#define LEC_MODE1_DATA_LEN 2048
|
||||
#define LEC_MODE1_EDC_OFFSET 2064
|
||||
#define LEC_MODE1_INTERMEDIATE_OFFSET 2068
|
||||
#define LEC_MODE1_P_PARITY_OFFSET 2076
|
||||
#define LEC_MODE1_Q_PARITY_OFFSET 2248
|
||||
#define LEC_MODE2_FORM1_DATA_LEN (2048+8)
|
||||
#define LEC_MODE2_FORM1_EDC_OFFSET 2072
|
||||
#define LEC_MODE2_FORM2_DATA_LEN (2324+8)
|
||||
#define LEC_MODE2_FORM2_EDC_OFFSET 2348
|
||||
|
||||
|
||||
typedef u_int8_t gf8_t;
|
||||
|
||||
static u_int8_t GF8_LOG[256];
|
||||
static gf8_t GF8_ILOG[256];
|
||||
|
||||
static const class Gf8_Q_Coeffs_Results_01 {
|
||||
private:
|
||||
u_int16_t table[43][256];
|
||||
public:
|
||||
Gf8_Q_Coeffs_Results_01();
|
||||
~Gf8_Q_Coeffs_Results_01() {}
|
||||
const u_int16_t *operator[] (int i) const { return &table[i][0]; }
|
||||
operator const u_int16_t *() const { return &table[0][0]; }
|
||||
} CF8_Q_COEFFS_RESULTS_01;
|
||||
|
||||
static const class CrcTable {
|
||||
private:
|
||||
u_int32_t table[256];
|
||||
public:
|
||||
CrcTable();
|
||||
~CrcTable() {}
|
||||
u_int32_t operator[](int i) const { return table[i]; }
|
||||
operator const u_int32_t *() const { return table; }
|
||||
} CRCTABLE;
|
||||
|
||||
static const class ScrambleTable {
|
||||
private:
|
||||
u_int8_t table[2340];
|
||||
public:
|
||||
ScrambleTable();
|
||||
~ScrambleTable() {}
|
||||
u_int8_t operator[](int i) const { return table[i]; }
|
||||
operator const u_int8_t *() const { return table; }
|
||||
} SCRAMBLE_TABLE;
|
||||
|
||||
/* Creates the logarithm and inverse logarithm table that is required
|
||||
* for performing multiplication in the GF(8) domain.
|
||||
*/
|
||||
static void gf8_create_log_tables()
|
||||
{
|
||||
u_int8_t log;
|
||||
u_int16_t b;
|
||||
|
||||
for (b = 0; b <= 255; b++) {
|
||||
GF8_LOG[b] = 0;
|
||||
GF8_ILOG[b] = 0;
|
||||
}
|
||||
|
||||
b = 1;
|
||||
|
||||
for (log = 0; log < 255; log++) {
|
||||
GF8_LOG[(u_int8_t)b] = log;
|
||||
GF8_ILOG[log] = (u_int8_t)b;
|
||||
|
||||
b <<= 1;
|
||||
|
||||
if ((b & 0x100) != 0)
|
||||
b ^= GF8_PRIM_POLY;
|
||||
}
|
||||
}
|
||||
|
||||
/* Addition in the GF(8) domain: just the XOR of the values.
|
||||
*/
|
||||
#define gf8_add(a, b) (a) ^ (b)
|
||||
|
||||
|
||||
/* Multiplication in the GF(8) domain: add the logarithms (modulo 255)
|
||||
* and return the inverse logarithm. Not used!
|
||||
*/
|
||||
#if 0
|
||||
static gf8_t gf8_mult(gf8_t a, gf8_t b)
|
||||
{
|
||||
int16_t sum;
|
||||
|
||||
if (a == 0 || b == 0)
|
||||
return 0;
|
||||
|
||||
sum = GF8_LOG[a] + GF8_LOG[b];
|
||||
|
||||
if (sum >= 255)
|
||||
sum -= 255;
|
||||
|
||||
return GF8_ILOG[sum];
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Division in the GF(8) domain: Like multiplication but logarithms a
|
||||
* subtracted.
|
||||
*/
|
||||
static gf8_t gf8_div(gf8_t a, gf8_t b)
|
||||
{
|
||||
int16_t sum;
|
||||
|
||||
assert(b != 0);
|
||||
|
||||
if (a == 0)
|
||||
return 0;
|
||||
|
||||
sum = GF8_LOG[a] - GF8_LOG[b];
|
||||
|
||||
if (sum < 0)
|
||||
sum += 255;
|
||||
|
||||
return GF8_ILOG[sum];
|
||||
}
|
||||
|
||||
Gf8_Q_Coeffs_Results_01::Gf8_Q_Coeffs_Results_01()
|
||||
{
|
||||
int i, j;
|
||||
u_int16_t c;
|
||||
gf8_t GF8_COEFFS_HELP[2][45];
|
||||
u_int8_t GF8_Q_COEFFS[2][45];
|
||||
|
||||
|
||||
gf8_create_log_tables();
|
||||
|
||||
/* build matrix H:
|
||||
* 1 1 ... 1 1
|
||||
* a^44 a^43 ... a^1 a^0
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
for (j = 0; j < 45; j++) {
|
||||
GF8_COEFFS_HELP[0][j] = 1; /* e0 */
|
||||
GF8_COEFFS_HELP[1][j] = GF8_ILOG[44-j]; /* e1 */
|
||||
}
|
||||
|
||||
|
||||
/* resolve equation system for parity byte 0 and 1 */
|
||||
|
||||
/* e1' = e1 + e0 */
|
||||
for (j = 0; j < 45; j++) {
|
||||
GF8_Q_COEFFS[1][j] = gf8_add(GF8_COEFFS_HELP[1][j],
|
||||
GF8_COEFFS_HELP[0][j]);
|
||||
}
|
||||
|
||||
/* e1'' = e1' / (a^1 + 1) */
|
||||
for (j = 0; j < 45; j++) {
|
||||
GF8_Q_COEFFS[1][j] = gf8_div(GF8_Q_COEFFS[1][j], GF8_Q_COEFFS[1][43]);
|
||||
}
|
||||
|
||||
/* e0' = e0 + e1 / a^1 */
|
||||
for (j = 0; j < 45; j++) {
|
||||
GF8_Q_COEFFS[0][j] = gf8_add(GF8_COEFFS_HELP[0][j],
|
||||
gf8_div(GF8_COEFFS_HELP[1][j],
|
||||
GF8_ILOG[1]));
|
||||
}
|
||||
|
||||
/* e0'' = e0' / (1 + 1 / a^1) */
|
||||
for (j = 0; j < 45; j++) {
|
||||
GF8_Q_COEFFS[0][j] = gf8_div(GF8_Q_COEFFS[0][j], GF8_Q_COEFFS[0][44]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the products of 0..255 with all of the Q coefficients in
|
||||
* advance. When building the scalar product between the data vectors
|
||||
* and the P/Q vectors the individual products can be looked up in
|
||||
* this table
|
||||
*
|
||||
* The P parity coefficients are just a subset of the Q coefficients so
|
||||
* that we do not need to create a separate table for them.
|
||||
*/
|
||||
|
||||
for (j = 0; j < 43; j++) {
|
||||
|
||||
table[j][0] = 0;
|
||||
|
||||
for (i = 1; i < 256; i++) {
|
||||
c = GF8_LOG[i] + GF8_LOG[GF8_Q_COEFFS[0][j]];
|
||||
if (c >= 255) c -= 255;
|
||||
table[j][i] = GF8_ILOG[c];
|
||||
|
||||
c = GF8_LOG[i] + GF8_LOG[GF8_Q_COEFFS[1][j]];
|
||||
if (c >= 255) c -= 255;
|
||||
table[j][i] |= GF8_ILOG[c]<<8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Reverses the bits in 'd'. 'bits' defines the bit width of 'd'.
|
||||
*/
|
||||
static u_int32_t mirror_bits(u_int32_t d, int bits)
|
||||
{
|
||||
int i;
|
||||
u_int32_t r = 0;
|
||||
|
||||
for (i = 0; i < bits; i++) {
|
||||
r <<= 1;
|
||||
|
||||
if ((d & 0x1) != 0)
|
||||
r |= 0x1;
|
||||
|
||||
d >>= 1;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Build the CRC lookup table for EDC_POLY poly. The CRC is 32 bit wide
|
||||
* and reversed (i.e. the bit stream is divided by the EDC_POLY with the
|
||||
* LSB first order).
|
||||
*/
|
||||
CrcTable::CrcTable ()
|
||||
{
|
||||
u_int32_t i, j;
|
||||
u_int32_t r;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
r = mirror_bits(i, 8);
|
||||
|
||||
r <<= 24;
|
||||
|
||||
for (j = 0; j < 8; j++) {
|
||||
if ((r & 0x80000000) != 0) {
|
||||
r <<= 1;
|
||||
r ^= EDC_POLY;
|
||||
}
|
||||
else {
|
||||
r <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
r = mirror_bits(r, 32);
|
||||
|
||||
table[i] = r;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculates the CRC of given data with given lengths based on the
|
||||
* table lookup algorithm.
|
||||
*/
|
||||
static u_int32_t calc_edc(u_int8_t *data, int len)
|
||||
{
|
||||
u_int32_t crc = 0;
|
||||
|
||||
while (len--) {
|
||||
crc = CRCTABLE[(int)(crc ^ *data++) & 0xff] ^ (crc >> 8);
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
/* Build the scramble table as defined in the yellow book. The bytes
|
||||
12 to 2351 of a sector will be XORed with the data of this table.
|
||||
*/
|
||||
ScrambleTable::ScrambleTable()
|
||||
{
|
||||
u_int16_t i, j;
|
||||
u_int16_t reg = 1;
|
||||
u_int8_t d;
|
||||
|
||||
for (i = 0; i < 2340; i++) {
|
||||
d = 0;
|
||||
|
||||
for (j = 0; j < 8; j++) {
|
||||
d >>= 1;
|
||||
|
||||
if ((reg & 0x1) != 0)
|
||||
d |= 0x80;
|
||||
|
||||
if ((reg & 0x1) != ((reg >> 1) & 0x1)) {
|
||||
reg >>= 1;
|
||||
reg |= 0x4000; /* 15-bit register */
|
||||
}
|
||||
else {
|
||||
reg >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
table[i] = d;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calc EDC for a MODE 1 sector
|
||||
*/
|
||||
static void calc_mode1_edc(u_int8_t *sector)
|
||||
{
|
||||
u_int32_t crc = calc_edc(sector, LEC_MODE1_DATA_LEN + 16);
|
||||
|
||||
sector[LEC_MODE1_EDC_OFFSET] = crc & 0xffL;
|
||||
sector[LEC_MODE1_EDC_OFFSET + 1] = (crc >> 8) & 0xffL;
|
||||
sector[LEC_MODE1_EDC_OFFSET + 2] = (crc >> 16) & 0xffL;
|
||||
sector[LEC_MODE1_EDC_OFFSET + 3] = (crc >> 24) & 0xffL;
|
||||
}
|
||||
|
||||
/* Calc EDC for a XA form 1 sector
|
||||
*/
|
||||
static void calc_mode2_form1_edc(u_int8_t *sector)
|
||||
{
|
||||
u_int32_t crc = calc_edc(sector + LEC_DATA_OFFSET,
|
||||
LEC_MODE2_FORM1_DATA_LEN);
|
||||
|
||||
sector[LEC_MODE2_FORM1_EDC_OFFSET] = crc & 0xffL;
|
||||
sector[LEC_MODE2_FORM1_EDC_OFFSET + 1] = (crc >> 8) & 0xffL;
|
||||
sector[LEC_MODE2_FORM1_EDC_OFFSET + 2] = (crc >> 16) & 0xffL;
|
||||
sector[LEC_MODE2_FORM1_EDC_OFFSET + 3] = (crc >> 24) & 0xffL;
|
||||
}
|
||||
|
||||
/* Calc EDC for a XA form 2 sector
|
||||
*/
|
||||
static void calc_mode2_form2_edc(u_int8_t *sector)
|
||||
{
|
||||
u_int32_t crc = calc_edc(sector + LEC_DATA_OFFSET,
|
||||
LEC_MODE2_FORM2_DATA_LEN);
|
||||
|
||||
sector[LEC_MODE2_FORM2_EDC_OFFSET] = crc & 0xffL;
|
||||
sector[LEC_MODE2_FORM2_EDC_OFFSET + 1] = (crc >> 8) & 0xffL;
|
||||
sector[LEC_MODE2_FORM2_EDC_OFFSET + 2] = (crc >> 16) & 0xffL;
|
||||
sector[LEC_MODE2_FORM2_EDC_OFFSET + 3] = (crc >> 24) & 0xffL;
|
||||
}
|
||||
|
||||
/* Writes the sync pattern to the given sector.
|
||||
*/
|
||||
static void set_sync_pattern(u_int8_t *sector)
|
||||
{
|
||||
sector[0] = 0;
|
||||
|
||||
sector[1] = sector[2] = sector[3] = sector[4] = sector[5] =
|
||||
sector[6] = sector[7] = sector[8] = sector[9] = sector[10] = 0xff;
|
||||
|
||||
sector[11] = 0;
|
||||
}
|
||||
|
||||
|
||||
static u_int8_t bin2bcd(u_int8_t b)
|
||||
{
|
||||
return (((b/10) << 4) & 0xf0) | ((b%10) & 0x0f);
|
||||
}
|
||||
|
||||
/* Builds the sector header.
|
||||
*/
|
||||
static void set_sector_header(u_int8_t mode, u_int32_t adr, u_int8_t *sector)
|
||||
{
|
||||
sector[LEC_HEADER_OFFSET] = bin2bcd(adr / (60*75));
|
||||
sector[LEC_HEADER_OFFSET + 1] = bin2bcd((adr / 75) % 60);
|
||||
sector[LEC_HEADER_OFFSET + 2] = bin2bcd(adr % 75);
|
||||
sector[LEC_HEADER_OFFSET + 3] = mode;
|
||||
}
|
||||
|
||||
/* Calculate the P parities for the sector.
|
||||
* The 43 P vectors of length 24 are combined with the GF8_P_COEFFS.
|
||||
*/
|
||||
static void calc_P_parity(u_int8_t *sector)
|
||||
{
|
||||
int i, j;
|
||||
u_int16_t p01_msb, p01_lsb;
|
||||
u_int8_t *p_lsb_start;
|
||||
u_int8_t *p_lsb;
|
||||
u_int8_t *p0, *p1;
|
||||
u_int8_t d0,d1;
|
||||
|
||||
p_lsb_start = sector + LEC_HEADER_OFFSET;
|
||||
|
||||
p1 = sector + LEC_MODE1_P_PARITY_OFFSET;
|
||||
p0 = sector + LEC_MODE1_P_PARITY_OFFSET + 2 * 43;
|
||||
|
||||
for (i = 0; i <= 42; i++) {
|
||||
p_lsb = p_lsb_start;
|
||||
|
||||
p01_lsb = p01_msb = 0;
|
||||
|
||||
for (j = 19; j <= 42; j++) {
|
||||
d0 = *p_lsb;
|
||||
d1 = *(p_lsb+1);
|
||||
|
||||
p01_lsb ^= CF8_Q_COEFFS_RESULTS_01[j][d0];
|
||||
p01_msb ^= CF8_Q_COEFFS_RESULTS_01[j][d1];
|
||||
|
||||
p_lsb += 2 * 43;
|
||||
}
|
||||
|
||||
*p0 = p01_lsb;
|
||||
*(p0 + 1) = p01_msb;
|
||||
|
||||
*p1 = p01_lsb>>8;
|
||||
*(p1 + 1) = p01_msb>>8;
|
||||
|
||||
p0 += 2;
|
||||
p1 += 2;
|
||||
|
||||
p_lsb_start += 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate the Q parities for the sector.
|
||||
* The 26 Q vectors of length 43 are combined with the GF8_Q_COEFFS.
|
||||
*/
|
||||
static void calc_Q_parity(u_int8_t *sector)
|
||||
{
|
||||
int i, j;
|
||||
u_int16_t q01_lsb, q01_msb;
|
||||
u_int8_t *q_lsb_start;
|
||||
u_int8_t *q_lsb;
|
||||
u_int8_t *q0, *q1, *q_start;
|
||||
u_int8_t d0,d1;
|
||||
|
||||
q_lsb_start = sector + LEC_HEADER_OFFSET;
|
||||
|
||||
q_start = sector + LEC_MODE1_Q_PARITY_OFFSET;
|
||||
q1 = sector + LEC_MODE1_Q_PARITY_OFFSET;
|
||||
q0 = sector + LEC_MODE1_Q_PARITY_OFFSET + 2 * 26;
|
||||
|
||||
for (i = 0; i <= 25; i++) {
|
||||
q_lsb = q_lsb_start;
|
||||
|
||||
q01_lsb = q01_msb = 0;
|
||||
|
||||
for (j = 0; j <= 42; j++) {
|
||||
d0 = *q_lsb;
|
||||
d1 = *(q_lsb+1);
|
||||
|
||||
q01_lsb ^= CF8_Q_COEFFS_RESULTS_01[j][d0];
|
||||
q01_msb ^= CF8_Q_COEFFS_RESULTS_01[j][d1];
|
||||
|
||||
q_lsb += 2 * 44;
|
||||
|
||||
if (q_lsb >= q_start) {
|
||||
q_lsb -= 2 * 1118;
|
||||
}
|
||||
}
|
||||
|
||||
*q0 = q01_lsb;
|
||||
*(q0 + 1) = q01_msb;
|
||||
|
||||
*q1 = q01_lsb>>8;
|
||||
*(q1 + 1) = q01_msb>>8;
|
||||
|
||||
q0 += 2;
|
||||
q1 += 2;
|
||||
|
||||
q_lsb_start += 2 * 43;
|
||||
}
|
||||
}
|
||||
|
||||
/* Encodes a MODE 0 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide
|
||||
*/
|
||||
void lec_encode_mode0_sector(u_int32_t adr, u_int8_t *sector)
|
||||
{
|
||||
u_int16_t i;
|
||||
|
||||
set_sync_pattern(sector);
|
||||
set_sector_header(0, adr, sector);
|
||||
|
||||
sector += 16;
|
||||
|
||||
for (i = 0; i < 2336; i++)
|
||||
*sector++ = 0;
|
||||
}
|
||||
|
||||
/* Encodes a MODE 1 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide containing 2048 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode1_sector(u_int32_t adr, u_int8_t *sector)
|
||||
{
|
||||
set_sync_pattern(sector);
|
||||
set_sector_header(1, adr, sector);
|
||||
|
||||
calc_mode1_edc(sector);
|
||||
|
||||
/* clear the intermediate field */
|
||||
sector[LEC_MODE1_INTERMEDIATE_OFFSET] =
|
||||
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 1] =
|
||||
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 2] =
|
||||
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 3] =
|
||||
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 4] =
|
||||
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 5] =
|
||||
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 6] =
|
||||
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 7] = 0;
|
||||
|
||||
calc_P_parity(sector);
|
||||
calc_Q_parity(sector);
|
||||
}
|
||||
|
||||
/* Encodes a MODE 2 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide containing 2336 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode2_sector(u_int32_t adr, u_int8_t *sector)
|
||||
{
|
||||
set_sync_pattern(sector);
|
||||
set_sector_header(2, adr, sector);
|
||||
}
|
||||
|
||||
/* Encodes a XA form 1 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide containing 2048+8 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode2_form1_sector(u_int32_t adr, u_int8_t *sector)
|
||||
{
|
||||
set_sync_pattern(sector);
|
||||
|
||||
calc_mode2_form1_edc(sector);
|
||||
|
||||
/* P/Q partiy must not contain the sector header so clear it */
|
||||
sector[LEC_HEADER_OFFSET] =
|
||||
sector[LEC_HEADER_OFFSET + 1] =
|
||||
sector[LEC_HEADER_OFFSET + 2] =
|
||||
sector[LEC_HEADER_OFFSET + 3] = 0;
|
||||
|
||||
calc_P_parity(sector);
|
||||
calc_Q_parity(sector);
|
||||
|
||||
/* finally add the sector header */
|
||||
set_sector_header(2, adr, sector);
|
||||
}
|
||||
|
||||
/* Encodes a XA form 2 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide containing 2324+8 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode2_form2_sector(u_int32_t adr, u_int8_t *sector)
|
||||
{
|
||||
set_sync_pattern(sector);
|
||||
|
||||
calc_mode2_form2_edc(sector);
|
||||
|
||||
set_sector_header(2, adr, sector);
|
||||
}
|
||||
|
||||
/* Scrambles and byte swaps an encoded sector.
|
||||
* 'sector' must be 2352 byte wide.
|
||||
*/
|
||||
void lec_scramble(u_int8_t *sector)
|
||||
{
|
||||
u_int16_t i;
|
||||
const u_int8_t *stable = SCRAMBLE_TABLE;
|
||||
u_int8_t *p = sector;
|
||||
u_int8_t tmp;
|
||||
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
/* just swap bytes of sector sync */
|
||||
tmp = *p;
|
||||
*p = *(p + 1);
|
||||
p++;
|
||||
*p++ = tmp;
|
||||
}
|
||||
for (;i < (2352 / 2); i++) {
|
||||
/* scramble and swap bytes */
|
||||
tmp = *p ^ *stable++;
|
||||
*p = *(p + 1) ^ *stable++;
|
||||
p++;
|
||||
*p++ = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *infile;
|
||||
char *outfile;
|
||||
int fd_in, fd_out;
|
||||
u_int8_t buffer1[2352];
|
||||
u_int8_t buffer2[2352];
|
||||
u_int32_t lba;
|
||||
int i;
|
||||
|
||||
#if 0
|
||||
for (i = 0; i < 2048; i++)
|
||||
buffer1[i + 16] = 234;
|
||||
|
||||
lba = 150;
|
||||
|
||||
for (i = 0; i < 100000; i++) {
|
||||
lec_encode_mode1_sector(lba, buffer1);
|
||||
lec_scramble(buffer2);
|
||||
lba++;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
if (argc != 3)
|
||||
return 1;
|
||||
|
||||
infile = argv[1];
|
||||
outfile = argv[2];
|
||||
|
||||
|
||||
if ((fd_in = open(infile, O_RDONLY)) < 0) {
|
||||
perror("Cannot open input file");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((fd_out = open(outfile, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
|
||||
perror("Cannot open output file");
|
||||
return 1;
|
||||
}
|
||||
|
||||
lba = 150;
|
||||
|
||||
do {
|
||||
if (read(fd_in, buffer1, 2352) != 2352)
|
||||
break;
|
||||
|
||||
switch (*(buffer1 + 12 + 3)) {
|
||||
case 1:
|
||||
memcpy(buffer2 + 16, buffer1 + 16, 2048);
|
||||
|
||||
lec_encode_mode1_sector(lba, buffer2);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if ((*(buffer1 + 12 + 4 + 2) & 0x20) != 0) {
|
||||
/* form 2 sector */
|
||||
memcpy(buffer2 + 16, buffer1 + 16, 2324 + 8);
|
||||
lec_encode_mode2_form2_sector(lba, buffer2);
|
||||
}
|
||||
else {
|
||||
/* form 1 sector */
|
||||
memcpy(buffer2 + 16, buffer1 + 16, 2048 + 8);
|
||||
lec_encode_mode2_form1_sector(lba, buffer2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (memcmp(buffer1, buffer2, 2352) != 0) {
|
||||
printf("Verify error at lba %ld\n", lba);
|
||||
}
|
||||
|
||||
lec_scramble(buffer2);
|
||||
write(fd_out, buffer2, 2352);
|
||||
|
||||
lba++;
|
||||
} while (1);
|
||||
|
||||
close(fd_in);
|
||||
close(fd_out);
|
||||
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,77 @@
|
|||
/* cdrdao - write audio CD-Rs in disc-at-once mode
|
||||
*
|
||||
* Copyright (C) 1998-2002 Andreas Mueller <mueller@daneb.ping.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef __LEC_H__
|
||||
#define __LEC_H__
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
typedef uint32_t u_int32_t;
|
||||
typedef uint16_t u_int16_t;
|
||||
typedef uint8_t u_int8_t;
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
/* Encodes a MODE 0 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide
|
||||
*/
|
||||
void lec_encode_mode0_sector(u_int32_t adr, u_int8_t *sector);
|
||||
|
||||
/* Encodes a MODE 1 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide containing 2048 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode1_sector(u_int32_t adr, u_int8_t *sector);
|
||||
|
||||
/* Encodes a MODE 2 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide containing 2336 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode2_sector(u_int32_t adr, u_int8_t *sector);
|
||||
|
||||
/* Encodes a XA form 1 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide containing 2048+8 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode2_form1_sector(u_int32_t adr, u_int8_t *sector);
|
||||
|
||||
/* Encodes a XA form 2 sector.
|
||||
* 'adr' is the current physical sector address
|
||||
* 'sector' must be 2352 byte wide containing 2324+8 bytes user data at
|
||||
* offset 16
|
||||
*/
|
||||
void lec_encode_mode2_form2_sector(u_int32_t adr, u_int8_t *sector);
|
||||
|
||||
/* Scrambles and byte swaps an encoded sector.
|
||||
* 'sector' must be 2352 byte wide.
|
||||
*/
|
||||
void lec_scramble(u_int8_t *sector);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,203 @@
|
|||
/* dvdisaster: Additional error correction for optical media.
|
||||
* Copyright (C) 2004-2007 Carsten Gnoerlich.
|
||||
* Project home page: http://www.dvdisaster.com
|
||||
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
||||
* or direct your browser at http://www.gnu.org.
|
||||
*/
|
||||
|
||||
#include "dvdisaster.h"
|
||||
|
||||
static GaloisTables *gt = NULL; /* for L-EC Reed-Solomon */
|
||||
static ReedSolomonTables *rt = NULL;
|
||||
|
||||
bool Init_LEC_Correct(void)
|
||||
{
|
||||
gt = CreateGaloisTables(0x11d);
|
||||
rt = CreateReedSolomonTables(gt, 0, 1, 10);
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
void Kill_LEC_Correct(void)
|
||||
{
|
||||
FreeGaloisTables(gt);
|
||||
FreeReedSolomonTables(rt);
|
||||
}
|
||||
|
||||
/***
|
||||
*** CD level CRC calculation
|
||||
***/
|
||||
|
||||
/*
|
||||
* Test raw sector against its 32bit CRC.
|
||||
* Returns TRUE if frame is good.
|
||||
*/
|
||||
|
||||
int CheckEDC(const unsigned char *cd_frame, bool xa_mode)
|
||||
{
|
||||
unsigned int expected_crc, real_crc;
|
||||
unsigned int crc_base = xa_mode ? 2072 : 2064;
|
||||
|
||||
expected_crc = cd_frame[crc_base + 0] << 0;
|
||||
expected_crc |= cd_frame[crc_base + 1] << 8;
|
||||
expected_crc |= cd_frame[crc_base + 2] << 16;
|
||||
expected_crc |= cd_frame[crc_base + 3] << 24;
|
||||
|
||||
if(xa_mode)
|
||||
real_crc = EDCCrc32(cd_frame+16, 2056);
|
||||
else
|
||||
real_crc = EDCCrc32(cd_frame, 2064);
|
||||
|
||||
if(expected_crc == real_crc)
|
||||
return(1);
|
||||
else
|
||||
{
|
||||
//printf("Bad EDC CRC: Calculated: %08x, Recorded: %08x\n", real_crc, expected_crc);
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
*** A very simple L-EC error correction.
|
||||
***
|
||||
* Perform just one pass over the Q and P vectors to see if everything
|
||||
* is okay respectively correct minor errors. This is pretty much the
|
||||
* same stuff the drive is supposed to do in the final L-EC stage.
|
||||
*/
|
||||
|
||||
static int simple_lec(unsigned char *frame)
|
||||
{
|
||||
unsigned char byte_state[2352];
|
||||
unsigned char p_vector[P_VECTOR_SIZE];
|
||||
unsigned char q_vector[Q_VECTOR_SIZE];
|
||||
unsigned char p_state[P_VECTOR_SIZE];
|
||||
int erasures[Q_VECTOR_SIZE], erasure_count;
|
||||
int ignore[2];
|
||||
int p_failures, q_failures;
|
||||
int p_corrected, q_corrected;
|
||||
int p,q;
|
||||
|
||||
/* Setup */
|
||||
|
||||
memset(byte_state, 0, 2352);
|
||||
|
||||
p_failures = q_failures = 0;
|
||||
p_corrected = q_corrected = 0;
|
||||
|
||||
/* Perform Q-Parity error correction */
|
||||
|
||||
for(q=0; q<N_Q_VECTORS; q++)
|
||||
{ int err;
|
||||
|
||||
/* We have no erasure information for Q vectors */
|
||||
|
||||
GetQVector(frame, q_vector, q);
|
||||
err = DecodePQ(rt, q_vector, Q_PADDING, ignore, 0);
|
||||
|
||||
/* See what we've got */
|
||||
|
||||
if(err < 0) /* Uncorrectable. Mark bytes are erasure. */
|
||||
{ q_failures++;
|
||||
FillQVector(byte_state, 1, q);
|
||||
}
|
||||
else /* Correctable */
|
||||
{ if(err == 1 || err == 2) /* Store back corrected vector */
|
||||
{ SetQVector(frame, q_vector, q);
|
||||
q_corrected++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform P-Parity error correction */
|
||||
|
||||
for(p=0; p<N_P_VECTORS; p++)
|
||||
{ int err,i;
|
||||
|
||||
/* Try error correction without erasure information */
|
||||
|
||||
GetPVector(frame, p_vector, p);
|
||||
err = DecodePQ(rt, p_vector, P_PADDING, ignore, 0);
|
||||
|
||||
/* If unsuccessful, try again using erasures.
|
||||
Erasure information is uncertain, so try this last. */
|
||||
|
||||
if(err < 0 || err > 2)
|
||||
{ GetPVector(byte_state, p_state, p);
|
||||
erasure_count = 0;
|
||||
|
||||
for(i=0; i<P_VECTOR_SIZE; i++)
|
||||
if(p_state[i])
|
||||
erasures[erasure_count++] = i;
|
||||
|
||||
if(erasure_count > 0 && erasure_count <= 2)
|
||||
{ GetPVector(frame, p_vector, p);
|
||||
err = DecodePQ(rt, p_vector, P_PADDING, erasures, erasure_count);
|
||||
}
|
||||
}
|
||||
|
||||
/* See what we've got */
|
||||
|
||||
if(err < 0) /* Uncorrectable. */
|
||||
{ p_failures++;
|
||||
}
|
||||
else /* Correctable. */
|
||||
{ if(err == 1 || err == 2) /* Store back corrected vector */
|
||||
{ SetPVector(frame, p_vector, p);
|
||||
p_corrected++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Sum up */
|
||||
|
||||
if(q_failures || p_failures || q_corrected || p_corrected)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
*** Validate CD raw sector
|
||||
***/
|
||||
|
||||
int ValidateRawSector(unsigned char *frame, bool xaMode)
|
||||
{
|
||||
int lec_did_sth = FALSE_0;
|
||||
|
||||
/* Do simple L-EC.
|
||||
It seems that drives stop their internal L-EC as soon as the
|
||||
EDC is okay, so we may see uncorrected errors in the parity bytes.
|
||||
Since we are also interested in the user data only and doing the
|
||||
L-EC is expensive, we skip our L-EC as well when the EDC is fine. */
|
||||
|
||||
if(!CheckEDC(frame, xaMode))
|
||||
{
|
||||
lec_did_sth = simple_lec(frame);
|
||||
}
|
||||
/* Test internal sector checksum again */
|
||||
|
||||
if(!CheckEDC(frame, xaMode))
|
||||
{
|
||||
/* EDC failure in RAW sector */
|
||||
return FALSE_0;
|
||||
}
|
||||
|
||||
return TRUE_1;
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef __GNUC__
|
||||
#pragma pack(push, 1)
|
||||
#pragma warning(disable : 4103)
|
||||
#endif
|
||||
|
||||
#ifndef __PACKED
|
||||
#ifdef __GNUC__
|
||||
#define __PACKED __attribute__((__packed__))
|
||||
#else
|
||||
#define __PACKED
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,3 @@
|
|||
#ifndef __GNUC__
|
||||
#pragma pack(pop)
|
||||
#endif
|
|
@ -0,0 +1,3 @@
|
|||
#include "emuware.h"
|
||||
|
||||
//this file intentionally empty
|
|
@ -0,0 +1,73 @@
|
|||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef __int64 s64;
|
||||
typedef __int32 s32;
|
||||
typedef __int16 s16;
|
||||
typedef __int8 s8;
|
||||
typedef unsigned __int64 u64;
|
||||
typedef unsigned __int32 u32;
|
||||
typedef unsigned __int16 u16;
|
||||
typedef unsigned __int8 u8;
|
||||
|
||||
typedef __int64 int64;
|
||||
typedef __int32 int32;
|
||||
typedef __int16 int16;
|
||||
typedef __int8 int8;
|
||||
typedef unsigned __int64 uint64;
|
||||
typedef unsigned __int32 uint32;
|
||||
typedef unsigned __int16 uint16;
|
||||
typedef unsigned __int8 uint8;
|
||||
|
||||
//#define MDFN_WARN_UNUSED_RESULT __attribute__ ((warn_unused_result))
|
||||
#define MDFN_WARN_UNUSED_RESULT
|
||||
|
||||
//#define MDFN_COLD __attribute__((cold))
|
||||
#define MDFN_COLD
|
||||
|
||||
//#define NO_INLINE __attribute__((noinline))
|
||||
#define NO_INLINE
|
||||
|
||||
//#define MDFN_UNLIKELY(n) __builtin_expect((n) != 0, 0)
|
||||
//#define MDFN_LIKELY(n) __builtin_expect((n) != 0, 1)
|
||||
#define MDFN_UNLIKELY(n) (n)
|
||||
#define MDFN_LIKELY(n) (n)
|
||||
|
||||
//#define MDFN_NOWARN_UNUSED __attribute__((unused))
|
||||
#define MDFN_NOWARN_UNUSED
|
||||
|
||||
//#define MDFN_FORMATSTR(a,b,c) __attribute__ ((format (a, b, c)))
|
||||
#define MDFN_FORMATSTR(a,b,c)
|
||||
|
||||
#define INLINE inline
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define snprintf _snprintf
|
||||
#define vsnprintf _vsnprintf
|
||||
#define strcasecmp _stricmp
|
||||
#define strncasecmp _strnicmp
|
||||
#endif
|
||||
|
||||
#define TRUE_1 1
|
||||
#define FALSE_0 0
|
||||
|
||||
//------------alignment macros-------------
|
||||
//dont apply these to types without further testing. it only works portably here on declarations of variables
|
||||
//cant we find a pattern other people use more successfully?
|
||||
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
|
||||
#define EW_VAR_ALIGN(X) __declspec(align(X))
|
||||
#elif defined(__GNUC__)
|
||||
#define EW_VAR_ALIGN(X) __attribute__ ((aligned (X)))
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
//---------------------------------------------
|
||||
|
||||
#ifdef EW_EXPORT
|
||||
#undef EW_EXPORT
|
||||
#define EW_EXPORT extern "C" __declspec(dllexport)
|
||||
#else
|
||||
#define EW_EXPORT extern "C" __declspec(dllimport)
|
||||
#endif
|
|
@ -0,0 +1,138 @@
|
|||
------------------------------------------------------------------------
|
||||
r26 | 2009-10-02 13:36:47 +0400 | 2 lines
|
||||
|
||||
[Issue 5] Change <stdint.h> to "stdint.h" to let compiler search for it in local directory.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r25 | 2009-09-17 23:46:49 +0400 | 2 lines
|
||||
|
||||
[Issue 4] Fix incorrect int8_t behaviour if compiled with /J flag.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r24 | 2009-05-13 14:53:48 +0400 | 2 lines
|
||||
|
||||
Forgot about #ifdef __cplusplus guard around 'extern "C"', so inclusion to C files has been broken.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r23 | 2009-05-12 01:27:45 +0400 | 3 lines
|
||||
|
||||
[Issue 2] Always wrap <wcharîàž with external "C" {}.
|
||||
It turns out that not only Visual Studio 6 requires this, but also newer versions when compiling for ARM.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r22 | 2009-05-11 22:22:15 +0400 | 3 lines
|
||||
|
||||
[Issue 3] Visual Studio 6 and Embedded Visual C++ 4 doesn't realize that, e.g. char has the same size as __int8 so we give up on __intX for them.
|
||||
his should close Issue 3 in issue tracker.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r21 | 2008-07-17 09:47:22 +0400 | 4 lines
|
||||
|
||||
Get rid of these compiler warnings when compiling for 32-bit:
|
||||
warning C4311: 'type cast' : pointer truncation from 'void *' to 'uintptr_t'
|
||||
warning C4312: 'type cast' : conversion from 'uintptr_t' to 'const void *' of greater size
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r20 | 2007-10-09 16:54:27 +0400 | 2 lines
|
||||
|
||||
Better C99 conformance: macros for format specifiers should only be included in C++ implementations if __STDC_FORMAT_MACROS is defined before <inttypes.h> is included.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r19 | 2007-07-04 02:14:40 +0400 | 3 lines
|
||||
|
||||
Explicitly cast to appropriate type INT8_MIN, INT16_MIN, INT32_MIN and INT64_MIN constants.
|
||||
Due to their unusual definition in Visual Studio headers (-_Ix_MAX-1) they are propagated to int and thus do not have expected type, causing VS6 strict compiler to claim about type inconsistency.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r18 | 2007-06-26 16:53:23 +0400 | 2 lines
|
||||
|
||||
Better handling of (U)INTx_C macros - now they generate constants of exact width.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r17 | 2007-03-29 20:16:14 +0400 | 2 lines
|
||||
|
||||
Fix typo: Miscrosoft -> Microsoft.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r16 | 2007-02-24 17:32:58 +0300 | 4 lines
|
||||
|
||||
Remove <BaseTsd.h> include, as it is not present in Visual Studio 2005 Epxress Edition and required only for INT_PTR and UINT_PTR types.
|
||||
|
||||
'intptr_t' and 'uintptr_t' types now defined explicitly with #ifdef _WIN64.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r15 | 2007-02-11 20:53:05 +0300 | 2 lines
|
||||
|
||||
More correct fix for compilation under VS6.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r14 | 2007-02-11 20:04:32 +0300 | 2 lines
|
||||
|
||||
Bugfix: fix compiling under VS6, when stdint.h enclosed in 'extern "C" {}'.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r13 | 2006-12-13 16:53:11 +0300 | 2 lines
|
||||
|
||||
Make _inline modifier for imaxdiv default option. Use STATIC_IMAXDIV to make it static.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r12 | 2006-12-13 16:42:24 +0300 | 2 lines
|
||||
|
||||
Error message changed: VC6 supported from now.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r11 | 2006-12-13 16:39:33 +0300 | 2 lines
|
||||
|
||||
All (U)INT* types changed to (unsigned) __int*. This should make stdint.h compatible with VC6.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r10 | 2006-12-13 16:20:57 +0300 | 3 lines
|
||||
|
||||
Added INLINE_IMAXDIV define switch.
|
||||
If INLINE_IMAXDIV is defined imaxdiv() have static modifier. If not - it is _inline.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r9 | 2006-12-13 15:53:52 +0300 | 2 lines
|
||||
|
||||
Error message for non-MSC compiler changed.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r8 | 2006-12-13 12:47:48 +0300 | 2 lines
|
||||
|
||||
Added #ifndef for SIZE_MAX (it is defined in limits.h on MSVSC 8).
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r7 | 2006-12-13 01:08:02 +0300 | 2 lines
|
||||
|
||||
License chaged to BSD-derivative.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r6 | 2006-12-13 00:53:20 +0300 | 2 lines
|
||||
|
||||
Added <wchar.h> include to avoid warnings when it is included after stdint.h.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r5 | 2006-12-12 00:58:05 +0300 | 2 lines
|
||||
|
||||
BUGFIX: Definitions of INTPTR_MIN, INTPTR_MAX and UINTPTR_MAX for WIN32 and WIN64 was mixed up.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r4 | 2006-12-12 00:51:55 +0300 | 2 lines
|
||||
|
||||
Rise #error if _MSC_VER is not defined. I.e. compiler other then Microsoft Visual C++ is used.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r3 | 2006-12-11 22:54:14 +0300 | 2 lines
|
||||
|
||||
Added <limits.h> include to stdint.h.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r2 | 2006-12-11 21:39:27 +0300 | 2 lines
|
||||
|
||||
Initial check in.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
r1 | 2006-12-11 21:30:23 +0300 | 1 line
|
||||
|
||||
Initial directory structure.
|
||||
------------------------------------------------------------------------
|
|
@ -0,0 +1,305 @@
|
|||
// ISO C9x compliant inttypes.h for Microsoft Visual Studio
|
||||
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
|
||||
//
|
||||
// Copyright (c) 2006 Alexander Chemeris
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
// 3. The name of the author may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _MSC_VER // [
|
||||
#error "Use this header only with Microsoft Visual C++ compilers!"
|
||||
#endif // _MSC_VER ]
|
||||
|
||||
#ifndef _MSC_INTTYPES_H_ // [
|
||||
#define _MSC_INTTYPES_H_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
// 7.8 Format conversion of integer types
|
||||
|
||||
typedef struct {
|
||||
intmax_t quot;
|
||||
intmax_t rem;
|
||||
} imaxdiv_t;
|
||||
|
||||
// 7.8.1 Macros for format specifiers
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198
|
||||
|
||||
// The fprintf macros for signed integers are:
|
||||
#define PRId8 "d"
|
||||
#define PRIi8 "i"
|
||||
#define PRIdLEAST8 "d"
|
||||
#define PRIiLEAST8 "i"
|
||||
#define PRIdFAST8 "d"
|
||||
#define PRIiFAST8 "i"
|
||||
|
||||
#define PRId16 "hd"
|
||||
#define PRIi16 "hi"
|
||||
#define PRIdLEAST16 "hd"
|
||||
#define PRIiLEAST16 "hi"
|
||||
#define PRIdFAST16 "hd"
|
||||
#define PRIiFAST16 "hi"
|
||||
|
||||
#define PRId32 "I32d"
|
||||
#define PRIi32 "I32i"
|
||||
#define PRIdLEAST32 "I32d"
|
||||
#define PRIiLEAST32 "I32i"
|
||||
#define PRIdFAST32 "I32d"
|
||||
#define PRIiFAST32 "I32i"
|
||||
|
||||
#define PRId64 "I64d"
|
||||
#define PRIi64 "I64i"
|
||||
#define PRIdLEAST64 "I64d"
|
||||
#define PRIiLEAST64 "I64i"
|
||||
#define PRIdFAST64 "I64d"
|
||||
#define PRIiFAST64 "I64i"
|
||||
|
||||
#define PRIdMAX "I64d"
|
||||
#define PRIiMAX "I64i"
|
||||
|
||||
#define PRIdPTR "Id"
|
||||
#define PRIiPTR "Ii"
|
||||
|
||||
// The fprintf macros for unsigned integers are:
|
||||
#define PRIo8 "o"
|
||||
#define PRIu8 "u"
|
||||
#define PRIx8 "x"
|
||||
#define PRIX8 "X"
|
||||
#define PRIoLEAST8 "o"
|
||||
#define PRIuLEAST8 "u"
|
||||
#define PRIxLEAST8 "x"
|
||||
#define PRIXLEAST8 "X"
|
||||
#define PRIoFAST8 "o"
|
||||
#define PRIuFAST8 "u"
|
||||
#define PRIxFAST8 "x"
|
||||
#define PRIXFAST8 "X"
|
||||
|
||||
#define PRIo16 "ho"
|
||||
#define PRIu16 "hu"
|
||||
#define PRIx16 "hx"
|
||||
#define PRIX16 "hX"
|
||||
#define PRIoLEAST16 "ho"
|
||||
#define PRIuLEAST16 "hu"
|
||||
#define PRIxLEAST16 "hx"
|
||||
#define PRIXLEAST16 "hX"
|
||||
#define PRIoFAST16 "ho"
|
||||
#define PRIuFAST16 "hu"
|
||||
#define PRIxFAST16 "hx"
|
||||
#define PRIXFAST16 "hX"
|
||||
|
||||
#define PRIo32 "I32o"
|
||||
#define PRIu32 "I32u"
|
||||
#define PRIx32 "I32x"
|
||||
#define PRIX32 "I32X"
|
||||
#define PRIoLEAST32 "I32o"
|
||||
#define PRIuLEAST32 "I32u"
|
||||
#define PRIxLEAST32 "I32x"
|
||||
#define PRIXLEAST32 "I32X"
|
||||
#define PRIoFAST32 "I32o"
|
||||
#define PRIuFAST32 "I32u"
|
||||
#define PRIxFAST32 "I32x"
|
||||
#define PRIXFAST32 "I32X"
|
||||
|
||||
#define PRIo64 "I64o"
|
||||
#define PRIu64 "I64u"
|
||||
#define PRIx64 "I64x"
|
||||
#define PRIX64 "I64X"
|
||||
#define PRIoLEAST64 "I64o"
|
||||
#define PRIuLEAST64 "I64u"
|
||||
#define PRIxLEAST64 "I64x"
|
||||
#define PRIXLEAST64 "I64X"
|
||||
#define PRIoFAST64 "I64o"
|
||||
#define PRIuFAST64 "I64u"
|
||||
#define PRIxFAST64 "I64x"
|
||||
#define PRIXFAST64 "I64X"
|
||||
|
||||
#define PRIoMAX "I64o"
|
||||
#define PRIuMAX "I64u"
|
||||
#define PRIxMAX "I64x"
|
||||
#define PRIXMAX "I64X"
|
||||
|
||||
#define PRIoPTR "Io"
|
||||
#define PRIuPTR "Iu"
|
||||
#define PRIxPTR "Ix"
|
||||
#define PRIXPTR "IX"
|
||||
|
||||
// The fscanf macros for signed integers are:
|
||||
#define SCNd8 "d"
|
||||
#define SCNi8 "i"
|
||||
#define SCNdLEAST8 "d"
|
||||
#define SCNiLEAST8 "i"
|
||||
#define SCNdFAST8 "d"
|
||||
#define SCNiFAST8 "i"
|
||||
|
||||
#define SCNd16 "hd"
|
||||
#define SCNi16 "hi"
|
||||
#define SCNdLEAST16 "hd"
|
||||
#define SCNiLEAST16 "hi"
|
||||
#define SCNdFAST16 "hd"
|
||||
#define SCNiFAST16 "hi"
|
||||
|
||||
#define SCNd32 "ld"
|
||||
#define SCNi32 "li"
|
||||
#define SCNdLEAST32 "ld"
|
||||
#define SCNiLEAST32 "li"
|
||||
#define SCNdFAST32 "ld"
|
||||
#define SCNiFAST32 "li"
|
||||
|
||||
#define SCNd64 "I64d"
|
||||
#define SCNi64 "I64i"
|
||||
#define SCNdLEAST64 "I64d"
|
||||
#define SCNiLEAST64 "I64i"
|
||||
#define SCNdFAST64 "I64d"
|
||||
#define SCNiFAST64 "I64i"
|
||||
|
||||
#define SCNdMAX "I64d"
|
||||
#define SCNiMAX "I64i"
|
||||
|
||||
#ifdef _WIN64 // [
|
||||
# define SCNdPTR "I64d"
|
||||
# define SCNiPTR "I64i"
|
||||
#else // _WIN64 ][
|
||||
# define SCNdPTR "ld"
|
||||
# define SCNiPTR "li"
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// The fscanf macros for unsigned integers are:
|
||||
#define SCNo8 "o"
|
||||
#define SCNu8 "u"
|
||||
#define SCNx8 "x"
|
||||
#define SCNX8 "X"
|
||||
#define SCNoLEAST8 "o"
|
||||
#define SCNuLEAST8 "u"
|
||||
#define SCNxLEAST8 "x"
|
||||
#define SCNXLEAST8 "X"
|
||||
#define SCNoFAST8 "o"
|
||||
#define SCNuFAST8 "u"
|
||||
#define SCNxFAST8 "x"
|
||||
#define SCNXFAST8 "X"
|
||||
|
||||
#define SCNo16 "ho"
|
||||
#define SCNu16 "hu"
|
||||
#define SCNx16 "hx"
|
||||
#define SCNX16 "hX"
|
||||
#define SCNoLEAST16 "ho"
|
||||
#define SCNuLEAST16 "hu"
|
||||
#define SCNxLEAST16 "hx"
|
||||
#define SCNXLEAST16 "hX"
|
||||
#define SCNoFAST16 "ho"
|
||||
#define SCNuFAST16 "hu"
|
||||
#define SCNxFAST16 "hx"
|
||||
#define SCNXFAST16 "hX"
|
||||
|
||||
#define SCNo32 "lo"
|
||||
#define SCNu32 "lu"
|
||||
#define SCNx32 "lx"
|
||||
#define SCNX32 "lX"
|
||||
#define SCNoLEAST32 "lo"
|
||||
#define SCNuLEAST32 "lu"
|
||||
#define SCNxLEAST32 "lx"
|
||||
#define SCNXLEAST32 "lX"
|
||||
#define SCNoFAST32 "lo"
|
||||
#define SCNuFAST32 "lu"
|
||||
#define SCNxFAST32 "lx"
|
||||
#define SCNXFAST32 "lX"
|
||||
|
||||
#define SCNo64 "I64o"
|
||||
#define SCNu64 "I64u"
|
||||
#define SCNx64 "I64x"
|
||||
#define SCNX64 "I64X"
|
||||
#define SCNoLEAST64 "I64o"
|
||||
#define SCNuLEAST64 "I64u"
|
||||
#define SCNxLEAST64 "I64x"
|
||||
#define SCNXLEAST64 "I64X"
|
||||
#define SCNoFAST64 "I64o"
|
||||
#define SCNuFAST64 "I64u"
|
||||
#define SCNxFAST64 "I64x"
|
||||
#define SCNXFAST64 "I64X"
|
||||
|
||||
#define SCNoMAX "I64o"
|
||||
#define SCNuMAX "I64u"
|
||||
#define SCNxMAX "I64x"
|
||||
#define SCNXMAX "I64X"
|
||||
|
||||
#ifdef _WIN64 // [
|
||||
# define SCNoPTR "I64o"
|
||||
# define SCNuPTR "I64u"
|
||||
# define SCNxPTR "I64x"
|
||||
# define SCNXPTR "I64X"
|
||||
#else // _WIN64 ][
|
||||
# define SCNoPTR "lo"
|
||||
# define SCNuPTR "lu"
|
||||
# define SCNxPTR "lx"
|
||||
# define SCNXPTR "lX"
|
||||
#endif // _WIN64 ]
|
||||
|
||||
#endif // __STDC_FORMAT_MACROS ]
|
||||
|
||||
// 7.8.2 Functions for greatest-width integer types
|
||||
|
||||
// 7.8.2.1 The imaxabs function
|
||||
#define imaxabs _abs64
|
||||
|
||||
// 7.8.2.2 The imaxdiv function
|
||||
|
||||
// This is modified version of div() function from Microsoft's div.c found
|
||||
// in %MSVC.NET%\crt\src\div.c
|
||||
#ifdef STATIC_IMAXDIV // [
|
||||
static
|
||||
#else // STATIC_IMAXDIV ][
|
||||
_inline
|
||||
#endif // STATIC_IMAXDIV ]
|
||||
imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom)
|
||||
{
|
||||
imaxdiv_t result;
|
||||
|
||||
result.quot = numer / denom;
|
||||
result.rem = numer % denom;
|
||||
|
||||
if (numer < 0 && result.rem > 0) {
|
||||
// did division wrong; must fix up
|
||||
++result.quot;
|
||||
result.rem -= denom;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 7.8.2.3 The strtoimax and strtoumax functions
|
||||
#define strtoimax _strtoi64
|
||||
#define strtoumax _strtoui64
|
||||
|
||||
// 7.8.2.4 The wcstoimax and wcstoumax functions
|
||||
#define wcstoimax _wcstoi64
|
||||
#define wcstoumax _wcstoui64
|
||||
|
||||
|
||||
#endif // _MSC_INTTYPES_H_ ]
|
|
@ -0,0 +1,247 @@
|
|||
// ISO C9x compliant stdint.h for Microsoft Visual Studio
|
||||
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
|
||||
//
|
||||
// Copyright (c) 2006-2008 Alexander Chemeris
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. 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.
|
||||
//
|
||||
// 3. The name of the author may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _MSC_VER // [
|
||||
#error "Use this header only with Microsoft Visual C++ compilers!"
|
||||
#endif // _MSC_VER ]
|
||||
|
||||
#ifndef _MSC_STDINT_H_ // [
|
||||
#define _MSC_STDINT_H_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
|
||||
// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
|
||||
// or compiler give many errors like this:
|
||||
// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
# include <wchar.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
// Define _W64 macros to mark types changing their size, like intptr_t.
|
||||
#ifndef _W64
|
||||
# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
|
||||
# define _W64 __w64
|
||||
# else
|
||||
# define _W64
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
// 7.18.1 Integer types
|
||||
|
||||
// 7.18.1.1 Exact-width integer types
|
||||
|
||||
// Visual Studio 6 and Embedded Visual C++ 4 doesn't
|
||||
// realize that, e.g. char has the same size as __int8
|
||||
// so we give up on __intX for them.
|
||||
#if (_MSC_VER < 1300)
|
||||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
||||
typedef signed int int32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
#else
|
||||
typedef signed __int8 int8_t;
|
||||
typedef signed __int16 int16_t;
|
||||
typedef signed __int32 int32_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
#endif
|
||||
typedef signed __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
|
||||
// 7.18.1.2 Minimum-width integer types
|
||||
typedef int8_t int_least8_t;
|
||||
typedef int16_t int_least16_t;
|
||||
typedef int32_t int_least32_t;
|
||||
typedef int64_t int_least64_t;
|
||||
typedef uint8_t uint_least8_t;
|
||||
typedef uint16_t uint_least16_t;
|
||||
typedef uint32_t uint_least32_t;
|
||||
typedef uint64_t uint_least64_t;
|
||||
|
||||
// 7.18.1.3 Fastest minimum-width integer types
|
||||
typedef int8_t int_fast8_t;
|
||||
typedef int16_t int_fast16_t;
|
||||
typedef int32_t int_fast32_t;
|
||||
typedef int64_t int_fast64_t;
|
||||
typedef uint8_t uint_fast8_t;
|
||||
typedef uint16_t uint_fast16_t;
|
||||
typedef uint32_t uint_fast32_t;
|
||||
typedef uint64_t uint_fast64_t;
|
||||
|
||||
// 7.18.1.4 Integer types capable of holding object pointers
|
||||
#ifdef _WIN64 // [
|
||||
typedef signed __int64 intptr_t;
|
||||
typedef unsigned __int64 uintptr_t;
|
||||
#else // _WIN64 ][
|
||||
typedef _W64 signed int intptr_t;
|
||||
typedef _W64 unsigned int uintptr_t;
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// 7.18.1.5 Greatest-width integer types
|
||||
typedef int64_t intmax_t;
|
||||
typedef uint64_t uintmax_t;
|
||||
|
||||
|
||||
// 7.18.2 Limits of specified-width integer types
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
|
||||
|
||||
// 7.18.2.1 Limits of exact-width integer types
|
||||
#define INT8_MIN ((int8_t)_I8_MIN)
|
||||
#define INT8_MAX _I8_MAX
|
||||
#define INT16_MIN ((int16_t)_I16_MIN)
|
||||
#define INT16_MAX _I16_MAX
|
||||
#define INT32_MIN ((int32_t)_I32_MIN)
|
||||
#define INT32_MAX _I32_MAX
|
||||
#define INT64_MIN ((int64_t)_I64_MIN)
|
||||
#define INT64_MAX _I64_MAX
|
||||
#define UINT8_MAX _UI8_MAX
|
||||
#define UINT16_MAX _UI16_MAX
|
||||
#define UINT32_MAX _UI32_MAX
|
||||
#define UINT64_MAX _UI64_MAX
|
||||
|
||||
// 7.18.2.2 Limits of minimum-width integer types
|
||||
#define INT_LEAST8_MIN INT8_MIN
|
||||
#define INT_LEAST8_MAX INT8_MAX
|
||||
#define INT_LEAST16_MIN INT16_MIN
|
||||
#define INT_LEAST16_MAX INT16_MAX
|
||||
#define INT_LEAST32_MIN INT32_MIN
|
||||
#define INT_LEAST32_MAX INT32_MAX
|
||||
#define INT_LEAST64_MIN INT64_MIN
|
||||
#define INT_LEAST64_MAX INT64_MAX
|
||||
#define UINT_LEAST8_MAX UINT8_MAX
|
||||
#define UINT_LEAST16_MAX UINT16_MAX
|
||||
#define UINT_LEAST32_MAX UINT32_MAX
|
||||
#define UINT_LEAST64_MAX UINT64_MAX
|
||||
|
||||
// 7.18.2.3 Limits of fastest minimum-width integer types
|
||||
#define INT_FAST8_MIN INT8_MIN
|
||||
#define INT_FAST8_MAX INT8_MAX
|
||||
#define INT_FAST16_MIN INT16_MIN
|
||||
#define INT_FAST16_MAX INT16_MAX
|
||||
#define INT_FAST32_MIN INT32_MIN
|
||||
#define INT_FAST32_MAX INT32_MAX
|
||||
#define INT_FAST64_MIN INT64_MIN
|
||||
#define INT_FAST64_MAX INT64_MAX
|
||||
#define UINT_FAST8_MAX UINT8_MAX
|
||||
#define UINT_FAST16_MAX UINT16_MAX
|
||||
#define UINT_FAST32_MAX UINT32_MAX
|
||||
#define UINT_FAST64_MAX UINT64_MAX
|
||||
|
||||
// 7.18.2.4 Limits of integer types capable of holding object pointers
|
||||
#ifdef _WIN64 // [
|
||||
# define INTPTR_MIN INT64_MIN
|
||||
# define INTPTR_MAX INT64_MAX
|
||||
# define UINTPTR_MAX UINT64_MAX
|
||||
#else // _WIN64 ][
|
||||
# define INTPTR_MIN INT32_MIN
|
||||
# define INTPTR_MAX INT32_MAX
|
||||
# define UINTPTR_MAX UINT32_MAX
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// 7.18.2.5 Limits of greatest-width integer types
|
||||
#define INTMAX_MIN INT64_MIN
|
||||
#define INTMAX_MAX INT64_MAX
|
||||
#define UINTMAX_MAX UINT64_MAX
|
||||
|
||||
// 7.18.3 Limits of other integer types
|
||||
|
||||
#ifdef _WIN64 // [
|
||||
# define PTRDIFF_MIN _I64_MIN
|
||||
# define PTRDIFF_MAX _I64_MAX
|
||||
#else // _WIN64 ][
|
||||
# define PTRDIFF_MIN _I32_MIN
|
||||
# define PTRDIFF_MAX _I32_MAX
|
||||
#endif // _WIN64 ]
|
||||
|
||||
#define SIG_ATOMIC_MIN INT_MIN
|
||||
#define SIG_ATOMIC_MAX INT_MAX
|
||||
|
||||
#ifndef SIZE_MAX // [
|
||||
# ifdef _WIN64 // [
|
||||
# define SIZE_MAX _UI64_MAX
|
||||
# else // _WIN64 ][
|
||||
# define SIZE_MAX _UI32_MAX
|
||||
# endif // _WIN64 ]
|
||||
#endif // SIZE_MAX ]
|
||||
|
||||
// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
|
||||
#ifndef WCHAR_MIN // [
|
||||
# define WCHAR_MIN 0
|
||||
#endif // WCHAR_MIN ]
|
||||
#ifndef WCHAR_MAX // [
|
||||
# define WCHAR_MAX _UI16_MAX
|
||||
#endif // WCHAR_MAX ]
|
||||
|
||||
#define WINT_MIN 0
|
||||
#define WINT_MAX _UI16_MAX
|
||||
|
||||
#endif // __STDC_LIMIT_MACROS ]
|
||||
|
||||
|
||||
// 7.18.4 Limits of other integer types
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
|
||||
|
||||
// 7.18.4.1 Macros for minimum-width integer constants
|
||||
|
||||
#define INT8_C(val) val##i8
|
||||
#define INT16_C(val) val##i16
|
||||
#define INT32_C(val) val##i32
|
||||
#define INT64_C(val) val##i64
|
||||
|
||||
#define UINT8_C(val) val##ui8
|
||||
#define UINT16_C(val) val##ui16
|
||||
#define UINT32_C(val) val##ui32
|
||||
#define UINT64_C(val) val##ui64
|
||||
|
||||
// 7.18.4.2 Macros for greatest-width integer constants
|
||||
#define INTMAX_C INT64_C
|
||||
#define UINTMAX_C UINT64_C
|
||||
|
||||
#endif // __STDC_CONSTANT_MACROS ]
|
||||
|
||||
|
||||
#endif // _MSC_STDINT_H_ ]
|
|
@ -0,0 +1,246 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "octoshock.h"
|
||||
#include "endian.h"
|
||||
|
||||
void Endian_A16_Swap(void *src, uint32 nelements)
|
||||
{
|
||||
uint32 i;
|
||||
uint8 *nsrc = (uint8 *)src;
|
||||
|
||||
for(i = 0; i < nelements; i++)
|
||||
{
|
||||
uint8 tmp = nsrc[i * 2];
|
||||
|
||||
nsrc[i * 2] = nsrc[i * 2 + 1];
|
||||
nsrc[i * 2 + 1] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void Endian_A32_Swap(void *src, uint32 nelements)
|
||||
{
|
||||
uint32 i;
|
||||
uint8 *nsrc = (uint8 *)src;
|
||||
|
||||
for(i = 0; i < nelements; i++)
|
||||
{
|
||||
uint8 tmp1 = nsrc[i * 4];
|
||||
uint8 tmp2 = nsrc[i * 4 + 1];
|
||||
|
||||
nsrc[i * 4] = nsrc[i * 4 + 3];
|
||||
nsrc[i * 4 + 1] = nsrc[i * 4 + 2];
|
||||
|
||||
nsrc[i * 4 + 2] = tmp2;
|
||||
nsrc[i * 4 + 3] = tmp1;
|
||||
}
|
||||
}
|
||||
|
||||
void Endian_A64_Swap(void *src, uint32 nelements)
|
||||
{
|
||||
uint32 i;
|
||||
uint8 *nsrc = (uint8 *)src;
|
||||
|
||||
for(i = 0; i < nelements; i++)
|
||||
{
|
||||
uint8 *base = &nsrc[i * 8];
|
||||
|
||||
for(int z = 0; z < 4; z++)
|
||||
{
|
||||
uint8 tmp = base[z];
|
||||
|
||||
base[z] = base[7 - z];
|
||||
base[7 - z] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Endian_A16_NE_to_LE(void *src, uint32 nelements)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
Endian_A16_Swap(src, nelements);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Endian_A32_NE_to_LE(void *src, uint32 nelements)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
Endian_A32_Swap(src, nelements);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Endian_A64_NE_to_LE(void *src, uint32 nelements)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
Endian_A64_Swap(src, nelements);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Endian_A16_LE_to_NE(void *src, uint32 nelements)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
uint32 i;
|
||||
uint8 *nsrc = (uint8 *)src;
|
||||
|
||||
for(i = 0; i < nelements; i++)
|
||||
{
|
||||
uint8 tmp = nsrc[i * 2];
|
||||
|
||||
nsrc[i * 2] = nsrc[i * 2 + 1];
|
||||
nsrc[i * 2 + 1] = tmp;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Endian_A16_BE_to_NE(void *src, uint32 nelements)
|
||||
{
|
||||
#ifdef LSB_FIRST
|
||||
uint32 i;
|
||||
uint8 *nsrc = (uint8 *)src;
|
||||
|
||||
for(i = 0; i < nelements; i++)
|
||||
{
|
||||
uint8 tmp = nsrc[i * 2];
|
||||
|
||||
nsrc[i * 2] = nsrc[i * 2 + 1];
|
||||
nsrc[i * 2 + 1] = tmp;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Endian_A32_LE_to_NE(void *src, uint32 nelements)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
uint32 i;
|
||||
uint8 *nsrc = (uint8 *)src;
|
||||
|
||||
for(i = 0; i < nelements; i++)
|
||||
{
|
||||
uint8 tmp1 = nsrc[i * 4];
|
||||
uint8 tmp2 = nsrc[i * 4 + 1];
|
||||
|
||||
nsrc[i * 4] = nsrc[i * 4 + 3];
|
||||
nsrc[i * 4 + 1] = nsrc[i * 4 + 2];
|
||||
|
||||
nsrc[i * 4 + 2] = tmp2;
|
||||
nsrc[i * 4 + 3] = tmp1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Endian_A64_LE_to_NE(void *src, uint32 nelements)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
uint32 i;
|
||||
uint8 *nsrc = (uint8 *)src;
|
||||
|
||||
for(i = 0; i < nelements; i++)
|
||||
{
|
||||
uint8 *base = &nsrc[i * 8];
|
||||
|
||||
for(int z = 0; z < 4; z++)
|
||||
{
|
||||
uint8 tmp = base[z];
|
||||
|
||||
base[z] = base[7 - z];
|
||||
base[7 - z] = tmp;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void FlipByteOrder(uint8 *src, uint32 count)
|
||||
{
|
||||
uint8 *start=src;
|
||||
uint8 *end=src+count-1;
|
||||
|
||||
if((count&1) || !count) return; /* This shouldn't happen. */
|
||||
|
||||
count >>= 1;
|
||||
|
||||
while(count--)
|
||||
{
|
||||
uint8 tmp;
|
||||
|
||||
tmp=*end;
|
||||
*end=*start;
|
||||
*start=tmp;
|
||||
end--;
|
||||
start++;
|
||||
}
|
||||
}
|
||||
|
||||
void Endian_V_LE_to_NE(void *src, uint32 bytesize)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
FlipByteOrder((uint8 *)src, bytesize);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Endian_V_NE_to_LE(void *src, uint32 bytesize)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
FlipByteOrder((uint8 *)src, bytesize);
|
||||
#endif
|
||||
}
|
||||
|
||||
int write16le(uint16 b, FILE *fp)
|
||||
{
|
||||
uint8 s[2];
|
||||
s[0]=b;
|
||||
s[1]=b>>8;
|
||||
return((fwrite(s,1,2,fp)<2)?0:2);
|
||||
}
|
||||
|
||||
int write32le(uint32 b, FILE *fp)
|
||||
{
|
||||
uint8 s[4];
|
||||
s[0]=b;
|
||||
s[1]=b>>8;
|
||||
s[2]=b>>16;
|
||||
s[3]=b>>24;
|
||||
return((fwrite(s,1,4,fp)<4)?0:4);
|
||||
}
|
||||
|
||||
int read32le(uint32 *Bufo, FILE *fp)
|
||||
{
|
||||
uint32 buf;
|
||||
if(fread(&buf,1,4,fp)<4)
|
||||
return 0;
|
||||
#ifdef LSB_FIRST
|
||||
*(uint32*)Bufo=buf;
|
||||
#else
|
||||
*(uint32*)Bufo=((buf&0xFF)<<24)|((buf&0xFF00)<<8)|((buf&0xFF0000)>>8)|((buf&0xFF000000)>>24);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
int read16le(char *d, FILE *fp)
|
||||
{
|
||||
#ifdef LSB_FIRST
|
||||
return((fread(d,1,2,fp)<2)?0:2);
|
||||
#else
|
||||
int ret;
|
||||
ret=fread(d+1,1,1,fp);
|
||||
ret+=fread(d,1,1,fp);
|
||||
return ret<2?0:2;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
#ifndef __MDFN_ENDIAN_H
|
||||
#define __MDFN_ENDIAN_H
|
||||
|
||||
#include "octoshock.h"
|
||||
|
||||
#ifdef MSB_FIRST
|
||||
#ifdef LSB_FIRST
|
||||
#error Only define one of LSB_FIRST and MSB_FIRST
|
||||
#endif
|
||||
|
||||
#ifndef le32toh
|
||||
#define le32toh(l) ((((l)>>24) & 0xff) | (((l)>>8) & 0xff00) \
|
||||
| (((l)<<8) & 0xff0000) | (((l)<<24) & 0xff000000))
|
||||
#endif
|
||||
#ifndef le16toh
|
||||
#define le16toh(l) ((((l)>>8) & 0xff) | (((l)<<8) & 0xff00))
|
||||
#endif
|
||||
#else
|
||||
#ifndef le32toh
|
||||
#define le32toh(l) (l)
|
||||
#endif
|
||||
#ifndef le16toh
|
||||
#define le16toh(l) (l)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef htole32
|
||||
#define htole32 le32toh
|
||||
#endif
|
||||
|
||||
#ifndef htole16
|
||||
#define htole16 le16toh
|
||||
#endif
|
||||
|
||||
|
||||
int write16le(uint16 b, FILE *fp);
|
||||
int write32le(uint32 b, FILE *fp);
|
||||
int read32le(uint32 *Bufo, FILE *fp);
|
||||
|
||||
void Endian_A16_Swap(void *src, uint32 nelements);
|
||||
void Endian_A32_Swap(void *src, uint32 nelements);
|
||||
void Endian_A64_Swap(void *src, uint32 nelements);
|
||||
|
||||
void Endian_A16_NE_to_LE(void *src, uint32 nelements);
|
||||
void Endian_A32_NE_to_LE(void *src, uint32 nelements);
|
||||
void Endian_A64_NE_to_LE(void *src, uint32 nelements);
|
||||
|
||||
void Endian_A16_LE_to_NE(void *src, uint32 nelements);
|
||||
void Endian_A16_BE_to_NE(void *src, uint32 nelements);
|
||||
void Endian_A32_LE_to_NE(void *src, uint32 nelements);
|
||||
void Endian_A64_LE_to_NE(void *src, uint32 nelements);
|
||||
|
||||
void Endian_V_LE_to_NE(void *src, uint32 bytesize);
|
||||
void Endian_V_NE_to_LE(void *src, uint32 bytesize);
|
||||
|
||||
void FlipByteOrder(uint8 *src, uint32 count);
|
||||
|
||||
// The following functions can encode/decode to unaligned addresses.
|
||||
|
||||
static INLINE void MDFN_en16lsb(uint8 *buf, uint16 morp)
|
||||
{
|
||||
buf[0]=morp;
|
||||
buf[1]=morp>>8;
|
||||
}
|
||||
|
||||
static INLINE void MDFN_en24lsb(uint8 *buf, uint32 morp)
|
||||
{
|
||||
buf[0]=morp;
|
||||
buf[1]=morp>>8;
|
||||
buf[2]=morp>>16;
|
||||
}
|
||||
|
||||
|
||||
static INLINE void MDFN_en32lsb(uint8 *buf, uint32 morp)
|
||||
{
|
||||
buf[0]=morp;
|
||||
buf[1]=morp>>8;
|
||||
buf[2]=morp>>16;
|
||||
buf[3]=morp>>24;
|
||||
}
|
||||
|
||||
static INLINE void MDFN_en64lsb(uint8 *buf, uint64 morp)
|
||||
{
|
||||
buf[0]=morp >> 0;
|
||||
buf[1]=morp >> 8;
|
||||
buf[2]=morp >> 16;
|
||||
buf[3]=morp >> 24;
|
||||
buf[4]=morp >> 32;
|
||||
buf[5]=morp >> 40;
|
||||
buf[6]=morp >> 48;
|
||||
buf[7]=morp >> 56;
|
||||
}
|
||||
|
||||
|
||||
static INLINE void MDFN_en16msb(uint8 *buf, uint16 morp)
|
||||
{
|
||||
buf[0] = morp >> 8;
|
||||
buf[1] = morp;
|
||||
}
|
||||
|
||||
static INLINE void MDFN_en24msb(uint8 *buf, uint32 morp)
|
||||
{
|
||||
buf[0] = morp >> 16;
|
||||
buf[1] = morp >> 8;
|
||||
buf[2] = morp;
|
||||
}
|
||||
|
||||
static INLINE void MDFN_en32msb(uint8 *buf, uint32 morp)
|
||||
{
|
||||
buf[0] = morp >> 24;
|
||||
buf[1] = morp >> 16;
|
||||
buf[2] = morp >> 8;
|
||||
buf[3] = morp;
|
||||
}
|
||||
|
||||
static INLINE void MDFN_en64msb(uint8 *buf, uint64 morp)
|
||||
{
|
||||
buf[0] = morp >> 56;
|
||||
buf[1] = morp >> 48;
|
||||
buf[2] = morp >> 40;
|
||||
buf[3] = morp >> 32;
|
||||
buf[4] = morp >> 24;
|
||||
buf[5] = morp >> 16;
|
||||
buf[6] = morp >> 8;
|
||||
buf[7] = morp >> 0;
|
||||
}
|
||||
|
||||
|
||||
// Overloaded functions, yay.
|
||||
static INLINE void MDFN_enlsb(uint16 * buf, uint16 value)
|
||||
{
|
||||
MDFN_en16lsb((uint8 *)buf, value);
|
||||
}
|
||||
|
||||
static INLINE void MDFN_enlsb(uint32 * buf, uint32 value)
|
||||
{
|
||||
MDFN_en32lsb((uint8 *)buf, value);
|
||||
}
|
||||
|
||||
static INLINE void MDFN_enlsb(uint64 * buf, uint64 value)
|
||||
{
|
||||
MDFN_en64lsb((uint8 *)buf, value);
|
||||
}
|
||||
|
||||
|
||||
static INLINE uint16 MDFN_de16lsb(const uint8 *morp)
|
||||
{
|
||||
return(morp[0] | (morp[1] << 8));
|
||||
}
|
||||
|
||||
|
||||
static INLINE uint32 MDFN_de24lsb(const uint8 *morp)
|
||||
{
|
||||
return(morp[0]|(morp[1]<<8)|(morp[2]<<16));
|
||||
}
|
||||
|
||||
static INLINE uint32 MDFN_de32lsb(const uint8 *morp)
|
||||
{
|
||||
return(morp[0]|(morp[1]<<8)|(morp[2]<<16)|(morp[3]<<24));
|
||||
}
|
||||
|
||||
static INLINE uint64 MDFN_de64lsb(const uint8 *morp)
|
||||
{
|
||||
uint64 ret = 0;
|
||||
|
||||
ret |= (uint64)morp[0];
|
||||
ret |= (uint64)morp[1] << 8;
|
||||
ret |= (uint64)morp[2] << 16;
|
||||
ret |= (uint64)morp[3] << 24;
|
||||
ret |= (uint64)morp[4] << 32;
|
||||
ret |= (uint64)morp[5] << 40;
|
||||
ret |= (uint64)morp[6] << 48;
|
||||
ret |= (uint64)morp[7] << 56;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static INLINE uint16 MDFN_delsb(const uint16 *buf)
|
||||
{
|
||||
return(MDFN_de16lsb((uint8 *)buf));
|
||||
}
|
||||
|
||||
static INLINE uint32 MDFN_delsb(const uint32 *buf)
|
||||
{
|
||||
return(MDFN_de32lsb((uint8 *)buf));
|
||||
}
|
||||
|
||||
static INLINE uint64 MDFN_delsb(const uint64 *buf)
|
||||
{
|
||||
return(MDFN_de64lsb((uint8 *)buf));
|
||||
}
|
||||
|
||||
static INLINE uint16 MDFN_de16msb(const uint8 *morp)
|
||||
{
|
||||
return(morp[1] | (morp[0] << 8));
|
||||
}
|
||||
|
||||
static INLINE uint32 MDFN_de24msb(const uint8 *morp)
|
||||
{
|
||||
return((morp[2]<<0)|(morp[1]<<8)|(morp[0]<<16));
|
||||
}
|
||||
|
||||
|
||||
static INLINE uint32 MDFN_de32msb(const uint8 *morp)
|
||||
{
|
||||
return(morp[3]|(morp[2]<<8)|(morp[1]<<16)|(morp[0]<<24));
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,75 @@
|
|||
#ifndef __MDFN_ERROR_H
|
||||
#define __MDFN_ERROR_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <exception>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
class ErrnoHolder;
|
||||
class MDFN_Error : public std::exception
|
||||
{
|
||||
public:
|
||||
|
||||
MDFN_Error() throw();
|
||||
|
||||
MDFN_Error(int errno_code_new, const char *format, ...) throw() MDFN_FORMATSTR(gnu_printf, 3, 4);
|
||||
MDFN_Error(const ErrnoHolder &enh);
|
||||
|
||||
~MDFN_Error() throw();
|
||||
|
||||
MDFN_Error(const MDFN_Error &ze_error) throw();
|
||||
MDFN_Error & operator=(const MDFN_Error &ze_error) throw();
|
||||
|
||||
virtual const char *what(void) const throw();
|
||||
int GetErrno(void) const throw();
|
||||
|
||||
private:
|
||||
|
||||
int errno_code;
|
||||
char *error_message;
|
||||
};
|
||||
|
||||
class ErrnoHolder
|
||||
{
|
||||
public:
|
||||
|
||||
ErrnoHolder()
|
||||
{
|
||||
//SetErrno(0);
|
||||
local_errno = 0;
|
||||
local_strerror[0] = 0;
|
||||
}
|
||||
|
||||
ErrnoHolder(int the_errno)
|
||||
{
|
||||
SetErrno(the_errno);
|
||||
}
|
||||
|
||||
inline int Errno(void) const
|
||||
{
|
||||
return(local_errno);
|
||||
}
|
||||
|
||||
const char *StrError(void) const
|
||||
{
|
||||
return(local_strerror);
|
||||
}
|
||||
|
||||
void operator=(int the_errno)
|
||||
{
|
||||
SetErrno(the_errno);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void SetErrno(int the_errno);
|
||||
|
||||
int local_errno;
|
||||
char local_strerror[256];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,562 @@
|
|||
///* Mednafen - Multi-system Emulator
|
||||
// *
|
||||
// * This program is free software; you can redistribute it and/or modify
|
||||
// * it under the terms of the GNU General Public License as published by
|
||||
// * the Free Software Foundation; either version 2 of the License, or
|
||||
// * (at your option) any later version.
|
||||
// *
|
||||
// * This program 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 General Public License for more details.
|
||||
// *
|
||||
// * You should have received a copy of the GNU General Public License
|
||||
// * along with this program; if not, write to the Free Software
|
||||
// * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// */
|
||||
//
|
||||
//#include <stdarg.h>
|
||||
//#include <string.h>
|
||||
//#include <sys/types.h>
|
||||
//#include <sys/stat.h>
|
||||
//#include <errno.h>
|
||||
//
|
||||
//#include "octoshock.h"
|
||||
//#include "FileStream.h"
|
||||
//
|
||||
//
|
||||
//#ifdef HAVE_MMAP
|
||||
//#include <sys/mman.h>
|
||||
//#include <sys/types.h>
|
||||
//#include <sys/stat.h>
|
||||
//#include <fcntl.h>
|
||||
//#endif
|
||||
//
|
||||
//#include "file.h"
|
||||
//
|
||||
//static const int64 MaxROMImageSize = (int64)1 << 26; // 2 ^ 26 = 64MiB
|
||||
//
|
||||
//enum
|
||||
//{
|
||||
// MDFN_FILETYPE_PLAIN = 0,
|
||||
// MDFN_FILETYPE_GZIP = 1,
|
||||
// MDFN_FILETYPE_ZIP = 2,
|
||||
//};
|
||||
//
|
||||
//void MDFNFILE::MakeMemWrap(void *tz, int type)
|
||||
//{
|
||||
// #ifdef HAVE_MMAP
|
||||
// is_mmap = FALSE;
|
||||
// #endif
|
||||
// location = 0;
|
||||
//
|
||||
// if(type == MDFN_FILETYPE_PLAIN)
|
||||
// {
|
||||
// ::fseek((FILE *)tz, 0, SEEK_END);
|
||||
// f_size = ::ftell((FILE *)tz);
|
||||
// ::fseek((FILE *)tz, 0, SEEK_SET);
|
||||
//
|
||||
//
|
||||
// #ifdef HAVE_MMAP
|
||||
// if((void *)-1 != (f_data = (uint8 *)mmap(NULL, size, PROT_READ, MAP_SHARED, fileno((FILE *)tz), 0)))
|
||||
// {
|
||||
// //puts("mmap'ed");
|
||||
// is_mmap = TRUE;
|
||||
// #ifdef HAVE_MADVISE
|
||||
// madvise(f_data, size, MADV_SEQUENTIAL | MADV_WILLNEED);
|
||||
// #endif
|
||||
// }
|
||||
// else
|
||||
// #endif
|
||||
// {
|
||||
// f_data = (uint8 *)MDFN_malloc_T(size, _("file read buffer"));
|
||||
//
|
||||
// if((int64)::fread(f_data, 1, size, (FILE *)tz) != size)
|
||||
// {
|
||||
// ErrnoHolder ene(errno);
|
||||
//
|
||||
// throw MDFN_Error(ene.Errno(), _("Error reading file: %s"), ene.StrError());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else if(type == MDFN_FILETYPE_GZIP)
|
||||
// {
|
||||
// uint32_t cur_size = 0;
|
||||
// uint32_t cur_alloced = 65536;
|
||||
// int howmany;
|
||||
//
|
||||
// f_data = (uint8 *)MDFN_malloc_T(cur_alloced, _("file read buffer"));
|
||||
//
|
||||
// while((howmany = gzread((gzFile)tz, f_data + cur_size, cur_alloced - cur_size)) > 0)
|
||||
// {
|
||||
// cur_size += howmany;
|
||||
// cur_alloced <<= 1;
|
||||
//
|
||||
// if(cur_size > MaxROMImageSize)
|
||||
// throw MDFN_Error(0, _("ROM image is too large; maximum size allowed is %llu bytes."), (unsigned long long)MaxROMImageSize);
|
||||
//
|
||||
// f_data = (uint8 *)MDFN_realloc_T(f_data, cur_alloced, _("file read buffer"));
|
||||
// }
|
||||
//
|
||||
// f_data = (uint8 *)MDFN_realloc_T(f_data, cur_size, _("file read buffer"));
|
||||
// f_size = cur_size;
|
||||
//
|
||||
// {
|
||||
// int gzerrnum = 0;
|
||||
// const char *gzerrstring;
|
||||
// if((gzerrstring = gzerror((gzFile)tz, &gzerrnum)) && gzerrnum != Z_OK && gzerrnum != Z_STREAM_END)
|
||||
// {
|
||||
// if(gzerrnum != Z_ERRNO)
|
||||
// {
|
||||
// throw MDFN_Error(0, _("Error reading file: zlib error: %s"), gzerrstring);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// ErrnoHolder ene(errno);
|
||||
// throw MDFN_Error(ene.Errno(), _("Error reading file: %s"), ene.StrError());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else if(type == MDFN_FILETYPE_ZIP)
|
||||
// {
|
||||
// unz_file_info ufo;
|
||||
// unzGetCurrentFileInfo((unzFile)tz, &ufo, 0, 0, 0, 0, 0, 0);
|
||||
//
|
||||
// f_size = ufo.uncompressed_size;
|
||||
//
|
||||
// if(size > MaxROMImageSize)
|
||||
// throw MDFN_Error(0, _("ROM image is too large; maximum size allowed is %llu bytes."), (unsigned long long)MaxROMImageSize);
|
||||
//
|
||||
// f_data = (uint8 *)MDFN_malloc_T(ufo.uncompressed_size, _("file read buffer"));
|
||||
// unzReadCurrentFile((unzFile)tz, f_data, ufo.uncompressed_size);
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//MDFNFILE::MDFNFILE(const char *path, const FileExtensionSpecStruct *known_ext, const char *purpose) : size(f_size), data((const uint8* const &)f_data), ext((const char * const &)f_ext), fbase((const char * const &)f_fbase)
|
||||
//{
|
||||
// f_data = NULL;
|
||||
// f_size = 0;
|
||||
// f_ext = NULL;
|
||||
// f_fbase = NULL;
|
||||
//
|
||||
// location = 0;
|
||||
//
|
||||
// #ifdef HAVE_MMAP
|
||||
// is_mmap = 0;
|
||||
// #endif
|
||||
//
|
||||
// Open(path, known_ext, purpose);
|
||||
//}
|
||||
//
|
||||
//MDFNFILE::~MDFNFILE()
|
||||
//{
|
||||
// Close();
|
||||
//}
|
||||
//
|
||||
//
|
||||
//void MDFNFILE::Open(const char *path, const FileExtensionSpecStruct *known_ext, const char *purpose)
|
||||
//{
|
||||
// unzFile tz = NULL;
|
||||
// FILE *fp = NULL;
|
||||
// gzFile gzp = NULL;
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// //
|
||||
// // Try opening it as a zip file first
|
||||
// //
|
||||
// if((tz = unzOpen(path)))
|
||||
// {
|
||||
// char tempu[1024];
|
||||
// int errcode;
|
||||
//
|
||||
// if((errcode = unzGoToFirstFile(tz)) != UNZ_OK)
|
||||
// {
|
||||
// throw MDFN_Error(0, _("Could not seek to first file in ZIP archive: %s"), unzErrorString(errcode));
|
||||
// }
|
||||
//
|
||||
// if(known_ext)
|
||||
// {
|
||||
// bool FileFound = FALSE;
|
||||
// while(!FileFound)
|
||||
// {
|
||||
// size_t tempu_strlen;
|
||||
// const FileExtensionSpecStruct *ext_search = known_ext;
|
||||
//
|
||||
// if((errcode = unzGetCurrentFileInfo(tz, 0, tempu, 1024, 0, 0, 0, 0)) != UNZ_OK)
|
||||
// {
|
||||
// throw MDFN_Error(0, _("Could not get file information in ZIP archive: %s"), unzErrorString(errcode));
|
||||
// }
|
||||
//
|
||||
// tempu[1023] = 0;
|
||||
// tempu_strlen = strlen(tempu);
|
||||
//
|
||||
// while(ext_search->extension && !FileFound)
|
||||
// {
|
||||
// size_t ttmeow = strlen(ext_search->extension);
|
||||
// if(tempu_strlen >= ttmeow)
|
||||
// {
|
||||
// if(!strcasecmp(tempu + tempu_strlen - ttmeow, ext_search->extension))
|
||||
// FileFound = TRUE;
|
||||
// }
|
||||
// ext_search++;
|
||||
// }
|
||||
//
|
||||
// if(FileFound)
|
||||
// break;
|
||||
//
|
||||
// if((errcode = unzGoToNextFile(tz)) != UNZ_OK)
|
||||
// {
|
||||
// if(errcode != UNZ_END_OF_LIST_OF_FILE)
|
||||
// {
|
||||
// throw MDFN_Error(0, _("Error seeking to next file in ZIP archive: %s"), unzErrorString(errcode));
|
||||
// }
|
||||
//
|
||||
// if((errcode = unzGoToFirstFile(tz)) != UNZ_OK)
|
||||
// {
|
||||
// throw MDFN_Error(0, _("Could not seek to first file in ZIP archive: %s"), unzErrorString(errcode));
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// } // end to while(!FileFound)
|
||||
// } // end to if(ext)
|
||||
//
|
||||
// if((errcode = unzOpenCurrentFile(tz)) != UNZ_OK)
|
||||
// {
|
||||
// throw MDFN_Error(0, _("Could not open file in ZIP archive: %s"), unzErrorString(errcode));
|
||||
// }
|
||||
//
|
||||
// MakeMemWrap(tz, MDFN_FILETYPE_ZIP);
|
||||
//
|
||||
// {
|
||||
// char *ld = strrchr(tempu, '.');
|
||||
//
|
||||
// f_ext = strdup(ld ? ld + 1 : "");
|
||||
// f_fbase = strdup(tempu);
|
||||
// if(ld)
|
||||
// f_fbase[ld - tempu] = 0;
|
||||
// }
|
||||
// }
|
||||
// else // If it's not a zip file, handle it as...another type of file!
|
||||
// {
|
||||
// if(!(fp = fopen(path, "rb")))
|
||||
// {
|
||||
// ErrnoHolder ene(errno);
|
||||
//
|
||||
// throw MDFN_Error(ene.Errno(), _("Error opening \"%s\": %s"), path, ene.StrError());
|
||||
// }
|
||||
//
|
||||
// const char *path_fnp = GetFNComponent(path);
|
||||
//
|
||||
// uint32 gzmagic;
|
||||
//
|
||||
// gzmagic = ::fgetc(fp);
|
||||
// gzmagic |= ::fgetc(fp) << 8;
|
||||
// gzmagic |= ::fgetc(fp) << 16;
|
||||
//
|
||||
// if(gzmagic != 0x088b1f) /* Not gzip... */
|
||||
// {
|
||||
// ::fseek(fp, 0, SEEK_SET);
|
||||
//
|
||||
// MakeMemWrap(fp, MDFN_FILETYPE_PLAIN);
|
||||
//
|
||||
// {
|
||||
// const char *ld = strrchr(path_fnp, '.');
|
||||
// f_ext = strdup(ld ? ld + 1 : "");
|
||||
// f_fbase = strdup(path_fnp);
|
||||
// if(ld)
|
||||
// f_fbase[ld - path_fnp] = 0;
|
||||
// }
|
||||
// }
|
||||
// else /* Probably gzip */
|
||||
// {
|
||||
// fclose(fp);
|
||||
// fp = NULL;
|
||||
//
|
||||
// // Clear errno so we can see if the error occurred within zlib or the C lib
|
||||
// errno = 0;
|
||||
// if(!(gzp = gzopen(path, "rb")))
|
||||
// {
|
||||
// if(errno != 0)
|
||||
// {
|
||||
// ErrnoHolder ene(errno);
|
||||
//
|
||||
// throw MDFN_Error(ene.Errno(), _("Error opening \"%s\": %s"), path, ene.StrError());
|
||||
// }
|
||||
// else
|
||||
// throw MDFN_Error(0, _("Error opening \"%s\": %s"), path, _("zlib error"));
|
||||
// }
|
||||
//
|
||||
// MakeMemWrap(gzp, MDFN_FILETYPE_GZIP);
|
||||
//
|
||||
// char *tmp_path = strdup(path_fnp);
|
||||
// char *ld = strrchr(tmp_path, '.');
|
||||
//
|
||||
// if(ld && ld > tmp_path)
|
||||
// {
|
||||
// char *last_ld = ld;
|
||||
// *ld = 0;
|
||||
// ld = strrchr(tmp_path, '.');
|
||||
// if(!ld) { ld = last_ld; }
|
||||
// else *ld = 0;
|
||||
// }
|
||||
// f_ext = strdup(ld ? ld + 1 : "");
|
||||
// f_fbase = tmp_path;
|
||||
// } // End gzip handling
|
||||
// } // End normal and gzip file handling else to zip
|
||||
// }
|
||||
// catch(...)
|
||||
// {
|
||||
// if(tz != NULL)
|
||||
// {
|
||||
// unzCloseCurrentFile(tz);
|
||||
// unzClose(tz);
|
||||
// }
|
||||
//
|
||||
// if(fp != NULL)
|
||||
// {
|
||||
// fclose(fp);
|
||||
// fp = NULL;
|
||||
// }
|
||||
//
|
||||
// if(gzp != NULL)
|
||||
// {
|
||||
// gzclose(gzp);
|
||||
// gzp = NULL;
|
||||
// }
|
||||
//
|
||||
// Close();
|
||||
// throw;
|
||||
// }
|
||||
//
|
||||
// if(tz != NULL)
|
||||
// {
|
||||
// unzCloseCurrentFile(tz);
|
||||
// unzClose(tz);
|
||||
// }
|
||||
//
|
||||
// if(fp != NULL)
|
||||
// {
|
||||
// fclose(fp);
|
||||
// fp = NULL;
|
||||
// }
|
||||
//
|
||||
// if(gzp != NULL)
|
||||
// {
|
||||
// gzclose(gzp);
|
||||
// gzp = NULL;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//void MDFNFILE::Close(void) throw()
|
||||
//{
|
||||
// if(f_ext)
|
||||
// {
|
||||
// free(f_ext);
|
||||
// f_ext = NULL;
|
||||
// }
|
||||
//
|
||||
// if(f_fbase)
|
||||
// {
|
||||
// free(f_fbase);
|
||||
// f_fbase = NULL;
|
||||
// }
|
||||
//
|
||||
// if(f_data)
|
||||
// {
|
||||
// #if HAVE_MMAP
|
||||
// if(is_mmap)
|
||||
// munmap(f_data, size);
|
||||
// else
|
||||
// #endif
|
||||
// free(f_data);
|
||||
// f_data = NULL;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//uint64 MDFNFILE::fread(void *ptr, size_t element_size, size_t nmemb)
|
||||
//{
|
||||
// uint32 total = element_size * nmemb;
|
||||
//
|
||||
// if(location >= f_size)
|
||||
// return 0;
|
||||
//
|
||||
// if((location + total) > f_size)
|
||||
// {
|
||||
// int64 ak = f_size - location;
|
||||
//
|
||||
// memcpy((uint8*)ptr, f_data + location, ak);
|
||||
//
|
||||
// location = f_size;
|
||||
//
|
||||
// return(ak / element_size);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// memcpy((uint8*)ptr, f_data + location, total);
|
||||
//
|
||||
// location += total;
|
||||
//
|
||||
// return nmemb;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//int MDFNFILE::fseek(int64 offset, int whence)
|
||||
//{
|
||||
// switch(whence)
|
||||
// {
|
||||
// case SEEK_SET:if(offset >= f_size)
|
||||
// return(-1);
|
||||
// location = offset;
|
||||
// break;
|
||||
//
|
||||
// case SEEK_CUR:if((offset + location) > f_size)
|
||||
// return(-1);
|
||||
//
|
||||
// location += offset;
|
||||
// break;
|
||||
// }
|
||||
// return 0;
|
||||
//}
|
||||
//
|
||||
//int MDFNFILE::read16le(uint16 *val)
|
||||
//{
|
||||
// if((location + 2) > size)
|
||||
// return 0;
|
||||
//
|
||||
// *val = MDFN_de16lsb(data + location);
|
||||
//
|
||||
// location += 2;
|
||||
//
|
||||
// return(1);
|
||||
//}
|
||||
//
|
||||
//int MDFNFILE::read32le(uint32 *val)
|
||||
//{
|
||||
// if((location + 4) > size)
|
||||
// return 0;
|
||||
//
|
||||
// *val = MDFN_de32lsb(data + location);
|
||||
//
|
||||
// location += 4;
|
||||
//
|
||||
// return(1);
|
||||
//}
|
||||
//
|
||||
//char *MDFNFILE::fgets(char *s, int buffer_size)
|
||||
//{
|
||||
// int pos = 0;
|
||||
//
|
||||
// if(!buffer_size)
|
||||
// return(NULL);
|
||||
//
|
||||
// if(location >= buffer_size)
|
||||
// return(NULL);
|
||||
//
|
||||
// while(pos < (buffer_size - 1) && location < buffer_size)
|
||||
// {
|
||||
// int v = data[location];
|
||||
// s[pos] = v;
|
||||
// location++;
|
||||
// pos++;
|
||||
// if(v == '\n') break;
|
||||
// }
|
||||
//
|
||||
// if(buffer_size)
|
||||
// s[pos] = 0;
|
||||
//
|
||||
// return(s);
|
||||
//}
|
||||
//
|
||||
//static INLINE bool MDFN_DumpToFileReal(const char *filename, int compress, const std::vector<PtrLengthPair> &pearpairs)
|
||||
//{
|
||||
// if(MDFN_GetSettingB("filesys.disablesavegz"))
|
||||
// compress = 0;
|
||||
//
|
||||
// if(compress)
|
||||
// {
|
||||
// char mode[64];
|
||||
// gzFile gp;
|
||||
//
|
||||
// trio_snprintf(mode, 64, "wb%d", compress);
|
||||
//
|
||||
// gp = gzopen(filename, mode);
|
||||
//
|
||||
// if(!gp)
|
||||
// {
|
||||
// ErrnoHolder ene(errno);
|
||||
//
|
||||
// MDFN_PrintError(_("Error opening \"%s\": %s"), filename, ene.StrError());
|
||||
// return(0);
|
||||
// }
|
||||
//
|
||||
// for(unsigned int i = 0; i < pearpairs.size(); i++)
|
||||
// {
|
||||
// const void *data = pearpairs[i].GetData();
|
||||
// const int64 length = pearpairs[i].GetLength();
|
||||
//
|
||||
// if(gzwrite(gp, data, length) != length)
|
||||
// {
|
||||
// int errnum;
|
||||
//
|
||||
// MDFN_PrintError(_("Error writing to \"%s\": %s"), filename, gzerror(gp, &errnum));
|
||||
// gzclose(gp);
|
||||
// return(0);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if(gzclose(gp) != Z_OK) // FIXME: Huhm, how should we handle this?
|
||||
// {
|
||||
// MDFN_PrintError(_("Error closing \"%s\""), filename);
|
||||
// return(0);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// FILE *fp = fopen(filename, "wb");
|
||||
// if(!fp)
|
||||
// {
|
||||
// ErrnoHolder ene(errno);
|
||||
//
|
||||
// MDFN_PrintError(_("Error opening \"%s\": %s"), filename, ene.StrError());
|
||||
// return(0);
|
||||
// }
|
||||
//
|
||||
// for(unsigned int i = 0; i < pearpairs.size(); i++)
|
||||
// {
|
||||
// const void *data = pearpairs[i].GetData();
|
||||
// const uint64 length = pearpairs[i].GetLength();
|
||||
//
|
||||
// if(fwrite(data, 1, length, fp) != length)
|
||||
// {
|
||||
// ErrnoHolder ene(errno);
|
||||
//
|
||||
// MDFN_PrintError(_("Error writing to \"%s\": %s"), filename, ene.StrError());
|
||||
// fclose(fp);
|
||||
// return(0);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if(fclose(fp) == EOF)
|
||||
// {
|
||||
// ErrnoHolder ene(errno);
|
||||
//
|
||||
// MDFN_PrintError(_("Error closing \"%s\": %s"), filename, ene.StrError());
|
||||
// return(0);
|
||||
// }
|
||||
// }
|
||||
// return(1);
|
||||
//}
|
||||
//
|
||||
//bool MDFN_DumpToFile(const char *filename, int compress, const std::vector<PtrLengthPair> &pearpairs)
|
||||
//{
|
||||
// return(MDFN_DumpToFileReal(filename, compress, pearpairs));
|
||||
//}
|
||||
//
|
||||
//bool MDFN_DumpToFile(const char *filename, int compress, const void *data, uint64 length)
|
||||
//{
|
||||
// std::vector<PtrLengthPair> tmp_pairs;
|
||||
// tmp_pairs.push_back(PtrLengthPair(data, length));
|
||||
// return(MDFN_DumpToFileReal(filename, compress, tmp_pairs));
|
||||
//}
|
|
@ -0,0 +1,122 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
class Stream;
|
||||
|
||||
#define MDFNFILE_EC_NOTFOUND 1
|
||||
#define MDFNFILE_EC_OTHER 2
|
||||
|
||||
class MDFNFILE
|
||||
{
|
||||
public:
|
||||
|
||||
MDFNFILE(const char *path, const void *known_ext, const char *purpose = NULL);
|
||||
~MDFNFILE();
|
||||
|
||||
void ApplyIPS(Stream *);
|
||||
void Close(void) throw();
|
||||
|
||||
const int64 &size;
|
||||
const uint8 * const &data;
|
||||
const char * const &ext;
|
||||
const char * const &fbase;
|
||||
|
||||
inline int64 Size(void)
|
||||
{
|
||||
return(f_size);
|
||||
}
|
||||
|
||||
inline const uint8 *Data(void)
|
||||
{
|
||||
return(f_data);
|
||||
}
|
||||
|
||||
uint64 fread(void *ptr, size_t size, size_t nmemb);
|
||||
int fseek(int64 offset, int whence);
|
||||
|
||||
inline uint64 ftell(void)
|
||||
{
|
||||
return(location);
|
||||
}
|
||||
|
||||
inline void rewind(void)
|
||||
{
|
||||
location = 0;
|
||||
}
|
||||
|
||||
int read32le(uint32 *Bufo);
|
||||
int read16le(uint16 *Bufo);
|
||||
|
||||
inline int fgetc(void)
|
||||
{
|
||||
if(location < f_size)
|
||||
return f_data[location++];
|
||||
|
||||
return EOF;
|
||||
}
|
||||
|
||||
inline int fisarchive(void)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
char *fgets(char *s, int size);
|
||||
|
||||
private:
|
||||
|
||||
uint8 *f_data;
|
||||
int64 f_size;
|
||||
char *f_ext;
|
||||
char *f_fbase;
|
||||
|
||||
int64 location;
|
||||
|
||||
#ifdef HAVE_MMAP
|
||||
bool is_mmap;
|
||||
#endif
|
||||
|
||||
void Open(const char *path, const void *known_ext, const char *purpose = NULL);
|
||||
void MakeMemWrap(void *tz, int type);
|
||||
};
|
||||
|
||||
class PtrLengthPair
|
||||
{
|
||||
public:
|
||||
|
||||
inline PtrLengthPair(const void *new_data, const uint64 new_length)
|
||||
{
|
||||
data = new_data;
|
||||
length = new_length;
|
||||
}
|
||||
|
||||
~PtrLengthPair()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
INLINE const void *GetData(void) const
|
||||
{
|
||||
return(data);
|
||||
}
|
||||
|
||||
INLINE uint64 GetLength(void) const
|
||||
{
|
||||
return(length);
|
||||
}
|
||||
|
||||
private:
|
||||
const void *data;
|
||||
uint64 length;
|
||||
};
|
||||
|
||||
#include <vector>
|
||||
|
||||
// These functions should be used for data like save states and non-volatile backup memory.
|
||||
// Until(if, even) we add LoadFromFile functions, for reading the files these functions generate, just use gzopen(), gzread(), etc.
|
||||
// "compress" is set to the zlib compression level. 0 disables compression entirely, and dumps the file without a gzip header or footer.
|
||||
// (Note: There is a setting that will force compress to 0 in the internal DumpToFile logic, for hackers who don't want to ungzip save files.)
|
||||
|
||||
bool MDFN_DumpToFile(const char *filename, int compress, const void *data, const uint64 length);
|
||||
bool MDFN_DumpToFile(const char *filename, int compress, const std::vector<PtrLengthPair> &pearpairs);
|
||||
|
|
@ -0,0 +1,357 @@
|
|||
#ifndef _GIT_H
|
||||
#define _GIT_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "octoshock.h"
|
||||
|
||||
#include "video.h"
|
||||
#include "file.h"
|
||||
|
||||
class CDIF;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *extension; // Example ".nes"
|
||||
const char *description; // Example "iNES Format ROM Image"
|
||||
} FileExtensionSpecStruct;
|
||||
|
||||
#include "file.h"
|
||||
|
||||
enum
|
||||
{
|
||||
MDFN_ROTATE0 = 0,
|
||||
MDFN_ROTATE90,
|
||||
MDFN_ROTATE180,
|
||||
MDFN_ROTATE270
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VIDSYS_NONE, // Can be used internally in system emulation code, but it is an error condition to let it continue to be
|
||||
// after the Load() or LoadCD() function returns!
|
||||
VIDSYS_PAL,
|
||||
VIDSYS_PAL_M, // Same timing as NTSC, but uses PAL-style colour encoding
|
||||
VIDSYS_NTSC,
|
||||
VIDSYS_SECAM
|
||||
} VideoSystems;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GMT_CART, // Self-explanatory!
|
||||
GMT_ARCADE, // VS Unisystem, PC-10...
|
||||
GMT_DISK, // Famicom Disk System, mostly
|
||||
GMT_CDROM, // PC Engine CD, PC-FX
|
||||
GMT_PLAYER // Music player(NSF, HES, GSF)
|
||||
} GameMediumTypes;
|
||||
|
||||
#include "state.h"
|
||||
|
||||
#ifdef WANT_DEBUGGER
|
||||
// #ifdef WANT_DEBUGGER
|
||||
// typedef struct DebuggerInfoStruct;
|
||||
// #else
|
||||
#include "debug.h"
|
||||
|
||||
#endif
|
||||
|
||||
typedef enum
|
||||
{
|
||||
IDIT_BUTTON, // 1-bit
|
||||
IDIT_BUTTON_CAN_RAPID, // 1-bit
|
||||
|
||||
IDIT_X_AXIS, // (mouse) 16-bits, signed - in-screen/window range: [0.0, nominal_width)
|
||||
IDIT_Y_AXIS, // (mouse) 16-bits, signed - in-screen/window range: [0.0, nominal_height)
|
||||
|
||||
IDIT_X_AXIS_REL, // (mouse) 32-bits, signed
|
||||
IDIT_Y_AXIS_REL, // (mouse) 32-bits, signed
|
||||
|
||||
IDIT_BYTE_SPECIAL,
|
||||
|
||||
IDIT_BUTTON_ANALOG, // 16-bits, 0 - 32767
|
||||
|
||||
IDIT_RUMBLE, // 16-bits, lower 8 bits are weak rumble(0-255), next 8 bits are strong rumble(0-255), 0=no rumble, 255=max rumble. Somewhat subjective, too...
|
||||
// It's a rather special case of game module->driver code communication.
|
||||
} InputDeviceInputType;
|
||||
|
||||
|
||||
#define IDIT_BUTTON_ANALOG_FLAG_SQLR 0x00000001 // Denotes analog data that may need to be scaled to ensure a more squareish logical range(for emulated
|
||||
// analog sticks).
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *SettingName; // No spaces, shouldbe all a-z0-9 and _. Definitely no ~!
|
||||
const char *Name;
|
||||
const int ConfigOrder; // Configuration order during in-game config process, -1 for no config.
|
||||
const InputDeviceInputType Type;
|
||||
const char *ExcludeName; // SettingName of a button that can't be pressed at the same time as this button
|
||||
// due to physical limitations.
|
||||
|
||||
const char *RotateName[3]; // 90, 180, 270
|
||||
unsigned Flags;
|
||||
} InputDeviceInputInfoStruct;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *ShortName;
|
||||
const char *FullName;
|
||||
const char *Description;
|
||||
|
||||
//struct InputPortInfoStruct *PortExpanderDeviceInfo;
|
||||
const void *PortExpanderDeviceInfo; // DON'T USE, IT'S NOT IMPLEMENTED PROPERLY CURRENTLY.
|
||||
int NumInputs; // Usually just the number of buttons....OR if PortExpanderDeviceInfo is non-NULL, it's the number of input
|
||||
// ports this port expander device provides.
|
||||
const InputDeviceInputInfoStruct *IDII;
|
||||
} InputDeviceInfoStruct;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *ShortName;
|
||||
const char *FullName;
|
||||
int NumTypes; // Number of unique input devices available for this input port
|
||||
InputDeviceInfoStruct *DeviceInfo;
|
||||
const char *DefaultDevice; // Default device for this port.
|
||||
} InputPortInfoStruct;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int InputPorts;
|
||||
const InputPortInfoStruct *Types;
|
||||
} InputInfoStruct;
|
||||
|
||||
struct MemoryPatch;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// Pitch(32-bit) must be equal to width and >= the "fb_width" specified in the MDFNGI struct for the emulated system.
|
||||
// Height must be >= to the "fb_height" specified in the MDFNGI struct for the emulated system.
|
||||
// The framebuffer pointed to by surface->pixels is written to by the system emulation code.
|
||||
MDFN_Surface *surface;
|
||||
|
||||
// Will be set to TRUE if the video pixel format has changed since the last call to Emulate(), FALSE otherwise.
|
||||
// Will be set to TRUE on the first call to the Emulate() function/method
|
||||
bool VideoFormatChanged;
|
||||
|
||||
// Set by the system emulation code every frame, to denote the horizontal and vertical offsets of the image, and the size
|
||||
// of the image. If the emulated system sets the elements of LineWidths, then the width(w) of this structure
|
||||
// is ignored while drawing the image.
|
||||
MDFN_Rect DisplayRect;
|
||||
|
||||
// Pointer to an array of int32, number of elements = fb_height, set by the driver code. Individual elements written
|
||||
// to by system emulation code. If the emulated system doesn't support multiple screen widths per frame, or if you handle
|
||||
// such a situation by outputting at a constant width-per-frame that is the least-common-multiple of the screen widths, then
|
||||
// you can ignore this. If you do wish to use this, you must set all elements every frame.
|
||||
int32 *LineWidths;
|
||||
|
||||
// TODO
|
||||
bool *IsFMV;
|
||||
|
||||
// Set(optionally) by emulation code. If InterlaceOn is true, then assume field height is 1/2 DisplayRect.h, and
|
||||
// only every other line in surface (with the start line defined by InterlacedField) has valid data
|
||||
// (it's up to internal Mednafen code to deinterlace it).
|
||||
bool InterlaceOn;
|
||||
bool InterlaceField;
|
||||
|
||||
// Skip rendering this frame if true. Set by the driver code.
|
||||
int skip;
|
||||
|
||||
//
|
||||
// If sound is disabled, the driver code must set SoundRate to false, SoundBuf to NULL, SoundBufMaxSize to 0.
|
||||
|
||||
// Will be set to TRUE if the sound format(only rate for now, at least) has changed since the last call to Emulate(), FALSE otherwise.
|
||||
// Will be set to TRUE on the first call to the Emulate() function/method
|
||||
bool SoundFormatChanged;
|
||||
|
||||
// Sound rate. Set by driver side.
|
||||
double SoundRate;
|
||||
|
||||
// Pointer to sound buffer, set by the driver code, that the emulation code should render sound to.
|
||||
// Guaranteed to be at least 500ms in length, but emulation code really shouldn't exceed 40ms or so. Additionally, if emulation code
|
||||
// generates >= 100ms,
|
||||
// DEPRECATED: Emulation code may set this pointer to a sound buffer internal to the emulation module.
|
||||
int16 *SoundBuf;
|
||||
|
||||
// Maximum size of the sound buffer, in frames. Set by the driver code.
|
||||
int32 SoundBufMaxSize;
|
||||
|
||||
// Number of frames currently in internal sound buffer. Set by the system emulation code, to be read by the driver code.
|
||||
int32 SoundBufSize;
|
||||
int32 SoundBufSizeALMS; // SoundBufSize value at last MidSync(), 0
|
||||
// if mid sync isn't implemented for the emulation module in use.
|
||||
|
||||
// Number of cycles that this frame consumed, using MDFNGI::MasterClock as a time base.
|
||||
// Set by emulation code.
|
||||
int64 MasterCycles;
|
||||
int64 MasterCyclesALMS; // MasterCycles value at last MidSync(), 0
|
||||
// if mid sync isn't implemented for the emulation module in use.
|
||||
|
||||
// Current sound volume(0.000...<=volume<=1.000...). If, after calling Emulate(), it is still != 1, Mednafen will handle it internally.
|
||||
// Emulation modules can handle volume themselves if they like, for speed reasons. If they do, afterwards, they should set its value to 1.
|
||||
double SoundVolume;
|
||||
|
||||
// Current sound speed multiplier. Set by the driver code. If, after calling Emulate(), it is still != 1, Mednafen will handle it internally
|
||||
// by resampling the audio. This means that emulation modules can handle(and set the value to 1 after handling it) it if they want to get the most
|
||||
// performance possible. HOWEVER, emulation modules must make sure the value is in a range(with minimum and maximum) that their code can handle
|
||||
// before they try to handle it.
|
||||
double soundmultiplier;
|
||||
|
||||
// True if we want to rewind one frame. Set by the driver code.
|
||||
bool NeedRewind;
|
||||
|
||||
// Sound reversal during state rewinding is normally done in mednafen.cpp, but
|
||||
// individual system emulation code can also do it if this is set, and clear it after it's done.
|
||||
// (Also, the driver code shouldn't touch this variable)
|
||||
bool NeedSoundReverse;
|
||||
|
||||
} EmulateSpecStruct;
|
||||
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* Private functions to Mednafen. Do not call directly
|
||||
from the driver code, or else bad things shall happen. Maybe. Probably not, but don't
|
||||
do it(yet)!
|
||||
*/
|
||||
// Short system name, lowercase a-z, 0-9, and _ are the only allowable characters!
|
||||
const char *shortname;
|
||||
|
||||
// Full system name. Preferably English letters, but can be UTF8
|
||||
const char *fullname;
|
||||
|
||||
// Pointer to an array of FileExtensionSpecStruct, with the last entry being { NULL, NULL } to terminate the list.
|
||||
// This list is used to make best-guess choices, when calling the TestMagic*() functions would be unreasonable, such
|
||||
// as when scanning a ZIP archive for a file to load. The list may also be used in the future for GUI file open windows.
|
||||
const FileExtensionSpecStruct *FileExtensions;
|
||||
|
||||
#ifdef WANT_DEBUGGER
|
||||
DebuggerInfoStruct *Debugger;
|
||||
#else
|
||||
void *Debugger;
|
||||
#endif
|
||||
InputInfoStruct *InputInfo;
|
||||
|
||||
//
|
||||
// Returns 1 on successful load.
|
||||
// throws exception on fatal error.
|
||||
//
|
||||
// DEPRECATED: Return 0 on fatal error.
|
||||
// DEPRECATED: Return -1 on unrecognized format.
|
||||
//
|
||||
// fp's stream position is guaranteed to be 0 when this function is called.
|
||||
//
|
||||
int (*Load)(MDFNFILE *fp);
|
||||
|
||||
//
|
||||
// Return true if the file is a recognized type, false if not.
|
||||
//
|
||||
// fp's stream position is guaranteed to be 0 when this function is called.
|
||||
//
|
||||
bool (*TestMagic)(MDFNFILE *fp);
|
||||
|
||||
//
|
||||
// (*CDInterfaces).size() is guaranteed to be >= 1.
|
||||
void (*LoadCD)(std::vector<CDIF *> *CDInterfaces);
|
||||
bool (*TestMagicCD)(std::vector<CDIF *> *CDInterfaces);
|
||||
|
||||
void (*CloseGame)(void);
|
||||
|
||||
void (*SetLayerEnableMask)(uint64 mask); // Video
|
||||
const char *LayerNames;
|
||||
|
||||
void (*SetChanEnableMask)(uint64 mask); // Audio(TODO, placeholder)
|
||||
const char *ChanNames;
|
||||
|
||||
//
|
||||
// InstallReadPatch and RemoveReadPatches should be non-NULL(even if only pointing to dummy functions) if the emulator module supports
|
||||
// read-substitution and read-substitution-with-compare style(IE Game Genie-style) cheats.
|
||||
//
|
||||
// See also "SubCheats" global stuff in mempatcher.h.
|
||||
//
|
||||
void (*InstallReadPatch)(uint32 address, uint8 value, int compare); // Compare is >= 0 when utilized.
|
||||
void (*RemoveReadPatches)(void);
|
||||
uint8 (*MemRead)(uint32 addr);
|
||||
|
||||
bool SaveStateAltersState; // true for bsnes and some libco-style emulators, false otherwise.
|
||||
// Main save state routine, called by the save state code in state.cpp.
|
||||
// When saving, load is set to 0. When loading, load is set to the version field of the save state being loaded.
|
||||
// data_only is true when the save state data is temporary, such as being saved into memory for state rewinding.
|
||||
int (*StateAction)(StateMem *sm, int load, int data_only);
|
||||
|
||||
void (*Emulate)(EmulateSpecStruct *espec);
|
||||
void (*SetInput)(int port, const char *type, void *ptr);
|
||||
|
||||
void (*DoSimpleCommand)(int cmd);
|
||||
|
||||
// Time base for EmulateSpecStruct::MasterCycles
|
||||
// MasterClock must be >= MDFN_MASTERCLOCK_FIXED(1.0)
|
||||
// All or part of the fractional component may be ignored in some timekeeping operations in the emulator to prevent integer overflow,
|
||||
// so it is unwise to have a fractional component when the integral component is very small(less than say, 10000).
|
||||
#define MDFN_MASTERCLOCK_FIXED(n) ((int64)((double)(n) * (1LL << 32)))
|
||||
int64 MasterClock;
|
||||
|
||||
// Nominal frames per second * 65536 * 256, truncated.
|
||||
// May be deprecated in the future due to many systems having slight frame rate programmability.
|
||||
uint32 fps;
|
||||
|
||||
// multires is a hint that, if set, indicates that the system has fairly programmable video modes(particularly, the ability
|
||||
// to display multiple horizontal resolutions, such as the PCE, PC-FX, or Genesis). In practice, it will cause the driver
|
||||
// code to set the linear interpolation on by default.
|
||||
//
|
||||
// lcm_width and lcm_height are the least common multiples of all possible
|
||||
// resolutions in the frame buffer as specified by DisplayRect/LineWidths(Ex for PCE: widths of 256, 341.333333, 512,
|
||||
// lcm = 1024)
|
||||
//
|
||||
// nominal_width and nominal_height specify the resolution that Mednafen should display
|
||||
// the framebuffer image in at 1x scaling, scaled from the dimensions of DisplayRect, and optionally the LineWidths array
|
||||
// passed through espec to the Emulate() function.
|
||||
//
|
||||
bool multires;
|
||||
|
||||
int lcm_width;
|
||||
int lcm_height;
|
||||
|
||||
void *dummy_separator; //
|
||||
|
||||
int nominal_width;
|
||||
int nominal_height;
|
||||
|
||||
int fb_width; // Width of the framebuffer(not necessarily width of the image). MDFN_Surface width should be >= this.
|
||||
int fb_height; // Height of the framebuffer passed to the Emulate() function(not necessarily height of the image)
|
||||
|
||||
int soundchan; // Number of output sound channels. Only values of 1 and 2 are currently supported.
|
||||
|
||||
|
||||
int rotated;
|
||||
|
||||
uint8 *name; /* Game name, UTF8 encoding */
|
||||
uint8 MD5[16];
|
||||
uint8 GameSetMD5[16]; /* A unique ID for the game set this CD belongs to, only used in PC-FX emulation. */
|
||||
bool GameSetMD5Valid; /* True if GameSetMD5 is valid. */
|
||||
|
||||
uint8 StateMD5[16]; // ID to use in save state naming and netplay session IDs, if
|
||||
bool StateMD5Valid; // StateMD5Valid is true(useful for systems with multiple BIOS revisions, e.g. PS1).
|
||||
|
||||
int soundrate; /* For Ogg Vorbis expansion sound wacky support. 0 for default. */
|
||||
|
||||
VideoSystems VideoSystem;
|
||||
GameMediumTypes GameType;
|
||||
|
||||
//int DiskLogicalCount; // A single double-sided disk would be 2 here.
|
||||
//const char *DiskNames; // Null-terminated.
|
||||
|
||||
const char *cspecial; /* Special cart expansion: DIP switches, barcode reader, etc. */
|
||||
|
||||
std::vector<const char *>DesiredInput; // Desired input device for the input ports, NULL for don't care
|
||||
|
||||
// For mouse relative motion.
|
||||
double mouse_sensitivity;
|
||||
|
||||
|
||||
//
|
||||
// For absolute coordinates(IDIT_X_AXIS and IDIT_Y_AXIS), usually mapped to a mouse(hence the naming).
|
||||
//
|
||||
float mouse_scale_x, mouse_scale_y;
|
||||
float mouse_offs_x, mouse_offs_y;
|
||||
} MDFNGI;
|
||||
#endif
|
|
@ -0,0 +1,209 @@
|
|||
#ifndef __MDFN_PSX_MASMEM_H
|
||||
#define __MDFN_PSX_MASMEM_H
|
||||
|
||||
// TODO, WIP (big-endian stores and loads not fully supported yet)
|
||||
|
||||
#ifdef LSB_FIRST
|
||||
#define MAS_NATIVE_IS_BIGENDIAN 0
|
||||
#else
|
||||
#define MAS_NATIVE_IS_BIGENDIAN 1
|
||||
#endif
|
||||
|
||||
static INLINE uint16 LoadU16_RBO(const uint16 *a)
|
||||
{
|
||||
#ifdef ARCH_POWERPC
|
||||
uint16 tmp;
|
||||
|
||||
__asm__ ("lhbrx %0, %y1" : "=r"(tmp) : "Z"(*a));
|
||||
|
||||
return(tmp);
|
||||
|
||||
#else
|
||||
uint16 tmp = *a;
|
||||
return((tmp << 8) | (tmp >> 8));
|
||||
#endif
|
||||
}
|
||||
|
||||
static INLINE uint32 LoadU32_RBO(const uint32 *a)
|
||||
{
|
||||
#ifdef ARCH_POWERPC
|
||||
uint32 tmp;
|
||||
|
||||
__asm__ ("lwbrx %0, %y1" : "=r"(tmp) : "Z"(*a));
|
||||
|
||||
return(tmp);
|
||||
#else
|
||||
uint32 tmp = *a;
|
||||
return((tmp << 24) | ((tmp & 0xFF00) << 8) | ((tmp >> 8) & 0xFF00) | (tmp >> 24));
|
||||
#endif
|
||||
}
|
||||
|
||||
static INLINE void StoreU16_RBO(uint16 *a, const uint16 v)
|
||||
{
|
||||
#ifdef ARCH_POWERPC
|
||||
__asm__ ("sthbrx %0, %y1" : : "r"(v), "Z"(*a));
|
||||
#else
|
||||
uint16 tmp = (v << 8) | (v >> 8);
|
||||
*a = tmp;
|
||||
#endif
|
||||
}
|
||||
|
||||
static INLINE void StoreU32_RBO(uint32 *a, const uint32 v)
|
||||
{
|
||||
#ifdef ARCH_POWERPC
|
||||
__asm__ ("stwbrx %0, %y1" : : "r"(v), "Z"(*a));
|
||||
#else
|
||||
uint32 tmp = (v << 24) | ((v & 0xFF00) << 8) | ((v >> 8) & 0xFF00) | (v >> 24);
|
||||
*a = tmp;
|
||||
#endif
|
||||
}
|
||||
|
||||
static INLINE uint16 LoadU16_LE(const uint16 *a)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
return LoadU16_RBO(a);
|
||||
#else
|
||||
return *a;
|
||||
#endif
|
||||
}
|
||||
|
||||
static INLINE uint32 LoadU32_LE(const uint32 *a)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
return LoadU32_RBO(a);
|
||||
#else
|
||||
return *a;
|
||||
#endif
|
||||
}
|
||||
|
||||
static INLINE void StoreU16_LE(uint16 *a, const uint16 v)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
StoreU16_RBO(a, v);
|
||||
#else
|
||||
*a = v;
|
||||
#endif
|
||||
}
|
||||
|
||||
static INLINE void StoreU32_LE(uint32 *a, const uint32 v)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
StoreU32_RBO(a, v);
|
||||
#else
|
||||
*a = v;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// address must not be >= size specified by template parameter, and address must be a multiple of the byte-size of the
|
||||
// unit(1,2,4) being read(except for Read/WriteU24, which only needs to be byte-aligned).
|
||||
//
|
||||
// max_unit_type should be uint16 or uint32
|
||||
//
|
||||
// pre_padding and post_padding are specified in units of sizeof(max_unit_type).
|
||||
//
|
||||
template<unsigned size, typename max_unit_type, bool big_endian> //, unsigned pre_padding_count, unsigned post_padding_count>
|
||||
struct MultiAccessSizeMem
|
||||
{
|
||||
//max_unit_type pre_padding[pre_padding_count ? pre_padding_count : 1];
|
||||
|
||||
union
|
||||
{
|
||||
uint8 data8[size];
|
||||
uint16 data16[size / sizeof(uint16)];
|
||||
uint32 data32[size / sizeof(uint32)];
|
||||
};
|
||||
|
||||
//max_unit_type post_padding[post_padding_count ? post_padding_count : 1];
|
||||
|
||||
INLINE uint8 ReadU8(uint32 address)
|
||||
{
|
||||
return data8[address];
|
||||
}
|
||||
|
||||
INLINE uint16 ReadU16(uint32 address)
|
||||
{
|
||||
if(MAS_NATIVE_IS_BIGENDIAN == big_endian)
|
||||
return *(uint16*)(((uint8*)data16) + address);
|
||||
else
|
||||
return LoadU16_RBO((uint16*)(((uint8*)data16) + address));
|
||||
}
|
||||
|
||||
INLINE uint32 ReadU32(uint32 address)
|
||||
{
|
||||
if(MAS_NATIVE_IS_BIGENDIAN == big_endian)
|
||||
return *(uint32*)(((uint8*)data32) + address);
|
||||
else
|
||||
return LoadU32_RBO((uint32*)(((uint8*)data32) + address));
|
||||
}
|
||||
|
||||
INLINE uint32 ReadU24(uint32 address)
|
||||
{
|
||||
uint32 ret;
|
||||
|
||||
if(!big_endian)
|
||||
{
|
||||
ret = ReadU8(address) | (ReadU8(address + 1) << 8) | (ReadU8(address + 2) << 16);
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
INLINE void WriteU8(uint32 address, uint8 value)
|
||||
{
|
||||
data8[address] = value;
|
||||
}
|
||||
|
||||
INLINE void WriteU16(uint32 address, uint16 value)
|
||||
{
|
||||
if(MAS_NATIVE_IS_BIGENDIAN == big_endian)
|
||||
*(uint16*)(((uint8*)data16) + address) = value;
|
||||
else
|
||||
StoreU16_RBO((uint16*)(((uint8*)data16) + address), value);
|
||||
}
|
||||
|
||||
INLINE void WriteU32(uint32 address, uint32 value)
|
||||
{
|
||||
if(MAS_NATIVE_IS_BIGENDIAN == big_endian)
|
||||
*(uint32*)(((uint8*)data32) + address) = value;
|
||||
else
|
||||
StoreU32_RBO((uint32*)(((uint8*)data32) + address), value);
|
||||
}
|
||||
|
||||
INLINE void WriteU24(uint32 address, uint32 value)
|
||||
{
|
||||
if(!big_endian)
|
||||
{
|
||||
WriteU8(address + 0, value >> 0);
|
||||
WriteU8(address + 1, value >> 8);
|
||||
WriteU8(address + 2, value >> 16);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE T Read(uint32 address)
|
||||
{
|
||||
if(sizeof(T) == 4)
|
||||
return(ReadU32(address));
|
||||
else if(sizeof(T) == 2)
|
||||
return(ReadU16(address));
|
||||
else
|
||||
return(ReadU8(address));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE void Write(uint32 address, T value)
|
||||
{
|
||||
if(sizeof(T) == 4)
|
||||
WriteU32(address, value);
|
||||
else if(sizeof(T) == 2)
|
||||
WriteU16(address, value);
|
||||
else
|
||||
WriteU8(address, value);
|
||||
}
|
||||
};
|
||||
|
||||
#undef MAS_NATIVE_IS_BIGENDIAN
|
||||
|
||||
#endif
|
|
@ -0,0 +1,87 @@
|
|||
#pragma once
|
||||
|
||||
#include "emuware/emuware.h"
|
||||
|
||||
// Source: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
|
||||
// Rounds up to the nearest power of 2.
|
||||
static INLINE uint64 round_up_pow2(uint64 v)
|
||||
{
|
||||
v--;
|
||||
v |= v >> 1;
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
v |= v >> 8;
|
||||
v |= v >> 16;
|
||||
v |= v >> 32;
|
||||
v++;
|
||||
|
||||
v += (v == 0);
|
||||
|
||||
return(v);
|
||||
}
|
||||
|
||||
static INLINE uint32 uilog2(uint32 v)
|
||||
{
|
||||
// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn
|
||||
|
||||
static const uint32 MultiplyDeBruijnBitPosition[32] =
|
||||
{
|
||||
0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
|
||||
8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
|
||||
};
|
||||
|
||||
v |= v >> 1; // first round down to one less than a power of 2
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
v |= v >> 8;
|
||||
v |= v >> 16;
|
||||
|
||||
return MultiplyDeBruijnBitPosition[(uint32_t)(v * 0x07C4ACDDU) >> 27];
|
||||
}
|
||||
|
||||
// Some compilers' optimizers and some platforms might fubar the generated code from these macros,
|
||||
// so some tests are run in...tests.cpp
|
||||
#define sign_8_to_s16(_value) ((int16)(int8)(_value))
|
||||
#define sign_9_to_s16(_value) (((int16)((unsigned int)(_value) << 7)) >> 7)
|
||||
#define sign_10_to_s16(_value) (((int16)((uint32)(_value) << 6)) >> 6)
|
||||
#define sign_11_to_s16(_value) (((int16)((uint32)(_value) << 5)) >> 5)
|
||||
#define sign_12_to_s16(_value) (((int16)((uint32)(_value) << 4)) >> 4)
|
||||
#define sign_13_to_s16(_value) (((int16)((uint32)(_value) << 3)) >> 3)
|
||||
#define sign_14_to_s16(_value) (((int16)((uint32)(_value) << 2)) >> 2)
|
||||
#define sign_15_to_s16(_value) (((int16)((uint32)(_value) << 1)) >> 1)
|
||||
|
||||
// This obviously won't convert higher-than-32 bit numbers to signed 32-bit ;)
|
||||
// Also, this shouldn't be used for 8-bit and 16-bit signed numbers, since you can
|
||||
// convert those faster with typecasts...
|
||||
#define sign_x_to_s32(_bits, _value) (((int32)((uint32)(_value) << (32 - _bits))) >> (32 - _bits))
|
||||
|
||||
static INLINE int32 clamp_to_u8(int32 i)
|
||||
{
|
||||
if(i & 0xFFFFFF00)
|
||||
i = (((~i) >> 30) & 0xFF);
|
||||
|
||||
return(i);
|
||||
}
|
||||
|
||||
static INLINE int32 clamp_to_u16(int32 i)
|
||||
{
|
||||
if(i & 0xFFFF0000)
|
||||
i = (((~i) >> 31) & 0xFFFF);
|
||||
|
||||
return(i);
|
||||
}
|
||||
|
||||
template<typename T, typename U, typename V> static INLINE void clamp(T *val, U minimum, V maximum)
|
||||
{
|
||||
if(*val < minimum)
|
||||
{
|
||||
//printf("Warning: clamping to minimum(%d)\n", (int)minimum);
|
||||
*val = minimum;
|
||||
}
|
||||
if(*val > maximum)
|
||||
{
|
||||
//printf("Warning: clamping to maximum(%d)\n", (int)maximum);
|
||||
*val = maximum;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
#include "octoshock.h"
|
||||
|
||||
//this file intentionally empty
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "emuware/emuware.h"
|
||||
|
||||
#define SIZEOF_DOUBLE 8
|
||||
|
||||
#define LSB_FIRST
|
||||
|
||||
EW_EXPORT int os_test();
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
DEFS = -DLOCALEDIR=\"$(datadir)/locale\" @DEFS@ @MMX_CFLAGS@ @SSE_CFLAGS@ @SSE2_CFLAGS@ -funroll-loops
|
||||
DEFAULT_INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include -I$(top_srcdir)/intl
|
||||
|
||||
noinst_LIBRARIES = libpsx.a
|
||||
libpsx_a_SOURCES = psx.cpp irq.cpp timer.cpp dma.cpp frontio.cpp sio.cpp cpu.cpp gte.cpp dis.cpp cdc.cpp spu.cpp gpu.cpp mdec.cpp
|
||||
|
||||
libpsx_a_SOURCES += input/gamepad.cpp input/dualanalog.cpp input/dualshock.cpp input/memcard.cpp input/multitap.cpp input/mouse.cpp input/negcon.cpp input/guncon.cpp input/justifier.cpp
|
||||
|
||||
if WANT_DEBUGGER
|
||||
libpsx_a_SOURCES += debug.cpp
|
||||
endif
|
|
@ -0,0 +1,699 @@
|
|||
# Makefile.in generated by automake 1.11.6 from Makefile.am.
|
||||
# @configure_input@
|
||||
|
||||
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
|
||||
# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
|
||||
# Foundation, Inc.
|
||||
# This Makefile.in is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
|
||||
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
# PARTICULAR PURPOSE.
|
||||
|
||||
@SET_MAKE@
|
||||
|
||||
VPATH = @srcdir@
|
||||
am__make_dryrun = \
|
||||
{ \
|
||||
am__dry=no; \
|
||||
case $$MAKEFLAGS in \
|
||||
*\\[\ \ ]*) \
|
||||
echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \
|
||||
| grep '^AM OK$$' >/dev/null || am__dry=yes;; \
|
||||
*) \
|
||||
for am__flg in $$MAKEFLAGS; do \
|
||||
case $$am__flg in \
|
||||
*=*|--*) ;; \
|
||||
*n*) am__dry=yes; break;; \
|
||||
esac; \
|
||||
done;; \
|
||||
esac; \
|
||||
test $$am__dry = yes; \
|
||||
}
|
||||
pkgdatadir = $(datadir)/@PACKAGE@
|
||||
pkgincludedir = $(includedir)/@PACKAGE@
|
||||
pkglibdir = $(libdir)/@PACKAGE@
|
||||
pkglibexecdir = $(libexecdir)/@PACKAGE@
|
||||
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
|
||||
install_sh_DATA = $(install_sh) -c -m 644
|
||||
install_sh_PROGRAM = $(install_sh) -c
|
||||
install_sh_SCRIPT = $(install_sh) -c
|
||||
INSTALL_HEADER = $(INSTALL_DATA)
|
||||
transform = $(program_transform_name)
|
||||
NORMAL_INSTALL = :
|
||||
PRE_INSTALL = :
|
||||
POST_INSTALL = :
|
||||
NORMAL_UNINSTALL = :
|
||||
PRE_UNINSTALL = :
|
||||
POST_UNINSTALL = :
|
||||
build_triplet = @build@
|
||||
host_triplet = @host@
|
||||
target_triplet = @target@
|
||||
@WANT_DEBUGGER_TRUE@am__append_1 = debug.cpp
|
||||
subdir = src/psx
|
||||
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
|
||||
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
||||
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cflags_gcc_option.m4 \
|
||||
$(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/fcntl-o.m4 \
|
||||
$(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/glibc2.m4 \
|
||||
$(top_srcdir)/m4/glibc21.m4 $(top_srcdir)/m4/iconv.m4 \
|
||||
$(top_srcdir)/m4/intdiv0.m4 $(top_srcdir)/m4/intl.m4 \
|
||||
$(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intmax.m4 \
|
||||
$(top_srcdir)/m4/inttypes-pri.m4 \
|
||||
$(top_srcdir)/m4/inttypes_h.m4 $(top_srcdir)/m4/lcmessage.m4 \
|
||||
$(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
|
||||
$(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
|
||||
$(top_srcdir)/m4/lock.m4 $(top_srcdir)/m4/longlong.m4 \
|
||||
$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
|
||||
$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
|
||||
$(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
|
||||
$(top_srcdir)/m4/printf-posix.m4 $(top_srcdir)/m4/progtest.m4 \
|
||||
$(top_srcdir)/m4/size_max.m4 $(top_srcdir)/m4/stdint_h.m4 \
|
||||
$(top_srcdir)/m4/threadlib.m4 $(top_srcdir)/m4/uintmax_t.m4 \
|
||||
$(top_srcdir)/m4/visibility.m4 $(top_srcdir)/m4/wchar_t.m4 \
|
||||
$(top_srcdir)/m4/wint_t.m4 $(top_srcdir)/m4/xsize.m4 \
|
||||
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
|
||||
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
|
||||
$(ACLOCAL_M4)
|
||||
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
|
||||
CONFIG_HEADER = $(top_builddir)/include/config.h
|
||||
CONFIG_CLEAN_FILES =
|
||||
CONFIG_CLEAN_VPATH_FILES =
|
||||
LIBRARIES = $(noinst_LIBRARIES)
|
||||
ARFLAGS = cru
|
||||
AM_V_AR = $(am__v_AR_@AM_V@)
|
||||
am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
|
||||
am__v_AR_0 = @echo " AR " $@;
|
||||
AM_V_at = $(am__v_at_@AM_V@)
|
||||
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
|
||||
am__v_at_0 = @
|
||||
libpsx_a_AR = $(AR) $(ARFLAGS)
|
||||
libpsx_a_LIBADD =
|
||||
am__libpsx_a_SOURCES_DIST = psx.cpp irq.cpp timer.cpp dma.cpp \
|
||||
frontio.cpp sio.cpp cpu.cpp gte.cpp dis.cpp cdc.cpp spu.cpp \
|
||||
gpu.cpp mdec.cpp input/gamepad.cpp input/dualanalog.cpp \
|
||||
input/dualshock.cpp input/memcard.cpp input/multitap.cpp \
|
||||
input/mouse.cpp input/negcon.cpp input/guncon.cpp \
|
||||
input/justifier.cpp debug.cpp
|
||||
am__dirstamp = $(am__leading_dot)dirstamp
|
||||
@WANT_DEBUGGER_TRUE@am__objects_1 = debug.$(OBJEXT)
|
||||
am_libpsx_a_OBJECTS = psx.$(OBJEXT) irq.$(OBJEXT) timer.$(OBJEXT) \
|
||||
dma.$(OBJEXT) frontio.$(OBJEXT) sio.$(OBJEXT) cpu.$(OBJEXT) \
|
||||
gte.$(OBJEXT) dis.$(OBJEXT) cdc.$(OBJEXT) spu.$(OBJEXT) \
|
||||
gpu.$(OBJEXT) mdec.$(OBJEXT) input/gamepad.$(OBJEXT) \
|
||||
input/dualanalog.$(OBJEXT) input/dualshock.$(OBJEXT) \
|
||||
input/memcard.$(OBJEXT) input/multitap.$(OBJEXT) \
|
||||
input/mouse.$(OBJEXT) input/negcon.$(OBJEXT) \
|
||||
input/guncon.$(OBJEXT) input/justifier.$(OBJEXT) \
|
||||
$(am__objects_1)
|
||||
libpsx_a_OBJECTS = $(am_libpsx_a_OBJECTS)
|
||||
depcomp = $(SHELL) $(top_srcdir)/depcomp
|
||||
am__depfiles_maybe = depfiles
|
||||
am__mv = mv -f
|
||||
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
|
||||
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
|
||||
AM_V_lt = $(am__v_lt_@AM_V@)
|
||||
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
|
||||
am__v_lt_0 = --silent
|
||||
LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
|
||||
$(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
|
||||
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
|
||||
$(AM_CXXFLAGS) $(CXXFLAGS)
|
||||
AM_V_CXX = $(am__v_CXX_@AM_V@)
|
||||
am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
|
||||
am__v_CXX_0 = @echo " CXX " $@;
|
||||
CXXLD = $(CXX)
|
||||
CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
|
||||
$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
|
||||
$(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
|
||||
AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
|
||||
am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
|
||||
am__v_CXXLD_0 = @echo " CXXLD " $@;
|
||||
AM_V_GEN = $(am__v_GEN_@AM_V@)
|
||||
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
|
||||
am__v_GEN_0 = @echo " GEN " $@;
|
||||
SOURCES = $(libpsx_a_SOURCES)
|
||||
DIST_SOURCES = $(am__libpsx_a_SOURCES_DIST)
|
||||
am__can_run_installinfo = \
|
||||
case $$AM_UPDATE_INFO_DIR in \
|
||||
n|no|NO) false;; \
|
||||
*) (install-info --version) >/dev/null 2>&1;; \
|
||||
esac
|
||||
ETAGS = etags
|
||||
CTAGS = ctags
|
||||
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
|
||||
ACLOCAL = @ACLOCAL@
|
||||
ALLOCA = @ALLOCA@
|
||||
ALSA_CFLAGS = @ALSA_CFLAGS@
|
||||
ALSA_LIBS = @ALSA_LIBS@
|
||||
AMTAR = @AMTAR@
|
||||
AM_CFLAGS = @AM_CFLAGS@
|
||||
AM_CXXFLAGS = @AM_CXXFLAGS@
|
||||
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
|
||||
AR = @AR@
|
||||
AUTOCONF = @AUTOCONF@
|
||||
AUTOHEADER = @AUTOHEADER@
|
||||
AUTOMAKE = @AUTOMAKE@
|
||||
AWK = @AWK@
|
||||
BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@
|
||||
CATOBJEXT = @CATOBJEXT@
|
||||
CC = @CC@
|
||||
CCAS = @CCAS@
|
||||
CCASDEPMODE = @CCASDEPMODE@
|
||||
CCASFLAGS = @CCASFLAGS@
|
||||
CCDEPMODE = @CCDEPMODE@
|
||||
CFLAGS = @CFLAGS@
|
||||
CFLAG_VISIBILITY = @CFLAG_VISIBILITY@
|
||||
CPP = @CPP@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
CXX = @CXX@
|
||||
CXXCPP = @CXXCPP@
|
||||
CXXDEPMODE = @CXXDEPMODE@
|
||||
CXXFLAGS = @CXXFLAGS@
|
||||
CYGPATH_W = @CYGPATH_W@
|
||||
DATADIRNAME = @DATADIRNAME@
|
||||
DEFS = -DLOCALEDIR=\"$(datadir)/locale\" @DEFS@ @MMX_CFLAGS@ @SSE_CFLAGS@ @SSE2_CFLAGS@ -funroll-loops
|
||||
DEPDIR = @DEPDIR@
|
||||
DLLTOOL = @DLLTOOL@
|
||||
DSYMUTIL = @DSYMUTIL@
|
||||
DUMPBIN = @DUMPBIN@
|
||||
ECHO_C = @ECHO_C@
|
||||
ECHO_N = @ECHO_N@
|
||||
ECHO_T = @ECHO_T@
|
||||
EGREP = @EGREP@
|
||||
EXEEXT = @EXEEXT@
|
||||
FGREP = @FGREP@
|
||||
GBA_EXTRA_FLAGS = @GBA_EXTRA_FLAGS@
|
||||
GENCAT = @GENCAT@
|
||||
GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
|
||||
GLIBC2 = @GLIBC2@
|
||||
GLIBC21 = @GLIBC21@
|
||||
GMSGFMT = @GMSGFMT@
|
||||
GMSGFMT_015 = @GMSGFMT_015@
|
||||
GREP = @GREP@
|
||||
HAVE_ASPRINTF = @HAVE_ASPRINTF@
|
||||
HAVE_NEWLOCALE = @HAVE_NEWLOCALE@
|
||||
HAVE_POSIX_PRINTF = @HAVE_POSIX_PRINTF@
|
||||
HAVE_SNPRINTF = @HAVE_SNPRINTF@
|
||||
HAVE_VISIBILITY = @HAVE_VISIBILITY@
|
||||
HAVE_WPRINTF = @HAVE_WPRINTF@
|
||||
INSTALL = @INSTALL@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
||||
INSTALL_SCRIPT = @INSTALL_SCRIPT@
|
||||
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
|
||||
INSTOBJEXT = @INSTOBJEXT@
|
||||
INTLBISON = @INTLBISON@
|
||||
INTLLIBS = @INTLLIBS@
|
||||
INTLOBJS = @INTLOBJS@
|
||||
INTL_DEFAULT_VERBOSITY = @INTL_DEFAULT_VERBOSITY@
|
||||
INTL_LIBTOOL_SUFFIX_PREFIX = @INTL_LIBTOOL_SUFFIX_PREFIX@
|
||||
INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
|
||||
JACK_CFLAGS = @JACK_CFLAGS@
|
||||
JACK_LIBS = @JACK_LIBS@
|
||||
LD = @LD@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBCDIO_CFLAGS = @LIBCDIO_CFLAGS@
|
||||
LIBCDIO_LIBS = @LIBCDIO_LIBS@
|
||||
LIBICONV = @LIBICONV@
|
||||
LIBINTL = @LIBINTL@
|
||||
LIBMULTITHREAD = @LIBMULTITHREAD@
|
||||
LIBOBJS = @LIBOBJS@
|
||||
LIBPTH = @LIBPTH@
|
||||
LIBPTH_PREFIX = @LIBPTH_PREFIX@
|
||||
LIBS = @LIBS@
|
||||
LIBTHREAD = @LIBTHREAD@
|
||||
LIBTOOL = @LIBTOOL@
|
||||
LIPO = @LIPO@
|
||||
LN_S = @LN_S@
|
||||
LTLIBC = @LTLIBC@
|
||||
LTLIBICONV = @LTLIBICONV@
|
||||
LTLIBINTL = @LTLIBINTL@
|
||||
LTLIBMULTITHREAD = @LTLIBMULTITHREAD@
|
||||
LTLIBOBJS = @LTLIBOBJS@
|
||||
LTLIBPTH = @LTLIBPTH@
|
||||
LTLIBTHREAD = @LTLIBTHREAD@
|
||||
MAKEINFO = @MAKEINFO@
|
||||
MANIFEST_TOOL = @MANIFEST_TOOL@
|
||||
MKDIR_P = @MKDIR_P@
|
||||
MMX_CFLAGS = @MMX_CFLAGS@
|
||||
MSGFMT = @MSGFMT@
|
||||
MSGFMT_015 = @MSGFMT_015@
|
||||
MSGMERGE = @MSGMERGE@
|
||||
NM = @NM@
|
||||
NMEDIT = @NMEDIT@
|
||||
OBJDUMP = @OBJDUMP@
|
||||
OBJEXT = @OBJEXT@
|
||||
OTOOL = @OTOOL@
|
||||
OTOOL64 = @OTOOL64@
|
||||
PACKAGE = @PACKAGE@
|
||||
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
|
||||
PACKAGE_NAME = @PACKAGE_NAME@
|
||||
PACKAGE_STRING = @PACKAGE_STRING@
|
||||
PACKAGE_TARNAME = @PACKAGE_TARNAME@
|
||||
PACKAGE_URL = @PACKAGE_URL@
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
PATH_SEPARATOR = @PATH_SEPARATOR@
|
||||
PKG_CONFIG = @PKG_CONFIG@
|
||||
PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
|
||||
PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
|
||||
POSUB = @POSUB@
|
||||
PRI_MACROS_BROKEN = @PRI_MACROS_BROKEN@
|
||||
RANLIB = @RANLIB@
|
||||
SDL_CFLAGS = @SDL_CFLAGS@
|
||||
SDL_CONFIG = @SDL_CONFIG@
|
||||
SDL_LIBS = @SDL_LIBS@
|
||||
SED = @SED@
|
||||
SET_MAKE = @SET_MAKE@
|
||||
SHELL = @SHELL@
|
||||
SNDFILE_CFLAGS = @SNDFILE_CFLAGS@
|
||||
SNDFILE_LIBS = @SNDFILE_LIBS@
|
||||
SNES_EXTRA_CXXFLAGS = @SNES_EXTRA_CXXFLAGS@
|
||||
SNES_EXTRA_FLAGS = @SNES_EXTRA_FLAGS@
|
||||
SSE2_CFLAGS = @SSE2_CFLAGS@
|
||||
SSE3_CFLAGS = @SSE3_CFLAGS@
|
||||
SSE_CFLAGS = @SSE_CFLAGS@
|
||||
STRIP = @STRIP@
|
||||
TRIO_CFLAGS = @TRIO_CFLAGS@
|
||||
USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@
|
||||
USE_NLS = @USE_NLS@
|
||||
VERSION = @VERSION@
|
||||
WARNING_FLAGS = @WARNING_FLAGS@
|
||||
WINDRES = @WINDRES@
|
||||
WOE32 = @WOE32@
|
||||
WOE32DLL = @WOE32DLL@
|
||||
XGETTEXT = @XGETTEXT@
|
||||
XGETTEXT_015 = @XGETTEXT_015@
|
||||
XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
|
||||
abs_builddir = @abs_builddir@
|
||||
abs_srcdir = @abs_srcdir@
|
||||
abs_top_builddir = @abs_top_builddir@
|
||||
abs_top_srcdir = @abs_top_srcdir@
|
||||
ac_ct_AR = @ac_ct_AR@
|
||||
ac_ct_CC = @ac_ct_CC@
|
||||
ac_ct_CXX = @ac_ct_CXX@
|
||||
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
|
||||
am__include = @am__include@
|
||||
am__leading_dot = @am__leading_dot@
|
||||
am__quote = @am__quote@
|
||||
am__tar = @am__tar@
|
||||
am__untar = @am__untar@
|
||||
bindir = @bindir@
|
||||
build = @build@
|
||||
build_alias = @build_alias@
|
||||
build_cpu = @build_cpu@
|
||||
build_os = @build_os@
|
||||
build_vendor = @build_vendor@
|
||||
builddir = @builddir@
|
||||
datadir = @datadir@
|
||||
datarootdir = @datarootdir@
|
||||
docdir = @docdir@
|
||||
dvidir = @dvidir@
|
||||
exec_prefix = @exec_prefix@
|
||||
host = @host@
|
||||
host_alias = @host_alias@
|
||||
host_cpu = @host_cpu@
|
||||
host_os = @host_os@
|
||||
host_vendor = @host_vendor@
|
||||
htmldir = @htmldir@
|
||||
includedir = @includedir@
|
||||
infodir = @infodir@
|
||||
install_sh = @install_sh@
|
||||
libdir = @libdir@
|
||||
libexecdir = @libexecdir@
|
||||
localedir = @localedir@
|
||||
localstatedir = @localstatedir@
|
||||
mandir = @mandir@
|
||||
mkdir_p = @mkdir_p@
|
||||
oldincludedir = @oldincludedir@
|
||||
pdfdir = @pdfdir@
|
||||
prefix = @prefix@
|
||||
program_transform_name = @program_transform_name@
|
||||
psdir = @psdir@
|
||||
sbindir = @sbindir@
|
||||
sharedstatedir = @sharedstatedir@
|
||||
srcdir = @srcdir@
|
||||
sysconfdir = @sysconfdir@
|
||||
target = @target@
|
||||
target_alias = @target_alias@
|
||||
target_cpu = @target_cpu@
|
||||
target_os = @target_os@
|
||||
target_vendor = @target_vendor@
|
||||
top_build_prefix = @top_build_prefix@
|
||||
top_builddir = @top_builddir@
|
||||
top_srcdir = @top_srcdir@
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
DEFAULT_INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include -I$(top_srcdir)/intl
|
||||
noinst_LIBRARIES = libpsx.a
|
||||
libpsx_a_SOURCES = psx.cpp irq.cpp timer.cpp dma.cpp frontio.cpp \
|
||||
sio.cpp cpu.cpp gte.cpp dis.cpp cdc.cpp spu.cpp gpu.cpp \
|
||||
mdec.cpp input/gamepad.cpp input/dualanalog.cpp \
|
||||
input/dualshock.cpp input/memcard.cpp input/multitap.cpp \
|
||||
input/mouse.cpp input/negcon.cpp input/guncon.cpp \
|
||||
input/justifier.cpp $(am__append_1)
|
||||
all: all-am
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .cpp .lo .o .obj
|
||||
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
|
||||
@for dep in $?; do \
|
||||
case '$(am__configure_deps)' in \
|
||||
*$$dep*) \
|
||||
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
|
||||
&& { if test -f $@; then exit 0; else break; fi; }; \
|
||||
exit 1;; \
|
||||
esac; \
|
||||
done; \
|
||||
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/psx/Makefile'; \
|
||||
$(am__cd) $(top_srcdir) && \
|
||||
$(AUTOMAKE) --gnu src/psx/Makefile
|
||||
.PRECIOUS: Makefile
|
||||
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
|
||||
@case '$?' in \
|
||||
*config.status*) \
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
|
||||
*) \
|
||||
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
|
||||
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
|
||||
esac;
|
||||
|
||||
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
|
||||
$(top_srcdir)/configure: $(am__configure_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(am__aclocal_m4_deps):
|
||||
|
||||
clean-noinstLIBRARIES:
|
||||
-test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
|
||||
input/$(am__dirstamp):
|
||||
@$(MKDIR_P) input
|
||||
@: > input/$(am__dirstamp)
|
||||
input/$(DEPDIR)/$(am__dirstamp):
|
||||
@$(MKDIR_P) input/$(DEPDIR)
|
||||
@: > input/$(DEPDIR)/$(am__dirstamp)
|
||||
input/gamepad.$(OBJEXT): input/$(am__dirstamp) \
|
||||
input/$(DEPDIR)/$(am__dirstamp)
|
||||
input/dualanalog.$(OBJEXT): input/$(am__dirstamp) \
|
||||
input/$(DEPDIR)/$(am__dirstamp)
|
||||
input/dualshock.$(OBJEXT): input/$(am__dirstamp) \
|
||||
input/$(DEPDIR)/$(am__dirstamp)
|
||||
input/memcard.$(OBJEXT): input/$(am__dirstamp) \
|
||||
input/$(DEPDIR)/$(am__dirstamp)
|
||||
input/multitap.$(OBJEXT): input/$(am__dirstamp) \
|
||||
input/$(DEPDIR)/$(am__dirstamp)
|
||||
input/mouse.$(OBJEXT): input/$(am__dirstamp) \
|
||||
input/$(DEPDIR)/$(am__dirstamp)
|
||||
input/negcon.$(OBJEXT): input/$(am__dirstamp) \
|
||||
input/$(DEPDIR)/$(am__dirstamp)
|
||||
input/guncon.$(OBJEXT): input/$(am__dirstamp) \
|
||||
input/$(DEPDIR)/$(am__dirstamp)
|
||||
input/justifier.$(OBJEXT): input/$(am__dirstamp) \
|
||||
input/$(DEPDIR)/$(am__dirstamp)
|
||||
libpsx.a: $(libpsx_a_OBJECTS) $(libpsx_a_DEPENDENCIES) $(EXTRA_libpsx_a_DEPENDENCIES)
|
||||
$(AM_V_at)-rm -f libpsx.a
|
||||
$(AM_V_AR)$(libpsx_a_AR) libpsx.a $(libpsx_a_OBJECTS) $(libpsx_a_LIBADD)
|
||||
$(AM_V_at)$(RANLIB) libpsx.a
|
||||
|
||||
mostlyclean-compile:
|
||||
-rm -f *.$(OBJEXT)
|
||||
-rm -f input/dualanalog.$(OBJEXT)
|
||||
-rm -f input/dualshock.$(OBJEXT)
|
||||
-rm -f input/gamepad.$(OBJEXT)
|
||||
-rm -f input/guncon.$(OBJEXT)
|
||||
-rm -f input/justifier.$(OBJEXT)
|
||||
-rm -f input/memcard.$(OBJEXT)
|
||||
-rm -f input/mouse.$(OBJEXT)
|
||||
-rm -f input/multitap.$(OBJEXT)
|
||||
-rm -f input/negcon.$(OBJEXT)
|
||||
|
||||
distclean-compile:
|
||||
-rm -f *.tab.c
|
||||
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cdc.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dis.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dma.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/frontio.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpu.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gte.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/irq.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdec.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psx.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sio.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/spu.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timer.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/dualanalog.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/dualshock.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/gamepad.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/guncon.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/justifier.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/memcard.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/mouse.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/multitap.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/negcon.Po@am__quote@
|
||||
|
||||
.cpp.o:
|
||||
@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
|
||||
@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
|
||||
@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
|
||||
|
||||
.cpp.obj:
|
||||
@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
|
||||
@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
|
||||
@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
|
||||
|
||||
.cpp.lo:
|
||||
@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
|
||||
@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
|
||||
@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
|
||||
|
||||
mostlyclean-libtool:
|
||||
-rm -f *.lo
|
||||
|
||||
clean-libtool:
|
||||
-rm -rf .libs _libs
|
||||
|
||||
ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | \
|
||||
$(AWK) '{ files[$$0] = 1; nonempty = 1; } \
|
||||
END { if (nonempty) { for (i in files) print i; }; }'`; \
|
||||
mkid -fID $$unique
|
||||
tags: TAGS
|
||||
|
||||
TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
|
||||
$(TAGS_FILES) $(LISP)
|
||||
set x; \
|
||||
here=`pwd`; \
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | \
|
||||
$(AWK) '{ files[$$0] = 1; nonempty = 1; } \
|
||||
END { if (nonempty) { for (i in files) print i; }; }'`; \
|
||||
shift; \
|
||||
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
|
||||
test -n "$$unique" || unique=$$empty_fix; \
|
||||
if test $$# -gt 0; then \
|
||||
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
|
||||
"$$@" $$unique; \
|
||||
else \
|
||||
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
|
||||
$$unique; \
|
||||
fi; \
|
||||
fi
|
||||
ctags: CTAGS
|
||||
CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
|
||||
$(TAGS_FILES) $(LISP)
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | \
|
||||
$(AWK) '{ files[$$0] = 1; nonempty = 1; } \
|
||||
END { if (nonempty) { for (i in files) print i; }; }'`; \
|
||||
test -z "$(CTAGS_ARGS)$$unique" \
|
||||
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
|
||||
$$unique
|
||||
|
||||
GTAGS:
|
||||
here=`$(am__cd) $(top_builddir) && pwd` \
|
||||
&& $(am__cd) $(top_srcdir) \
|
||||
&& gtags -i $(GTAGS_ARGS) "$$here"
|
||||
|
||||
distclean-tags:
|
||||
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
|
||||
|
||||
distdir: $(DISTFILES)
|
||||
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
list='$(DISTFILES)'; \
|
||||
dist_files=`for file in $$list; do echo $$file; done | \
|
||||
sed -e "s|^$$srcdirstrip/||;t" \
|
||||
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
|
||||
case $$dist_files in \
|
||||
*/*) $(MKDIR_P) `echo "$$dist_files" | \
|
||||
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
|
||||
sort -u` ;; \
|
||||
esac; \
|
||||
for file in $$dist_files; do \
|
||||
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
|
||||
if test -d $$d/$$file; then \
|
||||
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
|
||||
if test -d "$(distdir)/$$file"; then \
|
||||
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
|
||||
fi; \
|
||||
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
|
||||
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
|
||||
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
|
||||
fi; \
|
||||
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
|
||||
else \
|
||||
test -f "$(distdir)/$$file" \
|
||||
|| cp -p $$d/$$file "$(distdir)/$$file" \
|
||||
|| exit 1; \
|
||||
fi; \
|
||||
done
|
||||
check-am: all-am
|
||||
check: check-am
|
||||
all-am: Makefile $(LIBRARIES)
|
||||
installdirs:
|
||||
install: install-am
|
||||
install-exec: install-exec-am
|
||||
install-data: install-data-am
|
||||
uninstall: uninstall-am
|
||||
|
||||
install-am: all-am
|
||||
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
|
||||
|
||||
installcheck: installcheck-am
|
||||
install-strip:
|
||||
if test -z '$(STRIP)'; then \
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
|
||||
install; \
|
||||
else \
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
|
||||
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
|
||||
fi
|
||||
mostlyclean-generic:
|
||||
|
||||
clean-generic:
|
||||
|
||||
distclean-generic:
|
||||
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
|
||||
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
|
||||
-rm -f input/$(DEPDIR)/$(am__dirstamp)
|
||||
-rm -f input/$(am__dirstamp)
|
||||
|
||||
maintainer-clean-generic:
|
||||
@echo "This command is intended for maintainers to use"
|
||||
@echo "it deletes files that may require special tools to rebuild."
|
||||
clean: clean-am
|
||||
|
||||
clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
|
||||
mostlyclean-am
|
||||
|
||||
distclean: distclean-am
|
||||
-rm -rf ./$(DEPDIR) input/$(DEPDIR)
|
||||
-rm -f Makefile
|
||||
distclean-am: clean-am distclean-compile distclean-generic \
|
||||
distclean-tags
|
||||
|
||||
dvi: dvi-am
|
||||
|
||||
dvi-am:
|
||||
|
||||
html: html-am
|
||||
|
||||
html-am:
|
||||
|
||||
info: info-am
|
||||
|
||||
info-am:
|
||||
|
||||
install-data-am:
|
||||
|
||||
install-dvi: install-dvi-am
|
||||
|
||||
install-dvi-am:
|
||||
|
||||
install-exec-am:
|
||||
|
||||
install-html: install-html-am
|
||||
|
||||
install-html-am:
|
||||
|
||||
install-info: install-info-am
|
||||
|
||||
install-info-am:
|
||||
|
||||
install-man:
|
||||
|
||||
install-pdf: install-pdf-am
|
||||
|
||||
install-pdf-am:
|
||||
|
||||
install-ps: install-ps-am
|
||||
|
||||
install-ps-am:
|
||||
|
||||
installcheck-am:
|
||||
|
||||
maintainer-clean: maintainer-clean-am
|
||||
-rm -rf ./$(DEPDIR) input/$(DEPDIR)
|
||||
-rm -f Makefile
|
||||
maintainer-clean-am: distclean-am maintainer-clean-generic
|
||||
|
||||
mostlyclean: mostlyclean-am
|
||||
|
||||
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
|
||||
mostlyclean-libtool
|
||||
|
||||
pdf: pdf-am
|
||||
|
||||
pdf-am:
|
||||
|
||||
ps: ps-am
|
||||
|
||||
ps-am:
|
||||
|
||||
uninstall-am:
|
||||
|
||||
.MAKE: install-am install-strip
|
||||
|
||||
.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
|
||||
clean-libtool clean-noinstLIBRARIES ctags distclean \
|
||||
distclean-compile distclean-generic distclean-libtool \
|
||||
distclean-tags distdir dvi dvi-am html html-am info info-am \
|
||||
install install-am install-data install-data-am install-dvi \
|
||||
install-dvi-am install-exec install-exec-am install-html \
|
||||
install-html-am install-info install-info-am install-man \
|
||||
install-pdf install-pdf-am install-ps install-ps-am \
|
||||
install-strip installcheck installcheck-am installdirs \
|
||||
maintainer-clean maintainer-clean-generic mostlyclean \
|
||||
mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
|
||||
pdf pdf-am ps ps-am tags uninstall uninstall-am
|
||||
|
||||
|
||||
# Tell versions [3.59,3.63) of GNU make to not export all variables.
|
||||
# Otherwise a system limit (for SysV at least) may be exceeded.
|
||||
.NOEXPORT:
|
|
@ -0,0 +1,336 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/*
|
||||
TODO:
|
||||
Time string parsing convenience functions.
|
||||
|
||||
Character set autodetect heuristics and conversion for when the "utf8" tag is missing.
|
||||
*/
|
||||
#include "mednafen.h"
|
||||
#include "PSFLoader.h"
|
||||
#include "endian.h"
|
||||
#include "general.h"
|
||||
#include "string/trim.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <trio/trio.h>
|
||||
#include <ctype.h>
|
||||
//#include <iconv.h>
|
||||
|
||||
|
||||
PSFTags::PSFTags()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
PSFTags::~PSFTags()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PSFTags::AddTag(char *tag_line)
|
||||
{
|
||||
char *eq;
|
||||
|
||||
// Transform 0x01-0x1F -> 0x20
|
||||
for(unsigned int i = 0; i < strlen(tag_line); i++)
|
||||
if((unsigned char)tag_line[i] < 0x20)
|
||||
tag_line[i] = 0x20;
|
||||
|
||||
eq = strchr(tag_line, '=');
|
||||
|
||||
if(eq)
|
||||
{
|
||||
*eq = 0;
|
||||
|
||||
MDFN_trim(tag_line);
|
||||
MDFN_trim(eq + 1);
|
||||
|
||||
for(unsigned int i = 0; i < strlen(tag_line); i++)
|
||||
tag_line[i] = tolower(tag_line[i]);
|
||||
|
||||
if(TagExists(tag_line))
|
||||
tags[tag_line] = tags[std::string(tag_line)] + std::string(1, '\n') + std::string(eq + 1);
|
||||
else
|
||||
tags[tag_line] = std::string(eq + 1);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
static const char *DetectCharset(const uint8 *data, const uint32 data_size)
|
||||
{
|
||||
static const char *TestCharsets[] = { "UTF-8", /*"SJIS",*/ "WINDOWS-1252" };
|
||||
|
||||
for(unsigned int i = 0; i < sizeof(TestCharsets) / sizeof(TestCharsets[0]); i++)
|
||||
{
|
||||
iconv_t cd;
|
||||
|
||||
cd = iconv_open("UTF-32", TestCharsets[i]);
|
||||
if(cd != (iconv_t)-1)
|
||||
{
|
||||
size_t in_len = data_size;
|
||||
size_t out_len = data_size * 4 + 4;
|
||||
char *in_ptr = (char *)data;
|
||||
char *const out_ptr_mem = new char[out_len];
|
||||
char *out_ptr = out_ptr_mem;
|
||||
|
||||
if(iconv(cd, (ICONV_CONST char **)&in_ptr, &in_len, &out_ptr, &out_len) != (size_t)-1)
|
||||
{
|
||||
delete[] out_ptr_mem;
|
||||
return(TestCharsets[i]);
|
||||
}
|
||||
delete[] out_ptr_mem;
|
||||
}
|
||||
}
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
void PSFTags::LoadTags(const uint8 *data_in, uint32 size)
|
||||
{
|
||||
std::vector<char> tags_heap;
|
||||
char *data;
|
||||
char *spos;
|
||||
//const char *detected_charset = DetectCharset(data_in, size);
|
||||
|
||||
tags_heap.resize(size + 1);
|
||||
tags_heap[size] = 0;
|
||||
|
||||
memcpy(&tags_heap[0], data_in, size);
|
||||
|
||||
data = &tags_heap[0];
|
||||
spos = data;
|
||||
|
||||
while(size)
|
||||
{
|
||||
if(*data == 0x0A || *data == 0x00)
|
||||
{
|
||||
*data = 0;
|
||||
|
||||
if(data - spos)
|
||||
{
|
||||
if(*(data - 1) == 0xD) // handle \r
|
||||
*(data - 1) = 0;
|
||||
|
||||
AddTag(spos);
|
||||
}
|
||||
|
||||
spos = data + 1; // Skip \n for next tag
|
||||
}
|
||||
|
||||
size--;
|
||||
data++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int64 PSFTags::GetTagI(const char *name)
|
||||
{
|
||||
std::map<std::string, std::string>::iterator it;
|
||||
|
||||
it = tags.find(name);
|
||||
if(it != tags.end())
|
||||
{
|
||||
long long ret = 0;
|
||||
std::string &tmp = tags[name];
|
||||
|
||||
trio_sscanf(tmp.c_str(), "%lld", &ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
return(0); // INT64_MIN
|
||||
}
|
||||
|
||||
bool PSFTags::TagExists(const char *name)
|
||||
{
|
||||
if(tags.find(name) != tags.end())
|
||||
return(true);
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
||||
std::string PSFTags::GetTag(const char *name)
|
||||
{
|
||||
std::map<std::string, std::string>::iterator it;
|
||||
|
||||
it = tags.find(name);
|
||||
|
||||
if(it != tags.end())
|
||||
return(it->second);
|
||||
|
||||
return("");
|
||||
}
|
||||
|
||||
void PSFTags::EraseTag(const char *name)
|
||||
{
|
||||
std::map<std::string, std::string>::iterator it;
|
||||
|
||||
it = tags.find(name);
|
||||
if(it != tags.end())
|
||||
tags.erase(it);
|
||||
}
|
||||
|
||||
PSFLoader::PSFLoader()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
PSFLoader::~PSFLoader()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
bool PSFLoader::TestMagic(uint8 version, MDFNFILE *fp)
|
||||
{
|
||||
if(fp->size < (3 + 1 + 4 + 4 + 4))
|
||||
return(false);
|
||||
|
||||
if(memcmp(fp->data, "PSF", 3))
|
||||
return(false);
|
||||
|
||||
if(fp->data[3] != version)
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
PSFTags PSFLoader::LoadInternal(uint8 version, uint32 max_exe_size, MDFNFILE *fp, uint32 level, bool force_ignore_pcsp)
|
||||
{
|
||||
uint32 reserved_size, compressed_size, compressed_crc32;
|
||||
bool _lib_present = false;
|
||||
PSFTags tags;
|
||||
|
||||
std::vector<uint8> decompress_buffer;
|
||||
uLongf decompress_len;
|
||||
|
||||
if(!TestMagic(version, fp))
|
||||
throw(MDFN_Error(0, _("Not a PSF(version=0x%02x) file!"), version));
|
||||
|
||||
reserved_size = MDFN_de32lsb(fp->data + 4);
|
||||
compressed_size = MDFN_de32lsb(fp->data + 8);
|
||||
compressed_crc32 = MDFN_de32lsb(fp->data + 12);
|
||||
|
||||
if(fp->size < ((int64)16 + reserved_size + compressed_size))
|
||||
throw MDFN_Error(0, _("PSF is missing at least %lld bytes of data!"), (long long)((int64)16 + reserved_size + compressed_size - fp->size));
|
||||
|
||||
if(crc32(0, fp->data + 16 + reserved_size, compressed_size) != compressed_crc32)
|
||||
throw MDFN_Error(0, _("PSF compressed CRC32 mismatch(data is corrupt)!"));
|
||||
|
||||
|
||||
{
|
||||
const uint8 *tag_section = fp->data + 16 + reserved_size + compressed_size;
|
||||
uint32 tag_section_size = fp->size - 16 - reserved_size - compressed_size;
|
||||
|
||||
if(tag_section_size > 5 && !memcmp(tag_section, "[TAG]", 5))
|
||||
tags.LoadTags(tag_section + 5, tag_section_size - 5);
|
||||
}
|
||||
|
||||
//
|
||||
// Handle minipsf simple _lib
|
||||
//
|
||||
|
||||
if(level < 15)
|
||||
{
|
||||
if(tags.TagExists("_lib"))
|
||||
{
|
||||
std::string tp = tags.GetTag("_lib");
|
||||
|
||||
if(!MDFN_IsFIROPSafe(tp))
|
||||
{
|
||||
throw(MDFN_Error(0, _("Referenced path \"%s\" is potentially unsafe. See \"filesys.untrusted_fip_check\" setting."), tp.c_str()));
|
||||
}
|
||||
|
||||
MDFNFILE subfile(MDFN_MakeFName(MDFNMKF_AUX, 0, tp.c_str()).c_str(), NULL, NULL);
|
||||
|
||||
LoadInternal(version, max_exe_size, &subfile, level + 1);
|
||||
|
||||
_lib_present = true;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
decompress_buffer.resize(max_exe_size);
|
||||
decompress_len = max_exe_size;
|
||||
switch( uncompress((Bytef *)&decompress_buffer[0], &decompress_len, (const Bytef *)(fp->data + 16 + reserved_size), compressed_size) )
|
||||
{
|
||||
default:
|
||||
throw(MDFN_Error(0, "zlib unknown error"));
|
||||
|
||||
case Z_OK: break;
|
||||
|
||||
case Z_MEM_ERROR:
|
||||
throw(MDFN_Error(0, "zlib Z_MEM_ERROR"));
|
||||
|
||||
case Z_BUF_ERROR:
|
||||
throw(MDFN_Error(0, _("PSF decompressed size exceeds maximum allowed!")));
|
||||
|
||||
case Z_DATA_ERROR:
|
||||
throw(MDFN_Error(0, _("PSF compressed data is bad.")));
|
||||
}
|
||||
|
||||
HandleReserved(fp->data + 16, reserved_size);
|
||||
HandleEXE(&decompress_buffer[0], decompress_len, force_ignore_pcsp | _lib_present);
|
||||
decompress_buffer.resize(0);
|
||||
|
||||
//
|
||||
// handle libN
|
||||
//
|
||||
if(level < 15)
|
||||
{
|
||||
for(unsigned int n = 2; n <= INT_MAX; n++)
|
||||
{
|
||||
char tmpbuf[32];
|
||||
|
||||
trio_snprintf(tmpbuf, 32, "_lib%d", (int)n);
|
||||
|
||||
if(tags.TagExists(tmpbuf))
|
||||
{
|
||||
MDFNFILE subfile(MDFN_MakeFName(MDFNMKF_AUX, 0, tags.GetTag(tmpbuf).c_str()).c_str(), NULL, NULL);
|
||||
|
||||
LoadInternal(version, max_exe_size, &subfile, level + 1, true);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(tags);
|
||||
}
|
||||
|
||||
PSFTags PSFLoader::Load(uint8 version, uint32 max_exe_size, MDFNFILE *fp)
|
||||
{
|
||||
return(LoadInternal(version, max_exe_size, fp, 0, false));
|
||||
}
|
||||
|
||||
void PSFLoader::HandleReserved(const uint8 *data, uint32 len)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PSFLoader::HandleEXE(const uint8 *data, uint32 len, bool ignore_pcsp)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
#ifndef __MDFN_PSFLOADER_H
|
||||
#define __MDFN_PSFLOADER_H
|
||||
|
||||
#include <map>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class PSFTags
|
||||
{
|
||||
public:
|
||||
|
||||
PSFTags();
|
||||
~PSFTags();
|
||||
|
||||
int64 GetTagI(const char *name);
|
||||
std::string GetTag(const char *name);
|
||||
bool TagExists(const char *name);
|
||||
|
||||
void LoadTags(const uint8 *data, uint32 size);
|
||||
void EraseTag(const char *name);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void AddTag(char *tag_line);
|
||||
std::map<std::string, std::string> tags;
|
||||
};
|
||||
|
||||
class PSFLoader
|
||||
{
|
||||
public:
|
||||
PSFLoader();
|
||||
~PSFLoader();
|
||||
|
||||
static bool TestMagic(uint8 version, MDFNFILE *fp);
|
||||
|
||||
PSFTags Load(uint8 version, uint32 max_exe_size, MDFNFILE *fp);
|
||||
|
||||
virtual void HandleReserved(const uint8 *data, uint32 len);
|
||||
virtual void HandleEXE(const uint8 *data, uint32 len, bool ignore_pcsp = false);
|
||||
|
||||
private:
|
||||
|
||||
PSFTags LoadInternal(uint8 version, uint32 max_exe_size, MDFNFILE *fp, uint32 level, bool force_ignore_pcsp = false);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,280 @@
|
|||
#ifndef __MDFN_PSX_CDC_H
|
||||
#define __MDFN_PSX_CDC_H
|
||||
|
||||
#include "cdrom/CDUtility.h"
|
||||
|
||||
class ShockDiscRef;
|
||||
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
struct CD_Audio_Buffer
|
||||
{
|
||||
int16 Samples[2][0x1000]; // [0][...] = l, [1][...] = r
|
||||
int32 Size;
|
||||
uint32 Freq;
|
||||
int32 ReadPos;
|
||||
};
|
||||
|
||||
|
||||
class PS_CDC
|
||||
{
|
||||
public:
|
||||
|
||||
PS_CDC();
|
||||
~PS_CDC();
|
||||
|
||||
void SetDisc(bool tray_open, ShockDiscRef *disc, const char disc_id[4]);
|
||||
|
||||
void Power(void);
|
||||
int StateAction(StateMem *sm, int load, int data_only);
|
||||
void ResetTS(void);
|
||||
|
||||
int32 CalcNextEvent(void); // Returns in master cycles to next event.
|
||||
|
||||
pscpu_timestamp_t Update(const pscpu_timestamp_t timestamp);
|
||||
|
||||
void Write(const pscpu_timestamp_t timestamp, uint32 A, uint8 V);
|
||||
uint8 Read(const pscpu_timestamp_t timestamp, uint32 A);
|
||||
|
||||
bool DMACanRead(void);
|
||||
uint32 DMARead(void);
|
||||
void SoftReset(void);
|
||||
|
||||
void GetCDAudio(int32 samples[2]);
|
||||
|
||||
private:
|
||||
CDIF *Cur_CDIF;
|
||||
ShockDiscRef* Cur_disc;
|
||||
bool DiscChanged;
|
||||
int32 DiscStartupDelay;
|
||||
|
||||
CD_Audio_Buffer AudioBuffer;
|
||||
|
||||
uint8 Pending_DecodeVolume[2][2], DecodeVolume[2][2]; // [data_source][output_port]
|
||||
|
||||
int16 ADPCM_ResampBuf[2][32 * 2];
|
||||
uint8 ADPCM_ResampCurPos;
|
||||
uint8 ADPCM_ResampCurPhase;
|
||||
|
||||
void ApplyVolume(int32 samples[2]);
|
||||
void ReadAudioBuffer(int32 samples[2]);
|
||||
|
||||
void ClearAudioBuffers(void);
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
uint8 RegSelector;
|
||||
uint8 ArgsBuf[16];
|
||||
uint8 ArgsWP; // 5-bit(0 ... 31)
|
||||
uint8 ArgsRP; // 5-bit(0 ... 31)
|
||||
|
||||
uint8 ArgsReceiveLatch;
|
||||
uint8 ArgsReceiveBuf[32];
|
||||
uint8 ArgsReceiveIn;
|
||||
|
||||
uint8 ResultsBuffer[16];
|
||||
uint8 ResultsIn; // 5-bit(0 ... 31)
|
||||
uint8 ResultsWP; // Write position, 4 bit(0 ... 15).
|
||||
uint8 ResultsRP; // Read position, 4 bit(0 ... 15).
|
||||
|
||||
SimpleFIFO<uint8> DMABuffer;
|
||||
uint8 SB[2340];
|
||||
uint32 SB_In;
|
||||
|
||||
enum { SectorPipe_Count = 2 };
|
||||
uint8 SectorPipe[SectorPipe_Count][2352];
|
||||
uint8 SectorPipe_Pos;
|
||||
uint8 SectorPipe_In;
|
||||
|
||||
uint8 SubQBuf[0xC];
|
||||
uint8 SubQBuf_Safe[0xC];
|
||||
bool SubQChecksumOK;
|
||||
|
||||
bool HeaderBufValid;
|
||||
uint8 HeaderBuf[12];
|
||||
|
||||
void RecalcIRQ(void);
|
||||
enum
|
||||
{
|
||||
CDCIRQ_NONE = 0,
|
||||
CDCIRQ_DATA_READY = 1,
|
||||
CDCIRQ_COMPLETE = 2,
|
||||
CDCIRQ_ACKNOWLEDGE = 3,
|
||||
CDCIRQ_DATA_END = 4,
|
||||
CDCIRQ_DISC_ERROR = 5
|
||||
};
|
||||
|
||||
// Names are just guessed for these based on what conditions cause them:
|
||||
enum
|
||||
{
|
||||
ERRCODE_BAD_ARGVAL = 0x10,
|
||||
ERRCODE_BAD_NUMARGS = 0x20,
|
||||
ERRCODE_BAD_COMMAND = 0x40,
|
||||
ERRCODE_NOT_READY = 0x80, // 0x80 (happens with getlocl when drive isn't reading, pause when tray is open, and MAYBE when trying to run an async
|
||||
// command while another async command is currently in its asynch phase being executed[pause when in readtoc, todo test more])
|
||||
};
|
||||
|
||||
uint8 IRQBuffer;
|
||||
uint8 IRQOutTestMask;
|
||||
int32 CDCReadyReceiveCounter; // IRQBuffer being non-zero prevents new results and new IRQ from coming in and erasing the current results,
|
||||
// but apparently at least one CONFOUNDED game is clearing the IRQ state BEFORE reading the results, so we need to have a delay
|
||||
// between IRQBuffer being cleared to when we allow new results to come in. (The real thing should be like this too,
|
||||
// but the mechanism is probably more nuanced and complex and ugly and I like anchovy pizza)
|
||||
|
||||
void BeginResults(void);
|
||||
void WriteIRQ(uint8);
|
||||
void WriteResult(uint8);
|
||||
uint8 ReadResult(void);
|
||||
|
||||
uint8 FilterFile;
|
||||
uint8 FilterChan;
|
||||
|
||||
|
||||
uint8 PendingCommand;
|
||||
int PendingCommandPhase;
|
||||
int32 PendingCommandCounter;
|
||||
|
||||
int32 SPUCounter;
|
||||
|
||||
enum { MODE_SPEED = 0x80 };
|
||||
enum { MODE_STRSND = 0x40 };
|
||||
enum { MODE_SIZE = 0x20 };
|
||||
enum { MODE_SIZE2 = 0x10 };
|
||||
enum { MODE_SF = 0x08 };
|
||||
enum { MODE_REPORT = 0x04 };
|
||||
enum { MODE_AUTOPAUSE = 0x02 };
|
||||
enum { MODE_CDDA = 0x01 };
|
||||
uint8 Mode;
|
||||
|
||||
enum
|
||||
{
|
||||
DS_STANDBY = -2,
|
||||
DS_PAUSED = -1,
|
||||
DS_STOPPED = 0,
|
||||
DS_SEEKING,
|
||||
DS_SEEKING_LOGICAL,
|
||||
DS_PLAY_SEEKING,
|
||||
DS_PLAYING,
|
||||
DS_READING,
|
||||
DS_RESETTING
|
||||
};
|
||||
int DriveStatus;
|
||||
int StatusAfterSeek;
|
||||
bool Forward;
|
||||
bool Backward;
|
||||
bool Muted;
|
||||
|
||||
int32 PlayTrackMatch;
|
||||
|
||||
int32 PSRCounter;
|
||||
|
||||
int32 CurSector;
|
||||
|
||||
unsigned AsyncIRQPending;
|
||||
uint8 AsyncResultsPending[16];
|
||||
uint8 AsyncResultsPendingCount;
|
||||
|
||||
int32 CalcSeekTime(int32 initial, int32 target, bool motor_on, bool paused);
|
||||
|
||||
void ClearAIP(void);
|
||||
void CheckAIP(void);
|
||||
void SetAIP(unsigned irq, unsigned result_count, uint8 *r);
|
||||
void SetAIP(unsigned irq, uint8 result0);
|
||||
void SetAIP(unsigned irq, uint8 result0, uint8 result1);
|
||||
|
||||
int32 SeekTarget;
|
||||
|
||||
pscpu_timestamp_t lastts;
|
||||
|
||||
CDUtility::TOC toc;
|
||||
bool IsPSXDisc;
|
||||
uint8 DiscID[4];
|
||||
|
||||
int32 CommandLoc;
|
||||
bool CommandLoc_Dirty;
|
||||
|
||||
uint8 MakeStatus(bool cmd_error = false);
|
||||
bool DecodeSubQ(uint8 *subpw);
|
||||
bool CommandCheckDiscPresent(void);
|
||||
|
||||
void EnbufferizeCDDASector(const uint8 *buf);
|
||||
bool XA_Test(const uint8 *sdata);
|
||||
void XA_ProcessSector(const uint8 *sdata, CD_Audio_Buffer *ab);
|
||||
int16 xa_previous[2][2];
|
||||
bool xa_cur_set;
|
||||
uint8 xa_cur_file;
|
||||
uint8 xa_cur_chan;
|
||||
|
||||
uint8 ReportLastF;
|
||||
|
||||
void HandlePlayRead(void);
|
||||
|
||||
struct CDC_CTEntry
|
||||
{
|
||||
uint8 args_min;
|
||||
uint8 args_max;
|
||||
const char *name;
|
||||
int32 (PS_CDC::*func)(const int arg_count, const uint8 *args);
|
||||
int32 (PS_CDC::*func2)(void);
|
||||
};
|
||||
|
||||
void BeginSeek(uint32 target);
|
||||
void PreSeekHack(bool logical, uint32 target);
|
||||
void ReadBase(void);
|
||||
|
||||
static CDC_CTEntry Commands[0x20];
|
||||
|
||||
int32 Command_Sync(const int arg_count, const uint8 *args);
|
||||
int32 Command_Nop(const int arg_count, const uint8 *args);
|
||||
int32 Command_Setloc(const int arg_count, const uint8 *args);
|
||||
int32 Command_Play(const int arg_count, const uint8 *args);
|
||||
int32 Command_Forward(const int arg_count, const uint8 *args);
|
||||
int32 Command_Backward(const int arg_count, const uint8 *args);
|
||||
int32 Command_ReadN(const int arg_count, const uint8 *args);
|
||||
int32 Command_Standby(const int arg_count, const uint8 *args);
|
||||
int32 Command_Standby_Part2(void);
|
||||
int32 Command_Stop(const int arg_count, const uint8 *args);
|
||||
int32 Command_Stop_Part2(void);
|
||||
int32 Command_Pause(const int arg_count, const uint8 *args);
|
||||
int32 Command_Pause_Part2(void);
|
||||
int32 Command_Reset(const int arg_count, const uint8 *args);
|
||||
int32 Command_Mute(const int arg_count, const uint8 *args);
|
||||
int32 Command_Demute(const int arg_count, const uint8 *args);
|
||||
int32 Command_Setfilter(const int arg_count, const uint8 *args);
|
||||
int32 Command_Setmode(const int arg_count, const uint8 *args);
|
||||
int32 Command_Getparam(const int arg_count, const uint8 *args);
|
||||
int32 Command_GetlocL(const int arg_count, const uint8 *args);
|
||||
int32 Command_GetlocP(const int arg_count, const uint8 *args);
|
||||
|
||||
int32 Command_ReadT(const int arg_count, const uint8 *args);
|
||||
int32 Command_ReadT_Part2(void);
|
||||
|
||||
int32 Command_GetTN(const int arg_count, const uint8 *args);
|
||||
int32 Command_GetTD(const int arg_count, const uint8 *args);
|
||||
int32 Command_SeekL(const int arg_count, const uint8 *args);
|
||||
int32 Command_SeekP(const int arg_count, const uint8 *args);
|
||||
int32 Command_Seek_PartN(void);
|
||||
|
||||
int32 Command_Test(const int arg_count, const uint8 *args);
|
||||
|
||||
int32 Command_ID(const int arg_count, const uint8 *args);
|
||||
int32 Command_ID_Part2(void);
|
||||
|
||||
int32 Command_ReadS(const int arg_count, const uint8 *args);
|
||||
int32 Command_Init(const int arg_count, const uint8 *args);
|
||||
|
||||
int32 Command_ReadTOC(const int arg_count, const uint8 *args);
|
||||
int32 Command_ReadTOC_Part2(void);
|
||||
|
||||
int32 Command_0x1d(const int arg_count, const uint8 *args);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,954 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "psx.h"
|
||||
#include "cpu.h"
|
||||
|
||||
/* TODO
|
||||
Make sure load delays are correct.
|
||||
|
||||
Consider preventing IRQs being taken while in a branch delay slot, to prevent potential problems with games that like to be too clever and perform
|
||||
un-restartable sequences of instructions.
|
||||
*/
|
||||
|
||||
#define BIU_ENABLE_ICACHE_S1 0x00000800 // Enable I-cache, set 1
|
||||
#define BIU_ENABLE_DCACHE 0x00000080 // Enable D-cache
|
||||
#define BIU_TAG_TEST_MODE 0x00000004 // Enable TAG test mode(IsC must be set to 1 as well presumably?)
|
||||
#define BIU_INVALIDATE_MODE 0x00000002 // Enable Invalidate mode(IsC must be set to 1 as well presumably?)
|
||||
#define BIU_LOCK 0x00000001 // Enable Lock mode(IsC must be set to 1 as well presumably?)
|
||||
// Does lock mode prevent the actual data payload from being modified, while allowing tags to be modified/updated???
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
|
||||
PS_CPU::PS_CPU()
|
||||
{
|
||||
Halted = false;
|
||||
|
||||
memset(FastMap, 0, sizeof(FastMap));
|
||||
memset(DummyPage, 0xFF, sizeof(DummyPage)); // 0xFF to trigger an illegal instruction exception, so we'll know what's up when debugging.
|
||||
|
||||
for(uint64 a = 0x00000000; a < (1ULL << 32); a += FAST_MAP_PSIZE)
|
||||
SetFastMap(DummyPage, a, FAST_MAP_PSIZE);
|
||||
|
||||
CPUHook = NULL;
|
||||
ADDBT = NULL;
|
||||
}
|
||||
|
||||
PS_CPU::~PS_CPU()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
void PS_CPU::SetFastMap(void *region_mem, uint32_t region_address, uint32_t region_size)
|
||||
{
|
||||
uint64_t A;
|
||||
// FAST_MAP_SHIFT
|
||||
// FAST_MAP_PSIZE
|
||||
|
||||
for(A = region_address; A < (uint64)region_address + region_size; A += FAST_MAP_PSIZE)
|
||||
FastMap[A >> FAST_MAP_SHIFT] = ((uint8_t *)region_mem - region_address);
|
||||
}
|
||||
|
||||
INLINE void PS_CPU::RecalcIPCache(void)
|
||||
{
|
||||
IPCache = 0;
|
||||
|
||||
if(((CP0.SR & CP0.CAUSE & 0xFF00) && (CP0.SR & 1)) || Halted)
|
||||
IPCache = 0x80;
|
||||
}
|
||||
|
||||
void PS_CPU::SetHalt(bool status)
|
||||
{
|
||||
Halted = status;
|
||||
RecalcIPCache();
|
||||
}
|
||||
|
||||
void PS_CPU::Power(void)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
assert(sizeof(ICache) == sizeof(ICache_Bulk));
|
||||
|
||||
memset(GPR, 0, sizeof(GPR));
|
||||
memset(&CP0, 0, sizeof(CP0));
|
||||
LO = 0;
|
||||
HI = 0;
|
||||
|
||||
gte_ts_done = 0;
|
||||
muldiv_ts_done = 0;
|
||||
|
||||
BACKED_PC = 0xBFC00000;
|
||||
BACKED_new_PC = 4;
|
||||
BACKED_new_PC_mask = ~0U;
|
||||
|
||||
BACKED_LDWhich = 0x20;
|
||||
BACKED_LDValue = 0;
|
||||
LDAbsorb = 0;
|
||||
memset(ReadAbsorb, 0, sizeof(ReadAbsorb));
|
||||
ReadAbsorbWhich = 0;
|
||||
ReadFudge = 0;
|
||||
|
||||
//WriteAbsorb = 0;
|
||||
//WriteAbsorbCount = 0;
|
||||
//WriteAbsorbMonkey = 0;
|
||||
|
||||
CP0.SR |= (1 << 22); // BEV
|
||||
CP0.SR |= (1 << 21); // TS
|
||||
|
||||
CP0.PRID = 0x2;
|
||||
|
||||
RecalcIPCache();
|
||||
|
||||
|
||||
BIU = 0;
|
||||
|
||||
memset(ScratchRAM.data8, 0, 1024);
|
||||
|
||||
// Not quite sure about these poweron/reset values:
|
||||
for(i = 0; i < 1024; i++)
|
||||
{
|
||||
ICache[i].TV = 0x2 | ((BIU & 0x800) ? 0x0 : 0x1);
|
||||
ICache[i].Data = 0;
|
||||
}
|
||||
|
||||
GTE_Power();
|
||||
}
|
||||
|
||||
int PS_CPU::StateAction(StateMem *sm, int load, int data_only)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFARRAY32(GPR, 32),
|
||||
SFVAR(LO),
|
||||
SFVAR(HI),
|
||||
SFVAR(BACKED_PC),
|
||||
SFVAR(BACKED_new_PC),
|
||||
SFVAR(BACKED_new_PC_mask),
|
||||
|
||||
SFVAR(IPCache),
|
||||
SFVAR(Halted),
|
||||
|
||||
SFVAR(BACKED_LDWhich),
|
||||
SFVAR(BACKED_LDValue),
|
||||
SFVAR(LDAbsorb),
|
||||
|
||||
SFVAR(next_event_ts),
|
||||
SFVAR(gte_ts_done),
|
||||
SFVAR(muldiv_ts_done),
|
||||
|
||||
SFVAR(BIU),
|
||||
SFARRAY32(ICache_Bulk, 2048),
|
||||
|
||||
SFARRAY32(CP0.Regs, 32),
|
||||
|
||||
SFARRAY(ReadAbsorb, 0x20),
|
||||
SFVAR(ReadAbsorbDummy),
|
||||
SFVAR(ReadAbsorbWhich),
|
||||
SFVAR(ReadFudge),
|
||||
|
||||
SFARRAY(ScratchRAM.data8, 1024),
|
||||
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "CPU");
|
||||
|
||||
ret &= GTE_StateAction(sm, load, data_only);
|
||||
|
||||
if(load)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void PS_CPU::AssertIRQ(int which, bool asserted)
|
||||
{
|
||||
assert(which >= 0 && which <= 5);
|
||||
|
||||
CP0.CAUSE &= ~(1 << (10 + which));
|
||||
|
||||
if(asserted)
|
||||
CP0.CAUSE |= 1 << (10 + which);
|
||||
|
||||
RecalcIPCache();
|
||||
}
|
||||
|
||||
void PS_CPU::SetBIU(uint32_t val)
|
||||
{
|
||||
unsigned i;
|
||||
const uint32_t old_BIU = BIU;
|
||||
|
||||
BIU = val & ~(0x440);
|
||||
|
||||
if((BIU ^ old_BIU) & 0x800)
|
||||
{
|
||||
if(BIU & 0x800) // ICache enabled
|
||||
{
|
||||
for(i = 0; i < 1024; i++)
|
||||
ICache[i].TV &= ~0x1;
|
||||
}
|
||||
else // ICache disabled
|
||||
{
|
||||
for(i = 0; i < 1024; i++)
|
||||
ICache[i].TV |= 0x1;
|
||||
}
|
||||
}
|
||||
|
||||
PSX_DBG(PSX_DBG_SPARSE, "[CPU] Set BIU=0x%08x\n", BIU);
|
||||
}
|
||||
|
||||
uint32_t PS_CPU::GetBIU(void)
|
||||
{
|
||||
return BIU;
|
||||
}
|
||||
|
||||
static const uint32_t addr_mask[8] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
|
||||
0x7FFFFFFF, 0x1FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
|
||||
|
||||
template<typename T>
|
||||
INLINE T PS_CPU::PeekMemory(uint32_t address)
|
||||
{
|
||||
T ret;
|
||||
address &= addr_mask[address >> 29];
|
||||
|
||||
if(address >= 0x1F800000 && address <= 0x1F8003FF)
|
||||
return ScratchRAM.Read<T>(address & 0x3FF);
|
||||
|
||||
//assert(!(CP0.SR & 0x10000));
|
||||
|
||||
if(sizeof(T) == 1)
|
||||
ret = PSX_MemPeek8(address);
|
||||
else if(sizeof(T) == 2)
|
||||
ret = PSX_MemPeek16(address);
|
||||
else
|
||||
ret = PSX_MemPeek32(address);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE T PS_CPU::ReadMemory(pscpu_timestamp_t ×tamp, uint32_t address, bool DS24, bool LWC_timing)
|
||||
{
|
||||
T ret;
|
||||
|
||||
//WriteAbsorb >>= WriteAbsorbMonkey * 8;
|
||||
//WriteAbsorbCount -= WriteAbsorbMonkey;
|
||||
//WriteAbsorbMonkey = WriteAbsorbCount;
|
||||
|
||||
ReadAbsorb[ReadAbsorbWhich] = 0;
|
||||
ReadAbsorbWhich = 0;
|
||||
|
||||
address &= addr_mask[address >> 29];
|
||||
|
||||
if(address >= 0x1F800000 && address <= 0x1F8003FF)
|
||||
{
|
||||
LDAbsorb = 0;
|
||||
|
||||
if(DS24)
|
||||
return ScratchRAM.ReadU24(address & 0x3FF);
|
||||
else
|
||||
return ScratchRAM.Read<T>(address & 0x3FF);
|
||||
}
|
||||
|
||||
timestamp += (ReadFudge >> 4) & 2;
|
||||
|
||||
//assert(!(CP0.SR & 0x10000));
|
||||
|
||||
pscpu_timestamp_t lts = timestamp;
|
||||
|
||||
if(sizeof(T) == 1)
|
||||
ret = PSX_MemRead8(lts, address);
|
||||
else if(sizeof(T) == 2)
|
||||
ret = PSX_MemRead16(lts, address);
|
||||
else
|
||||
{
|
||||
if(DS24)
|
||||
ret = PSX_MemRead24(lts, address) & 0xFFFFFF;
|
||||
else
|
||||
ret = PSX_MemRead32(lts, address);
|
||||
}
|
||||
|
||||
if(LWC_timing)
|
||||
lts += 1;
|
||||
else
|
||||
lts += 2;
|
||||
|
||||
LDAbsorb = (lts - timestamp);
|
||||
timestamp = lts;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE void PS_CPU::WriteMemory(pscpu_timestamp_t ×tamp, uint32_t address, uint32_t value, bool DS24)
|
||||
{
|
||||
if(MDFN_LIKELY(!(CP0.SR & 0x10000)))
|
||||
{
|
||||
address &= addr_mask[address >> 29];
|
||||
|
||||
if(address >= 0x1F800000 && address <= 0x1F8003FF)
|
||||
{
|
||||
if(DS24)
|
||||
ScratchRAM.WriteU24(address & 0x3FF, value);
|
||||
else
|
||||
ScratchRAM.Write<T>(address & 0x3FF, value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//if(WriteAbsorbCount == 4)
|
||||
//{
|
||||
// WriteAbsorb >>= 8;
|
||||
// WriteAbsorbCount--;
|
||||
//
|
||||
// if(WriteAbsorbMonkey)
|
||||
// WriteAbsorbMonkey--;
|
||||
//}
|
||||
//timestamp += 3;
|
||||
//WriteAbsorb |= (3U << (WriteAbsorbCount * 8));
|
||||
//WriteAbsorbCount++;
|
||||
|
||||
if(sizeof(T) == 1)
|
||||
PSX_MemWrite8(timestamp, address, value);
|
||||
else if(sizeof(T) == 2)
|
||||
PSX_MemWrite16(timestamp, address, value);
|
||||
else
|
||||
{
|
||||
if(DS24)
|
||||
PSX_MemWrite24(timestamp, address, value);
|
||||
else
|
||||
PSX_MemWrite32(timestamp, address, value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(BIU & 0x800) // Instruction cache is enabled/active
|
||||
{
|
||||
if(BIU & 0x4) // TAG test mode.
|
||||
{
|
||||
// TODO: Respect written value.
|
||||
__ICache *ICI = &ICache[((address & 0xFF0) >> 2)];
|
||||
const uint8_t valid_bits = 0x00;
|
||||
|
||||
ICI[0].TV = ((valid_bits & 0x01) ? 0x00 : 0x02) | ((BIU & 0x800) ? 0x0 : 0x1);
|
||||
ICI[1].TV = ((valid_bits & 0x02) ? 0x00 : 0x02) | ((BIU & 0x800) ? 0x0 : 0x1);
|
||||
ICI[2].TV = ((valid_bits & 0x04) ? 0x00 : 0x02) | ((BIU & 0x800) ? 0x0 : 0x1);
|
||||
ICI[3].TV = ((valid_bits & 0x08) ? 0x00 : 0x02) | ((BIU & 0x800) ? 0x0 : 0x1);
|
||||
}
|
||||
else if(!(BIU & 0x1))
|
||||
{
|
||||
ICache[(address & 0xFFC) >> 2].Data = value << ((address & 0x3) * 8);
|
||||
}
|
||||
}
|
||||
|
||||
if((BIU & 0x081) == 0x080) // Writes to the scratchpad(TODO test)
|
||||
{
|
||||
if(DS24)
|
||||
ScratchRAM.WriteU24(address & 0x3FF, value);
|
||||
else
|
||||
ScratchRAM.Write<T>(address & 0x3FF, value);
|
||||
}
|
||||
//printf("IsC WRITE%d 0x%08x 0x%08x -- CP0.SR=0x%08x\n", (int)sizeof(T), address, value, CP0.SR);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t PS_CPU::Exception(uint32_t code, uint32_t PC, const uint32_t NPM)
|
||||
{
|
||||
const bool InBDSlot = !(NPM & 0x3);
|
||||
uint32_t handler = 0x80000080;
|
||||
|
||||
assert(code < 16);
|
||||
|
||||
if(code != EXCEPTION_INT && code != EXCEPTION_BP && code != EXCEPTION_SYSCALL)
|
||||
{
|
||||
PSX_DBG(PSX_DBG_WARNING, "Exception: %08x @ PC=0x%08x(IBDS=%d) -- IPCache=0x%02x -- IPEND=0x%02x -- SR=0x%08x ; IRQC_Status=0x%04x -- IRQC_Mask=0x%04x\n", code, PC, InBDSlot, IPCache, (CP0.CAUSE >> 8) & 0xFF, CP0.SR,
|
||||
IRQ_GetRegister(IRQ_GSREG_STATUS, NULL, 0), IRQ_GetRegister(IRQ_GSREG_MASK, NULL, 0));
|
||||
}
|
||||
|
||||
if(CP0.SR & (1 << 22)) // BEV
|
||||
handler = 0xBFC00180;
|
||||
|
||||
CP0.EPC = PC;
|
||||
if(InBDSlot)
|
||||
CP0.EPC -= 4;
|
||||
|
||||
if(ADDBT)
|
||||
ADDBT(PC, handler, true);
|
||||
|
||||
// "Push" IEc and KUc(so that the new IEc and KUc are 0)
|
||||
CP0.SR = (CP0.SR & ~0x3F) | ((CP0.SR << 2) & 0x3F);
|
||||
|
||||
// Setup cause register
|
||||
CP0.CAUSE &= 0x0000FF00;
|
||||
CP0.CAUSE |= code << 2;
|
||||
|
||||
// If EPC was adjusted -= 4 because we were in a branch delay slot, set the bit.
|
||||
if(InBDSlot)
|
||||
CP0.CAUSE |= 0x80000000;
|
||||
|
||||
RecalcIPCache();
|
||||
|
||||
return(handler);
|
||||
}
|
||||
|
||||
#define BACKING_TO_ACTIVE \
|
||||
PC = BACKED_PC; \
|
||||
new_PC = BACKED_new_PC; \
|
||||
new_PC_mask = BACKED_new_PC_mask; \
|
||||
LDWhich = BACKED_LDWhich; \
|
||||
LDValue = BACKED_LDValue;
|
||||
|
||||
#define ACTIVE_TO_BACKING \
|
||||
BACKED_PC = PC; \
|
||||
BACKED_new_PC = new_PC; \
|
||||
BACKED_new_PC_mask = new_PC_mask; \
|
||||
BACKED_LDWhich = LDWhich; \
|
||||
BACKED_LDValue = LDValue;
|
||||
|
||||
#define GPR_DEPRES_BEGIN { uint8_t back = ReadAbsorb[0];
|
||||
#define GPR_DEP(n) { unsigned tn = (n); ReadAbsorb[tn] = 0; }
|
||||
#define GPR_RES(n) { unsigned tn = (n); ReadAbsorb[tn] = 0; }
|
||||
#define GPR_DEPRES_END ReadAbsorb[0] = back; }
|
||||
|
||||
template<bool DebugMode, bool ILHMode>
|
||||
pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in)
|
||||
{
|
||||
register pscpu_timestamp_t timestamp = timestamp_in;
|
||||
|
||||
register uint32_t PC;
|
||||
register uint32_t new_PC;
|
||||
register uint32_t new_PC_mask;
|
||||
register uint32_t LDWhich;
|
||||
register uint32_t LDValue;
|
||||
|
||||
//printf("%d %d\n", gte_ts_done, muldiv_ts_done);
|
||||
|
||||
gte_ts_done += timestamp;
|
||||
muldiv_ts_done += timestamp;
|
||||
|
||||
BACKING_TO_ACTIVE;
|
||||
|
||||
do
|
||||
{
|
||||
//printf("Running: %d %d\n", timestamp, next_event_ts);
|
||||
while(MDFN_LIKELY(timestamp < next_event_ts))
|
||||
{
|
||||
uint32_t instr;
|
||||
uint32_t opf;
|
||||
|
||||
// Zero must be zero...until the Master Plan is enacted.
|
||||
GPR[0] = 0;
|
||||
|
||||
if(DebugMode && CPUHook)
|
||||
{
|
||||
ACTIVE_TO_BACKING;
|
||||
|
||||
// For save states in step mode.
|
||||
gte_ts_done -= timestamp;
|
||||
muldiv_ts_done -= timestamp;
|
||||
|
||||
CPUHook(timestamp, PC);
|
||||
|
||||
// For save states in step mode.
|
||||
gte_ts_done += timestamp;
|
||||
muldiv_ts_done += timestamp;
|
||||
|
||||
BACKING_TO_ACTIVE;
|
||||
}
|
||||
|
||||
if(!ILHMode)
|
||||
{
|
||||
if(PC == 0xB0)
|
||||
{
|
||||
if(MDFN_UNLIKELY(GPR[9] == 0x3D))
|
||||
{
|
||||
PSX_DBG(PSX_DBG_BIOS_PRINT, "%c", GPR[4]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instr = ICache[(PC & 0xFFC) >> 2].Data;
|
||||
|
||||
if(ICache[(PC & 0xFFC) >> 2].TV != PC)
|
||||
{
|
||||
//WriteAbsorb = 0;
|
||||
//WriteAbsorbCount = 0;
|
||||
//WriteAbsorbMonkey = 0;
|
||||
ReadAbsorb[ReadAbsorbWhich] = 0;
|
||||
ReadAbsorbWhich = 0;
|
||||
|
||||
// FIXME: Handle executing out of scratchpad.
|
||||
if(PC >= 0xA0000000 || !(BIU & 0x800))
|
||||
{
|
||||
instr = LoadU32_LE((uint32_t *)&FastMap[PC >> FAST_MAP_SHIFT][PC]);
|
||||
timestamp += 4; // Approximate best-case cache-disabled time, per PS1 tests(executing out of 0xA0000000+); it can be 5 in *some* sequences of code(like a lot of sequential "nop"s, probably other simple instructions too).
|
||||
}
|
||||
else
|
||||
{
|
||||
__ICache *ICI = &ICache[((PC & 0xFF0) >> 2)];
|
||||
const uint32_t *FMP = (uint32_t *)&FastMap[(PC &~ 0xF) >> FAST_MAP_SHIFT][PC &~ 0xF];
|
||||
|
||||
// | 0x2 to simulate (in)validity bits.
|
||||
ICI[0x00].TV = (PC &~ 0xF) | 0x00 | 0x2;
|
||||
ICI[0x01].TV = (PC &~ 0xF) | 0x04 | 0x2;
|
||||
ICI[0x02].TV = (PC &~ 0xF) | 0x08 | 0x2;
|
||||
ICI[0x03].TV = (PC &~ 0xF) | 0x0C | 0x2;
|
||||
|
||||
timestamp += 3;
|
||||
|
||||
switch(PC & 0xC)
|
||||
{
|
||||
case 0x0:
|
||||
timestamp++;
|
||||
ICI[0x00].TV &= ~0x2;
|
||||
ICI[0x00].Data = LoadU32_LE(&FMP[0]);
|
||||
case 0x4:
|
||||
timestamp++;
|
||||
ICI[0x01].TV &= ~0x2;
|
||||
ICI[0x01].Data = LoadU32_LE(&FMP[1]);
|
||||
case 0x8:
|
||||
timestamp++;
|
||||
ICI[0x02].TV &= ~0x2;
|
||||
ICI[0x02].Data = LoadU32_LE(&FMP[2]);
|
||||
case 0xC:
|
||||
timestamp++;
|
||||
ICI[0x03].TV &= ~0x2;
|
||||
ICI[0x03].Data = LoadU32_LE(&FMP[3]);
|
||||
break;
|
||||
}
|
||||
instr = ICache[(PC & 0xFFC) >> 2].Data;
|
||||
}
|
||||
}
|
||||
|
||||
//printf("PC=%08x, SP=%08x - op=0x%02x - funct=0x%02x - instr=0x%08x\n", PC, GPR[29], instr >> 26, instr & 0x3F, instr);
|
||||
//for(int i = 0; i < 32; i++)
|
||||
// printf("%02x : %08x\n", i, GPR[i]);
|
||||
//printf("\n");
|
||||
|
||||
opf = instr & 0x3F;
|
||||
|
||||
if(instr & (0x3F << 26))
|
||||
opf = 0x40 | (instr >> 26);
|
||||
|
||||
opf |= IPCache;
|
||||
|
||||
#if 0
|
||||
{
|
||||
uint32_t tmp = (ReadAbsorb[ReadAbsorbWhich] + 0x7FFFFFFF) >> 31;
|
||||
ReadAbsorb[ReadAbsorbWhich] -= tmp;
|
||||
timestamp = timestamp + 1 - tmp;
|
||||
}
|
||||
#else
|
||||
if(ReadAbsorb[ReadAbsorbWhich])
|
||||
ReadAbsorb[ReadAbsorbWhich]--;
|
||||
//else if((uint8)WriteAbsorb)
|
||||
//{
|
||||
// WriteAbsorb--;
|
||||
// if(!WriteAbsorb)
|
||||
// {
|
||||
// WriteAbsorbCount--;
|
||||
// if(WriteAbsorbMonkey)
|
||||
// WriteAbsorbMonkey--;
|
||||
// WriteAbsorb >>= 8;
|
||||
// }
|
||||
//}
|
||||
else
|
||||
timestamp++;
|
||||
#endif
|
||||
|
||||
#define DO_LDS() { GPR[LDWhich] = LDValue; ReadAbsorb[LDWhich] = LDAbsorb; ReadFudge = LDWhich; ReadAbsorbWhich |= LDWhich & 0x1F; LDWhich = 0x20; }
|
||||
#define BEGIN_OPF(name, arg_op, arg_funct) { op_##name: /*assert( ((arg_op) ? (0x40 | (arg_op)) : (arg_funct)) == opf); */
|
||||
#define END_OPF goto OpDone; }
|
||||
|
||||
#define DO_BRANCH(offset, mask) \
|
||||
{ \
|
||||
if(ILHMode) \
|
||||
{ \
|
||||
uint32_t old_PC = PC; \
|
||||
PC = (PC & new_PC_mask) + new_PC; \
|
||||
if(old_PC == ((PC & (mask)) + (offset))) \
|
||||
{ \
|
||||
if(*(uint32_t *)&FastMap[PC >> FAST_MAP_SHIFT][PC] == 0) \
|
||||
{ \
|
||||
if(next_event_ts > timestamp) /* Necessary since next_event_ts might be set to something like "0" to force a call to the event handler. */ \
|
||||
{ \
|
||||
timestamp = next_event_ts; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
else \
|
||||
PC = (PC & new_PC_mask) + new_PC; \
|
||||
new_PC = (offset); \
|
||||
new_PC_mask = (mask) & ~3; \
|
||||
/* Lower bits of new_PC_mask being clear signifies being in a branch delay slot. (overloaded behavior for performance) */ \
|
||||
\
|
||||
if(DebugMode && ADDBT) \
|
||||
{ \
|
||||
ADDBT(PC, (PC & new_PC_mask) + new_PC, false); \
|
||||
} \
|
||||
goto SkipNPCStuff; \
|
||||
}
|
||||
|
||||
#define ITYPE uint32_t rs MDFN_NOWARN_UNUSED = (instr >> 21) & 0x1F; uint32_t rt MDFN_NOWARN_UNUSED = (instr >> 16) & 0x1F; int32_t immediate = (int16)(instr & 0xFFFF); /*printf(" rs=%02x(%08x), rt=%02x(%08x), immediate=(%08x) ", rs, GPR[rs], rt, GPR[rt], immediate);*/
|
||||
#define ITYPE_ZE uint32_t rs MDFN_NOWARN_UNUSED = (instr >> 21) & 0x1F; uint32_t rt MDFN_NOWARN_UNUSED = (instr >> 16) & 0x1F; uint32_t immediate = instr & 0xFFFF; /*printf(" rs=%02x(%08x), rt=%02x(%08x), immediate=(%08x) ", rs, GPR[rs], rt, GPR[rt], immediate);*/
|
||||
#define JTYPE uint32_t target = instr & ((1 << 26) - 1); /*printf(" target=(%08x) ", target);*/
|
||||
#define RTYPE uint32_t rs MDFN_NOWARN_UNUSED = (instr >> 21) & 0x1F; uint32_t rt MDFN_NOWARN_UNUSED = (instr >> 16) & 0x1F; uint32_t rd MDFN_NOWARN_UNUSED = (instr >> 11) & 0x1F; uint32_t shamt MDFN_NOWARN_UNUSED = (instr >> 6) & 0x1F; /*printf(" rs=%02x(%08x), rt=%02x(%08x), rd=%02x(%08x) ", rs, GPR[rs], rt, GPR[rt], rd, GPR[rd]);*/
|
||||
|
||||
#if 1
|
||||
#include "cpu_bigswitch.c"
|
||||
#else
|
||||
#include "cpu_coputedgoto.c"
|
||||
#endif
|
||||
|
||||
OpDone: ;
|
||||
|
||||
PC = (PC & new_PC_mask) + new_PC;
|
||||
new_PC_mask = ~0U;
|
||||
new_PC = 4;
|
||||
|
||||
SkipNPCStuff: ;
|
||||
|
||||
//printf("\n");
|
||||
}
|
||||
} while(MDFN_LIKELY(PSX_EventHandler(timestamp)));
|
||||
|
||||
if(gte_ts_done > 0)
|
||||
gte_ts_done -= timestamp;
|
||||
|
||||
if(muldiv_ts_done > 0)
|
||||
muldiv_ts_done -= timestamp;
|
||||
|
||||
ACTIVE_TO_BACKING;
|
||||
|
||||
return(timestamp);
|
||||
}
|
||||
|
||||
pscpu_timestamp_t PS_CPU::Run(pscpu_timestamp_t timestamp_in, const bool ILHMode)
|
||||
{
|
||||
if(CPUHook || ADDBT)
|
||||
return(RunReal<true, false>(timestamp_in));
|
||||
if (ILHMode)
|
||||
return(RunReal<false, true>(timestamp_in));
|
||||
return(RunReal<false, false>(timestamp_in));
|
||||
}
|
||||
|
||||
void PS_CPU::SetCPUHook(void (*cpuh)(const pscpu_timestamp_t timestamp, uint32_t pc), void (*addbt)(uint32_t from, uint32_t to, bool exception))
|
||||
{
|
||||
ADDBT = addbt;
|
||||
CPUHook = cpuh;
|
||||
}
|
||||
|
||||
uint32_t PS_CPU::GetRegister(unsigned int which, char *special, const uint32_t special_len)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
|
||||
if(which >= GSREG_GPR && which < (GSREG_GPR + 32))
|
||||
return GPR[which];
|
||||
|
||||
switch(which)
|
||||
{
|
||||
case GSREG_PC:
|
||||
ret = BACKED_PC;
|
||||
break;
|
||||
|
||||
case GSREG_PC_NEXT:
|
||||
ret = BACKED_new_PC;
|
||||
break;
|
||||
|
||||
case GSREG_IN_BD_SLOT:
|
||||
ret = !(BACKED_new_PC_mask & 3);
|
||||
break;
|
||||
|
||||
case GSREG_LO:
|
||||
ret = LO;
|
||||
break;
|
||||
|
||||
case GSREG_HI:
|
||||
ret = HI;
|
||||
break;
|
||||
|
||||
case GSREG_SR:
|
||||
ret = CP0.SR;
|
||||
break;
|
||||
|
||||
case GSREG_CAUSE:
|
||||
ret = CP0.CAUSE;
|
||||
break;
|
||||
|
||||
case GSREG_EPC:
|
||||
ret = CP0.EPC;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void PS_CPU::SetRegister(unsigned int which, uint32_t value)
|
||||
{
|
||||
if(which >= GSREG_GPR && which < (GSREG_GPR + 32))
|
||||
{
|
||||
if(which != (GSREG_GPR + 0))
|
||||
GPR[which] = value;
|
||||
}
|
||||
else switch(which)
|
||||
{
|
||||
case GSREG_PC:
|
||||
BACKED_PC = value & ~0x3; // Remove masking if we ever add proper misaligned PC exception
|
||||
break;
|
||||
|
||||
case GSREG_LO:
|
||||
LO = value;
|
||||
break;
|
||||
|
||||
case GSREG_HI:
|
||||
HI = value;
|
||||
break;
|
||||
|
||||
case GSREG_SR:
|
||||
CP0.SR = value; // TODO: mask
|
||||
break;
|
||||
|
||||
case GSREG_CAUSE:
|
||||
CP0.CAUSE = value;
|
||||
break;
|
||||
|
||||
case GSREG_EPC:
|
||||
CP0.EPC = value & ~0x3U;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool PS_CPU::PeekCheckICache(uint32_t PC, uint32_t *iw)
|
||||
{
|
||||
if(ICache[(PC & 0xFFC) >> 2].TV == PC)
|
||||
{
|
||||
*iw = ICache[(PC & 0xFFC) >> 2].Data;
|
||||
return(true);
|
||||
}
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
||||
uint8_t PS_CPU::PeekMem8(uint32_t A)
|
||||
{
|
||||
return PeekMemory<uint8>(A);
|
||||
}
|
||||
|
||||
uint16_t PS_CPU::PeekMem16(uint32_t A)
|
||||
{
|
||||
return PeekMemory<uint16>(A);
|
||||
}
|
||||
|
||||
uint32_t PS_CPU::PeekMem32(uint32_t A)
|
||||
{
|
||||
return PeekMemory<uint32>(A);
|
||||
}
|
||||
|
||||
|
||||
#undef BEGIN_OPF
|
||||
#undef END_OPF
|
||||
#undef MK_OPF
|
||||
|
||||
#define MK_OPF(op, funct) ((op) ? (0x40 | (op)) : (funct))
|
||||
#define BEGIN_OPF(op, funct) case MK_OPF(op, funct): {
|
||||
#define END_OPF } break;
|
||||
|
||||
// FIXME: should we breakpoint on an illegal address? And with LWC2/SWC2 if CP2 isn't enabled?
|
||||
void PS_CPU::CheckBreakpoints(void (*callback)(bool write, uint32_t address, unsigned int len), uint32_t instr)
|
||||
{
|
||||
uint32_t opf;
|
||||
|
||||
opf = instr & 0x3F;
|
||||
|
||||
if(instr & (0x3F << 26))
|
||||
opf = 0x40 | (instr >> 26);
|
||||
|
||||
|
||||
switch(opf)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
|
||||
//
|
||||
// LB - Load Byte
|
||||
//
|
||||
BEGIN_OPF(0x20, 0);
|
||||
ITYPE;
|
||||
uint32_t address = GPR[rs] + immediate;
|
||||
|
||||
callback(false, address, 1);
|
||||
END_OPF;
|
||||
|
||||
//
|
||||
// LBU - Load Byte Unsigned
|
||||
//
|
||||
BEGIN_OPF(0x24, 0);
|
||||
ITYPE;
|
||||
uint32_t address = GPR[rs] + immediate;
|
||||
|
||||
callback(false, address, 1);
|
||||
END_OPF;
|
||||
|
||||
//
|
||||
// LH - Load Halfword
|
||||
//
|
||||
BEGIN_OPF(0x21, 0);
|
||||
ITYPE;
|
||||
uint32_t address = GPR[rs] + immediate;
|
||||
|
||||
callback(false, address, 2);
|
||||
END_OPF;
|
||||
|
||||
//
|
||||
// LHU - Load Halfword Unsigned
|
||||
//
|
||||
BEGIN_OPF(0x25, 0);
|
||||
ITYPE;
|
||||
uint32_t address = GPR[rs] + immediate;
|
||||
|
||||
callback(false, address, 2);
|
||||
END_OPF;
|
||||
|
||||
|
||||
//
|
||||
// LW - Load Word
|
||||
//
|
||||
BEGIN_OPF(0x23, 0);
|
||||
ITYPE;
|
||||
uint32_t address = GPR[rs] + immediate;
|
||||
|
||||
callback(false, address, 4);
|
||||
END_OPF;
|
||||
|
||||
//
|
||||
// SB - Store Byte
|
||||
//
|
||||
BEGIN_OPF(0x28, 0);
|
||||
ITYPE;
|
||||
uint32_t address = GPR[rs] + immediate;
|
||||
|
||||
callback(true, address, 1);
|
||||
END_OPF;
|
||||
|
||||
//
|
||||
// SH - Store Halfword
|
||||
//
|
||||
BEGIN_OPF(0x29, 0);
|
||||
ITYPE;
|
||||
uint32_t address = GPR[rs] + immediate;
|
||||
|
||||
callback(true, address, 2);
|
||||
END_OPF;
|
||||
|
||||
//
|
||||
// SW - Store Word
|
||||
//
|
||||
BEGIN_OPF(0x2B, 0);
|
||||
ITYPE;
|
||||
uint32_t address = GPR[rs] + immediate;
|
||||
|
||||
callback(true, address, 4);
|
||||
END_OPF;
|
||||
|
||||
//
|
||||
// LWL - Load Word Left
|
||||
//
|
||||
BEGIN_OPF(0x22, 0);
|
||||
ITYPE;
|
||||
uint32_t address = GPR[rs] + immediate;
|
||||
|
||||
do
|
||||
{
|
||||
callback(false, address, 1);
|
||||
} while((address--) & 0x3);
|
||||
|
||||
END_OPF;
|
||||
|
||||
//
|
||||
// SWL - Store Word Left
|
||||
//
|
||||
BEGIN_OPF(0x2A, 0);
|
||||
ITYPE;
|
||||
uint32_t address = GPR[rs] + immediate;
|
||||
|
||||
do
|
||||
{
|
||||
callback(true, address, 1);
|
||||
} while((address--) & 0x3);
|
||||
|
||||
END_OPF;
|
||||
|
||||
//
|
||||
// LWR - Load Word Right
|
||||
//
|
||||
BEGIN_OPF(0x26, 0);
|
||||
ITYPE;
|
||||
uint32_t address = GPR[rs] + immediate;
|
||||
|
||||
do
|
||||
{
|
||||
callback(false, address, 1);
|
||||
} while((++address) & 0x3);
|
||||
|
||||
END_OPF;
|
||||
|
||||
//
|
||||
// SWR - Store Word Right
|
||||
//
|
||||
BEGIN_OPF(0x2E, 0);
|
||||
ITYPE;
|
||||
uint32_t address = GPR[rs] + immediate;
|
||||
|
||||
do
|
||||
{
|
||||
callback(true, address, 1);
|
||||
} while((++address) & 0x3);
|
||||
|
||||
END_OPF;
|
||||
|
||||
//
|
||||
// LWC2
|
||||
//
|
||||
BEGIN_OPF(0x32, 0);
|
||||
ITYPE;
|
||||
uint32_t address = GPR[rs] + immediate;
|
||||
|
||||
callback(false, address, 4);
|
||||
END_OPF;
|
||||
|
||||
//
|
||||
// SWC2
|
||||
//
|
||||
BEGIN_OPF(0x3A, 0);
|
||||
ITYPE;
|
||||
uint32_t address = GPR[rs] + immediate;
|
||||
|
||||
callback(true, address, 4);
|
||||
END_OPF;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,260 @@
|
|||
#ifndef __MDFN_PSX_CPU_H
|
||||
#define __MDFN_PSX_CPU_H
|
||||
|
||||
/*
|
||||
Load delay notes:
|
||||
|
||||
// Takes 1 less
|
||||
".set noreorder\n\t"
|
||||
".set nomacro\n\t"
|
||||
"lw %0, 0(%2)\n\t"
|
||||
"nop\n\t"
|
||||
"nop\n\t"
|
||||
"or %0, %1, %1\n\t"
|
||||
|
||||
// cycle than this:
|
||||
".set noreorder\n\t"
|
||||
".set nomacro\n\t"
|
||||
"lw %0, 0(%2)\n\t"
|
||||
"nop\n\t"
|
||||
"or %0, %1, %1\n\t"
|
||||
"nop\n\t"
|
||||
|
||||
|
||||
// Both of these
|
||||
".set noreorder\n\t"
|
||||
".set nomacro\n\t"
|
||||
"lw %0, 0(%2)\n\t"
|
||||
"nop\n\t"
|
||||
"nop\n\t"
|
||||
"or %1, %0, %0\n\t"
|
||||
|
||||
// take same...(which is kind of odd).
|
||||
".set noreorder\n\t"
|
||||
".set nomacro\n\t"
|
||||
"lw %0, 0(%2)\n\t"
|
||||
"nop\n\t"
|
||||
"or %1, %0, %0\n\t"
|
||||
"nop\n\t"
|
||||
*/
|
||||
|
||||
#include "gte.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
#define PS_CPU_EMULATE_ICACHE 1
|
||||
|
||||
class PS_CPU
|
||||
{
|
||||
public:
|
||||
|
||||
PS_CPU();
|
||||
~PS_CPU();
|
||||
|
||||
// FAST_MAP_* enums are in BYTES(8-bit), not in 32-bit units("words" in MIPS context), but the sizes
|
||||
// will always be multiples of 4.
|
||||
enum { FAST_MAP_SHIFT = 16 };
|
||||
enum { FAST_MAP_PSIZE = 1 << FAST_MAP_SHIFT };
|
||||
|
||||
void SetFastMap(void *region_mem, uint32_t region_address, uint32_t region_size);
|
||||
|
||||
INLINE void SetEventNT(const pscpu_timestamp_t next_event_ts_arg)
|
||||
{
|
||||
next_event_ts = next_event_ts_arg;
|
||||
}
|
||||
|
||||
pscpu_timestamp_t Run(pscpu_timestamp_t timestamp_in, const bool ILHMode);
|
||||
|
||||
void Power(void);
|
||||
|
||||
// which ranges 0-5, inclusive
|
||||
void AssertIRQ(int which, bool asserted);
|
||||
|
||||
void SetHalt(bool status);
|
||||
|
||||
// TODO eventually: factor BIU address decoding directly in the CPU core somehow without hurting speed.
|
||||
void SetBIU(uint32_t val);
|
||||
uint32_t GetBIU(void);
|
||||
|
||||
int StateAction(StateMem *sm, int load, int data_only);
|
||||
|
||||
private:
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t GPR[32];
|
||||
uint32_t GPR_dummy; // Used in load delay simulation(indexing past the end of GPR)
|
||||
};
|
||||
uint32_t LO;
|
||||
uint32_t HI;
|
||||
|
||||
|
||||
uint32_t BACKED_PC;
|
||||
uint32_t BACKED_new_PC;
|
||||
uint32_t BACKED_new_PC_mask;
|
||||
|
||||
uint32_t IPCache;
|
||||
void RecalcIPCache(void);
|
||||
bool Halted;
|
||||
|
||||
uint32_t BACKED_LDWhich;
|
||||
uint32_t BACKED_LDValue;
|
||||
uint32_t LDAbsorb;
|
||||
|
||||
pscpu_timestamp_t next_event_ts;
|
||||
pscpu_timestamp_t gte_ts_done;
|
||||
pscpu_timestamp_t muldiv_ts_done;
|
||||
|
||||
uint32_t BIU;
|
||||
|
||||
struct __ICache
|
||||
{
|
||||
uint32_t TV;
|
||||
uint32_t Data;
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
__ICache ICache[1024];
|
||||
uint32 ICache_Bulk[2048];
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
CP0REG_BPC = 3, // PC breakpoint address.
|
||||
CP0REG_BDA = 5, // Data load/store breakpoint address.
|
||||
CP0REG_TAR = 6, // Target address(???)
|
||||
CP0REG_DCIC = 7, // Cache control
|
||||
CP0REG_BDAM = 9, // Data load/store address mask.
|
||||
CP0REG_BPCM = 11, // PC breakpoint address mask.
|
||||
CP0REG_SR = 12,
|
||||
CP0REG_CAUSE = 13,
|
||||
CP0REG_EPC = 14,
|
||||
CP0REG_PRID = 15, // Product ID
|
||||
CP0REG_ERREG = 16
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
union
|
||||
{
|
||||
uint32_t Regs[32];
|
||||
struct
|
||||
{
|
||||
uint32_t Unused00;
|
||||
uint32_t Unused01;
|
||||
uint32_t Unused02;
|
||||
uint32_t BPC; // RW
|
||||
uint32_t Unused04;
|
||||
uint32_t BDA; // RW
|
||||
uint32_t TAR;
|
||||
uint32_t DCIC; // RW
|
||||
uint32_t Unused08;
|
||||
uint32_t BDAM; // R/W
|
||||
uint32_t Unused0A;
|
||||
uint32_t BPCM; // R/W
|
||||
uint32_t SR; // R/W
|
||||
uint32_t CAUSE; // R/W(partial)
|
||||
uint32_t EPC; // R
|
||||
uint32_t PRID; // R
|
||||
uint32_t ERREG; // ?(may not exist, test)
|
||||
};
|
||||
};
|
||||
} CP0;
|
||||
|
||||
#if 1
|
||||
//uint32_t WrAbsorb;
|
||||
//uint8_t WrAbsorbShift;
|
||||
|
||||
// On read:
|
||||
//WrAbsorb = 0;
|
||||
//WrAbsorbShift = 0;
|
||||
|
||||
// On write:
|
||||
//WrAbsorb >>= (WrAbsorbShift >> 2) & 8;
|
||||
//WrAbsorbShift -= (WrAbsorbShift >> 2) & 8;
|
||||
|
||||
//WrAbsorb |= (timestamp - pre_write_timestamp) << WrAbsorbShift;
|
||||
//WrAbsorbShift += 8;
|
||||
#endif
|
||||
|
||||
struct
|
||||
{
|
||||
uint8_t ReadAbsorb[0x20];
|
||||
uint8_t ReadAbsorbDummy;
|
||||
};
|
||||
uint8_t ReadAbsorbWhich;
|
||||
uint8_t ReadFudge;
|
||||
|
||||
//uint32_t WriteAbsorb;
|
||||
//uint8_t WriteAbsorbCount;
|
||||
//uint8_t WriteAbsorbMonkey;
|
||||
|
||||
MultiAccessSizeMem<1024, uint32, false> ScratchRAM;
|
||||
|
||||
//PS_GTE GTE;
|
||||
|
||||
uint8_t *FastMap[1 << (32 - FAST_MAP_SHIFT)];
|
||||
uint8_t DummyPage[FAST_MAP_PSIZE];
|
||||
|
||||
enum
|
||||
{
|
||||
EXCEPTION_INT = 0,
|
||||
EXCEPTION_MOD = 1,
|
||||
EXCEPTION_TLBL = 2,
|
||||
EXCEPTION_TLBS = 3,
|
||||
EXCEPTION_ADEL = 4, // Address error on load
|
||||
EXCEPTION_ADES = 5, // Address error on store
|
||||
EXCEPTION_IBE = 6, // Instruction bus error
|
||||
EXCEPTION_DBE = 7, // Data bus error
|
||||
EXCEPTION_SYSCALL = 8, // System call
|
||||
EXCEPTION_BP = 9, // Breakpoint
|
||||
EXCEPTION_RI = 10, // Reserved instruction
|
||||
EXCEPTION_COPU = 11, // Coprocessor unusable
|
||||
EXCEPTION_OV = 12 // Arithmetic overflow
|
||||
};
|
||||
|
||||
uint32_t Exception(uint32_t code, uint32_t PC, const uint32_t NPM) MDFN_WARN_UNUSED_RESULT;
|
||||
|
||||
template<bool DebugMode, bool ILHMode> pscpu_timestamp_t RunReal(pscpu_timestamp_t timestamp_in);
|
||||
|
||||
template<typename T> T PeekMemory(uint32_t address) MDFN_COLD;
|
||||
template<typename T> T ReadMemory(pscpu_timestamp_t ×tamp, uint32_t address, bool DS24 = false, bool LWC_timing = false);
|
||||
template<typename T> void WriteMemory(pscpu_timestamp_t ×tamp, uint32_t address, uint32_t value, bool DS24 = false);
|
||||
|
||||
|
||||
//
|
||||
// Mednafen debugger stuff follows:
|
||||
//
|
||||
public:
|
||||
void SetCPUHook(void (*cpuh)(const pscpu_timestamp_t timestamp, uint32_t pc), void (*addbt)(uint32_t from, uint32_t to, bool exception));
|
||||
void CheckBreakpoints(void (*callback)(bool write, uint32_t address, unsigned int len), uint32_t instr);
|
||||
|
||||
enum
|
||||
{
|
||||
GSREG_GPR = 0,
|
||||
GSREG_PC = 32,
|
||||
GSREG_PC_NEXT,
|
||||
GSREG_IN_BD_SLOT,
|
||||
GSREG_LO,
|
||||
GSREG_HI,
|
||||
GSREG_SR,
|
||||
GSREG_CAUSE,
|
||||
GSREG_EPC,
|
||||
};
|
||||
|
||||
uint32_t GetRegister(unsigned int which, char *special, const uint32_t special_len);
|
||||
void SetRegister(unsigned int which, uint32_t value);
|
||||
bool PeekCheckICache(uint32_t PC, uint32_t *iw);
|
||||
uint8_t PeekMem8(uint32_t A);
|
||||
uint16_t PeekMem16(uint32_t A);
|
||||
uint32_t PeekMem32(uint32_t A);
|
||||
private:
|
||||
void (*CPUHook)(const pscpu_timestamp_t timestamp, uint32_t pc);
|
||||
void (*ADDBT)(uint32_t from, uint32_t to, bool exception);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,679 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "psx.h"
|
||||
#include "timer.h"
|
||||
#include "cdc.h"
|
||||
#include "spu.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
extern PS_GPU *GPU;
|
||||
extern PS_SPU *SPU;
|
||||
|
||||
static void RedoCPUHook(void);
|
||||
|
||||
static void (*CPUHook)(uint32, bool) = NULL;
|
||||
static bool CPUHookContinuous = false;
|
||||
|
||||
struct PSX_BPOINT
|
||||
{
|
||||
uint32 A[2];
|
||||
int type;
|
||||
};
|
||||
|
||||
static std::vector<PSX_BPOINT> BreakPointsPC, BreakPointsRead, BreakPointsWrite;
|
||||
static bool FoundBPoint;
|
||||
|
||||
static bool BTEnabled;
|
||||
static int BTIndex;
|
||||
|
||||
struct BTEntry
|
||||
{
|
||||
uint32 from;
|
||||
uint32 to;
|
||||
uint32 branch_count;
|
||||
bool exception;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
#define NUMBT 24
|
||||
static BTEntry BTEntries[NUMBT];
|
||||
|
||||
void DBG_Break(void)
|
||||
{
|
||||
FoundBPoint = true;
|
||||
}
|
||||
|
||||
static void AddBranchTrace(uint32 from, uint32 to, bool exception)
|
||||
{
|
||||
BTEntry *prevbt = &BTEntries[(BTIndex + NUMBT - 1) % NUMBT];
|
||||
|
||||
//if(BTEntries[(BTIndex - 1) & 0xF] == PC) return;
|
||||
|
||||
if(prevbt->from == from && prevbt->to == to && prevbt->exception == exception && prevbt->branch_count < 0xFFFFFFFF && prevbt->valid)
|
||||
prevbt->branch_count++;
|
||||
else
|
||||
{
|
||||
BTEntries[BTIndex].from = from;
|
||||
BTEntries[BTIndex].to = to;
|
||||
BTEntries[BTIndex].exception = exception;
|
||||
BTEntries[BTIndex].branch_count = 1;
|
||||
BTEntries[BTIndex].valid = true;
|
||||
|
||||
BTIndex = (BTIndex + 1) % NUMBT;
|
||||
}
|
||||
}
|
||||
|
||||
static void EnableBranchTrace(bool enable)
|
||||
{
|
||||
BTEnabled = enable;
|
||||
if(!enable)
|
||||
{
|
||||
BTIndex = 0;
|
||||
memset(BTEntries, 0, sizeof(BTEntries));
|
||||
}
|
||||
RedoCPUHook();
|
||||
}
|
||||
|
||||
static std::vector<BranchTraceResult> GetBranchTrace(void)
|
||||
{
|
||||
BranchTraceResult tmp;
|
||||
std::vector<BranchTraceResult> ret;
|
||||
|
||||
for(int x = 0; x < NUMBT; x++)
|
||||
{
|
||||
const BTEntry *bt = &BTEntries[(x + BTIndex) % NUMBT];
|
||||
|
||||
tmp.count = bt->branch_count;
|
||||
trio_snprintf(tmp.from, sizeof(tmp.from), "%08x", bt->from);
|
||||
trio_snprintf(tmp.to, sizeof(tmp.to), "%08x", bt->to);
|
||||
trio_snprintf(tmp.code, sizeof(tmp.code), "%s", bt->exception ? "e" : "");
|
||||
|
||||
ret.push_back(tmp);
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void CheckCPUBPCallB(bool write, uint32 address, unsigned int len)
|
||||
{
|
||||
std::vector<PSX_BPOINT>::iterator bpit;
|
||||
std::vector<PSX_BPOINT>::iterator bpit_end;
|
||||
|
||||
if(write)
|
||||
{
|
||||
bpit = BreakPointsWrite.begin();
|
||||
bpit_end = BreakPointsWrite.end();
|
||||
}
|
||||
else
|
||||
{
|
||||
bpit = BreakPointsRead.begin();
|
||||
bpit_end = BreakPointsRead.end();
|
||||
}
|
||||
|
||||
while(bpit != bpit_end)
|
||||
{
|
||||
if(address >= bpit->A[0] && address <= bpit->A[1])
|
||||
{
|
||||
FoundBPoint = true;
|
||||
break;
|
||||
}
|
||||
bpit++;
|
||||
}
|
||||
}
|
||||
|
||||
static void CPUHandler(const pscpu_timestamp_t timestamp, uint32 PC)
|
||||
{
|
||||
std::vector<PSX_BPOINT>::iterator bpit;
|
||||
|
||||
for(bpit = BreakPointsPC.begin(); bpit != BreakPointsPC.end(); bpit++)
|
||||
{
|
||||
if(PC >= bpit->A[0] && PC <= bpit->A[1])
|
||||
{
|
||||
FoundBPoint = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CPU->CheckBreakpoints(CheckCPUBPCallB, CPU->PeekMem32(PC));
|
||||
|
||||
CPUHookContinuous |= FoundBPoint;
|
||||
|
||||
if(CPUHookContinuous && CPUHook)
|
||||
{
|
||||
ForceEventUpdates(timestamp);
|
||||
CPUHook(PC, FoundBPoint);
|
||||
}
|
||||
|
||||
FoundBPoint = false;
|
||||
}
|
||||
|
||||
|
||||
static void RedoCPUHook(void)
|
||||
{
|
||||
const bool HappyTest = CPUHook || BreakPointsPC.size() || BreakPointsRead.size() || BreakPointsWrite.size();
|
||||
|
||||
CPU->SetCPUHook(HappyTest ? CPUHandler : NULL, BTEnabled ? AddBranchTrace : NULL);
|
||||
}
|
||||
|
||||
static void FlushBreakPoints(int type)
|
||||
{
|
||||
if(type == BPOINT_READ)
|
||||
BreakPointsRead.clear();
|
||||
else if(type == BPOINT_WRITE)
|
||||
BreakPointsWrite.clear();
|
||||
else if(type == BPOINT_PC)
|
||||
BreakPointsPC.clear();
|
||||
|
||||
RedoCPUHook();
|
||||
}
|
||||
|
||||
static void AddBreakPoint(int type, unsigned int A1, unsigned int A2, bool logical)
|
||||
{
|
||||
PSX_BPOINT tmp;
|
||||
|
||||
tmp.A[0] = A1;
|
||||
tmp.A[1] = A2;
|
||||
tmp.type = type;
|
||||
|
||||
if(type == BPOINT_READ)
|
||||
BreakPointsRead.push_back(tmp);
|
||||
else if(type == BPOINT_WRITE)
|
||||
BreakPointsWrite.push_back(tmp);
|
||||
else if(type == BPOINT_PC)
|
||||
BreakPointsPC.push_back(tmp);
|
||||
|
||||
RedoCPUHook();
|
||||
}
|
||||
|
||||
static void SetCPUCallback(void (*callb)(uint32 PC, bool bpoint), bool continuous)
|
||||
{
|
||||
CPUHook = callb;
|
||||
CPUHookContinuous = continuous;
|
||||
RedoCPUHook();
|
||||
}
|
||||
|
||||
static void GetAddressSpaceBytes(const char *name, uint32 Address, uint32 Length, uint8 *Buffer)
|
||||
{
|
||||
if(!strcmp(name, "cpu"))
|
||||
{
|
||||
while(Length--)
|
||||
{
|
||||
Address &= 0xFFFFFFFF;
|
||||
*Buffer = CPU->PeekMem8(Address);
|
||||
Address++;
|
||||
Buffer++;
|
||||
}
|
||||
}
|
||||
else if(!strcmp(name, "ram"))
|
||||
{
|
||||
while(Length--)
|
||||
{
|
||||
Address &= 0x1FFFFF;
|
||||
*Buffer = CPU->PeekMem8(Address);
|
||||
Address++;
|
||||
Buffer++;
|
||||
}
|
||||
}
|
||||
else if(!strcmp(name, "spu"))
|
||||
{
|
||||
while(Length--)
|
||||
{
|
||||
Address &= 0x7FFFF;
|
||||
*Buffer = SPU->PeekSPURAM(Address >> 1) >> ((Address & 1) * 8);
|
||||
Address++;
|
||||
Buffer++;
|
||||
}
|
||||
}
|
||||
else if(!strcmp(name, "gpu"))
|
||||
{
|
||||
while(Length--)
|
||||
{
|
||||
Address &= 0xFFFFF;
|
||||
*Buffer = GPU->PeekRAM(Address >> 1) >> ((Address & 1) * 8);
|
||||
Address++;
|
||||
Buffer++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void PutAddressSpaceBytes(const char *name, uint32 Address, uint32 Length, uint32 Granularity, bool hl, const uint8 *Buffer)
|
||||
{
|
||||
if(!strcmp(name, "cpu"))
|
||||
{
|
||||
while(Length--)
|
||||
{
|
||||
pscpu_timestamp_t dummy = 0;
|
||||
Address &= 0xFFFFFFFF;
|
||||
|
||||
// TODO
|
||||
PSX_MemWrite8(dummy, Address, *Buffer);
|
||||
|
||||
Address++;
|
||||
Buffer++;
|
||||
}
|
||||
}
|
||||
else if(!strcmp(name, "gpu"))
|
||||
{
|
||||
while(Length--)
|
||||
{
|
||||
Address &= 0xFFFFF;
|
||||
|
||||
uint16 peeko = GPU->PeekRAM(Address >> 1);
|
||||
|
||||
GPU->PokeRAM(Address >> 1, (*Buffer << ((Address & 1) * 8)) | (peeko & (0xFF00 >> ((Address & 1) * 8))) );
|
||||
Address++;
|
||||
Buffer++;
|
||||
}
|
||||
}
|
||||
else if(!strcmp(name, "spu"))
|
||||
{
|
||||
while(Length--)
|
||||
{
|
||||
Address &= 0x7FFFF;
|
||||
|
||||
uint16 peeko = SPU->PeekSPURAM(Address >> 1);
|
||||
|
||||
SPU->PokeSPURAM(Address >> 1, (*Buffer << ((Address & 1) * 8)) | (peeko & (0xFF00 >> ((Address & 1) * 8))) );
|
||||
Address++;
|
||||
Buffer++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static uint32 MemPeek(uint32 A, unsigned int bsize, bool hl, bool logical)
|
||||
{
|
||||
uint32 ret = 0;
|
||||
|
||||
for(unsigned int i = 0; i < bsize; i++)
|
||||
ret |= CPU->PeekMem8(A + i) << (i * 8);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static void Disassemble(uint32 &A, uint32 SpecialA, char *TextBuf)
|
||||
{
|
||||
assert(!(A & 0x3));
|
||||
|
||||
uint32 instr = CPU->PeekMem32(A);
|
||||
|
||||
CPU->PeekCheckICache(A, &instr);
|
||||
|
||||
strncpy(TextBuf, DisassembleMIPS(A, instr).c_str(), 256);
|
||||
TextBuf[255] = 0;
|
||||
|
||||
// trio_snprintf(TextBuf, 256, "0x%08x", instr);
|
||||
|
||||
A += 4;
|
||||
}
|
||||
|
||||
static MDFN_Surface *GfxDecode_Buf = NULL;
|
||||
static int GfxDecode_Line = -1;
|
||||
static int GfxDecode_Layer = 0;
|
||||
static int GfxDecode_Scroll = 0;
|
||||
static int GfxDecode_PBN = 0;
|
||||
|
||||
static void DoGfxDecode(void)
|
||||
{
|
||||
unsigned tp_w, tp_h;
|
||||
|
||||
tp_w = 256;
|
||||
tp_h = 256;
|
||||
|
||||
if(GfxDecode_Buf)
|
||||
{
|
||||
for(int sy = 0; sy < GfxDecode_Buf->h; sy++)
|
||||
{
|
||||
for(int sx = 0; sx < GfxDecode_Buf->w; sx++)
|
||||
{
|
||||
unsigned fb_x = ((sx % GfxDecode_Buf->w) + ((sy + GfxDecode_Scroll) / GfxDecode_Buf->w * GfxDecode_Buf->w)) & 1023;
|
||||
unsigned fb_y = (((sy + GfxDecode_Scroll) % GfxDecode_Buf->w) + ((((sx % GfxDecode_Buf->w) + ((sy + GfxDecode_Scroll) / GfxDecode_Buf->w * GfxDecode_Buf->w)) / 1024) * 256)) & 511;
|
||||
|
||||
uint16 pixel = GPU->PeekRAM(fb_y * 1024 + fb_x);
|
||||
|
||||
GfxDecode_Buf->pixels[(sy * GfxDecode_Buf->w * 3) + sx] = GfxDecode_Buf->MakeColor(((pixel >> 0) & 0x1F) * 255 / 31,
|
||||
((pixel >> 5) & 0x1F) * 255 / 31,
|
||||
((pixel >> 10) & 0x1F) * 255 / 31, 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DBG_GPUScanlineHook(unsigned scanline)
|
||||
{
|
||||
if((int)scanline == GfxDecode_Line)
|
||||
{
|
||||
DoGfxDecode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void SetGraphicsDecode(MDFN_Surface *surface, int line, int which, int xscroll, int yscroll, int pbn)
|
||||
{
|
||||
GfxDecode_Buf = surface;
|
||||
GfxDecode_Line = line;
|
||||
GfxDecode_Layer = which;
|
||||
GfxDecode_Scroll = yscroll;
|
||||
GfxDecode_PBN = pbn;
|
||||
|
||||
if(GfxDecode_Buf && GfxDecode_Line == -1)
|
||||
DoGfxDecode();
|
||||
}
|
||||
|
||||
DebuggerInfoStruct PSX_DBGInfo =
|
||||
{
|
||||
"shift_jis",
|
||||
4, // Max instruction byte size
|
||||
4, // Instruction alignment(bytes)
|
||||
32, // Logical address bits
|
||||
32, // Physical address bits
|
||||
0x00000000, // Default watch addr
|
||||
~0U, // ZP addr
|
||||
|
||||
MemPeek,
|
||||
Disassemble,
|
||||
NULL,
|
||||
NULL, //ForceIRQ,
|
||||
NULL, //NESDBG_GetVector,
|
||||
FlushBreakPoints,
|
||||
AddBreakPoint,
|
||||
SetCPUCallback,
|
||||
EnableBranchTrace,
|
||||
GetBranchTrace,
|
||||
SetGraphicsDecode,
|
||||
NULL, //PCFXDBG_SetLogFunc,
|
||||
};
|
||||
|
||||
static RegType Regs_Misc[] =
|
||||
{
|
||||
{ TIMER_GSREG_COUNTER0, "COUNT0", "Counter 0", 2 },
|
||||
{ TIMER_GSREG_MODE0, "MODE0", "Mode 0", 2 },
|
||||
{ TIMER_GSREG_TARGET0, "TARGET0", "Target 0", 2 },
|
||||
|
||||
{ 0, "------", "", 0xFFFF },
|
||||
|
||||
|
||||
{ TIMER_GSREG_COUNTER1, "COUNT1", "Counter 1", 2 },
|
||||
{ TIMER_GSREG_MODE1, "MODE1", "Mode 1", 2 },
|
||||
{ TIMER_GSREG_TARGET1, "TARGET1", "Target 1", 2 },
|
||||
|
||||
{ 0, "------", "", 0xFFFF },
|
||||
|
||||
{ TIMER_GSREG_COUNTER2, "COUNT2", "Counter 2", 2 },
|
||||
{ TIMER_GSREG_MODE2, "MODE2", "Mode 2", 2 },
|
||||
{ TIMER_GSREG_TARGET2, "TARGET2", "Target 2", 2 },
|
||||
|
||||
{ 0, "------", "", 0xFFFF },
|
||||
{ 0, "------", "", 0xFFFF },
|
||||
|
||||
{ 0x10000 | IRQ_GSREG_ASSERTED, "ASSERTD", "IRQ Asserted", 2 },
|
||||
{ 0x10000 | IRQ_GSREG_STATUS, "STATUS", "IRQ Status", 2 },
|
||||
{ 0x10000 | IRQ_GSREG_MASK, "MASK", "IRQ Mask", 2 },
|
||||
|
||||
{ 0, "", "", 0 }
|
||||
};
|
||||
|
||||
|
||||
static uint32 GetRegister_Misc(const unsigned int id, char *special, const uint32 special_len)
|
||||
{
|
||||
if(id & 0x10000)
|
||||
return(IRQ_GetRegister(id & 0xFFFF, special, special_len));
|
||||
else
|
||||
return(TIMER_GetRegister(id & 0xFFFF, special, special_len));
|
||||
}
|
||||
|
||||
static void SetRegister_Misc(const unsigned int id, uint32 value)
|
||||
{
|
||||
if(id & 0x10000)
|
||||
IRQ_SetRegister(id & 0xFFFF, value);
|
||||
else
|
||||
TIMER_SetRegister(id & 0xFFFF, value);
|
||||
}
|
||||
|
||||
static RegGroupType MiscRegsGroup =
|
||||
{
|
||||
NULL,
|
||||
Regs_Misc,
|
||||
GetRegister_Misc,
|
||||
SetRegister_Misc
|
||||
};
|
||||
|
||||
static RegType Regs_SPU[] =
|
||||
{
|
||||
{ PS_SPU::GSREG_SPUCONTROL, "SPUCTRL", "SPU Control", 2 },
|
||||
|
||||
{ PS_SPU::GSREG_FM_ON, "FMOn", "FM Enable", 3 },
|
||||
{ PS_SPU::GSREG_NOISE_ON, "NoiseOn", "Noise Enable", 3 },
|
||||
{ PS_SPU::GSREG_REVERB_ON, "ReverbOn", "Reverb Enable", 3 },
|
||||
|
||||
{ PS_SPU::GSREG_CDVOL_L, "CDVolL", "CD Volume Left", 2 },
|
||||
{ PS_SPU::GSREG_CDVOL_R, "CDVolR", "CD Volume Right", 2 },
|
||||
|
||||
{ PS_SPU::GSREG_DRYVOL_CTRL_L, "DryVolCL", "Dry Volume Control Left", 2 },
|
||||
{ PS_SPU::GSREG_DRYVOL_CTRL_R, "DryVolCR", "Dry Volume Control Right", 2 },
|
||||
|
||||
{ PS_SPU::GSREG_DRYVOL_L, "DryVolL", "Dry Volume Left", 2 },
|
||||
{ PS_SPU::GSREG_DRYVOL_R, "DryVolR", "Dry Volume Right", 2 },
|
||||
|
||||
{ PS_SPU::GSREG_WETVOL_L, "WetVolL", "Wet Volume Left", 2 },
|
||||
{ PS_SPU::GSREG_WETVOL_R, "WetVolR", "Wet Volume Right", 2 },
|
||||
|
||||
{ PS_SPU::GSREG_RWADDR, "RWAddr", "SPURAM Read/Write Address", 3 },
|
||||
|
||||
{ PS_SPU::GSREG_IRQADDR, "IRQAddr", "IRQ Compare Address", 3 },
|
||||
|
||||
{ PS_SPU::GSREG_REVERBWA, "ReverbWA", "Reverb Work Area(Raw)", 2 },
|
||||
|
||||
{ PS_SPU::GSREG_VOICEON, "VoiceOn", "Voice On", 3 },
|
||||
{ PS_SPU::GSREG_VOICEOFF, "VoiceOff", "Voice Off", 3 },
|
||||
{ PS_SPU::GSREG_BLOCKEND, "BlockEnd", "Block End", 3 },
|
||||
|
||||
|
||||
{ 0, "------", "", 0xFFFF },
|
||||
|
||||
{ PS_SPU::GSREG_FB_SRC_A, "FB_SRC_A", "", 2 },
|
||||
{ PS_SPU::GSREG_FB_SRC_B, "FB_SRC_B", "", 2 },
|
||||
{ PS_SPU::GSREG_IIR_ALPHA, "IIR_ALPHA", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_COEF_A, "ACC_COEF_A", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_COEF_B, "ACC_COEF_B", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_COEF_C, "ACC_COEF_C", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_COEF_D, "ACC_COEF_D", "", 2 },
|
||||
{ PS_SPU::GSREG_IIR_COEF, "IIR_COEF", "", 2 },
|
||||
{ PS_SPU::GSREG_FB_ALPHA, "FB_ALPHA", "", 2 },
|
||||
{ PS_SPU::GSREG_FB_X, "FB_X", "", 2 },
|
||||
{ PS_SPU::GSREG_IIR_DEST_A0, "IIR_DST_A0", "", 2 },
|
||||
{ PS_SPU::GSREG_IIR_DEST_A1, "IIR_DST_A1", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_SRC_A0, "ACC_SRC_A0", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_SRC_A1, "ACC_SRC_A1", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_SRC_B0, "ACC_SRC_B0", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_SRC_B1, "ACC_SRC_B1", "", 2 },
|
||||
{ PS_SPU::GSREG_IIR_SRC_A0, "IIR_SRC_A0", "", 2 },
|
||||
{ PS_SPU::GSREG_IIR_SRC_A1, "IIR_SRC_A1", "", 2 },
|
||||
{ PS_SPU::GSREG_IIR_DEST_B0, "IIR_DST_B0", "", 2 },
|
||||
{ PS_SPU::GSREG_IIR_DEST_B1, "IIR_DST_B1", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_SRC_C0, "ACC_SRC_C0", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_SRC_C1, "ACC_SRC_C1", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_SRC_D0, "ACC_SRC_D0", "", 2 },
|
||||
{ PS_SPU::GSREG_ACC_SRC_D1, "ACC_SRC_D1", "", 2 },
|
||||
{ PS_SPU::GSREG_IIR_SRC_B1, "IIR_SRC_B1", "", 2 },
|
||||
{ PS_SPU::GSREG_IIR_SRC_B0, "IIR_SRC_B0", "", 2 },
|
||||
{ PS_SPU::GSREG_MIX_DEST_A0, "MIX_DST_A0", "", 2 },
|
||||
{ PS_SPU::GSREG_MIX_DEST_A1, "MIX_DST_A1", "", 2 },
|
||||
{ PS_SPU::GSREG_MIX_DEST_B0, "MIX_DST_B0", "", 2 },
|
||||
{ PS_SPU::GSREG_MIX_DEST_B1, "MIX_DST_B1", "", 2 },
|
||||
{ PS_SPU::GSREG_IN_COEF_L, "IN_COEF_L", "", 2 },
|
||||
{ PS_SPU::GSREG_IN_COEF_R, "IN_COEF_R", "", 2 },
|
||||
|
||||
{ 0, "", "", 0 },
|
||||
};
|
||||
|
||||
#define VOICE_HELPER(v) \
|
||||
{ 0, "--V"#v"--", "", 0xFFFF }, \
|
||||
{ PS_SPU:: GSREG_V0_VOL_CTRL_L + v * 256, "VolCL", "Volume Control Left", 2 }, \
|
||||
{ PS_SPU:: GSREG_V0_VOL_CTRL_R + v * 256, "VolCR", "Volume Control Right", 2 }, \
|
||||
{ PS_SPU:: GSREG_V0_VOL_L + v * 256, "VolL", "Volume Left", 2 }, \
|
||||
{ PS_SPU:: GSREG_V0_VOL_R + v * 256, "VolR", "Volume Right", 2 }, \
|
||||
{ PS_SPU:: GSREG_V0_PITCH + v * 256, "Pitch", "Pitch", 2 }, \
|
||||
{ PS_SPU:: GSREG_V0_STARTADDR + v * 256, "SAddr", "Start Address", 3 }, \
|
||||
{ PS_SPU:: GSREG_V0_ADSR_CTRL + v * 256, "ADSRCTRL", "ADSR Control", 4 }, \
|
||||
{ PS_SPU:: GSREG_V0_ADSR_LEVEL + v * 256, "ADSRLev", "ADSR Level", 2 }, \
|
||||
{ PS_SPU:: GSREG_V0_LOOP_ADDR + v * 256, "LAddr", "Loop Address", 3 }, \
|
||||
{ PS_SPU:: GSREG_V0_READ_ADDR + v * 256, "RAddr", "Read Address", 3 }
|
||||
|
||||
|
||||
static RegType Regs_SPU_Voices[] =
|
||||
{
|
||||
#if 1
|
||||
VOICE_HELPER(0),
|
||||
VOICE_HELPER(1),
|
||||
VOICE_HELPER(2),
|
||||
VOICE_HELPER(3),
|
||||
#else
|
||||
VOICE_HELPER(9),
|
||||
VOICE_HELPER(12),
|
||||
VOICE_HELPER(17),
|
||||
VOICE_HELPER(22),
|
||||
|
||||
//VOICE_HELPER(20),
|
||||
//VOICE_HELPER(21),
|
||||
//VOICE_HELPER(22),
|
||||
//VOICE_HELPER(23),
|
||||
#endif
|
||||
{ 0, "", "", 0 },
|
||||
};
|
||||
|
||||
|
||||
static uint32 GetRegister_SPU(const unsigned int id, char *special, const uint32 special_len)
|
||||
{
|
||||
return(SPU->GetRegister(id, special, special_len));
|
||||
}
|
||||
|
||||
static void SetRegister_SPU(const unsigned int id, uint32 value)
|
||||
{
|
||||
SPU->SetRegister(id, value);
|
||||
}
|
||||
|
||||
static RegGroupType SPURegsGroup =
|
||||
{
|
||||
NULL,
|
||||
Regs_SPU,
|
||||
GetRegister_SPU,
|
||||
SetRegister_SPU
|
||||
};
|
||||
|
||||
|
||||
static RegGroupType SPUVoicesRegsGroup =
|
||||
{
|
||||
NULL,
|
||||
Regs_SPU_Voices,
|
||||
GetRegister_SPU,
|
||||
SetRegister_SPU
|
||||
};
|
||||
|
||||
static RegType Regs_CPU[] =
|
||||
{
|
||||
{ PS_CPU::GSREG_PC, "PC", "PC", 4 },
|
||||
{ PS_CPU::GSREG_PC_NEXT, "NPC", "Next PC", 4 },
|
||||
{ PS_CPU::GSREG_IN_BD_SLOT, "INBD", "In Branch Delay Slot", 1 },
|
||||
{ 0, "------", "", 0xFFFF },
|
||||
{ PS_CPU::GSREG_GPR + 1, "at", "Assembler Temporary", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 2, "v0", "Return Value 0", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 3, "v1", "Return Value 1", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 4, "a0", "Argument 0", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 5, "a1", "Argument 1", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 6, "a2", "Argument 2", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 7, "a3", "Argument 3", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 8, "t0", "Temporary 0", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 9, "t1", "Temporary 1", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 10, "t2", "Temporary 2", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 11, "t3", "Temporary 3", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 12, "t4", "Temporary 4", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 13, "t5", "Temporary 5", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 14, "t6", "Temporary 6", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 15, "t7", "Temporary 7", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 16, "s0", "Subroutine Reg Var 0", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 17, "s1", "Subroutine Reg Var 1", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 18, "s2", "Subroutine Reg Var 2", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 19, "s3", "Subroutine Reg Var 3", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 20, "s4", "Subroutine Reg Var 4", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 21, "s5", "Subroutine Reg Var 5", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 22, "s6", "Subroutine Reg Var 6", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 23, "s7", "Subroutine Reg Var 7", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 24, "t8", "Temporary 8", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 25, "t9", "Temporary 9", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 26, "k0", "Interrupt/Trap Handler Reg 0", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 27, "k1", "Interrupt/Trap Handler Reg 1", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 28, "gp", "Global Pointer", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 29, "sp", "Stack Pointer", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 30, "s8", "Subroutine Reg Var 8/Frame Pointer", 4 },
|
||||
{ PS_CPU::GSREG_GPR + 31, "ra", "Return Address", 4 },
|
||||
{ 0, "------", "", 0xFFFF },
|
||||
|
||||
{ PS_CPU::GSREG_SR, "SR", "Status Register", 4 },
|
||||
{ PS_CPU::GSREG_CAUSE, "CAU","Cause Register", 4 },
|
||||
{ PS_CPU::GSREG_EPC, "EPC", "EPC Register", 4 },
|
||||
{ 0, "", "", 0 }
|
||||
};
|
||||
|
||||
static uint32 GetRegister_CPU(const unsigned int id, char *special, const uint32 special_len)
|
||||
{
|
||||
return(CPU->GetRegister(id, special, special_len));
|
||||
}
|
||||
|
||||
static void SetRegister_CPU(const unsigned int id, uint32 value)
|
||||
{
|
||||
CPU->SetRegister(id, value);
|
||||
}
|
||||
|
||||
static RegGroupType CPURegsGroup =
|
||||
{
|
||||
NULL,
|
||||
Regs_CPU,
|
||||
GetRegister_CPU,
|
||||
SetRegister_CPU
|
||||
};
|
||||
|
||||
|
||||
bool DBG_Init(void)
|
||||
{
|
||||
CPUHook = NULL;
|
||||
CPUHookContinuous = false;
|
||||
FoundBPoint = false;
|
||||
|
||||
BTEnabled = false;
|
||||
BTIndex = false;
|
||||
memset(BTEntries, 0, sizeof(BTEntries));
|
||||
|
||||
MDFNDBG_AddRegGroup(&CPURegsGroup);
|
||||
MDFNDBG_AddRegGroup(&MiscRegsGroup);
|
||||
MDFNDBG_AddRegGroup(&SPURegsGroup);
|
||||
MDFNDBG_AddRegGroup(&SPUVoicesRegsGroup);
|
||||
ASpace_Add(GetAddressSpaceBytes, PutAddressSpaceBytes, "cpu", "CPU Physical", 32);
|
||||
ASpace_Add(GetAddressSpaceBytes, PutAddressSpaceBytes, "ram", "CPU Main Ram", 21);
|
||||
ASpace_Add(GetAddressSpaceBytes, PutAddressSpaceBytes, "spu", "SPU RAM", 19);
|
||||
ASpace_Add(GetAddressSpaceBytes, PutAddressSpaceBytes, "gpu", "GPU RAM", 20);
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef __MDFN_PSX_DEBUG_H
|
||||
#define __MDFN_PSX_DEBUG_H
|
||||
|
||||
#ifdef WANT_DEBUGGER
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
extern DebuggerInfoStruct PSX_DBGInfo;
|
||||
|
||||
bool DBG_Init(void);
|
||||
|
||||
void DBG_Break(void);
|
||||
|
||||
void DBG_GPUScanlineHook(unsigned scanline);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,416 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "psx.h"
|
||||
|
||||
#define trio_snprintf snprintf
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
struct OpEntry
|
||||
{
|
||||
uint32 mask;
|
||||
uint32 value;
|
||||
const char *mnemonic;
|
||||
const char *format;
|
||||
};
|
||||
|
||||
#define MASK_OP (0x3FU << 26)
|
||||
#define MASK_FUNC (0x3FU)
|
||||
#define MASK_RS (0x1FU << 21)
|
||||
#define MASK_RT (0x1FU << 16)
|
||||
#define MASK_RD (0x1FU << 11)
|
||||
#define MASK_SA (0x1FU << 6)
|
||||
|
||||
#define MK_OP(mnemonic, format, op, func, extra_mask) { MASK_OP | (op ? 0 : MASK_FUNC) | extra_mask, ((unsigned)op << 26) | func, mnemonic, format }
|
||||
|
||||
#define MK_OP_REGIMM(mnemonic, regop) { MASK_OP | MASK_RT, (0x01U << 26) | (regop << 16), mnemonic, "s, p" }
|
||||
|
||||
|
||||
#define MK_COPZ(z) { MASK_OP | (0x1U << 25), (0x1U << 25) | ((0x10U | z) << 26), "cop" #z, "F" }
|
||||
#define MK_COP0_FUNC(mnemonic, func) { MASK_OP | (0x1U << 25) | MASK_FUNC, (0x10U << 26) | (0x1U << 25) | func, mnemonic, "" }
|
||||
|
||||
#define MK_COPZ_XFER(z, mnemonic, format, xf) { MASK_OP | (0x1FU << 21), ((0x10U | z) << 26) | (xf << 21), mnemonic, format }
|
||||
|
||||
#define MK_GTE(mnemonic, format, func) { MASK_OP | (0x1U << 25) | MASK_FUNC, (0x1U << 25) | (0x12U << 26) | func, mnemonic, format }
|
||||
|
||||
static OpEntry ops[] =
|
||||
{
|
||||
MK_OP("nop", "", 0, 0, MASK_RT | MASK_RD | MASK_SA),
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
MK_OP("sll", "d, t, a", 0, 0, 0),
|
||||
MK_OP("srl", "d, t, a", 0, 2, 0),
|
||||
MK_OP("sra", "d, t, a", 0, 3, 0),
|
||||
|
||||
MK_OP("sllv", "d, t, s", 0, 4, 0),
|
||||
MK_OP("srlv", "d, t, s", 0, 6, 0),
|
||||
MK_OP("srav", "d, t, s", 0, 7, 0),
|
||||
|
||||
MK_OP("jr", "s", 0, 8, 0),
|
||||
MK_OP("jalr", "d, s", 0, 9, 0),
|
||||
|
||||
MK_OP("syscall", "", 0, 12, 0), // TODO
|
||||
MK_OP("break", "", 0, 13, 0), // TODO
|
||||
|
||||
MK_OP("mfhi", "d", 0, 16, 0),
|
||||
MK_OP("mthi", "s", 0, 17, 0),
|
||||
MK_OP("mflo", "d", 0, 18, 0),
|
||||
MK_OP("mtlo", "s", 0, 19, 0),
|
||||
|
||||
MK_OP("mult", "s, t", 0, 24, 0),
|
||||
MK_OP("multu", "s, t", 0, 25, 0),
|
||||
MK_OP("div", "s, t", 0, 26, 0),
|
||||
MK_OP("divu", "s, t", 0, 27, 0),
|
||||
|
||||
MK_OP("add", "d, s, t", 0, 32, 0),
|
||||
MK_OP("addu", "d, s, t", 0, 33, 0),
|
||||
MK_OP("sub", "d, s, t", 0, 34, 0),
|
||||
MK_OP("subu", "d, s, t", 0, 35, 0),
|
||||
MK_OP("and", "d, s, t", 0, 36, 0),
|
||||
MK_OP("or", "d, s, t", 0, 37, 0),
|
||||
MK_OP("xor", "d, s, t", 0, 38, 0),
|
||||
MK_OP("nor", "d, s, t", 0, 39, 0),
|
||||
MK_OP("slt", "d, s, t", 0, 42, 0),
|
||||
MK_OP("sltu", "d, s, t", 0, 43, 0),
|
||||
|
||||
MK_OP_REGIMM("bgez", 0x01),
|
||||
MK_OP_REGIMM("bgezal", 0x11),
|
||||
MK_OP_REGIMM("bltz", 0x00),
|
||||
MK_OP_REGIMM("bltzal", 0x10),
|
||||
|
||||
|
||||
MK_OP("j", "P", 2, 0, 0),
|
||||
MK_OP("jal", "P", 3, 0, 0),
|
||||
|
||||
MK_OP("beq", "s, t, p", 4, 0, 0),
|
||||
MK_OP("bne", "s, t, p", 5, 0, 0),
|
||||
MK_OP("blez", "s, p", 6, 0, 0),
|
||||
MK_OP("bgtz", "s, p", 7, 0, 0),
|
||||
|
||||
MK_OP("addi", "t, s, i", 8, 0, 0),
|
||||
MK_OP("addiu", "t, s, i", 9, 0, 0),
|
||||
MK_OP("slti", "t, s, i", 10, 0, 0),
|
||||
MK_OP("sltiu", "t, s, i", 11, 0, 0),
|
||||
|
||||
MK_OP("andi", "t, s, z", 12, 0, 0),
|
||||
|
||||
MK_OP("ori", "t, s, z", 13, 0, 0),
|
||||
MK_OP("xori", "t, s, z", 14, 0, 0),
|
||||
MK_OP("lui", "t, z", 15, 0, 0),
|
||||
|
||||
MK_COPZ_XFER(0, "mfc0", "t, 0", 0x00),
|
||||
MK_COPZ_XFER(1, "mfc1", "t, ?", 0x00),
|
||||
MK_COPZ_XFER(2, "mfc2", "t, g", 0x00),
|
||||
MK_COPZ_XFER(3, "mfc3", "t, ?", 0x00),
|
||||
|
||||
MK_COPZ_XFER(0, "mtc0", "t, 0", 0x04),
|
||||
MK_COPZ_XFER(1, "mtc1", "t, ?", 0x04),
|
||||
MK_COPZ_XFER(2, "mtc2", "t, g", 0x04),
|
||||
MK_COPZ_XFER(3, "mtc3", "t, ?", 0x04),
|
||||
|
||||
MK_COPZ_XFER(0, "cfc0", "t, ?", 0x02),
|
||||
MK_COPZ_XFER(1, "cfc1", "t, ?", 0x02),
|
||||
MK_COPZ_XFER(2, "cfc2", "t, G", 0x02),
|
||||
MK_COPZ_XFER(3, "cfc3", "t, ?", 0x02),
|
||||
|
||||
MK_COPZ_XFER(0, "ctc0", "t, ?", 0x06),
|
||||
MK_COPZ_XFER(1, "ctc1", "t, ?", 0x06),
|
||||
MK_COPZ_XFER(2, "ctc2", "t, G", 0x06),
|
||||
MK_COPZ_XFER(3, "ctc3", "t, ?", 0x06),
|
||||
|
||||
// COP0 stuff here
|
||||
MK_COP0_FUNC("rfe", 0x10),
|
||||
|
||||
MK_OP("lwc0", "?, i(s)", 0x30, 0, 0),
|
||||
MK_OP("lwc1", "?, i(s)", 0x31, 0, 0),
|
||||
MK_OP("lwc2", "h, i(s)", 0x32, 0, 0),
|
||||
MK_OP("lwc3", "?, i(s)", 0x33, 0, 0),
|
||||
|
||||
MK_OP("swc0", "?, i(s)", 0x38, 0, 0),
|
||||
MK_OP("swc1", "?, i(s)", 0x39, 0, 0),
|
||||
MK_OP("swc2", "h, i(s)", 0x3A, 0, 0),
|
||||
MK_OP("swc3", "?, i(s)", 0x3B, 0, 0),
|
||||
|
||||
MK_OP("lb", "t, i(s)", 0x20, 0, 0),
|
||||
MK_OP("lh", "t, i(s)", 0x21, 0, 0),
|
||||
MK_OP("lwl", "t, i(s)", 0x22, 0, 0),
|
||||
MK_OP("lw", "t, i(s)", 0x23, 0, 0),
|
||||
MK_OP("lbu", "t, i(s)", 0x24, 0, 0),
|
||||
MK_OP("lhu", "t, i(s)", 0x25, 0, 0),
|
||||
MK_OP("lwr", "t, i(s)", 0x26, 0, 0),
|
||||
|
||||
MK_OP("sb", "t, i(s)", 0x28, 0, 0),
|
||||
MK_OP("sh", "t, i(s)", 0x29, 0, 0),
|
||||
MK_OP("swl", "t, i(s)", 0x2A, 0, 0),
|
||||
MK_OP("sw", "t, i(s)", 0x2B, 0, 0),
|
||||
MK_OP("swr", "t, i(s)", 0x2E, 0, 0),
|
||||
|
||||
//
|
||||
// GTE specific instructions
|
||||
//
|
||||
// sf mx v cv lm
|
||||
//
|
||||
MK_GTE("rtps", "#sf# #lm#", 0x00),
|
||||
MK_GTE("rtps", "#sf# #lm#", 0x01),
|
||||
MK_GTE("nclip", "", 0x06),
|
||||
MK_GTE("op", "#sf# #lm#", 0x0C),
|
||||
|
||||
MK_GTE("dpcs", "#sf# #lm#", 0x10),
|
||||
MK_GTE("intpl", "#sf# #lm#", 0x11),
|
||||
MK_GTE("mvmva", "#sf# #mx# #v# #cv# #lm#", 0x12),
|
||||
MK_GTE("ncds", "#sf# #lm#", 0x13),
|
||||
MK_GTE("cdp", "#sf# #lm#", 0x14),
|
||||
MK_GTE("ncdt", "#sf# #lm#", 0x16),
|
||||
MK_GTE("dcpl", "#sf# #lm#", 0x1A),
|
||||
MK_GTE("nccs", "#sf# #lm#", 0x1B),
|
||||
MK_GTE("cc", "#sf# #lm#", 0x1C),
|
||||
MK_GTE("ncs", "#sf# #lm#", 0x1E),
|
||||
MK_GTE("nct", "#sf# #lm#", 0x20),
|
||||
MK_GTE("sqr", "#sf# #lm#", 0x28),
|
||||
MK_GTE("dcpl", "#sf# #lm#", 0x29),
|
||||
MK_GTE("dpct", "#sf# #lm#", 0x2A),
|
||||
MK_GTE("avsz3", "", 0x2D),
|
||||
MK_GTE("avsz4", "", 0x2E),
|
||||
MK_GTE("rtpt", "#sf# #lm#", 0x30),
|
||||
MK_GTE("gpf", "#sf# #lm#", 0x3D),
|
||||
MK_GTE("gpl", "#sf# #lm#", 0x3E),
|
||||
MK_GTE("ncct", "#sf# #lm#", 0x3F),
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
MK_COPZ(0),
|
||||
MK_COPZ(1),
|
||||
MK_COPZ(2),
|
||||
MK_COPZ(3),
|
||||
|
||||
{ 0, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
std::string DisassembleMIPS(uint32 PC, uint32 instr)
|
||||
{
|
||||
std::string ret = "UNKNOWN";
|
||||
unsigned int rs = (instr >> 21) & 0x1F;
|
||||
unsigned int rt = (instr >> 16) & 0x1F;
|
||||
unsigned int rd = (instr >> 11) & 0x1F;
|
||||
unsigned int shamt = (instr >> 6) & 0x1F;
|
||||
unsigned int immediate = (int32)(int16)(instr & 0xFFFF);
|
||||
unsigned int immediate_ze = (instr & 0xFFFF);
|
||||
unsigned int jt = instr & ((1 << 26) - 1);
|
||||
|
||||
static const char *gpr_names[32] =
|
||||
{
|
||||
"r0", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
|
||||
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra"
|
||||
};
|
||||
|
||||
static const char *cop0_names[32] =
|
||||
{
|
||||
"CPR0", "CPR1", "CPR2", "BPC", "CPR4", "BDA", "TAR", "DCIC", "CPR8", "BDAM", "CPR10", "BPCM", "SR", "CAUSE", "EPC", "PRID",
|
||||
"ERREG", "CPR17", "CPR18", "CPR19", "CPR20", "CPR21", "CPR22", "CPR23", "CPR24", "CPR25", "CPR26", "CPR27", "CPR28", "CPR29", "CPR30", "CPR31"
|
||||
};
|
||||
|
||||
static const char *gte_cr_names[32] =
|
||||
{
|
||||
"R11R12", "R13R21", "R22R23", "R31R32", "R33", "TRX", "TRY", "TRZ", "L11L12", "L13L21", "L22L23", "L31L32", "L33", "RBK", "GBK", "BBK",
|
||||
"LR1LR2", "LR3LG1", "LG2LG3", "LB1LB2", "LB3", "RFC", "GFC", "BFC", "OFX", "OFY", "H", "DQA", "DQB", "ZSF3", "ZSF4", "FLAG"
|
||||
};
|
||||
|
||||
static const char *gte_dr_names[32] =
|
||||
{
|
||||
"VXY0", "VZ0", "VXY1", "VZ1", "VXY2", "VZ2", "RGB", "OTZ", "IR0", "IR1", "IR2", "IR3", "SXY0", "SXY1", "SXY2", "SXYP",
|
||||
"SZ0", "SZ1", "SZ2", "SZ3", "RGB0", "RGB1", "RGB2", "RES1", "MAC0", "MAC1", "MAC2", "MAC3", "IRGB", "ORGB", "LZCS", "LZCR"
|
||||
};
|
||||
|
||||
OpEntry *op = ops;
|
||||
|
||||
while(op->mnemonic)
|
||||
{
|
||||
if((instr & op->mask) == op->value)
|
||||
{
|
||||
// a = shift amount
|
||||
// s = rs
|
||||
// t = rt
|
||||
// d = rd
|
||||
// i = immediate
|
||||
// z = immediate, zero-extended
|
||||
// p = PC + 4 + immediate
|
||||
// P = ((PC + 4) & 0xF0000000) | (26bitval << 2)
|
||||
//
|
||||
// 0 = rd(cop0 registers)
|
||||
// c = rd(copz data registers)
|
||||
// C = rd(copz control registers)
|
||||
// g = rd(GTE data registers)
|
||||
// G = rd(GTE control registers)
|
||||
// h = rt(GTE data registers)
|
||||
|
||||
char s_a[16];
|
||||
char s_i[16];
|
||||
char s_z[16];
|
||||
char s_p[16];
|
||||
char s_P[16];
|
||||
char s_c[16];
|
||||
char s_C[16];
|
||||
|
||||
trio_snprintf(s_a, sizeof(s_a), "%d", shamt);
|
||||
|
||||
if(immediate < 0)
|
||||
trio_snprintf(s_i, sizeof(s_i), "%d", immediate);
|
||||
else
|
||||
trio_snprintf(s_i, sizeof(s_i), "0x%04x", (uint32)immediate);
|
||||
|
||||
trio_snprintf(s_z, sizeof(s_z), "0x%04x", immediate_ze);
|
||||
|
||||
trio_snprintf(s_p, sizeof(s_p), "0x%08x", PC + 4 + (immediate << 2));
|
||||
|
||||
trio_snprintf(s_P, sizeof(s_P), "0x%08x", ((PC + 4) & 0xF0000000) | (jt << 2));
|
||||
|
||||
trio_snprintf(s_c, sizeof(s_c), "CPR%d", rd);
|
||||
trio_snprintf(s_C, sizeof(s_C), "CCR%d", rd);
|
||||
|
||||
ret = std::string(op->mnemonic);
|
||||
ret.append(10 - ret.size(), ' ');
|
||||
|
||||
for(unsigned int i = 0; i < strlen(op->format); i++)
|
||||
{
|
||||
switch(op->format[i])
|
||||
{
|
||||
case '#':
|
||||
// sf mx v cv lm
|
||||
{
|
||||
char as[16];
|
||||
|
||||
as[0] = 0;
|
||||
if(!strncmp(&op->format[i], "#sf#", 4))
|
||||
{
|
||||
i += 3;
|
||||
trio_snprintf(as, 16, "sf=%d", (int)(bool)(instr & (1 << 19)));
|
||||
}
|
||||
else if(!strncmp(&op->format[i], "#mx#", 4))
|
||||
{
|
||||
i += 3;
|
||||
trio_snprintf(as, 16, "mx=%d", (instr >> 17) & 0x3);
|
||||
}
|
||||
else if(!strncmp(&op->format[i], "#v#", 3))
|
||||
{
|
||||
i += 2;
|
||||
trio_snprintf(as, 16, "v=%d", (instr >> 15) & 0x3);
|
||||
}
|
||||
else if(!strncmp(&op->format[i], "#cv#", 4))
|
||||
{
|
||||
i += 3;
|
||||
trio_snprintf(as, 16, "cv=%d", (instr >> 13) & 0x3);
|
||||
}
|
||||
else if(!strncmp(&op->format[i], "#lm#", 4))
|
||||
{
|
||||
i += 3;
|
||||
trio_snprintf(as, 16, "lm=%d", (int)(bool)(instr & (1 << 10)));
|
||||
}
|
||||
ret.append(as);
|
||||
}
|
||||
break;
|
||||
case 'F':
|
||||
{
|
||||
char s_F[16];
|
||||
|
||||
trio_snprintf(s_F, 16, "0x%07x", instr & 0x1FFFFFF);
|
||||
ret.append(s_F);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
ret.append(gte_dr_names[rt]);
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
ret.append(gte_dr_names[rd]);
|
||||
break;
|
||||
|
||||
case 'G':
|
||||
ret.append(gte_cr_names[rd]);
|
||||
break;
|
||||
|
||||
case '0':
|
||||
ret.append(cop0_names[rd]);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
ret.append(s_c);
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
ret.append(s_C);
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
ret.append(s_a);
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
ret.append(s_i);
|
||||
break;
|
||||
|
||||
case 'z':
|
||||
ret.append(s_z);
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
ret.append(s_p);
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
ret.append(s_P);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
ret.append(gpr_names[rs]);
|
||||
break;
|
||||
|
||||
case 't':
|
||||
ret.append(gpr_names[rt]);
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
ret.append(gpr_names[rd]);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret.append(1, op->format[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
op++;
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include "emuware/emuware.h"
|
||||
#include <string>
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
std::string DisassembleMIPS(uint32 PC, uint32 instr);
|
||||
|
||||
}
|
|
@ -0,0 +1,821 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "psx.h"
|
||||
#include "mdec.h"
|
||||
#include "cdc.h"
|
||||
#include "spu.h"
|
||||
|
||||
//#include <map>
|
||||
|
||||
// Notes: DMA tested to abort when
|
||||
|
||||
/* Notes:
|
||||
|
||||
Channel 4(SPU):
|
||||
Write:
|
||||
Doesn't seem to work properly with CHCR=0x01000001
|
||||
Hung when CHCR=0x11000601
|
||||
|
||||
Channel 6:
|
||||
DMA hangs if D28 of CHCR is 0?
|
||||
D1 did not have an apparent effect.
|
||||
|
||||
*/
|
||||
|
||||
enum
|
||||
{
|
||||
CH_MDEC_IN = 0,
|
||||
CH_MDEC_OUT = 1,
|
||||
CH_GPU = 2,
|
||||
CH_CDC = 3,
|
||||
CH_SPU = 4,
|
||||
CH_FIVE = 5,
|
||||
CH_OT = 6,
|
||||
};
|
||||
|
||||
|
||||
// RunChannels(128 - whatevercounter);
|
||||
//
|
||||
// GPU next event, std::max<128, wait_time>, or something similar, for handling FIFO.
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
static int32 DMACycleCounter;
|
||||
|
||||
static uint32 DMAControl;
|
||||
static uint32 DMAIntControl;
|
||||
static uint8 DMAIntStatus;
|
||||
static bool IRQOut;
|
||||
|
||||
struct Channel
|
||||
{
|
||||
uint32 BaseAddr;
|
||||
uint32 BlockControl;
|
||||
uint32 ChanControl;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
uint32 CurAddr;
|
||||
uint16 WordCounter;
|
||||
|
||||
//
|
||||
//
|
||||
int32 ClockCounter;
|
||||
};
|
||||
|
||||
static Channel DMACH[7];
|
||||
static pscpu_timestamp_t lastts;
|
||||
|
||||
|
||||
static const char *PrettyChannelNames[7] = { "MDEC IN", "MDEC OUT", "GPU", "CDC", "SPU", "PIO", "OTC" };
|
||||
|
||||
void DMA_Init(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void DMA_Kill(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static INLINE void RecalcIRQOut(void)
|
||||
{
|
||||
bool irqo;
|
||||
|
||||
irqo = (bool)(DMAIntStatus & ((DMAIntControl >> 16) & 0x7F));
|
||||
irqo &= (DMAIntControl >> 23) & 1;
|
||||
|
||||
// I think it's logical OR, not XOR/invert. Still kind of weird, maybe it actually does something more complicated?
|
||||
//irqo ^= (DMAIntControl >> 15) & 1;
|
||||
irqo |= (DMAIntControl >> 15) & 1;
|
||||
|
||||
IRQOut = irqo;
|
||||
IRQ_Assert(IRQ_DMA, irqo);
|
||||
}
|
||||
|
||||
void DMA_ResetTS(void)
|
||||
{
|
||||
lastts = 0;
|
||||
}
|
||||
|
||||
void DMA_Power(void)
|
||||
{
|
||||
lastts = 0;
|
||||
|
||||
memset(DMACH, 0, sizeof(DMACH));
|
||||
|
||||
DMACycleCounter = 128;
|
||||
|
||||
DMAControl = 0;
|
||||
DMAIntControl = 0;
|
||||
DMAIntStatus = 0;
|
||||
RecalcIRQOut();
|
||||
}
|
||||
|
||||
void PSX_SetDMASuckSuck(unsigned);
|
||||
|
||||
static INLINE bool ChCan(const unsigned ch, const uint32 CRModeCache)
|
||||
{
|
||||
switch(ch)
|
||||
{
|
||||
default:
|
||||
abort();
|
||||
|
||||
case CH_MDEC_IN:
|
||||
return(MDEC_DMACanWrite());
|
||||
|
||||
case CH_MDEC_OUT:
|
||||
return(MDEC_DMACanRead());
|
||||
|
||||
case CH_GPU:
|
||||
if(CRModeCache & 0x1)
|
||||
return(GPU->DMACanWrite());
|
||||
else
|
||||
return(true);
|
||||
|
||||
case CH_CDC:
|
||||
return(true);
|
||||
|
||||
case CH_SPU:
|
||||
return(true);
|
||||
|
||||
case CH_FIVE:
|
||||
return(false);
|
||||
|
||||
case CH_OT:
|
||||
return((bool)(DMACH[ch].ChanControl & (1U << 28)));
|
||||
}
|
||||
}
|
||||
|
||||
static void RecalcHalt(void)
|
||||
{
|
||||
bool Halt = false;
|
||||
unsigned ch = 0;
|
||||
|
||||
for(ch = 0; ch < 7; ch++)
|
||||
{
|
||||
if(DMACH[ch].ChanControl & (1U << 24))
|
||||
{
|
||||
if(!(DMACH[ch].ChanControl & (7U << 8)))
|
||||
{
|
||||
if(DMACH[ch].WordCounter > 0)
|
||||
{
|
||||
Halt = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
if(DMACH[ch].ChanControl & 0x100) // DMA doesn't hog the bus when this bit is set, though the DMA takes longer.
|
||||
continue;
|
||||
|
||||
if(ch == 4 || ch == 5) // Not sure if these channels will typically hog the bus or not...investigate.
|
||||
continue;
|
||||
|
||||
if(!(DMACH[ch].ChanControl & (1U << 10))) // Not sure about HOGGERYNESS with linked-list mode, and it likely wouldn't work well either in regards
|
||||
// to GPU commands due to the rather large DMA update granularity.
|
||||
{
|
||||
if((DMACH[ch].WordCounter > 0) || ChCan(ch, DMACH[ch].ChanControl & 0x1))
|
||||
{
|
||||
Halt = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
if((DMACH[0].WordCounter || (DMACH[0].ChanControl & (1 << 24))) && (DMACH[0].ChanControl & 0x200) /*&& MDEC_DMACanWrite()*/)
|
||||
Halt = true;
|
||||
|
||||
if((DMACH[1].WordCounter || (DMACH[1].ChanControl & (1 << 24))) && (DMACH[1].ChanControl & 0x200) && (DMACH[1].WordCounter || MDEC_DMACanRead()))
|
||||
Halt = true;
|
||||
|
||||
if((DMACH[2].WordCounter || (DMACH[2].ChanControl & (1 << 24))) && (DMACH[2].ChanControl & 0x200) && ((DMACH[2].ChanControl & 0x1) && (DMACH[2].WordCounter || GPU->DMACanWrite())))
|
||||
Halt = true;
|
||||
|
||||
if((DMACH[3].WordCounter || (DMACH[3].ChanControl & (1 << 24))) && !(DMACH[3].ChanControl & 0x100))
|
||||
Halt = true;
|
||||
|
||||
if(DMACH[6].WordCounter || (DMACH[6].ChanControl & (1 << 24)))
|
||||
Halt = true;
|
||||
#endif
|
||||
|
||||
//printf("Halt: %d\n", Halt);
|
||||
|
||||
if(!Halt && (DMACH[2].ChanControl & (1U << 24)) && ((DMACH[2].ChanControl & 0x700) == 0x200) && ChCan(2, DMACH[2].ChanControl))
|
||||
{
|
||||
unsigned tmp = DMACH[2].BlockControl & 0xFFFF;
|
||||
|
||||
if(tmp > 0)
|
||||
tmp--;
|
||||
|
||||
if(tmp > 200) // Due to 8-bit limitations in the CPU core.
|
||||
tmp = 200;
|
||||
|
||||
PSX_SetDMASuckSuck(tmp);
|
||||
}
|
||||
else
|
||||
PSX_SetDMASuckSuck(0);
|
||||
|
||||
CPU->SetHalt(Halt);
|
||||
}
|
||||
|
||||
|
||||
static INLINE void ChRW(const unsigned ch, const uint32 CRModeCache, uint32 *V, int32 *offset)
|
||||
{
|
||||
unsigned extra_cyc_overhead = 0;
|
||||
|
||||
switch(ch)
|
||||
{
|
||||
default:
|
||||
abort();
|
||||
break;
|
||||
|
||||
case CH_MDEC_IN:
|
||||
if(CRModeCache & 0x1)
|
||||
MDEC_DMAWrite(*V);
|
||||
else
|
||||
*V = 0;
|
||||
break;
|
||||
|
||||
case CH_MDEC_OUT:
|
||||
if(CRModeCache & 0x1)
|
||||
{
|
||||
}
|
||||
else
|
||||
*V = MDEC_DMARead(offset);
|
||||
break;
|
||||
|
||||
case CH_GPU:
|
||||
if(CRModeCache & 0x1)
|
||||
GPU->WriteDMA(*V);
|
||||
else
|
||||
*V = GPU->ReadDMA();
|
||||
break;
|
||||
|
||||
case CH_CDC:
|
||||
// 0x1f801018 affects CDC DMA timing.
|
||||
#if 0
|
||||
if(CRModeCache & 0x100) // For CDC DMA(at least): When this bit is set, DMA controller doesn't appear to hog the (RAM?) bus.
|
||||
{
|
||||
if(CRModeCache & 0x00400000) // For CDC DMA(at least): When this bit is set, DMA controller appears to get even less bus time(or has a lower priority??)
|
||||
{
|
||||
DMACH[ch].ClockCounter -= 44 * 20 / 12;
|
||||
}
|
||||
else
|
||||
{
|
||||
DMACH[ch].ClockCounter -= 29 * 20 / 12;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DMACH[ch].ClockCounter -= 23 * 20 / 12; // (23 + 1) = 24. (Though closer to 24.5 or 24.4 on average per tests on a PS1)
|
||||
}
|
||||
#endif
|
||||
if(CRModeCache & 0x1)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
extra_cyc_overhead = 8; // FIXME: Test.
|
||||
*V = CDC->DMARead(); // Note: Legend of Mana's opening movie is sensitive to DMA timing, including CDC.
|
||||
}
|
||||
break;
|
||||
|
||||
case CH_SPU:
|
||||
// 0x1f801014 affects SPU DMA timing.
|
||||
// Wild conjecture about 0x1f801014:
|
||||
//
|
||||
// & 0x0000000F
|
||||
// & 0x000001E0 --- Used if (& 0x20000000) == 0?
|
||||
// & 0x00001000 --- Double total bus cycle time if value == 0?
|
||||
// & 0x0f000000 --- (value << 1) 33MHz cycles, bus cycle extension(added to 4?)?
|
||||
// & 0x20000000 ---
|
||||
//
|
||||
//
|
||||
// TODO?: SPU DMA will "complete" much faster if there's a mismatch between the CHCR read/write mode bit and the SPU control register DMA mode.
|
||||
//
|
||||
//
|
||||
// Investigate: SPU DMA doesn't seem to work right if the value written to 0x1F801DAA doesn't have the upper bit set to 1(0x8000) on a PS1.
|
||||
|
||||
extra_cyc_overhead = 47; // Should be closer to 69, average, but actual timing is...complicated.
|
||||
|
||||
if(CRModeCache & 0x1)
|
||||
SPU->WriteDMA(*V);
|
||||
else
|
||||
*V = SPU->ReadDMA();
|
||||
break;
|
||||
|
||||
case CH_FIVE:
|
||||
if(CRModeCache & 0x1)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
*V = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case CH_OT:
|
||||
if(DMACH[ch].WordCounter == 1)
|
||||
*V = 0xFFFFFF;
|
||||
else
|
||||
*V = (DMACH[ch].CurAddr - 4) & 0x1FFFFF;
|
||||
break;
|
||||
}
|
||||
|
||||
// GROSS APPROXIMATION, shoehorning multiple effects together, TODO separate(especially SPU and CDC)
|
||||
DMACH[ch].ClockCounter -= std::max<int>(extra_cyc_overhead, (CRModeCache & 0x100) ? 7 : 0);
|
||||
}
|
||||
|
||||
//
|
||||
// Remember to handle an end condition on the same iteration of the while(DMACH[ch].ClockCounter > 0) loop that caused it,
|
||||
// otherwise RecalcHalt() might take the CPU out of a halted state before the end-of-DMA is signaled(especially a problem considering our largeish
|
||||
// DMA update timing granularity).
|
||||
//
|
||||
static INLINE void RunChannelI(const unsigned ch, const uint32 CRModeCache, int32 clocks)
|
||||
{
|
||||
//const uint32 dc = (DMAControl >> (ch * 4)) & 0xF;
|
||||
|
||||
DMACH[ch].ClockCounter += clocks;
|
||||
|
||||
while(MDFN_LIKELY(DMACH[ch].ClockCounter > 0))
|
||||
{
|
||||
if(DMACH[ch].WordCounter == 0) // Begin WordCounter reload.
|
||||
{
|
||||
if(!(DMACH[ch].ChanControl & (1 << 24))) // Needed for the forced-DMA-stop kludge(see DMA_Write()).
|
||||
break;
|
||||
|
||||
if(!ChCan(ch, CRModeCache))
|
||||
break;
|
||||
|
||||
DMACH[ch].CurAddr = DMACH[ch].BaseAddr;
|
||||
|
||||
if(CRModeCache & (1U << 10))
|
||||
{
|
||||
uint32 header;
|
||||
|
||||
if(MDFN_UNLIKELY(DMACH[ch].CurAddr & 0x800000))
|
||||
{
|
||||
DMACH[ch].ChanControl &= ~(0x11 << 24);
|
||||
DMAIntControl |= 0x8000;
|
||||
RecalcIRQOut();
|
||||
break;
|
||||
}
|
||||
|
||||
header = MainRAM.ReadU32(DMACH[ch].CurAddr & 0x1FFFFC);
|
||||
DMACH[ch].CurAddr = (DMACH[ch].CurAddr + 4) & 0xFFFFFF;
|
||||
|
||||
DMACH[ch].WordCounter = header >> 24;
|
||||
DMACH[ch].BaseAddr = header & 0xFFFFFF;
|
||||
|
||||
// printf to debug Soul Reaver ;)
|
||||
//if(DMACH[ch].WordCounter > 0x10)
|
||||
// printf("What the lala? 0x%02x @ 0x%08x\n", DMACH[ch].WordCounter, DMACH[ch].CurAddr - 4);
|
||||
|
||||
if(DMACH[ch].WordCounter)
|
||||
DMACH[ch].ClockCounter -= 15;
|
||||
else
|
||||
DMACH[ch].ClockCounter -= 10;
|
||||
|
||||
goto SkipPayloadStuff; // 3 cheers for gluten-free spaghetticode(necessary because the newly-loaded WordCounter might be 0, and we actually
|
||||
// want 0 to mean 0 and not 65536 in this context)!
|
||||
}
|
||||
else
|
||||
{
|
||||
DMACH[ch].WordCounter = DMACH[ch].BlockControl & 0xFFFF;
|
||||
|
||||
if(CRModeCache & (1U << 9))
|
||||
{
|
||||
if(ch == 2) // Technically should apply to all channels, but since we don't implement CPU read penalties for channels other than 2 yet, it's like this to avoid making DMA longer than what games can handle.
|
||||
DMACH[ch].ClockCounter -= 7;
|
||||
|
||||
DMACH[ch].BlockControl = (DMACH[ch].BlockControl & 0xFFFF) | ((DMACH[ch].BlockControl - (1U << 16)) & 0xFFFF0000);
|
||||
}
|
||||
}
|
||||
} // End WordCounter reload.
|
||||
else if(CRModeCache & 0x100) // BLARGH BLARGH FISHWHALE
|
||||
{
|
||||
//printf("LoadWC: %u(oldWC=%u)\n", DMACH[ch].BlockControl & 0xFFFF, DMACH[ch].WordCounter);
|
||||
//MDFN_DispMessage("SPOOOON\n");
|
||||
DMACH[ch].CurAddr = DMACH[ch].BaseAddr;
|
||||
DMACH[ch].WordCounter = DMACH[ch].BlockControl & 0xFFFF;
|
||||
}
|
||||
|
||||
//
|
||||
// Do the payload read/write
|
||||
//
|
||||
{
|
||||
uint32 vtmp;
|
||||
int32 voffs = 0;
|
||||
|
||||
if(MDFN_UNLIKELY(DMACH[ch].CurAddr & 0x800000))
|
||||
{
|
||||
DMACH[ch].ChanControl &= ~(0x11 << 24);
|
||||
DMAIntControl |= 0x8000;
|
||||
RecalcIRQOut();
|
||||
break;
|
||||
}
|
||||
|
||||
if(CRModeCache & 0x1)
|
||||
vtmp = MainRAM.ReadU32(DMACH[ch].CurAddr & 0x1FFFFC);
|
||||
|
||||
ChRW(ch, CRModeCache, &vtmp, &voffs);
|
||||
|
||||
if(!(CRModeCache & 0x1))
|
||||
MainRAM.WriteU32((DMACH[ch].CurAddr + (voffs << 2)) & 0x1FFFFC, vtmp);
|
||||
}
|
||||
|
||||
if(CRModeCache & 0x2)
|
||||
DMACH[ch].CurAddr = (DMACH[ch].CurAddr - 4) & 0xFFFFFF;
|
||||
else
|
||||
DMACH[ch].CurAddr = (DMACH[ch].CurAddr + 4) & 0xFFFFFF;
|
||||
|
||||
DMACH[ch].WordCounter--;
|
||||
DMACH[ch].ClockCounter--;
|
||||
|
||||
SkipPayloadStuff: ;
|
||||
|
||||
if(CRModeCache & 0x100) // BLARGH BLARGH WHALEFISH
|
||||
{
|
||||
DMACH[ch].BaseAddr = DMACH[ch].CurAddr;
|
||||
DMACH[ch].BlockControl = (DMACH[ch].BlockControl & 0xFFFF0000) | DMACH[ch].WordCounter;
|
||||
//printf("SaveWC: %u\n", DMACH[ch].WordCounter);
|
||||
}
|
||||
|
||||
//
|
||||
// Handle channel end condition:
|
||||
//
|
||||
if(DMACH[ch].WordCounter == 0)
|
||||
{
|
||||
bool ChannelEndTC = false;
|
||||
|
||||
if(!(DMACH[ch].ChanControl & (1 << 24))) // Needed for the forced-DMA-stop kludge(see DMA_Write()).
|
||||
break;
|
||||
|
||||
switch((CRModeCache >> 9) & 0x3)
|
||||
{
|
||||
case 0x0:
|
||||
ChannelEndTC = true;
|
||||
break;
|
||||
|
||||
case 0x1:
|
||||
DMACH[ch].BaseAddr = DMACH[ch].CurAddr;
|
||||
if((DMACH[ch].BlockControl >> 16) == 0)
|
||||
ChannelEndTC = true;
|
||||
break;
|
||||
|
||||
case 0x2:
|
||||
case 0x3: // Not sure about 0x3.
|
||||
if(DMACH[ch].BaseAddr == 0xFFFFFF)
|
||||
ChannelEndTC = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(ChannelEndTC)
|
||||
{
|
||||
DMACH[ch].ChanControl &= ~(0x11 << 24);
|
||||
if(DMAIntControl & (1U << (16 + ch)))
|
||||
{
|
||||
DMAIntStatus |= 1U << ch;
|
||||
RecalcIRQOut();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(DMACH[ch].ClockCounter > 0)
|
||||
DMACH[ch].ClockCounter = 0;
|
||||
}
|
||||
|
||||
static INLINE void RunChannel(pscpu_timestamp_t timestamp, int32 clocks, int ch)
|
||||
{
|
||||
// Mask out the bits that the DMA controller will modify during the course of operation.
|
||||
const uint32 CRModeCache = DMACH[ch].ChanControl &~(0x11 << 24);
|
||||
|
||||
switch(ch)
|
||||
{
|
||||
default: abort();
|
||||
|
||||
case 0:
|
||||
if(MDFN_LIKELY(CRModeCache == 0x00000201))
|
||||
RunChannelI(0, 0x00000201, clocks);
|
||||
else
|
||||
RunChannelI(0, CRModeCache, clocks);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if(MDFN_LIKELY(CRModeCache == 0x00000200))
|
||||
RunChannelI(1, 0x00000200, clocks);
|
||||
else
|
||||
RunChannelI(1, CRModeCache, clocks);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if(MDFN_LIKELY(CRModeCache == 0x00000401))
|
||||
RunChannelI(2, 0x00000401, clocks);
|
||||
else if(MDFN_LIKELY(CRModeCache == 0x00000201))
|
||||
RunChannelI(2, 0x00000201, clocks);
|
||||
else if(MDFN_LIKELY(CRModeCache == 0x00000200))
|
||||
RunChannelI(2, 0x00000200, clocks);
|
||||
else
|
||||
RunChannelI(2, CRModeCache, clocks);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if(MDFN_LIKELY(CRModeCache == 0x00000000))
|
||||
RunChannelI(3, 0x00000000, clocks);
|
||||
else if(MDFN_LIKELY(CRModeCache == 0x00000100))
|
||||
RunChannelI(3, 0x00000100, clocks);
|
||||
else
|
||||
RunChannelI(3, CRModeCache, clocks);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if(MDFN_LIKELY(CRModeCache == 0x00000201))
|
||||
RunChannelI(4, 0x00000201, clocks);
|
||||
else if(MDFN_LIKELY(CRModeCache == 0x00000200))
|
||||
RunChannelI(4, 0x00000200, clocks);
|
||||
else
|
||||
RunChannelI(4, CRModeCache, clocks);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
RunChannelI(5, CRModeCache, clocks);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
if(MDFN_LIKELY(CRModeCache == 0x00000002))
|
||||
RunChannelI(6, 0x00000002, clocks);
|
||||
else
|
||||
RunChannelI(6, CRModeCache, clocks);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static INLINE int32 CalcNextEvent(int32 next_event)
|
||||
{
|
||||
if(DMACycleCounter < next_event)
|
||||
next_event = DMACycleCounter;
|
||||
|
||||
return(next_event);
|
||||
}
|
||||
|
||||
pscpu_timestamp_t DMA_Update(const pscpu_timestamp_t timestamp)
|
||||
{
|
||||
// uint32 dc = (DMAControl >> (ch * 4)) & 0xF;
|
||||
int32 clocks = timestamp - lastts;
|
||||
lastts = timestamp;
|
||||
|
||||
GPU->Update(timestamp);
|
||||
MDEC_Run(clocks);
|
||||
|
||||
RunChannel(timestamp, clocks, 0);
|
||||
RunChannel(timestamp, clocks, 1);
|
||||
RunChannel(timestamp, clocks, 2);
|
||||
RunChannel(timestamp, clocks, 3);
|
||||
RunChannel(timestamp, clocks, 4);
|
||||
RunChannel(timestamp, clocks, 6);
|
||||
|
||||
DMACycleCounter -= clocks;
|
||||
while(DMACycleCounter <= 0)
|
||||
DMACycleCounter += 128;
|
||||
|
||||
RecalcHalt();
|
||||
|
||||
return(timestamp + CalcNextEvent(0x10000000));
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void CheckLinkedList(uint32 addr)
|
||||
{
|
||||
std::map<uint32, bool> zoom;
|
||||
|
||||
do
|
||||
{
|
||||
if(zoom[addr])
|
||||
{
|
||||
printf("Bad linked list: 0x%08x\n", addr);
|
||||
break;
|
||||
}
|
||||
zoom[addr] = 1;
|
||||
|
||||
uint32 header = MainRAM.ReadU32(addr & 0x1FFFFC);
|
||||
|
||||
addr = header & 0xFFFFFF;
|
||||
|
||||
} while(addr != 0xFFFFFF && !(addr & 0x800000));
|
||||
}
|
||||
#endif
|
||||
|
||||
void DMA_Write(const pscpu_timestamp_t timestamp, uint32 A, uint32 V)
|
||||
{
|
||||
int ch = (A & 0x7F) >> 4;
|
||||
|
||||
//if(ch == 2 || ch == 7)
|
||||
//PSX_WARNING("[DMA] Write: %08x %08x, DMAIntStatus=%08x", A, V, DMAIntStatus);
|
||||
|
||||
// FIXME if we ever have "accurate" bus emulation
|
||||
V <<= (A & 3) * 8;
|
||||
|
||||
DMA_Update(timestamp);
|
||||
|
||||
if(ch == 7)
|
||||
{
|
||||
switch(A & 0xC)
|
||||
{
|
||||
case 0x0: //fprintf(stderr, "Global DMA control: 0x%08x\n", V);
|
||||
DMAControl = V;
|
||||
RecalcHalt();
|
||||
break;
|
||||
|
||||
case 0x4:
|
||||
//for(int x = 0; x < 7; x++)
|
||||
//{
|
||||
// if(DMACH[x].WordCounter || (DMACH[x].ChanControl & (1 << 24)))
|
||||
// {
|
||||
// fprintf(stderr, "Write DMAIntControl while channel %d active: 0x%08x\n", x, V);
|
||||
// }
|
||||
//}
|
||||
DMAIntControl = V & 0x00ff803f;
|
||||
DMAIntStatus &= ~(V >> 24);
|
||||
|
||||
//if(DMAIntStatus ^ (DMAIntStatus & (V >> 16)))
|
||||
// fprintf(stderr, "DMAINT Fudge: %02x\n", DMAIntStatus ^ (DMAIntStatus & (V >> 16)));
|
||||
DMAIntStatus &= (V >> 16); // THIS IS ALMOST CERTAINLY WRONG AND A HACK. Remove when CDC emulation is better.
|
||||
RecalcIRQOut();
|
||||
break;
|
||||
|
||||
default: PSX_WARNING("[DMA] Unknown write: %08x %08x", A, V);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch(A & 0xC)
|
||||
{
|
||||
case 0x0: DMACH[ch].BaseAddr = V & 0xFFFFFF;
|
||||
break;
|
||||
|
||||
case 0x4: DMACH[ch].BlockControl = V;
|
||||
break;
|
||||
|
||||
case 0xC:
|
||||
case 0x8:
|
||||
{
|
||||
uint32 OldCC = DMACH[ch].ChanControl;
|
||||
|
||||
//printf("CHCR: %u, %08x --- 0x%08x\n", ch, V, DMACH[ch].BlockControl);
|
||||
//
|
||||
// Kludge for DMA timing granularity and other issues. Needs to occur before setting all bits of ChanControl to the new value, to accommodate the
|
||||
// case of a game cancelling DMA and changing the type of DMA(read/write, etc.) at the same time.
|
||||
//
|
||||
if((DMACH[ch].ChanControl & (1 << 24)) && !(V & (1 << 24)))
|
||||
{
|
||||
DMACH[ch].ChanControl &= ~(1 << 24); // Clear bit before RunChannel(), so it will only finish the block it's on at most.
|
||||
RunChannel(timestamp, 128 * 16, ch);
|
||||
DMACH[ch].WordCounter = 0;
|
||||
|
||||
#if 0 // TODO(maybe, need to work out worst-case performance for abnormally/brokenly large block sizes)
|
||||
DMACH[ch].ClockCounter = (1 << 30);
|
||||
RunChannel(timestamp, 1, ch);
|
||||
DMACH[ch].ClockCounter = 0;
|
||||
#endif
|
||||
PSX_WARNING("[DMA] Forced stop for channel %d -- scanline=%d", ch, GPU->GetScanlineNum());
|
||||
//MDFN_DispMessage("[DMA] Forced stop for channel %d", ch);
|
||||
}
|
||||
|
||||
if(ch == 6)
|
||||
DMACH[ch].ChanControl = (V & 0x51000000) | 0x2;
|
||||
else
|
||||
DMACH[ch].ChanControl = V & 0x71770703;
|
||||
|
||||
if(!(OldCC & (1 << 24)) && (V & (1 << 24)))
|
||||
{
|
||||
//if(ch == 0 || ch == 1)
|
||||
// PSX_WARNING("[DMA] Started DMA for channel=%d --- CHCR=0x%08x --- BCR=0x%08x --- scanline=%d", ch, DMACH[ch].ChanControl, DMACH[ch].BlockControl, GPU->GetScanlineNum());
|
||||
|
||||
DMACH[ch].WordCounter = 0;
|
||||
DMACH[ch].ClockCounter = 0;
|
||||
|
||||
//
|
||||
// Viewpoint starts a short MEM->GPU LL DMA and apparently has race conditions that can cause a crash if it doesn't finish almost immediately(
|
||||
// or at least very quickly, which the current DMA granularity has issues with, so run the channel ahead a bit to take of this issue and potentially
|
||||
// games with similar issues).
|
||||
//
|
||||
// Though, Viewpoint isn't exactly a good game, so maybe we shouldn't bother? ;)
|
||||
//
|
||||
// Also, it's needed for RecalcHalt() to work with some semblance of workiness.
|
||||
//
|
||||
RunChannel(timestamp, 64, ch); //std::max<int>(128 - DMACycleCounter, 1)); //64); //1); //128 - DMACycleCounter);
|
||||
}
|
||||
|
||||
RecalcHalt();
|
||||
}
|
||||
break;
|
||||
}
|
||||
PSX_SetEventNT(PSX_EVENT_DMA, timestamp + CalcNextEvent(0x10000000));
|
||||
}
|
||||
|
||||
uint32 DMA_Read(const pscpu_timestamp_t timestamp, uint32 A)
|
||||
{
|
||||
int ch = (A & 0x7F) >> 4;
|
||||
uint32 ret = 0;
|
||||
|
||||
if(ch == 7)
|
||||
{
|
||||
switch(A & 0xC)
|
||||
{
|
||||
default: PSX_WARNING("[DMA] Unknown read: %08x", A);
|
||||
break;
|
||||
|
||||
case 0x0: ret = DMAControl;
|
||||
break;
|
||||
|
||||
case 0x4: ret = DMAIntControl | (DMAIntStatus << 24) | (IRQOut << 31);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else switch(A & 0xC)
|
||||
{
|
||||
case 0x0: ret = DMACH[ch].BaseAddr;
|
||||
break;
|
||||
|
||||
case 0x4: ret = DMACH[ch].BlockControl;
|
||||
break;
|
||||
|
||||
case 0xC:
|
||||
case 0x8: ret = DMACH[ch].ChanControl;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
ret >>= (A & 3) * 8;
|
||||
|
||||
//PSX_WARNING("[DMA] Read: %08x %08x", A, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
int DMA_StateAction(StateMem *sm, int load, int data_only)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(DMACycleCounter),
|
||||
SFVAR(DMAControl),
|
||||
SFVAR(DMAIntControl),
|
||||
SFVAR(DMAIntStatus),
|
||||
SFVAR(IRQOut),
|
||||
|
||||
#define SFDMACH(n) SFVARN(DMACH[n].BaseAddr, #n "BaseAddr"), \
|
||||
SFVARN(DMACH[n].BlockControl, #n "BlockControl"), \
|
||||
SFVARN(DMACH[n].ChanControl, #n "ChanControl"), \
|
||||
SFVARN(DMACH[n].CurAddr, #n "CurAddr"), \
|
||||
SFVARN(DMACH[n].WordCounter, #n "WordCounter"), \
|
||||
SFVARN(DMACH[n].ClockCounter, #n "ClockCounter")
|
||||
|
||||
SFDMACH(0),
|
||||
SFDMACH(1),
|
||||
SFDMACH(2),
|
||||
SFDMACH(3),
|
||||
SFDMACH(4),
|
||||
SFDMACH(5),
|
||||
SFDMACH(6),
|
||||
|
||||
#undef SFDMACH
|
||||
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "DMA");
|
||||
|
||||
if(load)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef __MDFN_PSX_DMA_H
|
||||
#define __MDFN_PSX_DMA_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
bool DMA_GPUWriteActive(void);
|
||||
|
||||
pscpu_timestamp_t DMA_Update(const pscpu_timestamp_t timestamp);
|
||||
void DMA_Write(const pscpu_timestamp_t timestamp, uint32 A, uint32 V);
|
||||
uint32 DMA_Read(const pscpu_timestamp_t timestamp, uint32 A);
|
||||
|
||||
void DMA_ResetTS(void);
|
||||
|
||||
void DMA_Power(void);
|
||||
|
||||
void DMA_Init(void);
|
||||
void DMA_Kill(void);
|
||||
|
||||
int DMA_StateAction(StateMem *sm, int load, int data_only);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,151 @@
|
|||
#ifndef __MDFN_PSX_FRONTIO_H
|
||||
#define __MDFN_PSX_FRONTIO_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_Multitap;
|
||||
|
||||
class InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice();
|
||||
virtual ~InputDevice();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual void UpdateInput(const void *data);
|
||||
virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name);
|
||||
|
||||
virtual bool RequireNoFrameskip(void);
|
||||
|
||||
// Divide mouse X coordinate by pix_clock_divider in the lightgun code to get the coordinate in pixel(clocks).
|
||||
virtual pscpu_timestamp_t GPULineHook(const pscpu_timestamp_t line_timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider);
|
||||
|
||||
virtual void Update(const pscpu_timestamp_t timestamp); // Partially-implemented, don't rely on for timing any more fine-grained than a video frame for now.
|
||||
virtual void ResetTS(void);
|
||||
|
||||
void DrawCrosshairs(uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock);
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetCrosshairsColor(uint32 color);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool GetDSR(void); // Currently unused.
|
||||
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
|
||||
//
|
||||
//
|
||||
virtual uint32 GetNVSize(void);
|
||||
virtual void ReadNV(uint8 *buffer, uint32 offset, uint32 count);
|
||||
virtual void WriteNV(const uint8 *buffer, uint32 offset, uint32 count);
|
||||
|
||||
//
|
||||
// Dirty count should be incremented on each call to a method this class that causes at least 1 write to occur to the
|
||||
// nonvolatile memory(IE Clock() in the correct command phase, and WriteNV()).
|
||||
//
|
||||
virtual uint64 GetNVDirtyCount(void);
|
||||
virtual void ResetNVDirtyCount(void);
|
||||
|
||||
private:
|
||||
unsigned chair_r, chair_g, chair_b;
|
||||
bool draw_chair;
|
||||
protected:
|
||||
int32 chair_x, chair_y;
|
||||
};
|
||||
|
||||
class FrontIO
|
||||
{
|
||||
public:
|
||||
|
||||
FrontIO(bool emulate_memcards_[8], bool emulate_multitap_[2]);
|
||||
~FrontIO();
|
||||
|
||||
void Power(void);
|
||||
void Write(pscpu_timestamp_t timestamp, uint32 A, uint32 V);
|
||||
uint32 Read(pscpu_timestamp_t timestamp, uint32 A);
|
||||
pscpu_timestamp_t CalcNextEventTS(pscpu_timestamp_t timestamp, int32 next_event);
|
||||
pscpu_timestamp_t Update(pscpu_timestamp_t timestamp);
|
||||
void ResetTS(void);
|
||||
|
||||
bool RequireNoFrameskip(void);
|
||||
void GPULineHook(const pscpu_timestamp_t timestamp, const pscpu_timestamp_t line_timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider);
|
||||
|
||||
void UpdateInput(void);
|
||||
void SetInput(unsigned int port, const char *type, void *ptr);
|
||||
void SetCrosshairsColor(unsigned port, uint32 color);
|
||||
|
||||
uint64 GetMemcardDirtyCount(unsigned int which);
|
||||
void LoadMemcard(unsigned int which, const char *path);
|
||||
void SaveMemcard(unsigned int which, const char *path); //, bool force_save = false);
|
||||
|
||||
int StateAction(StateMem* sm, int load, int data_only);
|
||||
|
||||
private:
|
||||
|
||||
void DoDSRIRQ(void);
|
||||
void CheckStartStopPending(pscpu_timestamp_t timestamp, bool skip_event_set = false);
|
||||
|
||||
void MapDevicesToPorts(void);
|
||||
|
||||
bool emulate_memcards[8];
|
||||
bool emulate_multitap[2];
|
||||
|
||||
InputDevice *Ports[2];
|
||||
InputDevice *MCPorts[2];
|
||||
|
||||
InputDevice *DummyDevice;
|
||||
InputDevice_Multitap *DevicesTap[2];
|
||||
|
||||
InputDevice *Devices[8];
|
||||
void *DeviceData[8];
|
||||
|
||||
InputDevice *DevicesMC[8];
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
int32 ClockDivider;
|
||||
|
||||
bool ReceivePending;
|
||||
bool TransmitPending;
|
||||
|
||||
bool ReceiveInProgress;
|
||||
bool TransmitInProgress;
|
||||
|
||||
bool ReceiveBufferAvail;
|
||||
|
||||
uint8 ReceiveBuffer;
|
||||
uint8 TransmitBuffer;
|
||||
|
||||
int32 ReceiveBitCounter;
|
||||
int32 TransmitBitCounter;
|
||||
|
||||
uint16 Mode;
|
||||
uint16 Control;
|
||||
uint16 Baudrate;
|
||||
|
||||
|
||||
bool istatus;
|
||||
//
|
||||
//
|
||||
pscpu_timestamp_t irq10_pulse_ts[2];
|
||||
|
||||
int32 dsr_pulse_delay[4];
|
||||
int32 dsr_active_until_ts[4];
|
||||
int32 lastts;
|
||||
//
|
||||
//
|
||||
uint32 chair_colors[8];
|
||||
};
|
||||
|
||||
extern InputInfoStruct FIO_InputInfo;
|
||||
|
||||
}
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,317 @@
|
|||
// WARNING WARNING WARNING: ONLY use CanRead() method of BlitterFIFO, and NOT CanWrite(), since the FIFO is larger than the actual PS1 GPU FIFO to accommodate
|
||||
// our lack of fancy superscalarish command sequencer.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cdrom/SimpleFIFO.h"
|
||||
#include "git.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class PS_GPU;
|
||||
|
||||
struct CTEntry
|
||||
{
|
||||
void (*func[4][8])(PS_GPU* g, const uint32 *cb);
|
||||
uint8 len;
|
||||
uint8 fifo_fb_len;
|
||||
bool ss_cmd;
|
||||
};
|
||||
|
||||
struct tri_vertex
|
||||
{
|
||||
int32 x, y;
|
||||
int32 u, v;
|
||||
int32 r, g, b;
|
||||
};
|
||||
|
||||
struct i_group;
|
||||
struct i_deltas;
|
||||
|
||||
struct line_point
|
||||
{
|
||||
int32 x, y;
|
||||
uint8 r, g, b;
|
||||
};
|
||||
|
||||
class PS_GPU
|
||||
{
|
||||
public:
|
||||
|
||||
PS_GPU(bool pal_clock_and_tv, int sls, int sle) MDFN_COLD;
|
||||
~PS_GPU() MDFN_COLD;
|
||||
|
||||
void FillVideoParams(MDFNGI* gi) MDFN_COLD;
|
||||
|
||||
void Power(void) MDFN_COLD;
|
||||
|
||||
int StateAction(StateMem *sm, int load, int data_only);
|
||||
|
||||
void ResetTS(void);
|
||||
|
||||
void StartFrame(EmulateSpecStruct *espec);
|
||||
|
||||
pscpu_timestamp_t Update(const pscpu_timestamp_t timestamp);
|
||||
|
||||
void Write(const pscpu_timestamp_t timestamp, uint32 A, uint32 V);
|
||||
|
||||
INLINE bool CalcFIFOReadyBit(void)
|
||||
{
|
||||
if(InCmd & (INCMD_PLINE | INCMD_QUAD))
|
||||
return(false);
|
||||
|
||||
if(BlitterFIFO.CanRead() == 0)
|
||||
return(true);
|
||||
|
||||
if(InCmd & (INCMD_FBREAD | INCMD_FBWRITE))
|
||||
return(false);
|
||||
|
||||
if(BlitterFIFO.CanRead() >= Commands[BlitterFIFO.ReadUnit(true) >> 24].fifo_fb_len)
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
INLINE bool DMACanWrite(void)
|
||||
{
|
||||
return CalcFIFOReadyBit();
|
||||
}
|
||||
|
||||
void WriteDMA(uint32 V);
|
||||
uint32 ReadDMA(void);
|
||||
|
||||
uint32 Read(const pscpu_timestamp_t timestamp, uint32 A);
|
||||
|
||||
inline int32 GetScanlineNum(void)
|
||||
{
|
||||
return(scanline);
|
||||
}
|
||||
|
||||
INLINE uint16 PeekRAM(uint32 A)
|
||||
{
|
||||
return(GPURAM[(A >> 10) & 0x1FF][A & 0x3FF]);
|
||||
}
|
||||
|
||||
INLINE void PokeRAM(uint32 A, uint16 V)
|
||||
{
|
||||
GPURAM[(A >> 10) & 0x1FF][A & 0x3FF] = V;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void ProcessFIFO(void);
|
||||
void WriteCB(uint32 data);
|
||||
uint32 ReadData(void);
|
||||
void SoftReset(void);
|
||||
|
||||
// Y, X
|
||||
uint16 GPURAM[512][1024];
|
||||
|
||||
uint32 DMAControl;
|
||||
|
||||
//
|
||||
// Drawing stuff
|
||||
//
|
||||
//int32 TexPageX; // 0, 64, 128, 192, etc up to 960
|
||||
//int32 TexPageY; // 0 or 256
|
||||
//uint32 abr; // Semi-transparency mode(0~3)
|
||||
//bool dtd; // Dithering enable
|
||||
|
||||
int32 ClipX0;
|
||||
int32 ClipY0;
|
||||
int32 ClipX1;
|
||||
int32 ClipY1;
|
||||
|
||||
int32 OffsX;
|
||||
int32 OffsY;
|
||||
|
||||
bool dtd;
|
||||
bool dfe;
|
||||
|
||||
uint32 MaskSetOR;
|
||||
uint32 MaskEvalAND;
|
||||
|
||||
uint8 tww, twh, twx, twy;
|
||||
struct
|
||||
{
|
||||
uint8 TexWindowXLUT_Pre[16];
|
||||
uint8 TexWindowXLUT[256];
|
||||
uint8 TexWindowXLUT_Post[16];
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
uint8 TexWindowYLUT_Pre[16];
|
||||
uint8 TexWindowYLUT[256];
|
||||
uint8 TexWindowYLUT_Post[16];
|
||||
};
|
||||
void RecalcTexWindowLUT(void);
|
||||
|
||||
int32 TexPageX;
|
||||
int32 TexPageY;
|
||||
|
||||
uint32 SpriteFlip;
|
||||
|
||||
uint32 abr;
|
||||
uint32 TexMode;
|
||||
|
||||
struct
|
||||
{
|
||||
uint8 RGB8SAT_Under[256];
|
||||
uint8 RGB8SAT[256];
|
||||
uint8 RGB8SAT_Over[256];
|
||||
};
|
||||
|
||||
uint8 DitherLUT[4][4][512]; // Y, X, 8-bit source value(256 extra for saturation)
|
||||
|
||||
bool LineSkipTest(unsigned y);
|
||||
|
||||
template<int BlendMode, bool MaskEval_TA, bool textured>
|
||||
void PlotPixel(int32 x, int32 y, uint16 pix);
|
||||
|
||||
template<uint32 TexMode_TA>
|
||||
uint16 GetTexel(uint32 clut_offset, int32 u, int32 v);
|
||||
|
||||
uint16 ModTexel(uint16 texel, int32 r, int32 g, int32 b, const int32 dither_x, const int32 dither_y);
|
||||
|
||||
template<bool goraud, bool textured, int BlendMode, bool TexMult, uint32 TexMode, bool MaskEval_TA>
|
||||
void DrawSpan(int y, uint32 clut_offset, const int32 x_start, const int32 x_bound, i_group ig, const i_deltas &idl);
|
||||
|
||||
template<bool shaded, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
void DrawTriangle(tri_vertex *vertices, uint32 clut);
|
||||
|
||||
template<bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA, bool FlipX, bool FlipY>
|
||||
void DrawSprite(int32 x_arg, int32 y_arg, int32 w, int32 h, uint8 u_arg, uint8 v_arg, uint32 color, uint32 clut_offset);
|
||||
|
||||
template<bool goraud, int BlendMode, bool MaskEval_TA>
|
||||
void DrawLine(line_point *vertices);
|
||||
|
||||
|
||||
public:
|
||||
template<int numvertices, bool shaded, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
void Command_DrawPolygon(const uint32 *cb);
|
||||
|
||||
template<uint8 raw_size, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
void Command_DrawSprite(const uint32 *cb);
|
||||
|
||||
template<bool polyline, bool goraud, int BlendMode, bool MaskEval_TA>
|
||||
void Command_DrawLine(const uint32 *cb);
|
||||
|
||||
void Command_ClearCache(const uint32 *cb);
|
||||
void Command_IRQ(const uint32 *cb);
|
||||
|
||||
void Command_FBFill(const uint32 *cb);
|
||||
void Command_FBCopy(const uint32 *cb);
|
||||
void Command_FBWrite(const uint32 *cb);
|
||||
void Command_FBRead(const uint32 *cb);
|
||||
|
||||
void Command_DrawMode(const uint32 *cb);
|
||||
void Command_TexWindow(const uint32 *cb);
|
||||
void Command_Clip0(const uint32 *cb);
|
||||
void Command_Clip1(const uint32 *cb);
|
||||
void Command_DrawingOffset(const uint32 *cb);
|
||||
void Command_MaskSetting(const uint32 *cb);
|
||||
|
||||
private:
|
||||
static CTEntry Commands[256];
|
||||
|
||||
SimpleFIFO<uint32> BlitterFIFO;
|
||||
|
||||
uint32 DataReadBuffer;
|
||||
|
||||
bool IRQPending;
|
||||
//
|
||||
//
|
||||
//
|
||||
// Powers of 2 for faster multiple equality testing(just for multi-testing; InCmd itself will only contain 0, or a power of 2).
|
||||
enum
|
||||
{
|
||||
INCMD_NONE = 0,
|
||||
INCMD_PLINE = (1 << 0),
|
||||
INCMD_QUAD = (1 << 1),
|
||||
INCMD_FBWRITE = (1 << 2),
|
||||
INCMD_FBREAD = (1 << 3)
|
||||
};
|
||||
uint8 InCmd;
|
||||
uint8 InCmd_CC;
|
||||
|
||||
tri_vertex InQuad_F3Vertices[3];
|
||||
uint32 InQuad_clut;
|
||||
|
||||
line_point InPLine_PrevPoint;
|
||||
|
||||
uint32 FBRW_X;
|
||||
uint32 FBRW_Y;
|
||||
uint32 FBRW_W;
|
||||
uint32 FBRW_H;
|
||||
uint32 FBRW_CurY;
|
||||
uint32 FBRW_CurX;
|
||||
|
||||
//
|
||||
// Display Parameters
|
||||
//
|
||||
uint32 DisplayMode;
|
||||
|
||||
bool DisplayOff;
|
||||
uint32 DisplayFB_XStart;
|
||||
uint32 DisplayFB_YStart;
|
||||
|
||||
uint32 HorizStart;
|
||||
uint32 HorizEnd;
|
||||
|
||||
uint32 VertStart;
|
||||
uint32 VertEnd;
|
||||
|
||||
//
|
||||
// Display work vars
|
||||
//
|
||||
uint32 DisplayFB_CurYOffset;
|
||||
uint32 DisplayFB_CurLineYReadout;
|
||||
|
||||
bool InVBlank;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
uint32 LinesPerField;
|
||||
uint32 scanline;
|
||||
bool field;
|
||||
bool field_ram_readout;
|
||||
bool PhaseChange;
|
||||
|
||||
uint32 DotClockCounter;
|
||||
|
||||
uint64 GPUClockCounter;
|
||||
uint32 GPUClockRatio;
|
||||
int32 LineClockCounter;
|
||||
int32 LinePhase;
|
||||
|
||||
int32 DrawTimeAvail;
|
||||
|
||||
pscpu_timestamp_t lastts;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
bool sl_zero_reached;
|
||||
//
|
||||
//
|
||||
|
||||
EmulateSpecStruct *espec;
|
||||
MDFN_Surface *surface;
|
||||
MDFN_Rect *DisplayRect;
|
||||
int32 *LineWidths;
|
||||
bool skip;
|
||||
bool HardwarePALType;
|
||||
int LineVisFirst, LineVisLast;
|
||||
|
||||
uint32 OutputLUT[32768];
|
||||
void ReorderRGB_Var(uint32 out_Rshift, uint32 out_Gshift, uint32 out_Bshift, bool bpp24, const uint16 *src, uint32 *dest, const int32 dx_start, const int32 dx_end, int32 fb_x);
|
||||
|
||||
template<uint32 out_Rshift, uint32 out_Gshift, uint32 out_Bshift>
|
||||
void ReorderRGB(bool bpp24, const uint16 *src, uint32 *dest, const int32 dx_start, const int32 dx_end, int32 fb_x) NO_INLINE;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,232 @@
|
|||
//#define BM_HELPER(fg) { fg(0), fg(1), fg(2), fg(3) }
|
||||
|
||||
#define POLY_HELPER_SUB(bm, cv, tm, mam) \
|
||||
G_Command_DrawPolygon<3 + ((cv & 0x8) >> 3), ((cv & 0x10) >> 4), ((cv & 0x4) >> 2), ((cv & 0x2) >> 1) ? bm : -1, ((cv & 1) ^ 1) & ((cv & 0x4) >> 2), tm, mam >
|
||||
|
||||
#define POLY_HELPER_FG(bm, cv) \
|
||||
{ \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 0 : 0), 0), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 1 : 0), 0), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 0), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 0), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 0 : 0), 1), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 1 : 0), 1), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 1), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 1), \
|
||||
}
|
||||
|
||||
#define POLY_HELPER(cv) \
|
||||
{ \
|
||||
{ POLY_HELPER_FG(0, cv), POLY_HELPER_FG(1, cv), POLY_HELPER_FG(2, cv), POLY_HELPER_FG(3, cv) }, \
|
||||
1 + (3 /*+ ((cv & 0x8) >> 3)*/) * ( 1 + ((cv & 0x4) >> 2) + ((cv & 0x10) >> 4) ) - ((cv & 0x10) >> 4), \
|
||||
1, \
|
||||
false \
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
|
||||
#define SPR_HELPER_SUB(bm, cv, tm, mam) G_Command_DrawSprite<(cv >> 3) & 0x3, ((cv & 0x4) >> 2), ((cv & 0x2) >> 1) ? bm : -1, ((cv & 1) ^ 1) & ((cv & 0x4) >> 2), tm, mam>
|
||||
|
||||
#define SPR_HELPER_FG(bm, cv) \
|
||||
{ \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 0 : 0), 0), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 1 : 0), 0), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 0), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 0), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 0 : 0), 1), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 1 : 0), 1), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 1), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 1), \
|
||||
}
|
||||
|
||||
|
||||
#define SPR_HELPER(cv) \
|
||||
{ \
|
||||
{ SPR_HELPER_FG(0, cv), SPR_HELPER_FG(1, cv), SPR_HELPER_FG(2, cv), SPR_HELPER_FG(3, cv) }, \
|
||||
2 + ((cv & 0x4) >> 2) + ((cv & 0x18) ? 0 : 1), \
|
||||
2 | ((cv & 0x4) >> 2) | ((cv & 0x18) ? 0 : 1), /* |, not +, for this */ \
|
||||
false \
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
|
||||
#define LINE_HELPER_SUB(bm, cv, mam) G_Command_DrawLine<((cv & 0x08) >> 3), ((cv & 0x10) >> 4), ((cv & 0x2) >> 1) ? bm : -1, mam>
|
||||
|
||||
#define LINE_HELPER_FG(bm, cv) \
|
||||
{ \
|
||||
LINE_HELPER_SUB(bm, cv, 0), \
|
||||
LINE_HELPER_SUB(bm, cv, 0), \
|
||||
LINE_HELPER_SUB(bm, cv, 0), \
|
||||
LINE_HELPER_SUB(bm, cv, 0), \
|
||||
LINE_HELPER_SUB(bm, cv, 1), \
|
||||
LINE_HELPER_SUB(bm, cv, 1), \
|
||||
LINE_HELPER_SUB(bm, cv, 1), \
|
||||
LINE_HELPER_SUB(bm, cv, 1) \
|
||||
}
|
||||
|
||||
#define LINE_HELPER(cv) \
|
||||
{ \
|
||||
{ LINE_HELPER_FG(0, cv), LINE_HELPER_FG(1, cv), LINE_HELPER_FG(2, cv), LINE_HELPER_FG(3, cv) }, \
|
||||
3 + ((cv & 0x10) >> 4), \
|
||||
1, \
|
||||
false \
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
#define OTHER_HELPER_FG(bm, arg_ptr) { arg_ptr, arg_ptr, arg_ptr, arg_ptr, arg_ptr, arg_ptr, arg_ptr, arg_ptr }
|
||||
#define OTHER_HELPER(arg_cs, arg_fbcs, arg_ss, arg_ptr) { { OTHER_HELPER_FG(0, arg_ptr), OTHER_HELPER_FG(1, arg_ptr), OTHER_HELPER_FG(2, arg_ptr), OTHER_HELPER_FG(3, arg_ptr) }, arg_cs, arg_fbcs, arg_ss }
|
||||
#define OTHER_HELPER_X2(arg_cs, arg_fbcs, arg_ss, arg_ptr) OTHER_HELPER(arg_cs, arg_fbcs, arg_ss, arg_ptr), OTHER_HELPER(arg_cs, arg_fbcs, arg_ss, arg_ptr)
|
||||
#define OTHER_HELPER_X4(arg_cs, arg_fbcs, arg_ss, arg_ptr) OTHER_HELPER_X2(arg_cs, arg_fbcs, arg_ss, arg_ptr), OTHER_HELPER_X2(arg_cs, arg_fbcs, arg_ss, arg_ptr)
|
||||
#define OTHER_HELPER_X8(arg_cs, arg_fbcs, arg_ss, arg_ptr) OTHER_HELPER_X4(arg_cs, arg_fbcs, arg_ss, arg_ptr), OTHER_HELPER_X4(arg_cs, arg_fbcs, arg_ss, arg_ptr)
|
||||
#define OTHER_HELPER_X16(arg_cs, arg_fbcs, arg_ss, arg_ptr) OTHER_HELPER_X8(arg_cs, arg_fbcs, arg_ss, arg_ptr), OTHER_HELPER_X8(arg_cs, arg_fbcs, arg_ss, arg_ptr)
|
||||
#define OTHER_HELPER_X32(arg_cs, arg_fbcs, arg_ss, arg_ptr) OTHER_HELPER_X16(arg_cs, arg_fbcs, arg_ss, arg_ptr), OTHER_HELPER_X16(arg_cs, arg_fbcs, arg_ss, arg_ptr)
|
||||
|
||||
#define NULLCMD_FG(bm) { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
|
||||
#define NULLCMD() { { NULLCMD_FG(0), NULLCMD_FG(1), NULLCMD_FG(2), NULLCMD_FG(3) }, 1, 1, true }
|
||||
|
||||
|
||||
/* 0x00 */
|
||||
NULLCMD(),
|
||||
OTHER_HELPER(1, 2, false, G_Command_ClearCache),
|
||||
OTHER_HELPER(3, 3, false, G_Command_FBFill),
|
||||
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
||||
/* 0x10 */
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
||||
OTHER_HELPER(1, 1, false, G_Command_IRQ),
|
||||
|
||||
/* 0x20 */
|
||||
POLY_HELPER(0x20),
|
||||
POLY_HELPER(0x21),
|
||||
POLY_HELPER(0x22),
|
||||
POLY_HELPER(0x23),
|
||||
POLY_HELPER(0x24),
|
||||
POLY_HELPER(0x25),
|
||||
POLY_HELPER(0x26),
|
||||
POLY_HELPER(0x27),
|
||||
POLY_HELPER(0x28),
|
||||
POLY_HELPER(0x29),
|
||||
POLY_HELPER(0x2a),
|
||||
POLY_HELPER(0x2b),
|
||||
POLY_HELPER(0x2c),
|
||||
POLY_HELPER(0x2d),
|
||||
POLY_HELPER(0x2e),
|
||||
POLY_HELPER(0x2f),
|
||||
POLY_HELPER(0x30),
|
||||
POLY_HELPER(0x31),
|
||||
POLY_HELPER(0x32),
|
||||
POLY_HELPER(0x33),
|
||||
POLY_HELPER(0x34),
|
||||
POLY_HELPER(0x35),
|
||||
POLY_HELPER(0x36),
|
||||
POLY_HELPER(0x37),
|
||||
POLY_HELPER(0x38),
|
||||
POLY_HELPER(0x39),
|
||||
POLY_HELPER(0x3a),
|
||||
POLY_HELPER(0x3b),
|
||||
POLY_HELPER(0x3c),
|
||||
POLY_HELPER(0x3d),
|
||||
POLY_HELPER(0x3e),
|
||||
POLY_HELPER(0x3f),
|
||||
|
||||
LINE_HELPER(0x40),
|
||||
LINE_HELPER(0x41),
|
||||
LINE_HELPER(0x42),
|
||||
LINE_HELPER(0x43),
|
||||
LINE_HELPER(0x44),
|
||||
LINE_HELPER(0x45),
|
||||
LINE_HELPER(0x46),
|
||||
LINE_HELPER(0x47),
|
||||
LINE_HELPER(0x48),
|
||||
LINE_HELPER(0x49),
|
||||
LINE_HELPER(0x4a),
|
||||
LINE_HELPER(0x4b),
|
||||
LINE_HELPER(0x4c),
|
||||
LINE_HELPER(0x4d),
|
||||
LINE_HELPER(0x4e),
|
||||
LINE_HELPER(0x4f),
|
||||
LINE_HELPER(0x50),
|
||||
LINE_HELPER(0x51),
|
||||
LINE_HELPER(0x52),
|
||||
LINE_HELPER(0x53),
|
||||
LINE_HELPER(0x54),
|
||||
LINE_HELPER(0x55),
|
||||
LINE_HELPER(0x56),
|
||||
LINE_HELPER(0x57),
|
||||
LINE_HELPER(0x58),
|
||||
LINE_HELPER(0x59),
|
||||
LINE_HELPER(0x5a),
|
||||
LINE_HELPER(0x5b),
|
||||
LINE_HELPER(0x5c),
|
||||
LINE_HELPER(0x5d),
|
||||
LINE_HELPER(0x5e),
|
||||
LINE_HELPER(0x5f),
|
||||
|
||||
SPR_HELPER(0x60),
|
||||
SPR_HELPER(0x61),
|
||||
SPR_HELPER(0x62),
|
||||
SPR_HELPER(0x63),
|
||||
SPR_HELPER(0x64),
|
||||
SPR_HELPER(0x65),
|
||||
SPR_HELPER(0x66),
|
||||
SPR_HELPER(0x67),
|
||||
SPR_HELPER(0x68),
|
||||
SPR_HELPER(0x69),
|
||||
SPR_HELPER(0x6a),
|
||||
SPR_HELPER(0x6b),
|
||||
SPR_HELPER(0x6c),
|
||||
SPR_HELPER(0x6d),
|
||||
SPR_HELPER(0x6e),
|
||||
SPR_HELPER(0x6f),
|
||||
SPR_HELPER(0x70),
|
||||
SPR_HELPER(0x71),
|
||||
SPR_HELPER(0x72),
|
||||
SPR_HELPER(0x73),
|
||||
SPR_HELPER(0x74),
|
||||
SPR_HELPER(0x75),
|
||||
SPR_HELPER(0x76),
|
||||
SPR_HELPER(0x77),
|
||||
SPR_HELPER(0x78),
|
||||
SPR_HELPER(0x79),
|
||||
SPR_HELPER(0x7a),
|
||||
SPR_HELPER(0x7b),
|
||||
SPR_HELPER(0x7c),
|
||||
SPR_HELPER(0x7d),
|
||||
SPR_HELPER(0x7e),
|
||||
SPR_HELPER(0x7f),
|
||||
|
||||
/* 0x80 ... 0x9F */
|
||||
OTHER_HELPER_X32(4, 2, false, G_Command_FBCopy),
|
||||
|
||||
/* 0xA0 ... 0xBF */
|
||||
OTHER_HELPER_X32(3, 2, false, G_Command_FBWrite),
|
||||
|
||||
/* 0xC0 ... 0xDF */
|
||||
OTHER_HELPER_X32(3, 2, false, G_Command_FBRead),
|
||||
|
||||
/* 0xE0 */
|
||||
|
||||
NULLCMD(),
|
||||
OTHER_HELPER(1, 2, false, G_Command_DrawMode),
|
||||
OTHER_HELPER(1, 2, false, G_Command_TexWindow),
|
||||
OTHER_HELPER(1, 1, true, G_Command_Clip0),
|
||||
OTHER_HELPER(1, 1, true, G_Command_Clip1),
|
||||
OTHER_HELPER(1, 1, true, G_Command_DrawingOffset),
|
||||
OTHER_HELPER(1, 2, false, G_Command_MaskSetting),
|
||||
|
||||
NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
||||
/* 0xF0 */
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
struct line_fxp_coord
|
||||
{
|
||||
int64 x, y;
|
||||
int32 r, g, b;
|
||||
};
|
||||
|
||||
struct line_fxp_step
|
||||
{
|
||||
int64 dx_dk, dy_dk;
|
||||
int32 dr_dk, dg_dk, db_dk;
|
||||
};
|
||||
|
||||
enum { Line_XY_FractBits = 32 };
|
||||
enum { Line_RGB_FractBits = 12 };
|
||||
|
||||
template<bool goraud>
|
||||
static INLINE void LinePointToFXPCoord(const line_point &point, const line_fxp_step &step, line_fxp_coord &coord)
|
||||
{
|
||||
coord.x = ((int64)point.x << Line_XY_FractBits) | (1LL << (Line_XY_FractBits - 1));
|
||||
coord.y = ((int64)point.y << Line_XY_FractBits) | (1LL << (Line_XY_FractBits - 1));
|
||||
|
||||
coord.x -= 1024;
|
||||
|
||||
if(step.dy_dk < 0)
|
||||
coord.y -= 1024;
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
coord.r = (point.r << Line_RGB_FractBits) | (1 << (Line_RGB_FractBits - 1));
|
||||
coord.g = (point.g << Line_RGB_FractBits) | (1 << (Line_RGB_FractBits - 1));
|
||||
coord.b = (point.b << Line_RGB_FractBits) | (1 << (Line_RGB_FractBits - 1));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, unsigned bits>
|
||||
static INLINE T LineDivide(T delta, int32 dk)
|
||||
{
|
||||
delta <<= bits;
|
||||
|
||||
if(delta < 0)
|
||||
delta -= dk - 1;
|
||||
if(delta > 0)
|
||||
delta += dk - 1;
|
||||
|
||||
return(delta / dk);
|
||||
}
|
||||
|
||||
template<bool goraud>
|
||||
static INLINE void LinePointsToFXPStep(const line_point &point0, const line_point &point1, const int32 dk, line_fxp_step &step)
|
||||
{
|
||||
if(!dk)
|
||||
{
|
||||
step.dx_dk = 0;
|
||||
step.dy_dk = 0;
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
step.dr_dk = 0;
|
||||
step.dg_dk = 0;
|
||||
step.db_dk = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
step.dx_dk = LineDivide<int64, Line_XY_FractBits>(point1.x - point0.x, dk);
|
||||
step.dy_dk = LineDivide<int64, Line_XY_FractBits>(point1.y - point0.y, dk);
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
step.dr_dk = ((point1.r - point0.r) << Line_RGB_FractBits) / dk;
|
||||
step.dg_dk = ((point1.g - point0.g) << Line_RGB_FractBits) / dk;
|
||||
step.db_dk = ((point1.b - point0.b) << Line_RGB_FractBits) / dk;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool goraud>
|
||||
static INLINE void AddLineStep(line_fxp_coord &point, const line_fxp_step &step, int32 count = 1)
|
||||
{
|
||||
point.x += step.dx_dk * count;
|
||||
point.y += step.dy_dk * count;
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
point.r += step.dr_dk * count;
|
||||
point.g += step.dg_dk * count;
|
||||
point.b += step.db_dk * count;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool goraud, int BlendMode, bool MaskEval_TA>
|
||||
void PS_GPU::DrawLine(line_point *points)
|
||||
{
|
||||
int32 i_dx;
|
||||
int32 i_dy;
|
||||
int32 k;
|
||||
line_fxp_coord cur_point;
|
||||
line_fxp_step step;
|
||||
|
||||
i_dx = abs(points[1].x - points[0].x);
|
||||
i_dy = abs(points[1].y - points[0].y);
|
||||
k = (i_dx > i_dy) ? i_dx : i_dy;
|
||||
|
||||
if(i_dx >= 1024)
|
||||
{
|
||||
PSX_DBG(PSX_DBG_WARNING, "[GPU] Line too long: i_dx=%d\n", i_dx);
|
||||
return;
|
||||
}
|
||||
|
||||
if(i_dy >= 512)
|
||||
{
|
||||
PSX_DBG(PSX_DBG_WARNING, "[GPU] Line too long: i_dy=%d\n", i_dy);
|
||||
return;
|
||||
}
|
||||
|
||||
// May not be correct(do tests for the case of k == i_dy on real thing.
|
||||
if(points[0].x > points[1].x)
|
||||
{
|
||||
line_point tmp = points[1];
|
||||
|
||||
points[1] = points[0];
|
||||
points[0] = tmp;
|
||||
}
|
||||
|
||||
DrawTimeAvail -= k * ((BlendMode >= 0) ? 2 : 1);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
LinePointsToFXPStep<goraud>(points[0], points[1], k, step);
|
||||
LinePointToFXPCoord<goraud>(points[0], step, cur_point);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
for(int32 i = 0; i <= k; i++) // <= is not a typo.
|
||||
{
|
||||
// Sign extension is not necessary here for x and y, due to the maximum values that ClipX1 and ClipY1 can contain.
|
||||
const int32 x = (cur_point.x >> Line_XY_FractBits) & 2047;
|
||||
const int32 y = (cur_point.y >> Line_XY_FractBits) & 2047;
|
||||
uint16 pix = 0x8000;
|
||||
|
||||
if(!LineSkipTest(y))
|
||||
{
|
||||
uint8 r, g, b;
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
r = cur_point.r >> Line_RGB_FractBits;
|
||||
g = cur_point.g >> Line_RGB_FractBits;
|
||||
b = cur_point.b >> Line_RGB_FractBits;
|
||||
}
|
||||
else
|
||||
{
|
||||
r = points[0].r;
|
||||
g = points[0].g;
|
||||
b = points[0].b;
|
||||
}
|
||||
|
||||
if(goraud && dtd)
|
||||
{
|
||||
pix |= DitherLUT[y & 3][x & 3][r] << 0;
|
||||
pix |= DitherLUT[y & 3][x & 3][g] << 5;
|
||||
pix |= DitherLUT[y & 3][x & 3][b] << 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
pix |= (r >> 3) << 0;
|
||||
pix |= (g >> 3) << 5;
|
||||
pix |= (b >> 3) << 10;
|
||||
}
|
||||
|
||||
// FIXME: There has to be a faster way than checking for being inside the drawing area for each pixel.
|
||||
if(x >= ClipX0 && x <= ClipX1 && y >= ClipY0 && y <= ClipY1)
|
||||
PlotPixel<BlendMode, MaskEval_TA, false>(x, y, pix);
|
||||
}
|
||||
|
||||
AddLineStep<goraud>(cur_point, step);
|
||||
}
|
||||
}
|
||||
|
||||
template<bool polyline, bool goraud, int BlendMode, bool MaskEval_TA>
|
||||
INLINE void PS_GPU::Command_DrawLine(const uint32 *cb)
|
||||
{
|
||||
const uint8 cc = cb[0] >> 24; // For pline handling later.
|
||||
line_point points[2];
|
||||
|
||||
DrawTimeAvail -= 16; // FIXME, correct time.
|
||||
|
||||
if(polyline && InCmd == INCMD_PLINE)
|
||||
{
|
||||
//printf("PLINE N\n");
|
||||
points[0] = InPLine_PrevPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
points[0].r = (*cb >> 0) & 0xFF;
|
||||
points[0].g = (*cb >> 8) & 0xFF;
|
||||
points[0].b = (*cb >> 16) & 0xFF;
|
||||
cb++;
|
||||
|
||||
points[0].x = sign_x_to_s32(11, ((*cb >> 0) & 0xFFFF)) + OffsX;
|
||||
points[0].y = sign_x_to_s32(11, ((*cb >> 16) & 0xFFFF)) + OffsY;
|
||||
cb++;
|
||||
}
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
points[1].r = (*cb >> 0) & 0xFF;
|
||||
points[1].g = (*cb >> 8) & 0xFF;
|
||||
points[1].b = (*cb >> 16) & 0xFF;
|
||||
cb++;
|
||||
}
|
||||
else
|
||||
{
|
||||
points[1].r = points[0].r;
|
||||
points[1].g = points[0].g;
|
||||
points[1].b = points[0].b;
|
||||
}
|
||||
|
||||
points[1].x = sign_x_to_s32(11, ((*cb >> 0) & 0xFFFF)) + OffsX;
|
||||
points[1].y = sign_x_to_s32(11, ((*cb >> 16) & 0xFFFF)) + OffsY;
|
||||
cb++;
|
||||
|
||||
if(polyline)
|
||||
{
|
||||
InPLine_PrevPoint = points[1];
|
||||
|
||||
if(InCmd != INCMD_PLINE)
|
||||
{
|
||||
InCmd = INCMD_PLINE;
|
||||
InCmd_CC = cc;
|
||||
}
|
||||
}
|
||||
|
||||
DrawLine<goraud, BlendMode, MaskEval_TA>(points);
|
||||
}
|
||||
|
|
@ -0,0 +1,514 @@
|
|||
#define COORD_FBS 12
|
||||
#define COORD_MF_INT(n) ((n) << COORD_FBS)
|
||||
|
||||
/*
|
||||
Store and do most math with interpolant coordinates and deltas as unsigned to avoid violating strict overflow(due to biasing),
|
||||
but when actually grabbing the coordinates, treat them as signed(with signed right shift) so we can do saturation properly.
|
||||
*/
|
||||
static INLINE int32 COORD_GET_INT(int32 n)
|
||||
{
|
||||
return(n >> COORD_FBS);
|
||||
}
|
||||
|
||||
struct i_group
|
||||
{
|
||||
uint32 u, v;
|
||||
uint32 r, g, b;
|
||||
uint32 dummy0[3];
|
||||
};
|
||||
|
||||
struct i_deltas
|
||||
{
|
||||
uint32 du_dx, dv_dx;
|
||||
uint32 dr_dx, dg_dx, db_dx;
|
||||
uint32 dummy0[3];
|
||||
|
||||
uint32 du_dy, dv_dy;
|
||||
uint32 dr_dy, dg_dy, db_dy;
|
||||
uint32 dummy1[3];
|
||||
};
|
||||
|
||||
static INLINE int64 MakePolyXFP(int32 x)
|
||||
{
|
||||
return ((int64)x << 32) + ((1LL << 32) - (1 << 11));
|
||||
}
|
||||
|
||||
static INLINE int64 MakePolyXFPStep(int32 dx, int32 dy)
|
||||
{
|
||||
int64 ret;
|
||||
int64 dx_ex = (int64)dx << 32;
|
||||
|
||||
if(dx_ex < 0)
|
||||
dx_ex -= dy - 1;
|
||||
|
||||
if(dx_ex > 0)
|
||||
dx_ex += dy - 1;
|
||||
|
||||
ret = dx_ex / dy;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static INLINE int32 GetPolyXFP_Int(int64 xfp)
|
||||
{
|
||||
return(xfp >> 32);
|
||||
}
|
||||
|
||||
//#define CALCIS(x,y) ( A.x * (B.y - C.y) + B.x * (C.y - A.y) + C.x * (A.y - B.y) )
|
||||
#define CALCIS(x,y) (((B.x - A.x) * (C.y - B.y)) - ((C.x - B.x) * (B.y - A.y)))
|
||||
static INLINE bool CalcIDeltas(i_deltas &idl, const tri_vertex &A, const tri_vertex &B, const tri_vertex &C)
|
||||
{
|
||||
const unsigned sa = 32;
|
||||
int64 num = ((int64)COORD_MF_INT(1)) << sa;
|
||||
int64 denom = CALCIS(x, y);
|
||||
int64 one_div;
|
||||
|
||||
if(!denom)
|
||||
return(false);
|
||||
|
||||
one_div = num / denom;
|
||||
|
||||
idl.dr_dx = ((one_div * CALCIS(r, y)) + 0x00000000) >> sa;
|
||||
idl.dr_dy = ((one_div * CALCIS(x, r)) + 0x00000000) >> sa;
|
||||
|
||||
idl.dg_dx = ((one_div * CALCIS(g, y)) + 0x00000000) >> sa;
|
||||
idl.dg_dy = ((one_div * CALCIS(x, g)) + 0x00000000) >> sa;
|
||||
|
||||
idl.db_dx = ((one_div * CALCIS(b, y)) + 0x00000000) >> sa;
|
||||
idl.db_dy = ((one_div * CALCIS(x, b)) + 0x00000000) >> sa;
|
||||
|
||||
idl.du_dx = ((one_div * CALCIS(u, y)) + 0x00000000) >> sa;
|
||||
idl.du_dy = ((one_div * CALCIS(x, u)) + 0x00000000) >> sa;
|
||||
|
||||
idl.dv_dx = ((one_div * CALCIS(v, y)) + 0x00000000) >> sa;
|
||||
idl.dv_dy = ((one_div * CALCIS(x, v)) + 0x00000000) >> sa;
|
||||
|
||||
// idl.du_dx = ((int64)CALCIS(u, y) << COORD_FBS) / denom;
|
||||
// idl.du_dy = ((int64)CALCIS(x, u) << COORD_FBS) / denom;
|
||||
|
||||
// idl.dv_dx = ((int64)CALCIS(v, y) << COORD_FBS) / denom;
|
||||
// idl.dv_dy = ((int64)CALCIS(x, v) << COORD_FBS) / denom;
|
||||
|
||||
//printf("Denom=%lld - CIS_UY=%d, CIS_XU=%d, CIS_VY=%d, CIS_XV=%d\n", denom, CALCIS(u, y), CALCIS(x, u), CALCIS(v, y), CALCIS(x, v));
|
||||
//printf(" du_dx=0x%08x, du_dy=0x%08x --- dv_dx=0x%08x, dv_dy=0x%08x\n", idl.du_dx, idl.du_dy, idl.dv_dx, idl.dv_dy);
|
||||
|
||||
return(true);
|
||||
}
|
||||
#undef CALCIS
|
||||
|
||||
template<bool goraud, bool textured>
|
||||
static INLINE void AddIDeltas_DX(i_group &ig, const i_deltas &idl, uint32 count = 1)
|
||||
{
|
||||
if(textured)
|
||||
{
|
||||
ig.u += idl.du_dx * count;
|
||||
ig.v += idl.dv_dx * count;
|
||||
}
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
ig.r += idl.dr_dx * count;
|
||||
ig.g += idl.dg_dx * count;
|
||||
ig.b += idl.db_dx * count;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool goraud, bool textured>
|
||||
static INLINE void AddIDeltas_DY(i_group &ig, const i_deltas &idl, uint32 count = 1)
|
||||
{
|
||||
if(textured)
|
||||
{
|
||||
ig.u += idl.du_dy * count;
|
||||
ig.v += idl.dv_dy * count;
|
||||
}
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
ig.r += idl.dr_dy * count;
|
||||
ig.g += idl.dg_dy * count;
|
||||
ig.b += idl.db_dy * count;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool goraud, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
INLINE void PS_GPU::DrawSpan(int y, uint32 clut_offset, const int32 x_start, const int32 x_bound, i_group ig, const i_deltas &idl)
|
||||
{
|
||||
int32 xs = x_start, xb = x_bound;
|
||||
|
||||
if(LineSkipTest(y))
|
||||
return;
|
||||
|
||||
if(xs < xb) // (xs != xb)
|
||||
{
|
||||
if(xs < ClipX0)
|
||||
xs = ClipX0;
|
||||
|
||||
if(xb > (ClipX1 + 1))
|
||||
xb = ClipX1 + 1;
|
||||
|
||||
if(xs < xb)
|
||||
{
|
||||
DrawTimeAvail -= (xb - xs);
|
||||
|
||||
if(goraud || textured)
|
||||
{
|
||||
DrawTimeAvail -= (xb - xs);
|
||||
}
|
||||
else if((BlendMode >= 0) || MaskEval_TA)
|
||||
{
|
||||
DrawTimeAvail -= (((xb + 1) & ~1) - (xs & ~1)) >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(textured)
|
||||
{
|
||||
ig.u += (xs * idl.du_dx) + (y * idl.du_dy);
|
||||
ig.v += (xs * idl.dv_dx) + (y * idl.dv_dy);
|
||||
}
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
ig.r += (xs * idl.dr_dx) + (y * idl.dr_dy);
|
||||
ig.g += (xs * idl.dg_dx) + (y * idl.dg_dy);
|
||||
ig.b += (xs * idl.db_dx) + (y * idl.db_dy);
|
||||
}
|
||||
|
||||
for(int32 x = xs; MDFN_LIKELY(x < xb); x++)
|
||||
{
|
||||
uint32 r, g, b;
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
r = RGB8SAT[COORD_GET_INT(ig.r)];
|
||||
g = RGB8SAT[COORD_GET_INT(ig.g)];
|
||||
b = RGB8SAT[COORD_GET_INT(ig.b)];
|
||||
}
|
||||
else
|
||||
{
|
||||
r = COORD_GET_INT(ig.r);
|
||||
g = COORD_GET_INT(ig.g);
|
||||
b = COORD_GET_INT(ig.b);
|
||||
}
|
||||
|
||||
if(textured)
|
||||
{
|
||||
uint16 fbw = GetTexel<TexMode_TA>(clut_offset, COORD_GET_INT(ig.u), COORD_GET_INT(ig.v));
|
||||
|
||||
if(fbw)
|
||||
{
|
||||
if(TexMult)
|
||||
{
|
||||
if(dtd)
|
||||
fbw = ModTexel(fbw, r, g, b, x & 3, y & 3);
|
||||
else
|
||||
fbw = ModTexel(fbw, r, g, b, 3, 2); //x & 3, y & 3);
|
||||
}
|
||||
PlotPixel<BlendMode, MaskEval_TA, true>(x, y, fbw);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16 pix = 0x8000;
|
||||
|
||||
if(goraud && dtd)
|
||||
{
|
||||
pix |= DitherLUT[y & 3][x & 3][r] << 0;
|
||||
pix |= DitherLUT[y & 3][x & 3][g] << 5;
|
||||
pix |= DitherLUT[y & 3][x & 3][b] << 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
pix |= (r >> 3) << 0;
|
||||
pix |= (g >> 3) << 5;
|
||||
pix |= (b >> 3) << 10;
|
||||
}
|
||||
|
||||
PlotPixel<BlendMode, MaskEval_TA, false>(x, y, pix);
|
||||
}
|
||||
|
||||
AddIDeltas_DX<goraud, textured>(ig, idl);
|
||||
//AddStep<goraud, textured>(perp_coord, perp_step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<bool goraud, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
void PS_GPU::DrawTriangle(tri_vertex *vertices, uint32 clut)
|
||||
{
|
||||
i_deltas idl;
|
||||
|
||||
//
|
||||
// Sort vertices by y.
|
||||
//
|
||||
if(vertices[2].y < vertices[1].y)
|
||||
{
|
||||
tri_vertex tmp = vertices[1];
|
||||
vertices[1] = vertices[2];
|
||||
vertices[2] = tmp;
|
||||
}
|
||||
|
||||
if(vertices[1].y < vertices[0].y)
|
||||
{
|
||||
tri_vertex tmp = vertices[0];
|
||||
vertices[0] = vertices[1];
|
||||
vertices[1] = tmp;
|
||||
}
|
||||
|
||||
if(vertices[2].y < vertices[1].y)
|
||||
{
|
||||
tri_vertex tmp = vertices[1];
|
||||
vertices[1] = vertices[2];
|
||||
vertices[2] = tmp;
|
||||
}
|
||||
|
||||
if(vertices[0].y == vertices[2].y)
|
||||
return;
|
||||
|
||||
if((vertices[2].y - vertices[0].y) >= 512)
|
||||
{
|
||||
//PSX_WARNING("[GPU] Triangle height too large: %d", (vertices[2].y - vertices[0].y));
|
||||
return;
|
||||
}
|
||||
|
||||
if(abs(vertices[2].x - vertices[0].x) >= 1024 ||
|
||||
abs(vertices[2].x - vertices[1].x) >= 1024 ||
|
||||
abs(vertices[1].x - vertices[0].x) >= 1024)
|
||||
{
|
||||
//PSX_WARNING("[GPU] Triangle width too large: %d %d %d", abs(vertices[2].x - vertices[0].x), abs(vertices[2].x - vertices[1].x), abs(vertices[1].x - vertices[0].x));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!CalcIDeltas(idl, vertices[0], vertices[1], vertices[2]))
|
||||
return;
|
||||
|
||||
// [0] should be top vertex, [2] should be bottom vertex, [1] should be off to the side vertex.
|
||||
//
|
||||
//
|
||||
int32 y_start = vertices[0].y;
|
||||
int32 y_middle = vertices[1].y;
|
||||
int32 y_bound = vertices[2].y;
|
||||
|
||||
int64 base_coord;
|
||||
int64 base_step;
|
||||
|
||||
int64 bound_coord_ul;
|
||||
int64 bound_coord_us;
|
||||
|
||||
int64 bound_coord_ll;
|
||||
int64 bound_coord_ls;
|
||||
|
||||
bool right_facing;
|
||||
//bool bottom_up;
|
||||
i_group ig;
|
||||
|
||||
//
|
||||
// Find vertex with lowest X coordinate, and use as the base for calculating interpolants from.
|
||||
//
|
||||
{
|
||||
unsigned iggvi = 0;
|
||||
|
||||
//
|
||||
// <=, not <
|
||||
//
|
||||
if(vertices[1].x <= vertices[iggvi].x)
|
||||
iggvi = 1;
|
||||
|
||||
if(vertices[2].x <= vertices[iggvi].x)
|
||||
iggvi = 2;
|
||||
|
||||
ig.u = COORD_MF_INT(vertices[iggvi].u) + (1 << (COORD_FBS - 1));
|
||||
ig.v = COORD_MF_INT(vertices[iggvi].v) + (1 << (COORD_FBS - 1));
|
||||
ig.r = COORD_MF_INT(vertices[iggvi].r);
|
||||
ig.g = COORD_MF_INT(vertices[iggvi].g);
|
||||
ig.b = COORD_MF_INT(vertices[iggvi].b);
|
||||
|
||||
AddIDeltas_DX<goraud, textured>(ig, idl, -vertices[iggvi].x);
|
||||
AddIDeltas_DY<goraud, textured>(ig, idl, -vertices[iggvi].y);
|
||||
}
|
||||
|
||||
base_coord = MakePolyXFP(vertices[0].x);
|
||||
base_step = MakePolyXFPStep((vertices[2].x - vertices[0].x), (vertices[2].y - vertices[0].y));
|
||||
|
||||
bound_coord_ul = MakePolyXFP(vertices[0].x);
|
||||
bound_coord_ll = MakePolyXFP(vertices[1].x);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
if(vertices[1].y == vertices[0].y)
|
||||
{
|
||||
bound_coord_us = 0;
|
||||
right_facing = (bool)(vertices[1].x > vertices[0].x);
|
||||
}
|
||||
else
|
||||
{
|
||||
bound_coord_us = MakePolyXFPStep((vertices[1].x - vertices[0].x), (vertices[1].y - vertices[0].y));
|
||||
right_facing = (bool)(bound_coord_us > base_step);
|
||||
}
|
||||
|
||||
if(vertices[2].y == vertices[1].y)
|
||||
bound_coord_ls = 0;
|
||||
else
|
||||
bound_coord_ls = MakePolyXFPStep((vertices[2].x - vertices[1].x), (vertices[2].y - vertices[1].y));
|
||||
|
||||
if(y_start < ClipY0)
|
||||
{
|
||||
int32 count = ClipY0 - y_start;
|
||||
|
||||
y_start = ClipY0;
|
||||
base_coord += base_step * count;
|
||||
bound_coord_ul += bound_coord_us * count;
|
||||
|
||||
if(y_middle < ClipY0)
|
||||
{
|
||||
int32 count_ls = ClipY0 - y_middle;
|
||||
|
||||
y_middle = ClipY0;
|
||||
bound_coord_ll += bound_coord_ls * count_ls;
|
||||
}
|
||||
}
|
||||
|
||||
if(y_bound > (ClipY1 + 1))
|
||||
{
|
||||
y_bound = ClipY1 + 1;
|
||||
|
||||
if(y_middle > y_bound)
|
||||
y_middle = y_bound;
|
||||
}
|
||||
|
||||
if(right_facing)
|
||||
{
|
||||
for(int32 y = y_start; y < y_middle; y++)
|
||||
{
|
||||
DrawSpan<goraud, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(y, clut, GetPolyXFP_Int(base_coord), GetPolyXFP_Int(bound_coord_ul), ig, idl);
|
||||
base_coord += base_step;
|
||||
bound_coord_ul += bound_coord_us;
|
||||
}
|
||||
|
||||
for(int32 y = y_middle; y < y_bound; y++)
|
||||
{
|
||||
DrawSpan<goraud, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(y, clut, GetPolyXFP_Int(base_coord), GetPolyXFP_Int(bound_coord_ll), ig, idl);
|
||||
base_coord += base_step;
|
||||
bound_coord_ll += bound_coord_ls;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int32 y = y_start; y < y_middle; y++)
|
||||
{
|
||||
DrawSpan<goraud, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(y, clut, GetPolyXFP_Int(bound_coord_ul), GetPolyXFP_Int(base_coord), ig, idl);
|
||||
base_coord += base_step;
|
||||
bound_coord_ul += bound_coord_us;
|
||||
}
|
||||
|
||||
for(int32 y = y_middle; y < y_bound; y++)
|
||||
{
|
||||
DrawSpan<goraud, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(y, clut, GetPolyXFP_Int(bound_coord_ll), GetPolyXFP_Int(base_coord), ig, idl);
|
||||
base_coord += base_step;
|
||||
bound_coord_ll += bound_coord_ls;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
printf("[GPU] Vertices: %d:%d(r=%d, g=%d, b=%d) -> %d:%d(r=%d, g=%d, b=%d) -> %d:%d(r=%d, g=%d, b=%d)\n\n\n", vertices[0].x, vertices[0].y,
|
||||
vertices[0].r, vertices[0].g, vertices[0].b,
|
||||
vertices[1].x, vertices[1].y,
|
||||
vertices[1].r, vertices[1].g, vertices[1].b,
|
||||
vertices[2].x, vertices[2].y,
|
||||
vertices[2].r, vertices[2].g, vertices[2].b);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<int numvertices, bool goraud, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
INLINE void PS_GPU::Command_DrawPolygon(const uint32 *cb)
|
||||
{
|
||||
const unsigned cb0 = cb[0];
|
||||
tri_vertex vertices[3];
|
||||
uint32 clut = 0;
|
||||
unsigned sv = 0;
|
||||
//uint32 tpage = 0;
|
||||
|
||||
// Base timing is approximate, and could be improved.
|
||||
if(numvertices == 4 && InCmd == INCMD_QUAD)
|
||||
DrawTimeAvail -= (28 + 18);
|
||||
else
|
||||
DrawTimeAvail -= (64 + 18);
|
||||
|
||||
if(goraud && textured)
|
||||
DrawTimeAvail -= 150 * 3;
|
||||
else if(goraud)
|
||||
DrawTimeAvail -= 96 * 3;
|
||||
else if(textured)
|
||||
DrawTimeAvail -= 60 * 3;
|
||||
|
||||
if(numvertices == 4)
|
||||
{
|
||||
if(InCmd == INCMD_QUAD)
|
||||
{
|
||||
memcpy(&vertices[0], &InQuad_F3Vertices[1], 2 * sizeof(tri_vertex));
|
||||
clut = InQuad_clut;
|
||||
sv = 2;
|
||||
}
|
||||
}
|
||||
//else
|
||||
// memset(vertices, 0, sizeof(vertices));
|
||||
|
||||
for(unsigned v = sv; v < 3; v++)
|
||||
{
|
||||
if(v == 0 || goraud)
|
||||
{
|
||||
uint32 raw_color = (*cb & 0xFFFFFF);
|
||||
|
||||
vertices[v].r = raw_color & 0xFF;
|
||||
vertices[v].g = (raw_color >> 8) & 0xFF;
|
||||
vertices[v].b = (raw_color >> 16) & 0xFF;
|
||||
|
||||
cb++;
|
||||
}
|
||||
else
|
||||
{
|
||||
vertices[v].r = vertices[0].r;
|
||||
vertices[v].g = vertices[0].g;
|
||||
vertices[v].b = vertices[0].b;
|
||||
}
|
||||
|
||||
vertices[v].x = sign_x_to_s32(11, ((int16)(*cb & 0xFFFF))) + OffsX;
|
||||
vertices[v].y = sign_x_to_s32(11, ((int16)(*cb >> 16))) + OffsY;
|
||||
cb++;
|
||||
|
||||
if(textured)
|
||||
{
|
||||
vertices[v].u = (*cb & 0xFF);
|
||||
vertices[v].v = (*cb >> 8) & 0xFF;
|
||||
|
||||
if(v == 0)
|
||||
{
|
||||
clut = ((*cb >> 16) & 0xFFFF) << 4;
|
||||
}
|
||||
|
||||
cb++;
|
||||
}
|
||||
}
|
||||
|
||||
if(numvertices == 4)
|
||||
{
|
||||
if(InCmd == INCMD_QUAD)
|
||||
{
|
||||
InCmd = INCMD_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
InCmd = INCMD_QUAD;
|
||||
InCmd_CC = cb0 >> 24;
|
||||
memcpy(&InQuad_F3Vertices[0], &vertices[0], sizeof(tri_vertex) * 3);
|
||||
InQuad_clut = clut;
|
||||
}
|
||||
}
|
||||
|
||||
DrawTriangle<goraud, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(vertices, clut);
|
||||
}
|
||||
|
||||
#undef COORD_FBS
|
||||
#undef COORD_MF_INT
|
|
@ -0,0 +1,235 @@
|
|||
template<bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA, bool FlipX, bool FlipY>
|
||||
void PS_GPU::DrawSprite(int32 x_arg, int32 y_arg, int32 w, int32 h, uint8 u_arg, uint8 v_arg, uint32 color, uint32 clut_offset)
|
||||
{
|
||||
const int32 r = color & 0xFF;
|
||||
const int32 g = (color >> 8) & 0xFF;
|
||||
const int32 b = (color >> 16) & 0xFF;
|
||||
const uint16 fill_color = 0x8000 | ((r >> 3) << 0) | ((g >> 3) << 5) | ((b >> 3) << 10);
|
||||
|
||||
int32 x_start, x_bound;
|
||||
int32 y_start, y_bound;
|
||||
uint8 u, v;
|
||||
int v_inc = 1, u_inc = 1;
|
||||
|
||||
//printf("[GPU] Sprite: x=%d, y=%d, w=%d, h=%d\n", x_arg, y_arg, w, h);
|
||||
|
||||
x_start = x_arg;
|
||||
x_bound = x_arg + w;
|
||||
|
||||
y_start = y_arg;
|
||||
y_bound = y_arg + h;
|
||||
|
||||
if(textured)
|
||||
{
|
||||
u = u_arg;
|
||||
v = v_arg;
|
||||
|
||||
//if(FlipX || FlipY || (u & 1) || (v & 1) || ((TexMode_TA == 0) && ((u & 3) || (v & 3))))
|
||||
// fprintf(stderr, "Flippy: %d %d 0x%02x 0x%02x\n", FlipX, FlipY, u, v);
|
||||
|
||||
if(FlipX)
|
||||
{
|
||||
u_inc = -1;
|
||||
u |= 1;
|
||||
}
|
||||
// FIXME: Something weird happens when lower bit of u is set and we're not doing horizontal flip, but I'm not sure what it is exactly(needs testing)
|
||||
// It may only happen to the first pixel, so look for that case too during testing.
|
||||
//else
|
||||
// u = (u + 1) & ~1;
|
||||
|
||||
if(FlipY)
|
||||
{
|
||||
v_inc = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(x_start < ClipX0)
|
||||
{
|
||||
if(textured)
|
||||
u += (ClipX0 - x_start) * u_inc;
|
||||
|
||||
x_start = ClipX0;
|
||||
}
|
||||
|
||||
if(y_start < ClipY0)
|
||||
{
|
||||
if(textured)
|
||||
v += (ClipY0 - y_start) * v_inc;
|
||||
|
||||
y_start = ClipY0;
|
||||
}
|
||||
|
||||
if(x_bound > (ClipX1 + 1))
|
||||
x_bound = ClipX1 + 1;
|
||||
|
||||
if(y_bound > (ClipY1 + 1))
|
||||
y_bound = ClipY1 + 1;
|
||||
|
||||
if(y_bound > y_start && x_bound > x_start)
|
||||
{
|
||||
//
|
||||
// Note(TODO): From tests on a PS1, even a 0-width sprite takes up time to "draw" proportional to its height.
|
||||
//
|
||||
int32 suck_time = (x_bound - x_start) * (y_bound - y_start);
|
||||
|
||||
// Disabled until we can get it to take into account texture windowing, which can cause large sprites to be drawn entirely from cache(and not suffer from a texturing
|
||||
// penalty); and disabled until we find a game that needs more accurate sprite draw timing. :b
|
||||
#if 0
|
||||
if(textured)
|
||||
{
|
||||
// Empirically-observed approximations of time(66MHz cycles) taken to draw large textured sprites in the various texture depths, when the texture data and CLUT data
|
||||
// was zero(non-zero takes longer to draw, TODO test that more):
|
||||
// 4bpp - area * 2
|
||||
// 8bpp - area * 3
|
||||
// 15/16bpp - area * 5
|
||||
// (other factors come into more importance for smaller sprites)
|
||||
static const int cw[4] = { 64, 32, 32, 32 };
|
||||
static const int ch[4] = { 64, 64, 32, 32 };
|
||||
static const int mm[4] = { 2 - 1, 3 - 1, 5 - 1, 5 - 1 };
|
||||
|
||||
// Parts of the first few(up to texture cache height) horizontal lines can be in cache, so assume they are.
|
||||
suck_time += mm[TexMode_TA] * std::max<int>(0, (x_bound - x_start) - cw[TexMode_TA]) * std::min<int>(ch[TexMode_TA], y_bound - y_start);
|
||||
|
||||
// The rest of the horizontal lines should not possibly have parts in the cache now.
|
||||
suck_time += mm[TexMode_TA] * (x_bound - x_start) * std::max<int>(0, (y_bound - y_start) - ch[TexMode_TA]);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
||||
if((BlendMode >= 0) || MaskEval_TA)
|
||||
{
|
||||
suck_time += ((((x_bound + 1) & ~1) - (x_start & ~1)) * (y_bound - y_start)) >> 1;
|
||||
}
|
||||
|
||||
DrawTimeAvail -= suck_time;
|
||||
}
|
||||
|
||||
|
||||
//HeightMode && !dfe && ((y & 1) == ((DisplayFB_YStart + !field_atvs) & 1)) && !DisplayOff
|
||||
//printf("%d:%d, %d, %d ---- heightmode=%d displayfb_ystart=%d field_atvs=%d displayoff=%d\n", w, h, scanline, dfe, HeightMode, DisplayFB_YStart, field_atvs, DisplayOff);
|
||||
|
||||
for(int32 y = y_start; MDFN_LIKELY(y < y_bound); y++)
|
||||
{
|
||||
uint8 u_r;
|
||||
|
||||
if(textured)
|
||||
u_r = u;
|
||||
|
||||
if(!LineSkipTest(y))
|
||||
{
|
||||
for(int32 x = x_start; MDFN_LIKELY(x < x_bound); x++)
|
||||
{
|
||||
if(textured)
|
||||
{
|
||||
uint16 fbw = GetTexel<TexMode_TA>(clut_offset, u_r, v);
|
||||
|
||||
if(fbw)
|
||||
{
|
||||
if(TexMult)
|
||||
{
|
||||
fbw = ModTexel(fbw, r, g, b, 3, 2);
|
||||
}
|
||||
PlotPixel<BlendMode, MaskEval_TA, true>(x, y, fbw);
|
||||
}
|
||||
}
|
||||
else
|
||||
PlotPixel<BlendMode, MaskEval_TA, false>(x, y, fill_color);
|
||||
|
||||
if(textured)
|
||||
u_r += u_inc;
|
||||
}
|
||||
}
|
||||
if(textured)
|
||||
v += v_inc;
|
||||
}
|
||||
}
|
||||
|
||||
template<uint8 raw_size, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
INLINE void PS_GPU::Command_DrawSprite(const uint32 *cb)
|
||||
{
|
||||
int32 x, y;
|
||||
int32 w, h;
|
||||
uint8 u = 0, v = 0;
|
||||
uint32 color = 0;
|
||||
uint32 clut = 0;
|
||||
|
||||
DrawTimeAvail -= 16; // FIXME, correct time.
|
||||
|
||||
color = *cb & 0x00FFFFFF;
|
||||
cb++;
|
||||
|
||||
x = sign_x_to_s32(11, (*cb & 0xFFFF));
|
||||
y = sign_x_to_s32(11, (*cb >> 16));
|
||||
cb++;
|
||||
|
||||
if(textured)
|
||||
{
|
||||
u = *cb & 0xFF;
|
||||
v = (*cb >> 8) & 0xFF;
|
||||
clut = ((*cb >> 16) & 0xFFFF) << 4;
|
||||
cb++;
|
||||
}
|
||||
|
||||
switch(raw_size)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
w = (*cb & 0x3FF);
|
||||
h = (*cb >> 16) & 0x1FF;
|
||||
cb++;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
w = 1;
|
||||
h = 1;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
w = 8;
|
||||
h = 8;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
w = 16;
|
||||
h = 16;
|
||||
break;
|
||||
}
|
||||
|
||||
//printf("SPRITE: %d %d %d -- %d %d\n", raw_size, x, y, w, h);
|
||||
|
||||
x = sign_x_to_s32(11, x + OffsX);
|
||||
y = sign_x_to_s32(11, y + OffsY);
|
||||
|
||||
switch(SpriteFlip & 0x3000)
|
||||
{
|
||||
case 0x0000:
|
||||
if(!TexMult || color == 0x808080)
|
||||
DrawSprite<textured, BlendMode, false, TexMode_TA, MaskEval_TA, false, false>(x, y, w, h, u, v, color, clut);
|
||||
else
|
||||
DrawSprite<textured, BlendMode, true, TexMode_TA, MaskEval_TA, false, false>(x, y, w, h, u, v, color, clut);
|
||||
break;
|
||||
|
||||
case 0x1000:
|
||||
if(!TexMult || color == 0x808080)
|
||||
DrawSprite<textured, BlendMode, false, TexMode_TA, MaskEval_TA, true, false>(x, y, w, h, u, v, color, clut);
|
||||
else
|
||||
DrawSprite<textured, BlendMode, true, TexMode_TA, MaskEval_TA, true, false>(x, y, w, h, u, v, color, clut);
|
||||
break;
|
||||
|
||||
case 0x2000:
|
||||
if(!TexMult || color == 0x808080)
|
||||
DrawSprite<textured, BlendMode, false, TexMode_TA, MaskEval_TA, false, true>(x, y, w, h, u, v, color, clut);
|
||||
else
|
||||
DrawSprite<textured, BlendMode, true, TexMode_TA, MaskEval_TA, false, true>(x, y, w, h, u, v, color, clut);
|
||||
break;
|
||||
|
||||
case 0x3000:
|
||||
if(!TexMult || color == 0x808080)
|
||||
DrawSprite<textured, BlendMode, false, TexMode_TA, MaskEval_TA, true, true>(x, y, w, h, u, v, color, clut);
|
||||
else
|
||||
DrawSprite<textured, BlendMode, true, TexMode_TA, MaskEval_TA, true, true>(x, y, w, h, u, v, color, clut);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include "state.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
void GTE_Power(void);
|
||||
int GTE_StateAction(StateMem *sm, int load, int data_only);
|
||||
|
||||
int32 GTE_Instruction(uint32 instr);
|
||||
|
||||
void GTE_WriteCR(unsigned int which, uint32 value);
|
||||
void GTE_WriteDR(unsigned int which, uint32 value);
|
||||
|
||||
uint32 GTE_ReadCR(unsigned int which);
|
||||
uint32 GTE_ReadDR(unsigned int which);
|
||||
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,322 @@
|
|||
#include "../psx.h"
|
||||
#include "../frontio.h"
|
||||
#include "dualanalog.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_DualAnalog : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_DualAnalog(bool joystick_mode_);
|
||||
virtual ~InputDevice_DualAnalog();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name);
|
||||
virtual void UpdateInput(const void *data);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool GetDSR(void);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
|
||||
private:
|
||||
|
||||
bool joystick_mode;
|
||||
bool dtr;
|
||||
|
||||
uint8 buttons[2];
|
||||
uint8 axes[2][2];
|
||||
|
||||
int32 command_phase;
|
||||
uint32 bitpos;
|
||||
uint8 receive_buffer;
|
||||
|
||||
uint8 command;
|
||||
|
||||
uint8 transmit_buffer[8];
|
||||
uint32 transmit_pos;
|
||||
uint32 transmit_count;
|
||||
};
|
||||
|
||||
InputDevice_DualAnalog::InputDevice_DualAnalog(bool joystick_mode_) : joystick_mode(joystick_mode_)
|
||||
{
|
||||
Power();
|
||||
}
|
||||
|
||||
InputDevice_DualAnalog::~InputDevice_DualAnalog()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void InputDevice_DualAnalog::Power(void)
|
||||
{
|
||||
dtr = 0;
|
||||
|
||||
buttons[0] = buttons[1] = 0;
|
||||
|
||||
command_phase = 0;
|
||||
|
||||
bitpos = 0;
|
||||
|
||||
receive_buffer = 0;
|
||||
|
||||
command = 0;
|
||||
|
||||
memset(transmit_buffer, 0, sizeof(transmit_buffer));
|
||||
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
|
||||
int InputDevice_DualAnalog::StateAction(StateMem* sm, int load, int data_only, const char* section_name)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(dtr),
|
||||
|
||||
SFARRAY(buttons, sizeof(buttons)),
|
||||
SFARRAY(&axes[0][0], sizeof(axes)),
|
||||
|
||||
SFVAR(command_phase),
|
||||
SFVAR(bitpos),
|
||||
SFVAR(receive_buffer),
|
||||
|
||||
SFVAR(command),
|
||||
|
||||
SFARRAY(transmit_buffer, sizeof(transmit_buffer)),
|
||||
SFVAR(transmit_pos),
|
||||
SFVAR(transmit_count),
|
||||
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name);
|
||||
|
||||
if(load)
|
||||
{
|
||||
if((transmit_pos + transmit_count) > sizeof(transmit_buffer))
|
||||
{
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
void InputDevice_DualAnalog::UpdateInput(const void *data)
|
||||
{
|
||||
uint8 *d8 = (uint8 *)data;
|
||||
|
||||
buttons[0] = d8[0];
|
||||
buttons[1] = d8[1];
|
||||
|
||||
for(int stick = 0; stick < 2; stick++)
|
||||
{
|
||||
for(int axis = 0; axis < 2; axis++)
|
||||
{
|
||||
const uint8* aba = &d8[2] + stick * 8 + axis * 4;
|
||||
int32 tmp;
|
||||
|
||||
tmp = 32768 + MDFN_de16lsb(&aba[0]) - ((int32)MDFN_de16lsb(&aba[2]) * 32768 / 32767);
|
||||
tmp >>= 8;
|
||||
|
||||
axes[stick][axis] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
//printf("%d %d %d %d\n", axes[0][0], axes[0][1], axes[1][0], axes[1][1]);
|
||||
}
|
||||
|
||||
|
||||
void InputDevice_DualAnalog::SetDTR(bool new_dtr)
|
||||
{
|
||||
if(!dtr && new_dtr)
|
||||
{
|
||||
command_phase = 0;
|
||||
bitpos = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
else if(dtr && !new_dtr)
|
||||
{
|
||||
//if(bitpos || transmit_count)
|
||||
// printf("[PAD] Abort communication!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
|
||||
}
|
||||
|
||||
dtr = new_dtr;
|
||||
}
|
||||
|
||||
bool InputDevice_DualAnalog::GetDSR(void)
|
||||
{
|
||||
if(!dtr)
|
||||
return(0);
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
return(1);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool InputDevice_DualAnalog::Clock(bool TxD, int32 &dsr_pulse_delay)
|
||||
{
|
||||
bool ret = 1;
|
||||
|
||||
dsr_pulse_delay = 0;
|
||||
|
||||
if(!dtr)
|
||||
return(1);
|
||||
|
||||
if(transmit_count)
|
||||
ret = (transmit_buffer[transmit_pos] >> bitpos) & 1;
|
||||
|
||||
receive_buffer &= ~(1 << bitpos);
|
||||
receive_buffer |= TxD << bitpos;
|
||||
bitpos = (bitpos + 1) & 0x7;
|
||||
|
||||
if(!bitpos)
|
||||
{
|
||||
//printf("[PAD] Receive: %02x -- command_phase=%d\n", receive_buffer, command_phase);
|
||||
|
||||
if(transmit_count)
|
||||
{
|
||||
transmit_pos++;
|
||||
transmit_count--;
|
||||
}
|
||||
|
||||
|
||||
switch(command_phase)
|
||||
{
|
||||
case 0:
|
||||
if(receive_buffer != 0x01)
|
||||
command_phase = -1;
|
||||
else
|
||||
{
|
||||
transmit_buffer[0] = joystick_mode ? 0x53 : 0x73;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
command = receive_buffer;
|
||||
command_phase++;
|
||||
|
||||
transmit_buffer[0] = 0x5A;
|
||||
|
||||
//if(command != 0x42)
|
||||
// fprintf(stderr, "Gamepad unhandled command: 0x%02x\n", command);
|
||||
|
||||
if(command == 0x42)
|
||||
{
|
||||
transmit_buffer[1] = 0xFF ^ buttons[0];
|
||||
transmit_buffer[2] = 0xFF ^ buttons[1];
|
||||
transmit_buffer[3] = axes[0][0];
|
||||
transmit_buffer[4] = axes[0][1];
|
||||
transmit_buffer[5] = axes[1][0];
|
||||
transmit_buffer[6] = axes[1][1];
|
||||
transmit_pos = 0;
|
||||
transmit_count = 7;
|
||||
}
|
||||
else
|
||||
{
|
||||
command_phase = -1;
|
||||
transmit_buffer[1] = 0;
|
||||
transmit_buffer[2] = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
//if(receive_buffer)
|
||||
// printf("%d: %02x\n", 7 - transmit_count, receive_buffer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
dsr_pulse_delay = 0x40; //0x100;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
InputDevice *Device_DualAnalog_Create(bool joystick_mode)
|
||||
{
|
||||
return new InputDevice_DualAnalog(joystick_mode);
|
||||
}
|
||||
|
||||
|
||||
InputDeviceInputInfoStruct Device_DualAnalog_IDII[24] =
|
||||
{
|
||||
{ "select", "SELECT", 4, IDIT_BUTTON, NULL },
|
||||
{ "l3", "Left Stick, Button(L3)", 18, IDIT_BUTTON, NULL },
|
||||
{ "r3", "Right stick, Button(R3)", 23, IDIT_BUTTON, NULL },
|
||||
{ "start", "START", 5, IDIT_BUTTON, NULL },
|
||||
{ "up", "D-Pad UP ↑", 0, IDIT_BUTTON, "down" },
|
||||
{ "right", "D-Pad RIGHT →", 3, IDIT_BUTTON, "left" },
|
||||
{ "down", "D-Pad DOWN ↓", 1, IDIT_BUTTON, "up" },
|
||||
{ "left", "D-Pad LEFT ←", 2, IDIT_BUTTON, "right" },
|
||||
|
||||
{ "l2", "L2 (rear left shoulder)", 11, IDIT_BUTTON, NULL },
|
||||
{ "r2", "R2 (rear right shoulder)", 13, IDIT_BUTTON, NULL },
|
||||
{ "l1", "L1 (front left shoulder)", 10, IDIT_BUTTON, NULL },
|
||||
{ "r1", "R1 (front right shoulder)", 12, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "triangle", "△ (upper)", 6, IDIT_BUTTON_CAN_RAPID, NULL },
|
||||
{ "circle", "○ (right)", 9, IDIT_BUTTON_CAN_RAPID, NULL },
|
||||
{ "cross", "x (lower)", 7, IDIT_BUTTON_CAN_RAPID, NULL },
|
||||
{ "square", "□ (left)", 8, IDIT_BUTTON_CAN_RAPID, NULL },
|
||||
|
||||
{ "rstick_right", "Right Stick RIGHT →", 22, IDIT_BUTTON_ANALOG, NULL, { NULL, NULL, NULL }, IDIT_BUTTON_ANALOG_FLAG_SQLR },
|
||||
{ "rstick_left", "Right Stick LEFT ←", 21, IDIT_BUTTON_ANALOG, NULL, { NULL, NULL, NULL }, IDIT_BUTTON_ANALOG_FLAG_SQLR },
|
||||
{ "rstick_down", "Right Stick DOWN ↓", 20, IDIT_BUTTON_ANALOG, NULL, { NULL, NULL, NULL }, IDIT_BUTTON_ANALOG_FLAG_SQLR },
|
||||
{ "rstick_up", "Right Stick UP ↑", 19, IDIT_BUTTON_ANALOG, NULL, { NULL, NULL, NULL }, IDIT_BUTTON_ANALOG_FLAG_SQLR },
|
||||
|
||||
{ "lstick_right", "Left Stick RIGHT →", 17, IDIT_BUTTON_ANALOG, NULL, { NULL, NULL, NULL }, IDIT_BUTTON_ANALOG_FLAG_SQLR },
|
||||
{ "lstick_left", "Left Stick LEFT ←", 16, IDIT_BUTTON_ANALOG, NULL, { NULL, NULL, NULL }, IDIT_BUTTON_ANALOG_FLAG_SQLR },
|
||||
{ "lstick_down", "Left Stick DOWN ↓", 15, IDIT_BUTTON_ANALOG, NULL, { NULL, NULL, NULL }, IDIT_BUTTON_ANALOG_FLAG_SQLR },
|
||||
{ "lstick_up", "Left Stick UP ↑", 14, IDIT_BUTTON_ANALOG, NULL, { NULL, NULL, NULL }, IDIT_BUTTON_ANALOG_FLAG_SQLR },
|
||||
|
||||
};
|
||||
|
||||
// Not sure if all these buttons are named correctly!
|
||||
InputDeviceInputInfoStruct Device_AnalogJoy_IDII[24] =
|
||||
{
|
||||
{ "select", "SELECT", 8, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", 0, IDIT_BUTTON },
|
||||
{ NULL, "empty", 0, IDIT_BUTTON },
|
||||
{ "start", "START", 9, IDIT_BUTTON, NULL },
|
||||
{ "up", "Thumbstick UP ↑", 14, IDIT_BUTTON, "down" },
|
||||
{ "right", "Thumbstick RIGHT →", 17, IDIT_BUTTON, "left" },
|
||||
{ "down", "Thumbstick DOWN ↓", 15, IDIT_BUTTON, "up" },
|
||||
{ "left", "Thumbstick LEFT ←", 16, IDIT_BUTTON, "right" },
|
||||
|
||||
{ "l2", "Left stick, Trigger", 2, IDIT_BUTTON, NULL },
|
||||
{ "r2", "Left stick, Pinky", 3, IDIT_BUTTON, NULL },
|
||||
{ "l1", "Left stick, L-thumb", 0, IDIT_BUTTON, NULL },
|
||||
{ "r1", "Left stick, R-thumb", 1, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "triangle", "Right stick, Pinky", 13, IDIT_BUTTON, NULL },
|
||||
{ "circle", "Right stick, R-thumb", 11, IDIT_BUTTON, NULL },
|
||||
{ "cross", "Right stick, L-thumb", 10, IDIT_BUTTON, NULL },
|
||||
{ "square", "Right stick, Trigger", 12, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "rstick_right", "Right Stick, RIGHT →", 21, IDIT_BUTTON_ANALOG, NULL, { NULL, NULL, NULL }, IDIT_BUTTON_ANALOG_FLAG_SQLR },
|
||||
{ "rstick_left", "Right Stick, LEFT ←", 20, IDIT_BUTTON_ANALOG, NULL, { NULL, NULL, NULL }, IDIT_BUTTON_ANALOG_FLAG_SQLR },
|
||||
{ "rstick_down", "Right Stick, BACK ↓", 19, IDIT_BUTTON_ANALOG, NULL, { NULL, NULL, NULL }, IDIT_BUTTON_ANALOG_FLAG_SQLR },
|
||||
{ "rstick_up", "Right Stick, FORE ↑", 18, IDIT_BUTTON_ANALOG, NULL, { NULL, NULL, NULL }, IDIT_BUTTON_ANALOG_FLAG_SQLR },
|
||||
|
||||
{ "lstick_right", "Left Stick, RIGHT →", 7, IDIT_BUTTON_ANALOG, NULL, { NULL, NULL, NULL }, IDIT_BUTTON_ANALOG_FLAG_SQLR },
|
||||
{ "lstick_left", "Left Stick, LEFT ←", 6, IDIT_BUTTON_ANALOG, NULL, { NULL, NULL, NULL }, IDIT_BUTTON_ANALOG_FLAG_SQLR },
|
||||
{ "lstick_down", "Left Stick, BACK ↓", 5, IDIT_BUTTON_ANALOG, NULL, { NULL, NULL, NULL }, IDIT_BUTTON_ANALOG_FLAG_SQLR },
|
||||
{ "lstick_up", "Left Stick, FORE ↑", 4, IDIT_BUTTON_ANALOG, NULL, { NULL, NULL, NULL }, IDIT_BUTTON_ANALOG_FLAG_SQLR },
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef __MDFN_PSX_INPUT_DUALANALOG_H
|
||||
#define __MDFN_PSX_INPUT_DUALANALOG_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
InputDevice *Device_DualAnalog_Create(bool joystick_mode);
|
||||
extern InputDeviceInputInfoStruct Device_DualAnalog_IDII[24];
|
||||
extern InputDeviceInputInfoStruct Device_AnalogJoy_IDII[24];
|
||||
}
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,11 @@
|
|||
#ifndef __MDFN_PSX_INPUT_DUALSHOCK_H
|
||||
#define __MDFN_PSX_INPUT_DUALSHOCK_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
InputDevice *Device_DualShock_Create(const std::string &name);
|
||||
extern InputDeviceInputInfoStruct Device_DualShock_IDII[26];
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,276 @@
|
|||
#include "../psx.h"
|
||||
#include "../frontio.h"
|
||||
#include "gamepad.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_Gamepad : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_Gamepad();
|
||||
virtual ~InputDevice_Gamepad();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name);
|
||||
virtual void UpdateInput(const void *data);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool GetDSR(void);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
|
||||
private:
|
||||
|
||||
bool dtr;
|
||||
|
||||
uint8 buttons[2];
|
||||
|
||||
int32 command_phase;
|
||||
uint32 bitpos;
|
||||
uint8 receive_buffer;
|
||||
|
||||
uint8 command;
|
||||
|
||||
uint8 transmit_buffer[3];
|
||||
uint32 transmit_pos;
|
||||
uint32 transmit_count;
|
||||
};
|
||||
|
||||
InputDevice_Gamepad::InputDevice_Gamepad()
|
||||
{
|
||||
Power();
|
||||
}
|
||||
|
||||
InputDevice_Gamepad::~InputDevice_Gamepad()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void InputDevice_Gamepad::Power(void)
|
||||
{
|
||||
dtr = 0;
|
||||
|
||||
buttons[0] = buttons[1] = 0;
|
||||
|
||||
command_phase = 0;
|
||||
|
||||
bitpos = 0;
|
||||
|
||||
receive_buffer = 0;
|
||||
|
||||
command = 0;
|
||||
|
||||
memset(transmit_buffer, 0, sizeof(transmit_buffer));
|
||||
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
|
||||
int InputDevice_Gamepad::StateAction(StateMem* sm, int load, int data_only, const char* section_name)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(dtr),
|
||||
|
||||
SFARRAY(buttons, sizeof(buttons)),
|
||||
|
||||
SFVAR(command_phase),
|
||||
SFVAR(bitpos),
|
||||
SFVAR(receive_buffer),
|
||||
|
||||
SFVAR(command),
|
||||
|
||||
SFARRAY(transmit_buffer, sizeof(transmit_buffer)),
|
||||
SFVAR(transmit_pos),
|
||||
SFVAR(transmit_count),
|
||||
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name);
|
||||
|
||||
if(load)
|
||||
{
|
||||
if((transmit_pos + transmit_count) > sizeof(transmit_buffer))
|
||||
{
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
void InputDevice_Gamepad::UpdateInput(const void *data)
|
||||
{
|
||||
uint8 *d8 = (uint8 *)data;
|
||||
|
||||
buttons[0] = d8[0];
|
||||
buttons[1] = d8[1];
|
||||
}
|
||||
|
||||
|
||||
void InputDevice_Gamepad::SetDTR(bool new_dtr)
|
||||
{
|
||||
if(!dtr && new_dtr)
|
||||
{
|
||||
command_phase = 0;
|
||||
bitpos = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
else if(dtr && !new_dtr)
|
||||
{
|
||||
//if(bitpos || transmit_count)
|
||||
// printf("[PAD] Abort communication!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
|
||||
}
|
||||
|
||||
dtr = new_dtr;
|
||||
}
|
||||
|
||||
bool InputDevice_Gamepad::GetDSR(void)
|
||||
{
|
||||
if(!dtr)
|
||||
return(0);
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
return(1);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool InputDevice_Gamepad::Clock(bool TxD, int32 &dsr_pulse_delay)
|
||||
{
|
||||
bool ret = 1;
|
||||
|
||||
dsr_pulse_delay = 0;
|
||||
|
||||
if(!dtr)
|
||||
return(1);
|
||||
|
||||
if(transmit_count)
|
||||
ret = (transmit_buffer[transmit_pos] >> bitpos) & 1;
|
||||
|
||||
receive_buffer &= ~(1 << bitpos);
|
||||
receive_buffer |= TxD << bitpos;
|
||||
bitpos = (bitpos + 1) & 0x7;
|
||||
|
||||
if(!bitpos)
|
||||
{
|
||||
//printf("[PAD] Receive: %02x -- command_phase=%d\n", receive_buffer, command_phase);
|
||||
|
||||
if(transmit_count)
|
||||
{
|
||||
transmit_pos++;
|
||||
transmit_count--;
|
||||
}
|
||||
|
||||
|
||||
switch(command_phase)
|
||||
{
|
||||
case 0:
|
||||
if(receive_buffer != 0x01)
|
||||
command_phase = -1;
|
||||
else
|
||||
{
|
||||
transmit_buffer[0] = 0x41;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
command = receive_buffer;
|
||||
command_phase++;
|
||||
|
||||
transmit_buffer[0] = 0x5A;
|
||||
|
||||
//if(command != 0x42)
|
||||
// fprintf(stderr, "Gamepad unhandled command: 0x%02x\n", command);
|
||||
//assert(command == 0x42);
|
||||
if(command == 0x42)
|
||||
{
|
||||
//printf("PAD COmmand 0x42, sl=%u\n", GPU->GetScanlineNum());
|
||||
|
||||
transmit_buffer[1] = 0xFF ^ buttons[0];
|
||||
transmit_buffer[2] = 0xFF ^ buttons[1];
|
||||
transmit_pos = 0;
|
||||
transmit_count = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
command_phase = -1;
|
||||
transmit_buffer[1] = 0;
|
||||
transmit_buffer[2] = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
dsr_pulse_delay = 0x40; //0x100;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
InputDevice *Device_Gamepad_Create(void)
|
||||
{
|
||||
return new InputDevice_Gamepad();
|
||||
}
|
||||
|
||||
|
||||
InputDeviceInputInfoStruct Device_Gamepad_IDII[16] =
|
||||
{
|
||||
{ "select", "SELECT", 4, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", 0, IDIT_BUTTON },
|
||||
{ NULL, "empty", 0, IDIT_BUTTON },
|
||||
{ "start", "START", 5, IDIT_BUTTON, NULL },
|
||||
{ "up", "UP ↑", 0, IDIT_BUTTON, "down" },
|
||||
{ "right", "RIGHT →", 3, IDIT_BUTTON, "left" },
|
||||
{ "down", "DOWN ↓", 1, IDIT_BUTTON, "up" },
|
||||
{ "left", "LEFT ←", 2, IDIT_BUTTON, "right" },
|
||||
|
||||
{ "l2", "L2 (rear left shoulder)", 11, IDIT_BUTTON, NULL },
|
||||
{ "r2", "R2 (rear right shoulder)", 13, IDIT_BUTTON, NULL },
|
||||
{ "l1", "L1 (front left shoulder)", 10, IDIT_BUTTON, NULL },
|
||||
{ "r1", "R1 (front right shoulder)", 12, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "triangle", "△ (upper)", 6, IDIT_BUTTON_CAN_RAPID, NULL },
|
||||
{ "circle", "○ (right)", 9, IDIT_BUTTON_CAN_RAPID, NULL },
|
||||
{ "cross", "x (lower)", 7, IDIT_BUTTON_CAN_RAPID, NULL },
|
||||
{ "square", "□ (left)", 8, IDIT_BUTTON_CAN_RAPID, NULL },
|
||||
};
|
||||
|
||||
InputDeviceInputInfoStruct Device_Dancepad_IDII[16] =
|
||||
{
|
||||
{ "select", "SELECT", 0, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", 0, IDIT_BUTTON },
|
||||
{ NULL, "empty", 0, IDIT_BUTTON },
|
||||
{ "start", "START", 1, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "up", "UP ↑", 3, IDIT_BUTTON, NULL },
|
||||
{ "right", "RIGHT →", 6, IDIT_BUTTON, NULL },
|
||||
{ "down", "DOWN ↓", 8, IDIT_BUTTON, NULL },
|
||||
{ "left", "LEFT ←", 5, IDIT_BUTTON, NULL },
|
||||
|
||||
{ NULL, "empty", 0, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", 0, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", 0, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", 0, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "triangle", "△ (lower left)", 7, IDIT_BUTTON, NULL },
|
||||
{ "circle", "○ (upper right)", 4, IDIT_BUTTON, NULL },
|
||||
{ "cross", "x (upper left)", 2, IDIT_BUTTON, NULL },
|
||||
{ "square", "□ (lower right)", 9, IDIT_BUTTON, NULL },
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef __MDFN_PSX_INPUT_GAMEPAD_H
|
||||
#define __MDFN_PSX_INPUT_GAMEPAD_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
InputDevice *Device_Gamepad_Create(void);
|
||||
extern InputDeviceInputInfoStruct Device_Gamepad_IDII[16];
|
||||
extern InputDeviceInputInfoStruct Device_Dancepad_IDII[16];
|
||||
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,383 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../psx.h"
|
||||
#include "../frontio.h"
|
||||
#include "guncon.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_GunCon : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_GunCon(void);
|
||||
virtual ~InputDevice_GunCon();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name);
|
||||
virtual void UpdateInput(const void *data);
|
||||
virtual bool RequireNoFrameskip(void);
|
||||
virtual pscpu_timestamp_t GPULineHook(const pscpu_timestamp_t line_timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool GetDSR(void);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
|
||||
private:
|
||||
|
||||
bool dtr;
|
||||
|
||||
uint8 buttons;
|
||||
bool trigger_eff;
|
||||
bool trigger_noclear;
|
||||
uint16 hit_x, hit_y;
|
||||
|
||||
int16 nom_x, nom_y;
|
||||
int32 os_shot_counter;
|
||||
bool prev_oss;
|
||||
|
||||
int32 command_phase;
|
||||
uint32 bitpos;
|
||||
uint8 receive_buffer;
|
||||
|
||||
uint8 command;
|
||||
|
||||
uint8 transmit_buffer[16];
|
||||
uint32 transmit_pos;
|
||||
uint32 transmit_count;
|
||||
|
||||
//
|
||||
// Video timing stuff
|
||||
bool prev_vsync;
|
||||
int line_counter;
|
||||
};
|
||||
|
||||
InputDevice_GunCon::InputDevice_GunCon(void)
|
||||
{
|
||||
Power();
|
||||
}
|
||||
|
||||
InputDevice_GunCon::~InputDevice_GunCon()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void InputDevice_GunCon::Power(void)
|
||||
{
|
||||
dtr = 0;
|
||||
|
||||
buttons = 0;
|
||||
trigger_eff = 0;
|
||||
trigger_noclear = 0;
|
||||
hit_x = 0;
|
||||
hit_y = 0;
|
||||
|
||||
nom_x = 0;
|
||||
nom_y = 0;
|
||||
|
||||
os_shot_counter = 0;
|
||||
prev_oss = 0;
|
||||
|
||||
command_phase = 0;
|
||||
|
||||
bitpos = 0;
|
||||
|
||||
receive_buffer = 0;
|
||||
|
||||
command = 0;
|
||||
|
||||
memset(transmit_buffer, 0, sizeof(transmit_buffer));
|
||||
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
|
||||
prev_vsync = 0;
|
||||
line_counter = 0;
|
||||
}
|
||||
|
||||
int InputDevice_GunCon::StateAction(StateMem* sm, int load, int data_only, const char* section_name)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(dtr),
|
||||
|
||||
SFVAR(buttons),
|
||||
SFVAR(trigger_eff),
|
||||
SFVAR(trigger_noclear),
|
||||
SFVAR(hit_x),
|
||||
SFVAR(hit_y),
|
||||
|
||||
SFVAR(nom_x),
|
||||
SFVAR(nom_y),
|
||||
SFVAR(os_shot_counter),
|
||||
SFVAR(prev_oss),
|
||||
|
||||
SFVAR(command_phase),
|
||||
SFVAR(bitpos),
|
||||
SFVAR(receive_buffer),
|
||||
|
||||
SFVAR(command),
|
||||
|
||||
SFARRAY(transmit_buffer, sizeof(transmit_buffer)),
|
||||
SFVAR(transmit_pos),
|
||||
SFVAR(transmit_count),
|
||||
|
||||
SFVAR(prev_vsync),
|
||||
SFVAR(line_counter),
|
||||
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name);
|
||||
|
||||
if(load)
|
||||
{
|
||||
if((transmit_pos + transmit_count) > sizeof(transmit_buffer))
|
||||
{
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
void InputDevice_GunCon::UpdateInput(const void *data)
|
||||
{
|
||||
uint8 *d8 = (uint8 *)data;
|
||||
|
||||
nom_x = (int16)MDFN_de16lsb(&d8[0]);
|
||||
nom_y = (int16)MDFN_de16lsb(&d8[2]);
|
||||
|
||||
trigger_noclear = (bool)(d8[4] & 0x1);
|
||||
trigger_eff |= trigger_noclear;
|
||||
|
||||
buttons = d8[4] >> 1;
|
||||
|
||||
if(os_shot_counter > 0) // FIXME if UpdateInput() is ever called more than once per video frame(at ~50 or ~60Hz).
|
||||
os_shot_counter--;
|
||||
|
||||
if((d8[4] & 0x8) && !prev_oss && os_shot_counter == 0)
|
||||
os_shot_counter = 4;
|
||||
prev_oss = d8[4] & 0x8;
|
||||
|
||||
//MDFN_DispMessage("%08x %08x", nom_x, nom_y);
|
||||
}
|
||||
|
||||
bool InputDevice_GunCon::RequireNoFrameskip(void)
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
|
||||
pscpu_timestamp_t InputDevice_GunCon::GPULineHook(const pscpu_timestamp_t line_timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width,
|
||||
const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider)
|
||||
{
|
||||
if(vsync && !prev_vsync)
|
||||
line_counter = 0;
|
||||
|
||||
if(pixels && pix_clock)
|
||||
{
|
||||
const int avs = 16; // Not 16 for PAL, fixme.
|
||||
int32 gx;
|
||||
int32 gy;
|
||||
|
||||
gx = (nom_x * 2 + pix_clock_divider) / (pix_clock_divider * 2);
|
||||
gy = nom_y;
|
||||
|
||||
for(int32 ix = gx; ix < (gx + (int32)(pix_clock / 762925)); ix++)
|
||||
{
|
||||
if(ix >= 0 && ix < (int)width && line_counter >= (avs + gy) && line_counter < (avs + gy + 8))
|
||||
{
|
||||
int r, g, b, a;
|
||||
|
||||
format->DecodeColor(pixels[ix], r, g, b, a);
|
||||
|
||||
if((r + g + b) >= 0x40) // Wrong, but not COMPLETELY ABSOLUTELY wrong, at least. ;)
|
||||
{
|
||||
hit_x = (int64)(ix + pix_clock_offset) * 8000000 / pix_clock; // GunCon has what appears to be an 8.00MHz ceramic resonator in it.
|
||||
hit_y = line_counter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chair_x = gx;
|
||||
chair_y = (avs + gy) - line_counter;
|
||||
}
|
||||
|
||||
line_counter++;
|
||||
|
||||
return(PSX_EVENT_MAXTS);
|
||||
}
|
||||
|
||||
void InputDevice_GunCon::SetDTR(bool new_dtr)
|
||||
{
|
||||
if(!dtr && new_dtr)
|
||||
{
|
||||
command_phase = 0;
|
||||
bitpos = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
else if(dtr && !new_dtr)
|
||||
{
|
||||
//if(bitpos || transmit_count)
|
||||
// printf("[PAD] Abort communication!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
|
||||
}
|
||||
|
||||
dtr = new_dtr;
|
||||
}
|
||||
|
||||
bool InputDevice_GunCon::GetDSR(void)
|
||||
{
|
||||
if(!dtr)
|
||||
return(0);
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
return(1);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool InputDevice_GunCon::Clock(bool TxD, int32 &dsr_pulse_delay)
|
||||
{
|
||||
bool ret = 1;
|
||||
|
||||
dsr_pulse_delay = 0;
|
||||
|
||||
if(!dtr)
|
||||
return(1);
|
||||
|
||||
if(transmit_count)
|
||||
ret = (transmit_buffer[transmit_pos] >> bitpos) & 1;
|
||||
|
||||
receive_buffer &= ~(1 << bitpos);
|
||||
receive_buffer |= TxD << bitpos;
|
||||
bitpos = (bitpos + 1) & 0x7;
|
||||
|
||||
if(!bitpos)
|
||||
{
|
||||
//printf("[PAD] Receive: %02x -- command_phase=%d\n", receive_buffer, command_phase);
|
||||
|
||||
if(transmit_count)
|
||||
{
|
||||
transmit_pos++;
|
||||
transmit_count--;
|
||||
}
|
||||
|
||||
|
||||
switch(command_phase)
|
||||
{
|
||||
case 0:
|
||||
if(receive_buffer != 0x01)
|
||||
command_phase = -1;
|
||||
else
|
||||
{
|
||||
transmit_buffer[0] = 0x63;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
//if(receive_buffer)
|
||||
// printf("%02x\n", receive_buffer);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
command = receive_buffer;
|
||||
command_phase++;
|
||||
|
||||
transmit_buffer[0] = 0x5A;
|
||||
|
||||
//puts("MOO");
|
||||
//if(command != 0x42)
|
||||
// fprintf(stderr, "GunCon unhandled command: 0x%02x\n", command);
|
||||
//assert(command == 0x42);
|
||||
if(command == 0x42)
|
||||
{
|
||||
transmit_buffer[1] = 0xFF ^ ((buttons & 0x01) << 3);
|
||||
transmit_buffer[2] = 0xFF ^ (trigger_eff << 5) ^ ((buttons & 0x02) << 5);
|
||||
|
||||
if(os_shot_counter > 0)
|
||||
{
|
||||
hit_x = 0x01;
|
||||
hit_y = 0x0A;
|
||||
transmit_buffer[2] |= (1 << 5);
|
||||
if(os_shot_counter == 2 || os_shot_counter == 3)
|
||||
{
|
||||
transmit_buffer[2] &= ~(1 << 5);
|
||||
}
|
||||
}
|
||||
|
||||
MDFN_en16lsb(&transmit_buffer[3], hit_x);
|
||||
MDFN_en16lsb(&transmit_buffer[5], hit_y);
|
||||
|
||||
hit_x = 0x01;
|
||||
hit_y = 0x0A;
|
||||
|
||||
transmit_pos = 0;
|
||||
transmit_count = 7;
|
||||
|
||||
trigger_eff = trigger_noclear;
|
||||
}
|
||||
else
|
||||
{
|
||||
command_phase = -1;
|
||||
transmit_buffer[1] = 0;
|
||||
transmit_buffer[2] = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
dsr_pulse_delay = 100; //0x80; //0x40;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
InputDevice *Device_GunCon_Create(void)
|
||||
{
|
||||
return new InputDevice_GunCon();
|
||||
}
|
||||
|
||||
|
||||
InputDeviceInputInfoStruct Device_GunCon_IDII[6] =
|
||||
{
|
||||
{ "x_axis", "X Axis", -1, IDIT_X_AXIS },
|
||||
{ "y_axis", "Y Axis", -1, IDIT_Y_AXIS },
|
||||
|
||||
{ "trigger", "Trigger", 0, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "a", "A", 1, IDIT_BUTTON, NULL },
|
||||
{ "b", "B", 2, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "offscreen_shot", "Offscreen Shot(Simulated)", 3, IDIT_BUTTON, NULL }, // Useful for "Judge Dredd", and probably not much else.
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef __MDFN_PSX_INPUT_GUNCON_H
|
||||
#define __MDFN_PSX_INPUT_GUNCON_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
InputDevice *Device_GunCon_Create(void);
|
||||
extern InputDeviceInputInfoStruct Device_GunCon_IDII[6];
|
||||
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,381 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../psx.h"
|
||||
#include "../frontio.h"
|
||||
#include "justifier.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_Justifier : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_Justifier(void);
|
||||
virtual ~InputDevice_Justifier();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name);
|
||||
virtual void UpdateInput(const void *data);
|
||||
virtual bool RequireNoFrameskip(void);
|
||||
virtual pscpu_timestamp_t GPULineHook(const pscpu_timestamp_t timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool GetDSR(void);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
|
||||
private:
|
||||
|
||||
bool dtr;
|
||||
|
||||
uint8 buttons;
|
||||
bool trigger_eff;
|
||||
bool trigger_noclear;
|
||||
|
||||
bool need_hit_detect;
|
||||
|
||||
int16 nom_x, nom_y;
|
||||
int32 os_shot_counter;
|
||||
bool prev_oss;
|
||||
|
||||
int32 command_phase;
|
||||
uint32 bitpos;
|
||||
uint8 receive_buffer;
|
||||
|
||||
uint8 command;
|
||||
|
||||
uint8 transmit_buffer[16];
|
||||
uint32 transmit_pos;
|
||||
uint32 transmit_count;
|
||||
|
||||
//
|
||||
// Video timing stuff
|
||||
bool prev_vsync;
|
||||
int line_counter;
|
||||
};
|
||||
|
||||
InputDevice_Justifier::InputDevice_Justifier(void)
|
||||
{
|
||||
Power();
|
||||
}
|
||||
|
||||
InputDevice_Justifier::~InputDevice_Justifier()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void InputDevice_Justifier::Power(void)
|
||||
{
|
||||
dtr = 0;
|
||||
|
||||
buttons = 0;
|
||||
trigger_eff = 0;
|
||||
trigger_noclear = 0;
|
||||
|
||||
need_hit_detect = false;
|
||||
|
||||
nom_x = 0;
|
||||
nom_y = 0;
|
||||
|
||||
os_shot_counter = 0;
|
||||
prev_oss = 0;
|
||||
|
||||
command_phase = 0;
|
||||
|
||||
bitpos = 0;
|
||||
|
||||
receive_buffer = 0;
|
||||
|
||||
command = 0;
|
||||
|
||||
memset(transmit_buffer, 0, sizeof(transmit_buffer));
|
||||
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
|
||||
prev_vsync = 0;
|
||||
line_counter = 0;
|
||||
}
|
||||
|
||||
void InputDevice_Justifier::UpdateInput(const void *data)
|
||||
{
|
||||
uint8 *d8 = (uint8 *)data;
|
||||
|
||||
nom_x = (int16)MDFN_de16lsb(&d8[0]);
|
||||
nom_y = (int16)MDFN_de16lsb(&d8[2]);
|
||||
|
||||
trigger_noclear = (bool)(d8[4] & 0x1);
|
||||
trigger_eff |= trigger_noclear;
|
||||
|
||||
buttons = (d8[4] >> 1) & 0x3;
|
||||
|
||||
if(os_shot_counter > 0) // FIXME if UpdateInput() is ever called more than once per video frame(at ~50 or ~60Hz).
|
||||
os_shot_counter--;
|
||||
|
||||
if((d8[4] & 0x8) && !prev_oss && os_shot_counter == 0)
|
||||
os_shot_counter = 10;
|
||||
prev_oss = d8[4] & 0x8;
|
||||
}
|
||||
|
||||
int InputDevice_Justifier::StateAction(StateMem* sm, int load, int data_only, const char* section_name)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(dtr),
|
||||
|
||||
SFVAR(buttons),
|
||||
SFVAR(trigger_eff),
|
||||
SFVAR(trigger_noclear),
|
||||
|
||||
SFVAR(need_hit_detect),
|
||||
|
||||
SFVAR(nom_x),
|
||||
SFVAR(nom_y),
|
||||
SFVAR(os_shot_counter),
|
||||
SFVAR(prev_oss),
|
||||
|
||||
SFVAR(command_phase),
|
||||
SFVAR(bitpos),
|
||||
SFVAR(receive_buffer),
|
||||
|
||||
SFVAR(command),
|
||||
|
||||
SFARRAY(transmit_buffer, sizeof(transmit_buffer)),
|
||||
SFVAR(transmit_pos),
|
||||
SFVAR(transmit_count),
|
||||
|
||||
SFVAR(prev_vsync),
|
||||
SFVAR(line_counter),
|
||||
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name);
|
||||
|
||||
if(load)
|
||||
{
|
||||
if((transmit_pos + transmit_count) > sizeof(transmit_buffer))
|
||||
{
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
bool InputDevice_Justifier::RequireNoFrameskip(void)
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
|
||||
pscpu_timestamp_t InputDevice_Justifier::GPULineHook(const pscpu_timestamp_t timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width,
|
||||
const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider)
|
||||
{
|
||||
pscpu_timestamp_t ret = PSX_EVENT_MAXTS;
|
||||
|
||||
if(vsync && !prev_vsync)
|
||||
line_counter = 0;
|
||||
|
||||
if(pixels && pix_clock)
|
||||
{
|
||||
const int avs = 16; // Not 16 for PAL, fixme.
|
||||
int32 gx;
|
||||
int32 gy;
|
||||
int32 gxa;
|
||||
|
||||
gx = (nom_x * 2 + pix_clock_divider) / (pix_clock_divider * 2);
|
||||
gy = nom_y;
|
||||
gxa = gx; // - (pix_clock / 400000);
|
||||
//if(gxa < 0 && gx >= 0)
|
||||
// gxa = 0;
|
||||
|
||||
if(!os_shot_counter && need_hit_detect && gxa >= 0 && gxa < (int)width && line_counter >= (avs + gy - 1) && line_counter <= (avs + gy + 1))
|
||||
{
|
||||
int r, g, b, a;
|
||||
|
||||
format->DecodeColor(pixels[gxa], r, g, b, a);
|
||||
|
||||
if((r + g + b) >= 0x40) // Wrong, but not COMPLETELY ABSOLUTELY wrong, at least. ;)
|
||||
{
|
||||
ret = timestamp + (int64)(gxa + pix_clock_offset) * (44100 * 768) / pix_clock - 177;
|
||||
}
|
||||
}
|
||||
|
||||
chair_x = gx;
|
||||
chair_y = (avs + gy) - line_counter;
|
||||
}
|
||||
|
||||
line_counter++;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void InputDevice_Justifier::SetDTR(bool new_dtr)
|
||||
{
|
||||
if(!dtr && new_dtr)
|
||||
{
|
||||
command_phase = 0;
|
||||
bitpos = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
else if(dtr && !new_dtr)
|
||||
{
|
||||
//if(bitpos || transmit_count)
|
||||
// printf("[PAD] Abort communication!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
|
||||
}
|
||||
|
||||
dtr = new_dtr;
|
||||
}
|
||||
|
||||
bool InputDevice_Justifier::GetDSR(void)
|
||||
{
|
||||
if(!dtr)
|
||||
return(0);
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
return(1);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool InputDevice_Justifier::Clock(bool TxD, int32 &dsr_pulse_delay)
|
||||
{
|
||||
bool ret = 1;
|
||||
|
||||
dsr_pulse_delay = 0;
|
||||
|
||||
if(!dtr)
|
||||
return(1);
|
||||
|
||||
if(transmit_count)
|
||||
ret = (transmit_buffer[transmit_pos] >> bitpos) & 1;
|
||||
|
||||
receive_buffer &= ~(1 << bitpos);
|
||||
receive_buffer |= TxD << bitpos;
|
||||
bitpos = (bitpos + 1) & 0x7;
|
||||
|
||||
if(!bitpos)
|
||||
{
|
||||
//printf("[PAD] Receive: %02x -- command_phase=%d\n", receive_buffer, command_phase);
|
||||
|
||||
if(transmit_count)
|
||||
{
|
||||
transmit_pos++;
|
||||
transmit_count--;
|
||||
}
|
||||
|
||||
|
||||
switch(command_phase)
|
||||
{
|
||||
case 0:
|
||||
if(receive_buffer != 0x01)
|
||||
command_phase = -1;
|
||||
else
|
||||
{
|
||||
transmit_buffer[0] = 0x31;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
//if(receive_buffer)
|
||||
// printf("%02x\n", receive_buffer);
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
need_hit_detect = receive_buffer & 0x10; // TODO, see if it's (val&0x10) == 0x10, or some other mask value.
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
command = receive_buffer;
|
||||
command_phase++;
|
||||
|
||||
transmit_buffer[0] = 0x5A;
|
||||
|
||||
//if(command != 0x42)
|
||||
// fprintf(stderr, "Justifier unhandled command: 0x%02x\n", command);
|
||||
//assert(command == 0x42);
|
||||
if(command == 0x42)
|
||||
{
|
||||
transmit_buffer[1] = 0xFF ^ ((buttons & 2) << 2);
|
||||
transmit_buffer[2] = 0xFF ^ (trigger_eff << 7) ^ ((buttons & 1) << 6);
|
||||
|
||||
if(os_shot_counter > 0)
|
||||
{
|
||||
transmit_buffer[2] |= (1 << 7);
|
||||
if(os_shot_counter == 6 || os_shot_counter == 5)
|
||||
{
|
||||
transmit_buffer[2] &= ~(1 << 7);
|
||||
}
|
||||
}
|
||||
|
||||
transmit_pos = 0;
|
||||
transmit_count = 3;
|
||||
|
||||
trigger_eff = trigger_noclear;
|
||||
}
|
||||
else
|
||||
{
|
||||
command_phase = -1;
|
||||
transmit_buffer[1] = 0;
|
||||
transmit_buffer[2] = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
dsr_pulse_delay = 200;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
InputDevice *Device_Justifier_Create(void)
|
||||
{
|
||||
return new InputDevice_Justifier();
|
||||
}
|
||||
|
||||
|
||||
InputDeviceInputInfoStruct Device_Justifier_IDII[6] =
|
||||
{
|
||||
{ "x_axis", "X Axis", -1, IDIT_X_AXIS },
|
||||
{ "y_axis", "Y Axis", -1, IDIT_Y_AXIS },
|
||||
|
||||
{ "trigger", "Trigger", 0, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "o", "O", 1, IDIT_BUTTON, NULL },
|
||||
{ "start", "Start", 2, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "offscreen_shot", "Offscreen Shot(Simulated)", 3, IDIT_BUTTON, NULL },
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef __MDFN_PSX_INPUT_JUSTIFIER_H
|
||||
#define __MDFN_PSX_INPUT_JUSTIFIER_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
InputDevice *Device_Justifier_Create(void);
|
||||
extern InputDeviceInputInfoStruct Device_Justifier_IDII[6];
|
||||
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,546 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
// I could find no other commands than 'R', 'W', and 'S' (not sure what 'S' is for, however)
|
||||
|
||||
#include "../psx.h"
|
||||
#include "../frontio.h"
|
||||
#include "memcard.h"
|
||||
|
||||
#define BIGCASE2(X) case X+0: case X+1:
|
||||
#define BIGCASE4(X) BIGCASE2(X+0) BIGCASE2(X+2)
|
||||
#define BIGCASE8(X) BIGCASE4(X+0) BIGCASE4(X+4)
|
||||
#define BIGCASE16(X) BIGCASE8(X+0) BIGCASE8(X+8)
|
||||
#define BIGCASE32(X) BIGCASE16(X+0) BIGCASE16(X+16)
|
||||
#define BIGCASE64(X) BIGCASE32(X+0) BIGCASE32(X+32)
|
||||
#define BIGCASE128(X) BIGCASE64(X+0) BIGCASE64(X+64)
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_Memcard : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_Memcard();
|
||||
virtual ~InputDevice_Memcard();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool GetDSR(void);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
|
||||
//
|
||||
//
|
||||
virtual uint32 GetNVSize(void);
|
||||
virtual void ReadNV(uint8 *buffer, uint32 offset, uint32 size);
|
||||
virtual void WriteNV(const uint8 *buffer, uint32 offset, uint32 size);
|
||||
|
||||
virtual uint64 GetNVDirtyCount(void);
|
||||
virtual void ResetNVDirtyCount(void);
|
||||
|
||||
private:
|
||||
|
||||
void Format(void);
|
||||
|
||||
bool presence_new;
|
||||
|
||||
uint8 card_data[1 << 17];
|
||||
uint8 rw_buffer[128];
|
||||
uint8 write_xor;
|
||||
|
||||
//
|
||||
// Used to avoid saving unused memory cards' card data in save states.
|
||||
// Set to false on object initialization, set to true when data is written to card_data that differs
|
||||
// from existing data(either from loading a memory card saved to disk, or from a game writing to the memory card).
|
||||
//
|
||||
// Save and load its state to/from save states.
|
||||
//
|
||||
bool data_used;
|
||||
|
||||
//
|
||||
// Do not save dirty_count in save states!
|
||||
//
|
||||
uint64 dirty_count;
|
||||
|
||||
bool dtr;
|
||||
int32 command_phase;
|
||||
uint32 bitpos;
|
||||
uint8 receive_buffer;
|
||||
|
||||
uint8 command;
|
||||
uint16 addr;
|
||||
uint8 calced_xor;
|
||||
|
||||
uint8 transmit_buffer;
|
||||
uint32 transmit_count;
|
||||
};
|
||||
|
||||
void InputDevice_Memcard::Format(void)
|
||||
{
|
||||
memset(card_data, 0x00, sizeof(card_data));
|
||||
|
||||
card_data[0x00] = 0x4D;
|
||||
card_data[0x01] = 0x43;
|
||||
card_data[0x7F] = 0x0E;
|
||||
|
||||
for(unsigned int A = 0x80; A < 0x800; A += 0x80)
|
||||
{
|
||||
card_data[A + 0x00] = 0xA0;
|
||||
card_data[A + 0x08] = 0xFF;
|
||||
card_data[A + 0x09] = 0xFF;
|
||||
card_data[A + 0x7F] = 0xA0;
|
||||
}
|
||||
|
||||
for(unsigned int A = 0x0800; A < 0x1200; A += 0x80)
|
||||
{
|
||||
card_data[A + 0x00] = 0xFF;
|
||||
card_data[A + 0x01] = 0xFF;
|
||||
card_data[A + 0x02] = 0xFF;
|
||||
card_data[A + 0x03] = 0xFF;
|
||||
card_data[A + 0x08] = 0xFF;
|
||||
card_data[A + 0x09] = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
InputDevice_Memcard::InputDevice_Memcard()
|
||||
{
|
||||
Power();
|
||||
|
||||
data_used = false;
|
||||
dirty_count = 0;
|
||||
|
||||
// Init memcard as formatted.
|
||||
assert(sizeof(card_data) == (1 << 17));
|
||||
Format();
|
||||
}
|
||||
|
||||
InputDevice_Memcard::~InputDevice_Memcard()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void InputDevice_Memcard::Power(void)
|
||||
{
|
||||
dtr = 0;
|
||||
|
||||
//buttons[0] = buttons[1] = 0;
|
||||
|
||||
command_phase = 0;
|
||||
|
||||
bitpos = 0;
|
||||
|
||||
receive_buffer = 0;
|
||||
|
||||
command = 0;
|
||||
|
||||
transmit_buffer = 0;
|
||||
|
||||
transmit_count = 0;
|
||||
|
||||
addr = 0;
|
||||
|
||||
presence_new = true;
|
||||
}
|
||||
|
||||
int InputDevice_Memcard::StateAction(StateMem* sm, int load, int data_only, const char* section_name)
|
||||
{
|
||||
// Don't save dirty_count.
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(presence_new),
|
||||
|
||||
SFARRAY(rw_buffer, sizeof(rw_buffer)),
|
||||
SFVAR(write_xor),
|
||||
|
||||
SFVAR(dtr),
|
||||
SFVAR(command_phase),
|
||||
SFVAR(bitpos),
|
||||
SFVAR(receive_buffer),
|
||||
|
||||
SFVAR(command),
|
||||
SFVAR(addr),
|
||||
SFVAR(calced_xor),
|
||||
|
||||
SFVAR(transmit_buffer),
|
||||
SFVAR(transmit_count),
|
||||
|
||||
SFVAR(data_used),
|
||||
|
||||
SFEND
|
||||
};
|
||||
|
||||
SFORMAT CD_StateRegs[] =
|
||||
{
|
||||
SFARRAY(card_data, sizeof(card_data)),
|
||||
SFEND
|
||||
};
|
||||
int ret = 1;
|
||||
|
||||
if(MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name) != 0)
|
||||
{
|
||||
//printf("%s data_used=%d\n", section_name, data_used);
|
||||
if(data_used)
|
||||
{
|
||||
std::string tmp_name = std::string(section_name) + "_DT";
|
||||
|
||||
ret &= MDFNSS_StateAction(sm, load, data_only, CD_StateRegs, tmp_name.c_str());
|
||||
}
|
||||
|
||||
if(load)
|
||||
{
|
||||
if(data_used)
|
||||
dirty_count++;
|
||||
else
|
||||
{
|
||||
//printf("Format: %s\n", section_name);
|
||||
Format();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
void InputDevice_Memcard::SetDTR(bool new_dtr)
|
||||
{
|
||||
if(!dtr && new_dtr)
|
||||
{
|
||||
command_phase = 0;
|
||||
bitpos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
else if(dtr && !new_dtr)
|
||||
{
|
||||
if(command_phase > 0)
|
||||
PSX_WARNING("[MCR] Communication aborted???");
|
||||
}
|
||||
dtr = new_dtr;
|
||||
}
|
||||
|
||||
bool InputDevice_Memcard::GetDSR(void)
|
||||
{
|
||||
if(!dtr)
|
||||
return(0);
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
return(1);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool InputDevice_Memcard::Clock(bool TxD, int32 &dsr_pulse_delay)
|
||||
{
|
||||
bool ret = 1;
|
||||
|
||||
dsr_pulse_delay = 0;
|
||||
|
||||
if(!dtr)
|
||||
return(1);
|
||||
|
||||
if(transmit_count)
|
||||
ret = (transmit_buffer >> bitpos) & 1;
|
||||
|
||||
receive_buffer &= ~(1 << bitpos);
|
||||
receive_buffer |= TxD << bitpos;
|
||||
bitpos = (bitpos + 1) & 0x7;
|
||||
|
||||
if(!bitpos)
|
||||
{
|
||||
//if(command_phase > 0 || transmit_count)
|
||||
// printf("[MCRDATA] Received_data=0x%02x, Sent_data=0x%02x\n", receive_buffer, transmit_buffer);
|
||||
|
||||
if(transmit_count)
|
||||
{
|
||||
transmit_count--;
|
||||
}
|
||||
|
||||
|
||||
switch(command_phase)
|
||||
{
|
||||
case 0:
|
||||
if(receive_buffer != 0x81)
|
||||
command_phase = -1;
|
||||
else
|
||||
{
|
||||
//printf("[MCR] Device selected\n");
|
||||
transmit_buffer = presence_new ? 0x08 : 0x00;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
command = receive_buffer;
|
||||
//printf("[MCR] Command received: %c\n", command);
|
||||
if(command == 'R' || command == 'W')
|
||||
{
|
||||
command_phase++;
|
||||
transmit_buffer = 0x5A;
|
||||
transmit_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(command == 'S')
|
||||
{
|
||||
PSX_WARNING("[MCR] Memcard S command unsupported.");
|
||||
}
|
||||
|
||||
command_phase = -1;
|
||||
transmit_buffer = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
transmit_buffer = 0x5D;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
transmit_buffer = 0x00;
|
||||
transmit_count = 1;
|
||||
if(command == 'R')
|
||||
command_phase = 1000;
|
||||
else if(command == 'W')
|
||||
command_phase = 2000;
|
||||
break;
|
||||
|
||||
//
|
||||
// Read
|
||||
//
|
||||
case 1000:
|
||||
addr = receive_buffer << 8;
|
||||
transmit_buffer = receive_buffer;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
case 1001:
|
||||
addr |= receive_buffer & 0xFF;
|
||||
transmit_buffer = '\\';
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
case 1002:
|
||||
//printf("[MCR] READ ADDR=0x%04x\n", addr);
|
||||
if(addr >= (sizeof(card_data) >> 7))
|
||||
addr = 0xFFFF;
|
||||
|
||||
calced_xor = 0;
|
||||
transmit_buffer = ']';
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
|
||||
// TODO: enable this code(or something like it) when CPU instruction timing is a bit better.
|
||||
//
|
||||
//dsr_pulse_delay = 32000;
|
||||
//goto SkipDPD;
|
||||
//
|
||||
|
||||
break;
|
||||
|
||||
case 1003:
|
||||
transmit_buffer = addr >> 8;
|
||||
calced_xor ^= transmit_buffer;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
case 1004:
|
||||
transmit_buffer = addr & 0xFF;
|
||||
calced_xor ^= transmit_buffer;
|
||||
|
||||
if(addr == 0xFFFF)
|
||||
{
|
||||
transmit_count = 1;
|
||||
command_phase = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
transmit_count = 1;
|
||||
command_phase = 1024;
|
||||
}
|
||||
break;
|
||||
|
||||
// Transmit actual 128 bytes data
|
||||
//case (1024 + 0) ... (1024 + 128 - 1):
|
||||
BIGCASE128(1024)
|
||||
transmit_buffer = card_data[(addr << 7) + (command_phase - 1024)];
|
||||
calced_xor ^= transmit_buffer;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
// XOR
|
||||
case (1024 + 128):
|
||||
transmit_buffer = calced_xor;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
// End flag
|
||||
case (1024 + 129):
|
||||
transmit_buffer = 'G';
|
||||
transmit_count = 1;
|
||||
command_phase = -1;
|
||||
break;
|
||||
|
||||
//
|
||||
// Write
|
||||
//
|
||||
case 2000:
|
||||
calced_xor = receive_buffer;
|
||||
addr = receive_buffer << 8;
|
||||
transmit_buffer = receive_buffer;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
case 2001:
|
||||
calced_xor ^= receive_buffer;
|
||||
addr |= receive_buffer & 0xFF;
|
||||
//printf("[MCR] WRITE ADDR=0x%04x\n", addr);
|
||||
transmit_buffer = receive_buffer;
|
||||
transmit_count = 1;
|
||||
command_phase = 2048;
|
||||
break;
|
||||
|
||||
//case (2048 + 0) ... (2048 + 128 - 1):
|
||||
BIGCASE128(2048)
|
||||
calced_xor ^= receive_buffer;
|
||||
rw_buffer[command_phase - 2048] = receive_buffer;
|
||||
|
||||
transmit_buffer = receive_buffer;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
case (2048 + 128): // XOR
|
||||
write_xor = receive_buffer;
|
||||
transmit_buffer = '\\';
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
case (2048 + 129):
|
||||
transmit_buffer = ']';
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
break;
|
||||
|
||||
case (2048 + 130): // End flag
|
||||
//MDFN_DispMessage("%02x %02x", calced_xor, write_xor);
|
||||
//printf("[MCR] Write End. Actual_XOR=0x%02x, CW_XOR=0x%02x\n", calced_xor, write_xor);
|
||||
|
||||
if(calced_xor != write_xor)
|
||||
transmit_buffer = 'N';
|
||||
else if(addr >= (sizeof(card_data) >> 7))
|
||||
transmit_buffer = 0xFF;
|
||||
else
|
||||
{
|
||||
transmit_buffer = 'G';
|
||||
presence_new = false;
|
||||
|
||||
// If the current data is different from the data to be written, increment the dirty count.
|
||||
// memcpy()'ing over to card_data is also conditionalized here for a slight optimization.
|
||||
if(memcmp(&card_data[addr << 7], rw_buffer, 128))
|
||||
{
|
||||
memcpy(&card_data[addr << 7], rw_buffer, 128);
|
||||
dirty_count++;
|
||||
data_used = true;
|
||||
}
|
||||
}
|
||||
|
||||
transmit_count = 1;
|
||||
command_phase = -1;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
//if(command_phase != -1 || transmit_count)
|
||||
// printf("[MCR] Receive: 0x%02x, Send: 0x%02x -- %d\n", receive_buffer, transmit_buffer, command_phase);
|
||||
}
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
dsr_pulse_delay = 0x100;
|
||||
|
||||
//SkipDPD: ;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
uint32 InputDevice_Memcard::GetNVSize(void)
|
||||
{
|
||||
return(sizeof(card_data));
|
||||
}
|
||||
|
||||
void InputDevice_Memcard::ReadNV(uint8 *buffer, uint32 offset, uint32 size)
|
||||
{
|
||||
while(size--)
|
||||
{
|
||||
*buffer = card_data[offset & (sizeof(card_data) - 1)];
|
||||
buffer++;
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
||||
void InputDevice_Memcard::WriteNV(const uint8 *buffer, uint32 offset, uint32 size)
|
||||
{
|
||||
if(size)
|
||||
{
|
||||
dirty_count++;
|
||||
}
|
||||
|
||||
while(size--)
|
||||
{
|
||||
if(card_data[offset & (sizeof(card_data) - 1)] != *buffer)
|
||||
data_used = true;
|
||||
|
||||
card_data[offset & (sizeof(card_data) - 1)] = *buffer;
|
||||
buffer++;
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
||||
uint64 InputDevice_Memcard::GetNVDirtyCount(void)
|
||||
{
|
||||
return(dirty_count);
|
||||
}
|
||||
|
||||
void InputDevice_Memcard::ResetNVDirtyCount(void)
|
||||
{
|
||||
dirty_count = 0;
|
||||
}
|
||||
|
||||
|
||||
InputDevice *Device_Memcard_Create(void)
|
||||
{
|
||||
return new InputDevice_Memcard();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef __MDFN_PSX_INPUT_MEMCARD_H
|
||||
#define __MDFN_PSX_INPUT_MEMCARD_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
InputDevice *Device_Memcard_Create(void);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,293 @@
|
|||
#include "../psx.h"
|
||||
#include "../frontio.h"
|
||||
#include "mouse.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_Mouse : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_Mouse();
|
||||
virtual ~InputDevice_Mouse();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name);
|
||||
virtual void UpdateInput(const void *data);
|
||||
|
||||
virtual void Update(const pscpu_timestamp_t timestamp);
|
||||
virtual void ResetTS(void);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
|
||||
private:
|
||||
|
||||
int32 lastts;
|
||||
int32 clear_timeout;
|
||||
|
||||
bool dtr;
|
||||
|
||||
uint8 button;
|
||||
uint8 button_post_mask;
|
||||
int32 accum_xdelta;
|
||||
int32 accum_ydelta;
|
||||
|
||||
int32 command_phase;
|
||||
uint32 bitpos;
|
||||
uint8 receive_buffer;
|
||||
|
||||
uint8 command;
|
||||
|
||||
uint8 transmit_buffer[5];
|
||||
uint32 transmit_pos;
|
||||
uint32 transmit_count;
|
||||
};
|
||||
|
||||
InputDevice_Mouse::InputDevice_Mouse()
|
||||
{
|
||||
Power();
|
||||
}
|
||||
|
||||
InputDevice_Mouse::~InputDevice_Mouse()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void InputDevice_Mouse::Update(const pscpu_timestamp_t timestamp)
|
||||
{
|
||||
int32 cycles = timestamp - lastts;
|
||||
|
||||
clear_timeout += cycles;
|
||||
if(clear_timeout >= (33868800 / 4))
|
||||
{
|
||||
//puts("Mouse timeout\n");
|
||||
clear_timeout = 0;
|
||||
accum_xdelta = 0;
|
||||
accum_ydelta = 0;
|
||||
button &= button_post_mask;
|
||||
}
|
||||
|
||||
lastts = timestamp;
|
||||
}
|
||||
|
||||
void InputDevice_Mouse::ResetTS(void)
|
||||
{
|
||||
lastts = 0;
|
||||
}
|
||||
|
||||
void InputDevice_Mouse::Power(void)
|
||||
{
|
||||
lastts = 0;
|
||||
clear_timeout = 0;
|
||||
|
||||
dtr = 0;
|
||||
|
||||
button = 0;
|
||||
button_post_mask = 0;
|
||||
accum_xdelta = 0;
|
||||
accum_ydelta = 0;
|
||||
|
||||
command_phase = 0;
|
||||
|
||||
bitpos = 0;
|
||||
|
||||
receive_buffer = 0;
|
||||
|
||||
command = 0;
|
||||
|
||||
memset(transmit_buffer, 0, sizeof(transmit_buffer));
|
||||
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
|
||||
int InputDevice_Mouse::StateAction(StateMem* sm, int load, int data_only, const char* section_name)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(clear_timeout),
|
||||
|
||||
SFVAR(dtr),
|
||||
|
||||
SFVAR(button),
|
||||
SFVAR(button_post_mask),
|
||||
SFVAR(accum_xdelta),
|
||||
SFVAR(accum_ydelta),
|
||||
|
||||
SFVAR(command_phase),
|
||||
SFVAR(bitpos),
|
||||
SFVAR(receive_buffer),
|
||||
|
||||
SFVAR(command),
|
||||
|
||||
SFARRAY(transmit_buffer, sizeof(transmit_buffer)),
|
||||
SFVAR(transmit_pos),
|
||||
SFVAR(transmit_count),
|
||||
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name);
|
||||
|
||||
if(load)
|
||||
{
|
||||
if((transmit_pos + transmit_count) > sizeof(transmit_buffer))
|
||||
{
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
void InputDevice_Mouse::UpdateInput(const void *data)
|
||||
{
|
||||
accum_xdelta += (int32)MDFN_de32lsb((uint8*)data + 0);
|
||||
accum_ydelta += (int32)MDFN_de32lsb((uint8*)data + 4);
|
||||
|
||||
if(accum_xdelta > 30 * 127) accum_xdelta = 30 * 127;
|
||||
if(accum_xdelta < 30 * -128) accum_xdelta = 30 * -128;
|
||||
|
||||
if(accum_ydelta > 30 * 127) accum_ydelta = 30 * 127;
|
||||
if(accum_ydelta < 30 * -128) accum_ydelta = 30 * -128;
|
||||
|
||||
button |= *((uint8 *)data + 8);
|
||||
button_post_mask = *((uint8 *)data + 8);
|
||||
|
||||
//if(button)
|
||||
// MDFN_DispMessage("Button\n");
|
||||
//printf("%d %d\n", accum_xdelta, accum_ydelta);
|
||||
}
|
||||
|
||||
|
||||
void InputDevice_Mouse::SetDTR(bool new_dtr)
|
||||
{
|
||||
if(!dtr && new_dtr)
|
||||
{
|
||||
command_phase = 0;
|
||||
bitpos = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
else if(dtr && !new_dtr)
|
||||
{
|
||||
//if(bitpos || transmit_count)
|
||||
// printf("[PAD] Abort communication!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
|
||||
}
|
||||
|
||||
dtr = new_dtr;
|
||||
}
|
||||
|
||||
bool InputDevice_Mouse::Clock(bool TxD, int32 &dsr_pulse_delay)
|
||||
{
|
||||
bool ret = 1;
|
||||
|
||||
dsr_pulse_delay = 0;
|
||||
|
||||
if(!dtr)
|
||||
return(1);
|
||||
|
||||
if(transmit_count)
|
||||
ret = (transmit_buffer[transmit_pos] >> bitpos) & 1;
|
||||
|
||||
receive_buffer &= ~(1 << bitpos);
|
||||
receive_buffer |= TxD << bitpos;
|
||||
bitpos = (bitpos + 1) & 0x7;
|
||||
|
||||
if(!bitpos)
|
||||
{
|
||||
//printf("[PAD] Receive: %02x -- command_phase=%d\n", receive_buffer, command_phase);
|
||||
|
||||
if(transmit_count)
|
||||
{
|
||||
transmit_pos++;
|
||||
transmit_count--;
|
||||
}
|
||||
|
||||
|
||||
switch(command_phase)
|
||||
{
|
||||
case 0:
|
||||
if(receive_buffer != 0x01)
|
||||
command_phase = -1;
|
||||
else
|
||||
{
|
||||
transmit_buffer[0] = 0x12;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
command = receive_buffer;
|
||||
command_phase++;
|
||||
|
||||
transmit_buffer[0] = 0x5A;
|
||||
|
||||
if(command == 0x42)
|
||||
{
|
||||
int32 xdelta = accum_xdelta;
|
||||
int32 ydelta = accum_ydelta;
|
||||
|
||||
if(xdelta < -128) xdelta = -128;
|
||||
if(xdelta > 127) xdelta = 127;
|
||||
|
||||
if(ydelta < -128) ydelta = -128;
|
||||
if(ydelta > 127) ydelta = 127;
|
||||
|
||||
transmit_buffer[1] = 0xFF;
|
||||
transmit_buffer[2] = 0xFC ^ (button << 2);
|
||||
transmit_buffer[3] = xdelta;
|
||||
transmit_buffer[4] = ydelta;
|
||||
|
||||
accum_xdelta -= xdelta;
|
||||
accum_ydelta -= ydelta;
|
||||
|
||||
button &= button_post_mask;
|
||||
|
||||
transmit_pos = 0;
|
||||
transmit_count = 5;
|
||||
|
||||
clear_timeout = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
command_phase = -1;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
dsr_pulse_delay = 0x40; //0x100;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
InputDevice *Device_Mouse_Create(void)
|
||||
{
|
||||
return new InputDevice_Mouse();
|
||||
}
|
||||
|
||||
|
||||
InputDeviceInputInfoStruct Device_Mouse_IDII[4] =
|
||||
{
|
||||
{ "x_axis", "X Axis", -1, IDIT_X_AXIS_REL },
|
||||
{ "y_axis", "Y Axis", -1, IDIT_Y_AXIS_REL },
|
||||
{ "right", "Right Button", 1, IDIT_BUTTON, NULL },
|
||||
{ "left", "Left Button", 0, IDIT_BUTTON, NULL },
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef __MDFN_PSX_INPUT_MOUSE_H
|
||||
#define __MDFN_PSX_INPUT_MOUSE_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
InputDevice *Device_Mouse_Create(void);
|
||||
extern InputDeviceInputInfoStruct Device_Mouse_IDII[4];
|
||||
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,434 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../psx.h"
|
||||
#include "../frontio.h"
|
||||
#include "multitap.h"
|
||||
|
||||
/*
|
||||
TODO: PS1 multitap appears to have some internal knowledge of controller IDs, so it won't get "stuck" waiting for data from a controller that'll never
|
||||
come. We currently sort of "cheat" due to how the dsr_pulse_delay stuff works, but in the future we should try to emulate this multitap functionality.
|
||||
|
||||
Also, full-mode read startup and subport controller ID read timing isn't quite right, so we should fix that too.
|
||||
*/
|
||||
|
||||
/*
|
||||
Notes from tests on real thing(not necessarily emulated the same way here):
|
||||
|
||||
Manual port selection read mode:
|
||||
Write 0x01-0x04 instead of 0x01 as first byte, selects port(1=A,2=B,3=C,4=D) to access.
|
||||
|
||||
Ports that don't exist(0x00, 0x05-0xFF) or don't have a device plugged in will not respond(no DSR pulse).
|
||||
|
||||
Full read mode:
|
||||
Bit0 of third byte(from-zero-index=0x02) should be set to 1 to enter full read mode, on subsequent reads.
|
||||
|
||||
Appears to require a controller to be plugged into the port specified by the first byte as per manual port selection read mode,
|
||||
to write the byte necessary to enter full-read mode; but once the third byte with the bit set has been written, no controller in
|
||||
that port is required for doing full reads(and the manual port selection is ignored when doing a full read).
|
||||
|
||||
However, if there are no controllers plugged in, the returned data will be short:
|
||||
% 0: 0xff
|
||||
% 1: 0x80
|
||||
% 2: 0x5a
|
||||
|
||||
Example full-read bytestream(with controllers plugged into port A, port B, and port C, with port D empty):
|
||||
% 0: 0xff
|
||||
% 1: 0x80
|
||||
% 2: 0x5a
|
||||
|
||||
% 3: 0x73 (Port A controller data start)
|
||||
% 4: 0x5a
|
||||
% 5: 0xff
|
||||
% 6: 0xff
|
||||
% 7: 0x80
|
||||
% 8: 0x8c
|
||||
% 9: 0x79
|
||||
% 10: 0x8f
|
||||
|
||||
% 11: 0x53 (Port B controller data start)
|
||||
% 12: 0x5a
|
||||
% 13: 0xff
|
||||
% 14: 0xff
|
||||
% 15: 0x80
|
||||
% 16: 0x80
|
||||
% 17: 0x75
|
||||
% 18: 0x8e
|
||||
|
||||
% 19: 0x41 (Port C controller data start)
|
||||
% 20: 0x5a
|
||||
% 21: 0xff
|
||||
% 22: 0xff
|
||||
% 23: 0xff
|
||||
% 24: 0xff
|
||||
% 25: 0xff
|
||||
% 26: 0xff
|
||||
|
||||
% 27: 0xff (Port D controller data start)
|
||||
% 28: 0xff
|
||||
% 29: 0xff
|
||||
% 30: 0xff
|
||||
% 31: 0xff
|
||||
% 32: 0xff
|
||||
% 33: 0xff
|
||||
% 34: 0xff
|
||||
|
||||
*/
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
InputDevice_Multitap::InputDevice_Multitap()
|
||||
{
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
pad_devices[i] = NULL;
|
||||
mc_devices[i] = NULL;
|
||||
}
|
||||
Power();
|
||||
}
|
||||
|
||||
InputDevice_Multitap::~InputDevice_Multitap()
|
||||
{
|
||||
}
|
||||
|
||||
void InputDevice_Multitap::SetSubDevice(unsigned int sub_index, InputDevice *device, InputDevice *mc_device)
|
||||
{
|
||||
assert(sub_index < 4);
|
||||
|
||||
//printf("%d\n", sub_index);
|
||||
|
||||
pad_devices[sub_index] = device;
|
||||
mc_devices[sub_index] = mc_device;
|
||||
}
|
||||
|
||||
|
||||
void InputDevice_Multitap::Power(void)
|
||||
{
|
||||
selected_device = -1;
|
||||
bit_counter = 0;
|
||||
receive_buffer = 0;
|
||||
byte_counter = 0;
|
||||
|
||||
mc_mode = false;
|
||||
full_mode = false;
|
||||
full_mode_setting = false;
|
||||
|
||||
fm_dp = 0;
|
||||
memset(fm_buffer, 0, sizeof(fm_buffer));
|
||||
fm_deferred_error_temp = false;
|
||||
fm_deferred_error = false;
|
||||
fm_command_error = false;
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
if(pad_devices[i])
|
||||
pad_devices[i]->Power();
|
||||
|
||||
if(mc_devices[i])
|
||||
mc_devices[i]->Power();
|
||||
}
|
||||
}
|
||||
|
||||
int InputDevice_Multitap::StateAction(StateMem* sm, int load, int data_only, const char* section_name)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(dtr),
|
||||
|
||||
SFVAR(selected_device),
|
||||
SFVAR(full_mode_setting),
|
||||
|
||||
SFVAR(full_mode),
|
||||
SFVAR(mc_mode),
|
||||
|
||||
SFVAR(fm_dp),
|
||||
SFARRAY(&fm_buffer[0][0], sizeof(fm_buffer) / sizeof(fm_buffer[0][0])),
|
||||
|
||||
SFVAR(fm_deferred_error_temp),
|
||||
SFVAR(fm_deferred_error),
|
||||
SFVAR(fm_command_error),
|
||||
|
||||
SFVAR(command),
|
||||
SFVAR(receive_buffer),
|
||||
SFVAR(bit_counter),
|
||||
SFVAR(byte_counter),
|
||||
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name);
|
||||
|
||||
if(load)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
void InputDevice_Multitap::SetDTR(bool new_dtr)
|
||||
{
|
||||
bool old_dtr = dtr;
|
||||
dtr = new_dtr;
|
||||
|
||||
if(!dtr)
|
||||
{
|
||||
if(old_dtr)
|
||||
{
|
||||
//printf("Multitap stop.\n");
|
||||
}
|
||||
|
||||
bit_counter = 0;
|
||||
receive_buffer = 0;
|
||||
selected_device = -1;
|
||||
mc_mode = false;
|
||||
full_mode = false;
|
||||
}
|
||||
|
||||
if(!old_dtr && dtr)
|
||||
{
|
||||
full_mode = full_mode_setting;
|
||||
|
||||
byte_counter = 0;
|
||||
|
||||
//if(full_mode)
|
||||
// printf("Multitap start: %d\n", full_mode);
|
||||
}
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
pad_devices[i]->SetDTR(dtr);
|
||||
mc_devices[i]->SetDTR(dtr);
|
||||
}
|
||||
}
|
||||
|
||||
bool InputDevice_Multitap::GetDSR(void)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool InputDevice_Multitap::Clock(bool TxD, int32 &dsr_pulse_delay)
|
||||
{
|
||||
if(!dtr)
|
||||
return(1);
|
||||
|
||||
bool ret = 1;
|
||||
int32 tmp_pulse_delay[2][4] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 } };
|
||||
|
||||
//printf("Receive bit: %d\n", TxD);
|
||||
//printf("TxD %d\n", TxD);
|
||||
|
||||
receive_buffer &= ~ (1 << bit_counter);
|
||||
receive_buffer |= TxD << bit_counter;
|
||||
|
||||
if(1)
|
||||
{
|
||||
if(byte_counter == 0)
|
||||
{
|
||||
bool mangled_txd = TxD;
|
||||
|
||||
if(bit_counter < 4)
|
||||
mangled_txd = (0x01 >> bit_counter) & 1;
|
||||
|
||||
for(unsigned i = 0; i < 4; i++)
|
||||
{
|
||||
pad_devices[i]->Clock(mangled_txd, tmp_pulse_delay[0][i]);
|
||||
mc_devices[i]->Clock(mangled_txd, tmp_pulse_delay[1][i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(full_mode)
|
||||
{
|
||||
if(byte_counter == 1)
|
||||
{
|
||||
ret = (0x80 >> bit_counter) & 1;
|
||||
|
||||
for(unsigned i = 0; i < 4; i++)
|
||||
{
|
||||
fm_buffer[i][0] &= (pad_devices[i]->Clock(TxD, tmp_pulse_delay[0][i]) << bit_counter) | (~(1U << bit_counter));
|
||||
}
|
||||
}
|
||||
else if(byte_counter == 2)
|
||||
{
|
||||
ret = (0x5A >> bit_counter) & 1;
|
||||
}
|
||||
// || byte_counter == (0x03 + 0x08 * 1) || byte_counter == (0x03 + 0x08 * 2) || byte_counter == (0x03 + 0x08 * 3))
|
||||
else if(byte_counter >= 0x03 && byte_counter < 0x03 + 0x08 * 4)
|
||||
{
|
||||
if(!fm_command_error && byte_counter >= (0x03 + 1) && byte_counter < (0x03 + 0x08))
|
||||
{
|
||||
for(unsigned i = 0; i < 4; i++)
|
||||
{
|
||||
fm_buffer[i][byte_counter - 0x03] &= (pad_devices[i]->Clock(0, tmp_pulse_delay[0][i]) << bit_counter) | (~(1U << bit_counter));
|
||||
}
|
||||
}
|
||||
ret &= ((&fm_buffer[0][0])[byte_counter - 0x03] >> bit_counter) & 1;
|
||||
}
|
||||
}
|
||||
else // to if(full_mode)
|
||||
{
|
||||
if((unsigned)selected_device < 4)
|
||||
{
|
||||
ret &= pad_devices[selected_device]->Clock(TxD, tmp_pulse_delay[0][selected_device]);
|
||||
ret &= mc_devices[selected_device]->Clock(TxD, tmp_pulse_delay[1][selected_device]);
|
||||
}
|
||||
}
|
||||
} // end else to if(byte_counter == 0)
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
bit_counter = (bit_counter + 1) & 0x7;
|
||||
if(bit_counter == 0)
|
||||
{
|
||||
//printf("Receive: 0x%02x\n", receive_buffer);
|
||||
if(byte_counter == 0)
|
||||
{
|
||||
mc_mode = (bool)(receive_buffer & 0xF0);
|
||||
if(mc_mode)
|
||||
full_mode = false;
|
||||
|
||||
//printf("Zoomba: 0x%02x\n", receive_buffer);
|
||||
//printf("Full mode: %d %d %d\n", full_mode, bit_counter, byte_counter);
|
||||
|
||||
if(full_mode)
|
||||
{
|
||||
memset(fm_buffer, 0xFF, sizeof(fm_buffer));
|
||||
selected_device = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//printf("Device select: %02x\n", receive_buffer);
|
||||
fm_deferred_error = false;
|
||||
selected_device = ((receive_buffer & 0xF) - 1) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
if(byte_counter == 1)
|
||||
{
|
||||
command = receive_buffer;
|
||||
|
||||
//printf("Multitap sub-command: %02x\n", command);
|
||||
|
||||
if(full_mode)
|
||||
{
|
||||
if(command != 0x42)
|
||||
fm_command_error = true;
|
||||
else
|
||||
fm_command_error = fm_deferred_error;
|
||||
}
|
||||
else
|
||||
{
|
||||
fm_command_error = false;
|
||||
}
|
||||
fm_deferred_error = false;
|
||||
}
|
||||
|
||||
if((!mc_mode || full_mode) && byte_counter == 2)
|
||||
{
|
||||
//printf("Full mode setting: %02x\n", receive_buffer);
|
||||
full_mode_setting = receive_buffer & 0x01;
|
||||
}
|
||||
|
||||
if(full_mode)
|
||||
{
|
||||
if(byte_counter == (3 + 8 * 0) || byte_counter == (3 + 8 * 1) || byte_counter == (3 + 8 * 2) || byte_counter == (3 + 8 * 3))
|
||||
{
|
||||
unsigned index = (byte_counter - 3) >> 3;
|
||||
assert(index < 4);
|
||||
|
||||
if(index == 0)
|
||||
fm_deferred_error_temp = false;
|
||||
|
||||
if((fm_dp & (1U << index)) && receive_buffer != 0x42)
|
||||
{
|
||||
//printf("Multitap command check failed: %u, 0x%02x\n", byte_counter, receive_buffer);
|
||||
fm_deferred_error_temp = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(byte_counter == 33)
|
||||
fm_deferred_error = fm_deferred_error_temp;
|
||||
}
|
||||
|
||||
// Handle DSR stuff
|
||||
if(full_mode)
|
||||
{
|
||||
if(byte_counter == 0) // Next byte: 0x80
|
||||
{
|
||||
dsr_pulse_delay = 1000;
|
||||
|
||||
fm_dp = 0;
|
||||
for(unsigned i = 0; i < 4; i++)
|
||||
fm_dp |= (((bool)(tmp_pulse_delay[0][i])) << i);
|
||||
}
|
||||
else if(byte_counter == 1) // Next byte: 0x5A
|
||||
dsr_pulse_delay = 0x40;
|
||||
else if(byte_counter == 2) // Next byte(typically, controller-dependent): 0x41
|
||||
{
|
||||
if(fm_dp)
|
||||
dsr_pulse_delay = 0x40;
|
||||
else
|
||||
dsr_pulse_delay = 0;
|
||||
}
|
||||
else if(byte_counter >= 3 && byte_counter < 34) // Next byte when byte_counter==3 (typically, controller-dependent): 0x5A
|
||||
{
|
||||
if(byte_counter < 10)
|
||||
{
|
||||
int d = 0x40;
|
||||
|
||||
for(unsigned i = 0; i < 4; i++)
|
||||
if(tmp_pulse_delay[0][i] > d)
|
||||
d = tmp_pulse_delay[0][i];
|
||||
|
||||
dsr_pulse_delay = d;
|
||||
}
|
||||
else
|
||||
dsr_pulse_delay = 0x20;
|
||||
|
||||
if(byte_counter == 3 && fm_command_error)
|
||||
dsr_pulse_delay = 0;
|
||||
}
|
||||
} // end if(full_mode)
|
||||
else
|
||||
{
|
||||
if((unsigned)selected_device < 4)
|
||||
{
|
||||
dsr_pulse_delay = std::max<int32>(tmp_pulse_delay[0][selected_device], tmp_pulse_delay[1][selected_device]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
//printf("Byte Counter Increment\n");
|
||||
if(byte_counter < 255)
|
||||
byte_counter++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
#ifndef __MDFN_PSX_INPUT_MULTITAP_H
|
||||
#define __MDFN_PSX_INPUT_MULTITAP_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_Multitap : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_Multitap();
|
||||
virtual ~InputDevice_Multitap();
|
||||
virtual void Power(void);
|
||||
virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name);
|
||||
|
||||
void SetSubDevice(unsigned int sub_index, InputDevice *device, InputDevice *mc_device);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool GetDSR(void);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
|
||||
private:
|
||||
|
||||
InputDevice *pad_devices[4];
|
||||
InputDevice *mc_devices[4];
|
||||
|
||||
bool dtr;
|
||||
|
||||
int selected_device;
|
||||
bool full_mode_setting;
|
||||
|
||||
bool full_mode;
|
||||
bool mc_mode;
|
||||
|
||||
uint8 fm_dp; // Device-present.
|
||||
uint8 fm_buffer[4][8];
|
||||
|
||||
bool fm_deferred_error_temp;
|
||||
bool fm_deferred_error;
|
||||
bool fm_command_error;
|
||||
|
||||
uint8 command;
|
||||
uint8 receive_buffer;
|
||||
uint8 bit_counter;
|
||||
uint8 byte_counter;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,297 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "../psx.h"
|
||||
#include "../frontio.h"
|
||||
#include "negcon.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_neGcon : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_neGcon(void);
|
||||
virtual ~InputDevice_neGcon();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name);
|
||||
virtual void UpdateInput(const void *data);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool GetDSR(void);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
|
||||
private:
|
||||
|
||||
bool dtr;
|
||||
|
||||
uint8 buttons[2];
|
||||
uint8 twist;
|
||||
uint8 anabuttons[3];
|
||||
|
||||
int32 command_phase;
|
||||
uint32 bitpos;
|
||||
uint8 receive_buffer;
|
||||
|
||||
uint8 command;
|
||||
|
||||
uint8 transmit_buffer[8];
|
||||
uint32 transmit_pos;
|
||||
uint32 transmit_count;
|
||||
};
|
||||
|
||||
InputDevice_neGcon::InputDevice_neGcon(void)
|
||||
{
|
||||
Power();
|
||||
}
|
||||
|
||||
InputDevice_neGcon::~InputDevice_neGcon()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void InputDevice_neGcon::Power(void)
|
||||
{
|
||||
dtr = 0;
|
||||
|
||||
buttons[0] = buttons[1] = 0;
|
||||
twist = 0;
|
||||
anabuttons[0] = 0;
|
||||
anabuttons[1] = 0;
|
||||
anabuttons[2] = 0;
|
||||
|
||||
command_phase = 0;
|
||||
|
||||
bitpos = 0;
|
||||
|
||||
receive_buffer = 0;
|
||||
|
||||
command = 0;
|
||||
|
||||
memset(transmit_buffer, 0, sizeof(transmit_buffer));
|
||||
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
|
||||
int InputDevice_neGcon::StateAction(StateMem* sm, int load, int data_only, const char* section_name)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(dtr),
|
||||
|
||||
SFARRAY(buttons, sizeof(buttons)),
|
||||
SFVAR(twist),
|
||||
SFARRAY(anabuttons, sizeof(anabuttons)),
|
||||
|
||||
SFVAR(command_phase),
|
||||
SFVAR(bitpos),
|
||||
SFVAR(receive_buffer),
|
||||
|
||||
SFVAR(command),
|
||||
|
||||
SFARRAY(transmit_buffer, sizeof(transmit_buffer)),
|
||||
SFVAR(transmit_pos),
|
||||
SFVAR(transmit_count),
|
||||
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name);
|
||||
|
||||
if(load)
|
||||
{
|
||||
if((transmit_pos + transmit_count) > sizeof(transmit_buffer))
|
||||
{
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
void InputDevice_neGcon::UpdateInput(const void *data)
|
||||
{
|
||||
uint8 *d8 = (uint8 *)data;
|
||||
|
||||
buttons[0] = d8[0];
|
||||
buttons[1] = d8[1];
|
||||
|
||||
twist = ((32768 + MDFN_de16lsb((const uint8 *)data + 2) - (((int32)MDFN_de16lsb((const uint8 *)data + 4) * 32768 + 16383) / 32767)) * 255 + 32767) / 65535;
|
||||
|
||||
anabuttons[0] = (MDFN_de16lsb((const uint8 *)data + 6) * 255 + 16383) / 32767;
|
||||
anabuttons[1] = (MDFN_de16lsb((const uint8 *)data + 8) * 255 + 16383) / 32767;
|
||||
anabuttons[2] = (MDFN_de16lsb((const uint8 *)data + 10) * 255 + 16383) / 32767;
|
||||
|
||||
//printf("%02x %02x %02x %02x\n", twist, anabuttons[0], anabuttons[1], anabuttons[2]);
|
||||
}
|
||||
|
||||
|
||||
void InputDevice_neGcon::SetDTR(bool new_dtr)
|
||||
{
|
||||
if(!dtr && new_dtr)
|
||||
{
|
||||
command_phase = 0;
|
||||
bitpos = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
else if(dtr && !new_dtr)
|
||||
{
|
||||
//if(bitpos || transmit_count)
|
||||
// printf("[PAD] Abort communication!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
|
||||
}
|
||||
|
||||
dtr = new_dtr;
|
||||
}
|
||||
|
||||
bool InputDevice_neGcon::GetDSR(void)
|
||||
{
|
||||
if(!dtr)
|
||||
return(0);
|
||||
|
||||
if(!bitpos && transmit_count)
|
||||
return(1);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool InputDevice_neGcon::Clock(bool TxD, int32 &dsr_pulse_delay)
|
||||
{
|
||||
bool ret = 1;
|
||||
|
||||
dsr_pulse_delay = 0;
|
||||
|
||||
if(!dtr)
|
||||
return(1);
|
||||
|
||||
if(transmit_count)
|
||||
ret = (transmit_buffer[transmit_pos] >> bitpos) & 1;
|
||||
|
||||
receive_buffer &= ~(1 << bitpos);
|
||||
receive_buffer |= TxD << bitpos;
|
||||
bitpos = (bitpos + 1) & 0x7;
|
||||
|
||||
if(!bitpos)
|
||||
{
|
||||
//printf("[PAD] Receive: %02x -- command_phase=%d\n", receive_buffer, command_phase);
|
||||
|
||||
if(transmit_count)
|
||||
{
|
||||
transmit_pos++;
|
||||
transmit_count--;
|
||||
}
|
||||
|
||||
|
||||
switch(command_phase)
|
||||
{
|
||||
case 0:
|
||||
if(receive_buffer != 0x01)
|
||||
command_phase = -1;
|
||||
else
|
||||
{
|
||||
transmit_buffer[0] = 0x23;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 1;
|
||||
command_phase++;
|
||||
dsr_pulse_delay = 256;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
command = receive_buffer;
|
||||
command_phase++;
|
||||
|
||||
transmit_buffer[0] = 0x5A;
|
||||
|
||||
//if(command != 0x42)
|
||||
// fprintf(stderr, "Gamepad unhandled command: 0x%02x\n", command);
|
||||
|
||||
if(command == 0x42)
|
||||
{
|
||||
transmit_buffer[1] = 0xFF ^ buttons[0];
|
||||
transmit_buffer[2] = 0xFF ^ buttons[1];
|
||||
transmit_buffer[3] = twist; // Twist, 0x00 through 0xFF, 0x80 center.
|
||||
transmit_buffer[4] = anabuttons[0]; // Analog button I, 0x00 through 0xFF, 0x00 = no pressing, 0xFF = max.
|
||||
transmit_buffer[5] = anabuttons[1]; // Analog button II, ""
|
||||
transmit_buffer[6] = anabuttons[2]; // Left shoulder analog button, ""
|
||||
transmit_pos = 0;
|
||||
transmit_count = 7;
|
||||
dsr_pulse_delay = 256;
|
||||
}
|
||||
else
|
||||
{
|
||||
command_phase = -1;
|
||||
transmit_buffer[1] = 0;
|
||||
transmit_buffer[2] = 0;
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if(transmit_count > 0)
|
||||
dsr_pulse_delay = 128;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
InputDevice *Device_neGcon_Create(void)
|
||||
{
|
||||
return new InputDevice_neGcon();
|
||||
}
|
||||
|
||||
|
||||
InputDeviceInputInfoStruct Device_neGcon_IDII[21] =
|
||||
{
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ "start", "START", 4, IDIT_BUTTON, NULL },
|
||||
{ "up", "D-Pad UP ↑", 0, IDIT_BUTTON, "down" },
|
||||
{ "right", "D-Pad RIGHT →", 3, IDIT_BUTTON, "left" },
|
||||
{ "down", "D-Pad DOWN ↓", 1, IDIT_BUTTON, "up" },
|
||||
{ "left", "D-Pad LEFT ←", 2, IDIT_BUTTON, "right" },
|
||||
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ "r", "Right Shoulder", 12, IDIT_BUTTON },
|
||||
|
||||
{ "b", "B", 9, IDIT_BUTTON, NULL },
|
||||
{ "a", "A", 10, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "twist_cwise", "Twist ↓|↑ (Analog, Turn Right)", 6, IDIT_BUTTON_ANALOG },
|
||||
{ "twist_ccwise", "Twist ↑|↓ (Analog, Turn Left)", 5, IDIT_BUTTON_ANALOG },
|
||||
{ "i", "I (Analog)", 8, IDIT_BUTTON_ANALOG },
|
||||
{ "ii", "II (Analog)", 7, IDIT_BUTTON_ANALOG },
|
||||
|
||||
{ "l", "Left Shoulder (Analog)", 11, IDIT_BUTTON_ANALOG },
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef __MDFN_PSX_INPUT_NEGCON_H
|
||||
#define __MDFN_PSX_INPUT_NEGCON_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
InputDevice *Device_neGcon_Create(void);
|
||||
extern InputDeviceInputInfoStruct Device_neGcon_IDII[21];
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,174 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "psx.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
static uint16 Asserted;
|
||||
static uint16 Mask;
|
||||
static uint16 Status;
|
||||
|
||||
static INLINE void Recalc(void)
|
||||
{
|
||||
CPU->AssertIRQ(0, (bool)(Status & Mask));
|
||||
}
|
||||
|
||||
void IRQ_Power(void)
|
||||
{
|
||||
Asserted = 0;
|
||||
Status = 0;
|
||||
Mask = 0;
|
||||
|
||||
Recalc();
|
||||
}
|
||||
|
||||
int IRQ_StateAction(StateMem *sm, int load, int data_only)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(Asserted),
|
||||
SFVAR(Mask),
|
||||
SFVAR(Status),
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "IRQ");
|
||||
|
||||
if(load)
|
||||
{
|
||||
Recalc();
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
void IRQ_Assert(int which, bool status)
|
||||
{
|
||||
uint32 old_Asserted = Asserted;
|
||||
//PSX_WARNING("[IRQ] Assert: %d %d", which, status);
|
||||
|
||||
//if(which == IRQ_SPU && status && (Asserted & (1 << which)))
|
||||
// MDFN_DispMessage("SPU IRQ glitch??");
|
||||
|
||||
Asserted &= ~(1 << which);
|
||||
|
||||
if(status)
|
||||
{
|
||||
Asserted |= 1 << which;
|
||||
//Status |= 1 << which;
|
||||
Status |= (old_Asserted ^ Asserted) & Asserted;
|
||||
}
|
||||
|
||||
Recalc();
|
||||
}
|
||||
|
||||
|
||||
void IRQ_Write(uint32 A, uint32 V)
|
||||
{
|
||||
// FIXME if we ever have "accurate" bus emulation
|
||||
V <<= (A & 3) * 8;
|
||||
|
||||
//printf("[IRQ] Write: 0x%08x 0x%08x --- PAD TEMP\n", A, V);
|
||||
|
||||
if(A & 4)
|
||||
Mask = V;
|
||||
else
|
||||
{
|
||||
Status &= V;
|
||||
//Status |= Asserted;
|
||||
}
|
||||
|
||||
Recalc();
|
||||
}
|
||||
|
||||
|
||||
uint32 IRQ_Read(uint32 A)
|
||||
{
|
||||
uint32 ret = 0;
|
||||
|
||||
if(A & 4)
|
||||
ret = Mask;
|
||||
else
|
||||
ret = Status;
|
||||
|
||||
// FIXME: Might want to move this out to psx.cpp eventually.
|
||||
ret |= 0x1F800000;
|
||||
ret >>= (A & 3) * 8;
|
||||
|
||||
|
||||
//printf("[IRQ] Read: 0x%08x 0x%08x --- PAD TEMP\n", A, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
void IRQ_Reset(void)
|
||||
{
|
||||
Asserted = 0;
|
||||
Status = 0;
|
||||
Mask = 0;
|
||||
|
||||
Recalc();
|
||||
}
|
||||
|
||||
|
||||
uint32 IRQ_GetRegister(unsigned int which, char *special, const uint32 special_len)
|
||||
{
|
||||
uint32 ret = 0;
|
||||
|
||||
switch(which)
|
||||
{
|
||||
case IRQ_GSREG_ASSERTED:
|
||||
ret = Asserted;
|
||||
break;
|
||||
|
||||
case IRQ_GSREG_STATUS:
|
||||
ret = Status;
|
||||
break;
|
||||
|
||||
case IRQ_GSREG_MASK:
|
||||
ret = Mask;
|
||||
break;
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void IRQ_SetRegister(unsigned int which, uint32 value)
|
||||
{
|
||||
switch(which)
|
||||
{
|
||||
case IRQ_GSREG_ASSERTED:
|
||||
Asserted = value;
|
||||
Recalc();
|
||||
break;
|
||||
|
||||
case IRQ_GSREG_STATUS:
|
||||
Status = value;
|
||||
Recalc();
|
||||
break;
|
||||
|
||||
case IRQ_GSREG_MASK:
|
||||
Mask = value;
|
||||
Recalc();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef __MDFN_PSX_IRQ_H
|
||||
#define __MDFN_PSX_IRQ_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
IRQ_VBLANK = 0,
|
||||
IRQ_GPU = 1,
|
||||
IRQ_CD = 2,
|
||||
IRQ_DMA = 3, // Probably
|
||||
IRQ_TIMER_0 = 4,
|
||||
IRQ_TIMER_1 = 5,
|
||||
IRQ_TIMER_2 = 6,
|
||||
IRQ_SIO = 7,
|
||||
IRQ_SPU = 9,
|
||||
IRQ_PIO = 10, // Probably
|
||||
};
|
||||
|
||||
void IRQ_Power(void);
|
||||
void IRQ_Assert(int which, bool asserted);
|
||||
|
||||
void IRQ_Write(uint32 A, uint32 V);
|
||||
uint32 IRQ_Read(uint32 A);
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
IRQ_GSREG_ASSERTED = 0,
|
||||
IRQ_GSREG_STATUS = 1,
|
||||
IRQ_GSREG_MASK = 2
|
||||
};
|
||||
|
||||
uint32 IRQ_GetRegister(unsigned int which, char *special, const uint32 special_len);
|
||||
void IRQ_SetRegister(unsigned int which, uint32 value);
|
||||
|
||||
int IRQ_StateAction(StateMem *sm, int load, int data_only);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,820 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
MDEC_READ_FIFO(tfr) vs InCounter vs MDEC_DMACanRead() is a bit fragile right now. Actually, the entire horrible state machine monstrosity is fragile.
|
||||
|
||||
TODO: OutFIFOReady, so <16bpp works right.
|
||||
|
||||
TODO CODE:
|
||||
|
||||
bool InFIFOReady;
|
||||
|
||||
if(InFIFO.CanWrite())
|
||||
{
|
||||
InFIFO.WriteUnit(V);
|
||||
|
||||
if(InCommand)
|
||||
{
|
||||
if(InCounter != 0xFFFF)
|
||||
{
|
||||
InCounter--;
|
||||
|
||||
// This condition when InFIFO.CanWrite() != 0 is a bit buggy on real hardware, decoding loop *seems* to be reading too
|
||||
// much and pulling garbage from the FIFO.
|
||||
if(InCounter == 0xFFFF)
|
||||
InFIFOReady = true;
|
||||
}
|
||||
|
||||
if(InFIFO.CanWrite() == 0)
|
||||
InFIFOReady = true;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Good test-case games:
|
||||
// Dragon Knight 4(bad disc?)
|
||||
// Final Fantasy 7 intro movie.
|
||||
// GameShark Version 4.0 intro movie; (clever) abuse of DMA channel 0.
|
||||
// SimCity 2000 startup.
|
||||
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "psx.h"
|
||||
#include "mdec.h"
|
||||
|
||||
#include "masmem.h"
|
||||
#include "cdrom/SimpleFIFO.h"
|
||||
|
||||
#if defined(__SSE2__)
|
||||
#include <xmmintrin.h>
|
||||
#include <emmintrin.h>
|
||||
#endif
|
||||
|
||||
#if defined(ARCH_POWERPC_ALTIVEC) && defined(HAVE_ALTIVEC_H)
|
||||
#include <altivec.h>
|
||||
#endif
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
static int32 ClockCounter;
|
||||
static unsigned MDRPhase;
|
||||
static SimpleFIFO<uint32> InFIFO(0x20);
|
||||
static SimpleFIFO<uint32> OutFIFO(0x20);
|
||||
|
||||
static int8 block_y[8][8];
|
||||
static int8 block_cb[8][8]; // [y >> 1][x >> 1]
|
||||
static int8 block_cr[8][8]; // [y >> 1][x >> 1]
|
||||
|
||||
static uint32 Control;
|
||||
static uint32 Command;
|
||||
static bool InCommand;
|
||||
|
||||
static uint8 QMatrix[2][64];
|
||||
static uint32 QMIndex;
|
||||
|
||||
static EW_VAR_ALIGN(16) int16 IDCTMatrix[64];
|
||||
static uint32 IDCTMIndex;
|
||||
|
||||
static uint8 QScale;
|
||||
|
||||
static EW_VAR_ALIGN(16) int16 Coeff[64];
|
||||
static uint32 CoeffIndex;
|
||||
static uint32 DecodeWB;
|
||||
|
||||
static union
|
||||
{
|
||||
uint32 pix32[48];
|
||||
uint16 pix16[96];
|
||||
uint8 pix8[192];
|
||||
} PixelBuffer;
|
||||
static uint32 PixelBufferReadOffset;
|
||||
static uint32 PixelBufferCount32;
|
||||
|
||||
static uint16 InCounter;
|
||||
|
||||
static uint8 RAMOffsetY;
|
||||
static uint8 RAMOffsetCounter;
|
||||
static uint8 RAMOffsetWWS;
|
||||
|
||||
static const uint8 ZigZag[64] =
|
||||
{
|
||||
0x00, 0x08, 0x01, 0x02, 0x09, 0x10, 0x18, 0x11,
|
||||
0x0a, 0x03, 0x04, 0x0b, 0x12, 0x19, 0x20, 0x28,
|
||||
0x21, 0x1a, 0x13, 0x0c, 0x05, 0x06, 0x0d, 0x14,
|
||||
0x1b, 0x22, 0x29, 0x30, 0x38, 0x31, 0x2a, 0x23,
|
||||
0x1c, 0x15, 0x0e, 0x07, 0x0f, 0x16, 0x1d, 0x24,
|
||||
0x2b, 0x32, 0x39, 0x3a, 0x33, 0x2c, 0x25, 0x1e,
|
||||
0x17, 0x1f, 0x26, 0x2d, 0x34, 0x3b, 0x3c, 0x35,
|
||||
0x2e, 0x27, 0x2f, 0x36, 0x3d, 0x3e, 0x37, 0x3f,
|
||||
};
|
||||
|
||||
void MDEC_Power(void)
|
||||
{
|
||||
ClockCounter = 0;
|
||||
MDRPhase = 0;
|
||||
|
||||
InFIFO.Flush();
|
||||
OutFIFO.Flush();
|
||||
|
||||
memset(block_y, 0, sizeof(block_y));
|
||||
memset(block_cb, 0, sizeof(block_cb));
|
||||
memset(block_cr, 0, sizeof(block_cr));
|
||||
|
||||
Control = 0;
|
||||
Command = 0;
|
||||
InCommand = false;
|
||||
|
||||
memset(QMatrix, 0, sizeof(QMatrix));
|
||||
QMIndex = 0;
|
||||
|
||||
memset(IDCTMatrix, 0, sizeof(IDCTMatrix));
|
||||
IDCTMIndex = 0;
|
||||
|
||||
QScale = 0;
|
||||
|
||||
memset(Coeff, 0, sizeof(Coeff));
|
||||
CoeffIndex = 0;
|
||||
DecodeWB = 0;
|
||||
|
||||
memset(PixelBuffer.pix32, 0, sizeof(PixelBuffer.pix32));
|
||||
PixelBufferReadOffset = 0;
|
||||
PixelBufferCount32 = 0;
|
||||
|
||||
InCounter = 0;
|
||||
|
||||
RAMOffsetY = 0;
|
||||
RAMOffsetCounter = 0;
|
||||
RAMOffsetWWS = 0;
|
||||
}
|
||||
|
||||
int MDEC_StateAction(StateMem *sm, int load, int data_only)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(ClockCounter),
|
||||
SFVAR(MDRPhase),
|
||||
|
||||
#define SFFIFO32(fifoobj) SFARRAY32(&fifoobj.data[0], fifoobj.data.size()), \
|
||||
SFVAR(fifoobj.read_pos), \
|
||||
SFVAR(fifoobj.write_pos), \
|
||||
SFVAR(fifoobj.in_count)
|
||||
|
||||
SFFIFO32(InFIFO),
|
||||
SFFIFO32(OutFIFO),
|
||||
#undef SFFIFO
|
||||
|
||||
SFARRAY(&block_y[0][0], sizeof(block_y) / sizeof(block_y[0][0])),
|
||||
SFARRAY(&block_cb[0][0], sizeof(block_cb) / sizeof(block_cb[0][0])),
|
||||
SFARRAY(&block_cr[0][0], sizeof(block_cr) / sizeof(block_cr[0][0])),
|
||||
|
||||
SFVAR(Control),
|
||||
SFVAR(Command),
|
||||
SFVAR(InCommand),
|
||||
|
||||
SFARRAY(&QMatrix[0][0], sizeof(QMatrix) / sizeof(QMatrix[0][0])),
|
||||
SFVAR(QMIndex),
|
||||
|
||||
SFARRAY16(&IDCTMatrix[0], sizeof(IDCTMatrix) / sizeof(IDCTMatrix[0])),
|
||||
SFVAR(IDCTMIndex),
|
||||
|
||||
SFVAR(QScale),
|
||||
|
||||
SFARRAY16(&Coeff[0], sizeof(Coeff) / sizeof(Coeff[0])),
|
||||
SFVAR(CoeffIndex),
|
||||
SFVAR(DecodeWB),
|
||||
|
||||
SFARRAY32(&PixelBuffer.pix32[0], sizeof(PixelBuffer.pix32) / sizeof(PixelBuffer.pix32[0])),
|
||||
SFVAR(PixelBufferReadOffset),
|
||||
SFVAR(PixelBufferCount32),
|
||||
|
||||
SFVAR(InCounter),
|
||||
|
||||
SFVAR(RAMOffsetY),
|
||||
SFVAR(RAMOffsetCounter),
|
||||
SFVAR(RAMOffsetWWS),
|
||||
|
||||
SFEND
|
||||
};
|
||||
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "MDEC");
|
||||
|
||||
if(load)
|
||||
{
|
||||
InFIFO.SaveStatePostLoad();
|
||||
OutFIFO.SaveStatePostLoad();
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static INLINE int8 Mask9ClampS8(int32 v)
|
||||
{
|
||||
v = sign_x_to_s32(9, v);
|
||||
|
||||
if(v < -128)
|
||||
v = -128;
|
||||
|
||||
if(v > 127)
|
||||
v = 127;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void IDCT_1D_Multi(int16 *in_coeff, T *out_coeff)
|
||||
{
|
||||
#if defined(__SSE2__)
|
||||
{
|
||||
for(unsigned col = 0; col < 8; col++)
|
||||
{
|
||||
__m128i c = _mm_load_si128((__m128i *)&in_coeff[(col * 8)]);
|
||||
|
||||
for(unsigned x = 0; x < 8; x++)
|
||||
{
|
||||
__m128i sum;
|
||||
__m128i m;
|
||||
int32 tmp[4] MDFN_ALIGN(16);
|
||||
|
||||
m = _mm_load_si128((__m128i *)&IDCTMatrix[(x * 8)]);
|
||||
sum = _mm_madd_epi16(m, c);
|
||||
sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6)));
|
||||
sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, (1 << 0) | (0 << 2)));
|
||||
|
||||
//_mm_store_ss((float *)&tmp[0], (__m128)sum);
|
||||
_mm_store_si128((__m128i*)tmp, sum);
|
||||
|
||||
if(sizeof(T) == 1)
|
||||
out_coeff[(col * 8) + x] = Mask9ClampS8((tmp[0] + 0x4000) >> 15);
|
||||
else
|
||||
out_coeff[(x * 8) + col] = (tmp[0] + 0x4000) >> 15;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
for(unsigned col = 0; col < 8; col++)
|
||||
{
|
||||
for(unsigned x = 0; x < 8; x++)
|
||||
{
|
||||
int32 sum = 0;
|
||||
|
||||
for(unsigned u = 0; u < 8; u++)
|
||||
{
|
||||
sum += (in_coeff[(col * 8) + u] * IDCTMatrix[(x * 8) + u]);
|
||||
}
|
||||
|
||||
if(sizeof(T) == 1)
|
||||
out_coeff[(col * 8) + x] = Mask9ClampS8((sum + 0x4000) >> 15);
|
||||
else
|
||||
out_coeff[(x * 8) + col] = (sum + 0x4000) >> 15;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void IDCT(int16 *in_coeff, int8 *out_coeff) NO_INLINE;
|
||||
static void IDCT(int16 *in_coeff, int8 *out_coeff)
|
||||
{
|
||||
EW_VAR_ALIGN(16) int16 tmpbuf[64];
|
||||
|
||||
IDCT_1D_Multi<int16>(in_coeff, tmpbuf);
|
||||
IDCT_1D_Multi<int8>(tmpbuf, out_coeff);
|
||||
}
|
||||
|
||||
static INLINE void YCbCr_to_RGB(const int8 y, const int8 cb, const int8 cr, int &r, int &g, int &b)
|
||||
{
|
||||
//
|
||||
// The formula for green is still a bit off(precision/rounding issues when both cb and cr are non-zero).
|
||||
//
|
||||
|
||||
r = Mask9ClampS8(y + (((359 * cr) + 0x80) >> 8));
|
||||
//g = Mask9ClampS8(y + (((-88 * cb) + (-183 * cr) + 0x80) >> 8));
|
||||
g = Mask9ClampS8(y + ((((-88 * cb) &~ 0x1F) + ((-183 * cr) &~ 0x07) + 0x80) >> 8));
|
||||
b = Mask9ClampS8(y + (((454 * cb) + 0x80) >> 8));
|
||||
|
||||
r ^= 0x80;
|
||||
g ^= 0x80;
|
||||
b ^= 0x80;
|
||||
}
|
||||
|
||||
static INLINE uint16 RGB_to_RGB555(uint8 r, uint8 g, uint8 b)
|
||||
{
|
||||
r = (r + 4) >> 3;
|
||||
g = (g + 4) >> 3;
|
||||
b = (b + 4) >> 3;
|
||||
|
||||
if(r > 0x1F)
|
||||
r = 0x1F;
|
||||
|
||||
if(g > 0x1F)
|
||||
g = 0x1F;
|
||||
|
||||
if(b > 0x1F)
|
||||
b = 0x1F;
|
||||
|
||||
return((r << 0) | (g << 5) | (b << 10));
|
||||
}
|
||||
|
||||
static void EncodeImage(const unsigned ybn)
|
||||
{
|
||||
//printf("ENCODE, %d\n", (Command & 0x08000000) ? 256 : 384);
|
||||
|
||||
PixelBufferCount32 = 0;
|
||||
|
||||
switch((Command >> 27) & 0x3)
|
||||
{
|
||||
case 0: // 4bpp
|
||||
{
|
||||
const uint8 us_xor = (Command & (1U << 26)) ? 0x00 : 0x88;
|
||||
uint8* pix_out = PixelBuffer.pix8;
|
||||
|
||||
for(int y = 0; y < 8; y++)
|
||||
{
|
||||
for(int x = 0; x < 8; x += 2)
|
||||
{
|
||||
uint8 p0 = std::min<int>(127, block_y[y][x + 0] + 8);
|
||||
uint8 p1 = std::min<int>(127, block_y[y][x + 1] + 8);
|
||||
|
||||
*pix_out = ((p0 >> 4) | (p1 & 0xF0)) ^ us_xor;
|
||||
pix_out++;
|
||||
}
|
||||
}
|
||||
PixelBufferCount32 = 8;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 1: // 8bpp
|
||||
{
|
||||
const uint8 us_xor = (Command & (1U << 26)) ? 0x00 : 0x80;
|
||||
uint8* pix_out = PixelBuffer.pix8;
|
||||
|
||||
for(int y = 0; y < 8; y++)
|
||||
{
|
||||
for(int x = 0; x < 8; x++)
|
||||
{
|
||||
*pix_out = (uint8)block_y[y][x] ^ us_xor;
|
||||
pix_out++;
|
||||
}
|
||||
}
|
||||
PixelBufferCount32 = 16;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // 24bpp
|
||||
{
|
||||
const uint8 rgb_xor = (Command & (1U << 26)) ? 0x80 : 0x00;
|
||||
uint8* pix_out = PixelBuffer.pix8;
|
||||
|
||||
for(int y = 0; y < 8; y++)
|
||||
{
|
||||
const int8* by = &block_y[y][0];
|
||||
const int8* cb = &block_cb[(y >> 1) | ((ybn & 2) << 1)][(ybn & 1) << 2];
|
||||
const int8* cr = &block_cr[(y >> 1) | ((ybn & 2) << 1)][(ybn & 1) << 2];
|
||||
|
||||
for(int x = 0; x < 8; x++)
|
||||
{
|
||||
int r, g, b;
|
||||
|
||||
YCbCr_to_RGB(by[x], cb[x >> 1], cr[x >> 1], r, g, b);
|
||||
|
||||
pix_out[0] = r ^ rgb_xor;
|
||||
pix_out[1] = g ^ rgb_xor;
|
||||
pix_out[2] = b ^ rgb_xor;
|
||||
pix_out += 3;
|
||||
}
|
||||
}
|
||||
PixelBufferCount32 = 48;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: // 16bpp
|
||||
{
|
||||
uint16 pixel_xor = ((Command & 0x02000000) ? 0x8000 : 0x0000) | ((Command & (1U << 26)) ? 0x4210 : 0x0000);
|
||||
uint16* pix_out = PixelBuffer.pix16;
|
||||
|
||||
for(int y = 0; y < 8; y++)
|
||||
{
|
||||
const int8* by = &block_y[y][0];
|
||||
const int8* cb = &block_cb[(y >> 1) | ((ybn & 2) << 1)][(ybn & 1) << 2];
|
||||
const int8* cr = &block_cr[(y >> 1) | ((ybn & 2) << 1)][(ybn & 1) << 2];
|
||||
|
||||
for(int x = 0; x < 8; x++)
|
||||
{
|
||||
int r, g, b;
|
||||
|
||||
YCbCr_to_RGB(by[x], cb[x >> 1], cr[x >> 1], r, g, b);
|
||||
|
||||
StoreU16_LE(pix_out, pixel_xor ^ RGB_to_RGB555(r, g, b));
|
||||
pix_out++;
|
||||
}
|
||||
}
|
||||
PixelBufferCount32 = 32;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static INLINE void WriteImageData(uint16 V, int32* eat_cycles)
|
||||
{
|
||||
const uint32 qmw = (bool)(DecodeWB < 2);
|
||||
|
||||
//printf("MDEC DMA SubWrite: %04x, %d\n", V, CoeffIndex);
|
||||
|
||||
if(!CoeffIndex)
|
||||
{
|
||||
if(V == 0xFE00)
|
||||
{
|
||||
//printf("FE00 @ %u\n", DecodeWB);
|
||||
return;
|
||||
}
|
||||
|
||||
QScale = V >> 10;
|
||||
|
||||
{
|
||||
int q = QMatrix[qmw][0]; // No QScale here!
|
||||
int ci = sign_10_to_s16(V & 0x3FF);
|
||||
int tmp;
|
||||
|
||||
if(q != 0)
|
||||
tmp = ((ci * q) << 4) + (ci ? ((ci < 0) ? 8 : -8) : 0);
|
||||
else
|
||||
tmp = (ci * 2) << 4;
|
||||
|
||||
// Not sure if it should be 0x3FFF or 0x3FF0 or maybe 0x3FF8?
|
||||
Coeff[ZigZag[0]] = std::min<int>(0x3FFF, std::max<int>(-0x4000, tmp));
|
||||
CoeffIndex++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(V == 0xFE00)
|
||||
{
|
||||
while(CoeffIndex < 64)
|
||||
Coeff[ZigZag[CoeffIndex++]] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32 rlcount = V >> 10;
|
||||
|
||||
for(uint32 i = 0; i < rlcount && CoeffIndex < 64; i++)
|
||||
{
|
||||
Coeff[ZigZag[CoeffIndex]] = 0;
|
||||
CoeffIndex++;
|
||||
}
|
||||
|
||||
if(CoeffIndex < 64)
|
||||
{
|
||||
int q = QScale * QMatrix[qmw][CoeffIndex];
|
||||
int ci = sign_10_to_s16(V & 0x3FF);
|
||||
int tmp;
|
||||
|
||||
if(q != 0)
|
||||
tmp = (((ci * q) >> 3) << 4) + (ci ? ((ci < 0) ? 8 : -8) : 0);
|
||||
else
|
||||
tmp = (ci * 2) << 4;
|
||||
|
||||
// Not sure if it should be 0x3FFF or 0x3FF0 or maybe 0x3FF8?
|
||||
Coeff[ZigZag[CoeffIndex]] = std::min<int>(0x3FFF, std::max<int>(-0x4000, tmp));
|
||||
CoeffIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(CoeffIndex == 64)
|
||||
{
|
||||
CoeffIndex = 0;
|
||||
|
||||
//printf("Block %d finished\n", DecodeWB);
|
||||
|
||||
switch(DecodeWB)
|
||||
{
|
||||
case 0: IDCT(Coeff, &block_cr[0][0]); break;
|
||||
case 1: IDCT(Coeff, &block_cb[0][0]); break;
|
||||
case 2: IDCT(Coeff, &block_y[0][0]); break;
|
||||
case 3: IDCT(Coeff, &block_y[0][0]); break;
|
||||
case 4: IDCT(Coeff, &block_y[0][0]); break;
|
||||
case 5: IDCT(Coeff, &block_y[0][0]); break;
|
||||
}
|
||||
|
||||
//
|
||||
// Approximate, actual timing seems to be a bit complex.
|
||||
//
|
||||
*eat_cycles += 341;
|
||||
|
||||
if(DecodeWB >= 2)
|
||||
{
|
||||
EncodeImage((DecodeWB + 4) % 6);
|
||||
}
|
||||
|
||||
DecodeWB++;
|
||||
if(DecodeWB == (((Command >> 27) & 2) ? 6 : 3))
|
||||
{
|
||||
DecodeWB = ((Command >> 27) & 2) ? 0 : 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 1
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
#define MDEC_WAIT_COND(n) { case __COUNTER__: if(!(n)) { MDRPhase = __COUNTER__ - MDRPhaseBias - 1; return; } }
|
||||
|
||||
#define MDEC_WRITE_FIFO(n) { MDEC_WAIT_COND(OutFIFO.CanWrite()); OutFIFO.WriteUnit(n); }
|
||||
#define MDEC_READ_FIFO(n) { MDEC_WAIT_COND(InFIFO.CanRead()); n = InFIFO.ReadUnit(); }
|
||||
#define MDEC_EAT_CLOCKS(n) { ClockCounter -= (n); MDEC_WAIT_COND(ClockCounter > 0); }
|
||||
|
||||
void MDEC_Run(int32 clocks)
|
||||
{
|
||||
static const unsigned MDRPhaseBias = __COUNTER__ + 1;
|
||||
|
||||
//MDFN_DispMessage("%u", OutFIFO.CanRead());
|
||||
|
||||
ClockCounter += clocks;
|
||||
|
||||
if(ClockCounter > 128)
|
||||
{
|
||||
//if(MDRPhase != 0)
|
||||
// printf("SNORT: %d\n", ClockCounter);
|
||||
ClockCounter = 128;
|
||||
}
|
||||
|
||||
switch(MDRPhase + MDRPhaseBias)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
InCommand = false;
|
||||
MDEC_READ_FIFO(Command); // This must be the first MDEC_* macro used!
|
||||
InCommand = true;
|
||||
MDEC_EAT_CLOCKS(1);
|
||||
|
||||
//printf("****************** Command: %08x, %02x\n", Command, Command >> 29);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
if(((Command >> 29) & 0x7) == 1)
|
||||
{
|
||||
InCounter = Command & 0xFFFF;
|
||||
OutFIFO.Flush();
|
||||
//OutBuffer.Flush();
|
||||
|
||||
PixelBufferCount32 = 0;
|
||||
CoeffIndex = 0;
|
||||
|
||||
if((Command >> 27) & 2)
|
||||
DecodeWB = 0;
|
||||
else
|
||||
DecodeWB = 2;
|
||||
|
||||
switch((Command >> 27) & 0x3)
|
||||
{
|
||||
case 0:
|
||||
case 1: RAMOffsetWWS = 0; break;
|
||||
case 2: RAMOffsetWWS = 6; break;
|
||||
case 3: RAMOffsetWWS = 4; break;
|
||||
}
|
||||
RAMOffsetY = 0;
|
||||
RAMOffsetCounter = RAMOffsetWWS;
|
||||
|
||||
InCounter--;
|
||||
do
|
||||
{
|
||||
uint32 tfr;
|
||||
int32 need_eat; // = 0;
|
||||
|
||||
MDEC_READ_FIFO(tfr);
|
||||
InCounter--;
|
||||
|
||||
// printf("KA: %04x %08x\n", InCounter, tfr);
|
||||
|
||||
need_eat = 0;
|
||||
PixelBufferCount32 = 0;
|
||||
WriteImageData(tfr, &need_eat);
|
||||
WriteImageData(tfr >> 16, &need_eat);
|
||||
|
||||
MDEC_EAT_CLOCKS(need_eat);
|
||||
|
||||
PixelBufferReadOffset = 0;
|
||||
while(PixelBufferReadOffset != PixelBufferCount32)
|
||||
{
|
||||
MDEC_WRITE_FIFO(LoadU32_LE(&PixelBuffer.pix32[PixelBufferReadOffset++]));
|
||||
}
|
||||
} while(InCounter != 0xFFFF);
|
||||
}
|
||||
//
|
||||
//
|
||||
//
|
||||
else if(((Command >> 29) & 0x7) == 2)
|
||||
{
|
||||
QMIndex = 0;
|
||||
InCounter = 0x10 + ((Command & 0x1) ? 0x10 : 0x00);
|
||||
|
||||
InCounter--;
|
||||
do
|
||||
{
|
||||
uint32 tfr;
|
||||
|
||||
MDEC_READ_FIFO(tfr);
|
||||
InCounter--;
|
||||
|
||||
//printf("KA: %04x %08x\n", InCounter, tfr);
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
QMatrix[QMIndex >> 6][QMIndex & 0x3F] = (uint8)tfr;
|
||||
QMIndex = (QMIndex + 1) & 0x7F;
|
||||
tfr >>= 8;
|
||||
}
|
||||
} while(InCounter != 0xFFFF);
|
||||
}
|
||||
//
|
||||
//
|
||||
//
|
||||
else if(((Command >> 29) & 0x7) == 3)
|
||||
{
|
||||
IDCTMIndex = 0;
|
||||
InCounter = 0x20;
|
||||
|
||||
InCounter--;
|
||||
do
|
||||
{
|
||||
uint32 tfr;
|
||||
|
||||
MDEC_READ_FIFO(tfr);
|
||||
InCounter--;
|
||||
|
||||
for(unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
IDCTMatrix[((IDCTMIndex & 0x7) << 3) | ((IDCTMIndex >> 3) & 0x7)] = (int16)(tfr & 0xFFFF) >> 3;
|
||||
IDCTMIndex = (IDCTMIndex + 1) & 0x3F;
|
||||
|
||||
tfr >>= 16;
|
||||
}
|
||||
} while(InCounter != 0xFFFF);
|
||||
}
|
||||
else
|
||||
{
|
||||
InCounter = Command & 0xFFFF;
|
||||
}
|
||||
} // end for(;;)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void MDEC_DMAWrite(uint32 V)
|
||||
{
|
||||
if(InFIFO.CanWrite())
|
||||
{
|
||||
InFIFO.WriteUnit(V);
|
||||
MDEC_Run(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
PSX_DBG(PSX_DBG_WARNING, "Input FIFO DMA write overflow?!\n");
|
||||
}
|
||||
}
|
||||
|
||||
uint32 MDEC_DMARead(int32* offs)
|
||||
{
|
||||
uint32 V = 0;
|
||||
|
||||
*offs = 0;
|
||||
|
||||
if(MDFN_LIKELY(OutFIFO.CanRead()))
|
||||
{
|
||||
V = OutFIFO.ReadUnit();
|
||||
|
||||
*offs = (RAMOffsetY & 0x7) * RAMOffsetWWS;
|
||||
|
||||
if(RAMOffsetY & 0x08)
|
||||
{
|
||||
*offs = (*offs - RAMOffsetWWS*7);
|
||||
}
|
||||
|
||||
RAMOffsetCounter--;
|
||||
if(!RAMOffsetCounter)
|
||||
{
|
||||
RAMOffsetCounter = RAMOffsetWWS;
|
||||
RAMOffsetY++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PSX_DBG(PSX_DBG_WARNING, "[MDEC] BONUS GNOMES\n");
|
||||
V = rand();
|
||||
}
|
||||
|
||||
return(V);
|
||||
}
|
||||
|
||||
bool MDEC_DMACanWrite(void)
|
||||
{
|
||||
return((InFIFO.CanWrite() >= 0x20) && (Control & (1U << 30)) && InCommand && InCounter != 0xFFFF);
|
||||
}
|
||||
|
||||
bool MDEC_DMACanRead(void)
|
||||
{
|
||||
return((OutFIFO.CanRead() >= 0x20) && (Control & (1U << 29)));
|
||||
}
|
||||
|
||||
void MDEC_Write(const pscpu_timestamp_t timestamp, uint32 A, uint32 V)
|
||||
{
|
||||
//PSX_WARNING("[MDEC] Write: 0x%08x 0x%08x, %d --- %u %u", A, V, timestamp, InFIFO.CanRead(), OutFIFO.CanRead());
|
||||
if(A & 4)
|
||||
{
|
||||
if(V & 0x80000000) // Reset?
|
||||
{
|
||||
MDRPhase = 0;
|
||||
InCounter = 0;
|
||||
Command = 0;
|
||||
InCommand = false;
|
||||
|
||||
PixelBufferCount32 = 0;
|
||||
ClockCounter = 0;
|
||||
QMIndex = 0;
|
||||
IDCTMIndex = 0;
|
||||
|
||||
QScale = 0;
|
||||
|
||||
memset(Coeff, 0, sizeof(Coeff));
|
||||
CoeffIndex = 0;
|
||||
DecodeWB = 0;
|
||||
|
||||
InFIFO.Flush();
|
||||
OutFIFO.Flush();
|
||||
}
|
||||
Control = V & 0x7FFFFFFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(InFIFO.CanWrite())
|
||||
{
|
||||
InFIFO.WriteUnit(V);
|
||||
|
||||
if(!InCommand)
|
||||
{
|
||||
if(ClockCounter < 1)
|
||||
ClockCounter = 1;
|
||||
}
|
||||
MDEC_Run(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
PSX_DBG(PSX_DBG_WARNING, "MDEC manual write FIFO overflow?!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32 MDEC_Read(const pscpu_timestamp_t timestamp, uint32 A)
|
||||
{
|
||||
uint32 ret = 0;
|
||||
|
||||
if(A & 4)
|
||||
{
|
||||
ret = 0;
|
||||
|
||||
ret |= (OutFIFO.CanRead() == 0) << 31;
|
||||
ret |= (InFIFO.CanWrite() == 0) << 30;
|
||||
ret |= InCommand << 29;
|
||||
|
||||
ret |= MDEC_DMACanWrite() << 28;
|
||||
ret |= MDEC_DMACanRead() << 27;
|
||||
|
||||
ret |= ((Command >> 25) & 0xF) << 23;
|
||||
|
||||
// Needs refactoring elsewhere to work right: ret |= ((DecodeWB + 4) % 6) << 16;
|
||||
|
||||
ret |= InCounter & 0xFFFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(OutFIFO.CanRead())
|
||||
ret = OutFIFO.ReadUnit();
|
||||
}
|
||||
|
||||
//PSX_WARNING("[MDEC] Read: 0x%08x 0x%08x -- %d %d", A, ret, InputBuffer.CanRead(), InCounter);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef __MDFN_PSX_MDEC_H
|
||||
#define __MDFN_PSX_MDEC_H
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
void MDEC_DMAWrite(uint32 V);
|
||||
|
||||
uint32 MDEC_DMARead(int32* offs);
|
||||
|
||||
void MDEC_Write(const pscpu_timestamp_t timestamp, uint32 A, uint32 V);
|
||||
uint32 MDEC_Read(const pscpu_timestamp_t timestamp, uint32 A);
|
||||
|
||||
|
||||
void MDEC_Power(void);
|
||||
|
||||
bool MDEC_DMACanWrite(void);
|
||||
bool MDEC_DMACanRead(void);
|
||||
void MDEC_Run(int32 clocks);
|
||||
|
||||
int MDEC_StateAction(StateMem *sm, int load, int data_only);
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,253 @@
|
|||
#pragma once
|
||||
|
||||
#include "emuware/emuware.h"
|
||||
#include "cdrom/cdromif.h"
|
||||
#include "video/surface.h"
|
||||
#include "masmem.h"
|
||||
#include "endian.h"
|
||||
|
||||
|
||||
//
|
||||
// Comment out these 2 defines for extra speeeeed.
|
||||
//
|
||||
#define PSX_DBGPRINT_ENABLE 1
|
||||
#define PSX_EVENT_SYSTEM_CHECKS 1
|
||||
|
||||
//
|
||||
// It's highly unlikely the user will want these if they're intentionally compiling without the debugger.
|
||||
#ifndef WANT_DEBUGGER
|
||||
#undef PSX_DBGPRINT_ENABLE
|
||||
#undef PSX_EVENT_SYSTEM_CHECKS
|
||||
#endif
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
#define PSX_DBG_ERROR 0 // Emulator-level error.
|
||||
#define PSX_DBG_WARNING 1 // Warning about game doing questionable things/hitting stuff that might not be emulated correctly.
|
||||
#define PSX_DBG_BIOS_PRINT 2 // BIOS printf/putchar output.
|
||||
#define PSX_DBG_SPARSE 3 // Sparse(relatively) information debug messages(CDC commands).
|
||||
#define PSX_DBG_FLOOD 4 // Heavy informational debug messages(GPU commands; TODO).
|
||||
|
||||
#if PSX_DBGPRINT_ENABLE
|
||||
void PSX_DBG(unsigned level, const char *format, ...) throw() MDFN_COLD MDFN_FORMATSTR(gnu_printf, 2, 3);
|
||||
|
||||
#define PSX_WARNING(format, ...) { PSX_DBG(PSX_DBG_WARNING, format "\n", ## __VA_ARGS__); }
|
||||
#define PSX_DBGINFO(format, ...) { }
|
||||
#else
|
||||
static INLINE void PSX_DBG(unsigned level, const char* format, ...) { }
|
||||
static INLINE void PSX_WARNING(const char* format, ...) { }
|
||||
static INLINE void PSX_DBGINFO(const char* format, ...) { }
|
||||
#endif
|
||||
|
||||
typedef int32 pscpu_timestamp_t;
|
||||
|
||||
bool PSX_EventHandler(const pscpu_timestamp_t timestamp);
|
||||
|
||||
void PSX_MemWrite8(pscpu_timestamp_t timestamp, uint32 A, uint32 V);
|
||||
void PSX_MemWrite16(pscpu_timestamp_t timestamp, uint32 A, uint32 V);
|
||||
void PSX_MemWrite24(pscpu_timestamp_t timestamp, uint32 A, uint32 V);
|
||||
void PSX_MemWrite32(pscpu_timestamp_t timestamp, uint32 A, uint32 V);
|
||||
|
||||
uint8 PSX_MemRead8(pscpu_timestamp_t ×tamp, uint32 A);
|
||||
uint16 PSX_MemRead16(pscpu_timestamp_t ×tamp, uint32 A);
|
||||
uint32 PSX_MemRead24(pscpu_timestamp_t ×tamp, uint32 A);
|
||||
uint32 PSX_MemRead32(pscpu_timestamp_t ×tamp, uint32 A);
|
||||
|
||||
uint8 PSX_MemPeek8(uint32 A);
|
||||
uint16 PSX_MemPeek16(uint32 A);
|
||||
uint32 PSX_MemPeek32(uint32 A);
|
||||
|
||||
// Should write to WO-locations if possible
|
||||
#if 0
|
||||
void PSX_MemPoke8(uint32 A, uint8 V);
|
||||
void PSX_MemPoke16(uint32 A, uint16 V);
|
||||
void PSX_MemPoke32(uint32 A, uint32 V);
|
||||
#endif
|
||||
|
||||
void PSX_RequestMLExit(void);
|
||||
void ForceEventUpdates(const pscpu_timestamp_t timestamp);
|
||||
|
||||
enum
|
||||
{
|
||||
PSX_EVENT__SYNFIRST = 0,
|
||||
PSX_EVENT_GPU,
|
||||
PSX_EVENT_CDC,
|
||||
//PSX_EVENT_SPU,
|
||||
PSX_EVENT_TIMER,
|
||||
PSX_EVENT_DMA,
|
||||
PSX_EVENT_FIO,
|
||||
PSX_EVENT__SYNLAST,
|
||||
PSX_EVENT__COUNT,
|
||||
};
|
||||
|
||||
#define PSX_EVENT_MAXTS 0x20000000
|
||||
void PSX_SetEventNT(const int type, const pscpu_timestamp_t next_timestamp);
|
||||
|
||||
void PSX_GPULineHook(const pscpu_timestamp_t timestamp, const pscpu_timestamp_t line_timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider);
|
||||
|
||||
uint32 PSX_GetRandU32(uint32 mina, uint32 maxa);
|
||||
};
|
||||
|
||||
|
||||
#include "dis.h"
|
||||
#include "cpu.h"
|
||||
#include "irq.h"
|
||||
#include "gpu.h"
|
||||
#include "dma.h"
|
||||
//#include "sio.h"
|
||||
#include "debug.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
class PS_CDC;
|
||||
class PS_SPU;
|
||||
|
||||
extern PS_CPU *CPU;
|
||||
extern PS_GPU *GPU;
|
||||
extern PS_CDC *CDC;
|
||||
extern PS_SPU *SPU;
|
||||
extern MultiAccessSizeMem<2048 * 1024, uint32, false> MainRAM;
|
||||
};
|
||||
|
||||
enum eRegion
|
||||
{
|
||||
REGION_JP = 0,
|
||||
REGION_NA = 1,
|
||||
REGION_EU = 2,
|
||||
REGION_NONE = 3
|
||||
};
|
||||
|
||||
enum eShockStep
|
||||
{
|
||||
eShockStep_Frame
|
||||
};
|
||||
|
||||
|
||||
enum eShockSetting
|
||||
{
|
||||
REGION_AUTODETECT = 0,
|
||||
REGION_DEFAULT = 1,
|
||||
SLSTART = 2,
|
||||
SLEND = 3,
|
||||
SLSTARTP = 4,
|
||||
SLENDP = 5
|
||||
};
|
||||
|
||||
int shock_GetSetting(eShockSetting setting);
|
||||
|
||||
#define MDFN_MSC_RESET 0
|
||||
#define MDFN_MSC_POWER 1
|
||||
#define MDFN_MSC_INSERT_DISK 2
|
||||
#define MDFN_MSC_SELECT_DISK 3
|
||||
#define MDFN_MSC_EJECT_DISK 4
|
||||
|
||||
#define SHOCK_OK 0
|
||||
#define SHOCK_ERROR -1
|
||||
#define SHOCK_NOCANDO -2
|
||||
|
||||
struct ShockTOCTrack
|
||||
{
|
||||
u8 adr;
|
||||
u8 control;
|
||||
u32 lba;
|
||||
};
|
||||
|
||||
struct ShockTOC
|
||||
{
|
||||
u8 first_track;
|
||||
u8 last_track;
|
||||
u8 disc_type;
|
||||
};
|
||||
|
||||
// [0] is unused, [100] is for the leadout track.
|
||||
// Also, for convenience, tracks[last_track + 1] will always refer
|
||||
// to the leadout track(even if last_track < 99, IE the leadout track details are duplicated).
|
||||
typedef s32 (*ShockDisc_ReadTOC)(void* opaque, ShockTOC *read_target, ShockTOCTrack tracks[100 + 1]);
|
||||
typedef s32 (*ShockDisc_ReadLBA)(void* opaque, s32 lba, void* dst);
|
||||
|
||||
class ShockDiscRef
|
||||
{
|
||||
public:
|
||||
ShockDiscRef(void *opaque, s32 lbaCount, ShockDisc_ReadTOC cbReadTOC, ShockDisc_ReadLBA cbReadLBA2448, bool suppliesDeinterleavedSubcode)
|
||||
: mOpaque(opaque)
|
||||
, mLbaCount(lbaCount)
|
||||
, mcbReadTOC(cbReadTOC)
|
||||
, mcbReadLBA2448(cbReadLBA2448)
|
||||
, mSuppliesDeinterleavedSubcode(suppliesDeinterleavedSubcode)
|
||||
{
|
||||
}
|
||||
|
||||
s32 ReadTOC( ShockTOC *read_target, ShockTOCTrack tracks[100 + 1])
|
||||
{
|
||||
return mcbReadTOC(mOpaque, read_target, tracks);
|
||||
}
|
||||
|
||||
s32 ReadLBA2448(s32 lba, void* dst2448);
|
||||
s32 ReadLBA2048(s32 lba, void* dst2048);
|
||||
|
||||
private:
|
||||
s32 InternalReadLBA2448(s32 lba, void* dst2448, bool needSubcode);
|
||||
void *mOpaque;
|
||||
s32 mLbaCount;
|
||||
ShockDisc_ReadTOC mcbReadTOC;
|
||||
ShockDisc_ReadLBA mcbReadLBA2448;
|
||||
bool mSuppliesDeinterleavedSubcode;
|
||||
};
|
||||
|
||||
struct ShockDiscInfo
|
||||
{
|
||||
s32 region;
|
||||
char id[5]; //SCEI, SCEA, SCEE, etc. with null terminator
|
||||
};
|
||||
|
||||
struct ShockFramebufferJob
|
||||
{
|
||||
int width, height;
|
||||
void* ptr;
|
||||
};
|
||||
|
||||
//Creates a ShockDiscRef (representing a disc) with the given properties. Returns it in the specified output pointer.
|
||||
//The ReadLBA2048 function should return 0x01 or 0x02 depending on which mode was there.
|
||||
//Others should return SHOCK_OK
|
||||
EW_EXPORT s32 shock_CreateDisc(ShockDiscRef** outDisc, void *Opaque, s32 lbaCount, ShockDisc_ReadTOC ReadTOC, ShockDisc_ReadLBA ReadLBA2448, bool suppliesDeinterleavedSubcode);
|
||||
|
||||
//Destroys a ShockDiscRef created with shock_CreateDisc. Make sure you havent left it in the playstation before destroying it!
|
||||
EW_EXPORT s32 shock_DestroyDisc(ShockDiscRef* disc);
|
||||
|
||||
//Inspects a disc by looking for the system.cnf and retrieves some necessary information about it.
|
||||
//Useful for determining the region of a disc
|
||||
EW_EXPORT s32 shock_AnalyzeDisc(ShockDiscRef* disc, ShockDiscInfo* info);
|
||||
|
||||
//Creates the psx instance as a console of the specified region.
|
||||
//Additionally mounts the firmware from the provided buffer (the contents are copied)
|
||||
//TODO - receive a model number parameter instead
|
||||
EW_EXPORT s32 shock_Create(void** psx, eRegion region, void* firmware512k);
|
||||
|
||||
//Frees the psx instance created with shock_Create
|
||||
EW_EXPORT s32 shock_Destroy(void* psx);
|
||||
|
||||
//Sets the power to ON. Returns SHOCK_NOCANDO if already on.
|
||||
EW_EXPORT s32 shock_PowerOn(void* psx);
|
||||
|
||||
//Sets the power to OFF. Returns SHOCK_NOCANDO if already off.
|
||||
EW_EXPORT s32 shock_PowerOff(void* psx);
|
||||
|
||||
//Opens the disc tray. Returns SHOCK_NOCANDO if already open.
|
||||
EW_EXPORT s32 shock_OpenTray(void* psx);
|
||||
|
||||
//Sets the disc in the tray. Returns SHOCK_NOCANDO if it's closed. You can pass NULL to remove a disc from the tray
|
||||
EW_EXPORT s32 shock_SetDisc(void* psx, ShockDiscRef* disc);
|
||||
|
||||
//Closes the disc tray. Returns SHOCK_NOCANDO if already closed.
|
||||
EW_EXPORT s32 shock_CloseTray(void* psx);
|
||||
|
||||
//Steps emulation by the specified interval
|
||||
EW_EXPORT s32 shock_Step(void* psx, eShockStep step);
|
||||
|
||||
//Fetches the framebuffer. Can retrieve parameters (set the job ptr to NULL) or fill the provided job ptr with the framebuffer (make sure its big enough).
|
||||
//This helps us copy fewer times.
|
||||
//TODO - support pitch and color format conversion if needed
|
||||
EW_EXPORT s32 shock_GetFramebuffer(void* psx, ShockFramebufferJob* fb);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue