mirror of
https://github.com/david-schmidt/gsport.git
synced 2025-02-21 12:29:07 +00:00
Merged revision(s) 262-316 from branches/pneubauer/appletalk: Add support for AppleTalk emulation and bridging.
This commit is contained in:
parent
20ca867301
commit
58cc512bc9
@ -17,6 +17,9 @@ GSport Release History
|
||||
|
||||
* Added Imagewriter LQ printer emulation
|
||||
|
||||
* Added AppleTalk networking emulation with bridging to EtherTalk.
|
||||
Related, there are improvements and bug fixes to the SCC emulation.
|
||||
|
||||
[]
|
||||
|
||||
Bug fixes:
|
||||
|
@ -494,9 +494,10 @@ you override it with "-audio 1".
|
||||
|
||||
* SCC (Serial Port) emulation
|
||||
|
||||
GSport emulates the two serial ports on a IIgs as being two Unix sockets.
|
||||
Port 1 (printer port) is at socket address 6501, and port 2 (modem)
|
||||
is at socket address 6502.
|
||||
You may use the SCC ports as either a LocalTalk networking connection
|
||||
or as traditional serial ports. GSport emulates the two serial ports on
|
||||
a IIgs as being two Unix sockets. Port 1 (printer port) is at socket
|
||||
address 6501, and port 2 (modem) is at socket address 6502.
|
||||
|
||||
By default, slot 1 is emulated using a simple receive socket, and slot 2
|
||||
emulates a Virtual Modem.
|
||||
|
BIN
lib/arch/win32/cygstdc++-6.dll
Normal file
BIN
lib/arch/win32/cygstdc++-6.dll
Normal file
Binary file not shown.
15
src/Makefile
15
src/Makefile
@ -3,7 +3,10 @@
|
||||
OBJECTS1 = adb.o clock.o config.o dis.o engine_c.o scc.o iwm.o \
|
||||
joystick_driver.o moremem.o paddles.o parallel.o printer.o \
|
||||
sim65816.o smartport.o sound.o sound_driver.o video.o \
|
||||
scc_socket_driver.o scc_imagewriter.o imagewriter.o
|
||||
scc_socket_driver.o imagewriter.o scc_imagewriter.o scc_llap.o
|
||||
ATOBJ = atbridge/aarp.o atbridge/atbridge.o atbridge/elap.o atbridge/llap.o atbridge/port.o
|
||||
PCAPOBJ = atbridge/pcap_delay.o
|
||||
TFEOBJ = tfe/tfe.o tfe/tfearch.o tfe/tfesupp.o
|
||||
|
||||
include vars
|
||||
|
||||
@ -139,12 +142,19 @@ compile_time.o: $(OBJECTS)
|
||||
|
||||
# dependency stuff
|
||||
adb.o: adb.c adb.h defc.h defcomm.h iwm.h protos.h
|
||||
atbridge/aarp.o: defc.h atbridge/atbridge.h atbridge/port.h atbridge/elap.h atbridge/aarp.h atbridge/elap_defs.h
|
||||
atbridge/atbridge.o: defc.h atbridge/atbridge.h atbridge/port.h atbridge/elap.h atbridge/llap.h atbridge/aarp.h
|
||||
atbridge/elap.o: defc.h atbridge/atbridge.h atbridge/port.h atbridge/elap.h atbridge/aarp.h atbridge/elap_defs.h atbridge/pcap_delay.h
|
||||
atbridge/llap.o: defc.h atbridge/atbridge.h atbridge/port.h atbridge/llap.h
|
||||
atbridge/port.o: atbridge/atalk.h atbridge/port.h
|
||||
atbridge/pcap_delay.o: atbridge/pcap_delay.h
|
||||
engine_c.o: engine_c.c defc.h defcomm.h iwm.h protos.h protos_engine_c.h size_c.h op_routs.h defs_instr.h 8inst_c.h 16inst_c.h
|
||||
clock.o: clock.c defc.h defcomm.h iwm.h protos.h
|
||||
compile_time.o: compile_time.c
|
||||
config.o: config.c defc.h defcomm.h iwm.h protos.h config.h
|
||||
dis.o: dis.c defc.h defcomm.h iwm.h protos.h disas.h
|
||||
scc.o: scc.c defc.h defcomm.h iwm.h protos.h scc.h
|
||||
scc_llap.o: atbridge/atbridge.h atbridge/llap.h defc.h scc.h
|
||||
scc_socket_driver.o: scc_socket_driver.c defc.h defcomm.h iwm.h protos.h scc.h
|
||||
scc_windriver.o: scc_windriver.c defc.h defcomm.h iwm.h protos.h scc.h
|
||||
scc_macdriver.o: scc_macdriver.c defc.h defcomm.h iwm.h protos.h scc.h
|
||||
@ -162,9 +172,8 @@ sound.o: sound.c defc.h defcomm.h iwm.h protos.h sound.h
|
||||
sound_driver.o: sound_driver.c defc.h defcomm.h iwm.h protos.h sound.h
|
||||
video.o: video.c defc.h defcomm.h iwm.h protos.h superhires.h gsportfont.h
|
||||
tfe.o: tfe/tfe.c tfe/tfe.h tfe/tfe_protos.h
|
||||
tfearch.o:arch/win32/tfearch.c tfe/tfearch.h tfe/tfe_protos.h
|
||||
tfearch.o: tfe/tfearch.c tfe/tfearch.h tfe/tfe_protos.h
|
||||
tfesupp.o: tfe/tfesupp.c tfe/tfesupp.h tfe/tfe_protos.h
|
||||
uilib.o: tfe/uilib.c tfe/uilib.h tfe/tfe_protos.h
|
||||
macdriver.o: macdriver.c defc.h defcomm.h iwm.h protos.h protos_macdriver.h
|
||||
macdriver_console.o: macdriver_console.c defc.h defcomm.h iwm.h protos.h protos_macdriver.h
|
||||
macdriver_generic.o: macdriver_generic.c defc.h defcomm.h iwm.h protos.h protos_macdriver.h
|
||||
|
BIN
src/SDL.dll
BIN
src/SDL.dll
Binary file not shown.
@ -1,563 +0,0 @@
|
||||
/*
|
||||
* tfearch.c - TFE ("The final ethernet") emulation,
|
||||
* architecture-dependant stuff
|
||||
*
|
||||
* Written by
|
||||
* Spiro Trikaliotis <Spiro.Trikaliotis@gmx.de>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/* #define WPCAP */
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN /* Tell windows we want less header gunk */
|
||||
#define STRICT /* Tell Windows we want compile type checks */
|
||||
#include <windows.h>
|
||||
|
||||
#include "pcap.h"
|
||||
#include "../../tfe/tfesupp.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "..\..\defc.h"
|
||||
#include "../../tfe/protos_tfe.h"
|
||||
|
||||
|
||||
typedef pcap_t *(*pcap_open_live_t)(const char *, int, int, int, char *);
|
||||
typedef int (*pcap_dispatch_t)(pcap_t *, int, pcap_handler, u_char *);
|
||||
typedef int (*pcap_setnonblock_t)(pcap_t *, int, char *);
|
||||
typedef int (*pcap_datalink_t)(pcap_t *);
|
||||
typedef int (*pcap_findalldevs_t)(pcap_if_t **, char *);
|
||||
typedef void (*pcap_freealldevs_t)(pcap_if_t *);
|
||||
typedef int (*pcap_sendpacket_t)(pcap_t *p, u_char *buf, int size);
|
||||
|
||||
/** #define TFE_DEBUG_ARCH 1 **/
|
||||
/** #define TFE_DEBUG_PKTDUMP 1 **/
|
||||
|
||||
/* #define TFE_DEBUG_FRAMES - might be defined in TFE.H! */
|
||||
|
||||
#define TFE_DEBUG_WARN 1 /* this should not be deactivated */
|
||||
|
||||
static pcap_open_live_t p_pcap_open_live;
|
||||
static pcap_dispatch_t p_pcap_dispatch;
|
||||
static pcap_setnonblock_t p_pcap_setnonblock;
|
||||
static pcap_findalldevs_t p_pcap_findalldevs;
|
||||
static pcap_freealldevs_t p_pcap_freealldevs;
|
||||
static pcap_sendpacket_t p_pcap_sendpacket;
|
||||
static pcap_datalink_t p_pcap_datalink;
|
||||
|
||||
static HINSTANCE pcap_library = NULL;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* variables needed */
|
||||
|
||||
|
||||
//static log_t tfe_arch_log = LOG_ERR;
|
||||
|
||||
|
||||
static pcap_if_t *TfePcapNextDev = NULL;
|
||||
static pcap_if_t *TfePcapAlldevs = NULL;
|
||||
static pcap_t *TfePcapFP = NULL;
|
||||
|
||||
static char TfePcapErrbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
#ifdef TFE_DEBUG_PKTDUMP
|
||||
|
||||
static
|
||||
void debug_output( const char *text, BYTE *what, int count )
|
||||
{
|
||||
char buffer[256];
|
||||
char *p = buffer;
|
||||
char *pbuffer1 = what;
|
||||
int len1 = count;
|
||||
int i;
|
||||
|
||||
sprintf(buffer, "\n%s: length = %u\n", text, len1);
|
||||
OutputDebugString(buffer);
|
||||
do {
|
||||
p = buffer;
|
||||
for (i=0; (i<8) && len1>0; len1--, i++) {
|
||||
sprintf( p, "%02x ", (unsigned int)(unsigned char)*pbuffer1++);
|
||||
p += 3;
|
||||
}
|
||||
*(p-1) = '\n'; *p = 0;
|
||||
OutputDebugString(buffer);
|
||||
} while (len1>0);
|
||||
}
|
||||
#endif // #ifdef TFE_DEBUG_PKTDUMP
|
||||
|
||||
|
||||
static
|
||||
void TfePcapFreeLibrary(void)
|
||||
{
|
||||
if (pcap_library) {
|
||||
if (!FreeLibrary(pcap_library)) {
|
||||
//log_message(tfe_arch_log, "FreeLibrary WPCAP.DLL failed!");
|
||||
}
|
||||
pcap_library = NULL;
|
||||
|
||||
p_pcap_open_live = NULL;
|
||||
p_pcap_dispatch = NULL;
|
||||
p_pcap_setnonblock = NULL;
|
||||
p_pcap_findalldevs = NULL;
|
||||
p_pcap_freealldevs = NULL;
|
||||
p_pcap_sendpacket = NULL;
|
||||
p_pcap_datalink = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* since I don't like typing too much... */
|
||||
#define GET_PROC_ADDRESS_AND_TEST( _name_ ) \
|
||||
p_##_name_ = (_name_##_t) GetProcAddress(pcap_library, #_name_ ); \
|
||||
if (!p_##_name_ ) { \
|
||||
/*log_message(tfe_arch_log, "GetProcAddress " #_name_ " failed!");*/ \
|
||||
TfePcapFreeLibrary(); \
|
||||
return FALSE; \
|
||||
}
|
||||
|
||||
static
|
||||
BOOL TfePcapLoadLibrary(void)
|
||||
{
|
||||
if (!pcap_library) {
|
||||
pcap_library = LoadLibrary("wpcap.dll");
|
||||
|
||||
if (!pcap_library) {
|
||||
//log_message(tfe_arch_log, "LoadLibrary WPCAP.DLL failed!" );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GET_PROC_ADDRESS_AND_TEST(pcap_open_live);
|
||||
GET_PROC_ADDRESS_AND_TEST(pcap_dispatch);
|
||||
GET_PROC_ADDRESS_AND_TEST(pcap_setnonblock);
|
||||
GET_PROC_ADDRESS_AND_TEST(pcap_findalldevs);
|
||||
GET_PROC_ADDRESS_AND_TEST(pcap_freealldevs);
|
||||
GET_PROC_ADDRESS_AND_TEST(pcap_sendpacket);
|
||||
GET_PROC_ADDRESS_AND_TEST(pcap_datalink);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#undef GET_PROC_ADDRESS_AND_TEST
|
||||
|
||||
|
||||
/*
|
||||
static
|
||||
void TfePcapCloseAdapter(void)
|
||||
{
|
||||
if (TfePcapAlldevs) {
|
||||
(*p_pcap_freealldevs)(TfePcapAlldevs);
|
||||
TfePcapAlldevs = NULL;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
These functions let the UI enumerate the available interfaces.
|
||||
|
||||
First, TfeEnumAdapterOpen() is used to start enumeration.
|
||||
|
||||
TfeEnumAdapter 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().
|
||||
|
||||
TfeEnumAdapterClose() must be used to stop processing.
|
||||
|
||||
Each function returns 1 on success, and 0 on failure.
|
||||
TfeEnumAdapter() only fails if there is no more adpater; in this case,
|
||||
*ppname and *ppdescription are not altered.
|
||||
*/
|
||||
int tfe_arch_enumadapter_open(void)
|
||||
{
|
||||
if (!TfePcapLoadLibrary()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((*p_pcap_findalldevs)(&TfePcapAlldevs, TfePcapErrbuf) == -1)
|
||||
{
|
||||
//log_message(tfe_arch_log, "ERROR in TfeEnumAdapterOpen: pcap_findalldevs: '%s'", TfePcapErrbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!TfePcapAlldevs) {
|
||||
/*log_message(tfe_arch_log, "ERROR in TfeEnumAdapterOpen, finding all pcap devices - "
|
||||
"Do we have the necessary privilege rights?");*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
TfePcapNextDev = TfePcapAlldevs;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int tfe_arch_enumadapter(char **ppname, char **ppdescription)
|
||||
{
|
||||
if (!TfePcapNextDev)
|
||||
return 0;
|
||||
|
||||
*ppname = lib_stralloc(TfePcapNextDev->name);
|
||||
*ppdescription = lib_stralloc(TfePcapNextDev->description);
|
||||
|
||||
TfePcapNextDev = TfePcapNextDev->next;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int tfe_arch_enumadapter_close(void)
|
||||
{
|
||||
if (TfePcapAlldevs) {
|
||||
(*p_pcap_freealldevs)(TfePcapAlldevs);
|
||||
TfePcapAlldevs = NULL;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static
|
||||
BOOL TfePcapOpenAdapter(const char *interface_name)
|
||||
{
|
||||
pcap_if_t *TfePcapDevice = NULL;
|
||||
|
||||
if (!tfe_enumadapter_open()) {
|
||||
return FALSE;
|
||||
}
|
||||
else {
|
||||
/* look if we can find the specified adapter */
|
||||
char *pname;
|
||||
char *pdescription;
|
||||
BOOL found = FALSE;
|
||||
|
||||
if (interface_name) {
|
||||
/* we have an interface name, try it */
|
||||
TfePcapDevice = TfePcapAlldevs;
|
||||
|
||||
while (tfe_enumadapter(&pname, &pdescription)) {
|
||||
if (strcmp(pname, interface_name)==0) {
|
||||
found = TRUE;
|
||||
}
|
||||
lib_free(pname);
|
||||
lib_free(pdescription);
|
||||
if (found) break;
|
||||
TfePcapDevice = TfePcapNextDev;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
/* just take the first adapter */
|
||||
TfePcapDevice = TfePcapAlldevs;
|
||||
}
|
||||
}
|
||||
|
||||
TfePcapFP = (*p_pcap_open_live)(TfePcapDevice->name, 1700, 1, 20, TfePcapErrbuf);
|
||||
if ( TfePcapFP == NULL)
|
||||
{
|
||||
//log_message(tfe_arch_log, "ERROR opening adapter: '%s'", TfePcapErrbuf);
|
||||
tfe_enumadapter_close();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ((*p_pcap_setnonblock)(TfePcapFP, 1, TfePcapErrbuf)<0)
|
||||
{
|
||||
//log_message(tfe_arch_log, "WARNING: Setting PCAP to non-blocking failed: '%s'", TfePcapErrbuf);
|
||||
}
|
||||
|
||||
/* Check the link layer. We support only Ethernet for simplicity. */
|
||||
if((*p_pcap_datalink)(TfePcapFP) != DLT_EN10MB)
|
||||
{
|
||||
//log_message(tfe_arch_log, "ERROR: TFE works only on Ethernet networks.");
|
||||
tfe_enumadapter_close();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
tfe_enumadapter_close();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* the architecture-dependend functions */
|
||||
|
||||
|
||||
int tfe_arch_init(void)
|
||||
{
|
||||
//tfe_arch_log = log_open("TFEARCH");
|
||||
|
||||
if (!TfePcapLoadLibrary()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void tfe_arch_pre_reset( void )
|
||||
{
|
||||
#ifdef TFE_DEBUG_ARCH
|
||||
log_message( tfe_arch_log, "tfe_arch_pre_reset()." );
|
||||
#endif
|
||||
}
|
||||
|
||||
void tfe_arch_post_reset( void )
|
||||
{
|
||||
#ifdef TFE_DEBUG_ARCH
|
||||
log_message( tfe_arch_log, "tfe_arch_post_reset()." );
|
||||
#endif
|
||||
}
|
||||
|
||||
int tfe_arch_activate(const char *interface_name)
|
||||
{
|
||||
#ifdef TFE_DEBUG_ARCH
|
||||
log_message( tfe_arch_log, "tfe_arch_activate()." );
|
||||
#endif
|
||||
if (!TfePcapOpenAdapter(interface_name)) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void tfe_arch_deactivate( void )
|
||||
{
|
||||
#ifdef TFE_DEBUG_ARCH
|
||||
log_message( tfe_arch_log, "tfe_arch_deactivate()." );
|
||||
#endif
|
||||
}
|
||||
|
||||
void tfe_arch_set_mac( const BYTE mac[6] )
|
||||
{
|
||||
#if defined(TFE_DEBUG_ARCH) || defined(TFE_DEBUG_FRAMES)
|
||||
log_message( tfe_arch_log, "New MAC address set: %02X:%02X:%02X:%02X:%02X:%02X.",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] );
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
void tfe_arch_set_hashfilter(const DWORD hash_mask[2])
|
||||
{
|
||||
#if defined(TFE_DEBUG_ARCH) || defined(TFE_DEBUG_FRAMES)
|
||||
log_message( tfe_arch_log, "New hash filter set: %08X:%08X.",
|
||||
hash_mask[1], hash_mask[0]);
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
void tfe_arch_receive_remove_committed_frame(void)
|
||||
{
|
||||
#ifdef TFE_DEBUG_ARCH
|
||||
log_message( tfe_arch_log, "tfe_arch_receive_remove_committed_frame()." );
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
|
||||
void tfe_arch_recv_ctl( int bBroadcast, /* broadcast */
|
||||
int bIA, /* individual address (IA) */
|
||||
int bMulticast, /* multicast if address passes the hash filter */
|
||||
int bCorrect, /* accept correct frames */
|
||||
int bPromiscuous, /* promiscuous mode */
|
||||
int bIAHash /* accept if IA passes the hash filter */
|
||||
)
|
||||
{
|
||||
#if defined(TFE_DEBUG_ARCH) || defined(TFE_DEBUG_FRAMES)
|
||||
log_message( tfe_arch_log, "tfe_arch_recv_ctl() called with the following parameters:" );
|
||||
log_message( tfe_arch_log, "\tbBroadcast = %s", bBroadcast ? "TRUE" : "FALSE" );
|
||||
log_message( tfe_arch_log, "\tbIA = %s", bIA ? "TRUE" : "FALSE" );
|
||||
log_message( tfe_arch_log, "\tbMulticast = %s", bMulticast ? "TRUE" : "FALSE" );
|
||||
log_message( tfe_arch_log, "\tbCorrect = %s", bCorrect ? "TRUE" : "FALSE" );
|
||||
log_message( tfe_arch_log, "\tbPromiscuous = %s", bPromiscuous ? "TRUE" : "FALSE" );
|
||||
log_message( tfe_arch_log, "\tbIAHash = %s", bIAHash ? "TRUE" : "FALSE" );
|
||||
#endif
|
||||
}
|
||||
|
||||
void tfe_arch_line_ctl(int bEnableTransmitter, int bEnableReceiver )
|
||||
{
|
||||
#if defined(TFE_DEBUG_ARCH) || defined(TFE_DEBUG_FRAMES)
|
||||
log_message( tfe_arch_log, "tfe_arch_line_ctl() called with the following parameters:" );
|
||||
log_message( tfe_arch_log, "\tbEnableTransmitter = %s", bEnableTransmitter ? "TRUE" : "FALSE" );
|
||||
log_message( tfe_arch_log, "\tbEnableReceiver = %s", bEnableReceiver ? "TRUE" : "FALSE" );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
typedef struct TFE_PCAP_INTERNAL_tag {
|
||||
|
||||
unsigned int len;
|
||||
BYTE *buffer;
|
||||
|
||||
} TFE_PCAP_INTERNAL;
|
||||
|
||||
/* Callback function invoked by libpcap for every incoming packet */
|
||||
static
|
||||
void TfePcapPacketHandler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
|
||||
{
|
||||
TFE_PCAP_INTERNAL *pinternal = (TFE_PCAP_INTERNAL*)param;
|
||||
|
||||
/* 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;
|
||||
|
||||
memcpy(pinternal->buffer, pkt_data, pinternal->len);
|
||||
}
|
||||
|
||||
/* the following function receives a frame.
|
||||
|
||||
If there's none, it returns a -1.
|
||||
If there is one, it returns the length of the frame in bytes.
|
||||
|
||||
It copies the frame to *buffer and returns the number of copied
|
||||
bytes as return value.
|
||||
|
||||
At most 'len' bytes are copied.
|
||||
*/
|
||||
static
|
||||
int tfe_arch_receive_frame(TFE_PCAP_INTERNAL *pinternal)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
/* check if there is something to receive */
|
||||
if ((*p_pcap_dispatch)(TfePcapFP, 1, (pcap_handler)TfePcapPacketHandler, (u_char*)pinternal)!=0) {
|
||||
/* Something has been received */
|
||||
ret = pinternal->len;
|
||||
}
|
||||
|
||||
#ifdef TFE_DEBUG_ARCH
|
||||
log_message( tfe_arch_log, "tfe_arch_receive_frame() called, returns %d.", ret );
|
||||
#endif
|
||||
|
||||
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 */
|
||||
int txlength, /* Frame length */
|
||||
BYTE *txframe /* Pointer to the frame to be transmitted */
|
||||
)
|
||||
{
|
||||
#ifdef TFE_DEBUG_ARCH
|
||||
log_message( tfe_arch_log, "tfe_arch_transmit() called, with: "
|
||||
"force = %s, onecoll = %s, inhibit_crc=%s, tx_pad_dis=%s, txlength=%u",
|
||||
force ? "TRUE" : "FALSE",
|
||||
onecoll ? "TRUE" : "FALSE",
|
||||
inhibit_crc ? "TRUE" : "FALSE",
|
||||
tx_pad_dis ? "TRUE" : "FALSE",
|
||||
txlength
|
||||
);
|
||||
#endif
|
||||
|
||||
#ifdef TFE_DEBUG_PKTDUMP
|
||||
debug_output( "Transmit frame: ", txframe, txlength);
|
||||
#endif // #ifdef TFE_DEBUG_PKTDUMP
|
||||
|
||||
if ((*p_pcap_sendpacket)(TfePcapFP, txframe, txlength) == -1) {
|
||||
//log_message(tfe_arch_log, "WARNING! Could not send packet!");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
tfe_arch_receive()
|
||||
|
||||
This function checks if there was a frame received.
|
||||
If so, it returns 1, else 0.
|
||||
|
||||
If there was no frame, none of the parameters is changed!
|
||||
|
||||
If there was a frame, the following actions are done:
|
||||
|
||||
- at maximum *plen byte are transferred into the buffer given by pbuffer
|
||||
- *plen gets the length of the received frame, EVEN if this is more
|
||||
than has been copied to pbuffer!
|
||||
- if the dest. address was accepted by the hash filter, *phashed is set, else
|
||||
cleared.
|
||||
- if the dest. address was accepted by the hash filter, *phash_index is
|
||||
set to the number of the rule leading to the acceptance
|
||||
- if the receive was ok (good CRC and valid length), *prx_ok is set,
|
||||
else cleared.
|
||||
- if the dest. address was accepted because it's exactly our MAC address
|
||||
(set by tfe_arch_set_mac()), *pcorrect_mac is set, else cleared.
|
||||
- if the dest. address was accepted since it was a broadcast address,
|
||||
*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 len;
|
||||
|
||||
TFE_PCAP_INTERNAL internal = { *plen, pbuffer };
|
||||
|
||||
|
||||
#ifdef TFE_DEBUG_ARCH
|
||||
log_message( tfe_arch_log, "tfe_arch_receive() called, with *plen=%u.", *plen );
|
||||
#endif
|
||||
|
||||
assert((*plen&1)==0);
|
||||
|
||||
len = tfe_arch_receive_frame(&internal);
|
||||
|
||||
if (len!=-1) {
|
||||
|
||||
#ifdef TFE_DEBUG_PKTDUMP
|
||||
debug_output( "Received frame: ", internal.buffer, internal.len );
|
||||
#endif // #ifdef TFE_DEBUG_PKTDUMP
|
||||
|
||||
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 0;
|
||||
}
|
302
src/atbridge/aarp.c
Normal file
302
src/atbridge/aarp.c
Normal file
@ -0,0 +1,302 @@
|
||||
/*
|
||||
GSport - an Apple //gs Emulator
|
||||
Copyright (C) 2013-2014 by Peter Neubauer
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
/** This module implements AARP, a necessary protocol for ELAP communication. **/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include "../defc.h"
|
||||
#include "atbridge.h"
|
||||
#include "elap.h"
|
||||
#include "port.h"
|
||||
#include "elap_defs.h"
|
||||
#include "aarp.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock.h>
|
||||
#elif __linux__
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
struct amt_entry_t
|
||||
{
|
||||
struct at_addr_t protocol;
|
||||
struct ether_addr_t hardware;
|
||||
|
||||
struct amt_entry_t* next;
|
||||
};
|
||||
|
||||
typedef struct amt_entry_t* amt_t;
|
||||
|
||||
static amt_t amt = 0;
|
||||
|
||||
static unsigned int retry_count;
|
||||
static clock_t retry_timer;
|
||||
|
||||
|
||||
void aarp_init()
|
||||
{
|
||||
aarp_retry_reset();
|
||||
}
|
||||
|
||||
void aarp_shutdown()
|
||||
{
|
||||
struct amt_entry_t* entry = amt;
|
||||
while (entry)
|
||||
{
|
||||
struct amt_entry_t* next = entry->next;
|
||||
free(entry);
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
static void aarp_send_packet(enum AARP_FUNCTION function, const struct at_addr_t* source_at_addr, const struct at_addr_t* dest_at_addr, const struct ether_addr_t* dest_hw_addr)
|
||||
{
|
||||
if (source_at_addr && dest_at_addr && dest_hw_addr)
|
||||
{
|
||||
struct aarp_header_t response;
|
||||
response.hardware_type = htons(AARP_HARDWARE_ETHER);
|
||||
response.protocol_type = htons(AARP_PROTOCOL_TYPE);
|
||||
response.hw_addr_len = AARP_HW_ADDR_LEN;
|
||||
response.protocol_addr_len = AARP_PROTOCOL_ADDR_LEN;
|
||||
response.function = htons(function);
|
||||
|
||||
memcpy(&response.source_proto_addr.addr, source_at_addr, sizeof(response.source_proto_addr.addr));
|
||||
response.source_proto_addr.addr.network = htons(response.source_proto_addr.addr.network);
|
||||
response.source_proto_addr.zero = 0x00;
|
||||
|
||||
memcpy(&response.dest_proto_addr.addr, dest_at_addr, sizeof(response.dest_proto_addr.addr));
|
||||
response.dest_proto_addr.addr.network = htons(response.dest_proto_addr.addr.network);
|
||||
response.dest_proto_addr.zero = 0x00;
|
||||
|
||||
memcpy(response.source_hw_addr.mac, elap_get_mac()->mac, sizeof(response.source_hw_addr.mac));
|
||||
|
||||
memcpy(response.dest_hw_addr.mac, &dest_hw_addr->mac, sizeof(response.dest_hw_addr.mac));
|
||||
|
||||
if (dest_hw_addr == &HW_ZERO)
|
||||
elap_send(&HW_APPLETALK_BROADCAST, &SNAP_AARP, sizeof(struct aarp_header_t), (byte*)&response);
|
||||
else
|
||||
elap_send(&response.dest_hw_addr, &SNAP_AARP, sizeof(struct aarp_header_t), (byte*)&response);
|
||||
}
|
||||
}
|
||||
|
||||
void aarp_probe(const struct at_addr_t* addr)
|
||||
{
|
||||
if (addr)
|
||||
{
|
||||
aarp_send_packet(AARP_FUNCTION_PROBE, addr, addr, &HW_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
static void aarp_request(const struct at_addr_t* addr)
|
||||
{
|
||||
if (addr)
|
||||
{
|
||||
aarp_send_packet(AARP_FUNCTION_REQUEST, atbridge_get_addr(), addr, &HW_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
static struct amt_entry_t* amt_lookup_entry_hardware(const struct ether_addr_t* hardware)
|
||||
{
|
||||
if (hardware)
|
||||
{
|
||||
struct amt_entry_t* entry = amt;
|
||||
while (entry)
|
||||
{
|
||||
if (memcmp(&entry->hardware, hardware, sizeof(entry->hardware)) == 0)
|
||||
return entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct amt_entry_t* amt_lookup_entry_protocol(const struct at_addr_t* protocol)
|
||||
{
|
||||
if (protocol)
|
||||
{
|
||||
struct amt_entry_t* entry = amt;
|
||||
while (entry)
|
||||
{
|
||||
if (memcmp(&entry->protocol, protocol, sizeof(entry->protocol)) == 0)
|
||||
return entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void amt_delete_entry_protocol(const struct at_addr_t* protocol)
|
||||
{
|
||||
if (protocol)
|
||||
{
|
||||
struct amt_entry_t* entry = amt;
|
||||
struct amt_entry_t* previous = amt;
|
||||
while (entry)
|
||||
{
|
||||
if (memcmp(&entry->protocol, protocol, sizeof(entry->protocol)) == 0)
|
||||
{
|
||||
previous->next = entry->next;
|
||||
free(entry);
|
||||
break;
|
||||
}
|
||||
previous = entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void amt_add(const struct at_addr_t* protocol, const struct ether_addr_t* hardware)
|
||||
{
|
||||
// Does an entry matching one of the protocol or hardware addresses exist? If so, update it.
|
||||
struct amt_entry_t* entry = amt_lookup_entry_protocol(protocol);
|
||||
if (entry)
|
||||
{
|
||||
memcpy(&entry->hardware, hardware, sizeof(entry->hardware));
|
||||
return;
|
||||
}
|
||||
|
||||
entry = amt_lookup_entry_hardware(hardware);
|
||||
if (entry)
|
||||
{
|
||||
memcpy(&entry->protocol, protocol, sizeof(entry->protocol));
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, add a new entry.
|
||||
entry = (struct amt_entry_t*)malloc(sizeof(struct amt_entry_t));
|
||||
memcpy(&entry->hardware, hardware, sizeof(entry->hardware));
|
||||
memcpy(&entry->protocol, protocol, sizeof(entry->protocol));
|
||||
entry->next = amt;
|
||||
amt = entry;
|
||||
}
|
||||
|
||||
const struct ether_addr_t* aarp_request_hardware(const struct at_addr_t* protocol)
|
||||
{
|
||||
struct amt_entry_t* entry = amt_lookup_entry_protocol(protocol);
|
||||
if (entry)
|
||||
{
|
||||
aarp_retry_reset();
|
||||
return (const struct ether_addr_t*)&entry->hardware;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The AMT doesn't have this protocol address so issue a request at no more than the AARP_PROBE_INTERVAL period.
|
||||
if (((clock() - retry_timer) >= (AARP_REQUEST_INTERVAL * CLOCKS_PER_SEC / 1000)) &&
|
||||
(retry_count > 0))
|
||||
{
|
||||
aarp_request(protocol);
|
||||
|
||||
retry_count--;
|
||||
retry_timer = clock();
|
||||
|
||||
//atbridge_printf("AARP request count %d timer %d.\n", retry_count, retry_timer);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const struct at_addr_t* aarp_request_protocol(const struct ether_addr_t* hardware)
|
||||
{
|
||||
struct amt_entry_t* entry = amt_lookup_entry_hardware(hardware);
|
||||
if (entry)
|
||||
return (const struct at_addr_t*)&entry->protocol;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool aarp_retry()
|
||||
{
|
||||
return retry_count > 0;
|
||||
}
|
||||
|
||||
void aarp_retry_reset()
|
||||
{
|
||||
retry_count = AARP_REQUEST_COUNT;
|
||||
retry_timer = clock();
|
||||
}
|
||||
|
||||
void aarp_glean(const struct at_addr_t* protocol, const struct ether_addr_t* hardware)
|
||||
{
|
||||
amt_add(protocol, hardware);
|
||||
}
|
||||
|
||||
bool aarp_address_used(const struct at_addr_t* protocol)
|
||||
{
|
||||
// reference 2-8
|
||||
if (protocol)
|
||||
{
|
||||
// Check for reserved node numbers, per reference 3-9.
|
||||
if (protocol->node == 0x00 || protocol->node == 0xfe || protocol->node == 0xff)
|
||||
return true;
|
||||
|
||||
// Look for the address in the AMT. If it's there, another node is using this address.
|
||||
struct amt_entry_t* entry = amt_lookup_entry_protocol(protocol);
|
||||
if (entry)
|
||||
return true;
|
||||
|
||||
// Try a probe. If this address is in use, another node will reply with an AARP RESPONSE packet.
|
||||
// Return true to advise the caller that the address is not known to be in use. The caller should
|
||||
// retry aarp_try_address() every 200 ms (AARP_PROBE_INTERVAL) and 10 times (AARP_PROBE_COUNT),
|
||||
// per the AARP protocol definition, before choosing this address.
|
||||
aarp_probe(protocol);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
void aarp_handle_packet(const struct aarp_header_t* aarp)
|
||||
{
|
||||
if (aarp &&
|
||||
aarp->hardware_type == AARP_HARDWARE_ETHER &&
|
||||
aarp->protocol_type == AARP_PROTOCOL_TYPE &&
|
||||
aarp->hw_addr_len == AARP_HW_ADDR_LEN &&
|
||||
aarp->protocol_addr_len == AARP_PROTOCOL_ADDR_LEN)
|
||||
{
|
||||
switch (aarp->function)
|
||||
{
|
||||
case AARP_FUNCTION_REQUEST:
|
||||
if (((aarp->dest_proto_addr.addr.network == atbridge_get_net()) ||
|
||||
(aarp->dest_proto_addr.addr.network == 0x00 /* reference 4-6 */)) &&
|
||||
(aarp->dest_proto_addr.addr.node == atbridge_get_node()))
|
||||
{
|
||||
// Generate a response for the AARP request.
|
||||
aarp_send_packet(AARP_FUNCTION_RESPONSE, &aarp->dest_proto_addr.addr, &aarp->source_proto_addr.addr, &aarp->source_hw_addr);
|
||||
}
|
||||
break;
|
||||
case AARP_FUNCTION_RESPONSE:
|
||||
aarp_glean(&aarp->source_proto_addr.addr, &aarp->source_hw_addr);
|
||||
aarp_glean(&aarp->dest_proto_addr.addr, &aarp->dest_hw_addr);
|
||||
break;
|
||||
case AARP_FUNCTION_PROBE:
|
||||
// AMT entry aging, method 2, reference 2-11
|
||||
amt_delete_entry_protocol(&aarp->dest_proto_addr.addr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
37
src/atbridge/aarp.h
Normal file
37
src/atbridge/aarp.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
GSport - an Apple //gs Emulator
|
||||
Copyright (C) 2013-2014 by Peter Neubauer
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
struct at_addr_t;
|
||||
struct aarp_header_t;
|
||||
struct ether_addr_t;
|
||||
|
||||
void aarp_init();
|
||||
void aarp_shutdown();
|
||||
|
||||
const struct ether_addr_t* aarp_request_hardware(const struct at_addr_t* protocol);
|
||||
const struct at_addr_t* aarp_request_protocol(const struct ether_addr_t* hardware);
|
||||
|
||||
bool aarp_retry();
|
||||
void aarp_retry_reset();
|
||||
|
||||
void aarp_glean(const struct at_addr_t* protocol, const struct ether_addr_t* hardware);
|
||||
|
||||
bool aarp_address_used(const struct at_addr_t* protocol);
|
||||
|
||||
void aarp_handle_packet(const struct aarp_header_t* aarp);
|
137
src/atbridge/atalk.h
Normal file
137
src/atbridge/atalk.h
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
GSport - an Apple //gs Emulator
|
||||
Copyright (C) 2013-2014 by Peter Neubauer
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
typedef byte at_node_t;
|
||||
static const at_node_t at_broadcast_node = 0xFF;
|
||||
|
||||
typedef word16 at_network_t;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct at_addr_t
|
||||
{
|
||||
at_network_t network;
|
||||
at_node_t node;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
enum LAP_TYPES { /* reference C-6 */
|
||||
LAP_DDP_SHORT = 0x01,
|
||||
LAP_DDP_LONG = 0x02
|
||||
};
|
||||
|
||||
enum DDP_SOCKETS { /* reference C-7 */
|
||||
DDP_SOCKET_INVALID_00 = 0x00,
|
||||
DDP_SOCKET_RTMP = 0x01,
|
||||
DDP_SOCKET_NIS = 0x02,
|
||||
DDP_SOCKET_ECHO = 0x04,
|
||||
DDP_SOCKET_ZIS = 0x06,
|
||||
DDP_SOCKET_INVALID_FF = 0xFF,
|
||||
};
|
||||
|
||||
enum DDP_TYPES { /* reference C-6 */
|
||||
DDP_TYPE_INVALID = 0x00,
|
||||
DDP_TYPE_RTMP = 0x01,
|
||||
DDP_TYPE_NBP = 0x02,
|
||||
DDP_TYPE_ATP = 0x03,
|
||||
DDP_TYPE_AEP = 0x04,
|
||||
DDP_TYPE_RTMP_REQUEST = 0x05,
|
||||
DDP_TYPE_ZIP = 0x06,
|
||||
DDP_TYPE_ADSP = 0x07,
|
||||
DDP_TYPE_RESERVED_08 = 0x08,
|
||||
DDP_TYPE_RESERVED_09 = 0x09,
|
||||
DDP_TYPE_RESERVED_0A = 0x0A,
|
||||
DDP_TYPE_RESERVED_0B = 0x0B,
|
||||
DDP_TYPE_RESERVED_0C = 0x0C,
|
||||
DDP_TYPE_RESERVED_0D = 0x0D,
|
||||
DDP_TYPE_RESERVED_0E = 0x0E,
|
||||
DDP_TYPE_RESERVED_0F = 0x0F
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct DDP_LONG
|
||||
{
|
||||
byte length[2];
|
||||
word16 checksum;
|
||||
at_network_t dest_net;
|
||||
at_network_t source_net;
|
||||
at_node_t dest_node;
|
||||
at_node_t source_node;
|
||||
byte dest_socket;
|
||||
byte source_socket;
|
||||
byte type;
|
||||
};
|
||||
|
||||
struct DDP_SHORT
|
||||
{
|
||||
byte length[2];
|
||||
byte dest_socket;
|
||||
byte source_socket;
|
||||
byte type;
|
||||
};
|
||||
|
||||
enum RTMP_FUNCTIONS { /* reference C-8*/
|
||||
RTMP_FUNCTION_REQUEST = 0x01,
|
||||
RTMP_FUNCTION_RDR_SPLIT = 0x02,
|
||||
RTMP_FUNCTION_RDR_NO_SPLIT = 0x03
|
||||
};
|
||||
|
||||
struct rtmp_request_t
|
||||
{
|
||||
byte function;
|
||||
};
|
||||
|
||||
struct rtmp_nonextended_data_t
|
||||
{
|
||||
at_network_t net;
|
||||
byte id_length;
|
||||
at_node_t node;
|
||||
word16 zero;
|
||||
byte delimiter;
|
||||
};
|
||||
|
||||
struct rtmp_nonextended_response_t
|
||||
{
|
||||
at_network_t net;
|
||||
byte id_length;
|
||||
at_node_t node;
|
||||
};
|
||||
|
||||
struct rtmp_extended_data_t
|
||||
{
|
||||
at_network_t net;
|
||||
byte id_length;
|
||||
at_node_t node;
|
||||
};
|
||||
|
||||
struct rtmp_nonextended_tuple_t
|
||||
{
|
||||
at_network_t net;
|
||||
byte distance;
|
||||
};
|
||||
|
||||
struct rtmp_extended_tuple_t
|
||||
{
|
||||
at_network_t range_start;
|
||||
byte distance;
|
||||
at_network_t range_end;
|
||||
byte delimiter;
|
||||
};
|
||||
|
||||
static const byte RTMP_TUPLE_DELIMITER = 0x82;
|
||||
#pragma pack(pop)
|
426
src/atbridge/atbridge.c
Normal file
426
src/atbridge/atbridge.c
Normal file
@ -0,0 +1,426 @@
|
||||
/*
|
||||
GSport - an Apple //gs Emulator
|
||||
Copyright (C) 2013-2014 by Peter Neubauer
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
/** This module is the "heart" of the bridge and provides the connection between the ELAP and LLAP ports. **/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "../defc.h"
|
||||
#include <stddef.h>
|
||||
#include <time.h>
|
||||
#include "atbridge.h"
|
||||
#include "port.h"
|
||||
#include "elap.h"
|
||||
#include "llap.h"
|
||||
#include "aarp.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock.h>
|
||||
#elif __linux__
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
extern struct packet_port_t elap_port;
|
||||
|
||||
static bool diagnostics = false;
|
||||
static bool sent_rtmp_request = false;
|
||||
|
||||
static struct at_addr_t local_address = { 0, 0 };
|
||||
|
||||
static const at_network_t NET_STARTUP_LOW = 0xFF00;
|
||||
static const at_network_t NET_STARTUP_HIGH = 0xFFFE;
|
||||
static const at_node_t NODE_STARTUP_LOW = 0x01;
|
||||
static const at_node_t NODE_STARTUP_HIGH = 0xFE;
|
||||
|
||||
static void send_rtmp_request();
|
||||
|
||||
bool atbridge_init()
|
||||
{
|
||||
// If the GS reboots, we may try to reinitialize the bridge. If this is the case, keep the old address and AMT.
|
||||
if (local_address.network == 0)
|
||||
{
|
||||
// Obtain a provisional node address and startup range network.
|
||||
//
|
||||
// This isn't correct for an extended network (like ELAP) but works adequately on small networks.
|
||||
// The bridge should follow the complicated process on page 4-9 to obtain the network and node number.
|
||||
srand((unsigned int)time(0));
|
||||
local_address.network = (at_network_t)((double)rand()/RAND_MAX * (NET_STARTUP_HIGH - NET_STARTUP_LOW) + NET_STARTUP_LOW);
|
||||
local_address.node = (at_node_t)((double)rand()/RAND_MAX + (NODE_STARTUP_HIGH - NODE_STARTUP_LOW) + 0x01);
|
||||
|
||||
aarp_init();
|
||||
llap_init();
|
||||
if (!elap_init())
|
||||
{
|
||||
atbridge_shutdown();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void atbridge_shutdown()
|
||||
{
|
||||
llap_shutdown();
|
||||
elap_shutdown();
|
||||
aarp_shutdown();
|
||||
}
|
||||
|
||||
void atbridge_set_diagnostics(bool enabled)
|
||||
{
|
||||
diagnostics = enabled;
|
||||
}
|
||||
|
||||
bool atbridge_get_diagnostics()
|
||||
{
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
void atbridge_printf(const char *fmt, ...)
|
||||
{
|
||||
if (atbridge_get_diagnostics())
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
const struct at_addr_t* atbridge_get_addr()
|
||||
{
|
||||
return &local_address;
|
||||
}
|
||||
|
||||
const at_network_t atbridge_get_net()
|
||||
{
|
||||
return local_address.network;
|
||||
}
|
||||
|
||||
const at_node_t atbridge_get_node()
|
||||
{
|
||||
return local_address.node;
|
||||
}
|
||||
|
||||
void atbridge_set_net(at_network_t net)
|
||||
{
|
||||
local_address.network = net;
|
||||
}
|
||||
|
||||
void atbridge_set_node(at_node_t node)
|
||||
{
|
||||
local_address.node = node;
|
||||
}
|
||||
|
||||
bool atbridge_address_used(const struct at_addr_t* addr)
|
||||
{
|
||||
if (!sent_rtmp_request)
|
||||
send_rtmp_request();
|
||||
return aarp_address_used(addr);
|
||||
}
|
||||
|
||||
/* Calculate a DDP checksum, per Apple's documented algorithm in 4-17 of "Inside AppleTalk". */
|
||||
static word16 get_checksum(size_t size, byte data[])
|
||||
{
|
||||
word16 cksum = 0;
|
||||
for (unsigned int i = 0; i < size; i++)
|
||||
{
|
||||
cksum += data[i];
|
||||
cksum = (cksum << 1) | ((cksum & 0x8000) >> 15); // roll left
|
||||
}
|
||||
if (cksum == 0)
|
||||
cksum = 0xffff;
|
||||
return cksum;
|
||||
}
|
||||
|
||||
static void calculate_checksum(struct packet_t* packet)
|
||||
{
|
||||
if (packet && packet->data && (packet->size >= sizeof(struct DDP_LONG)) && (packet->type == LAP_DDP_LONG))
|
||||
{
|
||||
struct DDP_LONG* header = (struct DDP_LONG*)(packet->data);
|
||||
header->checksum = htons(get_checksum(
|
||||
packet->size - offsetof(struct DDP_LONG, dest_net),
|
||||
(byte*)&header->dest_net));
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert a long-form DDP header to a short-form header. This function only converts the headers. */
|
||||
static word16 convert_ddp_header_to_short(const struct DDP_LONG* in, struct DDP_SHORT* out)
|
||||
{
|
||||
word16 size;
|
||||
|
||||
if (!in || !out)
|
||||
return 0;
|
||||
|
||||
size = ((in->length[0] & 0x3) << 8) + (in->length[1]) - (sizeof(struct DDP_LONG) - sizeof(struct DDP_SHORT));
|
||||
|
||||
out->length[0] = (size >> 8) & 0x03;
|
||||
out->length[1] = size & 0xff;
|
||||
|
||||
out->dest_socket = in->dest_socket;
|
||||
out->source_socket = in->source_socket;
|
||||
|
||||
out->type = in->type;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/* Convert a short-form DDP header to a long-form header. ELAP requires long-form, but LLAP often uses short-form. */
|
||||
/* This function only converts the headers. */
|
||||
static word16 convert_ddp_header_to_long(const struct at_addr_t dest, const struct at_addr_t source, const struct DDP_SHORT* in, struct DDP_LONG* out)
|
||||
{
|
||||
word16 size;
|
||||
|
||||
if (!in || !out)
|
||||
return 0;
|
||||
|
||||
size = ((in->length[0] & 0x3) << 8) + (in->length[1]) + (sizeof(struct DDP_LONG) - sizeof(struct DDP_SHORT));
|
||||
out->length[0] = (size >> 8) & 0x03;
|
||||
out->length[1] = size & 0xff;
|
||||
|
||||
out->checksum = 0x0000; /* 0x0000 == no checksum calculated, reference 4-17 */
|
||||
|
||||
if (dest.network)
|
||||
out->dest_net = dest.network;
|
||||
else
|
||||
out->dest_net = atbridge_get_net();
|
||||
out->dest_net = (at_network_t)htons(out->dest_net);
|
||||
|
||||
if (source.network)
|
||||
out->source_net = source.network;
|
||||
else
|
||||
out->source_net = atbridge_get_net();
|
||||
out->source_net = (at_network_t)htons(out->source_net);
|
||||
|
||||
out->dest_node = dest.node;
|
||||
out->source_node = source.node;
|
||||
|
||||
out->dest_socket = in->dest_socket;
|
||||
out->source_socket = in->source_socket;
|
||||
|
||||
out->type = in->type;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/* Convert a short-form DDP packet to a long-form packet. */
|
||||
/* This function converts an entire packet, not just the header. */
|
||||
static void convert_ddp_packet_to_long(struct packet_t* packet)
|
||||
{
|
||||
if (packet && (packet->type == LAP_DDP_SHORT) && packet->data && (packet->size >= sizeof(struct DDP_SHORT)))
|
||||
{
|
||||
struct DDP_SHORT* header_short = (struct DDP_SHORT*)packet->data;
|
||||
|
||||
const size_t payload_size = packet->size - sizeof(struct DDP_SHORT);
|
||||
byte* data = (byte*)malloc(payload_size + sizeof(struct DDP_LONG));
|
||||
struct DDP_LONG* header_long = (struct DDP_LONG*)data;
|
||||
|
||||
const word16 size = convert_ddp_header_to_long(packet->dest, packet->source, header_short, header_long);
|
||||
packet->dest.network = ntohs(header_long->dest_net);
|
||||
packet->source.network = ntohs(header_long->source_net);
|
||||
|
||||
memcpy(data + sizeof(struct DDP_LONG), packet->data + sizeof(struct DDP_SHORT), payload_size);
|
||||
|
||||
packet->type = LAP_DDP_LONG;
|
||||
packet->size = size;
|
||||
|
||||
// Replace the original short-form packet data.
|
||||
free(packet->data);
|
||||
packet->data = data;
|
||||
|
||||
calculate_checksum(packet);
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert a long-form DDP packet to short-form. */
|
||||
static void convert_ddp_packet_to_short(struct packet_t* packet)
|
||||
{
|
||||
if (packet && (packet->type == LAP_DDP_LONG) && packet->data)
|
||||
{
|
||||
struct DDP_LONG* header_long = (struct DDP_LONG*)packet->data;
|
||||
|
||||
const size_t payload_size = packet->size - sizeof(struct DDP_LONG);
|
||||
byte* data = (byte*)malloc(payload_size + sizeof(struct DDP_SHORT));
|
||||
struct DDP_SHORT* header_short = (struct DDP_SHORT*)data;
|
||||
|
||||
const word16 size = convert_ddp_header_to_short(header_long, header_short);
|
||||
|
||||
memcpy(data + sizeof(struct DDP_SHORT), packet->data + sizeof(struct DDP_LONG), payload_size);
|
||||
|
||||
packet->type = LAP_DDP_SHORT;
|
||||
packet->size = size;
|
||||
|
||||
free(packet->data);
|
||||
packet->data = data;
|
||||
}
|
||||
}
|
||||
|
||||
/*static void convert_rtmp_to_extended(struct packet_t* packet)
|
||||
{
|
||||
if (packet && (packet->type == LAP_DDP_SHORT) && packet->data)
|
||||
{
|
||||
struct DDP_SHORT* header_short = (struct DDP_SHORT*)packet->data;
|
||||
if (header_short->type != DDP_TYPE_RTMP || header_short->dest_socket != DDP_SOCKET_RTMP)
|
||||
return;
|
||||
|
||||
struct rtmp_nonextended_data_t* in = (struct rtmp_nonextended_data_t*)(packet->data + sizeof(struct DDP_SHORT));
|
||||
|
||||
// Construct a new long-form DDP packet header.
|
||||
size_t size = sizeof(struct DDP_LONG) + sizeof(struct rtmp_extended_data_t);
|
||||
byte* data = (byte*)malloc(size);
|
||||
struct DDP_LONG* header_long = (struct DDP_LONG*)data;
|
||||
convert_ddp_header_to_long(packet->dest, packet->source, header_short, header_long);
|
||||
|
||||
struct rtmp_extended_data_t* out = (struct rtmp_extended_data_t*)(data + sizeof(struct DDP_LONG));
|
||||
out->net = in->net;
|
||||
out->id_length = in->id_length;
|
||||
out->node = in->node;
|
||||
|
||||
// Copy the routing tuples.
|
||||
struct rtmp_nonextended_tuple_t* in_tuple = (struct rtmp_nonextended_tuple_t*)(packet->data + sizeof(struct DDP_SHORT) + sizeof(struct rtmp_nonextended_data_t));
|
||||
struct rtmp_extended_tuple_t* out_tuple = (struct rtmp_extended_tuple_t*)(data + size);
|
||||
while ((byte*)in_tuple < (packet->data + packet->size))
|
||||
{
|
||||
size += sizeof(struct rtmp_extended_tuple_t);
|
||||
realloc(data, size);
|
||||
out_tuple->range_start = in_tuple->net;
|
||||
out_tuple->distance = in_tuple->distance | 0x80;
|
||||
out_tuple->range_end = in_tuple->net;
|
||||
out_tuple->delimiter = RTMP_TUPLE_DELIMITER;
|
||||
in_tuple++;
|
||||
}
|
||||
|
||||
free(packet->data);
|
||||
packet->data = data;
|
||||
packet->size = size;
|
||||
packet->type = LAP_DDP_LONG;
|
||||
}
|
||||
}*/
|
||||
|
||||
static void convert_rtmp_to_nonextended(struct packet_t* packet)
|
||||
{
|
||||
if (packet && (packet->type == LAP_DDP_LONG) && packet->data)
|
||||
{
|
||||
struct DDP_LONG* header_long = (struct DDP_LONG*)packet->data;
|
||||
if (header_long->type != DDP_TYPE_RTMP || header_long->dest_socket != DDP_SOCKET_RTMP)
|
||||
return;
|
||||
|
||||
struct rtmp_extended_data_t* in = (struct rtmp_extended_data_t*)(packet->data + sizeof(struct DDP_LONG));
|
||||
|
||||
size_t size = sizeof(struct DDP_SHORT) + sizeof(struct rtmp_nonextended_response_t);
|
||||
byte* data = (byte*)malloc(size);
|
||||
struct DDP_SHORT* header_short = (struct DDP_SHORT*)data;
|
||||
convert_ddp_header_to_short(header_long, header_short);
|
||||
header_short->length[0] = (size >> 8) & 0x03;
|
||||
header_short->length[1] = size & 0xff;
|
||||
|
||||
struct rtmp_nonextended_response_t* out = (struct rtmp_nonextended_response_t*)(data + sizeof(struct DDP_SHORT));
|
||||
out->net = in->net;
|
||||
out->id_length = in->id_length;
|
||||
out->node = in->node;
|
||||
|
||||
/*rtmp_extended_tuple_t* in_tuple = (rtmp_extended_tuple_t*)(packet->data + sizeof(DDP_LONG) + sizeof(rtmp_extended_data_t));
|
||||
rtmp_nonextended_tuple_t* out_tuple = (rtmp_nonextended_tuple_t*)(data + size);
|
||||
while ((byte*)in_tuple < (packet->data + packet->size))
|
||||
{
|
||||
size += sizeof(rtmp_nonextended_tuple_t);
|
||||
realloc(data, size);
|
||||
out_tuple->net = in_tuple->range_start;
|
||||
out_tuple->distance = in_tuple->distance & 0x7f;
|
||||
in_tuple++;
|
||||
}*/
|
||||
|
||||
free(packet->data);
|
||||
packet->data = data;
|
||||
packet->size = size;
|
||||
packet->type = LAP_DDP_SHORT;
|
||||
}
|
||||
}
|
||||
|
||||
/* Learn our network number from RTMP packets. */
|
||||
/* "Inside AppleTalk", section 4-8, describes this approach for non-extended networks.
|
||||
Technically, we probably should be doing the more complicated extended network approach (also on 4-8),
|
||||
but the easy approach using RTMP seems adequate for now. */
|
||||
static void glean_net_from_rtmp(struct packet_t* packet)
|
||||
{
|
||||
if (packet && (packet->type == LAP_DDP_LONG) && packet->data)
|
||||
{
|
||||
struct DDP_LONG* header_long = (struct DDP_LONG*)packet->data;
|
||||
if (header_long->type != DDP_TYPE_RTMP || header_long->dest_socket != DDP_SOCKET_RTMP)
|
||||
return;
|
||||
|
||||
struct rtmp_extended_data_t* in = (struct rtmp_extended_data_t*)(packet->data + sizeof(struct DDP_LONG));
|
||||
|
||||
atbridge_set_net(ntohs(in->net));
|
||||
}
|
||||
}
|
||||
|
||||
static void send_rtmp_request()
|
||||
{
|
||||
struct packet_t* packet = (struct packet_t*)malloc(sizeof(struct packet_t));
|
||||
|
||||
packet->type = LAP_DDP_LONG;
|
||||
packet->dest.network = atbridge_get_net();
|
||||
packet->dest.node = 255;
|
||||
packet->source.network = atbridge_get_net();
|
||||
packet->source.node = atbridge_get_node();
|
||||
packet->next = 0;
|
||||
packet->size = sizeof(struct DDP_LONG) + sizeof(struct rtmp_request_t);
|
||||
packet->data = (byte*)malloc(packet->size);
|
||||
|
||||
struct DDP_LONG* header = (struct DDP_LONG*)packet->data;
|
||||
header->type = DDP_TYPE_RTMP_REQUEST;
|
||||
header->source_net = htons(packet->source.network);
|
||||
header->source_node = packet->source.node;
|
||||
header->source_socket = DDP_SOCKET_RTMP;
|
||||
header->dest_net = htons(packet->dest.network);
|
||||
header->dest_node = packet->dest.node;
|
||||
header->dest_socket = DDP_SOCKET_RTMP;
|
||||
header->length[0] = (packet->size >> 8) & 0x03;
|
||||
header->length[1] = packet->size & 0xff;
|
||||
|
||||
struct rtmp_request_t* request = (struct rtmp_request_t*)(packet->data + sizeof(struct DDP_LONG));
|
||||
request->function = RTMP_FUNCTION_REQUEST;
|
||||
|
||||
calculate_checksum(packet);
|
||||
|
||||
elap_enqueue_out(packet);
|
||||
sent_rtmp_request = true;
|
||||
}
|
||||
|
||||
void atbridge_process()
|
||||
{
|
||||
elap_process();
|
||||
//llap_process();
|
||||
|
||||
struct packet_t* packet = elap_dequeue_in();
|
||||
if (packet)
|
||||
{
|
||||
glean_net_from_rtmp(packet);
|
||||
convert_rtmp_to_nonextended(packet);
|
||||
// The GS should understand long-form DDP, but converting to short-form ought to slightly improve performance (fewer bytes for the GS to process).
|
||||
convert_ddp_packet_to_short(packet);
|
||||
llap_enqueue_out(packet);
|
||||
}
|
||||
packet = llap_dequeue_in();
|
||||
if (packet)
|
||||
{
|
||||
// ELAP does not support short-form DDP, so convert such packets to long-form.
|
||||
convert_ddp_packet_to_long(packet);
|
||||
elap_enqueue_out(packet);
|
||||
}
|
||||
}
|
47
src/atbridge/atbridge.h
Normal file
47
src/atbridge/atbridge.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
GSport - an Apple //gs Emulator
|
||||
Copyright (C) 2013-2014 by Peter Neubauer
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
/**
|
||||
ATBridge is a limited function AppleTalk bridge that allows GSPort to connect to an
|
||||
EtherTalk network. The bridge has two ports, one the emulated LocalTalk port and the
|
||||
other an EtherTalk Phase II port.
|
||||
|
||||
Due to timing requirements of LocalTalk, it is not reasonable to transparently bridge
|
||||
LLAP traffic to ELAP. For example, implementing the lapENQ/lapACK LLAP control packets
|
||||
requires AARP queries on the ELAP port, which can't be reasonably done within the 200us
|
||||
LLAP interframe gap. So, we must implement the local bridge functionality detailed
|
||||
in "Inside AppleTalk", including AARP, RTMP, ZIP, and DDP routing.
|
||||
**/
|
||||
#include "atalk.h"
|
||||
|
||||
bool atbridge_init();
|
||||
void atbridge_shutdown();
|
||||
void atbridge_process();
|
||||
|
||||
void atbridge_set_diagnostics(bool enabled);
|
||||
bool atbridge_get_diagnostics();
|
||||
void atbridge_printf(const char *fmt, ...);
|
||||
|
||||
const struct at_addr_t* atbridge_get_addr();
|
||||
const at_network_t atbridge_get_net();
|
||||
const at_node_t atbridge_get_node();
|
||||
void atbridge_set_net(at_network_t net);
|
||||
void atbridge_set_node(at_node_t node);
|
||||
bool atbridge_address_used(const struct at_addr_t* addr);
|
||||
|
111
src/atbridge/atbridge.vcxproj
Normal file
111
src/atbridge/atbridge.vcxproj
Normal file
@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\arch\win32\pcap.h" />
|
||||
<ClInclude Include="aarp.h" />
|
||||
<ClInclude Include="atalk.h" />
|
||||
<ClInclude Include="atbridge.h" />
|
||||
<ClInclude Include="elap.h" />
|
||||
<ClInclude Include="elap_defs.h" />
|
||||
<ClInclude Include="llap.h" />
|
||||
<ClInclude Include="pcap_delay.h" />
|
||||
<ClInclude Include="port.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="aarp.c" />
|
||||
<ClCompile Include="atbridge.c" />
|
||||
<ClCompile Include="elap.c" />
|
||||
<ClCompile Include="llap.c" />
|
||||
<ClCompile Include="pcap_delay.c" />
|
||||
<ClCompile Include="port.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="notes.txt" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>atbridge</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<CompileAs>Default</CompileAs>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
|
||||
<CompileAs>Default</CompileAs>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
69
src/atbridge/atbridge.vcxproj.filters
Normal file
69
src/atbridge/atbridge.vcxproj.filters
Normal file
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="llap.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="atbridge.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="elap.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\arch\win32\pcap.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="port.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="aarp.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="elap_defs.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="atalk.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="pcap_delay.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="llap.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="atbridge.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="elap.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="port.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="aarp.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pcap_delay.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="notes.txt" />
|
||||
</ItemGroup>
|
||||
</Project>
|
396
src/atbridge/elap.c
Normal file
396
src/atbridge/elap.c
Normal file
@ -0,0 +1,396 @@
|
||||
/*
|
||||
GSport - an Apple //gs Emulator
|
||||
Copyright (C) 2013-2014 by Peter Neubauer
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
/** This module implements the ELAP port of the bridge. **/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "../defc.h"
|
||||
#include "atbridge.h"
|
||||
#include "elap.h"
|
||||
#include "port.h"
|
||||
#include "aarp.h"
|
||||
#include "elap_defs.h"
|
||||
#include "pcap_delay.h"
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
#include <Windows.h>
|
||||
#include <NspAPI.h>
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
#include <winsock.h>
|
||||
#include <IPHlpApi.h>
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#include <netinet/in.h>
|
||||
#include <netpacket/packet.h>
|
||||
#endif
|
||||
|
||||
extern int g_ethernet_interface;
|
||||
|
||||
static pcap_t* pcap_session;
|
||||
|
||||
static struct packet_port_t elap_port;
|
||||
static struct ether_addr_t HW_LOCAL;
|
||||
|
||||
/*static void dump_device_list(pcap_if_t* devices)
|
||||
{
|
||||
int i = 0;
|
||||
for(pcap_if_t* device = devices; device; device = device->next)
|
||||
{
|
||||
printf("%d. %s", ++i, device->name);
|
||||
if (device->description)
|
||||
printf(" (%s)\n", device->description);
|
||||
else
|
||||
printf(" (No description available)\n");
|
||||
}
|
||||
}*/
|
||||
|
||||
static void elap_clone_host_mac(pcap_if_t* device)
|
||||
{
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
#ifdef WIN32
|
||||
////
|
||||
// Extract the device GUID, which Windows uses to identify the device.
|
||||
char* name = device->name;
|
||||
while (name && *name != '{')
|
||||
name++;
|
||||
size_t guidLen = strlen(name);
|
||||
|
||||
////
|
||||
// Find and copy the device MAC address.
|
||||
ULONG size = sizeof(IP_ADAPTER_ADDRESSES) * 15;
|
||||
IP_ADAPTER_ADDRESSES* addresses = malloc(size);
|
||||
|
||||
ULONG result = GetAdaptersAddresses(AF_UNSPEC,
|
||||
GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, NULL, addresses, &size);
|
||||
|
||||
if (result == ERROR_BUFFER_OVERFLOW)
|
||||
{
|
||||
// The addresses buffer is too small. Allocate a bigger buffer.
|
||||
free(addresses);
|
||||
addresses = malloc(size);
|
||||
result = GetAdaptersAddresses(AF_UNSPEC,
|
||||
GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, NULL, addresses, &size);
|
||||
}
|
||||
|
||||
if (result == NO_ERROR)
|
||||
{
|
||||
// Search for the desired adapter address.
|
||||
IP_ADAPTER_ADDRESSES* current = addresses;
|
||||
while (current)
|
||||
{
|
||||
if (current->PhysicalAddressLength == ETHER_ADDR_LEN && memcmp(current->AdapterName, name, guidLen) == 0)
|
||||
{
|
||||
memcpy(&HW_LOCAL.mac, ¤t->PhysicalAddress, sizeof(HW_LOCAL.mac));
|
||||
break;
|
||||
}
|
||||
current = current->Next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
halt_printf("ATBridge: Failed to find host MAC address (%d).", result);
|
||||
}
|
||||
|
||||
free(addresses);
|
||||
#else
|
||||
struct pcap_addr* address;
|
||||
for (address = device->addresses; address != 0; address = address->next)
|
||||
if (address->addr->sa_family == AF_PACKET)
|
||||
{
|
||||
struct sockaddr_ll* ll = (struct sockaddr_ll*)address->addr;
|
||||
memcpy(&HW_LOCAL.mac, ll->sll_addr, sizeof(HW_LOCAL.mac));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
const struct ether_addr_t* elap_get_mac()
|
||||
{
|
||||
return &HW_LOCAL;
|
||||
}
|
||||
|
||||
bool elap_init()
|
||||
{
|
||||
port_init(&elap_port);
|
||||
|
||||
memcpy(&HW_LOCAL, &HW_LOCAL_DEFAULT, sizeof(HW_LOCAL));
|
||||
|
||||
pcap_if_t* device;
|
||||
pcap_if_t* alldevs;
|
||||
int i = 0;
|
||||
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
// Load the PCAP library.
|
||||
if (!pcapdelay_load())
|
||||
{
|
||||
halt_printf("ATBridge: PCAP not available.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retrieve the device list.
|
||||
if(pcapdelay_findalldevs(&alldevs, errbuf) == -1)
|
||||
{
|
||||
atbridge_printf("ATBridge: Error enumerating PCAP devices: %s\n", errbuf);
|
||||
return false;
|
||||
}
|
||||
//dump_device_list(alldevs);
|
||||
|
||||
// Jump to the selected adapter.
|
||||
for (device = alldevs, i = 0; i < g_ethernet_interface; device = device->next, i++);
|
||||
if (!device)
|
||||
{
|
||||
halt_printf("ATBridge: PCAP device not found. Check interface number in settings.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clone the MAC address of the underlying interface. In certain configurations (e.g. Windows with an MS Loopback
|
||||
// interface), the interface, even in promiscous mode, filters foreign MAC addresses.
|
||||
elap_clone_host_mac(device);
|
||||
|
||||
// Open the adapter,
|
||||
if ((pcap_session = pcapdelay_open_live(device->name, // name of the device
|
||||
65536, // portion of the packet to capture.
|
||||
// 65536 grants that the whole packet will be captured on all the MACs.
|
||||
1, // promiscuous mode (nonzero means promiscuous)
|
||||
1, // read timeout
|
||||
errbuf // error buffer
|
||||
)) == NULL)
|
||||
{
|
||||
halt_printf("ATBridge: Unable to open the adapter. Pcap does not support %s.\n", device->name);
|
||||
pcapdelay_freealldevs(alldevs);
|
||||
return false;
|
||||
}
|
||||
|
||||
// The device must support Ethernet because the bridge "speaks" EtherTalk.
|
||||
if (pcapdelay_datalink(pcap_session) == DLT_EN10MB)
|
||||
{
|
||||
pcapdelay_setnonblock(pcap_session, 1, errbuf);
|
||||
|
||||
atbridge_printf("ATBridge: AppleTalk bridging using network device '%s' with Ethernet address %.2X:%.2X:%.2X:%.2X:%.2X:%.2X.\n",
|
||||
device->description, HW_LOCAL.mac[0], HW_LOCAL.mac[1], HW_LOCAL.mac[2], HW_LOCAL.mac[3], HW_LOCAL.mac[4], HW_LOCAL.mac[5]);
|
||||
|
||||
pcapdelay_freealldevs(alldevs);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
pcapdelay_close(pcap_session);
|
||||
pcap_session = 0;
|
||||
halt_printf("ATBridge: Selected network device %s must support Ethernet.\n", device->description);
|
||||
pcapdelay_freealldevs(alldevs);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void elap_shutdown()
|
||||
{
|
||||
port_shutdown(&elap_port);
|
||||
if (pcap_session)
|
||||
{
|
||||
pcapdelay_close(pcap_session);
|
||||
pcap_session = 0;
|
||||
}
|
||||
pcapdelay_unload();
|
||||
}
|
||||
|
||||
void elap_enqueue_out(struct packet_t* packet)
|
||||
{
|
||||
enqueue_packet(&elap_port.out, packet);
|
||||
}
|
||||
|
||||
struct packet_t* elap_dequeue_in()
|
||||
{
|
||||
return dequeue(&elap_port.in);
|
||||
}
|
||||
|
||||
void elap_send(const struct ether_addr_t* dest, const struct snap_discriminator_t* discriminator, size_t size, byte data[])
|
||||
{
|
||||
if (pcap_session && dest && discriminator)
|
||||
{
|
||||
// Allocate heap space for the frame.
|
||||
const size_t frame_size = sizeof(struct ethernet_header_t) + sizeof(struct snap_header_t) + size;
|
||||
u_char* frame_data;
|
||||
size_t pad = 0;
|
||||
if (frame_size < ETHER_MIN_SIZE)
|
||||
pad = ETHER_MIN_SIZE - frame_size;
|
||||
frame_data = (u_char*)malloc(frame_size + pad);
|
||||
|
||||
// Build the 802.3 header.
|
||||
struct ethernet_header_t* ether = (struct ethernet_header_t*)frame_data;
|
||||
memcpy(ether->dest.mac, dest, sizeof(ether->dest.mac));
|
||||
memcpy(ether->source.mac, HW_LOCAL.mac, sizeof(ether->source.mac));
|
||||
ether->length = htons(frame_size - sizeof(struct ethernet_header_t));
|
||||
|
||||
// Build the 802.2 header.
|
||||
struct snap_header_t* snap = (struct snap_header_t*)(frame_data + sizeof(struct ethernet_header_t));
|
||||
snap->dsap = SNAP_DSAP;
|
||||
snap->ssap = SNAP_SSAP;
|
||||
snap->control = SNAP_CONTROL;
|
||||
memcpy(&snap->discriminator, discriminator, sizeof(snap->discriminator));
|
||||
|
||||
// Add the data payload.
|
||||
struct snap_header_t* payload = (struct snap_header_t*)(frame_data + sizeof(struct ethernet_header_t) + sizeof(struct snap_header_t));
|
||||
memcpy(payload, data, size);
|
||||
|
||||
// Add padding to meet minimum Ethernet frame size.
|
||||
if (pad > 0)
|
||||
memset(frame_data + frame_size, 0, pad);
|
||||
|
||||
pcapdelay_sendpacket(pcap_session, frame_data, frame_size + pad);
|
||||
}
|
||||
}
|
||||
|
||||
static bool elap_send_one_queued()
|
||||
{
|
||||
// Attempt to send one packet out the host network interface.
|
||||
struct packet_t* packet = queue_peek(&elap_port.out);
|
||||
if (packet)
|
||||
{
|
||||
// Find the MAC address.
|
||||
const struct ether_addr_t* dest;
|
||||
if (packet->dest.node == at_broadcast_node)
|
||||
dest = &HW_APPLETALK_BROADCAST;
|
||||
else
|
||||
dest = aarp_request_hardware(&packet->dest);
|
||||
|
||||
// Send it.
|
||||
if (dest)
|
||||
{
|
||||
elap_send(dest, &SNAP_APPLETALK, packet->size, packet->data);
|
||||
|
||||
dequeue(&elap_port.out);
|
||||
free(packet->data);
|
||||
free(packet);
|
||||
}
|
||||
else
|
||||
{
|
||||
// AARP does not have the needed hardware address. Give AARP time to obtain the address and keep the current packet.
|
||||
if (!aarp_retry())
|
||||
{
|
||||
// However, if AARP has reached the retry limit, the packet is undeliverable. Discard the packet and move on.
|
||||
atbridge_printf("ATBridge: AARP failed to find MAC address for network %d node %d. Dropping packet.\n", packet->dest.network, packet->dest.node);
|
||||
aarp_retry_reset();
|
||||
dequeue(&elap_port.out);
|
||||
free(packet->data);
|
||||
free(packet);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static void elap_send_all_queued()
|
||||
{
|
||||
while (elap_send_one_queued());
|
||||
}
|
||||
|
||||
static bool elap_receive_one()
|
||||
{
|
||||
if (!pcap_session)
|
||||
return false;
|
||||
|
||||
struct pcap_pkthdr header;
|
||||
const u_char* packet = pcapdelay_next(pcap_session, &header);
|
||||
if (packet)
|
||||
{
|
||||
// Receive and process one packet from the host network interface.
|
||||
////
|
||||
|
||||
// Check the Ethernet 802.3 header.
|
||||
const struct ethernet_header_t* ether = (struct ethernet_header_t*)packet;
|
||||
if (header.len > sizeof(struct ethernet_header_t) &&
|
||||
ntohs(ether->length) <= ETHER_MAX_SIZE &&
|
||||
ntohs(ether->length) > sizeof(struct snap_header_t) &&
|
||||
(memcmp(ðer->source, &HW_LOCAL, sizeof(ether->source)) != 0) && /* Ignore packets sent from our node. */
|
||||
(memcmp(ðer->dest, &HW_LOCAL, sizeof(ether->dest)) == 0 || /* Accept packets destined for our node ... */
|
||||
memcmp(ðer->dest, &HW_APPLETALK_BROADCAST, sizeof(ether->dest)) == 0 /* ... or for broadcast. */)
|
||||
)
|
||||
{
|
||||
// Check the 802.2 SNAP header.
|
||||
const struct snap_header_t* snap = (struct snap_header_t*)(packet + sizeof(struct ethernet_header_t));
|
||||
if (snap->dsap == SNAP_DSAP &&
|
||||
snap->ssap == SNAP_SSAP &&
|
||||
snap->control == SNAP_CONTROL)
|
||||
{
|
||||
if (memcmp(&snap->discriminator, &SNAP_APPLETALK, sizeof(snap->discriminator)) == 0)
|
||||
{
|
||||
// Handle an AppleTalk packet.
|
||||
const size_t payload_size = ntohs(ether->length) - sizeof(struct snap_header_t);
|
||||
const u_char* payload = packet + sizeof(struct ethernet_header_t) + sizeof(struct snap_header_t);
|
||||
|
||||
byte* copy = (byte*)malloc(payload_size);
|
||||
memcpy(copy, payload, payload_size);
|
||||
|
||||
// ELAP does not support short-form DDP, so this must be a long-form DDP packet.
|
||||
struct at_addr_t source, dest;
|
||||
if (payload_size >= sizeof(struct DDP_LONG))
|
||||
{
|
||||
// Extract the protocol address from the header.
|
||||
//
|
||||
// ELAP really shouldn't be looking at the header for the next level of the protocol stack,
|
||||
// but this is a convenient place to glean addresses for the AMT, a process that needs both
|
||||
// hardware and protocol addresses.
|
||||
struct DDP_LONG* header = (struct DDP_LONG*)copy;
|
||||
dest.network = (at_network_t)ntohs(header->dest_net);
|
||||
source.network = (at_network_t)ntohs(header->source_net);
|
||||
dest.node = header->dest_node;
|
||||
source.node = header->source_node;
|
||||
|
||||
enqueue(&elap_port.in, dest, source, LAP_DDP_LONG, payload_size, copy);
|
||||
|
||||
aarp_glean(&source, ðer->source);
|
||||
}
|
||||
else
|
||||
atbridge_printf("ATBridge: Dropping invalid short ELAP frame.\n");
|
||||
}
|
||||
else if (memcmp(&snap->discriminator, &SNAP_AARP, sizeof(snap->discriminator)) == 0)
|
||||
{
|
||||
// Handle an AARP packet.
|
||||
struct aarp_header_t* aarp = (struct aarp_header_t*)(packet + sizeof(struct ethernet_header_t) + sizeof(struct snap_header_t));
|
||||
aarp->dest_proto_addr.addr.network = ntohs(aarp->dest_proto_addr.addr.network);
|
||||
aarp->source_proto_addr.addr.network = ntohs(aarp->source_proto_addr.addr.network);
|
||||
aarp->function = ntohs(aarp->function);
|
||||
aarp->hardware_type = ntohs(aarp->hardware_type);
|
||||
aarp->protocol_type = ntohs(aarp->protocol_type);
|
||||
aarp_handle_packet(aarp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static void elap_receive_all()
|
||||
{
|
||||
while (elap_receive_one());
|
||||
}
|
||||
|
||||
void elap_process()
|
||||
{
|
||||
elap_receive_all();
|
||||
elap_send_all_queued();
|
||||
}
|
36
src/atbridge/elap.h
Normal file
36
src/atbridge/elap.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
GSport - an Apple //gs Emulator
|
||||
Copyright (C) 2013-2014 by Peter Neubauer
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
/** ELAP port of the AppleTalk Bridge **/
|
||||
|
||||
bool elap_init();
|
||||
void elap_shutdown();
|
||||
void elap_process();
|
||||
|
||||
struct packet_t;
|
||||
|
||||
void elap_enqueue_out(struct packet_t* packet);
|
||||
struct packet_t* elap_dequeue_in();
|
||||
|
||||
struct ether_addr_t;
|
||||
struct snap_discriminator_t;
|
||||
|
||||
void elap_send(const struct ether_addr_t* dest, const struct snap_discriminator_t* discriminator, size_t size, byte data[]);
|
||||
|
||||
const struct ether_addr_t* elap_get_mac();
|
117
src/atbridge/elap_defs.h
Normal file
117
src/atbridge/elap_defs.h
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
GSport - an Apple //gs Emulator
|
||||
Copyright (C) 2013-2014 by Peter Neubauer
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
/* Ethernet addresses are 6 bytes */
|
||||
#define ETHER_ADDR_LEN 6
|
||||
|
||||
static const word16 ETHER_MAX_SIZE = 0x5DC;
|
||||
static const word16 ETHER_MIN_SIZE = 60;
|
||||
static const byte SNAP_DSAP = 0xAA;
|
||||
static const byte SNAP_SSAP = 0xAA;
|
||||
static const byte SNAP_CONTROL = 0x03;
|
||||
|
||||
#define OUI_APPLETALK_1 0x08
|
||||
#define OUI_APPLETALK_2 0x00
|
||||
#define OUI_APPLETALK_3 0x07
|
||||
#define TYPE_APPLETALK_1 0x80
|
||||
#define TYPE_APPLETALK_2 0x9B
|
||||
|
||||
#define OUI_AARP_1 0x00
|
||||
#define OUI_AARP_2 0x00
|
||||
#define OUI_AARP_3 0x00
|
||||
#define TYPE_AARP_1 0x80
|
||||
#define TYPE_AARP_2 0xF3
|
||||
|
||||
static const byte AARP_HARDWARE_ETHER = 0x01;
|
||||
static const word16 AARP_PROTOCOL_TYPE = 0x809B;
|
||||
static const byte AARP_HW_ADDR_LEN = 6;
|
||||
static const byte AARP_PROTOCOL_ADDR_LEN = 4;
|
||||
enum AARP_FUNCTION
|
||||
{
|
||||
AARP_FUNCTION_REQUEST = 0x01,
|
||||
AARP_FUNCTION_RESPONSE = 0x02,
|
||||
AARP_FUNCTION_PROBE = 0x03
|
||||
};
|
||||
|
||||
// reference C-4
|
||||
static const long AARP_PROBE_INTERVAL = 200; /* milliseconds */
|
||||
static const unsigned int AARP_PROBE_COUNT = 10;
|
||||
|
||||
// reference 2-9 and 3-9, optional and at developer discretion
|
||||
static const long AARP_REQUEST_INTERVAL = 200; /* milliseconds */
|
||||
static const unsigned int AARP_REQUEST_COUNT = 10;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct ether_addr_t
|
||||
{
|
||||
byte mac[ETHER_ADDR_LEN];
|
||||
};
|
||||
|
||||
/* Ethernet 802.2/802.3/SNAP header */
|
||||
struct ethernet_header_t
|
||||
{
|
||||
// 802.3 header
|
||||
struct ether_addr_t dest;
|
||||
struct ether_addr_t source;
|
||||
word16 length;
|
||||
};
|
||||
|
||||
struct snap_discriminator_t
|
||||
{
|
||||
byte oui[3];
|
||||
byte type[2];
|
||||
};
|
||||
|
||||
struct snap_header_t
|
||||
{
|
||||
// 802.2 header
|
||||
byte dsap;
|
||||
byte ssap;
|
||||
byte control;
|
||||
|
||||
// SNAP header
|
||||
struct snap_discriminator_t discriminator;
|
||||
};
|
||||
|
||||
struct protocol_addr_t
|
||||
{
|
||||
byte zero; // Reference C-4 and 3-11: The protocol address is four bytes with the high byte zero.
|
||||
struct at_addr_t addr;
|
||||
};
|
||||
|
||||
struct aarp_header_t
|
||||
{
|
||||
word16 hardware_type;
|
||||
word16 protocol_type;
|
||||
byte hw_addr_len;
|
||||
byte protocol_addr_len;
|
||||
word16 function;
|
||||
struct ether_addr_t source_hw_addr;
|
||||
struct protocol_addr_t source_proto_addr;
|
||||
struct ether_addr_t dest_hw_addr;
|
||||
struct protocol_addr_t dest_proto_addr;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
static const struct ether_addr_t HW_APPLETALK_BROADCAST = {{ 0x09, 0x00, 0x07, 0xff, 0xff, 0xff }};
|
||||
static const struct ether_addr_t HW_LOCAL_DEFAULT = {{0x02 /* unicast, locally administered */, 'A', '2', 'G', 'S', 0x00}};
|
||||
//static const struct ether_addr_t HW_LOCAL = {{ 0x02 /* unicast, locally administered */, 0x00, 0x4c, 0x4f, 0x4f, 0x50 }};
|
||||
static const struct ether_addr_t HW_ZERO = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
|
||||
static const struct snap_discriminator_t SNAP_APPLETALK = {{ OUI_APPLETALK_1, OUI_APPLETALK_2, OUI_APPLETALK_3}, {TYPE_APPLETALK_1, TYPE_APPLETALK_2 }};
|
||||
static const struct snap_discriminator_t SNAP_AARP = {{ OUI_AARP_1, OUI_AARP_2, OUI_AARP_3}, {TYPE_AARP_1, TYPE_AARP_2 }};
|
332
src/atbridge/llap.c
Normal file
332
src/atbridge/llap.c
Normal file
@ -0,0 +1,332 @@
|
||||
/*
|
||||
GSport - an Apple //gs Emulator
|
||||
Copyright (C) 2013-2014 by Peter Neubauer
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
/** This module implements the LLAP port of the bridge. **/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "../defc.h"
|
||||
#include "atbridge.h"
|
||||
#include "port.h"
|
||||
#include "llap.h"
|
||||
|
||||
typedef enum {
|
||||
LLAP_DDP_SHORT = 0x01,
|
||||
LLAP_DDP_LONG = 0x02,
|
||||
LLAP_ENQ = 0x81,
|
||||
LLAP_ACK = 0x82,
|
||||
LLAP_RTS = 0x84,
|
||||
LLAP_CTS = 0x85
|
||||
} LLAP_TYPES;
|
||||
|
||||
const unsigned int LLAP_PACKET_MAX = 603 /* bytes */;
|
||||
const unsigned int LLAP_PACKET_MIN = 3 /* bytes */;
|
||||
const double LLAP_IDG = 400 /* microseconds */;
|
||||
const double LLAP_IFG = 200 /* microseconds */;
|
||||
const double GAP_TOLERANCE = 4.0;
|
||||
|
||||
static struct packet_port_t llap_port;
|
||||
|
||||
typedef enum {
|
||||
DIALOG_READY,
|
||||
DIALOG_GOT_CTS,
|
||||
DIALOG_WAIT_IDG
|
||||
} DIALOG_STATE;
|
||||
static DIALOG_STATE dialog_state;
|
||||
static double dialog_end_dcycs;
|
||||
static double last_frame_dcycs;
|
||||
|
||||
void llap_init()
|
||||
{
|
||||
dialog_state = DIALOG_READY;
|
||||
last_frame_dcycs = 0;
|
||||
port_init(&llap_port);
|
||||
}
|
||||
|
||||
void llap_shutdown()
|
||||
{
|
||||
port_shutdown(&llap_port);
|
||||
}
|
||||
|
||||
|
||||
/** Queue one data packet out from the bridge's LLAP port and into the guest. **/
|
||||
void llap_enqueue_out(struct packet_t* packet)
|
||||
{
|
||||
// Generate the RTS.
|
||||
struct packet_t* rts = (struct packet_t*)malloc(sizeof(struct packet_t));
|
||||
rts->source.network = packet->source.network;
|
||||
rts->source.node = packet->source.node;
|
||||
rts->dest.network = packet->dest.network;
|
||||
rts->dest.node = packet->dest.node;
|
||||
rts->size = 0;
|
||||
rts->data = 0;
|
||||
rts->type = LLAP_RTS;
|
||||
enqueue_packet(&llap_port.out, rts);
|
||||
|
||||
// Enqueue the data.
|
||||
enqueue_packet(&llap_port.out, packet);
|
||||
}
|
||||
|
||||
struct packet_t* llap_dequeue_in()
|
||||
{
|
||||
return dequeue(&llap_port.in);
|
||||
}
|
||||
|
||||
static void llap_dump_packet(size_t size, byte data[])
|
||||
{
|
||||
if (size < LLAP_PACKET_MIN)
|
||||
atbridge_printf("LLAP short packet.\n");
|
||||
else if (size > LLAP_PACKET_MAX)
|
||||
atbridge_printf("LLAP long packet.\n");
|
||||
else
|
||||
{
|
||||
at_node_t dest = data[0];
|
||||
at_node_t source = data[1];
|
||||
LLAP_TYPES type = (LLAP_TYPES)(data[2]);
|
||||
|
||||
const char* typeName = 0;
|
||||
switch (type)
|
||||
{
|
||||
case LLAP_DDP_SHORT:
|
||||
typeName = "DDP (short)";
|
||||
break;
|
||||
case LLAP_DDP_LONG:
|
||||
typeName = "DDP (long)";
|
||||
break;
|
||||
case LLAP_ENQ:
|
||||
typeName = "lapENQ";
|
||||
break;
|
||||
case LLAP_ACK:
|
||||
typeName = "lapACK";
|
||||
break;
|
||||
case LLAP_RTS:
|
||||
typeName = "lapRTS";
|
||||
break;
|
||||
case LLAP_CTS:
|
||||
typeName = "lapCTS";
|
||||
break;
|
||||
}
|
||||
|
||||
if (typeName)
|
||||
atbridge_printf("LLAP[%d->%d] %s: %d bytes.\n", source, dest, typeName, size);
|
||||
else
|
||||
atbridge_printf("LLAP[%d->%d] %x: %d bytes.\n", source, dest, type, size);
|
||||
|
||||
/*for (size_t i = 0; i < size; i++)
|
||||
atbridge_printf("%02x ", data[i]);
|
||||
atbridge_printf("\n");*/
|
||||
}
|
||||
}
|
||||
|
||||
/** Reply to a control packet from the GS **/
|
||||
static void llap_reply_control(at_node_t dest, at_node_t source, LLAP_TYPES type)
|
||||
{
|
||||
struct at_addr_t dest_addr = { 0, dest };
|
||||
struct at_addr_t source_addr = { 0, source };
|
||||
|
||||
// Insert control packets at the head of the queue contrary to normal FIFO queue operation
|
||||
// to ensure that control frames arrive in the intended order.
|
||||
insert(&llap_port.out, dest_addr, source_addr, type, 0, 0);
|
||||
}
|
||||
|
||||
/** Accept a data packet from the GS. **/
|
||||
static void llap_handle_data(size_t size, byte data[])
|
||||
{
|
||||
at_node_t dest = data[0];
|
||||
at_node_t source = data[1];
|
||||
LLAP_TYPES type = (LLAP_TYPES)(data[2]);
|
||||
|
||||
const size_t data_size = size - 3;
|
||||
byte* data_copy = (byte*)malloc(data_size);
|
||||
memcpy(data_copy, data + 3, data_size);
|
||||
|
||||
struct at_addr_t dest_addr = { 0, dest };
|
||||
struct at_addr_t source_addr = { 0, source };
|
||||
enqueue(&llap_port.in, dest_addr, source_addr, type, data_size, data_copy);
|
||||
}
|
||||
|
||||
/** Accept a control packet from the GS. **/
|
||||
static void llap_handle_control(size_t size, byte data[])
|
||||
{
|
||||
at_node_t dest = data[0];
|
||||
at_node_t source = data[1];
|
||||
LLAP_TYPES type = (LLAP_TYPES)(data[2]);
|
||||
|
||||
struct at_addr_t addr = { atbridge_get_net(), dest };
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case LLAP_ENQ:
|
||||
// Require the GS to take a valid "client" address not known to be in use.
|
||||
if (dest > 127 || dest == 0 || atbridge_address_used(&addr))
|
||||
llap_reply_control(source, dest, LLAP_ACK);
|
||||
break;
|
||||
case LLAP_ACK:
|
||||
break;
|
||||
case LLAP_RTS:
|
||||
if (dest != at_broadcast_node)
|
||||
// The GS is trying to make a directed transmission. Provide the required RTS/CTS handshake.
|
||||
// Note that broadcast packets do not require a CTS.
|
||||
llap_reply_control(source, dest, LLAP_CTS);
|
||||
break;
|
||||
case LLAP_CTS:
|
||||
// The GS sent a CTS. If the bridge has pending data, prepare to deliver the packet.
|
||||
dialog_state = DIALOG_GOT_CTS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Occassionally, we receive an invalid packet from the GS. I'm unsure if this is due to a bug in GS/OS
|
||||
or, more likely, a bug in the SCC emulation. Regardless, when such a thing does occur, discard the
|
||||
current, corrupted dialog. Link errors are routine in real LocalTalk networks, and LocalTalk will recover.
|
||||
**/
|
||||
static void llap_reset_dialog()
|
||||
{
|
||||
dialog_state = DIALOG_READY;
|
||||
last_frame_dcycs = 0;
|
||||
|
||||
// Discard packets until the queue is either empty or the next dialog starts (and dialogs begin with an RTS).
|
||||
while (true)
|
||||
{
|
||||
struct packet_t* packet = queue_peek(&llap_port.out);
|
||||
|
||||
if (packet && (packet->type != LLAP_RTS))
|
||||
{
|
||||
packet = dequeue(&llap_port.out);
|
||||
if (packet->data)
|
||||
free(packet->data);
|
||||
free(packet);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Transfer (send) one LLAP packet from the GS. **/
|
||||
void llap_enqueue_in(double dcycs, size_t size, byte data[])
|
||||
{
|
||||
atbridge_printf("<%0.0f> TX: ", dcycs);
|
||||
llap_dump_packet(size, data);
|
||||
|
||||
if (size < LLAP_PACKET_MIN)
|
||||
atbridge_printf("ATBridge: Dropping LLAP short packet.\n");
|
||||
else if (size > LLAP_PACKET_MAX)
|
||||
atbridge_printf("ATBridge: Dropping LLAP long packet.\n");
|
||||
else
|
||||
{
|
||||
last_frame_dcycs = dcycs;
|
||||
LLAP_TYPES type = (LLAP_TYPES)(data[2]);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case LLAP_DDP_SHORT:
|
||||
case LLAP_DDP_LONG:
|
||||
llap_handle_data(size, data);
|
||||
break;
|
||||
case LLAP_ENQ:
|
||||
case LLAP_ACK:
|
||||
case LLAP_RTS:
|
||||
case LLAP_CTS:
|
||||
llap_handle_control(size, data);
|
||||
break;
|
||||
default:
|
||||
// Intentionally check for valid types and ingore packets with invalid types.
|
||||
// Sometimes, the bridge gets invalid packets from the GS, which tends to break the bridge.
|
||||
atbridge_printf("ATBridge: Dropping LLAP packet with invalid type.\n");
|
||||
llap_reset_dialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Transfer (receive) one LLAP packet to the GS. **/
|
||||
void llap_dequeue_out(double dcycs, size_t* size, byte* data[])
|
||||
{
|
||||
*size = 0;
|
||||
|
||||
// The LocalTalk protocol requires a minimum 400us gap between dialogs (called the IDG).
|
||||
// If necessary, wait for the IDG.
|
||||
if (dialog_state == DIALOG_WAIT_IDG)
|
||||
{
|
||||
if ((dcycs - dialog_end_dcycs) >= LLAP_IDG)
|
||||
// The IDG is done.
|
||||
dialog_state = DIALOG_READY;
|
||||
else
|
||||
// Continue waiting for the IDG.
|
||||
return;
|
||||
}
|
||||
// The LocalTalk protocols requires a maximum 200us gap between frames within a dialog (called the IFG).
|
||||
// If we exceed the IFG, the bridge must be stuck in an incomplete or corrupt dialog. In this case,
|
||||
// discard the current dialog and try again.
|
||||
if ((dialog_state != DIALOG_READY) && (last_frame_dcycs != 0) && ((dcycs - last_frame_dcycs) >= (GAP_TOLERANCE*LLAP_IFG)))
|
||||
{
|
||||
llap_reset_dialog();
|
||||
atbridge_printf("ATBridge: Dialog reset due to IFG violation.\n");
|
||||
}
|
||||
|
||||
struct packet_t* packet = queue_peek(&llap_port.out);
|
||||
|
||||
if ((dialog_state == DIALOG_READY) && (packet) && !(packet->type & 0x80) && (last_frame_dcycs != 0) && ((dcycs - last_frame_dcycs) >= (GAP_TOLERANCE*LLAP_IDG)))
|
||||
{
|
||||
llap_reset_dialog();
|
||||
packet = queue_peek(&llap_port.out);
|
||||
atbridge_printf("ATBridge: Dialog reset due to IDG violation.\n");
|
||||
}
|
||||
|
||||
if (packet &&
|
||||
((packet->type & 0x80) || /* Pass along control frames without waiting for a CTS. */
|
||||
(!(packet->type & 0x80) && (packet->dest.node == at_broadcast_node) && (dialog_state == DIALOG_READY)) || /* Pass along broadcast frames, which don't wait for CTS frames. */
|
||||
(!(packet->type & 0x80) && (packet->dest.node != at_broadcast_node) && (dialog_state == DIALOG_GOT_CTS)))) /* Pass along directed frames only after receiving a CTS handshake. */
|
||||
{
|
||||
dequeue(&llap_port.out);
|
||||
|
||||
// Prepend the LLAP header.
|
||||
*size = packet->size + 3 + 2;
|
||||
*data = (byte*)malloc(*size);
|
||||
(*data)[0] = packet->dest.node;
|
||||
(*data)[1] = packet->source.node;
|
||||
(*data)[2] = packet->type;
|
||||
|
||||
// Insert the data into the new LLAP packet.
|
||||
if (*size)
|
||||
memcpy((*data) + 3, packet->data, packet->size);
|
||||
|
||||
// Fake a frame check sequence (FCS). Since our SCC emulation doesn't actually
|
||||
// check the FCS, the value of the FCS doesn't matter.
|
||||
(*data)[packet->size + 3 + 0] = 0xff;
|
||||
(*data)[packet->size + 3 + 1] = 0xff;
|
||||
|
||||
atbridge_printf("<%0.0f> RX: ", dcycs);
|
||||
llap_dump_packet(*size, *data);
|
||||
|
||||
if (packet->type & 0x80)
|
||||
dialog_state = DIALOG_READY;
|
||||
else
|
||||
{
|
||||
// This was the last packet in the dialog.
|
||||
dialog_state = DIALOG_WAIT_IDG;
|
||||
dialog_end_dcycs = dcycs;
|
||||
}
|
||||
|
||||
last_frame_dcycs = dcycs;
|
||||
|
||||
free(packet->data);
|
||||
free(packet);
|
||||
}
|
||||
}
|
37
src/atbridge/llap.h
Normal file
37
src/atbridge/llap.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
GSport - an Apple //gs Emulator
|
||||
Copyright (C) 2013-2014 by Peter Neubauer
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
struct packet_t;
|
||||
|
||||
/** LLAP port of the AppleTalk Bridge **/
|
||||
|
||||
void llap_init();
|
||||
void llap_shutdown();
|
||||
|
||||
/** Send one LLAP packet from the GS
|
||||
*/
|
||||
void llap_enqueue_in(double dcycs, size_t size, byte data[]);
|
||||
|
||||
/** Receive one LLAP packet from the world to the GS and return the size
|
||||
*/
|
||||
void llap_dequeue_out(double dcycs, size_t* size, byte* data[]);
|
||||
|
||||
|
||||
void llap_enqueue_out(struct packet_t* packet);
|
||||
struct packet_t* llap_dequeue_in();
|
61
src/atbridge/notes.txt
Normal file
61
src/atbridge/notes.txt
Normal file
@ -0,0 +1,61 @@
|
||||
AppleTalk Emulation and Bridging for GSport
|
||||
Peter Neubauer, March 2014
|
||||
|
||||
Introduction
|
||||
============
|
||||
The Apple IIgs shipped with a fantastic but often overlooked feature -- AppleTalk networking. AppleTalk is a low-cost, easy-to-maintain network technology that
|
||||
enables network booting, file sharing, and printer sharing between your Apple IIgs, Workstation Card equipped Apple IIe, and classic Macintosh machines. As of
|
||||
March 2014, GSport is the first modern emulator with support for AppleTalk. Now, you can just "drag and drop" files between your machines without thinking about
|
||||
disk images, FTP, or serial cables. You can develop new software with an emulator and test on real hardware. You don't need to think much about version control.
|
||||
Rather, you can update the file once on your file server, and all of your emulated and real machines have the new file. You can download the latest Apple II
|
||||
software and immediately run it.
|
||||
|
||||
Using It
|
||||
========
|
||||
First, you need an AppleShare-compatible file server. A classic Mac or "netatalk" server could provide this function. For simplicity, I recommend Ivan Drucker's
|
||||
A2SERVER (http://appleii.ivanx.com/a2server/), which is a pre-configured and easy-to-use package with "netatalk" and other useful tools. You may run A2SERVER in
|
||||
a VirtualBox virtual machine, on an existing Linux server, or on a Raspberry Pi.
|
||||
|
||||
Second, you need GSport 0.3 or later with ROM03 running on a Windows or Linux (x86 or Raspberry Pi) machine. Start GSport and press F4 to access the configuration
|
||||
menu. If necessary, select a ROM03 image. Select the "Ethernet Card Configuration" menu option. Change "AppleTalk Bridging" to "On". Change "Use Interface
|
||||
Number" to select the network where you have attached your AppleShare server or A2SERVER. Exit the GSport configuration menu.
|
||||
|
||||
Third, party like it is 1989. Your GS is now connected to the AppleTalk network. Refer to Apple's documentation included with GS/OS System 5 and 6 for further
|
||||
directions.
|
||||
|
||||
Internal Overview
|
||||
=================
|
||||
GSport with AppleTalk networking is functionally equivalent to a real Apple IIgs with a LocalTalk/EtherTalk bridge. Originally, AppleTalk employed the "LocalTalk"
|
||||
physical layer, which requires special hardware not found on modern computers. GSport converts LocalTalk to EtherTalk, a somewhat more modern physical layer using
|
||||
familiar Ethernet cabling. Internally, GSport emulates the Zilog SCC chip in the IIgs, communicates with unmodified Apple-provided networking software built-in to
|
||||
the IIgs and GS/OS, and converts the network traffic to EtherTalk.
|
||||
|
||||
Limitations
|
||||
===========
|
||||
- A wireless network may not work because many wireless adapters drop EtherTalk packets. Instead, use a wired Ethernet connection.
|
||||
|
||||
- AppleTalk bridging has been tested with System 6.0.1 and System 5.0.2 on ROM03. Booting from a local disk or from the network works. Other ROM revisions and
|
||||
system software may not work.
|
||||
|
||||
- The SCC baud rate is incorrect because the SCC does not emulate line coding. Still, emulated network speed should be close to the 230.4kbps speed of a real
|
||||
LocalTalk network.
|
||||
|
||||
- The bridge supports Windows using Visual Studio, Cygwin, and Linux (x86 and Raspberry Pi). Other platforms should be straightforward, but I do not have a suitable
|
||||
build environment.
|
||||
|
||||
- The bridge requires a router on the network. The bridge should work in both a routerless and router-filled network.
|
||||
|
||||
- The bridge works with simple networks consisting of a single network on a single segment with a single zone. The bridge should function with all valid network
|
||||
configurations and hardware routers. Other configurations might not work, and I welcome reports.
|
||||
|
||||
- The bridge implements the non-extended method for acquiring the network number. Interoperability would likely be better using the extended method, but this method
|
||||
is much more complex.
|
||||
|
||||
Credits
|
||||
=======
|
||||
Thanks to Gursharan Sidhu, Richard Andrews, and Alan Oppenheimer for creating and documenting AppleTalk.
|
||||
Thanks to Kent Dickey and the GSport contributors for GSport and the original SCC emulation.
|
||||
Thanks to David Schmenk for testing, encouragement, and Raspberry Pi support.
|
||||
Thanks to Ivan Drucker for A2SERVER.
|
||||
Thanks to the Gus emulator engineers for showing that AppleTalk emulation is possible.
|
||||
Thanks to James Littlejohn for discussions about extending the capabilities of the Apple IIgs.
|
170
src/atbridge/pcap_delay.c
Normal file
170
src/atbridge/pcap_delay.c
Normal file
@ -0,0 +1,170 @@
|
||||
/*
|
||||
GSport - an Apple //gs Emulator
|
||||
Copyright (C) 2014 by Peter Neubauer
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "pcap_delay.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <Windows.h>
|
||||
static HMODULE module = NULL;
|
||||
#elif __linux__
|
||||
#include <dlfcn.h>
|
||||
static void* module = 0;
|
||||
#endif
|
||||
|
||||
|
||||
bool pcapdelay_load()
|
||||
{
|
||||
if (!pcapdelay_is_loaded())
|
||||
{
|
||||
#ifdef WIN32
|
||||
module = LoadLibrary("wpcap.dll");
|
||||
#elif __linux__
|
||||
module = dlopen("libpcap.so", RTLD_LAZY);
|
||||
#endif
|
||||
}
|
||||
return pcapdelay_is_loaded();
|
||||
}
|
||||
|
||||
bool pcapdelay_is_loaded()
|
||||
{
|
||||
#ifdef WIN32
|
||||
return module != NULL;
|
||||
#elif __linux__
|
||||
return module != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void pcapdelay_unload()
|
||||
{
|
||||
if (pcapdelay_is_loaded())
|
||||
{
|
||||
#ifdef WIN32
|
||||
FreeLibrary(module);
|
||||
module = NULL;
|
||||
#elif __linux__
|
||||
dlclose(module);
|
||||
module = 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
typedef void (*PFNVOID)();
|
||||
|
||||
static PFNVOID delay_load(const char* proc, PFNVOID* ppfn)
|
||||
{
|
||||
if (pcapdelay_load() && proc && ppfn && !*ppfn)
|
||||
{
|
||||
#ifdef WIN32
|
||||
*ppfn = (PFNVOID)GetProcAddress(module, proc);
|
||||
#elif __linux__
|
||||
*ppfn = (PFNVOID)dlsym(module, proc);
|
||||
#endif
|
||||
}
|
||||
if (ppfn)
|
||||
return *ppfn;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pcapdelay_freealldevs(pcap_if_t* a0)
|
||||
{
|
||||
typedef void (*PFN)(pcap_if_t*);
|
||||
static PFN pfn = 0;
|
||||
if ((pfn = (PFN)delay_load("pcap_freealldevs", (PFNVOID*)&pfn)))
|
||||
(*pfn)(a0);
|
||||
}
|
||||
|
||||
pcap_t* pcapdelay_open_live(const char* a0, int a1, int a2, int a3, char* a4)
|
||||
{
|
||||
typedef pcap_t* (*PFN)(const char*, int, int, int, char*);
|
||||
static PFN pfn = 0;
|
||||
if ((pfn = (PFN)delay_load("pcap_open_live", (PFNVOID*)&pfn)))
|
||||
return (*pfn)(a0, a1, a2, a3, a4);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pcapdelay_close(pcap_t* a0)
|
||||
{
|
||||
typedef void (*PFN)(pcap_t*);
|
||||
static PFN pfn = 0;
|
||||
if ((pfn = (PFN)delay_load("pcap_close", (PFNVOID*)&pfn)))
|
||||
(*pfn)(a0);
|
||||
}
|
||||
|
||||
int pcapdelay_findalldevs(pcap_if_t** a0, char* a1)
|
||||
{
|
||||
typedef int (*PFN)(pcap_if_t**, char*);
|
||||
static PFN pfn = 0;
|
||||
if ((pfn = (PFN)delay_load("pcap_findalldevs", (PFNVOID*)&pfn)))
|
||||
return (*pfn)(a0, a1);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pcapdelay_datalink(pcap_t* a0)
|
||||
{
|
||||
typedef int(*PFN)(pcap_t*);
|
||||
static PFN pfn = 0;
|
||||
if ((pfn = (PFN)delay_load("pcap_datalink", (PFNVOID*)&pfn)))
|
||||
return (*pfn)(a0);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pcapdelay_setnonblock(pcap_t* a0, int a1, char* a2)
|
||||
{
|
||||
typedef int(*PFN)(pcap_t*, int, char*);
|
||||
static PFN pfn = 0;
|
||||
if ((pfn = (PFN)delay_load("pcap_setnonblock", (PFNVOID*)&pfn)))
|
||||
return (*pfn)(a0, a1, a2);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pcapdelay_sendpacket(pcap_t* a0, u_char* a1, int a2)
|
||||
{
|
||||
typedef int(*PFN)(pcap_t*, u_char*, int);
|
||||
static PFN pfn = 0;
|
||||
if ((pfn = (PFN)delay_load("pcap_sendpacket", (PFNVOID*)&pfn)))
|
||||
return (*pfn)(a0, a1, a2);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
const u_char* pcapdelay_next(pcap_t* a0, struct pcap_pkthdr* a1)
|
||||
{
|
||||
typedef const u_char*(*PFN)(pcap_t*, struct pcap_pkthdr*);
|
||||
static PFN pfn = 0;
|
||||
if ((pfn = (PFN)delay_load("pcap_next", (PFNVOID*)&pfn)))
|
||||
return (*pfn)(a0, a1);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pcapdelay_dispatch(pcap_t* a0, int a1, pcap_handler a2, u_char* a3)
|
||||
{
|
||||
typedef const int(*PFN)(pcap_t *, int, pcap_handler, u_char *);
|
||||
static PFN pfn = 0;
|
||||
if ((pfn = (PFN)delay_load("pcap_dispatch", (PFNVOID*)&pfn)))
|
||||
return (*pfn)(a0, a1, a2, a3);
|
||||
else
|
||||
return 0;
|
||||
}
|
46
src/atbridge/pcap_delay.h
Normal file
46
src/atbridge/pcap_delay.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
GSport - an Apple //gs Emulator
|
||||
Copyright (C) 2014 by Peter Neubauer
|
||||
|
||||
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
|
||||
*/
|
||||
/*
|
||||
This interface provides a thin, delay-loaded wrapper around the PCAP library so that
|
||||
you may start GSport without intalling PCAP. Of course, some features that require
|
||||
PCAP won't be available.
|
||||
|
||||
This wrapper provides a subset of the available PCAP APIs necessary for ATBridge.
|
||||
Feel free to extend the wrapper.
|
||||
*/
|
||||
|
||||
#ifdef WIN32
|
||||
#include "../arch/win32/pcap.h"
|
||||
#elif __linux__
|
||||
#include <pcap.h>
|
||||
#endif
|
||||
|
||||
bool pcapdelay_load();
|
||||
bool pcapdelay_is_loaded();
|
||||
void pcapdelay_unload();
|
||||
|
||||
void pcapdelay_freealldevs(pcap_if_t *);
|
||||
pcap_t* pcapdelay_open_live(const char *, int, int, int, char *);
|
||||
void pcapdelay_close(pcap_t *);
|
||||
int pcapdelay_findalldevs(pcap_if_t **, char *);
|
||||
int pcapdelay_datalink(pcap_t *);
|
||||
int pcapdelay_setnonblock(pcap_t *, int, char *);
|
||||
int pcapdelay_sendpacket(pcap_t *p, u_char *buf, int size);
|
||||
const u_char* pcapdelay_next(pcap_t *, struct pcap_pkthdr *);
|
||||
int pcapdelay_dispatch(pcap_t *, int, pcap_handler, u_char *);
|
139
src/atbridge/port.c
Normal file
139
src/atbridge/port.c
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
GSport - an Apple //gs Emulator
|
||||
Copyright (C) 2013-2014 by Peter Neubauer
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
/** This module implements queues for storing and transferring packets within the bridge. **/
|
||||
|
||||
#include "../defc.h"
|
||||
#include "atalk.h"
|
||||
#include "port.h"
|
||||
|
||||
void port_init(struct packet_port_t* port)
|
||||
{
|
||||
if (port)
|
||||
{
|
||||
queue_init(&port->in);
|
||||
queue_init(&port->out);
|
||||
}
|
||||
}
|
||||
|
||||
void port_shutdown(struct packet_port_t* port)
|
||||
{
|
||||
if (port)
|
||||
{
|
||||
queue_shutdown(&port->in);
|
||||
queue_shutdown(&port->out);
|
||||
}
|
||||
}
|
||||
|
||||
void queue_init(struct packet_queue_t* queue)
|
||||
{
|
||||
if (queue)
|
||||
{
|
||||
queue->head = queue->tail = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void queue_shutdown(struct packet_queue_t* queue)
|
||||
{
|
||||
if (queue)
|
||||
{
|
||||
struct packet_t* packet = dequeue(queue);
|
||||
while (packet)
|
||||
{
|
||||
if (packet->data)
|
||||
free(packet->data);
|
||||
free(packet);
|
||||
packet = dequeue(queue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void enqueue(struct packet_queue_t* queue, struct at_addr_t dest, struct at_addr_t source, byte type, size_t size, byte data[])
|
||||
{
|
||||
if (!queue)
|
||||
return;
|
||||
|
||||
struct packet_t* packet = (struct packet_t*)malloc(sizeof(struct packet_t));
|
||||
packet->dest.network = dest.network;
|
||||
packet->dest.node = dest.node;
|
||||
packet->source.network = source.network;
|
||||
packet->source.node = source.node;
|
||||
packet->type = type;
|
||||
packet->size = size;
|
||||
packet->data = data;
|
||||
enqueue_packet(queue, packet);
|
||||
}
|
||||
|
||||
void enqueue_packet(struct packet_queue_t* queue, struct packet_t* packet)
|
||||
{
|
||||
packet->next = 0;
|
||||
|
||||
if (queue->tail)
|
||||
queue->tail->next = packet;
|
||||
else
|
||||
queue->head = packet;
|
||||
queue->tail = packet;
|
||||
}
|
||||
|
||||
void insert(struct packet_queue_t* queue, struct at_addr_t dest, struct at_addr_t source, byte type, size_t size, byte data[])
|
||||
{
|
||||
if (!queue)
|
||||
return;
|
||||
|
||||
struct packet_t* packet = (struct packet_t*)malloc(sizeof(struct packet_t));
|
||||
packet->dest.network = dest.network;
|
||||
packet->dest.node = dest.node;
|
||||
packet->source.network = source.network;
|
||||
packet->source.node = source.node;
|
||||
packet->type = type;
|
||||
packet->size = size;
|
||||
packet->data = data;
|
||||
insert_packet(queue, packet);
|
||||
}
|
||||
|
||||
void insert_packet(struct packet_queue_t* queue, struct packet_t* packet)
|
||||
{
|
||||
packet->next = queue->head;
|
||||
queue->head = packet;
|
||||
if (!queue->tail)
|
||||
queue->tail = queue->head;
|
||||
}
|
||||
|
||||
struct packet_t* dequeue(struct packet_queue_t* queue)
|
||||
{
|
||||
if (queue && queue->head)
|
||||
{
|
||||
struct packet_t* packet = queue->head;
|
||||
if (queue->tail == queue->head)
|
||||
queue->tail = queue->head = 0;
|
||||
else
|
||||
queue->head = packet->next;
|
||||
return packet;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct packet_t* queue_peek(struct packet_queue_t* queue)
|
||||
{
|
||||
if (queue)
|
||||
return queue->head;
|
||||
else
|
||||
return 0;
|
||||
}
|
58
src/atbridge/port.h
Normal file
58
src/atbridge/port.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
GSport - an Apple //gs Emulator
|
||||
Copyright (C) 2013-2014 by Peter Neubauer
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
struct packet_t
|
||||
{
|
||||
struct at_addr_t dest;
|
||||
struct at_addr_t source;
|
||||
byte type;
|
||||
size_t size;
|
||||
byte* data;
|
||||
struct packet_t* next;
|
||||
};
|
||||
|
||||
struct packet_queue_t
|
||||
{
|
||||
struct packet_t* head;
|
||||
struct packet_t* tail;
|
||||
};
|
||||
|
||||
void queue_init(struct packet_queue_t* queue);
|
||||
void queue_shutdown(struct packet_queue_t* queue);
|
||||
|
||||
void enqueue(struct packet_queue_t* queue, struct at_addr_t dest, struct at_addr_t source, byte type, size_t size, byte data[]);
|
||||
void enqueue_packet(struct packet_queue_t* queue, struct packet_t* packet);
|
||||
|
||||
struct packet_t* dequeue(struct packet_queue_t* queue);
|
||||
struct packet_t* queue_peek(struct packet_queue_t* queue);
|
||||
|
||||
// Insert the packet at the head of the queue, contrary to normal FIFO operation.
|
||||
void insert(struct packet_queue_t* queue, struct at_addr_t dest, struct at_addr_t source, byte type, size_t size, byte data[]);
|
||||
void insert_packet(struct packet_queue_t* queue, struct packet_t* packet);
|
||||
|
||||
|
||||
|
||||
struct packet_port_t
|
||||
{
|
||||
struct packet_queue_t in;
|
||||
struct packet_queue_t out;
|
||||
};
|
||||
|
||||
void port_init(struct packet_port_t* port);
|
||||
void port_shutdown(struct packet_port_t* port);
|
6681
src/config.c
6681
src/config.c
File diff suppressed because it is too large
Load Diff
@ -784,7 +784,13 @@ void fixed_memory_ptrs_shut()
|
||||
word32
|
||||
get_itimer()
|
||||
{
|
||||
#if defined(__i386) && defined(__GNUC__)
|
||||
#if defined(_WIN32)
|
||||
LARGE_INTEGER count;
|
||||
if (QueryPerformanceCounter(&count))
|
||||
return count.LowPart;
|
||||
else
|
||||
return 0;
|
||||
#elif defined(__i386) && defined(__GNUC__)
|
||||
/* Here's my bad ia32 asm code to do rdtsc */
|
||||
/* Linux source uses: */
|
||||
/* asm volatile("rdtsc" : "=a"(ret) : : "edx"); */
|
||||
@ -805,12 +811,6 @@ get_itimer()
|
||||
|
||||
asm volatile ("mftb %0" : "=r"(ret));
|
||||
return ret;
|
||||
#elif defined(_WIN32)
|
||||
LARGE_INTEGER count;
|
||||
if (QueryPerformanceCounter(&count))
|
||||
return count.LowPart;
|
||||
else
|
||||
return 0;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
|
@ -1,13 +1,16 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual C++ Express 2010
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Express 2012 for Windows Desktop
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gsport", "gsport.vcxproj", "{0B4E527A-DB50-4B5F-9B08-303ABAF7356A}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B} = {2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B}
|
||||
{E810477A-E004-4308-A58A-21393213EF89} = {E810477A-E004-4308-A58A-21393213EF89}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tfe", "tfe\tfe.vcxproj", "{E810477A-E004-4308-A58A-21393213EF89}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "atbridge", "atbridge\atbridge.vcxproj", "{2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
@ -22,6 +25,10 @@ Global
|
||||
{E810477A-E004-4308-A58A-21393213EF89}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{E810477A-E004-4308-A58A-21393213EF89}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{E810477A-E004-4308-A58A-21393213EF89}.Release|Win32.Build.0 = Release|Win32
|
||||
{2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -50,12 +50,12 @@
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN_SOUND;GSPORT_LITTLE_ENDIAN;HAVE_TFE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<CompileAs>CompileAsCpp</CompileAs>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN_SOUND;GSPORT_LITTLE_ENDIAN;HAVE_TFE;HAVE_ATBRIDGE;TOGGLE_STATUS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<CompileAs>CompileAsC</CompileAs>
|
||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<ErrorReporting>Prompt</ErrorReporting>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
@ -64,7 +64,7 @@
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Winmm.lib;Ws2_32.lib;Shlwapi.lib;$(SolutionDir)$(Configuration)\tfe.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>IPHLPAPI.lib;Winmm.lib;Ws2_32.lib;Shlwapi.lib;$(SolutionDir)$(Configuration)\tfe.lib;;$(SolutionDir)$(Configuration)\atbridge.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
@ -75,10 +75,10 @@
|
||||
<Optimization>Full</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN_SOUND;GSPORT_LITTLE_ENDIAN;HAVE_TFE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN_SOUND;GSPORT_LITTLE_ENDIAN;HAVE_ATBRIDGE;HAVE_TFE;TOGGLE_STATUS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<CompileAs>CompileAsCpp</CompileAs>
|
||||
<CompileAs>CompileAsC</CompileAs>
|
||||
<ErrorReporting>Prompt</ErrorReporting>
|
||||
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
@ -87,10 +87,10 @@
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>Winmm.lib;Ws2_32.lib;Shlwapi.lib;$(SolutionDir)$(Configuration)\tfe.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>IPHLPAPI.lib;Winmm.lib;Ws2_32.lib;Shlwapi.lib;$(SolutionDir)$(Configuration)\tfe.lib;;$(SolutionDir)$(Configuration)\atbridge.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
@ -118,6 +118,7 @@ perl make_inst s 16 instable.h > 16inst_s.h</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">8inst_c.h 16inst_c.h 8inst_s.h 16inst_s.h</Outputs>
|
||||
<LinkObjects Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkObjects>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="scc_llap.h" />
|
||||
<ClInclude Include="win_keymap.h" />
|
||||
<ClInclude Include="iwm.h" />
|
||||
<ClInclude Include="iwm_35_525.h" />
|
||||
@ -171,10 +172,17 @@ perl make_size c size_tab.h > size_c.h</Command>
|
||||
<ClCompile Include="moremem.c" />
|
||||
<ClCompile Include="paddles.c" />
|
||||
<ClCompile Include="parallel.c" />
|
||||
<ClCompile Include="printer.cpp" />
|
||||
<ClCompile Include="printer.cpp">
|
||||
<CompileAs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">CompileAsCpp</CompileAs>
|
||||
<CompileAs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">CompileAsCpp</CompileAs>
|
||||
</ClCompile>
|
||||
<ClCompile Include="scc_imagewriter.c" />
|
||||
<ClCompile Include="imagewriter.cpp" />
|
||||
<ClCompile Include="imagewriter.cpp">
|
||||
<CompileAs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">CompileAsCpp</CompileAs>
|
||||
<CompileAs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">CompileAsCpp</CompileAs>
|
||||
</ClCompile>
|
||||
<ClCompile Include="scc.c" />
|
||||
<ClCompile Include="scc_llap.c" />
|
||||
<ClCompile Include="scc_socket_driver.c" />
|
||||
<ClCompile Include="scc_windriver.c" />
|
||||
<ClCompile Include="sim65816.c" />
|
||||
|
@ -117,6 +117,9 @@
|
||||
<ClInclude Include="win_keymap.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="scc_llap.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="adb.c">
|
||||
@ -194,6 +197,9 @@
|
||||
<ClCompile Include="imagewriter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="scc_llap.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="size_tab.h">
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
GSport - an Apple //gs Emulator
|
||||
Copyright (C) 2010 by GSport contributors
|
||||
Copyright (C) 2010 - 2014 by GSport contributors
|
||||
|
||||
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
|
||||
|
||||
@ -1351,9 +1351,12 @@ io_read(word32 loc, double *cyc_ptr)
|
||||
return 0;
|
||||
case 0x41: /* 0xc041 */
|
||||
return g_c041_val;
|
||||
case 0x44: /* 0xc044 */
|
||||
// SCC LAD A
|
||||
return scc_read_lad(0);
|
||||
case 0x45: /* 0xc045 */
|
||||
halt_printf("Mega II mouse read: c045\n");
|
||||
return 0;
|
||||
// SCC LAD B
|
||||
return scc_read_lad(1);
|
||||
case 0x46: /* 0xc046 */
|
||||
tmp = g_c046_val;
|
||||
g_c046_val = (tmp & 0xbf) + ((tmp & 0x80) >> 1);
|
||||
@ -1385,7 +1388,6 @@ io_read(word32 loc, double *cyc_ptr)
|
||||
g_em_emubyte_cnt = 0;
|
||||
return 0;
|
||||
}
|
||||
case 0x44: /* 0xc044 */
|
||||
case 0x48: /* 0xc048 */
|
||||
case 0x49: /* 0xc049 */
|
||||
case 0x4a: /* 0xc04a */
|
||||
@ -2033,9 +2035,14 @@ io_write(word32 loc, int val, double *cyc_ptr)
|
||||
/* 0xc040 - 0xc04f */
|
||||
case 0x41: /* c041 */
|
||||
g_c041_val = val & 0x1f;
|
||||
if((val & 0xe7) != 0) {
|
||||
if((val & 0xe6) != 0) {
|
||||
halt_printf("write c041: %02x\n", val);
|
||||
}
|
||||
|
||||
if (val & C041_EN_MOUSE)
|
||||
{
|
||||
// Enable Mega II mouse
|
||||
}
|
||||
|
||||
if(!(val & C041_EN_VBL_INTS)) {
|
||||
/* no more vbl interrupt */
|
||||
@ -2331,7 +2338,8 @@ io_write(word32 loc, int val, double *cyc_ptr)
|
||||
case 0x9c: case 0x9d: case 0x9e: case 0x9f:
|
||||
if (g_parallel)
|
||||
{
|
||||
return parallel_write((word16)loc & 0xf, (byte)val);
|
||||
parallel_write((word16)loc & 0xf, (byte)val);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2379,7 +2387,8 @@ io_write(word32 loc, int val, double *cyc_ptr)
|
||||
case 0xbf:
|
||||
if (tfe_enabled)
|
||||
{
|
||||
return tfe_store((word16)loc & 0xf, (byte)val);
|
||||
tfe_store((word16)loc & 0xf, (byte)val);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
GSport - an Apple //gs Emulator
|
||||
Copyright (C) 2010 - 2011 by GSport contributors
|
||||
Copyright (C) 2010 - 2014 by GSport contributors
|
||||
|
||||
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
|
||||
|
||||
@ -289,12 +289,18 @@ private:
|
||||
Bit8u ASCII85CurCol; // Columns printed so far in the current lines
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
//Interfaces to C code
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#else
|
||||
#include <stdbool.h>
|
||||
typedef unsigned char Bit8u;
|
||||
#endif
|
||||
|
||||
void printer_init(int pdpi, int pwidth, int pheight, char* poutput, bool mpage);
|
||||
void printer_loop(Bit8u pchar);
|
||||
void printer_close();
|
||||
@ -304,4 +310,3 @@ void printer_feed();
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -249,6 +249,7 @@ void show_scc_state(void);
|
||||
void scc_log(int regnum, word32 val, double dcycs);
|
||||
void show_scc_log(void);
|
||||
word32 scc_read_reg(int port, double dcycs);
|
||||
word16 scc_read_lad(int port);
|
||||
void scc_write_reg(int port, word32 val, double dcycs);
|
||||
void scc_maybe_br_event(int port, double dcycs);
|
||||
void scc_evaluate_ints(int port);
|
||||
|
12
src/scc.h
12
src/scc.h
@ -36,8 +36,9 @@
|
||||
|
||||
/* my scc port 0 == channel A, port 1 = channel B */
|
||||
|
||||
#define SCC_INBUF_SIZE 512 /* must be a power of 2 */
|
||||
#define SCC_OUTBUF_SIZE 512 /* must be a power of 2 */
|
||||
// LLAP may have packets up to 603 bytes, and the buffers must be large enough to contain a single packet.
|
||||
#define SCC_INBUF_SIZE 1024 /* must be a power of 2 */
|
||||
#define SCC_OUTBUF_SIZE 1024 /* must be a power of 2 */
|
||||
|
||||
#define SCC_MODEM_MAX_CMD_STR 128
|
||||
|
||||
@ -47,7 +48,7 @@
|
||||
|
||||
STRUCT(Scc) {
|
||||
int port;
|
||||
int state;
|
||||
int state /* 0 == disconnected, 1 == real serial port, 2 == socket, 3 == LocalTalk */;
|
||||
int accfd;
|
||||
SOCKET sockfd;
|
||||
int socket_state;
|
||||
@ -64,6 +65,7 @@ STRUCT(Scc) {
|
||||
|
||||
int rx_queue_depth;
|
||||
byte rx_queue[4];
|
||||
unsigned int lad;
|
||||
|
||||
int in_rdptr;
|
||||
int in_wrptr;
|
||||
@ -78,7 +80,10 @@ STRUCT(Scc) {
|
||||
int wantint_rx;
|
||||
int wantint_tx;
|
||||
int wantint_zerocnt;
|
||||
int did_int_rx_first;
|
||||
int dcd;
|
||||
int sdlc_eof;
|
||||
int eom;
|
||||
|
||||
double br_dcycs;
|
||||
double tx_dcycs;
|
||||
@ -87,6 +92,7 @@ STRUCT(Scc) {
|
||||
int br_event_pending;
|
||||
int rx_event_pending;
|
||||
int tx_event_pending;
|
||||
byte irq_pending;
|
||||
|
||||
int char_size;
|
||||
int baud_rate;
|
||||
|
156
src/scc_llap.c
Normal file
156
src/scc_llap.c
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
GSport - an Apple //gs Emulator
|
||||
Copyright (C) 2013 - 2014 by GSport contributors
|
||||
Originally authored by Peter Neubauer
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
/* This is an interface between the SCC emulation and the LAP bridge. */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "defc.h"
|
||||
#include "scc.h"
|
||||
|
||||
int g_appletalk_bridging;
|
||||
int g_appletalk_turbo = 0;
|
||||
int g_appletalk_diagnostics = 0;
|
||||
int g_appletalk_network_hint = 0;
|
||||
|
||||
#ifdef HAVE_ATBRIDGE
|
||||
#include "atbridge/atbridge.h"
|
||||
#include "atbridge/llap.h"
|
||||
|
||||
extern Scc scc_stat[2];
|
||||
|
||||
extern int g_config_gsport_update_needed;
|
||||
static bool bridge_initialized = false;
|
||||
|
||||
void scc_llap_init()
|
||||
{
|
||||
bridge_initialized = atbridge_init();
|
||||
atbridge_set_net(g_appletalk_network_hint);
|
||||
}
|
||||
|
||||
void scc_llap_set_node(byte val)
|
||||
{
|
||||
atbridge_set_node(val);
|
||||
}
|
||||
|
||||
void scc_llap_update()
|
||||
{
|
||||
if (bridge_initialized)
|
||||
{
|
||||
atbridge_process();
|
||||
|
||||
if (g_appletalk_network_hint != atbridge_get_net())
|
||||
{
|
||||
g_appletalk_network_hint = atbridge_get_net();
|
||||
g_config_gsport_update_needed = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Transfer one packet from the bridge to the SCC **/
|
||||
void scc_llap_fill_readbuf(int port, int space_left, double dcycs)
|
||||
{
|
||||
atbridge_set_diagnostics(g_appletalk_diagnostics);
|
||||
|
||||
byte* data;
|
||||
|
||||
if (!bridge_initialized)
|
||||
return;
|
||||
|
||||
data = NULL;
|
||||
size_t bytes_read;
|
||||
size_t i;
|
||||
|
||||
llap_dequeue_out(dcycs, &bytes_read, &data);
|
||||
|
||||
for(i = 0; i < bytes_read; i++) {
|
||||
scc_add_to_readbuf(port, data[i], dcycs);
|
||||
}
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
/** Transfer one packet from the SCC to the AppleTalk bridge. **/
|
||||
void scc_llap_empty_writebuf(int port, double dcycs)
|
||||
{
|
||||
atbridge_set_diagnostics(g_appletalk_diagnostics);
|
||||
|
||||
Scc* scc_ptr;
|
||||
|
||||
if (!bridge_initialized)
|
||||
return;
|
||||
|
||||
int rdptr;
|
||||
int wrptr;
|
||||
int len;
|
||||
|
||||
scc_ptr = &(scc_stat[port]);
|
||||
|
||||
// If there's data in the output buffer, send it.
|
||||
rdptr = scc_ptr->out_rdptr;
|
||||
wrptr = scc_ptr->out_wrptr;
|
||||
if(rdptr == wrptr)
|
||||
return;
|
||||
|
||||
len = wrptr - rdptr;
|
||||
if (len < 0)
|
||||
{
|
||||
// The data is not contiguous since it wraps around the end of the buffer.
|
||||
// But, this should never happen since this function always empties the entire
|
||||
// buffer, and the buffer is large enough to hold the maximum packet size.
|
||||
halt_printf("SCC LLAP: Unexpected non-contiguous data. Dropping packet.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
// The data is contiguous, so read the data directly from the buffer.
|
||||
llap_enqueue_in(dcycs, len, &scc_ptr->out_buf[rdptr]);
|
||||
}
|
||||
|
||||
// Remove the sent data from the output buffer. Since the buffer contains
|
||||
// one complete packet, always send all of the data.
|
||||
scc_ptr->out_rdptr = 0;
|
||||
scc_ptr->out_wrptr = 0;
|
||||
|
||||
// Latch EOM to indicate that the SCC has sent the message.
|
||||
// The timing will be a bit off from the real hardware since we're not
|
||||
// emulating the sending hardware timing and CRC generation.
|
||||
scc_ptr->eom = 1;
|
||||
}
|
||||
|
||||
#else
|
||||
void scc_llap_init()
|
||||
{
|
||||
}
|
||||
|
||||
void scc_llap_set_node(byte val)
|
||||
{
|
||||
}
|
||||
|
||||
void scc_llap_update()
|
||||
{
|
||||
}
|
||||
|
||||
void scc_llap_fill_readbuf(int port, int space_left, double dcycs)
|
||||
{
|
||||
}
|
||||
|
||||
void scc_llap_empty_writebuf(int port, double dcycs)
|
||||
{
|
||||
}
|
||||
#endif
|
26
src/scc_llap.h
Normal file
26
src/scc_llap.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
GSport - an Apple //gs Emulator
|
||||
Copyright (C) 2013 by GSport contributors
|
||||
Originally authored by Peter Neubauer
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
void scc_llap_init();
|
||||
void scc_llap_shutdown();
|
||||
void scc_llap_update();
|
||||
void scc_llap_fill_readbuf(int port, int space_left, double dcycs);
|
||||
void scc_llap_empty_writebuf(int port, double dcycs);
|
||||
void scc_llap_set_node(byte val);
|
@ -25,7 +25,10 @@
|
||||
#include "scc.h"
|
||||
#ifndef UNDER_CE //OG
|
||||
#include <signal.h>
|
||||
#endif
|
||||
#endif
|
||||
#ifdef __CYGWIN__
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
extern Scc scc_stat[2];
|
||||
extern int g_serial_modem[];
|
||||
|
||||
|
@ -24,6 +24,11 @@
|
||||
#include "defc.h"
|
||||
#include "scc.h"
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
#include <Windows.h>
|
||||
#include <NspAPI.h>
|
||||
#endif
|
||||
|
||||
#ifdef UNDER_CE
|
||||
#define vsnprintf _vsnprintf
|
||||
#endif
|
||||
|
5079
src/sim65816.c
5079
src/sim65816.c
File diff suppressed because it is too large
Load Diff
@ -55,7 +55,7 @@
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||
<CompileAs>CompileAsCpp</CompileAs>
|
||||
<CompileAs>CompileAsC</CompileAs>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
@ -74,7 +74,7 @@
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;TFE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||
<CompileAs>CompileAsCpp</CompileAs>
|
||||
<CompileAs>CompileAsC</CompileAs>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
@ -98,8 +98,8 @@
|
||||
<ClInclude Include="types.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\arch\win32\tfearch.c" />
|
||||
<ClCompile Include="tfe.c" />
|
||||
<ClCompile Include="tfearch.c" />
|
||||
<ClCompile Include="tfesupp.c" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
@ -47,7 +47,7 @@
|
||||
<ClCompile Include="tfesupp.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\arch\win32\tfearch.c">
|
||||
<ClCompile Include="tfearch.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
|
54
src/arch/unix/tfearch.c → src/tfe/tfearch.c
Executable file → Normal file
54
src/arch/unix/tfearch.c → src/tfe/tfearch.c
Executable file → Normal file
@ -26,16 +26,16 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <pcap.h>
|
||||
#include "../../tfe/tfesupp.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "../../defc.h"
|
||||
#include "../../tfe/protos_tfe.h"
|
||||
#include "../atbridge/pcap_delay.h"
|
||||
#include "tfesupp.h"
|
||||
#include "../defc.h"
|
||||
#include "protos_tfe.h"
|
||||
|
||||
/** #define TFE_DEBUG_ARCH 1 **/
|
||||
/** #define TFE_DEBUG_PKTDUMP 1 **/
|
||||
@ -51,7 +51,6 @@
|
||||
|
||||
//static log_t tfe_arch_log = LOG_ERR;
|
||||
|
||||
|
||||
static pcap_if_t *TfePcapNextDev = NULL;
|
||||
static pcap_if_t *TfePcapAlldevs = NULL;
|
||||
static pcap_t *TfePcapFP = NULL;
|
||||
@ -82,16 +81,7 @@ void debug_output( const char *text, unsigned char *what, int count )
|
||||
} while (len1>0);
|
||||
}
|
||||
#endif // #ifdef TFE_DEBUG_PKTDUMP
|
||||
/*
|
||||
static
|
||||
void TfePcapCloseAdapter(void)
|
||||
{
|
||||
if (TfePcapAlldevs) {
|
||||
(*p_pcap_freealldevs)(TfePcapAlldevs);
|
||||
TfePcapAlldevs = NULL;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
These functions let the UI enumerate the available interfaces.
|
||||
@ -115,7 +105,7 @@ void TfePcapCloseAdapter(void)
|
||||
*/
|
||||
int tfe_arch_enumadapter_open(void)
|
||||
{
|
||||
if (pcap_findalldevs(&TfePcapAlldevs, TfePcapErrbuf) == -1)
|
||||
if (pcapdelay_findalldevs(&TfePcapAlldevs, TfePcapErrbuf) == -1)
|
||||
{
|
||||
#ifdef TFE_DEBUG_ARCH
|
||||
log_message(tfe_arch_log, "ERROR in TfeEnumAdapterOpen: pcap_findalldevs: '%s'", TfePcapErrbuf);
|
||||
@ -154,7 +144,7 @@ int tfe_arch_enumadapter(char **ppname, char **ppdescription)
|
||||
int tfe_arch_enumadapter_close(void)
|
||||
{
|
||||
if (TfePcapAlldevs) {
|
||||
pcap_freealldevs(TfePcapAlldevs);
|
||||
pcapdelay_freealldevs(TfePcapAlldevs);
|
||||
TfePcapAlldevs = NULL;
|
||||
}
|
||||
return 1;
|
||||
@ -195,7 +185,7 @@ int TfePcapOpenAdapter(const char *interface_name)
|
||||
}
|
||||
}
|
||||
|
||||
TfePcapFP = pcap_open_live(TfePcapDevice->name, 1700, 1, 20, TfePcapErrbuf);
|
||||
TfePcapFP = pcapdelay_open_live(TfePcapDevice->name, 1700, 1, 20, TfePcapErrbuf);
|
||||
if ( TfePcapFP == NULL)
|
||||
{
|
||||
#ifdef TFE_DEBUG_ARCH
|
||||
@ -205,7 +195,7 @@ int TfePcapOpenAdapter(const char *interface_name)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (pcap_setnonblock(TfePcapFP, 1, TfePcapErrbuf)<0)
|
||||
if (pcapdelay_setnonblock(TfePcapFP, 1, TfePcapErrbuf)<0)
|
||||
{
|
||||
#ifdef TFE_DEBUG_ARCH
|
||||
log_message(tfe_arch_log, "WARNING: Setting PCAP to non-blocking failed: '%s'", TfePcapErrbuf);
|
||||
@ -213,7 +203,7 @@ int TfePcapOpenAdapter(const char *interface_name)
|
||||
}
|
||||
|
||||
/* Check the link layer. We support only Ethernet for simplicity. */
|
||||
if(pcap_datalink(TfePcapFP) != DLT_EN10MB)
|
||||
if(pcapdelay_datalink(TfePcapFP) != DLT_EN10MB)
|
||||
{
|
||||
#ifdef TFE_DEBUG_ARCH
|
||||
log_message(tfe_arch_log, "ERROR: TFE works only on Ethernet networks.");
|
||||
@ -278,24 +268,6 @@ void tfe_arch_set_mac( const unsigned char mac[6] )
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
void tfe_arch_set_hashfilter(const DWORD hash_mask[2])
|
||||
{
|
||||
#if defined(TFE_DEBUG_ARCH) || defined(TFE_DEBUG_FRAMES)
|
||||
log_message( tfe_arch_log, "New hash filter set: %08X:%08X.",
|
||||
hash_mask[1], hash_mask[0]);
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
void tfe_arch_receive_remove_committed_frame(void)
|
||||
{
|
||||
#ifdef TFE_DEBUG_ARCH
|
||||
log_message( tfe_arch_log, "tfe_arch_receive_remove_committed_frame()." );
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
|
||||
void tfe_arch_recv_ctl( int bBroadcast, /* broadcast */
|
||||
int bIA, /* individual address (IA) */
|
||||
@ -364,7 +336,7 @@ int tfe_arch_receive_frame(TFE_PCAP_INTERNAL *pinternal)
|
||||
int ret = -1;
|
||||
|
||||
/* check if there is something to receive */
|
||||
if (pcap_dispatch(TfePcapFP, 1, (pcap_handler)TfePcapPacketHandler, (unsigned char*)pinternal)!=0) {
|
||||
if (pcapdelay_dispatch(TfePcapFP, 1, (pcap_handler)TfePcapPacketHandler, (unsigned char*)pinternal)!=0) {
|
||||
/* Something has been received */
|
||||
ret = pinternal->len;
|
||||
}
|
||||
@ -399,7 +371,7 @@ void tfe_arch_transmit(int force, /* FORCE: Delete waiting frames in trans
|
||||
debug_output( "Transmit frame: ", txframe, txlength);
|
||||
#endif // #ifdef TFE_DEBUG_PKTDUMP
|
||||
|
||||
if (pcap_sendpacket(TfePcapFP, txframe, txlength) == -1) {
|
||||
if (pcapdelay_sendpacket(TfePcapFP, txframe, txlength) == -1) {
|
||||
//log_message(tfe_arch_log, "WARNING! Could not send packet!");
|
||||
}
|
||||
}
|
@ -1,15 +1,14 @@
|
||||
TARGET = gsportfb
|
||||
TFEOBJ = tfe/tfe.o arch/unix/tfearch.o tfe/tfesupp.o
|
||||
OBJECTS = $(OBJECTS1) $(TFEOBJ) fbdriver.o
|
||||
OBJECTS = $(OBJECTS1) $(TFEOBJ) $(ATOBJ) $(TFEOBJ) fbdriver.o
|
||||
CC = g++
|
||||
CCOPTS = -O2 -Wall -fomit-frame-pointer -march=armv6
|
||||
OPTS = -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE
|
||||
CCOPTS = -O2 -Wall -fomit-frame-pointer -std=gnu99 -march=armv6
|
||||
OPTS = -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DHAVE_ATBRIDGE
|
||||
SUFFIX =
|
||||
NAME = gsportfb
|
||||
LDFLAGS =
|
||||
LDOPTS =
|
||||
LD = $(CC)
|
||||
EXTRA_LIBS = -lpcap
|
||||
EXTRA_LIBS =
|
||||
EXTRA_SPECIALS =
|
||||
|
||||
AS = cc
|
||||
|
@ -1,15 +1,14 @@
|
||||
TARGET = gsportx
|
||||
TFEOBJ = tfe/tfe.o arch/unix/tfearch.o tfe/tfesupp.o
|
||||
OBJECTS = $(OBJECTS1) $(TFEOBJ) xdriver.o
|
||||
OBJECTS = $(OBJECTS1) $(TFEOBJ) $(ATOBJ) $(PCAPOBJ) xdriver.o
|
||||
CC = g++
|
||||
CCOPTS = -O2 -Wall -fomit-frame-pointer -march=armv6
|
||||
OPTS = -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DTOGGLE_STATUS
|
||||
CCOPTS = -O2 -Wall -fomit-frame-pointer -std=gnu99 -march=armv6
|
||||
OPTS = -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DTOGGLE_STATUS -DHAVE_ATBRIDGE
|
||||
SUFFIX =
|
||||
NAME = gsportx
|
||||
LDFLAGS =
|
||||
LDOPTS =
|
||||
LD = $(CC)
|
||||
EXTRA_LIBS = -lXext -lpcap
|
||||
EXTRA_LIBS = -lXext
|
||||
EXTRA_SPECIALS =
|
||||
|
||||
AS = cc
|
||||
|
@ -1,11 +1,10 @@
|
||||
TARGET = gsport.exe
|
||||
TFEOBJ = tfe/tfe.o arch/win32/tfearch.o tfe/tfesupp.o
|
||||
OBJECTS = $(OBJECTS1) $(TFEOBJ) scc_windriver.o win32snd_driver.o win_console.o win_generic.o gsport32.o
|
||||
CCOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DWIN_SOUND -DTOGGLE_STATUS
|
||||
CPPOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DTOGGLE_STATUS
|
||||
OBJECTS = $(OBJECTS1) $(TFEOBJ) $(ATOBJ) $(PCAPOBJ) scc_windriver.o win32snd_driver.o win_console.o win_generic.o gsport32.o
|
||||
CCOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DWIN_SOUND -DTOGGLE_STATUS -DWIN32 -D_WIN32 -D__USE_W32_SOCKETS -D_WINSOCK2API_ -std=gnu99 -DHAVE_ATBRIDGE
|
||||
CPPOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DTOGGLE_STATUS -DWIN32 -D_WIN32 -D__USE_W32_SOCKETS -D_WINSOCK2API_ -DHAVE_ATBRIDGE
|
||||
SUFFIX = ".exe"
|
||||
NAME = gsport
|
||||
EXTRA_LIBS = -lcomdlg32 -lShlwapi
|
||||
EXTRA_LIBS = -Larch/win32 -lcomdlg32 -lShlwapi -lIPHlpApi
|
||||
|
||||
XOPTS = -Wall -fomit-frame-pointer -march=pentium
|
||||
XLIBS =
|
||||
XOPTS = -Wall -fomit-frame-pointer -march=i686
|
||||
XLIBS =
|
@ -1,11 +1,11 @@
|
||||
TARGET = gsport.exe
|
||||
TFEOBJ = tfe/tfe.o arch/win32/tfearch.o tfe/tfesupp.o
|
||||
OBJECTS = $(OBJECTS1) $(TFEOBJ) scc_windriver.o win32snd_driver.o win_console.o win_generic.o gsport32.o
|
||||
CCOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DWIN_SOUND -DHAVE_SDL -DWIN32 -D_WIN32 -D__USE_W32_SOCKETS -DTOGGLE_STATUS
|
||||
CPPOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DHAVE_SDL -DWIN32 -D_WIN32 -D__USE_W32_SOCKETS -I /usr/include/freetype2 -I/usr/include/SDL -DTOGGLE_STATUS
|
||||
OBJECTS = $(OBJECTS1) $(TFEOBJ) $(ATOBJ) $(PCAPOBJ) scc_windriver.o win32snd_driver.o win_console.o win_generic.o gsport32.o
|
||||
CCOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DWIN_SOUND -DHAVE_SDL -DTOGGLE_STATUS -DWIN32 -D_WIN32 -D__USE_W32_SOCKETS -D_WINSOCK2API_ -std=gnu99 -DHAVE_ATBRIDGE
|
||||
CPPOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DHAVE_SDL -DTOGGLE_STATUS -DWIN32 -D_WIN32 -D__USE_W32_SOCKETS -D_WINSOCK2API_ -DHAVE_ATBRIDGE -I /usr/include/freetype2 -I/usr/include/SDL
|
||||
|
||||
SUFFIX = ".exe"
|
||||
NAME = gsport
|
||||
EXTRA_LIBS = -lSDL -lfreetype -lcomdlg32 -lShlwapi
|
||||
EXTRA_LIBS = -Larch/win32 -lSDL -lfreetype -lcomdlg32 -lShlwapi -lIPHlpApi
|
||||
|
||||
XOPTS = -Wall -fomit-frame-pointer -march=pentium
|
||||
XLIBS =
|
||||
XOPTS = -Wall -fomit-frame-pointer -march=i686
|
||||
XLIBS =
|
@ -1,8 +1,7 @@
|
||||
|
||||
TARGET = gsportx
|
||||
OBJECTS = $(OBJECTS1) xdriver.o
|
||||
OBJECTS = $(OBJECTS1) $(TFEOBJ) $(ATOBJ) $(PCAPOBJ) xdriver.o
|
||||
CC = g++
|
||||
CCOPTS = -O2 -Wall -fomit-frame-pointer -march=pentium
|
||||
CCOPTS = -O2 -Wall -fomit-frame-pointer -std=gnu99 -march=i686 -DHAVE_TFE -DHAVE_ATBRIDGE -DTOGGLE_STATUS
|
||||
OPTS = -DGSPORT_LITTLE_ENDIAN
|
||||
SUFFIX =
|
||||
NAME = gsportx
|
||||
@ -15,5 +14,4 @@ EXTRA_SPECIALS =
|
||||
AS = cc
|
||||
PERL = perl
|
||||
|
||||
XOPTS = -I/usr/X11R6/include
|
||||
|
||||
XOPTS = -I/usr/X11R6/include
|
Loading…
x
Reference in New Issue
Block a user