mirror of
https://github.com/david-schmidt/gsport.git
synced 2024-06-17 23:29:33 +00:00
Merge ea638ad70e
into 7e788187b2
This commit is contained in:
commit
e3f97daece
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,2 +1,4 @@
|
|||
/.project
|
||||
/GSport.app
|
||||
/gsport
|
||||
.DS_Store
|
81
src/Makefile
81
src/Makefile
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
5
src/atbridge/CMakeLists.txt
Normal file
5
src/atbridge/CMakeLists.txt
Normal 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)
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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, ¤t->PhysicalAddress, sizeof(HW_LOCAL.mac));
|
||||
break;
|
||||
}
|
||||
current = current->Next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
halt_printf("ATBridge: Failed to find host MAC address (%d).", result);
|
||||
}
|
||||
|
||||
free(addresses);
|
||||
#else
|
||||
struct pcap_addr* address;
|
||||
for (address = device->addresses; address != 0; address = address->next)
|
||||
if (address->addr->sa_family == AF_PACKET)
|
||||
{
|
||||
struct sockaddr_ll* ll = (struct sockaddr_ll*)address->addr;
|
||||
memcpy(&HW_LOCAL.mac, ll->sll_addr, sizeof(HW_LOCAL.mac));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
const struct ether_addr_t* elap_get_mac()
|
||||
{
|
||||
return &HW_LOCAL;
|
||||
}
|
||||
|
||||
bool elap_init()
|
||||
{
|
||||
port_init(&elap_port);
|
||||
|
||||
memcpy(&HW_LOCAL, &HW_LOCAL_DEFAULT, sizeof(HW_LOCAL));
|
||||
|
||||
pcap_if_t* device;
|
||||
pcap_if_t* alldevs;
|
||||
int i = 0;
|
||||
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
// Load the PCAP library.
|
||||
if (!pcapdelay_load())
|
||||
{
|
||||
halt_printf("ATBridge: PCAP not available.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retrieve the device list.
|
||||
if(pcapdelay_findalldevs(&alldevs, errbuf) == -1)
|
||||
{
|
||||
atbridge_printf("ATBridge: Error enumerating PCAP devices: %s\n", errbuf);
|
||||
return false;
|
||||
}
|
||||
//dump_device_list(alldevs);
|
||||
|
||||
// Jump to the selected adapter.
|
||||
for (device = alldevs, i = 0; i < g_ethernet_interface; device = device->next, i++);
|
||||
if (!device)
|
||||
{
|
||||
halt_printf("ATBridge: PCAP device not found. Check interface number in settings.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clone the MAC address of the underlying interface. In certain configurations (e.g. Windows with an MS Loopback
|
||||
// interface), the interface, even in promiscous mode, filters foreign MAC addresses.
|
||||
elap_clone_host_mac(device);
|
||||
|
||||
// Open the adapter,
|
||||
if ((pcap_session = pcapdelay_open_live(device->name, // name of the device
|
||||
65536, // portion of the packet to capture.
|
||||
// 65536 grants that the whole packet will be captured on all the MACs.
|
||||
1, // promiscuous mode (nonzero means promiscuous)
|
||||
1, // read timeout
|
||||
errbuf // error buffer
|
||||
)) == NULL)
|
||||
{
|
||||
halt_printf("ATBridge: Unable to open the adapter. Pcap does not support %s.\n", device->name);
|
||||
pcapdelay_freealldevs(alldevs);
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
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(ðer->source, &HW_LOCAL, sizeof(ether->source)) != 0) && /* Ignore packets sent from our node. */
|
||||
(memcmp(ðer->dest, &HW_LOCAL, sizeof(ether->dest)) == 0 || /* Accept packets destined for our node ... */
|
||||
memcmp(ðer->dest, &HW_APPLETALK_BROADCAST, sizeof(ether->dest)) == 0 /* ... or for broadcast. */)
|
||||
)
|
||||
{
|
||||
// Check the 802.2 SNAP header.
|
||||
const struct snap_header_t* snap = (struct snap_header_t*)(packet + sizeof(struct ethernet_header_t));
|
||||
if (snap->dsap == SNAP_DSAP &&
|
||||
snap->ssap == SNAP_SSAP &&
|
||||
snap->control == SNAP_CONTROL)
|
||||
{
|
||||
if (memcmp(&snap->discriminator, &SNAP_APPLETALK, sizeof(snap->discriminator)) == 0)
|
||||
{
|
||||
// Handle an AppleTalk packet.
|
||||
const size_t payload_size = ntohs(ether->length) - sizeof(struct snap_header_t);
|
||||
const u_char* payload = packet + sizeof(struct ethernet_header_t) + sizeof(struct snap_header_t);
|
||||
|
||||
byte* copy = (byte*)malloc(payload_size);
|
||||
memcpy(copy, payload, payload_size);
|
||||
|
||||
// ELAP does not support short-form DDP, so this must be a long-form DDP packet.
|
||||
struct at_addr_t source, dest;
|
||||
if (payload_size >= sizeof(struct DDP_LONG))
|
||||
{
|
||||
// Extract the protocol address from the header.
|
||||
//
|
||||
// ELAP really shouldn't be looking at the header for the next level of the protocol stack,
|
||||
// but this is a convenient place to glean addresses for the AMT, a process that needs both
|
||||
// hardware and protocol addresses.
|
||||
struct DDP_LONG* header = (struct DDP_LONG*)copy;
|
||||
dest.network = (at_network_t)ntohs(header->dest_net);
|
||||
source.network = (at_network_t)ntohs(header->source_net);
|
||||
dest.node = header->dest_node;
|
||||
source.node = header->source_node;
|
||||
|
||||
enqueue(&elap_port.in, dest, source, LAP_DDP_LONG, payload_size, copy);
|
||||
|
||||
aarp_glean(&source, ðer->source);
|
||||
}
|
||||
else
|
||||
atbridge_printf("ATBridge: Dropping invalid short ELAP frame.\n");
|
||||
}
|
||||
else if (memcmp(&snap->discriminator, &SNAP_AARP, sizeof(snap->discriminator)) == 0)
|
||||
{
|
||||
// Handle an AARP packet.
|
||||
struct aarp_header_t* aarp = (struct aarp_header_t*)(packet + sizeof(struct ethernet_header_t) + sizeof(struct snap_header_t));
|
||||
aarp->dest_proto_addr.addr.network = ntohs(aarp->dest_proto_addr.addr.network);
|
||||
aarp->source_proto_addr.addr.network = ntohs(aarp->source_proto_addr.addr.network);
|
||||
aarp->function = ntohs(aarp->function);
|
||||
aarp->hardware_type = ntohs(aarp->hardware_type);
|
||||
aarp->protocol_type = ntohs(aarp->protocol_type);
|
||||
aarp_handle_packet(aarp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static void elap_receive_all()
|
||||
{
|
||||
while (elap_receive_one());
|
||||
}
|
||||
|
||||
void elap_process()
|
||||
{
|
||||
elap_receive_all();
|
||||
elap_send_all_queued();
|
||||
}
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/** This module implements the ELAP port of the bridge. **/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "../defc.h"
|
||||
#include "atbridge.h"
|
||||
#include "elap.h"
|
||||
#include "port.h"
|
||||
#include "aarp.h"
|
||||
#include "elap_defs.h"
|
||||
#include "pcap_delay.h"
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
#include <Windows.h>
|
||||
#include <NspAPI.h>
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
#include <winsock.h>
|
||||
#include <IPHlpApi.h>
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#include <netinet/in.h>
|
||||
#include <netpacket/packet.h>
|
||||
#endif
|
||||
|
||||
extern int g_ethernet_interface;
|
||||
|
||||
static pcap_t* pcap_session;
|
||||
|
||||
static struct packet_port_t elap_port;
|
||||
static struct ether_addr_t HW_LOCAL;
|
||||
|
||||
/*static void dump_device_list(pcap_if_t* devices)
|
||||
{
|
||||
int i = 0;
|
||||
for(pcap_if_t* device = devices; device; device = device->next)
|
||||
{
|
||||
printf("%d. %s", ++i, device->name);
|
||||
if (device->description)
|
||||
printf(" (%s)\n", device->description);
|
||||
else
|
||||
printf(" (No description available)\n");
|
||||
}
|
||||
}*/
|
||||
|
||||
static void elap_clone_host_mac(pcap_if_t* device) {
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
#ifdef WIN32
|
||||
////
|
||||
// Extract the device GUID, which Windows uses to identify the device.
|
||||
char* name = device->name;
|
||||
while (name && *name != '{')
|
||||
name++;
|
||||
size_t guidLen = strlen(name);
|
||||
|
||||
////
|
||||
// Find and copy the device MAC address.
|
||||
ULONG size = sizeof(IP_ADAPTER_ADDRESSES) * 15;
|
||||
IP_ADAPTER_ADDRESSES* addresses = malloc(size);
|
||||
|
||||
ULONG result = GetAdaptersAddresses(AF_UNSPEC,
|
||||
GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, NULL, addresses, &size);
|
||||
|
||||
if (result == ERROR_BUFFER_OVERFLOW)
|
||||
{
|
||||
// The addresses buffer is too small. Allocate a bigger buffer.
|
||||
free(addresses);
|
||||
addresses = malloc(size);
|
||||
result = GetAdaptersAddresses(AF_UNSPEC,
|
||||
GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, NULL, addresses, &size);
|
||||
}
|
||||
|
||||
if (result == NO_ERROR)
|
||||
{
|
||||
// Search for the desired adapter address.
|
||||
IP_ADAPTER_ADDRESSES* current = addresses;
|
||||
while (current)
|
||||
{
|
||||
if (current->PhysicalAddressLength == ETHER_ADDR_LEN && memcmp(current->AdapterName, name, guidLen) == 0)
|
||||
{
|
||||
memcpy(&HW_LOCAL.mac, ¤t->PhysicalAddress, sizeof(HW_LOCAL.mac));
|
||||
break;
|
||||
}
|
||||
current = current->Next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
halt_printf("ATBridge: Failed to find host MAC address (%d).", result);
|
||||
}
|
||||
|
||||
free(addresses);
|
||||
#else
|
||||
#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(ðer->source, &HW_LOCAL, sizeof(ether->source)) != 0) && /* Ignore packets sent from our node. */
|
||||
(memcmp(ðer->dest, &HW_LOCAL, sizeof(ether->dest)) == 0 || /* Accept packets destined for our node ... */
|
||||
memcmp(ðer->dest, &HW_APPLETALK_BROADCAST, sizeof(ether->dest)) == 0 /* ... or for broadcast. */)
|
||||
)
|
||||
{
|
||||
// Check the 802.2 SNAP header.
|
||||
const struct snap_header_t* snap = (struct snap_header_t*)(packet + sizeof(struct ethernet_header_t));
|
||||
if (snap->dsap == SNAP_DSAP &&
|
||||
snap->ssap == SNAP_SSAP &&
|
||||
snap->control == SNAP_CONTROL)
|
||||
{
|
||||
if (memcmp(&snap->discriminator, &SNAP_APPLETALK, sizeof(snap->discriminator)) == 0)
|
||||
{
|
||||
// Handle an AppleTalk packet.
|
||||
const size_t payload_size = ntohs(ether->length) - sizeof(struct snap_header_t);
|
||||
const u_char* payload = packet + sizeof(struct ethernet_header_t) + sizeof(struct snap_header_t);
|
||||
|
||||
byte* copy = (byte*)malloc(payload_size);
|
||||
memcpy(copy, payload, payload_size);
|
||||
|
||||
// ELAP does not support short-form DDP, so this must be a long-form DDP packet.
|
||||
struct at_addr_t source, dest;
|
||||
if (payload_size >= sizeof(struct DDP_LONG))
|
||||
{
|
||||
// Extract the protocol address from the header.
|
||||
//
|
||||
// ELAP really shouldn't be looking at the header for the next level of the protocol stack,
|
||||
// but this is a convenient place to glean addresses for the AMT, a process that needs both
|
||||
// hardware and protocol addresses.
|
||||
struct DDP_LONG* header = (struct DDP_LONG*)copy;
|
||||
dest.network = (at_network_t)ntohs(header->dest_net);
|
||||
source.network = (at_network_t)ntohs(header->source_net);
|
||||
dest.node = header->dest_node;
|
||||
source.node = header->source_node;
|
||||
|
||||
enqueue(&elap_port.in, dest, source, LAP_DDP_LONG, payload_size, copy);
|
||||
|
||||
aarp_glean(&source, ðer->source);
|
||||
}
|
||||
else
|
||||
atbridge_printf("ATBridge: Dropping invalid short ELAP frame.\n");
|
||||
}
|
||||
else if (memcmp(&snap->discriminator, &SNAP_AARP, sizeof(snap->discriminator)) == 0)
|
||||
{
|
||||
// Handle an AARP packet.
|
||||
struct aarp_header_t* aarp = (struct aarp_header_t*)(packet + sizeof(struct ethernet_header_t) + sizeof(struct snap_header_t));
|
||||
aarp->dest_proto_addr.addr.network = ntohs(aarp->dest_proto_addr.addr.network);
|
||||
aarp->source_proto_addr.addr.network = ntohs(aarp->source_proto_addr.addr.network);
|
||||
aarp->function = ntohs(aarp->function);
|
||||
aarp->hardware_type = ntohs(aarp->hardware_type);
|
||||
aarp->protocol_type = ntohs(aarp->protocol_type);
|
||||
aarp_handle_packet(aarp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static void elap_receive_all() {
|
||||
while (elap_receive_one());
|
||||
}
|
||||
|
||||
void elap_process() {
|
||||
elap_receive_all();
|
||||
elap_send_all_queued();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 }};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
5736
src/config.c
5736
src/config.c
File diff suppressed because it is too large
Load Diff
46
src/config.h
46
src/config.h
|
@ -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
45
src/config.txt
Normal 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
1520
src/debug.c
Normal file
File diff suppressed because it is too large
Load Diff
30
src/debug.h
Normal file
30
src/debug.h
Normal 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();
|
423
src/defc.h
423
src/defc.h
|
@ -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
|
||||
|
|
1651
src/engine_c.c
1651
src/engine_c.c
File diff suppressed because it is too large
Load Diff
47
src/fix_mac_menu.m
Normal file
47
src/fix_mac_menu.m
Normal 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
203
src/fst.h
Normal 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
63
src/glog.c
Normal 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
30
src/glog.h
Normal 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
186
src/gsos.h
Normal 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
BIN
src/gsport
Executable file
Binary file not shown.
650
src/host_common.c
Normal file
650
src/host_common.c
Normal 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
186
src/host_common.h
Normal 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
1879
src/host_fst.c
Normal file
File diff suppressed because it is too large
Load Diff
1567
src/host_mli.c
Normal file
1567
src/host_mli.c
Normal file
File diff suppressed because it is too large
Load Diff
2078
src/icongs.h
Normal file
2078
src/icongs.h
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
4459
src/moremem.c
4459
src/moremem.c
File diff suppressed because it is too large
Load Diff
453
src/options.c
Normal file
453
src/options.c
Normal 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 Don’t ignore bad memory accesses\n");
|
||||
printf(" -noignhalt Don’t 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
29
src/options.h
Normal 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
|
38
src/protos.h
38
src/protos.h
|
@ -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
887
src/sdl2_driver.c
Normal 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
222
src/sdl2snd_driver.c
Normal 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
|
||||
}
|
4422
src/sim65816.c
4422
src/sim65816.c
File diff suppressed because it is too large
Load Diff
|
@ -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
6
src/tfe/CMakeLists.txt
Normal 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)
|
||||
|
|
@ -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
|
||||
|
|
2139
src/tfe/tfe.c
2139
src/tfe/tfe.c
File diff suppressed because it is too large
Load Diff
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
533
src/unix_host_common.c
Normal 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
21
src/vars_osx_sdl2
Normal 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
20
src/vars_osx_x11
Normal 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
18
src/vars_rpilinux_fb
Normal 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
16
src/vars_rpilinux_sdl2
Normal 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
|
||||
|
|
@ -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 =
|
||||
|
|
|
@ -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
21
src/vars_win32_sdl2
Normal 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 =
|
|
@ -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
21
src/vars_x86linux_sdl2
Normal 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
21
src/vars_x86linux_x11
Normal 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
|
|
@ -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
|
||||
|
|
5854
src/video.c
5854
src/video.c
File diff suppressed because it is too large
Load Diff
633
src/win32_host_common.c
Normal file
633
src/win32_host_common.c
Normal 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
1862
src/win32_host_fst.c
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user