octoshock!

This commit is contained in:
zeromus 2014-12-10 19:41:13 +00:00
parent de5eaf3c4b
commit 5f6bb8d8fd
120 changed files with 71289 additions and 225 deletions

View File

@ -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":

View File

@ -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" />

View File

@ -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);
}

View File

@ -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]

View File

@ -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.

BIN
output/dll/octoshock.dll Normal file

Binary file not shown.

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

112
psx/octoshock/Stream.cpp Normal file
View File

@ -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;
}

206
psx/octoshock/Stream.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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
}

View File

@ -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

View File

@ -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];
}
}

View File

@ -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

View File

@ -0,0 +1,6 @@
#include "../mednafen.h"
#include "SimpleFIFO.h"

View File

@ -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

View File

@ -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);
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

691
psx/octoshock/cdrom/lec.cpp Normal file
View File

@ -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

77
psx/octoshock/cdrom/lec.h Normal file
View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -0,0 +1,3 @@
#ifndef __GNUC__
#pragma pack(pop)
#endif

View File

@ -0,0 +1,3 @@
#include "emuware.h"
//this file intentionally empty

View File

@ -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

View File

@ -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.
------------------------------------------------------------------------

View File

@ -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_ ]

View File

@ -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_ ]

246
psx/octoshock/endian.cpp Normal file
View File

@ -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
}

209
psx/octoshock/endian.h Normal file
View File

@ -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

75
psx/octoshock/error.h Normal file
View File

@ -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

562
psx/octoshock/file.cpp Normal file
View File

@ -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));
//}

122
psx/octoshock/file.h Normal file
View File

@ -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);

357
psx/octoshock/git.h Normal file
View File

@ -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

209
psx/octoshock/masmem.h Normal file
View File

@ -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

87
psx/octoshock/math_ops.h Normal file
View File

@ -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;
}
}

View File

@ -0,0 +1,3 @@
#include "octoshock.h"
//this file intentionally empty

10
psx/octoshock/octoshock.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include "emuware/emuware.h"
#define SIZEOF_DOUBLE 8
#define LSB_FIRST
EW_EXPORT int os_test();

View File

@ -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

View File

@ -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:

View File

@ -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)
{
}

View File

@ -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

2403
psx/octoshock/psx/cdc.cpp Normal file

File diff suppressed because it is too large Load Diff

280
psx/octoshock/psx/cdc.h Normal file
View File

@ -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

954
psx/octoshock/psx/cpu.cpp Normal file
View File

@ -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 &timestamp, 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 &timestamp, 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;
}
}
}

260
psx/octoshock/psx/cpu.h Normal file
View File

@ -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 &timestamp, uint32_t address, bool DS24 = false, bool LWC_timing = false);
template<typename T> void WriteMemory(pscpu_timestamp_t &timestamp, 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

679
psx/octoshock/psx/debug.cpp Normal file
View File

@ -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);
}
}

21
psx/octoshock/psx/debug.h Normal file
View File

@ -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

416
psx/octoshock/psx/dis.cpp Normal file
View File

@ -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);
}
}

11
psx/octoshock/psx/dis.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include "emuware/emuware.h"
#include <string>
namespace MDFN_IEN_PSX
{
std::string DisassembleMIPS(uint32 PC, uint32 instr);
}

821
psx/octoshock/psx/dma.cpp Normal file
View File

@ -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);
}
}

24
psx/octoshock/psx/dma.h Normal file
View File

@ -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

151
psx/octoshock/psx/frontio.h Normal file
View File

@ -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

1691
psx/octoshock/psx/gpu.cpp Normal file

File diff suppressed because it is too large Load Diff

317
psx/octoshock/psx/gpu.h Normal file
View File

@ -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;
};
}

View File

@ -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(),

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}
}

1736
psx/octoshock/psx/gte.cpp Normal file

File diff suppressed because it is too large Load Diff

21
psx/octoshock/psx/gte.h Normal file
View File

@ -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

View File

@ -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 },
};
}

View File

@ -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

View File

@ -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

View File

@ -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 },
};
}

View File

@ -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

View File

@ -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.
};
}

View File

@ -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

View File

@ -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 },
};
}

View File

@ -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

View File

@ -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();
}
}

View File

@ -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

View File

@ -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 },
};
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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

View File

@ -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 },
};
}

View File

@ -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

174
psx/octoshock/psx/irq.cpp Normal file
View File

@ -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;
}
}
}

43
psx/octoshock/psx/irq.h Normal file
View File

@ -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

820
psx/octoshock/psx/mdec.cpp Normal file
View File

@ -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);
}
}

24
psx/octoshock/psx/mdec.h Normal file
View File

@ -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

2929
psx/octoshock/psx/psx.cpp Normal file

File diff suppressed because it is too large Load Diff

253
psx/octoshock/psx/psx.h Normal file
View File

@ -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 &timestamp, uint32 A);
uint16 PSX_MemRead16(pscpu_timestamp_t &timestamp, uint32 A);
uint32 PSX_MemRead24(pscpu_timestamp_t &timestamp, uint32 A);
uint32 PSX_MemRead32(pscpu_timestamp_t &timestamp, 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