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())