Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
49885bbecb
20 changed files with 566 additions and 75 deletions
|
@ -114,6 +114,7 @@
|
|||
<ClInclude Include="source\StrFormat.h" />
|
||||
<ClInclude Include="source\SynchronousEventManager.h" />
|
||||
<ClInclude Include="source\Tape.h" />
|
||||
<ClInclude Include="source\Tfe\IPRaw.h" />
|
||||
<ClInclude Include="source\Tfe\NetworkBackend.h" />
|
||||
<ClInclude Include="source\Tfe\Bpf.h" />
|
||||
<ClInclude Include="source\Tfe\Ip6_misc.h" />
|
||||
|
@ -222,6 +223,7 @@
|
|||
<ClCompile Include="source\StrFormat.cpp" />
|
||||
<ClCompile Include="source\SynchronousEventManager.cpp" />
|
||||
<ClCompile Include="source\Tape.cpp" />
|
||||
<ClCompile Include="source\Tfe\IPRaw.cpp" />
|
||||
<ClCompile Include="source\Tfe\NetworkBackend.cpp" />
|
||||
<ClCompile Include="source\Tfe\PCapBackend.cpp" />
|
||||
<ClCompile Include="source\Tfe\tfearch.cpp">
|
||||
|
@ -467,7 +469,7 @@
|
|||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;ddraw.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>iphlpapi.lib;htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;ddraw.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalManifestDependencies>"type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'"</AdditionalManifestDependencies>
|
||||
<MinimumRequiredVersion>5.01</MinimumRequiredVersion>
|
||||
</Link>
|
||||
|
@ -495,7 +497,7 @@
|
|||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;ddraw_lib\x86\dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;ddraw_lib\x86\ddraw.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>iphlpapi.lib;htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;ddraw_lib\x86\dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;ddraw_lib\x86\ddraw.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalManifestDependencies>"type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'"</AdditionalManifestDependencies>
|
||||
<MinimumRequiredVersion>5.01</MinimumRequiredVersion>
|
||||
</Link>
|
||||
|
@ -522,7 +524,7 @@
|
|||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>iphlpapi.lib;htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalManifestDependencies>"type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'"</AdditionalManifestDependencies>
|
||||
<MinimumRequiredVersion>5.01</MinimumRequiredVersion>
|
||||
</Link>
|
||||
|
@ -553,7 +555,7 @@
|
|||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;ddraw.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>iphlpapi.lib;htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;ddraw.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||
<AdditionalManifestDependencies>"type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'"</AdditionalManifestDependencies>
|
||||
<MinimumRequiredVersion>5.01</MinimumRequiredVersion>
|
||||
|
@ -586,7 +588,7 @@
|
|||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;ddraw_lib\x86\dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;ddraw_lib\x86\ddraw.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>iphlpapi.lib;htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;ddraw_lib\x86\dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;ddraw_lib\x86\ddraw.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||
<AdditionalManifestDependencies>"type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'"</AdditionalManifestDependencies>
|
||||
<MinimumRequiredVersion>5.01</MinimumRequiredVersion>
|
||||
|
@ -619,7 +621,7 @@
|
|||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>iphlpapi.lib;htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||
<AdditionalManifestDependencies>"type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'"</AdditionalManifestDependencies>
|
||||
<MinimumRequiredVersion>5.01</MinimumRequiredVersion>
|
||||
|
|
|
@ -259,6 +259,9 @@
|
|||
<ClCompile Include="source\Uthernet2.cpp">
|
||||
<Filter>Source Files\Uthernet</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="source\Tfe\IPRaw.cpp">
|
||||
<Filter>Source Files\Uthernet</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="source\CommonVICE\6510core.h">
|
||||
|
@ -594,6 +597,9 @@
|
|||
<ClInclude Include="source\Uthernet2.h">
|
||||
<Filter>Source Files\Uthernet</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="source\Tfe\IPRaw.h">
|
||||
<Filter>Source Files\Uthernet</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="resource\Applewin.bmp">
|
||||
|
|
|
@ -5,7 +5,7 @@ AppleWin
|
|||
|
||||
AppleWin is a fully-featured emulator supporting different Apple II models and clones. A variety of peripheral cards and video display modes are supported (eg. NTSC, RGB); and there's an extensive built-in symbolic debugger.
|
||||
|
||||
Peripheral cards supported:
|
||||
Peripheral cards and add-on hardware supported:
|
||||
- Mockingboard, Phasor and SAM sound cards
|
||||
- Disk II interface for floppy disk drives
|
||||
- Hard disk controller
|
||||
|
@ -17,6 +17,8 @@ Peripheral cards supported:
|
|||
- CP/M SoftCard
|
||||
- Uthernet I (ethernet card)
|
||||
- Language Card and Saturn 64/128K for Apple II/II+
|
||||
- 4Play and SNES MAX joystick cards
|
||||
- VidHD card (functionality limited to IIgs' Super Hi-Res video modes)
|
||||
- No Slot Clock (NSC)
|
||||
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<p style="MARGIN-LEFT: 40px">Bob Sander-Cederlof: Applesoft Symbols (<a href="http://www.txbobsc.com/scsc/scdocumentor/index.html">http://www.txbobsc.com/scsc/scdocumentor/</a> S-C DocuMentor: Applesoft)</p>
|
||||
<p style="MARGIN-LEFT: 40px">David Schmidt: Updates to this help file</p>
|
||||
<p style="MARGIN-LEFT: 40px">Mike Harvey, Founder & Editor of Nibble Magazine: For providing us Apple fans the pleasure of eagerly awaiting each next month's issue to learn about the Apple! (<a href="http://www.nibblemagazine.com/">http://www.nibblemagazine.com/</a>)</p>
|
||||
<p style="MARGIN-LEFT: 40px">Andrea Odetti: working on making the source code more portable</p>
|
||||
<p style="MARGIN-LEFT: 40px">Andrea Odetti: working on making the source code more portable & Uthernet II card support</p>
|
||||
<p style="MARGIN-LEFT: 40px">Iván Izaguirre: Taiwanese Copam Base64A Apple II clone</p>
|
||||
<p style="MARGIN-LEFT: 40px">Arnaud C: debugger suggestions and help with 6502/6522/video timing issues</p>
|
||||
<p style="MARGIN-LEFT: 40px">Cyril Lambin: RGB card/monitor rendering, debugger improvements</p>
|
||||
|
|
|
@ -78,7 +78,7 @@
|
|||
|
||||
<strong>Ethernet Settings...:</strong><br>
|
||||
This allows to choose which network interface card (NIC) you want to
|
||||
use with the Uthernet card.<br>
|
||||
use with the Uthernet or Uthernet II card.<br>
|
||||
<br>
|
||||
|
||||
<strong>Emulation Speed Control:</strong><br>
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
<li>Parallel Printer card</li>
|
||||
<li>Super Serial card</li>
|
||||
<li>No-Slot clock</li>
|
||||
<li>Uthernet card</li>
|
||||
<li>Uthernet & Uthernet II cards</li>
|
||||
<li>4Play & SNES MAX joystick cards</li>
|
||||
<li>VidHD card</li>
|
||||
</ul>
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<li><a href="sound.html">Sound</a>
|
||||
<li><a href="clock.html">Clock</a>
|
||||
<li><a href="card-ssc.html">Super Serial card</a>
|
||||
<li><a href="uthernet.html">Uthernet network card</a>
|
||||
<li><a href="uthernet.html">Uthernet network cards</a>
|
||||
<li><a href="configuration.html">AppleWin Configuration</a>
|
||||
<li><a href="dbg-toc-intro.html">Using the Debugger</a>
|
||||
<li><a href="resources.html">Resources</a></li>
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<title>Uthernet network card</title>
|
||||
<title>Uthernet network cards</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||||
</head>
|
||||
<body style="FONT-FAMILY: verdana; BACKGROUND-COLOR: rgb(255,255,255)" alink="#008000"
|
||||
link="#008000" vlink="#008000">
|
||||
<h2 style="COLOR: rgb(0,128,0)">Uthernet network card</h2>
|
||||
<h2 style="COLOR: rgb(0,128,0)">Uthernet network cards</h2>
|
||||
<hr size="4">
|
||||
<p style="FONT-WEIGHT: bold">Overview:
|
||||
</p>
|
||||
<P>The Uthernet network card coupled with the Contiki OS allows you to browse the
|
||||
<P>The Uthernet network cards coupled with the Contiki OS allow you to browse the
|
||||
internet on your Apple.</P>
|
||||
<P style="FONT-WEIGHT: bold">Acknowledgment:
|
||||
</P>
|
||||
<P>Uthernet (TFE) support in Applewin was made possible by implementing the GPL
|
||||
<P>Uthernet (TFE) support in AppleWin was made possible by implementing the GPL
|
||||
source written by Spiro Trikaliotis for the Vice emulator - <A href="http://vice-emu.sourceforge.net/index.html#developers">
|
||||
http://vice-emu.sourceforge.net/index.html#developers</A></P>
|
||||
<P><A href="https://a2retrosystems.com/">Uthernet II</A> support in AppleWin has been contributed by Andrea (audetto) Odetti.</P>
|
||||
<P style="FONT-WEIGHT: bold">Details:
|
||||
</P>
|
||||
<P>To enable ethernet support in AppleWin you must first download and install
|
||||
|
@ -35,15 +36,15 @@
|
|||
<P>After AppleWin starts, select the settings icon and then select the ethernet
|
||||
settings button.
|
||||
</P>
|
||||
<P>Uthernet will be disabled. Select Uthernet from the list of available ethernet
|
||||
emulations (currently the only one).
|
||||
<P>Uthernet will be disabled. Select Uthernet or Uthernet II from the list of available ethernet
|
||||
emulations.
|
||||
</P>
|
||||
<P>Select the ethernet interface you want to work with. This must be a physical
|
||||
ethernet interface.
|
||||
</P>
|
||||
<P>If you have more than one interface you may need to select them in turn in order
|
||||
to get the text description for each interface vs what Npcap likes to use for
|
||||
a reference. Select Ok. and then close AppleWin.
|
||||
a reference.
|
||||
</P>
|
||||
<P><span style="font-weight: bold;">Note:</span> Wireless does not work
|
||||
with WinPcap (but see <A href="uthernet-wifi-workaround.html">WiFi Workaround</A>).
|
||||
|
@ -56,7 +57,7 @@
|
|||
also grab a copy of the Uthernet/Contiki getting started guide <A href="http://www.a2retrosystems.com/a2UtherManual.pdf">
|
||||
http://www.a2retrosystems.com/a2UtherManual.pdf</A>
|
||||
</P>
|
||||
<P>When you run AppleWin again, select the contiki80pri.dsk image. Boot AppleWin.
|
||||
<P>Select the contiki80pri.dsk image. Boot AppleWin.
|
||||
</P>
|
||||
<P>Once Contiki is loaded then press Enter to clear the welcome screen and press
|
||||
ESC for a menu.
|
||||
|
@ -87,5 +88,14 @@
|
|||
if you are still having difficulty then you should refer to the VICE network
|
||||
support page for additional information - <A href="http://vicekb.trikaliotis.net/13-005.shtml">
|
||||
http://vicekb.trikaliotis.net/13-005.shtml</A></P>
|
||||
</body>
|
||||
<P style="FONT-WEIGHT: bold">Uthernet II:
|
||||
</P>
|
||||
<P>Most features of the Uthernet II are emulated, with the following caveats:
|
||||
<ul>
|
||||
<li>PPPoE, interrupts and SPI are not implemented</li>
|
||||
<li>server side is not well tested</li>
|
||||
<li>after loading a save-state file, TCP and UDP sockets are closed</li>
|
||||
</ul>
|
||||
</P>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -443,7 +443,7 @@ bool Saturn128K::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version)
|
|||
}
|
||||
|
||||
// "Memory Bankxx"
|
||||
std::string memName = GetSnapshotMemStructName() + StrFormat("%02X", uBank);
|
||||
std::string memName = GetSnapshotMemStructName() + ByteToHexStr(uBank);
|
||||
|
||||
if (!yamlLoadHelper.GetSubMap(memName))
|
||||
throw std::runtime_error("Memory: Missing map name: " + memName);
|
||||
|
|
|
@ -2443,7 +2443,7 @@ static void MemLoadSnapshotAuxCommon(YamlLoadHelper& yamlLoadHelper, const std::
|
|||
}
|
||||
|
||||
// "Auxiliary Memory Bankxx"
|
||||
std::string auxMemName = MemGetSnapshotAuxMemStructName() + StrFormat("%02X", uBank-1);
|
||||
std::string auxMemName = MemGetSnapshotAuxMemStructName() + ByteToHexStr(uBank-1);
|
||||
|
||||
if (!yamlLoadHelper.GetSubMap(auxMemName))
|
||||
throw std::runtime_error("Memory: Missing map name: " + auxMemName);
|
||||
|
|
|
@ -86,7 +86,7 @@ void SSI_Output(void)
|
|||
LogOutput("SSI: ");
|
||||
for (int i = 0; i <= 4; i++)
|
||||
{
|
||||
std::string r = (ssiRegs[i] >= 0) ? StrFormat("%02X", ssiRegs[i]) : "--";
|
||||
std::string r = (ssiRegs[i] >= 0) ? ByteToHexStr(ssiRegs[i]) : "--";
|
||||
LogOutput("%s ", r.c_str());
|
||||
ssiRegs[i] = -1;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#if defined(_MSC_VER) && _MSC_VER < 1600
|
||||
#include <basetsd.h>
|
||||
typedef UINT8 uint8_t;
|
||||
typedef UINT16 uint16_t;
|
||||
#else
|
||||
#include <cstdint>
|
||||
#endif
|
||||
|
@ -19,10 +20,59 @@ typedef UINT8 uint8_t;
|
|||
std::string StrFormat(const char* format, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2);
|
||||
std::string StrFormatV(const char* format, va_list va);
|
||||
|
||||
namespace {
|
||||
|
||||
const char g_aHexDigits[16] = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
|
||||
};
|
||||
|
||||
// No buffer overflow check or null termination. Use with caution.
|
||||
inline char* StrBufferAppendByteAsHex(char* cp, uint8_t n)
|
||||
{
|
||||
*cp++ = g_aHexDigits[(n >> 4) & 0x0f];
|
||||
*cp++ = g_aHexDigits[(n >> 0) & 0x0f];
|
||||
return cp;
|
||||
}
|
||||
|
||||
// No buffer overflow check or null termination. Use with caution.
|
||||
inline char* StrBufferAppendWordAsHex(char* cp, uint16_t n)
|
||||
{
|
||||
*cp++ = g_aHexDigits[(n >> 12) & 0x0f];
|
||||
*cp++ = g_aHexDigits[(n >> 8) & 0x0f];
|
||||
*cp++ = g_aHexDigits[(n >> 4) & 0x0f];
|
||||
*cp++ = g_aHexDigits[(n >> 0) & 0x0f];
|
||||
return cp;
|
||||
}
|
||||
|
||||
inline std::string& StrAppendByteAsHex(std::string& s, uint8_t n)
|
||||
{
|
||||
const char szHex[] = "0123456789ABCDEF";
|
||||
s += szHex[(n >> 4) & 0x0f];
|
||||
s += szHex[n & 0x0f];
|
||||
const char hex[2] = { g_aHexDigits[(n >> 4) & 0x0f],
|
||||
g_aHexDigits[(n >> 0) & 0x0f] };
|
||||
return s.append(hex, 2);
|
||||
}
|
||||
|
||||
inline std::string& StrAppendWordAsHex(std::string& s, uint16_t n)
|
||||
{
|
||||
const char hex[4] = { g_aHexDigits[(n >> 12) & 0x0f],
|
||||
g_aHexDigits[(n >> 8) & 0x0f],
|
||||
g_aHexDigits[(n >> 4) & 0x0f],
|
||||
g_aHexDigits[(n >> 0) & 0x0f] };
|
||||
return s.append(hex, 4);
|
||||
}
|
||||
|
||||
inline std::string ByteToHexStr(uint8_t n)
|
||||
{
|
||||
std::string s;
|
||||
StrAppendByteAsHex(s, n);
|
||||
return s;
|
||||
}
|
||||
|
||||
inline std::string WordToHexStr(uint16_t n)
|
||||
{
|
||||
std::string s;
|
||||
StrAppendWordAsHex(s, n);
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
162
source/Tfe/IPRaw.cpp
Normal file
162
source/Tfe/IPRaw.cpp
Normal file
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
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 "IPRaw.h"
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#define IPV4 0x04
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1) // Ensure struct is packed
|
||||
struct IP4Header
|
||||
{
|
||||
uint8_t ihl : 4;
|
||||
uint8_t version : 4;
|
||||
uint8_t tos;
|
||||
uint16_t len;
|
||||
uint16_t id;
|
||||
uint16_t flags : 3;
|
||||
uint16_t fragmentOffset : 13;
|
||||
uint8_t ttl;
|
||||
uint8_t proto;
|
||||
uint16_t checksum;
|
||||
uint32_t sourceAddress;
|
||||
uint32_t destinationAddress;
|
||||
};
|
||||
|
||||
struct ETH2Frame
|
||||
{
|
||||
uint8_t destinationMac[6];
|
||||
uint8_t sourceMac[6];
|
||||
uint16_t type;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
uint32_t sum_every_16bits(const void *addr, int count)
|
||||
{
|
||||
uint32_t sum = 0;
|
||||
const uint16_t *ptr = reinterpret_cast<const uint16_t *>(addr);
|
||||
|
||||
while (count > 1)
|
||||
{
|
||||
/* This is the inner loop */
|
||||
sum += *ptr++;
|
||||
count -= 2;
|
||||
}
|
||||
|
||||
/* Add left-over byte, if any */
|
||||
if (count > 0)
|
||||
sum += *reinterpret_cast<const uint8_t *>(ptr);
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
uint16_t checksum(const void *addr, int count)
|
||||
{
|
||||
/* Compute Internet Checksum for "count" bytes
|
||||
* beginning at location "addr".
|
||||
* Taken from https://tools.ietf.org/html/rfc1071
|
||||
*/
|
||||
uint32_t sum = sum_every_16bits(addr, count);
|
||||
|
||||
/* Fold 32-bit sum to 16 bits */
|
||||
while (sum >> 16)
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
|
||||
return ~sum;
|
||||
}
|
||||
|
||||
// get the minimum size of a ETH Frame that contains a IP payload
|
||||
// 34 = 14 bytes for ETH2 + 20 bytes IPv4 (minimum)
|
||||
int getIPMinimumSize()
|
||||
{
|
||||
const int minimumSize = sizeof(ETH2Frame) + sizeof(IP4Header) + 0; // 0 len
|
||||
return minimumSize;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::vector<uint8_t> createETH2Frame(const std::vector<uint8_t> &data,
|
||||
const MACAddress *sourceMac, const MACAddress *destinationMac,
|
||||
const uint8_t ttl, const uint8_t tos, const uint8_t protocol,
|
||||
const uint32_t sourceAddress, const uint32_t destinationAddress)
|
||||
{
|
||||
const size_t total = sizeof(ETH2Frame) + sizeof(IP4Header) + data.size();
|
||||
std::vector<uint8_t> frame(total);
|
||||
ETH2Frame *eth2frame = reinterpret_cast<ETH2Frame *>(frame.data() + 0);
|
||||
memcpy(eth2frame->destinationMac, destinationMac, sizeof(eth2frame->destinationMac));
|
||||
memcpy(eth2frame->sourceMac, sourceMac, sizeof(eth2frame->destinationMac));
|
||||
eth2frame->type = htons(0x0800);
|
||||
IP4Header *ip4header = reinterpret_cast<IP4Header *>(frame.data() + sizeof(ETH2Frame));
|
||||
|
||||
ip4header->version = IPV4;
|
||||
ip4header->ihl = 0x05; // minimum size = 20 bytes, without any extra option
|
||||
ip4header->tos = tos;
|
||||
ip4header->len = htons(static_cast<uint16_t>(sizeof(IP4Header) + data.size()));
|
||||
ip4header->id = 0;
|
||||
ip4header->fragmentOffset = 0;
|
||||
ip4header->ttl = ttl;
|
||||
ip4header->proto = protocol;
|
||||
ip4header->sourceAddress = sourceAddress;
|
||||
ip4header->destinationAddress = destinationAddress;
|
||||
ip4header->checksum = checksum(ip4header, sizeof(IP4Header));
|
||||
|
||||
memcpy(frame.data() + sizeof(ETH2Frame) + sizeof(IP4Header), data.data(), data.size());
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
void getIPPayload(const int lengthOfFrame, const uint8_t *frame,
|
||||
size_t &lengthOfPayload, const uint8_t *&payload, uint32_t &destination, uint8_t &protocol)
|
||||
{
|
||||
const int minimumSize = getIPMinimumSize();
|
||||
if (lengthOfFrame > minimumSize)
|
||||
{
|
||||
const ETH2Frame *eth2Frame = reinterpret_cast<const ETH2Frame *>(frame);
|
||||
const IP4Header *ip4header = reinterpret_cast<const IP4Header *>(frame + sizeof(ETH2Frame));
|
||||
if (eth2Frame->type == htons(0x0800) && ip4header->version == IPV4)
|
||||
{
|
||||
const uint16_t ipv4HeaderSize = ip4header->ihl * 4;
|
||||
const uint16_t ipPacketSize = ntohs(ip4header->len);
|
||||
const int expectedSize = sizeof(ETH2Frame) + ipPacketSize;
|
||||
if (ipPacketSize > ipv4HeaderSize && lengthOfFrame >= expectedSize)
|
||||
{
|
||||
protocol = ip4header->proto;
|
||||
payload = frame + sizeof(ETH2Frame) + ipv4HeaderSize;
|
||||
lengthOfPayload = ipPacketSize - ipv4HeaderSize;
|
||||
destination = ip4header->destinationAddress;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// not a good packet
|
||||
protocol = 0xFF; // reserved protocol
|
||||
payload = nullptr;
|
||||
lengthOfPayload = 0;
|
||||
destination = 0;
|
||||
}
|
11
source/Tfe/IPRaw.h
Normal file
11
source/Tfe/IPRaw.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
struct MACAddress;
|
||||
|
||||
std::vector<uint8_t> createETH2Frame(const std::vector<uint8_t> &data,
|
||||
const MACAddress *sourceMac, const MACAddress *destinationMac,
|
||||
const uint8_t ttl, const uint8_t tos, const uint8_t protocol,
|
||||
const uint32_t sourceAddress, const uint32_t destinationAddress);
|
||||
|
||||
void getIPPayload(const int lengthOfFrame, const uint8_t *frame,
|
||||
size_t &lengthOfPayload, const uint8_t *&payload, uint32_t &destination, uint8_t &protocol);
|
|
@ -6,6 +6,14 @@
|
|||
#define MAX_RXLENGTH 1518
|
||||
#define MIN_RXLENGTH 64
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1) // Ensure struct is packed
|
||||
struct MACAddress
|
||||
{
|
||||
uint8_t address[6];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
class NetworkBackend
|
||||
{
|
||||
public:
|
||||
|
@ -26,6 +34,9 @@ public:
|
|||
// process pending packets
|
||||
virtual void update(const ULONG nExecutedCycles) = 0;
|
||||
|
||||
// get MAC for IPRAW (it is only supposed to handle addresses on the local network)
|
||||
virtual void getMACAddress(const uint32_t address, MACAddress & mac) = 0;
|
||||
|
||||
// if the backend is usable
|
||||
virtual bool isValid() = 0;
|
||||
};
|
||||
|
|
|
@ -25,6 +25,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||
#include "../Common.h"
|
||||
#include "../Registry.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <iphlpapi.h>
|
||||
#endif
|
||||
|
||||
std::string PCapBackend::tfe_interface;
|
||||
|
||||
PCapBackend::PCapBackend(const std::string & pcapInterface)
|
||||
|
@ -71,6 +75,16 @@ void PCapBackend::update(const ULONG /* nExecutedCycles */)
|
|||
// nothing to do
|
||||
}
|
||||
|
||||
void PCapBackend::getMACAddress(const uint32_t address, MACAddress & mac)
|
||||
{
|
||||
// this is only expected to be called for IP addresses on the same network
|
||||
#ifdef _MSC_VER
|
||||
const DWORD dwSourceAddress = INADDR_ANY;
|
||||
ULONG len = sizeof(MACAddress::address);
|
||||
SendARP(address, dwSourceAddress, mac.address, &len);
|
||||
#endif
|
||||
}
|
||||
|
||||
int PCapBackend::tfe_enumadapter_open(void)
|
||||
{
|
||||
return tfe_arch_enumadapter_open();
|
||||
|
|
|
@ -29,6 +29,9 @@ public:
|
|||
// process pending packets
|
||||
virtual bool isValid();
|
||||
|
||||
// get MAC for IPRAW (it is only supposed to handle addresses on the local network)
|
||||
virtual void getMACAddress(const uint32_t address, MACAddress & mac);
|
||||
|
||||
static void tfe_SetRegistryInterface(UINT slot, const std::string& name);
|
||||
static void get_disabled_state(int * param);
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||
#include "Interface.h"
|
||||
#include "Tfe/NetworkBackend.h"
|
||||
#include "Tfe/PCapBackend.h"
|
||||
#include "Tfe/IPRaw.h"
|
||||
#include "W5100.h"
|
||||
|
||||
// Linux uses EINPROGRESS while Windows returns WSAEWOULDBLOCK
|
||||
|
@ -73,6 +74,9 @@ typedef int socklen_t;
|
|||
#define SOCK_NONBLOCK O_NONBLOCK
|
||||
#endif
|
||||
|
||||
// Dest MAC + Source MAC + Ether Type
|
||||
#define ETH_MINIMUM_SIZE (6 + 6 + 2)
|
||||
|
||||
// #define U2_LOG_VERBOSE
|
||||
// #define U2_LOG_TRAFFIC
|
||||
// #define U2_LOG_STATE
|
||||
|
@ -95,6 +99,12 @@ namespace
|
|||
return host;
|
||||
}
|
||||
|
||||
uint32_t readAddress(const uint8_t *ptr)
|
||||
{
|
||||
const uint32_t address = *reinterpret_cast<const uint32_t *>(ptr);
|
||||
return address;
|
||||
}
|
||||
|
||||
uint8_t getIByte(const uint16_t value, const size_t shift)
|
||||
{
|
||||
return (value >> shift) & 0xFF;
|
||||
|
@ -141,6 +151,13 @@ namespace
|
|||
writeData(socket, memory, data, len);
|
||||
}
|
||||
|
||||
void writeDataIPRaw(Socket &socket, std::vector<uint8_t> &memory, const uint8_t *data, const size_t len, const uint32_t destination)
|
||||
{
|
||||
writeAny(socket, memory, destination);
|
||||
write16(socket, memory, static_cast<uint16_t>(len));
|
||||
writeData(socket, memory, data, len);
|
||||
}
|
||||
|
||||
void writeDataForProtocol(Socket &socket, std::vector<uint8_t> &memory, const uint8_t *data, const size_t len, const sockaddr_in &source)
|
||||
{
|
||||
if (socket.sn_sr == W5100_SN_SR_SOCK_UDP)
|
||||
|
@ -199,31 +216,46 @@ Socket::~Socket()
|
|||
clearFD();
|
||||
}
|
||||
|
||||
bool Socket::isOpen() const
|
||||
{
|
||||
return (myFD != INVALID_SOCKET) &&
|
||||
((sn_sr == W5100_SN_SR_ESTABLISHED) || (sn_sr == W5100_SN_SR_SOCK_UDP));
|
||||
}
|
||||
|
||||
void Socket::process()
|
||||
{
|
||||
if (myFD != INVALID_SOCKET && sn_sr == W5100_SN_SR_SOCK_INIT && (myErrno == SOCK_EINPROGRESS || myErrno == SOCK_EWOULDBLOCK))
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
FD_SET writefds;
|
||||
FD_SET writefds, exceptfds;
|
||||
FD_ZERO(&writefds);
|
||||
FD_ZERO(&exceptfds);
|
||||
FD_SET(myFD, &writefds);
|
||||
FD_SET(myFD, &exceptfds);
|
||||
const timeval timeout = {0, 0};
|
||||
if (select(0, NULL, &writefds, NULL, &timeout) > 0)
|
||||
if (select(0, NULL, &writefds, &exceptfds, &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<char *>(&err), &elen);
|
||||
socklen_t elen = sizeof(err);
|
||||
const int res = getsockopt(myFD, SOL_SOCKET, SO_ERROR, reinterpret_cast<char *>(&err), &elen);
|
||||
|
||||
if (err == 0)
|
||||
if (res == 0 && err == 0)
|
||||
{
|
||||
myErrno = 0;
|
||||
sn_sr = W5100_SN_SR_ESTABLISHED;
|
||||
#ifdef U2_LOG_STATE
|
||||
LogFileOutput("U2: TCP[]: Connected\n");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
clearFD();
|
||||
#ifdef U2_LOG_STATE
|
||||
LogFileOutput("U2: TCP[]: Connection error: %d - %" ERROR_FMT "\n", res, STRERROR(err));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -266,12 +298,18 @@ bool Socket::LoadSnapshot(YamlLoadHelper &yamlLoadHelper)
|
|||
|
||||
// transmit and receive sizes are restored from the card common registers
|
||||
|
||||
if (sn_sr != W5100_SN_SR_SOCK_MACRAW)
|
||||
switch (sn_sr)
|
||||
{
|
||||
case W5100_SN_SR_SOCK_MACRAW:
|
||||
case W5100_SN_SR_SOCK_IPRAW:
|
||||
// we can restore RAW sockets
|
||||
break;
|
||||
default:
|
||||
// no point in restoring a broken UDP or TCP connection
|
||||
// just reset the socket
|
||||
sn_sr = W5100_SN_SR_CLOSED;
|
||||
// for the same reason there is no point in saving myFD and myErrno
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -434,7 +472,7 @@ void Uthernet2::updateRSR(const size_t i)
|
|||
socket.sn_rx_rsr = dataPresent;
|
||||
}
|
||||
|
||||
int Uthernet2::receiveForMacAddress(const bool acceptAll, const int size, uint8_t * data)
|
||||
int Uthernet2::receiveForMacAddress(const bool acceptAll, const int size, uint8_t * data, PacketDestination & packetDestination)
|
||||
{
|
||||
const uint8_t * mac = myMemory.data() + W5100_SHAR0;
|
||||
|
||||
|
@ -442,15 +480,9 @@ int Uthernet2::receiveForMacAddress(const bool acceptAll, const int size, uint8_
|
|||
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)
|
||||
// smaller frames are not good anyway
|
||||
if (len >= ETH_MINIMUM_SIZE)
|
||||
{
|
||||
if (acceptAll)
|
||||
{
|
||||
return len;
|
||||
}
|
||||
|
||||
if (data[0] == mac[0] &&
|
||||
data[1] == mac[1] &&
|
||||
data[2] == mac[2] &&
|
||||
|
@ -458,6 +490,7 @@ int Uthernet2::receiveForMacAddress(const bool acceptAll, const int size, uint8_
|
|||
data[4] == mac[4] &&
|
||||
data[5] == mac[5])
|
||||
{
|
||||
packetDestination = HOST;
|
||||
return len;
|
||||
}
|
||||
|
||||
|
@ -468,8 +501,16 @@ int Uthernet2::receiveForMacAddress(const bool acceptAll, const int size, uint8_
|
|||
data[4] == 0xFF &&
|
||||
data[5] == 0xFF)
|
||||
{
|
||||
packetDestination = BROADCAST;
|
||||
return len;
|
||||
}
|
||||
|
||||
if (acceptAll)
|
||||
{
|
||||
packetDestination = OTHER;
|
||||
return len;
|
||||
}
|
||||
|
||||
}
|
||||
// skip this frame and try with another one
|
||||
}
|
||||
|
@ -477,34 +518,110 @@ int Uthernet2::receiveForMacAddress(const bool acceptAll, const int size, uint8_
|
|||
return len;
|
||||
}
|
||||
|
||||
void Uthernet2::receiveOnePacketMacRaw(const size_t i)
|
||||
void Uthernet2::receiveOnePacketRaw()
|
||||
{
|
||||
bool acceptAll = false;
|
||||
int macRawSocket = -1; // to which IPRaw soccket to send to (can only be 0)
|
||||
|
||||
Socket & socket0 = mySockets[0];
|
||||
if (socket0.sn_sr == W5100_SN_SR_SOCK_MACRAW)
|
||||
{
|
||||
macRawSocket = 0; // the only MAC Raw socket is open, packet will go there as a fallback
|
||||
const uint8_t mr = myMemory[socket0.registerAddress + W5100_SN_MR];
|
||||
|
||||
// see if MAC RAW filters or not
|
||||
const bool filterMAC = mr & W5100_SN_MR_MF;
|
||||
if (!filterMAC)
|
||||
{
|
||||
acceptAll = true;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t buffer[MAX_RXLENGTH];
|
||||
PacketDestination packetDestination;
|
||||
const int len = receiveForMacAddress(acceptAll, sizeof(buffer), buffer, packetDestination);
|
||||
if (len > 0)
|
||||
{
|
||||
const uint8_t * payload;
|
||||
size_t lengthOfPayload;
|
||||
uint32_t destination;
|
||||
uint8_t packetProtocol;
|
||||
getIPPayload(len, buffer, lengthOfPayload, payload, destination, packetProtocol);
|
||||
|
||||
// see if there is a IPRAW socket that should accept thi spacket
|
||||
int ipRawSocket = -1;
|
||||
if (packetDestination != OTHER) // IPRaw always filters (HOST or BROADCAST, never OTHER)
|
||||
{
|
||||
for (size_t i = 0; i < mySockets.size(); ++i)
|
||||
{
|
||||
Socket & socket = mySockets[i];
|
||||
|
||||
if (socket.sn_sr == W5100_SN_SR_SOCK_IPRAW)
|
||||
{
|
||||
// IP only accepts by protocol & always filters MAC
|
||||
const uint8_t socketProtocol = myMemory[socket.registerAddress + W5100_SN_PROTO];
|
||||
if (payload && packetProtocol == socketProtocol)
|
||||
{
|
||||
ipRawSocket = i;
|
||||
break; // a valid IPRAW socket has been found
|
||||
}
|
||||
}
|
||||
// we should probably check for UDP & TCP sockets and filter these packets too
|
||||
}
|
||||
}
|
||||
|
||||
// priority to IPRAW
|
||||
if (ipRawSocket >= 0)
|
||||
{
|
||||
receiveOnePacketIPRaw(ipRawSocket, lengthOfPayload, payload, destination, packetProtocol, len);
|
||||
}
|
||||
// fallback to MACRAW (if open)
|
||||
else if (macRawSocket >= 0)
|
||||
{
|
||||
receiveOnePacketMacRaw(macRawSocket, len, buffer);
|
||||
}
|
||||
// else packet is dropped
|
||||
}
|
||||
}
|
||||
|
||||
void Uthernet2::receiveOnePacketMacRaw(const size_t i, const int size, uint8_t * data)
|
||||
{
|
||||
Socket &socket = mySockets[i];
|
||||
|
||||
uint8_t buffer[MAX_RXLENGTH];
|
||||
|
||||
const uint8_t mr = myMemory[socket.registerAddress + W5100_SN_MR];
|
||||
const bool filterMAC = mr & W5100_SN_MR_MF;
|
||||
|
||||
const int len = receiveForMacAddress(!filterMAC, sizeof(buffer), buffer);
|
||||
if (len > 0)
|
||||
if (socket.isThereRoomFor(size, sizeof(uint16_t)))
|
||||
{
|
||||
// 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);
|
||||
writeDataMacRaw(socket, myMemory, data, size);
|
||||
#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);
|
||||
LogFileOutput("U2: Read MACRAW[%" SIZE_T_FMT "]: " MAC_FMT " -> " MAC_FMT ": +%d -> %d bytes\n", i, MAC_SOURCE(data), MAC_DEST(data),
|
||||
size, socket.sn_rx_rsr);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// drop it
|
||||
}
|
||||
else
|
||||
{
|
||||
// drop it
|
||||
#ifdef U2_LOG_TRAFFIC
|
||||
LogFileOutput("U2: Skip MACRAW[%" SIZE_T_FMT "]: %d bytes\n", i, len);
|
||||
LogFileOutput("U2: Skip MACRAW[%" SIZE_T_FMT "]: %d bytes\n", i, size);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void Uthernet2::receiveOnePacketIPRaw(const size_t i, const size_t lengthOfPayload, const uint8_t * payload, const uint32_t destination, const uint8_t protocol, const int len)
|
||||
{
|
||||
Socket &socket = mySockets[i];
|
||||
|
||||
if (socket.isThereRoomFor(lengthOfPayload, 4 + sizeof(uint16_t)))
|
||||
{
|
||||
writeDataIPRaw(socket, myMemory, payload, lengthOfPayload, destination);
|
||||
#ifdef U2_LOG_TRAFFIC
|
||||
LogFileOutput("U2: Read IPRAW[%" SIZE_T_FMT "]: +%" SIZE_T_FMT " (%d) -> %d bytes\n", i, lengthOfPayload, len, socket.sn_rx_rsr);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// drop it
|
||||
#ifdef U2_LOG_TRAFFIC
|
||||
LogFileOutput("U2: Skip IPRAW[%" SIZE_T_FMT "]: %" SIZE_T_FMT " (%d) bytes \n", i, lengthOfPayload, len);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -512,7 +629,7 @@ void Uthernet2::receiveOnePacketMacRaw(const size_t i)
|
|||
void Uthernet2::receiveOnePacketFromSocket(const size_t i)
|
||||
{
|
||||
Socket &socket = mySockets[i];
|
||||
if (socket.myFD != INVALID_SOCKET)
|
||||
if (socket.isOpen())
|
||||
{
|
||||
const uint16_t freeRoom = socket.getFreeRoom();
|
||||
if (freeRoom > 32) // avoid meaningless reads
|
||||
|
@ -557,7 +674,8 @@ void Uthernet2::receiveOnePacket(const size_t i)
|
|||
switch (socket.sn_sr)
|
||||
{
|
||||
case W5100_SN_SR_SOCK_MACRAW:
|
||||
receiveOnePacketMacRaw(i);
|
||||
case W5100_SN_SR_SOCK_IPRAW:
|
||||
receiveOnePacketRaw();
|
||||
break;
|
||||
case W5100_SN_SR_ESTABLISHED:
|
||||
case W5100_SN_SR_SOCK_UDP:
|
||||
|
@ -572,6 +690,29 @@ void Uthernet2::receiveOnePacket(const size_t i)
|
|||
};
|
||||
}
|
||||
|
||||
void Uthernet2::sendDataIPRaw(const size_t i, std::vector<uint8_t> &payload)
|
||||
{
|
||||
const Socket &socket = mySockets[i];
|
||||
|
||||
const uint8_t ttl = myMemory[socket.registerAddress + W5100_SN_TTL];
|
||||
const uint8_t tos = myMemory[socket.registerAddress + W5100_SN_TOS];
|
||||
const uint8_t protocol = myMemory[socket.registerAddress + W5100_SN_PROTO];
|
||||
const uint32_t source = readAddress(myMemory.data() + W5100_SIPR0);
|
||||
const uint32_t destination = readAddress(myMemory.data() + socket.registerAddress + W5100_SN_DIPR0);
|
||||
|
||||
const MACAddress * sourceMac = reinterpret_cast<const MACAddress *>(myMemory.data() + W5100_SHAR0);
|
||||
const MACAddress * destinationMac;
|
||||
getMACAddress(destination, destinationMac);
|
||||
|
||||
std::vector<uint8_t> packet = createETH2Frame(payload, sourceMac, destinationMac, ttl, tos, protocol, source, destination);
|
||||
|
||||
#ifdef U2_LOG_TRAFFIC
|
||||
LogFileOutput("U2: Send IPRAW[%" SIZE_T_FMT "]: %" SIZE_T_FMT " (%" SIZE_T_FMT ") bytes\n", i, payload.size(), packet.size());
|
||||
#endif
|
||||
|
||||
myNetworkBackend->transmit(packet.size(), packet.data());
|
||||
}
|
||||
|
||||
void Uthernet2::sendDataMacRaw(const size_t i, std::vector<uint8_t> &packet) const
|
||||
{
|
||||
#ifdef U2_LOG_TRAFFIC
|
||||
|
@ -592,7 +733,7 @@ void Uthernet2::sendDataMacRaw(const size_t i, std::vector<uint8_t> &packet) con
|
|||
void Uthernet2::sendDataToSocket(const size_t i, std::vector<uint8_t> &data)
|
||||
{
|
||||
Socket &socket = mySockets[i];
|
||||
if (socket.myFD != INVALID_SOCKET)
|
||||
if (socket.isOpen())
|
||||
{
|
||||
sockaddr_in destination = {};
|
||||
destination.sin_family = AF_INET;
|
||||
|
@ -656,6 +797,9 @@ void Uthernet2::sendData(const size_t i)
|
|||
case W5100_SN_SR_SOCK_MACRAW:
|
||||
sendDataMacRaw(i, data);
|
||||
break;
|
||||
case W5100_SN_SR_SOCK_IPRAW:
|
||||
sendDataIPRaw(i, data);
|
||||
break;
|
||||
case W5100_SN_SR_ESTABLISHED:
|
||||
case W5100_SN_SR_SOCK_UDP:
|
||||
sendDataToSocket(i, data);
|
||||
|
@ -680,7 +824,7 @@ void Uthernet2::resetRXTXBuffers(const size_t i)
|
|||
myMemory[socket.registerAddress + W5100_SN_RX_RD1] = 0x00;
|
||||
}
|
||||
|
||||
void Uthernet2::openSystemSocket(const size_t i, const int type, const int protocol, const int state)
|
||||
void Uthernet2::openSystemSocket(const size_t i, const int type, const int protocol, const int status)
|
||||
{
|
||||
Socket &s = mySockets[i];
|
||||
#ifdef _MSC_VER
|
||||
|
@ -691,7 +835,7 @@ void Uthernet2::openSystemSocket(const size_t i, const int type, const int proto
|
|||
if (fd == INVALID_SOCKET)
|
||||
{
|
||||
#ifdef U2_LOG_STATE
|
||||
const char *proto = state == W5100_SN_SR_SOCK_UDP ? "UDP" : "TCP";
|
||||
const char *proto = (status == W5100_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();
|
||||
|
@ -702,7 +846,7 @@ void Uthernet2::openSystemSocket(const size_t i, const int type, const int proto
|
|||
u_long on = 1;
|
||||
ioctlsocket(fd, FIONBIO, &on);
|
||||
#endif
|
||||
s.setFD(fd, state);
|
||||
s.setFD(fd, status);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -854,7 +998,7 @@ uint8_t Uthernet2::readSocketRegister(const uint16_t address)
|
|||
break;
|
||||
default:
|
||||
#ifdef U2_LOG_UNKNOWN
|
||||
LogFileOutput("U2: Get unknown socket register[%" SIZE_T_FMT "]: %04x\n", i, address);
|
||||
LogFileOutput("U2: Get unknown socket register[%d]: %04x\n", i, address);
|
||||
#endif
|
||||
value = myMemory[address];
|
||||
break;
|
||||
|
@ -988,7 +1132,7 @@ void Uthernet2::writeSocketRegister(const uint16_t address, const uint8_t value)
|
|||
break;
|
||||
#ifdef U2_LOG_UNKNOWN
|
||||
default:
|
||||
LogFileOutput("U2: Set unknown socket register[%" SIZE_T_FMT "]: %04x\n", i, address);
|
||||
LogFileOutput("U2: Set unknown socket register[%d]: %04x\n", i, address);
|
||||
break;
|
||||
#endif
|
||||
};
|
||||
|
@ -1073,6 +1217,7 @@ void Uthernet2::Reset(const bool powerCycle)
|
|||
// dataAddress is NOT reset, see page 10 of Uthernet II
|
||||
myDataAddress = 0;
|
||||
myNetworkBackend = GetFrame().CreateNetworkBackend();
|
||||
myARPCache.clear();
|
||||
}
|
||||
|
||||
mySockets.clear();
|
||||
|
@ -1084,14 +1229,25 @@ void Uthernet2::Reset(const bool powerCycle)
|
|||
{
|
||||
resetRXTXBuffers(i);
|
||||
mySockets[i].clearFD();
|
||||
mySockets[i].registerAddress = static_cast<uint16_t>(W5100_S0_BASE + (i << 8));
|
||||
const uint16_t registerAddress = static_cast<uint16_t>(W5100_S0_BASE + (i << 8));
|
||||
mySockets[i].registerAddress = registerAddress;
|
||||
|
||||
myMemory[registerAddress + W5100_SN_DHAR0] = 0xFF;
|
||||
myMemory[registerAddress + W5100_SN_DHAR1] = 0xFF;
|
||||
myMemory[registerAddress + W5100_SN_DHAR2] = 0xFF;
|
||||
myMemory[registerAddress + W5100_SN_DHAR3] = 0xFF;
|
||||
myMemory[registerAddress + W5100_SN_DHAR4] = 0xFF;
|
||||
myMemory[registerAddress + W5100_SN_DHAR5] = 0xFF;
|
||||
myMemory[registerAddress + W5100_SN_TTL] = 0x80;
|
||||
}
|
||||
|
||||
// initial values
|
||||
myMemory[W5100_RTR0] = 0x07;
|
||||
myMemory[W5100_RTR1] = 0xD0;
|
||||
myMemory[W5100_RTR0] = 0x07;
|
||||
myMemory[W5100_RTR1] = 0xD0;
|
||||
myMemory[W5100_RCR] = 0x08;
|
||||
setRXSizes(W5100_RMSR, 0x55);
|
||||
setTXSizes(W5100_TMSR, 0x55);
|
||||
myMemory[W5100_PTIMER] = 0x28;
|
||||
}
|
||||
|
||||
BYTE Uthernet2::IO_C0(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles)
|
||||
|
@ -1162,6 +1318,46 @@ void Uthernet2::InitializeIO(LPBYTE pCxRomPeripheral)
|
|||
RegisterIoHandler(m_slot, u2_C0, u2_C0, nullptr, nullptr, this, nullptr);
|
||||
}
|
||||
|
||||
void Uthernet2::getMACAddress(const uint32_t address, const MACAddress * & mac)
|
||||
{
|
||||
const auto it = myARPCache.find(address);
|
||||
if (it != myARPCache.end())
|
||||
{
|
||||
mac = &it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
MACAddress & macAddr = myARPCache[address];
|
||||
const uint32_t source = readAddress(myMemory.data() + W5100_SIPR0);
|
||||
|
||||
if (address == source)
|
||||
{
|
||||
const uint8_t * sourceMac = myMemory.data() + W5100_SHAR0;
|
||||
memcpy(macAddr.address, sourceMac, sizeof(macAddr.address));
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(macAddr.address, 0xFF, sizeof(macAddr.address)); // fallback to broadcast
|
||||
if (address != INADDR_BROADCAST)
|
||||
{
|
||||
const uint32_t subnet = readAddress(myMemory.data() + W5100_SUBR0);
|
||||
if ((address & subnet) == (source & subnet))
|
||||
{
|
||||
// same network: send ARP request
|
||||
myNetworkBackend->getMACAddress(address, macAddr);
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint32_t gateway = readAddress(myMemory.data() + W5100_GAR0);
|
||||
// different network: go via gateway
|
||||
myNetworkBackend->getMACAddress(gateway, macAddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
mac = &macAddr;
|
||||
}
|
||||
}
|
||||
|
||||
void Uthernet2::Update(const ULONG nExecutedCycles)
|
||||
{
|
||||
myNetworkBackend->update(nExecutedCycles);
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
#include "Card.h"
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
class NetworkBackend;
|
||||
struct MACAddress;
|
||||
|
||||
struct Socket
|
||||
{
|
||||
|
@ -28,6 +30,7 @@ struct Socket
|
|||
socket_t myFD;
|
||||
int myErrno;
|
||||
|
||||
bool isOpen() const;
|
||||
void clearFD();
|
||||
void setFD(const socket_t fd, const int status);
|
||||
void process();
|
||||
|
@ -47,6 +50,7 @@ struct 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
|
||||
* https://www.wiznet.io/wp-content/uploads/wiznethome/Chip/W5100/Document/3150Aplus_5100_ES_V260E.pdf
|
||||
*/
|
||||
|
||||
class Uthernet2 : public Card
|
||||
|
@ -54,6 +58,8 @@ class Uthernet2 : public Card
|
|||
public:
|
||||
static const std::string& GetSnapshotCardName();
|
||||
|
||||
enum PacketDestination { HOST, BROADCAST, OTHER };
|
||||
|
||||
Uthernet2(UINT slot);
|
||||
|
||||
virtual void Destroy(void) {}
|
||||
|
@ -72,6 +78,13 @@ private:
|
|||
uint16_t myDataAddress;
|
||||
std::shared_ptr<NetworkBackend> myNetworkBackend;
|
||||
|
||||
// the real Uthernet II card does not have a ARP Cache
|
||||
// but in the interest of speeding up the emulator
|
||||
// we introduce one
|
||||
std::map<uint32_t, MACAddress> myARPCache;
|
||||
|
||||
void getMACAddress(const uint32_t address, const MACAddress * & mac);
|
||||
|
||||
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);
|
||||
|
@ -79,11 +92,14 @@ private:
|
|||
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 receiveOnePacketRaw();
|
||||
void receiveOnePacketIPRaw(const size_t i, const size_t lengthOfPayload, const uint8_t * payload, const uint32_t destination, const uint8_t protocol, const int len);
|
||||
void receiveOnePacketMacRaw(const size_t i, const int size, uint8_t * data);
|
||||
void receiveOnePacketFromSocket(const size_t i);
|
||||
void receiveOnePacket(const size_t i);
|
||||
int receiveForMacAddress(const bool acceptAll, const int size, uint8_t * data);
|
||||
int receiveForMacAddress(const bool acceptAll, const int size, uint8_t * data, PacketDestination & packetDestination);
|
||||
|
||||
void sendDataIPRaw(const size_t i, std::vector<uint8_t> &data);
|
||||
void sendDataMacRaw(const size_t i, std::vector<uint8_t> &data) const;
|
||||
void sendDataToSocket(const size_t i, std::vector<uint8_t> &data);
|
||||
void sendData(const size_t i);
|
||||
|
@ -91,7 +107,7 @@ private:
|
|||
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 openSystemSocket(const size_t i, const int type, const int protocol, const int status);
|
||||
void openSocket(const size_t i);
|
||||
void closeSocket(const size_t i);
|
||||
void connectSocket(const size_t i);
|
||||
|
|
|
@ -20,8 +20,10 @@
|
|||
#define W5100_SIPR3 0x0012
|
||||
#define W5100_RTR0 0x0017
|
||||
#define W5100_RTR1 0x0018
|
||||
#define W5100_RCR 0x0019
|
||||
#define W5100_RMSR 0x001A
|
||||
#define W5100_TMSR 0x001B
|
||||
#define W5100_PTIMER 0x0028
|
||||
#define W5100_UPORT1 0x002F
|
||||
#define W5100_S0_BASE 0x0400
|
||||
#define W5100_S3_MAX 0x07FF
|
||||
|
@ -58,6 +60,12 @@
|
|||
#define W5100_SN_SR 0x03
|
||||
#define W5100_SN_PORT0 0x04
|
||||
#define W5100_SN_PORT1 0x05
|
||||
#define W5100_SN_DHAR0 0x06
|
||||
#define W5100_SN_DHAR1 0x07
|
||||
#define W5100_SN_DHAR2 0x08
|
||||
#define W5100_SN_DHAR3 0x09
|
||||
#define W5100_SN_DHAR4 0x0A
|
||||
#define W5100_SN_DHAR5 0x0B
|
||||
#define W5100_SN_DIPR0 0x0C
|
||||
#define W5100_SN_DIPR1 0x0D
|
||||
#define W5100_SN_DIPR2 0x0E
|
||||
|
|
Loading…
Add table
Reference in a new issue