Merge branch 'uthernet2'
This commit is contained in:
commit
f5c929d2a2
14 changed files with 1160 additions and 9 deletions
|
@ -30,3 +30,4 @@ matrix:
|
|||
- libsdl2-image-dev
|
||||
- libgles-dev
|
||||
- libpcap-dev
|
||||
- libslirp-dev
|
||||
|
|
|
@ -73,6 +73,9 @@ set(SOURCE_FILES
|
|||
linux/keyboard.cpp
|
||||
linux/linuxframe.cpp
|
||||
linux/context.cpp
|
||||
linux/network/uthernet2.cpp
|
||||
linux/network/tfe2.cpp
|
||||
linux/network/slirp2.cpp
|
||||
|
||||
linux/duplicates/Debug.cpp
|
||||
linux/duplicates/Debug_Display.cpp
|
||||
|
@ -159,6 +162,10 @@ set(HEADER_FILES
|
|||
linux/registry.h
|
||||
linux/keyboard.h
|
||||
linux/linuxframe.h
|
||||
linux/network/uthernet2.h
|
||||
linux/network/tfe2.h
|
||||
linux/network/slirp2.h
|
||||
linux/network/registers.h
|
||||
linux/win.h
|
||||
|
||||
Z80VICE/z80.h
|
||||
|
@ -175,12 +182,14 @@ add_library(appleii SHARED
|
|||
pkg_check_modules(YAML REQUIRED yaml-0.1)
|
||||
pkg_check_modules(MINIZIP REQUIRED minizip)
|
||||
pkg_check_modules(PCAP REQUIRED libpcap)
|
||||
pkg_check_modules(SLIRP REQUIRED slirp)
|
||||
find_package(Boost REQUIRED)
|
||||
|
||||
target_include_directories(appleii PRIVATE
|
||||
${YAML_INCLUDE_DIRS}
|
||||
${PCAP_INCLUDE_DIRS}
|
||||
${Boost_INCLUDE_DIRS}
|
||||
${SLIRP_INCLUDE_DIRS}
|
||||
Debugger
|
||||
)
|
||||
|
||||
|
@ -193,6 +202,7 @@ target_link_libraries(appleii PRIVATE
|
|||
${YAML_LIBRARIES}
|
||||
${MINIZIP_LIBRARIES}
|
||||
${PCAP_LIBRARIES}
|
||||
${SLIRP_LIBRARIES}
|
||||
)
|
||||
|
||||
target_compile_options(appleii PUBLIC
|
||||
|
|
|
@ -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 tx_pad_dis, /* TXPADDIS: Disable padding to 60 Bytes */
|
||||
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
|
||||
|
|
|
@ -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 tx_pad_dis, /* TXPADDIS: Disable padding to 60 Bytes */
|
||||
int txlength, /* Frame length */
|
||||
BYTE *txframe /* Pointer to the frame to be transmitted */
|
||||
const BYTE *txframe /* Pointer to the frame to be transmitted */
|
||||
);
|
||||
|
||||
extern
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include "Tfe/tfe.h"
|
||||
#include "Tfe/tfesupp.h"
|
||||
#include "linux/network/uthernet2.h"
|
||||
|
||||
#include "imgui_internal.h"
|
||||
|
||||
|
@ -556,33 +557,44 @@ namespace sa2
|
|||
standardLabelText("Status", tfe_enabled ? "enabled" : "disabled");
|
||||
if (ImGui::BeginCombo("Interface", static_cast<const char *>(get_tfe_interface())))
|
||||
{
|
||||
std::vector<char *> ifaces;
|
||||
if (tfe_enumadapter_open())
|
||||
{
|
||||
char *pname;
|
||||
char *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
|
||||
// as update_tfe_interface() will invalidate it
|
||||
const char * current = static_cast<const char *>(get_tfe_interface());
|
||||
const bool isSelected = strcmp(pname, current) == 0;
|
||||
if (ImGui::Selectable(pname, isSelected))
|
||||
const bool isSelected = strcmp(iface, current) == 0;
|
||||
if (ImGui::Selectable(iface, isSelected))
|
||||
{
|
||||
update_tfe_interface(pname, nullptr);
|
||||
RegSaveString(TEXT(REG_CONFIG), TEXT(REGVALUE_UTHERNET_INTERFACE), 1, pname);
|
||||
// the following line interacts with tfe_enumadapter, so we must run it outside the above loop
|
||||
update_tfe_interface(iface, nullptr);
|
||||
RegSaveString(TEXT(REG_CONFIG), TEXT(REGVALUE_UTHERNET_INTERFACE), 1, iface);
|
||||
}
|
||||
if (isSelected)
|
||||
{
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
lib_free(pname);
|
||||
lib_free(pdescription);
|
||||
lib_free(iface);
|
||||
}
|
||||
tfe_enumadapter_close();
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
if (ImGui::Button("Enable Uthernet 2"))
|
||||
{
|
||||
registerUthernet2();
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "linux/interface.h"
|
||||
#include "linux/benchmark.h"
|
||||
#include "linux/context.h"
|
||||
#include "linux/network/uthernet2.h"
|
||||
|
||||
#include "frontends/common2/fileregistry.h"
|
||||
#include "frontends/common2/utils.h"
|
||||
|
@ -168,6 +169,7 @@ void run_sdl(int argc, const char * argv [])
|
|||
|
||||
eventTimer.tic();
|
||||
sa2::writeAudio();
|
||||
processEventsUthernet2(5);
|
||||
frame->ProcessEvents(quit);
|
||||
eventTimer.toc();
|
||||
|
||||
|
|
72
source/linux/network/registers.h
Normal file
72
source/linux/network/registers.h
Normal 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
|
169
source/linux/network/slirp2.cpp
Normal file
169
source/linux/network/slirp2.cpp
Normal 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;
|
||||
}
|
27
source/linux/network/slirp2.h
Normal file
27
source/linux/network/slirp2.h
Normal 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;
|
||||
};
|
106
source/linux/network/tfe2.cpp
Normal file
106
source/linux/network/tfe2.cpp
Normal 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;
|
||||
}
|
5
source/linux/network/tfe2.h
Normal file
5
source/linux/network/tfe2.h
Normal 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);
|
740
source/linux/network/uthernet2.cpp
Normal file
740
source/linux/network/uthernet2.cpp
Normal 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
|
||||
}
|
6
source/linux/network/uthernet2.h
Normal file
6
source/linux/network/uthernet2.h
Normal 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);
|
|
@ -12,3 +12,4 @@ libsdl2-image-dev
|
|||
libsdl2-dev
|
||||
libgles-dev
|
||||
libpcap-dev
|
||||
libslirp-dev
|
||||
|
|
Loading…
Add table
Reference in a new issue