From 5a570ce7fe3ec7ec328801559641ad836ebf5ded Mon Sep 17 00:00:00 2001 From: Matthew Daniels Date: Wed, 14 Apr 2021 01:20:45 +0000 Subject: [PATCH] Adding simulated netboard; all linked games except Sega Rally 2 working at full speed. --- Config/Supermodel.ini | 11 +- Makefiles/Rules.inc | 3 +- Src/Debugger/SupermodelDebugger.cpp | 7 +- Src/Game.h | 2 +- Src/GameLoader.cpp | 2 +- Src/Model3/Model3.cpp | 1034 +++++++++++++-------------- Src/Model3/Model3.h | 9 +- Src/Network/INetBoard.h | 47 ++ Src/Network/NetBoard.cpp | 58 +- Src/Network/NetBoard.h | 6 +- Src/Network/SimNetBoard.cpp | 549 ++++++++++++++ Src/Network/SimNetBoard.h | 99 +++ Src/Network/TCPReceive.cpp | 5 + Src/Network/TCPReceive.h | 1 + Src/Network/TCPSend.cpp | 8 +- Src/OSD/SDL/Main.cpp | 15 +- Src/Supermodel.h | 13 +- VS2008/Supermodel.vcxproj | 3 + VS2008/Supermodel.vcxproj.filters | 174 +++-- 19 files changed, 1389 insertions(+), 657 deletions(-) create mode 100644 Src/Network/INetBoard.h create mode 100644 Src/Network/SimNetBoard.cpp create mode 100644 Src/Network/SimNetBoard.h diff --git a/Config/Supermodel.ini b/Config/Supermodel.ini index 9d8a4d2..051db69 100644 --- a/Config/Supermodel.ini +++ b/Config/Supermodel.ini @@ -34,11 +34,12 @@ [ Global ] ; Input settings can only be read from the global section! -; Network board - experimental build for win32 only -EmulateNet = 0 -port_in = 1970 -port_out = 1971 -addr_out = "127.0.0.1" +; Network board +Network = 0 +SimulateNet = 1 +PortIn = 1970 +PortOut = 1971 +AddressOut = "127.0.0.1" ; Common InputStart1 = "KEY_1,JOY1_BUTTON9" diff --git a/Makefiles/Rules.inc b/Makefiles/Rules.inc index 465e759..a493e13 100644 --- a/Makefiles/Rules.inc +++ b/Makefiles/Rules.inc @@ -169,7 +169,8 @@ ifeq ($(strip $(NET_BOARD)),1) SRC_FILES += \ Src/Network/TCPReceive.cpp \ Src/Network/TCPSend.cpp \ - Src/Network/NetBoard.cpp + Src/Network/NetBoard.cpp \ + Src/Network/SimNetBoard.cpp endif ifeq ($(strip $(ENABLE_DEBUGGER)),1) diff --git a/Src/Debugger/SupermodelDebugger.cpp b/Src/Debugger/SupermodelDebugger.cpp index 9367400..050fad4 100644 --- a/Src/Debugger/SupermodelDebugger.cpp +++ b/Src/Debugger/SupermodelDebugger.cpp @@ -30,6 +30,9 @@ #include "ConsoleDebugger.h" #include "CPUDebug.h" #include "Label.h" +#ifdef NET_BOARD +#include "Network/NetBoard.h" +#endif // NET_BOARD #include #include @@ -302,7 +305,9 @@ namespace Debugger #ifdef NET_BOARD CCPUDebug *CSupermodelDebugger::CreateNetBoardCPUDebug(::CModel3 * model3) { - CNetBoard *netBrd = model3->GetNetBoard(); + CNetBoard *netBrd = dynamic_cast(model3->GetNetBoard()); + if (!netBrd) + return NULL; if (!netBrd->IsAttached()) return NULL; CMusashi68KDebug *cpu = new CMusashi68KDebug("NET68K", netBrd->GetM68K()); diff --git a/Src/Game.h b/Src/Game.h index cc078a2..4ffbf33 100644 --- a/Src/Game.h +++ b/Src/Game.h @@ -18,7 +18,7 @@ struct Game uint32_t real3d_pci_id = 0; // overrides default Real3D PCI ID for stepping (0 for default) float real3d_status_bit_set_percent_of_frame = 0; // overrides default status bit timing (0 for default) uint32_t encryption_key = 0; - std::string netboard_present; + bool netboard_present; enum Inputs { diff --git a/Src/GameLoader.cpp b/Src/GameLoader.cpp index 952a0d4..dc1a128 100644 --- a/Src/GameLoader.cpp +++ b/Src/GameLoader.cpp @@ -202,7 +202,7 @@ static void PopulateGameInfo(Game *game, const Util::Config::Node &game_node) game->real3d_pci_id = game_node["hardware/real3d_pci_id"].ValueAsDefault(0); game->real3d_status_bit_set_percent_of_frame = game_node["hardware/real3d_status_bit_set_percent_of_frame"].ValueAsDefault(0); game->encryption_key = game_node["hardware/encryption_key"].ValueAsDefault(0); - game->netboard_present = game_node["hardware/netboard"].ValueAsDefault("false"); + game->netboard_present = game_node["hardware/netboard"].ValueAsDefault("false"); std::map input_flags { diff --git a/Src/Model3/Model3.cpp b/Src/Model3/Model3.cpp index af07519..3642f4f 100644 --- a/Src/Model3/Model3.cpp +++ b/Src/Model3/Model3.cpp @@ -217,6 +217,10 @@ #include "Supermodel.h" #include "Game.h" #include "ROMSet.h" +#ifdef NET_BOARD +#include "Network/NetBoard.h" +#include "Network/SimNetBoard.h" +#endif // NET_BOARD #include "Util/Format.h" #include "Util/ByteSwap.h" #include @@ -551,13 +555,13 @@ UINT8 CModel3::ReadInputs(unsigned reg) if (m_game.inputs & (Game::INPUT_GUN1 | Game::INPUT_GUN2)) { return 0x0C; } - else { - return 0; - } - - case 0x3C: // ADC - - // Load ADC channels with input data + else { + return 0; + } + + case 0x3C: // ADC + + // Load ADC channels with input data memset(adc, 0, sizeof(adc)); if ((m_game.inputs & Game::INPUT_VEHICLE)) { @@ -926,31 +930,31 @@ UINT8 CModel3::Read8(UINT32 addr) if (addr<0x00800000) 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 - case 0x04: - return ReadInputs(addr & 0x3F); - - // Sound Board + // 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 + case 0x04: + return ReadInputs(addr & 0x3F); + + // Sound Board case 0x08: switch (addr & 0xf) { @@ -960,97 +964,97 @@ UINT8 CModel3::Read8(UINT32 addr) return 0x83; // magtruck country check default: return 0; - } - 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: - //printf("CMODEL3 : unknown R8 mirror : %x\n", addr >> 16); - break; - } - - break; - - // Tile generator - case 0xF1: - if (addr < 0xF1120000) - { - // Tile generator accesses its RAM as little endian, no adjustment needed here - return TileGen.ReadRAM8(addr & 0x1FFFFF); - } - break; - - // 53C810 SCSI - case 0xC0: // only on Step 1.0 -#ifndef NET_BOARD - if (m_game.stepping != "1.0") - { - //printf("Model3 : Read8 %x\n", addr); - break; - } -#endif -#ifdef NET_BOARD - if (m_runNetBoard) - { - switch ((addr & 0x3ffff) >> 16) - { - case 0: - //printf("R8 netbuffer @%x=%x\n", (addr & 0xFFFF), netBuffer[(addr & 0xFFFF)]); - return netBuffer[(addr & 0xFFFF) ^ 2]; - - case 1: // ioreg 32bits access in 16bits environment - if (addr > 0xc00101ff) - { - printf("R8 ATTENTION OUT OF RANGE\n"); - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Info", "Out of Range", NULL); - } - //printf("R8 ioreg @%x=%x\n", (addr & 0x1FF), netBuffer[0x10000 + ((addr & 0x1FF) / 2)]); - return netBuffer[0x10000 + ((addr & 0x1FF) / 2)]; - - case 2: - case 3: - if (addr > 0xc002ffff) - { - printf("R8 ATTENTION OUT OF RANGE\n"); - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Info", "Out of Range", NULL); - } - //printf("R8 netram @%x=%x\n", (addr & 0x1FFFF), netRAM[addr & 0x1ffff]); - return netRAM[((addr & 0x1FFFF) / 2)]; - /*case 3: - //printf("R8 netram @%x=%x\n", (addr & 0x1FFFF), netRAM[addr & 0x1ffff]); - return netRAM[((addr & 0x1FFFF) / 2)];*/ - - default: - printf("R8 ATTENTION OUT OF RANGE\n"); - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Info", "Out of Range", NULL); - break; - } - } - else if (m_game.stepping != "1.0") break; -#endif - case 0xF9: - case 0xC1: + } + 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: + //printf("CMODEL3 : unknown R8 mirror : %x\n", addr >> 16); + break; + } + + break; + + // Tile generator + case 0xF1: + if (addr < 0xF1120000) + { + // Tile generator accesses its RAM as little endian, no adjustment needed here + return TileGen.ReadRAM8(addr & 0x1FFFFF); + } + break; + + // 53C810 SCSI + case 0xC0: // only on Step 1.0 +#ifndef NET_BOARD + if (m_game.stepping != "1.0") + { + //printf("Model3 : Read8 %x\n", addr); + break; + } +#endif +#ifdef NET_BOARD + if (m_runNetBoard) + { + switch ((addr & 0x3ffff) >> 16) + { + case 0: + //printf("R8 netbuffer @%x=%x\n", (addr & 0xFFFF), netBuffer[(addr & 0xFFFF)]); + return netBuffer[(addr & 0xFFFF) ^ 2]; + + case 1: // ioreg 32bits access in 16bits environment + if (addr > 0xc00101ff) + { + printf("R8 ATTENTION OUT OF RANGE\n"); + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Info", "Out of Range", NULL); + } + //printf("R8 ioreg @%x=%x\n", (addr & 0x1FF), netBuffer[0x10000 + ((addr & 0x1FF) / 2)]); + return netBuffer[0x10000 + ((addr & 0x1FF) / 2)]; + + case 2: + case 3: + if (addr > 0xc002ffff) + { + printf("R8 ATTENTION OUT OF RANGE\n"); + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Info", "Out of Range", NULL); + } + //printf("R8 netram @%x=%x\n", (addr & 0x1FFFF), netRAM[addr & 0x1ffff]); + return netRAM[((addr & 0x1FFFF) / 2)]; + /*case 3: + //printf("R8 netram @%x=%x\n", (addr & 0x1FFFF), netRAM[addr & 0x1ffff]); + return netRAM[((addr & 0x1FFFF) / 2)];*/ + + default: + printf("R8 ATTENTION OUT OF RANGE\n"); + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Info", "Out of Range", NULL); + break; + } + } + else if (m_game.stepping != "1.0") break; +#endif + case 0xF9: + case 0xC1: return SCSI.ReadRegister(addr&0xFF); - // Unknown - default: -#ifdef NET_BOARD - printf("CMODEL3 : unknown R8 : %x\n", addr >> 24); -#endif - break; - } + // Unknown + default: +#ifdef NET_BOARD + printf("CMODEL3 : unknown R8 : %x\n", addr >> 24); +#endif + break; + } DebugLog("PC=%08X\tread8 : %08X\n", ppc_get_pc(), addr); return 0xFF; @@ -1119,13 +1123,13 @@ UINT16 CModel3::Read16(UINT32 addr) case 0xEE: case 0xEF: return PCIBridge.ReadPCIConfigData(16,addr&3); - - // Unknown - default: - //printf("CMODEL3 : unknown R16 mirror : %x\n", addr >> 16); - break; - } - + + // Unknown + default: + //printf("CMODEL3 : unknown R16 mirror : %x\n", addr >> 16); + break; + } + break; // Tile generator @@ -1137,33 +1141,33 @@ UINT16 CModel3::Read16(UINT32 addr) return FLIPENDIAN16(data); } break; - -#ifdef NET_BOARD - case 0xc0: // spikeout call this - // interresting : poking @4 master to same value as slave (0x100) or simply !=0 -> connected and go in game, but freeze (prints comm error) as soon as players appear after the gate - // sort of sync ack ? who writes this 16b value ? - { - UINT16 result; - switch ((addr & 0x3ffff) >> 16) - { - case 0: - //printf("R16 netbuffer @%x=%x\n", (addr & 0xFFFF), FLIPENDIAN16(*(UINT16 *)&netBuffer[(addr & 0xFFFF)])); - result = *(UINT16 *)&netBuffer[(addr & 0xFFFF) ^ 2]; - return FLIPENDIAN16(result); // result - default: - printf("CMODEL3 : unknown R16 : %x (C0)\n", addr); - break; - } - } -#endif - // Unknown - default: -#ifdef NET_BOARD - printf("CMODEL3 : unknown R16 : %x (%x)\n", addr, addr >> 24); - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Info", "CMODEL3 : Unknown R16", NULL); -#endif - break; - } + +#ifdef NET_BOARD + case 0xc0: // spikeout call this + // interresting : poking @4 master to same value as slave (0x100) or simply !=0 -> connected and go in game, but freeze (prints comm error) as soon as players appear after the gate + // sort of sync ack ? who writes this 16b value ? + { + UINT16 result; + switch ((addr & 0x3ffff) >> 16) + { + case 0: + //printf("R16 netbuffer @%x=%x\n", (addr & 0xFFFF), FLIPENDIAN16(*(UINT16 *)&netBuffer[(addr & 0xFFFF)])); + result = *(UINT16 *)&netBuffer[(addr & 0xFFFF) ^ 2]; + return FLIPENDIAN16(result); // result + default: + printf("CMODEL3 : unknown R16 : %x (C0)\n", addr); + break; + } + } +#endif + // Unknown + default: +#ifdef NET_BOARD + printf("CMODEL3 : unknown R16 : %x (%x)\n", addr, addr >> 24); + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Info", "CMODEL3 : Unknown R16", NULL); +#endif + break; + } DebugLog("PC=%08X\tread16: %08X\n", ppc_get_pc(), addr); return 0xFFFF; @@ -1273,13 +1277,13 @@ UINT32 CModel3::Read32(UINT32 addr) // Security board registers case 0x1A: return ReadSecurity(addr&0x3F); - - // Unknown - default: - //printf("CModel 3 unknown R32 mirror %x", (addr >> 16) & 0xFF); - break; - } - + + // Unknown + default: + //printf("CModel 3 unknown R32 mirror %x", (addr >> 16) & 0xFF); + break; + } + break; // Tile generator @@ -1311,14 +1315,14 @@ UINT32 CModel3::Read32(UINT32 addr) switch ((addr & 0x3ffff) >> 16) { - case 0: - //printf("R32 netbuffer @%x=%x\n", (addr & 0xFFFF), FLIPENDIAN32(*(UINT32 *)&netBuffer[(addr & 0xFFFF)])); - result = *(UINT32 *)&netBuffer[(addr & 0xFFFF)]; - result = FLIPENDIAN32(result); - return ((result << 16) | (result >> 16)); - //return FLIPENDIAN32(result); // result - - case 1: // ioreg 32bits access to 16bits range + case 0: + //printf("R32 netbuffer @%x=%x\n", (addr & 0xFFFF), FLIPENDIAN32(*(UINT32 *)&netBuffer[(addr & 0xFFFF)])); + result = *(UINT32 *)&netBuffer[(addr & 0xFFFF)]; + result = FLIPENDIAN32(result); + return ((result << 16) | (result >> 16)); + //return FLIPENDIAN32(result); // result + + case 1: // ioreg 32bits access to 16bits range //printf("R32 ioreg @%x=%x\n", (addr & 0x1FF), FLIPENDIAN32(*(UINT32 *)&netBuffer[0x10000 + ((addr & 0x1FF) / 2)])); if (addr > 0xc00101ff) { @@ -1353,26 +1357,26 @@ UINT32 CModel3::Read32(UINT32 addr) default: printf("R32 ATTENTION OUT OF RANGE\n"); break; - } - - } - else if (m_game.stepping != "1.0") break; -#endif - case 0xF9: - case 0xC1: + } + + } + else if (m_game.stepping != "1.0") break; +#endif + 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: -#ifdef NET_BOARD - printf("CMODEL3 : unknown R32 : %x\n", addr >> 24); -#endif - break; - } + // Unknown + default: +#ifdef NET_BOARD + printf("CMODEL3 : unknown R32 : %x\n", addr >> 24); +#endif + break; + } DebugLog("PC=%08X\tread32: %08X\n", ppc_get_pc(), addr); return 0xFFFFFFFF; @@ -1450,13 +1454,13 @@ void CModel3::Write8(UINT32 addr, UINT8 data) if ((addr&3)==0) RTC.WriteRegister((addr>>2)&0xF,data); break; - - // Unknown - default: - //printf("CMODEL3 : unknown W8 mirror : %x\n", addr >> 16); - break; - } - + + // Unknown + default: + //printf("CMODEL3 : unknown W8 mirror : %x\n", addr >> 16); + break; + } + DebugLog("PC=%08X\twrite8 : %08X=%02X\n", ppc_get_pc(), addr, data); break; @@ -1465,13 +1469,13 @@ void CModel3::Write8(UINT32 addr, UINT8 data) if (addr < 0xF1120000) { // Tile generator accesses its RAM as little endian, no adjustment needed here - TileGen.WriteRAM8(addr&0x1FFFFF, data); - break; - } - goto Unknown8; - - // MPC105/106 - case 0xF8: + TileGen.WriteRAM8(addr&0x1FFFFF, data); + break; + } + goto Unknown8; + + // MPC105/106 + case 0xF8: PCIBridge.WriteRegister(addr&0xFF,data); break; @@ -1511,6 +1515,8 @@ void CModel3::Write8(UINT32 addr, UINT8 data) } //printf("W8 netram @%x<-%x\n", (addr & 0x1FFFF), data); + if (((addr & 0x1FFFF) == 0x180) && (data == 0x00)) + NetBoard->Reset(); *(UINT8 *)&netRAM[(addr & 0x1FFFF)/2] = data; break; /*case 3: @@ -1523,19 +1529,12 @@ void CModel3::Write8(UINT32 addr, UINT8 data) break; } - if ((*(UINT8 *)&netBuffer[(0xc00100c0 & 0x3FFFF)] == 0xff) && NetBoard.CodeReady == false) // c0=180/2 - { - printf("Network code copy ending\n"); - NetBoard.CodeReady = true; - NetBoard.Reset(); - } - - break; - } - else if (m_game.stepping != "1.0") break; -#endif - case 0xF9: - case 0xC1: + break; + } + else if (m_game.stepping != "1.0") break; +#endif + case 0xF9: + case 0xC1: SCSI.WriteRegister(addr&0xFF,data); break; @@ -1590,13 +1589,13 @@ void CModel3::Write16(UINT32 addr, UINT16 data) case 0xC0: // F0C00CF8 PCIBridge.WritePCIConfigData(16,addr&2,data); break; - - // Unknown - default: - //printf("CMODEL3 : unknown W16 mirror : %x\n", addr >> 16); - break; - } - + + // Unknown + default: + //printf("CMODEL3 : unknown W16 mirror : %x\n", addr >> 16); + break; + } + DebugLog("PC=%08X\twrite16 : %08X=%04X\n", ppc_get_pc(), addr, data); break; @@ -1615,35 +1614,35 @@ void CModel3::Write16(UINT32 addr, UINT16 data) PCIBridge.WriteRegister((addr&0xFF)+0,data>>8); PCIBridge.WriteRegister((addr&0xFF)+1,data&0xFF); break; - -#ifdef NET_BOARD - case 0xC0: // skichamp only - //printf("CModel 3 : write16 %x<-%x\n", addr, data); - - - switch ((addr & 0x3ffff) >> 16) - { - case 0: - //printf("W16 netbuffer @%x<-%x\n", (addr & 0xFFFF), data); - *(UINT16 *)&netBuffer[(addr & 0xFFFF) ^ 2] = FLIPENDIAN16(data); - break; - - default: - //printf("CMODEL3 : unknown W16 : %x\n", addr >> 24); - break; - } - - break; -#endif - - // Unknown - default: - Unknown16: - DebugLog("PC=%08X\twrite16: %08X=%04X\n", ppc_get_pc(), addr, data); - break; - } -} - + +#ifdef NET_BOARD + case 0xC0: // skichamp only + //printf("CModel 3 : write16 %x<-%x\n", addr, data); + + + switch ((addr & 0x3ffff) >> 16) + { + case 0: + //printf("W16 netbuffer @%x<-%x\n", (addr & 0xFFFF), data); + *(UINT16 *)&netBuffer[(addr & 0xFFFF) ^ 2] = FLIPENDIAN16(data); + break; + + default: + //printf("CMODEL3 : unknown W16 : %x\n", addr >> 24); + break; + } + + break; +#endif + + // Unknown + default: + Unknown16: + DebugLog("PC=%08X\twrite16: %08X=%04X\n", ppc_get_pc(), addr, data); + break; + } +} + void CModel3::Write32(UINT32 addr, UINT32 data) { if ((addr&3)) @@ -1731,28 +1730,28 @@ void CModel3::Write32(UINT32 addr, UINT32 data) // 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 + 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); @@ -1778,16 +1777,16 @@ void CModel3::Write32(UINT32 addr, UINT32 data) break; // Security board registers - case 0x1A: - WriteSecurity(addr&0x3F,data); - break; - - // Unknown - default: - //printf("CMODEL3 : unknown W32 mirror : %x\n", addr >> 16); - break; - } - + case 0x1A: + WriteSecurity(addr&0x3F,data); + break; + + // Unknown + default: + //printf("CMODEL3 : unknown W32 mirror : %x\n", addr >> 16); + break; + } + DebugLog("PC=%08X\twrite32: %08X=%08X\n", ppc_get_pc(), addr, data); break; @@ -1818,25 +1817,25 @@ void CModel3::Write32(UINT32 addr, UINT32 data) break; // 53C810 SCSI - case 0xC0: // step 1.0 only -#ifndef NET_BOARD - if (m_game.stepping != "1.0") - goto Unknown32; -#endif -#ifdef NET_BOARD - if (m_runNetBoard) + case 0xC0: // step 1.0 only +#ifndef NET_BOARD + if (m_game.stepping != "1.0") + goto Unknown32; +#endif +#ifdef NET_BOARD + if (m_runNetBoard) { UINT32 temp; switch ((addr & 0x3ffff) >> 16) { - case 0: - //printf("W32 netbuffer @%x<-%x\n", (addr & 0xFFFF), data); - //*(UINT32 *)&netBuffer[(addr & 0xFFFF)] = FLIPENDIAN32(data); - temp = FLIPENDIAN32(data); - *(UINT32 *)&netBuffer[(addr & 0xFFFF)] = (temp << 16) | (temp >> 16); - break; - - case 1: // ioreg 32bits access to 16bits range + case 0: + //printf("W32 netbuffer @%x<-%x\n", (addr & 0xFFFF), data); + //*(UINT32 *)&netBuffer[(addr & 0xFFFF)] = FLIPENDIAN32(data); + temp = FLIPENDIAN32(data); + *(UINT32 *)&netBuffer[(addr & 0xFFFF)] = (temp << 16) | (temp >> 16); + break; + + case 1: // ioreg 32bits access to 16bits range if (addr > 0xc00101ff) { printf("W32 ATTENTION OUT OF RANGE\n"); @@ -1854,6 +1853,8 @@ void CModel3::Write32(UINT32 addr, UINT32 data) } //printf("W32 netram @%x<-%x\n", (addr & 0x1FFFF), data); + if (((addr & 0x1FFFF) == 0x180) && ((data >> 16) == 0x0000)) + NetBoard->Reset(); *(UINT16 *)&netRAM[((addr & 0x1FFFF) / 2)] = FLIPENDIAN16(data >> 16); break; /*case 3: @@ -1862,29 +1863,15 @@ void CModel3::Write32(UINT32 addr, UINT32 data) break;*/ default: printf("W32 ATTENTION OUT OF RANGE\n"); - break; - } - - if ((*(UINT16 *)&netBuffer[(0xc00100c0 & 0x3FFFF)] == FLIPENDIAN16(0x0000)) && NetBoard.CodeReady == true) // c0=180/2 // reset net when reboot - not perfect, I think memory must be cleared - { - printf("Network pause\n"); - NetBoard.CodeReady = false; - NetBoard.Reset(); - } - - if ((*(UINT16 *)&netBuffer[(0xc0010088 & 0x3FFFF)] == FLIPENDIAN16(0x0080)) && NetBoard.CodeReady == false) // 88=110/2 - { - printf("Network code copy ending\n"); - NetBoard.CodeReady = true; - NetBoard.Reset(); + break; } - - break; - } - else if (m_game.stepping != "1.0") break; -#endif - case 0xF9: - case 0xC1: + + break; + } + else if (m_game.stepping != "1.0") break; +#endif + 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); @@ -1902,14 +1889,14 @@ void CModel3::Write32(UINT32 addr, UINT32 data) break; } } - -void CModel3::Write64(UINT32 addr, UINT64 data) -{ - //printf("write64 %x <- %x\n", addr, data); - Write32(addr+0, (UINT32) (data>>32)); - Write32(addr+4, (UINT32) data); -} - + +void CModel3::Write64(UINT32 addr, UINT64 data) +{ + //printf("write64 %x <- %x\n", addr, data); + Write32(addr+0, (UINT32) (data>>32)); + Write32(addr+4, (UINT32) data); +} + /****************************************************************************** Emulation and Interface Functions @@ -2063,40 +2050,49 @@ void CModel3::RunFrame(void) if (!notifyLock->Unlock()) goto ThreadError; - // If multi-threading GPU, then sync GPUs last while PPC main board thread is waiting - if (m_gpuMultiThreaded) - SyncGPUs(); - - /*if (NetBoard.IsAttached()) - RunNetBoardFrame();*/ - } - else - { + // If multi-threading GPU, then sync GPUs last while PPC main board thread is waiting + if (m_gpuMultiThreaded) + SyncGPUs(); + +#ifdef NET_BOARD + if (NetBoard->IsRunning() && m_config["SimulateNet"].ValueAs()) + { + // ppc irq network needed ? no effect, is it really active/needed ? + IRQ.Assert(0x10); + ppc_execute(200); // give PowerPC time to acknowledge IRQ + IRQ.Deassert(0x10); + ppc_execute(200); // acknowledge that IRQ was deasserted (TODO: is this really needed?) + RunNetBoardFrame(); + // Hum hum, if runnetboardframe is called at 1st place or between ppc irq assert/deassert, spikout freezes just after the gate with net error + // if runnetboardframe is called after ppc irq assert/deassert, spikout works + } +#endif + } + else + { // If not multi-threaded, then just process and render a single frame for PPC main board, sound board and drive board in turn in this thread RunMainBoardFrame(); SyncGPUs(); RenderFrame(); RunSoundBoardFrame(); - if (DriveBoard->IsAttached()) - RunDriveBoardFrame(); -#ifdef NET_BOARD - if (NetBoard.IsAttached() && (m_config["EmulateNet"].ValueAs()) && ((*(UINT16 *)&netBuffer[(0xc00100C0 & 0x3FFFF)] == 0xFFFF) || (netBuffer[(0xc00100C0 & 0x3FFFF)] == 0xFF) || (*(UINT16 *)&netBuffer[(0xc00100C0 & 0x3FFFF)] == 0x0001)) && (NetBoard.CodeReady == true)) - { - // ppc irq network needed ? no effect, is it really active/needed ? - //RunNetBoardFrame(); - IRQ.Assert(0x10); - ppc_execute(200); // give PowerPC time to acknowledge IRQ - //RunNetBoardFrame(); - IRQ.Deassert(0x10); - ppc_execute(200); // acknowledge that IRQ was deasserted (TODO: is this really needed?) - RunNetBoardFrame(); - // Hum hum, if runnetboardframe is called at 1st place or between ppc irq assert/deassert, spikout freezes just after the gate with net error - // if runnetboardframe is called after ppc irq assert/deassert, spikout works - } -#endif - } - - timings.frameTicks = CThread::GetTicks() - start; + if (DriveBoard->IsAttached()) + RunDriveBoardFrame(); +#ifdef NET_BOARD + if (NetBoard->IsRunning()) + { + // ppc irq network needed ? no effect, is it really active/needed ? + IRQ.Assert(0x10); + ppc_execute(200); // give PowerPC time to acknowledge IRQ + IRQ.Deassert(0x10); + ppc_execute(200); // acknowledge that IRQ was deasserted (TODO: is this really needed?) + RunNetBoardFrame(); + // Hum hum, if runnetboardframe is called at 1st place or between ppc irq assert/deassert, spikout freezes just after the gate with net error + // if runnetboardframe is called after ppc irq assert/deassert, spikout works + } +#endif + } + + timings.frameTicks = CThread::GetTicks() - start; return; @@ -2236,13 +2232,13 @@ void CModel3::RunDriveBoardFrame(void) timings.drvTicks = CThread::GetTicks() - start; } -#ifdef NET_BOARD -void CModel3::RunNetBoardFrame(void) -{ - NetBoard.RunFrame(); -} -#endif - +#ifdef NET_BOARD +void CModel3::RunNetBoardFrame(void) +{ + NetBoard->RunFrame(); +} +#endif + bool CModel3::StartThreads(void) { if (startedThreads) @@ -2305,13 +2301,13 @@ bool CModel3::StartThreads(void) goto ThreadError; } - // Set audio callback if sound board thread is unsync'd - if (!syncSndBrdThread) - { - SetAudioCallback(AudioCallback, this); - } - - startedThreads = true; + // Set audio callback if sound board thread is unsync'd + if (!syncSndBrdThread) + { + SetAudioCallback(AudioCallback, this); + } + + startedThreads = true; return true; ThreadError: @@ -2503,10 +2499,10 @@ void CModel3::DumpTimings(void) timings.renderTicks, (timings.renderTicks > timings.ppcTicks ? '!' : ','), timings.syncSize / 1024, (timings.syncSize / 1024 > 128 ? '!' : ','), timings.syncTicks, (timings.syncTicks > 1 ? '!' : ','), - timings.sndTicks, (timings.sndTicks > 10 ? '!' : ','), - timings.drvTicks, (timings.drvTicks > 10 ? '!' : ','), - timings.frameTicks, (timings.frameTicks > 16 ? '!' : ' ')); -} + timings.sndTicks, (timings.sndTicks > 10 ? '!' : ','), + timings.drvTicks, (timings.drvTicks > 10 ? '!' : ','), + timings.frameTicks, (timings.frameTicks > 16 ? '!' : ' ')); +} FrameTimings CModel3::GetTimings(void) { @@ -2538,12 +2534,12 @@ int CModel3::StartDriveBoardThread(void *data) { // Call method on CModel3 to run drive board thread CModel3 *model3 = (CModel3*)data; - return model3->RunDriveBoardThread(); -} - -int CModel3::RunMainBoardThread(void) -{ - for (;;) + return model3->RunDriveBoardThread(); +} + +int CModel3::RunMainBoardThread(void) +{ + for (;;) { bool wait = true; bool exit = false; @@ -2835,12 +2831,12 @@ int CModel3::RunDriveBoardThread(void) ThreadError: ErrorLog("Threading error in RunDriveBoardThread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); m_multiThreaded = false; - return 1; -} - -void CModel3::Reset(void) -{ - // Clear memory (but do not modify backup RAM!) + return 1; +} + +void CModel3::Reset(void) +{ + // Clear memory (but do not modify backup RAM!) memset(ram, 0, 0x800000); // Initial bank is bank 0 @@ -2887,7 +2883,7 @@ void CModel3::Reset(void) timings.drvTicks = 0; #ifdef NET_BOARD timings.netTicks = 0; - NetBoard.CodeReady = false; + NetBoard->Reset(); #endif timings.frameTicks = 0; @@ -2944,13 +2940,13 @@ bool CModel3::LoadGame(const Game &game, const ROMSet &rom_set) else rom_set.get_rom("sound_samples").CopyTo(sampleROM, 16*0x100000); rom_set.get_rom("sound_program").CopyTo(soundROM, 512*1024); - rom_set.get_rom("mpeg_program").CopyTo(dsbROM, 128*1024); - rom_set.get_rom("mpeg_music").CopyTo(mpegROM, 16*0x100000); - rom_set.get_rom("driveboard_program").CopyTo(driveROM, 64*1024); - - // Convert PowerPC and 68K ROMs to little endian words - Util::FlipEndian32(crom, 8*0x100000 + 128*0x100000); - Util::FlipEndian16(soundROM, 512*1024); + rom_set.get_rom("mpeg_program").CopyTo(dsbROM, 128*1024); + rom_set.get_rom("mpeg_music").CopyTo(mpegROM, 16*0x100000); + rom_set.get_rom("driveboard_program").CopyTo(driveROM, 64*1024); + + // Convert PowerPC and 68K ROMs to little endian words + Util::FlipEndian32(crom, 8*0x100000 + 128*0x100000); + Util::FlipEndian16(soundROM, 512*1024); Util::FlipEndian16(sampleROM, 16*0x100000); // Configure CPU and PCI bridge @@ -2964,17 +2960,17 @@ bool CModel3::LoadGame(const Game &game, const ROMSet &rom_set) } else if (game.stepping == "1.5") { - ppc_config.pvr = PPC_MODEL_603E; // 100 MHz - ppc_config.bus_frequency = BUS_FREQUENCY_66MHZ; - ppc_config.bus_frequency_multiplier = 0x15; // 1.5X multiplier - PCIBridge.SetModel(0x105); // MPC105 + ppc_config.pvr = PPC_MODEL_603E; // 100 MHz + ppc_config.bus_frequency = BUS_FREQUENCY_66MHZ; + ppc_config.bus_frequency_multiplier = 0x15; // 1.5X multiplier + PCIBridge.SetModel(0x105); // MPC105 } else if (game.stepping == "1.0") { - ppc_config.pvr = PPC_MODEL_603R; // 66 MHz - ppc_config.bus_frequency = BUS_FREQUENCY_66MHZ; - ppc_config.bus_frequency_multiplier = 0x10; // 1X multiplier - PCIBridge.SetModel(0x105); // MPC105 + ppc_config.pvr = PPC_MODEL_603R; // 66 MHz + ppc_config.bus_frequency = BUS_FREQUENCY_66MHZ; + ppc_config.bus_frequency_multiplier = 0x10; // 1X multiplier + PCIBridge.SetModel(0x105); // MPC105 } else { @@ -3005,18 +3001,18 @@ bool CModel3::LoadGame(const Game &game, const ROMSet &rom_set) PPCFetchRegions[2].end = 0; PPCFetchRegions[2].ptr = NULL; ppc_set_fetch(PPCFetchRegions); - - // Initialize Real3D + + // Initialize Real3D int stepping = ((game.stepping[0] - '0') << 4) | (game.stepping[2] - '0'); uint32_t real3DPCIID = game.real3d_pci_id; if (0 == real3DPCIID) { real3DPCIID = stepping >= 0x20 ? CReal3D::PCIID::Step2x : CReal3D::PCIID::Step1x; - } - GPU.SetStepping(stepping, real3DPCIID); - - // MPEG board (if present) - if (rom_set.get_rom("mpeg_program").size) + } + GPU.SetStepping(stepping, real3DPCIID); + + // MPEG board (if present) + if (rom_set.get_rom("mpeg_program").size) { if (game.mpeg_board == "DSB1") { @@ -3078,8 +3074,6 @@ bool CModel3::LoadGame(const Game &game, const ROMSet &rom_set) // Print game information std::set extra_hw; - std::string netboard_present = game.netboard_present; - std::transform(netboard_present.begin(), netboard_present.end(), netboard_present.begin(), ::tolower); if (DSB) extra_hw.insert(Util::Format() << "Digital Sound Board (Type " << game.mpeg_board << ")"); @@ -3092,7 +3086,7 @@ bool CModel3::LoadGame(const Game &game, const ROMSet &rom_set) } if (game.encryption_key) extra_hw.insert("Security Board"); - if (netboard_present.compare("true")==0) + if (game.netboard_present) extra_hw.insert("Net Board"); if (!game.version.empty()) std::cout << " Title: " << game.title << " (" << game.version << ")" << std::endl; @@ -3108,13 +3102,13 @@ bool CModel3::LoadGame(const Game &game, const ROMSet &rom_set) m_game = game; #ifdef NET_BOARD - NetBoard.GetGame(m_game); - if (OKAY != NetBoard.Init(netRAM, netBuffer)) + NetBoard->GetGame(m_game); + if (OKAY != NetBoard->Init(netRAM, netBuffer)) { return FAIL; } - m_runNetBoard = m_game.stepping != "1.0" && (NetBoard.IsAttached() && (m_config["EmulateNet"].ValueAs())); + m_runNetBoard = m_game.stepping != "1.0" && NetBoard->IsAttached(); #endif return OKAY; } @@ -3147,40 +3141,40 @@ void CModel3::AttachOutputs(COutputs *OutputsPtr) DebugLog("Model 3 attached outputs\n"); } -const static int RAM_SIZE = 0x800000; //8MB -const static int CROM_SIZE = 0x800000; //8MB -const static int CROMxx_SIZE = 0x8000000; //128MB -const static int VROM_SIZE = 0x4000000; //64MB -const static int BACKUPRAM_SIZE = 0x20000; //128KB -const static int SECURITYRAM_SIZE = 0x20000; //128KB -const static int SOUNDROM_SIZE = 0x80000; //512KB -const static int SAMPLEROM_SIZE = 0x1000000; //16MB -const static int DSBPROGROM_SIZE = 0x20000; //128KB -const static int DSBMPEGROM_SIZE = 0x1000000; //16MB -const static int DRIVEROM_SIZE = 0x10000; //64KB -const static int NETBUFFER_SIZE = 0x20000; //128KB -const static int NETRAM_SIZE = 0x20000; //128KB - -const static int MEM_POOL_SIZE = RAM_SIZE + CROM_SIZE + - CROMxx_SIZE + VROM_SIZE + - BACKUPRAM_SIZE + SECURITYRAM_SIZE + - SOUNDROM_SIZE + SAMPLEROM_SIZE + - DSBPROGROM_SIZE + DSBMPEGROM_SIZE + - DRIVEROM_SIZE + NETBUFFER_SIZE + - NETRAM_SIZE; - -const static int RAM_OFFSET = 0; -const static int CROM_OFFSET = RAM_OFFSET + RAM_SIZE; -const static int CROMxx_OFFSET = CROM_OFFSET + CROM_SIZE; -const static int VROM_OFFSET = CROMxx_OFFSET + CROMxx_SIZE; -const static int BACKUPRAM_OFFSET = VROM_OFFSET + VROM_SIZE; -const static int SECURITYRAM_OFFSET = BACKUPRAM_OFFSET + BACKUPRAM_SIZE; -const static int SOUNDROM_OFFSET = SECURITYRAM_OFFSET + SECURITYRAM_SIZE; -const static int SAMPLEROM_OFFSET = SOUNDROM_OFFSET + SOUNDROM_SIZE; -const static int DSBPROGROM_OFFSET = SAMPLEROM_OFFSET + SAMPLEROM_SIZE; -const static int DSBMPEGROM_OFFSET = DSBPROGROM_OFFSET + DSBPROGROM_SIZE; -const static int DRIVEROM_OFFSET = DSBMPEGROM_OFFSET + DSBMPEGROM_SIZE; -const static int NETBUFFER_OFFSET = DRIVEROM_OFFSET + DRIVEROM_SIZE; +const static int RAM_SIZE = 0x800000; //8MB +const static int CROM_SIZE = 0x800000; //8MB +const static int CROMxx_SIZE = 0x8000000; //128MB +const static int VROM_SIZE = 0x4000000; //64MB +const static int BACKUPRAM_SIZE = 0x20000; //128KB +const static int SECURITYRAM_SIZE = 0x20000; //128KB +const static int SOUNDROM_SIZE = 0x80000; //512KB +const static int SAMPLEROM_SIZE = 0x1000000; //16MB +const static int DSBPROGROM_SIZE = 0x20000; //128KB +const static int DSBMPEGROM_SIZE = 0x1000000; //16MB +const static int DRIVEROM_SIZE = 0x10000; //64KB +const static int NETBUFFER_SIZE = 0x20000; //128KB +const static int NETRAM_SIZE = 0x20000; //128KB + +const static int MEM_POOL_SIZE = RAM_SIZE + CROM_SIZE + + CROMxx_SIZE + VROM_SIZE + + BACKUPRAM_SIZE + SECURITYRAM_SIZE + + SOUNDROM_SIZE + SAMPLEROM_SIZE + + DSBPROGROM_SIZE + DSBMPEGROM_SIZE + + DRIVEROM_SIZE + NETBUFFER_SIZE + + NETRAM_SIZE; + +const static int RAM_OFFSET = 0; +const static int CROM_OFFSET = RAM_OFFSET + RAM_SIZE; +const static int CROMxx_OFFSET = CROM_OFFSET + CROM_SIZE; +const static int VROM_OFFSET = CROMxx_OFFSET + CROMxx_SIZE; +const static int BACKUPRAM_OFFSET = VROM_OFFSET + VROM_SIZE; +const static int SECURITYRAM_OFFSET = BACKUPRAM_OFFSET + BACKUPRAM_SIZE; +const static int SOUNDROM_OFFSET = SECURITYRAM_OFFSET + SECURITYRAM_SIZE; +const static int SAMPLEROM_OFFSET = SOUNDROM_OFFSET + SOUNDROM_SIZE; +const static int DSBPROGROM_OFFSET = SAMPLEROM_OFFSET + SAMPLEROM_SIZE; +const static int DSBMPEGROM_OFFSET = DSBPROGROM_OFFSET + DSBPROGROM_SIZE; +const static int DRIVEROM_OFFSET = DSBMPEGROM_OFFSET + DSBMPEGROM_SIZE; +const static int NETBUFFER_OFFSET = DRIVEROM_OFFSET + DRIVEROM_SIZE; const static int NETRAM_OFFSET = NETBUFFER_OFFSET + NETBUFFER_SIZE; // Model 3 initialization. Some initialization is deferred until ROMs are loaded in LoadROMSet() @@ -3222,49 +3216,53 @@ bool CModel3::Init(void) if (OKAY != GPU.Init(vrom,this,&IRQ,0x100)) // same for Real3D DMA interrupt return FAIL; if (OKAY != SoundBoard.Init(soundROM,sampleROM)) - return FAIL; - - PCIBridge.AttachPCIBus(&PCIBus); - PCIBus.AttachDevice(13,&GPU); - PCIBus.AttachDevice(14,&SCSI); + return FAIL; + + PCIBridge.AttachPCIBus(&PCIBus); + PCIBus.AttachDevice(13,&GPU); + PCIBus.AttachDevice(14,&SCSI); PCIBus.AttachDevice(16,this); +#ifdef NET_BOARD + if (m_config["SimulateNet"].ValueAs()) + NetBoard = new CSimNetBoard(m_config); + else + NetBoard = new CNetBoard(m_config); +#endif // NET_BOARD + DebugLog("Initialized Model 3 (allocated %1.1f MB)\n", memSizeMB); return OKAY; } - -CSoundBoard *CModel3::GetSoundBoard(void) -{ - return &SoundBoard; -} - -CDriveBoard *CModel3::GetDriveBoard(void) + +CSoundBoard *CModel3::GetSoundBoard(void) +{ + return &SoundBoard; +} + +CDriveBoard *CModel3::GetDriveBoard(void) { return DriveBoard; } -#ifdef NET_BOARD -CNetBoard *CModel3::GetNetBoard(void) -{ - return &NetBoard; -} -#endif - +#ifdef NET_BOARD +INetBoard *CModel3::GetNetBoard(void) +{ + return NetBoard; +} +#endif + CModel3::CModel3(const Util::Config::Node &config) : m_config(config), m_multiThreaded(config["MultiThreaded"].ValueAs()), m_gpuMultiThreaded(config["GPUMultiThreaded"].ValueAs()), TileGen(config), GPU(config), - SoundBoard(config), -#ifdef NET_BOARD - NetBoard(config), -#endif - m_jtag(GPU) -{ - // Initialize pointers so dtor can know whether to free them - memoryPool = NULL; + SoundBoard(config), + m_jtag(GPU) +{ + // Initialize pointers so dtor can know whether to free them + memoryPool = NULL; // Various uninitialized pointers Inputs = NULL; @@ -3280,38 +3278,38 @@ CModel3::CModel3(const Util::Config::Node &config) backupRAM = NULL; securityRAM = NULL; netRAM = NULL; - netBuffer = NULL; - - DSB = NULL; - DriveBoard = NULL; - - securityPtr = 0; - - startedThreads = false; - pauseThreads = false; - stopThreads = false; - ppcBrdThread = NULL; - sndBrdThread = NULL; - drvBrdThread = NULL; - - ppcBrdThreadRunning = false; - ppcBrdThreadDone = false; - sndBrdThreadRunning = false; - sndBrdThreadDone = false; - drvBrdThreadRunning = false; - drvBrdThreadDone = false; - - syncSndBrdThread = false; - ppcBrdThreadSync = NULL; - sndBrdThreadSync = NULL; - drvBrdThreadSync = NULL; - - notifyLock = NULL; - notifySync = NULL; - - DebugLog("Built Model 3\n"); -} - + netBuffer = NULL; + + DSB = NULL; + DriveBoard = NULL; + + securityPtr = 0; + + startedThreads = false; + pauseThreads = false; + stopThreads = false; + ppcBrdThread = NULL; + sndBrdThread = NULL; + drvBrdThread = NULL; + + ppcBrdThreadRunning = false; + ppcBrdThreadDone = false; + sndBrdThreadRunning = false; + sndBrdThreadDone = false; + drvBrdThreadRunning = false; + drvBrdThreadDone = false; + + syncSndBrdThread = false; + ppcBrdThreadSync = NULL; + sndBrdThreadSync = NULL; + drvBrdThreadSync = NULL; + + notifyLock = NULL; + notifySync = NULL; + + DebugLog("Built Model 3\n"); +} + // Dumps a memory region to a file for debugging purposes #if 0 static void Dump(const char *file, uint8_t *buf, size_t size, bool reverse32, bool reverse16) @@ -3355,18 +3353,18 @@ CModel3::~CModel3(void) if (DSB != NULL) { delete DSB; - DSB = NULL; - } - + DSB = NULL; + } + if (DriveBoard != NULL) { delete DriveBoard; - DriveBoard = NULL; - } - - Inputs = NULL; - Outputs = NULL; - ram = NULL; + DriveBoard = NULL; + } + + Inputs = NULL; + Outputs = NULL; + ram = NULL; crom = NULL; vrom = NULL; soundROM = NULL; @@ -3377,7 +3375,7 @@ CModel3::~CModel3(void) backupRAM = NULL; securityRAM = NULL; netRAM = NULL; - netBuffer = NULL; - - DebugLog("Destroyed Model 3\n"); -} + netBuffer = NULL; + + DebugLog("Destroyed Model 3\n"); +} diff --git a/Src/Model3/Model3.h b/Src/Model3/Model3.h index 815b751..302bd57 100644 --- a/Src/Model3/Model3.h +++ b/Src/Model3/Model3.h @@ -32,6 +32,9 @@ #include "Model3/IEmulator.h" #include "Model3/JTAG.h" #include "Model3/Crypto.h" +#ifdef NET_BOARD +#include "Network/INetBoard.h" +#endif // NET_BOARD #include "Util/NewConfig.h" /* @@ -140,9 +143,9 @@ public: * Returns a reference to the net board. * Returns: - * Pointer to CNetBoard object. + * Pointer to CNetBoard or CSimNetBoard object. */ - CNetBoard * GetNetBoard(void); + INetBoard * GetNetBoard(void); #endif /* @@ -306,7 +309,7 @@ private: CCrypto m_cryptoDevice; // Encryption device CJTAG m_jtag; // JTAG interface #ifdef NET_BOARD - CNetBoard NetBoard; // Net board + INetBoard *NetBoard; // Net board bool m_runNetBoard; #endif diff --git a/Src/Network/INetBoard.h b/Src/Network/INetBoard.h new file mode 100644 index 0000000..b1d98f3 --- /dev/null +++ b/Src/Network/INetBoard.h @@ -0,0 +1,47 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011-2020 Bart Trzynadlowski, Nik Henson, Ian Curtis, + ** Harry Tuttle, and Spindizzi + ** + ** 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 . + **/ + +#ifndef INCLUDED_INETBOARD_H +#define INCLUDED_INETBOARD_H + +#include "Types.h" +#include "Game.h" +#include "BlockFile.h" + +class INetBoard +{ +public: + virtual void SaveState(CBlockFile* SaveState) = 0; + virtual void LoadState(CBlockFile* SaveState) = 0; + + virtual void RunFrame(void) = 0; + virtual void Reset(void) = 0; + + virtual bool IsAttached(void) = 0; + virtual bool IsRunning(void) = 0; + + virtual bool Init(UINT8* netRAMPtr, UINT8* netBufferPtr) = 0; + + virtual void GetGame(Game) = 0; +}; + +#endif \ No newline at end of file diff --git a/Src/Network/NetBoard.cpp b/Src/Network/NetBoard.cpp index b40c598..fb777bc 100644 --- a/Src/Network/NetBoard.cpp +++ b/Src/Network/NetBoard.cpp @@ -61,35 +61,35 @@ // change port/ip you want, I only tested in local on same machine (watch your firewall) // // add for master -// EmulateNet=1 -// port_in = 1970 -// port_out = 1971 +// Network=1 +// PortIn = 1970 +// PortOut = 1971 // addr_out = "127.0.0.1" // // add for slave -// EmulateNet=1 -// port_in = 1971 -// port_out = 1970 +// Network=1 +// PortIn = 1971 +// PortOut = 1970 // addr_out = "127.0.0.1" // // or in case of 3 cabs // // add for master -// EmulateNet=1 -// port_in = 1970 -// port_out = 1971 +// Network=1 +// PortIn = 1970 +// PortOut = 1971 // addr_out = "127.0.0.1" // // add for slave1 -// EmulateNet=1 -// port_in = 1971 -// port_out = 1972 +// Network=1 +// PortIn = 1971 +// PortOut = 1972 // addr_out = "127.0.0.1" // // add for slave2 -// EmulateNet=1 -// port_in = 1972 -// port_out = 1970 +// Network=1 +// PortIn = 1972 +// PortOut = 1970 // addr_out = "127.0.0.1" //#define NET_DEBUG @@ -1095,9 +1095,7 @@ bool CNetBoard::Init(UINT8 * netRAMPtr, UINT8 *netBufferPtr) netRAM = netRAMPtr; netBuffer = netBufferPtr; - std::string netboard_present = Gameinfo.netboard_present; - std::transform(netboard_present.begin(), netboard_present.end(), netboard_present.begin(), ::tolower); - m_attached = (netboard_present.compare("true") == 0) ? true : false; + m_attached = Gameinfo.netboard_present && m_config["Network"].ValueAs(); test_irq = 0; @@ -1159,14 +1157,14 @@ bool CNetBoard::Init(UINT8 * netRAMPtr, UINT8 *netBufferPtr) //netsocks - port_in = m_config["port_in"].ValueAs(); - port_out = m_config["port_out"].ValueAs(); - addr_out = m_config["addr_out"].ValueAs(); + port_in = m_config["PortIn"].ValueAs(); + port_out = m_config["PortOut"].ValueAs(); + addr_out = m_config["AddressOut"].ValueAs(); nets = std::make_unique(addr_out, port_out); netr = std::make_unique(port_in); - if (m_config["EmulateNet"].ValueAs() && m_attached) { + if (m_config["Network"].ValueAs() && m_attached) { while (!nets->Connect()) { printf("Connecting to %s:%i ..\n", addr_out.c_str(), port_out); } @@ -1189,7 +1187,6 @@ CNetBoard::CNetBoard(const Util::Config::Node &config) : m_config(config) ioreg = NULL; ctrlrw = NULL; - CodeReady = false; test_irq = 0; int5 = false; @@ -1224,12 +1221,10 @@ void CNetBoard::LoadState(CBlockFile * SaveState) { } -bool CNetBoard::RunFrame(void) +void CNetBoard::RunFrame(void) { - if (!CodeReady) - { - return true; - } + if (!IsRunning()) + return; M68KSetContext(&M68K); @@ -1269,8 +1264,6 @@ bool CNetBoard::RunFrame(void) M68KRun((4000000 / 60)); M68KGetContext(&M68K); - - return true; } void CNetBoard::Reset(void) @@ -1311,6 +1304,11 @@ bool CNetBoard::IsAttached(void) return m_attached; } +bool CNetBoard::IsRunning(void) +{ + return m_attached && ((ioreg[0xc0] == 0xff) || (ioreg[0xc0] == 0x01)); +} + void CNetBoard::GetGame(Game gameinfo) { Gameinfo = gameinfo; diff --git a/Src/Network/NetBoard.h b/Src/Network/NetBoard.h index bb2cd52..6f99a70 100644 --- a/Src/Network/NetBoard.h +++ b/Src/Network/NetBoard.h @@ -27,13 +27,14 @@ #include "CPU/Bus.h" #include "OSD/Thread.h" #include +#include "INetBoard.h" #include "TCPSend.h" #include "TCPSendAsync.h" #include "TCPReceive.h" //#define NET_BUF_SIZE 32800 // 16384 not enough -class CNetBoard : public IBus +class CNetBoard : public IBus, public INetBoard { public: @@ -48,12 +49,13 @@ public: void SaveState(CBlockFile *SaveState); void LoadState(CBlockFile *SaveState); - bool RunFrame(void); + void RunFrame(void); void Reset(void); // Returns a reference to the 68K CPU context M68KCtx *GetM68K(void); bool IsAttached(void); + bool IsRunning(void); bool CodeReady; bool Init(UINT8 *netRAMPtr, UINT8 *netBufferPtr); diff --git a/Src/Network/SimNetBoard.cpp b/Src/Network/SimNetBoard.cpp new file mode 100644 index 0000000..e582183 --- /dev/null +++ b/Src/Network/SimNetBoard.cpp @@ -0,0 +1,549 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011-2020 Bart Trzynadlowski, Nik Henson, Ian Curtis, + ** Harry Tuttle, and Spindizzi + ** + ** 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 . + **/ + +#include +#include +#include "Supermodel.h" +#include "SimNetBoard.h" + + // these make 16-bit read/writes much neater +#define RAM16 *(uint16_t*)&RAM +#define CommRAM16 *(uint16_t*)&CommRAM +#define ioreg16 *(uint16_t*)&ioreg + +static const uint64_t netGUID = 0x5bf177da34872; + +inline bool CSimNetBoard::IsGame(const char* gameName) +{ + return (m_gameInfo.name == gameName) || (m_gameInfo.parent == gameName); +} + +CSimNetBoard::CSimNetBoard(const Util::Config::Node& config) : m_config(config) +{ + m_running = true; +} + +CSimNetBoard::~CSimNetBoard(void) +{ + m_running = false; + + if (m_connectThread.joinable()) + m_connectThread.join(); +} + +void CSimNetBoard::SaveState(CBlockFile* SaveState) +{ + +} + +void CSimNetBoard::LoadState(CBlockFile* SaveState) +{ + +} + +bool CSimNetBoard::Init(uint8_t* netRAMPtr, uint8_t* netBufferPtr) +{ + RAM = netRAMPtr; + CommRAM = netBufferPtr; + ioreg = netBufferPtr + 0x10000; + + m_attached = m_gameInfo.netboard_present && m_config["Network"].ValueAs(); + + if (!m_attached) + return 0; + + if (IsGame("daytona2") || IsGame("harley") || IsGame("scud") || IsGame("srally2") || + IsGame("skichamp") || IsGame("spikeout") || IsGame("spikeofe")) + m_gameType = GameType::one; + else if (IsGame("lemans24") || IsGame("von2") || IsGame("dirtdvls")) + m_gameType = GameType::two; + else + return ErrorLog("Game not recognized or supported"); + + m_state = State::start; + + //netsocks + port_in = m_config["PortIn"].ValueAs(); + port_out = m_config["PortOut"].ValueAs(); + addr_out = m_config["AddressOut"].ValueAs(); + + nets = std::make_unique(addr_out, port_out); + netr = std::make_unique(port_in); + + return 0; +} + +void CSimNetBoard::RunFrame(void) +{ + if (!IsRunning()) + return; + + switch (m_state) + { + case State::start: + if (!m_connected && !m_connectThread.joinable()) + m_connectThread = std::thread(&CSimNetBoard::ConnectProc, this); + ioreg16[0x88] = 0; + ioreg16[0x8a] = IsGame("dirtdvls") ? 0x4004 : 0xe000; + m_state = State::init; + break; + + case State::init: + memset(CommRAM, 0, 0x10000); + if (m_gameType == GameType::one) + { + if (ioreg16[0x88] & 0x8000) // has main board changed this register? + { + ioreg[0] |= 0x01; // simulate IRQ 2 ack + if (ioreg16[0x88] == 0xf000) + { + // initialization complete + ioreg16[0x8a] = 0; + CommRAM16[0x72] = FLIPENDIAN16(0x1); // is this necessary? + m_state = State::testing; + } + ioreg16[0x88] = 0; // 0 should work for all init subroutines + } + } + else + { + // type 2 performs initialization on its own + ioreg16[0x8a] = 0; + m_state = State::testing; + m_counter = 0; + } + break; + + case State::testing: + if (m_gameType == GameType::one) + { + ioreg16[0x88] += 1; // type 1 games require this to be incremented every frame + + if (!m_connected) + break; + + uint8_t numMachines, machineIndex; + + if (RAM16[0x400] == 0) // master + { + // flush receive buffer + while (netr->CheckDataAvailable()) + { + netr->Receive(); + } + + // check all linked instances have the same GUID + nets->Send(&netGUID, sizeof(netGUID)); + auto& recv_data = netr->Receive(); + if (recv_data.empty()) + break; + uint64_t testGUID; + memcpy(&testGUID, &recv_data[0], recv_data.size()); + if (testGUID != netGUID) + testGUID = 0; + + // send the GUID for one more loop + nets->Send(&testGUID, sizeof(testGUID)); + netr->Receive(); + + if (testGUID != netGUID) + { + ErrorLog("unable to verify connection. Make sure all machines are using same build!"); + m_state = State::error; + break; + } + + // master has an index of zero + machineIndex = 0; + nets->Send(&machineIndex, sizeof(machineIndex)); + + // receive back the number of other linked machines + recv_data = netr->Receive(); + if (recv_data.empty()) + break; + numMachines = recv_data[0]; + + // send the number of other linked machines + nets->Send(&numMachines, sizeof(numMachines)); + netr->Receive(); + } + else + { + // receive GUID from the previous machine and check it matches + auto& recv_data = netr->Receive(); + if (recv_data.empty()) + break; + uint64_t testGUID; + memcpy(&testGUID, &recv_data[0], recv_data.size()); + if (testGUID != netGUID) + testGUID = 0; + nets->Send(&testGUID, sizeof(testGUID)); + + // one more time, in case a later machine has a GUID mismatch + recv_data = netr->Receive(); + if (recv_data.empty()) + break; + memcpy(&testGUID, &recv_data[0], recv_data.size()); + if (testGUID != netGUID) + testGUID = 0; + nets->Send(&testGUID, sizeof(testGUID)); + + if (testGUID != netGUID) + { + ErrorLog("unable to verify connection. Make sure all machines are using same build!"); + m_state = State::error; + break; + } + + // receive the previous machine's index, increment it, send it to the next machine + recv_data = netr->Receive(); + if (recv_data.empty()) + break; + machineIndex = recv_data[0] + 1; + nets->Send(&machineIndex, sizeof(machineIndex)); + + // receive the number of other linked machines and forward it on + recv_data = netr->Receive(); + if (recv_data.empty()) + break; + numMachines = recv_data[0]; + nets->Send(&numMachines, sizeof(numMachines)); + } + + m_numMachines = numMachines + 1; + + ioreg16[0x88] = 0; // supposed to cycle between 0 and 1 (also 2 for Daytona 2); doesn't seem to matter + ioreg16[0x8a] = 0x2021 + (numMachines * 0x20) + machineIndex; + + CommRAM16[0x0] = RAM16[0x400]; // 0 if master, 1 if slave + CommRAM16[0x2] = numMachines; + CommRAM16[0x4] = machineIndex; + + m_counter = 0; + CommRAM16[0x6] = 0; + + m_segmentSize = RAM16[0x404]; + + // don't know if these are actually required, but it never hurts to include them + CommRAM16[0x8] = FLIPENDIAN16(0x100 + m_segmentSize); + CommRAM16[0xa] = FLIPENDIAN16(RAM16[0x402] - m_segmentSize - 1); + CommRAM16[0xc] = FLIPENDIAN16(0x100); + CommRAM16[0xe] = FLIPENDIAN16(RAM16[0x402] - m_segmentSize + 0x200); + + m_state = State::ready; + } + else + { + if (!m_connected) + break; + + // we have to track both playable and non-playable machines for type 2 + struct + { + uint8_t total; + uint8_t playable; + } numMachines, machineIndex; + + if (RAM16[0x200] == 0) // master + { + // flush receive buffer + while (netr->CheckDataAvailable()) + netr->Receive(); + + // check all linked instances have the same GUID + nets->Send(&netGUID, sizeof(netGUID)); + auto& recv_data = netr->Receive(); + if (recv_data.empty()) + break; + + uint64_t testGUID; + memcpy(&testGUID, &recv_data[0], recv_data.size()); + if (testGUID != netGUID) + testGUID = 0; + + // send the GUID for one more loop + nets->Send(&testGUID, sizeof(testGUID)); + netr->Receive(); + + if (testGUID != netGUID) + { + ErrorLog("unable to verify connection. Make sure all machines are using same build!"); + m_state = State::error; + break; + } + + // master has indices set to zero + machineIndex.total = 0, machineIndex.playable = 0; + nets->Send(&machineIndex, sizeof(machineIndex)); + + // receive back the number of other linked machines + recv_data = netr->Receive(); + if (recv_data.empty()) + break; + memcpy(&numMachines, &recv_data[0], recv_data.size()); + + // send the number of other linked machines + nets->Send(&numMachines, sizeof(numMachines)); + netr->Receive(); + } + else if (RAM16[0x200] < 0x8000) // slave + { + // receive GUID from the previous machine and check it matches + auto& recv_data = netr->Receive(); + if (recv_data.empty()) + break; + uint64_t testGUID; + memcpy(&testGUID, &recv_data[0], recv_data.size()); + if (testGUID != netGUID) + testGUID = 0; + nets->Send(&testGUID, sizeof(testGUID)); + + // one more time, in case a later machine has a GUID mismatch + recv_data = netr->Receive(); + if (recv_data.empty()) + break; + memcpy(&testGUID, &recv_data[0], recv_data.size()); + if (testGUID != netGUID) + testGUID = 0; + nets->Send(&testGUID, sizeof(testGUID)); + + if (testGUID != netGUID) + { + ErrorLog("unable to verify connection. Make sure all machines are using same build!"); + m_state = State::error; + break; + } + + // receive the indices of the previous machine and increment them + recv_data = netr->Receive(); + if (recv_data.empty()) + break; + memcpy(&machineIndex, &recv_data[0], recv_data.size()); + machineIndex.total++, machineIndex.playable++; + + // send our indices to the next machine + nets->Send(&machineIndex, sizeof(machineIndex)); + + // receive the number of machines + recv_data = netr->Receive(); + if (recv_data.empty()) + break; + memcpy(&numMachines, &recv_data[0], recv_data.size()); + + // forward the number of machines + nets->Send(&numMachines, sizeof(numMachines)); + } + else + { + // relay/satellite + + // receive GUID from the previous machine and check it matches + auto& recv_data = netr->Receive(); + if (recv_data.empty()) + break; + uint64_t testGUID; + memcpy(&testGUID, &recv_data[0], recv_data.size()); + if (testGUID != netGUID) + testGUID = 0; + nets->Send(&testGUID, sizeof(testGUID)); + + // one more time, in case a later machine has a GUID mismatch + recv_data = netr->Receive(); + if (recv_data.empty()) + break; + memcpy(&testGUID, &recv_data[0], recv_data.size()); + if (testGUID != netGUID) + testGUID = 0; + nets->Send(&testGUID, sizeof(testGUID)); + + if (testGUID != netGUID) + { + ErrorLog("unable to verify connection. Make sure all machines are using same build!"); + m_state = State::error; + break; + } + + // receive the indices of the previous machine; don't increment the playable index + recv_data = netr->Receive(); + if (recv_data.empty()) + break; + memcpy(&machineIndex, &recv_data[0], recv_data.size()); + machineIndex.total++; + + // send our indices to the next machine + nets->Send(&machineIndex, sizeof(machineIndex)); + + // receive the number of machines + recv_data = netr->Receive(); + if (recv_data.empty()) + break; + memcpy(&numMachines, &recv_data[0], recv_data.size()); + + // forward the number of machines + nets->Send(&numMachines, sizeof(numMachines)); + + // indicate that this machine is a relay/satellite + if (!IsGame("dirtdvls")) + machineIndex.playable |= 0x80; + } + + // if there are no other linked machines, only continue if Supermodel is linked to itself + // there might be more than one machine set to master which would cause glitches + if ((numMachines.total == 0) && ((port_in != port_out) || !addr_out.compare("127.0.0.1"))) + { + ErrorLog("no slave machines detected. Make sure only one machine is set to master!"); + if (IsGame("dirtdvls")) + ioreg16[0x8a] = 0x8085; // seems like the netboard code writers really liked their CPU model numbers + m_state = State::error; + break; + } + + m_numMachines = numMachines.total + 1; + + ioreg16[0x88] = 5; // probably not necessary + if (IsGame("dirtdvls")) + ioreg16[0x8a] = (numMachines.playable << 4) | machineIndex.playable | 0x7400; + else + ioreg16[0x8a] = (numMachines.playable << 8) | machineIndex.playable; + + CommRAM16[0x0] = RAM16[0x200]; // master/slave/relay status + CommRAM16[0x2] = (numMachines.playable << 8) | numMachines.total; + CommRAM16[0x4] = (machineIndex.playable << 8) | machineIndex.total; + + m_counter = 0; + CommRAM16[0x6] = 0; + + m_segmentSize = RAM16[0x204]; + + // don't know if these are actually required, but it never hurts to include them + CommRAM16[0x8] = FLIPENDIAN16(0x100 + m_segmentSize); + CommRAM16[0xa] = FLIPENDIAN16(RAM16[0x206]); + CommRAM16[0xc] = FLIPENDIAN16(0x100); + CommRAM16[0xe] = FLIPENDIAN16(RAM16[0x206] + 0x80); + + m_state = State::ready; + } + break; + + case State::ready: + m_counter++; + CommRAM16[0x6] = FLIPENDIAN16(m_counter); + + if (IsGame("spikeofe")) // temporary hack for spikeout final edition (avoids comm error) + { + nets->Send(CommRAM + 0x100, m_segmentSize * m_numMachines); + auto& recv_data = netr->Receive(); + if (recv_data.size() == 0) + { + // link broken - send an "empty" packet to alert other machines + nets->Send(nullptr, 0); + m_state = State::error; + ioreg16[0x8a] = 0x40; // send "link broken" message to mainboard + break; + } + memcpy(CommRAM + 0x100 + m_segmentSize, recv_data.data(), recv_data.size()); + } + else + { + // we only send what we need to; helps cut down on bandwidth + // each machine has to receive back its own data (TODO: copy this data manually?) + for (int i = 0; i < m_numMachines; i++) + { + nets->Send(CommRAM + 0x100 + i * m_segmentSize, m_segmentSize); + auto& recv_data = netr->Receive(); + if (recv_data.size() == 0) + { + // link broken - send an "empty" packet to alert other machines + nets->Send(nullptr, 0); + m_state = State::error; + if (m_gameType == GameType::one) + ioreg16[0x8a] = 0x40; // send "link broken" message to mainboard + break; + } + memcpy(CommRAM + 0x100 + (i + 1) * m_segmentSize, recv_data.data(), recv_data.size()); + } + } + + break; + + case State::error: + // do nothing + break; + } +} + +void CSimNetBoard::Reset(void) +{ + // if netboard was active, send an "empty" packet so the other machines don't get stuck waiting for data + if (m_state == State::ready) + { + nets->Send(nullptr, 0); + netr->Receive(); + } + + ioreg[0xc0] = 0; + m_state = State::start; +} + +bool CSimNetBoard::IsAttached(void) +{ + return m_attached; +} + +bool CSimNetBoard::IsRunning(void) +{ + return (ioreg[0xc0] == 0xff) || (ioreg[0xc0] == 0x01); // there's probably a better way of checking +} + +void CSimNetBoard::GetGame(Game gameInfo) +{ + m_gameInfo = gameInfo; +} + +void CSimNetBoard::ConnectProc(void) +{ + using namespace std::chrono_literals; + + if (m_connected) + return; + + printf("Connecting to %s:%i ..\n", addr_out.c_str(), port_out); + + // wait until TCPSend has connected to the next machine + while (!nets->Connect()) + { + if (!m_running) + return; + } + + // wait until TCPReceive has accepted a connection from the previous machine + while (!netr->Connected()) + { + if (!m_running) + return; + std::this_thread::sleep_for(1ms); + } + + printf("Successfully connected.\n"); + + m_connected = true; +} \ No newline at end of file diff --git a/Src/Network/SimNetBoard.h b/Src/Network/SimNetBoard.h new file mode 100644 index 0000000..f5b9ccc --- /dev/null +++ b/Src/Network/SimNetBoard.h @@ -0,0 +1,99 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011-2020 Bart Trzynadlowski, Nik Henson, Ian Curtis, + ** Harry Tuttle, and Spindizzi + ** + ** 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 . + **/ + +#ifndef INCLUDED_SIMNETBOARD_H +#define INCLUDED_SIMNETBOARD_H + +#include +#include "TCPSend.h" +#include "TCPReceive.h" +#include "INetBoard.h" + +enum class State +{ + start, + init, + testing, + ready, + error +}; + +enum class GameType +{ + unknown, + one, // all games except those listed below + two // Le Mans 24, Virtual-On, Dirt Devils +}; + +class CSimNetBoard : public INetBoard +{ +public: + CSimNetBoard(const Util::Config::Node& config); + ~CSimNetBoard(void); + + void SaveState(CBlockFile* SaveState); + void LoadState(CBlockFile* SaveState); + + bool Init(uint8_t* netRAMPtr, uint8_t* netBufferPtr); + void RunFrame(void); + void Reset(void); + + bool IsAttached(void); + bool IsRunning(void); + + void GetGame(Game gameInfo); + +private: + // Config + const Util::Config::Node& m_config; + + uint8_t* RAM = nullptr; + uint8_t* CommRAM = nullptr; + uint8_t* ioreg = nullptr; + + // netsock + uint16_t port_in = 0; + uint16_t port_out = 0; + std::string addr_out = ""; + std::thread m_connectThread; + std::atomic_bool m_running = false; + std::atomic_bool m_connected = false; + + std::unique_ptr nets = nullptr; + std::unique_ptr netr = nullptr; + + Game m_gameInfo; + GameType m_gameType = GameType::unknown; + State m_state = State::start; + + uint8_t m_numMachines = 0; + uint16_t m_counter = 0; + + uint16_t m_segmentSize = 0; + + bool m_attached = false; + + inline bool IsGame(const char* gameName); + void ConnectProc(void); +}; + +#endif \ No newline at end of file diff --git a/Src/Network/TCPReceive.cpp b/Src/Network/TCPReceive.cpp index 02dc87e..6e235c6 100644 --- a/Src/Network/TCPReceive.cpp +++ b/Src/Network/TCPReceive.cpp @@ -152,3 +152,8 @@ void TCPReceive::ListenFunc() } } + +bool TCPReceive::Connected() +{ + return (m_receiveSocket != 0); +} \ No newline at end of file diff --git a/Src/Network/TCPReceive.h b/Src/Network/TCPReceive.h index 77e4370..8a4a198 100644 --- a/Src/Network/TCPReceive.h +++ b/Src/Network/TCPReceive.h @@ -36,6 +36,7 @@ public: bool CheckDataAvailable(int timeoutMS = 0); // timeoutMS -1 = wait forever until data arrives, 0 = no waiting, 1+ wait time in milliseconds std::vector& Receive(); + bool Connected(); private: diff --git a/Src/Network/TCPSend.cpp b/Src/Network/TCPSend.cpp index 0541de2..d67486b 100644 --- a/Src/Network/TCPSend.cpp +++ b/Src/Network/TCPSend.cpp @@ -60,13 +60,13 @@ bool TCPSend::Send(const void * data, int length) DPRINTF("Sending %i bytes\n", length); - if (!length) { - return true; // 0 sized packet will blow our connex - } - int sent = 0; sent = SDLNet_TCP_Send(m_socket, &length, sizeof(int)); // pack the length at the start of transmission. + + if (!length) + return true; // 0 sized packet will blow our connex + sent = SDLNet_TCP_Send(m_socket, data, length); if (sent < length) { diff --git a/Src/OSD/SDL/Main.cpp b/Src/OSD/SDL/Main.cpp index 6c8abd5..a02aa73 100644 --- a/Src/OSD/SDL/Main.cpp +++ b/Src/OSD/SDL/Main.cpp @@ -1425,7 +1425,8 @@ static Util::Config::Node DefaultConfig() config.Set("SDLConstForceThreshold", "30"); #ifdef NET_BOARD // NetBoard - config.Set("EmulateNet", false); + config.Set("Network", false); + config.Set("SimulateNet", true); #endif #else config.Set("InputSystem", "sdl"); @@ -1507,8 +1508,10 @@ static void Help(void) puts(""); #ifdef NET_BOARD puts("Net Options:"); - puts(" -no-net Disable net board emulation [Default]"); - puts(" -net Enable net board emulation (requires -no-threads)"); + puts(" -no-net Disable net board [Default]"); + puts(" -net Enable net board"); + puts(" -simulate-netboard Simulate the net board [Default]"); + puts(" -emulate-netboard Emulate the net board (requires -no-threads)"); puts(""); #endif puts("Input Options:"); @@ -1610,8 +1613,10 @@ static ParsedCommandLine ParseCommandLine(int argc, char **argv) { "-legacy-scsp", { "LegacySoundDSP", true } }, { "-new-scsp", { "LegacySoundDSP", false } }, #ifdef NET_BOARD - { "-net", { "EmulateNet", true } }, - { "-no-net", { "EmulateNet", false } }, + { "-net", { "Network", true } }, + { "-no-net", { "Network", false } }, + { "-simulate-netboard", { "SimulateNet", true } }, + { "-emulate-netboard", { "SimulateNet", false } }, #endif { "-no-force-feedback", { "ForceFeedback", false } }, { "-force-feedback", { "ForceFeedback", true } }, diff --git a/Src/Supermodel.h b/Src/Supermodel.h index 7030b7b..d539b59 100644 --- a/Src/Supermodel.h +++ b/Src/Supermodel.h @@ -38,11 +38,11 @@ /****************************************************************************** Program-Wide Definitions -******************************************************************************/ +******************************************************************************/ + +#include "Version.h" + -#include "Version.h" - - /****************************************************************************** OS-Dependent (OSD) Items @@ -112,7 +112,7 @@ All primary header files for modules used throughout Supermodel are included here, except for external packages and APIs. ******************************************************************************/ - + #include "BlockFile.h" #include "Graphics/New3D/New3D.h" #include "Graphics/Render2D.h" @@ -152,9 +152,6 @@ #include "Model3/DriveBoard/JoystickBoard.h" #include "Model3/DriveBoard/SkiBoard.h" #include "Model3/DriveBoard/BillBoard.h" -#ifdef NET_BOARD -#include "Network/NetBoard.h" -#endif #include "Model3/Model3.h" diff --git a/VS2008/Supermodel.vcxproj b/VS2008/Supermodel.vcxproj index e5ab21a..86578d9 100644 --- a/VS2008/Supermodel.vcxproj +++ b/VS2008/Supermodel.vcxproj @@ -343,6 +343,7 @@ xcopy /D /Y "$(ProjectDir)..\Config\*" "$(TargetDir)Config" + @@ -517,7 +518,9 @@ xcopy /D /Y "$(ProjectDir)..\Config\*" "$(TargetDir)Config" + + diff --git a/VS2008/Supermodel.vcxproj.filters b/VS2008/Supermodel.vcxproj.filters index 98ce804..24798eb 100644 --- a/VS2008/Supermodel.vcxproj.filters +++ b/VS2008/Supermodel.vcxproj.filters @@ -144,6 +144,15 @@ {5adb385b-66b5-4aec-9f7c-37a82388f0d2} + + {aff86ef8-d9ca-471a-8839-9094616dab52} + + + {08ed9259-68ad-49c9-b013-978b5806f96a} + + + {fdb4e5a0-a0ec-4865-a5e6-56eb54c36a2d} + @@ -455,6 +464,9 @@ Source Files\Model3\DriveBoard + + Source Files\Network + @@ -688,57 +700,6 @@ Header Files\Debugger\CPU - - Source Files\Graphics - - - Source Files\Graphics - - - Source Files\Graphics - - - Source Files\Graphics - - - Source Files\Graphics\Legacy - - - Source Files\Graphics\Legacy - - - Source Files\Graphics\Legacy - - - Source Files\Graphics\New - - - Source Files\Graphics\New - - - Source Files\Graphics\New - - - Source Files\Graphics\New - - - Source Files\Graphics\New - - - Source Files\Graphics\New - - - Source Files\Graphics\New - - - Source Files\Graphics\New - - - Source Files\Graphics\New - - - Source Files\Graphics\New - Header Files\Model3 @@ -748,18 +709,9 @@ Header Files\Util - - Source Files\Graphics\New - - - Source Files\Graphics\New - Header Files\Pkgs - - Source Files\Graphics\New - Header Files\Util @@ -790,30 +742,12 @@ Header Files\Debugger - - Source Files\Graphics\New - - - Source Files\Graphics\New - - - Source Files\Graphics\New - - - Source Files\Graphics\New - Header Files\Sound\MPEG Header Files\Pkgs - - Source Files\Network - - - Source Files\Network - Header Files\Model3\DriveBoard @@ -829,6 +763,90 @@ Header Files\Model3\DriveBoard + + Header Files\Network + + + Header Files\Network + + + Header Files\Network + + + Header Files\Network + + + Header Files\Graphics + + + Header Files\Graphics + + + Header Files\Graphics + + + Header Files\Graphics + + + Header Files\Graphics\Legacy + + + Header Files\Graphics\Legacy + + + Header Files\Graphics\Legacy + + + Header Files\Graphics\New + + + Header Files\Graphics\New + + + Header Files\Graphics\New + + + Header Files\Graphics\New + + + Header Files\Graphics\New + + + Header Files\Graphics\New + + + Header Files\Graphics\New + + + Header Files\Graphics\New + + + Header Files\Graphics\New + + + Header Files\Graphics\New + + + Header Files\Graphics\New + + + Header Files\Graphics\New + + + Header Files\Graphics\New + + + Header Files\Graphics\New + + + Header Files\Graphics\New + + + Header Files\Graphics\New + + + Header Files\Graphics\New +