This commit is contained in:
David Schmidt 2021-02-12 12:19:49 -05:00 committed by GitHub
commit e3f97daece
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
72 changed files with 32033 additions and 20006 deletions

2
.gitignore vendored
View File

@ -1,2 +1,4 @@
/.project
/GSport.app
/gsport
.DS_Store

View File

@ -1,12 +1,12 @@
# GSport central makefile - you need a 'vars' file linked/copied from a 'vars_xxx' template to build.
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 imagewriter.o scc_imagewriter.o scc_llap.o
ATOBJ = atbridge/aarp.o atbridge/atbridge.o atbridge/elap.o atbridge/llap.o atbridge/port.o
OBJECTS1 = adb.o clock.o config.o debug.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 glog.o \
imagewriter.o scc_imagewriter.o scc_llap.o options.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
FSTOBJ = unix_host_common.o host_common.o host_fst.o host_mli.o
include vars
@ -25,68 +25,51 @@ clean:
- rm -f compile_time.o
- rm -f 8inst_c.h
- rm -f 16inst_c.h
- rm -rf ../GSport.app
- rm -rf ../GSportDmg
specials: 8inst_s 16inst_s 8size 16size 8inst_c 16inst_c size_c size_s
specials_clean:
rm -f 8inst_s 16inst_s 8size 16size 8inst_c 16inst_c size_c size_s
# Linux/OSX/RPi SDL builds
gsport: $(OBJECTS) compile_time.o
$(LD) $(CCOPTS) $(LDOPTS) $(OBJECTS) compile_time.o $(LDFLAGS) -o $(NAME)$(SUFFIX) $(EXTRA_LIBS)
echo $(OBJECTS)
cp gsport ..
# Mac builds:
gsportmac: $(OBJECTS) compile_time.o
$(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) compile_time.o $(LDFLAGS) -o gsport $(EXTRA_LIBS)
mkdir -p ../GSport.app/Contents/Resources/English.lproj/main.nib
mkdir -p ../GSport.app/Contents/MacOS
mv gsport ../GSport.app/Contents/MacOS/GSport
echo "APPL????" > ../GSport.app/Contents/PkgInfo
cp -f arch/mac/Info.plist ../GSport.app/Contents/
cp -f arch/mac/info.nib ../GSport.app/Contents/Resources/English.lproj/main.nib
cp -f arch/mac/classes.nib ../GSport.app/Contents/Resources/English.lproj/main.nib
cp -f arch/mac/objects.xib ../GSport.app/Contents/Resources/English.lproj/main.nib
cp -f arch/mac/gsporticon.icns ../GSport.app/Contents/Resources/
cp -f arch/mac/525.icns ../GSport.app/Contents/Resources/
cp -f arch/mac/2mg.icns ../GSport.app/Contents/Resources/
touch '../GSport.app/Icon?'
rm -rf ../GSportDmg
mkdir ../GSportDmg
mkdir ../GSportDmg/GSport
cp ../LICENSE ../GSportDmg/GSport
cp -f parallel.rom ../GSportDmg/GSport
cp -f ../lib/NoBoot.po ../GSportDmg/GSport
mv ../GSport.app ../GSportDmg/GSport
cp -f ../config.template ../GSportDmg/GSport/config.txt
cp ../GSport.html ../GSportDmg/GSport/GSport.html
arch/mac/makedmg.sh .. GSportDmg GSport GSport 7
# Linux for X builds:
# Linux/OSX XWindows builds
gsportx: $(OBJECTS) compile_time.o
$(LD) $(CCOPTS) $(LDOPTS) $(OBJECTS) compile_time.o $(LDFLAGS) -o $(NAME)$(SUFFIX) $(XLIBS) $(EXTRA_LIBS) -lX11
echo $(OBJECTS)
mv gsportx ..
cp -f ../config.template ../config.txt
# NOT CURRENTLY SUPPORTED
# Linux framebuffer builds:
gsportfb: $(OBJECTS) compile_time.o
$(LD) $(CCOPTS) $(LDOPTS) $(OBJECTS) compile_time.o $(LDFLAGS) -o $(NAME)$(SUFFIX) $(EXTRA_LIBS)
echo $(OBJECTS)
mv gsportfb ..
cp -f ../config.template ../config.txt
# Mingw32 (native windows) builds:
gsport.exe: $(OBJECTS) compile_time.o
# Mingw32 / Cygwin builds: The Win32 API version
gsport32.exe: $(OBJECTS) compile_time.o
g++ $(CCOPTS) $(LDOPTS) $(OBJECTS) compile_time.o $(LDFLAGS) -o $(NAME)$(SUFFIX) $(EXTRA_LIBS) -lwinmm -lgdi32 -ldsound -lcomctl32 -lws2_32 -lshell32
mkdir -p ../GSport.app/lib
cp -f gsport.exe ../GSport.app/GSport.exe
cp -f ../config.template ../GSport.app/config.txt
cp -f ../lib/*.ttf ../GSport.app/lib
cp -f ../lib/arch/win32/*.dll ../GSport.app
cp -f ../lib/NoBoot.po ../GSport.app
cp -f GSport.bat ../GSport.app/GSport.bat
cp -f parallel.rom ../GSport.app
cp -f ../LICENSE ../GSport.app
cp -f ../GSport.html ../GSport.app
#mkdir -p ../GSport.app/lib
#cp -f gsport.exe ../GSport.app/GSport.exe
#cp -f ../config.template ../GSport.app/config.txt
#cp -f ../lib/*.ttf ../GSport.app/lib
#cp -f ../lib/arch/win32/*.dll ../GSport.app
#cp -f ../lib/NoBoot.po ../GSport.app
#cp -f GSport.bat ../GSport.app/GSport.bat
#cp -f parallel.rom ../GSport.app
#cp -f ../COPYING.txt ../GSport.app
cp gsport32.exe ..
# Mingw32 / Cygwin builds: The SDL version (builds, but non-functioning)
gsport.exe: $(OBJECTS) compile_time.o
#g++ $(CCOPTS) $(LDOPTS) $(OBJECTS) compile_time.o $(LDFLAGS) -o $(NAME)$(SUFFIX) $(EXTRA_LIBS) -lwinmm -lgdi32 -ldsound -lcomctl32 -lws2_32 -lshell32
#g++ $(CCOPTS) $(LDOPTS) $(OBJECTS) compile_time.o $(LDFLAGS) -o $(NAME)$(SUFFIX) $(EXTRA_LIBS) -mwindows
g++ $(CCOPTS) $(LDOPTS) $(OBJECTS) compile_time.o $(LDFLAGS) -o $(NAME)$(SUFFIX) $(EXTRA_LIBS) -lwinmm -lgdi32 -ldsound -lcomctl32 -lws2_32 -lshell32
cp gsport.exe ..
8inst_c.h: instable.h
$(PERL) make_inst c 8 instable.h > 8inst_c.h

3812
src/adb.c

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,7 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 by GSport contributors
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey

View File

@ -0,0 +1,5 @@
set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_library(atbridge aarp.c atbridge.c elap.c llap.c pcap_delay.c port.c)
target_compile_definitions(atbridge PUBLIC HAVE_ATBRIDGE)

View File

@ -1,302 +1,289 @@
/*
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;
}
}
}
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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;
}
}
}

View File

@ -1,37 +1,40 @@
/*
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);
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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);

View File

@ -1,137 +1,140 @@
/*
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)
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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)

View File

@ -1,426 +1,408 @@
/*
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);
}
}
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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);
}
}

View File

@ -1,47 +1,49 @@
/*
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);
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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

@ -1,108 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.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>
<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>v140</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</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>
<?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>
<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

@ -1,66 +1,66 @@
<?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>
</Project>
<?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>
</Project>

View File

@ -1,396 +1,389 @@
/*
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;
}
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
// 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();
}
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
#ifdef AF_PACKET
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
#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();
}

View File

@ -1,36 +1,39 @@
/*
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();
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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();

View File

@ -1,117 +1,120 @@
/*
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 }};
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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 }};

View File

@ -1,332 +1,324 @@
/*
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);
}
}
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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);
}
}

View File

@ -1,37 +1,40 @@
/*
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();
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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();

View File

@ -1,170 +1,161 @@
/*
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;
}
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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
return 0;
}
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;
}

View File

@ -1,46 +1,52 @@
/*
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 *);
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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>
#elif __APPLE__
#include <pcap/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 *);

View File

@ -1,139 +1,132 @@
/*
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;
}
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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;
}

View File

@ -1,58 +1,61 @@
/*
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);
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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

@ -1,6 +1,7 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 by GSport contributors
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
@ -19,20 +20,19 @@
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define CONF_BUF_LEN 1024
#define COPY_BUF_SIZE 4096
#define CONF_BUF_LEN 1024
#define COPY_BUF_SIZE 4096
#define CFG_PRINTF_BUFSIZE 2048
#define CFG_PATH_MAX 1024
#define CFG_NUM_SHOWENTS 16
#define CFG_PATH_MAX 1024
#define CFG_NUM_SHOWENTS 16
#define CFGTYPE_MENU 1
#define CFGTYPE_INT 2
#define CFGTYPE_INT 2
#define CFGTYPE_DISK 3
#define CFGTYPE_FUNC 4
#define CFGTYPE_FILE 5
#define CFGTYPE_STR 6
#define CFGTYPE_DIR 7
/* CFGTYPE limited to just 4 bits: 0-15 */
/* Cfg_menu, Cfg_dirent and Cfg_listhdr are defined in defc.h */
@ -42,3 +42,33 @@ STRUCT(Cfg_defval) {
int intval;
char *strval;
};
int cfg_get_fd_size(char *filename);
int cfg_partition_read_block(FILE *file, void *buf, int blk, int blk_size);
int cfg_partition_find_by_name_or_num(FILE *file, const char *partnamestr, int part_num, Disk *dsk);
int cfg_maybe_insert_disk(int slot, int drive, const char *namestr);
int cfg_stat(char *path, struct stat *sb);
int cfg_partition_make_list(char *filename, FILE *file);
void cfg_htab_vtab(int x, int y);
void cfg_home(void);
void cfg_cleol(void);
void cfg_putchar(int c);
void cfg_printf(const char *fmt, ...);
void cfg_print_num(int num, int max_len);
void cfg_get_disk_name(char *outstr, int maxlen, int type_ext, int with_extras);
void cfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int change);
void cfg_get_base_path(char *pathptr, const char *inptr, int go_up);
void cfg_file_init(void);
void cfg_free_alldirents(Cfg_listhdr *listhdrptr);
void cfg_file_add_dirent(Cfg_listhdr *listhdrptr, const char *nameptr, int is_dir, int size, int image_start, int part_num);
int cfg_dirent_sortfn(const void *obj1, const void *obj2);
int cfg_str_match(const char *str1, const char *str2, int len);
void cfg_file_readdir(const char *pathptr);
char *cfg_shorten_filename(const char *in_ptr, int maxlen);
void cfg_fix_topent(Cfg_listhdr *listhdrptr);
void cfg_file_draw(void);
void cfg_partition_selected(void);
void cfg_file_update_ptr(char *str);
void cfg_file_selected(int);
void cfg_file_handle_key(int key);

45
src/config.txt Normal file
View File

@ -0,0 +1,45 @@
# GSport configuration file version 0.4
s5d1 =
s5d2 =
s6d1 =
s6d2 =
s7d1 =
bram1[00] = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bram1[10] = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bram1[20] = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bram1[30] = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bram1[40] = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bram1[50] = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bram1[60] = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bram1[70] = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bram1[80] = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bram1[90] = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bram1[a0] = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bram1[b0] = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bram1[c0] = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bram1[d0] = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bram1[e0] = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bram1[f0] = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bram3[00] = 00 00 00 01 00 00 0d 06 02 01 01 00 01 00 00 00
bram3[10] = 00 00 07 06 02 01 01 00 00 00 0f 06 06 00 05 06
bram3[20] = 01 00 00 00 00 00 00 01 00 00 00 00 05 02 02 00
bram3[30] = 00 00 2d 2d 00 00 00 00 00 00 02 02 02 06 08 00
bram3[40] = 01 02 03 04 05 06 07 0a 00 01 02 03 04 05 06 07
bram3[50] = 08 09 0a 0b 0c 0d 0e 0f 00 00 ff ff ff ff ff ff
bram3[60] = ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
bram3[70] = ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
bram3[80] = ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
bram3[90] = ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
bram3[a0] = ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
bram3[b0] = ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
bram3[c0] = ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
bram3[d0] = ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
bram3[e0] = ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
bram3[f0] = ff ff ff ff ff ff ff ff ff ff ff ff 36 2d 9c 87

1520
src/debug.c Normal file

File diff suppressed because it is too large Load Diff

30
src/debug.h Normal file
View File

@ -0,0 +1,30 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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
*/
extern void debug_init();
extern void debug_poll_only();
extern void debug_cpu_test();
// used in sim65816.c
void debug_server_poll();
int debug_events_waiting();
void debug_handle_event();

View File

@ -1,6 +1,7 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 by GSport contributors
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
@ -21,15 +22,15 @@
#include "defcomm.h"
// OG redirect printf to console
#ifdef ACTIVEGS
#include <stdio.h>
extern "C" int outputInfo(const char* format,...);
extern "C" int fOutputInfo(FILE*,const char* format,...);
#define printf outputInfo
#define fprintf fOutputInfo
#endif
// OG redirect printf to console
#ifdef ACTIVEGS
#include <stdio.h>
extern "C" int outputInfo(const char* format,...);
extern "C" int fOutputInfo(FILE*,const char* format,...);
#define printf outputInfo
#define fprintf fOutputInfo
#endif
#define STRUCT(a) typedef struct _ ## a a; struct _ ## a
typedef unsigned char byte;
@ -44,30 +45,31 @@ typedef unsigned long long word64;
void U_STACK_TRACE();
/* 28MHz crystal, plus every 65th 1MHz cycle is stretched 140ns */
#define CYCS_28_MHZ (28636360)
#define DCYCS_28_MHZ (1.0*CYCS_28_MHZ)
#define CYCS_3_5_MHZ (CYCS_28_MHZ/8)
#define DCYCS_1_MHZ ((DCYCS_28_MHZ/28.0)*(65.0*7/(65.0*7+1.0)))
#define CYCS_1_MHZ ((int)DCYCS_1_MHZ)
#define CYCS_28_MHZ (28636360)
#define DCYCS_28_MHZ (1.0*CYCS_28_MHZ)
#define CYCS_3_5_MHZ (CYCS_28_MHZ/8)
#define DCYCS_1_MHZ ((DCYCS_28_MHZ/28.0)*(65.0*7/(65.0*7+1.0)))
#define CYCS_1_MHZ ((int)DCYCS_1_MHZ)
/* #define DCYCS_IN_16MS_RAW (DCYCS_1_MHZ / 60.0) */
#define DCYCS_IN_16MS_RAW (262.0 * 65.0)
#define DCYCS_IN_16MS_RAW (262.0 * 65.0)
/* Use precisely 17030 instead of forcing 60 Hz since this is the number of */
/* 1MHz cycles per screen */
#define DCYCS_IN_16MS ((double)((int)DCYCS_IN_16MS_RAW))
#define DRECIP_DCYCS_IN_16MS (1.0 / (DCYCS_IN_16MS))
#define DCYCS_IN_16MS ((double)((int)DCYCS_IN_16MS_RAW))
#define DRECIP_DCYCS_IN_16MS (1.0 / (DCYCS_IN_16MS))
#ifdef GSPORT_LITTLE_ENDIAN
# define BIGEND(a) ((((a) >> 24) & 0xff) + \
(((a) >> 8) & 0xff00) + \
(((a) << 8) & 0xff0000) + \
(((a) << 24) & 0xff000000))
# define GET_BE_WORD16(a) ((((a) >> 8) & 0xff) + (((a) << 8) & 0xff00))
# define GET_BE_WORD32(a) (BIGEND(a))
// @todo: look at using <byteswap.h> for fastest platform implementations
# define BIGEND(a) ((((a) >> 24) & 0xff) + \
(((a) >> 8) & 0xff00) + \
(((a) << 8) & 0xff0000) + \
(((a) << 24) & 0xff000000))
# define GET_BE_WORD16(a) ((((a) >> 8) & 0xff) + (((a) << 8) & 0xff00))
# define GET_BE_WORD32(a) (BIGEND(a))
#else
# define BIGEND(a) (a)
# define GET_BE_WORD16(a) (a)
# define GET_BE_WORD32(a) (a)
# define BIGEND(a) (a)
# define GET_BE_WORD16(a) (a)
# define GET_BE_WORD32(a) (a)
#endif
#define MAXNUM_HEX_PER_LINE 32
@ -76,54 +78,54 @@ void U_STACK_TRACE();
# include <libc.h>
#endif
#if !defined(_WIN32) && !defined (__OS2__) && !defined(UNDER_CE) // OG
#if !defined(_WIN32) && !defined(UNDER_CE) // OG
# include <unistd.h>
# include <sys/ioctl.h>
# include <sys/wait.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#ifndef UNDER_CE // OG CE SPecific
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#ifndef UNDER_CE // OG CE SPecific
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
// OG Adding support for open
#ifdef WIN32
#include <io.h>
#endif
#else
extern int errno;
extern int open(const char* name,int,...);
extern int read(int,char*,int);
extern int close(int);
extern int write( int fd, const void *buffer, unsigned int count );
extern int lseek(int,int,int);
struct stat { int st_size; };
extern int stat(const char* name, struct stat*);
extern int fstat(int, struct stat*);
#define O_RDWR 1
#define O_BINARY 2
#define O_RDONLY 4
#define O_WRONLY 8
#define O_CREAT 16
#define O_TRUNC 32
#define EAGAIN 11
#define EINTR 4
#endif
// OG Adding support for open
#ifdef WIN32
#include <io.h>
#endif
#else
extern int errno;
extern int open(const char* name,int,...);
extern int read(int,char*,int);
extern int close(int);
extern int write( int fd, const void *buffer, unsigned int count );
extern int lseek(int,int,int);
struct stat { int st_size; };
extern int stat(const char* name, struct stat*);
extern int fstat(int, struct stat*);
#define O_RDWR 1
#define O_BINARY 2
#define O_RDONLY 4
#define O_WRONLY 8
#define O_CREAT 16
#define O_TRUNC 32
#define EAGAIN 11
#define EINTR 4
#endif
#ifdef HPUX
# include <machine/inline.h> /* for GET_ITIMER */
# include <machine/inline.h> /* for GET_ITIMER */
#endif
#ifdef SOLARIS
@ -132,239 +134,242 @@ extern int fstat(int, struct stat*);
#ifndef O_BINARY
/* work around some Windows junk */
# define O_BINARY 0
# define O_BINARY 0
#endif
STRUCT(Pc_log) {
double dcycs;
word32 dbank_kpc;
word32 instr;
word32 psr_acc;
word32 xreg_yreg;
word32 stack_direct;
word32 pad;
double dcycs;
word32 dbank_kpc;
word32 instr;
word32 psr_acc;
word32 xreg_yreg;
word32 stack_direct;
word32 pad;
};
STRUCT(Data_log) {
double dcycs;
word32 addr;
word32 val;
word32 size;
double dcycs;
word32 addr;
word32 val;
word32 size;
};
STRUCT(Event) {
double dcycs;
int type;
Event *next;
double dcycs;
int type;
Event *next;
};
STRUCT(Fplus) {
double plus_1;
double plus_2;
double plus_3;
double plus_x_minus_1;
double plus_1;
double plus_2;
double plus_3;
double plus_x_minus_1;
};
STRUCT(Engine_reg) {
double fcycles;
word32 kpc;
word32 acc;
double fcycles;
word32 kpc;
word32 acc;
word32 xreg;
word32 yreg;
word32 xreg;
word32 yreg;
word32 stack;
word32 dbank;
word32 stack;
word32 dbank;
word32 direct;
word32 psr;
Fplus *fplus_ptr;
word32 direct;
word32 psr;
Fplus *fplus_ptr;
};
STRUCT(Kimage) {
void *dev_handle;
void *dev_handle2;
byte *data_ptr;
int width_req;
int width_act;
int height;
int depth;
int mdepth;
int aux_info;
void *dev_handle;
void *dev_handle2;
byte *data_ptr;
int width_req;
int width_act;
int height;
int depth;
int mdepth;
int aux_info;
};
typedef byte *Pg_info;
STRUCT(Page_info) {
Pg_info rd_wr;
Pg_info rd_wr;
};
STRUCT(Cfg_menu) {
const char *str;
void *ptr;
const char *name_str;
void *defptr;
int cfgtype;
const char *str;
void *ptr;
const char *name_str;
void *defptr;
int cfgtype;
};
STRUCT(Cfg_dirent) {
char *name;
int is_dir;
int size;
int image_start;
int part_num;
char *name;
int is_dir;
int size;
int image_start;
int part_num;
};
STRUCT(Cfg_listhdr) {
Cfg_dirent *direntptr;
int max;
int last;
int invalid;
Cfg_dirent *direntptr;
int max;
int last;
int invalid;
int curent;
int topent;
int curent;
int topent;
int num_to_show;
int num_to_show;
};
STRUCT(Emustate_intlist) {
const char *str;
int *iptr;
const char *str;
int *iptr;
};
STRUCT(Emustate_dbllist) {
const char *str;
double *dptr;
const char *str;
double *dptr;
};
STRUCT(Emustate_word32list) {
const char *str;
word32 *wptr;
const char *str;
word32 *wptr;
};
#ifdef __LP64__
# define PTR2WORD(a) ((unsigned long)(a))
# define PTR2WORD(a) ((unsigned long)(a))
#else
# define PTR2WORD(a) ((unsigned int)(a))
# define PTR2WORD(a) ((unsigned int)(a))
#endif
#define ALTZP (g_c068_statereg & 0x80)
#define ALTZP (g_c068_statereg & 0x80)
/* #define PAGE2 (g_c068_statereg & 0x40) */
#define RAMRD (g_c068_statereg & 0x20)
#define RAMWRT (g_c068_statereg & 0x10)
#define RDROM (g_c068_statereg & 0x08)
#define LCBANK2 (g_c068_statereg & 0x04)
#define ROMB (g_c068_statereg & 0x02)
#define INTCX (g_c068_statereg & 0x01)
#define RAMRD (g_c068_statereg & 0x20)
#define RAMWRT (g_c068_statereg & 0x10)
#define RDROM (g_c068_statereg & 0x08)
#define LCBANK2 (g_c068_statereg & 0x04)
#define ROMB (g_c068_statereg & 0x02)
#define INTCX (g_c068_statereg & 0x01)
#define C041_EN_25SEC_INTS 0x10
#define C041_EN_VBL_INTS 0x08
#define C041_EN_SWITCH_INTS 0x04
#define C041_EN_MOVE_INTS 0x02
#define C041_EN_MOUSE 0x01
#define C041_EN_25SEC_INTS 0x10
#define C041_EN_VBL_INTS 0x08
#define C041_EN_SWITCH_INTS 0x04
#define C041_EN_MOVE_INTS 0x02
#define C041_EN_MOUSE 0x01
/* WARNING: SCC1 and SCC0 interrupts must be in this order for scc.c */
/* This order matches the SCC hardware */
#define IRQ_PENDING_SCC1_ZEROCNT 0x00001
#define IRQ_PENDING_SCC1_TX 0x00002
#define IRQ_PENDING_SCC1_RX 0x00004
#define IRQ_PENDING_SCC0_ZEROCNT 0x00008
#define IRQ_PENDING_SCC0_TX 0x00010
#define IRQ_PENDING_SCC0_RX 0x00020
#define IRQ_PENDING_C023_SCAN 0x00100
#define IRQ_PENDING_C023_1SEC 0x00200
#define IRQ_PENDING_C046_25SEC 0x00400
#define IRQ_PENDING_C046_VBL 0x00800
#define IRQ_PENDING_ADB_KBD_SRQ 0x01000
#define IRQ_PENDING_ADB_DATA 0x02000
#define IRQ_PENDING_ADB_MOUSE 0x04000
#define IRQ_PENDING_DOC 0x08000
#define IRQ_PENDING_SCC1_ZEROCNT 0x00001
#define IRQ_PENDING_SCC1_TX 0x00002
#define IRQ_PENDING_SCC1_RX 0x00004
#define IRQ_PENDING_SCC0_ZEROCNT 0x00008
#define IRQ_PENDING_SCC0_TX 0x00010
#define IRQ_PENDING_SCC0_RX 0x00020
#define IRQ_PENDING_C023_SCAN 0x00100
#define IRQ_PENDING_C023_1SEC 0x00200
#define IRQ_PENDING_C046_25SEC 0x00400
#define IRQ_PENDING_C046_VBL 0x00800
#define IRQ_PENDING_ADB_KBD_SRQ 0x01000
#define IRQ_PENDING_ADB_DATA 0x02000
#define IRQ_PENDING_ADB_MOUSE 0x04000
#define IRQ_PENDING_DOC 0x08000
#define EXTRU(val, pos, len) \
( ( (len) >= (pos) + 1) ? ((val) >> (31-(pos))) : \
(((val) >> (31-(pos)) ) & ( (1<<(len) ) - 1) ) )
#define EXTRU(val, pos, len) \
( ( (len) >= (pos) + 1) ? ((val) >> (31-(pos))) : \
(((val) >> (31-(pos)) ) & ( (1<<(len) ) - 1) ) )
#define DEP1(val, pos, old_val) \
(((old_val) & ~(1 << (31 - (pos))) ) | \
( ((val) & 1) << (31 - (pos))) )
#define DEP1(val, pos, old_val) \
(((old_val) & ~(1 << (31 - (pos))) ) | \
( ((val) & 1) << (31 - (pos))) )
#define set_halt(val) \
if(val) { set_halt_act(val); }
if(val) { set_halt_act(val); }
#define clear_halt() \
clr_halt_act()
clr_halt_act()
#define GET_PAGE_INFO_RD(page) \
(page_info_rd_wr[page].rd_wr)
(page_info_rd_wr[page].rd_wr)
#define GET_PAGE_INFO_WR(page) \
(page_info_rd_wr[0x10000 + PAGE_INFO_PAD_SIZE + (page)].rd_wr)
(page_info_rd_wr[0x10000 + PAGE_INFO_PAD_SIZE + (page)].rd_wr)
#define SET_PAGE_INFO_RD(page,val) \
;page_info_rd_wr[page].rd_wr = (Pg_info)val;
; page_info_rd_wr[page].rd_wr = (Pg_info)val;
#define SET_PAGE_INFO_WR(page,val) \
;page_info_rd_wr[0x10000 + PAGE_INFO_PAD_SIZE + (page)].rd_wr = \
(Pg_info)val;
; page_info_rd_wr[0x10000 + PAGE_INFO_PAD_SIZE + (page)].rd_wr = \
(Pg_info)val;
#define VERBOSE_DISK 0x001
#define VERBOSE_IRQ 0x002
#define VERBOSE_CLK 0x004
#define VERBOSE_SHADOW 0x008
#define VERBOSE_IWM 0x010
#define VERBOSE_DOC 0x020
#define VERBOSE_ADB 0x040
#define VERBOSE_SCC 0x080
#define VERBOSE_TEST 0x100
#define VERBOSE_VIDEO 0x200
#define VERBOSE_MAC 0x400
#define VERBOSE_DISK 0x001
#define VERBOSE_IRQ 0x002
#define VERBOSE_CLK 0x004
#define VERBOSE_SHADOW 0x008
#define VERBOSE_IWM 0x010
#define VERBOSE_DOC 0x020
#define VERBOSE_ADB 0x040
#define VERBOSE_SCC 0x080
#define VERBOSE_TEST 0x100
#define VERBOSE_VIDEO 0x200
#define VERBOSE_MAC 0x400
#ifdef NO_VERB
# define DO_VERBOSE 0
# define DO_VERBOSE 0
#else
# define DO_VERBOSE 1
# define DO_VERBOSE 1
#endif
#define disk_printf if(DO_VERBOSE && (Verbose & VERBOSE_DISK)) printf
#define irq_printf if(DO_VERBOSE && (Verbose & VERBOSE_IRQ)) printf
#define clk_printf if(DO_VERBOSE && (Verbose & VERBOSE_CLK)) printf
#define shadow_printf if(DO_VERBOSE && (Verbose & VERBOSE_SHADOW)) printf
#define iwm_printf if(DO_VERBOSE && (Verbose & VERBOSE_IWM)) printf
#define doc_printf if(DO_VERBOSE && (Verbose & VERBOSE_DOC)) printf
#define adb_printf if(DO_VERBOSE && (Verbose & VERBOSE_ADB)) printf
#define scc_printf if(DO_VERBOSE && (Verbose & VERBOSE_SCC)) printf
#define test_printf if(DO_VERBOSE && (Verbose & VERBOSE_TEST)) printf
#define vid_printf if(DO_VERBOSE && (Verbose & VERBOSE_VIDEO)) printf
#define mac_printf if(DO_VERBOSE && (Verbose & VERBOSE_MAC)) printf
#define disk_printf if(DO_VERBOSE && (Verbose & VERBOSE_DISK)) printf
#define irq_printf if(DO_VERBOSE && (Verbose & VERBOSE_IRQ)) printf
#define clk_printf if(DO_VERBOSE && (Verbose & VERBOSE_CLK)) printf
#define shadow_printf if(DO_VERBOSE && (Verbose & VERBOSE_SHADOW)) printf
#define iwm_printf if(DO_VERBOSE && (Verbose & VERBOSE_IWM)) printf
#define doc_printf if(DO_VERBOSE && (Verbose & VERBOSE_DOC)) printf
#define adb_printf if(DO_VERBOSE && (Verbose & VERBOSE_ADB)) printf
#define scc_printf if(DO_VERBOSE && (Verbose & VERBOSE_SCC)) printf
#define test_printf if(DO_VERBOSE && (Verbose & VERBOSE_TEST)) printf
#define vid_printf if(DO_VERBOSE && (Verbose & VERBOSE_VIDEO)) printf
#define mac_printf if(DO_VERBOSE && (Verbose & VERBOSE_MAC)) printf
#define HALT_ON_SCAN_INT 0x001
#define HALT_ON_IRQ 0x002
#define HALT_ON_SHADOW_REG 0x004
#define HALT_ON_C70D_WRITES 0x008
#define HALT_ON_SCAN_INT 0x001
#define HALT_ON_IRQ 0x002
#define HALT_ON_SHADOW_REG 0x004
#define HALT_ON_C70D_WRITES 0x008
#define HALT_ON(a, msg) \
if(Halt_on & a) { \
halt_printf(msg); \
}
#define HALT_ON(a, msg) \
if(Halt_on & a) { \
halt_printf(msg); \
}
#ifndef MIN
# define MIN(a,b) (((a) < (b)) ? (a) : (b))
# define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif
#ifndef MAX
# define MAX(a,b) (((a) < (b)) ? (b) : (a))
# define MAX(a,b) (((a) < (b)) ? (b) : (a))
#endif
#define GET_ITIMER(dest) dest = get_itimer();
#define GET_ITIMER(dest) dest = get_itimer();
#include "iwm.h"
#include "protos.h"
// OG Added define for joystick
#define JOYSTICK_TYPE_KEYPAD 0
#define JOYSTICK_TYPE_MOUSE 1
#define JOYSTICK_TYPE_NATIVE_1 2
#define JOYSTICK_TYPE_NATIVE_2 3
#define JOYSTICK_TYPE_NONE 4 // OG Added Joystick None
#define NB_JOYSTICK_TYPE 5
// OG Added define for joystick
#define JOYSTICK_TYPE_KEYPAD 0
#define JOYSTICK_TYPE_MOUSE 1
#define JOYSTICK_TYPE_NATIVE_1 2
#define JOYSTICK_TYPE_NATIVE_2 3
#define JOYSTICK_TYPE_NONE 4 // OG Added Joystick None
#define NB_JOYSTICK_TYPE 5
// starting window x/y position if Undefined
#define WINDOWPOS_UNDEFINED 0xFFFFFFFF

