diff --git a/BizHawk.Emulation/BizHawk.Emulation.csproj b/BizHawk.Emulation/BizHawk.Emulation.csproj
index aa9d8804a6..18c4dacae0 100644
--- a/BizHawk.Emulation/BizHawk.Emulation.csproj
+++ b/BizHawk.Emulation/BizHawk.Emulation.csproj
@@ -112,8 +112,9 @@
+
-
+
@@ -222,6 +223,7 @@
+
diff --git a/BizHawk.Emulation/CPUs/HuC6280/HuC6280.cs b/BizHawk.Emulation/CPUs/HuC6280/HuC6280.cs
index b88012e801..c36581232e 100644
--- a/BizHawk.Emulation/CPUs/HuC6280/HuC6280.cs
+++ b/BizHawk.Emulation/CPUs/HuC6280/HuC6280.cs
@@ -47,7 +47,7 @@ namespace BizHawk.Emulation.CPUs.H6280
public bool TimerAssert;
public byte IRQControlByte, IRQNextControlByte;
- public int TotalExecutedCycles;
+ public long TotalExecutedCycles;
public int PendingCycles;
public bool LowSpeed;
@@ -124,7 +124,7 @@ namespace BizHawk.Emulation.CPUs.H6280
else if (args[0] == "IRQNextControlByte")
IRQNextControlByte = byte.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "ExecutedCycles")
- TotalExecutedCycles = int.Parse(args[1]);
+ TotalExecutedCycles = long.Parse(args[1]);
else if (args[0] == "PendingCycles")
PendingCycles = int.Parse(args[1]);
else if (args[0] == "LowSpeed")
@@ -194,7 +194,7 @@ namespace BizHawk.Emulation.CPUs.H6280
TimerAssert = reader.ReadBoolean();
IRQControlByte = reader.ReadByte();
IRQNextControlByte = reader.ReadByte();
- TotalExecutedCycles = reader.ReadInt32();
+ TotalExecutedCycles = reader.ReadInt64();
PendingCycles = reader.ReadInt32();
LowSpeed = reader.ReadBoolean();
diff --git a/BizHawk.Emulation/Consoles/PC Engine/Input.cs b/BizHawk.Emulation/Consoles/PC Engine/Input.cs
index 36b20cf896..ce74ab10c2 100644
--- a/BizHawk.Emulation/Consoles/PC Engine/Input.cs
+++ b/BizHawk.Emulation/Consoles/PC Engine/Input.cs
@@ -63,8 +63,8 @@
if (Region == "Japan") value |= 0x40;
- /*if (Type != NecSystemType.TurboCD)
- value |= 0x80;*/
+ if (Type != NecSystemType.TurboCD && BramEnabled == false)
+ value |= 0x80;
return value;
}
diff --git a/BizHawk.Emulation/Consoles/PC Engine/MemoryMap.TurboCD.cs b/BizHawk.Emulation/Consoles/PC Engine/MemoryMap.TurboCD.cs
new file mode 100644
index 0000000000..1af3e5f154
--- /dev/null
+++ b/BizHawk.Emulation/Consoles/PC Engine/MemoryMap.TurboCD.cs
@@ -0,0 +1,83 @@
+namespace BizHawk.Emulation.Consoles.TurboGrafx
+{
+ public partial class PCEngine
+ {
+ private byte ReadMemoryCD(int addr)
+ {
+ if (addr < 0xD0000) // read ROM
+ return RomData[addr % RomLength];
+
+ if (addr >= 0x1F0000 && addr < 0x1F8000) // read RAM
+ return Ram[addr & 0x1FFF];
+
+ if (addr >= 0x100000 && addr < 0x110000) // read CD RAM
+ return CDRam[addr & 0xFFFF];
+
+ if (addr >= 0xD0000 && addr < 0x100000 && SuperRam != null) // Super SysCard RAM
+ return SuperRam[addr - 0xD0000];
+
+ if (addr >= 0x1FE000) // hardware page.
+ {
+ if (addr < 0x1FE400) return VDC1.ReadVDC(addr);
+ if (addr < 0x1FE800) { Cpu.PendingCycles--; return VCE.ReadVCE(addr); }
+ if (addr < 0x1FEC00) return IOBuffer;
+ if (addr < 0x1FF000) { IOBuffer = (byte)(Cpu.ReadTimerValue() | (IOBuffer & 0x80)); return IOBuffer; }
+ if (addr >= 0x1FF000 &&
+ addr < 0x1FF400) { IOBuffer = ReadInput(); return IOBuffer; }
+ if ((addr & ~1) == 0x1FF400) return IOBuffer;
+ if (addr == 0x1FF402) { IOBuffer = (byte)(Cpu.IRQControlByte | (IOBuffer & 0xF8)); return IOBuffer; }
+ if (addr == 0x1FF403) { IOBuffer = (byte)(Cpu.ReadIrqStatus() | (IOBuffer & 0xF8)); return IOBuffer; }
+ if (addr >= 0x1FF800) return ReadCD(addr);
+ }
+
+ if (addr >= 0x1EE000 && addr <= 0x1EE7FF) // BRAM
+ {
+ if (BramEnabled && BramLocked == false)
+ return BRAM[addr & 0x7FF];
+ return 0xFF;
+ }
+
+ Log.Error("MEM", "UNHANDLED READ: {0:X6}", addr);
+ return 0xFF;
+ }
+
+ private void WriteMemoryCD(int addr, byte value)
+ {
+ if (addr >= 0x1F0000 && addr < 0x1F8000) // write RAM.
+ Ram[addr & 0x1FFF] = value;
+
+ else if (addr >= 0x100000 && addr < 0x110000) // write CD-RAM
+ CDRam[addr & 0xFFFF] = value;
+
+ else if (addr >= 0xD0000 && addr < 0x100000 && SuperRam != null) // Super SysCard RAM
+ SuperRam[addr - 0xD0000] = value;
+
+ else if (addr >= 0x1FE000) // hardware page.
+ {
+ if (addr < 0x1FE400) VDC1.WriteVDC(addr, value);
+ else if (addr < 0x1FE800) { Cpu.PendingCycles--; VCE.WriteVCE(addr, value); }
+ else if (addr < 0x1FEC00) { IOBuffer = value; PSG.WritePSG((byte)addr, value, Cpu.TotalExecutedCycles); }
+ else if (addr == 0x1FEC00) { IOBuffer = value; Cpu.WriteTimer(value); }
+ else if (addr == 0x1FEC01) { IOBuffer = value; Cpu.WriteTimerEnable(value); }
+ else if (addr >= 0x1FF000 &&
+ addr < 0x1FF400) { IOBuffer = value; WriteInput(value); }
+ else if (addr == 0x1FF402) { IOBuffer = value; Cpu.WriteIrqControl(value); }
+ else if (addr == 0x1FF403) { IOBuffer = value; Cpu.WriteIrqStatus(); }
+ else if (addr >= 0x1FF800) { WriteCD(addr, value); }
+ else Log.Error("MEM", "unhandled hardware write [{0:X6}] : {1:X2}", addr, value);
+ }
+
+ else if (addr >= 0x1EE000 && addr <= 0x1EE7FF) // BRAM
+ {
+ if (BramEnabled && BramLocked == false)
+ {
+ BRAM[addr & 0x7FF] = value;
+ SaveRamModified = true;
+ }
+ }
+
+ else
+ Log.Error("MEM", "UNHANDLED WRITE: {0:X6}:{1:X2}", addr, value);
+ }
+ }
+}
\ No newline at end of file
diff --git a/BizHawk.Emulation/Consoles/PC Engine/MemoryMap.cs b/BizHawk.Emulation/Consoles/PC Engine/MemoryMap.cs
index 397962ccce..ad3ff1adef 100644
--- a/BizHawk.Emulation/Consoles/PC Engine/MemoryMap.cs
+++ b/BizHawk.Emulation/Consoles/PC Engine/MemoryMap.cs
@@ -29,11 +29,7 @@
if (addr >= 0x1EE000 && addr <= 0x1EE7FF) // BRAM
{
if (BramEnabled && BramLocked == false)
- {
- System.Console.WriteLine("READ BRAM[{0}] ; ret {1:X2}", addr & 0x7FF, BRAM[addr & 0x7FF]);
return BRAM[addr & 0x7FF];
- }
- System.Console.WriteLine("attemped BRAM read while locked");
return 0xFF;
}
@@ -44,11 +40,7 @@
private void WriteMemory(int addr, byte value)
{
if (addr >= 0x1F0000 && addr < 0x1F8000) // write RAM.
- {
- //if (Cpu.debug)
- //Log.Note("MEM", "*Mem* Changed {0:X4} from {1:X2} to {2:X2}", addr & 0x1FFF, Ram[addr & 0x1FFF], value);
Ram[addr & 0x1FFF] = value;
- }
else if (addr >= 0x1FE000) // hardware page.
{
@@ -69,11 +61,9 @@
{
if (BramEnabled && BramLocked == false)
{
- System.Console.WriteLine("WRITE BRAM[{0}] : {1:X2}", addr & 0x7FF, value);
BRAM[addr & 0x7FF] = value;
SaveRamModified = true;
}
- else System.Console.WriteLine("attemped BRAM write while locked!");
}
else
diff --git a/BizHawk.Emulation/Consoles/PC Engine/PCEngine.cs b/BizHawk.Emulation/Consoles/PC Engine/PCEngine.cs
index 0f5c5a2070..5208eafcaf 100644
--- a/BizHawk.Emulation/Consoles/PC Engine/PCEngine.cs
+++ b/BizHawk.Emulation/Consoles/PC Engine/PCEngine.cs
@@ -27,8 +27,14 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
public HuC6280 Cpu;
public VDC VDC1, VDC2;
public VCE VCE;
- public HuC6280PSG PSG;
public VPC VPC;
+ public ScsiCDBus SCSI;
+
+ public HuC6280PSG PSG;
+ public CDAudio CDAudio;
+ // TODO ADPCM
+ public SoundMixer SoundMixer;
+ public MetaspuSoundProvider SoundSynchronizer;
private bool TurboGrafx { get { return Type == NecSystemType.TurboGrafx; } }
private bool SuperGrafx { get { return Type == NecSystemType.SuperGrafx; } }
@@ -40,15 +46,18 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
private byte[] BRAM;
// Memory system
- public byte[] Ram;
+ public byte[] Ram; // PCE= 8K base ram, SGX= 64k base ram
+ public byte[] CDRam; // TurboCD extra 64k of ram
+ public byte[] SuperRam; // Super System Card 192K of additional RAM
+ public byte[] ArcadeRam; // Arcade Card 2048K of additional RAM
- // PC Engine timings:
// 21,477,270 Machine clocks / sec
// 7,159,090 Cpu cycles / sec
public PCEngine(GameInfo game, byte[] rom)
{
CoreOutputComm = new CoreOutputComm();
+
switch (game.System)
{
case "PCE": Type = NecSystemType.TurboGrafx; break;
@@ -72,16 +81,19 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
VCE = new VCE();
VDC1 = new VDC(Cpu, VCE);
PSG = new HuC6280PSG();
+ InitScsiBus();
- if (TurboGrafx || TurboCD)
+ if (TurboGrafx)
{
Ram = new byte[0x2000];
Cpu.ReadMemory21 = ReadMemory;
Cpu.WriteMemory21 = WriteMemory;
Cpu.WriteVDC = VDC1.WriteVDC;
+ soundProvider = PSG;
+ CDAudio = new CDAudio(null, 0);
}
- if (SuperGrafx)
+ else if (SuperGrafx)
{
VDC2 = new VDC(Cpu, VCE);
VPC = new VPC(VDC1, VDC2, VCE, Cpu);
@@ -89,6 +101,23 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
Cpu.ReadMemory21 = ReadMemorySGX;
Cpu.WriteMemory21 = WriteMemorySGX;
Cpu.WriteVDC = VDC1.WriteVDC;
+ soundProvider = PSG;
+ CDAudio = new CDAudio(null, 0);
+ }
+
+ else if (TurboCD)
+ {
+ Ram = new byte[0x2000];
+ CDRam = new byte[0x10000];
+ Cpu.ReadMemory21 = ReadMemoryCD;
+ Cpu.WriteMemory21 = WriteMemoryCD;
+ Cpu.WriteVDC = VDC1.WriteVDC;
+ CDAudio = new CDAudio(disc, short.MaxValue);
+ // TODO ADPCM
+ SoundMixer = new SoundMixer(PSG, CDAudio);
+ SoundSynchronizer = new MetaspuSoundProvider(ESynchMethod.ESynchMethod_V);
+ soundProvider = SoundSynchronizer;
+ VDC1.MidScanlineThink = () => SCSI.Think();
}
if (rom.Length == 0x60000)
@@ -114,7 +143,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
RomLength = RomData.Length;
}
- if (game["BRAM"])
+ if (game["BRAM"] || Type == NecSystemType.TurboCD)
{
BramEnabled = true;
BRAM = new byte[2048];
@@ -125,6 +154,12 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
BRAM[4] = 0x00; BRAM[5] = 0x88; BRAM[6] = 0x10; BRAM[7] = 0x80;
}
+ if (game["SuperSysCard"])
+ SuperRam = new byte[0x30000];
+
+ if (game["ArcadeCard"])
+ ArcadeRam = new byte[0x200000];
+
if (game["PopulousSRAM"])
{
PopulousRAM = new byte[0x8000];
@@ -188,6 +223,9 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
VDC1.ExecFrame(render);
PSG.EndFrame(Cpu.TotalExecutedCycles);
+ if (TurboCD)
+ SoundSynchronizer.PullSamples(SoundMixer);
+
if (lagged)
{
_lagcount++;
@@ -205,9 +243,10 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
get { return (IVideoProvider) VPC ?? VDC1; }
}
+ private ISoundProvider soundProvider;
public ISoundProvider SoundProvider
{
- get { return PSG; }
+ get { return soundProvider; }
}
public string SystemId { get { return "PCE"; } }
@@ -254,6 +293,10 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
VDC1.SaveStateText(writer, 1);
PSG.SaveStateText(writer);
}
+ if (TurboCD)
+ {
+ CDAudio.SaveStateText(writer);
+ }
writer.WriteLine("[/PCEngine]");
}
@@ -289,6 +332,8 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
VDC1.LoadStateText(reader, 1);
else if (args[0] == "[VDC2]")
VDC2.LoadStateText(reader, 2);
+ else if (args[0] == "[CDAudio]")
+ CDAudio.LoadStateText(reader);
else
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
}
@@ -299,8 +344,12 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
if (SuperGrafx == false)
{
writer.Write(Ram);
+ if (BRAM != null)
+ writer.Write(BRAM);
if (PopulousRAM != null)
writer.Write(PopulousRAM);
+ if (SuperRam != null)
+ writer.Write(SuperRam);
writer.Write(Frame);
writer.Write(_lagcount);
writer.Write(SF2MapperLatch);
@@ -309,6 +358,10 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
VCE.SaveStateBinary(writer);
VDC1.SaveStateBinary(writer);
PSG.SaveStateBinary(writer);
+ if (TurboCD)
+ {
+ CDAudio.SaveStateBinary(writer);
+ }
} else {
writer.Write(Ram);
writer.Write(Frame);
@@ -328,8 +381,12 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
if (SuperGrafx == false)
{
Ram = reader.ReadBytes(0x2000);
+ if (BRAM != null)
+ BRAM = reader.ReadBytes(0x800);
if (PopulousRAM != null)
PopulousRAM = reader.ReadBytes(0x8000);
+ if (SuperRam != null)
+ SuperRam = reader.ReadBytes(0x30000);
Frame = reader.ReadInt32();
_lagcount = reader.ReadInt32();
SF2MapperLatch = reader.ReadByte();
@@ -338,6 +395,10 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
VCE.LoadStateBinary(reader);
VDC1.LoadStateBinary(reader);
PSG.LoadStateBinary(reader);
+ if (TurboCD)
+ {
+ CDAudio.LoadStateBinary(reader);
+ }
} else {
Ram = reader.ReadBytes(0x8000);
Frame = reader.ReadInt32();
@@ -354,16 +415,19 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
public byte[] SaveStateBinary()
{
- int buflen = 75866;
+ int buflen = 75870;
if (SuperGrafx) buflen += 90698;
if (BramEnabled) buflen += 2048;
if (PopulousRAM != null) buflen += 0x8000;
+ if (SuperRam != null) buflen += 0x30000;
+ if (TurboCD) buflen += 26;
+ //Console.WriteLine("LENGTH1 " + buflen);
var buf = new byte[buflen];
var stream = new MemoryStream(buf);
var writer = new BinaryWriter(stream);
SaveStateBinary(writer);
- //Console.WriteLine("LENGTH " + stream.Position);
+ //Console.WriteLine("LENGTH2 " + stream.Position);
writer.Close();
return buf;
}
diff --git a/BizHawk.Emulation/Consoles/PC Engine/ScsiCD.cs b/BizHawk.Emulation/Consoles/PC Engine/ScsiCD.cs
deleted file mode 100644
index bc16e2888d..0000000000
--- a/BizHawk.Emulation/Consoles/PC Engine/ScsiCD.cs
+++ /dev/null
@@ -1,192 +0,0 @@
-using System;
-
-namespace BizHawk.Emulation
-{
- public sealed class ScsiCD
- {
- private bool bsy, sel, cd, io, msg, req, ack, atn, rst;
- private bool lagBSY, lagSEL, lagCD, lagIO, lagMSG, lagREQ, lagACK, lagATN, lagRST;
-
- public bool BSY {
- get { return bsy; }
- set {
- if (value != lagBSY) signalsChanged = true;
- lagBSY = bsy;
- bsy = value;
- }
- }
- public bool SEL
- {
- get { return sel; }
- set
- {
- if (value != lagSEL) signalsChanged = true;
- lagSEL = sel;
- sel = value;
- }
- }
- public bool CD // false=data, true=control
- {
- get { return cd; }
- set
- {
- if (value != lagCD) signalsChanged = true;
- lagCD = cd;
- cd = value;
- }
- }
- public bool IO
- {
- get { return io; }
- set
- {
- if (value != lagIO) signalsChanged = true;
- lagIO = io;
- io = value;
- }
- }
- public bool MSG
- {
- get { return msg; }
- set
- {
- if (value != lagMSG) signalsChanged = true;
- lagMSG = msg;
- msg = value;
- }
- }
- public bool REQ
- {
- get { return req; }
- set
- {
- if (value != lagREQ) signalsChanged = true;
- lagREQ = req;
- req = value;
- }
- }
- public bool ACK
- {
- get { return ack; }
- set
- {
- if (value != lagACK) signalsChanged = true;
- lagACK = ack;
- ack = value;
- }
- }
- public bool ATN
- {
- get { return atn; }
- set
- {
- if (value != lagATN) signalsChanged = true;
- lagATN = atn;
- atn = value;
- }
- }
- public bool RST
- {
- get { return rst; }
- set
- {
- if (value != lagRST) signalsChanged = true;
- lagRST = rst;
- rst = value;
- }
- }
- public byte DB { get; set; } // data bits
-
- private bool signalsChanged;
- private bool driveSelected;
-
- public void Think()
- {
- if (BSY == false && SEL == false)
- {
- Console.WriteLine("BUS FREE!");
- // zap the rest of the signals
- CD = false;
- IO = false;
- MSG = false;
- REQ = false;
- ACK = false;
- ATN = false;
- DB = 0;
- }
-
- if (SEL && lagSEL == false)
- {
- driveSelected = true;
- CD = true;
- BSY = true;
- MSG = false;
- IO = false;
- REQ = true;
- }
-
- if (RST && lagRST == false)
- {
- // reset buffers and CDDA and stuff.
- CD = false;
- IO = false;
- MSG = false;
- REQ = false;
- ACK = false;
- ATN = false;
- DB = 0;
- }
- }
-
- /* SCSI BUS SIGNALS
-
- BSY (BUSY). An "OR-tied" signal that indicates that the bus is being used.
-
- SEL (SELECT). A signal used by an initiator to select a target or by a target
- to reselect an initiator.
-
- C/D (CONTROL/DATA). A signal driven by a target that indicates whether
- CONTROL or DATA information is on the DATA BUS. True indicates CONTROL.
-
- I/O (INPUT/OUTPUT). A signal driven by a target that controls the direction
- of data movement on the DATA BUS with respect to an initiator. True indicates
- input to the initiator. This signal is also used to distinguish between
- SELECTION and RESELECTION phases.
-
- MSG (MESSAGE). A signal driven by a target during the MESSAGE phase.
-
- REQ (REQUEST). A signal driven by a target to indicate a request for a
- REQ/ACK data transfer handshake.
-
- ACK (ACKNOWLEDGE). A signal driven by an initiator to indicate an
- acknowledgment for a REQ/ACK data transfer handshake.
-
- ATN (ATTENTION). A signal driven by an initiator to indicate the ATTENTION
- condition.
-
- RST (RESET). An "OR-tied" signal that indicates the RESET condition.
-
- Plus 8 data bits (DB7-0) and parity (P).
-
- ==============================================================================
- Signals
- ----------------------------------------------------------
- C/D, I/O,
- Bus Phase BSY SEL MSG, REQ ACK/ATN DB(7-0,P)
- ------------------------------------------------------------------------------
- BUS FREE None None None None None
- ARBITRATION All Winner None None SCSI ID
- SELECTION I&T Initiator None Initiator Initiator
- RESELECTION I&T Target Target Initiator Target
- COMMAND Target None Target Initiator Initiator
- DATA IN Target None Target Initiator Target
- DATA OUT Target None Target Initiator Initiator
- STATUS Target None Target Initiator Target
- MESSAGE IN Target None Target Initiator Target
- MESSAGE OUT Target None Target Initiator Initiator
- ==============================================================================
-
- */
-
- }
-}
diff --git a/BizHawk.Emulation/Consoles/PC Engine/ScsiCDBus.cs b/BizHawk.Emulation/Consoles/PC Engine/ScsiCDBus.cs
new file mode 100644
index 0000000000..927ea2829f
--- /dev/null
+++ b/BizHawk.Emulation/Consoles/PC Engine/ScsiCDBus.cs
@@ -0,0 +1,652 @@
+using System;
+using BizHawk.DiscSystem;
+using BizHawk.Emulation.Sound;
+
+namespace BizHawk.Emulation.Consoles.TurboGrafx
+{
+ public sealed class ScsiCDBus
+ {
+ private const int STATUS_GOOD = 0;
+ private const int STATUS_CHECK_CONDITION = 1;
+ private const int STATUS_CONDITION_MET = 2;
+ private const int STATUS_BUSY = 4;
+ private const int STATUS_INTERMEDIATE = 8;
+
+ private const int SCSI_TEST_UNIT_READY = 0x00;
+ private const int SCSI_REQUEST_SENSE = 0x03;
+ private const int SCSI_READ = 0x08;
+ private const int SCSI_AUDIO_START_POS = 0xD8;
+ private const int SCSI_AUDIO_END_POS = 0xD9;
+ private const int SCSI_PAUSE = 0xDA;
+ private const int SCSI_READ_SUBCODE_Q = 0xDD;
+ private const int SCSI_READ_TOC = 0xDE;
+
+ private bool bsy, sel, cd, io, msg, req, ack, atn, rst;
+ private bool signalsChanged;
+
+ public bool BSY
+ {
+ get { return bsy; }
+ set {
+ if (value != BSY) signalsChanged = true;
+ bsy = value;
+ }
+ }
+ public bool SEL
+ {
+ get { return sel; }
+ set
+ {
+ if (value != SEL) signalsChanged = true;
+ sel = value;
+ }
+ }
+ public bool CD // CONTROL = true, DATA = false
+ {
+ get { return cd; }
+ set
+ {
+ if (value != CD) signalsChanged = true;
+ cd = value;
+ }
+ }
+ public bool IO // INPUT = true, OUTPUT = false
+ {
+ get { return io; }
+ set
+ {
+ if (value != IO) signalsChanged = true;
+ io = value;
+ }
+ }
+ public bool MSG
+ {
+ get { return msg; }
+ set
+ {
+ if (value != MSG) signalsChanged = true;
+ msg = value;
+ }
+ }
+ public bool REQ
+ {
+ get { return req; }
+ set
+ {
+ if (value != REQ) signalsChanged = true;
+ req = value;
+ }
+ }
+ public bool ACK
+ {
+ get { return ack; }
+ set
+ {
+ if (value != ACK) signalsChanged = true;
+ ack = value;
+ }
+ }
+ public bool ATN
+ {
+ get { return atn; }
+ set
+ {
+ if (value != ATN) signalsChanged = true;
+ atn = value;
+ }
+ }
+ public bool RST
+ {
+ get { return rst; }
+ set
+ {
+ if (value != RST) signalsChanged = true;
+ rst = value;
+ }
+ }
+ public byte DataBits { get; set; } // data bits
+
+ private enum BusPhase
+ {
+ BusFree,
+ Command,
+ DataIn,
+ DataOut,
+ MessageIn,
+ MessageOut,
+ Status
+ }
+
+ private bool busPhaseChanged;
+ private BusPhase Phase = BusPhase.BusFree;
+
+ private bool MessageCompleted;
+ private bool StatusCompleted;
+ private byte MessageValue;
+
+ private QuickList CommandBuffer = new QuickList(10); // 10 = biggest command
+ public QuickQueue DataIn = new QuickQueue(2048); // proper size
+
+ // ******** Data Transfer / READ command support ********
+ private long DataReadWaitTimer;
+ private bool DataReadInProgress;
+ private bool DataTransferWasDone;
+ private int CurrentReadingSector;
+ private int SectorsLeftToRead;
+
+ // ******** Resources ********
+
+ private PCEngine pce;
+ public Disc disc;
+
+ // ******** Events ********
+
+ public Action DataTransferReady;
+ public Action DataTransferComplete;
+
+ public ScsiCDBus(PCEngine pce, Disc disc)
+ {
+ this.pce = pce;
+ this.disc = disc;
+ }
+
+ public void Think()
+ {
+ if (RST)
+ {
+ ResetDevice();
+ return;
+ }
+
+ if (DataReadInProgress && pce.Cpu.TotalExecutedCycles > DataReadWaitTimer)
+ {
+ if (DataIn.Count == 0)
+ {
+ Console.WriteLine("Sector available to read!!!");
+ // read in a sector and shove it in the queue
+ disc.ReadLBA_2048(CurrentReadingSector, DataIn.GetBuffer(), 0);
+ DataIn.SignalBufferFilled(2048);
+ CurrentReadingSector++;
+ SectorsLeftToRead--;
+
+ DataTransferReady(true);
+
+ // If more sectors, should set the next think-clock to however long it takes to read 1 sector
+ // but I dont. I dont think transfers actually happen sector by sector
+ // like this, they probably become available as the bits come off the disc.
+ // but lets get some basic functionality before we go crazy.
+ // Idunno, maybe they do come in a sector at a time.
+ if (SectorsLeftToRead == 0)
+ {
+ DataReadInProgress = false;
+ DataTransferWasDone = true;
+ }
+ SetPhase(BusPhase.DataIn);
+ }
+ }
+
+ do
+ {
+ signalsChanged = false;
+ busPhaseChanged = false;
+
+ if (SEL && !BSY)
+ {
+ SetPhase(BusPhase.Command);
+ }
+ else if (ATN && !REQ && !ACK)
+ {
+ SetPhase(BusPhase.MessageOut);
+ }
+ else switch (Phase)
+ {
+ case BusPhase.Command: ThinkCommandPhase(); break;
+ case BusPhase.DataIn: ThinkDataInPhase(); break;
+ case BusPhase.DataOut: ThinkDataOutPhase(); break;
+ case BusPhase.MessageIn: ThinkMessageInPhase(); break;
+ case BusPhase.MessageOut: ThinkMessageOutPhase(); break;
+ case BusPhase.Status: ThinkStatusPhase(); break;
+ default: break;
+ }
+ } while (signalsChanged || busPhaseChanged);
+ }
+
+ private void ResetDevice()
+ {
+ CD = false;
+ IO = false;
+ MSG = false;
+ REQ = false;
+ ACK = false;
+ ATN = false;
+ DataBits = 0;
+ Phase = BusPhase.BusFree;
+
+ CommandBuffer.Clear();
+ DataIn.Clear();
+ DataReadInProgress = false;
+ pce.CDAudio.Stop();
+ }
+
+ private void ThinkCommandPhase()
+ {
+ if (REQ && ACK)
+ {
+ CommandBuffer.Add(DataBits);
+ REQ = false;
+ }
+
+ if (!REQ && !ACK && CommandBuffer.Count > 0)
+ {
+ bool complete = CheckCommandBuffer();
+
+ if (complete)
+ {
+ CommandBuffer.Clear();
+ }
+ else
+ {
+ REQ = true; // needs more data!
+ }
+ }
+ }
+
+ private void ThinkDataInPhase()
+ {
+ if (REQ && ACK)
+ {
+ REQ = false;
+ }
+ else if (!REQ && !ACK)
+ {
+ if (DataIn.Count > 0)
+ {
+ DataBits = DataIn.Dequeue();
+ REQ = true;
+ } else {
+ // data transfer is finished
+
+ DataTransferReady(false);
+ if (DataTransferWasDone)
+ {
+ Console.WriteLine("DATA TRANSFER FINISHED!");
+ DataTransferWasDone = false;
+ DataTransferComplete(true);
+ }
+ SetStatusMessage(STATUS_GOOD, 0);
+ }
+ }
+ }
+
+ private void ThinkDataOutPhase()
+ {
+ Console.WriteLine("*********** DATA OUT PHASE, DOES THIS HAPPEN? ****************");
+ SetPhase(BusPhase.BusFree);
+ }
+
+ private void ThinkMessageInPhase()
+ {
+ if (REQ && ACK)
+ {
+ REQ = false;
+ MessageCompleted = true;
+ }
+
+ if (!REQ && !ACK && MessageCompleted)
+ {
+ MessageCompleted = false;
+ SetPhase(BusPhase.BusFree);
+ }
+ }
+
+ private void ThinkMessageOutPhase()
+ {
+ Console.WriteLine("******* IN MESSAGE OUT PHASE. DOES THIS EVER HAPPEN? ********");
+ SetPhase(BusPhase.BusFree);
+ }
+
+ private void ThinkStatusPhase()
+ {
+ if (REQ && ACK)
+ {
+ REQ = false;
+ StatusCompleted = true;
+ }
+ if (!REQ && !ACK && StatusCompleted)
+ {
+ StatusCompleted = false;
+ DataBits = MessageValue;
+ SetPhase(BusPhase.MessageIn);
+ }
+ }
+
+ // returns true if command completed, false if more data bytes needed
+ private bool CheckCommandBuffer()
+ {
+ switch (CommandBuffer[0])
+ {
+ case SCSI_TEST_UNIT_READY:
+ if (CommandBuffer.Count < 6) return false;
+ Log.Note("CD", "Execute TEST_UNIT_READY");
+ SetStatusMessage(STATUS_GOOD, 0);
+ return true;
+
+ case SCSI_READ:
+ if (CommandBuffer.Count < 6) return false;
+ CommandRead();
+ return true;
+
+ case SCSI_AUDIO_START_POS:
+ if (CommandBuffer.Count < 10) return false;
+ CommandAudioStartPos();
+ return true;
+
+ case SCSI_AUDIO_END_POS:
+ if (CommandBuffer.Count < 10) return false;
+ CommandAudioEndPos();
+ return true;
+
+ case SCSI_PAUSE:
+ if (CommandBuffer.Count < 10) return false;
+ CommandPause();
+ return true;
+
+ case SCSI_READ_SUBCODE_Q:
+ if (CommandBuffer.Count < 10) return false;
+ CommandReadSubcodeQ();
+ return true;
+
+ case SCSI_READ_TOC:
+ if (CommandBuffer.Count < 10) return false;
+ CommandReadTOC();
+ return true;
+
+ default:
+ Console.WriteLine("UNRECOGNIZED SCSI COMMAND! {0:X2}", CommandBuffer[0]);
+ break;
+ }
+ return false;
+ }
+
+ private void CommandRead()
+ {
+ int sector = (CommandBuffer[1] & 0x1f) << 16;
+ sector |= CommandBuffer[2] << 8;
+ sector |= CommandBuffer[3];
+ sector += 150; // BLEH
+
+if (CommandBuffer[4] == 0)
+throw new Exception("requesting 0 sectors read.............................");
+
+ DataReadInProgress = true;
+ CurrentReadingSector = sector;
+ SectorsLeftToRead = CommandBuffer[4];
+
+ Console.WriteLine("STARTED READ: {0} SECTORS FROM {1}",SectorsLeftToRead, CurrentReadingSector);
+ DataReadWaitTimer = pce.Cpu.TotalExecutedCycles + 5000; // figure out proper read delay later
+ pce.CDAudio.Stop();
+ }
+
+ private int audioStartLBA;
+ private int audioEndLBA;
+
+ private void CommandAudioStartPos()
+ {
+ switch (CommandBuffer[9] & 0xC0)
+ {
+ case 0x00: // Set start offset in LBA units
+ audioStartLBA = (CommandBuffer[3] << 16) | (CommandBuffer[4] << 8) | CommandBuffer[5];
+ Console.WriteLine("Set Start LBA: "+audioStartLBA);
+ break;
+
+ case 0x40: // Set start offset in MSF units
+ byte m = CommandBuffer[2].BCDtoBin();
+ byte s = CommandBuffer[3].BCDtoBin();
+ byte f = CommandBuffer[4].BCDtoBin();
+ audioStartLBA = Disc.ConvertMSFtoLBA(m, s, f);
+ Console.WriteLine("Set Start MSF: {0} {1} {2} lba={3}",m,s,f,audioStartLBA);
+ break;
+
+ case 0x80: // Set start offset in track units
+ byte trackNo = CommandBuffer[2].BCDtoBin();
+ audioStartLBA = disc.TOC.Sessions[0].Tracks[trackNo - 1].Indexes[1].lba;
+ Console.WriteLine("Set Start track: {0} lba={1}", trackNo, audioStartLBA);
+ break;
+ }
+
+ if (CommandBuffer[1] == 0)
+ {
+ pce.CDAudio.Pause();
+ // silent?
+ } else {
+ pce.CDAudio.PlayStartingAtLba(audioStartLBA);
+ }
+
+ // TODO there are some flags in command[1]
+ // wat we do if audio is already playing
+ // wat we do if audio paused
+
+ SetStatusMessage(STATUS_GOOD, 0);
+ // irq callback?
+ }
+
+ private void CommandAudioEndPos()
+ {
+ switch (CommandBuffer[9] & 0xC0)
+ {
+ case 0x00: // Set end offset in LBA units
+ audioEndLBA = (CommandBuffer[3] << 16) | (CommandBuffer[4] << 8) | CommandBuffer[5];
+ Console.WriteLine("Set End LBA: " + audioEndLBA);
+ break;
+
+ case 0x40: // Set end offset in MSF units
+ byte m = CommandBuffer[2].BCDtoBin();
+ byte s = CommandBuffer[3].BCDtoBin();
+ byte f = CommandBuffer[4].BCDtoBin();
+ audioEndLBA = Disc.ConvertMSFtoLBA(m, s, f);
+ Console.WriteLine("Set End MSF: {0} {1} {2} lba={3}", m, s, f, audioEndLBA);
+ break;
+
+ case 0x80: // Set end offset in track units
+ byte trackNo = CommandBuffer[2].BCDtoBin();
+ audioEndLBA = disc.TOC.Sessions[0].Tracks[trackNo - 1].Indexes[1].lba;
+ Console.WriteLine("Set End track: {0} lba={1}", trackNo, audioEndLBA);
+ break;
+ }
+
+ switch (CommandBuffer[1])
+ {
+ case 0: // end immediately
+ pce.CDAudio.Stop();
+ break;
+ case 1: // play in loop mode. I guess this constitues A-B looping
+ Console.WriteLine("DOING A-B LOOP. NOT SURE IF RIGHT.");
+ pce.CDAudio.PlayStartingAtLba(audioStartLBA);
+ pce.CDAudio.EndLBA = audioEndLBA;
+ pce.CDAudio.PlayMode = CDAudio.PlaybackMode.LoopOnCompletion;
+ break;
+ case 2: // Play audio, fire IRQ2 when end position reached
+ Console.WriteLine("STOP MODE 2 ENGAGED, BUT NOTE. IRQ WILL NOT FIRE YET.");
+ pce.CDAudio.PlayStartingAtLba(audioStartLBA);
+ pce.CDAudio.EndLBA = audioEndLBA;
+ pce.CDAudio.PlayMode = CDAudio.PlaybackMode.IRQOnCompletion;
+ break;
+ case 3: // Play normal
+ Console.WriteLine("*** SET END POS, IN PLAY NORMAL MODE? STARTING AT _START_ POS. IS THAT RIGHT?");
+ pce.CDAudio.PlayStartingAtLba(audioStartLBA);
+ pce.CDAudio.EndLBA = audioEndLBA;
+ pce.CDAudio.PlayMode = CDAudio.PlaybackMode.StopOnCompletion;
+ break;
+ }
+ SetStatusMessage(STATUS_GOOD, 0);
+ }
+
+ private void CommandPause()
+ {
+ // apparently pause means stop? I guess? Idunno.
+ pce.CDAudio.Stop();
+ SetStatusMessage(STATUS_GOOD, 0);
+ // TODO send error if already stopped.. or paused... or something.
+ }
+
+ private void CommandReadSubcodeQ()
+ {
+ DataIn.Clear();
+
+ switch (pce.CDAudio.Mode)
+ {
+ case CDAudio.CDAudioMode.Playing: DataIn.Enqueue(0); break;
+ case CDAudio.CDAudioMode.Paused: DataIn.Enqueue(2); break;
+ case CDAudio.CDAudioMode.Stopped: DataIn.Enqueue(3); break;
+ }
+ DataIn.Enqueue(0); // unused?
+ DataIn.Enqueue((byte)pce.CDAudio.PlayingTrack); // track
+ DataIn.Enqueue(1); // index
+ DataIn.Enqueue(1); // M(rel)
+ DataIn.Enqueue(1); // S(rel)
+ DataIn.Enqueue(1); // F(rel)
+ DataIn.Enqueue(1); // M(abs)
+ DataIn.Enqueue(1); // S(abs)
+ DataIn.Enqueue(1); // F(abs)
+ SetPhase(BusPhase.DataIn);
+ }
+
+ private void CommandReadTOC()
+ {
+ switch (CommandBuffer[1])
+ {
+ case 0: // return number of tracks
+ {
+ Log.Error("CD","Execute READ_TOC : num of tracks");
+ DataIn.Clear();
+ DataIn.Enqueue(0x01);
+ DataIn.Enqueue(((byte) disc.TOC.Sessions[0].Tracks.Count).BinToBCD());
+ SetPhase(BusPhase.DataIn);
+ break;
+ }
+ case 1: // return total disc length in minutes/seconds/frames
+ {
+ int totalLbaLength = disc.LBACount;
+
+ byte m, s, f;
+ Disc.ConvertLBAtoMSF(totalLbaLength, out m, out s, out f);
+
+ DataIn.Clear();
+ DataIn.Enqueue(m.BinToBCD());
+ DataIn.Enqueue(s.BinToBCD());
+ DataIn.Enqueue(f.BinToBCD());
+ SetPhase(BusPhase.DataIn);
+
+ Log.Error("CD","EXECUTE READ_TOC : length of disc, LBA {0}, m:{1},s:{2},f:{3}",
+ totalLbaLength, m, s, f);
+ break;
+ }
+ case 2: // Return starting position of specified track in MSF format
+ {
+ int track = CommandBuffer[2].BCDtoBin();
+ if (CommandBuffer[2] > 0x99)
+ throw new Exception("invalid track number BCD request... is something I need to handle?");
+ if (track == 0) track = 1;
+ track--;
+ if (track > disc.TOC.Sessions[0].Tracks.Count)
+ throw new Exception("Request more tracks than exist.... need to do error handling");
+ // I error handled your mom last night
+
+ int lbaPos = disc.TOC.Sessions[0].Tracks[track].Indexes[1].lba;
+ byte m, s, f;
+ Disc.ConvertLBAtoMSF(lbaPos, out m, out s, out f);
+
+ DataIn.Clear();
+ DataIn.Enqueue(m.BinToBCD());
+ DataIn.Enqueue(s.BinToBCD());
+ DataIn.Enqueue(f.BinToBCD());
+ if (disc.TOC.Sessions[0].Tracks[track].TrackType == ETrackType.Audio)
+ DataIn.Enqueue(0);
+ else
+ DataIn.Enqueue(4);
+ SetPhase(BusPhase.DataIn);
+
+ Log.Error("CD", "EXECUTE READ_TOC : start pos of TRACK {4}, LBA {0}, m:{1},s:{2},f:{3}",
+ lbaPos, m, s, f, track);
+
+ break;
+ }
+ default:
+ Console.WriteLine("unimplemented READ TOC command argument!");
+ break;
+ }
+ }
+
+ private void SetStatusMessage(byte status, byte message)
+ {
+ MessageValue = message;
+ StatusCompleted = false;
+ MessageCompleted = false;
+ DataBits = status == STATUS_GOOD ? (byte) 0x00 : (byte) 0x01;
+ SetPhase(BusPhase.Status);
+ }
+
+ private void SetPhase(BusPhase phase)
+ {
+ if (Phase == phase)
+ return;
+
+ Phase = phase;
+ busPhaseChanged = true;
+
+ switch (phase)
+ {
+ case BusPhase.BusFree:
+ BSY = false;
+ MSG = false;
+ CD = false;
+ IO = false;
+ REQ = false;
+ DataTransferComplete(false);
+ break;
+ case BusPhase.Command:
+ BSY = true;
+ MSG = false;
+ CD = true;
+ IO = false;
+ REQ = true;
+ break;
+ case BusPhase.DataIn:
+ BSY = true;
+ MSG = false;
+ CD = false;
+ IO = true;
+ REQ = false;
+ break;
+ case BusPhase.DataOut:
+ BSY = true;
+ MSG = false;
+ CD = false;
+ IO = false;
+ REQ = true;
+ break;
+ case BusPhase.MessageIn:
+ BSY = true;
+ MSG = true;
+ CD = true;
+ IO = true;
+ REQ = true;
+ break;
+ case BusPhase.MessageOut:
+ BSY = true;
+ MSG = true;
+ CD = true;
+ IO = false;
+ REQ = true;
+ break;
+ case BusPhase.Status:
+ BSY = true;
+ MSG = false;
+ CD = true;
+ IO = true;
+ REQ = true;
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/BizHawk.Emulation/Consoles/PC Engine/TurboCD.cs b/BizHawk.Emulation/Consoles/PC Engine/TurboCD.cs
index eff7a426a0..0b2b5e4449 100644
--- a/BizHawk.Emulation/Consoles/PC Engine/TurboCD.cs
+++ b/BizHawk.Emulation/Consoles/PC Engine/TurboCD.cs
@@ -2,144 +2,213 @@
namespace BizHawk.Emulation.Consoles.TurboGrafx
{
- public partial class PCEngine
- {
- private byte[] CdIoPorts = new byte[16];
- private ScsiCD scsi = new ScsiCD();
+ public partial class PCEngine
+ {
+ private byte[] CdIoPorts = new byte[16];
- private void WriteCD(int addr, byte value)
- {
- Console.WriteLine("Write to reg[{0:X4}] {1:X2}", addr & 0x1FFF, value);
- switch (addr & 0x1FFF)
- {
- case 0x1800: // SCSI Drive Control Line
- CdIoPorts[0] = value;
- // Console.WriteLine("Write to CDC Status [0] {0:X2}", value);
+ private void InitScsiBus()
+ {
+ SCSI = new ScsiCDBus(this, disc);
+ // this is kind of stupid
+ SCSI.DataTransferReady = yes =>
+ {
+ // set or clear Ready Bit
+ if (yes)
+ CdIoPorts[3] |= 0x40;
+ else
+ CdIoPorts[3] &= 0xBF;
+ };
+ SCSI.DataTransferComplete = yes =>
+ {
+ if (yes)
+ CdIoPorts[3] |= 0x20; // Set "Complete"
+ else
+ {
+ CdIoPorts[3] &= 0xBF; // Clear "ready"
+
+ }
+
+ };
+ }
- scsi.SEL = true;
- scsi.Think();
- scsi.SEL = false;
- scsi.Think();
+ private void WriteCD(int addr, byte value)
+ {
+ //Log.Note("CD","Write: {0:X4} {1:X2} (PC={2:X4})", addr & 0x1FFF, value, Cpu.PC);
+ switch (addr & 0x1FFF)
+ {
+ case 0x1800: // SCSI Drive Control Line
+ CdIoPorts[0] = value;
+ // Console.WriteLine("Write to CDC Status [0] {0:X2}", value);
- // this probably does some things
- // possibly clear irq line or trigger or who knows
- break;
+ SCSI.SEL = true;
+ SCSI.Think();
+ SCSI.SEL = false;
+ SCSI.Think();
- case 0x1801: // CDC Command
- CdIoPorts[1] = value;
- scsi.DB = value;
- scsi.Think();
- // Console.WriteLine("Write to CDC Command [1] {0:X2}", value);
- break;
+ // this probably does some things
+ // possibly clear irq line or trigger or who knows
+ break;
- case 0x1802: // ACK and Interrupt Control
- CdIoPorts[2] = value;
- scsi.ACK = ((value & 0x80) != 0);
- scsi.Think();
- RefreshIRQ2();
- break;
+ case 0x1801: // CDC Command
+ CdIoPorts[1] = value;
+ SCSI.DataBits = value;
+ SCSI.Think();
+// Console.WriteLine("Write to CDC Command [1] {0:X2}", value);
+ break;
- case 0x1804: // CD Reset Command
- CdIoPorts[4] = value;
- scsi.RST = ((value & 0x02) != 0);
- scsi.Think();
- if (scsi.RST)
- {
- CdIoPorts[3] &= 0x8F; // Clear interrupt control bits
- RefreshIRQ2();
- }
- break;
+ case 0x1802: // ACK and Interrupt Control
+ CdIoPorts[2] = value;
+ SCSI.ACK = ((value & 0x80) != 0);
- case 0x1807: // BRAM Unlock
- if (BramEnabled && (value & 0x80) != 0)
- {
- Console.WriteLine("UNLOCK BRAM!");
- BramLocked = false;
- }
- break;
+ if ((CdIoPorts[2] & 0x04) != 0) Log.Note("CD", "INTAEN enable");
+ if ((CdIoPorts[2] & 0x08) != 0) Log.Note("CD", "INTSTOPEN enable");
+ if ((CdIoPorts[2] & 0x10) != 0) Log.Note("CD", "INTSUBEN enable");
+ if ((CdIoPorts[2] & 0x20) != 0) Log.Note("CD", "INTMEN enable");
+ if ((CdIoPorts[2] & 0x40) != 0) Log.Note("CD", "INTDEN enable");
+ if ((Cpu.IRQControlByte & 0x01) != 0) Log.Note("CD", "BTW, IRQ2 is not masked");
- case 0x180B: // ADPCM DMA Control
- CdIoPorts[0x0B] = value;
- // Console.WriteLine("Write to ADPCM DMA Control [B]");
- // TODO... there is DMA to be done
- break;
+ SCSI.Think();
+ RefreshIRQ2();
+ break;
- case 0x180D: // ADPCM Address Control
- CdIoPorts[0x0D] = value;
- // Console.WriteLine("Write to ADPCM Address Control [D]");
- break;
+ case 0x1804: // CD Reset Command
+ CdIoPorts[4] = value;
+ SCSI.RST = ((value & 0x02) != 0);
+ SCSI.Think();
+ if (SCSI.RST)
+ {
+ CdIoPorts[3] &= 0x8F; // Clear interrupt control bits
+ RefreshIRQ2();
+ }
+ break;
- case 0x180E: // ADPCM Playback Rate
- CdIoPorts[0x0E] = value;
- // Console.WriteLine("Write to ADPCM Address Control [E]");
- break;
+ case 0x1805:
+ case 0x1806:
+ // Latch CDDA data... no action needed for us
+ break;
- case 0x180F: // Audio Fade Timer
- CdIoPorts[0x0F] = value;
- // Console.WriteLine("Write to CD Audio fade timer [F]");
- // TODO: hook this up to audio system);
- break;
+ case 0x1807: // BRAM Unlock
+ if (BramEnabled && (value & 0x80) != 0)
+ {
+ //Console.WriteLine("UNLOCK BRAM!");
+ BramLocked = false;
+ }
+ break;
- default:
- Console.WriteLine("unknown write to {0:X4}:{1:X2}", addr, value);
- break;
- }
- }
+ case 0x180B: // ADPCM DMA Control
+ CdIoPorts[0x0B] = value;
+ Console.WriteLine("Write to ADPCM DMA Control [B]");
+ // TODO... there is DMA to be done
+ break;
- public byte ReadCD(int addr)
- {
- byte returnValue = 0;
- switch (addr & 0x1FFF)
- {
- case 0x1800: // SCSI Drive Control Line
- scsi.Think();
- if (scsi.IO) returnValue |= 0x08;
- if (scsi.CD) returnValue |= 0x10;
- if (scsi.MSG) returnValue |= 0x20;
- if (scsi.REQ) returnValue |= 0x40;
- if (scsi.BSY) returnValue |= 0x80;
- //if (returnValue != 0) returnValue = 0x40;
- Console.WriteLine("Read SCSI Drive Control Line [0]: {0:X2} btw, pc={1:X4} ", returnValue, this.Cpu.PC);
- return returnValue;
+ case 0x180D: // ADPCM Address Control
+ CdIoPorts[0x0D] = value;
+ Console.WriteLine("Write to ADPCM Address Control [D]");
+ break;
- case 0x1802: // ADPCM / CD Control
- Console.WriteLine("Read 1802 {0:X2}", CdIoPorts[2]);
- return CdIoPorts[2];
+ case 0x180E: // ADPCM Playback Rate
+ CdIoPorts[0x0E] = value;
+ Console.WriteLine("Write to ADPCM Address Control [E]");
+ break;
- case 0x1803: // BRAM Lock
- if (BramEnabled)
- {
- Console.WriteLine("LOCKED BRAM! (read 1803)");
- BramLocked = true;
- }
- return CdIoPorts[3];
+ case 0x180F: // Audio Fade Timer
+ CdIoPorts[0x0F] = value;
+ Console.WriteLine("Write to CD Audio fade timer [F]");
+ // TODO: hook this up to audio system. and to your mother
+ break;
- case 0x1804: // CD Reset
- Console.WriteLine("Read 1804 {0:X2}", CdIoPorts[4]);
- return CdIoPorts[4];
+ default:
+ Console.WriteLine("unknown write to {0:X4}:{1:X2}",addr, value);
+ break;
+ }
+ }
- case 0x180F: // Audio Fade Timer
- Console.WriteLine("Read 180F {0:X2}", CdIoPorts[0xF]);
- return CdIoPorts[0x0F];
+ public byte ReadCD(int addr)
+ {
+ byte returnValue = 0;
+ short sample;
- // These are some retarded version check
- case 0x18C1: return 0xAA;
- case 0x18C2: return 0x55;
- case 0x18C3: return 0x00;
- case 0x18C5: return 0xAA;
- case 0x18C6: return 0x55;
- case 0x18C7: return 0x03;
+ switch (addr & 0x1FFF)
+ {
+ case 0x1800: // SCSI Drive Control Line
+ if (SCSI.IO) returnValue |= 0x08;
+ if (SCSI.CD) returnValue |= 0x10;
+ if (SCSI.MSG) returnValue |= 0x20;
+ if (SCSI.REQ) returnValue |= 0x40;
+ if (SCSI.BSY) returnValue |= 0x80;
+ //Log.Note("CD", "Read: 1800 {0:X2} (PC={1:X4})", returnValue, Cpu.PC);
+ return returnValue;
- default:
- Console.WriteLine("unknown read to {0:X4}", addr);
- return 0xFF;
- }
- }
+ case 0x1801: // Read data bus
+ //Log.Note("CD", "Read: 1801 {0:X2} (PC={1:X4})", SCSI.DataBits, Cpu.PC);
+ return SCSI.DataBits;
- private void RefreshIRQ2()
- {
- int mask = CdIoPorts[2] & CdIoPorts[3] & 0x7C;
- Cpu.IRQ2Assert = (mask != 0);
- }
- }
-}
+ case 0x1802: // ADPCM / CD Control
+ //Log.Note("CD", "Read: 1802 {0:X2} (PC={1:X4})", CdIoPorts[2], Cpu.PC);
+ return CdIoPorts[2];
+
+ case 0x1803: // BRAM Lock
+ if (BramEnabled)
+ {
+ Log.Note("CD", "Read: 1803 {0:X2} (PC={1:X4})", CdIoPorts[3], Cpu.PC);
+ BramLocked = true;
+
+ }
+ returnValue = CdIoPorts[3];
+ CdIoPorts[3] ^= 2;
+ return returnValue;
+
+ case 0x1804: // CD Reset
+ //Log.Note("CD", "Read: 1804 {0:X2} (PC={1:X4})", CdIoPorts[4], Cpu.PC);
+ return CdIoPorts[4];
+
+ case 0x1805: // CD audio data Low
+ if ((CdIoPorts[0x3] & 0x2) == 0)
+ sample = CDAudio.VolumeLeft;
+ else
+ sample = CDAudio.VolumeRight;
+ return (byte) sample;
+
+ case 0x1806: // CD audio data High
+ if ((CdIoPorts[0x3] & 0x2) == 0)
+ sample = CDAudio.VolumeLeft;
+ else
+ sample = CDAudio.VolumeRight;
+ return (byte) (sample >> 8);
+
+ case 0x1808: // "auto handshake data input"
+ byte ret = SCSI.DataBits;
+ //Console.WriteLine("read 1808 {0:X2} remain: {1}", ret, SCSI.DataIn.Count);
+ if (SCSI.REQ && SCSI.IO && !SCSI.CD)
+ {
+ SCSI.ACK = false;
+ SCSI.REQ = false;
+ SCSI.Think();
+ }
+ return ret;
+
+ case 0x180F: // Audio Fade Timer
+ Log.Note("CD", "Read: 180F {0:X2} (PC={1:X4})", CdIoPorts[0xF], Cpu.PC);
+ return CdIoPorts[0x0F];
+
+ // These are some retarded version check
+ case 0x18C1: return 0xAA;
+ case 0x18C2: return 0x55;
+ case 0x18C3: return 0x00;
+ case 0x18C5: return 0xAA;
+ case 0x18C6: return 0x55;
+ case 0x18C7: return 0x03;
+
+ default:
+ Log.Note("CD", "unknown read to {0:X4}", addr);
+ return 0xFF;
+ }
+ }
+
+ private void RefreshIRQ2()
+ {
+ int mask = CdIoPorts[2] & CdIoPorts[3] & 0x7C;
+ Cpu.IRQ2Assert = (mask != 0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/BizHawk.Emulation/Consoles/PC Engine/VDC.Render.cs b/BizHawk.Emulation/Consoles/PC Engine/VDC.Render.cs
index d6d28d2ad4..80e2e959da 100644
--- a/BizHawk.Emulation/Consoles/PC Engine/VDC.Render.cs
+++ b/BizHawk.Emulation/Consoles/PC Engine/VDC.Render.cs
@@ -86,7 +86,10 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
if ((StatusByte & (StatusVerticalBlanking | StatusVramSatDmaComplete)) != 0)
cpu.IRQ1Assert = true;
- cpu.Execute(455 - HBlankCycles - 2);
+ cpu.Execute(50);
+ MidScanlineThink();
+
+ cpu.Execute(455 - HBlankCycles - 52);
if (InActiveDisplay == false && DmaRequested)
RunDmaForScanline();
@@ -130,9 +133,7 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
int yTile = (vertLine / 8);
int yOfs = vertLine % 8;
- // TODO: x-scrolling is done super quick and shitty here and slow.
- // Ergo, make it better later.
-
+ // This is not optimized. But it seems likely to remain that way.
int xScroll = Registers[BXR] & 0x3FF;
for (int x = 0; x < FrameWidth; x++)
{
diff --git a/BizHawk.Emulation/Consoles/PC Engine/VDC.cs b/BizHawk.Emulation/Consoles/PC Engine/VDC.cs
index 8fba0c3f3d..abdfa96117 100644
--- a/BizHawk.Emulation/Consoles/PC Engine/VDC.cs
+++ b/BizHawk.Emulation/Consoles/PC Engine/VDC.cs
@@ -84,9 +84,10 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
public const byte StatusSprite0Collision = 0x01;
private const int VramSize = 0x8000;
-
+
private HuC6280 cpu;
private VCE vce;
+ public Action MidScanlineThink = () => { };
public int MultiResHack = 0;
diff --git a/BizHawk.Emulation/Log.cs b/BizHawk.Emulation/Log.cs
index 5e4ca379ee..786ffd53b1 100644
--- a/BizHawk.Emulation/Log.cs
+++ b/BizHawk.Emulation/Log.cs
@@ -10,11 +10,12 @@ namespace BizHawk
{
// You can set current desired logging settings here.
// Production builds should be done with all logging disabled.
- //LogToConsole = true;
+ LogToConsole = true;
//LogToFile = true;
//LogFilename = "d:/bizhawk.log";
+ //EnableDomain("CD");
//EnableDomain("CPU");
- //EnableDomain("VDC");
+ //EnableDomain("VDC");
//EnableDomain("MEM");
}
diff --git a/BizHawk.Emulation/QuickCollections.cs b/BizHawk.Emulation/QuickCollections.cs
index 877d1e332a..923c0f0a07 100644
--- a/BizHawk.Emulation/QuickCollections.cs
+++ b/BizHawk.Emulation/QuickCollections.cs
@@ -53,7 +53,7 @@ namespace BizHawk
buffer = new T[capacity];
}
- public int Count { get { return tail - head; } }
+ public int Count { get { return size; } }
public void Enqueue(T item)
{
@@ -83,6 +83,18 @@ namespace BizHawk
size = 0;
}
+ public T[] GetBuffer()
+ {
+ return buffer;
+ }
+
+ public void SignalBufferFilled(int count)
+ {
+ head = 0;
+ tail = count;
+ size = count;
+ }
+
// TODO serialization functions
}
diff --git a/BizHawk.Emulation/Sound/HuC6280PSG.cs b/BizHawk.Emulation/Sound/HuC6280PSG.cs
index 34377363b1..18b27af98e 100644
--- a/BizHawk.Emulation/Sound/HuC6280PSG.cs
+++ b/BizHawk.Emulation/Sound/HuC6280PSG.cs
@@ -30,7 +30,7 @@ namespace BizHawk.Emulation.Sound
private byte WaveTableWriteOffset;
private Queue commands = new Queue(256);
- private int frameStartTime, frameStopTime;
+ private long frameStartTime, frameStopTime;
private const int SampleRate = 44100;
private const int PsgBase = 3580000;
@@ -47,7 +47,7 @@ namespace BizHawk.Emulation.Sound
Channels[i] = new PSGChannel();
}
- public void BeginFrame(int cycles)
+ public void BeginFrame(long cycles)
{
while (commands.Count > 0)
{
@@ -57,12 +57,12 @@ namespace BizHawk.Emulation.Sound
frameStartTime = cycles;
}
- public void EndFrame(int cycles)
+ public void EndFrame(long cycles)
{
frameStopTime = cycles;
}
- public void WritePSG(byte register, byte value, int cycles)
+ public void WritePSG(byte register, byte value, long cycles)
{
commands.Enqueue(new QueuedCommand { Register = register, Value = value, Time = cycles-frameStartTime });
}
@@ -132,12 +132,12 @@ namespace BizHawk.Emulation.Sound
public void DiscardSamples() { /*TBD*/ }
public void GetSamples(short[] samples)
{
- int elapsedCycles = frameStopTime - frameStartTime;
+ int elapsedCycles = (int) (frameStopTime - frameStartTime);
int start = 0;
while (commands.Count > 0)
{
var cmd = commands.Dequeue();
- int pos = ((cmd.Time * samples.Length) / elapsedCycles) & ~1;
+ int pos = (int) ((cmd.Time * samples.Length) / elapsedCycles) & ~1;
MixSamples(samples, start, pos - start);
start = pos;
WritePSGImmediate(cmd.Register, cmd.Value);
@@ -348,7 +348,7 @@ namespace BizHawk.Emulation.Sound
{
public byte Register;
public byte Value;
- public int Time;
+ public long Time;
}
}
}
\ No newline at end of file
diff --git a/BizHawk.Emulation/Sound/Utilities/Metaspu.cs b/BizHawk.Emulation/Sound/Utilities/Metaspu.cs
index 9c1b8defa0..a3eca1c2d6 100644
--- a/BizHawk.Emulation/Sound/Utilities/Metaspu.cs
+++ b/BizHawk.Emulation/Sound/Utilities/Metaspu.cs
@@ -10,15 +10,24 @@ namespace BizHawk.Emulation.Sound
{
buffer = Metaspu.metaspu_construct(method);
}
- public MetaspuSoundProvider()
- : this(ESynchMethod.ESynchMethod_Z)
+
+ public MetaspuSoundProvider() : this(ESynchMethod.ESynchMethod_V)
{
}
+ private short[] pullBuffer = new short[1470];
+ public void PullSamples(ISoundProvider source)
+ {
+ Array.Clear(pullBuffer, 0, 1470);
+ source.GetSamples(pullBuffer);
+ buffer.enqueue_samples(pullBuffer, 735);
+ }
+
public void GetSamples(short[] samples)
{
buffer.output_samples(samples, samples.Length / 2);
}
+
public void DiscardSamples()
{
buffer.clear();
@@ -573,16 +582,13 @@ namespace BizHawk.Emulation.Sound
public void enqueue_samples(short[] buf, int samples_provided)
{
- throw new Exception("bluh");
- int samplesToEnqueue = samples_provided;
- if (samples_provided + buffer.Count > MaxExcessSamples)
- samplesToEnqueue = MaxExcessSamples - buffer.Count;
-
- //for (int i = 0; i < samplesToEnqueue; i++)
- //buffer.Enqueue(buf[i]);
-
- Console.WriteLine("enqueue {0} samples, buffer at {1}/4096 max capacity, {2} excess samples",
- samplesToEnqueue, buffer.Count, buffer.Count - SamplesInOneFrame);
+ int ctr = 0;
+ for (int i = 0; i < samples_provided; i++)
+ {
+ short left = buf[ctr++];
+ short right = buf[ctr++];
+ enqueue_sample(left, right);
+ }
}
public void enqueue_sample(short left, short right)
@@ -597,7 +603,6 @@ namespace BizHawk.Emulation.Sound
public void clear()
{
- Console.WriteLine("clear requested... but why! it makes me sad :'(");
buffer.Clear();
}
@@ -611,11 +616,6 @@ namespace BizHawk.Emulation.Sound
// if we're within 75% of target, then I guess we suck it up and resample.
// we sample in a goofy way, we could probably do it a bit smarter, if we cared more.
- Console.WriteLine("REASONABLE UNDERFLOW, RESAMPLING.");
-
- if (samples_requested > 2730)
- throw new Exception("something rather bad has happened");
-
int samples_available = buffer.Count;
for (int i = 0; buffer.Count > 0; i++)
resampleBuffer[i] = buffer.Dequeue();
@@ -627,17 +627,15 @@ namespace BizHawk.Emulation.Sound
buf[index++] = sample.left;
buf[index++] = sample.right;
}
-
-
} else {
// we're outside of a "reasonable" underflow. Give up and output silence.
- Console.WriteLine("EXCESSIVE UNDERFLOW. GIVE UP AND MAKE A POP");
+ // Do nothing. The whole frame will be excess buffer.
}
}
else
{
// normal operation
- Console.WriteLine("samples in buffer {0}, requested {1}", buffer.Count, samples_requested);
+ //Console.WriteLine("samples in buffer {0}, requested {1}", buffer.Count, samples_requested);
int index = 0;
for (int i = 0; i < samples_requested && buffer.Count > 0; i++)
{
diff --git a/BizHawk.MultiClient/BizHawk.MultiClient.csproj b/BizHawk.MultiClient/BizHawk.MultiClient.csproj
index 2e2e8e88e6..2392a211f4 100644
--- a/BizHawk.MultiClient/BizHawk.MultiClient.csproj
+++ b/BizHawk.MultiClient/BizHawk.MultiClient.csproj
@@ -579,9 +579,6 @@
-
-
-
diff --git a/BizHawk.MultiClient/Config.cs b/BizHawk.MultiClient/Config.cs
index e648e50b05..366a142d62 100644
--- a/BizHawk.MultiClient/Config.cs
+++ b/BizHawk.MultiClient/Config.cs
@@ -55,14 +55,14 @@
public string PathSMSScreenshots = ".\\Screenshots";
public string PathSMSCheats = ".\\Cheats";
- public string BaseGG = ".\\GG";
+ public string BaseGG = ".\\Game Gear";
public string PathGGROMs = ".";
public string PathGGSavestates = ".\\State";
public string PathGGSaveRAM = ".\\SaveRAM";
public string PathGGScreenshots = ".\\Screenshots";
public string PathGGCheats = ".\\Cheats";
- public string BaseSG = ".\\GG";
+ public string BaseSG = ".\\SG-1000";
public string PathSGROMs = ".";
public string PathSGSavestates = ".\\State";
public string PathSGSaveRAM = ".\\SaveRAM";
@@ -76,7 +76,7 @@
public string PathGenesisScreenshots = ".\\Screenshots";
public string PathGenesisCheats = ".\\Cheats";
- public string BasePCE = ".\\PCE";
+ public string BasePCE = ".\\PC Engine";
public string PathPCEROMs = ".";
public string PathPCESavestates = ".\\State";
public string PathPCESaveRAM = ".\\SaveRAM";
diff --git a/BizHawk.MultiClient/MainForm.cs b/BizHawk.MultiClient/MainForm.cs
index 41d3609136..e1997366d0 100644
--- a/BizHawk.MultiClient/MainForm.cs
+++ b/BizHawk.MultiClient/MainForm.cs
@@ -945,7 +945,7 @@ namespace BizHawk.MultiClient
game = new GameInfo();
game.System = "PCE";
- game.Name = file.Name;
+ game.Name = Path.GetFileNameWithoutExtension(file.Name);
}
switch (game.System)
@@ -953,10 +953,14 @@ namespace BizHawk.MultiClient
case "PCE":
if (File.Exists(Global.Config.PathPCEBios) == false)
{
- MessageBox.Show("PCE-CD System Card not found. Please check the BIOS path in Config->Paths.");
+ MessageBox.Show("PCE-CD System Card not found. Please check the BIOS path in Config->Paths->PC Engine.");
return false;
}
rom = new RomGame(new HawkFile(Global.Config.PathPCEBios));
+ if (rom.GameInfo["SuperSysCard"])
+ game.AddOption("SuperSysCard");
+ if ((game["NeedSuperSysCard"]) && game["SuperSysCard"] == false)
+ MessageBox.Show("This game requires a version 3.0 System card and won't run with the system card you've selected. Try selecting a 3.0 System Card in Config->Paths->PC Engine.");
nextEmulator = new PCEngine(game, disc, rom.RomData);
break;
}
@@ -2033,8 +2037,8 @@ namespace BizHawk.MultiClient
ofd.InitialDirectory = PathManager.GetRomsPath(Global.Emulator.SystemId);
//"Rom Files|*.NES;*.SMS;*.GG;*.SG;*.PCE;*.SGX;*.GB;*.BIN;*.SMD;*.ROM;*.ZIP;*.7z|NES (*.NES)|*.NES|Master System|*.SMS;*.GG;*.SG;*.ZIP;*.7z|PC Engine|*.PCE;*.SGX;*.ZIP;*.7z|Gameboy|*.GB;*.ZIP;*.7z|TI-83|*.rom|Archive Files|*.zip;*.7z|Savestate|*.state|All Files|*.*";
ofd.Filter = FormatFilter(
- "Rom Files", "*.nes;*.sms;*.gg;*.sg;*.pce;*.sgx;*.gb;*.bin;*.smd;*.rom;*.iso;%ARCH%",
- "Disc Images", "*.iso",
+ "Rom Files", "*.nes;*.sms;*.gg;*.sg;*.pce;*.sgx;*.gb;*.bin;*.smd;*.rom;*.cue;%ARCH%",
+ "Disc Images", "*.cue",
"NES", "*.nes;%ARCH%",
"Master System", "*.sms;*.gg;*.sg;%ARCH%",
"PC Engine", "*.pce;*.sgx;%ARCH%",
diff --git a/BizHawk.MultiClient/output/gamedb_pce_cd.txt b/BizHawk.MultiClient/output/gamedb_pce_cd.txt
index bfa822de8b..632b924ab6 100644
--- a/BizHawk.MultiClient/output/gamedb_pce_cd.txt
+++ b/BizHawk.MultiClient/output/gamedb_pce_cd.txt
@@ -1 +1,80 @@
-067CB059A300BD731E7620803CAE044E Bomberman '94 Special Version PCE
\ No newline at end of file
+3E9ED52416C4642096EC78B16AFA3956 Alzadick PCE
+3770A9888388B41A10C28841F9EFD15B Black Hole Assault PCE NeedSuperSysCard
+147F46AA1E72C204488035ABB6E8DD37 Blood Gear PCE NeedSuperSysCard
+067CB059A300BD731E7620803CAE044E Bomberman '94 Special Version PCE NeedSuperSysCard
+F4BCF27A1407EDD8AAF51C34E3EAF0C3 Bonk 3 PCE NeedSuperSysCard
+491B7FAB01C8A4017221270EFBDBFF37 Brandish PCE NeedSuperSysCard;ArcadeCard
+5835601EC3879D4E5161DE2BC30BDF44 Cardangels PCE NeedSuperSysCard
+60AAF2DA21D2AA0BADFCFC0EDE4006D5 Cosmic Fantasy II (U) PCE
+EDD630721F0719F1648A2E00621AD7B7 Cotton (U) PCE NeedSuperSysCard
+78009190135209BE042D8355B647BE35 Dracula X - Rondo of Blood PCE NeedSuperSysCard
+F6869EBAB1F3F5927B92F59DCF34D512 Double Dragon 2 - The Revenge PCE NeedSuperSysCard
+DBF82B69AB6AE97E7E16F64FC9267253 Download II PCE
+0C260D7C5758E786D80D75ED60E135B8 Dragon Slayer (U) PCE NeedSuperSysCard
+00C357F33118BCD033F98952370126A8 Dungeon Explorer II (U) PCE NeedSuperSysCard
+65069522E26B5A41F457ADAF97474889 Dungeon Master - Theron's Quest (U) PCE NeedSuperSysCard
+3CCA1F2CEFC009906EDC19BD5AD30138 Emerald Dragon PCE NeedSuperSysCard
+E5C69B9D84AEA0641041FB355F66B33A Exile (U) PCE
+6DCB244E33963B5F82B0B96B1BA4A4A5 Exile II - Wicked Phenomenon (U) PCE NeedSuperSysCard
+A067C38F8D5E3AB35FA0EB238C141B81 Fantasy Star Soldeier PCE NeedSuperSysCard
+68C794604F210B82D21C5B9DCFAB4596 Final Zone II (U) PCE
+71CB34D94D36C19901EA67B6355CDC98 Flash Hiders PCE NeedSuperSysCard
+579CFB162AF2B56BB5D178CE32AEC328 Forgotten Worlds (U) PCE NeedSuperSysCard
+E7C724F744BE836B955D212160F5475E Gain Ground SX PCE NeedSuperSysCard
+5D4E8379FDA5160C4400D926ADEA0F92 Gates Of Thunder (U) PCE NeedSuperSysCard
+619424C78C17F4D759994810B3253048 God Panic PCE NeedSuperSysCard
+5B969960E0B1DA41BCF1A2352724EB63 Golden Axe PCE
+7F83141788B5A94A339689FD5F4A0840 Gradius II PCE NeedSuperSysCard
+C96792AA34C6EA050DEBD1EDA5944046 Hellfire S PCE
+9808305827B37767FA8F0A201E8ADD43 Image Fight II PCE NeedSuperSysCard
+692AC5B78CB31DD880E652E8CBDF0AFC Jim Power - In Mutant Planet PCE NeedSuperSysCard
+83A2F0661448457DF7AF2B39FCFEFA26 Kaze Kiri Ninja Action PCE NeedSuperSysCard
+23E7D0D4E468A6F2A801049BA5D36D41 Kiaidan 00 PCE NeedSuperSysCard
+6A556ED91862FA957866661E47F35320 Langrisser - Hikari no Matseui PCE NeedSuperSysCard
+33D4467131049E22E34A02DBA43D3F3D Last Alert PCE
+0CF51518CCE1F807D5CF92ACBB6BEE33 Legion PCE
+26873893BAE98A25DF06118228B2B470 Lemmings PCE NeedSuperSysCard
+8C26362AB1C7FA22AE981431500E40DF Loom (U) PCE NeedSuperSysCard
+4EA3DA589C2A40D7E114B316FEA353A4 Lords of Thunder PCE NeedSuperSysCard
+FD8166D038239F1AEF641B14AF0F6BC4 Macross 2036 PCE
+F3C6E4A5BF25C1D0A6219FCCF8933046 Mad Stalker - Full Metal Force PCE NeedSuperSysCard;ArcadeCard
+52CA7465F10DDA85DC2D17B96A508621 Magical Fantasy Adventure - Popful Mail PCE NeedSuperSysCard;ArcadeCard;
+A82FA56B18356FA2553E445B92FD0317 Metamor Jupiter PCE NeedSuperSysCard
+A9B7FE6A879DBC47BA83579ECBE8FE2A Might and Magic III - Isles of Terra (U) PCE NeedSuperSysCard
+BFB522337FB86B6A029894442DEE2647 Monster Lair (U) PCE
+CF7EBAE2FEAC0F968F2C7D7C815F53F3 Motoroader MC PCE NeedSuperSysCard
+A52AB896E603673E6EA30C1769449DF4 Mystic Formula PCE NeedSuperSysCard
+8C793F868A30A176EBB02CADE64F7F57 Nexzr PCE NeedSuperSysCard
+C80A4ACE402AB5D7D72D2CA643A108A6 Prince of Persia (U) PCE NeedSuperSysCard
+F338D84D2B3E77DD81D611964C0D460F Psychic Storm PCE NeedSuperSysCard
+B16C8BC632CBDEC676B9874E39975DDD Puyo Puyo CD PCE NeedSuperSysCard
+9398887DB9C2771F1FBB3D4FEF561A14 Rayxanber II PCE
+7FEC58BDB95CBF1F3AE553933DB5F60B Rayxanber III PCE NeedSuperSysCard
+EFF6C174835503B034CF6A4079627DBD Road Spirits PCE
+FBC374F66ED4228120B74D327DE81298 R-Type Complete CD PCE NeedSuperSysCard
+5D16FE7D452DC9B46CAA7A436F45468D Sapphire PCE NeedSuperSysCard;ArcadeCard
+D67A425615770ECE13CF5157CA32EF61 Shubibinman III PCE
+E6CB40FD1E315B84D09100915DCABF9C Shadow of the Beast(U) PCE NeedSuperSysCard
+C8559632FE3EA3B4FBABBEF80865B36C Shape Shifter (U) PCE NeedSuperSysCard
+B2FEE487FBE1CB5A81F1FAAA5C49AF0A Sim Earth PCE NeedSuperSysCard
+C0DD9A7A52ACF8D98F0BA36F907C486A Snatcher PCE NeedSuperSysCard
+8E352778CD8A5530761DFD4127D1BBE2 Splash Lake (U) PCE
+25607EDD1F77B2C993185A8C2DBD785E Spriggan PCE
+506B41BDEEB6EA6BD4AFA89C9C594BEE Spriggan Mark II PCE NeedSuperSysCard
+DEF4EAE3C3565FAA7C1CADCAB61C89A3 Strider Hiryu PCE NeedSuperSysCard;ArcadeCard
+23484EC2987B58B93285E1EC827BE839 Super Air Zonk (U) PCE NeedSuperSysCard
+6BB02DEEA591C30A71CA201EA6A84197 Super Darius II PCE NeedSuperSysCard
+AE912784A40DEE31133F33ABFAB9522C Super Raiden PCE NeedSuperSysCard
+ECFD4A849AF8C7C88B8DAD3A9CFD804E Super Schwartzschild PCE NeedSuperSysCard
+3355BDD8F966E413E31A50412B18A282 Super Schwartzschild II PCE NeedSuperSysCard
+065F306DD079E506F953251FDCCE2444 Syd Mead's Terraforming PCE
+9E9BA0A787C06167919857AAC3505E11 Sylphia PCE NeedSuperSysCard
+BCB0530021FE2E2EAAA258D3C61EF2E4 Valis PCE NeedSuperSysCard
+05A0213934814E08EC6E7D4112200BD6 Valis II (U) PCE
+187AF2A604CC66A9790B547D04745BF8 Valis III (U) PCE
+8BA20724E08300C69E2F67DA76E1F0D2 Valis IV PCE
+FB571B290600A18B90E1BBF1D18FC8AA World Heroes II PCE NeedSuperSysCard;ArcadeCard
+BA451E33D76FD6C644F46309708BABF2 Ys Book I & II (U) PCE
+725BF9524040917AC8D5DD1765483527 Ys III - Wanderers from Ys (U) PCE
+844E0E4D40BE2EBD5550429AE53EAFB2 Ys IV - The Dawn of Ys PCE NeedSuperSysCard
+1F91B1245003DB964C35AFE6A165964E Zero Wing PCE
\ No newline at end of file
diff --git a/BizHawk.MultiClient/output/gamedb_pce_hucards.txt b/BizHawk.MultiClient/output/gamedb_pce_hucards.txt
index 6503f6de5d..367838032e 100644
--- a/BizHawk.MultiClient/output/gamedb_pce_hucards.txt
+++ b/BizHawk.MultiClient/output/gamedb_pce_hucards.txt
@@ -1143,11 +1143,11 @@ B915FC01C039C1B0003436432CB73AA0 V Tatsujin (J) [b3] PCE
EEA13EF4FE8DE1686D0BBDD877579E31 Tatsujin Sounds PCE
8F458B72EC050504A1B68C49DCD6942C Ten no Koe Memory Bank (J) [o1] PCE
E0047B06DC84CB242CCED8C5FA7EFAFE Ten no Koe Memory Bank (J) PCE
-B724F9514E1F44AA3BCB4C678C06D51D V Tenseiryuu - Saint Dragon (J) [b1] PCE
-C0E0C5C42ACF339155E11293FF2629EE V Tenseiryuu - Saint Dragon (J) [b1][o1] PCE
-B9BD4CE494C2DFD4663D3E366E41422C V Tenseiryuu - Saint Dragon (J) [b2][o1] PCE
-BFEE45769C69CBF4AE7761F650F06E99 Tenseiryuu - Saint Dragon (J) [o1] PCE
-92857243D4E985F1A58BEC9CB2E16D58 Tenseiryuu - Saint Dragon (J) PCE
+B724F9514E1F44AA3BCB4C678C06D51D V Tenseiryuu - Saint Dragon (J) [b1] PCE HBlankPeriod=83
+C0E0C5C42ACF339155E11293FF2629EE V Tenseiryuu - Saint Dragon (J) [b1][o1] PCE HBlankPeriod=83
+B9BD4CE494C2DFD4663D3E366E41422C V Tenseiryuu - Saint Dragon (J) [b2][o1] PCE HBlankPeriod=83
+BFEE45769C69CBF4AE7761F650F06E99 Tenseiryuu - Saint Dragon (J) [o1] PCE HBlankPeriod=83
+92857243D4E985F1A58BEC9CB2E16D58 Tenseiryuu - Saint Dragon (J) PCE HBlankPeriod=83
7C7A752CB98A2E2AC8F9BD3D6B2EC581 Tenseiryuu - Saint Dragon Sounds PCE
64716E1013C5230E25F0249D8E641D5B Terra Cresta II - Mandrer no Gyakushuu (J) PCE
F914D101D22CB30D8CE5CAA5775E00F8 Terra Cresta II - Mandrer no Gyakushuu Sounds PCE
@@ -1592,10 +1592,10 @@ FFD159AF6240051B15867476B6A9B808 I CD-ROM System V2.01 (U) PCE BRAM
F544FEE6DCD0162A40C2C612DF360B2D H CD-ROM System V2.10 (J) (reports V1) [h1] PCE BRAM
34C93E053615758218FAC19A3900723E V CD-ROM System V2.10 (J) [b1] PCE BRAM
3CDD6614A918616BFC41C862E889DD79 I CD-ROM System V2.10 (J) PCE BRAM
-FC3D75364EF8CCCB4FA6B633E4BD5258 V Super CD-ROM2 System V3.00 (J) [o1] PCE BRAM
-CB65B86FAABF0F4717CF8C84230B187C V Super CD-ROM2 System V3.00 (J) [o2] PCE BRAM
-0FD6A4CFD78F2242025370D39AA3F287 V Super CD-ROM2 System V3.00 (J) [o3] PCE BRAM
-D3A12A001E22EFB1436EC509D453A10F V Super CD-ROM2 System V3.00 (J) [o4] PCE BRAM
-38179DF8F4AC870017DB21EBCBF53114 I Super CD-ROM2 System V3.00 (J) PCE BRAM
-0754F903B52E3B3342202BDAFB13EFA5 I Super CD-ROM2 System V3.00 (U) PCE BRAM
-98D43A097A165B03DF170FD5C2AD2C2F I Super CD-ROM2 System V3.01 (U) PCE BRAM
\ No newline at end of file
+FC3D75364EF8CCCB4FA6B633E4BD5258 V Super CD-ROM2 System V3.00 (J) [o1] PCE BRAM;SuperSysCard
+CB65B86FAABF0F4717CF8C84230B187C V Super CD-ROM2 System V3.00 (J) [o2] PCE BRAM;SuperSysCard
+0FD6A4CFD78F2242025370D39AA3F287 V Super CD-ROM2 System V3.00 (J) [o3] PCE BRAM;SuperSysCard
+D3A12A001E22EFB1436EC509D453A10F V Super CD-ROM2 System V3.00 (J) [o4] PCE BRAM;SuperSysCard
+38179DF8F4AC870017DB21EBCBF53114 I Super CD-ROM2 System V3.00 (J) PCE BRAM;SuperSysCard
+0754F903B52E3B3342202BDAFB13EFA5 I Super CD-ROM2 System V3.00 (U) PCE BRAM;SuperSysCard
+98D43A097A165B03DF170FD5C2AD2C2F I Super CD-ROM2 System V3.01 (U) PCE BRAM;SuperSysCard
\ No newline at end of file