Merge branch 'uthernet2'

This commit is contained in:
Andrea Odetti 2021-05-01 19:37:55 +01:00
commit f5c929d2a2
14 changed files with 1160 additions and 9 deletions

View file

@ -30,3 +30,4 @@ matrix:
- libsdl2-image-dev - libsdl2-image-dev
- libgles-dev - libgles-dev
- libpcap-dev - libpcap-dev
- libslirp-dev

View file

@ -73,6 +73,9 @@ set(SOURCE_FILES
linux/keyboard.cpp linux/keyboard.cpp
linux/linuxframe.cpp linux/linuxframe.cpp
linux/context.cpp linux/context.cpp
linux/network/uthernet2.cpp
linux/network/tfe2.cpp
linux/network/slirp2.cpp
linux/duplicates/Debug.cpp linux/duplicates/Debug.cpp
linux/duplicates/Debug_Display.cpp linux/duplicates/Debug_Display.cpp
@ -159,6 +162,10 @@ set(HEADER_FILES
linux/registry.h linux/registry.h
linux/keyboard.h linux/keyboard.h
linux/linuxframe.h linux/linuxframe.h
linux/network/uthernet2.h
linux/network/tfe2.h
linux/network/slirp2.h
linux/network/registers.h
linux/win.h linux/win.h
Z80VICE/z80.h Z80VICE/z80.h
@ -175,12 +182,14 @@ add_library(appleii SHARED
pkg_check_modules(YAML REQUIRED yaml-0.1) pkg_check_modules(YAML REQUIRED yaml-0.1)
pkg_check_modules(MINIZIP REQUIRED minizip) pkg_check_modules(MINIZIP REQUIRED minizip)
pkg_check_modules(PCAP REQUIRED libpcap) pkg_check_modules(PCAP REQUIRED libpcap)
pkg_check_modules(SLIRP REQUIRED slirp)
find_package(Boost REQUIRED) find_package(Boost REQUIRED)
target_include_directories(appleii PRIVATE target_include_directories(appleii PRIVATE
${YAML_INCLUDE_DIRS} ${YAML_INCLUDE_DIRS}
${PCAP_INCLUDE_DIRS} ${PCAP_INCLUDE_DIRS}
${Boost_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}
${SLIRP_INCLUDE_DIRS}
Debugger Debugger
) )
@ -193,6 +202,7 @@ target_link_libraries(appleii PRIVATE
${YAML_LIBRARIES} ${YAML_LIBRARIES}
${MINIZIP_LIBRARIES} ${MINIZIP_LIBRARIES}
${PCAP_LIBRARIES} ${PCAP_LIBRARIES}
${SLIRP_LIBRARIES}
) )
target_compile_options(appleii PUBLIC target_compile_options(appleii PUBLIC

View file

@ -501,7 +501,7 @@ void tfe_arch_transmit(int force, /* FORCE: Delete waiting frames in trans
int inhibit_crc, /* INHIBITCRC: Do not append CRC to the transmission */ int inhibit_crc, /* INHIBITCRC: Do not append CRC to the transmission */
int tx_pad_dis, /* TXPADDIS: Disable padding to 60 Bytes */ int tx_pad_dis, /* TXPADDIS: Disable padding to 60 Bytes */
int txlength, /* Frame length */ int txlength, /* Frame length */
BYTE *txframe /* Pointer to the frame to be transmitted */ const BYTE *txframe /* Pointer to the frame to be transmitted */
) )
{ {
#ifdef TFE_DEBUG_ARCH #ifdef TFE_DEBUG_ARCH

View file

@ -56,7 +56,7 @@ void tfe_arch_transmit(int force, /* FORCE: Delete waiting frames in trans
int inhibit_crc, /* INHIBITCRC: Do not append CRC to the transmission */ int inhibit_crc, /* INHIBITCRC: Do not append CRC to the transmission */
int tx_pad_dis, /* TXPADDIS: Disable padding to 60 Bytes */ int tx_pad_dis, /* TXPADDIS: Disable padding to 60 Bytes */
int txlength, /* Frame length */ int txlength, /* Frame length */
BYTE *txframe /* Pointer to the frame to be transmitted */ const BYTE *txframe /* Pointer to the frame to be transmitted */
); );
extern extern

View file

@ -23,6 +23,7 @@
#include "Tfe/tfe.h" #include "Tfe/tfe.h"
#include "Tfe/tfesupp.h" #include "Tfe/tfesupp.h"
#include "linux/network/uthernet2.h"
#include "imgui_internal.h" #include "imgui_internal.h"
@ -556,33 +557,44 @@ namespace sa2
standardLabelText("Status", tfe_enabled ? "enabled" : "disabled"); standardLabelText("Status", tfe_enabled ? "enabled" : "disabled");
if (ImGui::BeginCombo("Interface", static_cast<const char *>(get_tfe_interface()))) if (ImGui::BeginCombo("Interface", static_cast<const char *>(get_tfe_interface())))
{ {
std::vector<char *> ifaces;
if (tfe_enumadapter_open()) if (tfe_enumadapter_open())
{ {
char *pname; char *pname;
char *pdescription; char *pdescription;
while (tfe_enumadapter(&pname, &pdescription)) while (tfe_enumadapter(&pname, &pdescription))
{
ifaces.push_back(pname);
lib_free(pdescription);
}
tfe_enumadapter_close();
for (const auto & iface : ifaces)
{ {
// must call it each time // must call it each time
// as update_tfe_interface() will invalidate it // as update_tfe_interface() will invalidate it
const char * current = static_cast<const char *>(get_tfe_interface()); const char * current = static_cast<const char *>(get_tfe_interface());
const bool isSelected = strcmp(pname, current) == 0; const bool isSelected = strcmp(iface, current) == 0;
if (ImGui::Selectable(pname, isSelected)) if (ImGui::Selectable(iface, isSelected))
{ {
update_tfe_interface(pname, nullptr); // the following line interacts with tfe_enumadapter, so we must run it outside the above loop
RegSaveString(TEXT(REG_CONFIG), TEXT(REGVALUE_UTHERNET_INTERFACE), 1, pname); update_tfe_interface(iface, nullptr);
RegSaveString(TEXT(REG_CONFIG), TEXT(REGVALUE_UTHERNET_INTERFACE), 1, iface);
} }
if (isSelected) if (isSelected)
{ {
ImGui::SetItemDefaultFocus(); ImGui::SetItemDefaultFocus();
} }
lib_free(pname); lib_free(iface);
lib_free(pdescription);
} }
tfe_enumadapter_close();
} }
ImGui::EndCombo(); ImGui::EndCombo();
} }
if (ImGui::Button("Enable Uthernet 2"))
{
registerUthernet2();
}
ImGui::EndTabItem(); ImGui::EndTabItem();
} }

View file

@ -8,6 +8,7 @@
#include "linux/interface.h" #include "linux/interface.h"
#include "linux/benchmark.h" #include "linux/benchmark.h"
#include "linux/context.h" #include "linux/context.h"
#include "linux/network/uthernet2.h"
#include "frontends/common2/fileregistry.h" #include "frontends/common2/fileregistry.h"
#include "frontends/common2/utils.h" #include "frontends/common2/utils.h"
@ -168,6 +169,7 @@ void run_sdl(int argc, const char * argv [])
eventTimer.tic(); eventTimer.tic();
sa2::writeAudio(); sa2::writeAudio();
processEventsUthernet2(5);
frame->ProcessEvents(quit); frame->ProcessEvents(quit);
eventTimer.toc(); eventTimer.toc();

View file

@ -0,0 +1,72 @@
#pragma once
// Uthernet II registers
#define C0X_MODE_REGISTER 0x04
#define C0X_ADDRESS_HIGH 0x05
#define C0X_ADDRESS_LOW 0x06
#define C0X_DATA_PORT 0x07
// W5100 registers and values
#define MR 0x0000
#define GAR0 0x0001
#define GAR3 0x0004
#define SUBR0 0x0005
#define SUBR3 0x0008
#define SHAR0 0x0009
#define SHAR5 0x000E
#define SIPR0 0x000F
#define SIPR3 0x0012
#define RTR0 0x0017
#define RTR1 0x0018
#define RMSR 0x001A
#define TMSR 0x001B
#define UPORT1 0x002F
#define S0_BASE 0x0400
#define S3_MAX 0x07FF
#define TX_BASE 0x4000
#define RX_BASE 0x6000
#define MEM_MAX 0x7FFF
#define MEM_SIZE 0x8000
#define MR_IND 0x01 // 0
#define MR_AI 0x02 // 1
#define MR_PPOE 0x08 // 3
#define MR_PB 0x10 // 4
#define MR_RST 0x80 // 7
#define SN_MR_PROTO_MASK 0x0F
#define SN_MR_CLOSED 0x00
#define SN_MR_TCP 0x01
#define SN_MR_UDP 0x02
#define SN_MR_IPRAW 0x03
#define SN_MR_MACRAW 0x04
#define SN_MR_PPPOE 0x05
#define SN_CR_OPEN 0x01
#define SN_CR_CLOSE 0x10
#define SN_CR_SEND 0x20
#define SN_CR_RECV 0x40
#define SN_MR 0x00
#define SN_CR 0x01
#define SN_SR 0x03
#define SN_DIPR0 0x0C
#define SN_DIPR3 0x0F
#define SN_PROTO 0x14
#define SN_TOS 0x15
#define SN_TTL 0x16
#define SN_TX_FSR0 0x20
#define SN_TX_FSR1 0x21
#define SN_TX_RD0 0x22
#define SN_TX_RD1 0x23
#define SN_TX_WR0 0x24
#define SN_TX_WR1 0x25
#define SN_RX_RSR0 0x26
#define SN_RX_RSR1 0x27
#define SN_RX_RD0 0x28
#define SN_RX_RD1 0x29
#define SN_SR_SOCK_IPRAW 0x32
#define SN_SR_SOCK_MACRAW 0x42

View file

@ -0,0 +1,169 @@
#include <StdAfx.h>
#include <libslirp.h>
#include "Log.h"
#include "CPU.h"
#include "Core.h"
#include "linux/network/slirp2.h"
#define IP_PACK(a,b,c,d) htonl( ((a)<<24) | ((b)<<16) | ((c)<<8) | (d))
namespace
{
short vdeslirp_slirp_to_poll(int events)
{
short ret = 0;
if (events & SLIRP_POLL_IN) ret |= POLLIN;
if (events & SLIRP_POLL_OUT) ret |= POLLOUT;
if (events & SLIRP_POLL_PRI) ret |= POLLPRI;
if (events & SLIRP_POLL_ERR) ret |= POLLERR;
if (events & SLIRP_POLL_HUP) ret |= POLLHUP;
return ret;
}
int vdeslirp_poll_to_slirp(short events)
{
int ret = 0;
if (events & POLLIN) ret |= SLIRP_POLL_IN;
if (events & POLLOUT) ret |= SLIRP_POLL_OUT;
if (events & POLLPRI) ret |= SLIRP_POLL_PRI;
if (events & POLLERR) ret |= SLIRP_POLL_ERR;
if (events & POLLHUP) ret |= SLIRP_POLL_HUP;
return ret;
}
ssize_t net_slirp_send_packet(const void *buf, size_t len, void *opaque)
{
SlirpNet * slirp = reinterpret_cast<SlirpNet *>(opaque);
slirp->sendToGuest(reinterpret_cast<const uint8_t *>(buf), len);
return len;
}
void net_slirp_guest_error(const char *msg, void *opaque)
{
LogOutput("SLIRP: %s\n", msg);
}
int64_t net_slirp_clock_get_ns(void *opaque)
{
const int64_t ns = (10e9 * g_nCumulativeCycles) / g_fCurrentCLK6502;
return ns;
}
void *net_slirp_timer_new(SlirpTimerCb cb, void *cb_opaque, void *opaque)
{
LogFileOutput("SLIRP: slirp_timer_new()\n");
return nullptr;
}
void net_slirp_timer_free(void *timer, void *opaque)
{
LogFileOutput("SLIRP: slirp_timer_free()\n");
}
void net_slirp_timer_mod(void *timer, int64_t expire_timer, void *opaque)
{
LogFileOutput("SLIRP: slirp_timer_mod()\n");
}
void net_slirp_register_poll_fd(int /* fd */, void * /* opaque */)
{
// most existing implementations are a NOOP???
}
void net_slirp_unregister_poll_fd(int /* fd */, void * /* opaque */)
{
// most existing implementations are a NOOP???
}
void net_slirp_notify(void *opaque)
{
LogFileOutput("SLIRP: slirp_notify()\n");
}
int net_slirp_add_poll(int fd, int events, void *opaque)
{
SlirpNet * slirp = reinterpret_cast<SlirpNet *>(opaque);
return slirp->addPoll(fd, events);
}
int net_slirp_get_revents(int idx, void *opaque)
{
const SlirpNet * slirp = reinterpret_cast<SlirpNet *>(opaque);
return slirp->getREvents(idx);
}
}
SlirpNet::SlirpNet()
{
const SlirpConfig cfg =
{
.version = SLIRP_CONFIG_VERSION_MAX,
.in_enabled = 1,
.vnetwork = { .s_addr = IP_PACK(10, 0, 0, 0) },
.vnetmask = { .s_addr = IP_PACK(255, 255, 255, 0) },
.vhost = { .s_addr = IP_PACK(10, 0, 0, 1) },
.vhostname = "applewin",
.vdhcp_start = { .s_addr = IP_PACK(10, 0, 0, 2) },
};
static const SlirpCb slirp_cb =
{
.send_packet = net_slirp_send_packet,
.guest_error = net_slirp_guest_error,
.clock_get_ns = net_slirp_clock_get_ns,
.timer_new = net_slirp_timer_new,
.timer_free = net_slirp_timer_free,
.timer_mod = net_slirp_timer_mod,
.register_poll_fd = net_slirp_register_poll_fd,
.unregister_poll_fd = net_slirp_unregister_poll_fd,
.notify = net_slirp_notify,
};
Slirp * slirp = slirp_new(&cfg, &slirp_cb, this);
mySlirp.reset(slirp, slirp_cleanup);
}
void SlirpNet::sendFromGuest(const uint8_t *pkt, const int pkt_len)
{
slirp_input(mySlirp.get(), pkt, pkt_len);
}
void SlirpNet::sendToGuest(const uint8_t *pkt, int pkt_len)
{
myQueue.emplace(pkt, pkt + pkt_len);
}
void SlirpNet::process(uint32_t timeout)
{
myFDs.clear();
slirp_pollfds_fill(mySlirp.get(), &timeout, net_slirp_add_poll, this);
int pollout;
if (myFDs.empty())
{
pollout = 0;
}
else
{
pollout = poll(myFDs.data(), myFDs.size(), timeout);
}
slirp_pollfds_poll(mySlirp.get(), (pollout <= 0), net_slirp_get_revents, this);
}
int SlirpNet::addPoll(const int fd, const int events)
{
const pollfd ff = { .fd = fd, .events = vdeslirp_slirp_to_poll(events) };
myFDs.push_back(ff);
return myFDs.size() - 1;
}
int SlirpNet::getREvents(const int idx) const
{
return vdeslirp_poll_to_slirp(myFDs[idx].revents);
}
std::queue<std::vector<uint8_t>> & SlirpNet::getQueue()
{
return myQueue;
}

View file

@ -0,0 +1,27 @@
#pragma once
#include <memory>
#include <vector>
#include <queue>
#include <poll.h>
struct Slirp;
class SlirpNet
{
public:
SlirpNet();
void sendFromGuest(const uint8_t *pkt, int pkt_len);
void sendToGuest(const uint8_t *pkt, int pkt_len);
void process(const uint32_t timeout);
int addPoll(const int fd, const int events);
int getREvents(const int idx) const;
std::queue<std::vector<uint8_t>> & getQueue();
private:
std::shared_ptr<Slirp> mySlirp;
std::vector<pollfd> myFDs;
std::queue<std::vector<uint8_t>> myQueue;
};

View file

@ -0,0 +1,106 @@
#include "StdAfx.h"
#include "linux/network/tfe2.h"
#include "Tfe/tfearch.h"
#include "Tfe/tfe.h"
#include <arpa/inet.h>
#include "../Log.h"
namespace
{
bool shouldAccept(const uint8_t * mac, const BYTE *buffer, const int len)
{
if (len < 6)
{
return false;
}
if (buffer[0] == mac[0] &&
buffer[1] == mac[1] &&
buffer[2] == mac[2] &&
buffer[3] == mac[3] &&
buffer[4] == mac[4] &&
buffer[5] == mac[5])
{
return true;
}
if (buffer[0] == 0xFF &&
buffer[1] == 0xFF &&
buffer[2] == 0xFF &&
buffer[3] == 0xFF &&
buffer[4] == 0xFF &&
buffer[5] == 0xFF)
{
return true;
}
return false;
}
}
void tfeTransmitOnePacket(const BYTE * buffer, const int len)
{
if (tfe_enabled)
{
tfe_arch_transmit(0, 0, 0, 0, len, buffer);
}
}
bool tfeReceiveOnePacket(const uint8_t * mac, BYTE * buffer, int & len)
{
if (!tfe_enabled)
{
return false;
}
bool done;
do
{
done = true;
int hashed;
int hash_index;
int rx_ok;
int correct_mac;
int broadcast;
int multicast = 0;
int crc_error;
const int newframe = tfe_arch_receive(
buffer, /* where to store a frame */
&len, /* length of received frame */
&hashed, /* set if the dest. address is accepted by the hash filter */
&hash_index, /* hash table index if hashed == TRUE */
&rx_ok, /* set if good CRC and valid length */
&correct_mac, /* set if dest. address is exactly our IA */
&broadcast, /* set if dest. address is a broadcast address */
&crc_error /* set if received frame had a CRC error */
);
if (newframe)
{
/* determine ourself the type of frame */
if (shouldAccept(mac, buffer, len))
{
return true;
}
else
{
done = false; /* try another frame */
}
}
} while (!done);
return false;
}
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;
}

View file

@ -0,0 +1,5 @@
#pragma once
bool tfeReceiveOnePacket(const uint8_t * mac, BYTE * buffer, int & len);
void tfeTransmitOnePacket(const BYTE * buffer, const int len);
uint16_t readNetworkWord(const uint8_t * address);

View file

@ -0,0 +1,740 @@
#include <StdAfx.h>
#define MAX_RXLENGTH 1518
// #define U2_LOG_VERBOSE
// #define U2_LOG_TRAFFIC
#define U2_LOG_UNKNOWN
// if this is defined, libslirp is used as opposed to libpcap
#define U2_USE_SLIRP
#include "linux/network/uthernet2.h"
#include "linux/network/tfe2.h"
#include "linux/network/registers.h"
#ifdef U2_USE_SLIRP
#include "linux/network/slirp2.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;
};
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();
bool isThereRoomFor(const size_t i, const size_t len)
{
const Socket & socket = sockets[i];
const uint16_t rsr = socket.sn_rx_rsr; // already present
const int size = socket.receiveSize; // total size
return rsr + len + sizeof(uint16_t) < size; // "not =": we do not want to fill the buffer.
}
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;
}
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 BYTE * data, const size_t len)
{
const uint16_t size = len + sizeof(uint16_t);
write16(i, size);
for (size_t c = 0; c < len; ++c)
{
write8(i, data[c]);
}
}
void writePacketString(const size_t i, const std::string & s)
{
const uint16_t size = s.size() + sizeof(uint16_t); // no NULL
write16(i, size);
for (size_t c = 0; c < s.size(); ++c)
{
write8(i, s[c]);
}
}
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:
LogFileOutput("U2: Mode[%d]: CLOSED\n", i);
break;
case SN_MR_IPRAW:
LogFileOutput("U2: Mode[%d]: IPRAW\n", i);
break;
case SN_MR_MACRAW:
LogFileOutput("U2: Mode[%d]: MACRAW\n", i);
break;
#ifdef U2_LOG_UNKNOWN
default:
LogFileOutput("U2: Unknown protocol: %02x\n", value);
#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 (size_t i = 0; i < 4; ++i)
{
sockets[i].transmitBase = base;
const uint8_t bits = value & 0x03;
value >>= 2;
const uint16_t size = 1 << (10 + bits);
base += size;
if (base > end)
{
base = end;
}
sockets[i].transmitSize = base - sockets[i].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 (size_t i = 0; i < 4; ++i)
{
sockets[i].receiveBase = base;
const uint8_t bits = value & 0x03;
value >>= 2;
const uint16_t size = 1 << (10 + bits);
base += size;
if (base > end)
{
base = end;
}
sockets[i].receiveSize = base - sockets[i].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 = sockets[i].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
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();
if (!queue.empty())
{
const std::vector<uint8_t> & packet = queue.front();
if (isThereRoomFor(i, packet.size()))
{
writeData(i, packet.data(), packet.size());
#ifdef U2_LOG_TRAFFIC
LogFileOutput("U2: READ MACRAW[%d]: +%d -> %d bytes\n", i, packet.size(), socket.sn_rx_rsr);
#endif
}
// maybe we should wait?
queue.pop();
}
}
#else
{
BYTE buffer[MAX_RXLENGTH];
int len = sizeof(buffer);
if (tfeReceiveOnePacket(memory.data() + SHAR0, buffer, len))
{
if (isThereRoomFor(i, len))
{
writeData(i, buffer, len);
#ifdef U2_LOG_TRAFFIC
LogFileOutput("U2: READ MACRAW[%d]: +%d -> %d bytes\n", i, len, socket.sn_rx_rsr);
#endif
}
else
{
// ??? we just skip it
#ifdef U2_LOG_TRAFFIC
LogFileOutput("U2: SKIP MACRAW[%d]: %d bytes\n", i, len);
#endif
}
}
}
#endif
}
void receiveOnePacket(const size_t i)
{
const Socket & socket = sockets[i];
const uint8_t sr = memory[socket.registers + SN_SR];
switch (sr)
{
case SN_SR_SOCK_MACRAW:
receiveOnePacketMacRaw(i);
break;
};
}
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 sendDataIPRaw(const size_t i, std::vector<uint8_t> & data)
{
#ifdef U2_LOG_TRAFFIC
const Socket & socket = sockets[i];
const uint16_t ip = socket.registers + SN_DIPR0;
LogFileOutput("U2: SEND IPRAW[%d]: %d bytes", i, data.size());
const uint8_t * source = memory.data() + SIPR0;
const uint8_t * dest = memory.data() + ip;
const uint8_t * gway = memory.data() + GAR0;
LogFileOutput(" from %d.%d.%d.%d", source[0], source[1], source[2], source[3]);
LogFileOutput(" to %d.%d.%d.%d", dest[0], dest[1], dest[2], dest[3]);
LogFileOutput(" via %d.%d.%d.%d\n", gway[0], gway[1], gway[2], gway[3]);
#endif
// dont know how to send IP raw.
}
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);
const uint8_t sr = memory[socket.registers + SN_SR];
switch (sr)
{
case SN_SR_SOCK_IPRAW:
sendDataIPRaw(i, data);
break;
case SN_SR_SOCK_MACRAW:
sendDataMacRaw(i, data);
break;
}
}
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 openSocket(const size_t i)
{
const 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 = memory[socket.registers + SN_SR];
switch (protocol)
{
case SN_MR_IPRAW:
sr = SN_SR_SOCK_IPRAW;
break;
case SN_MR_MACRAW:
sr = SN_SR_SOCK_MACRAW;
break;
#ifdef U2_LOG_UNKNOWN
default:
LogFileOutput("U2: OPEN[%d]: unknown mode: %02x\n", i, mr);
#endif
}
resetRXTXBuffers(i);
LogFileOutput("U2: OPEN[%d]: %02x\n", i, sr);
}
void closeSocket(const size_t i)
{
const Socket & socket = sockets[i];
memory[socket.registers + SN_SR] = SN_MR_CLOSED;
LogFileOutput("U2: CLOSE[%d]\n", i);
}
void setCommandRegister(const size_t i, const uint8_t value)
{
switch (value)
{
case SN_CR_OPEN:
openSocket(i);
break;
case SN_CR_CLOSE:
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 >> (2 + 8 + 8));
const uint16_t loc = address & 0xFF;
uint8_t value;
switch (loc)
{
case SN_MR:
case SN_CR:
value = memory[address];
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:
value = getRXDataSizeRegister(i, 8);
break;
case SN_RX_RSR1:
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 ... 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 uint8_t value)
{
LogFileOutput("U2: IP PROTO[%d] = %d\n", i, value);
}
void setIPTypeOfService(const size_t i, const uint8_t value)
{
LogFileOutput("U2: IP TOS[%d] = %d\n", i, value);
}
void setIPTTL(const size_t i, const uint8_t value)
{
LogFileOutput("U2: IP TTL[%d] = %d\n", i, value);
}
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_DIPR0 ... SN_DIPR3:
memory[address] = value;
break;
case SN_PROTO:
setIPProtocol(i, value);
break;
case SN_TOS:
setIPTypeOfService(i, value);
break;
case SN_TTL:
setIPTTL(i, 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)
{
modeRegister = value;
if (modeRegister & MR_RST)
{
initialise();
}
memory[address] = value; // redundant
}
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].registers = S0_BASE + (i << 8);
}
// initial values
memory[RTR0] = 0x07;
memory[RTR1] = 0xD0;
setRXSizes(RMSR, 0x55);
setTXSizes(TMSR, 0x55);
}
void receivePackets()
{
for (size_t i = 0; i < sockets.size(); ++i)
{
receiveOnePacket(i);
}
}
BYTE u2_C0(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles)
{
receivePackets();
BYTE res = write ? 0 : MemReadFloatingBus(nCycles);
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 %02x = %02x, %c\n", programcounter, mode, address, value, res, c);
#endif
return res;
}
}
void registerUthernet2()
{
initialise();
#ifdef U2_USE_SLIRP
slirp.reset();
slirp = std::make_shared<SlirpNet>();
#endif
RegisterIoHandler(SLOT3, u2_C0, u2_C0, nullptr, nullptr, nullptr, nullptr);
}
void processEventsUthernet2(uint32_t timeout)
{
#ifdef U2_USE_SLIRP
if (slirp)
{
slirp->process(timeout);
}
#endif
}

View file

@ -0,0 +1,6 @@
#pragma once
// this register the IOCalls for Uthernet 2 on slot 3
// if TFE is not enabled (or available) all operations will timeout
void registerUthernet2();
void processEventsUthernet2(uint32_t timeout);

View file

@ -12,3 +12,4 @@ libsdl2-image-dev
libsdl2-dev libsdl2-dev
libgles-dev libgles-dev
libpcap-dev libpcap-dev
libslirp-dev