2070
src/dis.c

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

47
src/fix_mac_menu.m Normal file
View File

@ -0,0 +1,47 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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
*/
#import <Cocoa/Cocoa.h>
void fix_mac_menu(void) {
/*
* add an option-key modifier to all menu shortcuts
* eg, command-Q -> option+command-Q
*/
@autoreleasepool {
if (NSApp) {
NSMenu *menu = [NSApp mainMenu];
for (NSMenuItem *a in [menu itemArray]) {
for (NSMenuItem *b in [[a submenu] itemArray]) {
unsigned m = [b keyEquivalentModifierMask];
if (m & NSEventModifierFlagCommand)
[b setKeyEquivalentModifierMask: m | NSEventModifierFlagOption];
}
}
}
}
}

203
src/fst.h Normal file
View File

@ -0,0 +1,203 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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
*/
/* generated on Tue Oct 18 20:54:09 2016 */
#define GSString255_length 0
#define GSString255_text 2
#define GSString32_length 0
#define GSString32_text 2
#define ResultBuf255_bufSize 0
#define ResultBuf255_bufString 2
#define ResultBuf32_bufSize 0
#define ResultBuf32_bufString 2
#define TimeRec_second 0
#define TimeRec_minute 1
#define TimeRec_hour 2
#define TimeRec_year 3
#define TimeRec_day 4
#define TimeRec_month 5
#define TimeRec_extra 6
#define TimeRec_weekDay 7
#define CreateRecGS_pCount 0
#define CreateRecGS_pathname 2
#define CreateRecGS_access 6
#define CreateRecGS_fileType 8
#define CreateRecGS_auxType 10
#define CreateRecGS_storageType 14
#define CreateRecGS_eof 16
#define CreateRecGS_resourceEOF 20
#define CreateRec_pathname 0
#define CreateRec_fAccess 4
#define CreateRec_fileType 6
#define CreateRec_auxType 8
#define CreateRec_storageType 12
#define CreateRec_createDate 14
#define CreateRec_createTime 16
#define DirEntryRecGS_pCount 0
#define DirEntryRecGS_refNum 2
#define DirEntryRecGS_flags 4
#define DirEntryRecGS_base 6
#define DirEntryRecGS_displacement 8
#define DirEntryRecGS_name 10
#define DirEntryRecGS_entryNum 14
#define DirEntryRecGS_fileType 16
#define DirEntryRecGS_eof 18
#define DirEntryRecGS_blockCount 22
#define DirEntryRecGS_createDateTime 26
#define DirEntryRecGS_modDateTime 34
#define DirEntryRecGS_access 42
#define DirEntryRecGS_auxType 44
#define DirEntryRecGS_fileSysID 48
#define DirEntryRecGS_optionList 50
#define DirEntryRecGS_resourceEOF 54
#define DirEntryRecGS_resourceBlocks 58
#define DirEntryRec_refNum 0
#define DirEntryRec_flags 2
#define DirEntryRec_base 4
#define DirEntryRec_displacement 6
#define DirEntryRec_nameBuffer 8
#define DirEntryRec_entryNum 12
#define DirEntryRec_fileType 14
#define DirEntryRec_endOfFile 16
#define DirEntryRec_blockCount 20
#define DirEntryRec_createTime 24
#define DirEntryRec_modTime 32
#define DirEntryRec_access 40
#define DirEntryRec_auxType 42
#define DirEntryRec_fileSysID 46
#define FileInfoRecGS_pCount 0
#define FileInfoRecGS_pathname 2
#define FileInfoRecGS_access 6
#define FileInfoRecGS_fileType 8
#define FileInfoRecGS_auxType 10
#define FileInfoRecGS_storageType 14
#define FileInfoRecGS_createDateTime 16
#define FileInfoRecGS_modDateTime 24
#define FileInfoRecGS_optionList 32
#define FileInfoRecGS_eof 36
#define FileInfoRecGS_blocksUsed 40
#define FileInfoRecGS_resourceEOF 44
#define FileInfoRecGS_resourceBlocks 48
#define FileRec_pathname 0
#define FileRec_fAccess 4
#define FileRec_fileType 6
#define FileRec_auxType 8
#define FileRec_storageType 12
#define FileRec_createDate 14
#define FileRec_createTime 16
#define FileRec_modDate 18
#define FileRec_modTime 20
#define FileRec_blocksUsed 22
#define OpenRecGS_pCount 0
#define OpenRecGS_refNum 2
#define OpenRecGS_pathname 4
#define OpenRecGS_requestAccess 8
#define OpenRecGS_resourceNumber 10
#define OpenRecGS_access 12
#define OpenRecGS_fileType 14
#define OpenRecGS_auxType 16
#define OpenRecGS_storageType 20
#define OpenRecGS_createDateTime 22
#define OpenRecGS_modDateTime 30
#define OpenRecGS_optionList 38
#define OpenRecGS_eof 42
#define OpenRecGS_blocksUsed 46
#define OpenRecGS_resourceEOF 50
#define OpenRecGS_resourceBlocks 54
#define OpenRec_openRefNum 0
#define OpenRec_openPathname 2
#define OpenRec_ioBuffer 6
#define VolumeRecGS_pCount 0
#define VolumeRecGS_devName 2
#define VolumeRecGS_volName 6
#define VolumeRecGS_totalBlocks 10
#define VolumeRecGS_freeBlocks 14
#define VolumeRecGS_fileSysID 18
#define VolumeRecGS_blockSize 20
#define VolumeRecGS_characteristics 22
#define VolumeRecGS_deviceID 24
#define VolumeRec_deviceName 0
#define VolumeRec_volName 4
#define VolumeRec_totalBlocks 8
#define VolumeRec_freeBlocks 12
#define VolumeRec_fileSysID 16
#define JudgeNameRecGS_pCount 0
#define JudgeNameRecGS_fileSysID 2
#define JudgeNameRecGS_nameType 4
#define JudgeNameRecGS_syntax 6
#define JudgeNameRecGS_maxLen 10
#define JudgeNameRecGS_name 12
#define JudgeNameRecGS_nameFlags 16
#define PositionRecGS_pCount 0
#define PositionRecGS_refNum 2
#define PositionRecGS_position 4
#define MarkRec_markRefNum 0
#define MarkRec_position 2
#define EOFRecGS_pCount 0
#define EOFRecGS_refNum 2
#define EOFRecGS_eof 4
#define EOFRec_eofRefNum 0
#define EOFRec_eofPosition 2
#define IORecGS_pCount 0
#define IORecGS_refNum 2
#define IORecGS_dataBuffer 4
#define IORecGS_requestCount 8
#define IORecGS_transferCount 12
#define IORecGS_cachePriority 16
#define FileIORec_fileRefNum 0
#define FileIORec_dataBuffer 2
#define FileIORec_requestCount 6
#define FileIORec_transferCount 10
#define SetPositionRecGS_pCount 0
#define SetPositionRecGS_refNum 2
#define SetPositionRecGS_base 4
#define SetPositionRecGS_displacement 6
#define DevNumRecGS_pCount 0
#define DevNumRecGS_devName 2
#define DevNumRecGS_devNum 6
#define DevNumRec_devName 0
#define DevNumRec_devNum 4

63
src/glog.c Normal file
View File

@ -0,0 +1,63 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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 <stdio.h>
#include <time.h>
#include <stdarg.h>
#include "glog.h"
int glog(const char *s) {
time_t timer;
char buffer[26];
struct tm* tm_info;
time(&timer);
tm_info = localtime(&timer);
strftime(buffer, 26, "%Y-%m-%d %H:%M:%S", tm_info);
printf("%s - %s\n", buffer, s);
return 0;
}
int glogf(const char *fmt, ...) {
time_t timer;
char buffer[26];
struct tm* tm_info;
time(&timer);
tm_info = localtime(&timer);
strftime(buffer, 26, "%Y-%m-%d %H:%M:%S", tm_info);
printf("%s - ", buffer);
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
fputc('\n', stdout);
return 0;
}

30
src/glog.h Normal file
View File

@ -0,0 +1,30 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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
*/
#ifdef __cplusplus
extern "C" {
#endif
int glog(const char *s);
int glogf(const char *s, ...);
#ifdef __cplusplus
}
#endif

186
src/gsos.h Normal file
View File

@ -0,0 +1,186 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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 readEnableAllowWrite 0x0000
#define readEnable 0x0001
#define writeEnable 0x0002
#define readWriteEnable 0x0003
#define fileInvisible 0x0004 /* Invisible bit */
#define backupNeeded 0x0020 /* backup needed bit: CreateRec/ OpenRec access field. (Must be 0 in requestAccess field ) */
#define renameEnable 0x0040 /* rename enable bit: CreateRec/ OpenRec access and requestAccess fields */
#define destroyEnable 0x0080 /* destroy enable bit: CreateRec/ OpenRec access and requestAccess fields */
#define startPlus 0x0000 /* base -> setMark = displacement */
#define eofMinus 0x0001 /* base -> setMark = eof - displacement */
#define markPlus 0x0002 /* base -> setMark = mark + displacement */
#define markMinus 0x0003 /* base -> setMark = mark - displacement */
/* cachePriority Codes */
#define cacheOff 0x0000 /* do not cache blocks invloved in this read */
#define cacheOn 0x0001 /* cache blocks invloved in this read if possible */
/* Error Codes */
#define badSystemCall 0x0001 /* bad system call number */
#define invalidPcount 0x0004 /* invalid parameter count */
#define gsosActive 0x0007 /* GS/OS already active */
#ifndef devNotFound /* device not found */
#define devNotFound 0x0010
#endif
#define invalidDevNum 0x0011 /* invalid device number */
#define drvrBadReq 0x0020 /* bad request or command */
#define drvrBadCode 0x0021 /* bad control or status code */
#define drvrBadParm 0x0022 /* bad call parameter */
#define drvrNotOpen 0x0023 /* character device not open */
#define drvrPriorOpen 0x0024 /* character device already open */
#define irqTableFull 0x0025 /* interrupt table full */
#define drvrNoResrc 0x0026 /* resources not available */
#define drvrIOError 0x0027 /* I/O error */
#define drvrNoDevice 0x0028 /* device not connected */
#define drvrBusy 0x0029 /* call aborted; driver is busy */
#define drvrWrtProt 0x002B /* device is write protected */
#define drvrBadCount 0x002C /* invalid byte count */
#define drvrBadBlock 0x002D /* invalid block address */
#define drvrDiskSwitch 0x002E /* disk has been switched */
#define drvrOffLine 0x002F /* device off line/ no media present */
#define badPathSyntax 0x0040 /* invalid pathname syntax */
#define tooManyFilesOpen 0x0042 /* too many files open on server volume */
#define invalidRefNum 0x0043 /* invalid reference number */
#ifndef pathNotFound /* subdirectory does not exist */
#define pathNotFound 0x0044
#endif
#define volNotFound 0x0045 /* volume not found */
#ifndef fileNotFound /* file not found */
#define fileNotFound 0x0046
#endif
#define dupPathname 0x0047 /* create or rename with existing name */
#define volumeFull 0x0048 /* volume full error */
#define volDirFull 0x0049 /* volume directory full */
#define badFileFormat 0x004A /* version error (incompatible file format) */
#ifndef badStoreType /* unsupported (or incorrect) storage type */
#define badStoreType 0x004B
#endif
#ifndef eofEncountered /* end-of-file encountered */
#define eofEncountered 0x004C
#endif
#define outOfRange 0x004D /* position out of range */
#define invalidAccess 0x004E /* access not allowed */
#define buffTooSmall 0x004F /* buffer too small */
#define fileBusy 0x0050 /* file is already open */
#define dirError 0x0051 /* directory error */
#define unknownVol 0x0052 /* unknown volume type */
#ifndef paramRangeErr /* parameter out of range */
#define paramRangeErr 0x0053
#endif
#define outOfMem 0x0054 /* out of memory */
#define dupVolume 0x0057 /* duplicate volume name */
#define notBlockDev 0x0058 /* not a block device */
#ifndef invalidLevel /* specifield level outside legal range */
#define invalidLevel 0x0059
#endif
#define damagedBitMap 0x005A /* block number too large */
#define badPathNames 0x005B /* invalid pathnames for ChangePath */
#define notSystemFile 0x005C /* not an executable file */
#define osUnsupported 0x005D /* Operating System not supported */
#ifndef stackOverflow /* too many applications on stack */
#define stackOverflow 0x005F
#endif
#define dataUnavail 0x0060 /* Data unavailable */
#define endOfDir 0x0061 /* end of directory has been reached */
#define invalidClass 0x0062 /* invalid FST call class */
#define resForkNotFound 0x0063 /* file does not contain required resource */
#define invalidFSTID 0x0064 /* error - FST ID is invalid */
#define invalidFSTop 0x0065 /* invalid FST operation */
#define fstCaution 0x0066 /* FST handled call, but result is weird */
#define devNameErr 0x0067 /* device exists with same name as replacement name */
#define defListFull 0x0068 /* device list is full */
#define supListFull 0x0069 /* supervisor list is full */
#define fstError 0x006a /* generic FST error */
#define resExistsErr 0x0070 /* cannot expand file, resource already exists */
#define resAddErr 0x0071 /* cannot add resource fork to this type file */
#define networkError 0x0088 /* generic network error */
/* fileSys IDs */
#define proDOSFSID 0x0001 /* ProDOS/SOS */
#define dos33FSID 0x0002 /* DOS 3.3 */
#define dos32FSID 0x0003 /* DOS 3.2 */
#define dos31FSID 0x0003 /* DOS 3.1 */
#define appleIIPascalFSID 0x0004 /* Apple II Pascal */
#define mfsFSID 0x0005 /* Macintosh (flat file system) */
#define hfsFSID 0x0006 /* Macintosh (hierarchical file system) */
#define lisaFSID 0x0007 /* Lisa file system */
#define appleCPMFSID 0x0008 /* Apple CP/M */
#define charFSTFSID 0x0009 /* Character FST */
#define msDOSFSID 0x000A /* MS/DOS */
#define highSierraFSID 0x000B /* High Sierra */
#define iso9660FSID 0x000C /* ISO 9660 */
#define appleShareFSID 0x000D /* ISO 9660 */
/* FSTInfo.attributes Codes */
#define characterFST 0x4000 /* character FST */
#define ucFST 0x8000 /* SCM should upper case pathnames before passing them to the FST */
/* QuitRec.flags Codes */
#define onStack 0x8000 /* place state information about quitting program on the quit return stack */
#define restartable 0x4000 /* the quitting program is capable of being restarted from its dormant memory */
/* storageType Codes */
#define seedling 0x0001 /* standard file with seedling structure */
#define standardFile 0x0001 /* standard file type (no resource fork) */
#define sapling 0x0002 /* standard file with sapling structure */
#define tree 0x0003 /* standard file with tree structure */
#define pascalRegion 0x0004 /* UCSD Pascal region on a partitioned disk */
#define extendedFile 0x0005 /* extended file type (with resource fork) */
#define directoryFile 0x000D /* volume directory or subdirectory file */
/* version Codes */
#define minorRelNumMask 0x00FF /* minor release number */
#define majorRelNumMask 0x7F00 /* major release number */
#define finalRelNumMask 0x8000 /* final release number */
/* Other Constants */
#define isFileExtended 0x8000 /* GetDirEntryGS */
/* DControl Codes */
#define resetDevice 0x0000
#define formatDevice 0x0001
#define eject 0x0002
#define setConfigParameters 0x0003
#define setWaitStatus 0x0004
#define setFormatOptions 0x0005
#define assignPartitionOwner 0x0006
#define armSignal 0x0007
#define disarmSignal 0x0008
#define setPartitionMap 0x0009

BIN
src/gsport Executable file

Binary file not shown.

650
src/host_common.c Normal file
View File

@ -0,0 +1,650 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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 _BSD_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <libgen.h>
#include "defc.h"
#include "gsos.h"
#include "fst.h"
#ifdef _WIN32
#include <Windows.h>
#endif
#include "host_common.h"
char *host_root = NULL;
char *g_cfg_host_path = ""; // must not be null.
int g_cfg_host_read_only = 0;
int g_cfg_host_crlf = 1;
int g_cfg_host_merlin = 0;
/*
* simple malloc pool to simplify code. Should never need > 4 allocations.
*
*/
static void *gc[16];
static void **gc_ptr = &gc[0];
void *host_gc_malloc(size_t size) {
if (gc_ptr == &gc[16]) {
errno = ENOMEM;
return NULL;
}
void *ptr = malloc(size);
if (ptr) {
*gc_ptr++ = ptr;
}
return ptr;
}
void host_gc_free(void) {
while (gc_ptr > gc) free(*--gc_ptr);
}
char *host_gc_strdup(const char *src) {
if (!src) return "";
if (!*src) return "";
int len = strlen(src) + 1;
char *cp = host_gc_malloc(len);
memcpy(cp, src, len);
return cp;
}
char *host_gc_append_path(const char *a, const char *b) {
/* strip all leading /s from b) */
while (*b == '/') ++b;
int aa = strlen(a);
int bb = strlen(b);
char *cp = host_gc_malloc(aa + bb + 2);
if (!cp) return NULL;
memcpy(cp, a, aa);
int len = aa;
/* strip all trailing /s from b */
while (len > 2 && cp[len-1] == '/') --len;
cp[len++] = '/';
memcpy(cp + len, b, bb);
len += bb;
cp[len] = 0;
return cp;
}
char *host_gc_append_string(const char *a, const char *b) {
int aa = strlen(a);
int bb = strlen(b);
char *cp = host_gc_malloc(aa + bb + 2);
if (!cp) return NULL;
memcpy(cp, a, aa);
int len = aa;
memcpy(cp + len, b, bb);
len += bb;
cp[len] = 0;
return cp;
}
/*
* text conversion.
*/
void host_cr_to_lf(byte *buffer, size_t size) {
size_t i;
for (i = 0; i < size; ++i) {
if (buffer[i] == '\r') buffer[i] = '\n';
}
}
void host_lf_to_cr(byte *buffer, size_t size) {
size_t i;
for (i = 0; i < size; ++i) {
if (buffer[i] == '\n') buffer[i] = '\r';
}
}
void host_merlin_to_text(byte *buffer, size_t size) {
size_t i;
for (i = 0; i < size; ++i) {
byte b = buffer[i];
if (b == 0xa0) b = '\t';
b &= 0x7f;
if (b == '\r') b = '\n';
buffer[i] = b;
}
}
void host_text_to_merlin(byte *buffer, size_t size) {
size_t i;
for (i = 0; i < size; ++i) {
byte b = buffer[i];
if (b == '\t') b = 0xa0;
if (b == '\n') b = '\r';
if (b != ' ') b |= 0x80;
buffer[i] = b;
}
}
/*
* error remapping.
* NOTE - GS/OS errors are a superset of P8 errors
*/
static word32 enoent(const char *path) {
/*
some op on path return ENOENT. check if it's
fileNotFound or pathNotFound
*/
char *p = (char *)path;
for(;;) {
struct stat st;
p = dirname(p);
if (p == NULL) break;
if (p[0] == '.' && p[1] == 0) break;
if (p[0] == '/' && p[1] == 0) break;
if (stat(p, &st) < 0) return pathNotFound;
}
return fileNotFound;
}
word32 host_map_errno(int xerrno) {
switch(xerrno) {
case 0: return 0;
case EBADF:
return invalidAccess;
#ifdef EDQUOT
case EDQUOT:
#endif
case EFBIG:
return volumeFull;
case ENOENT:
return fileNotFound;
case ENOTDIR:
return pathNotFound;
case ENOMEM:
return outOfMem;
case EEXIST:
return dupPathname;
case ENOTEMPTY:
return invalidAccess;
default:
return drvrIOError;
}
}
word32 host_map_errno_path(int xerrno, const char *path) {
if (xerrno == ENOENT) return enoent(path);
return host_map_errno(xerrno);
}
const char *host_error_name(word16 error) {
static char *errors[] = {
"",
"badSystemCall",
"",
"",
"invalidPcount",
"",
"",
"gsosActive",
"",
"",
"",
"",
"",
"",
"",
"",
// 0x10
"devNotFound",
"invalidDevNum",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
// 0x20
"drvrBadReq",
"drvrBadCode",
"drvrBadParm",
"drvrNotOpen",
"drvrPriorOpen",
"irqTableFull",
"drvrNoResrc",
"drvrIOError",
"drvrNoDevice",
"drvrBusy",
"",
"drvrWrtProt",
"drvrBadCount",
"drvrBadBlock",
"drvrDiskSwitch",
"drvrOffLine",
// 0x30
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
// 0x40
"badPathSyntax",
"",
"tooManyFilesOpen",
"invalidRefNum",
"pathNotFound",
"volNotFound",
"fileNotFound",
"dupPathname",
"volumeFull",
"volDirFull",
"badFileFormat",
"badStoreType",
"eofEncountered",
"outOfRange",
"invalidAccess",
"buffTooSmall",
// 0x50
"fileBusy",
"dirError",
"unknownVol",
"paramRangeErr",
"outOfMem",
"",
"badBufferAddress", /* P8 MLI only */
"dupVolume",
"notBlockDev",
"invalidLevel",
"damagedBitMap",
"badPathNames",
"notSystemFile",
"osUnsupported",
"",
"stackOverflow",
// 0x60
"dataUnavail",
"endOfDir",
"invalidClass",
"resForkNotFound",
"invalidFSTID",
"invalidFSTop",
"fstCaution",
"devNameErr",
"defListFull",
"supListFull",
"fstError",
"",
"",
"",
"",
"",
//0x70
"resExistsErr",
"resAddErr",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
//0x80
"",
"",
"",
"",
"",
"",
"",
"",
"networkError"
};
if (error < sizeof(errors) / sizeof(errors[0]))
return errors[error];
return "";
}
/*
* File info.
*/
static int hex(byte c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c + 10 - 'a';
if (c >= 'A' && c <= 'F') return c + 10 - 'A';
return 0;
}
int host_finder_info_to_filetype(const byte *buffer, word16 *file_type, word32 *aux_type) {
if (!memcmp("pdos", buffer + 4, 4))
{
if (buffer[0] == 'p') {
*file_type = buffer[1];
*aux_type = (buffer[2] << 8) | buffer[3];
return 0;
}
if (!memcmp("PSYS", buffer, 4)) {
*file_type = 0xff;
*aux_type = 0x0000;
return 0;
}
if (!memcmp("PS16", buffer, 4)) {
*file_type = 0xb3;
*aux_type = 0x0000;
return 0;
}
// old mpw method for encoding.
if (!isxdigit(buffer[0]) && isxdigit(buffer[1]) && buffer[2] == ' ' && buffer[3] == ' ')
{
*file_type = (hex(buffer[0]) << 8) | hex(buffer[1]);
*aux_type = 0;
return 0;
}
}
if (!memcmp("TEXT", buffer, 4)) {
*file_type = 0x04;
*aux_type = 0x0000;
return 0;
}
if (!memcmp("BINA", buffer, 4)) {
*file_type = 0x00;
*aux_type = 0x0000;
return 0;
}
if (!memcmp("dImgdCpy", buffer, 8)) {
*file_type = 0xe0;
*aux_type = 0x0005;
return 0;
}
if (!memcmp("MIDI", buffer, 4)) {
*file_type = 0xd7;
*aux_type = 0x0000;
return 0;
}
if (!memcmp("AIFF", buffer, 4)) {
*file_type = 0xd8;
*aux_type = 0x0000;
return 0;
}
if (!memcmp("AIFC", buffer, 4)) {
*file_type = 0xd8;
*aux_type = 0x0001;
return 0;
}
return -1;
}
int host_file_type_to_finder_info(byte *buffer, word16 file_type, word32 aux_type) {
if (file_type > 0xff || aux_type > 0xffff) return -1;
if (!file_type && aux_type == 0x0000) {
memcpy(buffer, "BINApdos", 8);
return 0;
}
if (file_type == 0x04 && aux_type == 0x0000) {
memcpy(buffer, "TEXTpdos", 8);
return 0;
}
if (file_type == 0xff && aux_type == 0x0000) {
memcpy(buffer, "PSYSpdos", 8);
return 0;
}
if (file_type == 0xb3 && aux_type == 0x0000) {
memcpy(buffer, "PS16pdos", 8);
return 0;
}
if (file_type == 0xd7 && aux_type == 0x0000) {
memcpy(buffer, "MIDIpdos", 8);
return 0;
}
if (file_type == 0xd8 && aux_type == 0x0000) {
memcpy(buffer, "AIFFpdos", 8);
return 0;
}
if (file_type == 0xd8 && aux_type == 0x0001) {
memcpy(buffer, "AIFCpdos", 8);
return 0;
}
if (file_type == 0xe0 && aux_type == 0x0005) {
memcpy(buffer, "dImgdCpy", 8);
return 0;
}
memcpy(buffer, "p pdos", 8);
buffer[1] = (file_type) & 0xff;
buffer[2] = (aux_type >> 8) & 0xff;
buffer[3] = (aux_type) & 0xff;
return 0;
}
#undef _
#define _(a, b, c) { a, sizeof(a) - 1, b, c }
struct ftype_entry {
char *ext;
unsigned length;
unsigned file_type;
unsigned aux_type;
};
static struct ftype_entry suffixes[] = {
_("c", 0xb0, 0x0008),
_("cc", 0xb0, 0x0008),
_("h", 0xb0, 0x0008),
_("rez", 0xb0, 0x0015),
_("asm", 0xb0, 0x0003),
_("mac", 0xb0, 0x0003),
_("pas", 0xb0, 0x0005),
_("txt", 0x04, 0x0000),
_("text", 0x04, 0x0000),
_("s", 0x04, 0x0000),
{ 0, 0, 0, 0}
};
static struct ftype_entry prefixes[] = {
_("m16.", 0xb0, 0x0003),
_("e16.", 0xb0, 0x0003),
{ 0, 0, 0, 0}
};
#undef _
void host_synthesize_file_xinfo(const char *path, struct file_info *fi) {
/* guess the file type / auxtype based on extension */
int n;
const char *dot = NULL;
const char *slash = NULL;
for(n = 0;; ++n) {
char c = path[n];
if (c == 0) break;
else if (c == '/') { slash = path + n + 1; dot = NULL; }
else if (c == '.') dot = path + n + 1;
}
if (dot && *dot) {
for (n = 0; n < sizeof(suffixes) / sizeof(suffixes[0]); ++n) {
if (!suffixes[n].ext) break;
if (!strcasecmp(dot, suffixes[n].ext)) {
fi->file_type = suffixes[n].file_type;
fi->aux_type = suffixes[n].aux_type;
return;
}
}
}
return;
}
void host_hexdump(word32 address, int size) {
const char *HexMap = "0123456789abcdef";
char buffer1[16 * 3 + 1 + 1];
char buffer2[16 + 1];
unsigned i, j;
printf("\n");
while (size > 0) {
memset(buffer1, ' ', sizeof(buffer1));
memset(buffer2, ' ', sizeof(buffer2));
int linelen = size;
if (linelen > 16) linelen = 16;
for (i = 0, j = 0; i < linelen; i++) {
unsigned x = get_memory_c(address + i, 0);
buffer1[j++] = HexMap[x >> 4];
buffer1[j++] = HexMap[x & 0x0f];
j++;
if (i == 7) j++;
x &= 0x7f;
// isascii not part of std:: and may be a macro.
buffer2[i] = isascii(x) && isprint(x) ? x : '.';
}
buffer1[sizeof(buffer1) - 1] = 0;
buffer2[sizeof(buffer2) - 1] = 0;
printf("%06x:\t%s\t%s\n",
address, buffer1, buffer2);
address += 16;
size -= 16;
}
printf("\n");
}
void host_hexdump_native(void *data, unsigned address, int size) {
const char *HexMap = "0123456789abcdef";
char buffer1[16 * 3 + 1 + 1];
char buffer2[16 + 1];
unsigned i, j;
printf("\n");
while (size > 0) {
memset(buffer1, ' ', sizeof(buffer1));
memset(buffer2, ' ', sizeof(buffer2));
int linelen = size;
if (linelen > 16) linelen = 16;
for (i = 0, j = 0; i < linelen; i++) {
unsigned x = ((byte *)data)[address + i];
buffer1[j++] = HexMap[x >> 4];
buffer1[j++] = HexMap[x & 0x0f];
j++;
if (i == 7) j++;
x &= 0x7f;
// isascii not part of std:: and may be a macro.
buffer2[i] = isascii(x) && isprint(x) ? x : '.';
}
buffer1[sizeof(buffer1) - 1] = 0;
buffer2[sizeof(buffer2) - 1] = 0;
printf("%06x:\t%s\t%s\n",
address, buffer1, buffer2);
address += 16;
size -= 16;
}
printf("\n");
}
void host_free_directory(char **data, size_t count) {
for (int i = 0; i < count; ++i) free(data[i]);
free(data);
}

186
src/host_common.h Normal file
View File

