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
|
- libsdl2-image-dev
|
||||||
- libgles-dev
|
- libgles-dev
|
||||||
- libpcap-dev
|
- libpcap-dev
|
||||||
|
- libslirp-dev
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
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
|
libsdl2-dev
|
||||||
libgles-dev
|
libgles-dev
|
||||||
libpcap-dev
|
libpcap-dev
|
||||||
|
libslirp-dev
|
||||||
|
|
Loading…
Add table
Reference in a new issue