AppleWin/source/linux/network/uthernet2.cpp
Andrea Odetti abc4a4e1d3 Safer check that Uthernet is active.
Unfortunately we cannot force a hot-activation as this will cause SegFaults in the TFE code.
It does not check if the pcap handle is good, but relies on the initialisation to have succeeded.

Unfortunately, visual feedback on the settings is lost.

Signed-off-by: Andrea Odetti <mariofutire@gmail.com>
2021-06-05 16:57:12 +01:00

1052 lines
25 KiB
C++

#include <StdAfx.h>
#include "linux/network/uthernet2.h"
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <poll.h>
#define MAX_RXLENGTH 1518
// #define U2_LOG_VERBOSE
// #define U2_LOG_TRAFFIC
// #define U2_LOG_STATE
// #define U2_LOG_UNKNOWN
#include "linux/network/uthernet2.h"
#include "linux/network/registers.h"
#include "linux/network/slirp2.h"
#ifndef U2_USE_SLIRP
#include "linux/network/tfe2.h"
#include "Tfe/tfe.h"
#endif
#include "Memory.h"
#include "Log.h"
namespace
{
struct Socket
{
uint16_t transmitBase;
uint16_t transmitSize;
uint16_t receiveBase;
uint16_t receiveSize;
uint16_t registers;
uint16_t sn_rx_wr;
uint16_t sn_rx_rsr;
uint8_t sn_sr = SN_SR_CLOSED;
int myFD = -1;
int myErrno = 0;
void clearFD();
void setFD(const int fd, const int status);
void process();
~Socket();
};
void Socket::clearFD()
{
if (myFD != -1)
{
close(myFD);
}
myFD = -1;
sn_sr = SN_SR_CLOSED;
}
void Socket::setFD(const int fd, const int status)
{
clearFD();
myFD = fd;
myErrno = 0;
sn_sr = status;
}
Socket::~Socket()
{
clearFD();
}
void Socket::process()
{
if (myFD != -1 && sn_sr == SN_SR_SOCK_INIT && myErrno == EINPROGRESS)
{
pollfd pfd = {.fd = myFD, .events = POLLOUT};
if (poll(&pfd, 1, 0) > 0)
{
int err = 0;
socklen_t elen = sizeof err;
getsockopt(myFD, SOL_SOCKET, SO_ERROR, &err, &elen);
if (err == 0)
{
myErrno = 0;
sn_sr = SN_SR_ESTABLISHED;
#ifdef U2_LOG_STATE
LogFileOutput("U2: TCP[]: CONNECTED\n");
#endif
}
}
}
}
std::vector<uint8_t> memory;
std::vector<Socket> sockets;
uint8_t modeRegister = 0;
uint16_t dataAddress = 0;
#ifdef U2_USE_SLIRP
std::shared_ptr<SlirpNet> slirp;
#endif
void initialise();
uint16_t readNetworkWord(const uint8_t * address)
{
const uint16_t network = *reinterpret_cast<const uint16_t *>(address);
const uint16_t host = ntohs(network);
return host;
}
bool isThereRoomFor(const size_t i, const size_t len, const size_t header)
{
const Socket & socket = sockets[i];
const uint16_t rsr = socket.sn_rx_rsr; // already present
const uint16_t size = socket.receiveSize; // total size
return rsr + len + header < size; // "not =": we do not want to fill the buffer.
}
uint16_t getFreeRoom(const size_t i)
{
const Socket & socket = sockets[i];
const uint16_t rsr = socket.sn_rx_rsr; // already present
const uint16_t size = socket.receiveSize; // total size
return size - rsr;
}
uint8_t getIByte(const uint16_t value, const size_t shift)
{
return (value >> shift) & 0xFF;
}
void write8(const size_t i, const uint8_t value)
{
Socket & socket = sockets[i];
const uint16_t base = socket.receiveBase;
const uint16_t address = base + socket.sn_rx_wr;
memory[address] = value;
socket.sn_rx_wr = (socket.sn_rx_wr + 1) % socket.receiveSize;
++socket.sn_rx_rsr;
}
// reverses the byte order
void write16(const size_t i, const uint16_t value)
{
write8(i, getIByte(value, 8)); // high
write8(i, getIByte(value, 0)); // low
}
void writeData(const size_t i, const uint8_t * data, const size_t len)
{
for (size_t c = 0; c < len; ++c)
{
write8(i, data[c]);
}
}
// no byte reversal
template<typename T>
void writeAny(const size_t i, const T & t)
{
const uint8_t * data = reinterpret_cast<const uint8_t *>(&t);
const uint16_t len = sizeof(T);
writeData(i, data, len);
}
void writeDataMacRaw(const size_t i, const uint8_t * data, const size_t len)
{
// size includes sizeof(size)
const uint16_t size = len + sizeof(uint16_t);
write16(i, size);
writeData(i, data, len);
}
void writeDataForProtocol(const size_t i, const uint8_t * data, const size_t len, const sockaddr_in & source)
{
const Socket & socket = sockets[i];
if (socket.sn_sr == SN_SR_SOCK_UDP)
{
// these are already in network order
writeAny(i, source.sin_addr);
writeAny(i, source.sin_port);
// size does not include sizeof(size)
write16(i, len);
} // no header for TCP
writeData(i, data, len);
}
void setSocketModeRegister(const size_t i, const uint16_t address, const uint8_t value)
{
memory[address] = value;
const uint8_t protocol = value & SN_MR_PROTO_MASK;
switch (protocol)
{
case SN_MR_CLOSED:
#ifdef U2_LOG_STATE
LogFileOutput("U2: Mode[%d]: CLOSED\n", i);
#endif
break;
case SN_MR_TCP:
#ifdef U2_LOG_STATE
LogFileOutput("U2: Mode[%d]: TCP\n", i);
#endif
break;
case SN_MR_UDP:
#ifdef U2_LOG_STATE
LogFileOutput("U2: Mode[%d]: UDP\n", i);
#endif
break;
case SN_MR_IPRAW:
#ifdef U2_LOG_STATE
LogFileOutput("U2: Mode[%d]: IPRAW\n", i);
#endif
break;
case SN_MR_MACRAW:
#ifdef U2_LOG_STATE
LogFileOutput("U2: Mode[%d]: MACRAW\n", i);
#endif
break;
#ifdef U2_LOG_UNKNOWN
default:
LogFileOutput("U2: Unknown protocol: %02x\n", protocol);
#endif
}
}
void setTXSizes(const uint16_t address, uint8_t value)
{
memory[address] = value;
uint16_t base = TX_BASE;
const uint16_t end = RX_BASE;
for (Socket & socket : sockets)
{
socket.transmitBase = base;
const uint8_t bits = value & 0x03;
value >>= 2;
const uint16_t size = 1 << (10 + bits);
base += size;
if (base > end)
{
base = end;
}
socket.transmitSize = base - socket.transmitBase;
}
}
void setRXSizes(const uint16_t address, uint8_t value)
{
memory[address] = value;
uint16_t base = RX_BASE;
const uint16_t end = MEM_SIZE;
for (Socket & socket : sockets)
{
socket.receiveBase = base;
const uint8_t bits = value & 0x03;
value >>= 2;
const uint16_t size = 1 << (10 + bits);
base += size;
if (base > end)
{
base = end;
}
socket.receiveSize = base - socket.receiveBase;
}
}
uint16_t getTXDataSize(const size_t i)
{
const Socket & socket = sockets[i];
const uint16_t size = socket.transmitSize;
const uint16_t mask = size - 1;
const int sn_tx_rd = readNetworkWord(memory.data() + socket.registers + SN_TX_RD0) & mask;
const int sn_tx_wr = readNetworkWord(memory.data() + socket.registers + SN_TX_WR0) & mask;
int dataPresent = sn_tx_wr - sn_tx_rd;
if (dataPresent < 0)
{
dataPresent += size;
}
return dataPresent;
}
uint8_t getTXFreeSizeRegister(const size_t i, const size_t shift)
{
const int size = sockets[i].transmitSize;
const uint16_t present = getTXDataSize(i);
const uint16_t free = size - present;
const uint8_t reg = getIByte(free, shift);
return reg;
}
uint8_t getRXDataSizeRegister(const size_t i, const size_t shift)
{
const uint16_t rsr = sockets[i].sn_rx_rsr;
const uint8_t reg = getIByte(rsr, shift);
return reg;
}
void updateRSR(const size_t i)
{
Socket & socket = sockets[i];
const int size = socket.receiveSize;
const uint16_t mask = size - 1;
const int sn_rx_rd = readNetworkWord(memory.data() + socket.registers + SN_RX_RD0) & mask;
const int sn_rx_wr = socket.sn_rx_wr & mask;
int dataPresent = sn_rx_wr - sn_rx_rd;
if (dataPresent < 0)
{
dataPresent += size;
}
// is this logic correct?
// here we are re-synchronising the size with the pointers
// elsewhere I have seen people updating this value
// by the amount of how much 0x28 has moved forward
// but then we need to keep track of where it was
// the final result should be the same
#ifdef U2_LOG_TRAFFIC
if (socket.sn_rx_rsr != dataPresent)
{
LogFileOutput("U2: RECV[%d]: %d -> %d bytes\n", i, socket.sn_rx_rsr, dataPresent);
}
#endif
socket.sn_rx_rsr = dataPresent;
}
void receiveOnePacketMacRaw(const size_t i)
{
const Socket & socket = sockets[i];
const uint16_t rsr = socket.sn_rx_rsr;
#ifdef U2_USE_SLIRP
{
std::queue<std::vector<uint8_t>> & queue = slirp->getQueue();
while (!queue.empty())
{
const std::vector<uint8_t> & packet = queue.front();
if (isThereRoomFor(i, packet.size(), sizeof(uint16_t)))
{
writeDataMacRaw(i, packet.data(), packet.size());
queue.pop();
#ifdef U2_LOG_TRAFFIC
LogFileOutput("U2: READ MACRAW[%d]: +%d -> %d bytes (%04x)\n", i, packet.size(), socket.sn_rx_rsr, socket.sn_rx_wr);
#endif
}
else
{
break;
}
}
}
#else
{
BYTE buffer[MAX_RXLENGTH];
int len;
if (tfeReceiveOnePacket(memory.data() + SHAR0, sizeof(buffer), buffer, len))
{
if (isThereRoomFor(i, len, sizeof(uint16_t)))
{
writeDataMacRaw(i, buffer, len);
#ifdef U2_LOG_TRAFFIC
LogFileOutput("U2: READ MACRAW[%d]: +%d -> %d bytes\n", i, len, socket.sn_rx_rsr);
#endif
}
else
{
// drop it
#ifdef U2_LOG_TRAFFIC
LogFileOutput("U2: SKIP MACRAW[%d]: %d bytes\n", i, len);
#endif
}
}
}
#endif
}
// UDP & TCP
void receiveOnePacketFromSocket(const size_t i)
{
Socket & socket = sockets[i];
if (socket.myFD != -1)
{
const uint16_t freeRoom = getFreeRoom(i);
if (freeRoom > 32) // avoid meaningless reads
{
std::vector<uint8_t> buffer(freeRoom - 1); // do not fill the buffer completely
sockaddr_in source = {0};
socklen_t len = sizeof(sockaddr_in);
const ssize_t data = recvfrom(socket.myFD, buffer.data(), buffer.size(), 0, (struct sockaddr *) &source, &len);
#ifdef U2_LOG_TRAFFIC
const char * proto = socket.sn_sr == SN_SR_SOCK_UDP ? "UDP" : "TCP";
#endif
if (data > 0)
{
writeDataForProtocol(i, buffer.data(), data, source);
#ifdef U2_LOG_TRAFFIC
LogFileOutput("U2: READ %s[%d]: +%d -> %d bytes\n", proto, i, data, socket.sn_rx_rsr);
#endif
}
else if (data == 0)
{
// gracefull termination
socket.clearFD();
}
else // data < 0;
{
const int error = errno;
if (error != EAGAIN && error != EWOULDBLOCK)
{
#ifdef U2_LOG_TRAFFIC
LogFileOutput("U2: ERROR %s[%d]: %d\n", proto, i, error);
#endif
socket.clearFD();
}
}
}
}
}
void receiveOnePacket(const size_t i)
{
const Socket & socket = sockets[i];
switch (socket.sn_sr)
{
case SN_SR_SOCK_MACRAW:
receiveOnePacketMacRaw(i);
break;
case SN_SR_ESTABLISHED:
case SN_SR_SOCK_UDP:
receiveOnePacketFromSocket(i);
break;
case SN_SR_CLOSED:
break; // nothing to do
#ifdef U2_LOG_UNKNOWN
default:
LogFileOutput("U2: READ[%d]: unknown mode: %02x\n", i, socket.sn_sr);
#endif
};
}
void sendDataMacRaw(const size_t i, const std::vector<uint8_t> & data)
{
#ifdef U2_LOG_TRAFFIC
LogFileOutput("U2: SEND MACRAW[%d]: %d bytes\n", i, data.size());
#endif
#ifdef U2_USE_SLIRP
slirp->sendFromGuest(data.data(), data.size());
#else
tfeTransmitOnePacket(data.data(), data.size());
#endif
}
void sendDataToSocket(const size_t i, std::vector<uint8_t> & data)
{
Socket & socket = sockets[i];
if (socket.myFD != -1)
{
sockaddr_in destination = {};
destination.sin_family = AF_INET;
// already in network order
// this seems to be ignored for TCP, and so we reuse the same code
const uint8_t * dest = memory.data() + socket.registers + SN_DIPR0;
destination.sin_addr.s_addr = *reinterpret_cast<const uint32_t *>(dest);
destination.sin_port = *reinterpret_cast<const uint16_t *>(memory.data() + socket.registers + SN_DPORT0);
const ssize_t res = sendto(socket.myFD, data.data(), data.size(), 0, (const struct sockaddr *) &destination, sizeof(destination));
#ifdef U2_LOG_TRAFFIC
const char * proto = socket.sn_sr == SN_SR_SOCK_UDP ? "UDP" : "TCP";
LogFileOutput("U2: SEND %s[%d]: %d of %d bytes\n", proto, i, res, data.size());
#endif
if (res < 0)
{
const int error = errno;
if (error != EAGAIN && error != EWOULDBLOCK)
{
#ifdef U2_LOG_TRAFFIC
LogFileOutput("U2: ERROR %s[%d]: %d\n", proto, i, error);
#endif
socket.clearFD();
}
}
}
}
void sendData(const size_t i)
{
const Socket & socket = sockets[i];
const uint16_t size = socket.transmitSize;
const uint16_t mask = size - 1;
const int sn_tx_rr = readNetworkWord(memory.data() + socket.registers + SN_TX_RD0) & mask;
const int sn_tx_wr = readNetworkWord(memory.data() + socket.registers + SN_TX_WR0) & mask;
const uint16_t base = socket.transmitBase;
const uint16_t rr_address = base + sn_tx_rr;
const uint16_t wr_address = base + sn_tx_wr;
std::vector<uint8_t> data;
if (rr_address < wr_address)
{
data.assign(memory.begin() + rr_address, memory.begin() + wr_address);
}
else
{
const uint16_t end = base + size;
data.assign(memory.begin() + rr_address, memory.begin() + end);
data.insert(data.end(), memory.begin() + base, memory.begin() + wr_address);
}
// move read pointer to writer
memory[socket.registers + SN_TX_RD0] = getIByte(sn_tx_wr, 8);
memory[socket.registers + SN_TX_RD1] = getIByte(sn_tx_wr, 0);
switch (socket.sn_sr)
{
case SN_SR_SOCK_MACRAW:
sendDataMacRaw(i, data);
break;
case SN_SR_ESTABLISHED:
case SN_SR_SOCK_UDP:
sendDataToSocket(i, data);
break;
#ifdef U2_LOG_UNKNOWN
default:
LogFileOutput("U2: SEND[%d]: unknown mode: %02x\n", i, socket.sn_sr);
#endif
}
}
void resetRXTXBuffers(const size_t i)
{
Socket & socket = sockets[i];
socket.sn_rx_wr = 0x00;
socket.sn_rx_rsr = 0x00;
memory[socket.registers + SN_TX_RD0] = 0x00;
memory[socket.registers + SN_TX_RD1] = 0x00;
memory[socket.registers + SN_TX_WR0] = 0x00;
memory[socket.registers + SN_TX_WR1] = 0x00;
memory[socket.registers + SN_RX_RD0] = 0x00;
memory[socket.registers + SN_RX_RD1] = 0x00;
}
void openSystemSocket(const size_t i, const int type, const int protocol, const int state)
{
Socket & s = sockets[i];
const int fd = socket(AF_INET, type | SOCK_NONBLOCK, protocol);
if (fd < -1)
{
#ifdef U2_LOG_STATE
const char * proto = state == SN_SR_SOCK_UDP ? "UDP" : "TCP";
LogFileOutput("U2: %s[%d]: %s\n", proto, i, strerror(errno));
#endif
s.clearFD();
}
else
{
s.setFD(fd, state);
}
}
void openSocket(const size_t i)
{
Socket & socket = sockets[i];
const uint8_t mr = memory[socket.registers + SN_MR];
const uint8_t protocol = mr & SN_MR_PROTO_MASK;
uint8_t & sr = socket.sn_sr;
switch (protocol)
{
case SN_MR_IPRAW:
sr = SN_SR_SOCK_IPRAW;
break;
case SN_MR_MACRAW:
sr = SN_SR_SOCK_MACRAW;
break;
case SN_MR_TCP:
openSystemSocket(i, SOCK_STREAM, IPPROTO_TCP, SN_SR_SOCK_INIT);
break;
case SN_MR_UDP:
openSystemSocket(i, SOCK_DGRAM, IPPROTO_UDP, SN_SR_SOCK_UDP);
break;
#ifdef U2_LOG_UNKNOWN
default:
LogFileOutput("U2: OPEN[%d]: unknown mode: %02x\n", i, mr);
#endif
}
resetRXTXBuffers(i); // needed?
#ifdef U2_LOG_STATE
LogFileOutput("U2: OPEN[%d]: %02x\n", i, sr);
#endif
}
void closeSocket(const size_t i)
{
Socket & socket = sockets[i];
socket.clearFD();
#ifdef U2_LOG_STATE
LogFileOutput("U2: CLOSE[%d]\n", i);
#endif
}
void connectSocket(const size_t i)
{
Socket & socket = sockets[i];
const uint8_t * dest = memory.data() + socket.registers + SN_DIPR0;
sockaddr_in destination = {};
destination.sin_family = AF_INET;
// already in network order
destination.sin_port = *reinterpret_cast<const uint16_t *>(memory.data() + socket.registers + SN_DPORT0);
destination.sin_addr.s_addr = *reinterpret_cast<const uint32_t *>(dest);
const int res = connect(socket.myFD, (struct sockaddr *)&destination, sizeof(destination));
if (res == 0)
{
socket.sn_sr = SN_SR_ESTABLISHED;
socket.myErrno = 0;
#ifdef U2_LOG_STATE
const uint16_t port = readNetworkWord(memory.data() + socket.registers + SN_DPORT0);
LogFileOutput("U2: TCP[%d]: CONNECT to %d.%d.%d.%d:%d\n", i, dest[0], dest[1], dest[2], dest[3], port);
#endif
}
else
{
const int error = errno;
if (error == EINPROGRESS)
{
socket.myErrno = error;
}
#ifdef U2_LOG_STATE
LogFileOutput("U2: TCP[%d]: connect: %s\n", i, strerror(error));
#endif
}
}
void setCommandRegister(const size_t i, const uint8_t value)
{
switch (value)
{
case SN_CR_OPEN:
openSocket(i);
break;
case SN_CR_CONNECT:
connectSocket(i);
break;
case SN_CR_CLOSE:
case SN_CR_DISCON:
closeSocket(i);
break;
case SN_CR_SEND:
sendData(i);
break;
case SN_CR_RECV:
updateRSR(i);
break;
#ifdef U2_LOG_UNKNOWN
default:
LogFileOutput("U2: Unknown command[%d]: %02x\n", i, value);
#endif
}
}
uint8_t readSocketRegister(const uint16_t address)
{
const uint16_t i = (address >> 8) - 0x04;
const uint16_t loc = address & 0xFF;
uint8_t value;
switch (loc)
{
case SN_MR:
case SN_CR:
value = memory[address];
break;
case SN_SR:
value = sockets[i].sn_sr;
break;
case SN_TX_FSR0:
value = getTXFreeSizeRegister(i, 8);
break;
case SN_TX_FSR1:
value = getTXFreeSizeRegister(i, 0);
break;
case SN_TX_RD0:
case SN_TX_RD1:
value = memory[address];
break;
case SN_TX_WR0:
case SN_TX_WR1:
value = memory[address];
break;
case SN_RX_RSR0:
receiveOnePacket(i);
value = getRXDataSizeRegister(i, 8);
break;
case SN_RX_RSR1:
receiveOnePacket(i);
value = getRXDataSizeRegister(i, 0);
break;
case SN_RX_RD0:
case SN_RX_RD1:
value = memory[address];
break;
default:
#ifdef U2_LOG_UNKNOWN
LogFileOutput("U2: Get unknown socket register[%d]: %04x\n", i, address);
#endif
value = memory[address];
break;
}
return value;
}
uint8_t readValueAt(const uint16_t address)
{
uint8_t value;
switch (address)
{
case MR:
value = modeRegister;
break;
case GAR0 ... UPORT1:
value = memory[address];
break;
case S0_BASE ... S3_MAX:
value = readSocketRegister(address);
break;
case TX_BASE ... MEM_MAX:
value = memory[address];
break;
default:
#ifdef U2_LOG_UNKNOWN
LogFileOutput("U2: Read unknown location: %04x\n", address);
#endif
// this might not be 100% correct if address >= 0x8000
// see top of page 13 Uthernet II
value = memory[address & MEM_MAX];
break;
}
return value;
}
void autoIncrement()
{
if (modeRegister & MR_AI)
{
++dataAddress;
// Read bottom of Uthernet II page 12
// Setting the address to values >= 0x8000 is not really supported
switch (dataAddress)
{
case RX_BASE:
case MEM_SIZE:
dataAddress -= 0x2000;
break;
}
}
}
uint8_t readValue()
{
const uint8_t value = readValueAt(dataAddress);
autoIncrement();
return value;
}
void setIPProtocol(const size_t i, const uint16_t address, const uint8_t value)
{
memory[address] = value;
#ifdef U2_LOG_STATE
LogFileOutput("U2: IP PROTO[%d] = %d\n", i, value);
#endif
}
void setIPTypeOfService(const size_t i, const uint16_t address, const uint8_t value)
{
memory[address] = value;
#ifdef U2_LOG_STATE
LogFileOutput("U2: IP TOS[%d] = %d\n", i, value);
#endif
}
void setIPTTL(const size_t i, const uint16_t address, const uint8_t value)
{
memory[address] = value;
#ifdef U2_LOG_STATE
LogFileOutput("U2: IP TTL[%d] = %d\n", i, value);
#endif
}
void writeSocketRegister(const uint16_t address, const uint8_t value)
{
const uint16_t i = (address >> 8) - 0x04;
const uint16_t loc = address & 0xFF;
switch (loc)
{
case SN_MR:
setSocketModeRegister(i, address, value);
break;
case SN_CR:
setCommandRegister(i, value);
break;
case SN_PORT0:
case SN_PORT1:
case SN_DPORT0:
case SN_DPORT1:
memory[address] = value;
break;
case SN_DIPR0 ... SN_DIPR3:
memory[address] = value;
break;
case SN_PROTO:
setIPProtocol(i, address, value);
break;
case SN_TOS:
setIPTypeOfService(i, address, value);
break;
case SN_TTL:
setIPTTL(i, address, value);
break;
case SN_TX_WR0:
memory[address] = value;
break;
case SN_TX_WR1:
memory[address] = value;
break;
case SN_RX_RD0:
memory[address] = value;
break;
case SN_RX_RD1:
memory[address] = value;
break;
#ifdef U2_LOG_UNKNOWN
default:
LogFileOutput("U2: Set unknown socket register[%d]: %04x\n", i, address);
break;
#endif
};
}
void setModeRegister(const uint16_t address, const uint8_t value)
{
if (value & MR_RST)
{
initialise();
}
else
{
modeRegister = value;
}
}
void writeCommonRegister(const uint16_t address, const uint8_t value)
{
switch (address)
{
case MR:
setModeRegister(address, value);
break;
case GAR0 ... GAR3:
case SUBR0 ... SUBR3:
case SHAR0 ... SHAR5:
case SIPR0 ... SIPR3:
memory[address] = value;
break;
case RMSR:
setRXSizes(address, value);
break;
case TMSR:
setTXSizes(address, value);
break;
#ifdef U2_LOG_UNKNOWN
default:
LogFileOutput("U2: Set unknown common register: %04x\n", address);
break;
#endif
};
}
void writeValueAt(const uint16_t address, const uint8_t value)
{
switch (address)
{
case MR ... UPORT1:
writeCommonRegister(address, value);
break;
case S0_BASE ... S3_MAX:
writeSocketRegister(address, value);
break;
case TX_BASE ... MEM_MAX:
memory[address] = value;
break;
#ifdef U2_LOG_UNKNOWN
default:
LogFileOutput("U2: Write to unknown location: %02x to %04x\n", value, address);
break;
#endif
}
}
void writeValue(const uint8_t value)
{
writeValueAt(dataAddress, value);
autoIncrement();
}
void initialise()
{
LogFileOutput("U2: Uthernet 2 initialisation\n");
modeRegister = 0;
// dataAddress is NOT reset, see page 10 of Uthernet II
sockets.resize(4);
memory.clear();
memory.resize(MEM_SIZE, 0);
for (size_t i = 0; i < sockets.size(); ++i)
{
sockets[i].clearFD();
sockets[i].registers = S0_BASE + (i << 8);
}
// initial values
memory[RTR0] = 0x07;
memory[RTR1] = 0xD0;
setRXSizes(RMSR, 0x55);
setTXSizes(TMSR, 0x55);
#ifdef U2_USE_SLIRP
slirp->clearQueue();
#endif
}
BYTE u2_C0(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles)
{
BYTE res = write ? 0 : MemReadFloatingBus(nCycles);
#ifdef U2_LOG_VERBOSE
const uint16_t oldAddress = dataAddress;
#endif
const uint8_t loc = address & 0x0F;
if (write)
{
switch (loc)
{
case C0X_MODE_REGISTER:
setModeRegister(MR, value);
break;
case C0X_ADDRESS_HIGH:
dataAddress = (value << 8) | (dataAddress & 0x00FF);
break;
case C0X_ADDRESS_LOW:
dataAddress = (value << 0) | (dataAddress & 0xFF00);
break;
case C0X_DATA_PORT:
writeValue(value);
break;
}
}
else
{
switch (loc)
{
case C0X_MODE_REGISTER:
res = modeRegister;
break;
case C0X_ADDRESS_HIGH:
res = getIByte(dataAddress, 8);
break;
case C0X_ADDRESS_LOW:
res = getIByte(dataAddress, 0);
break;
case C0X_DATA_PORT:
res = readValue();
break;
}
}
#ifdef U2_LOG_VERBOSE
const char * mode = write ? "WRITE " : "READ ";
const char c = std::isprint(res) ? res : '.';
LogFileOutput("U2: %04x: %s %04x[%04x] %02x = %02x, %c [%d = %d]\n", programcounter, mode, address, oldAddress, value, res, c, value, res);
#endif
return res;
}
}
void registerUthernet2()
{
#ifdef U2_USE_SLIRP
slirp.reset();
slirp = std::make_shared<SlirpNet>();
#else
const int check = tfe_enabled;
tfe_init();
if (tfe_enabled != check)
{
// tfe initialisation failed
return;
}
#endif
dataAddress = 0;
initialise();
RegisterIoHandler(SLOT3, u2_C0, u2_C0, nullptr, nullptr, nullptr, nullptr);
}
void unRegisterUthernet2()
{
#ifdef U2_USE_SLIRP
slirp.reset();
#endif
memory.clear();
sockets.clear();
}
void processEventsUthernet2(uint32_t timeout)
{
#ifdef U2_USE_SLIRP
if (slirp)
{
slirp->process(timeout);
}
#endif
for (Socket & socket : sockets)
{
socket.process();
}
}