@ -0,0 +1,186 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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
*/
#ifdef _WIN32
#include <stdint.h>
#pragma pack(push, 2)
struct AFP_Info {
uint32_t magic;
uint32_t version;
uint32_t file_id;
uint32_t backup_date;
uint8_t finder_info[32];
uint16_t prodos_file_type;
uint32_t prodos_aux_type;
uint8_t reserved[6];
};
#pragma pack(pop)
void afp_init(struct AFP_Info *info, word16 file_type, word32 aux_type);
BOOL afp_verify(struct AFP_Info *info);
int afp_to_filetype(struct AFP_Info *info, word16 *file_type, word32 *aux_type);
enum { prefer_prodos, prefer_hfs };
void afp_synchronize(struct AFP_Info *info, int preference);
#endif
#ifdef _WIN32
typedef FILETIME host_time_t;
typedef struct AFP_Info host_finder_info_t;
#else
typedef time_t host_time_t;
typedef unsigned char host_finder_info_t[32];
#endif
enum {
file_non,
file_regular,
file_resource,
file_directory,
};
enum {
translate_none,
translate_crlf,
translate_merlin,
};
struct file_info {
host_time_t create_date;
host_time_t modified_date;
word16 access;
word16 storage_type;
word16 file_type;
word32 aux_type;
word32 eof;
word32 blocks;
word32 resource_eof;
word32 resource_blocks;
int has_fi;
#ifdef _WIN32
struct AFP_Info afp;
#else
byte finder_info[32];
#endif
};
extern Engine_reg engine;
#define SEC() engine.psr |= 0x01
#define CLC() engine.psr &= ~0x01
#define SEV() engine.psr |= 0x40
#define CLV() engine.psr &= ~0x40
#define SEZ() engine.psr |= 0x02
#define CLZ() engine.psr &= ~0x02
#define SEI() engine.psr |= 0x04
#define CLI() engine.psr &= ~0x04
enum {
C = 0x01,
Z = 0x02,
I = 0x04,
D = 0x08,
X = 0x10,
M = 0x20,
V = 0x40,
N = 0x80
};
extern char *g_cfg_host_path;
extern int g_cfg_host_read_only;
extern int g_cfg_host_crlf;
extern int g_cfg_host_merlin;
extern char *host_root;
unsigned host_startup(void);
void host_shutdown(void);
#ifdef _WIN32
int host_is_root(const BY_HANDLE_FILE_INFORMATION *info);
#else
int host_is_root(struct stat *);
#endif
/* garbage collected string routines */
void *host_gc_malloc(size_t size);
void host_gc_free(void);
char *host_gc_strdup(const char *src);
char *host_gc_append_path(const char *a, const char *b);
char *host_gc_append_string(const char *a, const char *b);
/* text conversion */
void host_cr_to_lf(byte *buffer, size_t size);
void host_lf_to_cr(byte *buffer, size_t size);
void host_merlin_to_text(byte *buffer, size_t size);
void host_text_to_merlin(byte *buffer, size_t size);
/* errno -> IIgs/mli error */
word32 host_map_errno(int xerrno);
word32 host_map_errno_path(int xerrno, const char *path);
const char *host_error_name(word16 error);
#ifdef _WIN32
word32 host_map_win32_error(DWORD);
word32 host_map_win32_error_path(DWORD, const char *path);
#endif
/* file info */
int host_finder_info_to_filetype(const byte *buffer, word16 *file_type, word32 *aux_type);
int host_file_type_to_finder_info(byte *buffer, word16 file_type, word32 aux_type);
void host_get_file_xinfo(const char *path, struct file_info *fi);
word32 host_get_file_info(const char *path, struct file_info *fi);
word32 host_set_file_info(const char *path, struct file_info *fi);
/* guesses filetype/auxtype from extension */
void host_synthesize_file_xinfo(const char *path, struct file_info *fi);
void host_set_date_time_rec(word32 ptr, host_time_t time);
void host_set_date_time(word32 ptr, host_time_t time);
host_time_t host_get_date_time(word32 ptr);
host_time_t host_get_date_time_rec(word32 ptr);
/* convert to prodos date/time */
word32 host_convert_date_time(host_time_t time);
/* scan a directory, return array of char * */
unsigned host_scan_directory(const char *path, char ***out, size_t *entries, unsigned p8);
void host_free_directory(char **data, size_t count);
/* 0x01, 0x0d, 0x0f, 0 on error */
unsigned host_storage_type(const char *path, word16 *error_ptr);
void host_hexdump(word32 address, int size);
void host_hexdump_native(void *data, unsigned address, int size);

1879
src/host_fst.c Normal file

File diff suppressed because it is too large Load Diff

1567
src/host_mli.c Normal file

File diff suppressed because it is too large Load Diff

