diff --git a/AppleWinExpress2008.vcproj b/AppleWinExpress2008.vcproj
index c5dc60dc..b2c2080d 100644
--- a/AppleWinExpress2008.vcproj
+++ b/AppleWinExpress2008.vcproj
@@ -302,6 +302,22 @@
RelativePath=".\source\StrFormat.h"
>
+
+
+
+
+
+
+
+
@@ -437,6 +453,14 @@
+
+
+
+
@@ -450,27 +474,11 @@
>
-
-
-
-
-
-
+
+
+
-
+
+
+
+
@@ -218,14 +222,8 @@
-
- NotUsing
- NotUsing
- NotUsing
- NotUsing
- NotUsing
- NotUsing
-
+
+
NotUsing
NotUsing
@@ -250,6 +248,8 @@
NotUsing
NotUsing
+
+
diff --git a/AppleWinExpress2019.vcxproj.filters b/AppleWinExpress2019.vcxproj.filters
index e025ed4f..617a7d87 100644
--- a/AppleWinExpress2019.vcxproj.filters
+++ b/AppleWinExpress2019.vcxproj.filters
@@ -142,9 +142,6 @@
Source Files\Emulator
-
- Source Files\Uthernet
-
Source Files\Uthernet
@@ -250,6 +247,18 @@
Source Files
+
+ Source Files\Uthernet
+
+
+ Source Files\Uthernet
+
+
+ Source Files\Uthernet
+
+
+ Source Files\Uthernet
+
@@ -444,9 +453,6 @@
Source Files\Emulator
-
- Source Files\Uthernet
-
Source Files\Uthernet
@@ -573,6 +579,21 @@
Source Files
+
+ Source Files\Uthernet
+
+
+ Source Files\Uthernet
+
+
+ Source Files\Uthernet
+
+
+ Source Files\Uthernet
+
+
+ Source Files\Uthernet
+
diff --git a/resource/Applewin.rc b/resource/Applewin.rc
index dc7c5687..f1e34e4b 100644
--- a/resource/Applewin.rc
+++ b/resource/Applewin.rc
@@ -212,7 +212,7 @@ CAPTION "Ethernet Settings"
FONT 8, "MS Shell Dlg", 0, 0, 0x0
BEGIN
LTEXT "Ethernet",IDC_TFE_SETTINGS_ENABLE_T,9,7,30,8
- COMBOBOX IDC_TFE_SETTINGS_ENABLE,45,5,50,80,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ COMBOBOX IDC_TFE_SETTINGS_ENABLE,45,5,60,80,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Interface",IDC_TFE_SETTINGS_INTERFACE_T,9,24,30,8
COMBOBOX IDC_TFE_SETTINGS_INTERFACE,45,22,210,80,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "",IDC_TFE_SETTINGS_INTERFACE_NAME,9,44,250,8
diff --git a/source/Card.cpp b/source/Card.cpp
index 05b36465..1a3c5141 100644
--- a/source/Card.cpp
+++ b/source/Card.cpp
@@ -21,7 +21,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "StdAfx.h"
#include "Card.h"
-#include "Tfe/tfe.h"
+#include "Uthernet1.h"
+#include "Uthernet2.h"
#include "Mockingboard.h"
#include "ParallelPrinter.h"
#include "z80emu.h"
@@ -71,9 +72,6 @@ void DummyCard::InitializeIO(LPBYTE pCxRomPeripheral)
case CT_GenericPrinter:
PrintLoadRom(pCxRomPeripheral, m_slot);
break;
- case CT_Uthernet:
- tfe_InitializeIO(pCxRomPeripheral, m_slot);
- break;
case CT_GenericClock:
break; // nothing to do
case CT_MockingboardC:
@@ -124,9 +122,6 @@ void DummyCard::SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
case CT_Z80:
Z80_SaveSnapshot(yamlSaveHelper, m_slot);
break;
- case CT_Uthernet:
- tfe_SaveSnapshot(yamlSaveHelper, m_slot);
- break;
}
}
@@ -142,8 +137,6 @@ bool DummyCard::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version)
return Phasor_LoadSnapshot(yamlLoadHelper, m_slot, version);
case CT_Z80:
return Z80_LoadSnapshot(yamlLoadHelper, m_slot, version);
- case CT_Uthernet:
- return tfe_LoadSnapshot(yamlLoadHelper, m_slot, version);
}
return false;
}
@@ -186,13 +179,15 @@ std::string Card::GetCardName(const SS_CARDTYPE cardType)
case CT_SAM:
return SAMCard::GetSnapshotCardName();
case CT_Uthernet:
- return tfe_GetSnapshotCardName();
+ return Uthernet1::GetSnapshotCardName();
case CT_FourPlay:
return FourPlayCard::GetSnapshotCardName();
case CT_SNESMAX:
return SNESMAXCard::GetSnapshotCardName();
case CT_VidHD:
return VidHDCard::GetSnapshotCardName();
+ case CT_Uthernet2:
+ return Uthernet2::GetSnapshotCardName();
default:
return "Unknown";
}
@@ -236,7 +231,7 @@ SS_CARDTYPE Card::GetCardType(const std::string & card)
{
return CT_GenericHDD;
}
- else if (card == tfe_GetSnapshotCardName())
+ else if (card == Uthernet1::GetSnapshotCardName())
{
return CT_Uthernet;
}
@@ -260,6 +255,10 @@ SS_CARDTYPE Card::GetCardType(const std::string & card)
{
return CT_VidHD;
}
+ else if (card == Uthernet2::GetSnapshotCardName())
+ {
+ return CT_Uthernet2;
+ }
else
{
throw std::runtime_error("Slots: Unknown card: " + card); // todo: don't throw - just ignore & continue
diff --git a/source/Card.h b/source/Card.h
index 47be1f99..15dc59d3 100644
--- a/source/Card.h
+++ b/source/Card.h
@@ -24,6 +24,7 @@ enum SS_CARDTYPE
CT_FourPlay, // 4 port Atari 2600 style digital joystick card
CT_SNESMAX, // 2 port Nintendo NES/SNES controller serial interface card
CT_VidHD,
+ CT_Uthernet2,
};
enum SLOTS { SLOT0=0, SLOT1, SLOT2, SLOT3, SLOT4, SLOT5, SLOT6, SLOT7, NUM_SLOTS, SLOT_AUX };
diff --git a/source/CardManager.cpp b/source/CardManager.cpp
index fabecaa6..f10d7daa 100644
--- a/source/CardManager.cpp
+++ b/source/CardManager.cpp
@@ -40,6 +40,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "SAM.h"
#include "SerialComms.h"
#include "SNESMAX.h"
+#include "Uthernet1.h"
+#include "Uthernet2.h"
#include "VidHD.h"
#include "LanguageCard.h"
#include "Memory.h"
@@ -91,7 +93,7 @@ void CardManager::InsertInternal(UINT slot, SS_CARDTYPE type)
m_slot[slot] = new SAMCard(slot);
break;
case CT_Uthernet:
- m_slot[slot] = new DummyCard(type, slot);
+ m_slot[slot] = new Uthernet1(slot);
break;
case CT_FourPlay:
m_slot[slot] = new FourPlayCard(slot);
@@ -102,6 +104,9 @@ void CardManager::InsertInternal(UINT slot, SS_CARDTYPE type)
case CT_VidHD:
m_slot[slot] = new VidHDCard(slot);
break;
+ case CT_Uthernet2:
+ m_slot[slot] = new Uthernet2(slot);
+ break;
case CT_LanguageCard:
_ASSERT(m_pLanguageCard == NULL);
diff --git a/source/Configuration/Config.h b/source/Configuration/Config.h
index 72cbc616..aae3c5ba 100644
--- a/source/Configuration/Config.h
+++ b/source/Configuration/Config.h
@@ -6,7 +6,7 @@
#include "../DiskImage.h" // Disk_Status_e
#include "../Harddisk.h"
#include "../Interface.h" // VideoRefreshRate_e, GetVideoRefreshRate()
-#include "../Tfe/tfe.h"
+#include "../Tfe/PCapBackend.h"
class CConfigNeedingRestart
{
@@ -40,7 +40,7 @@ public:
for (UINT slot = SLOT0; slot < NUM_SLOTS; slot++)
m_Slot[slot] = cardManager.QuerySlot(slot);
m_SlotAux = cardManager.QueryAux();
- m_tfeInterface = get_tfe_interface();
+ m_tfeInterface = PCapBackend::tfe_interface;
m_bEnableTheFreezesF8Rom = GetPropertySheet().GetTheFreezesF8Rom();
m_uSaveLoadStateMsg = 0;
m_videoRefreshRate = GetVideo().GetVideoRefreshRate();
diff --git a/source/Configuration/PageConfig.cpp b/source/Configuration/PageConfig.cpp
index 1a8ffb15..98fe811a 100644
--- a/source/Configuration/PageConfig.cpp
+++ b/source/Configuration/PageConfig.cpp
@@ -112,7 +112,8 @@ INT_PTR CPageConfig::DlgProcInternal(HWND hWnd, UINT message, WPARAM wparam, LPA
case IDC_ETHERNET:
ui_tfe_settings_dialog(hWnd);
- m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT3] = m_PageConfigTfe.m_tfe_enabled ? CT_Uthernet : CT_Empty;
+ m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT3] = m_PageConfigTfe.m_tfe_selected;
+ m_PropertySheetHelper.GetConfigNew().m_tfeInterface = m_PageConfigTfe.m_tfe_interface_name;
InitOptions(hWnd);
break;
@@ -249,8 +250,18 @@ INT_PTR CPageConfig::DlgProcInternal(HWND hWnd, UINT message, WPARAM wparam, LPA
}
{
- m_PageConfigTfe.m_tfe_enabled = get_tfe_enabled();
- m_PageConfigTfe.m_tfe_interface_name = get_tfe_interface();
+ SS_CARDTYPE cardInSlot3 = GetCardMgr().QuerySlot(SLOT3);
+ switch (cardInSlot3) {
+ case CT_Uthernet:
+ case CT_Uthernet2:
+ m_PageConfigTfe.m_tfe_selected = cardInSlot3;
+ break;
+ default:
+ m_PageConfigTfe.m_tfe_selected = CT_Empty;
+ break;
+ }
+
+ m_PageConfigTfe.m_tfe_interface_name = PCapBackend::tfe_interface;
}
InitOptions(hWnd);
@@ -325,8 +336,6 @@ void CPageConfig::DlgOK(HWND hWnd)
m_PropertySheetHelper.GetConfigNew().m_videoRefreshRate = isNewVideoRate50Hz ? VR_50HZ : VR_60HZ;
}
- m_PropertySheetHelper.GetConfigNew().m_tfeInterface = m_PageConfigTfe.m_tfe_interface_name;
-
if (bVideoReinit)
{
win32Frame.FrameRefreshStatus(DRAW_TITLE);
@@ -381,7 +390,7 @@ void CPageConfig::DlgOK(HWND hWnd)
void CPageConfig::InitOptions(HWND hWnd)
{
const SS_CARDTYPE slot3 = m_PropertySheetHelper.GetConfigNew().m_Slot[SLOT3];
- const BOOL enableUthernetDialog = slot3 == CT_Empty || slot3 == CT_Uthernet;
+ const BOOL enableUthernetDialog = slot3 == CT_Empty || slot3 == CT_Uthernet || slot3 == CT_Uthernet2;
EnableWindow(GetDlgItem(hWnd, IDC_ETHERNET), enableUthernetDialog);
const bool bIsSlot3VidHD = slot3 == CT_VidHD;
diff --git a/source/Configuration/PageConfigTfe.cpp b/source/Configuration/PageConfigTfe.cpp
index 7241c64f..03c3bd71 100644
--- a/source/Configuration/PageConfigTfe.cpp
+++ b/source/Configuration/PageConfigTfe.cpp
@@ -28,7 +28,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../Common.h"
#include "../Registry.h"
#include "../resource/resource.h"
-#include "../Tfe/tfe.h"
+#include "../Tfe/PCapBackend.h"
#include "../Tfe/tfesupp.h"
CPageConfigTfe* CPageConfigTfe::ms_this = 0; // reinit'd in ctor
@@ -111,29 +111,29 @@ void CPageConfigTfe::DlgCANCEL(HWND window)
BOOL CPageConfigTfe::get_tfename(int number, char **ppname, char **ppdescription)
{
- if (tfe_enumadapter_open())
+ if (PCapBackend::tfe_enumadapter_open())
{
char *pname = NULL;
char *pdescription = NULL;
while (number--)
{
- if (!tfe_enumadapter(&pname, &pdescription))
+ if (!PCapBackend::tfe_enumadapter(&pname, &pdescription))
break;
lib_free(pname);
lib_free(pdescription);
}
- if (tfe_enumadapter(&pname, &pdescription))
+ if (PCapBackend::tfe_enumadapter(&pname, &pdescription))
{
*ppname = pname;
*ppdescription = pdescription;
- tfe_enumadapter_close();
+ PCapBackend::tfe_enumadapter_close();
return TRUE;
}
- tfe_enumadapter_close();
+ PCapBackend::tfe_enumadapter_close();
}
return FALSE;
@@ -145,7 +145,7 @@ int CPageConfigTfe::gray_ungray_items(HWND hwnd)
int number;
int disabled = 0;
- get_disabled_state(&disabled);
+ PCapBackend::get_disabled_state(&disabled);
if (disabled)
{
@@ -199,14 +199,26 @@ void CPageConfigTfe::init_tfe_dialog(HWND hwnd)
uilib_adjust_group_width(hwnd, ms_leftgroup);
uilib_move_group(hwnd, ms_rightgroup, xsize + 30);
- active_value = (m_tfe_enabled > 0 ? 1 : 0);
+ switch (m_tfe_selected)
+ {
+ case CT_Uthernet:
+ active_value = 1;
+ break;
+ case CT_Uthernet2:
+ active_value = 2;
+ break;
+ default:
+ active_value = 0;
+ break;
+ }
temp_hwnd=GetDlgItem(hwnd,IDC_TFE_SETTINGS_ENABLE);
SendMessage(temp_hwnd, CB_ADDSTRING, 0, (LPARAM)"Disabled");
SendMessage(temp_hwnd, CB_ADDSTRING, 0, (LPARAM)"Uthernet");
+ SendMessage(temp_hwnd, CB_ADDSTRING, 0, (LPARAM)"Uthernet II");
SendMessage(temp_hwnd, CB_SETCURSEL, (WPARAM)active_value, 0);
- if (tfe_enumadapter_open())
+ if (PCapBackend::tfe_enumadapter_open())
{
int cnt = 0;
@@ -215,7 +227,7 @@ void CPageConfigTfe::init_tfe_dialog(HWND hwnd)
temp_hwnd=GetDlgItem(hwnd,IDC_TFE_SETTINGS_INTERFACE);
- for (cnt = 0; tfe_enumadapter(&pname, &pdescription); cnt++)
+ for (cnt = 0; PCapBackend::tfe_enumadapter(&pname, &pdescription); cnt++)
{
BOOL this_entry = FALSE;
@@ -237,7 +249,7 @@ void CPageConfigTfe::init_tfe_dialog(HWND hwnd)
}
}
- tfe_enumadapter_close();
+ PCapBackend::tfe_enumadapter_close();
}
if (gray_ungray_items(hwnd))
@@ -246,12 +258,12 @@ void CPageConfigTfe::init_tfe_dialog(HWND hwnd)
// TC (18 Dec 2017) this vicekb URL is a broken link now, so I copied it to the AppleWin repo, here:
// . https://github.com/AppleWin/AppleWin/blob/master/docs/VICE%20Knowledge%20Base%20-%20Article%2013-005.htm
MessageBox( hwnd,
- "TFE support is not available on your system,\n"
- "there is some important part missing. Please have a\n"
- "look at the VICE knowledge base support page\n"
- "\n http://vicekb.trikaliotis.net/13-005\n\n"
- "for possible reasons and to activate networking with VICE.",
- "TFE support", MB_ICONINFORMATION|MB_OK);
+ "Uthernet support is not available on your system,\n"
+ "WPCAP.DLL cannot be loaded.\n\n"
+ "Install Npcap from\n\n"
+ " https://npcap.com\n\n"
+ "to activate networking with AppleWin.",
+ "Uthernet support", MB_ICONINFORMATION|MB_OK);
/* just quit the dialog before it is open */
SendMessage( hwnd, WM_COMMAND, IDCANCEL, 0);
@@ -271,11 +283,22 @@ void CPageConfigTfe::save_tfe_dialog(HWND hwnd)
{
m_tfe_interface_name = buffer;
active_value = SendMessage(GetDlgItem(hwnd, IDC_TFE_SETTINGS_ENABLE), CB_GETCURSEL, 0, 0);
- m_tfe_enabled = active_value > 0 ? 1 : 0;
+ switch (active_value)
+ {
+ case 1:
+ m_tfe_selected = CT_Uthernet;
+ break;
+ case 2:
+ m_tfe_selected = CT_Uthernet2;
+ break;
+ default:
+ m_tfe_selected = CT_Empty;
+ break;
+ }
}
else
{
- m_tfe_enabled = 0;
+ m_tfe_selected = CT_Empty;
m_tfe_interface_name.clear();
}
}
diff --git a/source/Configuration/PageConfigTfe.h b/source/Configuration/PageConfigTfe.h
index 3bf92e57..bd47a146 100644
--- a/source/Configuration/PageConfigTfe.h
+++ b/source/Configuration/PageConfigTfe.h
@@ -2,6 +2,7 @@
#include "IPropertySheetPage.h"
#include "../Tfe/Uilib.h"
+#include "../Card.h"
#include
@@ -11,13 +12,13 @@ public:
CPageConfigTfe()
{
CPageConfigTfe::ms_this = this;
- m_tfe_enabled = 0;
+ m_tfe_selected = CT_Empty;
}
virtual ~CPageConfigTfe(){}
static INT_PTR CALLBACK DlgProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam);
- int m_tfe_enabled;
+ SS_CARDTYPE m_tfe_selected;
std::string m_tfe_interface_name;
protected:
diff --git a/source/Configuration/PropertySheetHelper.cpp b/source/Configuration/PropertySheetHelper.cpp
index 809f41ec..6f296976 100644
--- a/source/Configuration/PropertySheetHelper.cpp
+++ b/source/Configuration/PropertySheetHelper.cpp
@@ -32,6 +32,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../Log.h"
#include "../Registry.h"
#include "../SaveState.h"
+#include "../Tfe/PCapBackend.h"
/*
Config causing AfterClose msgs:
@@ -330,12 +331,10 @@ void CPropertySheetHelper::ApplyNewConfig(const CConfigNeedingRestart& ConfigNew
UINT slot = SLOT3;
if (CONFIG_CHANGED_LOCAL(m_Slot[slot]))
- {
SetSlot(slot, ConfigNew.m_Slot[slot]);
- if (ConfigNew.m_Slot[slot] == CT_Uthernet) // TODO: move this to UthernetCard object
- tfe_SetRegistryInterface(slot, ConfigNew.m_tfeInterface);
- }
+ // unconditionally save it, as the previous SetSlot might have removed the setting
+ PCapBackend::tfe_SetRegistryInterface(slot, ConfigNew.m_tfeInterface);
slot = SLOT4;
if (CONFIG_CHANGED_LOCAL(m_Slot[slot]))
@@ -451,6 +450,9 @@ bool CPropertySheetHelper::HardwareConfigChanged(HWND hWnd)
if (CONFIG_CHANGED(m_Slot[SLOT3]))
strMsgMain += GetSlot(SLOT3);
+ if (CONFIG_CHANGED(m_tfeInterface))
+ strMsgMain += ". Uthernet interface has changed\n";
+
if (CONFIG_CHANGED(m_Slot[SLOT4]))
strMsgMain += GetSlot(SLOT4);
diff --git a/source/FrameBase.h b/source/FrameBase.h
index c68f0a5e..c180d46d 100644
--- a/source/FrameBase.h
+++ b/source/FrameBase.h
@@ -2,6 +2,8 @@
#include "Video.h"
+class NetworkBackend;
+
class FrameBase
{
public:
@@ -41,6 +43,10 @@ public:
// this function merges LoadBitmap and GetBitmapBits from windows.h
virtual void GetBitmap(LPCSTR lpBitmapName, LONG cb, LPVOID lpvBits) = 0;
+ // create the network backed for Uthernet 1 and 2
+ // useful to use libslirp in Linux
+ virtual std::shared_ptr CreateNetworkBackend() = 0;
+
// FindResource, MAKEINTRESOURCE, SizeofResource, LoadResource, LockResource
// Return pointer to resource if size is correct.
// NULL if resource is invalid or size check fails
diff --git a/source/Memory.cpp b/source/Memory.cpp
index 0d9b4e80..67dde266 100644
--- a/source/Memory.cpp
+++ b/source/Memory.cpp
@@ -54,7 +54,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "SerialComms.h"
#include "Speaker.h"
#include "Tape.h"
-#include "Tfe/tfe.h"
#include "RGBMonitor.h"
#include "VidHD.h"
diff --git a/source/Tfe/NetworkBackend.cpp b/source/Tfe/NetworkBackend.cpp
new file mode 100644
index 00000000..66f67b3c
--- /dev/null
+++ b/source/Tfe/NetworkBackend.cpp
@@ -0,0 +1,26 @@
+/*
+AppleWin : An Apple //e emulator for Windows
+
+Copyright (C) 2022, Andrea Odetti
+
+AppleWin is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+AppleWin is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with AppleWin; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "StdAfx.h"
+#include "NetworkBackend.h"
+
+NetworkBackend::~NetworkBackend()
+{
+}
diff --git a/source/Tfe/NetworkBackend.h b/source/Tfe/NetworkBackend.h
new file mode 100644
index 00000000..e883c9cb
--- /dev/null
+++ b/source/Tfe/NetworkBackend.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#define MAX_TXLENGTH 1518
+#define MIN_TXLENGTH 4
+
+#define MAX_RXLENGTH 1518
+#define MIN_RXLENGTH 64
+
+class NetworkBackend
+{
+public:
+ virtual ~NetworkBackend();
+
+ // transmit a packet
+ virtual void transmit(
+ const int txlength, /* Frame length */
+ uint8_t *txframe /* Pointer to the frame to be transmitted */
+ ) = 0;
+
+ // receive a single packet, return size (>0) or missing (-1)
+ virtual int receive(
+ const int size, /* Buffer size */
+ uint8_t * rxframe /* Pointer to the buffer */
+ ) = 0;
+
+ // process pending packets
+ virtual void update(const ULONG nExecutedCycles) = 0;
+
+ // if the backend is usable
+ virtual bool isValid() = 0;
+};
diff --git a/source/Tfe/PCapBackend.cpp b/source/Tfe/PCapBackend.cpp
new file mode 100644
index 00000000..e87d9f18
--- /dev/null
+++ b/source/Tfe/PCapBackend.cpp
@@ -0,0 +1,98 @@
+/*
+AppleWin : An Apple //e emulator for Windows
+
+Copyright (C) 2022, Andrea Odetti
+
+AppleWin is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+AppleWin is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with AppleWin; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "StdAfx.h"
+
+#include "PCapBackend.h"
+#include "tfearch.h"
+#include "../Common.h"
+#include "../Registry.h"
+
+std::string PCapBackend::tfe_interface;
+
+PCapBackend::PCapBackend(const std::string & pcapInterface)
+{
+ tfePcapFP = TfePcapOpenAdapter(pcapInterface);
+}
+
+PCapBackend::~PCapBackend()
+{
+ TfePcapCloseAdapter(tfePcapFP);
+ tfePcapFP = NULL;
+}
+
+void PCapBackend::transmit(
+ const int txlength, /* Frame length */
+ uint8_t *txframe /* Pointer to the frame to be transmitted */
+)
+{
+ if (tfePcapFP)
+ {
+ tfe_arch_transmit(tfePcapFP, txlength, txframe);
+ }
+}
+
+int PCapBackend::receive(const int size, uint8_t * rxframe)
+{
+ if (tfePcapFP)
+ {
+ return tfe_arch_receive(tfePcapFP, size, rxframe);
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+bool PCapBackend::isValid()
+{
+ return tfePcapFP;
+}
+
+void PCapBackend::update(const ULONG /* nExecutedCycles */)
+{
+ // nothing to do
+}
+
+int PCapBackend::tfe_enumadapter_open(void)
+{
+ return tfe_arch_enumadapter_open();
+}
+
+int PCapBackend::tfe_enumadapter(char **ppname, char **ppdescription)
+{
+ return tfe_arch_enumadapter(ppname, ppdescription);
+}
+
+int PCapBackend::tfe_enumadapter_close(void)
+{
+ return tfe_arch_enumadapter_close();
+}
+
+void PCapBackend::tfe_SetRegistryInterface(UINT slot, const std::string& name)
+{
+ std::string regSection = RegGetConfigSlotSection(slot);
+ RegSaveString(regSection.c_str(), REGVALUE_UTHERNET_INTERFACE, 1, name);
+}
+
+void PCapBackend::get_disabled_state(int * param)
+{
+ *param = tfe_cannot_use;
+}
diff --git a/source/Tfe/PCapBackend.h b/source/Tfe/PCapBackend.h
new file mode 100644
index 00000000..02a45aab
--- /dev/null
+++ b/source/Tfe/PCapBackend.h
@@ -0,0 +1,64 @@
+#pragma once
+
+#include "NetworkBackend.h"
+
+#include
+
+struct pcap;
+typedef struct pcap pcap_t;
+
+class PCapBackend : public NetworkBackend
+{
+public:
+ PCapBackend(const std::string & pcapInterface);
+
+ virtual ~PCapBackend();
+
+ // transmit a packet
+ virtual void transmit(
+ const int txlength, /* Frame length */
+ uint8_t *txframe /* Pointer to the frame to be transmitted */
+ );
+
+ // receive a single packet, return size (>0) or missing (-1)
+ virtual int receive(const int size, uint8_t * rxframe);
+
+ // receive all pending packets (to the queue)
+ virtual void update(const ULONG nExecutedCycles);
+
+ // process pending packets
+ virtual bool isValid();
+
+ static void tfe_SetRegistryInterface(UINT slot, const std::string& name);
+ static void get_disabled_state(int * param);
+
+ /*
+ These functions let the UI enumerate the available interfaces.
+
+ First, tfe_enumadapter_open() is used to start enumeration.
+
+ tfe_enum_adapter is then used to gather information for each adapter present
+ on the system, where:
+
+ ppname points to a pointer which will hold the name of the interface
+ ppdescription points to a pointer which will hold the description of the interface
+
+ For each of these parameters, new memory is allocated, so it has to be
+ freed with lib_free().
+
+ tfe_enumadapter_close() must be used to stop processing.
+
+ Each function returns 1 on success, and 0 on failure.
+ tfe_enumadapter() only fails if there is no more adpater; in this case,
+ *ppname and *ppdescription are not altered.
+ */
+
+ static int tfe_enumadapter_open(void);
+ static int tfe_enumadapter(char **ppname, char **ppdescription);
+ static int tfe_enumadapter_close(void);
+
+ static std::string tfe_interface;
+
+private:
+ pcap_t * tfePcapFP;
+};
diff --git a/source/Tfe/tfe.cpp b/source/Tfe/tfe.cpp
index 15c22e22..c6aa1ca3 100644
--- a/source/Tfe/tfe.cpp
+++ b/source/Tfe/tfe.cpp
@@ -30,9 +30,6 @@
#include
#include
#include
-#ifdef DOS_TFE
-#include
-#endif
#include "tfe.h"
#include "tfearch.h"
@@ -44,1336 +41,29 @@
typedef unsigned int UINT;
#include "../Common.h" // For: IS_APPLE2
-#include "../Memory.h"
#include "../Registry.h"
-#include "../YamlHelper.h"
-/**/
-/** #define TFE_DEBUG_DUMP 1 **/
-
-/* #define TFE_DEBUG_FRAMES - might be defined in TFE.H! */
-
-#define TFE_DEBUG_WARN 1 /* this should not be deactivated */
-#define TFE_DEBUG_INIT 1
-/** #define TFE_DEBUG_LOAD 1 **/
-/** #define TFE_DEBUG_STORE 1 **/
-/**/
-
-/* ------------------------------------------------------------------------- */
-/* variables needed */
-
-/* status which received packages to accept
- This is used in tfe_should_accept().
-*/
-static BYTE tfe_ia_mac[6];
-
-/* remember the value of the hash mask */
-static DWORD tfe_hash_mask[2];
-
-static int tfe_recv_broadcast = 0; /* broadcast */
-static int tfe_recv_mac = 0; /* individual address (IA) */
-static int tfe_recv_multicast = 0; /* multicast if address passes the hash filter */
-static int tfe_recv_correct = 0; /* accept correct frames */
-static int tfe_recv_promiscuous = 0; /* promiscuous mode */
-static int tfe_recv_hashfilter = 0; /* accept if IA passes the hash filter */
-
-
-#ifdef TFE_DEBUG_WARN
-/* remember if the TXCMD has been completed before a new one is issued */
-static int tfe_started_tx = 0;
-#endif
-
-
-/* Flag: Can we even use TFE, or is the hardware not available? */
-static int tfe_cannot_use = 0;
-
-/* Flag: Do we have the TFE enabled? */
-int tfe_enabled = 0;
/* Flag: Do we use the "original" memory map or the memory map of the RR-Net? */
//static int tfe_as_rr_net = 0;
-std::string tfe_interface;
-
-/* TFE registers */
-/* these are the 8 16-bit-ports for "I/O space configuration"
- (see 4.10 on page 75 of cs8900a-4.pdf, the cs8900a data sheet)
-
- REMARK: The TFE operatoes the cs8900a in IO space configuration, as
- it generates I/OW and I/OR signals.
-*/
-#define TFE_COUNT_IO_REGISTER 0x10 /* we have 16 I/O register */
-
-static BYTE tfe[TFE_COUNT_IO_REGISTER] = { 0 };
-/*
- RW: RXTXDATA = DE00/DE01
- RW: RXTXDATA2 = DE02/DE03 (for 32-bit-operation)
- -W: TXCMD = DE04/DE05 (TxCMD, Transmit Command) mapped to PP + 0144 (Reg. 9, Sec. 4.4, page 46)
- -W: TXLENGTH = DE06/DE07 (TxLenght, Transmit Length) mapped to PP + 0146
- R-: INTSTQUEUE = DE08/DE09 (Interrupt Status Queue) mapped to PP + 0120 (ISQ, Sec. 5.1, page 78)
- RW: PP_PTR = DE0A/DE0B (PacketPage Pointer) (see. page 75p: Read -011.---- ----.----)
- RW: PP_DATA0 = DE0C/DE0D (PacketPage Data (Port 0))
- RW: PP_DATA1 = DE0E/DE0F (PacketPage Data (Port 1)) (for 32 bit only)
-*/
-
-#define TFE_ADDR_RXTXDATA 0x00 /* RW */
-#define TFE_ADDR_RXTXDATA2 0x02 /* RW 32 bit only! */
-#define TFE_ADDR_TXCMD 0x04 /* -W Maps to PP+0144 */
-#define TFE_ADDR_TXLENGTH 0x06 /* -W Maps to PP+0146 */
-#define TFE_ADDR_INTSTQUEUE 0x08 /* R- Interrupt status queue, maps to PP + 0120 */
-#define TFE_ADDR_PP_PTR 0x0a /* RW PacketPage Pointer */
-#define TFE_ADDR_PP_DATA 0x0c /* RW PacketPage Data, Port 0 */
-#define TFE_ADDR_PP_DATA2 0x0e /* RW PacketPage Data, Port 1 - 32 bit only */
-
-/* Makros for reading and writing the visible TFE register: */
-#define GET_TFE_8( _xxx_ ) \
- ( assert(_xxx_> 8) & 0xff; \
- } while (0)
-
-/* The PacketPage register */
-/* note: The locations 0 to MAX_PACKETPAGE_ARRAY-1 are handled in this array. */
-
-#define MAX_PACKETPAGE_ARRAY 0x1000 /* 4 KB */
-
-static BYTE tfe_packetpage[MAX_PACKETPAGE_ARRAY] = { 0 };
-
-static WORD tfe_packetpage_ptr = 0;
-
-/* Makros for reading and writing the PacketPage register: */
-
-#define GET_PP_8( _xxx_ ) \
- (assert(_xxx_> 8) & 0xFF; \
- } while (0)
-
-#define SET_PP_32( _xxx_, _val_ ) \
- do { \
- assert(_xxx_> 8) & 0xFF; \
- tfe_packetpage[_xxx_+2] = (_val_>>16) & 0xFF; \
- tfe_packetpage[_xxx_+3] = (_val_>>24) & 0xFF; \
- } while (0)
-
-
-/* The packetpage register: see p. 39f */
-#define TFE_PP_ADDR_PRODUCTID 0x0000 /* R- - 4.3., p. 41 */
-#define TFE_PP_ADDR_IOBASE 0x0020 /* i RW - 4.3., p. 41 - 4.7., p. 72 */
-#define TFE_PP_ADDR_INTNO 0x0022 /* i RW - 3.2., p. 17 - 4.3., p. 41 */
-#define TFE_PP_ADDR_DMA_CHAN 0x0024 /* i RW - 3.2., p. 17 - 4.3., p. 41 */
-#define TFE_PP_ADDR_DMA_SOF 0x0026 /* ? R- - 4.3., p. 41 - 5.4., p. 89 */
-#define TFE_PP_ADDR_DMA_FC 0x0028 /* ? R- - 4.3., p. 41, "Receive DMA" */
-#define TFE_PP_ADDR_RXDMA_BC 0x002a /* ? R- - 4.3., p. 41 - 5.4., p. 89 */
-#define TFE_PP_ADDR_MEMBASE 0x002c /* i RW - 4.3., p. 41 - 4.9., p. 73 */
-#define TFE_PP_ADDR_BPROM_BASE 0x0030 /* i RW - 3.6., p. 24 - 4.3., p. 41 */
-#define TFE_PP_ADDR_BPROM_MASK 0x0034 /* i RW - 3.6., p. 24 - 4.3., p. 41 */
-/* 0x0038 - 0x003F: reserved */
-#define TFE_PP_ADDR_EEPROM_CMD 0x0040 /* i RW - 3.5., p. 23 - 4.3., p. 41 */
-#define TFE_PP_ADDR_EEPROM_DATA 0x0042 /* i RW - 3.5., p. 23 - 4.3., p. 41 */
-/* 0x0044 - 0x004F: reserved */
-#define TFE_PP_ADDR_REC_FRAME_BC 0x0050 /* RW - 4.3., p. 41 - 5.2.9., p. 86 */
-/* 0x0052 - 0x00FF: reserved */
-#define TFE_PP_ADDR_CONF_CTRL 0x0100 /* - RW - 4.4., p. 46; see below */
-#define TFE_PP_ADDR_STATUS_EVENT 0x0120 /* - R- - 4.4., p. 46; see below */
-/* 0x0140 - 0x0143: reserved */
-#define TFE_PP_ADDR_TXCMD 0x0144 /* # -W - 4.5., p. 70 - 5.7., p. 98 */
-#define TFE_PP_ADDR_TXLENGTH 0x0146 /* # -W - 4.5., p. 70 - 5.7., p. 98 */
-/* 0x0148 - 0x014F: reserved */
-#define TFE_PP_ADDR_LOG_ADDR_FILTER 0x0150 /* RW - 4.6., p. 71 - 5.3., p. 86 */
-#define TFE_PP_ADDR_MAC_ADDR 0x0158 /* # RW - 4.6., p. 71 - 5.3., p. 86 */
-/* 0x015E - 0x03FF: reserved */
-#define TFE_PP_ADDR_RXSTATUS 0x0400 /* R- - 4.7., p. 72 - 5.2., p. 78 */
-#define TFE_PP_ADDR_RXLENGTH 0x0402 /* R- - 4.7., p. 72 - 5.2., p. 78 */
-#define TFE_PP_ADDR_RX_FRAMELOC 0x0404 /* R- - 4.7., p. 72 - 5.2., p. 78 */
-/* here, the received frame is stored */
-#define TFE_PP_ADDR_TX_FRAMELOC 0x0A00 /* -W - 4.7., p. 72 - 5.7., p. 98 */
-/* here, the frame to transmit is stored */
-#define TFE_PP_ADDR_END 0x1000 /* memory to TFE_PP_ADDR_END-1 is used */
-
-
-/* TFE_PP_ADDR_CONF_CTRL is subdivided: */
-#define TFE_PP_ADDR_CC_RXCFG 0x0102 /* # RW - 4.4.6., p. 52 - 0003 */
-#define TFE_PP_ADDR_CC_RXCTL 0x0104 /* # RW - 4.4.8., p. 54 - 0005 */
-#define TFE_PP_ADDR_CC_TXCFG 0x0106 /* RW - 4.4.9., p. 55 - 0007 */
-#define TFE_PP_ADDR_CC_TXCMD 0x0108 /* R- - 4.4.11., p. 57 - 0009 */
-#define TFE_PP_ADDR_CC_BUFCFG 0x010A /* RW - 4.4.12., p. 58 - 000B */
-#define TFE_PP_ADDR_CC_LINECTL 0x0112 /* # RW - 4.4.16., p. 62 - 0013 */
-#define TFE_PP_ADDR_CC_SELFCTL 0x0114 /* RW - 4.4.18., p. 64 - 0015 */
-#define TFE_PP_ADDR_CC_BUSCTL 0x0116 /* RW - 4.4.20., p. 66 - 0017 */
-#define TFE_PP_ADDR_CC_TESTCTL 0x0118 /* RW - 4.4.22., p. 68 - 0019 */
-
-/* TFE_PP_ADDR_STATUS_EVENT is subdivided: */
-#define TFE_PP_ADDR_SE_ISQ 0x0120 /* R- - 4.4.5., p. 51 - 0000 */
-#define TFE_PP_ADDR_SE_RXEVENT 0x0124 /* # R- - 4.4.7., p. 53 - 0004 */
-#define TFE_PP_ADDR_SE_TXEVENT 0x0128 /* R- - 4.4.10., p. 56 - 0008 */
-#define TFE_PP_ADDR_SE_BUFEVENT 0x012C /* R- - 4.4.13., p. 59 - 000C */
-#define TFE_PP_ADDR_SE_RXMISS 0x0130 /* R- - 4.4.14., p. 60 - 0010 */
-#define TFE_PP_ADDR_SE_TXCOL 0x0132 /* R- - 4.4.15., p. 61 - 0012 */
-#define TFE_PP_ADDR_SE_LINEST 0x0134 /* R- - 4.4.17., p. 63 - 0014 */
-#define TFE_PP_ADDR_SE_SELFST 0x0136 /* R- - 4.4.19., p. 65 - 0016 */
-#define TFE_PP_ADDR_SE_BUSST 0x0138 /* # R- - 4.4.21., p. 67 - 0018 */
-#define TFE_PP_ADDR_SE_TDR 0x013C /* R- - 4.4.23., p. 69 - 001C */
-
-
/* ------------------------------------------------------------------------- */
-/* more variables needed */
-
-static WORD txcollect_buffer = TFE_PP_ADDR_TX_FRAMELOC;
-static WORD rx_buffer = TFE_PP_ADDR_RXSTATUS;
+/* functions for selecting and querying available NICs */
-
-/* ------------------------------------------------------------------------- */
-/* some parameter definitions */
-
-#define MAX_TXLENGTH 1518
-#define MIN_TXLENGTH 4
-
-#define MAX_RXLENGTH 1518
-#define MIN_RXLENGTH 64
-
-
-/* ------------------------------------------------------------------------- */
-/* debugging functions */
-
-#ifdef TFE_DEBUG_FRAMES
-
-static int TfeDebugMaxFrameLengthToDump = 150;
-
-char *debug_outbuffer(const int length, const unsigned char * const buffer)
+// Called by: tfe_LoadSnapshot() & ApplyNewConfig()
+void tfe_SetRegistryInterface(UINT slot, const std::string& name)
{
-#define MAXLEN_DEBUG 1600
-
- int i;
- static char outbuffer[MAXLEN_DEBUG*4+1];
- char *p = outbuffer;
-
- assert( TfeDebugMaxFrameLengthToDump <= MAXLEN_DEBUG );
-
- *p = 0;
-
- for (i=0; i=length)
- break;
-
- sprintf( p, "%02X%c", buffer[i], ((i+1)%16==0)?'*':(((i+1)%8==0)?'-':' '));
- p+=3;
- }
-
- return outbuffer;
+ std::string regSection = RegGetConfigSlotSection(slot);
+ RegSaveString(regSection.c_str(), REGVALUE_UTHERNET_INTERFACE, 1, name);
}
-#endif
-
-
-#ifdef TFE_DEBUG_DUMP
-
-#define NUMBER_PER_LINE 8
-
-static
-void tfe_debug_output_general( char *what, WORD (*getFunc)(int), int count )
-{
- int i;
- char buffer[7+(6*NUMBER_PER_LINE)+2];
-
- if(g_fh) fprintf(g_fh, "%s contents:", what );
- for (i=0; i=6); /* we need at least 6 octets since the DA has this length */
-
- /* first of all, delete any status */
- *phashed = 0;
- *phash_index = 0;
- *pcorrect_mac = 0;
- *pbroadcast = 0;
- *pmulticast = 0;
-
-#ifdef TFE_DEBUG_FRAMES
- if(g_fh) fprintf(g_fh, "tfe_should_accept called with %02X:%02X:%02X:%02X:%02X:%02X, length=%4u and buffer %s",
- tfe_ia_mac[0], tfe_ia_mac[1], tfe_ia_mac[2],
- tfe_ia_mac[3], tfe_ia_mac[4], tfe_ia_mac[5],
- length,
- debug_outbuffer(length, buffer)
- );
-#endif
-
-
- if ( buffer[0]==tfe_ia_mac[0]
- && buffer[1]==tfe_ia_mac[1]
- && buffer[2]==tfe_ia_mac[2]
- && buffer[3]==tfe_ia_mac[3]
- && buffer[4]==tfe_ia_mac[4]
- && buffer[5]==tfe_ia_mac[5]
- ) {
- /* this is our individual address (IA) */
-
- *pcorrect_mac = 1;
-
- /* if we don't want "correct MAC", we might have the chance
- * that this address fits the hash index
- */
- if (tfe_recv_mac || tfe_recv_promiscuous)
- return(1);
- }
-
- if ( buffer[0]==0xFF
- && buffer[1]==0xFF
- && buffer[2]==0xFF
- && buffer[3]==0xFF
- && buffer[4]==0xFF
- && buffer[5]==0xFF
- ) {
- /* this is a broadcast address */
- *pbroadcast = 1;
-
- /* broadcasts cannot be accepted by the hash filter */
- return((tfe_recv_broadcast || tfe_recv_promiscuous) ? 1 : 0);
- }
-
- /* now check if DA passes the hash filter */
- /* RGJ added (const char *) for AppleWin */
- hashreg = (~crc32_buf((const char *)buffer,6) >> 26) & 0x3F;
-
- *phashed = (tfe_hash_mask[(hashreg>=32)?1:0] & (1 << (hashreg&0x1F))) ? 1 : 0;
- if (*phashed) {
- *phash_index = hashreg;
-
- if (buffer[0] & 0x80) {
- /* we have a multicast address */
- *pmulticast = 1;
-
- /* if the multicast address fits into the hash filter,
- * the hashed bit has to be clear
- */
- *phashed = 0;
-
- return((tfe_recv_multicast || tfe_recv_promiscuous) ? 1 : 0);
- }
- return((tfe_recv_hashfilter || tfe_recv_promiscuous) ? 1 : 0);
- }
-
- return(tfe_recv_promiscuous ? 1 : 0);
-}
-
-#ifdef TFE_DEBUG_FRAMES
- #undef return
-#endif
-
-static
-WORD tfe_receive(void)
-{
- WORD ret_val = 0x0004;
-
- BYTE buffer[MAX_RXLENGTH];
-
- int len;
- int hashed;
- int hash_index;
- int rx_ok;
- int correct_mac;
- int broadcast;
- int multicast = 0;
- int crc_error;
-
- int newframe;
-
- int ready;
-
-#ifdef TFE_DEBUG_FRAMES
- if(g_fh) fprintf( g_fh, "");
-#endif
-
- do {
- len = MAX_RXLENGTH;
-
- ready = 1 ; /* assume we will find a good frame */
-
- 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 */
- );
-
- assert((len&1) == 0); /* length has to be even! */
-
- if (newframe) {
- if (hashed || correct_mac || broadcast) {
- /* we already know the type of frame: Trust it! */
-#ifdef TFE_DEBUG_FRAMES
- if(g_fh) fprintf( g_fh, "+++ tfe_receive(): *** hashed=%u, correct_mac=%u, "
- "broadcast=%u", hashed, correct_mac, broadcast);
-#endif
- }
- else {
- /* determine ourself the type of frame */
- if (!tfe_should_accept(buffer,
- len, &hashed, &hash_index, &correct_mac, &broadcast, &multicast)) {
-
- /* if we should not accept this frame, just do nothing
- * now, look for another one */
- ready = 0; /* try another frame */
- continue;
- }
- }
-
-
- /* we did receive a frame, return that status */
- ret_val |= rx_ok ? 0x0100 : 0;
- ret_val |= multicast ? 0x0200 : 0;
-
- if (!multicast) {
- ret_val |= hashed ? 0x0040 : 0;
- }
-
- if (hashed && rx_ok) {
- /* we have the 2nd, special format with hash index: */
- assert(hash_index < 64);
- ret_val |= hash_index << 9;
- }
- else {
- /* we have the regular format */
- ret_val |= correct_mac ? 0x0400 : 0;
- ret_val |= broadcast ? 0x0800 : 0;
- ret_val |= crc_error ? 0x1000 : 0;
- ret_val |= (lenMAX_RXLENGTH) ? 0x4000 : 0;
- }
-
- /* discard any octets that are beyond the MAX_RXLEN */
- if (len>MAX_RXLENGTH) {
- len = MAX_RXLENGTH;
- }
-
- if (rx_ok) {
- int i;
-
- /* set relevant parts of the PP area to correct values */
- SET_PP_16(TFE_PP_ADDR_RXLENGTH, len);
-
- for (i=0;iMAX_TXLENGTH)
- || ((txlen>MAX_TXLENGTH-4) && (!(txcmd&0x1000)))
- || (txlenMAX_TXLENGTH)
- || ((txlength>MAX_TXLENGTH-4) && (!(txcommand&0x1000)))
- ) {
- /* txlength too big, mark an error */
- SET_PP_16(TFE_PP_ADDR_SE_BUSST, (GET_PP_16(TFE_PP_ADDR_SE_BUSST) | 0x80) & ~0x100);
- }
- else {
- /* all right, signal that we're ready for the next frame */
- SET_PP_16(TFE_PP_ADDR_SE_BUSST, (GET_PP_16(TFE_PP_ADDR_SE_BUSST) & ~0x80) | 0x100);
- }
- }
- break;
-
- case TFE_PP_ADDR_LOG_ADDR_FILTER:
- case TFE_PP_ADDR_LOG_ADDR_FILTER+2:
- case TFE_PP_ADDR_LOG_ADDR_FILTER+4:
- case TFE_PP_ADDR_LOG_ADDR_FILTER+6:
- {
- unsigned int pos = 8 * (ppaddress - TFE_PP_ADDR_LOG_ADDR_FILTER + oddaddress);
- DWORD *p = (pos < 32) ? &tfe_hash_mask[0] : &tfe_hash_mask[1];
-
- *p &= ~(0xFF << pos); /* clear out relevant bits */
- *p |= GET_PP_8(ppaddress+oddaddress) << pos;
-
- tfe_arch_set_hashfilter(tfe_hash_mask);
- }
- break;
-
- case TFE_PP_ADDR_MAC_ADDR:
- case TFE_PP_ADDR_MAC_ADDR+2:
- case TFE_PP_ADDR_MAC_ADDR+4:
- /* the MAC address has been changed */
- tfe_ia_mac[ppaddress-TFE_PP_ADDR_MAC_ADDR+oddaddress] =
- GET_PP_8(ppaddress+oddaddress);
- tfe_arch_set_mac(tfe_ia_mac);
- break;
- }
-}
-
-/*
- This is called *before* the relevant octets are read
-*/
-static
-void tfe_sideeffects_read_pp(WORD ppaddress)
-{
- assert((ppaddress & 1) == 0);
-
- switch (ppaddress)
- {
- case TFE_PP_ADDR_SE_RXEVENT:
- /* reading this before all octets of the frame are read
- performs an "implied skip" */
- {
- WORD ret_val = tfe_receive();
-
- /*
- RXSTATUS and RXEVENT are the same, except that RXSTATUS buffers
- the old value while RXEVENT sets a new value whenever it is called
- */
- SET_PP_16(TFE_PP_ADDR_RXSTATUS, ret_val);
- SET_PP_16(TFE_PP_ADDR_SE_RXEVENT, ret_val);
- }
-
- break;
-
- case TFE_PP_ADDR_SE_BUSST:
- break;
-
- case TFE_PP_ADDR_TXCMD:
-#ifdef TFE_DEBUG_WARN
- if(g_fh) fprintf(g_fh, "WARNING! Read write-only register TFE_PP_ADDR_TXCMD: IGNORED\n");
-#endif
- break;
-
- case TFE_PP_ADDR_TXLENGTH:
-#ifdef TFE_DEBUG_WARN
- if(g_fh) fprintf(g_fh, "WARNING! Read write-only register TFE_PP_ADDR_TXLENGTH: IGNORED\n");
-#endif
- break;
- }
-}
-
-
-void tfe_proceed_rx_buffer(int oddaddress) {
- /*
- According to the CS8900 spec, the handling is the following:
- first read H, then L, then H, then L.
- Now, we're inside the RX frame, now, we always get L then H, until the end is reached.
-
- even odd
- TFE_PP_ADDR_RXSTATUS: - proceed 1)
- TFE_PP_ADDR_RXLENGTH: - proceed
- TFE_PP_ADDR_RX_FRAMELOC: 2),3) -
- TFE_PP_ADDR_RX_FRAMELOC+2: proceed -
- TFE_PP_ADDR_RX_FRAMELOC+4: like TFE_PP_ADDR_RX_FRAMELOC+2
-
- 1) set status "Inside FRAMELOC" FALSE
- 2) set status "Inside FRAMELOC" TRUE if it is not already
- 3) if "Inside FRAMELOC", proceed
-
- */
-
- static int inside_frameloc;
- int proceed = 0;
-
- if (rx_buffer==TFE_PP_ADDR_RX_FRAMELOC+GET_PP_16(TFE_PP_ADDR_RXLENGTH)) {
- /* we've read all that is available, go to start again */
- rx_buffer = TFE_PP_ADDR_RXSTATUS;
- inside_frameloc = 0;
- }
- else {
- switch (rx_buffer) {
- case TFE_PP_ADDR_RXSTATUS:
- if (oddaddress) {
- proceed = 1;
- inside_frameloc = 0;
- }
- break;
-
- case TFE_PP_ADDR_RXLENGTH:
- if (oddaddress) {
- proceed = 1;
- }
- break;
-
- case TFE_PP_ADDR_RX_FRAMELOC:
- if (oddaddress==0) {
- if (inside_frameloc) {
- proceed = 1;
- }
- else {
- inside_frameloc = 1;
- }
- }
- break;
-
- default:
- proceed = (oddaddress==0) ? 1 : 0;
- break;
- }
- }
-
- if (proceed) {
- SET_TFE_16(TFE_ADDR_RXTXDATA, GET_PP_16(rx_buffer));
- rx_buffer += 2;
- }
-}
-
-
-BYTE REGPARM1 tfe_read(WORD ioaddress)
-{
- BYTE retval;
-
- assert( ioaddress < TFE_COUNT_IO_REGISTER);
-
- switch (ioaddress) {
-
- case TFE_ADDR_TXCMD:
- case TFE_ADDR_TXCMD+1:
- case TFE_ADDR_TXLENGTH:
- case TFE_ADDR_TXLENGTH+1:
-#ifdef TFE_DEBUG_WARN
- if(g_fh) fprintf(g_fh, "WARNING! Reading write-only TFE register $%02X!\n", ioaddress);
-#endif
- /* @SRT TODO: Verify with reality */
- retval = GET_TFE_8(ioaddress);
- break;
-
- case TFE_ADDR_RXTXDATA2:
- case TFE_ADDR_RXTXDATA2+1:
- case TFE_ADDR_PP_DATA2:
- case TFE_ADDR_PP_DATA2+1:
-#ifdef TFE_DEBUG_WARN
- if(g_fh) fprintf(g_fh, "WARNING! Reading not supported TFE register $%02X!\n", ioaddress);
-#endif
- /* @SRT TODO */
- retval = GET_TFE_8(ioaddress);
- break;
-
- case TFE_ADDR_PP_DATA:
- case TFE_ADDR_PP_DATA+1:
- /* make sure the TFE register have the correct content */
- {
- WORD ppaddress = tfe_packetpage_ptr & (MAX_PACKETPAGE_ARRAY-1);
-
- /* perform side-effects the read may perform */
- tfe_sideeffects_read_pp( ppaddress );
-
- /* [3] make sure the data matches the real value - [1] assumes this! */
- SET_TFE_16( TFE_ADDR_PP_DATA, GET_PP_16(ppaddress) );
- }
-
-
-#ifdef TFE_DEBUG_LOAD
- if(g_fh) fprintf(g_fh, "reading PP Ptr: $%04X => $%04X.",
- tfe_packetpage_ptr, GET_PP_16(tfe_packetpage_ptr) );
-#endif
-
- retval = GET_TFE_8(ioaddress);
- break;
-
- case TFE_ADDR_INTSTQUEUE:
- case TFE_ADDR_INTSTQUEUE+1:
- SET_TFE_16( TFE_ADDR_INTSTQUEUE, GET_PP_16(0x0120) );
- retval = GET_TFE_8(ioaddress);
- break;
-
- case TFE_ADDR_RXTXDATA:
- case TFE_ADDR_RXTXDATA+1:
- /* we're trying to read a new 16 bit word, get it from the
- receive buffer
- */
- tfe_proceed_rx_buffer(ioaddress & 0x01);
- retval = GET_TFE_8(ioaddress);
- break;
-
- default:
- retval = GET_TFE_8(ioaddress);
- break;
- };
-
-#ifdef TFE_DEBUG_LOAD
- if(g_fh) fprintf(g_fh, "read [$%02X] => $%02X.", ioaddress, retval);
-#endif
- return retval;
-}
-
-void REGPARM2 tfe_store(WORD ioaddress, BYTE byte)
-{
- assert( ioaddress < TFE_COUNT_IO_REGISTER);
-
- switch (ioaddress)
- {
- case TFE_ADDR_RXTXDATA:
- case TFE_ADDR_RXTXDATA+1:
- SET_PP_8(txcollect_buffer, byte);
- tfe_sideeffects_write_pp_on_txframe(txcollect_buffer++);
- break;
-
- case TFE_ADDR_INTSTQUEUE:
- case TFE_ADDR_INTSTQUEUE+1:
-#ifdef TFE_DEBUG_WARN
- if(g_fh) fprintf(g_fh, "WARNING! Writing read-only TFE register $%02X!\n", ioaddress);
-#endif
- /* @SRT TODO: Verify with reality */
- /* do nothing */
- return;
-
- case TFE_ADDR_RXTXDATA2:
- case TFE_ADDR_RXTXDATA2+1:
- case TFE_ADDR_PP_DATA2:
- case TFE_ADDR_PP_DATA2+1:
-#ifdef TFE_DEBUG_WARN
- if(g_fh) fprintf(g_fh, "WARNING! Writing not supported TFE register $%02X!\n", ioaddress);
-#endif
- /* do nothing */
- return;
-
- case TFE_ADDR_TXCMD:
- case TFE_ADDR_TXCMD+1:
- SET_TFE_8(ioaddress, byte);
- SET_PP_8((ioaddress-TFE_ADDR_TXCMD)+TFE_PP_ADDR_TXCMD, byte); /* perform the mapping to PP+0144 */
- tfe_sideeffects_write_pp(TFE_PP_ADDR_TXCMD, ioaddress-TFE_ADDR_TXCMD);
- break;
-
- case TFE_ADDR_TXLENGTH:
- case TFE_ADDR_TXLENGTH+1:
-
- SET_TFE_8(ioaddress, byte);
- SET_PP_8((ioaddress-TFE_ADDR_TXLENGTH)+TFE_PP_ADDR_TXLENGTH, byte ); /* perform the mapping to PP+0144 */
-
- tfe_sideeffects_write_pp(TFE_PP_ADDR_TXLENGTH, ioaddress-TFE_ADDR_TXLENGTH);
- break;
-
-/*
-#define TFE_ADDR_TXCMD 0x04 * -W Maps to PP+0144 *
-#define TFE_ADDR_TXLENGTH 0x06 * -W Maps to PP+0146 *
-#define TFE_ADDR_INTSTQUEUE 0x08 * R- Interrupt status queue, maps to PP + 0120 *
-*/
- case TFE_ADDR_PP_DATA:
- case TFE_ADDR_PP_DATA+1:
-
- /* [2] make sure the data matches the real value - [1] assumes this! */
- SET_TFE_16(TFE_ADDR_PP_DATA, GET_PP_16(tfe_packetpage_ptr));
- /* FALL THROUGH */
-
- default:
- SET_TFE_8(ioaddress, byte);
- }
-
-#ifdef TFE_DEBUG_STORE
- if(g_fh) fprintf(g_fh, "store [$%02X] <= $%02X.", ioaddress, (int)byte);
-#endif
-
- /* now check if we have to do any side-effects */
- switch (ioaddress)
- {
- case TFE_ADDR_PP_PTR:
- case TFE_ADDR_PP_PTR+1:
- tfe_packetpage_ptr = GET_TFE_16(TFE_ADDR_PP_PTR);
-
-#ifdef TFE_DEBUG_STORE
- if(g_fh) fprintf(g_fh, "set PP Ptr to $%04X.", tfe_packetpage_ptr);
-#endif
-
- if ((tfe_packetpage_ptr & 1) != 0) {
-
-#ifdef TFE_DEBUG_WARN
- if(g_fh) fprintf(g_fh,
- "WARNING! PacketPage register set to odd address $%04X (not allowed!)\n",
- tfe_packetpage_ptr );
-#endif /* #ifdef TFE_DEBUG_WARN */
-
- /* "correct" the address to the next lower address
- REMARK: I don't know how a real cs8900a will behave in this case,
- since it is not allowed. Nevertheless, this "correction"
- prevents assert()s to fail.
- */
- tfe_packetpage_ptr -= 1;
- }
-
- /*
- [1] The TFE_ADDR_PP_DATA does not need to be modified here,
- since it will be modified just before a read or store operation
- is to be performed.
- See [2] and [3]
- */
- break;
-
- case TFE_ADDR_PP_DATA:
- case TFE_ADDR_PP_DATA+1:
-
- {
- WORD ppaddress = tfe_packetpage_ptr & (MAX_PACKETPAGE_ARRAY-1);
-
-#ifdef TFE_DEBUG_STORE
- if(g_fh) fprintf(g_fh, "before writing to PP Ptr: $%04X <= $%04X.",
- ppaddress, GET_PP_16(ppaddress) );
-#endif
- {
- register WORD tmpIoAddr = ioaddress & ~1; /* word-align the address */
- SET_PP_16(ppaddress, GET_TFE_16(tmpIoAddr));
- }
-
- /* perform side-effects the write may perform */
- /* the addresses are always aligned on the whole 16-bit-word */
- tfe_sideeffects_write_pp(ppaddress, ioaddress-TFE_ADDR_PP_DATA);
-
-#ifdef TFE_DEBUG_STORE
- if(g_fh) fprintf(g_fh, "after writing to PP Ptr: $%04X <= $%04X.",
- ppaddress, GET_PP_16(ppaddress) );
-#endif
- }
- break;
- }
-
- TFE_DEBUG_OUTPUT_REG();
-}
-
-
-
-#if 0
-static
-int set_tfe_disabled(void *v, void *param)
-{
- /* dummy function since we don't want "disabled" to be stored on disk */
- return 0;
-}
-#endif
-
-
-#if 0
-static
-int set_tfe_enabled(void *v, void *param)
-{
- if (!tfe_cannot_use) {
-
- if (!v) {
- /* TFE should be deactived */
- if (tfe_enabled) {
- tfe_enabled = 0;
- /* RGJ Commented out forAppleWin */
- //c64export_remove(&export_res);
- if (tfe_deactivate() < 0) {
- return -1;
- }
- }
- return 0;
- } else {
- if (!tfe_enabled) {
- /* RGJ Commented out forAppleWin */
- //if (c64export_query(&export_res) < 0)
- // return -1;
-
- tfe_enabled = 1;
- if (tfe_activate() < 0) {
- return -1;
- }
- /* RGJ Commented out forAppleWin */
- //if (c64export_add(&export_res) < 0)
- // return -1;
-
- }
-
- return 0;
- }
-
- }
- return 0;
-}
-#endif
-
-
-static
-int set_tfe_interface(const std::string & name)
-{
- tfe_interface = name;
- return 0;
-}
-
-/* ------------------------------------------------------------------------- */
-/* commandline support functions */
-
-//#ifdef HAS_TRANSLATION
-//static const cmdline_option_t cmdline_options[] =
-//{
-// { "-tfe", SET_RESOURCE, 0, NULL, NULL, "ETHERNET_ACTIVE", (resource_value_t)1,
-// 0, IDCLS_ENABLE_TFE },
-// { "+tfe", SET_RESOURCE, 0, NULL, NULL, "ETHERNET_ACTIVE", (resource_value_t)0,
-// 0, IDCLS_DISABLE_TFE },
-// { NULL }
-//};
-//#else
-//static const cmdline_option_t cmdline_options[] =
-//{
-// { "-tfe", SET_RESOURCE, 0, NULL, NULL, "ETHERNET_ACTIVE", (resource_value_t)1,
-// NULL, N_("Enable the TFE (\"the final ethernet\") unit") },
-// { "+tfe", SET_RESOURCE, 0, NULL, NULL, "ETHERNET_ACTIVE", (resource_value_t)0,
-// NULL, N_("Disable the TFE (\"the final ethernet\") unit") },
-// { NULL }
-//};
-//#endif
-
-//int tfe_cmdline_options_init(void)
-//{
-// return cmdline_register_options(cmdline_options);
-//}
-
-
/* ------------------------------------------------------------------------- */
/* functions for selecting and querying available NICs */
int tfe_enumadapter_open(void)
{
- if (!tfe_arch_enumadapter_open()) {
- tfe_cannot_use = 1;
- return 0;
- }
- return 1;
+ return tfe_arch_enumadapter_open();
}
int tfe_enumadapter(char **ppname, char **ppdescription)
@@ -1386,176 +76,9 @@ int tfe_enumadapter_close(void)
return tfe_arch_enumadapter_close();
}
-// Go via TfeIoCxxx() instead of directly calling IO_Null() to include this specific (slot-3) _DEBUG check
-static BYTE __stdcall TfeIoCxxx (WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles)
-{
-#ifdef _DEBUG
- if (!IS_APPLE2)
- {
- // Derived from UTAIIe:5-28
- //
- // INTCXROM SLOTC3ROM TFE floating bus?
- // 0 0 N (internal ROM)
- // 0 1 Y
- // 1 0 N (internal ROM)
- // 1 1 N (internal ROM)
- if (! (!MemCheckINTCXROM() && MemCheckSLOTC3ROM()) )
- {
- _ASSERT(0); // Card ROM disabled, so IO_Cxxx() returns the internal ROM
- }
- }
-#endif
-
- return IO_Null(programcounter, address, write, value,nCycles);
-}
-
-static BYTE __stdcall TfeIo (WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles)
-{
- BYTE ret = 0;
-
- if (write) {
- if (tfe_enabled)
- tfe_store((WORD)(address & 0x0f), value);
- }
- else {
- if (tfe_enabled)
- ret = tfe_read((WORD)(address & 0x0f));
- }
-
-return ret;
-
-}
-
-void tfe_InitializeIO(LPBYTE pCxRomPeripheral, UINT slot)
-{
- RegisterIoHandler(slot, TfeIo, TfeIo, TfeIoCxxx, TfeIoCxxx, NULL, NULL);
-}
-
void get_disabled_state(int * param)
{
*param = tfe_cannot_use;
}
-int update_tfe_interface(const std::string & name)
-{
- return set_tfe_interface(name);
-}
-
-const std::string & get_tfe_interface(void)
-{
- return tfe_interface;
-}
-
-int get_tfe_enabled(void)
-{
- return tfe_enabled;
-}
-
-// Called by: tfe_LoadSnapshot() & ApplyNewConfig()
-void tfe_SetRegistryInterface(UINT slot, const std::string& name)
-{
- std::string regSection = RegGetConfigSlotSection(slot);
- RegSaveString(regSection.c_str(), REGVALUE_UTHERNET_INTERFACE, 1, name);
-}
-
-/* ------------------------------------------------------------------------- */
-/* snapshot support functions */
-
-#define SS_YAML_KEY_ENABLED "Enabled"
-#define SS_YAML_KEY_NETWORK_INTERFACE "Network Interface"
-#define SS_YAML_KEY_STARTED_TX "Started Tx"
-#define SS_YAML_KEY_CANNOT_USE "Cannot Use"
-#define SS_YAML_KEY_TXCOLLECT_BUFFER "Tx Collect Buffer"
-#define SS_YAML_KEY_RX_BUFFER "Rx Buffer"
-#define SS_YAML_KEY_CS8900A_REGS "CS8900A Registers"
-#define SS_YAML_KEY_PACKETPAGE_REGS "PacketPage Registers"
-
-static const UINT kUNIT_VERSION = 1;
-
-std::string tfe_GetSnapshotCardName(void)
-{
- static const std::string name("Uthernet");
- return name;
-}
-
-void tfe_SaveSnapshot(class YamlSaveHelper& yamlSaveHelper, const UINT uSlot)
-{
- YamlSaveHelper::Slot slot(yamlSaveHelper, tfe_GetSnapshotCardName(), uSlot, kUNIT_VERSION);
-
- YamlSaveHelper::Label unit(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);
-
- yamlSaveHelper.SaveBool(SS_YAML_KEY_ENABLED, tfe_enabled ? true : false);
- yamlSaveHelper.SaveString(SS_YAML_KEY_NETWORK_INTERFACE, get_tfe_interface());
-
- yamlSaveHelper.SaveBool(SS_YAML_KEY_STARTED_TX, tfe_started_tx ? true : false);
- yamlSaveHelper.SaveBool(SS_YAML_KEY_CANNOT_USE, tfe_cannot_use ? true : false);
-
- yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_TXCOLLECT_BUFFER, txcollect_buffer);
- yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_RX_BUFFER, rx_buffer);
-
- {
- YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_CS8900A_REGS);
- yamlSaveHelper.SaveMemory(tfe, TFE_COUNT_IO_REGISTER);
- }
-
- {
- YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_PACKETPAGE_REGS);
- yamlSaveHelper.SaveMemory(tfe_packetpage, MAX_PACKETPAGE_ARRAY);
- }
-}
-
-bool tfe_LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version)
-{
- if (slot != SLOT3) // fixme
- Card::ThrowErrorInvalidSlot(CT_Uthernet, slot);
-
- if (version < 1 || version > kUNIT_VERSION)
- Card::ThrowErrorInvalidVersion(CT_Uthernet, version);
-
- tfe_enabled = yamlLoadHelper.LoadBool(SS_YAML_KEY_ENABLED) ? true : false;
- set_tfe_interface(yamlLoadHelper.LoadString(SS_YAML_KEY_NETWORK_INTERFACE));
-
- tfe_started_tx = yamlLoadHelper.LoadBool(SS_YAML_KEY_STARTED_TX) ? true : false;
- tfe_cannot_use = yamlLoadHelper.LoadBool(SS_YAML_KEY_CANNOT_USE) ? true : false;
-
- txcollect_buffer = yamlLoadHelper.LoadUint(SS_YAML_KEY_TXCOLLECT_BUFFER);
- rx_buffer = yamlLoadHelper.LoadUint(SS_YAML_KEY_RX_BUFFER);
-
- if (!yamlLoadHelper.GetSubMap(SS_YAML_KEY_CS8900A_REGS))
- throw std::runtime_error("Card: Expected key: " SS_YAML_KEY_CS8900A_REGS);
-
- memset(tfe, 0, TFE_COUNT_IO_REGISTER);
- yamlLoadHelper.LoadMemory(tfe, TFE_COUNT_IO_REGISTER);
- yamlLoadHelper.PopMap();
-
- if (!yamlLoadHelper.GetSubMap(SS_YAML_KEY_PACKETPAGE_REGS))
- throw std::runtime_error("Card: Expected key: " SS_YAML_KEY_PACKETPAGE_REGS);
-
- memset(tfe_packetpage, 0, MAX_PACKETPAGE_ARRAY);
- yamlLoadHelper.LoadMemory(tfe_packetpage, MAX_PACKETPAGE_ARRAY);
- yamlLoadHelper.PopMap();
-
- // Side effects after PackagePage has been loaded
-
- tfe_packetpage_ptr = GET_TFE_16(TFE_ADDR_PP_PTR);
-
- tfe_sideeffects_write_pp(TFE_PP_ADDR_CC_RXCTL, 0); // set the 6 tfe_recv_* vars
-
- for (UINT i = 0; i < 8; i++)
- tfe_sideeffects_write_pp((TFE_PP_ADDR_LOG_ADDR_FILTER + i) & ~1, i & 1); // set tfe_hash_mask
-
- for (UINT i = 0; i < 6; i++)
- tfe_sideeffects_write_pp((TFE_PP_ADDR_MAC_ADDR + i) & ~1, i & 1); // set tfe_ia_mac
-
- //
-
- tfe_SetRegistryInterface(slot, get_tfe_interface());
-
- // Setup the npcap.dll func ptrs & open/configure the interface
- // NB. Overrides tfe_enabled and tfe_cannot_use, which are set above
- tfe_init(false); // reset=false
-
- return true;
-}
-
//#endif /* #ifdef HAVE_TFE */
diff --git a/source/Tfe/tfe.h b/source/Tfe/tfe.h
deleted file mode 100644
index 8e6d493e..00000000
--- a/source/Tfe/tfe.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * tfe.h - TFE ("The final ethernet" emulation.
- *
- * Written by
- * Spiro Trikaliotis
- *
- * This file is part of VICE, the Versatile Commodore Emulator.
- * See README for copyright notice.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- * 02111-1307 USA.
- *
- */
-
-#ifndef _TFE_H
-#define _TFE_H
-
-#include "../CommonVICE/types.h"
-#include
-#include
-
-/* define this only if VICE should write each and every frame received
- and send into the VICE log
- WARNING: The log grows very fast!
-*/
-/** #define TFE_DEBUG_FRAMES **/
-
-extern int tfe_enabled;
-
-extern void tfe_init(bool reset);
-extern int tfe_resources_init(void);
-extern int tfe_cmdline_options_init(void);
-extern int update_tfe_interface(const std::string & name);
-void get_disabled_state(int * param);
-
-extern void tfe_shutdown(void);
-extern BYTE REGPARM1 tfe_read(WORD addr);
-extern void REGPARM2 tfe_store(WORD addr, BYTE byte);
-void tfe_InitializeIO(LPBYTE pCxRomPeripheral, UINT slot);
-void tfe_SetRegistryInterface(UINT slot, const std::string& name);
-
-std::string tfe_GetSnapshotCardName(void);
-void tfe_SaveSnapshot(class YamlSaveHelper& yamlSaveHelper, const UINT uSlot);
-bool tfe_LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version);
-
-/*
- These functions let the UI enumerate the available interfaces.
-
- First, tfe_enumadapter_open() is used to start enumeration.
-
- tfe_enum_adapter is then used to gather information for each adapter present
- on the system, where:
-
- ppname points to a pointer which will hold the name of the interface
- ppdescription points to a pointer which will hold the description of the interface
-
- For each of these parameters, new memory is allocated, so it has to be
- freed with lib_free().
-
- tfe_enumadapter_close() must be used to stop processing.
-
- Each function returns 1 on success, and 0 on failure.
- tfe_enumadapter() only fails if there is no more adpater; in this case,
- *ppname and *ppdescription are not altered.
-*/
-extern int tfe_enumadapter_open(void);
-extern int tfe_enumadapter(char **ppname, char **ppdescription);
-extern int tfe_enumadapter_close(void);
-
-extern int get_tfe_enabled(void);
-extern const std::string & get_tfe_interface(void);
-
-extern FILE* g_fh; // Filehandle for log file
-
-#endif
diff --git a/source/Tfe/tfearch.cpp b/source/Tfe/tfearch.cpp
index 8b926561..bdab39dc 100644
--- a/source/Tfe/tfearch.cpp
+++ b/source/Tfe/tfearch.cpp
@@ -40,7 +40,6 @@
#include
#include // this is necessary in linux, but in MSVC windows.h MUST come after winsock2.h (from pcap.h above)
-#include "tfe.h"
#include "tfearch.h"
#include "tfesupp.h"
#include "../Log.h"
@@ -53,6 +52,8 @@
#define TFE_DEBUG_WARN 1 /* this should not be deactivated */
+int tfe_cannot_use = 0;
+
#ifdef _MSC_VER
typedef pcap_t *(*pcap_open_live_t)(const char *, int, int, int, char *);
@@ -121,6 +122,7 @@ BOOL TfePcapLoadLibrary(void)
pcap_library = LoadLibrary("wpcap.dll");
if (!pcap_library) {
+ tfe_cannot_use = 1;
if(g_fh) fprintf(g_fh, "LoadLibrary WPCAP.DLL failed!\n" );
return FALSE;
}
@@ -179,7 +181,6 @@ static BOOL TfePcapLoadLibrary(void)
static pcap_if_t *TfePcapNextDev = NULL;
static pcap_if_t *TfePcapAlldevs = NULL;
-static pcap_t *TfePcapFP = NULL;
static char TfePcapErrbuf[PCAP_ERRBUF_SIZE];
@@ -284,13 +285,13 @@ int tfe_arch_enumadapter_close(void)
return 1;
}
-static
-BOOL TfePcapOpenAdapter(const std::string & interface_name)
+
+pcap_t * TfePcapOpenAdapter(const std::string & interface_name)
{
pcap_if_t *TfePcapDevice = NULL;
- if (!tfe_enumadapter_open()) {
- return FALSE;
+ if (!tfe_arch_enumadapter_open()) {
+ return NULL;
}
else {
/* look if we can find the specified adapter */
@@ -302,7 +303,7 @@ BOOL TfePcapOpenAdapter(const std::string & interface_name)
/* we have an interface name, try it */
TfePcapDevice = TfePcapAlldevs;
- while (tfe_enumadapter(&pname, &pdescription)) {
+ while (tfe_arch_enumadapter(&pname, &pdescription)) {
if (strcmp(pname, interface_name.c_str())==0) {
found = TRUE;
}
@@ -319,12 +320,12 @@ BOOL TfePcapOpenAdapter(const std::string & interface_name)
}
}
- TfePcapFP = (*p_pcap_open_live)(TfePcapDevice->name, 1700, 1, 20, TfePcapErrbuf);
+ pcap_t * TfePcapFP = (*p_pcap_open_live)(TfePcapDevice->name, 1700, 1, 20, TfePcapErrbuf);
if ( TfePcapFP == NULL)
{
if(g_fh) fprintf(g_fh, "ERROR opening adapter: '%s'\n", TfePcapErrbuf);
- tfe_enumadapter_close();
- return FALSE;
+ tfe_arch_enumadapter_close();
+ return NULL;
}
if ((*p_pcap_setnonblock)(TfePcapFP, 1, TfePcapErrbuf)<0)
@@ -336,68 +337,29 @@ BOOL TfePcapOpenAdapter(const std::string & interface_name)
if((*p_pcap_datalink)(TfePcapFP) != DLT_EN10MB)
{
if(g_fh) fprintf(g_fh, "ERROR: TFE works only on Ethernet networks.\n");
- tfe_enumadapter_close();
+ tfe_arch_enumadapter_close();
(*p_pcap_close)(TfePcapFP);
TfePcapFP = NULL;
- return FALSE;
+ return NULL;
}
-
- tfe_enumadapter_close();
- return TRUE;
+
+ if(g_fh) fprintf(g_fh, "PCAP: Succesfully opened adapter: '%s'\n", TfePcapDevice->name);
+
+ tfe_arch_enumadapter_close();
+ return TfePcapFP;
}
+void TfePcapCloseAdapter(pcap_t * TfePcapFP)
+{
+ if (TfePcapFP)
+ {
+ (*p_pcap_close)(TfePcapFP);
+ }
+}
/* ------------------------------------------------------------------------- */
/* the architecture-dependend functions */
-
-int tfe_arch_init(void)
-{
- // g_fh = log_open("TFEARCH");
-
- if (!TfePcapLoadLibrary()) {
- return 0;
- }
-
- return 1;
-}
-
-void tfe_arch_pre_reset( void )
-{
-#ifdef TFE_DEBUG_ARCH
- if(g_fh) fprintf( g_fh, "tfe_arch_pre_reset().\n" );
-#endif
-}
-
-void tfe_arch_post_reset( void )
-{
-#ifdef TFE_DEBUG_ARCH
- if(g_fh) fprintf( g_fh, "tfe_arch_post_reset().\n" );
-#endif
-}
-
-int tfe_arch_activate(const std::string & interface_name)
-{
-#ifdef TFE_DEBUG_ARCH
- if(g_fh) fprintf( g_fh, "tfe_arch_activate().\n" );
-#endif
- if (!TfePcapOpenAdapter(interface_name)) {
- return 0;
- }
- return 1;
-}
-
-void tfe_arch_deactivate( void )
-{
-#ifdef TFE_DEBUG_ARCH
- if(g_fh) fprintf( g_fh, "tfe_arch_deactivate().\n" );
-#endif
- if (TfePcapFP) {
- (*p_pcap_close)(TfePcapFP);
- TfePcapFP = NULL;
- }
-}
-
void tfe_arch_set_mac( const BYTE mac[6] )
{
#if defined(TFE_DEBUG_ARCH) || defined(TFE_DEBUG_FRAMES)
@@ -460,9 +422,9 @@ void tfe_arch_line_ctl(int bEnableTransmitter, int bEnableReceiver )
typedef struct TFE_PCAP_INTERNAL_tag {
-
- unsigned int len;
+ const unsigned int size;
BYTE *buffer;
+ unsigned int rxlength;
} TFE_PCAP_INTERNAL;
@@ -476,10 +438,9 @@ void TfePcapPacketHandler(u_char *param, const struct pcap_pkthdr *header, const
/* determine the count of bytes which has been returned,
* but make sure not to overrun the buffer
*/
- if (header->caplen < pinternal->len)
- pinternal->len = header->caplen;
+ pinternal->rxlength = min(pinternal->size, header->caplen);
- memcpy(pinternal->buffer, pkt_data, pinternal->len);
+ memcpy(pinternal->buffer, pkt_data, pinternal->rxlength);
}
/* the following function receives a frame.
@@ -493,7 +454,7 @@ void TfePcapPacketHandler(u_char *param, const struct pcap_pkthdr *header, const
At most 'len' bytes are copied.
*/
static
-int tfe_arch_receive_frame(TFE_PCAP_INTERNAL *pinternal)
+int tfe_arch_receive_frame(pcap_t * TfePcapFP, TFE_PCAP_INTERNAL *pinternal)
{
int ret = -1;
@@ -501,7 +462,7 @@ int tfe_arch_receive_frame(TFE_PCAP_INTERNAL *pinternal)
/* RGJ changed from void to u_char for AppleWin */
if ((*p_pcap_dispatch)(TfePcapFP, 1, TfePcapPacketHandler, (u_char *)pinternal)!=0) {
/* Something has been received */
- ret = pinternal->len;
+ ret = pinternal->rxlength;
}
#ifdef TFE_DEBUG_ARCH
@@ -511,23 +472,13 @@ int tfe_arch_receive_frame(TFE_PCAP_INTERNAL *pinternal)
return ret;
}
-void tfe_arch_transmit(int force, /* FORCE: Delete waiting frames in transmit buffer */
- int onecoll, /* ONECOLL: Terminate after just one collision */
- int inhibit_crc, /* INHIBITCRC: Do not append CRC to the transmission */
- int tx_pad_dis, /* TXPADDIS: Disable padding to 60 Bytes */
+void tfe_arch_transmit(pcap_t * TfePcapFP,
int txlength, /* Frame length */
BYTE *txframe /* Pointer to the frame to be transmitted */
)
{
#ifdef TFE_DEBUG_ARCH
- if(g_fh) fprintf( g_fh, "tfe_arch_transmit() called, with: "
- "force = %s, onecoll = %s, inhibit_crc=%s, tx_pad_dis=%s, txlength=%u\n",
- force ? "TRUE" : "FALSE",
- onecoll ? "TRUE" : "FALSE",
- inhibit_crc ? "TRUE" : "FALSE",
- tx_pad_dis ? "TRUE" : "FALSE",
- txlength
- );
+ if(g_fh) fprintf( g_fh, "tfe_arch_transmit() called, with: txlength=%u\n", txlength);
#endif
#ifdef TFE_DEBUG_PKTDUMP
@@ -543,7 +494,7 @@ void tfe_arch_transmit(int force, /* FORCE: Delete waiting frames in trans
tfe_arch_receive()
This function checks if there was a frame received.
- If so, it returns 1, else 0.
+ If so, it returns its size, else -1.
If there was no frame, none of the parameters is changed!
@@ -564,31 +515,20 @@ void tfe_arch_transmit(int force, /* FORCE: Delete waiting frames in trans
*pbroadcast is set, else cleared.
- if the received frame had a crc error, *pcrc_error is set, else cleared
*/
-int tfe_arch_receive(BYTE *pbuffer , /* where to store a frame */
- int *plen, /* IN: maximum length of frame to copy;
- OUT: length of received frame
- OUT can be bigger than IN if received frame was
- longer than supplied buffer */
- int *phashed, /* set if the dest. address is accepted by the hash filter */
- int *phash_index, /* hash table index if hashed == TRUE */
- int *prx_ok, /* set if good CRC and valid length */
- int *pcorrect_mac, /* set if dest. address is exactly our IA */
- int *pbroadcast, /* set if dest. address is a broadcast address */
- int *pcrc_error /* set if received frame had a CRC error */
+int tfe_arch_receive(pcap_t * TfePcapFP,
+ const int size , /* Size of buffer */
+ BYTE *pbuffer /* where to store a frame */
)
{
- int len;
-
- TFE_PCAP_INTERNAL internal = { static_cast(*plen), pbuffer };
-
+ TFE_PCAP_INTERNAL internal = { static_cast(size), pbuffer, 0 };
#ifdef TFE_DEBUG_ARCH
- if(g_fh) fprintf( g_fh, "tfe_arch_receive() called, with *plen=%u.\n", *plen );
+ if(g_fh) fprintf( g_fh, "tfe_arch_receive() called, with size=%u.\n", size );
#endif
- assert((*plen&1)==0);
+ assert((size & 1)==0);
- len = tfe_arch_receive_frame(&internal);
+ int len = tfe_arch_receive_frame(TfePcapFP, &internal);
if (len!=-1) {
@@ -599,25 +539,10 @@ int tfe_arch_receive(BYTE *pbuffer , /* where to store a frame */
if (len&1)
++len;
- *plen = len;
-
- /* we don't decide if this frame fits the needs;
- * by setting all zero, we let tfe.c do the work
- * for us
- */
- *phashed =
- *phash_index =
- *pbroadcast =
- *pcorrect_mac =
- *pcrc_error = 0;
-
- /* this frame has been received correctly */
- *prx_ok = 1;
-
- return 1;
+ return len;
}
- return 0;
+ return -1;
}
//#endif /* #ifdef HAVE_TFE */
diff --git a/source/Tfe/tfearch.h b/source/Tfe/tfearch.h
index a7b5be49..2a5daf5c 100644
--- a/source/Tfe/tfearch.h
+++ b/source/Tfe/tfearch.h
@@ -31,14 +31,18 @@
#include "../CommonVICE/types.h"
#include
-extern int tfe_arch_init(void);
-extern void tfe_arch_pre_reset(void);
-extern void tfe_arch_post_reset(void);
-extern int tfe_arch_activate(const std::string & interface_name);
-extern void tfe_arch_deactivate(void);
extern void tfe_arch_set_mac(const BYTE mac[6]);
extern void tfe_arch_set_hashfilter(const DWORD hash_mask[2]);
+/* Flag: Can we even use TFE, or is the hardware not available? */
+extern int tfe_cannot_use;
+
+struct pcap;
+typedef struct pcap pcap_t;
+
+pcap_t * TfePcapOpenAdapter(const std::string & interface_name);
+void TfePcapCloseAdapter(pcap_t * TfePcapFP);
+
extern
void tfe_arch_recv_ctl( int bBroadcast, /* broadcast */
int bIA, /* individual address (IA) */
@@ -52,38 +56,16 @@ extern
void tfe_arch_line_ctl(int bEnableTransmitter, int bEnableReceiver);
extern
-void tfe_arch_transmit(int force, /* FORCE: Delete waiting frames in transmit buffer */
- int onecoll, /* ONECOLL: Terminate after just one collision */
- int inhibit_crc, /* INHIBITCRC: Do not append CRC to the transmission */
- int tx_pad_dis, /* TXPADDIS: Disable padding to 60 Bytes */
+void tfe_arch_transmit(pcap_t * TfePcapFP,
int txlength, /* Frame length */
BYTE *txframe /* Pointer to the frame to be transmitted */
);
extern
-int tfe_arch_receive(BYTE *pbuffer , /* where to store a frame */
- int *plen, /* IN: maximum length of frame to copy;
- OUT: length of received frame
- OUT can be bigger than IN if received frame was
- longer than supplied buffer */
- int *phashed, /* set if the dest. address is accepted by the hash filter */
- int *phash_index, /* hash table index if hashed == TRUE */
- int *prx_ok, /* set if good CRC and valid length */
- int *pcorrect_mac, /* set if dest. address is exactly our IA */
- int *pbroadcast, /* set if dest. address is a broadcast address */
- int *pcrc_error /* set if received frame had a CRC error */
- );
-
-/*
- This is a helper for tfe_receive() to determine if the received frame should be accepted
- according to the settings.
-
- This function is even allowed to be called in tfearch.c from tfe_arch_receive() if
- necessary, which is the reason why its prototype is included here in tfearch.h.
-*/
-extern
-int tfe_should_accept(unsigned char *buffer, int length, int *phashed, int *phash_index,
- int *pcorrect_mac, int *pbroadcast, int *pmulticast);
+int tfe_arch_receive(pcap_t * TfePcapFP,
+ const int size , /* Size of buffer */
+ BYTE *pbuffer /* where to store a frame */
+ );
extern int tfe_arch_enumadapter_open(void);
extern int tfe_arch_enumadapter(char **ppname, char **ppdescription);
diff --git a/source/Uthernet1.cpp b/source/Uthernet1.cpp
new file mode 100644
index 00000000..97ad39d3
--- /dev/null
+++ b/source/Uthernet1.cpp
@@ -0,0 +1,1156 @@
+/*
+ * tfe.h - TFE ("The final ethernet" emulation.
+ *
+ * Written by
+ * Spiro Trikaliotis
+ *
+ * This file is part of VICE, the Versatile Commodore Emulator.
+ * See README for copyright notice.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA.
+ *
+ */
+
+/* Emulate a Uthernet 1 card (adapted from VICE's TFE support) */
+
+#include "StdAfx.h"
+
+#include "Uthernet1.h"
+#include "YamlHelper.h"
+#include "Log.h"
+#include "Memory.h"
+#include "Interface.h"
+#include "Tfe/tfearch.h"
+#include "Tfe/tfesupp.h"
+#include "Tfe/NetworkBackend.h"
+#include "Tfe/PCapBackend.h"
+
+/* Makros for reading and writing the visible TFE register: */
+#define GET_TFE_8( _xxx_ ) \
+ ( assert(_xxx_> 8) & 0xff; \
+ } while (0)
+
+
+/* Makros for reading and writing the PacketPage register: */
+
+#define GET_PP_8( _xxx_ ) \
+ (assert(_xxx_> 8) & 0xFF; \
+ } while (0)
+
+#define SET_PP_32( _xxx_, _val_ ) \
+ do { \
+ assert(_xxx_> 8) & 0xFF; \
+ tfe_packetpage[_xxx_+2] = (_val_>>16) & 0xFF; \
+ tfe_packetpage[_xxx_+3] = (_val_>>24) & 0xFF; \
+ } while (0)
+
+
+/* ------------------------------------------------------------------------- */
+/* debugging functions */
+
+#ifdef TFE_DEBUG_FRAMES
+
+static int TfeDebugMaxFrameLengthToDump = 150;
+
+char *debug_outbuffer(const int length, const unsigned char * const buffer)
+{
+#define MAXLEN_DEBUG 1600
+
+ int i;
+ static char outbuffer[MAXLEN_DEBUG*4+1];
+ char *p = outbuffer;
+
+ assert( TfeDebugMaxFrameLengthToDump <= MAXLEN_DEBUG );
+
+ *p = 0;
+
+ for (i=0; i=length)
+ break;
+
+ sprintf( p, "%02X%c", buffer[i], ((i+1)%16==0)?'*':(((i+1)%8==0)?'-':' '));
+ p+=3;
+ }
+
+ return outbuffer;
+}
+
+#endif
+
+
+#ifdef TFE_DEBUG_DUMP
+
+#define NUMBER_PER_LINE 8
+
+void Uthernet1::tfe_debug_output_general( const char *what, WORD (Uthernet1::*getFunc)(int), int count )
+{
+ int i;
+ char buffer[7+(6*NUMBER_PER_LINE)+2];
+
+ if(g_fh) fprintf(g_fh, "%s contents:", what );
+ for (i=0; i*getFunc)(i+j+j) );
+ p += 6;
+ }
+ *p = 0;
+
+ if(g_fh) fprintf(g_fh, "%s", buffer );
+ }
+}
+
+WORD Uthernet1::tfe_debug_output_io_getFunc( int i )
+{
+ return GET_TFE_16(i);
+}
+
+void Uthernet1::tfe_debug_output_io( void )
+{
+ tfe_debug_output_general( "TFE I/O", &Uthernet1::tfe_debug_output_io_getFunc, TFE_COUNT_IO_REGISTER );
+}
+#define TFE_DEBUG_OUTPUT_IO() tfe_debug_output_io()
+
+WORD Uthernet1::tfe_debug_output_pp_getFunc( int i )
+{
+ return GET_PP_16(i);
+}
+
+void Uthernet1::tfe_debug_output_pp( void )
+{
+ tfe_debug_output_general( "PacketPage", &Uthernet1::tfe_debug_output_pp_getFunc, 0x0160 /* MAX_PACKETPAGE_ARRAY */ );
+}
+#define TFE_DEBUG_OUTPUT_PP() tfe_debug_output_pp()
+
+#define TFE_DEBUG_OUTPUT_REG() \
+ do { TFE_DEBUG_OUTPUT_IO(); TFE_DEBUG_OUTPUT_PP(); } while (0)
+
+#else
+
+ #define TFE_DEBUG_OUTPUT_IO()
+ #define TFE_DEBUG_OUTPUT_PP()
+ #define TFE_DEBUG_OUTPUT_REG()
+
+#endif
+
+Uthernet1::Uthernet1(UINT slot) : Card(CT_Uthernet, slot)
+{
+ if (m_slot != SLOT3) // fixme
+ ThrowErrorInvalidSlot();
+ Init();
+}
+
+void Uthernet1::InitialiseBackend()
+{
+ Destroy();
+ networkBackend = GetFrame().CreateNetworkBackend();
+}
+
+void Uthernet1::Init(void)
+{
+ // Initialise all state member variables
+ // in the same order as the header file to ease maintenance
+ memset( tfe_ia_mac, 0, sizeof(tfe_ia_mac) );
+ memset( tfe_hash_mask, 0, sizeof(tfe_hash_mask) );
+
+ tfe_recv_broadcast = 0;
+ tfe_recv_mac = 0;
+ tfe_recv_multicast = 0;
+ tfe_recv_correct = 0;
+ tfe_recv_promiscuous = 0;
+ tfe_recv_hashfilter = 0;
+
+#ifdef TFE_DEBUG_WARN
+ tfe_started_tx = 0;
+#endif
+
+ /* initialize visible IO register and PacketPage registers */
+ memset( tfe, 0, sizeof(tfe) );
+
+ txcollect_buffer = TFE_PP_ADDR_TX_FRAMELOC;
+ rx_buffer = TFE_PP_ADDR_RXSTATUS;
+
+ memset( tfe_packetpage, 0, sizeof(tfe_packetpage) );
+
+ tfe_packetpage_ptr = 0;
+
+ /* according to page 19 unless stated otherwise */
+ SET_PP_32(TFE_PP_ADDR_PRODUCTID, 0x0700630E ); /* p.41: 0E630007 for Rev. B; reversed order! */
+ SET_PP_16(TFE_PP_ADDR_IOBASE, 0x0300);
+ SET_PP_16(TFE_PP_ADDR_INTNO, 0x0004); /* xxxx xxxx xxxx x100b */
+ SET_PP_16(TFE_PP_ADDR_DMA_CHAN, 0x0003); /* xxxx xxxx xxxx xx11b */
+
+#if 0 /* not needed since all memory is initialized with 0 */
+ SET_PP_16(TFE_PP_ADDR_DMA_SOF, 0x0000);
+ SET_PP_16(TFE_PP_ADDR_DMA_FC, 0x0000); /* x000h */
+ SET_PP_16(TFE_PP_ADDR_RXDMA_BC, 0x0000);
+ SET_PP_32(TFE_PP_ADDR_MEMBASE, 0x0000); /* xxx0 0000h */
+ SET_PP_32(TFE_PP_ADDR_BPROM_BASE, 0x00000000); /* xxx0 0000h */
+ SET_PP_32(TFE_PP_ADDR_BPROM_MASK, 0x00000000); /* xxx0 0000h */
+
+ SET_PP_16(TFE_PP_ADDR_SE_ISQ, 0x0000); /* p. 51 */
+#endif
+
+ /* according to descriptions of the registers, see definitions of
+ TFE_PP_ADDR_CC_... and TFE_PP_ADDR_SE_... above! */
+
+ SET_PP_16(TFE_PP_ADDR_CC_RXCFG, 0x0003);
+ SET_PP_16(TFE_PP_ADDR_CC_RXCTL, 0x0005);
+ SET_PP_16(TFE_PP_ADDR_CC_TXCFG, 0x0007);
+ SET_PP_16(TFE_PP_ADDR_CC_TXCMD, 0x0009);
+ SET_PP_16(TFE_PP_ADDR_CC_BUFCFG, 0x000B);
+ SET_PP_16(TFE_PP_ADDR_CC_LINECTL, 0x0013);
+ SET_PP_16(TFE_PP_ADDR_CC_SELFCTL, 0x0015);
+ SET_PP_16(TFE_PP_ADDR_CC_BUSCTL, 0x0017);
+ SET_PP_16(TFE_PP_ADDR_CC_TESTCTL, 0x0019);
+
+ SET_PP_16(TFE_PP_ADDR_SE_ISQ, 0x0000);
+ SET_PP_16(TFE_PP_ADDR_SE_RXEVENT, 0x0004);
+ SET_PP_16(TFE_PP_ADDR_SE_TXEVENT, 0x0008);
+ SET_PP_16(TFE_PP_ADDR_SE_BUFEVENT, 0x000C);
+ SET_PP_16(TFE_PP_ADDR_SE_RXMISS, 0x0010);
+ SET_PP_16(TFE_PP_ADDR_SE_TXCOL, 0x0012);
+ SET_PP_16(TFE_PP_ADDR_SE_LINEST, 0x0014);
+ SET_PP_16(TFE_PP_ADDR_SE_SELFST, 0x0016);
+ SET_PP_16(TFE_PP_ADDR_SE_BUSST, 0x0018);
+ SET_PP_16(TFE_PP_ADDR_SE_TDR, 0x001C);
+
+ TFE_DEBUG_OUTPUT_REG();
+}
+
+
+void Uthernet1::tfe_sideeffects_write_pp_on_txframe(WORD ppaddress)
+{
+ if (ppaddress==TFE_PP_ADDR_TX_FRAMELOC+GET_PP_16(TFE_PP_ADDR_TXLENGTH)-1) {
+
+ /* we have collected the whole frame, now start transmission */
+ WORD txcmd = GET_PP_16(TFE_PP_ADDR_TXCMD);
+ WORD txlen = GET_PP_16(TFE_PP_ADDR_TXLENGTH);
+ WORD busst = GET_PP_16(TFE_PP_ADDR_SE_BUSST);
+
+ if ( (txlen>MAX_TXLENGTH)
+ || ((txlen>MAX_TXLENGTH-4) && (!(txcmd&0x1000)))
+ || (txlenMAX_TXLENGTH)
+ || ((txlength>MAX_TXLENGTH-4) && (!(txcommand&0x1000)))
+ ) {
+ /* txlength too big, mark an error */
+ SET_PP_16(TFE_PP_ADDR_SE_BUSST, (GET_PP_16(TFE_PP_ADDR_SE_BUSST) | 0x80) & ~0x100);
+ }
+ else {
+ /* all right, signal that we're ready for the next frame */
+ SET_PP_16(TFE_PP_ADDR_SE_BUSST, (GET_PP_16(TFE_PP_ADDR_SE_BUSST) & ~0x80) | 0x100);
+ }
+ }
+ break;
+
+ case TFE_PP_ADDR_LOG_ADDR_FILTER:
+ case TFE_PP_ADDR_LOG_ADDR_FILTER+2:
+ case TFE_PP_ADDR_LOG_ADDR_FILTER+4:
+ case TFE_PP_ADDR_LOG_ADDR_FILTER+6:
+ {
+ unsigned int pos = 8 * (ppaddress - TFE_PP_ADDR_LOG_ADDR_FILTER + oddaddress);
+ DWORD *p = (pos < 32) ? &tfe_hash_mask[0] : &tfe_hash_mask[1];
+
+ *p &= ~(0xFF << pos); /* clear out relevant bits */
+ *p |= GET_PP_8(ppaddress+oddaddress) << pos;
+
+ tfe_arch_set_hashfilter(tfe_hash_mask);
+ }
+ break;
+
+ case TFE_PP_ADDR_MAC_ADDR:
+ case TFE_PP_ADDR_MAC_ADDR+2:
+ case TFE_PP_ADDR_MAC_ADDR+4:
+ /* the MAC address has been changed */
+ tfe_ia_mac[ppaddress-TFE_PP_ADDR_MAC_ADDR+oddaddress] =
+ GET_PP_8(ppaddress+oddaddress);
+ tfe_arch_set_mac(tfe_ia_mac);
+ break;
+ }
+}
+
+/*
+ This is called *before* the relevant octets are read
+*/
+void Uthernet1::tfe_sideeffects_read_pp(WORD ppaddress)
+{
+ assert((ppaddress & 1) == 0);
+
+ switch (ppaddress)
+ {
+ case TFE_PP_ADDR_SE_RXEVENT:
+ /* reading this before all octets of the frame are read
+ performs an "implied skip" */
+ {
+ WORD ret_val = tfe_receive();
+
+ /*
+ RXSTATUS and RXEVENT are the same, except that RXSTATUS buffers
+ the old value while RXEVENT sets a new value whenever it is called
+ */
+ SET_PP_16(TFE_PP_ADDR_RXSTATUS, ret_val);
+ SET_PP_16(TFE_PP_ADDR_SE_RXEVENT, ret_val);
+ }
+
+ break;
+
+ case TFE_PP_ADDR_SE_BUSST:
+ break;
+
+ case TFE_PP_ADDR_TXCMD:
+#ifdef TFE_DEBUG_WARN
+ if(g_fh) fprintf(g_fh, "WARNING! Read write-only register TFE_PP_ADDR_TXCMD: IGNORED\n");
+#endif
+ break;
+
+ case TFE_PP_ADDR_TXLENGTH:
+#ifdef TFE_DEBUG_WARN
+ if(g_fh) fprintf(g_fh, "WARNING! Read write-only register TFE_PP_ADDR_TXLENGTH: IGNORED\n");
+#endif
+ break;
+ }
+}
+
+
+void Uthernet1::tfe_proceed_rx_buffer(int oddaddress) {
+ /*
+ According to the CS8900 spec, the handling is the following:
+ first read H, then L, then H, then L.
+ Now, we're inside the RX frame, now, we always get L then H, until the end is reached.
+
+ even odd
+ TFE_PP_ADDR_RXSTATUS: - proceed 1)
+ TFE_PP_ADDR_RXLENGTH: - proceed
+ TFE_PP_ADDR_RX_FRAMELOC: 2),3) -
+ TFE_PP_ADDR_RX_FRAMELOC+2: proceed -
+ TFE_PP_ADDR_RX_FRAMELOC+4: like TFE_PP_ADDR_RX_FRAMELOC+2
+
+ 1) set status "Inside FRAMELOC" FALSE
+ 2) set status "Inside FRAMELOC" TRUE if it is not already
+ 3) if "Inside FRAMELOC", proceed
+
+ */
+
+ static int inside_frameloc;
+ int proceed = 0;
+
+ if (rx_buffer==TFE_PP_ADDR_RX_FRAMELOC+GET_PP_16(TFE_PP_ADDR_RXLENGTH)) {
+ /* we've read all that is available, go to start again */
+ rx_buffer = TFE_PP_ADDR_RXSTATUS;
+ inside_frameloc = 0;
+ }
+ else {
+ switch (rx_buffer) {
+ case TFE_PP_ADDR_RXSTATUS:
+ if (oddaddress) {
+ proceed = 1;
+ inside_frameloc = 0;
+ }
+ break;
+
+ case TFE_PP_ADDR_RXLENGTH:
+ if (oddaddress) {
+ proceed = 1;
+ }
+ break;
+
+ case TFE_PP_ADDR_RX_FRAMELOC:
+ if (oddaddress==0) {
+ if (inside_frameloc) {
+ proceed = 1;
+ }
+ else {
+ inside_frameloc = 1;
+ }
+ }
+ break;
+
+ default:
+ proceed = (oddaddress==0) ? 1 : 0;
+ break;
+ }
+ }
+
+ if (proceed) {
+ SET_TFE_16(TFE_ADDR_RXTXDATA, GET_PP_16(rx_buffer));
+ rx_buffer += 2;
+ }
+}
+
+
+BYTE REGPARM1 Uthernet1::tfe_read(WORD ioaddress)
+{
+ BYTE retval;
+
+ assert( ioaddress < TFE_COUNT_IO_REGISTER);
+
+ switch (ioaddress) {
+
+ case TFE_ADDR_TXCMD:
+ case TFE_ADDR_TXCMD+1:
+ case TFE_ADDR_TXLENGTH:
+ case TFE_ADDR_TXLENGTH+1:
+#ifdef TFE_DEBUG_WARN
+ if(g_fh) fprintf(g_fh, "WARNING! Reading write-only TFE register $%02X!\n", ioaddress);
+#endif
+ /* @SRT TODO: Verify with reality */
+ retval = GET_TFE_8(ioaddress);
+ break;
+
+ case TFE_ADDR_RXTXDATA2:
+ case TFE_ADDR_RXTXDATA2+1:
+ case TFE_ADDR_PP_DATA2:
+ case TFE_ADDR_PP_DATA2+1:
+#ifdef TFE_DEBUG_WARN
+ if(g_fh) fprintf(g_fh, "WARNING! Reading not supported TFE register $%02X!\n", ioaddress);
+#endif
+ /* @SRT TODO */
+ retval = GET_TFE_8(ioaddress);
+ break;
+
+ case TFE_ADDR_PP_DATA:
+ case TFE_ADDR_PP_DATA+1:
+ /* make sure the TFE register have the correct content */
+ {
+ WORD ppaddress = tfe_packetpage_ptr & (MAX_PACKETPAGE_ARRAY-1);
+
+ /* perform side-effects the read may perform */
+ tfe_sideeffects_read_pp( ppaddress );
+
+ /* [3] make sure the data matches the real value - [1] assumes this! */
+ SET_TFE_16( TFE_ADDR_PP_DATA, GET_PP_16(ppaddress) );
+ }
+
+
+#ifdef TFE_DEBUG_LOAD
+ if(g_fh) fprintf(g_fh, "reading PP Ptr: $%04X => $%04X.",
+ tfe_packetpage_ptr, GET_PP_16(tfe_packetpage_ptr) );
+#endif
+
+ retval = GET_TFE_8(ioaddress);
+ break;
+
+ case TFE_ADDR_INTSTQUEUE:
+ case TFE_ADDR_INTSTQUEUE+1:
+ SET_TFE_16( TFE_ADDR_INTSTQUEUE, GET_PP_16(0x0120) );
+ retval = GET_TFE_8(ioaddress);
+ break;
+
+ case TFE_ADDR_RXTXDATA:
+ case TFE_ADDR_RXTXDATA+1:
+ /* we're trying to read a new 16 bit word, get it from the
+ receive buffer
+ */
+ tfe_proceed_rx_buffer(ioaddress & 0x01);
+ retval = GET_TFE_8(ioaddress);
+ break;
+
+ default:
+ retval = GET_TFE_8(ioaddress);
+ break;
+ };
+
+#ifdef TFE_DEBUG_LOAD
+ if(g_fh) fprintf(g_fh, "read [$%02X] => $%02X.", ioaddress, retval);
+#endif
+ return retval;
+}
+
+void REGPARM2 Uthernet1::tfe_store(WORD ioaddress, BYTE byte)
+{
+ assert( ioaddress < TFE_COUNT_IO_REGISTER);
+
+ switch (ioaddress)
+ {
+ case TFE_ADDR_RXTXDATA:
+ case TFE_ADDR_RXTXDATA+1:
+ SET_PP_8(txcollect_buffer, byte);
+ tfe_sideeffects_write_pp_on_txframe(txcollect_buffer++);
+ break;
+
+ case TFE_ADDR_INTSTQUEUE:
+ case TFE_ADDR_INTSTQUEUE+1:
+#ifdef TFE_DEBUG_WARN
+ if(g_fh) fprintf(g_fh, "WARNING! Writing read-only TFE register $%02X!\n", ioaddress);
+#endif
+ /* @SRT TODO: Verify with reality */
+ /* do nothing */
+ return;
+
+ case TFE_ADDR_RXTXDATA2:
+ case TFE_ADDR_RXTXDATA2+1:
+ case TFE_ADDR_PP_DATA2:
+ case TFE_ADDR_PP_DATA2+1:
+#ifdef TFE_DEBUG_WARN
+ if(g_fh) fprintf(g_fh, "WARNING! Writing not supported TFE register $%02X!\n", ioaddress);
+#endif
+ /* do nothing */
+ return;
+
+ case TFE_ADDR_TXCMD:
+ case TFE_ADDR_TXCMD+1:
+ SET_TFE_8(ioaddress, byte);
+ SET_PP_8((ioaddress-TFE_ADDR_TXCMD)+TFE_PP_ADDR_TXCMD, byte); /* perform the mapping to PP+0144 */
+ tfe_sideeffects_write_pp(TFE_PP_ADDR_TXCMD, ioaddress-TFE_ADDR_TXCMD);
+ break;
+
+ case TFE_ADDR_TXLENGTH:
+ case TFE_ADDR_TXLENGTH+1:
+
+ SET_TFE_8(ioaddress, byte);
+ SET_PP_8((ioaddress-TFE_ADDR_TXLENGTH)+TFE_PP_ADDR_TXLENGTH, byte ); /* perform the mapping to PP+0144 */
+
+ tfe_sideeffects_write_pp(TFE_PP_ADDR_TXLENGTH, ioaddress-TFE_ADDR_TXLENGTH);
+ break;
+
+/*
+#define TFE_ADDR_TXCMD 0x04 * -W Maps to PP+0144 *
+#define TFE_ADDR_TXLENGTH 0x06 * -W Maps to PP+0146 *
+#define TFE_ADDR_INTSTQUEUE 0x08 * R- Interrupt status queue, maps to PP + 0120 *
+*/
+ case TFE_ADDR_PP_DATA:
+ case TFE_ADDR_PP_DATA+1:
+
+ /* [2] make sure the data matches the real value - [1] assumes this! */
+ SET_TFE_16(TFE_ADDR_PP_DATA, GET_PP_16(tfe_packetpage_ptr));
+ /* FALL THROUGH */
+
+ default:
+ SET_TFE_8(ioaddress, byte);
+ }
+
+#ifdef TFE_DEBUG_STORE
+ if(g_fh) fprintf(g_fh, "store [$%02X] <= $%02X.", ioaddress, (int)byte);
+#endif
+
+ /* now check if we have to do any side-effects */
+ switch (ioaddress)
+ {
+ case TFE_ADDR_PP_PTR:
+ case TFE_ADDR_PP_PTR+1:
+ tfe_packetpage_ptr = GET_TFE_16(TFE_ADDR_PP_PTR);
+
+#ifdef TFE_DEBUG_STORE
+ if(g_fh) fprintf(g_fh, "set PP Ptr to $%04X.", tfe_packetpage_ptr);
+#endif
+
+ if ((tfe_packetpage_ptr & 1) != 0) {
+
+#ifdef TFE_DEBUG_WARN
+ if(g_fh) fprintf(g_fh,
+ "WARNING! PacketPage register set to odd address $%04X (not allowed!)\n",
+ tfe_packetpage_ptr );
+#endif /* #ifdef TFE_DEBUG_WARN */
+
+ /* "correct" the address to the next lower address
+ REMARK: I don't know how a real cs8900a will behave in this case,
+ since it is not allowed. Nevertheless, this "correction"
+ prevents assert()s to fail.
+ */
+ tfe_packetpage_ptr -= 1;
+ }
+
+ /*
+ [1] The TFE_ADDR_PP_DATA does not need to be modified here,
+ since it will be modified just before a read or store operation
+ is to be performed.
+ See [2] and [3]
+ */
+ break;
+
+ case TFE_ADDR_PP_DATA:
+ case TFE_ADDR_PP_DATA+1:
+
+ {
+ WORD ppaddress = tfe_packetpage_ptr & (MAX_PACKETPAGE_ARRAY-1);
+
+#ifdef TFE_DEBUG_STORE
+ if(g_fh) fprintf(g_fh, "before writing to PP Ptr: $%04X <= $%04X.",
+ ppaddress, GET_PP_16(ppaddress) );
+#endif
+ {
+ WORD tmpIoAddr = ioaddress & ~1; /* word-align the address */
+ SET_PP_16(ppaddress, GET_TFE_16(tmpIoAddr));
+ }
+
+ /* perform side-effects the write may perform */
+ /* the addresses are always aligned on the whole 16-bit-word */
+ tfe_sideeffects_write_pp(ppaddress, ioaddress-TFE_ADDR_PP_DATA);
+
+#ifdef TFE_DEBUG_STORE
+ if(g_fh) fprintf(g_fh, "after writing to PP Ptr: $%04X <= $%04X.",
+ ppaddress, GET_PP_16(ppaddress) );
+#endif
+ }
+ break;
+ }
+
+ TFE_DEBUG_OUTPUT_REG();
+}
+
+
+#ifdef TFE_DEBUG_FRAMES
+ #define return( _x_ ) \
+ { \
+ int retval = _x_; \
+ \
+ if(g_fh) fprintf(g_fh, "%s correct_mac=%u, broadcast=%u, multicast=%u, hashed=%u, hash_index=%u", (retval? "+++ ACCEPTED":"--- rejected"), *pcorrect_mac, *pbroadcast, *pmulticast, *phashed, *phash_index); \
+ \
+ return retval; \
+ }
+
+#endif
+/*
+ This is a helper for tfe_receive() to determine if the received frame should be accepted
+ according to the settings.
+
+ This function is even allowed to be called in tfearch.c from tfe_arch_receive() if
+ necessary, which is the reason why its prototype is included here in tfearch.h.
+*/
+int Uthernet1::tfe_should_accept(unsigned char *buffer, int length, int *phashed, int *phash_index,
+ int *pcorrect_mac, int *pbroadcast, int *pmulticast)
+{
+ int hashreg; /* Hash Register (for hash computation) */
+
+ assert(length>=6); /* we need at least 6 octets since the DA has this length */
+
+ /* first of all, delete any status */
+ *phashed = 0;
+ *phash_index = 0;
+ *pcorrect_mac = 0;
+ *pbroadcast = 0;
+ *pmulticast = 0;
+
+#ifdef TFE_DEBUG_FRAMES
+ if(g_fh) fprintf(g_fh, "tfe_should_accept called with %02X:%02X:%02X:%02X:%02X:%02X, length=%4u and buffer %s",
+ tfe_ia_mac[0], tfe_ia_mac[1], tfe_ia_mac[2],
+ tfe_ia_mac[3], tfe_ia_mac[4], tfe_ia_mac[5],
+ length,
+ debug_outbuffer(length, buffer)
+ );
+#endif
+
+
+ if ( buffer[0]==tfe_ia_mac[0]
+ && buffer[1]==tfe_ia_mac[1]
+ && buffer[2]==tfe_ia_mac[2]
+ && buffer[3]==tfe_ia_mac[3]
+ && buffer[4]==tfe_ia_mac[4]
+ && buffer[5]==tfe_ia_mac[5]
+ ) {
+ /* this is our individual address (IA) */
+
+ *pcorrect_mac = 1;
+
+ /* if we don't want "correct MAC", we might have the chance
+ * that this address fits the hash index
+ */
+ if (tfe_recv_mac || tfe_recv_promiscuous)
+ return(1);
+ }
+
+ if ( buffer[0]==0xFF
+ && buffer[1]==0xFF
+ && buffer[2]==0xFF
+ && buffer[3]==0xFF
+ && buffer[4]==0xFF
+ && buffer[5]==0xFF
+ ) {
+ /* this is a broadcast address */
+ *pbroadcast = 1;
+
+ /* broadcasts cannot be accepted by the hash filter */
+ return((tfe_recv_broadcast || tfe_recv_promiscuous) ? 1 : 0);
+ }
+
+ /* now check if DA passes the hash filter */
+ /* RGJ added (const char *) for AppleWin */
+ hashreg = (~crc32_buf((const char *)buffer,6) >> 26) & 0x3F;
+
+ *phashed = (tfe_hash_mask[(hashreg>=32)?1:0] & (1 << (hashreg&0x1F))) ? 1 : 0;
+ if (*phashed) {
+ *phash_index = hashreg;
+
+ if (buffer[0] & 0x80) {
+ /* we have a multicast address */
+ *pmulticast = 1;
+
+ /* if the multicast address fits into the hash filter,
+ * the hashed bit has to be clear
+ */
+ *phashed = 0;
+
+ return((tfe_recv_multicast || tfe_recv_promiscuous) ? 1 : 0);
+ }
+ return((tfe_recv_hashfilter || tfe_recv_promiscuous) ? 1 : 0);
+ }
+
+ return(tfe_recv_promiscuous ? 1 : 0);
+}
+
+#ifdef TFE_DEBUG_FRAMES
+ #undef return
+#endif
+
+
+WORD Uthernet1::tfe_receive(void)
+{
+ WORD ret_val = 0x0004;
+
+ BYTE buffer[MAX_RXLENGTH];
+
+ int multicast = 0;
+
+ int ready;
+
+#ifdef TFE_DEBUG_FRAMES
+ if(g_fh) fprintf( g_fh, "");
+#endif
+
+ do {
+ ready = 1 ; /* assume we will find a good frame */
+
+ int len = networkBackend->receive(
+ sizeof(buffer), /* size of buffer */
+ buffer /* where to store a frame */
+ );
+
+ if (len > 0) {
+ assert((len&1) == 0); /* length has to be even! */
+
+ int hashed = 0;
+ int hash_index = 0;
+ int broadcast = 0;
+ int correct_mac = 0;
+ int crc_error = 0;
+
+ int rx_ok = 1;
+
+ /* determine ourself the type of frame */
+ if (!tfe_should_accept(buffer,
+ len, &hashed, &hash_index, &correct_mac, &broadcast, &multicast)) {
+
+ /* if we should not accept this frame, just do nothing
+ * now, look for another one */
+ ready = 0; /* try another frame */
+ continue;
+ }
+
+ /* we did receive a frame, return that status */
+ ret_val |= rx_ok ? 0x0100 : 0;
+ ret_val |= multicast ? 0x0200 : 0;
+
+ if (!multicast) {
+ ret_val |= hashed ? 0x0040 : 0;
+ }
+
+ if (hashed && rx_ok) {
+ /* we have the 2nd, special format with hash index: */
+ assert(hash_index < 64);
+ ret_val |= hash_index << 9;
+ }
+ else {
+ /* we have the regular format */
+ ret_val |= correct_mac ? 0x0400 : 0;
+ ret_val |= broadcast ? 0x0800 : 0;
+ ret_val |= crc_error ? 0x1000 : 0;
+ ret_val |= (lenMAX_RXLENGTH) ? 0x4000 : 0;
+ }
+
+ /* discard any octets that are beyond the MAX_RXLEN */
+ if (len>MAX_RXLENGTH) {
+ len = MAX_RXLENGTH;
+ }
+
+ if (rx_ok) {
+ int i;
+
+ /* set relevant parts of the PP area to correct values */
+ SET_PP_16(TFE_PP_ADDR_RXLENGTH, len);
+
+ for (i=0;itransmit(txlength, txframe);
+}
+
+
+// Go via TfeIoCxxx() instead of directly calling IO_Null() to include this specific (slot-3) _DEBUG check
+static BYTE __stdcall TfeIoCxxx (WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles)
+{
+#ifdef _DEBUG
+ if (!IS_APPLE2)
+ {
+ // Derived from UTAIIe:5-28
+ //
+ // INTCXROM SLOTC3ROM TFE floating bus?
+ // 0 0 N (internal ROM)
+ // 0 1 Y
+ // 1 0 N (internal ROM)
+ // 1 1 N (internal ROM)
+ if (! (!MemCheckINTCXROM() && MemCheckSLOTC3ROM()) )
+ {
+ _ASSERT(0); // Card ROM disabled, so IO_Cxxx() returns the internal ROM
+ }
+ }
+#endif
+
+ return IO_Null(programcounter, address, write, value,nCycles);
+}
+
+static BYTE __stdcall TfeIo (WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles)
+{
+ UINT uSlot = ((address & 0xff) >> 4) - 8;
+ Uthernet1* pCard = (Uthernet1*) MemGetSlotParameters(uSlot);
+ BYTE ret = 0;
+
+ if (write)
+ {
+ pCard->tfe_store((WORD)(address & 0x0f), value);
+ }
+ else
+ {
+ ret = pCard->tfe_read((WORD)(address & 0x0f));
+ }
+
+ return ret;
+}
+
+void Uthernet1::InitializeIO(LPBYTE pCxRomPeripheral)
+{
+ InitialiseBackend();
+ if (networkBackend->isValid())
+ {
+ RegisterIoHandler(m_slot, TfeIo, TfeIo, TfeIoCxxx, TfeIoCxxx, this, NULL);
+ }
+}
+
+void Uthernet1::Destroy()
+{
+ networkBackend.reset();
+}
+
+void Uthernet1::Reset(const bool powerCycle)
+{
+ if (powerCycle)
+ {
+ Init();
+ }
+}
+
+void Uthernet1::Update(const ULONG nExecutedCycles)
+{
+ networkBackend->update(nExecutedCycles);
+}
+
+/* ------------------------------------------------------------------------- */
+/* snapshot support functions */
+
+#define SS_YAML_KEY_ENABLED "Enabled"
+#define SS_YAML_KEY_NETWORK_INTERFACE "Network Interface"
+#define SS_YAML_KEY_STARTED_TX "Started Tx"
+#define SS_YAML_KEY_CANNOT_USE "Cannot Use"
+#define SS_YAML_KEY_TXCOLLECT_BUFFER "Tx Collect Buffer"
+#define SS_YAML_KEY_RX_BUFFER "Rx Buffer"
+#define SS_YAML_KEY_CS8900A_REGS "CS8900A Registers"
+#define SS_YAML_KEY_PACKETPAGE_REGS "PacketPage Registers"
+
+static const UINT kUNIT_VERSION = 1;
+
+std::string Uthernet1::GetSnapshotCardName(void)
+{
+ static const std::string name("Uthernet");
+ return name;
+}
+
+void Uthernet1::SaveSnapshot(class YamlSaveHelper& yamlSaveHelper)
+{
+ YamlSaveHelper::Slot slot(yamlSaveHelper, GetSnapshotCardName(), m_slot, kUNIT_VERSION);
+
+ YamlSaveHelper::Label unit(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);
+
+ yamlSaveHelper.SaveBool(SS_YAML_KEY_ENABLED, networkBackend->isValid() ? true : false);
+ yamlSaveHelper.SaveString(SS_YAML_KEY_NETWORK_INTERFACE, PCapBackend::tfe_interface);
+
+ yamlSaveHelper.SaveBool(SS_YAML_KEY_STARTED_TX, tfe_started_tx ? true : false);
+ yamlSaveHelper.SaveBool(SS_YAML_KEY_CANNOT_USE, tfe_cannot_use ? true : false);
+
+ yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_TXCOLLECT_BUFFER, txcollect_buffer);
+ yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_RX_BUFFER, rx_buffer);
+
+ {
+ YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_CS8900A_REGS);
+ yamlSaveHelper.SaveMemory(tfe, TFE_COUNT_IO_REGISTER);
+ }
+
+ {
+ YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_PACKETPAGE_REGS);
+ yamlSaveHelper.SaveMemory(tfe_packetpage, MAX_PACKETPAGE_ARRAY);
+ }
+}
+
+bool Uthernet1::LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT version)
+{
+ if (version < 1 || version > kUNIT_VERSION)
+ ThrowErrorInvalidVersion(version);
+
+ yamlLoadHelper.LoadBool(SS_YAML_KEY_ENABLED); // FIXME: what is the point of this?
+ PCapBackend::tfe_interface = yamlLoadHelper.LoadString(SS_YAML_KEY_NETWORK_INTERFACE);
+
+ tfe_started_tx = yamlLoadHelper.LoadBool(SS_YAML_KEY_STARTED_TX) ? true : false;
+ tfe_cannot_use = yamlLoadHelper.LoadBool(SS_YAML_KEY_CANNOT_USE) ? true : false;
+
+ txcollect_buffer = yamlLoadHelper.LoadUint(SS_YAML_KEY_TXCOLLECT_BUFFER);
+ rx_buffer = yamlLoadHelper.LoadUint(SS_YAML_KEY_RX_BUFFER);
+
+ if (!yamlLoadHelper.GetSubMap(SS_YAML_KEY_CS8900A_REGS))
+ throw std::runtime_error("Card: Expected key: " SS_YAML_KEY_CS8900A_REGS);
+
+ memset(tfe, 0, TFE_COUNT_IO_REGISTER);
+ yamlLoadHelper.LoadMemory(tfe, TFE_COUNT_IO_REGISTER);
+ yamlLoadHelper.PopMap();
+
+ if (!yamlLoadHelper.GetSubMap(SS_YAML_KEY_PACKETPAGE_REGS))
+ throw std::runtime_error("Card: Expected key: " SS_YAML_KEY_PACKETPAGE_REGS);
+
+ memset(tfe_packetpage, 0, MAX_PACKETPAGE_ARRAY);
+ yamlLoadHelper.LoadMemory(tfe_packetpage, MAX_PACKETPAGE_ARRAY);
+ yamlLoadHelper.PopMap();
+
+ // Side effects after PackagePage has been loaded
+
+ tfe_packetpage_ptr = GET_TFE_16(TFE_ADDR_PP_PTR);
+
+ tfe_sideeffects_write_pp(TFE_PP_ADDR_CC_RXCTL, 0); // set the 6 tfe_recv_* vars
+
+ for (UINT i = 0; i < 8; i++)
+ tfe_sideeffects_write_pp((TFE_PP_ADDR_LOG_ADDR_FILTER + i) & ~1, i & 1); // set tfe_hash_mask
+
+ for (UINT i = 0; i < 6; i++)
+ tfe_sideeffects_write_pp((TFE_PP_ADDR_MAC_ADDR + i) & ~1, i & 1); // set tfe_ia_mac
+
+ PCapBackend::tfe_SetRegistryInterface(m_slot, PCapBackend::tfe_interface);
+
+ return true;
+}
diff --git a/source/Uthernet1.h b/source/Uthernet1.h
new file mode 100644
index 00000000..8bf6a5dc
--- /dev/null
+++ b/source/Uthernet1.h
@@ -0,0 +1,212 @@
+#pragma once
+
+#include "Card.h"
+
+/* define this only if VICE should write each and every frame received
+ and send into the VICE log
+ WARNING: The log grows very fast!
+*/
+/** #define TFE_DEBUG_FRAMES **/
+/** #define TFE_DEBUG_DUMP 1 **/
+
+#define TFE_DEBUG_WARN 1 /* this should not be deactivated */
+#define TFE_DEBUG_INIT 1
+/** #define TFE_DEBUG_LOAD 1 **/
+/** #define TFE_DEBUG_STORE 1 **/
+
+#define TFE_COUNT_IO_REGISTER 0x10 /* we have 16 I/O register */
+#define MAX_PACKETPAGE_ARRAY 0x1000 /* 4 KB */
+
+/* ------------------------------------------------------------------------- */
+/* reading and writing TFE register functions */
+
+/*
+These registers are currently fully or partially supported:
+
+TFE_PP_ADDR_CC_RXCFG 0x0102 * # RW - 4.4.6., p. 52 - 0003 *
+TFE_PP_ADDR_CC_RXCTL 0x0104 * # RW - 4.4.8., p. 54 - 0005 *
+TFE_PP_ADDR_CC_LINECTL 0x0112 * # RW - 4.4.16., p. 62 - 0013 *
+TFE_PP_ADDR_SE_RXEVENT 0x0124 * # R- - 4.4.7., p. 53 - 0004 *
+TFE_PP_ADDR_SE_BUSST 0x0138 * # R- - 4.4.21., p. 67 - 0018 *
+TFE_PP_ADDR_TXCMD 0x0144 * # -W - 4.5., p. 70 - 5.7., p. 98 *
+TFE_PP_ADDR_TXLENGTH 0x0146 * # -W - 4.5., p. 70 - 5.7., p. 98 *
+TFE_PP_ADDR_MAC_ADDR 0x0158 * # RW - 4.6., p. 71 - 5.3., p. 86 *
+ 0x015a
+ 0x015c
+*/
+
+/*
+ RW: RXTXDATA = DE00/DE01
+ RW: RXTXDATA2 = DE02/DE03 (for 32-bit-operation)
+ -W: TXCMD = DE04/DE05 (TxCMD, Transmit Command) mapped to PP + 0144 (Reg. 9, Sec. 4.4, page 46)
+ -W: TXLENGTH = DE06/DE07 (TxLenght, Transmit Length) mapped to PP + 0146
+ R-: INTSTQUEUE = DE08/DE09 (Interrupt Status Queue) mapped to PP + 0120 (ISQ, Sec. 5.1, page 78)
+ RW: PP_PTR = DE0A/DE0B (PacketPage Pointer) (see. page 75p: Read -011.---- ----.----)
+ RW: PP_DATA0 = DE0C/DE0D (PacketPage Data (Port 0))
+ RW: PP_DATA1 = DE0E/DE0F (PacketPage Data (Port 1)) (for 32 bit only)
+*/
+
+#define TFE_ADDR_RXTXDATA 0x00 /* RW */
+#define TFE_ADDR_RXTXDATA2 0x02 /* RW 32 bit only! */
+#define TFE_ADDR_TXCMD 0x04 /* -W Maps to PP+0144 */
+#define TFE_ADDR_TXLENGTH 0x06 /* -W Maps to PP+0146 */
+#define TFE_ADDR_INTSTQUEUE 0x08 /* R- Interrupt status queue, maps to PP + 0120 */
+#define TFE_ADDR_PP_PTR 0x0a /* RW PacketPage Pointer */
+#define TFE_ADDR_PP_DATA 0x0c /* RW PacketPage Data, Port 0 */
+#define TFE_ADDR_PP_DATA2 0x0e /* RW PacketPage Data, Port 1 - 32 bit only */
+
+/* The packetpage register: see p. 39f */
+#define TFE_PP_ADDR_PRODUCTID 0x0000 /* R- - 4.3., p. 41 */
+#define TFE_PP_ADDR_IOBASE 0x0020 /* i RW - 4.3., p. 41 - 4.7., p. 72 */
+#define TFE_PP_ADDR_INTNO 0x0022 /* i RW - 3.2., p. 17 - 4.3., p. 41 */
+#define TFE_PP_ADDR_DMA_CHAN 0x0024 /* i RW - 3.2., p. 17 - 4.3., p. 41 */
+#define TFE_PP_ADDR_DMA_SOF 0x0026 /* ? R- - 4.3., p. 41 - 5.4., p. 89 */
+#define TFE_PP_ADDR_DMA_FC 0x0028 /* ? R- - 4.3., p. 41, "Receive DMA" */
+#define TFE_PP_ADDR_RXDMA_BC 0x002a /* ? R- - 4.3., p. 41 - 5.4., p. 89 */
+#define TFE_PP_ADDR_MEMBASE 0x002c /* i RW - 4.3., p. 41 - 4.9., p. 73 */
+#define TFE_PP_ADDR_BPROM_BASE 0x0030 /* i RW - 3.6., p. 24 - 4.3., p. 41 */
+#define TFE_PP_ADDR_BPROM_MASK 0x0034 /* i RW - 3.6., p. 24 - 4.3., p. 41 */
+/* 0x0038 - 0x003F: reserved */
+#define TFE_PP_ADDR_EEPROM_CMD 0x0040 /* i RW - 3.5., p. 23 - 4.3., p. 41 */
+#define TFE_PP_ADDR_EEPROM_DATA 0x0042 /* i RW - 3.5., p. 23 - 4.3., p. 41 */
+/* 0x0044 - 0x004F: reserved */
+#define TFE_PP_ADDR_REC_FRAME_BC 0x0050 /* RW - 4.3., p. 41 - 5.2.9., p. 86 */
+/* 0x0052 - 0x00FF: reserved */
+#define TFE_PP_ADDR_CONF_CTRL 0x0100 /* - RW - 4.4., p. 46; see below */
+#define TFE_PP_ADDR_STATUS_EVENT 0x0120 /* - R- - 4.4., p. 46; see below */
+/* 0x0140 - 0x0143: reserved */
+#define TFE_PP_ADDR_TXCMD 0x0144 /* # -W - 4.5., p. 70 - 5.7., p. 98 */
+#define TFE_PP_ADDR_TXLENGTH 0x0146 /* # -W - 4.5., p. 70 - 5.7., p. 98 */
+/* 0x0148 - 0x014F: reserved */
+#define TFE_PP_ADDR_LOG_ADDR_FILTER 0x0150 /* RW - 4.6., p. 71 - 5.3., p. 86 */
+#define TFE_PP_ADDR_MAC_ADDR 0x0158 /* # RW - 4.6., p. 71 - 5.3., p. 86 */
+/* 0x015E - 0x03FF: reserved */
+#define TFE_PP_ADDR_RXSTATUS 0x0400 /* R- - 4.7., p. 72 - 5.2., p. 78 */
+#define TFE_PP_ADDR_RXLENGTH 0x0402 /* R- - 4.7., p. 72 - 5.2., p. 78 */
+#define TFE_PP_ADDR_RX_FRAMELOC 0x0404 /* R- - 4.7., p. 72 - 5.2., p. 78 */
+/* here, the received frame is stored */
+#define TFE_PP_ADDR_TX_FRAMELOC 0x0A00 /* -W - 4.7., p. 72 - 5.7., p. 98 */
+/* here, the frame to transmit is stored */
+#define TFE_PP_ADDR_END 0x1000 /* memory to TFE_PP_ADDR_END-1 is used */
+
+
+/* TFE_PP_ADDR_CONF_CTRL is subdivided: */
+#define TFE_PP_ADDR_CC_RXCFG 0x0102 /* # RW - 4.4.6., p. 52 - 0003 */
+#define TFE_PP_ADDR_CC_RXCTL 0x0104 /* # RW - 4.4.8., p. 54 - 0005 */
+#define TFE_PP_ADDR_CC_TXCFG 0x0106 /* RW - 4.4.9., p. 55 - 0007 */
+#define TFE_PP_ADDR_CC_TXCMD 0x0108 /* R- - 4.4.11., p. 57 - 0009 */
+#define TFE_PP_ADDR_CC_BUFCFG 0x010A /* RW - 4.4.12., p. 58 - 000B */
+#define TFE_PP_ADDR_CC_LINECTL 0x0112 /* # RW - 4.4.16., p. 62 - 0013 */
+#define TFE_PP_ADDR_CC_SELFCTL 0x0114 /* RW - 4.4.18., p. 64 - 0015 */
+#define TFE_PP_ADDR_CC_BUSCTL 0x0116 /* RW - 4.4.20., p. 66 - 0017 */
+#define TFE_PP_ADDR_CC_TESTCTL 0x0118 /* RW - 4.4.22., p. 68 - 0019 */
+
+/* TFE_PP_ADDR_STATUS_EVENT is subdivided: */
+#define TFE_PP_ADDR_SE_ISQ 0x0120 /* R- - 4.4.5., p. 51 - 0000 */
+#define TFE_PP_ADDR_SE_RXEVENT 0x0124 /* # R- - 4.4.7., p. 53 - 0004 */
+#define TFE_PP_ADDR_SE_TXEVENT 0x0128 /* R- - 4.4.10., p. 56 - 0008 */
+#define TFE_PP_ADDR_SE_BUFEVENT 0x012C /* R- - 4.4.13., p. 59 - 000C */
+#define TFE_PP_ADDR_SE_RXMISS 0x0130 /* R- - 4.4.14., p. 60 - 0010 */
+#define TFE_PP_ADDR_SE_TXCOL 0x0132 /* R- - 4.4.15., p. 61 - 0012 */
+#define TFE_PP_ADDR_SE_LINEST 0x0134 /* R- - 4.4.17., p. 63 - 0014 */
+#define TFE_PP_ADDR_SE_SELFST 0x0136 /* R- - 4.4.19., p. 65 - 0016 */
+#define TFE_PP_ADDR_SE_BUSST 0x0138 /* # R- - 4.4.21., p. 67 - 0018 */
+#define TFE_PP_ADDR_SE_TDR 0x013C /* R- - 4.4.23., p. 69 - 001C */
+
+/* ------------------------------------------------------------------------- */
+/* some parameter definitions */
+
+class NetworkBackend;
+
+class Uthernet1 : public Card
+{
+public:
+ Uthernet1(UINT slot);
+
+ virtual void InitializeIO(LPBYTE pCxRomPeripheral);
+ virtual void Init(void);
+ virtual void Reset(const bool powerCycle);
+ virtual void Update(const ULONG nExecutedCycles);
+ virtual void SaveSnapshot(YamlSaveHelper& yamlSaveHelper);
+ virtual bool LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version);
+
+ void Destroy();
+
+ BYTE tfe_read(WORD ioaddress);
+ void tfe_store(WORD ioaddress, BYTE byte);
+
+ static std::string GetSnapshotCardName(void);
+
+private:
+
+ void InitialiseBackend();
+
+ void tfe_sideeffects_write_pp_on_txframe(WORD ppaddress);
+ void tfe_sideeffects_write_pp(WORD ppaddress, int oddaddress);
+ void tfe_sideeffects_read_pp(WORD ppaddress);
+ void tfe_proceed_rx_buffer(int oddaddress);
+
+ WORD tfe_receive(void);
+ int tfe_should_accept(unsigned char *buffer, int length, int *phashed, int *phash_index,
+ int *pcorrect_mac, int *pbroadcast, int *pmulticast);
+
+ // this function is virtually useless
+ // it is only here to keep a record of these unused arguments
+ void tfe_transmit(
+ int force, /* FORCE: Delete waiting frames in transmit buffer */
+ int onecoll, /* ONECOLL: Terminate after just one collision */
+ 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 */
+ uint8_t *txframe /* Pointer to the frame to be transmitted */
+ );
+
+ std::shared_ptr networkBackend;
+
+#ifdef TFE_DEBUG_DUMP
+ void tfe_debug_output_general( const char *what, WORD (Uthernet1::*getFunc)(int), int count );
+ WORD tfe_debug_output_io_getFunc( int i );
+ WORD tfe_debug_output_pp_getFunc( int i );
+ void tfe_debug_output_io( void );
+ void tfe_debug_output_pp( void );
+#endif
+
+ /* status which received packages to accept
+ This is used in tfe_should_accept().
+ */
+ BYTE tfe_ia_mac[6];
+
+ /* remember the value of the hash mask */
+ DWORD tfe_hash_mask[2];
+
+ int tfe_recv_broadcast; /* broadcast */
+ int tfe_recv_mac; /* individual address (IA) */
+ int tfe_recv_multicast; /* multicast if address passes the hash filter */
+ int tfe_recv_correct; /* accept correct frames */
+ int tfe_recv_promiscuous; /* promiscuous mode */
+ int tfe_recv_hashfilter; /* accept if IA passes the hash filter */
+
+#ifdef TFE_DEBUG_WARN
+ /* remember if the TXCMD has been completed before a new one is issued */
+ int tfe_started_tx;
+#endif
+
+ /* TFE registers */
+ /* these are the 8 16-bit-ports for "I/O space configuration"
+ (see 4.10 on page 75 of cs8900a-4.pdf, the cs8900a data sheet)
+
+ REMARK: The TFE operatoes the cs8900a in IO space configuration, as
+ it generates I/OW and I/OR signals.
+ */
+
+ BYTE tfe[TFE_COUNT_IO_REGISTER];
+
+ WORD txcollect_buffer;
+ WORD rx_buffer;
+
+ /* The PacketPage register */
+ /* note: The locations 0 to MAX_PACKETPAGE_ARRAY-1 are handled in this array. */
+
+ BYTE tfe_packetpage[MAX_PACKETPAGE_ARRAY];
+ WORD tfe_packetpage_ptr;
+};
diff --git a/source/Uthernet2.cpp b/source/Uthernet2.cpp
new file mode 100644
index 00000000..cfa2ff09
--- /dev/null
+++ b/source/Uthernet2.cpp
@@ -0,0 +1,1167 @@
+/*
+AppleWin : An Apple //e emulator for Windows
+
+Copyright (C) 2022, Andrea Odetti
+
+AppleWin is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+AppleWin is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with AppleWin; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include
+#include "YamlHelper.h"
+#include "Uthernet2.h"
+#include "Interface.h"
+#include "Tfe/NetworkBackend.h"
+#include "Tfe/PCapBackend.h"
+#include "W5100.h"
+
+#include
+
+// Linux uses EINPROGRESS while Windows returns WSAEWOULDBLOCK
+// when the connect() calls is ongoing
+//
+// in the checks below we allow both in all cases
+// (myErrno == SOCK_EINPROGRESS || myErrno == SOCK_EWOULDBLOCK)
+// this works, bu we could instead define 2 functions and check only the correct one
+#ifdef _MSC_VER
+
+typedef int ssize_t;
+typedef int socklen_t;
+
+#define sock_error() WSAGetLastError()
+// too complicated to call FormatMessage, just print number
+#define ERROR_FMT "d"
+#define STRERROR(x) x
+
+#define SOCK_EAGAIN WSAEWOULDBLOCK
+#define SOCK_EWOULDBLOCK WSAEWOULDBLOCK
+#define SOCK_EINPROGRESS WSAEINPROGRESS
+
+#else
+
+#define sock_error() errno
+
+#define ERROR_FMT "s"
+#define STRERROR(x) strerror(x)
+
+#define SOCK_EAGAIN EAGAIN
+#define SOCK_EWOULDBLOCK EWOULDBLOCK
+#define SOCK_EINPROGRESS EINPROGRESS
+#include
+#include
+#include
+#include
+#include
+#include
+
+#endif
+
+// fix SOCK_NONBLOCK for e.g. macOS
+#ifndef SOCK_NONBLOCK
+// DISCALIMER
+// totally untested, use at your own risk
+#include
+#define SOCK_NONBLOCK O_NONBLOCK
+#endif
+
+// #define U2_LOG_VERBOSE
+// #define U2_LOG_TRAFFIC
+// #define U2_LOG_STATE
+// #define U2_LOG_UNKNOWN
+
+#define MAC_FMT "%02X:%02X:%02X:%02X:%02X:%02X"
+#define MAC_DEST(p) p[0], p[1], p[2], p[3], p[4], p[5]
+#define MAC_SOURCE(p) p[6], p[7], p[8], p[9], p[10], p[11]
+
+#include "Memory.h"
+#include "Log.h"
+
+namespace
+{
+
+ uint16_t readNetworkWord(const uint8_t *address)
+ {
+ const uint16_t network = *reinterpret_cast(address);
+ const uint16_t host = ntohs(network);
+ return host;
+ }
+
+ uint8_t getIByte(const uint16_t value, const size_t shift)
+ {
+ return (value >> shift) & 0xFF;
+ }
+
+ void write8(Socket &socket, std::vector &memory, const uint8_t value)
+ {
+ const uint16_t base = socket.receiveBase;
+ const uint16_t address = base + socket.sn_rx_wr;
+ memory[address] = value;
+ socket.sn_rx_wr = (socket.sn_rx_wr + 1) % socket.receiveSize;
+ ++socket.sn_rx_rsr;
+ }
+
+ // reverses the byte order
+ void write16(Socket &socket, std::vector &memory, const uint16_t value)
+ {
+ write8(socket, memory, getIByte(value, 8)); // high
+ write8(socket, memory, getIByte(value, 0)); // low
+ }
+
+ void writeData(Socket &socket, std::vector &memory, const uint8_t *data, const size_t len)
+ {
+ for (size_t c = 0; c < len; ++c)
+ {
+ write8(socket, memory, data[c]);
+ }
+ }
+
+ // no byte reversal
+ template
+ void writeAny(Socket &socket, std::vector &memory, const T &t)
+ {
+ const uint8_t *data = reinterpret_cast(&t);
+ const uint16_t len = sizeof(T);
+ writeData(socket, memory, data, len);
+ }
+
+ void writeDataMacRaw(Socket &socket, std::vector &memory, const uint8_t *data, const size_t len)
+ {
+ // size includes sizeof(size)
+ const size_t size = len + sizeof(uint16_t);
+ write16(socket, memory, static_cast(size));
+ writeData(socket, memory, data, len);
+ }
+
+ void writeDataForProtocol(Socket &socket, std::vector &memory, const uint8_t *data, const size_t len, const sockaddr_in &source)
+ {
+ if (socket.sn_sr == SN_SR_SOCK_UDP)
+ {
+ // these are already in network order
+ writeAny(socket, memory, source.sin_addr);
+ writeAny(socket, memory, source.sin_port);
+
+ // size does not include sizeof(size)
+ write16(socket, memory, static_cast(len));
+ } // no header for TCP
+
+ writeData(socket, memory, data, len);
+ }
+
+}
+
+Socket::Socket()
+ : sn_sr(SN_SR_CLOSED), myFD(INVALID_SOCKET), myErrno(0)
+{
+}
+
+void Socket::clearFD()
+{
+ if (myFD != INVALID_SOCKET)
+ {
+#ifdef _MSC_VER
+ closesocket(myFD);
+#else
+ close(myFD);
+#endif
+ }
+ myFD = INVALID_SOCKET;
+ sn_sr = SN_SR_CLOSED;
+}
+
+void Socket::setFD(const socket_t fd, const int status)
+{
+ clearFD();
+ myFD = fd;
+ myErrno = 0;
+ sn_sr = status;
+}
+
+Socket::~Socket()
+{
+ clearFD();
+}
+
+void Socket::process()
+{
+ if (myFD != INVALID_SOCKET && sn_sr == SN_SR_SOCK_INIT && (myErrno == SOCK_EINPROGRESS || myErrno == SOCK_EWOULDBLOCK))
+ {
+#ifdef _MSC_VER
+ FD_SET writefds;
+ FD_ZERO(&writefds);
+ FD_SET(myFD, &writefds);
+ const timeval timeout = {0, 0};
+ if (select(0, NULL, &writefds, NULL, &timeout) > 0)
+#else
+ pollfd pfd = {.fd = myFD, .events = POLLOUT};
+ if (poll(&pfd, 1, 0) > 0)
+#endif
+ {
+ int err = 0;
+ socklen_t elen = sizeof err;
+ getsockopt(myFD, SOL_SOCKET, SO_ERROR, reinterpret_cast(&err), &elen);
+
+ if (err == 0)
+ {
+ myErrno = 0;
+ sn_sr = SN_SR_ESTABLISHED;
+#ifdef U2_LOG_STATE
+ LogFileOutput("U2: TCP[]: Connected\n");
+#endif
+ }
+ }
+ }
+}
+
+bool Socket::isThereRoomFor(const size_t len, const size_t header) const
+{
+ const uint16_t rsr = sn_rx_rsr; // already present
+ const uint16_t size = receiveSize; // total size
+
+ return rsr + len + header < size; // "not =": we do not want to fill the buffer.
+}
+
+uint16_t Socket::getFreeRoom() const
+{
+ const uint16_t rsr = sn_rx_rsr; // already present
+ const uint16_t size = receiveSize; // total size
+
+ return size - rsr;
+}
+
+std::string Uthernet2::GetSnapshotCardName()
+{
+ static const std::string name("Uthernet2");
+ return name;
+}
+
+Uthernet2::Uthernet2(UINT slot) : Card(CT_Uthernet2, slot)
+{
+ myNetworkBackend = GetFrame().CreateNetworkBackend();
+ Reset(true);
+}
+
+void Uthernet2::Destroy()
+{
+ myNetworkBackend.reset();
+}
+
+void Uthernet2::setSocketModeRegister(const size_t i, const uint16_t address, const uint8_t value)
+{
+ myMemory[address] = value;
+ const uint8_t protocol = value & SN_MR_PROTO_MASK;
+ switch (protocol)
+ {
+ case SN_MR_CLOSED:
+#ifdef U2_LOG_STATE
+ LogFileOutput("U2: Mode[%" SIZE_T_FMT "]: closed\n", i);
+#endif
+ break;
+ case SN_MR_TCP:
+#ifdef U2_LOG_STATE
+ LogFileOutput("U2: Mode[%" SIZE_T_FMT "]: TCP\n", i);
+#endif
+ break;
+ case SN_MR_UDP:
+#ifdef U2_LOG_STATE
+ LogFileOutput("U2: Mode[%" SIZE_T_FMT "]: UDP\n", i);
+#endif
+ break;
+ case SN_MR_IPRAW:
+#ifdef U2_LOG_STATE
+ LogFileOutput("U2: Mode[%" SIZE_T_FMT "]: IPRAW\n", i);
+#endif
+ break;
+ case SN_MR_MACRAW:
+#ifdef U2_LOG_STATE
+ LogFileOutput("U2: Mode[%" SIZE_T_FMT "]: MACRAW\n", i);
+#endif
+ break;
+#ifdef U2_LOG_UNKNOWN
+ default:
+ LogFileOutput("U2: Unknown protocol: %02x\n", protocol);
+#endif
+ }
+}
+
+void Uthernet2::setTXSizes(const uint16_t address, uint8_t value)
+{
+ myMemory[address] = value;
+ uint16_t base = TX_BASE;
+ const uint16_t end = RX_BASE;
+ for (Socket &socket : mySockets)
+ {
+ socket.transmitBase = base;
+
+ const uint8_t bits = value & 0x03;
+ value >>= 2;
+
+ const uint16_t size = 1 << (10 + bits);
+ base += size;
+
+ if (base > end)
+ {
+ base = end;
+ }
+ socket.transmitSize = base - socket.transmitBase;
+ }
+}
+
+void Uthernet2::setRXSizes(const uint16_t address, uint8_t value)
+{
+ myMemory[address] = value;
+ uint16_t base = RX_BASE;
+ const uint16_t end = MEM_SIZE;
+ for (Socket &socket : mySockets)
+ {
+ socket.receiveBase = base;
+
+ const uint8_t bits = value & 0x03;
+ value >>= 2;
+
+ const uint16_t size = 1 << (10 + bits);
+ base += size;
+
+ if (base > end)
+ {
+ base = end;
+ }
+ socket.receiveSize = base - socket.receiveBase;
+ }
+}
+
+uint16_t Uthernet2::getTXDataSize(const size_t i) const
+{
+ const Socket &socket = mySockets[i];
+ const uint16_t size = socket.transmitSize;
+ const uint16_t mask = size - 1;
+
+ const int sn_tx_rd = readNetworkWord(myMemory.data() + socket.registers + SN_TX_RD0) & mask;
+ const int sn_tx_wr = readNetworkWord(myMemory.data() + socket.registers + SN_TX_WR0) & mask;
+
+ int dataPresent = sn_tx_wr - sn_tx_rd;
+ if (dataPresent < 0)
+ {
+ dataPresent += size;
+ }
+ return dataPresent;
+}
+
+uint8_t Uthernet2::getTXFreeSizeRegister(const size_t i, const size_t shift) const
+{
+ const int size = mySockets[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 Uthernet2::getRXDataSizeRegister(const size_t i, const size_t shift) const
+{
+ const uint16_t rsr = mySockets[i].sn_rx_rsr;
+ const uint8_t reg = getIByte(rsr, shift);
+ return reg;
+}
+
+void Uthernet2::updateRSR(const size_t i)
+{
+ Socket &socket = mySockets[i];
+
+ const int size = socket.receiveSize;
+ const uint16_t mask = size - 1;
+
+ const int sn_rx_rd = readNetworkWord(myMemory.data() + socket.registers + SN_RX_RD0) & mask;
+ const int sn_rx_wr = socket.sn_rx_wr & mask;
+ int dataPresent = sn_rx_wr - sn_rx_rd;
+ if (dataPresent < 0)
+ {
+ dataPresent += size;
+ }
+ // is this logic correct?
+ // here we are re-synchronising the size with the pointers
+ // elsewhere I have seen people updating this value
+ // by the amount of how much 0x28 has moved forward
+ // but then we need to keep track of where it was
+ // the final result should be the same
+#ifdef U2_LOG_TRAFFIC
+ if (socket.sn_rx_rsr != dataPresent)
+ {
+ LogFileOutput("U2: Recv[%" SIZE_T_FMT "]: %d -> %d bytes\n", i, socket.sn_rx_rsr, dataPresent);
+ }
+#endif
+ socket.sn_rx_rsr = dataPresent;
+}
+
+int Uthernet2::receiveForMacAddress(const bool acceptAll, const int size, uint8_t * data)
+{
+ const uint8_t * mac = myMemory.data() + SHAR0;
+
+ // loop until we receive a valid frame, or there is nothing to receive
+ int len;
+ while ((len = myNetworkBackend->receive(size, data)) > 0)
+ {
+ // minimum valid Ethernet frame is actually 64 bytes
+ // 12 is the minimum to ensure valid MAC Address logging later
+ if (len >= 12)
+ {
+ if (acceptAll)
+ {
+ return len;
+ }
+
+ if (data[0] == mac[0] &&
+ data[1] == mac[1] &&
+ data[2] == mac[2] &&
+ data[3] == mac[3] &&
+ data[4] == mac[4] &&
+ data[5] == mac[5])
+ {
+ return len;
+ }
+
+ if (data[0] == 0xFF &&
+ data[1] == 0xFF &&
+ data[2] == 0xFF &&
+ data[3] == 0xFF &&
+ data[4] == 0xFF &&
+ data[5] == 0xFF)
+ {
+ return len;
+ }
+ }
+ // skip this frame and try with another one
+ }
+ // no frames available to process
+ return len;
+}
+
+void Uthernet2::receiveOnePacketMacRaw(const size_t i)
+{
+ Socket &socket = mySockets[i];
+
+ uint8_t buffer[MAX_RXLENGTH];
+
+ const uint8_t mr = myMemory[socket.registers + SN_MR];
+ const bool filterMAC = mr & SN_MR_MF;
+
+ const int len = receiveForMacAddress(!filterMAC, sizeof(buffer), buffer);
+ if (len > 0)
+ {
+ // we know the packet is at least 12 bytes, and logging is ok
+ if (socket.isThereRoomFor(len, sizeof(uint16_t)))
+ {
+ writeDataMacRaw(socket, myMemory, buffer, len);
+#ifdef U2_LOG_TRAFFIC
+ LogFileOutput("U2: Read MACRAW[%" SIZE_T_FMT "]: " MAC_FMT " -> " MAC_FMT ": +%d -> %d bytes\n", i, MAC_SOURCE(buffer), MAC_DEST(buffer),
+ len, socket.sn_rx_rsr);
+#endif
+ }
+ else
+ {
+ // drop it
+#ifdef U2_LOG_TRAFFIC
+ LogFileOutput("U2: Skip MACRAW[%" SIZE_T_FMT "]: %d bytes\n", i, len);
+#endif
+ }
+ }
+}
+
+// UDP & TCP
+void Uthernet2::receiveOnePacketFromSocket(const size_t i)
+{
+ Socket &socket = mySockets[i];
+ if (socket.myFD != INVALID_SOCKET)
+ {
+ const uint16_t freeRoom = socket.getFreeRoom();
+ if (freeRoom > 32) // avoid meaningless reads
+ {
+ std::vector buffer(freeRoom - 1); // do not fill the buffer completely
+ sockaddr_in source = {0};
+ socklen_t len = sizeof(sockaddr_in);
+ const ssize_t data = recvfrom(socket.myFD, reinterpret_cast(buffer.data()), buffer.size(), 0, (struct sockaddr *)&source, &len);
+#ifdef U2_LOG_TRAFFIC
+ const char *proto = socket.sn_sr == SN_SR_SOCK_UDP ? "UDP" : "TCP";
+#endif
+ if (data > 0)
+ {
+ writeDataForProtocol(socket, myMemory, buffer.data(), data, source);
+#ifdef U2_LOG_TRAFFIC
+ LogFileOutput("U2: Read %s[%" SIZE_T_FMT "]: +%" SIZE_T_FMT " -> %d bytes\n", proto, i, data, socket.sn_rx_rsr);
+#endif
+ }
+ else if (data == 0)
+ {
+ // gracefull termination
+ socket.clearFD();
+ }
+ else // data < 0;
+ {
+ const int error = sock_error();
+ if (error != SOCK_EAGAIN && error != SOCK_EWOULDBLOCK)
+ {
+#ifdef U2_LOG_TRAFFIC
+ LogFileOutput("U2: %s[%" SIZE_T_FMT "]: recvfrom error %" ERROR_FMT "\n", proto, i, STRERROR(error));
+#endif
+ socket.clearFD();
+ }
+ }
+ }
+ }
+}
+
+void Uthernet2::receiveOnePacket(const size_t i)
+{
+ const Socket &socket = mySockets[i];
+ switch (socket.sn_sr)
+ {
+ case SN_SR_SOCK_MACRAW:
+ receiveOnePacketMacRaw(i);
+ break;
+ case SN_SR_ESTABLISHED:
+ case SN_SR_SOCK_UDP:
+ receiveOnePacketFromSocket(i);
+ break;
+ case SN_SR_CLOSED:
+ break; // nothing to do
+#ifdef U2_LOG_UNKNOWN
+ default:
+ LogFileOutput("U2: Read[%" SIZE_T_FMT "]: unknown mode: %02x\n", i, socket.sn_sr);
+#endif
+ };
+}
+
+void Uthernet2::sendDataMacRaw(const size_t i, std::vector &packet) const
+{
+#ifdef U2_LOG_TRAFFIC
+ if (packet.size() >= 12)
+ {
+ const uint8_t * data = packet.data();
+ LogFileOutput("U2: Send MACRAW[%" SIZE_T_FMT "]: " MAC_FMT " -> " MAC_FMT ": %" SIZE_T_FMT " bytes\n", i, MAC_SOURCE(data), MAC_DEST(data), packet.size());
+ }
+ else
+ {
+ // this is not a valid Ethernet Frame
+ LogFileOutput("U2: Send MACRAW[%" SIZE_T_FMT "]: XX:XX:XX:XX:XX:XX -> XX:XX:XX:XX:XX:XX: %" SIZE_T_FMT " bytes\n", i, packet.size());
+ }
+#endif
+ myNetworkBackend->transmit(packet.size(), packet.data());
+}
+
+void Uthernet2::sendDataToSocket(const size_t i, std::vector &data)
+{
+ Socket &socket = mySockets[i];
+ if (socket.myFD != INVALID_SOCKET)
+ {
+ sockaddr_in destination = {};
+ destination.sin_family = AF_INET;
+
+ // already in network order
+ // this seems to be ignored for TCP, and so we reuse the same code
+ const uint8_t *dest = myMemory.data() + socket.registers + SN_DIPR0;
+ destination.sin_addr.s_addr = *reinterpret_cast(dest);
+ destination.sin_port = *reinterpret_cast(myMemory.data() + socket.registers + SN_DPORT0);
+
+ const ssize_t res = sendto(socket.myFD, reinterpret_cast(data.data()), data.size(), 0, (const struct sockaddr *)&destination, sizeof(destination));
+#ifdef U2_LOG_TRAFFIC
+ const char *proto = socket.sn_sr == SN_SR_SOCK_UDP ? "UDP" : "TCP";
+ LogFileOutput("U2: Send %s[%" SIZE_T_FMT "]: %" SIZE_T_FMT " of %" SIZE_T_FMT " bytes\n", proto, i, res, data.size());
+#endif
+ if (res < 0)
+ {
+ const int error = sock_error();
+ if (error != SOCK_EAGAIN && error != SOCK_EWOULDBLOCK)
+ {
+#ifdef U2_LOG_TRAFFIC
+ LogFileOutput("U2: %s[%" SIZE_T_FMT "]: sendto error %" ERROR_FMT "\n", proto, i, STRERROR(error));
+#endif
+ socket.clearFD();
+ }
+ }
+ }
+}
+
+void Uthernet2::sendData(const size_t i)
+{
+ const Socket &socket = mySockets[i];
+ const uint16_t size = socket.transmitSize;
+ const uint16_t mask = size - 1;
+
+ const int sn_tx_rr = readNetworkWord(myMemory.data() + socket.registers + SN_TX_RD0) & mask;
+ const int sn_tx_wr = readNetworkWord(myMemory.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 data;
+ if (rr_address < wr_address)
+ {
+ data.assign(myMemory.begin() + rr_address, myMemory.begin() + wr_address);
+ }
+ else
+ {
+ const uint16_t end = base + size;
+ data.assign(myMemory.begin() + rr_address, myMemory.begin() + end);
+ data.insert(data.end(), myMemory.begin() + base, myMemory.begin() + wr_address);
+ }
+
+ // move read pointer to writer
+ myMemory[socket.registers + SN_TX_RD0] = getIByte(sn_tx_wr, 8);
+ myMemory[socket.registers + SN_TX_RD1] = getIByte(sn_tx_wr, 0);
+
+ switch (socket.sn_sr)
+ {
+ case SN_SR_SOCK_MACRAW:
+ sendDataMacRaw(i, data);
+ break;
+ case SN_SR_ESTABLISHED:
+ case SN_SR_SOCK_UDP:
+ sendDataToSocket(i, data);
+ break;
+#ifdef U2_LOG_UNKNOWN
+ default:
+ LogFileOutput("U2: Send[%" SIZE_T_FMT "]: unknown mode: %02x\n", i, socket.sn_sr);
+#endif
+ }
+}
+
+void Uthernet2::resetRXTXBuffers(const size_t i)
+{
+ Socket &socket = mySockets[i];
+ socket.sn_rx_wr = 0x00;
+ socket.sn_rx_rsr = 0x00;
+ myMemory[socket.registers + SN_TX_RD0] = 0x00;
+ myMemory[socket.registers + SN_TX_RD1] = 0x00;
+ myMemory[socket.registers + SN_TX_WR0] = 0x00;
+ myMemory[socket.registers + SN_TX_WR1] = 0x00;
+ myMemory[socket.registers + SN_RX_RD0] = 0x00;
+ myMemory[socket.registers + SN_RX_RD1] = 0x00;
+}
+
+void Uthernet2::openSystemSocket(const size_t i, const int type, const int protocol, const int state)
+{
+ Socket &s = mySockets[i];
+#ifdef _MSC_VER
+ const Socket::socket_t fd = socket(AF_INET, type, protocol);
+#else
+ const Socket::socket_t fd = socket(AF_INET, type | SOCK_NONBLOCK, protocol);
+#endif
+ if (fd == INVALID_SOCKET)
+ {
+#ifdef U2_LOG_STATE
+ const char *proto = state == SN_SR_SOCK_UDP ? "UDP" : "TCP";
+ LogFileOutput("U2: %s[%" SIZE_T_FMT "]: socket error: %" ERROR_FMT "\n", proto, i, STRERROR(sock_error()));
+#endif
+ s.clearFD();
+ }
+ else
+ {
+#ifdef _MSC_VER
+ u_long on = 1;
+ ioctlsocket(fd, FIONBIO, &on);
+#endif
+ s.setFD(fd, state);
+ }
+}
+
+void Uthernet2::openSocket(const size_t i)
+{
+ Socket &socket = mySockets[i];
+ const uint8_t mr = myMemory[socket.registers + SN_MR];
+ const uint8_t protocol = mr & SN_MR_PROTO_MASK;
+ uint8_t &sr = socket.sn_sr;
+ switch (protocol)
+ {
+ case SN_MR_IPRAW:
+ sr = SN_SR_SOCK_IPRAW;
+ break;
+ case SN_MR_MACRAW:
+ sr = SN_SR_SOCK_MACRAW;
+ break;
+ case SN_MR_TCP:
+ openSystemSocket(i, SOCK_STREAM, IPPROTO_TCP, SN_SR_SOCK_INIT);
+ break;
+ case SN_MR_UDP:
+ openSystemSocket(i, SOCK_DGRAM, IPPROTO_UDP, SN_SR_SOCK_UDP);
+ break;
+#ifdef U2_LOG_UNKNOWN
+ default:
+ LogFileOutput("U2: Open[%" SIZE_T_FMT "]: unknown mode: %02x\n", i, mr);
+#endif
+ }
+ resetRXTXBuffers(i); // needed?
+#ifdef U2_LOG_STATE
+ LogFileOutput("U2: Open[%" SIZE_T_FMT "]: SR = %02x\n", i, sr);
+#endif
+}
+
+void Uthernet2::closeSocket(const size_t i)
+{
+ Socket &socket = mySockets[i];
+ socket.clearFD();
+#ifdef U2_LOG_STATE
+ LogFileOutput("U2: Close[%" SIZE_T_FMT "]\n", i);
+#endif
+}
+
+void Uthernet2::connectSocket(const size_t i)
+{
+ Socket &socket = mySockets[i];
+ const uint8_t *dest = myMemory.data() + socket.registers + SN_DIPR0;
+
+ sockaddr_in destination = {};
+ destination.sin_family = AF_INET;
+
+ // already in network order
+ destination.sin_port = *reinterpret_cast(myMemory.data() + socket.registers + SN_DPORT0);
+ destination.sin_addr.s_addr = *reinterpret_cast(dest);
+
+ const int res = connect(socket.myFD, (struct sockaddr *)&destination, sizeof(destination));
+
+ if (res == 0)
+ {
+ socket.sn_sr = SN_SR_ESTABLISHED;
+ socket.myErrno = 0;
+#ifdef U2_LOG_STATE
+ const uint16_t port = readNetworkWord(myMemory.data() + socket.registers + SN_DPORT0);
+ LogFileOutput("U2: TCP[%" SIZE_T_FMT "]: CONNECT to %d.%d.%d.%d:%d\n", i, dest[0], dest[1], dest[2], dest[3], port);
+#endif
+ }
+ else
+ {
+ const int error = sock_error();
+ if (error == SOCK_EINPROGRESS || error == SOCK_EWOULDBLOCK)
+ {
+ socket.myErrno = error;
+ }
+ else
+ {
+#ifdef U2_LOG_STATE
+ LogFileOutput("U2: TCP[%" SIZE_T_FMT "]: connect error: %" ERROR_FMT "\n", i, STRERROR(error));
+#endif
+ }
+ }
+}
+
+void Uthernet2::setCommandRegister(const size_t i, const uint8_t value)
+{
+ switch (value)
+ {
+ case SN_CR_OPEN:
+ openSocket(i);
+ break;
+ case SN_CR_CONNECT:
+ connectSocket(i);
+ break;
+ case SN_CR_CLOSE:
+ case SN_CR_DISCON:
+ closeSocket(i);
+ break;
+ case SN_CR_SEND:
+ sendData(i);
+ break;
+ case SN_CR_RECV:
+ updateRSR(i);
+ break;
+#ifdef U2_LOG_UNKNOWN
+ default:
+ LogFileOutput("U2: Unknown command[%" SIZE_T_FMT "]: %02x\n", i, value);
+#endif
+ }
+}
+
+uint8_t Uthernet2::readSocketRegister(const uint16_t address)
+{
+ const uint16_t i = (address >> 8) - 0x04;
+ const uint16_t loc = address & 0xFF;
+ uint8_t value;
+ switch (loc)
+ {
+ case SN_MR:
+ case SN_CR:
+ value = myMemory[address];
+ break;
+ case SN_SR:
+ value = mySockets[i].sn_sr;
+ break;
+ case SN_TX_FSR0:
+ value = getTXFreeSizeRegister(i, 8);
+ break;
+ case SN_TX_FSR1:
+ value = getTXFreeSizeRegister(i, 0);
+ break;
+ case SN_TX_RD0:
+ case SN_TX_RD1:
+ value = myMemory[address];
+ break;
+ case SN_TX_WR0:
+ case SN_TX_WR1:
+ value = myMemory[address];
+ break;
+ case SN_RX_RSR0:
+ receiveOnePacket(i);
+ value = getRXDataSizeRegister(i, 8);
+ break;
+ case SN_RX_RSR1:
+ receiveOnePacket(i);
+ value = getRXDataSizeRegister(i, 0);
+ break;
+ case SN_RX_RD0:
+ case SN_RX_RD1:
+ value = myMemory[address];
+ break;
+ default:
+#ifdef U2_LOG_UNKNOWN
+ LogFileOutput("U2: Get unknown socket register[%" SIZE_T_FMT "]: %04x\n", i, address);
+#endif
+ value = myMemory[address];
+ break;
+ }
+ return value;
+}
+
+uint8_t Uthernet2::readValueAt(const uint16_t address)
+{
+ uint8_t value;
+ if (address == MR)
+ {
+ value = myModeRegister;
+ }
+ else if (address >= GAR0 && address <= UPORT1)
+ {
+ value = myMemory[address];
+ }
+ else if (address >= S0_BASE && address <= S3_MAX)
+ {
+ value = readSocketRegister(address);
+ }
+ else if (address >= TX_BASE && address <= MEM_MAX)
+ {
+ value = myMemory[address];
+ }
+ else
+ {
+#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 = myMemory[address & MEM_MAX];
+ }
+ return value;
+}
+
+void Uthernet2::autoIncrement()
+{
+ if (myModeRegister & MR_AI)
+ {
+ ++myDataAddress;
+ // Read bottom of Uthernet II page 12
+ // Setting the address to values >= 0x8000 is not really supported
+ switch (myDataAddress)
+ {
+ case RX_BASE:
+ case MEM_SIZE:
+ myDataAddress -= 0x2000;
+ break;
+ }
+ }
+}
+
+uint8_t Uthernet2::readValue()
+{
+ const uint8_t value = readValueAt(myDataAddress);
+ autoIncrement();
+ return value;
+}
+
+void Uthernet2::setIPProtocol(const size_t i, const uint16_t address, const uint8_t value)
+{
+ myMemory[address] = value;
+#ifdef U2_LOG_STATE
+ LogFileOutput("U2: IP PROTO[%" SIZE_T_FMT "] = %d\n", i, value);
+#endif
+}
+
+void Uthernet2::setIPTypeOfService(const size_t i, const uint16_t address, const uint8_t value)
+{
+ myMemory[address] = value;
+#ifdef U2_LOG_STATE
+ LogFileOutput("U2: IP TOS[%" SIZE_T_FMT "] = %d\n", i, value);
+#endif
+}
+
+void Uthernet2::setIPTTL(const size_t i, const uint16_t address, const uint8_t value)
+{
+ myMemory[address] = value;
+#ifdef U2_LOG_STATE
+ LogFileOutput("U2: IP TTL[%" SIZE_T_FMT "] = %d\n", i, value);
+#endif
+}
+
+void Uthernet2::writeSocketRegister(const uint16_t address, const uint8_t value)
+{
+ const uint16_t i = (address >> 8) - 0x04;
+ const uint16_t loc = address & 0xFF;
+ switch (loc)
+ {
+ case SN_MR:
+ setSocketModeRegister(i, address, value);
+ break;
+ case SN_CR:
+ setCommandRegister(i, value);
+ break;
+ case SN_PORT0:
+ case SN_PORT1:
+ case SN_DPORT0:
+ case SN_DPORT1:
+ myMemory[address] = value;
+ break;
+ case SN_DIPR0:
+ case SN_DIPR1:
+ case SN_DIPR2:
+ case SN_DIPR3:
+ myMemory[address] = value;
+ break;
+ case SN_PROTO:
+ setIPProtocol(i, address, value);
+ break;
+ case SN_TOS:
+ setIPTypeOfService(i, address, value);
+ break;
+ case SN_TTL:
+ setIPTTL(i, address, value);
+ break;
+ case SN_TX_WR0:
+ myMemory[address] = value;
+ break;
+ case SN_TX_WR1:
+ myMemory[address] = value;
+ break;
+ case SN_RX_RD0:
+ myMemory[address] = value;
+ break;
+ case SN_RX_RD1:
+ myMemory[address] = value;
+ break;
+#ifdef U2_LOG_UNKNOWN
+ default:
+ LogFileOutput("U2: Set unknown socket register[%" SIZE_T_FMT "]: %04x\n", i, address);
+ break;
+#endif
+ };
+}
+
+void Uthernet2::setModeRegister(const uint16_t address, const uint8_t value)
+{
+ if (value & MR_RST)
+ {
+ Reset(false);
+ }
+ else
+ {
+ myModeRegister = value;
+ }
+}
+
+void Uthernet2::writeCommonRegister(const uint16_t address, const uint8_t value)
+{
+ if (address == MR)
+ {
+ setModeRegister(address, value);
+ }
+ else if (address >= GAR0 && address <= GAR3 ||
+ address >= SUBR0 && address <= SUBR3 ||
+ address >= SHAR0 && address <= SHAR5 ||
+ address >= SIPR0 && address <= SIPR3)
+ {
+ myMemory[address] = value;
+ }
+ else if (address == RMSR)
+ {
+ setRXSizes(address, value);
+ }
+ else if (address == TMSR)
+ {
+ setTXSizes(address, value);
+ }
+#ifdef U2_LOG_UNKNOWN
+ else
+ {
+ LogFileOutput("U2: Set unknown common register: %04x\n", address);
+ }
+#endif
+}
+
+void Uthernet2::writeValueAt(const uint16_t address, const uint8_t value)
+{
+ if (address >= MR && address <= UPORT1)
+ {
+ writeCommonRegister(address, value);
+ }
+ else if (address >= S0_BASE && address <= S3_MAX)
+ {
+ writeSocketRegister(address, value);
+ }
+ else if (address >= TX_BASE && address <= MEM_MAX)
+ {
+ myMemory[address] = value;
+ }
+#ifdef U2_LOG_UNKNOWN
+ else
+ {
+ LogFileOutput("U2: Write to unknown location: %02x to %04x\n", value, address);
+ }
+#endif
+}
+
+void Uthernet2::writeValue(const uint8_t value)
+{
+ writeValueAt(myDataAddress, value);
+ autoIncrement();
+}
+
+void Uthernet2::Reset(const bool powerCycle)
+{
+ LogFileOutput("U2: Uthernet II initialisation\n");
+ myModeRegister = 0;
+
+ if (powerCycle)
+ {
+ // dataAddress is NOT reset, see page 10 of Uthernet II
+ myDataAddress = 0;
+ }
+
+ mySockets.resize(4);
+ myMemory.clear();
+ myMemory.resize(MEM_SIZE, 0);
+
+ for (size_t i = 0; i < mySockets.size(); ++i)
+ {
+ mySockets[i].clearFD();
+ mySockets[i].registers = static_cast(S0_BASE + (i << 8));
+ }
+
+ // initial values
+ myMemory[RTR0] = 0x07;
+ myMemory[RTR1] = 0xD0;
+ setRXSizes(RMSR, 0x55);
+ setTXSizes(TMSR, 0x55);
+}
+
+BYTE Uthernet2::IO_C0(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles)
+{
+ BYTE res = write ? 0 : MemReadFloatingBus(nCycles);
+
+#ifdef U2_LOG_VERBOSE
+ const uint16_t oldAddress = myDataAddress;
+#endif
+
+ const uint8_t loc = address & 0x0F;
+
+ if (write)
+ {
+ switch (loc)
+ {
+ case C0X_MODE_REGISTER:
+ setModeRegister(MR, value);
+ break;
+ case C0X_ADDRESS_HIGH:
+ myDataAddress = (value << 8) | (myDataAddress & 0x00FF);
+ break;
+ case C0X_ADDRESS_LOW:
+ myDataAddress = (value << 0) | (myDataAddress & 0xFF00);
+ break;
+ case C0X_DATA_PORT:
+ writeValue(value);
+ break;
+ }
+ }
+ else
+ {
+ switch (loc)
+ {
+ case C0X_MODE_REGISTER:
+ res = myModeRegister;
+ break;
+ case C0X_ADDRESS_HIGH:
+ res = getIByte(myDataAddress, 8);
+ break;
+ case C0X_ADDRESS_LOW:
+ res = getIByte(myDataAddress, 0);
+ break;
+ case C0X_DATA_PORT:
+ res = readValue();
+ break;
+ }
+ }
+
+#ifdef U2_LOG_VERBOSE
+ const char *mode = write ? "WRITE" : "READ ";
+ const char c = std::isprint(res) ? res : '.';
+ LogFileOutput("U2: %04x: %s %04x[%04x] %02x -> %02x, '%c' (%d -> %d)\n", programcounter, mode, address, oldAddress, value, res, c, value, res);
+#endif
+
+ return res;
+}
+
+BYTE __stdcall u2_C0(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles)
+{
+ UINT uSlot = ((address & 0xff) >> 4) - 8;
+ Uthernet2 *pCard = (Uthernet2 *)MemGetSlotParameters(uSlot);
+ return pCard->IO_C0(programcounter, address, write, value, nCycles);
+}
+
+void Uthernet2::InitializeIO(LPBYTE pCxRomPeripheral)
+{
+ RegisterIoHandler(m_slot, u2_C0, u2_C0, nullptr, nullptr, this, nullptr);
+}
+
+void Uthernet2::Init()
+{
+}
+
+void Uthernet2::Update(const ULONG nExecutedCycles)
+{
+ myNetworkBackend->update(nExecutedCycles);
+ for (Socket &socket : mySockets)
+ {
+ socket.process();
+ }
+}
+
+static const UINT kUNIT_VERSION = 1;
+#define SS_YAML_KEY_ENABLED "Enabled"
+#define SS_YAML_KEY_NETWORK_INTERFACE "Network Interface"
+
+void Uthernet2::SaveSnapshot(YamlSaveHelper &yamlSaveHelper)
+{
+ YamlSaveHelper::Slot slot(yamlSaveHelper, GetSnapshotCardName(), m_slot, kUNIT_VERSION);
+
+ YamlSaveHelper::Label unit(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);
+ yamlSaveHelper.SaveBool(SS_YAML_KEY_ENABLED, myNetworkBackend->isValid() ? true : false);
+ yamlSaveHelper.SaveString(SS_YAML_KEY_NETWORK_INTERFACE, PCapBackend::tfe_interface);
+}
+
+bool Uthernet2::LoadSnapshot(YamlLoadHelper &yamlLoadHelper, UINT version)
+{
+ if (version < 1 || version > kUNIT_VERSION)
+ throw std::runtime_error("Card: wrong version");
+
+ yamlLoadHelper.LoadBool(SS_YAML_KEY_ENABLED); // FIXME: what is the point of this?
+ PCapBackend::tfe_interface = yamlLoadHelper.LoadString(SS_YAML_KEY_NETWORK_INTERFACE);
+
+ PCapBackend::tfe_SetRegistryInterface(m_slot, PCapBackend::tfe_interface);
+
+ return true;
+}
diff --git a/source/Uthernet2.h b/source/Uthernet2.h
new file mode 100644
index 00000000..5cf37b64
--- /dev/null
+++ b/source/Uthernet2.h
@@ -0,0 +1,115 @@
+#pragma once
+
+#include "Card.h"
+
+#include
+
+class NetworkBackend;
+
+struct Socket
+{
+#ifdef _MSC_VER
+ typedef SOCKET socket_t;
+#else
+ typedef int socket_t;
+#endif
+
+ uint16_t transmitBase;
+ uint16_t transmitSize;
+ uint16_t receiveBase;
+ uint16_t receiveSize;
+ uint16_t registers;
+
+ uint16_t sn_rx_wr;
+ uint16_t sn_rx_rsr;
+
+ uint8_t sn_sr;
+
+ socket_t myFD;
+ int myErrno;
+
+ void clearFD();
+ void setFD(const socket_t fd, const int status);
+ void process();
+
+ bool isThereRoomFor(const size_t len, const size_t header) const;
+ uint16_t getFreeRoom() const;
+
+ Socket();
+
+ ~Socket();
+};
+
+/*
+* Documentation from
+* http://dserver.macgui.com/Uthernet%20II%20manual%2017%20Nov%2018.pdf
+* https://www.wiznet.io/wp-content/uploads/wiznethome/Chip/W5100/Document/W5100_DS_V128E.pdf
+*/
+
+class Uthernet2 : public Card
+{
+public:
+ static std::string GetSnapshotCardName();
+
+ Uthernet2(UINT slot);
+
+ void Destroy();
+
+ virtual void InitializeIO(LPBYTE pCxRomPeripheral);
+ virtual void Init();
+ virtual void Reset(const bool powerCycle);
+ virtual void Update(const ULONG nExecutedCycles);
+ virtual void SaveSnapshot(YamlSaveHelper &yamlSaveHelper);
+ virtual bool LoadSnapshot(YamlLoadHelper &yamlLoadHelper, UINT version);
+
+ BYTE IO_C0(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles);
+
+private:
+ std::vector myMemory;
+ std::vector mySockets;
+ uint8_t myModeRegister;
+ uint16_t myDataAddress;
+ std::shared_ptr myNetworkBackend;
+
+ void setSocketModeRegister(const size_t i, const uint16_t address, const uint8_t value);
+ void setTXSizes(const uint16_t address, uint8_t value);
+ void setRXSizes(const uint16_t address, uint8_t value);
+ uint16_t getTXDataSize(const size_t i) const;
+ uint8_t getTXFreeSizeRegister(const size_t i, const size_t shift) const;
+ uint8_t getRXDataSizeRegister(const size_t i, const size_t shift) const;
+
+ void receiveOnePacketMacRaw(const size_t i);
+ void receiveOnePacketFromSocket(const size_t i);
+ void receiveOnePacket(const size_t i);
+ int receiveForMacAddress(const bool acceptAll, const int size, uint8_t * data);
+
+ void sendDataMacRaw(const size_t i, std::vector &data) const;
+ void sendDataToSocket(const size_t i, std::vector &data);
+ void sendData(const size_t i);
+
+ void resetRXTXBuffers(const size_t i);
+ void updateRSR(const size_t i);
+
+ void openSystemSocket(const size_t i, const int type, const int protocol, const int state);
+ void openSocket(const size_t i);
+ void closeSocket(const size_t i);
+ void connectSocket(const size_t i);
+
+ void setCommandRegister(const size_t i, const uint8_t value);
+
+ uint8_t readSocketRegister(const uint16_t address);
+ uint8_t readValueAt(const uint16_t address);
+
+ void autoIncrement();
+ uint8_t readValue();
+
+ void setIPProtocol(const size_t i, const uint16_t address, const uint8_t value);
+ void setIPTypeOfService(const size_t i, const uint16_t address, const uint8_t value);
+ void setIPTTL(const size_t i, const uint16_t address, const uint8_t value);
+ void writeSocketRegister(const uint16_t address, const uint8_t value);
+
+ void setModeRegister(const uint16_t address, const uint8_t value);
+ void writeCommonRegister(const uint16_t address, const uint8_t value);
+ void writeValueAt(const uint16_t address, const uint8_t value);
+ void writeValue(const uint8_t value);
+};
diff --git a/source/Utilities.cpp b/source/Utilities.cpp
index 4a8f883c..88ede893 100644
--- a/source/Utilities.cpp
+++ b/source/Utilities.cpp
@@ -49,7 +49,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "SoundCore.h"
#include "Configuration/IPropertySheet.h"
-#include "Tfe/tfe.h"
+#include "Tfe/PCapBackend.h"
#ifdef USE_SPEECH_API
#include "Speech.h"
@@ -253,39 +253,31 @@ void LoadConfiguration(bool loadImages)
if (RegLoadValue(regSection.c_str(), REGVALUE_CARD_TYPE, TRUE, &dwTmp))
{
- GetCardMgr().Insert(slot, (SS_CARDTYPE)dwTmp, false);
-
if (slot == SLOT3)
{
- tfe_enabled = 0;
-
- if ((SS_CARDTYPE)dwTmp == CT_Uthernet) // TODO: move this to when UthernetCard object is instantiated
+ // this must happen before the card is instantitated
+ // TODO move to the card
+ if ((SS_CARDTYPE)dwTmp == CT_Uthernet || (SS_CARDTYPE)dwTmp == CT_Uthernet2) // TODO: move this to when UthernetCard object is instantiated
{
std::string regSection = RegGetConfigSlotSection(slot);
if (RegLoadString(regSection.c_str(), REGVALUE_UTHERNET_INTERFACE, TRUE, szFilename, MAX_PATH, TEXT("")))
- update_tfe_interface(szFilename);
-
- tfe_init(true);
+ PCapBackend::tfe_interface = szFilename;
}
}
+
+ GetCardMgr().Insert(slot, (SS_CARDTYPE)dwTmp, false);
}
else // legacy (AppleWin 1.30.3 or earlier)
{
if (slot == SLOT3)
{
- tfe_enabled = 0;
+ // TODO: move this to when UthernetCard object is instantiated
+ RegLoadString(TEXT(REG_CONFIG), TEXT(REGVALUE_UTHERNET_INTERFACE), 1, szFilename, MAX_PATH, TEXT(""));
+ PCapBackend::tfe_interface = szFilename;
DWORD tfeEnabled;
REGLOAD_DEFAULT(TEXT(REGVALUE_UTHERNET_ACTIVE), &tfeEnabled, 0);
-
- GetCardMgr().Insert(SLOT3, get_tfe_enabled() ? CT_Uthernet : CT_Empty);
-
- // TODO: move this to when UthernetCard object is instantiated
- RegLoadString(TEXT(REG_CONFIG), TEXT(REGVALUE_UTHERNET_INTERFACE), 1, szFilename, MAX_PATH, TEXT(""));
- update_tfe_interface(szFilename);
-
- if (tfeEnabled)
- tfe_init(true);
+ GetCardMgr().Insert(SLOT3, tfeEnabled ? CT_Uthernet : CT_Empty);
}
else if (slot == SLOT4 && REGLOAD(TEXT(REGVALUE_SLOT4), &dwTmp))
GetCardMgr().Insert(SLOT4, (SS_CARDTYPE)dwTmp);
@@ -545,6 +537,10 @@ void ResetMachineState()
GetCardMgr().GetRef(SLOT7).Reset(true);
if (GetCardMgr().QuerySlot(SLOT3) == CT_VidHD)
GetCardMgr().GetRef(SLOT3).Reset(true);
+ if (GetCardMgr().QuerySlot(SLOT3) == CT_Uthernet)
+ GetCardMgr().GetRef(SLOT3).Reset(true);
+ if (GetCardMgr().QuerySlot(SLOT3) == CT_Uthernet2)
+ GetCardMgr().GetRef(SLOT3).Reset(true);
g_bFullSpeed = 0; // Might've hit reset in middle of InternalCpuExecute() - so beep may get (partially) muted
MemReset(); // calls CpuInitialize(), CNoSlotClock.Reset()
@@ -602,6 +598,10 @@ void CtrlReset()
GetCardMgr().GetRef(SLOT7).Reset(false);
if (GetCardMgr().QuerySlot(SLOT3) == CT_VidHD)
GetCardMgr().GetRef(SLOT3).Reset(false);
+ if (GetCardMgr().QuerySlot(SLOT3) == CT_Uthernet)
+ GetCardMgr().GetRef(SLOT3).Reset(false);
+ if (GetCardMgr().QuerySlot(SLOT3) == CT_Uthernet2)
+ GetCardMgr().GetRef(SLOT3).Reset(false);
KeybReset();
if (GetCardMgr().IsSSCInstalled())
GetCardMgr().GetSSC()->CommReset();
diff --git a/source/W5100.h b/source/W5100.h
new file mode 100644
index 00000000..1d0205cf
--- /dev/null
+++ b/source/W5100.h
@@ -0,0 +1,86 @@
+#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_MF 0x40 // 6
+#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_LISTENT 0x02
+#define SN_CR_CONNECT 0x04
+#define SN_CR_DISCON 0x08
+#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_PORT0 0x04
+#define SN_PORT1 0x05
+#define SN_DIPR0 0x0C
+#define SN_DIPR1 0x0D
+#define SN_DIPR2 0x0E
+#define SN_DIPR3 0x0F
+#define SN_DPORT0 0x10
+#define SN_DPORT1 0x11
+#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_CLOSED 0x00
+#define SN_SR_SOCK_INIT 0x13
+#define SN_SR_ESTABLISHED 0x17
+#define SN_SR_SOCK_UDP 0x22
+#define SN_SR_SOCK_IPRAW 0x32
+#define SN_SR_SOCK_MACRAW 0x42
diff --git a/source/Windows/AppleWin.cpp b/source/Windows/AppleWin.cpp
index ee5aabda..9b278591 100644
--- a/source/Windows/AppleWin.cpp
+++ b/source/Windows/AppleWin.cpp
@@ -55,7 +55,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "Configuration/About.h"
#include "Configuration/PropertySheet.h"
-#include "Tfe/tfe.h"
//=================================================
@@ -932,9 +931,6 @@ static void Shutdown(void)
CoUninitialize();
LogFileOutput("Exit: CoUninitialize()\n");
- tfe_shutdown();
- LogFileOutput("Exit: tfe_shutdown()\n");
-
LogDone();
RiffFinishWriteFile();
diff --git a/source/Windows/Win32Frame.cpp b/source/Windows/Win32Frame.cpp
index 275e6125..ed3e5800 100644
--- a/source/Windows/Win32Frame.cpp
+++ b/source/Windows/Win32Frame.cpp
@@ -9,6 +9,7 @@
#include "Memory.h"
#include "CardManager.h"
#include "Debugger/Debug.h"
+#include "Tfe/PCapBackend.h"
#include "../resource/resource.h"
// Win32Frame methods are implemented in AppleWin, WinFrame and WinVideo.
@@ -623,3 +624,9 @@ std::string Win32Frame::Video_GetScreenShotFolder() const
// save in current folder
return std::string();
}
+
+std::shared_ptr Win32Frame::CreateNetworkBackend()
+{
+ std::shared_ptr backend(new PCapBackend(PCapBackend::tfe_interface));
+ return backend;
+}
diff --git a/source/Windows/Win32Frame.h b/source/Windows/Win32Frame.h
index b345200c..63853ef1 100644
--- a/source/Windows/Win32Frame.h
+++ b/source/Windows/Win32Frame.h
@@ -57,6 +57,8 @@ public:
virtual std::string Video_GetScreenShotFolder() const;
+ virtual std::shared_ptr CreateNetworkBackend();
+
bool GetFullScreenShowSubunitStatus(void);
int GetFullScreenOffsetX(void);
int GetFullScreenOffsetY(void);
diff --git a/source/Windows/WinFrame.cpp b/source/Windows/WinFrame.cpp
index 44e8bbfb..497d36f7 100644
--- a/source/Windows/WinFrame.cpp
+++ b/source/Windows/WinFrame.cpp
@@ -44,6 +44,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "SaveState.h"
#include "SerialComms.h"
#include "SoundCore.h"
+#include "Uthernet1.h"
+#include "Uthernet2.h"
#include "Speaker.h"
#include "Utilities.h"
#include "../resource/resource.h"
@@ -966,6 +968,10 @@ LRESULT Win32Frame::WndProc(
GetCardMgr().GetDisk2CardMgr().Destroy();
if (GetCardMgr().QuerySlot(SLOT7) == CT_GenericHDD)
dynamic_cast(GetCardMgr().GetRef(SLOT7)).Destroy();
+ if (GetCardMgr().QuerySlot(SLOT3) == CT_Uthernet)
+ dynamic_cast(GetCardMgr().GetRef(SLOT3)).Destroy();
+ if (GetCardMgr().QuerySlot(SLOT3) == CT_Uthernet2)
+ dynamic_cast(GetCardMgr().GetRef(SLOT3)).Destroy();
}
PrintDestroy();
if (GetCardMgr().IsSSCInstalled())