From 2a48049216ecc378e2b74296ff82887594a506be Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Wed, 10 Jan 2024 15:56:21 +0100 Subject: [PATCH] systemsp: cart network hle. hopper emulation. WIP emulation of cart network features. Hopper emulation. WIP medal emulation. Playable games: kingyo, manpuku and shateki --- core/hw/naomi/systemsp.cpp | 486 +++++++++++++++++++++++++++++++++++-- core/hw/naomi/systemsp.h | 68 ++++++ 2 files changed, 537 insertions(+), 17 deletions(-) diff --git a/core/hw/naomi/systemsp.cpp b/core/hw/naomi/systemsp.cpp index 68989dc8c4..ec11f07b03 100644 --- a/core/hw/naomi/systemsp.cpp +++ b/core/hw/naomi/systemsp.cpp @@ -34,6 +34,7 @@ #include "card_reader.h" #include "naomi_roms.h" #include "stdclass.h" +#include "hw/mem/addrspace.h" #include #include @@ -816,9 +817,6 @@ class DefaultInPortManager : public InPortManager // IN_PORT0 u8 getCN9_17_24() override { - MapleInputState mapleInputState[4]; - ggpo::getInput(mapleInputState); - u8 v = 0xff; // 0: P1 start // 1: P2 start @@ -828,6 +826,7 @@ class DefaultInPortManager : public InPortManager // 5: P2 left // 6: P1 up // 7: P2 up + getInputState(); if (!(mapleInputState[0].kcode & DC_BTN_START)) v &= ~0x01; if (!(mapleInputState[1].kcode & DC_BTN_START)) @@ -860,8 +859,7 @@ class DefaultInPortManager : public InPortManager // 5: P2 button 2 // 6: P1 button 3 // 7: P2 button 3 - MapleInputState mapleInputState[4]; - ggpo::getInput(mapleInputState); + getInputState(); if (!(mapleInputState[0].kcode & DC_DPAD_DOWN)) v &= ~0x01; if (!(mapleInputState[1].kcode & DC_DPAD_DOWN)) @@ -896,8 +894,7 @@ class DefaultInPortManager : public InPortManager // 2: P1 test // 4: P1 coin // 5: P2 coin - MapleInputState mapleInputState[4]; - ggpo::getInput(mapleInputState); + getInputState(); if (!(mapleInputState[0].kcode & DC_DPAD2_UP)) // service v &= ~0x01; if (!(mapleInputState[0].kcode & DC_DPAD2_DOWN)) // test @@ -917,8 +914,7 @@ class DefaultInPortManager : public InPortManager // FIXME these are outputs?? // 0: P1 coin meter // 1: P2 coin meter - MapleInputState mapleInputState[4]; - ggpo::getInput(mapleInputState); + getInputState(); if (!(mapleInputState[0].kcode & DC_BTN_D)) // coin v |= 1; if (!(mapleInputState[1].kcode & DC_BTN_D)) @@ -926,6 +922,13 @@ class DefaultInPortManager : public InPortManager IO_LOG("systemsp::read IN_PORT4 %x", v); return v; } + +protected: + void getInputState() { + ggpo::getInput(mapleInputState); + } + + MapleInputState mapleInputState[4]; }; class CardReaderInPortManager : public DefaultInPortManager @@ -933,8 +936,7 @@ class CardReaderInPortManager : public DefaultInPortManager public: u8 getCN9_17_24() override { - MapleInputState mapleInputState[4]; - ggpo::getInput(mapleInputState); + getInputState(); for (size_t i = 0; i < 2; i++) { if ((mapleInputState[i].kcode & DC_BTN_INSERT_CARD) == 0 @@ -973,6 +975,166 @@ class IsshoniInPortManager : public CardReaderInPortManager } }; +class HopperInPortManager : public DefaultInPortManager +{ + // IN_PORT1 + u8 getCN9_41_48() override + { + u8 v = 0xbf; + // 0: P1 service + // 2: P1 test + // 4: P1 coin + // 5: P2 coin + // 6: must be 0 (hopper: not connected / mount error) + // 7: must be 1 (hopper: coin2 chute jammed) + getInputState(); + if (!(mapleInputState[0].kcode & DC_DPAD2_UP)) // service + v &= ~0x01; + if (!(mapleInputState[0].kcode & DC_DPAD2_DOWN)) // test + v &= ~0x04; + if (!(mapleInputState[0].kcode & DC_BTN_D)) // coin + v &= ~0x10; + if (!(mapleInputState[1].kcode & DC_BTN_D)) + v &= ~0x20; + if (hopperActiveTime != 0) + { + if (sh4_sched_now64() - hopperActiveTime >= SH4_MAIN_CLOCK / 10) + hopperActiveTime = 0; + else + v |= 0x40; + } + IO_LOG("systemsp::read IN_PORT1 %x", v); + return v; + } + + void setCN9_49_56(u8 v) override { + if ((v & 0x10) != 0 && hopperActiveTime == 0) + hopperActiveTime = sh4_sched_now64(); + } + + u64 hopperActiveTime = 0; +}; + +class ManpukuInPortManager : public HopperInPortManager +{ + // IN_PORT0 + u8 getCN9_17_24() override + { + u8 v = 0xff; + // 4: Left + // 5: Middle + // 6: Right + getInputState(); + if (!(mapleInputState[0].kcode & DC_DPAD_LEFT)) + v &= ~0x10; + if (!(mapleInputState[0].kcode & DC_DPAD_DOWN)) + v &= ~0x20; + if (!(mapleInputState[0].kcode & DC_DPAD_RIGHT)) + v &= ~0x40; + IO_LOG("systemsp::read IN_PORT0 %x", v); + return v; + } +}; + +class KingyoInPortManager : public HopperInPortManager +{ + // IN_PORT0 + u8 getCN9_17_24() override + { + u8 v = 0xff; + // 0: Left + // 1: Right + // 2: Down + // 3: Up + // 4: Button 1 + getInputState(); + if (!(mapleInputState[0].kcode & DC_DPAD_LEFT)) + v &= ~0x01; + if (!(mapleInputState[0].kcode & DC_DPAD_RIGHT)) + v &= ~0x02; + if (!(mapleInputState[0].kcode & DC_DPAD_DOWN)) + v &= ~0x04; + if (!(mapleInputState[0].kcode & DC_DPAD_UP)) + v &= ~0x08; + if (!(mapleInputState[0].kcode & DC_BTN_A)) + v &= ~0x10; + IO_LOG("systemsp::read IN_PORT0 %x", v); + return v; + } +}; + +class MedalInPortManager : public DefaultInPortManager +{ + // IN_PORT0 + u8 getCN9_17_24() override + { + u8 v = 0x50; // 0x51; + // 0: slope sensor up (active high) + // 1: slope sensor l (active high) + // 2: c.hopper rot. (active high) + // 3: c.hopper sensor (active high) + // 4: hopper sensor + // 5: puser sensor (active high) + // 6: tilt sensor bo + // 7: chacker 9 (active high) + return v; + } + + // IN_PORT1 + u8 getCN9_41_48() override + { + u8 v = 0x2f; + // 0: service/reset sw + // 1: left sw + // 2: test sw + // 3: center sw + // 4: door sensor up (active high) + // 5: right sw + // 6: door sensor left (active high) + // 7: tilt sensor br (active high) + getInputState(); + if (!(mapleInputState[0].kcode & DC_DPAD2_UP)) // service + v &= ~0x01; + if (!(mapleInputState[0].kcode & DC_DPAD2_DOWN)) // test + v &= ~0x04; + return v; + } + + // IN_PORT3 / IO-0 + u8 getCN9_25_32() override + { + u8 v = 0; + // 0: chacker 1 (active high) + // 1: chacker 2 (active high) + // 2: chacker 3 (active high) + // 3: chacker 4 (active high) + // 4: chacker 5 (active high) + // 5: chacker 6 (active high) + // 6: chacker 7 (active high) + // 7: chacker 8 (active high) + return v; + } + + // OUT-0 (CN9 49-56) + // 4: sw lamp L + // 5: sw lamp R + // 6: patrol lamp + // 7: jackpot lamp + + // OUT-1 (CN10 17-24) + // 0: sw.lamp c + // 1: jp solenoid + // 6: side lamp L + // 7: side lamp R + + // IO-1 (CN9 33-40) + // 3: jpmec motor + // 4: c.hop motor + // 5: hoper motor + // 6: puser motor + // 7: slope motor +}; + template T readMemArea0(u32 addr) { @@ -1176,8 +1338,8 @@ T SystemSpCart::readMemArea0(u32 addr) // 1: unknown, active low // 2: monitor (1: 31 kHz, 0: 15 kHz) // 3: unknown, must be on (active low) - // 4: JP5 - // 5: JP6 + // 4: JP5 (system service) + // 5: JP6 (system test) // 6: JP7 // 7: JP8 IO_LOG("systemsp::read(%x) IN_PORT2 %x", addr, 7); @@ -1405,7 +1567,11 @@ void SystemSpCart::writeMemArea0(u32 addr, T v) // 5: cd2 card pickout (not used) // 6: cd1 jam reset // 7: cd2 jam reset (not used) + // hopper: + // 4: payout? + // 7: ? IO_LOG("systemsp::write(%x) OUT_PORT4 %x", addr, v & 0xfc); + inPortManager->setCN9_49_56(v); break; case 0x14: // OUT CN10 17-24 // dinosaur king, love & berry: @@ -1802,6 +1968,19 @@ void SystemSpCart::Init(LoadProgress *progress, std::vector *digest) new Touchscreen(&uart1); inPortManager = std::make_unique(); } + else if (!strcmp(game->name, "manpuku")) { + inPortManager = std::make_unique(); + } + else if (!strncmp(game->name, "kingyo", 6) || !strcmp(game->name, "shateki")) { + inPortManager = std::make_unique(); + } + else if (!strcmp(game->name, "magicpop") + || !strcmp(game->name, "ochaken") + || !strcmp(game->name, "puyomedal") + || !strcmp(game->name, "unomedal") + || !strcmp(game->name, "westdrmg")) { + inPortManager = std::make_unique(); + } if (!strncmp(game->name, "dinoki", 6) || !strncmp(game->name, "loveber", 7)) inPortManager = std::make_unique(); if (!inPortManager) @@ -1812,12 +1991,31 @@ void SystemSpCart::Init(LoadProgress *progress, std::vector *digest) u32 SystemSpCart::ReadMem(u32 address, u32 size) { - return M4Cartridge::ReadMem(address, size); + if (address == NAOMI_DIMM_STATUS) + { + u32 rc = DIMM_STATUS & ~(((SB_ISTEXT >> 3) & 1) << 8); + DEBUG_LOG(NAOMI, "DIMM STATUS read -> %x", rc); + return rc; + } + else { + return M4Cartridge::ReadMem(address, size); + } } void SystemSpCart::WriteMem(u32 address, u32 data, u32 size) { - M4Cartridge::WriteMem(address, data, size); + if (address == NAOMI_DIMM_STATUS) + { + DEBUG_LOG(NAOMI, "DIMM STATUS Write<%d>: %x", size, data); + if (data & 0x100) + asic_CancelInterrupt(holly_EXP_PCI); + if ((data & 2) == 0) + // irq to dimm + process(); + } + else { + M4Cartridge::WriteMem(address, data, size); + } } bool SystemSpCart::Read(u32 offset, u32 size, void *dst) @@ -1827,16 +2025,60 @@ bool SystemSpCart::Read(u32 offset, u32 size, void *dst) if ((offset & 0x3f000000) == 0x3f000000) { // network card present - DEBUG_LOG(NAOMI, "SystemSpCart::Read<%d>%x: net card present -> 0", size, offset); - int rc = 0; + int rc = 1; + DEBUG_LOG(NAOMI, "SystemSpCart::Read<%d>%x: net card present -> %d", size, offset, rc); memcpy(dst, &rc, size); return true; } + if ((offset & 0x3f000000) == 0x3d000000) + { + // network card mem + switch (size) + { + case 4: + *(u32 *)dst = readNetMem(offset); + break; + case 2: + *(u16 *)dst = readNetMem(offset); + break; + case 1: + default: + *(u8 *)dst = readNetMem(offset); + break; + } + u16 d = readNetMem(offset); + DEBUG_LOG(NAOMI, "SystemSpCart::Read<%d>%x: net mem -> %x", size, offset, d); + return true; + } return M4Cartridge::Read(offset, size, dst); } bool SystemSpCart::Write(u32 offset, u32 size, u32 data) { + if ((offset & 0x3f000000) == 0x3d000000) + { + // network card mem + switch (size) + { + case 4: + writeNetMem(offset, data); + break; + case 2: + writeNetMem(offset, (u16)data); + break; + case 1: + default: + writeNetMem(offset, (u8)data); + break; + } + DEBUG_LOG(NAOMI, "SystemSpCart::Write<%d>%x: net mem = %x", size, offset, data); + int idx = offset & (sizeof(netmem) - 2); + // FIXME + if (idx == 2) + memcpy(&netmem[idx + 0x200], &data, size); + return true; + } + // ??? if ((offset & 0x3f000000) == 0x39000000) switch (flash.cmdState) { case CmdState::INIT: @@ -2013,4 +2255,214 @@ void SystemSpCart::saveFiles() eeprom.Save(getEepromPath()); } +// TODO implement actual networking features. Only socket() and close() are currently functional. +void SystemSpCart::process() +{ + DmaOffset |= 0x40000000; // needed by BIOS. why? + u8 reqId = readNetMem(0x200); + int cmd = readNetMem(0x202); + DEBUG_LOG(NAOMI, "SystemSpCart::process cmd %03x", cmd); + const u8 netmem4 = readNetMem(4); // FIXME memset should be done later on + memset(&netmem[0], 0, 32); + writeNetMem(0, reqId); + writeNetMem(1, (u8)0); + switch (cmd) + { + case 1: // init? nop? + INFO_LOG(NAOMI, "process: init/nop"); + writeNetMem(2, NET_OK); + break; + case 0x100: // get dimm status + INFO_LOG(NAOMI, "process: get dimm status"); + writeNetMem(2, NET_OK); + // 0 initializing + // 1 checking network + // 2 ? + // 3 testing prg + // 4 loading prg + // 5 ready + writeNetMem(4, 5); + break; + case 0x101: // get firmware version + INFO_LOG(NAOMI, "process: get firmware version"); + writeNetMem(2, NET_OK); + writeNetMem(4, (u8)0x25); // minor + writeNetMem(5, (u8)1); // major + break; + case 0x104: // get network type and address + INFO_LOG(NAOMI, "process: get network type"); + writeNetMem(2, NET_OK); + writeNetMem(4, 5); // 4: ether (dhcp), 5: ether (static ip) + writeNetMem(0x24, inetaddr); + writeNetMem(0x28, netmask); + break; + + case 0x204: // set network type and address + // 4: type (0 none, 3,4 ether) + inetaddr = readNetMem(0x24); + netmask = readNetMem(0x28); + INFO_LOG(NAOMI, "process: set net type %d ip %08x mask %08x", netmem4, inetaddr, netmask); + writeNetMem(2, NET_OK); + break; + + case 0x301: // network test + { + // 204: 4 + u32 addr = readNetMem(0x208); + INFO_LOG(NAOMI, "process: network_test(%x, %x)", readNetMem(0x204), addr); + bool isMem; + char *p = (char *)addrspace::writeConst(addr, isMem, 4); + strcpy(p, "CHECKING NETWORK\n"); + p = (char *)addrspace::writeConst(addr + 0x11, isMem, 4); + strcpy(p, "PRETENDING... :P\n"); + p = (char *)addrspace::writeConst(addr + 0x22, isMem, 4); + strcpy(p, "--- COMPLETED---\n"); + writeNetMem(2, NET_OK); + writeNetMem(4, 1); // TODO how to signal the test progress/completion? + } + break; + + case 0x401: // accept + INFO_LOG(NAOMI, "process: accept(%d)", readNetMem(0x208)); + writeNetMem(2, NET_ERROR); + break; + case 0x402: // bind + { + u32 addr = readNetMem(0x20c); + u32 len = readNetMem(0x210); + sockaddr_in sa{}; + memcpy(&sa, &netmem[addr & (sizeof(netmem) - 1)], len); + INFO_LOG(NAOMI, "process: bind(%d, %08x:%d)", readNetMem(0x208), htonl(sa.sin_addr.s_addr), htons(sa.sin_port)); + writeNetMem(2, NET_OK); + } + break; + case 0x403: // close socket + { + const int sockidx = readNetMem(0x208); + const sock_t sockfd = sockidx < 1 || sockidx > 0x3f || sockidx > (int)sockets.size() ? INVALID_SOCKET : sockets[sockidx - 1].fd; + u16 rc; + if (sockfd == INVALID_SOCKET) + { + INFO_LOG(NAOMI, "process: closesocket(%d) invalid socket", sockidx); + rc = NET_ERROR; + } + else + { + rc = sockets[sockidx - 1].close() != 0 ? NET_ERROR : NET_OK; + INFO_LOG(NAOMI, "process: closesocket(%d) %d -> %x", sockidx, sockfd, rc); + } + writeNetMem(2, rc); + } + break; + case 0x404: // connect + { + u32 addr = readNetMem(0x20c); + u32 len = readNetMem(0x210); + sockaddr_in sa{}; + memcpy(&sa, &netmem[addr & (sizeof(netmem) - 1)], len); + INFO_LOG(NAOMI, "process: connect(%d, %08x:%d)", readNetMem(0x208), htonl(sa.sin_addr.s_addr), htons(sa.sin_port)); + writeNetMem(2, NET_OK); + } + break; + case 0x406: // inet_addr + { + u32 addr = readNetMem(0x208); + //u32 len = readNetMem(0x20c); + const char *s = (const char *)&netmem[addr & (sizeof(netmem) - 1)]; + INFO_LOG(NAOMI, "process: inet_addr(%s)", s); + u32 ipaddr = inet_addr(s); + writeNetMem(2, NET_OK); + writeNetMem(4, ipaddr); + } + break; + case 0x408: // listen + INFO_LOG(NAOMI, "process: listen(%d)", readNetMem(0x208)); // backlog??? or 208 is backlog and 201 is sockfd? + writeNetMem(2, NET_OK); + break; + case 0x409: // recv + { + u32 addr = readNetMem(0x20c); + u32 len = readNetMem(0x210); + INFO_LOG(NAOMI, "process: recv(%d, %x, %x)", readNetMem(0x208), addr, len); + writeNetMem(2, NET_OK); + //writeNetMem(4, len); // ?? + } + break; + case 0x40a: // send + { + u32 addr = readNetMem(0x20c); + u32 len = readNetMem(0x210); + INFO_LOG(NAOMI, "process: send(%d, %x, %x)", readNetMem(0x208), addr, len); + writeNetMem(2, NET_OK); + writeNetMem(4, len); // ?? + } + break; + case 0x40b: // openSocket + { + int domain = readNetMem(0x208); + int type = readNetMem(0x20c); + int protocol = readNetMem(0x210); + const sock_t fd = socket(domain, type, protocol); + int sockidx = -1; + if (fd != INVALID_SOCKET) + { + // TODO? + //set_non_blocking(fd); + size_t i = 0; + for (; i < sockets.size(); i++) + if (sockets[i].fd == INVALID_SOCKET) + break; + if (i == sockets.size()) + sockets.emplace_back(fd); + else + sockets[i].fd = fd; + sockidx = i + 1; + } + INFO_LOG(NAOMI, "process: openSocket(%d, %d, %d) %d -> %d", domain, type, protocol, fd, sockidx); + writeNetMem(2, sockidx != -1 ? NET_OK : NET_ERROR); + if (sockidx != -1) + writeNetMem(4, sockidx); + } + break; + case 0x40e: // setsockopt + { + // TODO socket option level/name are different (mips linux?) + u32 addr = readNetMem(0x214); + //u32 len = readNetMem(0x218); + INFO_LOG(NAOMI, "process: setsockopt(%d, level: %x, name: %x, value:%x)", readNetMem(0x208), readNetMem(0x20c), readNetMem(0x210), + readNetMem(addr)); + writeNetMem(2, NET_OK); + } + break; + case 0x410: // settimeout + INFO_LOG(NAOMI, "process: settimeout(%d, %d, %d, %d)", readNetMem(0x208), readNetMem(0x20c), readNetMem(0x210), readNetMem(0x214)); + writeNetMem(2, NET_OK); + break; + case 0x411: // geterrno + INFO_LOG(NAOMI, "process: getterrno(%d)", readNetMem(0x208)); + writeNetMem(2, NET_OK); + writeNetMem(4, 111); // FIXME errno values TBD + break; + case 0x414: // getParambyDHCP? + INFO_LOG(NAOMI, "process: getParambyDHCP"); + writeNetMem(2, NET_OK); + writeNetMem(4, 1); // ??? + break; + case 0x415: // modifyMyIPaddr? + { + u32 addr = readNetMem(0x208); + //u32 len = readNetMem(0x20c); + INFO_LOG(NAOMI, "process: modifyMyIPaddr(%s, %08x)", &netmem[addr & (sizeof(netmem) - 1)], readNetMem(0x210)); + writeNetMem(2, NET_OK); + } + break; + + default: + WARN_LOG(NAOMI, "Unknown netdimm command: %x", cmd); + writeNetMem(2, NET_ERROR); + break; + } + updateInterrupt(INT_DIMM); +} + } diff --git a/core/hw/naomi/systemsp.h b/core/hw/naomi/systemsp.h index e451b6de1d..e6b5268418 100644 --- a/core/hw/naomi/systemsp.h +++ b/core/hw/naomi/systemsp.h @@ -23,6 +23,7 @@ #include "hw/naomi/m4cartridge.h" #include "hw/flashrom/at93cxx.h" #include "serialize.h" +#include "network/net_platform.h" #include #include @@ -138,6 +139,7 @@ class InPortManager virtual u8 getCN9_33_40() = 0; virtual u8 getCN9_41_48() = 0; virtual u8 getCN9_49_56() = 0; + virtual void setCN9_49_56(u8 v) {} virtual ~InPortManager() = default; }; @@ -191,6 +193,17 @@ class SystemSpCart : public M4Cartridge ((SystemSpCart *)p)->saveFiles(); } + void process(); + + template + T readNetMem(u32 addr) { + return ReadMemArr(netmem, addr & (sizeof(netmem) - sizeof(T))); + } + template + void writeNetMem(u32 addr, T data) { + WriteMemArr(netmem, addr & (sizeof(netmem) - sizeof(T)), data); + } + int schedId; const char *mediaName = nullptr; const char *parentName = nullptr; @@ -242,6 +255,61 @@ class SystemSpCart : public M4Cartridge u16 wordCount = 0; } flash; + static constexpr u16 DIMM_STATUS = 0x8102; + u8 netmem[8_MB] {}; + u32 inetaddr = 0x641ea8c0; // 192.168.30.100 + u32 netmask = 0x00ffffff; // 255.255.255.0 + struct Socket + { + Socket() = default; + Socket(sock_t fd) : fd(fd) {} + + int close() + { + int rc = 0; + if (fd != INVALID_SOCKET) + rc = ::closesocket(fd); + fd = INVALID_SOCKET; + connecting = false; + receiving = false; + sending = false; + connectTimeout = 0; + connectTime = 0; + sendTimeout = 0; + sendTime = 0; + recvTimeout = 0; + recvTime = 0; + return rc; + } + + bool isClosed() const { + return fd == INVALID_SOCKET; + } + + bool isBusy() const { + return connecting || receiving || sending; + } + + sock_t fd = INVALID_SOCKET; + bool connecting = false; + bool receiving = false; + u8 *recvData = nullptr; + u32 recvLen = 0; + bool sending = false; + const u8 *sendData = nullptr; + u32 sendLen = 0; + u64 connectTimeout = 0; + u64 connectTime = 0; + u64 sendTimeout = 0; + u64 sendTime = 0; + u64 recvTimeout = 0; + u64 recvTime = 0; + int lastError = 0; + }; + std::vector sockets; + static constexpr u16 NET_OK = 0x8001; + static constexpr u16 NET_ERROR = 0x8000; + public: static constexpr u32 INT_UART1 = 0x01; static constexpr u32 INT_UART2 = 0x02;