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