diff --git a/Src/Model3/DSB.cpp b/Src/Model3/DSB.cpp
index 2efeed5..d2b694e 100644
--- a/Src/Model3/DSB.cpp
+++ b/Src/Model3/DSB.cpp
@@ -509,6 +509,10 @@ void CDSB1::SaveState(CBlockFile *StateFile)
// Z80 CPU state
Z80.SaveState(StateFile, "DSB1 Z80");
+
+ //DEBUG
+ //printf("usingMPEGStart=%X usingMPEGEnd=%X\n", usingMPEGStart, usingMPEGEnd);
+ //printf("usingLoopStart=%X usingLoopEnd=%X\n", usingLoopStart, usingLoopEnd);
}
void CDSB1::LoadState(CBlockFile *StateFile)
@@ -554,6 +558,10 @@ void CDSB1::LoadState(CBlockFile *StateFile)
}
else
MPEG_StopPlaying();
+
+ //DEBUG
+ //printf("usingMPEGStart=%X usingMPEGEnd=%X\n", usingMPEGStart, usingMPEGEnd);
+ //printf("usingLoopStart=%X usingLoopEnd=%X\n", usingLoopStart, usingLoopEnd);
}
// Offsets of memory regions within DSB1's pool
@@ -1065,6 +1073,11 @@ void CDSB2::SaveState(CBlockFile *StateFile)
// 68K CPU state
M68KSetContext(&M68K);
M68KSaveState(StateFile, "DSB2 68K");
+
+ //DEBUG
+ //printf("mpegStart=%X, mpegEnd=%X\n", mpegStart, mpegEnd);
+ //printf("usingMPEGStart=%X, usingMPEGEnd=%X\n", usingMPEGStart, usingMPEGEnd);
+ //printf("usingLoopStart=%X, usingLoopEnd=%X\n", usingLoopStart, usingLoopEnd);
}
void CDSB2::LoadState(CBlockFile *StateFile)
diff --git a/Src/Model3/Model3.cpp b/Src/Model3/Model3.cpp
index f61650f..0c88969 100644
--- a/Src/Model3/Model3.cpp
+++ b/Src/Model3/Model3.cpp
@@ -158,10 +158,10 @@
* G20 View Change
*
* Lost World, LA Machineguns:
- * G20 Gun trigger
- *
- * Star Wars Trilogy:
- * G20 Event button
+ * G20 Gun trigger
+ *
+ * Star Wars Trilogy:
+ * G20 Event button
* G25 Trigger
*
* Virtual On 2:
@@ -795,18 +795,18 @@ void CModel3::WriteSystemRegister(unsigned reg, UINT8 data)
/******************************************************************************
- Optimized Address Space Access Handlers
-
- Although I have not yet profiled the code, these ought to be more optimal,
- especially if the compiler can generate jump tables.
-
- NOTE: Testing of some of the address ranges is not strict enough, especially
- for the MPC10x. Write32() handles the MPC10x most correctly. For now,
- accesses outside of the handled ranges have not been observed. Use the DEBUG
+ Optimized Address Space Access Handlers
+
+ Although I have not yet profiled the code, these ought to be more optimal,
+ especially if the compiler can generate jump tables.
+
+ NOTE: Testing of some of the address ranges is not strict enough, especially
+ for the MPC10x. Write32() handles the MPC10x most correctly. For now,
+ accesses outside of the handled ranges have not been observed. Use the DEBUG
version of these handlers for validation of new games.
******************************************************************************/
-
-#ifndef DEBUG
+
+#ifndef DEBUG
/*
* CModel3::Read8(addr):
@@ -816,33 +816,33 @@ void CModel3::WriteSystemRegister(unsigned reg, UINT8 data)
*
* Read handlers.
*/
-UINT8 CModel3::Read8(UINT32 addr)
-{
- // RAM (most frequently accessed)
+UINT8 CModel3::Read8(UINT32 addr)
+{
+ // RAM (most frequently accessed)
if (addr<0x00800000)
- return ram[addr^3];
-
- // Other
- switch ((addr>>24))
- {
- // CROM
- case 0xFF:
+ return ram[addr^3];
+
+ // Other
+ switch ((addr>>24))
+ {
+ // CROM
+ case 0xFF:
if (addr < 0xFF800000)
return cromBank[(addr&0x7FFFFF)^3];
else
- return crom[(addr&0x7FFFFF)^3];
-
- // Real3D DMA
- case 0xC2:
- return GPU.ReadDMARegister8(addr&0xFF);
-
- // Various
- case 0xF0:
- case 0xFE: // mirror
-
- switch ((addr>>16)&0xFF)
- {
- // Inputs
+ return crom[(addr&0x7FFFFF)^3];
+
+ // Real3D DMA
+ case 0xC2:
+ return GPU.ReadDMARegister8(addr&0xFF);
+
+ // Various
+ case 0xF0:
+ case 0xFE: // mirror
+
+ switch ((addr>>16)&0xFF)
+ {
+ // Inputs
case 0x04:
return ReadInputs(addr&0x3F);
@@ -850,46 +850,46 @@ UINT8 CModel3::Read8(UINT32 addr)
case 0x08:
if ((addr&0xF) == 4) // MIDI control port
return 0xFF; // one of these bits (0x80?) indicates "ready"
- break;
-
- // System registers
- case 0x10:
- return ReadSystemRegister(addr&0x3F);
-
- // RTC
- case 0x14:
+ break;
+
+ // System registers
+ case 0x10:
+ return ReadSystemRegister(addr&0x3F);
+
+ // RTC
+ case 0x14:
if ((addr&3)==1) // battery voltage test
return 0x03;
else if ((addr&3)==0)
return RTC.ReadRegister((addr>>2)&0xF);
- return 0;
-
- // Unknown
- default:
- break;
- }
-
- break;
-
- // 53C810 SCSI
- case 0xC0: // only on Step 1.0
- if (Game->step != 0x10) // check for Step 1.0
- break;
- case 0xF9:
- case 0xC1:
- return SCSI.ReadRegister(addr&0xFF);
-
- // Unknown
- default:
- break;
- }
-
+ return 0;
+
+ // Unknown
+ default:
+ break;
+ }
+
+ break;
+
+ // 53C810 SCSI
+ case 0xC0: // only on Step 1.0
+ if (Game->step != 0x10) // check for Step 1.0
+ break;
+ case 0xF9:
+ case 0xC1:
+ return SCSI.ReadRegister(addr&0xFF);
+
+ // Unknown
+ default:
+ break;
+ }
+
DebugLog("PC=%08X\tread8 : %08X\n", ppc_get_pc(), addr);
- return 0xFF;
+ return 0xFF;
}
-UINT16 CModel3::Read16(UINT32 addr)
-{
+UINT16 CModel3::Read16(UINT32 addr)
+{
UINT16 data;
if ((addr&1))
@@ -897,119 +897,119 @@ UINT16 CModel3::Read16(UINT32 addr)
data = Read8(addr+0)<<8;
data |= Read8(addr+1);
return data;
- }
-
- // RAM (most frequently accessed)
+ }
+
+ // RAM (most frequently accessed)
if (addr<0x00800000)
- return *(UINT16 *) &ram[addr^2];
-
- // Other
- switch ((addr>>24))
- {
- // CROM
- case 0xFF:
+ return *(UINT16 *) &ram[addr^2];
+
+ // Other
+ switch ((addr>>24))
+ {
+ // CROM
+ case 0xFF:
if (addr < 0xFF800000)
return *(UINT16 *) &cromBank[(addr&0x7FFFFF)^2];
else
- return *(UINT16 *) &crom[(addr&0x7FFFFF)^2];
-
- // Various
- case 0xF0:
- case 0xFE: // mirror
-
- switch ((addr>>16)&0xFF)
- {
- // Backup RAM
- case 0x0C:
- case 0x0D:
+ return *(UINT16 *) &crom[(addr&0x7FFFFF)^2];
+
+ // Various
+ case 0xF0:
+ case 0xFE: // mirror
+
+ switch ((addr>>16)&0xFF)
+ {
+ // Backup RAM
+ case 0x0C:
+ case 0x0D:
return *(UINT16 *) &backupRAM[(addr&0x1FFFF)^2];
// Sound Board
case 0x08:
//printf("PPC: Read16 %08X\n", addr);
- break;
-
- // MPC105
- case 0xC0: // F0C00CF8
- return PCIBridge.ReadPCIConfigData(16,addr&3);
-
- // MPC106
- case 0xE0:
- case 0xE1:
- case 0xE2:
- case 0xE3:
- case 0xE4:
- case 0xE5:
- case 0xE6:
- case 0xE7:
- case 0xE8:
- case 0xE9:
- case 0xEA:
- case 0xEB:
- case 0xEC:
- case 0xED:
- case 0xEE:
+ break;
+
+ // MPC105
+ case 0xC0: // F0C00CF8
+ return PCIBridge.ReadPCIConfigData(16,addr&3);
+
+ // MPC106
+ case 0xE0:
+ case 0xE1:
+ case 0xE2:
+ case 0xE3:
+ case 0xE4:
+ case 0xE5:
+ case 0xE6:
+ case 0xE7:
+ case 0xE8:
+ case 0xE9:
+ case 0xEA:
+ case 0xEB:
+ case 0xEC:
+ case 0xED:
+ case 0xEE:
case 0xEF:
- return PCIBridge.ReadPCIConfigData(16,addr&3);
-
- // Unknown
- default:
- break;
- }
-
- break;
-
- // Unknown
- default:
- break;
- }
-
+ return PCIBridge.ReadPCIConfigData(16,addr&3);
+
+ // Unknown
+ default:
+ break;
+ }
+
+ break;
+
+ // Unknown
+ default:
+ break;
+ }
+
DebugLog("PC=%08X\tread16: %08X\n", ppc_get_pc(), addr);
- return 0xFFFF;
+ return 0xFFFF;
}
-
-UINT32 CModel3::Read32(UINT32 addr)
-{
- UINT32 data;
-
+
+UINT32 CModel3::Read32(UINT32 addr)
+{
+ UINT32 data;
+
if ((addr&3))
{
data = Read16(addr+0)<<16;
data |= Read16(addr+2);
return data;
- }
-
- // RAM (most frequently accessed)
- if (addr < 0x00800000)
- return *(UINT32 *) &ram[addr];
-
- // Other
- switch ((addr>>24))
- {
- // CROM
- case 0xFF:
+ }
+
+ // RAM (most frequently accessed)
+ if (addr < 0x00800000)
+ return *(UINT32 *) &ram[addr];
+
+ // Other
+ switch ((addr>>24))
+ {
+ // CROM
+ case 0xFF:
if (addr < 0xFF800000)
return *(UINT32 *) &cromBank[(addr&0x7FFFFF)];
else
- return *(UINT32 *) &crom[(addr&0x7FFFFF)];
-
- // Real3D registers
- case 0x84:
- return GPU.ReadRegister(addr&0x3F);
-
- // Real3D DMA
- case 0xC2:
+ return *(UINT32 *) &crom[(addr&0x7FFFFF)];
+
+ // Real3D registers
+ case 0x84:
+ return GPU.ReadRegister(addr&0x3F);
+
+ // Real3D DMA
+ case 0xC2:
data = GPU.ReadDMARegister32(addr&0xFF);
- return FLIPENDIAN32(data);
-
- // Various
- case 0xF0:
- case 0xFE: // mirror
-
- switch ((addr>>16)&0xFF)
- {
- // Inputs
- case 0x04:
+ return FLIPENDIAN32(data);
+
+ // Various
+ case 0xF0:
+ case 0xFE: // mirror
+
+ switch ((addr>>16)&0xFF)
+ {
+ // Inputs
+ case 0x04:
data = ReadInputs((addr&0x3F)+0) << 24;
data |= ReadInputs((addr&0x3F)+1) << 16;
data |= ReadInputs((addr&0x3F)+2) << 8;
@@ -1019,101 +1019,101 @@ UINT32 CModel3::Read32(UINT32 addr)
// Sound Board
case 0x08:
//printf("PPC: Read32 %08X\n", addr);
- break;
-
- // Backup RAM
- case 0x0C:
- case 0x0D:
- return *(UINT32 *) &backupRAM[(addr&0x1FFFF)];
-
- // System registers
- case 0x10:
+ break;
+
+ // Backup RAM
+ case 0x0C:
+ case 0x0D:
+ return *(UINT32 *) &backupRAM[(addr&0x1FFFF)];
+
+ // System registers
+ case 0x10:
data = ReadSystemRegister((addr&0x3F)+0) << 24;
data |= ReadSystemRegister((addr&0x3F)+1) << 16;
data |= ReadSystemRegister((addr&0x3F)+2) << 8;
data |= ReadSystemRegister((addr&0x3F)+3) << 0;
- return data;
-
- // MPC105
- case 0xC0: // F0C00CF8
- return PCIBridge.ReadPCIConfigData(32,0);
-
- // MPC106
- case 0xE0:
- case 0xE1:
- case 0xE2:
- case 0xE3:
- case 0xE4:
- case 0xE5:
- case 0xE6:
- case 0xE7:
- case 0xE8:
- case 0xE9:
- case 0xEA:
- case 0xEB:
- case 0xEC:
- case 0xED:
- case 0xEE:
+ return data;
+
+ // MPC105
+ case 0xC0: // F0C00CF8
+ return PCIBridge.ReadPCIConfigData(32,0);
+
+ // MPC106
+ case 0xE0:
+ case 0xE1:
+ case 0xE2:
+ case 0xE3:
+ case 0xE4:
+ case 0xE5:
+ case 0xE6:
+ case 0xE7:
+ case 0xE8:
+ case 0xE9:
+ case 0xEA:
+ case 0xEB:
+ case 0xEC:
+ case 0xED:
+ case 0xEE:
case 0xEF:
- return PCIBridge.ReadPCIConfigData(32,0);
-
- // RTC
- case 0x14:
+ return PCIBridge.ReadPCIConfigData(32,0);
+
+ // RTC
+ case 0x14:
data = (RTC.ReadRegister((addr>>2)&0xF) << 24);
data |= 0x00030000; // set these bits to pass battery voltage test
- return data;
-
- // Security board RAM
- case 0x18:
- case 0x19:
- return *(UINT32 *) &securityRAM[(addr&0x1FFFF)]; // so far, only 32-bit access observed, so we use little endian access
-
- // Security board registers
- case 0x1A:
- return ReadSecurity(addr&0x3F);
-
- // Unknown
- default:
- break;
- }
-
- break;
-
- // Tile generator
+ return data;
+
+ // Security board RAM
+ case 0x18:
+ case 0x19:
+ return *(UINT32 *) &securityRAM[(addr&0x1FFFF)]; // so far, only 32-bit access observed, so we use little endian access
+
+ // Security board registers
+ case 0x1A:
+ return ReadSecurity(addr&0x3F);
+
+ // Unknown
+ default:
+ break;
+ }
+
+ break;
+
+ // Tile generator
case 0xF1:
if (addr==0xF1180000) // fixes 2D graphics (TO-DO: integrate register reads into TileGen.cpp)
- return 0;
-
+ return 0;
+
// Tile generator accesses its RAM as little endian, must flip for big endian PowerPC
- if (addr < 0xF1120000)
- {
+ if (addr < 0xF1120000)
+ {
data = TileGen.ReadRAM(addr&0x1FFFFF);
- return FLIPENDIAN32(data);
- }
-
- break;
-
- // 53C810 SCSI
- case 0xC0: // only on Step 1.0
- if (Game->step != 0x10) // check for Step 1.0
- break;
- case 0xF9:
- case 0xC1:
+ return FLIPENDIAN32(data);
+ }
+
+ break;
+
+ // 53C810 SCSI
+ case 0xC0: // only on Step 1.0
+ if (Game->step != 0x10) // check for Step 1.0
+ break;
+ case 0xF9:
+ case 0xC1:
data = (SCSI.ReadRegister((addr+0)&0xFF) << 24);
data |= (SCSI.ReadRegister((addr+1)&0xFF) << 16);
data |= (SCSI.ReadRegister((addr+2)&0xFF) << 8);
data |= (SCSI.ReadRegister((addr+3)&0xFF) << 0);
- return data;
-
- // Unknown
- default:
- break;
- }
-
+ return data;
+
+ // Unknown
+ default:
+ break;
+ }
+
DebugLog("PC=%08X\tread32: %08X\n", ppc_get_pc(), addr);
- return 0xFFFFFFFF;
-}
-
+ return 0xFFFFFFFF;
+}
+
UINT64 CModel3::Read64(UINT32 addr)
{
UINT64 data;
@@ -1133,32 +1133,32 @@ UINT64 CModel3::Read64(UINT32 addr)
*
* Write handlers.
*/
-void CModel3::Write8(UINT32 addr, UINT8 data)
-{
- // RAM (most frequently accessed)
- if (addr < 0x00800000)
- {
- ram[addr^3] = data;
- return;
- }
-
- // Other
- switch ((addr>>24))
- {
- // Real3D DMA
- case 0xC2:
- GPU.WriteDMARegister8(addr&0xFF,data);
- break;
-
- // Various
- case 0xF0:
- case 0xFE: // mirror
-
- switch ((addr>>16)&0xFF)
- {
- // Inputs
- case 0x04:
- WriteInputs(addr&0x3F,data);
+void CModel3::Write8(UINT32 addr, UINT8 data)
+{
+ // RAM (most frequently accessed)
+ if (addr < 0x00800000)
+ {
+ ram[addr^3] = data;
+ return;
+ }
+
+ // Other
+ switch ((addr>>24))
+ {
+ // Real3D DMA
+ case 0xC2:
+ GPU.WriteDMARegister8(addr&0xFF,data);
+ break;
+
+ // Various
+ case 0xF0:
+ case 0xFE: // mirror
+
+ switch ((addr>>16)&0xFF)
+ {
+ // Inputs
+ case 0x04:
+ WriteInputs(addr&0x3F,data);
break;
// Sound Board
@@ -1168,322 +1168,322 @@ void CModel3::Write8(UINT32 addr, UINT8 data)
SoundBoard.WriteMIDIPort(data);
else if ((addr&0xF) == 4) // MIDI control port
midiCtrlPort = data;
- break;
-
- // Backup RAM
- case 0x0C:
- case 0x0D:
- backupRAM[(addr&0x1FFFF)^3] = data;
- break;
-
- // System registers
- case 0x10:
- WriteSystemRegister(addr&0x3F,data);
- break;
-
- // RTC
- case 0x14:
+ break;
+
+ // Backup RAM
+ case 0x0C:
+ case 0x0D:
+ backupRAM[(addr&0x1FFFF)^3] = data;
+ break;
+
+ // System registers
+ case 0x10:
+ WriteSystemRegister(addr&0x3F,data);
+ break;
+
+ // RTC
+ case 0x14:
if ((addr&3)==0)
- RTC.WriteRegister((addr>>2)&0xF,data);
- break;
-
- // Unknown
- default:
- break;
- }
-
- DebugLog("PC=%08X\twrite8 : %08X=%02X\n", ppc_get_pc(), addr, data);
- break;
-
- // MPC105/106
- case 0xF8:
- PCIBridge.WriteRegister(addr&0xFF,data);
- break;
-
- // 53C810 SCSI
- case 0xC0: // only on Step 1.0
- if (Game->step != 0x10)
- goto Unknown8;
- case 0xF9:
- case 0xC1:
- SCSI.WriteRegister(addr&0xFF,data);
- break;
-
- // Unknown:
- default:
- Unknown8:
- DebugLog("PC=%08X\twrite8 : %08X=%02X\n", ppc_get_pc(), addr, data);
- break;
- }
+ RTC.WriteRegister((addr>>2)&0xF,data);
+ break;
+
+ // Unknown
+ default:
+ break;
+ }
+
+ DebugLog("PC=%08X\twrite8 : %08X=%02X\n", ppc_get_pc(), addr, data);
+ break;
+
+ // MPC105/106
+ case 0xF8:
+ PCIBridge.WriteRegister(addr&0xFF,data);
+ break;
+
+ // 53C810 SCSI
+ case 0xC0: // only on Step 1.0
+ if (Game->step != 0x10)
+ goto Unknown8;
+ case 0xF9:
+ case 0xC1:
+ SCSI.WriteRegister(addr&0xFF,data);
+ break;
+
+ // Unknown:
+ default:
+ Unknown8:
+ DebugLog("PC=%08X\twrite8 : %08X=%02X\n", ppc_get_pc(), addr, data);
+ break;
+ }
}
-
-void CModel3::Write16(UINT32 addr, UINT16 data)
-{
- if ((addr&1))
- {
+
+void CModel3::Write16(UINT32 addr, UINT16 data)
+{
+ if ((addr&1))
+ {
Write8(addr+0,data>>8);
Write8(addr+1,data&0xFF);
return;
- }
-
- // RAM (most frequently accessed)
- if (addr < 0x00800000)
- {
- *(UINT16 *) &ram[addr^2] = data;
- return;
- }
-
- // Other
- switch ((addr>>24))
- {
- // Various
- case 0xF0:
- case 0xFE: // mirror
-
- switch ((addr>>16)&0xFF)
- {
+ }
+
+ // RAM (most frequently accessed)
+ if (addr < 0x00800000)
+ {
+ *(UINT16 *) &ram[addr^2] = data;
+ return;
+ }
+
+ // Other
+ switch ((addr>>24))
+ {
+ // Various
+ case 0xF0:
+ case 0xFE: // mirror
+
+ switch ((addr>>16)&0xFF)
+ {
// Sound Board
case 0x08:
//printf("%08X=%04X\n", addr, data);
break;
- // Backup RAM
- case 0x0C:
- case 0x0D:
- *(UINT16 *) &backupRAM[(addr&0x1FFFF)^2] = data;
- break;
-
- // MPC105
- case 0xC0: // F0C00CF8
- PCIBridge.WritePCIConfigData(16,addr&2,data);
- break;
-
- // Unknown
- default:
- break;
- }
-
- DebugLog("PC=%08X\twrite16 : %08X=%04X\n", ppc_get_pc(), addr, data);
- break;
-
- // MPC105/106
- case 0xF8:
+ // Backup RAM
+ case 0x0C:
+ case 0x0D:
+ *(UINT16 *) &backupRAM[(addr&0x1FFFF)^2] = data;
+ break;
+
+ // MPC105
+ case 0xC0: // F0C00CF8
+ PCIBridge.WritePCIConfigData(16,addr&2,data);
+ break;
+
+ // Unknown
+ default:
+ break;
+ }
+
+ DebugLog("PC=%08X\twrite16 : %08X=%04X\n", ppc_get_pc(), addr, data);
+ break;
+
+ // MPC105/106
+ case 0xF8:
// Write in big endian order, like a real PowerPC
PCIBridge.WriteRegister((addr&0xFF)+0,data>>8);
- PCIBridge.WriteRegister((addr&0xFF)+1,data&0xFF);
- break;
-
- // Unknown
- default:
- DebugLog("PC=%08X\twrite16: %08X=%04X\n", ppc_get_pc(), addr, data);
- break;
- }
+ PCIBridge.WriteRegister((addr&0xFF)+1,data&0xFF);
+ break;
+
+ // Unknown
+ default:
+ DebugLog("PC=%08X\twrite16: %08X=%04X\n", ppc_get_pc(), addr, data);
+ break;
+ }
}
-
-void CModel3::Write32(UINT32 addr, UINT32 data)
+
+void CModel3::Write32(UINT32 addr, UINT32 data)
{
if ((addr&3))
{
Write16(addr+0,data>>16);
Write16(addr+2,data);
return;
- }
-
- // RAM (most frequently accessed)
- if (addr<0x00800000)
+ }
+
+ // RAM (most frequently accessed)
+ if (addr<0x00800000)
{
- *(UINT32 *) &ram[addr] = data;
- return;
- }
-
- // Other
- switch ((addr>>24))
- {
- // Real3D trigger
- case 0x88: // 88000000
- GPU.Flush();
- break;
-
- // Real3D low culling RAM
- case 0x8C: // 8C000000-8C400000
- GPU.WriteLowCullingRAM(addr&0x3FFFFF,FLIPENDIAN32(data));
- break;
-
- // Real3D high culling RAM
- case 0x8E: // 8E000000-8E100000
- GPU.WriteHighCullingRAM(addr&0xFFFFF,FLIPENDIAN32(data));
- break;
-
- // Real3D texture port
- case 0x90: // 90000000-90000018
- GPU.WriteTexturePort(addr&0xFF,FLIPENDIAN32(data));
- break;
-
- // Real3D texture FIFO
- case 0x94: // 94000000-94100000
- GPU.WriteTextureFIFO(FLIPENDIAN32(data));
- break;
-
- // Real3D polygon RAM
- case 0x98: // 98000000-98400000
- GPU.WritePolygonRAM(addr&0x3FFFFF,FLIPENDIAN32(data));
- break;
-
- // Real3D DMA
- case 0xC2: // C2000000-C2000100
- GPU.WriteDMARegister32(addr&0xFF,FLIPENDIAN32(data));
- break;
-
- // Various
- case 0xF0:
- case 0xFE: // mirror
-
- switch ((addr>>16)&0xFF)
- {
- // Inputs
- case 0x04:
+ *(UINT32 *) &ram[addr] = data;
+ return;
+ }
+
+ // Other
+ switch ((addr>>24))
+ {
+ // Real3D trigger
+ case 0x88: // 88000000
+ GPU.Flush();
+ break;
+
+ // Real3D low culling RAM
+ case 0x8C: // 8C000000-8C400000
+ GPU.WriteLowCullingRAM(addr&0x3FFFFF,FLIPENDIAN32(data));
+ break;
+
+ // Real3D high culling RAM
+ case 0x8E: // 8E000000-8E100000
+ GPU.WriteHighCullingRAM(addr&0xFFFFF,FLIPENDIAN32(data));
+ break;
+
+ // Real3D texture port
+ case 0x90: // 90000000-90000018
+ GPU.WriteTexturePort(addr&0xFF,FLIPENDIAN32(data));
+ break;
+
+ // Real3D texture FIFO
+ case 0x94: // 94000000-94100000
+ GPU.WriteTextureFIFO(FLIPENDIAN32(data));
+ break;
+
+ // Real3D polygon RAM
+ case 0x98: // 98000000-98400000
+ GPU.WritePolygonRAM(addr&0x3FFFFF,FLIPENDIAN32(data));
+ break;
+
+ // Real3D DMA
+ case 0xC2: // C2000000-C2000100
+ GPU.WriteDMARegister32(addr&0xFF,FLIPENDIAN32(data));
+ break;
+
+ // Various
+ case 0xF0:
+ case 0xFE: // mirror
+
+ switch ((addr>>16)&0xFF)
+ {
+ // Inputs
+ case 0x04:
WriteInputs((addr&0x3F)+0,(data>>24)&0xFF);
WriteInputs((addr&0x3F)+1,(data>>16)&0xFF);
WriteInputs((addr&0x3F)+2,(data>>8)&0xFF);
- WriteInputs((addr&0x3F)+3,(data>>0)&0xFF);
- break;
-
+ WriteInputs((addr&0x3F)+3,(data>>0)&0xFF);
+ break;
+
// Sound Board
case 0x08:
//printf("PPC: %08X=%08X\n", addr, data);
break;
- // Backup RAM
- case 0x0C:
- case 0x0D:
- *(UINT32 *) &backupRAM[(addr&0x1FFFF)] = data;
- break;
-
- // MPC105
- case 0x80: // F0800CF8 (never observed at 0xFExxxxxx)
- PCIBridge.WritePCIConfigAddress(data);
- break;
-
- // MPC105/106
- case 0xC0: case 0xD0: case 0xE0:
- case 0xC1: case 0xD1: case 0xE1:
- case 0xC2: case 0xD2: case 0xE2:
- case 0xC3: case 0xD3: case 0xE3:
- case 0xC4: case 0xD4: case 0xE4:
- case 0xC5: case 0xD5: case 0xE5:
- case 0xC6: case 0xD6: case 0xE6:
- case 0xC7: case 0xD7: case 0xE7:
- case 0xC8: case 0xD8: case 0xE8:
- case 0xC9: case 0xD9: case 0xE9:
- case 0xCA: case 0xDA: case 0xEA:
- case 0xCB: case 0xDB: case 0xEB:
- case 0xCC: case 0xDC: case 0xEC:
- case 0xCD: case 0xDD: case 0xED:
- case 0xCE: case 0xDE: case 0xEE:
- case 0xCF: case 0xDF: case 0xEF:
+ // Backup RAM
+ case 0x0C:
+ case 0x0D:
+ *(UINT32 *) &backupRAM[(addr&0x1FFFF)] = data;
+ break;
+
+ // MPC105
+ case 0x80: // F0800CF8 (never observed at 0xFExxxxxx)
+ PCIBridge.WritePCIConfigAddress(data);
+ break;
+
+ // MPC105/106
+ case 0xC0: case 0xD0: case 0xE0:
+ case 0xC1: case 0xD1: case 0xE1:
+ case 0xC2: case 0xD2: case 0xE2:
+ case 0xC3: case 0xD3: case 0xE3:
+ case 0xC4: case 0xD4: case 0xE4:
+ case 0xC5: case 0xD5: case 0xE5:
+ case 0xC6: case 0xD6: case 0xE6:
+ case 0xC7: case 0xD7: case 0xE7:
+ case 0xC8: case 0xD8: case 0xE8:
+ case 0xC9: case 0xD9: case 0xE9:
+ case 0xCA: case 0xDA: case 0xEA:
+ case 0xCB: case 0xDB: case 0xEB:
+ case 0xCC: case 0xDC: case 0xEC:
+ case 0xCD: case 0xDD: case 0xED:
+ case 0xCE: case 0xDE: case 0xEE:
+ case 0xCF: case 0xDF: case 0xEF:
if ((addr>=0xF0C00CF8) && (addr<0xF0C00D00)) // MPC105
PCIBridge.WritePCIConfigData(32,0,data);
else if ((addr>=0xFEC00000) && (addr<0xFEE00000)) // MPC106
PCIBridge.WritePCIConfigAddress(data);
else if ((addr>=0xFEE00000) && (addr<0xFEF00000)) // MPC106
- PCIBridge.WritePCIConfigData(32,0,data);
- break;
-
- // System registers
- case 0x10:
+ PCIBridge.WritePCIConfigData(32,0,data);
+ break;
+
+ // System registers
+ case 0x10:
WriteSystemRegister((addr&0x3F)+0,(data>>24)&0xFF);
WriteSystemRegister((addr&0x3F)+1,(data>>16)&0xFF);
WriteSystemRegister((addr&0x3F)+2,(data>>8)&0xFF);
- WriteSystemRegister((addr&0x3F)+3,(data>>0)&0xFF);
- break;
-
- // RTC
- case 0x14:
- RTC.WriteRegister((addr>>2)&0xF,data);
- break;
-
- // Security board RAM
- case 0x18:
- *(UINT32 *) &securityRAM[(addr&0x1FFFF)] = data;
- break;
-
- // Security board registers
- case 0x1A:
- WriteSecurity(addr&0x3F,data);
- break;
-
- // Unknown
- default:
- break;
- }
-
- DebugLog("PC=%08X\twrite32: %08X=%08X\n", ppc_get_pc(), addr, data);
- break;
-
- // Tile generator
- case 0xF1:
+ WriteSystemRegister((addr&0x3F)+3,(data>>0)&0xFF);
+ break;
+
+ // RTC
+ case 0x14:
+ RTC.WriteRegister((addr>>2)&0xF,data);
+ break;
+
+ // Security board RAM
+ case 0x18:
+ *(UINT32 *) &securityRAM[(addr&0x1FFFF)] = data;
+ break;
+
+ // Security board registers
+ case 0x1A:
+ WriteSecurity(addr&0x3F,data);
+ break;
+
+ // Unknown
+ default:
+ break;
+ }
+
+ DebugLog("PC=%08X\twrite32: %08X=%08X\n", ppc_get_pc(), addr, data);
+ break;
+
+ // Tile generator
+ case 0xF1:
if (addr < 0xF1120000)
{
// Tile generator accesses its RAM as little endian, must flip for big endian PowerPC
data = FLIPENDIAN32(data);
- TileGen.WriteRAM(addr&0x1FFFFF,data);
+ TileGen.WriteRAM(addr&0x1FFFFF,data);
break;
}
- else if ((addr>=0xF1180000) && (addr<0xF1180100))
+ else if ((addr>=0xF1180000) && (addr<0xF1180100))
{
- TileGen.WriteRegister(addr&0xFF,FLIPENDIAN32(data));
- break;
- }
-
- goto Unknown32;
-
- // MPC105/106
- case 0xF8: // F8FFF000-F8FFF100
+ TileGen.WriteRegister(addr&0xFF,FLIPENDIAN32(data));
+ break;
+ }
+
+ goto Unknown32;
+
+ // MPC105/106
+ case 0xF8: // F8FFF000-F8FFF100
// Write in big endian order, like a real PowerPC
PCIBridge.WriteRegister((addr&0xFF)+0,(data>>24)&0xFF);
PCIBridge.WriteRegister((addr&0xFF)+1,(data>>16)&0xFF);
PCIBridge.WriteRegister((addr&0xFF)+2,(data>>8)&0xFF);
- PCIBridge.WriteRegister((addr&0xFF)+3,data&0xFF);
- break;
-
- // 53C810 SCSI
- case 0xC0: // step 1.0 only
- if (Game->step != 0x10)
- goto Unknown32;
- case 0xF9:
- case 0xC1:
+ PCIBridge.WriteRegister((addr&0xFF)+3,data&0xFF);
+ break;
+
+ // 53C810 SCSI
+ case 0xC0: // step 1.0 only
+ if (Game->step != 0x10)
+ goto Unknown32;
+ case 0xF9:
+ case 0xC1:
SCSI.WriteRegister((addr&0xFF)+0,(data>>24)&0xFF);
SCSI.WriteRegister((addr&0xFF)+1,(data>>16)&0xFF);
SCSI.WriteRegister((addr&0xFF)+2,(data>>8)&0xFF);
- SCSI.WriteRegister((addr&0xFF)+3,data&0xFF);
- break;
-
- // Unknown
- default:
- Unknown32:
- DebugLog("PC=%08X\twrite32: %08X=%08X\n", ppc_get_pc(), addr, data);
- break;
- }
-}
+ SCSI.WriteRegister((addr&0xFF)+3,data&0xFF);
+ break;
+
+ // Unknown
+ default:
+ Unknown32:
+ DebugLog("PC=%08X\twrite32: %08X=%08X\n", ppc_get_pc(), addr, data);
+ break;
+ }
+}
void CModel3::Write64(UINT32 addr, UINT64 data)
{
Write32(addr+0, (UINT32) (data>>32));
Write32(addr+4, (UINT32) data);
-}
-
-#endif
-
-
+}
+
+#endif
+
+
/******************************************************************************
- Debug Mode (Strict) Address Space Access Handlers
-
- Enabled only if DEBUG is defined. These perform stricter checks than the
- "optimized" handlers but may be slower.
+ Debug Mode (Strict) Address Space Access Handlers
+
+ Enabled only if DEBUG is defined. These perform stricter checks than the
+ "optimized" handlers but may be slower.
******************************************************************************/
-
-#ifdef DEBUG
-
+
+#ifdef DEBUG
+
/*
* CModel3::Read8(addr):
* CModel3::Read16(addr):
@@ -1491,7 +1491,7 @@ void CModel3::Write64(UINT32 addr, UINT64 data)
* CModel3::Read64(addr):
*
* Read handlers.
- */
+ */
UINT8 CModel3::Read8(UINT32 addr)
{
if (addr<0x00800000)
@@ -1526,8 +1526,8 @@ UINT8 CModel3::Read8(UINT32 addr)
DebugLog("PC=%08X\tread8 : %08X\n", ppc_get_pc(), addr);
return 0xFF;
-}
-
+}
+
UINT16 CModel3::Read16(UINT32 addr)
{
UINT16 data;
@@ -1554,8 +1554,8 @@ UINT16 CModel3::Read16(UINT32 addr)
DebugLog("PC=%08X\tread16: %08X\n", ppc_get_pc(), addr);
return 0xFFFF;
-}
-
+}
+
UINT32 CModel3::Read32(UINT32 addr)
{
UINT32 data;
@@ -1633,7 +1633,7 @@ UINT32 CModel3::Read32(UINT32 addr)
DebugLog("PC=%08X\tread32: %08X\n", ppc_get_pc(), addr);
return 0xFFFFFFFF;
-}
+}
UINT64 CModel3::Read64(UINT32 addr)
{
@@ -1645,7 +1645,7 @@ UINT64 CModel3::Read64(UINT32 addr)
return data;
}
-
+
/*
* CModel3::Write8(addr, data):
* CModel3::Write16(addr, data):
@@ -1653,7 +1653,7 @@ UINT64 CModel3::Read64(UINT32 addr)
* CModel3::Write64(addr, data):
*
* Write handlers.
- */
+ */
void CModel3::Write8(UINT32 addr, UINT8 data)
{
if (addr<0x00800000)
@@ -1687,8 +1687,8 @@ void CModel3::Write8(UINT32 addr, UINT8 data)
DebugLog("PC=%08X\twrite8 : %08X=%02X\n", ppc_get_pc(), addr, data);
//printf("PC=%08X\twrite8 : %08X=%02X\n", ppc_get_pc(), addr, data);
}
-}
-
+}
+
void CModel3::Write16(UINT32 addr, UINT16 data)
{
if ((addr&1))
@@ -1715,8 +1715,8 @@ void CModel3::Write16(UINT32 addr, UINT16 data)
DebugLog("PC=%08X\twrite16: %08X=%04X\n", ppc_get_pc(), addr, data);
//printf("PC=%08X\twrite16: %08X=%04X\n", ppc_get_pc(), addr, data);
}
-}
-
+}
+
void CModel3::Write32(UINT32 addr, UINT32 data)
{
if ((addr&3))
@@ -1724,8 +1724,8 @@ void CModel3::Write32(UINT32 addr, UINT32 data)
Write16(addr+0,data>>16);
Write16(addr+2,data);
return;
- }
-
+ }
+
if (addr<0x00800000)
*(UINT32 *) &ram[addr] = data;
else if ((addr>=0x88000000) && (addr<0x88000008))
@@ -1807,8 +1807,8 @@ void CModel3::Write64(UINT32 addr, UINT64 data)
Write32(addr+0, (UINT32) (data>>32));
Write32(addr+4, (UINT32) data);
}
-
-
+
+
#endif
@@ -1909,7 +1909,7 @@ void CModel3::ClearNVRAM(void)
}
void CModel3::RunFrame(void)
-{
+{
// See if currently running multi-threaded
if (g_Config.multiThreaded)
{
@@ -1917,8 +1917,8 @@ void CModel3::RunFrame(void)
if (!StartThreads())
goto ThreadError;
- // Wake sound board and drive board threads so they can process a frame
- if (!sndBrdThreadSync->Post() || DriveBoard.IsAttached() && !drvBrdThreadSync->Post())
+ // Wake threads for sound board (if sync'd) and drive board (if attached) so they can process a frame
+ if (syncSndBrdThread && !sndBrdThreadSync->Post() || DriveBoard.IsAttached() && !drvBrdThreadSync->Post())
goto ThreadError;
// At the same time, process a single frame for main board (PPC) in this thread
@@ -1929,7 +1929,7 @@ void CModel3::RunFrame(void)
goto ThreadError;
// Wait for sound board and drive board threads to finish their work (if they haven't done so already)
- while (!sndBrdThreadDone || DriveBoard.IsAttached() && !drvBrdThreadDone)
+ while (syncSndBrdThread && !sndBrdThreadDone || DriveBoard.IsAttached() && !drvBrdThreadDone)
{
if (!notifySync->Wait(notifyLock))
goto ThreadError;
@@ -1957,7 +1957,7 @@ ThreadError:
g_Config.multiThreaded = false;
}
-bool CModel3::StartThreads()
+bool CModel3::StartThreads(void)
{
if (startedThreads)
return true;
@@ -1966,6 +1966,12 @@ bool CModel3::StartThreads()
sndBrdThreadSync = CThread::CreateSemaphore(1);
if (sndBrdThreadSync == NULL)
goto ThreadError;
+ sndBrdNotifyLock = CThread::CreateMutex();
+ if (sndBrdNotifyLock == NULL)
+ goto ThreadError;
+ sndBrdNotifySync = CThread::CreateCondVar();
+ if (sndBrdNotifySync == NULL)
+ goto ThreadError;
if (DriveBoard.IsAttached())
{
drvBrdThreadSync = CThread::CreateSemaphore(1);
@@ -1979,18 +1985,25 @@ bool CModel3::StartThreads()
if (notifySync == NULL)
goto ThreadError;
- // Create sound board thread
- sndBrdThread = CThread::CreateThread(StartSoundBoardThread, this);
+ // Create sound board thread (sync'd or unsync'd)
+ if (syncSndBrdThread)
+ sndBrdThread = CThread::CreateThread(StartSoundBoardThreadSyncd, this);
+ else
+ sndBrdThread = CThread::CreateThread(StartSoundBoardThread, this);
if (sndBrdThread == NULL)
goto ThreadError;
- // Create drive board thread, if drive board is attached
+ // Create drive board thread (sync'd), if drive board is attached
if (DriveBoard.IsAttached())
{
- drvBrdThread = CThread::CreateThread(StartDriveBoardThread, this);
+ drvBrdThread = CThread::CreateThread(StartDriveBoardThreadSyncd, this);
if (drvBrdThread == NULL)
goto ThreadError;
}
+
+ // Set audio callback if unsync'd
+ if (!syncSndBrdThread)
+ SetAudioCallback(AudioCallback, this);
startedThreads = true;
return true;
@@ -2002,16 +2015,55 @@ ThreadError:
return false;
}
-void CModel3::StopThreads()
+bool CModel3::PauseThreads(void)
+{
+ if (!startedThreads)
+ return true;
+
+ // Enter notify critical section
+ if (!notifyLock->Lock())
+ goto ThreadError;
+
+ // Wait for all threads to finish their processing
+ pausedThreads = true;
+ while (sndBrdThreadRunning || drvBrdThreadRunning)
+ {
+ if (!notifySync->Wait(notifyLock))
+ goto ThreadError;
+ }
+
+ // Leave notify critical section
+ if (!notifyLock->Unlock())
+ goto ThreadError;
+ return true;
+
+ThreadError:
+ ErrorLog("Threading error in CModel3::PauseThreads: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
+ g_Config.multiThreaded = false;
+ return false;
+}
+
+void CModel3::ResumeThreads(void)
+{
+ // No need to use any locking here
+ pausedThreads = false;
+ return;
+}
+
+void CModel3::StopThreads(void)
{
if (!startedThreads)
return;
+ // Remove callback
+ if (!syncSndBrdThread)
+ SetAudioCallback(NULL, NULL);
+
DeleteThreadObjects();
startedThreads = false;
}
-void CModel3::DeleteThreadObjects()
+void CModel3::DeleteThreadObjects(void)
{
// Delete (which in turn kills) sound board and drive board threads
// Note that can do so here safely because threads will always be waiting on their semaphores when this method is called
@@ -2037,6 +2089,16 @@ void CModel3::DeleteThreadObjects()
delete drvBrdThreadSync;
drvBrdThreadSync = NULL;
}
+ if (sndBrdNotifyLock != NULL)
+ {
+ delete sndBrdNotifyLock;
+ sndBrdNotifyLock = NULL;
+ }
+ if (sndBrdNotifySync != NULL)
+ {
+ delete sndBrdNotifySync;
+ sndBrdNotifySync = NULL;
+ }
if (notifyLock != NULL)
{
delete notifyLock;
@@ -2048,31 +2110,148 @@ void CModel3::DeleteThreadObjects()
notifySync = NULL;
}
}
-
+
int CModel3::StartSoundBoardThread(void *data)
{
- // Call sound board thread method on CModel3
+ // Call method on CModel3 to run unsync'd sound board thread
CModel3 *model3 = (CModel3*)data;
model3->RunSoundBoardThread();
return 0;
}
-int CModel3::StartDriveBoardThread(void *data)
+int CModel3::StartSoundBoardThreadSyncd(void *data)
{
- // Call drive board thread method on CModel3
+ // Call method on CModel3 to run sync'd sound board thread
CModel3 *model3 = (CModel3*)data;
- model3->RunDriveBoardThread();
+ model3->RunSoundBoardThreadSyncd();
return 0;
}
-void CModel3::RunSoundBoardThread()
+int CModel3::StartDriveBoardThreadSyncd(void *data)
+{
+ // Call method on CModel3 to run sync'd drive board thread
+ CModel3 *model3 = (CModel3*)data;
+ model3->RunDriveBoardThreadSyncd();
+ return 0;
+}
+
+void CModel3::AudioCallback(void *data)
+{
+ // Call method on CModel3 to wake sound board thread
+ CModel3 *model3 = (CModel3*)data;
+ model3->WakeSoundBoardThread();
+}
+
+void CModel3::WakeSoundBoardThread(void)
+{
+ // Enter sound board notify critical section
+ if (!sndBrdNotifyLock->Lock())
+ goto ThreadError;
+
+ // Signal to sound board that it should start processing again
+ if (!sndBrdNotifySync->Signal())
+ goto ThreadError;
+
+ // Exit sound board notify critical section
+ if (!sndBrdNotifyLock->Unlock())
+ goto ThreadError;
+ return;
+
+ThreadError:
+ ErrorLog("Threading error in WakeSoundBoardThread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
+ g_Config.multiThreaded = false;
+}
+
+void CModel3::RunSoundBoardThread(void)
{
for (;;)
{
- // Wait on sound board thread semaphore
- if (!sndBrdThreadSync->Wait())
+ bool wait = true;
+ while (wait)
+ {
+ // Enter sound board notify critical section
+ if (!sndBrdNotifyLock->Lock())
+ goto ThreadError;
+
+ // Wait for notification from audio callback
+ if (!sndBrdNotifySync->Wait(sndBrdNotifyLock))
+ goto ThreadError;
+
+ // Exit sound board notify critical section
+ if (!sndBrdNotifyLock->Unlock())
+ goto ThreadError;
+
+ // Enter main notify critical section
+ if (!notifyLock->Lock())
+ goto ThreadError;
+
+ // Check threads not paused
+ if (!pausedThreads)
+ {
+ wait = false;
+ sndBrdThreadRunning = true;
+ }
+
+ // Leave main notify critical section
+ if (!notifyLock->Unlock())
+ goto ThreadError;
+ }
+
+ // Keep processing frames until audio buffer is half full
+ bool repeat = true;
+ // NOTE - performs an unlocked read of pausedThreads here, but this is okay
+ while (!pausedThreads && !SoundBoard.RunFrame())
+ {
+ //printf("Rerunning sound board\n");
+ }
+
+ // Enter main notify critical section
+ if (!notifyLock->Lock())
goto ThreadError;
+ // Let other threads know processing has finished
+ sndBrdThreadRunning = false;
+ sndBrdThreadDone = true;
+ if (!notifySync->SignalAll())
+ goto ThreadError;
+
+ // Leave main notify critical section
+ if (!notifyLock->Unlock())
+ goto ThreadError;
+ }
+
+ThreadError:
+ ErrorLog("Threading error in RunSoundBoardThread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
+ g_Config.multiThreaded = false;
+}
+
+void CModel3::RunSoundBoardThreadSyncd(void)
+{
+ for (;;)
+ {
+ bool wait = true;
+ while (wait)
+ {
+ // Wait on sound board thread semaphore
+ if (!sndBrdThreadSync->Wait())
+ goto ThreadError;
+
+ // Enter notify critical section
+ if (!notifyLock->Lock())
+ goto ThreadError;
+
+ // Check threads not paused
+ if (!pausedThreads)
+ {
+ wait = false;
+ sndBrdThreadRunning = true;
+ }
+
+ // Leave notify critical section
+ if (!notifyLock->Unlock())
+ goto ThreadError;
+ }
+
// Process a single frame for sound board
SoundBoard.RunFrame();
@@ -2080,9 +2259,10 @@ void CModel3::RunSoundBoardThread()
if (!notifyLock->Lock())
goto ThreadError;
- // Let main thread know processing has finished
+ // Let other threads know processing has finished
+ sndBrdThreadRunning = false;
sndBrdThreadDone = true;
- if (!notifySync->Signal())
+ if (!notifySync->SignalAll())
goto ThreadError;
// Leave notify critical section
@@ -2091,17 +2271,36 @@ void CModel3::RunSoundBoardThread()
}
ThreadError:
- ErrorLog("Threading error in sound board thread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
+ ErrorLog("Threading error in RunSoundBoardThreadSyncd: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
g_Config.multiThreaded = false;
}
-void CModel3::RunDriveBoardThread()
+void CModel3::RunDriveBoardThreadSyncd(void)
{
for (;;)
{
- // Wait on drive board thread semaphore
- if (!drvBrdThreadSync->Wait())
- goto ThreadError;
+ bool wait = true;
+ while (wait)
+ {
+ // Wait on drive board thread semaphore
+ if (!drvBrdThreadSync->Wait())
+ goto ThreadError;
+
+ // Enter notify critical section
+ if (!notifyLock->Lock())
+ goto ThreadError;
+
+ // Check threads not paused
+ if (!pausedThreads)
+ {
+ wait = false;
+ drvBrdThreadRunning = true;
+ }
+
+ // Leave notify critical section
+ if (!notifyLock->Unlock())
+ goto ThreadError;
+ }
// Process a single frame for drive board
DriveBoard.RunFrame();
@@ -2110,9 +2309,10 @@ void CModel3::RunDriveBoardThread()
if (!notifyLock->Lock())
goto ThreadError;
- // Let main thread know processing has finished
+ // Let other threads know processing has finished
+ drvBrdThreadRunning = false;
drvBrdThreadDone = true;
- if (!notifySync->Signal())
+ if (!notifySync->SignalAll())
goto ThreadError;
// Leave notify critical section
@@ -2121,13 +2321,19 @@ void CModel3::RunDriveBoardThread()
}
ThreadError:
- ErrorLog("Threading error in drive board thread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
+ ErrorLog("Threading error in RunDriveBoardThreadSyncd: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
g_Config.multiThreaded = false;
}
+
void CModel3::RunMainBoardFrame(void)
{
- // Run the PowerPC for a frame
- ppc_execute(g_Config.GetPowerPCFrequency()*1000000/60-10000);
+ // Compute display and VBlank timings
+ unsigned frameCycles = g_Config.GetPowerPCFrequency()*1000000/60;
+ unsigned vblCycles = (unsigned) ((float) frameCycles * 20.0f/100.0f); // 20% vblank (just a guess; probably too long)
+ unsigned dispCycles = frameCycles - vblCycles;
+
+ // Run the PowerPC for the active display part of the frame
+ ppc_execute(dispCycles);
//printf("PC=%08X LR=%08X\n", ppc_get_pc(), ppc_get_lr());
// VBlank
@@ -2135,7 +2341,7 @@ void CModel3::RunMainBoardFrame(void)
GPU.BeginFrame();
GPU.RenderFrame();
IRQ.Assert(0x02);
- ppc_execute(10000); // TO-DO: Vblank probably needs to be longer. Maybe that's why some games run too fast/slow
+ ppc_execute(vblCycles);
//printf("PC=%08X LR=%08X\n", ppc_get_pc(), ppc_get_lr());
/*
@@ -2271,8 +2477,8 @@ void CModel3::Patch(void)
* 1540: Reset vector transfers control here. Effective start of
* program. On error, game often resets here.
* 14844: Appears to be beginning of the actual boot-up process.
- */
-
+ */
+
// Base offset of program in CROM: 710000
// *(UINT32 *) &crom[0x713724] = 0x60000000;
// *(UINT32 *) &crom[0x713744] = 0x60000000;
@@ -2316,7 +2522,7 @@ void CModel3::Patch(void)
}
else if (!strcmp(Game->id, "daytona2"))
{
- // Base address of program in CROM: 0x600000
+ // Base address of program in CROM: 0x600000
// 0x10019E is the location in RAM which contains link type.
// Region menu can be accessed by entering test mode, holding start,
// and pressing: green, green, blue, yellow, red, yellow, blue (VR4,4,2,3,1,3,2)
@@ -2736,10 +2942,14 @@ CModel3::CModel3(void)
securityPtr = 0;
startedThreads = false;
+ pausedThreads = false;
sndBrdThread = NULL;
drvBrdThread = NULL;
+ sndBrdThreadRunning = false;
sndBrdThreadDone = false;
+ drvBrdThreadRunning = false;
drvBrdThreadDone = false;
+ syncSndBrdThread = false;
sndBrdThreadSync = NULL;
drvBrdThreadSync = NULL;
notifyLock = NULL;
@@ -2789,4 +2999,4 @@ CModel3::~CModel3(void)
securityRAM = NULL;
DebugLog("Destroyed Model 3\n");
-}
+}
\ No newline at end of file
diff --git a/Src/Model3/Model3.h b/Src/Model3/Model3.h
index d436279..492d283 100644
--- a/Src/Model3/Model3.h
+++ b/Src/Model3/Model3.h
@@ -1,386 +1,400 @@
-/**
- ** Supermodel
- ** A Sega Model 3 Arcade Emulator.
- ** Copyright 2011 Bart Trzynadlowski
- **
- ** This file is part of Supermodel.
- **
- ** Supermodel is free software: you can redistribute it and/or modify it under
- ** the terms of the GNU General Public License as published by the Free
- ** Software Foundation, either version 3 of the License, or (at your option)
- ** any later version.
- **
- ** Supermodel is distributed in the hope that it will be useful, but WITHOUT
- ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- ** more details.
- **
- ** You should have received a copy of the GNU General Public License along
- ** with Supermodel. If not, see .
- **/
-
-/*
- * Model3.h
- *
- * Header file defining the CModel3, CModel3Config, and CModel3Inputs classes.
- */
-
-#ifndef INCLUDED_MODEL3_H
-#define INCLUDED_MODEL3_H
-
-/*
- * CModel3Config:
- *
- * Settings used by CModel3.
- */
-class CModel3Config
-{
-public:
- bool multiThreaded; // Multi-threading (enabled if true)
-
- // PowerPC clock frequency in MHz (minimum: 1 MHz)
- inline void SetPowerPCFrequency(unsigned f)
- {
- if ((f<1) || (f>1000))
- {
- ErrorLog("PowerPC frequency must be between 1 and 1000 MHz; setting to 40 MHz.");
- f = 40;
- }
- ppcFrequency = f*1000000;
- }
- inline unsigned GetPowerPCFrequency(void)
- {
- return ppcFrequency/1000000;
- }
-
- // Defaults
- CModel3Config(void)
- {
- multiThreaded = false; // disable by default
- ppcFrequency = 40*1000000; // 40 MHz
- }
-
-private:
- unsigned ppcFrequency; // in Hz
-};
-
-/*
- * CModel3:
- *
- * A complete Model 3 system.
- *
- * Inherits CBus in order to pass the address space handlers to devices that
- * may need them (CPU, DMA, etc.)
- *
- * NOTE: Currently NOT re-entrant due to a non-OOP PowerPC core. Do NOT create
- * create more than one CModel3 object!
- */
-class CModel3: public CBus, public CPCIDevice
-{
-public:
- /*
- * ReadPCIConfigSpace(device, reg, bits, offset):
- *
- * Handles unknown PCI devices. See CPCIDevice definition for more details.
- *
- * Parameters:
- * device Device number.
- * reg Register number.
- * bits Bit width of access (8, 16, or 32 only).;
- * offset Byte offset within register, aligned to the specified bit
- * width, and offset from the 32-bit aligned base of the
- * register number.
- *
- * Returns:
- * Register data.
- */
- UINT32 ReadPCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned width);
-
- /*
- * WritePCIConfigSpace(device, reg, bits, offset, data):
- *
- * Handles unknown PCI devices. See CPCIDevice definition for more details.
- *
- * Parameters:
- * device Device number.
- * reg Register number.
- * bits Bit width of access (8, 16, or 32 only).
- * offset Byte offset within register, aligned to the specified bit
- * width, and offset from the 32-bit aligned base of the
- * register number.
- * data Data.
- */
- void WritePCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned width, UINT32 data);
-
- /*
- * Read8(addr):
- * Read16(addr):
- * Read32(addr):
- * Read64(addr):
- *
- * Read a byte, 16-bit half word, 32-bit word, or 64-bit double word from
- * the PowerPC address space. This implements the PowerPC address bus. Note
- * that it is big endian, so when accessing from a little endian device,
- * the byte order must be manually reversed.
- *
- * Parameters:
- * addr Address to read.
- *
- * Returns:
- * Data at the address.
- */
- UINT8 Read8(UINT32 addr);
- UINT16 Read16(UINT32 addr);
- UINT32 Read32(UINT32 addr);
- UINT64 Read64(UINT32 addr);
-
- /*
- * Write8(addr, data):
- * Write16(addr, data):
- * Write32(addr, data):
- * Write64(addr, data):
- *
- * Write a byte, half word, word, or double word to the PowerPC address
- * space. Note that everything is stored in big endian form, so when
- * accessing with a little endian device, the byte order must be manually
- * reversed.
- *
- * Parameters:
- * addr Address to write.
- * data Data to write.
- */
- void Write8(UINT32 addr, UINT8 data);
- void Write16(UINT32 addr, UINT16 data);
- void Write32(UINT32 addr, UINT32 data);
- void Write64(UINT32 addr, UINT64 data);
-
- /*
- * SaveState(SaveState):
- *
- * Saves an image of the current state. Must never be called while emulator
- * is running (inside RunFrame()).
- *
- * Parameters:
- * SaveState Block file to save state information to.
- */
- void SaveState(CBlockFile *SaveState);
-
- /*
- * LoadState(SaveState):
- *
- * Loads and resumes execution from a state image. Modifies data that may
- * be used by multiple threads -- use with caution and ensure threads are
- * not accessing data that will be touched. Must never be called while
- * emulator is running (inside RunFrame()).
- *
- * Parameters:
- * SaveState Block file to load state information from.
- */
- void LoadState(CBlockFile *SaveState);
-
- /*
- * SaveNVRAM(NVRAM):
- *
- * Saves an image of the current NVRAM state.
- *
- * Parameters:
- * NVRAM Block file to save NVRAM to.
- */
- void SaveNVRAM(CBlockFile *NVRAM);
-
- /*
- * LoadNVRAM(NVRAM):
- *
- * Loads an NVRAM image.
- *
- * Parameters:
- * NVRAM Block file to load NVRAM state from.
- */
- void LoadNVRAM(CBlockFile *NVRAM);
-
- /*
- * ClearNVRAM(void):
- *
- * Clears all NVRAM (backup RAM and EEPROM).
- */
- void ClearNVRAM(void);
-
- /*
- * RunFrame(void):
- *
- * Runs one frame (assuming 60 Hz video refresh rate).
- */
- void RunFrame(void);
-
- /*
- * Reset(void):
- *
- * Resets the system. Does not modify non-volatile memory.
- */
- void Reset(void);
-
- /*
- * GetGameInfo(void):
- *
- * Returns:
- * A pointer to the presently loaded game's information structure (or
- * NULL if no ROM set has yet been loaded).
- */
- const struct GameInfo * GetGameInfo(void);
-
- /*
- * LoadROMSet(GameList, zipFile):
- *
- * Loads a complete ROM set from the specified ZIP archive.
- *
- * NOTE: Command line settings will not have been applied here yet.
- *
- * Parameters:
- * GameList List of all supported games and their ROMs.
- * zipFile ZIP file to load from.
- *
- * Returns:
- * OKAY if successful, FAIL otherwise. Prints errors.
- */
- bool LoadROMSet(const struct GameInfo *GameList, const char *zipFile);
-
- /*
- * AttachRenderers(Render2DPtr, Render3DPtr):
- *
- * Attaches the renderers to the appropriate device objects.
- *
- * Parameters:
- * Render2DPtr Pointer to a tile renderer object.
- * Render3DPtr Same as above but for a 3D renderer.
- */
- void AttachRenderers(CRender2D *Render2DPtr, CRender3D *Render3DPtr);
-
- /*
- * AttachInputs(InputsPtr):
- *
- * Attaches OSD-managed inputs.
- *
- * Parameters:
- * InputsPtr Pointer to the object containing input states.
- */
- void AttachInputs(CInputs *InputsPtr);
-
- /*
- * Init(void):
- *
- * One-time initialization of the context. Must be called prior to all
- * other members. Allocates memory and initializes device states.
- *
- * NOTE: Command line settings will not have been applied here yet.
- *
- * Returns:
- * OKAY is successful, otherwise FAILED if a non-recoverable error
- * occurred. Prints own error messages.
- */
- bool Init(void);
-
- /*
- * CModel3(void):
- * ~CModel3(void):
- *
- * Constructor and destructor for Model 3 class. Constructor performs a
- * bare-bones initialization of object; does not perform any memory
- * allocation or any actions that can fail. The destructor will deallocate
- * memory and free resources used by the object (and its child objects).
- */
- CModel3(void);
- ~CModel3(void);
-
- /*
- * Private Property.
- * Tresspassers will be shot! ;)
- */
-private:
- // Private member functions
- UINT8 ReadInputs(unsigned reg);
- void WriteInputs(unsigned reg, UINT8 data);
- UINT32 ReadSecurity(unsigned reg);
- void WriteSecurity(unsigned reg, UINT32 data);
- void SetCROMBank(unsigned idx);
- UINT8 ReadSystemRegister(unsigned reg);
- void WriteSystemRegister(unsigned reg, UINT8 data);
- void Patch(void);
-
- void RunMainBoardFrame(); // Runs the main board (PPC) for a frame
- bool StartThreads(); // Starts all threads
- void StopThreads(); // Stops all threads
- void DeleteThreadObjects(); // Deletes all threads and synchronization objects
-
- static int StartSoundBoardThread(void *data); // Callback to start sound board thread
- static int StartDriveBoardThread(void *data); // Callback to start drive board thread
-
- void RunSoundBoardThread(); // Runs sound board thread
- void RunDriveBoardThread(); // Runs drive board thread
-
- // Game and hardware information
- const struct GameInfo *Game;
-
- // Game inputs
- CInputs *Inputs;
-
- // Input registers (game controls)
- UINT8 inputBank;
- UINT8 serialFIFO1, serialFIFO2;
- UINT8 gunReg;
- int adcChannel;
-
- // MIDI port
- UINT8 midiCtrlPort; // controls MIDI (SCSP) IRQ behavior
-
- // Emulated core Model 3 memory regions
- UINT8 *memoryPool; // single allocated region for all ROM and system RAM
- UINT8 *ram; // 8 MB PowerPC RAM
- UINT8 *crom; // 8+128 MB CROM (fixed CROM first, then 64MB of banked CROMs -- Daytona2 might need extra?)
- UINT8 *vrom; // 64 MB VROM (video ROM, visible only to Real3D)
- UINT8 *soundROM; // 512 KB sound ROM (68K program)
- UINT8 *sampleROM; // 8 MB samples (68K)
- UINT8 *dsbROM; // 128 KB DSB ROM (Z80 program)
- UINT8 *mpegROM; // 8 MB DSB MPEG ROM
- UINT8 *backupRAM; // 128 KB Backup RAM (battery backed)
- UINT8 *securityRAM; // 128 KB Security Board RAM
- UINT8 *driveROM; // 32 KB drive board ROM (Z80 program) (optional)
-
- // Banked CROM
- UINT8 *cromBank; // currently mapped in CROM bank
- unsigned cromBankReg; // the CROM bank register
-
- // Security device
- unsigned securityPtr; // pointer to current offset in security data
-
- // PowerPC
- PPC_FETCH_REGION PPCFetchRegions[3];
-
- // Multiple threading
- bool startedThreads; // True if threads have been created and started
- CThread *sndBrdThread; // Sound board thread
- CThread *drvBrdThread; // Drive board thread
- bool sndBrdThreadDone; // Flag to indicate sound board thread has finished processing for current frame
- bool drvBrdThreadDone; // Flag to indicate drive board thread has finished processing for current frame
-
- // Thread synchronization objects
- CSemaphore *sndBrdThreadSync;
- CSemaphore *drvBrdThreadSync;
- CMutex *notifyLock;
- CCondVar *notifySync;
-
- // Other devices
- CIRQ IRQ; // Model 3 IRQ controller
- CMPC10x PCIBridge; // MPC10x PCI/bridge/memory controller
- CPCIBus PCIBus; // Model 3's PCI bus
- C53C810 SCSI; // NCR 53C810 SCSI controller
- CRTC72421 RTC; // Epson RTC-72421 real-time clock
- C93C46 EEPROM; // 93C46 EEPROM
- CTileGen TileGen; // Sega 2D tile generator
- CReal3D GPU; // Real3D graphics hardware
- CSoundBoard SoundBoard; // Sound board
- CDSB *DSB; // Digital Sound Board (type determined dynamically at load time)
- CDriveBoard DriveBoard; // Drive board
-};
-
-
-#endif // INCLUDED_MODEL3_H
+/**
+ ** Supermodel
+ ** A Sega Model 3 Arcade Emulator.
+ ** Copyright 2011 Bart Trzynadlowski
+ **
+ ** This file is part of Supermodel.
+ **
+ ** Supermodel is free software: you can redistribute it and/or modify it under
+ ** the terms of the GNU General Public License as published by the Free
+ ** Software Foundation, either version 3 of the License, or (at your option)
+ ** any later version.
+ **
+ ** Supermodel is distributed in the hope that it will be useful, but WITHOUT
+ ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ ** more details.
+ **
+ ** You should have received a copy of the GNU General Public License along
+ ** with Supermodel. If not, see .
+ **/
+
+/*
+ * Model3.h
+ *
+ * Header file defining the CModel3, CModel3Config, and CModel3Inputs classes.
+ */
+
+#ifndef INCLUDED_MODEL3_H
+#define INCLUDED_MODEL3_H
+
+/*
+ * CModel3Config:
+ *
+ * Settings used by CModel3.
+ */
+class CModel3Config
+{
+public:
+ bool multiThreaded; // Multi-threading (enabled if true)
+
+ // PowerPC clock frequency in MHz (minimum: 1 MHz)
+ inline void SetPowerPCFrequency(unsigned f)
+ {
+ if ((f<1) || (f>1000))
+ {
+ ErrorLog("PowerPC frequency must be between 1 and 1000 MHz; setting to 40 MHz.");
+ f = 40;
+ }
+ ppcFrequency = f*1000000;
+ }
+ inline unsigned GetPowerPCFrequency(void)
+ {
+ return ppcFrequency/1000000;
+ }
+
+ // Defaults
+ CModel3Config(void)
+ {
+ multiThreaded = false; // disable by default
+ ppcFrequency = 40*1000000; // 40 MHz
+ }
+
+private:
+ unsigned ppcFrequency; // in Hz
+};
+
+/*
+ * CModel3:
+ *
+ * A complete Model 3 system.
+ *
+ * Inherits CBus in order to pass the address space handlers to devices that
+ * may need them (CPU, DMA, etc.)
+ *
+ * NOTE: Currently NOT re-entrant due to a non-OOP PowerPC core. Do NOT create
+ * create more than one CModel3 object!
+ */
+class CModel3: public CBus, public CPCIDevice
+{
+public:
+ /*
+ * ReadPCIConfigSpace(device, reg, bits, offset):
+ *
+ * Handles unknown PCI devices. See CPCIDevice definition for more details.
+ *
+ * Parameters:
+ * device Device number.
+ * reg Register number.
+ * bits Bit width of access (8, 16, or 32 only).;
+ * offset Byte offset within register, aligned to the specified bit
+ * width, and offset from the 32-bit aligned base of the
+ * register number.
+ *
+ * Returns:
+ * Register data.
+ */
+ UINT32 ReadPCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned width);
+
+ /*
+ * WritePCIConfigSpace(device, reg, bits, offset, data):
+ *
+ * Handles unknown PCI devices. See CPCIDevice definition for more details.
+ *
+ * Parameters:
+ * device Device number.
+ * reg Register number.
+ * bits Bit width of access (8, 16, or 32 only).
+ * offset Byte offset within register, aligned to the specified bit
+ * width, and offset from the 32-bit aligned base of the
+ * register number.
+ * data Data.
+ */
+ void WritePCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned width, UINT32 data);
+
+ /*
+ * Read8(addr):
+ * Read16(addr):
+ * Read32(addr):
+ * Read64(addr):
+ *
+ * Read a byte, 16-bit half word, 32-bit word, or 64-bit double word from
+ * the PowerPC address space. This implements the PowerPC address bus. Note
+ * that it is big endian, so when accessing from a little endian device,
+ * the byte order must be manually reversed.
+ *
+ * Parameters:
+ * addr Address to read.
+ *
+ * Returns:
+ * Data at the address.
+ */
+ UINT8 Read8(UINT32 addr);
+ UINT16 Read16(UINT32 addr);
+ UINT32 Read32(UINT32 addr);
+ UINT64 Read64(UINT32 addr);
+
+ /*
+ * Write8(addr, data):
+ * Write16(addr, data):
+ * Write32(addr, data):
+ * Write64(addr, data):
+ *
+ * Write a byte, half word, word, or double word to the PowerPC address
+ * space. Note that everything is stored in big endian form, so when
+ * accessing with a little endian device, the byte order must be manually
+ * reversed.
+ *
+ * Parameters:
+ * addr Address to write.
+ * data Data to write.
+ */
+ void Write8(UINT32 addr, UINT8 data);
+ void Write16(UINT32 addr, UINT16 data);
+ void Write32(UINT32 addr, UINT32 data);
+ void Write64(UINT32 addr, UINT64 data);
+
+ /*
+ * SaveState(SaveState):
+ *
+ * Saves an image of the current state. Must never be called while emulator
+ * is running (inside RunFrame()).
+ *
+ * Parameters:
+ * SaveState Block file to save state information to.
+ */
+ void SaveState(CBlockFile *SaveState);
+
+ /*
+ * LoadState(SaveState):
+ *
+ * Loads and resumes execution from a state image. Modifies data that may
+ * be used by multiple threads -- use with caution and ensure threads are
+ * not accessing data that will be touched. Must never be called while
+ * emulator is running (inside RunFrame()).
+ *
+ * Parameters:
+ * SaveState Block file to load state information from.
+ */
+ void LoadState(CBlockFile *SaveState);
+
+ /*
+ * SaveNVRAM(NVRAM):
+ *
+ * Saves an image of the current NVRAM state.
+ *
+ * Parameters:
+ * NVRAM Block file to save NVRAM to.
+ */
+ void SaveNVRAM(CBlockFile *NVRAM);
+
+ /*
+ * LoadNVRAM(NVRAM):
+ *
+ * Loads an NVRAM image.
+ *
+ * Parameters:
+ * NVRAM Block file to load NVRAM state from.
+ */
+ void LoadNVRAM(CBlockFile *NVRAM);
+
+ /*
+ * ClearNVRAM(void):
+ *
+ * Clears all NVRAM (backup RAM and EEPROM).
+ */
+ void ClearNVRAM(void);
+
+ /*
+ * RunFrame(void):
+ *
+ * Runs one frame (assuming 60 Hz video refresh rate).
+ */
+ void RunFrame(void);
+
+ /*
+ * Reset(void):
+ *
+ * Resets the system. Does not modify non-volatile memory.
+ */
+ void Reset(void);
+
+ /*
+ * GetGameInfo(void):
+ *
+ * Returns:
+ * A pointer to the presently loaded game's information structure (or
+ * NULL if no ROM set has yet been loaded).
+ */
+ const struct GameInfo * GetGameInfo(void);
+
+ /*
+ * LoadROMSet(GameList, zipFile):
+ *
+ * Loads a complete ROM set from the specified ZIP archive.
+ *
+ * NOTE: Command line settings will not have been applied here yet.
+ *
+ * Parameters:
+ * GameList List of all supported games and their ROMs.
+ * zipFile ZIP file to load from.
+ *
+ * Returns:
+ * OKAY if successful, FAIL otherwise. Prints errors.
+ */
+ bool LoadROMSet(const struct GameInfo *GameList, const char *zipFile);
+
+ /*
+ * AttachRenderers(Render2DPtr, Render3DPtr):
+ *
+ * Attaches the renderers to the appropriate device objects.
+ *
+ * Parameters:
+ * Render2DPtr Pointer to a tile renderer object.
+ * Render3DPtr Same as above but for a 3D renderer.
+ */
+ void AttachRenderers(CRender2D *Render2DPtr, CRender3D *Render3DPtr);
+
+ /*
+ * AttachInputs(InputsPtr):
+ *
+ * Attaches OSD-managed inputs.
+ *
+ * Parameters:
+ * InputsPtr Pointer to the object containing input states.
+ */
+ void AttachInputs(CInputs *InputsPtr);
+
+ /*
+ * Init(void):
+ *
+ * One-time initialization of the context. Must be called prior to all
+ * other members. Allocates memory and initializes device states.
+ *
+ * NOTE: Command line settings will not have been applied here yet.
+ *
+ * Returns:
+ * OKAY is successful, otherwise FAILED if a non-recoverable error
+ * occurred. Prints own error messages.
+ */
+ bool Init(void);
+
+ /*
+ * CModel3(void):
+ * ~CModel3(void):
+ *
+ * Constructor and destructor for Model 3 class. Constructor performs a
+ * bare-bones initialization of object; does not perform any memory
+ * allocation or any actions that can fail. The destructor will deallocate
+ * memory and free resources used by the object (and its child objects).
+ */
+ CModel3(void);
+ ~CModel3(void);
+
+ bool PauseThreads(void);
+ void ResumeThreads(void);
+
+ /*
+ * Private Property.
+ * Tresspassers will be shot! ;)
+ */
+private:
+ // Private member functions
+ UINT8 ReadInputs(unsigned reg);
+ void WriteInputs(unsigned reg, UINT8 data);
+ UINT32 ReadSecurity(unsigned reg);
+ void WriteSecurity(unsigned reg, UINT32 data);
+ void SetCROMBank(unsigned idx);
+ UINT8 ReadSystemRegister(unsigned reg);
+ void WriteSystemRegister(unsigned reg, UINT8 data);
+ void Patch(void);
+
+ void RunMainBoardFrame(void); // Runs the main board (PPC) for a frame
+ bool StartThreads(void); // Starts all threads
+ void StopThreads(void); // Stops all threads
+ void DeleteThreadObjects(void); // Deletes all threads and synchronization objects
+
+ static int StartSoundBoardThread(void *data); // Callback to start unsync'd sound board thread
+ static int StartSoundBoardThreadSyncd(void *data); // Callback to start sync'd sound board thread
+ static int StartDriveBoardThreadSyncd(void *data); // Callback to start sync'd drive board thread
+
+ static void AudioCallback(void *data); // Audio buffer callback
+
+ void WakeSoundBoardThread(void); // Used by audio callback to wake sound board thread when not sync'd with PPC thread
+ void RunSoundBoardThread(void); // Runs sound board thread unsync'd with PPC thread, ie at full speed
+ void RunSoundBoardThreadSyncd(void); // Runs sound board thread sync'd in step with PPC thread
+ void RunDriveBoardThreadSyncd(void); // Runs drive board thread sync'd in step with PPC thread
+
+ // Game and hardware information
+ const struct GameInfo *Game;
+
+ // Game inputs
+ CInputs *Inputs;
+
+ // Input registers (game controls)
+ UINT8 inputBank;
+ UINT8 serialFIFO1, serialFIFO2;
+ UINT8 gunReg;
+ int adcChannel;
+
+ // MIDI port
+ UINT8 midiCtrlPort; // controls MIDI (SCSP) IRQ behavior
+
+ // Emulated core Model 3 memory regions
+ UINT8 *memoryPool; // single allocated region for all ROM and system RAM
+ UINT8 *ram; // 8 MB PowerPC RAM
+ UINT8 *crom; // 8+128 MB CROM (fixed CROM first, then 64MB of banked CROMs -- Daytona2 might need extra?)
+ UINT8 *vrom; // 64 MB VROM (video ROM, visible only to Real3D)
+ UINT8 *soundROM; // 512 KB sound ROM (68K program)
+ UINT8 *sampleROM; // 8 MB samples (68K)
+ UINT8 *dsbROM; // 128 KB DSB ROM (Z80 program)
+ UINT8 *mpegROM; // 8 MB DSB MPEG ROM
+ UINT8 *backupRAM; // 128 KB Backup RAM (battery backed)
+ UINT8 *securityRAM; // 128 KB Security Board RAM
+ UINT8 *driveROM; // 32 KB drive board ROM (Z80 program) (optional)
+
+ // Banked CROM
+ UINT8 *cromBank; // currently mapped in CROM bank
+ unsigned cromBankReg; // the CROM bank register
+
+ // Security device
+ unsigned securityPtr; // pointer to current offset in security data
+
+ // PowerPC
+ PPC_FETCH_REGION PPCFetchRegions[3];
+
+ // Multiple threading
+ bool startedThreads; // True if threads have been created and started
+ bool pausedThreads; // True if threads are currently paused
+ CThread *sndBrdThread; // Sound board thread
+ CThread *drvBrdThread; // Drive board thread
+ bool sndBrdThreadRunning; // Flag to indicate sound board thread is currently processing
+ bool sndBrdThreadDone; // Flag to indicate sound board thread has finished processing
+ bool drvBrdThreadRunning; // Flag to indicate drive board thread is currently processing
+ bool drvBrdThreadDone; // Flag to indicate drive board thread has finished processing
+
+ // Thread synchronization objects
+ bool syncSndBrdThread;
+ CSemaphore *sndBrdThreadSync;
+ CMutex *sndBrdNotifyLock;
+ CCondVar *sndBrdNotifySync;
+ CSemaphore *drvBrdThreadSync;
+ CMutex *notifyLock;
+ CCondVar *notifySync;
+
+ // Other devices
+ CIRQ IRQ; // Model 3 IRQ controller
+ CMPC10x PCIBridge; // MPC10x PCI/bridge/memory controller
+ CPCIBus PCIBus; // Model 3's PCI bus
+ C53C810 SCSI; // NCR 53C810 SCSI controller
+ CRTC72421 RTC; // Epson RTC-72421 real-time clock
+ C93C46 EEPROM; // 93C46 EEPROM
+ CTileGen TileGen; // Sega 2D tile generator
+ CReal3D GPU; // Real3D graphics hardware
+ CSoundBoard SoundBoard; // Sound board
+ CDSB *DSB; // Digital Sound Board (type determined dynamically at load time)
+ CDriveBoard DriveBoard; // Drive board
+};
+
+
+#endif // INCLUDED_MODEL3_H
\ No newline at end of file
diff --git a/Src/Model3/SoundBoard.cpp b/Src/Model3/SoundBoard.cpp
index 441d374..becb3d2 100644
--- a/Src/Model3/SoundBoard.cpp
+++ b/Src/Model3/SoundBoard.cpp
@@ -1,4 +1,3 @@
-//TODO: before release, comment out printf()'s
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
@@ -358,7 +357,7 @@ void CSoundBoard::WriteMIDIPort(UINT8 data)
DSB->SendCommand(data);
}
-void CSoundBoard::RunFrame(void)
+bool CSoundBoard::RunFrame(void)
{
#ifdef SUPERMODEL_SOUND
// Run sound board first to generate SCSP audio
@@ -379,7 +378,7 @@ void CSoundBoard::RunFrame(void)
DSB->RunFrame(audioL, audioR);
// Output the audio buffers
- OutputAudio(44100/60, audioL, audioR);
+ bool bufferFull = OutputAudio(44100/60, audioL, audioR);
#ifdef SUPERMODEL_LOG_AUDIO
// Output to binary file
@@ -391,8 +390,10 @@ void CSoundBoard::RunFrame(void)
s = audioR[i];
fwrite(&s, sizeof(INT16), 1, soundFP); // right channel
}
-#endif
-#endif
+#endif // SUPERMODEL_LOG_AUDIO
+#endif // SUPERMODEL_SOUND
+
+ return bufferFull;
}
void CSoundBoard::Reset(void)
diff --git a/Src/Model3/SoundBoard.h b/Src/Model3/SoundBoard.h
index be5c8b4..c8e0924 100644
--- a/Src/Model3/SoundBoard.h
+++ b/Src/Model3/SoundBoard.h
@@ -126,7 +126,7 @@ public:
*
* Runs the sound board for one frame, updating sound in the process.
*/
- void RunFrame(void);
+ bool RunFrame(void);
/*
* Reset(void):
@@ -161,7 +161,7 @@ public:
* error messages.
*/
bool Init(const UINT8 *soundROMPtr, const UINT8 *sampleROMPtr);
-
+
/*
* CSoundBoard(void):
* ~CSoundBoard(void):
diff --git a/Src/OSD/Audio.h b/Src/OSD/Audio.h
index 241cab9..0b075b2 100755
--- a/Src/OSD/Audio.h
+++ b/Src/OSD/Audio.h
@@ -7,6 +7,12 @@
* Function-based interface for audio output.
*/
+typedef void (*AudioCallbackFPtr)(void *data);
+
+extern void SetAudioCallback(AudioCallbackFPtr callback, void *data);
+
+extern void SetAudioEnabled(bool enabled);
+
/*
* OpenAudio()
*
@@ -19,7 +25,7 @@ extern bool OpenAudio();
*
* Sends a chunk of two-channel audio with the given number of samples to the audio system.
*/
-extern void OutputAudio(unsigned numSamples, INT16 *leftBuffer, INT16 *rightBuffer);
+extern bool OutputAudio(unsigned numSamples, INT16 *leftBuffer, INT16 *rightBuffer);
/*
* CloseAudio()
diff --git a/Src/OSD/SDL/Audio.cpp b/Src/OSD/SDL/Audio.cpp
index 3d0f07a..e7e6f45 100755
--- a/Src/OSD/SDL/Audio.cpp
+++ b/Src/OSD/SDL/Audio.cpp
@@ -38,11 +38,30 @@ static bool writeWrapped = false; // True if write position has wrapped around
static unsigned underRuns = 0; // Number of buffer under-runs that have occured
static unsigned overRuns = 0; // Number of buffer over-runs that have occured
+static AudioCallbackFPtr callback = NULL;
+static void *callbackData = NULL;
+
+void SetAudioCallback(AudioCallbackFPtr newCallback, void *newData)
+{
+ // Lock audio whilst changing callback pointers
+ SDL_LockAudio();
+
+ callback = newCallback;
+ callbackData = newData;
+
+ SDL_UnlockAudio();
+}
+
+void SetAudioEnabled(bool newEnabled)
+{
+ enabled = newEnabled;
+}
+
static void PlayCallback(void *data, Uint8 *stream, int len)
{
//printf("PlayCallback(%d) [writePos = %u, writeWrapped = %s, playPos = %u, audioBufferSize = %u]\n",
// len, writePos, (writeWrapped ? "true" : "false"), playPos, audioBufferSize);
-
+
// Get current write position and adjust it if write has wrapped but play position has not
UINT32 adjWritePos = writePos;
if (writeWrapped)
@@ -128,6 +147,8 @@ static void PlayCallback(void *data, Uint8 *stream, int len)
// Move play position forward for next time
playPos += len;
+ bool halfEmpty = adjWritePos + audioBufferSize / 2 - BYTES_PER_FRAME / 2 < playPos + audioBufferSize;
+
// Check if play position has moved past end of buffer
if (playPos >= audioBufferSize)
{
@@ -135,6 +156,10 @@ static void PlayCallback(void *data, Uint8 *stream, int len)
playPos -= audioBufferSize;
writeWrapped = false;
}
+
+ // If buffer is more than half empty then call callback
+ if (callback && halfEmpty)
+ callback(callbackData);
}
static void MixChannels(unsigned numSamples, INT16 *leftBuffer, INT16 *rightBuffer, void *dest)
@@ -227,11 +252,13 @@ bool OpenAudio()
return OKAY;
}
-void OutputAudio(unsigned numSamples, INT16 *leftBuffer, INT16 *rightBuffer)
+bool OutputAudio(unsigned numSamples, INT16 *leftBuffer, INT16 *rightBuffer)
{
//printf("OutputAudio(%u) [writePos = %u, writeWrapped = %s, playPos = %u, audioBufferSize = %u]\n",
// numSamples, writePos, (writeWrapped ? "true" : "false"), playPos, audioBufferSize);
+ bool halfFull = false;
+
UINT32 bytesRemaining;
UINT32 bytesToCopy;
INT16 *src;
@@ -296,6 +323,9 @@ void OutputAudio(unsigned numSamples, INT16 *leftBuffer, INT16 *rightBuffer)
// Check if write position has caught up with play region and now overlaps it (ie buffer over-run)
bool overRun = writePos + numBytes > playPos + audioBufferSize;
+ if (writePos + audioBufferSize / 2 + BYTES_PER_FRAME / 2 > playPos + audioBufferSize)
+ halfFull = true;
+
// Move write position back to within buffer
if (writePos >= audioBufferSize)
writePos -= audioBufferSize;
@@ -308,6 +338,8 @@ void OutputAudio(unsigned numSamples, INT16 *leftBuffer, INT16 *rightBuffer)
//printf("Audio buffer over-run #%u in OutputAudio(%u) [writePos = %u, writeWrapped = %s, playPos = %u, audioBufferSize = %u, numBytes = %u]\n",
// overRuns, numSamples, writePos, (writeWrapped ? "true" : "false"), playPos, audioBufferSize, numBytes);
+ halfFull = true;
+
// Discard current chunk of data
goto Finish;
}
@@ -366,6 +398,9 @@ void OutputAudio(unsigned numSamples, INT16 *leftBuffer, INT16 *rightBuffer)
Finish:
// Unlock SDL audio callback
SDL_UnlockAudio();
+
+ // Return whether buffer is half full
+ return halfFull;
}
void CloseAudio()
diff --git a/Src/OSD/SDL/Main.cpp b/Src/OSD/SDL/Main.cpp
index 4a4ed58..3acae72 100644
--- a/Src/OSD/SDL/Main.cpp
+++ b/Src/OSD/SDL/Main.cpp
@@ -762,17 +762,25 @@ int Supermodel(const char *zipFile, CInputs *Inputs, CINIFile *CmdLine)
quit = 1;
#ifdef SUPERMODEL_DEBUGGER
+ bool processUI = true;
if (Debugger != NULL)
{
Debugger->Poll();
// Check if debugger requests exit or pause
if (Debugger->CheckExit())
- quit = 1;
- else if (Debugger->CheckPause())
- paused = 1;
- else
{
+ quit = 1;
+ processUI = false;
+ }
+ else if (Debugger->CheckPause())
+ {
+ paused = 1;
+ processUI = false;
+ }
+ }
+ if (processUI)
+ {
#endif // SUPERMODEL_DEBUGGER
// Check UI controls
@@ -783,6 +791,12 @@ int Supermodel(const char *zipFile, CInputs *Inputs, CINIFile *CmdLine)
}
else if (Inputs->uiReset->Pressed())
{
+ if (!paused)
+ {
+ Model3->PauseThreads();
+ SetAudioEnabled(false);
+ }
+
// Reset emulator
Model3->Reset();
@@ -792,17 +806,46 @@ int Supermodel(const char *zipFile, CInputs *Inputs, CINIFile *CmdLine)
Debugger->Reset();
#endif // SUPERMODEL_DEBUGGER
+ if (!paused)
+ {
+ Model3->ResumeThreads();
+ SetAudioEnabled(true);
+ }
+
puts("Model 3 reset.");
}
else if (Inputs->uiPause->Pressed())
{
// Toggle emulator paused flag
paused = !paused;
+
+ if (paused)
+ {
+ Model3->PauseThreads();
+ SetAudioEnabled(false);
+ }
+ else
+ {
+ Model3->ResumeThreads();
+ SetAudioEnabled(true);
+ }
}
else if (Inputs->uiSaveState->Pressed())
{
+ if (!paused)
+ {
+ Model3->PauseThreads();
+ SetAudioEnabled(false);
+ }
+
// Save game state
SaveState(Model3);
+
+ if (!paused)
+ {
+ Model3->ResumeThreads();
+ SetAudioEnabled(true);
+ }
}
else if (Inputs->uiChangeSlot->Pressed())
{
@@ -813,6 +856,12 @@ int Supermodel(const char *zipFile, CInputs *Inputs, CINIFile *CmdLine)
}
else if (Inputs->uiLoadState->Pressed())
{
+ if (!paused)
+ {
+ Model3->PauseThreads();
+ SetAudioEnabled(false);
+ }
+
// Load game state
LoadState(Model3);
@@ -821,6 +870,12 @@ int Supermodel(const char *zipFile, CInputs *Inputs, CINIFile *CmdLine)
if (Debugger != NULL)
Debugger->Reset();
#endif // SUPERMODEL_DEBUGGER
+
+ if (!paused)
+ {
+ Model3->ResumeThreads();
+ SetAudioEnabled(true);
+ }
}
else if (Inputs->uiMusicVolUp->Pressed())
{
@@ -904,12 +959,12 @@ int Supermodel(const char *zipFile, CInputs *Inputs, CINIFile *CmdLine)
printf("Frame limiting: %s\n", g_Config.throttle?"On":"Off");
}
#ifdef SUPERMODEL_DEBUGGER
- else if (Inputs->uiEnterDebugger->Pressed())
- {
- // Break execution and enter debugger
- Debugger->ForceBreak(true);
- }
+ else if (Debugger != NULL && Inputs->uiEnterDebugger->Pressed())
+ {
+ // Break execution and enter debugger
+ Debugger->ForceBreak(true);
}
+ }
#endif // SUPERMODEL_DEBUGGER
// FPS and frame rate
diff --git a/Src/OSD/SDL/Thread.cpp b/Src/OSD/SDL/Thread.cpp
index d73df16..fa3e776 100755
--- a/Src/OSD/SDL/Thread.cpp
+++ b/Src/OSD/SDL/Thread.cpp
@@ -122,6 +122,11 @@ bool CCondVar::Signal()
return SDL_CondSignal((SDL_cond*)m_impl) == 0;
}
+bool CCondVar::SignalAll()
+{
+ return SDL_CondBroadcast((SDL_cond*)m_impl) == 0;
+}
+
CMutex::CMutex(void *impl) : m_impl(impl)
{
//