Merged revision(s) 262-316 from branches/pneubauer/appletalk: Add support for AppleTalk emulation and bridging.

This commit is contained in:
Peter 2014-04-15 03:23:34 +00:00
parent 20ca867301
commit 58cc512bc9
46 changed files with 10332 additions and 7826 deletions

View File

@ -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:

View File

@ -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.

Binary file not shown.

View File

@ -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

Binary file not shown.

View File

@ -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
View 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
View 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
View 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
View 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
View 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);

View 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>

View 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
View 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, &current->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(&ether->source, &HW_LOCAL, sizeof(ether->source)) != 0) && /* Ignore packets sent from our node. */
(memcmp(&ether->dest, &HW_LOCAL, sizeof(ether->dest)) == 0 || /* Accept packets destined for our node ... */
memcmp(&ether->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, &ether->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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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);

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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 &gt; 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 &gt; 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" />

View File

@ -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">

View File

@ -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
{

View File

@ -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

View File

@ -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);

2876
src/scc.c

File diff suppressed because it is too large Load Diff

View File

@ -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
View 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
View 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);

View File

@ -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[];

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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" />

View File

@ -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
View 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!");
}
}

View File

@ -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

View File

@ -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

View File

@ -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 =

View File

@ -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 =

View File

@ -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