Extremely preliminary TurboCD support

This commit is contained in:
beirich 2011-08-14 18:20:13 +00:00
parent cfccc65672
commit 0a5157d9a6
20 changed files with 1159 additions and 398 deletions

View File

@ -112,8 +112,9 @@
<Compile Include="Consoles\Nintendo\NES\PPU.cs" />
<Compile Include="Consoles\Nintendo\NES\PPU.regs.cs" />
<Compile Include="Consoles\Nintendo\NES\PPU.run.cs" />
<Compile Include="Consoles\PC Engine\MemoryMap.TurboCD.cs" />
<Compile Include="Consoles\PC Engine\MemoryMap.Populous.cs" />
<Compile Include="Consoles\PC Engine\ScsiCD.cs" />
<Compile Include="Consoles\PC Engine\ScsiCDBus.cs" />
<Compile Include="Consoles\PC Engine\TurboCD.cs" />
<Compile Include="Consoles\Sega\SMS\MemoryMap.CodeMasters.cs" />
<Compile Include="Consoles\Sega\SMS\MemoryMap.Sega.cs" />
@ -222,6 +223,7 @@
<Compile Include="Interfaces\Base Implementations\NullEmulator.cs" />
<Compile Include="Interfaces\CoreComms.cs" />
<Compile Include="QuickCollections.cs" />
<Compile Include="Sound\CDAudio.cs" />
<Compile Include="Sound\Utilities\BufferedAsync.cs" />
<Compile Include="Sound\Utilities\Metaspu.cs" />
<Compile Include="Interfaces\IController.cs" />

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<byte> CommandBuffer = new QuickList<byte>(10); // 10 = biggest command
public QuickQueue<byte> DataIn = new QuickQueue<byte>(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<bool> DataTransferReady;
public Action<bool> 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;
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -30,7 +30,7 @@ namespace BizHawk.Emulation.Sound
private byte WaveTableWriteOffset;
private Queue<QueuedCommand> commands = new Queue<QueuedCommand>(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;
}
}
}

View File

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

View File

@ -579,9 +579,6 @@
<ItemGroup>
<None Include="config\ControllerImages\GBController.png" />
</ItemGroup>
<ItemGroup>
<None Include="config\ControllerImages\220px-TurboGrafx-16-Controller.jpg" />
</ItemGroup>
<ItemGroup>
<None Include="config\ControllerImages\PCEngineController.png" />
</ItemGroup>

View File

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

View File

@ -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%",

View File

@ -1 +1,80 @@
067CB059A300BD731E7620803CAE044E Bomberman '94 Special Version PCE
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

View File

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