2078
src/icongs.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,7 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2013 by GSport contributors
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
@ -20,6 +21,7 @@
*/
#include "defc.h"
#include "glog.h"
#ifdef __linux__
# include <linux/joystick.h>
@ -32,251 +34,331 @@
# include <time.h>
#endif
extern int g_joystick_native_type1; /* in paddles.c */
extern int g_joystick_native_type2; /* in paddles.c */
extern int g_joystick_native_type; /* in paddles.c */
#ifdef HAVE_SDL
# include "SDL.h"
//@todo: multiple joysticks/more buttons/button config
//static SDL_Joystick *joy0, *joy1;
SDL_Joystick *gGameController = NULL;
#endif
extern int g_joystick_native_type1; /* in paddles.c */
extern int g_joystick_native_type2; /* in paddles.c */
extern int g_joystick_native_type; /* in paddles.c */
extern int g_joystick_type;
extern int g_paddle_buttons;
extern int g_paddle_val[];
const char *g_joystick_dev = "/dev/input/js0"; /* default joystick dev file */
#define MAX_JOY_NAME 128
const char *g_joystick_dev = "/dev/input/js0"; /* default joystick dev file */
#define MAX_JOY_NAME 128
int g_joystick_native_fd = -1;
int g_joystick_num_axes = 0;
int g_joystick_num_buttons = 0;
int g_joystick_native_fd = -1;
int g_joystick_num_axes = 0;
int g_joystick_num_buttons = 0;
int g_joystick_number = 0; // SDL2
int g_joystick_x_axis = 0; // SDL2
int g_joystick_y_axis = 1; // SDL2
int g_joystick_button_0 = 0; // SDL2
int g_joystick_button_1 = 1; // SDL2
int g_joystick_x2_axis = 2; // SDL2
int g_joystick_y2_axis = 3; // SDL2
int g_joystick_button_2 = 2; // SDL2
int g_joystick_button_3 = 3; // SDL2
#define JOY2SUPPORT
#ifdef __linux__
#if defined(HAVE_SDL) && !defined(JOYSTICK_DEFINED)
# define JOYSTICK_DEFINED
void
joystick_init()
{
char joy_name[MAX_JOY_NAME];
int version;
int fd;
int i;
void joystick_init() {
int i;
if( SDL_Init( SDL_INIT_JOYSTICK ) < 0 ) {
glogf( "SDL could not initialize joystick! SDL Error: %s", SDL_GetError() );
} else {
glog("SDL2 joystick initialized");
}
if (SDL_NumJoysticks()<1) {
glog("No joysticks detected");
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
} else {
// @todo: make controller configurable
// @todo: add multiple controller support
gGameController = SDL_JoystickOpen( g_joystick_number );
if( gGameController == NULL ) {
glogf( "Warning: Unable to open game controller! SDL Error: %s", SDL_GetError() );
}
}
g_joystick_native_type = 2;
g_joystick_native_type1 = 2;
g_joystick_native_type2 = -1;
for(i = 0; i < 4; i++) {
g_paddle_val[i] = 180;
}
g_joystick_type = JOYSTICK_TYPE_NATIVE_1;
SDL_JoystickUpdate();
joystick_update(0.0);
}
fd = open(g_joystick_dev, O_RDONLY | O_NONBLOCK);
if(fd < 0) {
printf("Unable to open joystick dev file: %s, errno: %d\n",
g_joystick_dev, errno);
printf("Defaulting to mouse joystick\n");
return;
}
void joystick_update(double dcycs) {
if (gGameController) {
SDL_JoystickUpdate();
g_paddle_val[0] = (int)SDL_JoystickGetAxis(gGameController, g_joystick_x_axis); // default is 0
g_paddle_val[1] = (int)SDL_JoystickGetAxis(gGameController, g_joystick_y_axis); // default is 1
g_paddle_val[2] = (int)SDL_JoystickGetAxis(gGameController, g_joystick_x2_axis); // default is 2
g_paddle_val[3] = (int)SDL_JoystickGetAxis(gGameController, g_joystick_y2_axis); // default is 3
strcpy(&joy_name[0], "Unknown Joystick");
version = 0x800;
if (SDL_JoystickGetButton(gGameController, g_joystick_button_0)) {
g_paddle_buttons = g_paddle_buttons | 1;
} else {
g_paddle_buttons = g_paddle_buttons & (~1);
}
if (SDL_JoystickGetButton(gGameController, g_joystick_button_1)) {
g_paddle_buttons = g_paddle_buttons | 2;
} else {
g_paddle_buttons = g_paddle_buttons & (~2);
}
if (SDL_JoystickGetButton(gGameController, g_joystick_button_2)) {
g_paddle_buttons = g_paddle_buttons | 4;
} else {
g_paddle_buttons = g_paddle_buttons & (~4);
}
if (SDL_JoystickGetButton(gGameController, g_joystick_button_3)) {
g_paddle_buttons = g_paddle_buttons | 8;
} else {
g_paddle_buttons = g_paddle_buttons & (~8);
}
paddle_update_trigger_dcycs(dcycs);
}
}
ioctl(fd, JSIOCGNAME(MAX_JOY_NAME), &joy_name[0]);
ioctl(fd, JSIOCGAXES, &g_joystick_num_axes);
ioctl(fd, JSIOCGBUTTONS, &g_joystick_num_buttons);
ioctl(fd, JSIOCGVERSION, &version);
void joystick_update_buttons() {
}
printf("Detected joystick: %s [%d axes, %d buttons vers: %08x]\n",
joy_name, g_joystick_num_axes, g_joystick_num_buttons,
version);
void joystick_shut() {
SDL_JoystickClose( gGameController );
gGameController = NULL;
}
#endif
g_joystick_native_type1 = 1;
g_joystick_native_type2 = -1;
g_joystick_native_fd = fd;
for(i = 0; i < 4; i++) {
g_paddle_val[i] = 32767;
}
g_paddle_buttons = 0xc;
joystick_update(0.0);
#if defined(__linux__) && !defined(JOYSTICK_DEFINED)
# define JOYSTICK_DEFINED
void joystick_init() {
char joy_name[MAX_JOY_NAME];
int version;
int fd;
int i;
fd = open(g_joystick_dev, O_RDONLY | O_NONBLOCK);
if(fd < 0) {
printf("Unable to open joystick dev file: %s, errno: %d\n",
g_joystick_dev, errno);
printf("Defaulting to mouse joystick\n");
return;
}
strcpy(&joy_name[0], "Unknown Joystick");
version = 0x800;
ioctl(fd, JSIOCGNAME(MAX_JOY_NAME), &joy_name[0]);
ioctl(fd, JSIOCGAXES, &g_joystick_num_axes);
ioctl(fd, JSIOCGBUTTONS, &g_joystick_num_buttons);
ioctl(fd, JSIOCGVERSION, &version);
printf("Detected joystick: %s [%d axes, %d buttons vers: %08x]\n",
joy_name, g_joystick_num_axes, g_joystick_num_buttons,
version);
g_joystick_native_type1 = 1;
g_joystick_native_type2 = -1;
g_joystick_native_fd = fd;
for(i = 0; i < 4; i++) {
g_paddle_val[i] = 32767;
}
g_paddle_buttons = 0xc;
joystick_update(0.0);
}
/* joystick_update_linux() called from paddles.c. Update g_paddle_val[] */
/* and g_paddle_buttons with current information */
void
joystick_update(double dcycs)
{
struct js_event js; /* the linux joystick event record */
int mask;
int val;
int num;
int type;
int ret;
int len;
int i;
void joystick_update(double dcycs) {
struct js_event js; /* the linux joystick event record */
int mask;
int val;
int num;
int type;
int ret;
int len;
int i;
/* suck up to 20 events, then give up */
len = sizeof(struct js_event);
for(i = 0; i < 20; i++) {
ret = read(g_joystick_native_fd, &js, len);
if(ret != len) {
/* just get out */
break;
}
type = js.type & ~JS_EVENT_INIT;
val = js.value;
num = js.number & 3; /* clamp to 0-3 */
switch(type) {
case JS_EVENT_BUTTON:
mask = 1 << num;
if(val) {
val = mask;
}
g_paddle_buttons = (g_paddle_buttons & ~mask) | val;
break;
case JS_EVENT_AXIS:
/* val is -32767 to +32767 */
g_paddle_val[num] = val;
break;
}
}
/* suck up to 20 events, then give up */
len = sizeof(struct js_event);
for(i = 0; i < 20; i++) {
ret = read(g_joystick_native_fd, &js, len);
if(ret != len) {
/* just get out */
break;
}
type = js.type & ~JS_EVENT_INIT;
val = js.value;
num = js.number & 3; /* clamp to 0-3 */
switch(type) {
case JS_EVENT_BUTTON:
mask = 1 << num;
if(val) {
val = mask;
}
g_paddle_buttons = (g_paddle_buttons & ~mask) | val;
break;
case JS_EVENT_AXIS:
/* val is -32767 to +32767 */
g_paddle_val[num] = val;
break;
}
}
// if(i > 0) {
// Note from Dave Schmenk: paddle_update_trigger_dcycles(dcycs) always has to be called to keep the triggers current.
paddle_update_trigger_dcycs(dcycs);
paddle_update_trigger_dcycs(dcycs);
// }
}
void
joystick_update_buttons()
{
void joystick_update_buttons() {
}
#endif /* LINUX */
#ifdef _WIN32
#if defined(_WIN32) && !defined(JOYSTICK_DEFINED)
# define JOYSTICK_DEFINED
void
joystick_init()
{
JOYINFO info;
JOYCAPS joycap;
MMRESULT ret1, ret2;
int i;
void joystick_init() {
JOYINFO info;
JOYCAPS joycap;
MMRESULT ret1, ret2;
int i;
// Check that there is a joystick device
if(joyGetNumDevs() <= 0) {
printf("No joystick hardware detected\n");
g_joystick_native_type1 = -1;
g_joystick_native_type2 = -1;
return;
}
// Check that there is a joystick device
if(joyGetNumDevs() <= 0) {
glog("No joystick hardware detected");
g_joystick_native_type1 = -1;
g_joystick_native_type2 = -1;
return;
}
g_joystick_native_type1 = -1;
g_joystick_native_type2 = -1;
g_joystick_native_type1 = -1;
g_joystick_native_type2 = -1;
// Check that at least joystick 1 or joystick 2 is available
ret1 = joyGetPos(JOYSTICKID1, &info);
ret2 = joyGetDevCaps(JOYSTICKID1, &joycap, sizeof(joycap));
if(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) {
g_joystick_native_type1 = JOYSTICKID1;
printf("Joystick #1 = %s\n", joycap.szPname);
g_joystick_native_type = JOYSTICKID1;
}
ret1 = joyGetPos(JOYSTICKID2, &info);
ret2 = joyGetDevCaps(JOYSTICKID2, &joycap, sizeof(joycap));
if(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) {
g_joystick_native_type2 = JOYSTICKID2;
printf("Joystick #2 = %s\n", joycap.szPname);
if(g_joystick_native_type < 0) {
g_joystick_native_type = JOYSTICKID2;
}
}
// Check that at least joystick 1 or joystick 2 is available
ret1 = joyGetPos(JOYSTICKID1, &info);
ret2 = joyGetDevCaps(JOYSTICKID1, &joycap, sizeof(joycap));
if(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) {
g_joystick_native_type1 = JOYSTICKID1;
printf("Joystick #1 = %s\n", joycap.szPname);
g_joystick_native_type = JOYSTICKID1;
}
ret1 = joyGetPos(JOYSTICKID2, &info);
ret2 = joyGetDevCaps(JOYSTICKID2, &joycap, sizeof(joycap));
if(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) {
g_joystick_native_type2 = JOYSTICKID2;
printf("Joystick #2 = %s\n", joycap.szPname);
if(g_joystick_native_type < 0) {
g_joystick_native_type = JOYSTICKID2;
}
}
if (g_joystick_native_type1<0 && g_joystick_native_type2 <0) {
printf ("No joystick is attached\n");
return;
}
if (g_joystick_native_type1<0 && g_joystick_native_type2 <0) {
glog("No joystick is attached");
return;
}
for(i = 0; i < 4; i++) {
g_paddle_val[i] = 32767;
}
g_paddle_buttons = 0xc;
for(i = 0; i < 4; i++) {
g_paddle_val[i] = 32767;
}
g_paddle_buttons = 0xc;
joystick_update(0.0);
joystick_update(0.0);
}
void
joystick_update(double dcycs)
{
JOYCAPS joycap;
JOYINFO info;
UINT id;
MMRESULT ret1, ret2;
void joystick_update(double dcycs) {
JOYCAPS joycap;
JOYINFO info;
UINT id;
MMRESULT ret1, ret2;
id = g_joystick_native_type;
id = g_joystick_native_type;
ret1 = joyGetDevCaps(id, &joycap, sizeof(joycap));
ret2 = joyGetPos(id, &info);
if(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) {
/* val should be -32767 to +32767 */
g_paddle_val[0] = -32767 + (info.wXpos - joycap.wXmin) * 65535 /
(joycap.wXmax - joycap.wXmin);
g_paddle_val[1] = -32767 + (info.wYpos - joycap.wYmin) * 65535 /
(joycap.wYmax - joycap.wYmin);
if(info.wButtons & JOY_BUTTON1) {
g_paddle_buttons = g_paddle_buttons | 1;
} else {
g_paddle_buttons = g_paddle_buttons & (~1);
}
if(info.wButtons & JOY_BUTTON2) {
g_paddle_buttons = g_paddle_buttons | 2;
} else {
g_paddle_buttons = g_paddle_buttons & (~2);
}
paddle_update_trigger_dcycs(dcycs);
}
ret1 = joyGetDevCaps(id, &joycap, sizeof(joycap));
ret2 = joyGetPos(id, &info);
if(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) {
g_paddle_val[0] = (info.wXpos - joycap.wXmin) * 32768 /
(joycap.wXmax - joycap.wXmin);
g_paddle_val[1] = (info.wYpos - joycap.wYmin) * 32768 /
(joycap.wYmax - joycap.wYmin);
if(info.wButtons & JOY_BUTTON1) {
g_paddle_buttons = g_paddle_buttons | 1;
} else {
g_paddle_buttons = g_paddle_buttons & (~1);
}
if(info.wButtons & JOY_BUTTON2) {
g_paddle_buttons = g_paddle_buttons | 2;
} else {
g_paddle_buttons = g_paddle_buttons & (~2);
}
paddle_update_trigger_dcycs(dcycs);
}
}
void
joystick_update_buttons()
{
JOYINFOEX info;
UINT id;
void joystick_update_buttons() {
JOYINFOEX info;
UINT id;
id = g_joystick_native_type;
id = g_joystick_native_type;
info.dwSize = sizeof(JOYINFOEX);
info.dwFlags = JOY_RETURNBUTTONS;
if(joyGetPosEx(id, &info) == JOYERR_NOERROR) {
if(info.dwButtons & JOY_BUTTON1) {
g_paddle_buttons = g_paddle_buttons | 1;
} else {
g_paddle_buttons = g_paddle_buttons & (~1);
}
if(info.dwButtons & JOY_BUTTON2) {
g_paddle_buttons = g_paddle_buttons | 2;
} else {
g_paddle_buttons = g_paddle_buttons & (~2);
}
}
info.dwSize = sizeof(JOYINFOEX);
info.dwFlags = JOY_RETURNBUTTONS;
if(joyGetPosEx(id, &info) == JOYERR_NOERROR) {
if(info.dwButtons & JOY_BUTTON1) {
g_paddle_buttons = g_paddle_buttons | 1;
} else {
g_paddle_buttons = g_paddle_buttons & (~1);
}
if(info.dwButtons & JOY_BUTTON2) {
g_paddle_buttons = g_paddle_buttons | 2;
} else {
g_paddle_buttons = g_paddle_buttons & (~2);
}
}
}
#endif
#ifndef JOYSTICK_DEFINED
/* stubs for the routines */
void
joystick_init()
{
g_joystick_native_type1 = -1;
g_joystick_native_type2 = -1;
g_joystick_native_type = -1;
void joystick_init() {
g_joystick_native_type1 = -1;
g_joystick_native_type2 = -1;
g_joystick_native_type = -1;
}
void
joystick_update(double dcycs)
{
int i;
void joystick_update(double dcycs) {
int i;
for(i = 0; i < 4; i++) {
g_paddle_val[i] = 32767;
}
g_paddle_buttons = 0xc;
for(i = 0; i < 4; i++) {
g_paddle_val[i] = 32767;
}
g_paddle_buttons = 0xc;
}
void
joystick_update_buttons()
{
void joystick_update_buttons() {
}
// OG
void joystick_shut()
{
void joystick_shut() {
}
#endif

View File

@ -1,860 +0,0 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2012 by GSport contributors
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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
*/
#ifdef ACTIVEIPHONE
#include <CoreGraphics/CGContext.h>
#include <CoreGraphics/CGBitmapContext.h>
#include <CoreFoundation/CFURL.h>
#else
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>
#include <Carbon/Carbon.h>
#define ENABLEQD
#endif
#include "defc.h"
#include "protos_macdriver.h"
pascal OSStatus quit_event_handler(EventHandlerCallRef call_ref, EventRef event, void *ignore);
pascal OSStatus my_cmd_handler(EventHandlerCallRef handlerRef, EventRef event, void *userdata);
pascal OSStatus my_win_handler(EventHandlerCallRef handlerRef, EventRef event, void *userdata);
pascal OSStatus dummy_event_handler(EventHandlerCallRef call_ref, EventRef in_event, void *ignore);
int g_quit_seen = 0;
#define MAX_STATUS_LINES 7
#define X_LINE_LENGTH 88
#define MAX_MAC_ARGS 128
int g_mac_argc = 0;
char *g_mac_argv[MAX_MAC_ARGS];
extern char g_argv0_path[];
extern char *g_status_ptrs[MAX_STATUS_LINES];
extern const char g_gsport_version_str[];
extern int g_warp_pointer;
extern WindowRef g_main_window;
WindowRef g_main_window_saved;
EventHandlerUPP g_quit_handler_UPP;
EventHandlerUPP g_dummy_event_handler_UPP;
RgnHandle g_event_rgnhandle = 0;
FMFontFamily g_status_font_family;
extern word32 g_red_mask;
extern word32 g_green_mask;
extern word32 g_blue_mask;
extern int g_red_left_shift;
extern int g_green_left_shift;
extern int g_blue_left_shift;
extern int g_red_right_shift;
extern int g_green_right_shift;
extern int g_blue_right_shift;
int g_ignore_next_click = 0;
int g_mainwin_active = 0;
int g_mac_mouse_x = 0;
int g_mac_mouse_y = 0;
extern int g_video_act_width;
extern int g_video_act_height;
extern int g_video_act_margin_left;
extern int g_video_act_margin_right;
extern int g_video_act_margin_top;
extern int g_video_act_margin_bottom;
extern int g_screen_depth;
extern int g_force_depth;
extern int g_screen_mdepth;
Ptr g_mac_fullscreen_state = 0;
Rect g_main_window_saved_rect;
extern char *g_fatal_log_strs[];
extern int g_fatal_log;
int
x_show_alert(int is_fatal, const char *str)
{
DialogRef alert;
DialogItemIndex out_item_hit;
CFStringRef cfstrref, cfstrref2;
CFStringRef okstrref;
AlertStdCFStringAlertParamRec alert_param;
OSStatus osstat;
char *bufptr, *buf2ptr;
int sum, len;
int i;
/* The dialog eats all events--including key-up events */
/* Call adb_all_keys_up() to prevent annoying key-repeat problems */
/* for instance, a key-down causes a dialog to appear--and the */
/* eats the key-up event...then as soon as the dialog goes, adb.c */
/* auto-repeat will repeat the key, and the dialog re-appears...*/
adb_all_keys_up();
sum = 20;
for(i = 0; i < g_fatal_log; i++) {
sum += strlen(g_fatal_log_strs[i]);
}
bufptr = (char*)malloc(sum);
buf2ptr = bufptr;
for(i = 0; i < g_fatal_log; i++) {
len = strlen(g_fatal_log_strs[i]);
len = MIN(len, sum);
len = MAX(len, 0);
memcpy(bufptr, g_fatal_log_strs[i], MIN(len, sum));
bufptr += len;
bufptr[0] = 0;
sum = sum - len;
}
cfstrref = CFStringCreateWithCString(NULL, buf2ptr,
kCFStringEncodingMacRoman);
printf("buf2ptr: :%s:\n", buf2ptr);
osstat = GetStandardAlertDefaultParams(&alert_param,
kStdCFStringAlertVersionOne);
if(str) {
// Provide an extra option--create a file
cfstrref2 = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
CFSTR("Create ./%s"), str);
alert_param.otherText = cfstrref2;
}
okstrref = CFSTR("Click OK to continue");
if(is_fatal) {
okstrref = CFSTR("Click OK to exit GSport");
}
CreateStandardAlert(kAlertStopAlert, cfstrref, okstrref,
&alert_param, &alert);
out_item_hit = -1;
RunStandardAlert(alert, NULL, &out_item_hit);
printf("out_item_hit: %d\n", out_item_hit);
free(buf2ptr);
clear_fatal_logs(); /* free the fatal_log string memory */
return (out_item_hit >= 3);
}
pascal OSStatus
quit_event_handler(EventHandlerCallRef call_ref, EventRef event, void *ignore)
{
OSStatus err;
err = CallNextEventHandler(call_ref, event);
if(err == noErr) {
g_quit_seen = 1;
}
return err;
}
void
show_simple_alert(char *str1, char *str2, char *str3, int num)
{
char buf[256];
g_fatal_log_strs[0] = gsport_malloc_str(str1);
g_fatal_log_strs[1] = gsport_malloc_str(str2);
g_fatal_log_strs[2] = gsport_malloc_str(str3);
g_fatal_log = 3;
if(num != 0) {
snprintf(buf, 250, ": %d", num);
g_fatal_log_strs[g_fatal_log++] = gsport_malloc_str(buf);
}
x_show_alert(0, 0);
}
void
x_dialog_create_gsport_conf(const char *str)
{
char *path;
char tmp_buf[512];
int ret;
ret = x_show_alert(1, str);
if(ret) {
config_write_config_gsport_file();
}
}
pascal OSStatus
my_cmd_handler( EventHandlerCallRef handlerRef, EventRef event, void *userdata)
{
OSStatus osresult;
HICommand command;
word32 command_id;
osresult = eventNotHandledErr;
GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL,
sizeof(HICommand), NULL, &command);
command_id = (word32)command.commandID;
switch(command_id) {
case 'Kbep':
SysBeep(10);
osresult = noErr;
break;
case 'abou':
show_simple_alert("GSport v", (char *)g_gsport_version_str,
"\nCopyright 2010 - 2011 GSport Contributors\n"
"Latest version at http://gsport.sourceforge.net/\n", 0);
osresult = noErr;
break;
case 'KCFG':
#ifdef ACTIVEGS
#else
cfg_toggle_config_panel();
#endif
osresult = noErr;
break;
case 'quit':
break;
case 'swin':
/* not sure what this is, but Panther sends it */
break;
default:
printf("commandID %08x unknown\n", command_id);
SysBeep(90);
break;
}
return osresult;
}
pascal OSStatus
my_win_handler(EventHandlerCallRef handlerRef, EventRef event, void *userdata)
{
OSStatus os_result;
UInt32 event_kind;
os_result = eventNotHandledErr;
// SysBeep(1);
event_kind = GetEventKind(event);
// show_alert("win handler", event_kind);
if(event_kind == kEventWindowDrawContent)
{
update_window();
} if(event_kind == kEventWindowClose) {
// OG Use HALT_WANTTOQUIT pardigme
//g_quit_sim_now = 1;
set_halt_act(HALT_WANTTOQUIT);
#ifndef ACTIVEGS
g_quit_seen = 1;
my_exit(0);
#endif
} else {
//show_event(GetEventClass(event), event_kind, 0);
update_window();
}
return os_result;
}
pascal OSStatus
dummy_event_handler(EventHandlerCallRef call_ref, EventRef in_event,
void *ignore)
{
OSStatus err;
EventHandlerRef installed_handler;
EventTypeSpec event_spec = { kEventClassApplication, kEventAppQuit };
// From http://developer.apple.com/qa/qa2001/qa1061.html
// Trick to move main event queue to use ReceiveNextEvent in an event
// handler called by RunApplicationEventLoop
err = InstallApplicationEventHandler(g_quit_handler_UPP, 1, &event_spec,
NULL, &installed_handler);
gsportmain(g_mac_argc, g_mac_argv);
return noErr;
}
void
check_input_events()
{
OSStatus err;
EventTargetRef target;
EventRef event;
UInt32 event_class, event_kind;
byte mac_keycode;
UInt32 keycode;
UInt32 modifiers;
Point mouse_point, mouse_delta_point;
WindowRef window_ref;
int button, button_state;
EventMouseButton mouse_button;
int handled;
int mouse_events;
int is_up;
int in_win;
int ignore;
if(g_quit_seen) {
exit(0);
}
SetPortWindowPort(g_main_window);
mouse_events = 0;
target = GetEventDispatcherTarget();
while(1) {
err = ReceiveNextEvent(0, NULL, kEventDurationNoWait,
true, &event);
if(err == eventLoopTimedOutErr) {
break;
}
if(err != noErr) {
printf("ReceiveNextEvent err: %d\n", (int)err);
break;
}
event_class = GetEventClass(event);
event_kind = GetEventKind(event);
handled = 0;
switch(event_class) {
case kEventClassKeyboard:
handled = 1;
keycode = 0;
modifiers = 0;
GetEventParameter(event, kEventParamKeyMacCharCodes,
typeChar, NULL, sizeof(byte), NULL,
&mac_keycode);
GetEventParameter(event, kEventParamKeyCode,
typeUInt32, NULL, sizeof(UInt32), NULL,
&keycode);
GetEventParameter(event, kEventParamKeyModifiers,
typeUInt32, NULL, sizeof(UInt32), NULL,
&modifiers);
mac_update_modifiers((word32)modifiers);
// Key up/down event
is_up = -1;
switch(event_kind) {
case kEventRawKeyDown:
is_up = 0;
//printf("key down: %02x, %08x\n",
// (int)mac_keycode, (int)keycode);
break;
case kEventRawKeyUp:
is_up = 1;
//printf("key up: %02x, %08x\n",
// (int)mac_keycode, (int)keycode);
break;
case kEventRawKeyModifiersChanged:
is_up = -1;
//printf("key xxx: %08x\n", (int)modifiers);
break;
}
if(is_up >= 0) {
adb_physical_key_update((int)keycode, is_up);
}
break;
case kEventClassMouse:
handled = 2;
mouse_events++;
GetEventParameter(event, kEventParamMouseLocation,
typeQDPoint, NULL, sizeof(Point), NULL,
&mouse_point);
GetWindowRegion(g_main_window, kWindowContentRgn,
g_event_rgnhandle);
in_win = PtInRgn(mouse_point, g_event_rgnhandle);
// in_win = 1 if it was in the contect region of window
err = GetEventParameter(event, kEventParamMouseDelta,
typeQDPoint, NULL, sizeof(Point), NULL,
&mouse_delta_point);
button = 0;
button_state = -1;
switch(event_kind) {
case kEventMouseDown:
button_state = 7;
handled = 3;
break;
case kEventMouseUp:
button_state = 0;
handled = 3;
break;
}
if(button_state >= 0) {
GetEventParameter(event, kEventParamMouseButton,
typeMouseButton, NULL,
sizeof(EventMouseButton), NULL,
&mouse_button);
button = mouse_button;
if(button > 1) {
button = 4 - button;
button = 1 << button;
}
ignore = (button_state != 0) &&
(!in_win || g_ignore_next_click);
ignore = ignore || !g_mainwin_active;
if(ignore) {
// Outside of A2 window, ignore clicks
button = 0;
}
if(button_state == 0) {
g_ignore_next_click = 0;
}
}
GlobalToLocal(&mouse_point);
if(g_warp_pointer) {
if(err == 0) {
g_mac_mouse_x += mouse_delta_point.h;
g_mac_mouse_y += mouse_delta_point.v;
}
mac_warp_mouse();
} else {
g_mac_mouse_x = mouse_point.h -
g_video_act_margin_left;
g_mac_mouse_y = mouse_point.v -
g_video_act_margin_top;
}
#if 0
printf("Mouse %d at: %d,%d button:%d, button_st:%d\n",
mouse_events, g_mac_mouse_x, g_mac_mouse_y,
button, button_state);
printf("Mouse deltas: err:%d, %d,%d\n", (int)err,
mouse_delta_point.h, mouse_delta_point.v);
#endif
update_mouse(g_mac_mouse_x, g_mac_mouse_y,
button_state, button & 7);
if(g_warp_pointer) {
g_mac_mouse_x = A2_WINDOW_WIDTH/2;
g_mac_mouse_y = A2_WINDOW_HEIGHT/2;
update_mouse(g_mac_mouse_x, g_mac_mouse_y,0,-1);
}
break;
case kEventClassApplication:
switch(event_kind) {
case kEventAppActivated:
handled = 1;
g_mainwin_active = 1;
window_ref = 0;
GetEventParameter(event, kEventParamWindowRef,
typeWindowRef, NULL, sizeof(WindowRef),
NULL, &window_ref);
if(window_ref == g_main_window) {
g_ignore_next_click = 1;
}
break;
case kEventAppDeactivated:
handled = 1;
g_mainwin_active = 0;
g_ignore_next_click = 1;
break;
}
break;
}
// show_event(event_class, event_kind, handled);
if(handled != 1) {
(void)SendEventToEventTarget(event, target);
}
ReleaseEvent(event);
}
return;
}
void
temp_run_application_event_loop(void)
{
OSStatus err;
EventRef dummy_event;
EventHandlerRef install_handler;
EventTypeSpec event_spec = { 'KWIN', 'KWIN' };
// Create UPP for dummy_event_handler and for quit_event_handler
err = noErr;
dummy_event = 0;
g_dummy_event_handler_UPP = NewEventHandlerUPP(dummy_event_handler);
g_quit_handler_UPP = NewEventHandlerUPP(quit_event_handler);
if((g_dummy_event_handler_UPP == 0) || (g_quit_handler_UPP == 0)) {
err = memFullErr;
}
if(err == noErr) {
err = InstallApplicationEventHandler(g_dummy_event_handler_UPP,
1, &event_spec, 0, &install_handler);
if(err == noErr) {
err = MacCreateEvent(NULL, 'KWIN', 'KWIN',
GetCurrentEventTime(), kEventAttributeNone,
&dummy_event);
if(err == noErr) {
err = PostEventToQueue(GetMainEventQueue(),
dummy_event, kEventPriorityHigh);
}
if(err == noErr) {
RunApplicationEventLoop();
}
(void)RemoveEventHandler(install_handler);
}
}
if(dummy_event != NULL) {
ReleaseEvent(dummy_event);
}
}
int
#ifdef ACTIVEGS
macmain
#else
main
#endif
(int argc, char* argv[])
{
ProcessSerialNumber my_psn;
IBNibRef nibRef;
EventHandlerUPP handlerUPP;
EventTypeSpec cmd_event[3];
GDHandle g_gdhandle ;
Rect win_rect;
OSStatus err;
char *argptr;
int slash_cnt;
int i;
#ifndef ACTIVEGS
/* Prepare argv0 */
slash_cnt = 0;
argptr = argv[0];
for(i = strlen(argptr); i >= 0; i--) {
if(argptr[i] == '/') {
slash_cnt++;
if(slash_cnt == 3) {
strncpy(&(g_argv0_path[0]), argptr, i);
g_argv0_path[i] = 0;
}
}
}
printf("g_argv0_path is %s\n", g_argv0_path);
g_mac_argv[0] = argv[0];
g_mac_argc = 1;
i = 1;
while((i < argc) && (g_mac_argc < MAX_MAC_ARGS)) {
if(!strncmp(argv[i], "-psn", 4)) {
/* skip this argument */
} else {
g_mac_argv[g_mac_argc++] = argv[i];
}
i++;
}
#endif
InitCursor();
g_event_rgnhandle = NewRgn();
g_status_font_family = FMGetFontFamilyFromName("\pCourier");
SetRect(&win_rect, 0, 0, X_A2_WINDOW_WIDTH, X_A2_WINDOW_HEIGHT
// OG Remove status line from ActiveGS window
#ifndef ACTIVEGS
+ MAX_STATUS_LINES*16 + 8
#endif
);
OffsetRect(&win_rect, 64, 50);
// Create a Nib reference passing the name of the nib file
// CreateNibReference only searches into the application bundle.
err = CreateNibReference(CFSTR("main"), &nibRef);
require_noerr( err, CantGetNibRef );
// Once the nib reference is created, set the menu bar.
err = SetMenuBarFromNib(nibRef, CFSTR("MenuBar"));
require_noerr( err, CantSetMenuBar );
#ifndef ACTIVEGS
err = CreateNewWindow(kDocumentWindowClass,
kWindowStandardDocumentAttributes |
kWindowStandardHandlerAttribute,
&win_rect, &g_main_window);
err = SetWindowTitleWithCFString(g_main_window, CFSTR("GSport"));
#else
err = CreateNewWindow(kDocumentWindowClass,
(kWindowCloseBoxAttribute /*| kWindowFullZoomAttribute */| kWindowCollapseBoxAttribute /*| kWindowResizableAttribute*/) /*kWindowStandardDocumentAttributes*/ |
kWindowStandardHandlerAttribute,
&win_rect, &g_main_window);
extern CFStringRef activeGSversionSTR;
err = SetWindowTitleWithCFString(g_main_window, activeGSversionSTR);
#endif
//printf("CreateNewWindow ret: %d, g_main_window: %p\n", (int)err, g_main_window);
// We don't need the nib reference anymore.
DisposeNibReference(nibRef);
SysBeep(120);
handlerUPP = NewEventHandlerUPP( my_cmd_handler );
cmd_event[0].eventClass = kEventClassCommand;
cmd_event[0].eventKind = kEventProcessCommand;
InstallWindowEventHandler(g_main_window, handlerUPP, 1, &cmd_event[0],
(void *)g_main_window, NULL);
handlerUPP = NewEventHandlerUPP(my_win_handler);
cmd_event[0].eventClass = kEventClassWindow;
cmd_event[0].eventKind = kEventWindowDrawContent;
cmd_event[1].eventClass = kEventClassWindow;
cmd_event[1].eventKind = kEventWindowUpdate;
cmd_event[2].eventClass = kEventClassWindow;
cmd_event[2].eventKind = kEventWindowClose;
err = InstallWindowEventHandler(g_main_window, handlerUPP, 3,
&cmd_event[0], (void *)g_main_window, NULL);
require_noerr(err, CantCreateWindow);
// Get screen depth
g_gdhandle = GetGDevice();
g_screen_mdepth = (**((**g_gdhandle).gdPMap)).pixelSize;
g_screen_depth = g_screen_mdepth;
if(g_screen_depth > 16) {
/* 32-bit display */
g_red_mask = 0xff;
g_green_mask = 0xff;
g_blue_mask = 0xff;
/*
if (macUsingCoreGraphics)
{
g_red_left_shift = 0;
g_green_left_shift = 8;
g_blue_left_shift = 16;
}
else
*/
{
g_red_left_shift = 16;
g_green_left_shift = 8;
g_blue_left_shift = 0;
}
g_red_right_shift = 0;
g_green_right_shift = 0;
g_blue_right_shift = 0;
} else if(g_screen_depth > 8) {
/* 16-bit display */
g_red_mask = 0x1f;
g_green_mask = 0x1f;
g_blue_mask = 0x1f;
g_red_left_shift = 10;
g_green_left_shift = 5;
g_blue_left_shift = 0;
g_red_right_shift = 3;
g_green_right_shift = 3;
g_blue_right_shift = 3;
}
// show_alert("About to show window", (int)g_main_window);
update_main_window_size();
update_window();
// The window was created hidden so show it.
ShowWindow( g_main_window );
BringToFront( g_main_window );
update_window();
// Make us pop to the front a different way
err = GetCurrentProcess(&my_psn);
if(err == noErr) {
(void)SetFrontProcess(&my_psn);
}
// Call the event loop
temp_run_application_event_loop();
CantCreateWindow:
CantSetMenuBar:
CantGetNibRef:
show_simple_alert("ending", "", "error code", err);
return err;
}
void
xdriver_end()
{
printf("xdriver_end\n");
if(g_fatal_log >= 0) {
x_show_alert(1, 0);
}
}
void
x_redraw_status_lines()
{
// OG Disable status line
#ifndef ACTIVEGS
Rect rect;
Pattern white_pattern;
char tmp_buf[256];
char *buf;
int len;
int line;
int height;
int margin;
SetPortWindowPort(g_main_window);
PenNormal();
height = 16;
margin = 0;
TextFont(g_status_font_family);
TextFace(normal);
TextSize(12);
SetRect(&rect, 0, X_A2_WINDOW_HEIGHT + margin, X_A2_WINDOW_WIDTH,
X_A2_WINDOW_HEIGHT + margin + MAX_STATUS_LINES*height);
GetQDGlobalsWhite(&white_pattern);
FillRect(&rect, &white_pattern);
for(line = 0; line < MAX_STATUS_LINES; line++) {
buf = g_status_ptrs[line];
if(buf == 0) {
/* skip it */
continue;
}
MoveTo(10, X_A2_WINDOW_HEIGHT + height*line + margin + height);
len = MIN(250, strlen(buf));
strncpy(&tmp_buf[1], buf, len);
tmp_buf[0] = len;
DrawString((const unsigned char*)&tmp_buf[0]);
}
#endif
}
void
x_full_screen(int do_full)
{
#if 0
WindowRef new_window;
short width, height;
OSErr ret;
width = 640;
height = 480;
if(do_full && (g_mac_fullscreen_state == 0)) {
g_main_window_saved = g_main_window;
GetWindowBounds(g_main_window, kWindowContentRgn,
&g_main_window_saved_rect);
ret = BeginFullScreen(&g_mac_fullscreen_state, 0,
&width, &height, &new_window, 0, 0);
printf("Ret beginfullscreen: %d\n", (int)ret);
printf("New width: %d, new height: %d\n", width, height);
if(ret == noErr) {
g_main_window = new_window;
} else {
g_mac_fullscreen_state = 0;
}
} else if(!do_full && (g_mac_fullscreen_state != 0)) {
ret = EndFullScreen(g_mac_fullscreen_state, 0);
printf("ret endfullscreen: %d\n", (int)ret);
g_main_window = g_main_window_saved;
g_mac_fullscreen_state = 0;
//InitCursor();
SetWindowBounds(g_main_window, kWindowContentRgn,
&g_main_window_saved_rect);
}
update_main_window_size();
ShowWindow(g_main_window);
BringToFront(g_main_window);
update_window();
#endif
}
void
x_push_done()
{
CGrafPtr window_port;
SetPortWindowPort(g_main_window);
window_port = GetWindowPort(g_main_window);
QDFlushPortBuffer(window_port, 0);
}
void
mac_warp_mouse()
{
#ifndef ACTIVEGS
Rect port_rect;
Point win_origin_pt;
CGPoint cgpoint;
CGDisplayErr cg_err;
GetPortBounds(GetWindowPort(g_main_window), &port_rect);
SetPt(&win_origin_pt, port_rect.left, port_rect.top);
LocalToGlobal(&win_origin_pt);
cgpoint = CGPointMake( (float)(win_origin_pt.h + X_A2_WINDOW_WIDTH/2),
(float)(win_origin_pt.v + X_A2_WINDOW_HEIGHT/2));
cg_err = CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
#endif
}

File diff suppressed because it is too large Load Diff

453
src/options.c Normal file
View File

@ -0,0 +1,453 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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 <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "options.h"
#include "glog.h"
#include "defc.h"
// config is parsed in config.c :: config_parse_config_gsport_file()
// cli is parsed here. would be nice to reuse some code
// Halts on bad reads. Sets flags via engine_s.s:set_halt_act() function
extern int g_halt_on_bad_read; // defined in sim65816.c
// Ignore bad memory accesses.
extern int g_ignore_bad_acc; // defined in sim65816.c
// Ignore red alert halts.
extern int g_ignore_halts; // defined in sim65816.c
// Size of RAM memory expansion card in bytes (default = 8*1024*1024 = 8MB)
extern int g_mem_size_exp; // defined in sim65816.c
// Implemented in display drivers (not SDL2 though) ?
extern int g_screen_redraw_skip_amt; // defined in video.c, implemented in various driver files
// Using simple dhires color map
extern int g_use_dhr140; // defined in video.c
// Force B/W hires modes
extern int g_use_bw_hires; // defined in video.c
// Set starting X/Y positions
extern int g_startx; // defined in video.c
extern int g_starty; // defined in video.c
extern int g_startw; // defined in video.c
extern int g_starth; // defined in video.c
// Use High DPI (Retina) display - SDL2
extern int g_highdpi; // defined in video.c
// Create borderless window - SDL2
extern int g_borderless; // defined in video.c
// Allow window resizing, dragging to scale - SDL2
extern int g_resizeable; // defined in video.c
// Allow the window scaling to be free from aspect contraints - SDL2
extern int g_noaspect;
// Don't explicitly set vsync present flag on renderer - SDL2
extern int g_novsync; // defined in video.c
// Don't explicitly set HW accelerator flag on renderer - SDL2
extern int g_nohwaccel; // defined in video.c
// Use SDL_WINDOW_FULLSCREEN_DESKTOP for fullscreen instead of switching modes
extern int g_fullscreen_desktop;
// Enable Dagen's scanline simulator (SDL2)
extern int g_scanline_simulator; // defined in sim65816.c
// Ethernet (interface?)
extern int g_ethernet; // defined in sim65816.c
// Enable and set port for Dagen's debugger
extern int g_dbg_enable_port; // defined in debug.c
// Set preferred audio frequency
extern int g_preferred_rate; // defined in sound_driver.c, implemented in various driver files
// Enable/disable audio
extern int g_audio_enable; // defined in sound.c
// Start in fullscreen mode
extern int g_fullscreen; // defined in adb.c, because weird driver writing for x
// Specify the joystick - SDL2
extern int g_joystick_number; // defined in joystick_driver.c
extern int g_joystick_x_axis; // defined in joystick_driver.c
extern int g_joystick_y_axis; // defined in joystick_driver.c
extern int g_joystick_x2_axis; // defined in joystick_driver.c
extern int g_joystick_y2_axis; // defined in joystick_driver.c
extern int g_joystick_button_0; // defined in joystick_driver.c
extern int g_joystick_button_1; // defined in joystick_driver.c
extern int g_joystick_button_2; // defined in joystick_driver.c
extern int g_joystick_button_3; // defined in joystick_driver.c
// DEPRECATED: force bit depth (15/16/24) for X-Windows, might still work.
extern int g_force_depth; // defined in sim65816.c
// DEPRECATED: Use X shared memory (MIT-SHM)
extern int g_use_shmem; // defined in all the various drivers
// DEPRECATED: Set DISPLAY environment variable for X-Windows
extern char g_display_env[512]; // defined in sim65816.c
// DEPRECATED: Set VERBOSE flags for one or more subsystems as defined below
extern int Verbose; // defined in sim65816.c
// #define VERBOSE_DISK 0x001
// #define VERBOSE_IRQ 0x002
// #define VERBOSE_CLK 0x004
// #define VERBOSE_SHADOW 0x008
// #define VERBOSE_IWM 0x010
// #define VERBOSE_DOC 0x020
// #define VERBOSE_ADB 0x040
// #define VERBOSE_SCC 0x080
// #define VERBOSE_TEST 0x100
// #define VERBOSE_VIDEO 0x200
// #define VERBOSE_MAC
// This is deprecated because it is not well-defined or supported
// It should still work and some sort of system should be put in place
// to extend and fix this, or take it out.
extern const char *g_config_gsport_name_list[];
extern char g_config_gsport_screenshot_dir[];
extern char *final_arg;
static const char parse_log_prefix[] = "Option set [CLI]:";
// this is here because we need to flip a bit to force B/W modes
extern int g_cur_a2_stat;
void help_exit(); // displays the cli help text and exits with 1
int parse_int(const char *str1, int min, int max)
{
int tmp;
tmp = strtol(str1, 0, 0);
if (tmp > max) { tmp = max; }
if (tmp < min) { tmp = min; }
return tmp;
}
void parse_cli_options(int argc, char **argv) {
int i;
int tmp1;
int skip_amt;
char *final_arg = 0;
for(i = 1; i < argc; i++) {
if( (!strcmp("-?", argv[i])) || (!strcmp("-h", argv[i])) || (!strcmp("-help", argv[i]))) {
help_exit();
} else if(!strcmp("-badrd", argv[i])) {
glogf("%s Halting on bad reads", parse_log_prefix);
g_halt_on_bad_read = 2;
} else if(!strcmp("-fullscreen", argv[i])) {
glogf("%s Starting emulator in fullscreen", parse_log_prefix);
g_fullscreen = 1;
} else if(!strcmp("-highdpi", argv[i])) {
glogf("%s Creating window in High DPI mode", parse_log_prefix);
g_highdpi = 1;
} else if(!strcmp("-borderless", argv[i])) {
glogf("%s Creating borderless window", parse_log_prefix);
g_borderless = 1;
} else if(!strcmp("-resizeable", argv[i])) {
glogf("%s Window will be resizeable", parse_log_prefix);
g_resizeable = 1;
} else if(!strcmp("-noaspect", argv[i])) {
glogf("%s Window will scale freely, without locking the aspect ratio", parse_log_prefix);
g_noaspect = 1;
} else if(!strcmp("-novsync", argv[i])) {
glogf("%s Renderer skipping vsync flag", parse_log_prefix);
g_novsync = 1;
} else if(!strcmp("-nohwaccel", argv[i])) {
glogf("%s Renderer skipping HW accel flag", parse_log_prefix);
g_nohwaccel = 1;
} else if(!strcmp("-fulldesk", argv[i])) {
glogf("%s Using desktop fullscreen mode", parse_log_prefix);
g_fullscreen_desktop = 1;
} else if(!strcmp("-noignbadacc", argv[i])) {
glogf("%s Not ignoring bad memory accesses", parse_log_prefix);
g_ignore_bad_acc = 0;
} else if(!strcmp("-noignhalt", argv[i])) {
glogf("%s Not ignoring code red halts", parse_log_prefix);
g_ignore_halts = 0;
} else if(!strcmp("-mem", argv[i])) {
if((i+1) >= argc) {
glogf("%s Error, option '-mem' missing argument", parse_log_prefix);
exit(1);
}
g_mem_size_exp = strtol(argv[i+1], 0, 0) & 0x00ff0000;
glogf("%s Using %d as memory size", parse_log_prefix, g_mem_size_exp);
i++;
} else if(!strcmp("-skip", argv[i])) {
if((i+1) >= argc) {
glogf("%s Error, option '-skip' missing argument", parse_log_prefix);
exit(1);
}
skip_amt = strtol(argv[i+1], 0, 0);
glogf("%s Using %d as skip_amt", parse_log_prefix, skip_amt);
g_screen_redraw_skip_amt = skip_amt;
i++;
} else if(!strcmp("-audio", argv[i])) {
if((i+1) >= argc) {
glogf("%s Error, option '-audio' missing argument", parse_log_prefix);
exit(1);
}
tmp1 = strtol(argv[i+1], 0, 0);
glogf("%s Using %d as audio enable val", parse_log_prefix, tmp1);
g_audio_enable = tmp1;
i++;
} else if(!strcmp("-arate", argv[i])) {
if((i+1) >= argc) {
glogf("%s Error, option '-arate' missing argument", parse_log_prefix);
exit(1);
}
tmp1 = strtol(argv[i+1], 0, 0);
glogf("%s Using %d as preferred audio rate", parse_log_prefix, tmp1);
g_preferred_rate = tmp1;
i++;
} else if(!strcmp("-v", argv[i])) {
if((i+1) >= argc) {
glogf("%s Error, option '-v' missing argument", parse_log_prefix);
exit(1);
}
tmp1 = strtol(argv[i+1], 0, 0);
glogf("%s Setting Verbose = 0x%03x", parse_log_prefix, tmp1);
Verbose = tmp1;
i++;
} else if(!strcmp("-display", argv[i])) {
if((i+1) >= argc) {
glogf("%s Error, option '-display' missing argument", parse_log_prefix);
exit(1);
}
glogf("%s Using %s as display", parse_log_prefix, argv[i+1]);
sprintf(g_display_env, "DISPLAY=%s", argv[i+1]);
putenv(&g_display_env[0]);
i++;
} else if(!strcmp("-noshm", argv[i])) {
glogf("%s Not using X shared memory", parse_log_prefix);
g_use_shmem = 0;
} else if(!strcmp("-joy", argv[i])) {
if((i+1) >= argc) {
glogf("%s Error, option '-joy' missing argument", parse_log_prefix);
exit(1);
}
tmp1 = strtol(argv[i+1], 0, 0); // no bounds check, not sure what ids we get
glogf("%s Setting joystick number %d", parse_log_prefix, tmp1);
g_joystick_number = tmp1;
i++;
} else if(!strcmp("-joy-x", argv[i])) {
if((i+1) >= argc) {
glogf("%s Error, option '-joy-x' missing argument", parse_log_prefix);
exit(1);
}
tmp1 = strtol(argv[i+1], 0, 0); // no bounds check, not sure what ids we get
glogf("%s Setting joystick X axis %d", parse_log_prefix, tmp1);
g_joystick_x_axis = tmp1;
i++;
} else if(!strcmp("-joy-y", argv[i])) {
if((i+1) >= argc) {
glogf("%s Error, option '-joy-y' missing argument", parse_log_prefix);
exit(1);
}
tmp1 = strtol(argv[i+1], 0, 0); // no bounds check, not sure what ids we get
glogf("%s Setting joystick Y axis %d", parse_log_prefix, tmp1);
g_joystick_y_axis = tmp1;
i++;
} else if(!strcmp("-joy-x2", argv[i])) {
if((i+1) >= argc) {
glogf("%s Error, option '-joy-x2' missing argument", parse_log_prefix);
exit(1);
}
tmp1 = strtol(argv[i+1], 0, 0); // no bounds check, not sure what ids we get
glogf("%s Setting joystick X2 axis %d", parse_log_prefix, tmp1);
g_joystick_x2_axis = tmp1;
i++;
} else if(!strcmp("-joy-y2", argv[i])) {
if((i+1) >= argc) {
glogf("%s Error, option '-joy-y2' missing argument", parse_log_prefix);
exit(1);
}
tmp1 = strtol(argv[i+1], 0, 0); // no bounds check, not sure what ids we get
glogf("%s Setting joystick Y2 axis %d", parse_log_prefix, tmp1);
g_joystick_y2_axis = tmp1;
i++;
} else if(!strcmp("-joy-b0", argv[i])) {
if((i+1) >= argc) {
glogf("%s Error, option '-joy-b0' missing argument", parse_log_prefix);
exit(1);
}
tmp1 = strtol(argv[i+1], 0, 0); // no bounds check, not sure what ids we get
glogf("%s Setting joystick Button 0 to Gamepad %d", parse_log_prefix, tmp1);
g_joystick_button_0 = tmp1;
i++;
} else if(!strcmp("-joy-b1", argv[i])) {
if((i+1) >= argc) {
glogf("%s Error, option '-joy-b1' missing argument", parse_log_prefix);
exit(1);
}
tmp1 = strtol(argv[i+1], 0, 0); // no bounds check, not sure what ids we get
glogf("%s Setting joystick Button 1 to Gamepad %d", parse_log_prefix, tmp1);
g_joystick_button_1 = tmp1;
i++;
} else if(!strcmp("-joy-b2", argv[i])) {
if((i+1) >= argc) {
glogf("%s Error, option '-joy-b2' missing argument", parse_log_prefix);
exit(1);
}
tmp1 = strtol(argv[i+1], 0, 0); // no bounds check, not sure what ids we get
glogf("%s Setting joystick Button 2 to Gamepad %d", parse_log_prefix, tmp1);
g_joystick_button_2 = tmp1;
i++;
} else if(!strcmp("-joy-b3", argv[i])) {
if((i+1) >= argc) {
glogf("%s Error, option '-joy-b3' missing argument", parse_log_prefix);
exit(1);
}
tmp1 = strtol(argv[i+1], 0, 0); // no bounds check, not sure what ids we get
glogf("%s Setting joystick Button 3 to Gamepad %d", parse_log_prefix, tmp1);
g_joystick_button_3 = tmp1;
i++;
} else if(!strcmp("-dhr140", argv[i])) {
glogf("%s Using simple dhires color map", parse_log_prefix);
g_use_dhr140 = 1;
} else if(!strcmp("-bw", argv[i])) {
glogf("%s Forcing black-and-white hires modes", parse_log_prefix);
g_cur_a2_stat |= ALL_STAT_COLOR_C021;
g_use_bw_hires = 1;
} else if(!strcmp("-scanline", argv[i])) {
glogf("%s Enable scanline simulation", parse_log_prefix);
if((i+1) >= argc) {
glogf("%s Error, option '-scanline' missing argument", parse_log_prefix);
exit(1);
}
tmp1 = parse_int(argv[i+1], 0, 100);
glogf("%s Setting scanline simulator darkness to %d%%", parse_log_prefix, tmp1);
g_scanline_simulator = tmp1;
i++;
} else if(!strcmp("-enet", argv[i])) {
if((i+1) >= argc) {
glogf("%s Error, option '-enet' missing argument", parse_log_prefix);
exit(1);
}
tmp1 = strtol(argv[i+1], 0, 0);
glogf("%s Using %d as ethernet enable val", parse_log_prefix, tmp1);
g_ethernet = tmp1;
i++;
} else if(!strcmp("-x", argv[i])) {
if((i+1) >= argc) {
glogf("%s Error, option '-x' missing argument", parse_log_prefix);
exit(1);
}
tmp1 = strtol(argv[i+1], 0, 0);
glogf("%s Using %d as x val", parse_log_prefix, tmp1);
g_startx = tmp1;
i++;
} else if(!strcmp("-y", argv[i])) {
if((i+1) >= argc) {
glogf("%s Error, option '-y' missing argument", parse_log_prefix);
exit(1);
}
tmp1 = strtol(argv[i+1], 0, 0);
glogf("%s Using %d as y val", parse_log_prefix, tmp1);
g_starty = tmp1;
i++;
} else if(!strcmp("-sw", argv[i])) {
if((i+1) >= argc) {
glogf("%s Error, option '-sw' missing argument", parse_log_prefix);
exit(1);
}
tmp1 = strtol(argv[i+1], 0, 0);
glogf("%s Using %d as width val", parse_log_prefix, tmp1);
g_startw = tmp1;
i++;
} else if(!strcmp("-sh", argv[i])) {
if((i+1) >= argc) {
glogf("%s Error, option '-sh' missing argument", parse_log_prefix);
exit(1);
}
tmp1 = strtol(argv[i+1], 0, 0);
glogf("%s Using %d as height val", parse_log_prefix, tmp1);
g_starth = tmp1;
i++;
} else if(!strcmp("-config", argv[i])) { // Config file passed
if((i+1) >= argc) {
glogf("%s Error, option '-config' missing argument", parse_log_prefix);
exit(1);
}
glogf("%s Using %s as configuration file", parse_log_prefix, argv[i+1]);
g_config_gsport_name_list[0] = argv[i+1]; // overwrite default list with passed item as sole option
g_config_gsport_name_list[1] = 0; // terminate string array
i++;
} else if (!strcmp("-ssdir", argv[i])) { // screenshot directory passed
strcpy(g_config_gsport_screenshot_dir, argv[i+1]);
struct stat path_stat;
stat(g_config_gsport_screenshot_dir, &path_stat); // (weakly) validate path
if (!S_ISDIR(path_stat.st_mode)) {
strcpy(g_config_gsport_screenshot_dir, "./");
}
glogf("%s Using %s for screenshot path", parse_log_prefix, g_config_gsport_screenshot_dir);
i++;
} else if(!strcmp("-debugport", argv[i])) { // Debug port passed
if((i+1) >= argc) {
glogf("%s Error, option '-debugport' missing argument", parse_log_prefix);
exit(1);
}
g_dbg_enable_port = strtol(argv[i+1], 0, 0);
glogf("%s Using %d for debug port", parse_log_prefix, g_dbg_enable_port);
i++;
} else {
if ((i == (argc - 1)) && (strncmp("-", argv[i], 1) != 0)) {
final_arg = argv[i];
} else {
glogf("%s Error, bad option: %s for debug port", parse_log_prefix, argv[i]);
exit(3);
}
}
}
}
void help_exit() {
printf(" USAGE: \n\n");
printf(" ./gsport # simple - uses default config.txt\n");
printf(" ./gsport -config games_hds.gsp # set custom config file\n\n");
printf(" You need to supply your own Apple IIgs Firmware ROM image.\n");
printf(" Press F4 when running gsport to enter config menu and select ROM image location.\n");
printf(" Or copy the ROM image to the gsport directory.\n");
printf(" It will search for: \"ROM\", \"ROM.01\", \"ROM.03\" \n\n\n");
printf(" Other command line options: \n\n");
printf(" -badrd Halt on bad reads\n");
printf(" -noignbadacc Dont ignore bad memory accesses\n");
printf(" -noignhalt Dont ignore code red halts\n");
printf(" -test Allow testing\n");
printf(" -joy Set joystick number\n");
printf(" -bw Force B/W modes\n");
printf(" -dhr140 Use simple double-hires color map\n");
printf(" -fullscreen Attempt to start emulator in fullscreen\n");
printf(" -highdpi Attempt to open window in high DPI\n");
printf(" -borderless Attempt to create borderless window\n");
printf(" -resizeable Allow you to resize window (non-integral scaling to pixel)\n");
printf(" -mem value Set memory size to value\n");
printf(" -skip value Set skip_amt to value\n");
printf(" -audio value Set audio enable to value\n");
printf(" -arate value Set preferred audio rate to value\n");
printf(" -enet value Set ethernet to value\n");
printf(" -config value Set config file to value\n");
printf(" -debugport value Set debugport to value\n");
printf(" -ssdir value Set screenshot save directory to value\n");
printf(" -scanline value Enable scanline simulator at value %%\n");
printf(" -x value Open emulator window at x value\n");
printf(" -y value Open emulator window at y value\n");
printf(" -sw value Scale window to sw pixels wide\n");
printf(" -sh value Scale window to sh pixels high\n");
printf(" -novsync Don't force emulator to sync each frame\n");
printf(" -fulldesk Use desktop 'fake' fullscreen mode\n");
//printf(" -v value Set verbose flags to value\n\n");
printf(" Note: The final argument, if not a flag, will be tried as a mountable device.\n\n");
exit(1);
}

29
src/options.h Normal file
View File

@ -0,0 +1,29 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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
*/
#ifdef __cplusplus
extern "C" {
#endif
void parse_cli_options(int argc, char **argv);
#ifdef __cplusplus
}
#endif

View File

@ -1,6 +1,7 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2012 by GSport contributors
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
@ -45,9 +46,6 @@ void x_full_screen(int do_full);
void clipboard_paste(void);
int clipboard_get_char(void);
/* test65.c */
void do_gen_test(int got_num, int base_seed);
/* engine.s and engine_c.c */
void fixed_memory_ptrs_init();
@ -57,6 +55,7 @@ word32 get_itimer(void);
word32 get_memory_c(word32 addr, int cycs);
word32 get_memory16_c(word32 addr, int cycs);
word32 get_memory24_c(word32 addr, int cycs);
word32 get_memory32_c(word32 addr, int cycs);
int get_memory_asm(word32 addr, int cycs);
int get_memory16_asm(word32 addr, int cycs);
@ -67,6 +66,7 @@ int get_memory16_act_stub_asm(word32 addr, int cycs);
void set_memory_c(word32 addr, word32 val, int cycs);
void set_memory16_c(word32 addr, word32 val, int cycs);
void set_memory24_c(word32 addr, word32 val, int cycs);
void set_memory32_c(word32 addr, word32 val, int cycs);
int enter_engine(Engine_reg *ptr);
void clr_halt_act(void);
@ -121,6 +121,7 @@ int read_adb_ram(word32 addr);
void write_adb_ram(word32 addr, int val);
int adb_get_keypad_xy(int get_y);
int update_mouse(int x, int y, int button_states, int buttons_valid);
int update_mouse_w_delta(int x, int y, int button_states, int buttons_valid, int delta_x, int delta_y);
int mouse_read_c024(double dcycs);
void mouse_compress_fifo(double dcycs);
void adb_key_event(int a2code, int is_up);
@ -176,34 +177,6 @@ void insert_disk(int slot, int drive, const char *name, int ejected, int force_s
void eject_named_disk(Disk *dsk, const char *name, const char *partition_name);
void eject_disk_by_num(int slot, int drive);
void eject_disk(Disk *dsk);
int cfg_get_fd_size(char *filename);
int cfg_partition_read_block(FILE *file, void *buf, int blk, int blk_size);
int cfg_partition_find_by_name_or_num(FILE *file, const char *partnamestr, int part_num, Disk *dsk);
int cfg_maybe_insert_disk(int slot, int drive, const char *namestr);
int cfg_stat(char *path, struct stat *sb);
int cfg_partition_make_list(char *filename, FILE *file);
void cfg_htab_vtab(int x, int y);
void cfg_home(void);
void cfg_cleol(void);
void cfg_putchar(int c);
void cfg_printf(const char *fmt, ...);
void cfg_print_num(int num, int max_len);
void cfg_get_disk_name(char *outstr, int maxlen, int type_ext, int with_extras);
void cfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int change);
void cfg_get_base_path(char *pathptr, const char *inptr, int go_up);
void cfg_file_init(void);
void cfg_free_alldirents(Cfg_listhdr *listhdrptr);
void cfg_file_add_dirent(Cfg_listhdr *listhdrptr, const char *nameptr, int is_dir, int size, int image_start, int part_num);
int cfg_dirent_sortfn(const void *obj1, const void *obj2);
int cfg_str_match(const char *str1, const char *str2, int len);
void cfg_file_readdir(const char *pathptr);
char *cfg_shorten_filename(const char *in_ptr, int maxlen);
void cfg_fix_topent(Cfg_listhdr *listhdrptr);
void cfg_file_draw(void);
void cfg_partition_selected(void);
void cfg_file_update_ptr(char *str);
void cfg_file_selected(void);
void cfg_file_handle_key(int key);
void config_control_panel(void);
@ -219,6 +192,7 @@ void show_bp(void);
void delete_bp(word32 addr);
void do_blank(void);
void do_go(void);
void do_go_debug(void); // socket debug ver
void do_step(void);
void xam_mem(int count);
void show_hex_mem(int startbank, word32 start, int endbank, word32 end, int count);

887
src/sdl2_driver.c Normal file
View File

@ -0,0 +1,887 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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
*/
// fps shiz
unsigned int lastTime = 0, currentTime, frames;
// @todo: mouse clip bugs.. great western shootout. Paint 8/16. still in win32
#include "SDL.h"
#include "SDL_image.h"
#include "glog.h"
#include <stdbool.h>
#include <time.h>
#include <stdlib.h>
#include <signal.h>
#include <libgen.h> // just for basename :P
#include <string.h>
#include "defc.h"
#ifdef HAVE_ICON // Currently a flag because not supported outside of SDL builds. Looking at full solution.
#include "icongs.h"
#endif
#ifdef _WIN32
#include <unistd.h>
#endif
// BITMASKS
#define ShiftMask 1
#define ControlMask 4
#define LockMask 2
int g_use_shmem = 0;
int g_num_check_input_calls = 0;
int g_check_input_flush_rate = 2;
int g_win_status_debug = 0; // Current visibility of status lines.
int g_win_status_debug_request = 0; // Desired visibility of status lines.
int g_screen_mdepth = 0;
int kb_shift_control_state = 0;
void debuginfo_renderer(SDL_Renderer *r);
void x_take_screenshot(); // screenshot stuff
void x_grabmouse();
int g_screenshot_requested = 0; // DB to know if we want to save a screenshot
extern char g_config_gsport_name[];
extern char g_config_gsport_screenshot_dir[];
int screenshot_index = 0; // allows us to save time by not scanning from 0 each time
char screenshot_filename[256];
extern int g_fullscreen; // only checked at start if set via CLI, otherwise it's set via function call x_full_screen()
extern int g_grabmouse;
extern int g_highdpi;
extern int g_borderless;
extern int g_resizeable;
extern int g_noaspect;
extern int g_novsync;
extern int g_nohwaccel;
extern int g_fullscreen_desktop;
extern int g_scanline_simulator;
extern int g_startx;
extern int g_starty;
extern int g_startw;
extern int g_starth;
extern int g_screen_depth;
extern int g_quit_sim_now;
extern int g_border_sides_refresh_needed;
extern int g_border_special_refresh_needed;
extern int g_status_refresh_needed;
extern int g_lores_colors[];
extern int g_a2vid_palette;
extern int g_installed_full_superhires_colormap;
extern char *g_status_ptrs[MAX_STATUS_LINES];
extern word32 g_a2_screen_buffer_changed;
extern word32 g_full_refresh_needed;
extern word32 g_palette_8to1624[256];
extern word32 g_a2palette_8to1624[256];
extern Kimage g_mainwin_kimage;
extern const char g_gsport_version_str[]; // version string for title bar
SDL_Window *window; // Declare a pointer
SDL_Renderer *renderer;
SDL_Texture *texture;
SDL_Texture *overlay_texture; // This is used for scanline simulation. Could be more in future (HUD).
Uint32 *overlay_pixels;
static char *g_clipboard = NULL; // clipboard variables
static size_t g_clipboard_pos = 0;
void dev_video_init_sdl();
void handle_sdl_key_event(SDL_Event event);
void check_input_events_sdl();
void handle_sdl_mouse_event(SDL_Event event);
int g_num_a2_keycodes = 0;
int a2_key_to_sdlkeycode[][3] = {
{ 0x35, SDLK_ESCAPE, 0 },
{ 0x7a, SDLK_F1, 0 },
{ 0x78, SDLK_F2, 0 },
{ 0x63, SDLK_F3, 0 },
{ 0x76, SDLK_F4, 0 },
{ 0x60, SDLK_F5, 0 },
{ 0x61, SDLK_F6, 0 },
{ 0x62, SDLK_F7, 0 },
{ 0x64, SDLK_F8, 0 },
{ 0x65, SDLK_F9, 0 },
{ 0x6d, SDLK_F10, 0 },
{ 0x67, SDLK_F11, 0 },
{ 0x6f, SDLK_F12, 0 },
{ 0x69, SDLK_F13, 0 },
{ 0x6b, SDLK_F14, 0 },
{ 0x71, SDLK_F15, 0 },
{ 0x7f, SDLK_PAUSE, 0 },
{ 0x32, SDLK_BACKQUOTE, '~' }, /* Key number 18? */
{ 0x12, SDLK_1, '!' },
{ 0x13, SDLK_2, '@' },
{ 0x14, SDLK_3, '#' },
{ 0x15, SDLK_4, '$' },
{ 0x17, SDLK_5, '%' },
{ 0x16, SDLK_6, '^' },
{ 0x1a, SDLK_7, '&' },
{ 0x1c, SDLK_8, '*' },
{ 0x19, SDLK_9, '(' },
{ 0x1d, SDLK_0, ')' },
{ 0x1b, SDLK_MINUS, SDLK_UNDERSCORE },
{ 0x18, SDLK_EQUALS, SDLK_PLUS },
{ 0x33, SDLK_BACKSPACE, 0 },
{ 0x72, SDLK_INSERT, 0 }, /* Help? XK_Help */
/* { 0x73, XK_Home, 0 }, alias XK_Home to be XK_KP_Equal! */
{ 0x74, SDLK_PAGEUP, 0 },
{ 0x47, SDLK_NUMLOCKCLEAR, 0 }, /* Clear, XK_Clear */
{ 0x51, SDLK_KP_EQUALS, 0 }, /* Note XK_Home alias! XK_Home */
{ 0x4b, SDLK_KP_DIVIDE, 0 },
{ 0x43, SDLK_KP_MULTIPLY, 0 },
{ 0x30, SDLK_TAB, 0 },
{ 0x0c, SDLK_q, 'Q' },
{ 0x0d, SDLK_w, 'W' },
{ 0x0e, SDLK_e, 'E' },
{ 0x0f, SDLK_r, 'R' },
{ 0x11, SDLK_t, 'T' },
{ 0x10, SDLK_y, 'Y' },
{ 0x20, SDLK_u, 'U' },
{ 0x22, SDLK_i, 'I' },
{ 0x1f, SDLK_o, 'O' },
{ 0x23, SDLK_p, 'P' },
{ 0x21, SDLK_RIGHTBRACKET, '{' },
{ 0x1e, SDLK_LEFTBRACKET, '}' },
{ 0x2a, SDLK_BACKSLASH, '|' }, /* backslash, bar */
{ 0x75, SDLK_DELETE, 0 },
{ 0x77, SDLK_END, 0 },
{ 0x79, SDLK_PAGEDOWN, 0 },
{ 0x59, SDLK_KP_7, SDLK_HOME },
{ 0x5b, SDLK_KP_8, SDLK_UP },
{ 0x5c, SDLK_KP_9, SDLK_PAGEUP },
{ 0x4e, SDLK_KP_MINUS, 0 },
{ 0x39, SDLK_CAPSLOCK, 0 },
{ 0x00, SDLK_a, 'A' },
{ 0x01, SDLK_s, 'S' },
{ 0x02, SDLK_d, 'D' },
{ 0x03, SDLK_f, 'F' },
{ 0x05, SDLK_g, 'G' },
{ 0x04, SDLK_h, 'H' },
{ 0x26, SDLK_j, 'J' },
{ 0x28, SDLK_k, 'K' },
{ 0x25, SDLK_l, 'L' },
{ 0x29, SDLK_SEMICOLON, SDLK_COLON },
{ 0x27, SDLK_QUOTE, SDLK_QUOTEDBL },
{ 0x24, SDLK_RETURN, 0 },
{ 0x56, SDLK_KP_4, SDLK_LEFT},
{ 0x57, SDLK_KP_5, 0 },
{ 0x58, SDLK_KP_6, SDLK_RIGHT },
{ 0x45, SDLK_KP_PLUS, 0 },
{ 0x38, SDLK_LSHIFT, SDLK_RSHIFT },
{ 0x06, SDLK_z, 'Z' },
{ 0x07, SDLK_x, 'X' },
{ 0x08, SDLK_c, 'C' },
{ 0x09, SDLK_v, 'V' },
{ 0x0b, SDLK_b, 'B' },
{ 0x2d, SDLK_n, 'N' },
{ 0x2e, SDLK_m, 'M' },
{ 0x2b, SDLK_COMMA, SDLK_LESS },
{ 0x2f, SDLK_PERIOD, SDLK_GREATER },
{ 0x2c, SDLK_SLASH, SDLK_QUESTION },
{ 0x3e, SDLK_UP, 0 },
{ 0x53, SDLK_KP_1, 0 },
{ 0x54, SDLK_KP_2, SDLK_DOWN },
{ 0x55, SDLK_KP_3, SDLK_PAGEDOWN },
{ 0x36, SDLK_RCTRL, SDLK_LCTRL },
#if defined(__APPLE__)
{ 0x3a, SDLK_LALT, SDLK_RALT }, /* Option */
{ 0x37, SDLK_LGUI, SDLK_RGUI }, /* Command */
#else
{ 0x3a, SDLK_LGUI, SDLK_RGUI }, /* Command */
{ 0x37, SDLK_LALT, SDLK_RALT }, /* Option */
#endif
{ 0x31, SDLK_SPACE, 0 },
{ 0x3b, SDLK_LEFT, 0 },
{ 0x3d, SDLK_DOWN, 0 },
{ 0x3c, SDLK_RIGHT, 0 },
{ 0x52, SDLK_KP_0, 0 },
{ 0x41, SDLK_KP_PERIOD, 0 },
{ 0x4c, SDLK_KP_ENTER, 0 },
{ -1, -1, -1 }
};
int main(int argc, char **argv) {
return gsportmain(argc, argv);
}
const char *byte_to_binary(int x) {
static char b[9];
b[0] = '\0';
int z;
for (z = 128; z > 0; z >>= 1)
{
strcat(b, ((x & z) == z) ? "1" : "0");
}
return b;
}
// Queries the Screen to see if set to Fullscreen or Not
// @return SDL_FALSE when windowed, SDL_TRUE when fullscreen
SDL_bool IsFullScreen(SDL_Window *win) {
Uint32 flags = SDL_GetWindowFlags(win);
if (flags & SDL_WINDOW_FULLSCREEN) {
return SDL_TRUE; // return SDL_TRUE if fullscreen
}
return SDL_FALSE; // Return SDL_FALSE if windowed
}
void dev_video_init() {
word32 lores_col;
// build keycode map ??
g_num_a2_keycodes = 0;
int i;
int keycode;
for(i = 0; i < 0x7f; i++) {
keycode = a2_key_to_sdlkeycode[i][0];
if(keycode < 0) {
g_num_a2_keycodes = i;
break;
} else if(keycode > 0x7f) {
glogf("a2_key_to_xsym[%d] = %02x!\n", i, keycode);
exit(2);
}
}
// This actually creates our window
dev_video_init_sdl();
// @todo DANGER DANGER. HARD CODING THESE.. there was logic for stepping values in xdriver
g_screen_depth = 24;
g_screen_mdepth =32;
video_get_kimages();
video_get_kimage(&g_mainwin_kimage, 0, g_screen_depth,g_screen_mdepth);
for(i = 0; i < 256; i++) {
//g_xcolor_a2vid_array[i].pixel = i;
lores_col = g_lores_colors[i & 0xf];
video_update_color_raw(i, lores_col);
g_a2palette_8to1624[i] = g_palette_8to1624[i];
}
}
void do_icon() {
#ifdef HAVE_ICON
//surface = SDL_CreateRGBSurfaceFrom(pixels,w,h,depth,pitch,rmask,gmask,bmask,amask);
int size = 256; // icon size
SDL_Surface *surface; // declare an SDL_Surface to be filled in with pixel data from an image file
surface = SDL_CreateRGBSurfaceFrom(icon_pixels,size,size,32,size*4,0xff000000,0x00ff0000,0x0000ff00,0x000000ff);
// The icon is attached to the window pointer
SDL_SetWindowIcon(window, surface);
// ...and the surface containing the icon pixel data is no longer required.
SDL_FreeSurface(surface);
#endif
}
// Initialize our SDL window and texture
void dev_video_init_sdl() {
SDL_Init(SDL_INIT_VIDEO); // Initialize SDL2
#if defined __APPLE__
extern void fix_mac_menu();
fix_mac_menu();
#endif
// Create an application window with the following settings:
char window_title[32];
sprintf(window_title, "GSport v%-6s", g_gsport_version_str);
int startx = SDL_WINDOWPOS_UNDEFINED;
int starty = SDL_WINDOWPOS_UNDEFINED;
if (g_startx != WINDOWPOS_UNDEFINED) { startx = g_startx; }
if (g_starty != WINDOWPOS_UNDEFINED) { starty = g_starty; }
int more_flags = 0;
// check for CLI fullscreen
if (g_fullscreen) {
more_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
}
if (g_highdpi) {
more_flags |= SDL_WINDOW_ALLOW_HIGHDPI;
}
if (g_borderless) {
more_flags |= SDL_WINDOW_BORDERLESS;
}
if (g_resizeable) {
more_flags |= SDL_WINDOW_RESIZABLE;
}
window = SDL_CreateWindow(
window_title, // window title (GSport vX.X)
startx,
starty,
g_startw, // width, in pixels
g_starth, // height, in pixels
SDL_WINDOW_OPENGL // flags - see below
| more_flags
);
// Check that the window was successfully created
if (window == NULL) {
// In the case that the window could not be made...
glogf("Could not create window: %s", SDL_GetError());
//@todo die, i guess
} else {
glog("SDL2 graphics initialized");
}
// SET WINDOW ICON
do_icon();
int renderer_hints = 0;
if (!g_novsync) {
renderer_hints |= SDL_RENDERER_PRESENTVSYNC;
}
if (!g_nohwaccel) {
renderer_hints |= SDL_RENDERER_ACCELERATED;
}
renderer = SDL_CreateRenderer(window, -1, renderer_hints);
debuginfo_renderer(renderer);
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); // make the scaled rendering look smoother.
// SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best"); // make the scaled rendering look smoother.
if (!g_noaspect) {
SDL_RenderSetLogicalSize(renderer, BASE_WINDOW_WIDTH, X_A2_WINDOW_HEIGHT);
}
texture = SDL_CreateTexture(renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
BASE_WINDOW_WIDTH, X_A2_WINDOW_HEIGHT);
// The window is open: could enter program loop here (see SDL_PollEvent())
//overlay test
overlay_texture = SDL_CreateTexture(renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
BASE_WINDOW_WIDTH,
X_A2_WINDOW_HEIGHT);
SDL_SetTextureBlendMode(overlay_texture, SDL_BLENDMODE_BLEND);
overlay_pixels = malloc(BASE_WINDOW_WIDTH*X_A2_WINDOW_HEIGHT*sizeof(Uint32));
Uint32 pixelARGB = 0x33000000; // default "low grey"
if (overlay_pixels) {
if (g_scanline_simulator > 0) {
pixelARGB = (int)(g_scanline_simulator*2.56) << 24;
}
for (int y=0; y<X_A2_WINDOW_HEIGHT; y++) {
for (int x=0; x<BASE_WINDOW_WIDTH; x++) {
if (y%2 == 1) {
overlay_pixels[(y*BASE_WINDOW_WIDTH)+x] = pixelARGB;
}
}
}
}
SDL_Rect dstrect;
dstrect.x = 0;
dstrect.y = 0;
dstrect.w = BASE_WINDOW_WIDTH;
dstrect.h = X_A2_WINDOW_HEIGHT;
int pitch = BASE_WINDOW_WIDTH;
// UPDATE A RECT OF THE APPLE II SCREEN TEXTURE
SDL_UpdateTexture(overlay_texture, &dstrect, overlay_pixels, pitch*sizeof(Uint32) );
// Turn off host mouse cursor
SDL_ShowCursor(SDL_DISABLE);
}
// Copy a rect to our SDL window
void sdl_push_kimage(Kimage *kimage_ptr, int destx, int desty, int srcx, int srcy, int width, int height) {
byte *src_ptr;
int pixel_size = 4;
src_ptr = kimage_ptr->data_ptr + (srcy * kimage_ptr->width_act + srcx) * pixel_size;
SDL_Rect dstrect;
dstrect.x = destx;
dstrect.y = desty;
dstrect.w = width;
dstrect.h = height;
int pitch = 640;
if (width < 560) {
pitch = EFF_BORDER_WIDTH;
// seems to be the correct value, but would like clarification
pitch = BORDER_WIDTH+72;
}
SDL_UpdateTexture(texture, &dstrect, src_ptr, pitch*4 );
// We now call the render step seperately in sdl_present_buffer once per frame
// SDL picks up the buffer and waits for VBLANK to send it
}
void set_refresh_needed() {
g_a2_screen_buffer_changed = -1;
g_full_refresh_needed = -1;
g_border_sides_refresh_needed = 1;
g_border_special_refresh_needed = 1;
g_status_refresh_needed = 1;
}
void x_get_kimage(Kimage *kimage_ptr) {
byte *data;
int width;
int height;
int depth;
width = kimage_ptr->width_req;
height = kimage_ptr->height;
depth = kimage_ptr->depth;
// this might be too big!!! I had it at depth/3 but it segfaults
data = malloc(width*height*(depth/4));
kimage_ptr->data_ptr = data;
}
void check_input_events() {
check_input_events_sdl();
}
void check_input_events_sdl() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
/* Check all window events (mostly for Fullscreen) */
if (event.type == SDL_WINDOWEVENT) {
set_refresh_needed();
}
switch( event.type ) {
case SDL_KEYDOWN:
case SDL_KEYUP:
handle_sdl_key_event(event);
break;
case SDL_MOUSEMOTION:
case SDL_MOUSEBUTTONUP:
case SDL_MOUSEBUTTONDOWN:
handle_sdl_mouse_event(event);
break;
case SDL_QUIT:
xdriver_end();
my_exit(1);
break;
case SDL_DROPFILE:
{
char *file = event.drop.file;
cfg_inspect_maybe_insert_file(file, 0);
SDL_free(file);
}
break;
default:
break;
}
}
}
int sdl_keysym_to_a2code(int keysym, int is_up) {
int i;
if(keysym == 0) {
return -1;
}
if((keysym == SDLK_LSHIFT) || (keysym == SDLK_RSHIFT)) {
if(is_up) {
kb_shift_control_state &= ~ShiftMask;
} else {
kb_shift_control_state |= ShiftMask;
}
}
if(keysym == SDLK_CAPSLOCK) {
if(is_up) {
kb_shift_control_state &= ~LockMask;
} else {
kb_shift_control_state |= LockMask;
}
}
if((keysym == SDLK_LCTRL) || (keysym == SDLK_RCTRL)) {
if(is_up) {
kb_shift_control_state &= ~ControlMask;
} else {
kb_shift_control_state |= ControlMask;
}
}
/* Look up Apple 2 keycode */
for(i = g_num_a2_keycodes - 1; i >= 0; i--) {
if((keysym == a2_key_to_sdlkeycode[i][1]) ||
(keysym == a2_key_to_sdlkeycode[i][2])) {
return a2_key_to_sdlkeycode[i][0];
}
}
return -1;
}
void handle_sdl_key_event(SDL_Event event) {
int state_xor;
int state = 0;
int is_up;
int mod = event.key.keysym.mod;
// simulate xmask style here
// @todo: this can probably all be refactored now that X is gone
//state = state & (ControlMask | LockMask | ShiftMask);
// when mod key is first press, comes as event, otherwise just a modifier
if( mod & KMOD_LCTRL || mod & KMOD_RCTRL ||
event.type == (SDL_KEYDOWN && (event.key.keysym.sym == SDLK_LCTRL || event.key.keysym.sym == SDLK_RCTRL))) {
state = state | ControlMask;
}
if( (mod & KMOD_LSHIFT) || (mod & KMOD_RSHIFT) ||
event.type == (SDL_KEYDOWN && (event.key.keysym.sym == SDLK_LSHIFT || event.key.keysym.sym == SDLK_RSHIFT))) {
state = state | ShiftMask;
}
if( mod & KMOD_CAPS) {
state = state | LockMask;
}
state_xor = kb_shift_control_state ^ state;
is_up = 0;
if(state_xor & ControlMask) {
is_up = ((state & ControlMask) == 0);
adb_physical_key_update(0x36, is_up);
}
if(state_xor & LockMask) {
is_up = ((state & LockMask) == 0);
adb_physical_key_update(0x39, is_up);
}
if(state_xor & ShiftMask) {
is_up = ((state & ShiftMask) == 0);
adb_physical_key_update(0x38, is_up);
}
kb_shift_control_state = state;
is_up = 0;
int a2code;
if (event.type == SDL_KEYUP) {
is_up = 1;
}
switch( event.key.keysym.sym ) {
case SDLK_F11:
if (kb_shift_control_state & ShiftMask) { // SHIFT+F11
if (!is_up) {
if (g_scanline_simulator) {
glog("Enable scanline simulator");
g_scanline_simulator = 0;
} else {
glog("Disable scanline simulator");
g_scanline_simulator = 1;
}
set_refresh_needed(); // make sure user sees it right away
}
} else {
if (!is_up) {
if (!IsFullScreen(window)) {
glog("Enable fullscreen");
SDL_SetWindowGrab(window, true);
SDL_SetRelativeMouseMode(true);
Uint32 fullmode = g_fullscreen_desktop ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN;
SDL_SetWindowFullscreen(window, fullmode);
} else {
glog("Disable fullscreen");
SDL_SetWindowFullscreen(window, 0);
SDL_SetWindowGrab(window, false);
SDL_SetWindowSize(window, g_startw, g_starth);
SDL_SetRelativeMouseMode(false);
}
}
}
break;
default:
a2code = sdl_keysym_to_a2code(event.key.keysym.sym, is_up);
if(a2code >= 0) {
adb_physical_key_update(a2code, is_up);
}
break;
}
}
void handle_sdl_mouse_event(SDL_Event event) {
int x, y;
int scaledmotion = 0;
if (scaledmotion) {
x = event.motion.x * A2_WINDOW_WIDTH / g_startw;
y = event.motion.y * A2_WINDOW_HEIGHT / g_starth;
} else {
x = event.motion.x - BASE_MARGIN_LEFT;
y = event.motion.y - BASE_MARGIN_TOP;
}
switch (event.type) {
case SDL_MOUSEMOTION:
update_mouse_w_delta(x, y, 0, 0, event.motion.xrel, event.motion.yrel);
break;
case SDL_MOUSEBUTTONUP:
update_mouse_w_delta(x, y, 0, event.motion.state &7, 0, 0);
break;
case SDL_MOUSEBUTTONDOWN:
update_mouse_w_delta(x, y, event.motion.state &7, event.motion.state &7 , 0, 0);
break;
}
}
void x_push_kimage(Kimage *kimage_ptr, int destx, int desty, int srcx, int srcy, int width, int height) {
sdl_push_kimage(kimage_ptr, destx, desty, srcx, srcy, width, height);
}
// called by src/sim65816.c
void x_dialog_create_gsport_conf(const char *str) {
// Just write the config file already...
config_write_config_gsport_file();
}
void x_full_screen(int do_full) {
if (do_full) {
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
} else {
SDL_SetWindowFullscreen(window, 0);
SDL_SetWindowSize(window, BASE_WINDOW_WIDTH, X_A2_WINDOW_HEIGHT);
}
}
int file_exists(char *fname) {
if( access( fname, F_OK ) != -1 ) {
return 1; // file exists
} else {
return 0; // file does not exist
}
}
// This tries to determine the next screenshot name.
// It uses the config name as the basename.
void make_next_screenshot_filename() {
char filepart[256];
char filename[256];
int available_filename = 0;
while (!available_filename) {
char *bn = basename(g_config_gsport_name);
// get location of '.'
char *dotptr = strchr(bn, '.');
int index = dotptr - bn;
strncpy(filepart, bn, index);
filepart[index] = '\0'; //terminator
// handle trailing "/" vs no "/"
char tchar = g_config_gsport_screenshot_dir[strlen(g_config_gsport_screenshot_dir) - 1];
if (tchar == '/') {
sprintf(filename, "%s%s%04d.png",g_config_gsport_screenshot_dir,filepart,screenshot_index);
} else {
sprintf(filename, "%s/%s%04d.png",g_config_gsport_screenshot_dir,filepart,screenshot_index);
}
screenshot_index++;
if (!file_exists(filename)) {
available_filename = 1;
}
}
strcpy(screenshot_filename, filename);
}
// @todo: some error with writing data direct to png. output is empty/transparent?
// workaround is this horrible hack of saving the bmp -> load bmp -> save png
void x_take_screenshot() {
make_next_screenshot_filename();
glogf("Taking screenshot - %s", screenshot_filename);
SDL_Surface *sshot = SDL_CreateRGBSurface(0, BASE_WINDOW_WIDTH, X_A2_WINDOW_HEIGHT, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
SDL_LockSurface(sshot);
int read = SDL_RenderReadPixels(renderer, NULL, SDL_PIXELFORMAT_ARGB8888, sshot->pixels, sshot->pitch);
if (read != 0) {
printf("READPXL FAIL!\n%s\n", SDL_GetError());
}
SDL_SaveBMP(sshot, "screenshot.bmp");
SDL_UnlockSurface(sshot);
SDL_FreeSurface(sshot);
SDL_Surface *s = SDL_CreateRGBSurface(0, BASE_WINDOW_WIDTH, X_A2_WINDOW_HEIGHT, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
if (s) {
SDL_Surface * image = SDL_LoadBMP("screenshot.bmp");
IMG_SavePNG(image, screenshot_filename);
SDL_FreeSurface(image);
}
SDL_FreeSurface(s);
}
void clipboard_paste(void) {
char *cp;
if (g_clipboard) {
free(g_clipboard);
g_clipboard = NULL;
g_clipboard_pos = 0;
}
cp = SDL_GetClipboardText();
if (!cp) return;
g_clipboard = strdup(cp);
g_clipboard_pos = 0;
SDL_free(cp);
}
int clipboard_get_char(void) {
char c;
if (!g_clipboard)
return 0;
/* skip utf-8 characters. */
do {
c = g_clipboard[g_clipboard_pos++];
} while (c & 0x80);
/* windows -- skip the \n in \r\n. */
if (c == '\r' && g_clipboard[g_clipboard_pos] == '\n')
g_clipboard_pos++;
/* everybody else -- convert \n to \r */
if (c == '\n') c = '\r';
if (c == 0) {
free(g_clipboard);
g_clipboard = NULL;
g_clipboard_pos = 0;
return 0;
}
return c | 0x80;
}
int x_show_alert(int is_fatal, const char *str) {
if (str) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "GSport Alert", str, NULL);
}
return 0;
}
void xdriver_end() {
SDL_DestroyWindow(window);
iwm_shut();
// Clean up
SDL_Quit();
}
// This will help us determine how well and which drivers are supported on
// different SDL platforms
void debuginfo_renderer(SDL_Renderer *r) {
int n = SDL_GetNumRenderDrivers();
glogf("**--- SDL DEBUG ------ (%i) drivers", n);
for(int i = 0; i < n; i++) {
SDL_RendererInfo info;
SDL_GetRenderDriverInfo(i, &info);
glogf("* '%s'", info.name);
}
SDL_RendererInfo info = {0};
if (SDL_GetRendererInfo(r,&info) == 0) {
glogf("* SDL_RENDERER_SOFTWARE: %d", (info.flags & SDL_RENDERER_SOFTWARE) > 0 );
glogf("* SDL_RENDERER_ACCELERATED: %d", (info.flags & SDL_RENDERER_ACCELERATED) > 0 );
glogf("* SDL_RENDERER_PRESENTVSYNC: %d", (info.flags & SDL_RENDERER_PRESENTVSYNC) > 0 );
glogf("* SDL_RENDERER_TARGETTEXTURE: %d", (info.flags & SDL_RENDERER_TARGETTEXTURE) > 0 );
glogf("* active renderer -> '%s'", info.name);
} else {
glog("NO Renderinfo");
}
}
// as this is triggered when new images were pushed to backing buffer,
// this is when we want to update frames.
// putting it in x_push_done means we can skip frames that weren't changed.
// the emulator will still run at whatever specified speed. but this way,
// when running in faster 8mhz/unlimited modes, it won't be slowed down by
// forcing every draw at 60FPS sync.
void x_push_done() {
void sdl_present_buffer();
sdl_present_buffer();
}
void sdl_present_buffer() {
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
if (g_scanline_simulator) {
SDL_RenderCopy(renderer, overlay_texture, NULL, NULL);
}
SDL_RenderPresent(renderer);
if (g_screenshot_requested) {
x_take_screenshot();
g_screenshot_requested = 0;
}
}
void x_grabmouse() {
SDL_SetWindowGrab(window, g_grabmouse);
SDL_SetRelativeMouseMode(g_grabmouse);
}
// BELOW ARE FUNCTIONS THAT ARE EITHER UNIMPLEMENTED, OR AR NOT RELEVANT TO
// THIS DRIVER.
// called by src/sim65816.c
void get_ximage(Kimage *kimage_ptr) { }
void x_toggle_status_lines() { }
void x_redraw_status_lines() { }
void x_hide_pointer(int do_hide) { }
void x_auto_repeat_on(int must) { }
void x_auto_repeat_off(int must) { }
void x_release_kimage(Kimage* kimage_ptr) { }
int x_calc_ratio(float x,float y) { return 1; }
void x_set_mask_and_shift(word32 x_mask, word32 *mask_ptr, int *shift_left_ptr, int *shift_right_ptr) { return; }
void x_update_color(int col_num, int red, int green, int blue, word32 rgb) { }
void x_update_physical_colormap() { }
void show_xcolor_array() { }

222
src/sdl2snd_driver.c Normal file
View File

@ -0,0 +1,222 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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 "SDL.h"
#include "defc.h"
#include "glog.h"
#include "sound.h"
#include <assert.h>
extern word32 *g_sound_shm_addr;
extern int g_sound_shm_pos;
extern int g_audio_enable;
extern int g_preferred_rate;
static byte *playbuf = 0;
static int g_playbuf_buffered = 0;
static SDL_AudioSpec spec;
static int snd_buf;
static int snd_write; /* write position into playbuf */
static /* volatile */ int snd_read = 0;
static int g_sound_paused;
static int g_zeroes_buffered;
static int g_zeroes_seen;
// newer SDL allows you to specify devices. for now, we use what it gives us,
// but this can be made configurable in the future
SDL_AudioDeviceID dev = 0;
void sdlsnd_init(word32 *shmaddr) {
if (SDL_Init(SDL_INIT_AUDIO) < 0) {
glog("Could not initialize SDL2 audio");
g_audio_enable = 0;
} else {
glog("SDL2 audio initialized");
}
child_sound_loop(-1, -1, shmaddr);
return;
}
void sound_write_sdl(int real_samps, int size) {
#ifdef HAVE_SDL
int shm_read;
if (real_samps) {
shm_read = (g_sound_shm_pos - size + SOUND_SHM_SAMP_SIZE)%SOUND_SHM_SAMP_SIZE;
SDL_LockAudioDevice(dev);
while(size > 0) {
if(g_playbuf_buffered >= snd_buf) {
printf("sound_write_sdl failed @%d, %d buffered, %d samples skipped\n",snd_write,g_playbuf_buffered, size);
shm_read += size;
shm_read %= SOUND_SHM_SAMP_SIZE;
size = 0;
} else {
((word32*)playbuf)[snd_write/SAMPLE_CHAN_SIZE] = g_sound_shm_addr[shm_read];
shm_read++;
if (shm_read >= SOUND_SHM_SAMP_SIZE)
shm_read = 0;
snd_write += SAMPLE_CHAN_SIZE;
if (snd_write >= snd_buf)
snd_write = 0;
size--;
g_playbuf_buffered += SAMPLE_CHAN_SIZE;
}
}
assert((snd_buf+snd_write - snd_read)%snd_buf == g_playbuf_buffered%snd_buf);
assert(g_sound_shm_pos == shm_read);
SDL_UnlockAudioDevice(dev);
}
if(g_sound_paused && (g_playbuf_buffered > 0)) {
glogf("Unpausing sound, %d buffered",g_playbuf_buffered);
g_sound_paused = 0;
SDL_PauseAudioDevice(dev, 0);
}
if(!g_sound_paused && (g_playbuf_buffered <= 0)) {
glog("Pausing sound");
g_sound_paused = 1;
SDL_PauseAudioDevice(dev, 1);
}
#endif
}
#ifdef HAVE_SDL
/* Callback for sound */
static void _snd_callback(void* userdata, Uint8 *stream, int len) {
int i;
/* Slurp off the play buffer */
assert((snd_buf+snd_write - snd_read)%snd_buf == g_playbuf_buffered%snd_buf);
/*printf("slurp %d, %d buffered\n",len, g_playbuf_buffered);*/
for(i = 0; i < len; ++i) {
if(g_playbuf_buffered <= 0) {
stream[i] = 0;
} else {
stream[i] = playbuf[snd_read++];
if(snd_read == snd_buf)
snd_read = 0;
g_playbuf_buffered--;
}
}
#if 0
if (g_playbuf_buffered <= 0) {
printf("snd_callback: buffer empty, Pausing sound\n");
g_sound_paused = 1;
SDL_PauseAudio(1);
}
#endif
//printf("end slurp %d, %d buffered\n",len, g_playbuf_buffered);
}
#endif
long sound_init_device_sdl() {
#ifdef HAVE_SDL
long rate;
SDL_AudioSpec wanted;
if (SDL_Init(SDL_INIT_AUDIO) < 0) {
glogf("SDL2 Couldn't init SDL_INIT_AUDIO: %s!", SDL_GetError());
return 0;
}
/* Set the desired format */
wanted.freq = g_preferred_rate;
wanted.format = AUDIO_S16SYS;
wanted.channels = NUM_CHANNELS;
wanted.samples = 512;
wanted.callback = _snd_callback;
wanted.userdata = NULL;
/* Open audio, and get the real spec */
dev = SDL_OpenAudioDevice(NULL, 0, &wanted, &spec, 0);
if (dev == 0) {
glogf("SDL2 Couldn't open audio: %s!", SDL_GetError());
SDL_QuitSubSystem(SDL_INIT_AUDIO);
return 0;
} else {
glogf("SDL2 opened audio device: %d", dev);
}
/* Check everything */
if(spec.channels != wanted.channels) {
glogf("SDL2 Warning, couldn't get stereo audio format!");
//goto snd_error;
}
if(spec.format != wanted.format) {
glog("SDL2 Warning, couldn't get a supported audio format!");
glogf("SDL2 wanted %X, got %X",wanted.format,spec.format);
//goto snd_error;
}
if(spec.freq != wanted.freq) {
glogf("SDL2 wanted rate = %d, got rate = %d", wanted.freq, spec.freq);
}
/* Set things as they really are */
rate = spec.freq;
snd_buf = SOUND_SHM_SAMP_SIZE*SAMPLE_CHAN_SIZE;
playbuf = (byte*) malloc(snd_buf);
if (!playbuf)
goto snd_error;
g_playbuf_buffered = 0;
glogf("SDL2 sound shared memory size=%d", SOUND_SHM_SAMP_SIZE * SAMPLE_CHAN_SIZE);
g_sound_shm_addr = malloc(SOUND_SHM_SAMP_SIZE * SAMPLE_CHAN_SIZE);
memset(g_sound_shm_addr,0,SOUND_SHM_SAMP_SIZE * SAMPLE_CHAN_SIZE);
/* It's all good! */
g_zeroes_buffered = 0;
g_zeroes_seen = 0;
/* Let's start playing sound */
g_sound_paused = 0;
SDL_PauseAudioDevice(dev, 0);
set_audio_rate(rate);
return rate;
snd_error:
/* Oops! Something bad happened, cleanup. */
SDL_CloseAudioDevice(dev);
SDL_QuitSubSystem(SDL_INIT_AUDIO);
if(playbuf)
free((void*)playbuf);
playbuf = 0;
snd_buf = 0;
return 0;
#else
return 0;
#endif
}
void sound_shutdown_sdl() {
#ifdef HAVE_SDL
SDL_CloseAudioDevice(dev);
if(playbuf)
free((void*)playbuf);
playbuf = 0;
#endif
}

File diff suppressed because it is too large Load Diff

View File

@ -1,440 +1,425 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2012 by GSport contributors
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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 "defc.h"
#include "sound.h"
#ifdef HPUX
# include <sys/audio.h>
#endif
#if defined(__linux__) || defined(OSS)
# include <sys/soundcard.h>
#endif
#ifndef WIN_SOUND /* Workaround - gcc in cygwin wasn't defining _WIN32 */
# include <sys/socket.h>
# include <netinet/in.h>
#endif
#ifndef UNDER_CE
#include <errno.h>
#endif
extern int Verbose;
extern int g_audio_rate;
int g_preferred_rate = 48000;
int g_audio_socket = -1;
int g_bytes_written = 0;
#define ZERO_BUF_SIZE 2048
word32 g_snd_zero_buf[ZERO_BUF_SIZE];
#define ZERO_PAUSE_SAFETY_SAMPS (g_audio_rate >> 5)
#define ZERO_PAUSE_NUM_SAMPS (4*g_audio_rate)
int g_zeroes_buffered = 0;
int g_zeroes_seen = 0;
int g_sound_paused = 0;
int g_childsnd_vbl = 0;
int g_childsnd_pos = 0;
word32 *g_childsnd_shm_addr = 0;
void child_sound_init_linux();
void child_sound_init_hpdev();
void child_sound_initWIN_SOUND();
void child_sound_init_mac();
void
reliable_buf_write(word32 *shm_addr, int pos, int size)
{
byte *ptr;
int ret;
if(size < 1 || pos < 0 || pos > SOUND_SHM_SAMP_SIZE ||
size > SOUND_SHM_SAMP_SIZE ||
(pos + size) > SOUND_SHM_SAMP_SIZE) {
printf("reliable_buf_write: pos: %04x, size: %04x\n",
pos, size);
exit(1);
}
ptr = (byte *)&(shm_addr[pos]);
size = size * 4;
while(size > 0) {
#ifdef WIN_SOUND
ret = win32_send_audio(ptr, size);
#else
# ifdef MAC
ret = mac_send_audio(ptr, size);
# else
ret = write(g_audio_socket, ptr, size);
# endif
#endif
if(ret < 0) {
printf("audio write, errno: %d\n", errno);
exit(1);
}
size = size - ret;
ptr += ret;
g_bytes_written += ret;
}
}
void
reliable_zero_write(int amt)
{
int len;
while(amt > 0) {
len = MIN(amt, ZERO_BUF_SIZE);
reliable_buf_write(g_snd_zero_buf, 0, len);
amt -= len;
}
}
void
child_sound_loop(int read_fd, int write_fd, word32 *shm_addr)
{
#ifdef HPUX
long status_return;
#endif
word32 tmp;
int ret;
doc_printf("Child pipe fd: %d\n", read_fd);
g_audio_rate = g_preferred_rate;
g_zeroes_buffered = 0;
g_zeroes_seen = 0;
g_sound_paused = 0;
g_childsnd_pos = 0;
g_childsnd_vbl = 0;
g_childsnd_shm_addr = shm_addr;
#ifdef HPUX
child_sound_init_hpdev();
#endif
#if defined(__linux__) || defined(OSS)
child_sound_init_linux();
#endif
#ifdef WIN_SOUND
child_sound_init_win32();
return;
#endif
#ifdef MAC
child_sound_init_mac();
return;
#endif
tmp = g_audio_rate;
ret = write(write_fd, &tmp, 4);
if(ret != 4) {
printf("Unable to send back audio rate to parent\n");
printf("ret: %d fd: %d, errno: %d\n", ret, write_fd, errno);
exit(1);
}
printf("Wrote to fd %d the audio rate\n", write_fd);
close(write_fd);
while(1) {
errno = 0;
ret = read(read_fd, (char*)&tmp, 4);
if(ret <= 0) {
printf("child dying from ret: %d, errno: %d\n",
ret, errno);
break;
}
child_sound_playit(tmp);
}
#ifdef HPUX
ioctl(g_audio_socket, AUDIO_DRAIN, 0);
#endif
close(g_audio_socket);
exit(0);
}
void
child_sound_playit(word32 tmp)
{
int size;
size = tmp & 0xffffff;
//printf("child_sound_playit: %08x\n", tmp);
if((tmp >> 24) == 0xa2) {
/* play sound here */
#if 0
g_childsnd_pos += g_zeroes_buffered;
while(g_childsnd_pos >= SOUND_SHM_SAMP_SIZE) {
g_childsnd_pos -= SOUND_SHM_SAMP_SIZE;
}
#endif
if(g_zeroes_buffered) {
reliable_zero_write(g_zeroes_buffered);
}
g_zeroes_buffered = 0;
g_zeroes_seen = 0;
if((size + g_childsnd_pos) > SOUND_SHM_SAMP_SIZE) {
reliable_buf_write(g_childsnd_shm_addr, g_childsnd_pos,
SOUND_SHM_SAMP_SIZE - g_childsnd_pos);
size = (g_childsnd_pos + size) - SOUND_SHM_SAMP_SIZE;
g_childsnd_pos = 0;
}
reliable_buf_write(g_childsnd_shm_addr, g_childsnd_pos, size);
if(g_sound_paused) {
printf("Unpausing sound, zb: %d\n", g_zeroes_buffered);
g_sound_paused = 0;
}
} else if((tmp >> 24) == 0xa1) {
if(g_sound_paused) {
if(g_zeroes_buffered < ZERO_PAUSE_SAFETY_SAMPS) {
g_zeroes_buffered += size;
}
} else {
/* not paused, send it through */
g_zeroes_seen += size;
reliable_zero_write(size);
if(g_zeroes_seen >= ZERO_PAUSE_NUM_SAMPS) {
printf("Pausing sound\n");
g_sound_paused = 1;
}
}
} else {
printf("tmp received bad: %08x\n", tmp);
exit(3);
}
g_childsnd_pos += size;
while(g_childsnd_pos >= SOUND_SHM_SAMP_SIZE) {
g_childsnd_pos -= SOUND_SHM_SAMP_SIZE;
}
g_childsnd_vbl++;
if(g_childsnd_vbl >= 60) {
g_childsnd_vbl = 0;
#if 0
printf("sound bytes written: %06x\n", g_bytes_written);
printf("Sample samples[0]: %08x %08x %08x %08x\n",
g_childsnd_shm_addr[0], g_childsnd_shm_addr[1],
g_childsnd_shm_addr[2], g_childsnd_shm_addr[3]);
printf("Sample samples[100]: %08x %08x %08x %08x\n",
g_childsnd_shm_addr[100], g_childsnd_shm_addr[101],
g_childsnd_shm_addr[102], g_childsnd_shm_addr[103]);
#endif
g_bytes_written = 0;
}
}
#ifdef HPUX
void
child_sound_init_hpdev()
{
struct audio_describe audio_descr;
int output_channel;
char *str;
int speaker;
int ret;
int i;
g_audio_socket = open("/dev/audio", O_WRONLY, 0);
if(g_audio_socket < 0) {
printf("open /dev/audio failed, ret: %d, errno:%d\n",
g_audio_socket, errno);
exit(1);
}
ret = ioctl(g_audio_socket, AUDIO_DESCRIBE, &audio_descr);
if(ret < 0) {
printf("ioctl AUDIO_DESCRIBE failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
for(i = 0; i < audio_descr.nrates; i++) {
printf("Audio rate[%d] = %d\n", i,
audio_descr.sample_rate[i]);
}
ret = ioctl(g_audio_socket, AUDIO_SET_DATA_FORMAT,
AUDIO_FORMAT_LINEAR16BIT);
if(ret < 0) {
printf("ioctl AUDIO_SET_DATA_FORMAT failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
ret = ioctl(g_audio_socket, AUDIO_SET_CHANNELS, NUM_CHANNELS);
if(ret < 0) {
printf("ioctl AUDIO_SET_CHANNELS failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
ret = ioctl(g_audio_socket, AUDIO_SET_TXBUFSIZE, 16*1024);
if(ret < 0) {
printf("ioctl AUDIO_SET_TXBUFSIZE failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
ret = ioctl(g_audio_socket, AUDIO_SET_SAMPLE_RATE, g_audio_rate);
if(ret < 0) {
printf("ioctl AUDIO_SET_SAMPLE_RATE failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
ret = ioctl(g_audio_socket, AUDIO_GET_OUTPUT, &output_channel);
if(ret < 0) {
printf("ioctl AUDIO_GET_OUTPUT failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
speaker = 1;
str = getenv("SPEAKER");
if(str) {
if(str[0] != 'i' && str[0] != 'I') {
speaker = 0;
}
}
if(speaker) {
printf("Sending sound to internal speaker\n");
output_channel |= AUDIO_OUT_SPEAKER;
} else {
printf("Sending sound to external jack\n");
output_channel &= (~AUDIO_OUT_SPEAKER);
output_channel |= AUDIO_OUT_HEADPHONE;
}
ret = ioctl(g_audio_socket, AUDIO_SET_OUTPUT, output_channel);
if(ret < 0) {
printf("ioctl AUDIO_SET_OUTPUT failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
}
#endif /* HPUX */
#if defined(__linux__) || defined(OSS)
void
child_sound_init_linux()
{
int stereo;
int sample_size;
int rate;
int fragment;
int fmt;
int ret;
g_audio_socket = open("/dev/dsp", O_WRONLY, 0);
if(g_audio_socket < 0) {
printf("open /dev/dsp failed, ret: %d, errno:%d\n",
g_audio_socket, errno);
exit(1);
}
fragment = 0x00200009;
#if 0
ret = ioctl(g_audio_socket, SNDCTL_DSP_SETFRAGMENT, &fragment);
if(ret < 0) {
printf("ioctl SETFRAGEMNT failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
#endif
sample_size = 16;
ret = ioctl(g_audio_socket, SNDCTL_DSP_SAMPLESIZE, &sample_size);
if(ret < 0) {
printf("ioctl SNDCTL_DSP_SAMPLESIZE failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
#if defined(GSPORT_LITTLE_ENDIAN) || defined (__LITTLE_ENDIAN__) // OSX needs to calculate endianness mid-compilation, can't be passed on compile command
fmt = AFMT_S16_LE;
#else
fmt = AFMT_S16_BE;
#endif
ret = ioctl(g_audio_socket, SNDCTL_DSP_SETFMT, &fmt);
if(ret < 0) {
printf("ioctl SNDCTL_DSP_SETFMT failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
stereo = 1;
ret = ioctl(g_audio_socket, SNDCTL_DSP_STEREO, &stereo);
if(ret < 0) {
printf("ioctl SNDCTL_DSP_STEREO failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
rate = g_audio_rate;
ret = ioctl(g_audio_socket, SNDCTL_DSP_SPEED, &rate);
if(ret < 0) {
printf("ioctl SNDCTL_DSP_SPEED failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
if(ret > 0) {
rate = ret; /* rate is returned value */
}
if(rate < 8000) {
printf("Audio rate of %d which is < 8000!\n", rate);
exit(1);
}
g_audio_rate = rate;
printf("Sound initialized\n");
}
#endif
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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 "defc.h"
#include "sound.h"
#include "glog.h"
#ifdef HPUX
# include <sys/audio.h>
#endif
#ifdef HAVE_SDL
# include "SDL.h"
long sound_init_device_sdl();
#endif
#if defined(__linux__) || defined(OSS)
# include <sys/soundcard.h>
#endif
#ifndef WIN_SOUND /* Workaround - gcc in cygwin wasn't defining _WIN32 */
# include <sys/socket.h>
# include <netinet/in.h>
#endif
#ifndef UNDER_CE
#include <errno.h>
#endif
extern int Verbose;
extern int g_audio_rate;
int g_preferred_rate = 48000;
int g_audio_socket = -1;
int g_bytes_written = 0;
#define ZERO_BUF_SIZE 2048
word32 g_snd_zero_buf[ZERO_BUF_SIZE];
#define ZERO_PAUSE_SAFETY_SAMPS (g_audio_rate >> 5)
#define ZERO_PAUSE_NUM_SAMPS (4*g_audio_rate)
int g_zeroes_buffered = 0;
int g_zeroes_seen = 0;
int g_sound_paused = 0;
int g_childsnd_vbl = 0;
int g_childsnd_pos = 0;
word32 *g_childsnd_shm_addr = 0;
void child_sound_init_linux();
void child_sound_init_hpdev();
void child_sound_initWIN_SOUND();
void child_sound_init_mac();
void child_sound_init_sdl();
long sound_init_device_sdl();
void reliable_buf_write(word32 *shm_addr, int pos, int size) {
byte *ptr;
int ret = 0;
if(size < 1 || pos < 0 || pos > SOUND_SHM_SAMP_SIZE ||
size > SOUND_SHM_SAMP_SIZE ||
(pos + size) > SOUND_SHM_SAMP_SIZE) {
printf("reliable_buf_write: pos: %04x, size: %04x\n", pos, size);
exit(1);
}
ptr = (byte *)&(shm_addr[pos]);
size = size * 4;
while(size > 0) {
#if defined(HAVE_SDL)
//ret = sdl_send_audio(ptr, size);
#elif defined(WIN_SOUND)
ret = win32_send_audio(ptr, size);
#elif defined(MAC) && !defined(HAVE_SDL)
ret = mac_send_audio(ptr, size);
#else
ret = write(g_audio_socket, ptr, size);
#endif
if(ret < 0) {
printf("audio write, errno: %d\n", errno);
exit(1);
}
size = size - ret;
ptr += ret;
g_bytes_written += ret;
}
}
void reliable_zero_write(int amt) {
int len;
while(amt > 0) {
len = MIN(amt, ZERO_BUF_SIZE);
reliable_buf_write(g_snd_zero_buf, 0, len);
amt -= len;
}
}
void child_sound_loop(int read_fd, int write_fd, word32 *shm_addr) {
word32 tmp;
int ret;
g_audio_rate = g_preferred_rate;
g_zeroes_buffered = 0;
g_zeroes_seen = 0;
g_sound_paused = 0;
g_childsnd_pos = 0;
g_childsnd_vbl = 0;
g_childsnd_shm_addr = shm_addr;
#if defined(HAVE_SDL)
//child_sound_init_sdl();
sound_init_device_sdl(); // ignores long return value of sample rate
return;
#elif defined(__linux__) || defined(OSS)
child_sound_init_linux();
#elif HPUX
child_sound_init_hpdev();
#elif WIN_SOUND
child_sound_init_win32();
return;
#elif defined(MAC) && !defined(HAVE_SDL)
child_sound_init_mac();
return;
#endif
doc_printf("Child pipe fd: %d\n", read_fd);
tmp = g_audio_rate;
ret = write(write_fd, &tmp, 4);
if(ret != 4) {
printf("Unable to send back audio rate to parent\n");
printf("ret: %d fd: %d, errno: %d\n", ret, write_fd, errno);
exit(1);
}
printf("Wrote to fd %d the audio rate\n", write_fd);
close(write_fd);
while(1) {
errno = 0;
ret = read(read_fd, (char*)&tmp, 4);
if(ret <= 0) {
printf("child dying from ret: %d, errno: %d\n",
ret, errno);
break;
}
child_sound_playit(tmp);
}
#ifdef HPUX
ioctl(g_audio_socket, AUDIO_DRAIN, 0);
#endif
close(g_audio_socket);
exit(0);
}
// called by sound.c:send_sound()
void child_sound_playit(word32 tmp) {
int size;
size = tmp & 0xffffff;
if((tmp >> 24) == 0xa2) {
/* play sound here */
#if 0
g_childsnd_pos += g_zeroes_buffered;
while(g_childsnd_pos >= SOUND_SHM_SAMP_SIZE) {
g_childsnd_pos -= SOUND_SHM_SAMP_SIZE;
}
#endif
if(g_zeroes_buffered) {
reliable_zero_write(g_zeroes_buffered);
}
g_zeroes_buffered = 0;
g_zeroes_seen = 0;
// only write up to end of buffer
if((size + g_childsnd_pos) > SOUND_SHM_SAMP_SIZE) {
reliable_buf_write(g_childsnd_shm_addr, g_childsnd_pos,
SOUND_SHM_SAMP_SIZE - g_childsnd_pos);
size = (g_childsnd_pos + size) - SOUND_SHM_SAMP_SIZE;
g_childsnd_pos = 0;
}
reliable_buf_write(g_childsnd_shm_addr, g_childsnd_pos, size);
if(g_sound_paused) {
glogf("Unpausing sound, zb: %d\n", g_zeroes_buffered);
g_sound_paused = 0;
}
} else if((tmp >> 24) == 0xa1) {
if(g_sound_paused) {
if(g_zeroes_buffered < ZERO_PAUSE_SAFETY_SAMPS) {
g_zeroes_buffered += size;
}
} else {
/* not paused, send it through */
g_zeroes_seen += size;
reliable_zero_write(size);
if(g_zeroes_seen >= ZERO_PAUSE_NUM_SAMPS) {
glog("Pausing sound");
g_sound_paused = 1;
}
}
} else {
printf("tmp received bad: %08x\n", tmp);
exit(3);
}
g_childsnd_pos += size;
while(g_childsnd_pos >= SOUND_SHM_SAMP_SIZE) {
g_childsnd_pos -= SOUND_SHM_SAMP_SIZE;
}
g_childsnd_vbl++;
if(g_childsnd_vbl >= 60) {
g_childsnd_vbl = 0;
g_bytes_written = 0;
}
}
#ifdef HPUX
void child_sound_init_hpdev() {
struct audio_describe audio_descr;
int output_channel;
char *str;
int speaker;
int ret;
int i;
g_audio_socket = open("/dev/audio", O_WRONLY, 0);
if(g_audio_socket < 0) {
printf("open /dev/audio failed, ret: %d, errno:%d\n",
g_audio_socket, errno);
exit(1);
}
ret = ioctl(g_audio_socket, AUDIO_DESCRIBE, &audio_descr);
if(ret < 0) {
printf("ioctl AUDIO_DESCRIBE failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
for(i = 0; i < audio_descr.nrates; i++) {
printf("Audio rate[%d] = %d\n", i,
audio_descr.sample_rate[i]);
}
ret = ioctl(g_audio_socket, AUDIO_SET_DATA_FORMAT,
AUDIO_FORMAT_LINEAR16BIT);
if(ret < 0) {
printf("ioctl AUDIO_SET_DATA_FORMAT failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
ret = ioctl(g_audio_socket, AUDIO_SET_CHANNELS, NUM_CHANNELS);
if(ret < 0) {
printf("ioctl AUDIO_SET_CHANNELS failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
ret = ioctl(g_audio_socket, AUDIO_SET_TXBUFSIZE, 16*1024);
if(ret < 0) {
printf("ioctl AUDIO_SET_TXBUFSIZE failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
ret = ioctl(g_audio_socket, AUDIO_SET_SAMPLE_RATE, g_audio_rate);
if(ret < 0) {
printf("ioctl AUDIO_SET_SAMPLE_RATE failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
ret = ioctl(g_audio_socket, AUDIO_GET_OUTPUT, &output_channel);
if(ret < 0) {
printf("ioctl AUDIO_GET_OUTPUT failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
speaker = 1;
str = getenv("SPEAKER");
if(str) {
if(str[0] != 'i' && str[0] != 'I') {
speaker = 0;
}
}
if(speaker) {
printf("Sending sound to internal speaker\n");
output_channel |= AUDIO_OUT_SPEAKER;
} else {
printf("Sending sound to external jack\n");
output_channel &= (~AUDIO_OUT_SPEAKER);
output_channel |= AUDIO_OUT_HEADPHONE;
}
ret = ioctl(g_audio_socket, AUDIO_SET_OUTPUT, output_channel);
if(ret < 0) {
printf("ioctl AUDIO_SET_OUTPUT failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
}
#endif /* HPUX */
#if defined(__linux__) || defined(OSS)
void child_sound_init_linux() {
int stereo;
int sample_size;
int rate;
int fragment;
int fmt;
int ret;
g_audio_socket = open("/dev/dsp", O_WRONLY, 0);
if(g_audio_socket < 0) {
printf("open /dev/dsp failed, ret: %d, errno:%d\n",
g_audio_socket, errno);
exit(1);
}
fragment = 0x00200009;
#if 0
ret = ioctl(g_audio_socket, SNDCTL_DSP_SETFRAGMENT, &fragment);
if(ret < 0) {
printf("ioctl SETFRAGEMNT failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
#endif
sample_size = 16;
ret = ioctl(g_audio_socket, SNDCTL_DSP_SAMPLESIZE, &sample_size);
if(ret < 0) {
printf("ioctl SNDCTL_DSP_SAMPLESIZE failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
#if defined(GSPORT_LITTLE_ENDIAN) || defined (__LITTLE_ENDIAN__) // OSX needs to calculate endianness mid-compilation, can't be passed on compile command
fmt = AFMT_S16_LE;
#else
fmt = AFMT_S16_BE;
#endif
ret = ioctl(g_audio_socket, SNDCTL_DSP_SETFMT, &fmt);
if(ret < 0) {
printf("ioctl SNDCTL_DSP_SETFMT failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
stereo = 1;
ret = ioctl(g_audio_socket, SNDCTL_DSP_STEREO, &stereo);
if(ret < 0) {
printf("ioctl SNDCTL_DSP_STEREO failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
rate = g_audio_rate;
ret = ioctl(g_audio_socket, SNDCTL_DSP_SPEED, &rate);
if(ret < 0) {
printf("ioctl SNDCTL_DSP_SPEED failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
if(ret > 0) {
rate = ret; /* rate is returned value */
}
if(rate < 8000) {
printf("Audio rate of %d which is < 8000!\n", rate);
exit(1);
}
g_audio_rate = rate;
printf("Sound initialized\n");
}
#endif

6
src/tfe/CMakeLists.txt Normal file
View File

@ -0,0 +1,6 @@
set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_library(tfe tfe.c tfearch.c tfesupp.c)
target_compile_definitions(tfe PUBLIC HAVE_TFE)

View File

@ -1,6 +1,7 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 by GSport contributors
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
@ -101,4 +102,4 @@ int tfe_arch_enumadapter_open(void);
int tfe_arch_enumadapter(char **ppname, char **ppdescription);
int tfe_arch_enumadapter_close(void);
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,108 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.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>
<PropertyGroup Label="Globals">
<ProjectGuid>{E810477A-E004-4308-A58A-21393213EF89}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>tfe</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>MultiByte</CharacterSet>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
<PlatformToolset>v140</PlatformToolset>
</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 Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;TFE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<BufferSecurityCheck>false</BufferSecurityCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<CompileAs>CompileAsC</CompileAs>
<FunctionLevelLinking>true</FunctionLevelLinking>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>Full</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;TFE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<CompileAs>CompileAsC</CompileAs>
<BufferSecurityCheck>false</BufferSecurityCheck>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\arch\win32\bittypes.h" />
<ClInclude Include="..\arch\win32\bpf.h" />
<ClInclude Include="..\arch\win32\dirent-win32.h" />
<ClInclude Include="..\arch\win32\ip6_misc.h" />
<ClInclude Include="..\arch\win32\pcap-stdinc.h" />
<ClInclude Include="..\arch\win32\pcap.h" />
<ClInclude Include="tfesupp.h" />
<ClInclude Include="types.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="tfe.c" />
<ClCompile Include="tfearch.c" />
<ClCompile Include="tfesupp.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
<?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>
<PropertyGroup Label="Globals">
<ProjectGuid>{E810477A-E004-4308-A58A-21393213EF89}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>tfe</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>MultiByte</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</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 Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;TFE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<BufferSecurityCheck>false</BufferSecurityCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<CompileAs>CompileAsC</CompileAs>
<FunctionLevelLinking>true</FunctionLevelLinking>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<Optimization>Full</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;TFE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<CompileAs>CompileAsC</CompileAs>
<BufferSecurityCheck>false</BufferSecurityCheck>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\arch\win32\bittypes.h" />
<ClInclude Include="..\arch\win32\bpf.h" />
<ClInclude Include="..\arch\win32\dirent-win32.h" />
<ClInclude Include="..\arch\win32\ip6_misc.h" />
<ClInclude Include="..\arch\win32\pcap-stdinc.h" />
<ClInclude Include="..\arch\win32\pcap.h" />
<ClInclude Include="tfesupp.h" />
<ClInclude Include="types.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="tfe.c" />
<ClCompile Include="tfearch.c" />
<ClCompile Include="tfesupp.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -1,54 +1,54 @@
<?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="tfesupp.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="types.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\arch\win32\bittypes.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\arch\win32\bpf.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\arch\win32\dirent-win32.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\arch\win32\ip6_misc.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\arch\win32\pcap.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\arch\win32\pcap-stdinc.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="tfe.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="tfesupp.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="tfearch.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
<?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="tfesupp.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="types.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\arch\win32\bittypes.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\arch\win32\bpf.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\arch\win32\dirent-win32.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\arch\win32\ip6_misc.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\arch\win32\pcap.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\arch\win32\pcap-stdinc.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="tfe.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="tfesupp.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="tfearch.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -1,10 +1,10 @@
/*
* tfearch.c - TFE ("The final ethernet") emulation,
* 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.
*
@ -24,7 +24,7 @@
* 02111-1307 USA.
*
*/
#include <assert.h>
#include <stdio.h>
@ -33,7 +33,7 @@
#include <stdbool.h>
#include "../atbridge/pcap_delay.h"
#include "tfesupp.h"
#include "tfesupp.h"
#include "../defc.h"
#include "protos_tfe.h"
@ -60,36 +60,35 @@ static char TfePcapErrbuf[PCAP_ERRBUF_SIZE];
#ifdef TFE_DEBUG_PKTDUMP
static
void debug_output( const char *text, unsigned char *what, int count )
{
char buffer[256];
char *p = buffer;
char *pbuffer1 = what;
int len1 = count;
int i;
void debug_output( const char *text, unsigned char *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);
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);
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);
} while (len1>0);
}
#endif // #ifdef TFE_DEBUG_PKTDUMP
/*
These functions let the UI enumerate the available interfaces.
These functions let the UI enumerate the available interfaces.
First, TfeEnumAdapterOpen() is used to start enumeration.
First, TfeEnumAdapterOpen() is used to start enumeration.
TfeEnumAdapter is then used to gather information for each adapter present
on the system, where:
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
@ -97,123 +96,119 @@ void debug_output( const char *text, unsigned char *what, int count )
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.
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 (pcapdelay_findalldevs(&TfePcapAlldevs, TfePcapErrbuf) == -1)
{
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 (pcapdelay_findalldevs(&TfePcapAlldevs, TfePcapErrbuf) == -1)
{
#ifdef TFE_DEBUG_ARCH
log_message(tfe_arch_log, "ERROR in TfeEnumAdapterOpen: pcap_findalldevs: '%s'", TfePcapErrbuf);
log_message(tfe_arch_log, "ERROR in TfeEnumAdapterOpen: pcap_findalldevs: '%s'", TfePcapErrbuf);
#endif
return 0;
}
return 0;
}
if (!TfePcapAlldevs) {
if (!TfePcapAlldevs) {
#ifdef TFE_DEBUG_ARCH
log_message(tfe_arch_log, "ERROR in TfeEnumAdapterOpen, finding all pcap devices - "
"Do we have the necessary privilege rights?");
log_message(tfe_arch_log, "ERROR in TfeEnumAdapterOpen, finding all pcap devices - "
"Do we have the necessary privilege rights?");
#endif
return 0;
}
return 0;
}
TfePcapNextDev = TfePcapAlldevs;
TfePcapNextDev = TfePcapAlldevs;
return 1;
return 1;
}
int tfe_arch_enumadapter(char **ppname, char **ppdescription)
{
if (!TfePcapNextDev || (TfePcapNextDev->name == NULL))
return 0;
int tfe_arch_enumadapter(char **ppname, char **ppdescription) {
if (!TfePcapNextDev || (TfePcapNextDev->name == NULL))
return 0;
*ppname = lib_stralloc(TfePcapNextDev->name);
if (TfePcapNextDev->description)
*ppdescription = lib_stralloc(TfePcapNextDev->description);
else
*ppdescription = lib_stralloc(TfePcapNextDev->name);
TfePcapNextDev = TfePcapNextDev->next;
*ppname = lib_stralloc(TfePcapNextDev->name);
if (TfePcapNextDev->description)
*ppdescription = lib_stralloc(TfePcapNextDev->description);
else
*ppdescription = lib_stralloc(TfePcapNextDev->name);
TfePcapNextDev = TfePcapNextDev->next;
return 1;
return 1;
}
int tfe_arch_enumadapter_close(void)
{
if (TfePcapAlldevs) {
pcapdelay_freealldevs(TfePcapAlldevs);
TfePcapAlldevs = NULL;
}
return 1;
int tfe_arch_enumadapter_close(void) {
if (TfePcapAlldevs) {
pcapdelay_freealldevs(TfePcapAlldevs);
TfePcapAlldevs = NULL;
}
return 1;
}
static
int TfePcapOpenAdapter(const char *interface_name)
{
pcap_if_t *TfePcapDevice = NULL;
int 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;
int found = FALSE;
if (!tfe_enumadapter_open()) {
return FALSE;
}
else {
/* look if we can find the specified adapter */
char *pname;
char *pdescription;
int found = FALSE;
if (interface_name) {
/* we have an interface name, try it */
TfePcapDevice = TfePcapAlldevs;
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;
while (tfe_enumadapter(&pname, &pdescription)) {
if (strcmp(pname, interface_name)==0) {
found = TRUE;
}
lib_free(pname);
lib_free(pdescription);
if (found) break;
TfePcapDevice = TfePcapNextDev;
}
}
TfePcapFP = pcapdelay_open_live(TfePcapDevice->name, 1700, 1, 20, TfePcapErrbuf);
if ( TfePcapFP == NULL)
{
#ifdef TFE_DEBUG_ARCH
log_message(tfe_arch_log, "ERROR opening adapter: '%s'", TfePcapErrbuf);
#endif
tfe_enumadapter_close();
return FALSE;
if (!found) {
/* just take the first adapter */
TfePcapDevice = TfePcapAlldevs;
}
}
if (pcapdelay_setnonblock(TfePcapFP, 1, TfePcapErrbuf)<0)
{
TfePcapFP = pcapdelay_open_live(TfePcapDevice->name, 1700, 1, 20, TfePcapErrbuf);
if ( TfePcapFP == NULL)
{
#ifdef TFE_DEBUG_ARCH
log_message(tfe_arch_log, "WARNING: Setting PCAP to non-blocking failed: '%s'", TfePcapErrbuf);
log_message(tfe_arch_log, "ERROR opening adapter: '%s'", TfePcapErrbuf);
#endif
}
/* Check the link layer. We support only Ethernet for simplicity. */
if(pcapdelay_datalink(TfePcapFP) != DLT_EN10MB)
{
#ifdef TFE_DEBUG_ARCH
log_message(tfe_arch_log, "ERROR: TFE works only on Ethernet networks.");
#endif
tfe_enumadapter_close();
return FALSE;
}
tfe_enumadapter_close();
return TRUE;
return FALSE;
}
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);
#endif
}
/* Check the link layer. We support only Ethernet for simplicity. */
if(pcapdelay_datalink(TfePcapFP) != DLT_EN10MB)
{
#ifdef TFE_DEBUG_ARCH
log_message(tfe_arch_log, "ERROR: TFE works only on Ethernet networks.");
#endif
tfe_enumadapter_close();
return FALSE;
}
tfe_enumadapter_close();
return TRUE;
}
@ -221,50 +216,44 @@ int TfePcapOpenAdapter(const char *interface_name)
/* the architecture-dependend functions */
int tfe_arch_init(void)
{
//tfe_arch_log = log_open("TFEARCH");
int tfe_arch_init(void) {
//tfe_arch_log = log_open("TFEARCH");
return 1;
return 1;
}
void tfe_arch_pre_reset( void )
{
void tfe_arch_pre_reset( void ) {
#ifdef TFE_DEBUG_ARCH
log_message( tfe_arch_log, "tfe_arch_pre_reset()." );
log_message( tfe_arch_log, "tfe_arch_pre_reset()." );
#endif
}
void tfe_arch_post_reset( void )
{
void tfe_arch_post_reset( void ) {
#ifdef TFE_DEBUG_ARCH
log_message( tfe_arch_log, "tfe_arch_post_reset()." );
log_message( tfe_arch_log, "tfe_arch_post_reset()." );
#endif
}
int tfe_arch_activate(const char *interface_name)
{
int tfe_arch_activate(const char *interface_name) {
#ifdef TFE_DEBUG_ARCH
log_message( tfe_arch_log, "tfe_arch_activate()." );
log_message( tfe_arch_log, "tfe_arch_activate()." );
#endif
if (!TfePcapOpenAdapter(interface_name)) {
return 0;
}
return 1;
if (!TfePcapOpenAdapter(interface_name)) {
return 0;
}
return 1;
}
void tfe_arch_deactivate( void )
{
void tfe_arch_deactivate( void ) {
#ifdef TFE_DEBUG_ARCH
log_message( tfe_arch_log, "tfe_arch_deactivate()." );
log_message( tfe_arch_log, "tfe_arch_deactivate()." );
#endif
}
void tfe_arch_set_mac( const unsigned char mac[6] )
{
void tfe_arch_set_mac( const unsigned char 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] );
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
}
@ -275,49 +264,46 @@ void tfe_arch_recv_ctl( int bBroadcast, /* broadcast */
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" );
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 )
{
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" );
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;
unsigned char *buffer;
unsigned int len;
unsigned char *buffer;
} TFE_PCAP_INTERNAL;
/* Callback function invoked by libpcap for every incoming packet */
static
void TfePcapPacketHandler(unsigned char *param, const struct pcap_pkthdr *header, const unsigned char *pkt_data)
{
TFE_PCAP_INTERNAL *pinternal = (TFE_PCAP_INTERNAL*)param;
void TfePcapPacketHandler(unsigned char *param, const struct pcap_pkthdr *header, const unsigned 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;
/* 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);
memcpy(pinternal->buffer, pkt_data, pinternal->len);
}
/* the following function receives a frame.
@ -325,27 +311,26 @@ void TfePcapPacketHandler(unsigned char *param, const struct pcap_pkthdr *header
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
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;
*/
static
int tfe_arch_receive_frame(TFE_PCAP_INTERNAL *pinternal) {
int ret = -1;
/* check if there is something to receive */
if (pcapdelay_dispatch(TfePcapFP, 1, (pcap_handler)TfePcapPacketHandler, (unsigned char*)pinternal)!=0) {
/* Something has been received */
ret = pinternal->len;
}
/* check if there is something to receive */
if (pcapdelay_dispatch(TfePcapFP, 1, (pcap_handler)TfePcapPacketHandler, (unsigned 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 );
log_message( tfe_arch_log, "tfe_arch_receive_frame() called, returns %d.", ret );
#endif
return ret;
return ret;
}
void tfe_arch_transmit(int force, /* FORCE: Delete waiting frames in transmit buffer */
@ -354,105 +339,103 @@ void tfe_arch_transmit(int force, /* FORCE: Delete waiting frames in trans
int tx_pad_dis, /* TXPADDIS: Disable padding to 60 Bytes */
int txlength, /* Frame length */
unsigned char *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
);
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);
debug_output( "Transmit frame: ", txframe, txlength);
#endif // #ifdef TFE_DEBUG_PKTDUMP
if (pcapdelay_sendpacket(TfePcapFP, txframe, txlength) == -1) {
//log_message(tfe_arch_log, "WARNING! Could not send packet!");
}
if (pcapdelay_sendpacket(TfePcapFP, txframe, txlength) == -1) {
//log_message(tfe_arch_log, "WARNING! Could not send packet!");
}
}
/*
tfe_arch_receive()
tfe_arch_receive()
This function checks if there was a frame received.
If so, it returns 1, else 0.
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 no frame, none of the parameters is changed!
If there was a frame, the following actions are done:
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
- 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
- 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
- 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,
- 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
- 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(unsigned char *pbuffer , /* where to store a frame */
int *plen, /* IN: maximum length of frame to copy;
OUT: length of received frame
- 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(unsigned char *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 *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;
) {
int len;
TFE_PCAP_INTERNAL internal = { *plen, pbuffer };
TFE_PCAP_INTERNAL internal = { *plen, pbuffer };
#ifdef TFE_DEBUG_ARCH
log_message( tfe_arch_log, "tfe_arch_receive() called, with *plen=%u.", *plen );
log_message( tfe_arch_log, "tfe_arch_receive() called, with *plen=%u.", *plen );
#endif
assert((*plen&1)==0);
assert((*plen&1)==0);
len = tfe_arch_receive_frame(&internal);
len = tfe_arch_receive_frame(&internal);
if (len!=-1) {
if (len!=-1) {
#ifdef TFE_DEBUG_PKTDUMP
debug_output( "Received frame: ", internal.buffer, internal.len );
debug_output( "Received frame: ", internal.buffer, internal.len );
#endif // #ifdef TFE_DEBUG_PKTDUMP
if (len&1)
++len;
if (len&1)
++len;
*plen = 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;
/* 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;
/* this frame has been received correctly */
*prx_ok = 1;
return 1;
}
return 1;
}
return 0;
return 0;
}

View File

@ -39,7 +39,7 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifdef WIN32
#ifdef WIN32
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
@ -61,128 +61,117 @@
static unsigned long crc32_table[256];
static int crc32_is_initialized = 0;
void lib_free(void *ptr)
{
void lib_free(void *ptr) {
#ifdef LIB_DEBUG
lib_debug_free(ptr, 1, 1);
lib_debug_free(ptr, 1, 1);
#endif
#ifdef LIB_DEBUG
lib_debug_libc_free(ptr);
lib_debug_libc_free(ptr);
#else
free(ptr);
free(ptr);
#endif
}
void *lib_malloc(size_t size)
{
void *lib_malloc(size_t size) {
#ifdef LIB_DEBUG
void *ptr = lib_debug_libc_malloc(size);
void *ptr = lib_debug_libc_malloc(size);
#else
void *ptr = malloc(size);
void *ptr = malloc(size);
#endif
#ifndef __OS2__
if (ptr == NULL && size > 0)
exit(-1);
#endif
if (ptr == NULL && size > 0)
exit(-1);
#ifdef LIB_DEBUG
lib_debug_alloc(ptr, size, 3);
lib_debug_alloc(ptr, size, 3);
#endif
return ptr;
return ptr;
}
/*-----------------------------------------------------------------------*/
/* Malloc enough space for `str', copy `str' into it and return its
address. */
char *lib_stralloc(const char *str)
{
size_t size;
char *ptr;
char *lib_stralloc(const char *str) {
size_t size;
char *ptr;
if (str == NULL)
exit(-1);
if (str == NULL)
exit(-1);
size = strlen(str) + 1;
ptr = (char *)lib_malloc(size);
size = strlen(str) + 1;
ptr = (char *)lib_malloc(size);
memcpy(ptr, str, size);
return ptr;
memcpy(ptr, str, size);
return ptr;
}
/* Like realloc, but abort if not enough memory is available. */
void *lib_realloc(void *ptr, size_t size)
{
void *lib_realloc(void *ptr, size_t size) {
#ifdef LIB_DEBUG
void *new_ptr = lib_debug_libc_realloc(ptr, size);
void *new_ptr = lib_debug_libc_realloc(ptr, size);
#else
void *new_ptr = realloc(ptr, size);
void *new_ptr = realloc(ptr, size);
#endif
#ifndef __OS2__
if (new_ptr == NULL)
exit(-1);
#endif
if (new_ptr == NULL)
exit(-1);
#ifdef LIB_DEBUG
lib_debug_free(ptr, 1, 0);
lib_debug_alloc(new_ptr, size, 1);
lib_debug_free(ptr, 1, 0);
lib_debug_alloc(new_ptr, size, 1);
#endif
return new_ptr;
return new_ptr;
}
// Util Stuff
/* Set a new value to the dynamically allocated string *str.
Returns `-1' if nothing has to be done. */
int util_string_set(char **str, const char *new_value)
{
if (*str == NULL) {
if (new_value != NULL)
*str = lib_stralloc(new_value);
int util_string_set(char **str, const char *new_value) {
if (*str == NULL) {
if (new_value != NULL)
*str = lib_stralloc(new_value);
} else {
if (new_value == NULL) {
lib_free(*str);
*str = NULL;
} else {
if (new_value == NULL) {
lib_free(*str);
*str = NULL;
} else {
/* Skip copy if src and dest are already the same. */
if (strcmp(*str, new_value) == 0)
return -1;
/* Skip copy if src and dest are already the same. */
if (strcmp(*str, new_value) == 0)
return -1;
*str = (char *)lib_realloc(*str, strlen(new_value) + 1);
strcpy(*str, new_value);
}
*str = (char *)lib_realloc(*str, strlen(new_value) + 1);
strcpy(*str, new_value);
}
return 0;
}
return 0;
}
// crc32 Stuff
unsigned long crc32_buf(const char *buffer, unsigned int len)
{
int i, j;
unsigned long crc, c;
const char *p;
unsigned long crc32_buf(const char *buffer, unsigned int len) {
int i, j;
unsigned long crc, c;
const char *p;
if (!crc32_is_initialized) {
for (i = 0; i < 256; i++) {
c = (unsigned long) i;
for (j = 0; j < 8; j++)
c = c & 1 ? CRC32_POLY ^ (c >> 1) : c >> 1;
crc32_table[i] = c;
}
crc32_is_initialized = 1;
if (!crc32_is_initialized) {
for (i = 0; i < 256; i++) {
c = (unsigned long) i;
for (j = 0; j < 8; j++)
c = c & 1 ? CRC32_POLY ^ (c >> 1) : c >> 1;
crc32_table[i] = c;
}
crc32_is_initialized = 1;
}
crc = 0xffffffff;
for (p = buffer; len > 0; ++p, --len)
crc = (crc >> 8) ^ crc32_table[(crc ^ *p) & 0xff];
return ~crc;
crc = 0xffffffff;
for (p = buffer; len > 0; ++p, --len)
crc = (crc >> 8) ^ crc32_table[(crc ^ *p) & 0xff];
return ~crc;
}

533
src/unix_host_common.c Normal file
View File

@ -0,0 +1,533 @@
/*
GSport - an Apple //gs Emulator
Copyright (C) 2010 - 2019 by GSport contributors
Copyright (C) 2016 - 2018 Dagen Brock
Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey
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 _BSD_SOURCE
#include <ctype.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#if defined(__APPLE__)
#include <sys/xattr.h>
#include <sys/attr.h>
#include <sys/paths.h>
#endif
#ifdef __linux__
#include <sys/xattr.h>
#endif
#if defined(__FreeBSD__)
#include <sys/types.h>
#include <sys/extattr.h>
#endif
#if defined(_AIX)
#include <sys/ea.h>
#endif
#ifndef XATTR_FINDERINFO_NAME
#define XATTR_FINDERINFO_NAME "com.apple.FinderInfo"
#endif
#ifndef XATTR_RESOURCEFORK_NAME
#define XATTR_RESOURCEFORK_NAME "com.apple.ResourceFork"
#endif
#include "defc.h"
#include "gsos.h"
#include "host_common.h"
static ino_t root_ino = 0;
static dev_t root_dev = 0;
unsigned host_startup(void) {
struct stat st;
if (!g_cfg_host_path) return invalidFSTop;
if (!*g_cfg_host_path) return invalidFSTop;
if (host_root) free(host_root);
host_root = strdup(g_cfg_host_path);
if (stat(host_root, &st) < 0) {
fprintf(stderr, "%s does not exist\n", host_root);
return invalidFSTop;
}
if (!S_ISDIR(st.st_mode)) {
fprintf(stderr, "%s is not a directory\n", host_root);
return invalidFSTop;
}
root_ino = st.st_ino;
root_dev = st.st_dev;
return 0;
}
void host_shutdown(void) {
if (host_root) free(host_root);
host_root = NULL;
root_ino = 0;
root_dev = 0;
}
int host_is_root(struct stat *st) {
return st->st_ino == root_ino && st->st_dev == root_dev;
}
/*
* Date/time conversion
*/
/*
* converts time_t to a gs/os readhextime date/time record.
*/
void host_set_date_time_rec(word32 ptr, time_t time) {
if (time == 0) {
for (int i = 0; i < 8; ++i) set_memory_c(ptr++, 0, 0);
return;
}
struct tm *tm = localtime(&time);
if (tm->tm_sec == 60) tm->tm_sec = 59; /* leap second */
set_memory_c(ptr++, tm->tm_sec, 0);
set_memory_c(ptr++, tm->tm_min, 0);
set_memory_c(ptr++, tm->tm_hour, 0);
set_memory_c(ptr++, tm->tm_year, 0);
set_memory_c(ptr++, tm->tm_mday - 1, 0);
set_memory_c(ptr++, tm->tm_mon, 0);
set_memory_c(ptr++, 0, 0);
set_memory_c(ptr++, tm->tm_wday + 1, 0);
}
/*
* converts time_t to a prodos16 date/time record.
*/
void host_set_date_time(word32 ptr, time_t time) {
if (time == 0) {
for (int i = 0; i < 4; ++i) set_memory_c(ptr++, 0, 0);
return;
}
struct tm *tm = localtime(&time);
word16 tmp = 0;
tmp |= (tm->tm_year % 100) << 9;
tmp |= tm->tm_mon << 5;
tmp |= tm->tm_mday;
set_memory16_c(ptr, tmp, 0);
ptr += 2;
tmp = 0;
tmp |= tm->tm_hour << 8;
tmp |= tm->tm_min;
set_memory16_c(ptr, tmp, 0);
}
word32 host_convert_date_time(time_t time) {
if (time == 0) return 0;
struct tm *tm = localtime(&time);
word16 dd = 0;
dd |= (tm->tm_year % 100) << 9;
dd |= tm->tm_mon << 5;
dd |= tm->tm_mday;
word16 tt = 0;
tt |= tm->tm_hour << 8;
tt |= tm->tm_min;
return (tt << 16) | dd;
}
time_t host_get_date_time(word32 ptr) {
word16 a = get_memory16_c(ptr + 0, 0);
word16 b = get_memory16_c(ptr + 2, 0);
if (!a && !b) return 0;
struct tm tm;
memset(&tm, 0, sizeof(tm));
tm.tm_year = (a >> 9) & 0x7f;
tm.tm_mon = ((a >> 5) & 0x0f) - 1;
tm.tm_mday = (a >> 0) & 0x1f;
tm.tm_hour = (b >> 8) & 0x1f;
tm.tm_min = (b >> 0) & 0x3f;
tm.tm_sec = 0;
tm.tm_isdst = -1;
// 00 - 39 => 2000-2039
// 40 - 99 => 1940-1999
if (tm.tm_year < 40) tm.tm_year += 100;
return mktime(&tm);
}
time_t host_get_date_time_rec(word32 ptr) {
byte buffer[8];
for (int i = 0; i < 8; ++i) buffer[i] = get_memory_c(ptr++, 0);
if (!memcmp(buffer, "\x00\x00\x00\x00\x00\x00\x00\x00", 8)) return 0;
struct tm tm;
memset(&tm, 0, sizeof(tm));
tm.tm_sec = buffer[0];
tm.tm_min = buffer[1];
tm.tm_hour = buffer[2];
tm.tm_year = buffer[3];
tm.tm_mday = buffer[4] + 1;
tm.tm_mon = buffer[5];
tm.tm_isdst = -1;
return mktime(&tm);
}
#if defined(__APPLE__)
void host_get_file_xinfo(const char *path, struct file_info *fi) {
ssize_t tmp;
tmp = getxattr(path, XATTR_RESOURCEFORK_NAME, NULL, 0, 0, 0);
if (tmp < 0) tmp = 0;
fi->resource_eof = tmp;
fi->resource_blocks = (tmp + 511) / 512;
tmp = getxattr(path, XATTR_FINDERINFO_NAME, fi->finder_info, 32, 0, 0);
if (tmp == 16 || tmp == 32) {
fi->has_fi = 1;
host_finder_info_to_filetype(fi->finder_info, &fi->file_type, &fi->aux_type);
}
}
#elif defined(__sun)
void host_get_file_xinfo(const char *path, struct file_info *fi) {
struct stat st;
// can't stat an xattr directly?
int fd;
fd = attropen(path, XATTR_RESOURCEFORK_NAME, O_RDONLY);
if (fd >= 0) {
if (fstat(fd, &st) == 0) {
fi->resource_eof = st.st_size;
fi->resource_blocks = st.st_blocks;
}
close(fd);
}
fd = attropen(path, XATTR_FINDERINFO_NAME, O_RDONLY);
if (fd >= 0) {
int tmp = read(fd, fi->finder_info, 32);
if (tmp == 16 || tmp == 32) {
fi->has_fi = 1;
host_finder_info_to_filetype(fi->finder_info, &fi->file_type, &fi->aux_type);
}
close(fd);
}
}
#elif defined(__linux__)
void host_get_file_xinfo(const char *path, struct file_info *fi) {
ssize_t tmp;
tmp = getxattr(path, "user.com.apple.ResourceFork", NULL, 0);
if (tmp < 0) tmp = 0;
fi->resource_eof = tmp;
fi->resource_blocks = (tmp + 511) / 512;
tmp = getxattr(path, "user.com.apple.FinderInfo", fi->finder_info, 32);
if (tmp == 16 || tmp == 32) {
fi->has_fi = 1;
host_finder_info_to_filetype(fi->finder_info, &fi->file_type, &fi->aux_type);
}
}
#else
void host_get_file_xinfo(const char *path, struct file_info *fi) {
}
#endif
word32 host_get_file_info(const char *path, struct file_info *fi) {
struct stat st;
memset(fi, 0, sizeof(*fi));
int ok = stat(path, &st);
if (ok < 0) return host_map_errno(errno);
fi->eof = st.st_size;
fi->blocks = st.st_blocks;
fi->create_date = st.st_ctime;
fi->modified_date = st.st_mtime;
#if defined(__APPLE__)
fi->create_date = st.st_birthtime;
#endif
if (S_ISDIR(st.st_mode)) {
fi->storage_type = directoryFile;
fi->file_type = 0x0f;
if (host_is_root(&st))
fi->storage_type = 0x0f;
} else if (S_ISREG(st.st_mode)) {
fi->file_type = 0x06;
if (st.st_size < 0x200) fi->storage_type = seedling;
else if (st.st_size < 0x20000) fi->storage_type = sapling;
else fi->storage_type = tree;
} else {
fi->storage_type = st.st_mode & S_IFMT;
fi->file_type = 0;
}
// 0x01 = read enable
// 0x02 = write enable
// 0x04 = invisible
// 0x08 = reserved
// 0x10 = reserved
// 0x20 = backup needed
// 0x40 = rename enable
// 0x80 = destroy enable
fi->access = 0xc3; // placeholder...
if (S_ISREG(st.st_mode)) {
host_get_file_xinfo(path, fi);
if (!fi->has_fi) {
host_synthesize_file_xinfo(path, fi);
}
}
// get file type/aux type
if (fi->resource_eof) fi->storage_type = extendedFile;
return 0;
}
#if defined(__APPLE__)
word32 host_set_file_info(const char *path, struct file_info *fi) {
int ok;
struct attrlist list;
unsigned i = 0;
struct timespec dates[2];
if (fi->has_fi && fi->storage_type != 0x0d) {
ok = setxattr(path, XATTR_FINDERINFO_NAME, fi->finder_info, 32, 0, 0);
if (ok < 0) return host_map_errno(errno);
}
memset(&list, 0, sizeof(list));
memset(dates, 0, sizeof(dates));
list.bitmapcount = ATTR_BIT_MAP_COUNT;
list.commonattr = 0;
if (fi->create_date)
{
dates[i++].tv_sec = fi->create_date;
list.commonattr |= ATTR_CMN_CRTIME;
}
if (fi->modified_date)
{
dates[i++].tv_sec = fi->modified_date;
list.commonattr |= ATTR_CMN_MODTIME;
}
ok = 0;
if (i) ok = setattrlist(path, &list, dates, i * sizeof(struct timespec), 0);
return 0;
}
#elif defined(__sun)
word32 host_set_file_info(const char *path, struct file_info *fi) {
if (fi->has_fi && fi->storage_type != 0x0d) {
int fd = attropen(path, XATTR_FINDERINFO_NAME, O_WRONLY | O_CREAT, 0666);
if (fd < 0) return host_map_errno(errno);
write(fd, fi->finder_info, 32);
close(fd);
}
if (fi->modified_date) {
struct timeval times[2];
memset(times, 0, sizeof(times));
//times[0] = 0; // access
times[1].tv_sec = fi.modified_date; // modified
int ok = utimes(path, times);
if (ok < 0) return host_map_errno(errno);
}
return 0;
}
#elif defined(__linux__)
word32 host_set_file_info(const char *path, struct file_info *fi) {
if (fi->has_fi && fi->storage_type != 0x0d) {
int ok = setxattr(path, "user.apple.FinderInfo", fi->finder_info, 32, 0);
if (ok < 0) return host_map_errno(errno);
}
if (fi->modified_date) {
struct timeval times[2];
memset(times, 0, sizeof(times));
//times[0] = 0; // access
times[1].tv_sec = fi->modified_date; // modified
int ok = utimes(path, times);
if (ok < 0) return host_map_errno(errno);
}
return 0;
}
#else
word32 host_set_file_info(const char *path, struct file_info *fi) {
if (fi->modified_date) {
struct timeval times[2];
memset(times, 0, sizeof(times));
times[0] = 0; // access
times[1].tv_sec = fi->modified_date; // modified
int ok = utimes(path, times);
if (ok < 0) return host_map_errno(errno);
}
return 0;
}
#endif
static int qsort_callback(const void *a, const void *b) {
return strcasecmp(*(const char **)a, *(const char **)b);
}
unsigned host_scan_directory(const char *path, char ***out, size_t *entries, unsigned p8) {
DIR *dp;
char **data = NULL;
size_t capacity = 0;
size_t count = 0;
dp = opendir(path);
if (!dp) return host_map_errno_path(errno, path);
for(;;) {
struct dirent *d = readdir(dp);
if (!d) break;
const char *name = d->d_name;
if (name[0] == 0) continue;
if (name[0] == '.') continue;
if (p8) {
int ok = 1;
int n = strlen(name);
if (n > 15) continue;
/* check for invalid characters? */
for (int i = 0; i < n; ++i) {
unsigned char c = name[i];
if (isalpha(c) || isdigit(c) || c == '.') continue;
ok = 0;
break;
}
if (!ok) continue;
}
if (count == capacity) {
char **tmp;
tmp = realloc(data, (capacity + 100) * sizeof(char *));
if (!tmp) {
closedir(dp);
host_free_directory(data, count);
return outOfMem;
}
data = tmp;
for (int i = count; i < capacity; ++i) data[i] = 0;
capacity += 100;
}
data[count++] = strdup(name);
}
closedir(dp);
qsort(data, count, sizeof(char *), qsort_callback);
*entries = count;
*out = data;
return 0;
}
unsigned host_storage_type(const char *path, word16 *error) {
struct stat st;
if (!path) {
*error = badPathSyntax;
return 0;
}
if (stat(path, &st) < 0) {
*error = host_map_errno_path(errno, path);
return 0;
}
if (S_ISREG(st.st_mode)) {
return host_is_root(&st) ? 0x0f : directoryFile;
}
if (S_ISDIR(st.st_mode)) return standardFile;
*error = badStoreType;
return 0;
}

21
src/vars_osx_sdl2 Normal file
View File

@ -0,0 +1,21 @@
TARGET = gsport
NAME = gsport
PERL = perl
CC = clang
LD = clang++
AS = cc
OBJECTS = $(OBJECTS1) $(TFEOBJ) $(ATOBJ) $(PCAPOBJ) $(FSTOBJ) sdl2_driver.o sdl2snd_driver.o fix_mac_menu.o
ARCHS = ppc, i386, ppc64, x86_64
# OPTIONS FOR COMPILING C SOURCE
CCOPTS = -O2 -Wall -fomit-frame-pointer -std=gnu99 -DHAVE_ICON -DHAVE_SDL `sdl2-config --cflags`
# OPTIONS FOR COMPILING C++ SOURCE
CPPOPTS = -O2 -DHAVE_TFE -DHAVE_SDL `freetype-config --cflags` `sdl2-config --cflags`
EXTRA_LIBS = -lSDL2_image
OPTS = -DGSPORT_LITTLE_ENDIAN
SUFFIX =
LDFLAGS = `sdl2-config --static-libs` `freetype-config --libs` -framework Cocoa
LDOPTS =
EXTRA_SPECIALS =

20
src/vars_osx_x11 Normal file
View File

@ -0,0 +1,20 @@
TARGET = gsportx
NAME = gsportx
PERL = perl
CC = clang
LD = g++
AS = cc
OBJECTS = $(OBJECTS1) $(TFEOBJ) $(ATOBJ) $(PCAPOBJ) xdriver.o
CCOPTS = -O2 -Wall -fomit-frame-pointer -std=gnu99 -DHAVE_SDL -DTOGGLE_STATUS -I/usr/local/include/SDL2 -I/usr/local/include/freetype2 -L/usr/X11/lib
CPPOPTS = -O2 -DHAVE_TFE -DHAVE_SDL -DTOGGLE_STATUS -I/usr/local/include/freetype2 -I/usr/local/include/SDL2
OPTS = -DGSPORT_LITTLE_ENDIAN
SUFFIX =
LDFLAGS =
LDOPTS =
EXTRA_LIBS = -lX11 -lfreetype -lSDL2 -lpcap -lXext
EXTRA_SPECIALS =
XOPTS = -I/usr/X11/include

18
src/vars_rpilinux_fb Normal file
View File

@ -0,0 +1,18 @@
TARGET = gsportfb
OBJECTS = $(OBJECTS1) $(TFEOBJ) $(ATOBJ) $(PCAPOBJ) fbdriver.o
CC = gcc
CCOPTS = -O2 -Wall -fomit-frame-pointer -std=gnu99 -march=armv6
OPTS = -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DHAVE_ATBRIDGE
SUFFIX =
NAME = gsportfb
LDFLAGS =
LDOPTS =
LD = g++
EXTRA_LIBS = -ldl
EXTRA_SPECIALS =
AS = cc
PERL = perl
XOPTS = -I/usr/X11R6/include

16
src/vars_rpilinux_sdl2 Normal file
View File

@ -0,0 +1,16 @@
TARGET = gsport
NAME = gsport
PERL = perl
CC = gcc
LD = g++
#LD = gcc
AS = cc
OBJECTS = $(OBJECTS1) $(TFEOBJ) $(ATOBJ) $(PCAPOBJ) $(FSTOBJ) sdl2_driver.o sdl2snd_driver.o
CCOPTS = -O2 -Wall -fomit-frame-pointer -std=gnu99 -march=armv6
OPTS = -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DHAVE_ATBRIDGE -DHAVE_SDL -I/usr/include/SDL2 -I/usr/include/freetype2
EXTRA_LIBS = -ldl -lfreetype -lSDL2 -lSDL2_image
XOPTS = -I/usr/X11R6/include

View File

@ -1,10 +1,14 @@
TARGET = gsport.exe
OBJECTS = $(OBJECTS1) $(TFEOBJ) $(ATOBJ) $(PCAPOBJ) scc_windriver.o win32snd_driver.o win_console.o win_generic.o gsport32.o
TARGET = gsport32.exe
FSTOBJ = host_common.o win32_host_common.o host_mli.o win32_host_fst.o
OBJECTS = $(OBJECTS1) $(TFEOBJ) $(ATOBJ) $(PCAPOBJ) $(FSTOBJ) 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
NAME = gsport32
EXTRA_LIBS = -Larch/win32 -lcomdlg32 -lShlwapi -lIPHlpApi
XOPTS = -Wall -fomit-frame-pointer -march=i686
XLIBS =
XLIBS =

View File

@ -1,11 +1,18 @@
TARGET = gsport.exe
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
NAME = gsport
FSTOBJ = win32_host_fst.o
OBJECTS = sdl2_driver.o $(OBJECTS1) $(FSTOBJ) sdl2snd_driver.o
CCOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_SDL -DWIN_SDL -DTOGGLE_STATUS -I/usr/include/SDL2 -L/cygdrive/c/mingw/lib -I/cygdrive/c/mingw/include/SDL2/
CPPOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_SDL -DWIN_SDL -DTOGGLE_STATUS -I/usr/include/freetype2 -L/cygdrive/c/mingw/lib -I/cygdrive/c/mingw/include/SDL2/
SUFFIX = ".exe"
NAME = gsport
EXTRA_LIBS = -Larch/win32 -lSDL -lfreetype -lcomdlg32 -lShlwapi -lIPHlpApi
EXTRA_LIBS = -Larch/win32 -lSDL2main -lSDL2 -lfreetype -lcomdlg32 -lShlwapi -lIPHlpApi -lcygwin
EXTRA_LIBS = -L/usr/local/lib -lcygwin -lSDL2main -lSDL2 -mwindows -lfreetype -lcomdlg32 -lShlwapi -lIPHlpApi -L/usr/lib -lpthread -lSDL2_image -L/cygdrive/c/mingw/lib -I/cygdrive/c/mingw/include/SDL2/
XOPTS = -Wall -fomit-frame-pointer -march=i686
XLIBS =
XLIBS =

21
src/vars_win32_sdl2 Normal file
View File

@ -0,0 +1,21 @@
TARGET = gsport.exe
NAME = gsport
MINGW_HOME = /cygdrive/c/mingw/i686-w64-mingw32
FSTOBJ = host_common.o win32_host_common.o host_mli.o win32_host_fst.o
OBJECTS = sdl2_driver.o $(OBJECTS1) $(FSTOBJ) sdl2snd_driver.o scc_windriver.o
CCOPTS = -O3 -DGSPORT_LITTLE_ENDIAN -DWIN32 -D_WIN32 -DHAVE_SDL -DWIN_SDL -DTOGGLE_STATUS -I$(MINGW_HOME)/include/SDL2 -DWINSDL_BORDERHACK -D__USE_W32_SOCKETS -D_WINSOCK2API_
CPPOPTS = -O3 -DGSPORT_LITTLE_ENDIAN -DWIN32 -D_WIN32 -DHAVE_SDL -DWIN_SDL -DTOGGLE_STATUS -I/usr/include/freetype2 -I$(MINGW_HOME)/include/SDL2 -D__USE_W32_SOCKETS -D_WINSOCK2API_
SUFFIX = ".exe"
# working in cygwin
EXTRA_LIBS = -lcygwin -lSDL2main -lSDL2 -lfreetype -lcomdlg32 -lShlwapi -lIPHlpApi -lpthread -lSDL2_image -L$(MINGW_HOME)/lib/
EXTRA_LIBS = -lcygwin -lSDL2main -lSDL2 -lfreetype -lcomdlg32 -lShlwapi -lIPHlpApi -lpthread -lSDL2_image -L$(MINGW_HOME)/lib/ -Larch/win32 -lshell32
#XOPTS = -Wall -fomit-frame-pointer -march=i686
XLIBS =

View File

@ -1,18 +1,24 @@
TARGET = gsportx
OBJECTS = $(OBJECTS1) $(TFEOBJ) $(ATOBJ) $(PCAPOBJ) xdriver.o
CC = gcc
CCOPTS = -O2 -Wall -fomit-frame-pointer -std=gnu99 -march=i686 -DHAVE_SDL -DHAVE_TFE -DHAVE_ATBRIDGE -DTOGGLE_STATUS -I/usr/include/SDL -I/usr/include/freetype2
CPPOPTS = -O2 -DHAVE_TFE -DHAVE_SDL -DTOGGLE_STATUS -DHAVE_ATBRIDGE -I/usr/include/freetype2 -I/usr/include/SDL
OPTS = -DGSPORT_LITTLE_ENDIAN
SUFFIX =
NAME = gsportx
LDFLAGS =
LDOPTS =
LD = g++
EXTRA_LIBS = -lXext -lfreetype -lSDL
EXTRA_SPECIALS =
AS = cc
PERL = perl
XOPTS = -I/usr/X11R6/include
TARGET = gsport
NAME = gsport
PERL = perl
CC = gcc
LD = g++
AS = cc
OBJECTS = $(OBJECTS1) $(TFEOBJ) $(ATOBJ) $(PCAPOBJ) $(FSTOBJ) sdl2_driver.o sdl2snd_driver.o
# C Compiler Options
CCOPTS = -O2 -Wall -fomit-frame-pointer -std=gnu99 -DHAVE_SDL -DHAVE_TFE -DHAVE_ATBRIDGE -DTOGGLE_STATUS -I/usr/include/SDL2 -I/usr/include/freetype2
# C++ Compiler Options
CPPOPTS = -O2 -DHAVE_TFE -DHAVE_SDL -DTOGGLE_STATUS -DHAVE_ATBRIDGE -I/usr/include/freetype2 -I/usr/include/SDL2
EXTRA_LIBS = -lfreetype -lSDL2 -lSDL2_image -ldl
OPTS = -DGSPORT_LITTLE_ENDIAN
SUFFIX =
LDFLAGS =
LDOPTS =
EXTRA_SPECIALS =
XOPTS = -I/usr/X11R6/include

21
src/vars_x86linux_sdl2 Normal file
View File

@ -0,0 +1,21 @@
TARGET = gsport
NAME = gsport
PERL = perl
CC = gcc
LD = gcc
AS = cc
OBJECTS = $(OBJECTS1) $(TFEOBJ) $(ATOBJ) $(PCAPOBJ) $(FSTOBJ) sdl2_driver.o sdl2snd_driver.o
# C Compiler Options
CCOPTS = -O2 -Wall -fomit-frame-pointer -std=gnu99 -DHAVE_SDL -DHAVE_TFE -DHAVE_ATBRIDGE -DTOGGLE_STATUS -I/usr/include/SDL2 -I/usr/include/freetype2
# C++ Compiler Options
CPPOPTS = -O2 -DHAVE_TFE -DHAVE_SDL -DTOGGLE_STATUS -DHAVE_ATBRIDGE -I/usr/include/freetype2 -I/usr/include/SDL2
EXTRA_LIBS = -lfreetype -lSDL2 -lSDL2_image -ldl -lm -lstdc++
OPTS = -DGSPORT_LITTLE_ENDIAN
SUFFIX =
LDFLAGS =
LDOPTS = -I.
EXTRA_SPECIALS =

21
src/vars_x86linux_x11 Normal file
View File

@ -0,0 +1,21 @@
TARGET = gsportx
NAME = gsportx
PERL = perl
CC = gcc
LD = g++
AS = cc
OBJECTS = $(OBJECTS1) $(TFEOBJ) $(ATOBJ) $(PCAPOBJ) $(FSTOBJ) xdriver.o
#-march=i686 is causing "error: CPU you selected does not support x86-64 instruction set" on ubuntu
#CCOPTS = -O2 -Wall -fomit-frame-pointer -std=gnu99 -march=i686 -DHAVE_TFE -DHAVE_ATBRIDGE -DTOGGLE_STATUS
CCOPTS = -O2 -Wall -fomit-frame-pointer -std=gnu99 -DHAVE_TFE -DHAVE_ATBRIDGE -DTOGGLE_STATUS
OPTS = -DGSPORT_LITTLE_ENDIAN
SUFFIX =
LDFLAGS =
LDOPTS =
# added -ldl for ubuntu
EXTRA_LIBS = -lXext -ldl
EXTRA_SPECIALS =
XOPTS = -I/usr/X11R6/include

View File

@ -1,6 +1,6 @@
TARGET = gsportx
OBJECTS = $(OBJECTS1) xdriver.o
OBJECTS = $(OBJECTS1) $(FSTOBJ) xdriver.o
CC = gcc
CCOPTS = -O
OPTS = -DNDEBUG -DSOLARIS -DGSPORT_LITTLE_ENDIAN -DSOLARISSOUND

File diff suppressed because it is too large Load Diff

633
src/win32_host_common.c Normal file
View File

@ -0,0 +1,633 @@
#define _WIN32_WINNT 0x0600 // vista+
#include <Windows.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "defc.h"
#include "gsos.h"
#include "host_common.h"
void afp_init(struct AFP_Info *info, word16 file_type, word32 aux_type) {
//static_assert(sizeof(AFP_Info) == 60, "Incorrect AFP_Info size");
memset(info, 0, sizeof(*info));
info->magic = 0x00504641;
info->version = 0x00010000;
info->prodos_file_type = file_type;
info->prodos_aux_type = aux_type;
if (file_type || aux_type)
host_file_type_to_finder_info(info->finder_info, file_type, aux_type);
}
BOOL afp_verify(struct AFP_Info *info) {
if (!info) return 0;
if (info->magic != 0x00504641) return 0;
if (info->version != 0x00010000) return 0;
return 1;
}
int afp_to_filetype(struct AFP_Info *info, word16 *file_type, word32 *aux_type) {
// check for prodos ftype/auxtype...
if (info->prodos_file_type || info->prodos_aux_type) {
*file_type = info->prodos_file_type;
*aux_type = info->prodos_aux_type;
return 0;
}
int ok = host_finder_info_to_filetype(info->finder_info, file_type, aux_type);
if (ok == 0) {
info->prodos_file_type = *file_type;
info->prodos_aux_type = *aux_type;
}
return 0;
}
void afp_synchronize(struct AFP_Info *info, int preference) {
// if ftype/auxtype is inconsistent between prodos and finder info, use
// prodos as source of truth.
word16 f;
word32 a;
if (host_finder_info_to_filetype(info->finder_info, &f, &a) != 0) return;
if (f == info->prodos_file_type && a == info->prodos_aux_type) return;
if (preference == prefer_prodos)
host_file_type_to_finder_info(info->finder_info, info->prodos_file_type, info->prodos_aux_type);
else {
info->prodos_file_type = f;
info->prodos_aux_type = a;
}
}
static DWORD root_file_id[3] = {};
unsigned host_startup(void) {
if (!g_cfg_host_path) return invalidFSTop;
if (!*g_cfg_host_path) return invalidFSTop;
if (host_root) free(host_root);
host_root = strdup(g_cfg_host_path);
HANDLE h = CreateFile(host_root, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (h == INVALID_HANDLE_VALUE) {
fprintf(stderr, "%s does not exist or is not accessible\n", host_root);
return invalidFSTop;
}
FILE_BASIC_INFO fbi;
BY_HANDLE_FILE_INFORMATION info;
GetFileInformationByHandle(h, &info);
GetFileInformationByHandleEx(h, FileBasicInfo, &fbi, sizeof(fbi));
// can't delete volume root.
CloseHandle(h);
root_file_id[0] = info.dwVolumeSerialNumber;
root_file_id[1] = info.nFileIndexHigh;
root_file_id[2] = info.nFileIndexLow;
if (!(fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
fprintf(stderr, "%s is not a directory\n", host_root);
CloseHandle(h);
return invalidFSTop;
}
CloseHandle(h);
return 0;
}
void host_shutdown(void) {
if (host_root) free(host_root);
host_root = NULL;
memset(root_file_id, 0, sizeof(root_file_id));
}
int host_is_root(const BY_HANDLE_FILE_INFORMATION *info) {
DWORD id[3] = { info->dwVolumeSerialNumber, info->nFileIndexHigh, info->nFileIndexLow };
return memcmp(&id, &root_file_id, sizeof(root_file_id)) == 0;
}
/*
* Date/time conversion
*/
static int dayofweek(int y, int m, int d) {
/* 1 <= m <= 12, y > 1752 (in the U.K.) */
static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
y -= m < 3;
return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
}
/*
* converts time_t to a gs/os readhextime date/time record.
*/
void host_set_date_time_rec(word32 ptr, FILETIME utc) {
if (utc.dwLowDateTime == 0 && utc.dwHighDateTime == 0) {
for (int i = 0; i < 8; ++i) set_memory_c(ptr++, 0, 0);
return;
}
SYSTEMTIME tmLocal;
SYSTEMTIME tmUTC;
FileTimeToSystemTime(&utc, &tmUTC);
SystemTimeToTzSpecificLocalTime(NULL, &tmUTC, &tmLocal);
if (tmLocal.wYear < 1900 || tmLocal.wYear > 1900 + 255) {
for (int i = 0; i < 8; ++i) set_memory_c(ptr++, 0, 0);
return;
}
if (tmLocal.wSecond == 60) tmLocal.wSecond = 59; /* leap second */
int dow = dayofweek(tmLocal.wYear, tmLocal.wMonth, tmLocal.wDay);
set_memory_c(ptr++, tmLocal.wSecond, 0);
set_memory_c(ptr++, tmLocal.wMinute, 0);
set_memory_c(ptr++, tmLocal.wHour, 0);
set_memory_c(ptr++, tmLocal.wYear - 1900, 0);
set_memory_c(ptr++, tmLocal.wDay - 1, 0); // 1 = sunday
set_memory_c(ptr++, tmLocal.wMonth - 1, 0);
set_memory_c(ptr++, 0, 0);
set_memory_c(ptr++, dow + 1, 0);
}
/*
* converts time_t to a prodos16 date/time record.
*/
void host_set_date_time(word32 ptr, FILETIME utc) {
if (utc.dwLowDateTime == 0 && utc.dwHighDateTime == 0) {
for (int i = 0; i < 4; ++i) set_memory_c(ptr++, 0, 0);
return;
}
SYSTEMTIME tmLocal;
SYSTEMTIME tmUTC;
FileTimeToSystemTime(&utc, &tmUTC);
SystemTimeToTzSpecificLocalTime(NULL, &tmUTC, &tmLocal);
if (tmLocal.wYear < 1940 || tmLocal.wYear > 2039) {
for (int i = 0; i < 4; ++i) set_memory_c(ptr++, 0, 0);
return;
}
if (tmLocal.wSecond == 60) tmLocal.wSecond = 59; /* leap second */
word16 tmp = 0;
tmp |= (tmLocal.wYear % 100) << 9;
tmp |= tmLocal.wMonth << 5;
tmp |= tmLocal.wDay;
set_memory16_c(ptr, tmp, 0);
ptr += 2;
tmp = 0;
tmp |= tmLocal.wHour << 8;
tmp |= tmLocal.wMinute;
set_memory16_c(ptr, tmp, 0);
}
FILETIME host_get_date_time(word32 ptr) {
FILETIME utc = {0, 0};
word16 a = get_memory16_c(ptr + 0, 0);
word16 b = get_memory16_c(ptr + 2, 0);
if (!a && !b) return utc;
SYSTEMTIME tmLocal;
SYSTEMTIME tmUTC;
memset(&tmLocal, 0, sizeof(tmLocal));
memset(&tmUTC, 0, sizeof(tmUTC));
tmLocal.wYear = ((a >> 9) & 0x7f) + 1900;
tmLocal.wMonth = ((a >> 5) & 0x0f);
tmLocal.wDay = (a >> 0) & 0x1f;
tmLocal.wHour = (b >> 8) & 0x1f;
tmLocal.wMinute = (b >> 0) & 0x3f;
tmLocal.wSecond = 0;
// 00 - 39 => 2000-2039
// 40 - 99 => 1940-1999
if (tmLocal.wYear < 40) tmLocal.wYear += 100;
TzSpecificLocalTimeToSystemTime(NULL, &tmLocal, &tmUTC);
if (!SystemTimeToFileTime(&tmUTC, &utc)) utc =(FILETIME){0, 0};
return utc;
}
FILETIME host_get_date_time_rec(word32 ptr) {
FILETIME utc = {0, 0};
byte buffer[8];
for (int i = 0; i < 8; ++i) buffer[i] = get_memory_c(ptr++, 0);
if (!memcmp(buffer, "\x00\x00\x00\x00\x00\x00\x00\x00", 8)) return utc;
SYSTEMTIME tmLocal;
SYSTEMTIME tmUTC;
memset(&tmLocal, 0, sizeof(tmLocal));
memset(&tmUTC, 0, sizeof(tmUTC));
tmLocal.wSecond = buffer[0];
tmLocal.wMinute = buffer[1];
tmLocal.wHour = buffer[2];
tmLocal.wYear = 1900 + buffer[3];
tmLocal.wDay = buffer[4] + 1;
tmLocal.wMonth = buffer[5] + 1;
TzSpecificLocalTimeToSystemTime(NULL, &tmLocal, &tmUTC);
if (!SystemTimeToFileTime(&tmUTC, &utc)) utc =(FILETIME){0, 0};
return utc;
}
word32 host_convert_date_time(FILETIME utc) {
if (utc.dwLowDateTime == 0 && utc.dwHighDateTime == 0) {
return 0;
}
SYSTEMTIME tmLocal;
SYSTEMTIME tmUTC;
FileTimeToSystemTime(&utc, &tmUTC);
SystemTimeToTzSpecificLocalTime(NULL, &tmUTC, &tmLocal);
if (tmLocal.wYear < 1940 || tmLocal.wYear > 2039) {
return 0;
}
if (tmLocal.wSecond == 60) tmLocal.wSecond = 59; /* leap second */
word16 dd = 0;
dd |= (tmLocal.wYear % 100) << 9;
dd |= tmLocal.wMonth << 5;
dd |= tmLocal.wDay;
word16 tt = 0;
tt |= tmLocal.wHour << 8;
tt |= tmLocal.wMinute;
return (tt << 16) | dd;
}
word32 host_map_win32_error(DWORD e) {
switch (e) {
case ERROR_NO_MORE_FILES:
return endOfDir;
case ERROR_FILE_NOT_FOUND:
return fileNotFound;
case ERROR_PATH_NOT_FOUND:
return pathNotFound;
case ERROR_INVALID_ACCESS:
return invalidAccess;
case ERROR_FILE_EXISTS:
case ERROR_ALREADY_EXISTS:
return dupPathname;
case ERROR_DISK_FULL:
return volumeFull;
case ERROR_INVALID_PARAMETER:
return paramRangeErr;
case ERROR_DRIVE_LOCKED:
return drvrWrtProt;
case ERROR_NEGATIVE_SEEK:
return outOfRange;
case ERROR_SHARING_VIOLATION:
return fileBusy; // destroy open file, etc.
case ERROR_DIR_NOT_EMPTY:
return invalidAccess;
// ...
default:
fprintf(stderr, "GetLastError: %08x - %d\n", (int)e, (int)e);
return drvrIOError;
}
}
void host_get_file_xinfo(const char *path, struct file_info *fi) {
HANDLE h;
char *p = host_gc_append_string(path, ":AFP_Resource");
LARGE_INTEGER size = { 0 };
h = CreateFile(p, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (h != INVALID_HANDLE_VALUE) {
GetFileSizeEx(h, &size);
CloseHandle(h);
}
fi->resource_eof = size.LowPart;
fi->resource_blocks = (size.LowPart + 511) / 512;
p = host_gc_append_string(path, ":AFP_AfpInfo");
h = CreateFile(p, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h != INVALID_HANDLE_VALUE) {
DWORD read = 0;
if (ReadFile(h, &fi->afp, sizeof(struct AFP_Info), &read, NULL) && read == sizeof(struct AFP_Info)) {
if (afp_verify(&fi->afp)) fi->has_fi = 1;
afp_to_filetype(&fi->afp, &fi->file_type, &fi->aux_type);
}
CloseHandle(h);
}
}
static word16 map_attributes(DWORD dwFileAttributes) {
// 0x01 = read enable
// 0x02 = write enable
// 0x04 = invisible
// 0x08 = reserved
// 0x10 = reserved
// 0x20 = backup needed
// 0x40 = rename enable
// 0x80 = destroy enable
word16 access = 0;
if (dwFileAttributes & FILE_ATTRIBUTE_READONLY)
access = readEnable;
else
access = readEnable | writeEnable | renameEnable | destroyEnable;
if (dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
access |= fileInvisible;
// map FILE_ATTRIBUTE_ARCHIVE to backup needed bit?
return access;
}
word32 host_get_file_info(const char *path, struct file_info *fi) {
HANDLE h = CreateFile(path, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (h == INVALID_HANDLE_VALUE) return host_map_win32_error(GetLastError());
//FILE_BASIC_INFO fbi;
//FILE_STANDARD_INFO fsi;
//FILE_ID_INFO fii;
BY_HANDLE_FILE_INFORMATION info;
memset(fi, 0, sizeof(*fi));
//memset(&fbi, 0, sizeof(fbi));
//memset(&fsi, 0, sizeof(fsi));
GetFileInformationByHandle(h, &info);
//GetFileInformationByHandleEx(h, FileBasicInfo, &fbi, sizeof(fbi));
//GetFileInformationByHandleEx(h, FileStandardInfo, &fsi, sizeof(fsi));
//GetFileInformationByHandleEx(h, FileIdInfo, &fii, sizeof(fii));
word32 size = info.nFileSizeLow;
fi->eof = size;
fi->blocks = (size + 511) / 512;
fi->create_date = info.ftCreationTime;
fi->modified_date = info.ftLastWriteTime;
if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
fi->storage_type = directoryFile;
fi->file_type = 0x0f;
if (host_is_root(&info)) fi->storage_type = 0x0f;
} else {
fi->file_type = 0x06;
if (size < 0x200) fi->storage_type = seedling;
else if (size < 0x20000) fi->storage_type = sapling;
else fi->storage_type = tree;
host_get_file_xinfo(path, fi);
if (fi->resource_eof) fi->storage_type = extendedFile;
if (!fi->has_fi) host_synthesize_file_xinfo(path, fi);
}
// 0x01 = read enable
// 0x02 = write enable
// 0x04 = invisible
// 0x08 = reserved
// 0x10 = reserved
// 0x20 = backup needed
// 0x40 = rename enable
// 0x80 = destroy enable
word16 access = 0;
if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
access = 0x01;
else
access = 0x01 | 0x02 | 0x40 | 0x80;
if (info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
access |= 0x04;
// map FILE_ATTRIBUTE_ARCHIVE to backup needed bit?
fi->access = map_attributes(info.dwFileAttributes);
CloseHandle(h);
return 0;
}
word32 host_set_file_info(const char *path, struct file_info *fi) {
if (fi->has_fi && fi->storage_type != 0x0d && fi->storage_type != 0x0f) {
char *rpath = host_gc_append_string(path, ":AFP_AfpInfo");
HANDLE h = CreateFile(rpath, GENERIC_WRITE,
FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) return host_map_win32_error(GetLastError());
WriteFile(h, &fi->afp, sizeof(struct AFP_Info), NULL, NULL);
CloseHandle(h);
}
if (fi->create_date.dwLowDateTime || fi->create_date.dwHighDateTime
|| fi->modified_date.dwLowDateTime || fi->modified_date.dwHighDateTime) {
// SetFileInformationByHandle can modify dates.
HANDLE h;
h = CreateFile(path, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) return host_map_win32_error(GetLastError());
FILE_BASIC_INFO fbi;
FILE_BASIC_INFO newfbi;
memset(&fbi, 0, sizeof(fbi));
memset(&newfbi, 0, sizeof(newfbi));
BOOL ok;
ok = GetFileInformationByHandleEx(h, FileBasicInfo, &fbi, sizeof(fbi));
int delta = 0;
word16 old_access = map_attributes(fbi.FileAttributes);
if (fi->access && fi->access != old_access) {
newfbi.FileAttributes = fbi.FileAttributes;
if (fi->access & fileInvisible) {
delta = 1;
newfbi.FileAttributes |= FILE_ATTRIBUTE_HIDDEN;
}
// hfs fst only marks it read enable if all are clear.
word16 locked = writeEnable | destroyEnable | renameEnable;
if ((fi->access & locked) == 0) {
delta = 1;
newfbi.FileAttributes |= FILE_ATTRIBUTE_READONLY;
}
}
// todo -- compare against nt file time to see if it's actually changed.
// to prevent time stamp truncation.
if (fi->create_date.dwLowDateTime || fi->create_date.dwHighDateTime) {
delta = 1;
newfbi.CreationTime.LowPart = fi->create_date.dwLowDateTime;
newfbi.CreationTime.HighPart = fi->create_date.dwHighDateTime;
}
if (fi->modified_date.dwLowDateTime || fi->modified_date.dwHighDateTime) {
delta = 1;
newfbi.LastWriteTime.LowPart = fi->modified_date.dwLowDateTime;
newfbi.LastWriteTime.HighPart = fi->modified_date.dwHighDateTime;
//newfbi.ChangeTime.LowPart = fi->modified_date.dwLowDateTime; //?
//newfbi.ChangeTime.HighPart = fi->modified_date.dwHighDateTime; //?
}
if (delta)
ok = SetFileInformationByHandle(h, FileBasicInfo, &newfbi, sizeof(newfbi));
CloseHandle(h);
}
return 0;
}
static int qsort_callback(const void *a, const void *b) {
return strcasecmp(*(const char **)a, *(const char **)b);
}
unsigned host_scan_directory(const char *path, char ***out, size_t *entries, unsigned p8) {
WIN32_FIND_DATA fdata;
HANDLE h;
char **data = NULL;
size_t capacity = 0;
size_t count = 0;
DWORD e;
path = host_gc_append_path(path, "*");
h = FindFirstFile(path, &fdata);
if (h == INVALID_HANDLE_VALUE) {
e = GetLastError();
if (e == ERROR_FILE_NOT_FOUND) {
/* empty directory */
*out = NULL;
*entries = 0;
return 0;
}
return host_map_win32_error(e);
}
do {
char *name = fdata.cFileName;
if (name[0] == 0) continue;
if (name[0] == '.') continue;
if (p8) {
int ok = 1;
int n = strlen(name);
if (n > 15) continue;
/* check for invalid characters? */
for (int i = 0; i < n; ++i) {
unsigned char c = name[i];
if (isalpha(c) || isdigit(c) || c == '.') continue;
ok = 0;
break;
}
if (!ok) continue;
}
if (count == capacity) {
char **tmp;
tmp = realloc(data, (capacity + 100) * sizeof(char *));
if (!tmp) {
FindClose(h);
host_free_directory(data, count);
return outOfMem;
}
data = tmp;
for (int i = count; i < capacity; ++i) data[i] = 0;
capacity += 100;
}
data[count++] = strdup(name);
} while (FindNextFile(h, &fdata) != 0);
e = GetLastError();
FindClose(h);
if (e && e != ERROR_NO_MORE_FILES) {
host_free_directory(data, count);
return host_map_win32_error(e);
}
qsort(data, count, sizeof(char *), qsort_callback);
*entries = count;
*out = data;
return 0;
}
unsigned host_storage_type(const char *path, word16 *error) {
if (!path) {
*error = badPathSyntax;
return 0;
}
HANDLE h;
h = CreateFile(path, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (h == INVALID_HANDLE_VALUE) {
*error = host_map_win32_error(GetLastError());
return 0;
}
BY_HANDLE_FILE_INFORMATION info;
GetFileInformationByHandle(h, &info);
CloseHandle(h);
if (host_is_root(&info)) return 0x0f;
if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
return directoryFile;
return standardFile;
}

1862
src/win32_host_fst.c Normal file

File diff suppressed because it is too large Load Diff