flycast/core/hw/naomi/netdimm.cpp

751 lines
21 KiB
C++

/*
Copyright 2023 flyinghead
This file is part of Flycast.
Flycast 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 2 of the License, or
(at your option) any later version.
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#include "netdimm.h"
#include "naomi.h"
#include "hw/holly/holly_intc.h"
#include "hw/holly/sb.h"
#include "hw/sh4/sh4_sched.h"
#include "hw/mem/addrspace.h"
#include "network/net_platform.h"
#include "serialize.h"
#include "naomi_roms.h"
#include "naomi_regs.h"
//#define HTTP_TRACE
const char *SERVER_NAME = "vfnet.flyca.st";
NetDimm::NetDimm(u32 size) : GDCartridge(size)
{
if (serverIp == 0)
{
hostent *hp = gethostbyname(SERVER_NAME);
if (hp != nullptr && hp->h_length > 0) {
memcpy(&serverIp, hp->h_addr_list[0], sizeof(serverIp));
NOTICE_LOG(NAOMI, "%s IP is %x", SERVER_NAME, serverIp);
}
}
}
void NetDimm::Init(LoadProgress *progress, std::vector<u8> *digest)
{
GDCartridge::Init(progress, digest);
dimmBufferOffset = dimm_data_size - 16_MB;
finalTuned = strcmp(game->name, "vf4tuned") == 0;
}
bool NetDimm::Write(u32 offset, u32 size, u32 data)
{
// u8 b0 = data;
// u8 b1 = data >> 8;
// INFO_LOG(NAOMI, "Write<%d>%x: %x %c %c", size, offset, data, b0 == 0 ? ' ' : b0, b1 == 0 ? ' ' : b1);
if (dimm_data != nullptr)
{
u32 addr = offset & (dimm_data_size - 1);
memcpy(&dimm_data[addr], &data, std::min(size, dimm_data_size - addr));
}
return true;
}
int NetDimm::schedCallback()
{
fd_set readFds {};
fd_set writeFds {};
fd_set exceptFds {};
int nfds = -1;
for (Socket& socket : sockets)
{
if (socket.connecting || socket.sending)
{
FD_SET(socket.fd, &writeFds);
FD_SET(socket.fd, &exceptFds);
nfds = std::max(nfds, (int)socket.fd);
}
if (socket.receiving)
{
FD_SET(socket.fd, &readFds);
FD_SET(socket.fd, &exceptFds);
nfds = std::max(nfds, (int)socket.fd);
}
}
if (nfds > -1)
{
if (SB_ISTEXT & 8) // holly_EXP_PCI
return POLL_CYCLES;
timeval tv {};
int rc = select(nfds + 1, &readFds, &writeFds, &exceptFds, &tv);
for (Socket& socket : sockets)
{
if (socket.connecting)
{
if (rc > 0)
{
int so_error;
socklen_t len = sizeof(so_error);
getsockopt(socket.fd, SOL_SOCKET, SO_ERROR, (char *)&so_error, &len);
if (so_error != L_EINPROGRESS && so_error != L_EWOULDBLOCK)
{
INFO_LOG(NAOMI, "connect(%d) completed -> %d", socket.fd, so_error);
socket.connecting = false;
socket.lastError = so_error;
returnToNaomi(so_error != 0, &socket - &sockets[0] + 1, so_error);
break;
}
}
if (socket.connectTimeout > 0 && socket.connectTime + socket.connectTimeout <= sh4_sched_now64())
{
WARN_LOG(NAOMI, "connect(%d) timeout", socket.fd);
socket.connecting = false;
socket.lastError = ECONNREFUSED;
returnToNaomi(true, &socket - &sockets[0] + 1, ECONNREFUSED); // error code?
break;
}
}
else if (socket.receiving)
{
if (rc > 0)
{
rc = recv(socket.fd, (char *)socket.recvData, socket.recvLen, 0);
if (rc == -1)
{
int error = get_last_error();
if (error != L_EAGAIN && error != L_EWOULDBLOCK)
{
socket.lastError = get_last_error();
socket.receiving = false;
}
}
#ifdef HTTP_TRACE
else if (rc > 0)
{
fwrite(socket.recvData, 1, rc, stdout);
fflush(stdout);
}
#endif
INFO_LOG(NAOMI, "recv(%d, %d) -> %d", (int)(&socket - &sockets[0] + 1), socket.recvLen, rc);
if (rc >= 0)
socket.receiving = false;
if (!socket.receiving)
{
returnToNaomi(rc == -1, &socket - &sockets[0] + 1, rc);
break;
}
}
if (socket.recvTimeout > 0 && socket.recvTime + socket.recvTimeout <= sh4_sched_now64())
{
WARN_LOG(NAOMI, "recv(%d) timeout", socket.fd);
socket.receiving = false;
socket.lastError = ETIMEDOUT;
returnToNaomi(true, &socket - &sockets[0] + 1, ETIMEDOUT); // error code?
break;
}
}
else if (socket.sending)
{
if (rc > 0)
{
rc = send(socket.fd, (const char *)socket.sendData, socket.sendLen, 0);
if (rc == -1)
{
int error = get_last_error();
if (error != L_EAGAIN && error != L_EWOULDBLOCK)
{
socket.lastError = get_last_error();
socket.sending = false;
}
}
#ifdef HTTP_TRACE
else if (rc > 0)
{
fwrite(socket.sendData, 1, rc, stdout);
fflush(stdout);
}
#endif
INFO_LOG(NAOMI, "send(%d, %d) -> %d", (int)(&socket - &sockets[0] + 1), socket.sendLen, rc);
if (rc >= 0)
socket.sending = false;
if (!socket.sending)
{
returnToNaomi(rc == -1, &socket - &sockets[0] + 1, rc);
break;
}
}
if (socket.sendTimeout > 0 && socket.sendTime + socket.sendTimeout <= sh4_sched_now64())
{
WARN_LOG(NAOMI, "send(%d) timeout", socket.fd);
socket.sending = false;
socket.lastError = ETIMEDOUT;
returnToNaomi(true, &socket - &sockets[0] + 1, ETIMEDOUT); // error code?
break;
}
}
}
return isBusy() ? POLL_CYCLES : SH4_MAIN_CLOCK;
}
if (SB_ISTEXT & 8) // holly_EXP_PCI
return SH4_MAIN_CLOCK;
// if (dnsInProgress)
// {
// NOTICE_LOG(NAOMI, "getIpByDns completed");
// returnToNaomi(false, 0, serverIp);
// dnsInProgress = false;
// return SH4_MAIN_CLOCK;
// }
// regularly peek the test request address
peek<u32>(0xc01fc08);
asic_RaiseInterrupt(holly_EXP_PCI);
u32 testRequest = addrspace::read32(0xc01fc08);
if (testRequest & 1)
{
// bios dimm test
addrspace::write32(0xc01fc08, testRequest & ~1);
bool isMem;
char *p = (char *)addrspace::writeConst(0xc01fd00, isMem, 4);
strcpy(p, "CHECKING DIMM BD");
p = (char *)addrspace::writeConst(0xc01fd10, isMem, 4);
strcpy(p, "DIMM0 - GOOD");
p = (char *)addrspace::writeConst(0xc01fd20, isMem, 4);
strcpy(p, "DIMM1 - GOOD");
p = (char *)addrspace::writeConst(0xc01fd30, isMem, 4);
strcpy(p, "--- COMPLETED---");
addrspace::write32(0xc01fc0c, 0x0317a264);
}
else if (testRequest & 0x40)
{
// when entering vf4 test mode
addrspace::write32(0xc01fc08, testRequest & ~0x40);
addrspace::write32(0xc01fc60, htonl(0xc0a80101)); // FIXME ip address (192.168.1.1)
addrspace::write32(0xc01fc0c, 0x03170264);
INFO_LOG(NAOMI, "TEST REQUEST %x", testRequest);
}
else if (testRequest & 0x400)
{
// when entering vf4 test mode
addrspace::write32(0xc01fc08, testRequest & ~0x400);
addrspace::write32(0xc01fc70, 0x08080808); // FIXME dns2??? we might be off by one; and this would be dns1?
addrspace::write32(0xc01fc0c, 0x03170264);
INFO_LOG(NAOMI, "TEST REQUEST %x", testRequest);
}
else if (testRequest & 0x10000)
{
// bios network settings
addrspace::write32(0xc01fc08, testRequest & ~0x10000);
// TODO save to PIC?
addrspace::write32(0xc01fc0c, 0x03170264);
}
else if (testRequest & 0x20000)
{
// network test
addrspace::write32(0xc01fc08, testRequest & ~0x20000);
bool isMem;
char *p = (char *)addrspace::writeConst(0xc01fd00, isMem, 4);
strcpy(p, "CHECKING NETWORK");
p = (char *)addrspace::writeConst(0xc01fd10, isMem, 4);
strcpy(p, "PRETENDING... :P");
p = (char *)addrspace::writeConst(0xc01fd20, isMem, 4);
strcpy(p, "--- COMPLETED---");
addrspace::write32(0xc01fc0c, 0x03170264);
}
else if (testRequest != 0)
{
addrspace::write32(0xc01fc08, 0);
addrspace::write32(0xc01fc0c, 0x03170100);
INFO_LOG(NAOMI, "TEST REQUEST %x", testRequest);
}
return SH4_MAIN_CLOCK;
}
void NetDimm::systemCmd(int cmd)
{
switch (cmd)
{
case 0xf: // startup
NOTICE_LOG(NAOMI, "NetDIMM startup");
// bit 16,17: dimm0 size (none, 128, 256, 512)
// bit 18,19: dimm1 size
// bit 28: network enabled (network settings appear in bios menu)
// bit 29: set
// bit 30: gd-rom connected
// bit 31: mobile/ppp network?
// (| 30, 70, F0, 1F0, 3F0, 7F0)
// | offset >> 20 (dimm buffers offset @ size - 16MB)
// offset = (64MB << 0-5) - 16MB
// vf4 forces this value to 0f000000 (256MB) if != 1f000000 (512MB)
if (dimm_data_size == 512_MB)
addrspace::write32(0xc01fc04, (3 << 16) | 0x70000000 | (dimmBufferOffset >> 20)); // dimm board config 1 x 512 MB
else if (dimm_data_size == 256_MB)
addrspace::write32(0xc01fc04, (2 << 16) | 0x70000000 | (dimmBufferOffset >> 20)); // dimm board config 1 x 256 MB
else if (dimm_data_size == 128_MB)
addrspace::write32(0xc01fc04, (1 << 16) | 0x70000000 | (dimmBufferOffset >> 20)); // dimm board config 1 x 128 MB
else
die("Unsupported dimm mem size");
addrspace::write32(0xc01fc0c, 0x3170000 | 0x264); // fw version | 100/264/364?
addrspace::write32(0xc01fc10, 0);
// additional pokes (initPoke_maybe)
addrspace::write32(0xc01fc14, 1);
// new in 3.17
addrspace::write32(0xc01fc20, 0x78000);
addrspace::write32(0xc01fc24, 0x3e000a);
addrspace::write32(0xc01fc28, 0x18077f);
addrspace::write32(0xc01fc2c, 0x10014);
// DIMM board serial Id
{
const u32 *serial = (u32 *)(getGameSerialId() + 0x20); // get only the serial id
addrspace::write32(0xc01fc40, *serial++);
addrspace::write32(0xc01fc44, *serial++);
addrspace::write32(0xc01fc48, *serial++);
addrspace::write32(0xc01fc4c, *serial++);
}
addrspace::write32(0xc01fc18, 0x10002); // net mode (2 or 4 is mobile), bit 16 dhcp?
// network order
addrspace::write32(0xc01fc60, htonl(0xc0a80101)); // ip address (192.168.1.1)
addrspace::write32(0xc01fc64, htonl(0xffffff00)); // netmask
addrspace::write32(0xc01fc68, htonl(0xc0a801fe)); // gateway 192.168.1.254
addrspace::write32(0xc01fc6c, htonl(0xc0a801fe)); // dns1
addrspace::write32(0xc01fc70, htonl(0x08080808)); // dns2
addrspace::write32(0xc01fc74, 0); // ?
addrspace::write32(0xc01fc78, 0); // ?
addrspace::write32(0xc01fc7c, 0); // ppp ip addr
addrspace::write32(0xc01fc80, 0); // ppp param
addrspace::write32(0xc01fc84, 0); // ?
addrspace::write32(0xc01fc88, 0); // ?
addrspace::write32(0xc01fc8c, 0); // ?
addrspace::write32(0xc01fc90, 0); // ?
addrspace::write32(0xc01fc94, 0); // ?
// SET_BASE_ADDRESS(0c000000, 0)
dimm_command = 0x8600;
dimm_offsetl = 0;
dimm_parameterl = 0;
dimm_parameterh = 0x0c00;
asic_RaiseInterrupt(holly_EXP_PCI);
sh4_sched_request(schedId, SH4_MAIN_CLOCK);
break;
case 0: // nop
case 1: // control read
case 3: // set base address
case 4: // peek8
case 5: // peek16
case 6: // peek32
case 8: // poke8
case 9: // poke16
case 10: // poke32
// These are callbacks from naomi
INFO_LOG(NAOMI, "System callback command %x", cmd);
break;
default:
WARN_LOG(NAOMI, "Unknown system command %x", cmd);
break;
}
}
void NetDimm::netCmd(int cmd)
{
u32 *buffer = (u32 *)&dimm_data[dimmBufferOffset + 0x800000 + 0x1000 * (dimm_command & 0xff)];
cmd = buffer[0];
switch (cmd)
{
case 0: // returnToNaomiRawCmd
WARN_LOG(NAOMI, "netdimm: returnToNaomiRawCmd not implemented");
returnToNaomi(true, 0, -1);
break;
case 1: // accept
WARN_LOG(NAOMI, "netdimm: accept not implemented");
returnToNaomi(true, buffer[1], -1);
break;
case 2: // bind
WARN_LOG(NAOMI, "netdimm: bind not implemented");
returnToNaomi(true, buffer[1], -1);
break;
case 3: // close
{
const int sockidx = buffer[1];
const sock_t sockfd = getSocket(sockidx);
int rc;
if (sockfd == INVALID_SOCKET)
{
WARN_LOG(NAOMI, "closesocket(%d) invalid socket", sockidx);
rc = -1;
}
else
{
rc = sockets[sockidx - 1].close();
INFO_LOG(NAOMI, "closesocket(%d) %d -> %d", sockidx, sockfd, rc);
}
returnToNaomi(rc != 0, sockidx, rc);
break;
}
case 4: // connect
{
const int sockidx = buffer[1];
const sockaddr_in *addr = (const sockaddr_in *)&dimm_data[buffer[2]];
sock_t sockfd = getSocket(sockidx);
int rc;
if (sockfd == INVALID_SOCKET)
{
WARN_LOG(NAOMI, "connect(%d, %x) invalid socket", sockidx, htonl(addr->sin_addr.s_addr));
rc = -1;
}
else
{
//socklen_t len = (socklen_t)buffer[3];
sockaddr_in a {};
a.sin_family = AF_INET; // for some reason the family is in network order too. Just ignore it.
a.sin_port = addr->sin_port;
a.sin_addr.s_addr = addr->sin_addr.s_addr;
rc = connect(sockfd, (sockaddr *)&a, sizeof(a));
if (rc == -1)
{
int error = get_last_error();
if (error == L_EINPROGRESS || error == L_EWOULDBLOCK)
{
sockets[sockidx - 1].connecting = true;
sockets[sockidx - 1].connectTime = sh4_sched_now64();
sh4_sched_request(schedId, POLL_CYCLES);
INFO_LOG(NAOMI, "connect(%d, %x:%d) delayed", sockidx, htonl(a.sin_addr.s_addr), htons(a.sin_port));
return;
}
else
{
sockets[sockidx - 1].lastError = error;
}
}
else
{
if (finalTuned)
set_non_blocking(sockfd);
}
INFO_LOG(NAOMI, "connect(%d, %x:%d) -> %d", sockidx, htonl(a.sin_addr.s_addr), htons(a.sin_port), rc);
}
returnToNaomi(rc != 0, sockidx, rc);
break;
}
case 5: // getIpByDns
{
char *name = (char *)&dimm_data[buffer[1]];
INFO_LOG(NAOMI, "getIpByDns %s", name);
//dnsInProgress = true;
//sh4_sched_request(schedId, POLL_CYCLES * 10);
//int len = buffer[2];
//u32 dns1 = buffer[3];
//u32 dns2 = buffer[4];
returnToNaomi(false, 0, serverIp);
break;
}
case 6: // inet_addr
WARN_LOG(NAOMI, "netdimm: inet_addr not implemented");
returnToNaomi(true, 0, -1);
break;
case 7: // ioctl
WARN_LOG(NAOMI, "netdimm: ioctl not implemented");
returnToNaomi(true, buffer[1], -1);
break;
case 8: // listen
WARN_LOG(NAOMI, "netdimm: listen not implemented");
returnToNaomi(true, buffer[1], -1);
break;
case 9: // recv
{
const int sockidx = buffer[1];
sock_t sockfd = getSocket(sockidx);
int rc;
if (sockfd == INVALID_SOCKET)
{
WARN_LOG(NAOMI, "recv(%d) invalid socket", sockidx);
rc = -1;
}
else
{
u32 len = buffer[3];
u32 offset = buffer[2] & (dimm_data_size - 1);
u8 *data = &dimm_data[offset];
rc = recv(sockfd, (char *)data, len, 0);
if (rc == -1)
{
int error = get_last_error();
if (error == L_EAGAIN || error == L_EWOULDBLOCK)
{
sockets[sockidx - 1].receiving = true;
sockets[sockidx - 1].recvTime = sh4_sched_now64();
sockets[sockidx - 1].recvData = data;
sockets[sockidx - 1].recvLen = len;
sh4_sched_request(schedId, POLL_CYCLES);
INFO_LOG(NAOMI, "recv(%d, %d) delayed", sockidx, len);
return;
}
else
{
sockets[sockidx - 1].lastError = get_last_error();
}
}
#ifdef HTTP_TRACE
else if (rc > 0)
{
fwrite(data, 1, rc, stdout);
fflush(stdout);
}
#endif
INFO_LOG(NAOMI, "recv(%d, %d) -> %d", sockidx, len, rc);
}
returnToNaomi(rc == -1, sockidx, rc);
break;
}
case 10: // send
{
const int sockidx = buffer[1];
sock_t sockfd = getSocket(sockidx);
int rc;
if (sockfd == INVALID_SOCKET)
{
INFO_LOG(NAOMI, "send(%d) invalid socket", sockidx);
rc = -1;
}
else
{
u32 len = buffer[3];
u32 offset = buffer[2] & (dimm_data_size - 1);
u8 *data = &dimm_data[offset];
rc = send(sockfd, (const char *)data, len, 0);
if (rc == -1)
{
int error = get_last_error();
if (error == L_EAGAIN || error == L_EWOULDBLOCK)
{
sockets[sockidx - 1].sending = true;
sockets[sockidx - 1].sendTime = sh4_sched_now64();
sockets[sockidx - 1].sendData = data;
sockets[sockidx - 1].sendLen = len;
sh4_sched_request(schedId, POLL_CYCLES);
INFO_LOG(NAOMI, "send(%d, %d) delayed", sockidx, len);
return;
}
else
{
sockets[sockidx - 1].lastError = get_last_error();
}
}
INFO_LOG(NAOMI, "send(%d, %d) -> %d", sockidx, len, rc);
#ifdef HTTP_TRACE
fwrite(data, 1, len, stdout);
fflush(stdout);
#endif
}
returnToNaomi(rc == -1, sockidx, rc);
break;
}
case 11: // openSocket
{
const u32 domain = buffer[1];
const u32 type = buffer[2];
const u32 protocol = buffer[3];
const sock_t fd = socket(domain, type, protocol);
int sockidx = -1;
if (fd != INVALID_SOCKET)
{
// FIXME async mode still not right with FT
if (!finalTuned)
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, "openSocket(%d, %d, %d) %d -> %d", domain, type, protocol, fd, sockidx);
returnToNaomi(sockidx == -1, 0, sockidx);
break;
}
case 12: // netSelect
{
const u32 readFds = buffer[2];
const u32 writeFds = buffer[3];
const u32 exceptFds = buffer[4];
const u32 timeoutAddr = buffer[4];
int nfds = -1;
fd_set read {};
fd_set write {};
fd_set except {};
timeval timeout;
const auto& setFdsets = [this, buffer, &nfds](u32 fdsOffset, fd_set *fdset)
{
if (fdsOffset == 0)
return;
fd_set fds;
memcpy(&fds, &dimm_data[fdsOffset & (dimm_data_size - 1)], std::min<size_t>(sizeof(fds), 32));
const int count = buffer[1];
for (int i = 0; i < count; i++)
{
if (FD_ISSET(i, &fds))
{
sock_t sockfd = getSocket(i);
if (sockfd != INVALID_SOCKET) {
FD_SET(sockfd, fdset);
nfds = std::max(nfds, (int)sockfd);
}
}
}
};
setFdsets(readFds, &read);
setFdsets(writeFds, &write);
setFdsets(exceptFds, &except);
if (timeoutAddr != 0)
{
timeout.tv_sec = *(u32 *)&dimm_data[timeoutAddr & (dimm_data_size - 1)];
timeout.tv_usec = *(u32 *)&dimm_data[(timeoutAddr + 4) & (dimm_data_size - 1)];
}
int rc = select(nfds + 1, &read, &write, &except, timeoutAddr == 0 ? nullptr : &timeout);
INFO_LOG(NAOMI, "select(%d, %x, %x, %x, %x) -> %d", nfds, readFds, writeFds, exceptFds, timeoutAddr, rc);
returnToNaomi(rc == -1, 0, rc);
break;
}
case 13: // shutdown (not implemented on real hardware)
WARN_LOG(NAOMI, "netdimm: shutdown not implemented");
returnToNaomi(true, buffer[1], -3);
break;
case 14: // setsockopt
WARN_LOG(NAOMI, "netdimm: setsockopt not implemented");
returnToNaomi(true, buffer[1], -1);
break;
case 15: // getsockopt
WARN_LOG(NAOMI, "netdimm: getsockopt not implemented");
returnToNaomi(true, buffer[1], -1);
break;
case 16: // settimeout
{
const int sockidx = buffer[1];
sock_t sockfd = getSocket(sockidx);
int rc;
if (sockfd == INVALID_SOCKET)
{
WARN_LOG(NAOMI, "settimeout(%d) invalid socket", sockidx);
rc = -1;
}
else
{
sockets[sockidx - 1].connectTimeout = (u64)buffer[2] * SH4_MAIN_CLOCK / 1000;
sockets[sockidx - 1].sendTimeout = (u64)buffer[3] * SH4_MAIN_CLOCK / 1000;
sockets[sockidx - 1].recvTimeout = (u64)buffer[4] * SH4_MAIN_CLOCK / 1000;
INFO_LOG(NAOMI, "setTimeout(%d, %d, %d, %d)", sockidx, buffer[2], buffer[3], buffer[4]);
rc = 0;
}
returnToNaomi(rc != 0, sockidx, 0);
break;
}
case 17: // geterrno
{
int sockidx = buffer[1];
sock_t sockfd = getSocket(sockidx);
if (sockfd != INVALID_SOCKET)
{
int rc = sockets[sockidx - 1].lastError;
INFO_LOG(NAOMI, "geterrno(%d) -> %d", sockidx, rc);
returnToNaomi(false, sockidx, rc);
}
else
{
returnToNaomi(true, sockidx, -1);
}
break;
}
case 18: // routeAdd
WARN_LOG(NAOMI, "netdimm: routeAdd not implemented");
returnToNaomi(true, 0, -1);
break;
case 19: // routeDelete
WARN_LOG(NAOMI, "netdimm: routeDelete not implemented");
returnToNaomi(true, 0, -1);
break;
case 20: // getParambyDHCP
WARN_LOG(NAOMI, "netdimm: getParambyDHCP not implemented");
returnToNaomi(true, 0, -1);
break;
case 21: // modifyMyIPaddr
WARN_LOG(NAOMI, "netdimm: modifyMyIPaddr not implemented");
returnToNaomi(true, 0, -1);
break;
case 22: // recvfrom
WARN_LOG(NAOMI, "netdimm: recvfrom not implemented");
returnToNaomi(true, buffer[1], -1);
break;
case 23: // sendto
WARN_LOG(NAOMI, "netdimm: sendto not implemented");
returnToNaomi(true, buffer[1], -1);
break;
default:
WARN_LOG(NAOMI, "netdimm: Invalid Net command: %d", cmd);
returnToNaomi(true, 0, 0);
break;
}
}
void NetDimm::process()
{
INFO_LOG(NAOMI, "NetDIMM cmd %04x sock %d offset %04x paramh/l %04x %04x", (dimm_command >> 9) & 0x3f,
dimm_command & 0xff, dimm_offsetl, dimm_parameterh, dimm_parameterl);
int cmdGroup = (dimm_command >> 13) & 3;
int cmd = (dimm_command >> 9) & 0xf;
switch (cmdGroup)
{
case 0: // system commands
systemCmd(cmd);
break;
case 1: // network client
netCmd(cmd);
break;
default:
WARN_LOG(NAOMI, "Unknown DIMM command group %d cmd %x", cmdGroup, cmd);
returnToNaomi(true, 0, -1);
break;
}
}
void NetDimm::Deserialize(Deserializer &deser)
{
GDCartridge::Deserialize(deser);
for (Socket& socket : sockets)
socket.close();
if (deser.version() >= Deserializer::V36 && deser.version() < Deserializer::V53)
{
// moved to parent class in v53
deser >> dimm_command;
deser >> dimm_offsetl;
deser >> dimm_parameterl;
deser >> dimm_parameterh;
sh4_sched_deserialize(deser, schedId);
}
}