BizHawk/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs

1337 lines
45 KiB
C#
Raw Normal View History

2014-12-10 19:41:13 +00:00
//TODO hook up newer file ID stuff, think about how to combine it with the disc ID
//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
2014-12-16 04:02:55 +00:00
//TODO mednafen 0.9.37 changed some disc region detection heuristics. analyze and apply in c# side. also the SCEX id handling changed, maybe simplified
2014-12-10 19:41:13 +00:00
2014-12-17 10:50:20 +00:00
//TODO - ok, think about this. we MUST load a state with the CDC completely intact. no quickly changing discs. thats madness.
//well, I could savestate the disc index and validate the disc collection when loading a state.
//the big problem is, it's completely at odds with the slider-based disc changing model.
//but, maybe it can be reconciled with that model by using the disc ejection to our advantage.
//perhaps moving the slider is meaningless if the disc is ejected--it only affects what disc is inserted when the disc gets inserted!! yeah! this might could save us!
//not exactly user friendly but maybe we can build it from there with a custom UI.. a disk-changer? dunno if that would help
2014-12-10 19:41:13 +00:00
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using Newtonsoft.Json;
using BizHawk.Emulation.Common;
using BizHawk.Common;
#pragma warning disable 649 //adelikat: Disable dumb warnings until this file is complete
namespace BizHawk.Emulation.Cores.Sony.PSX
{
[CoreAttributes(
2014-12-10 19:41:13 +00:00
"Octoshock",
"Ryphecha",
isPorted: true,
isReleased: true
)]
public unsafe class Octoshock : IEmulator, IVideoProvider, ISyncSoundProvider, ISaveRam, IStatable, IDriveLight, ISettable<Octoshock.Settings, Octoshock.SyncSettings>, IDebuggable, IRegionable, IInputPollable
{
2014-12-11 20:31:55 +00:00
public string SystemId { get { return "PSX"; } }
2015-09-22 00:02:49 +00:00
public static ControllerDefinition CreateControllerDefinition(SyncSettings syncSettings)
{
2015-09-22 00:02:49 +00:00
ControllerDefinition definition = new ControllerDefinition();
definition.Name = "PSX DualShock Controller"; // <-- for compatibility
//ControllerDefinition.Name = "PSX FrontIO"; // TODO - later rename to this, I guess, so it's less misleading. don't want to wreck keybindings yet.
var cfg = syncSettings.FIOConfig.ToLogical();
for (int i = 0; i < cfg.NumPlayers; i++)
{
int pnum = i + 1;
2015-09-22 00:02:49 +00:00
definition.BoolButtons.AddRange(new[]
{
"P" + pnum + " Up",
"P" + pnum + " Down",
"P" + pnum + " Left",
"P" + pnum + " Right",
"P" + pnum + " Select",
"P" + pnum + " Start",
"P" + pnum + " Square",
"P" + pnum + " Triangle",
"P" + pnum + " Circle",
"P" + pnum + " Cross",
"P" + pnum + " L1",
"P" + pnum + " R1",
"P" + pnum + " L2",
"P" + pnum + " R2",
});
var type = cfg.DevicesPlayer[i];
if (type == OctoshockDll.ePeripheralType.DualShock || type == OctoshockDll.ePeripheralType.DualAnalog)
{
definition.BoolButtons.Add("P" + pnum + " L3");
definition.BoolButtons.Add("P" + pnum + " R3");
definition.BoolButtons.Add("P" + pnum + " MODE");
2015-09-22 00:02:49 +00:00
definition.FloatControls.AddRange(new[]
{
"P" + pnum + " LStick X",
"P" + pnum + " LStick Y",
"P" + pnum + " RStick X",
"P" + pnum + " RStick Y"
});
2015-09-22 00:02:49 +00:00
definition.FloatRanges.Add(new[] { 0.0f, 128.0f, 255.0f });
definition.FloatRanges.Add(new[] { 255.0f, 128.0f, 0.0f });
definition.FloatRanges.Add(new[] { 0.0f, 128.0f, 255.0f });
definition.FloatRanges.Add(new[] { 255.0f, 128.0f, 0.0f });
}
}
2015-09-22 00:02:49 +00:00
definition.BoolButtons.AddRange(new[]
{
"Open",
"Close",
"Reset"
});
2015-09-22 00:02:49 +00:00
definition.FloatControls.Add("Disc Select");
2015-09-22 00:02:49 +00:00
definition.FloatRanges.Add(
//new[] {-1f,-1f,-1f} //this is carefully chosen so that we end up with a -1 disc by default (indicating that it's never been set)
//hmm.. I don't see why this wouldn't work
new[] { 0f, 1f, 1f }
);
2015-09-22 00:02:49 +00:00
return definition;
}
private void SetControllerButtons()
{
2015-09-22 00:02:49 +00:00
ControllerDefinition = CreateControllerDefinition(_SyncSettings);
}
public string BoardName { get { return null; } }
private int[] frameBuffer = new int[0];
private Random rand = new Random();
public CoreComm CoreComm { get; private set; }
//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;
2014-12-10 19:41:13 +00:00
IntPtr psx;
TraceBuffer tracer = new TraceBuffer();
bool disposed = false;
public void Dispose()
{
if (disposed) return;
disposed = true;
//discs arent bound to shock core instances, but they may be mounted. kill the core instance first to effectively dereference the disc
2014-12-10 19:41:13 +00:00
OctoshockDll.shock_Destroy(psx);
psx = IntPtr.Zero;
//destroy all discs we're managing (and the unmanaged octoshock resources)
foreach (var di in discInterfaces)
{
di.Disc.Dispose();
di.Dispose();
}
discInterfaces.Clear();
}
2014-12-11 08:30:37 +00:00
/// <summary>
/// Wraps the ShockDiscRef returned from the DLL and acts as a bridge between it and a DiscSystem disc
/// </summary>
2014-12-10 19:41:13 +00:00
class DiscInterface : IDisposable
{
2014-12-16 10:47:50 +00:00
public DiscInterface(DiscSystem.Disc disc, Action<DiscInterface> cbActivity)
2014-12-10 19:41:13 +00:00
{
this.Disc = disc;
cbReadTOC = ShockDisc_ReadTOC;
cbReadLBA = ShockDisc_ReadLBA2448;
2014-12-10 23:48:47 +00:00
this.cbActivity = cbActivity;
2015-07-08 03:29:11 +00:00
OctoshockDll.shock_CreateDisc(out OctoshockHandle, IntPtr.Zero, disc.Session1.LeadoutLBA, cbReadTOC, cbReadLBA, true);
2014-12-10 19:41:13 +00:00
}
2014-12-10 19:41:13 +00:00
OctoshockDll.ShockDisc_ReadTOC cbReadTOC;
OctoshockDll.ShockDisc_ReadLBA cbReadLBA;
2014-12-16 10:47:50 +00:00
Action<DiscInterface> cbActivity;
2014-12-10 19:41:13 +00:00
public DiscSystem.Disc Disc;
public IntPtr OctoshockHandle;
2014-12-10 19:41:13 +00:00
public void Dispose()
{
OctoshockDll.shock_DestroyDisc(OctoshockHandle);
OctoshockHandle = IntPtr.Zero;
}
2014-12-10 19:41:13 +00:00
int ShockDisc_ReadTOC(IntPtr opaque, OctoshockDll.ShockTOC* read_target, OctoshockDll.ShockTOCTrack* tracks101)
{
2015-07-08 04:12:06 +00:00
read_target->disc_type = (byte)Disc.TOC.Session1Format;
read_target->first_track = (byte)Disc.TOC.FirstRecordedTrackNumber; //i _think_ thats what is meant here
read_target->last_track = (byte)Disc.TOC.LastRecordedTrackNumber; //i _think_ thats what is meant here
2014-12-10 19:41:13 +00:00
tracks101[0].lba = tracks101[0].adr = tracks101[0].control = 0;
2014-12-10 19:41:13 +00:00
for (int i = 1; i < 100; i++)
{
2015-07-08 04:12:06 +00:00
var item = Disc.TOC.TOCItems[i];
2015-06-23 18:57:11 +00:00
tracks101[i].adr = (byte)(item.Exists ? 1 : 0);
2015-07-19 04:23:15 +00:00
tracks101[i].lba = (uint)item.LBA;
2014-12-10 19:41:13 +00:00
tracks101[i].control = (byte)item.Control;
}
2014-12-10 19:41:13 +00:00
////the lead-out track is to be synthesized
tracks101[read_target->last_track + 1].adr = 1;
tracks101[read_target->last_track + 1].control = 0;
2015-07-19 04:23:15 +00:00
tracks101[read_target->last_track + 1].lba = (uint)Disc.TOC.LeadoutLBA;
2014-12-10 19:41:13 +00:00
//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[2448];
2014-12-10 19:41:13 +00:00
int ShockDisc_ReadLBA2448(IntPtr opaque, int lba, void* dst)
{
2014-12-16 10:47:50 +00:00
cbActivity(this);
2014-12-10 23:48:47 +00:00
//todo - cache reader
DiscSystem.DiscSectorReader dsr = new DiscSystem.DiscSectorReader(Disc);
int readed = dsr.ReadLBA_2448(lba, SectorBuffer, 0);
if (readed == 2448)
{
Marshal.Copy(SectorBuffer, 0, new IntPtr(dst), 2448);
return OctoshockDll.SHOCK_OK;
}
else
return OctoshockDll.SHOCK_ERROR;
}
2014-12-10 19:41:13 +00:00
}
2015-07-13 01:08:30 +00:00
public List<DiscSystem.Disc> Discs;
2014-12-16 10:47:50 +00:00
List<DiscInterface> discInterfaces = new List<DiscInterface>();
DiscInterface currentDiscInterface;
2014-12-10 23:48:47 +00:00
public DisplayType Region { get { return SystemVidStandard == OctoshockDll.eVidStandard.PAL ? DisplayType.PAL : DisplayType.NTSC; } }
public OctoshockDll.eRegion SystemRegion { get; private set; }
public OctoshockDll.eVidStandard SystemVidStandard { get; private set; }
public System.Drawing.Size CurrentVideoSize { get; private set; }
public bool CurrentTrayOpen { get; private set; }
public int CurrentDiscIndexMounted { get; private set; }
public List<string> HackyDiscButtons = new List<string>();
2015-07-21 04:10:49 +00:00
public Octoshock(CoreComm comm, PSF psf, object settings, object syncSettings)
{
Load(comm, null, null, null, settings, syncSettings, psf);
OctoshockDll.shock_PowerOn(psx);
}
//note: its annoying that we have to have a disc before constructing this.
//might want to change that later. HOWEVER - we need to definitely have a region, at least
public Octoshock(CoreComm comm, List<DiscSystem.Disc> discs, List<string> discNames, byte[] exe, object settings, object syncSettings)
2015-07-21 04:10:49 +00:00
{
Load(comm, discs, discNames, exe, settings, syncSettings, null);
OctoshockDll.shock_PowerOn(psx);
}
void Load(CoreComm comm, List<DiscSystem.Disc> discs, List<string> discNames, byte[] exe, object settings, object syncSettings, PSF psf)
2014-12-10 19:41:13 +00:00
{
ServiceProvider = new BasicServiceProvider(this);
(ServiceProvider as BasicServiceProvider).Register<ITraceable>(tracer);
2014-12-10 19:41:13 +00:00
CoreComm = comm;
DriveLightEnabled = true;
_Settings = (Settings)settings ?? new Settings();
_SyncSettings = (SyncSettings)syncSettings ?? new SyncSettings();
2015-07-13 01:08:30 +00:00
Discs = discs;
2014-12-10 19:41:13 +00:00
Attach();
//assume this region for EXE and PSF, maybe not correct though
2014-12-10 19:41:13 +00:00
string firmwareRegion = "U";
SystemRegion = OctoshockDll.eRegion.NA;
2014-12-11 08:30:37 +00:00
2014-12-16 10:47:50 +00:00
if (discs != null)
{
2015-07-21 04:10:49 +00:00
HackyDiscButtons.AddRange(discNames);
foreach (var disc in discs)
2014-12-16 10:47:50 +00:00
{
var discInterface = new DiscInterface(disc,
(di) =>
{
//if current disc this delegate disc, activity is happening
if (di == currentDiscInterface)
DriveLightOn = true;
});
discInterfaces.Add(discInterface);
}
}
else
{
//assume its NA region for test programs, for now. could it be read out of the ps-exe header?
}
if (discInterfaces.Count != 0)
2014-12-11 08:30:37 +00:00
{
2014-12-16 10:47:50 +00:00
//determine region of one of the discs
2014-12-11 08:30:37 +00:00
OctoshockDll.ShockDiscInfo discInfo;
2014-12-16 10:47:50 +00:00
OctoshockDll.shock_AnalyzeDisc(discInterfaces[0].OctoshockHandle, out discInfo);
2014-12-11 08:30:37 +00:00
//try to acquire the appropriate firmware
if (discInfo.region == OctoshockDll.eRegion.EU) firmwareRegion = "E";
if (discInfo.region == OctoshockDll.eRegion.JP) firmwareRegion = "J";
SystemRegion = discInfo.region;
2014-12-11 08:30:37 +00:00
}
//see http://problemkaputt.de/psx-spx.htm
int CpuClock_n = 44100 * 768;
int CpuClock_d = 1;
int VidClock_n = CpuClock_n * 11;
int VidClock_d = CpuClock_d * 7;
if (SystemRegion == OctoshockDll.eRegion.EU)
{
CoreComm.VsyncNum = VidClock_n;
CoreComm.VsyncDen = VidClock_d * 314 * 3406;
SystemVidStandard = OctoshockDll.eVidStandard.PAL;
}
else
{
CoreComm.VsyncNum = VidClock_n;
CoreComm.VsyncDen = VidClock_d * 263 * 3413;
SystemVidStandard = OctoshockDll.eVidStandard.NTSC;
}
//TODO - known bad firmwares are a no-go. we should refuse to boot them. (thats the mednafen policy)
byte[] firmware = comm.CoreFileProvider.GetFirmware("PSX", firmwareRegion, true, "A PSX `" + firmwareRegion + "` region bios file is required");
2014-12-10 19:41:13 +00:00
//create the instance
fixed (byte* pFirmware = firmware)
OctoshockDll.shock_Create(out psx, SystemRegion, pFirmware);
2014-12-11 01:56:21 +00:00
SetMemoryDomains();
2016-01-22 03:47:16 +00:00
InitMemCallbacks();
//set a default framebuffer based on the first frame of emulation, to cut down on flickering or whatever
//this is probably quixotic, but we have to pick something
{
BufferWidth = 280;
BufferHeight = 240;
if (SystemVidStandard == OctoshockDll.eVidStandard.PAL)
{
BufferWidth = 280;
BufferHeight = 288;
}
CurrentVideoSize = new System.Drawing.Size(BufferWidth, BufferHeight);
var ri = Octoshock.CalculateResolution(SystemVidStandard, _Settings, BufferWidth, BufferHeight);
BufferWidth = VirtualWidth = ri.Resolution.Width;
BufferHeight = VirtualHeight = ri.Resolution.Height;
//VideoProvider_Padding = new System.Drawing.Size(50,50);
frameBuffer = new int[BufferWidth * BufferHeight];
}
2014-12-16 10:47:50 +00:00
if (discInterfaces.Count != 0)
2014-12-11 08:30:37 +00:00
{
//start with first disc inserted and tray closed. it's a sensible default.
//it will be possible for the user to specify a different initial configuration, but this will inform the UI
CurrentTrayOpen = false;
CurrentDiscIndexMounted = 1;
2014-12-11 08:30:37 +00:00
}
2015-07-21 04:10:49 +00:00
else if (psf == null)
2014-12-11 08:30:37 +00:00
{
//must be an exe
fixed (byte* pExeBuffer = exe)
2015-07-21 04:10:49 +00:00
OctoshockDll.shock_MountEXE(psx, pExeBuffer, exe.Length, false);
//start with no disc inserted and tray closed
CurrentTrayOpen = false;
CurrentDiscIndexMounted = 0;
OctoshockDll.shock_CloseTray(psx);
}
else
{
//must be a psf
if (psf.LibData != null)
2015-07-21 04:10:49 +00:00
fixed (byte* pBuf = psf.LibData)
OctoshockDll.shock_MountEXE(psx, pBuf, psf.LibData.Length, true);
fixed (byte* pBuf = psf.Data)
OctoshockDll.shock_MountEXE(psx, pBuf, psf.Data.Length, false);
//start with no disc inserted and tray closed
CurrentTrayOpen = false;
CurrentDiscIndexMounted = 0;
OctoshockDll.shock_CloseTray(psx);
2014-12-11 08:30:37 +00:00
}
2014-12-17 04:01:26 +00:00
2015-02-01 07:45:41 +00:00
//setup the controller based on sync settings
SetControllerButtons();
var fioCfg = _SyncSettings.FIOConfig;
if(fioCfg.Devices8[0] != OctoshockDll.ePeripheralType.None)
OctoshockDll.shock_Peripheral_Connect(psx, 0x01, fioCfg.Devices8[0]);
if (fioCfg.Devices8[4] != OctoshockDll.ePeripheralType.None)
OctoshockDll.shock_Peripheral_Connect(psx, 0x02, fioCfg.Devices8[4]);
var memcardTransaction = new OctoshockDll.ShockMemcardTransaction()
{
transaction = OctoshockDll.eShockMemcardTransaction.Connect
};
if (fioCfg.Memcards[0]) OctoshockDll.shock_Peripheral_MemcardTransact(psx, 0x01, ref memcardTransaction);
if (fioCfg.Memcards[1]) OctoshockDll.shock_Peripheral_MemcardTransact(psx, 0x02, ref memcardTransaction);
2014-12-12 11:07:47 +00:00
//do this after framebuffers and peripherals and whatever crap are setup. kind of lame, but thats how it is for now
StudySaveBufferSize();
}
2014-12-10 19:41:13 +00:00
public IEmulatorServiceProvider ServiceProvider { get; private set; }
public bool DriveLightEnabled { get; private set; }
public bool DriveLightOn { get; private set; }
2014-12-10 19:41:13 +00:00
void Attach()
{
2014-12-10 19:41:13 +00:00
//attach this core as the current
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
}
2014-12-10 19:41:13 +00:00
static Octoshock()
{
}
2014-12-10 19:41:13 +00:00
public void ResetCounters()
{
Frame = 0;
2015-01-06 03:40:01 +00:00
LagCount = 0;
IsLagFrame = false;
}
void SetInput()
{
var fioCfg = _SyncSettings.FIOConfig.ToLogical();
int portNum = 0x01;
foreach (int slot in new[] { 0, 4 })
{
//no input to set
if (fioCfg.Devices8[slot] == OctoshockDll.ePeripheralType.None)
continue;
uint buttons = 0;
string pstring = "P" + fioCfg.PlayerAssignments[slot] + " ";
if (Controller[pstring + "Select"]) buttons |= 1;
if (Controller[pstring + "Start"]) buttons |= 8;
if (Controller[pstring + "Up"]) buttons |= 16;
if (Controller[pstring + "Right"]) buttons |= 32;
if (Controller[pstring + "Down"]) buttons |= 64;
if (Controller[pstring + "Left"]) buttons |= 128;
if (Controller[pstring + "L2"]) buttons |= 256;
if (Controller[pstring + "R2"]) buttons |= 512;
if (Controller[pstring + "L1"]) buttons |= 1024;
if (Controller[pstring + "R1"]) buttons |= 2048;
if (Controller[pstring + "Triangle"]) buttons |= 4096;
if (Controller[pstring + "Circle"]) buttons |= 8192;
if (Controller[pstring + "Cross"]) buttons |= 16384;
if (Controller[pstring + "Square"]) buttons |= 32768;
byte left_x = 0, left_y = 0, right_x = 0, right_y = 0;
if (fioCfg.Devices8[slot] == OctoshockDll.ePeripheralType.DualShock || fioCfg.Devices8[slot] == OctoshockDll.ePeripheralType.DualAnalog)
{
if (Controller[pstring + "L3"]) buttons |= 2;
if (Controller[pstring + "R3"]) buttons |= 4;
if (Controller[pstring + "MODE"]) buttons |= 65536;
left_x = (byte)Controller.GetFloat(pstring + "LStick X");
left_y = (byte)Controller.GetFloat(pstring + "LStick Y");
right_x = (byte)Controller.GetFloat(pstring + "RStick X");
right_y = (byte)Controller.GetFloat(pstring + "RStick Y");
}
OctoshockDll.shock_Peripheral_SetPadInput(psx, portNum, buttons, left_x, left_y, right_x, right_y);
portNum <<= 1;
}
}
public class ResolutionInfo
{
public System.Drawing.Size Resolution, Padding;
public System.Drawing.Size Total { get { return System.Drawing.Size.Add(Resolution, Padding); } }
}
/// <summary>
/// Calculates what the output resolution would be for the given input resolution and settings
/// </summary>
public static ResolutionInfo CalculateResolution(OctoshockDll.eVidStandard standard, Settings settings, int w, int h)
{
ResolutionInfo ret = new ResolutionInfo();
2015-08-02 16:35:19 +00:00
//some of this logic is duplicated in the c++ side, be sure to check there
//TODO - scanline control + framebuffer mode is majorly broken
2015-08-02 16:35:19 +00:00
int virtual_width = 800;
if (settings.HorizontalClipping == eHorizontalClipping.Basic) virtual_width = 768;
if (settings.HorizontalClipping == eHorizontalClipping.Framebuffer) virtual_width = 736;
int scanline_start = standard == OctoshockDll.eVidStandard.NTSC ? settings.ScanlineStart_NTSC : settings.ScanlineStart_PAL;
int scanline_end = standard == OctoshockDll.eVidStandard.NTSC ? settings.ScanlineEnd_NTSC : settings.ScanlineEnd_PAL;
int scanline_num = scanline_end - scanline_start + 1;
//int scanline_num = h; // I wanted to do this, but our logic for mednafen modes here is based on un-doubled resolution. i could do a hack to divide it by 2 though
int real_scanline_num = standard == OctoshockDll.eVidStandard.NTSC ? 240 : 288;
int VirtualWidth=-1, VirtualHeight=-1;
switch (settings.ResolutionMode)
{
case eResolutionMode.Mednafen:
//mednafen uses 320xScanlines as the 1x size
//it does change the 1x width when doing basic clipping.
//and it does easily change the height when doing scanline removal.
//now, our framebuffer cropping mode is more complex...
VirtualWidth = (standard == OctoshockDll.eVidStandard.NTSC) ? 320 : 363;
VirtualHeight = scanline_num;
2015-08-02 16:35:19 +00:00
if (settings.HorizontalClipping == eHorizontalClipping.Basic)
VirtualWidth = (standard == OctoshockDll.eVidStandard.NTSC) ? 302 : 384;
2015-08-02 16:35:19 +00:00
if (settings.HorizontalClipping == eHorizontalClipping.Framebuffer)
{
//mednafen typically sends us a framebuffer with overscan. 350x240 is a nominal example here. it's squished inward to 320x240 for correct PAR.
//ok: here we have a framebuffer without overscan. 320x240 nominal. So the VirtualWidth of what we got is off by a factor of 109.375%
//so a beginning approach would be this:
//VirtualWidth = (int)(VirtualWidth * 320.0f / 350);
//but that will shrink things which are already annoyingly shrunken.
//therefore, lets do that, but then scale the whole window by the same factor so the width becomes unscaled and now the height is scaled up!
//weird, huh?
VirtualHeight = (int)(VirtualHeight * 350.0f / 320);
//now unfortunately we may have lost vertical pixels. common in the case of PAL (rendering 256 on a field of 288)
//therefore we'll be stretching way too much vertically here.
//lets add those pixels back with a new hack
if (standard == OctoshockDll.eVidStandard.PAL)
{
if (h > 288) ret.Padding = new System.Drawing.Size(0, 576 - h);
else ret.Padding = new System.Drawing.Size(0, 288 - h);
}
else
{
if (h > 288) ret.Padding = new System.Drawing.Size(0, 480 - h);
else ret.Padding = new System.Drawing.Size(0, 240 - h);
}
}
break;
//384 / 288 = 1.3333333333333333333333333333333
case eResolutionMode.TweakedMednafen:
if (standard == OctoshockDll.eVidStandard.NTSC)
{
//dont make this 430, it's already been turned into 400 from 368+30 and then some fudge factor
VirtualWidth = 400;
VirtualHeight = (int)(scanline_num * 300.0f / 240);
if (settings.HorizontalClipping == eHorizontalClipping.Basic)
VirtualWidth = 378;
}
else
{
//this is a bit tricky. we know we want 400 for the virtualwidth.
VirtualWidth = 400;
if (settings.HorizontalClipping == eHorizontalClipping.Basic)
VirtualWidth = 378;
//I'll be honest, I was just guessing here mostly
//I need the AR to basically work out to be 363/288 (thats what it was in mednafen mode) so...
VirtualHeight = (int)(scanline_num * (400.0f/363*288) / 288);
}
if (settings.HorizontalClipping == eHorizontalClipping.Framebuffer)
{
//see discussion above
VirtualHeight = (int)(VirtualHeight * 350.0f / 320);
if (standard == OctoshockDll.eVidStandard.PAL)
{
if (h > 288) ret.Padding = new System.Drawing.Size(0, 576 - h);
else ret.Padding = new System.Drawing.Size(0, 288 - h);
}
else
{
if (h > 288) ret.Padding = new System.Drawing.Size(0, 480 - h);
else ret.Padding = new System.Drawing.Size(0, 240 - h);
}
}
break;
case eResolutionMode.PixelPro:
VirtualWidth = virtual_width;
VirtualHeight = scanline_num * 2;
break;
case eResolutionMode.Debug:
VirtualWidth = w;
VirtualHeight = h;
break;
}
ret.Resolution = new System.Drawing.Size(VirtualWidth, VirtualHeight);
return ret;
}
void PokeDisc()
{
if (CurrentDiscIndexMounted == 0)
{
currentDiscInterface = null;
OctoshockDll.shock_PokeDisc(psx, IntPtr.Zero);
}
else
{
currentDiscInterface = discInterfaces[CurrentDiscIndexMounted - 1];
OctoshockDll.shock_PokeDisc(psx, currentDiscInterface.OctoshockHandle);
}
}
void FrameAdvance_PrepDiscState()
{
//reminder: if this is the beginning of time, we can begin with the disc ejected or inserted.
//if tray open is requested, and valid, apply it
//in the first frame, go ahead and open it up so we have a chance to put a disc in it
if (Controller["Open"] && !CurrentTrayOpen || Frame == 0)
{
OctoshockDll.shock_OpenTray(psx);
CurrentTrayOpen = true;
}
//change the disc if needed, and valid
//also if frame is 0, we need to set a disc no matter what
int requestedDisc = (int)Controller.GetFloat("Disc Select");
if (requestedDisc != CurrentDiscIndexMounted && CurrentTrayOpen
|| Frame == 0
)
{
//dont replace default disc with the leave-default placeholder!
if (requestedDisc == -1)
{
}
else
{
CurrentDiscIndexMounted = requestedDisc;
}
2015-07-21 04:10:49 +00:00
if (CurrentDiscIndexMounted == 0 || discInterfaces.Count == 0)
{
currentDiscInterface = null;
OctoshockDll.shock_SetDisc(psx, IntPtr.Zero);
}
else
{
currentDiscInterface = discInterfaces[CurrentDiscIndexMounted - 1];
OctoshockDll.shock_SetDisc(psx, currentDiscInterface.OctoshockHandle);
}
}
2014-12-16 10:47:50 +00:00
//if tray close is requested, and valid, apply it.
if (Controller["Close"] && CurrentTrayOpen)
{
OctoshockDll.shock_CloseTray(psx);
CurrentTrayOpen = false;
}
//if frame is 0 and user has made no preference, close the tray
if (!Controller["Close"] && !Controller["Open"] && Frame == 0 && CurrentTrayOpen)
2014-12-16 10:47:50 +00:00
{
OctoshockDll.shock_CloseTray(psx);
CurrentTrayOpen = false;
2014-12-16 10:47:50 +00:00
}
}
2014-12-16 10:47:50 +00:00
public void FrameAdvance(bool render, bool rendersound)
{
FrameAdvance_PrepDiscState();
//clear drive light. itll get set to light up by sector-reading callbacks
//TODO - debounce this by a frame or so perhaps?
2015-08-02 16:35:19 +00:00
//TODO - actually, make this feedback from the core. there should be a register or status which effectively corresponds to whether it's reading.
DriveLightOn = false;
Frame++;
SetInput();
2014-12-16 10:47:50 +00:00
OctoshockDll.shock_SetLEC(psx, _SyncSettings.EnableLEC);
var ropts = new OctoshockDll.ShockRenderOptions()
{
scanline_start = SystemVidStandard == OctoshockDll.eVidStandard.NTSC ? _Settings.ScanlineStart_NTSC : _Settings.ScanlineStart_PAL,
scanline_end = SystemVidStandard == OctoshockDll.eVidStandard.NTSC ? _Settings.ScanlineEnd_NTSC : _Settings.ScanlineEnd_PAL,
};
2015-08-02 16:35:19 +00:00
if (_Settings.HorizontalClipping == eHorizontalClipping.Basic)
ropts.renderType = OctoshockDll.eShockRenderType.ClipOverscan;
if (_Settings.HorizontalClipping == eHorizontalClipping.Framebuffer)
ropts.renderType = OctoshockDll.eShockRenderType.Framebuffer;
if (_Settings.DeinterlaceMode == eDeinterlaceMode.Weave) ropts.deinterlaceMode = OctoshockDll.eShockDeinterlaceMode.Weave;
if (_Settings.DeinterlaceMode == eDeinterlaceMode.Bob) ropts.deinterlaceMode = OctoshockDll.eShockDeinterlaceMode.Bob;
if (_Settings.DeinterlaceMode == eDeinterlaceMode.BobOffset) ropts.deinterlaceMode = OctoshockDll.eShockDeinterlaceMode.BobOffset;
OctoshockDll.shock_SetRenderOptions(psx, ref ropts);
2014-12-16 10:47:50 +00:00
//prep tracer
if (tracer.Enabled)
OctoshockDll.shock_SetTraceCallback(psx, IntPtr.Zero, ShockTraceCallback);
else
OctoshockDll.shock_SetTraceCallback(psx, IntPtr.Zero, null);
2015-07-17 21:37:20 +00:00
//apply soft reset if needed
if (Controller["Reset"])
OctoshockDll.shock_SoftReset(psx);
//------------------------
2014-12-10 19:41:13 +00:00
OctoshockDll.shock_Step(psx, OctoshockDll.eShockStep.Frame);
//------------------------
2014-12-10 19:41:13 +00:00
2015-01-06 03:40:01 +00:00
//lag maintenance:
int pad1 = OctoshockDll.shock_Peripheral_PollActive(psx, 0x01, true);
int pad2 = OctoshockDll.shock_Peripheral_PollActive(psx, 0x02, true);
IsLagFrame = true;
if (pad1 == OctoshockDll.SHOCK_TRUE) IsLagFrame = false;
if (pad2 == OctoshockDll.SHOCK_TRUE) IsLagFrame = false;
if (IsLagFrame)
LagCount++;
//what happens to sound in this case?
if (render == false) return;
OctoshockDll.ShockFramebufferInfo fb = new OctoshockDll.ShockFramebufferInfo();
//run this once to get current logical size
OctoshockDll.shock_GetFramebuffer(psx, ref fb);
CurrentVideoSize = new System.Drawing.Size(fb.width, fb.height);
if (_Settings.ResolutionMode == eResolutionMode.PixelPro)
fb.flags = OctoshockDll.eShockFramebufferFlags.Normalize;
OctoshockDll.shock_GetFramebuffer(psx, ref fb);
2014-12-10 19:41:13 +00:00
int w = fb.width;
int h = fb.height;
2014-12-16 10:59:39 +00:00
BufferWidth = w;
BufferHeight = h;
var ri = CalculateResolution(this.SystemVidStandard, _Settings, w, h);
VirtualWidth = ri.Resolution.Width;
VirtualHeight = ri.Resolution.Height;
VideoProvider_Padding = ri.Padding;
int len = w * h;
if (frameBuffer.Length != len)
2014-12-10 19:41:13 +00:00
{
Console.WriteLine("PSX FB size: {0},{1}", fb.width, fb.height);
frameBuffer = new int[len];
2014-12-10 19:41:13 +00:00
}
2014-12-10 19:41:13 +00:00
fixed (int* ptr = frameBuffer)
{
fb.ptr = ptr;
OctoshockDll.shock_GetFramebuffer(psx, ref fb);
}
fixed (short* samples = sbuff)
{
2014-12-11 01:56:21 +00:00
sbuffcontains = OctoshockDll.shock_GetSamples(psx, null);
if (sbuffcontains * 2 > sbuff.Length) throw new InvalidOperationException("shock_GetSamples returned too many samples: " + sbuffcontains);
2014-12-11 01:56:21 +00:00
OctoshockDll.shock_GetSamples(psx, samples);
}
}
2015-02-01 07:45:41 +00:00
public ControllerDefinition ControllerDefinition { get; private set; }
public IController Controller { get; set; }
public int Frame { get; private set; }
public int LagCount { get; set; }
public bool IsLagFrame { get; set; }
public IInputCallbackSystem InputCallbacks
{
[FeatureNotImplemented]
get
{ throw new NotImplementedException(); }
}
[FeatureNotImplemented]
public bool DeterministicEmulation { get { return true; } }
public int[] GetVideoBuffer() { return frameBuffer; }
public int VirtualWidth { get; private set; }
public int VirtualHeight { get; private set; }
public int BufferWidth { get; private set; }
public int BufferHeight { get; private set; }
public int BackgroundColor { get { return 0; } }
public System.Drawing.Size VideoProvider_Padding { get; private set; }
2014-12-11 01:56:21 +00:00
#region Debugging
2016-01-22 03:47:16 +00:00
OctoshockDll.ShockCallback_Mem mem_cb;
void ShockMemCallback(uint address, OctoshockDll.eShockMemCb type, uint size, uint value)
{
switch (type)
{
case OctoshockDll.eShockMemCb.Read:
MemoryCallbacks.CallReads(address);
break;
case OctoshockDll.eShockMemCb.Write:
MemoryCallbacks.CallWrites(address);
break;
case OctoshockDll.eShockMemCb.Execute:
MemoryCallbacks.CallExecutes(address);
break;
}
}
void InitMemCallbacks()
{
mem_cb = new OctoshockDll.ShockCallback_Mem(ShockMemCallback);
_memoryCallbacks.ActiveChanged += RefreshMemCallbacks;
}
void RefreshMemCallbacks()
{
OctoshockDll.eShockMemCb mask = OctoshockDll.eShockMemCb.None;
if (MemoryCallbacks.HasReads) mask |= OctoshockDll.eShockMemCb.Read;
if (MemoryCallbacks.HasWrites) mask |= OctoshockDll.eShockMemCb.Write;
if (MemoryCallbacks.HasExecutes) mask |= OctoshockDll.eShockMemCb.Execute;
OctoshockDll.shock_SetMemCb(psx, mem_cb, mask);
}
2014-12-11 01:56:21 +00:00
unsafe void SetMemoryDomains()
{
var mmd = new List<MemoryDomain>();
IntPtr ptr;
int size;
OctoshockDll.shock_GetMemData(psx, out ptr, out size, OctoshockDll.eMemType.MainRAM);
mmd.Add(MemoryDomain.FromIntPtr("MainRAM", size, MemoryDomain.Endian.Little, ptr, true, 4));
2014-12-11 01:56:21 +00:00
OctoshockDll.shock_GetMemData(psx, out ptr, out size, OctoshockDll.eMemType.GPURAM);
mmd.Add(MemoryDomain.FromIntPtr("GPURAM", size, MemoryDomain.Endian.Little, ptr, true, 4));
2014-12-11 01:56:21 +00:00
OctoshockDll.shock_GetMemData(psx, out ptr, out size, OctoshockDll.eMemType.SPURAM);
mmd.Add(MemoryDomain.FromIntPtr("SPURAM", size, MemoryDomain.Endian.Little, ptr, true, 4));
2014-12-11 01:56:21 +00:00
OctoshockDll.shock_GetMemData(psx, out ptr, out size, OctoshockDll.eMemType.BiosROM);
mmd.Add(MemoryDomain.FromIntPtr("BiosROM", size, MemoryDomain.Endian.Little, ptr, true, 4));
2014-12-11 01:56:21 +00:00
OctoshockDll.shock_GetMemData(psx, out ptr, out size, OctoshockDll.eMemType.PIOMem);
mmd.Add(MemoryDomain.FromIntPtr("PIOMem", size, MemoryDomain.Endian.Little, ptr, true, 4));
2014-12-11 01:56:21 +00:00
2014-12-15 05:28:06 +00:00
OctoshockDll.shock_GetMemData(psx, out ptr, out size, OctoshockDll.eMemType.DCache);
mmd.Add(MemoryDomain.FromIntPtr("DCache", size, MemoryDomain.Endian.Little, ptr, true, 4));
2014-12-15 05:28:06 +00:00
MemoryDomains = new MemoryDomainList(mmd);
(ServiceProvider as BasicServiceProvider).Register<IMemoryDomains>(MemoryDomains);
2014-12-11 01:56:21 +00:00
}
private IMemoryDomains MemoryDomains;
2014-12-11 01:56:21 +00:00
#endregion
#region ISoundProvider
//private short[] sbuff = new short[1454 * 2]; //this is the most ive ever seen.. dont know why. two frames worth i guess
private short[] sbuff = new short[1611 * 2]; //need this for pal
private int sbuffcontains = 0;
public ISoundProvider SoundProvider { get { throw new InvalidOperationException(); } }
public ISyncSoundProvider SyncSoundProvider { get { return this; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
public void GetSamples(out short[] samples, out int nsamp)
{
samples = sbuff;
nsamp = sbuffcontains;
}
public void DiscardSamples()
{
sbuffcontains = 0;
}
#endregion
#region ISaveRam
public byte[] CloneSaveRam()
{
var cfg = _SyncSettings.FIOConfig.ToLogical();
int nMemcards = cfg.NumMemcards;
var buf = new byte[128 * 1024 * nMemcards];
for (int i = 0, idx = 0, addr=0x01; i < 2; i++, addr<<=1)
{
if (cfg.Memcards[i])
{
fixed (byte* pbuf = buf)
{
var transaction = new OctoshockDll.ShockMemcardTransaction();
transaction.buffer128k = pbuf + idx * 128 * 1024;
transaction.transaction = OctoshockDll.eShockMemcardTransaction.Read;
OctoshockDll.shock_Peripheral_MemcardTransact(psx, addr, ref transaction);
idx++;
}
}
}
return buf;
}
public void StoreSaveRam(byte[] data)
{
var cfg = _SyncSettings.FIOConfig.ToLogical();
for (int i = 0, idx = 0, addr = 0x01; i < 2; i++, addr <<= 1)
{
if (cfg.Memcards[i])
{
fixed (byte* pbuf = data)
{
var transaction = new OctoshockDll.ShockMemcardTransaction();
transaction.buffer128k = pbuf + idx * 128 * 1024;
transaction.transaction = OctoshockDll.eShockMemcardTransaction.Write;
OctoshockDll.shock_Peripheral_MemcardTransact(psx, addr, ref transaction);
idx++;
}
}
}
}
public bool SaveRamModified
{
get
{
var cfg = _SyncSettings.FIOConfig.ToLogical();
for (int i = 0, addr = 0x01; i < 2; i++, addr <<= 1)
{
if (cfg.Memcards[i])
{
var transaction = new OctoshockDll.ShockMemcardTransaction();
transaction.transaction = OctoshockDll.eShockMemcardTransaction.CheckDirty;
OctoshockDll.shock_Peripheral_MemcardTransact(psx, addr, ref transaction);
if (OctoshockDll.shock_Peripheral_MemcardTransact(psx, addr, ref transaction) == OctoshockDll.SHOCK_TRUE)
return true;
}
}
return false;
}
}
#endregion //ISaveRam
#region Savestates
//THIS IS STILL AWFUL
JsonSerializer ser = new JsonSerializer() { Formatting = Formatting.Indented };
class TextStateData
{
public int Frame;
public int LagCount;
public bool IsLagFrame;
public bool CurrentDiscEjected;
public int CurrentDiscIndexMounted;
}
public void SaveStateText(TextWriter writer)
{
var s = new TextState<TextStateData>();
s.Prepare();
var transaction = new OctoshockDll.ShockStateTransaction()
{
transaction = OctoshockDll.eShockStateTransaction.TextSave,
ff = s.GetFunctionPointersSave()
};
int result = OctoshockDll.shock_StateTransaction(psx, ref transaction);
if (result != OctoshockDll.SHOCK_OK)
throw new InvalidOperationException("eShockStateTransaction.TextSave returned error!");
s.ExtraData.IsLagFrame = IsLagFrame;
s.ExtraData.LagCount = LagCount;
s.ExtraData.Frame = Frame;
s.ExtraData.CurrentDiscEjected = CurrentTrayOpen;
s.ExtraData.CurrentDiscIndexMounted = CurrentDiscIndexMounted;
ser.Serialize(writer, s);
// TODO write extra copy of stuff we don't use (WHY?)
}
public void LoadStateText(TextReader reader)
{
var s = (TextState<TextStateData>)ser.Deserialize(reader, typeof(TextState<TextStateData>));
s.Prepare();
var transaction = new OctoshockDll.ShockStateTransaction()
{
transaction = OctoshockDll.eShockStateTransaction.TextLoad,
2014-12-12 11:07:47 +00:00
ff = s.GetFunctionPointersLoad()
};
int result = OctoshockDll.shock_StateTransaction(psx, ref transaction);
if (result != OctoshockDll.SHOCK_OK)
throw new InvalidOperationException("eShockStateTransaction.TextLoad returned error!");
IsLagFrame = s.ExtraData.IsLagFrame;
LagCount = s.ExtraData.LagCount;
Frame = s.ExtraData.Frame;
CurrentTrayOpen = s.ExtraData.CurrentDiscEjected;
CurrentDiscIndexMounted = s.ExtraData.CurrentDiscIndexMounted;
PokeDisc();
}
byte[] savebuff;
byte[] savebuff2;
void StudySaveBufferSize()
{
var transaction = new OctoshockDll.ShockStateTransaction();
transaction.transaction = OctoshockDll.eShockStateTransaction.BinarySize;
int size = OctoshockDll.shock_StateTransaction(psx, ref transaction);
savebuff = new byte[size];
savebuff2 = new byte[savebuff.Length + 4 + 4 + 4 + 1 + 1 + 4];
}
public void SaveStateBinary(BinaryWriter writer)
{
fixed (byte* psavebuff = savebuff)
{
var transaction = new OctoshockDll.ShockStateTransaction()
{
transaction = OctoshockDll.eShockStateTransaction.BinarySave,
buffer = psavebuff,
bufferLength = savebuff.Length
};
int result = OctoshockDll.shock_StateTransaction(psx, ref transaction);
if (result != OctoshockDll.SHOCK_OK)
throw new InvalidOperationException("eShockStateTransaction.BinarySave returned error!");
writer.Write(savebuff.Length);
writer.Write(savebuff);
// other variables
writer.Write(IsLagFrame);
writer.Write(LagCount);
writer.Write(Frame);
writer.Write(CurrentTrayOpen);
writer.Write(CurrentDiscIndexMounted);
}
}
public void LoadStateBinary(BinaryReader reader)
{
fixed (byte* psavebuff = savebuff)
{
var transaction = new OctoshockDll.ShockStateTransaction()
{
transaction = OctoshockDll.eShockStateTransaction.BinaryLoad,
buffer = psavebuff,
bufferLength = savebuff.Length
};
int length = reader.ReadInt32();
if (length != savebuff.Length)
throw new InvalidOperationException("Save buffer size mismatch!");
reader.Read(savebuff, 0, length);
int ret = OctoshockDll.shock_StateTransaction(psx, ref transaction);
if (ret != OctoshockDll.SHOCK_OK)
throw new InvalidOperationException("eShockStateTransaction.BinaryLoad returned error!");
// other variables
IsLagFrame = reader.ReadBoolean();
LagCount = reader.ReadInt32();
Frame = reader.ReadInt32();
CurrentTrayOpen = reader.ReadBoolean();
CurrentDiscIndexMounted = reader.ReadInt32();
PokeDisc();
}
}
public byte[] SaveStateBinary()
{
//this are objectionable shenanigans, but theyre required to get the extra info in the stream. we need a better approach.
var ms = new MemoryStream(savebuff2, true);
var bw = new BinaryWriter(ms);
SaveStateBinary(bw);
bw.Flush();
if (ms.Position != savebuff2.Length)
throw new InvalidOperationException();
ms.Close();
return savebuff2;
}
public bool BinarySaveStatesPreferred
{
get { return true; }
}
#endregion
#region Settings
Settings _Settings = new Settings();
SyncSettings _SyncSettings;
public enum eResolutionMode
{
PixelPro, Debug,
Mednafen, TweakedMednafen
}
public class SyncSettings
{
public SyncSettings Clone()
{
return JsonConvert.DeserializeObject<SyncSettings>(JsonConvert.SerializeObject(this));
}
public bool EnableLEC;
public SyncSettings()
{
//initialize with historical default settings
var user = new OctoshockFIOConfigUser();
user.Memcards[0] = user.Memcards[1] = true;
user.Multitaps[0] = user.Multitaps[0] = false;
user.Devices8[0] = OctoshockDll.ePeripheralType.DualShock;
user.Devices8[4] = OctoshockDll.ePeripheralType.DualShock;
FIOConfig = user;
}
public OctoshockFIOConfigUser FIOConfig;
}
2015-08-02 16:35:19 +00:00
public enum eHorizontalClipping
{
None,
Basic,
Framebuffer
}
public enum eDeinterlaceMode
{
Weave,
Bob,
BobOffset
}
public class Settings
{
[DisplayName("Resolution Mode")]
[Description("Stuff")]
[DefaultValue(eResolutionMode.PixelPro)]
public eResolutionMode ResolutionMode { get; set; }
2015-08-02 16:35:19 +00:00
[DisplayName("Horizontal Clipping")]
[DefaultValue(eHorizontalClipping.None)]
public eHorizontalClipping HorizontalClipping { get; set; }
[DisplayName("ScanlineStart_NTSC")]
[DefaultValue(0)]
public int ScanlineStart_NTSC { get; set; }
[DisplayName("ScanlineEnd_NTSC")]
[DefaultValue(239)]
public int ScanlineEnd_NTSC { get; set; }
[DisplayName("ScanlineStart_PAL")]
[DefaultValue(0)]
public int ScanlineStart_PAL { get; set; }
[DisplayName("ScanlineEnd_PAL")]
[DefaultValue(287)]
public int ScanlineEnd_PAL { get; set; }
[DisplayName("DeinterlaceMode")]
[DefaultValue(eDeinterlaceMode.Weave)]
public eDeinterlaceMode DeinterlaceMode { get; set; }
public void Validate()
{
if (ScanlineStart_NTSC < 0) ScanlineStart_NTSC = 0;
if (ScanlineStart_PAL < 0) ScanlineStart_PAL = 0;
if (ScanlineEnd_NTSC > 239) ScanlineEnd_NTSC = 239;
if (ScanlineEnd_PAL > 287) ScanlineEnd_PAL = 287;
//make sure theyre not in the wrong order
if (ScanlineEnd_NTSC < ScanlineStart_NTSC)
{
int temp = ScanlineEnd_NTSC;
ScanlineEnd_NTSC = ScanlineStart_NTSC;
ScanlineStart_NTSC = temp;
}
if (ScanlineEnd_PAL < ScanlineStart_PAL)
{
int temp = ScanlineEnd_PAL;
ScanlineEnd_PAL = ScanlineStart_PAL;
ScanlineStart_PAL = temp;
}
}
public Settings()
{
SettingsUtil.SetDefaultValues(this);
}
public Settings Clone()
{
return (Settings)MemberwiseClone();
}
}
public Settings GetSettings()
{
return _Settings.Clone();
}
public SyncSettings GetSyncSettings()
{
return _SyncSettings.Clone();
}
public bool PutSettings(Settings o)
{
_Settings.Validate();
_Settings = o;
//TODO - store settings into core? or we can just keep doing it before frameadvance
return false;
}
public bool PutSyncSettings(SyncSettings o)
{
//currently LEC and pad settings changes both require reboot
bool reboot = true;
//we could do it this way roughly if we need to
//if(JsonConvert.SerializeObject(o.FIOConfig) != JsonConvert.SerializeObject(_SyncSettings.FIOConfig)
_SyncSettings = o;
return reboot;
}
#endregion
2014-12-15 05:28:06 +00:00
#region IDebuggable
// TODO: don't cast to int, and are any of these not 32 bit?
2014-12-20 13:16:15 +00:00
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
2014-12-15 05:28:06 +00:00
{
2014-12-20 13:16:15 +00:00
Dictionary<string, RegisterValue> ret = new Dictionary<string, RegisterValue>();
2014-12-15 05:28:06 +00:00
var regs = new OctoshockDll.ShockRegisters_CPU();
OctoshockDll.shock_GetRegisters_CPU(psx, ref regs);
ret["r1"] = (int)regs.GPR[1]; ret["r2"] = (int)regs.GPR[2]; ret["r3"] = (int)regs.GPR[3];
ret["r4"] = (int)regs.GPR[4]; ret["r5"] = (int)regs.GPR[5]; ret["r6"] = (int)regs.GPR[6]; ret["r7"] = (int)regs.GPR[7];
ret["r8"] = (int)regs.GPR[8]; ret["r9"] = (int)regs.GPR[9]; ret["r10"] = (int)regs.GPR[10]; ret["r11"] = (int)regs.GPR[11];
ret["r12"] = (int)regs.GPR[12]; ret["r13"] = (int)regs.GPR[13]; ret["r14"] = (int)regs.GPR[14]; ret["r15"] = (int)regs.GPR[15];
ret["r16"] = (int)regs.GPR[16]; ret["r17"] = (int)regs.GPR[17]; ret["r18"] = (int)regs.GPR[18]; ret["r19"] = (int)regs.GPR[19];
ret["r20"] = (int)regs.GPR[20]; ret["r21"] = (int)regs.GPR[21]; ret["r22"] = (int)regs.GPR[22]; ret["r23"] = (int)regs.GPR[23];
ret["r24"] = (int)regs.GPR[24]; ret["r25"] = (int)regs.GPR[25]; ret["r26"] = (int)regs.GPR[26]; ret["r27"] = (int)regs.GPR[27];
ret["r28"] = (int)regs.GPR[28]; ret["r29"] = (int)regs.GPR[29]; ret["r30"] = (int)regs.GPR[30]; ret["r31"] = (int)regs.GPR[31];
ret["at"] = (int)regs.GPR[1];
ret["v0"] = (int)regs.GPR[2]; ret["v1"] = (int)regs.GPR[3];
ret["a0"] = (int)regs.GPR[4]; ret["a1"] = (int)regs.GPR[5]; ret["a2"] = (int)regs.GPR[6]; ret["a3"] = (int)regs.GPR[7];
ret["t0"] = (int)regs.GPR[8]; ret["t1"] = (int)regs.GPR[9]; ret["t2"] = (int)regs.GPR[10]; ret["t3"] = (int)regs.GPR[11];
ret["t4"] = (int)regs.GPR[12]; ret["t5"] = (int)regs.GPR[13]; ret["t6"] = (int)regs.GPR[14]; ret["t7"] = (int)regs.GPR[15];
ret["s0"] = (int)regs.GPR[16]; ret["s1"] = (int)regs.GPR[17]; ret["s2"] = (int)regs.GPR[18]; ret["s3"] = (int)regs.GPR[19];
ret["s4"] = (int)regs.GPR[20]; ret["s5"] = (int)regs.GPR[21]; ret["s6"] = (int)regs.GPR[22]; ret["s7"] = (int)regs.GPR[23];
ret["t8"] = (int)regs.GPR[24]; ret["t9"] = (int)regs.GPR[25];
ret["k0"] = (int)regs.GPR[26]; ret["k1"] = (int)regs.GPR[27];
ret["gp"] = (int)regs.GPR[28];
ret["sp"] = (int)regs.GPR[29];
ret["fp"] = (int)regs.GPR[30];
ret["ra"] = (int)regs.GPR[31];
2014-12-18 08:21:48 +00:00
ret["pc"] = (int)regs.PC;
ret["lo"] = (int)regs.LO;
ret["hi"] = (int)regs.HI;
2014-12-18 11:30:06 +00:00
ret["sr"] = (int)regs.SR;
ret["cause"] = (int)regs.CAUSE;
ret["epc"] = (int)regs.EPC;
2014-12-18 08:21:48 +00:00
2014-12-15 05:28:06 +00:00
return ret;
}
static Dictionary<string, int> CpuRegisterIndices = new Dictionary<string, int>() {
{"r1",1},{"r2",2},{"r3",3},{"r4",4},{"r5",5},{"r6",6},{"r7",7},
{"r8",8},{"r9",9},{"r10",10},{"r11",11},{"r12",12},{"r13",13},{"r14",14},{"r15",15},
{"r16",16},{"r17",17},{"r18",18},{"r19",19},{"r20",20},{"r21",21},{"r22",22},{"r23",23},
{"r24",24},{"r25",25},{"r26",26},{"r27",27},{"r28",28},{"r29",29},{"r30",30},{"r31",31},
{"at",1},{"v0",2},{"v1",3},
{"a0",4},{"a1",5},{"a2",6},{"a3",7},
{"t0",8},{"t1",9},{"t2",10},{"t3",11},
{"t4",12},{"t5",13},{"t6",14},{"t7",15},
{"s0",16},{"s1",17},{"s2",18},{"s3",19},
{"s4",20},{"s5",21},{"s6",22},{"s7",23},
{"t8",24},{"t9",25},
{"k0",26},{"k1",27},
2014-12-18 08:21:48 +00:00
{"gp",28},{"sp",29},{"fp",30},{"ra",31},
{"pc",32},
//33 - PC_NEXT
//34 - IN_BD_SLOT
{"lo",35},
{"hi",36},
{"sr",37},
{"cause",38},
{"epc",39},
2014-12-15 05:28:06 +00:00
};
public void SetCpuRegister(string register, int value)
{
int index = CpuRegisterIndices[register];
OctoshockDll.shock_SetRegister_CPU(psx, index, (uint)value);
}
public ITraceable Tracer { get { return tracer; } }
2016-01-22 03:47:16 +00:00
public void ShockTraceCallback(IntPtr opaque, uint PC, uint inst, string dis)
{
Tracer.Put(dis);
}
2014-12-15 05:28:06 +00:00
2016-01-22 03:47:16 +00:00
private readonly MemoryCallbackSystem _memoryCallbacks = new MemoryCallbackSystem();
public IMemoryCallbackSystem MemoryCallbacks { get { return _memoryCallbacks; } }
2014-12-15 05:28:06 +00:00
public bool CanStep(StepType type) { return false; }
2014-12-15 05:28:06 +00:00
[FeatureNotImplemented]
public void Step(StepType type) { throw new NotImplementedException(); }
2014-12-15 05:28:06 +00:00
#endregion //IDebuggable
}
}