From 58cc512bc99eedb6c331651174c9e5c5795d3473 Mon Sep 17 00:00:00 2001 From: Peter Date: Tue, 15 Apr 2014 03:23:34 +0000 Subject: [PATCH] Merged revision(s) 262-316 from branches/pneubauer/appletalk: Add support for AppleTalk emulation and bridging. --- doc/web/src/site/apt/history.apt | 3 + doc/web/src/site/apt/operating.apt | 7 +- lib/arch/win32/cygstdc++-6.dll | Bin 0 -> 915485 bytes src/Makefile | 15 +- src/SDL.dll | Bin 324096 -> 0 bytes src/arch/win32/tfearch.c | 563 --- src/atbridge/aarp.c | 302 ++ src/atbridge/aarp.h | 37 + src/atbridge/atalk.h | 137 + src/atbridge/atbridge.c | 426 ++ src/atbridge/atbridge.h | 47 + src/atbridge/atbridge.vcxproj | 111 + src/atbridge/atbridge.vcxproj.filters | 69 + src/atbridge/elap.c | 396 ++ src/atbridge/elap.h | 36 + src/atbridge/elap_defs.h | 117 + src/atbridge/llap.c | 332 ++ src/atbridge/llap.h | 37 + src/atbridge/notes.txt | 61 + src/atbridge/pcap_delay.c | 170 + src/atbridge/pcap_delay.h | 46 + src/atbridge/port.c | 139 + src/atbridge/port.h | 58 + src/config.c | 6681 +++++++++++++------------ src/engine_c.c | 14 +- src/gsport.sln | 11 +- src/gsport.vcxproj | 30 +- src/gsport.vcxproj.filters | 6 + src/moremem.c | 23 +- src/printer.h | 9 +- src/protos.h | 1 + src/scc.c | 2876 ++++++----- src/scc.h | 12 +- src/scc_llap.c | 156 + src/scc_llap.h | 26 + src/scc_socket_driver.c | 5 +- src/scc_windriver.c | 5 + src/sim65816.c | 5079 ++++++++++--------- src/tfe/tfe.vcxproj | 6 +- src/tfe/tfe.vcxproj.filters | 2 +- src/{arch/unix => tfe}/tfearch.c | 54 +- src/vars_fbrpilinux | 9 +- src/vars_pi | 9 +- src/vars_win32 | 13 +- src/vars_win32_sdl | 14 +- src/vars_x86linux | 8 +- 46 files changed, 10332 insertions(+), 7826 deletions(-) create mode 100644 lib/arch/win32/cygstdc++-6.dll delete mode 100644 src/SDL.dll delete mode 100644 src/arch/win32/tfearch.c create mode 100644 src/atbridge/aarp.c create mode 100644 src/atbridge/aarp.h create mode 100644 src/atbridge/atalk.h create mode 100644 src/atbridge/atbridge.c create mode 100644 src/atbridge/atbridge.h create mode 100644 src/atbridge/atbridge.vcxproj create mode 100644 src/atbridge/atbridge.vcxproj.filters create mode 100644 src/atbridge/elap.c create mode 100644 src/atbridge/elap.h create mode 100644 src/atbridge/elap_defs.h create mode 100644 src/atbridge/llap.c create mode 100644 src/atbridge/llap.h create mode 100644 src/atbridge/notes.txt create mode 100644 src/atbridge/pcap_delay.c create mode 100644 src/atbridge/pcap_delay.h create mode 100644 src/atbridge/port.c create mode 100644 src/atbridge/port.h create mode 100644 src/scc_llap.c create mode 100644 src/scc_llap.h rename src/{arch/unix => tfe}/tfearch.c (92%) mode change 100755 => 100644 diff --git a/doc/web/src/site/apt/history.apt b/doc/web/src/site/apt/history.apt index f1b8794..ab4a1dd 100644 --- a/doc/web/src/site/apt/history.apt +++ b/doc/web/src/site/apt/history.apt @@ -17,6 +17,9 @@ GSport Release History * Added Imagewriter LQ printer emulation + * Added AppleTalk networking emulation with bridging to EtherTalk. + Related, there are improvements and bug fixes to the SCC emulation. + [] Bug fixes: diff --git a/doc/web/src/site/apt/operating.apt b/doc/web/src/site/apt/operating.apt index 198da77..6ed5553 100644 --- a/doc/web/src/site/apt/operating.apt +++ b/doc/web/src/site/apt/operating.apt @@ -494,9 +494,10 @@ you override it with "-audio 1". * SCC (Serial Port) emulation - GSport emulates the two serial ports on a IIgs as being two Unix sockets. -Port 1 (printer port) is at socket address 6501, and port 2 (modem) -is at socket address 6502. + You may use the SCC ports as either a LocalTalk networking connection +or as traditional serial ports. GSport emulates the two serial ports on +a IIgs as being two Unix sockets. Port 1 (printer port) is at socket +address 6501, and port 2 (modem) is at socket address 6502. By default, slot 1 is emulated using a simple receive socket, and slot 2 emulates a Virtual Modem. diff --git a/lib/arch/win32/cygstdc++-6.dll b/lib/arch/win32/cygstdc++-6.dll new file mode 100644 index 0000000000000000000000000000000000000000..77e79f65adcd73c9895136c0eb1f8f982be56ba4 GIT binary patch literal 915485 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 literal 0 HcmV?d00001 diff --git a/src/Makefile b/src/Makefile index 0eb8e81..b798a3d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -3,7 +3,10 @@ OBJECTS1 = adb.o clock.o config.o dis.o engine_c.o scc.o iwm.o \ joystick_driver.o moremem.o paddles.o parallel.o printer.o \ sim65816.o smartport.o sound.o sound_driver.o video.o \ - scc_socket_driver.o scc_imagewriter.o imagewriter.o + scc_socket_driver.o imagewriter.o scc_imagewriter.o scc_llap.o +ATOBJ = atbridge/aarp.o atbridge/atbridge.o atbridge/elap.o atbridge/llap.o atbridge/port.o +PCAPOBJ = atbridge/pcap_delay.o +TFEOBJ = tfe/tfe.o tfe/tfearch.o tfe/tfesupp.o include vars @@ -139,12 +142,19 @@ compile_time.o: $(OBJECTS) # dependency stuff adb.o: adb.c adb.h defc.h defcomm.h iwm.h protos.h +atbridge/aarp.o: defc.h atbridge/atbridge.h atbridge/port.h atbridge/elap.h atbridge/aarp.h atbridge/elap_defs.h +atbridge/atbridge.o: defc.h atbridge/atbridge.h atbridge/port.h atbridge/elap.h atbridge/llap.h atbridge/aarp.h +atbridge/elap.o: defc.h atbridge/atbridge.h atbridge/port.h atbridge/elap.h atbridge/aarp.h atbridge/elap_defs.h atbridge/pcap_delay.h +atbridge/llap.o: defc.h atbridge/atbridge.h atbridge/port.h atbridge/llap.h +atbridge/port.o: atbridge/atalk.h atbridge/port.h +atbridge/pcap_delay.o: atbridge/pcap_delay.h engine_c.o: engine_c.c defc.h defcomm.h iwm.h protos.h protos_engine_c.h size_c.h op_routs.h defs_instr.h 8inst_c.h 16inst_c.h clock.o: clock.c defc.h defcomm.h iwm.h protos.h compile_time.o: compile_time.c config.o: config.c defc.h defcomm.h iwm.h protos.h config.h dis.o: dis.c defc.h defcomm.h iwm.h protos.h disas.h scc.o: scc.c defc.h defcomm.h iwm.h protos.h scc.h +scc_llap.o: atbridge/atbridge.h atbridge/llap.h defc.h scc.h scc_socket_driver.o: scc_socket_driver.c defc.h defcomm.h iwm.h protos.h scc.h scc_windriver.o: scc_windriver.c defc.h defcomm.h iwm.h protos.h scc.h scc_macdriver.o: scc_macdriver.c defc.h defcomm.h iwm.h protos.h scc.h @@ -162,9 +172,8 @@ sound.o: sound.c defc.h defcomm.h iwm.h protos.h sound.h sound_driver.o: sound_driver.c defc.h defcomm.h iwm.h protos.h sound.h video.o: video.c defc.h defcomm.h iwm.h protos.h superhires.h gsportfont.h tfe.o: tfe/tfe.c tfe/tfe.h tfe/tfe_protos.h -tfearch.o:arch/win32/tfearch.c tfe/tfearch.h tfe/tfe_protos.h +tfearch.o: tfe/tfearch.c tfe/tfearch.h tfe/tfe_protos.h tfesupp.o: tfe/tfesupp.c tfe/tfesupp.h tfe/tfe_protos.h -uilib.o: tfe/uilib.c tfe/uilib.h tfe/tfe_protos.h macdriver.o: macdriver.c defc.h defcomm.h iwm.h protos.h protos_macdriver.h macdriver_console.o: macdriver_console.c defc.h defcomm.h iwm.h protos.h protos_macdriver.h macdriver_generic.o: macdriver_generic.c defc.h defcomm.h iwm.h protos.h protos_macdriver.h diff --git a/src/SDL.dll b/src/SDL.dll deleted file mode 100644 index 628cdfcf0183d17c802025e041e685f3f92537f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 324096 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 E02O&aUH||9 diff --git a/src/arch/win32/tfearch.c b/src/arch/win32/tfearch.c deleted file mode 100644 index 7ac6a98..0000000 --- a/src/arch/win32/tfearch.c +++ /dev/null @@ -1,563 +0,0 @@ -/* - * tfearch.c - TFE ("The final ethernet") emulation, - * architecture-dependant stuff - * - * Written by - * Spiro Trikaliotis - * - * This file is part of VICE, the Versatile Commodore Emulator. - * See README for copyright notice. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307 USA. - * - */ - - -/* #define WPCAP */ - -#define WIN32_LEAN_AND_MEAN /* Tell windows we want less header gunk */ -#define STRICT /* Tell Windows we want compile type checks */ -#include - -#include "pcap.h" -#include "../../tfe/tfesupp.h" - -#include -#include -#include -#include - -#include "..\..\defc.h" -#include "../../tfe/protos_tfe.h" - - -typedef pcap_t *(*pcap_open_live_t)(const char *, int, int, int, char *); -typedef int (*pcap_dispatch_t)(pcap_t *, int, pcap_handler, u_char *); -typedef int (*pcap_setnonblock_t)(pcap_t *, int, char *); -typedef int (*pcap_datalink_t)(pcap_t *); -typedef int (*pcap_findalldevs_t)(pcap_if_t **, char *); -typedef void (*pcap_freealldevs_t)(pcap_if_t *); -typedef int (*pcap_sendpacket_t)(pcap_t *p, u_char *buf, int size); - -/** #define TFE_DEBUG_ARCH 1 **/ -/** #define TFE_DEBUG_PKTDUMP 1 **/ - -/* #define TFE_DEBUG_FRAMES - might be defined in TFE.H! */ - -#define TFE_DEBUG_WARN 1 /* this should not be deactivated */ - -static pcap_open_live_t p_pcap_open_live; -static pcap_dispatch_t p_pcap_dispatch; -static pcap_setnonblock_t p_pcap_setnonblock; -static pcap_findalldevs_t p_pcap_findalldevs; -static pcap_freealldevs_t p_pcap_freealldevs; -static pcap_sendpacket_t p_pcap_sendpacket; -static pcap_datalink_t p_pcap_datalink; - -static HINSTANCE pcap_library = NULL; - - -/* ------------------------------------------------------------------------- */ -/* variables needed */ - - -//static log_t tfe_arch_log = LOG_ERR; - - -static pcap_if_t *TfePcapNextDev = NULL; -static pcap_if_t *TfePcapAlldevs = NULL; -static pcap_t *TfePcapFP = NULL; - -static char TfePcapErrbuf[PCAP_ERRBUF_SIZE]; - -#ifdef TFE_DEBUG_PKTDUMP - -static -void debug_output( const char *text, BYTE *what, int count ) -{ - char buffer[256]; - char *p = buffer; - char *pbuffer1 = what; - int len1 = count; - int i; - - sprintf(buffer, "\n%s: length = %u\n", text, len1); - OutputDebugString(buffer); - do { - p = buffer; - for (i=0; (i<8) && len1>0; len1--, i++) { - sprintf( p, "%02x ", (unsigned int)(unsigned char)*pbuffer1++); - p += 3; - } - *(p-1) = '\n'; *p = 0; - OutputDebugString(buffer); - } while (len1>0); -} -#endif // #ifdef TFE_DEBUG_PKTDUMP - - -static -void TfePcapFreeLibrary(void) -{ - if (pcap_library) { - if (!FreeLibrary(pcap_library)) { - //log_message(tfe_arch_log, "FreeLibrary WPCAP.DLL failed!"); - } - pcap_library = NULL; - - p_pcap_open_live = NULL; - p_pcap_dispatch = NULL; - p_pcap_setnonblock = NULL; - p_pcap_findalldevs = NULL; - p_pcap_freealldevs = NULL; - p_pcap_sendpacket = NULL; - p_pcap_datalink = NULL; - } -} - -/* since I don't like typing too much... */ -#define GET_PROC_ADDRESS_AND_TEST( _name_ ) \ - p_##_name_ = (_name_##_t) GetProcAddress(pcap_library, #_name_ ); \ - if (!p_##_name_ ) { \ - /*log_message(tfe_arch_log, "GetProcAddress " #_name_ " failed!");*/ \ - TfePcapFreeLibrary(); \ - return FALSE; \ - } - -static -BOOL TfePcapLoadLibrary(void) -{ - if (!pcap_library) { - pcap_library = LoadLibrary("wpcap.dll"); - - if (!pcap_library) { - //log_message(tfe_arch_log, "LoadLibrary WPCAP.DLL failed!" ); - return FALSE; - } - - GET_PROC_ADDRESS_AND_TEST(pcap_open_live); - GET_PROC_ADDRESS_AND_TEST(pcap_dispatch); - GET_PROC_ADDRESS_AND_TEST(pcap_setnonblock); - GET_PROC_ADDRESS_AND_TEST(pcap_findalldevs); - GET_PROC_ADDRESS_AND_TEST(pcap_freealldevs); - GET_PROC_ADDRESS_AND_TEST(pcap_sendpacket); - GET_PROC_ADDRESS_AND_TEST(pcap_datalink); - } - - return TRUE; -} - -#undef GET_PROC_ADDRESS_AND_TEST - - -/* -static -void TfePcapCloseAdapter(void) -{ - if (TfePcapAlldevs) { - (*p_pcap_freealldevs)(TfePcapAlldevs); - TfePcapAlldevs = NULL; - } -} -*/ - -/* - These functions let the UI enumerate the available interfaces. - - First, TfeEnumAdapterOpen() is used to start enumeration. - - TfeEnumAdapter is then used to gather information for each adapter present - on the system, where: - - ppname points to a pointer which will hold the name of the interface - ppdescription points to a pointer which will hold the description of the interface - - For each of these parameters, new memory is allocated, so it has to be - freed with lib_free(). - - TfeEnumAdapterClose() must be used to stop processing. - - Each function returns 1 on success, and 0 on failure. - TfeEnumAdapter() only fails if there is no more adpater; in this case, - *ppname and *ppdescription are not altered. -*/ -int tfe_arch_enumadapter_open(void) -{ - if (!TfePcapLoadLibrary()) { - return 0; - } - - if ((*p_pcap_findalldevs)(&TfePcapAlldevs, TfePcapErrbuf) == -1) - { - //log_message(tfe_arch_log, "ERROR in TfeEnumAdapterOpen: pcap_findalldevs: '%s'", TfePcapErrbuf); - return 0; - } - - if (!TfePcapAlldevs) { - /*log_message(tfe_arch_log, "ERROR in TfeEnumAdapterOpen, finding all pcap devices - " - "Do we have the necessary privilege rights?");*/ - return 0; - } - - TfePcapNextDev = TfePcapAlldevs; - - return 1; -} - -int tfe_arch_enumadapter(char **ppname, char **ppdescription) -{ - if (!TfePcapNextDev) - return 0; - - *ppname = lib_stralloc(TfePcapNextDev->name); - *ppdescription = lib_stralloc(TfePcapNextDev->description); - - TfePcapNextDev = TfePcapNextDev->next; - - return 1; -} - -int tfe_arch_enumadapter_close(void) -{ - if (TfePcapAlldevs) { - (*p_pcap_freealldevs)(TfePcapAlldevs); - TfePcapAlldevs = NULL; - } - return 1; -} - -static -BOOL TfePcapOpenAdapter(const char *interface_name) -{ - pcap_if_t *TfePcapDevice = NULL; - - if (!tfe_enumadapter_open()) { - return FALSE; - } - else { - /* look if we can find the specified adapter */ - char *pname; - char *pdescription; - BOOL found = FALSE; - - if (interface_name) { - /* we have an interface name, try it */ - TfePcapDevice = TfePcapAlldevs; - - while (tfe_enumadapter(&pname, &pdescription)) { - if (strcmp(pname, interface_name)==0) { - found = TRUE; - } - lib_free(pname); - lib_free(pdescription); - if (found) break; - TfePcapDevice = TfePcapNextDev; - } - } - - if (!found) { - /* just take the first adapter */ - TfePcapDevice = TfePcapAlldevs; - } - } - - TfePcapFP = (*p_pcap_open_live)(TfePcapDevice->name, 1700, 1, 20, TfePcapErrbuf); - if ( TfePcapFP == NULL) - { - //log_message(tfe_arch_log, "ERROR opening adapter: '%s'", TfePcapErrbuf); - tfe_enumadapter_close(); - return FALSE; - } - - if ((*p_pcap_setnonblock)(TfePcapFP, 1, TfePcapErrbuf)<0) - { - //log_message(tfe_arch_log, "WARNING: Setting PCAP to non-blocking failed: '%s'", TfePcapErrbuf); - } - - /* Check the link layer. We support only Ethernet for simplicity. */ - if((*p_pcap_datalink)(TfePcapFP) != DLT_EN10MB) - { - //log_message(tfe_arch_log, "ERROR: TFE works only on Ethernet networks."); - tfe_enumadapter_close(); - return FALSE; - } - - tfe_enumadapter_close(); - return TRUE; -} - - -/* ------------------------------------------------------------------------- */ -/* the architecture-dependend functions */ - - -int tfe_arch_init(void) -{ - //tfe_arch_log = log_open("TFEARCH"); - - if (!TfePcapLoadLibrary()) { - return 0; - } - - return 1; -} - -void tfe_arch_pre_reset( void ) -{ -#ifdef TFE_DEBUG_ARCH - log_message( tfe_arch_log, "tfe_arch_pre_reset()." ); -#endif -} - -void tfe_arch_post_reset( void ) -{ -#ifdef TFE_DEBUG_ARCH - log_message( tfe_arch_log, "tfe_arch_post_reset()." ); -#endif -} - -int tfe_arch_activate(const char *interface_name) -{ -#ifdef TFE_DEBUG_ARCH - log_message( tfe_arch_log, "tfe_arch_activate()." ); -#endif - if (!TfePcapOpenAdapter(interface_name)) { - return 0; - } - return 1; -} - -void tfe_arch_deactivate( void ) -{ -#ifdef TFE_DEBUG_ARCH - log_message( tfe_arch_log, "tfe_arch_deactivate()." ); -#endif -} - -void tfe_arch_set_mac( const BYTE mac[6] ) -{ -#if defined(TFE_DEBUG_ARCH) || defined(TFE_DEBUG_FRAMES) - log_message( tfe_arch_log, "New MAC address set: %02X:%02X:%02X:%02X:%02X:%02X.", - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] ); -#endif -} - -/* -void tfe_arch_set_hashfilter(const DWORD hash_mask[2]) -{ -#if defined(TFE_DEBUG_ARCH) || defined(TFE_DEBUG_FRAMES) - log_message( tfe_arch_log, "New hash filter set: %08X:%08X.", - hash_mask[1], hash_mask[0]); -#endif -} -*/ - -/* -void tfe_arch_receive_remove_committed_frame(void) -{ -#ifdef TFE_DEBUG_ARCH - log_message( tfe_arch_log, "tfe_arch_receive_remove_committed_frame()." ); -#endif -} -*/ - -void tfe_arch_recv_ctl( int bBroadcast, /* broadcast */ - int bIA, /* individual address (IA) */ - int bMulticast, /* multicast if address passes the hash filter */ - int bCorrect, /* accept correct frames */ - int bPromiscuous, /* promiscuous mode */ - int bIAHash /* accept if IA passes the hash filter */ - ) -{ -#if defined(TFE_DEBUG_ARCH) || defined(TFE_DEBUG_FRAMES) - log_message( tfe_arch_log, "tfe_arch_recv_ctl() called with the following parameters:" ); - log_message( tfe_arch_log, "\tbBroadcast = %s", bBroadcast ? "TRUE" : "FALSE" ); - log_message( tfe_arch_log, "\tbIA = %s", bIA ? "TRUE" : "FALSE" ); - log_message( tfe_arch_log, "\tbMulticast = %s", bMulticast ? "TRUE" : "FALSE" ); - log_message( tfe_arch_log, "\tbCorrect = %s", bCorrect ? "TRUE" : "FALSE" ); - log_message( tfe_arch_log, "\tbPromiscuous = %s", bPromiscuous ? "TRUE" : "FALSE" ); - log_message( tfe_arch_log, "\tbIAHash = %s", bIAHash ? "TRUE" : "FALSE" ); -#endif -} - -void tfe_arch_line_ctl(int bEnableTransmitter, int bEnableReceiver ) -{ -#if defined(TFE_DEBUG_ARCH) || defined(TFE_DEBUG_FRAMES) - log_message( tfe_arch_log, "tfe_arch_line_ctl() called with the following parameters:" ); - log_message( tfe_arch_log, "\tbEnableTransmitter = %s", bEnableTransmitter ? "TRUE" : "FALSE" ); - log_message( tfe_arch_log, "\tbEnableReceiver = %s", bEnableReceiver ? "TRUE" : "FALSE" ); -#endif -} - - -typedef struct TFE_PCAP_INTERNAL_tag { - - unsigned int len; - BYTE *buffer; - -} TFE_PCAP_INTERNAL; - -/* Callback function invoked by libpcap for every incoming packet */ -static -void TfePcapPacketHandler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data) -{ - TFE_PCAP_INTERNAL *pinternal = (TFE_PCAP_INTERNAL*)param; - - /* determine the count of bytes which has been returned, - * but make sure not to overrun the buffer - */ - if (header->caplen < pinternal->len) - pinternal->len = header->caplen; - - memcpy(pinternal->buffer, pkt_data, pinternal->len); -} - -/* the following function receives a frame. - - If there's none, it returns a -1. - If there is one, it returns the length of the frame in bytes. - - It copies the frame to *buffer and returns the number of copied - bytes as return value. - - At most 'len' bytes are copied. -*/ -static -int tfe_arch_receive_frame(TFE_PCAP_INTERNAL *pinternal) -{ - int ret = -1; - - /* check if there is something to receive */ - if ((*p_pcap_dispatch)(TfePcapFP, 1, (pcap_handler)TfePcapPacketHandler, (u_char*)pinternal)!=0) { - /* Something has been received */ - ret = pinternal->len; - } - -#ifdef TFE_DEBUG_ARCH - log_message( tfe_arch_log, "tfe_arch_receive_frame() called, returns %d.", ret ); -#endif - - return ret; -} - -void tfe_arch_transmit(int force, /* FORCE: Delete waiting frames in transmit buffer */ - int onecoll, /* ONECOLL: Terminate after just one collision */ - int inhibit_crc, /* INHIBITCRC: Do not append CRC to the transmission */ - int tx_pad_dis, /* TXPADDIS: Disable padding to 60 Bytes */ - int txlength, /* Frame length */ - BYTE *txframe /* Pointer to the frame to be transmitted */ - ) -{ -#ifdef TFE_DEBUG_ARCH - log_message( tfe_arch_log, "tfe_arch_transmit() called, with: " - "force = %s, onecoll = %s, inhibit_crc=%s, tx_pad_dis=%s, txlength=%u", - force ? "TRUE" : "FALSE", - onecoll ? "TRUE" : "FALSE", - inhibit_crc ? "TRUE" : "FALSE", - tx_pad_dis ? "TRUE" : "FALSE", - txlength - ); -#endif - -#ifdef TFE_DEBUG_PKTDUMP - debug_output( "Transmit frame: ", txframe, txlength); -#endif // #ifdef TFE_DEBUG_PKTDUMP - - if ((*p_pcap_sendpacket)(TfePcapFP, txframe, txlength) == -1) { - //log_message(tfe_arch_log, "WARNING! Could not send packet!"); - } -} - -/* - tfe_arch_receive() - - This function checks if there was a frame received. - If so, it returns 1, else 0. - - If there was no frame, none of the parameters is changed! - - If there was a frame, the following actions are done: - - - at maximum *plen byte are transferred into the buffer given by pbuffer - - *plen gets the length of the received frame, EVEN if this is more - than has been copied to pbuffer! - - if the dest. address was accepted by the hash filter, *phashed is set, else - cleared. - - if the dest. address was accepted by the hash filter, *phash_index is - set to the number of the rule leading to the acceptance - - if the receive was ok (good CRC and valid length), *prx_ok is set, - else cleared. - - if the dest. address was accepted because it's exactly our MAC address - (set by tfe_arch_set_mac()), *pcorrect_mac is set, else cleared. - - if the dest. address was accepted since it was a broadcast address, - *pbroadcast is set, else cleared. - - if the received frame had a crc error, *pcrc_error is set, else cleared -*/ -int tfe_arch_receive(BYTE *pbuffer , /* where to store a frame */ - int *plen, /* IN: maximum length of frame to copy; - OUT: length of received frame - OUT can be bigger than IN if received frame was - longer than supplied buffer */ - int *phashed, /* set if the dest. address is accepted by the hash filter */ - int *phash_index, /* hash table index if hashed == TRUE */ - int *prx_ok, /* set if good CRC and valid length */ - int *pcorrect_mac, /* set if dest. address is exactly our IA */ - int *pbroadcast, /* set if dest. address is a broadcast address */ - int *pcrc_error /* set if received frame had a CRC error */ - ) -{ - int len; - - TFE_PCAP_INTERNAL internal = { *plen, pbuffer }; - - -#ifdef TFE_DEBUG_ARCH - log_message( tfe_arch_log, "tfe_arch_receive() called, with *plen=%u.", *plen ); -#endif - - assert((*plen&1)==0); - - len = tfe_arch_receive_frame(&internal); - - if (len!=-1) { - -#ifdef TFE_DEBUG_PKTDUMP - debug_output( "Received frame: ", internal.buffer, internal.len ); -#endif // #ifdef TFE_DEBUG_PKTDUMP - - if (len&1) - ++len; - - *plen = len; - - /* we don't decide if this frame fits the needs; - * by setting all zero, we let tfe.c do the work - * for us - */ - *phashed = - *phash_index = - *pbroadcast = - *pcorrect_mac = - *pcrc_error = 0; - - /* this frame has been received correctly */ - *prx_ok = 1; - - return 1; - } - - return 0; -} diff --git a/src/atbridge/aarp.c b/src/atbridge/aarp.c new file mode 100644 index 0000000..bdc3aef --- /dev/null +++ b/src/atbridge/aarp.c @@ -0,0 +1,302 @@ +/* +GSport - an Apple //gs Emulator +Copyright (C) 2013-2014 by Peter Neubauer + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/** This module implements AARP, a necessary protocol for ELAP communication. **/ + +#include +#include +#include "../defc.h" +#include "atbridge.h" +#include "elap.h" +#include "port.h" +#include "elap_defs.h" +#include "aarp.h" + +#ifdef WIN32 +#include +#elif __linux__ +#include +#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; + } + } +} diff --git a/src/atbridge/aarp.h b/src/atbridge/aarp.h new file mode 100644 index 0000000..461bcec --- /dev/null +++ b/src/atbridge/aarp.h @@ -0,0 +1,37 @@ +/* +GSport - an Apple //gs Emulator +Copyright (C) 2013-2014 by Peter Neubauer + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +struct at_addr_t; +struct aarp_header_t; +struct ether_addr_t; + +void aarp_init(); +void aarp_shutdown(); + +const struct ether_addr_t* aarp_request_hardware(const struct at_addr_t* protocol); +const struct at_addr_t* aarp_request_protocol(const struct ether_addr_t* hardware); + +bool aarp_retry(); +void aarp_retry_reset(); + +void aarp_glean(const struct at_addr_t* protocol, const struct ether_addr_t* hardware); + +bool aarp_address_used(const struct at_addr_t* protocol); + +void aarp_handle_packet(const struct aarp_header_t* aarp); \ No newline at end of file diff --git a/src/atbridge/atalk.h b/src/atbridge/atalk.h new file mode 100644 index 0000000..d4d1df4 --- /dev/null +++ b/src/atbridge/atalk.h @@ -0,0 +1,137 @@ +/* +GSport - an Apple //gs Emulator +Copyright (C) 2013-2014 by Peter Neubauer + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +typedef byte at_node_t; +static const at_node_t at_broadcast_node = 0xFF; + +typedef word16 at_network_t; + +#pragma pack(push, 1) +struct at_addr_t +{ + at_network_t network; + at_node_t node; +}; +#pragma pack(pop) + +enum LAP_TYPES { /* reference C-6 */ + LAP_DDP_SHORT = 0x01, + LAP_DDP_LONG = 0x02 +}; + +enum DDP_SOCKETS { /* reference C-7 */ + DDP_SOCKET_INVALID_00 = 0x00, + DDP_SOCKET_RTMP = 0x01, + DDP_SOCKET_NIS = 0x02, + DDP_SOCKET_ECHO = 0x04, + DDP_SOCKET_ZIS = 0x06, + DDP_SOCKET_INVALID_FF = 0xFF, +}; + +enum DDP_TYPES { /* reference C-6 */ + DDP_TYPE_INVALID = 0x00, + DDP_TYPE_RTMP = 0x01, + DDP_TYPE_NBP = 0x02, + DDP_TYPE_ATP = 0x03, + DDP_TYPE_AEP = 0x04, + DDP_TYPE_RTMP_REQUEST = 0x05, + DDP_TYPE_ZIP = 0x06, + DDP_TYPE_ADSP = 0x07, + DDP_TYPE_RESERVED_08 = 0x08, + DDP_TYPE_RESERVED_09 = 0x09, + DDP_TYPE_RESERVED_0A = 0x0A, + DDP_TYPE_RESERVED_0B = 0x0B, + DDP_TYPE_RESERVED_0C = 0x0C, + DDP_TYPE_RESERVED_0D = 0x0D, + DDP_TYPE_RESERVED_0E = 0x0E, + DDP_TYPE_RESERVED_0F = 0x0F +}; + +#pragma pack(push, 1) +struct DDP_LONG +{ + byte length[2]; + word16 checksum; + at_network_t dest_net; + at_network_t source_net; + at_node_t dest_node; + at_node_t source_node; + byte dest_socket; + byte source_socket; + byte type; +}; + +struct DDP_SHORT +{ + byte length[2]; + byte dest_socket; + byte source_socket; + byte type; +}; + +enum RTMP_FUNCTIONS { /* reference C-8*/ + RTMP_FUNCTION_REQUEST = 0x01, + RTMP_FUNCTION_RDR_SPLIT = 0x02, + RTMP_FUNCTION_RDR_NO_SPLIT = 0x03 +}; + +struct rtmp_request_t +{ + byte function; +}; + +struct rtmp_nonextended_data_t +{ + at_network_t net; + byte id_length; + at_node_t node; + word16 zero; + byte delimiter; +}; + +struct rtmp_nonextended_response_t +{ + at_network_t net; + byte id_length; + at_node_t node; +}; + +struct rtmp_extended_data_t +{ + at_network_t net; + byte id_length; + at_node_t node; +}; + +struct rtmp_nonextended_tuple_t +{ + at_network_t net; + byte distance; +}; + +struct rtmp_extended_tuple_t +{ + at_network_t range_start; + byte distance; + at_network_t range_end; + byte delimiter; +}; + +static const byte RTMP_TUPLE_DELIMITER = 0x82; +#pragma pack(pop) \ No newline at end of file diff --git a/src/atbridge/atbridge.c b/src/atbridge/atbridge.c new file mode 100644 index 0000000..00f7343 --- /dev/null +++ b/src/atbridge/atbridge.c @@ -0,0 +1,426 @@ +/* +GSport - an Apple //gs Emulator +Copyright (C) 2013-2014 by Peter Neubauer + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/** This module is the "heart" of the bridge and provides the connection between the ELAP and LLAP ports. **/ + +#include +#include "../defc.h" +#include +#include +#include "atbridge.h" +#include "port.h" +#include "elap.h" +#include "llap.h" +#include "aarp.h" + +#ifdef WIN32 +#include +#elif __linux__ +#include +#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); + } +} \ No newline at end of file diff --git a/src/atbridge/atbridge.h b/src/atbridge/atbridge.h new file mode 100644 index 0000000..f7bb27e --- /dev/null +++ b/src/atbridge/atbridge.h @@ -0,0 +1,47 @@ +/* +GSport - an Apple //gs Emulator +Copyright (C) 2013-2014 by Peter Neubauer + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/** +ATBridge is a limited function AppleTalk bridge that allows GSPort to connect to an +EtherTalk network. The bridge has two ports, one the emulated LocalTalk port and the +other an EtherTalk Phase II port. + +Due to timing requirements of LocalTalk, it is not reasonable to transparently bridge +LLAP traffic to ELAP. For example, implementing the lapENQ/lapACK LLAP control packets +requires AARP queries on the ELAP port, which can't be reasonably done within the 200us +LLAP interframe gap. So, we must implement the local bridge functionality detailed +in "Inside AppleTalk", including AARP, RTMP, ZIP, and DDP routing. +**/ +#include "atalk.h" + +bool atbridge_init(); +void atbridge_shutdown(); +void atbridge_process(); + +void atbridge_set_diagnostics(bool enabled); +bool atbridge_get_diagnostics(); +void atbridge_printf(const char *fmt, ...); + +const struct at_addr_t* atbridge_get_addr(); +const at_network_t atbridge_get_net(); +const at_node_t atbridge_get_node(); +void atbridge_set_net(at_network_t net); +void atbridge_set_node(at_node_t node); +bool atbridge_address_used(const struct at_addr_t* addr); + diff --git a/src/atbridge/atbridge.vcxproj b/src/atbridge/atbridge.vcxproj new file mode 100644 index 0000000..3a05747 --- /dev/null +++ b/src/atbridge/atbridge.vcxproj @@ -0,0 +1,111 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + + + + + + + + + + + + + + + + + + + + + + + {2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B} + Win32Proj + atbridge + + + + StaticLibrary + true + v120 + MultiByte + + + StaticLibrary + false + v120 + true + MultiByte + + + + + + + + + + + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + Default + false + + + Windows + true + + + %(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + Speed + StreamingSIMDExtensions2 + Default + + + Windows + true + true + true + + + %(AdditionalDependencies) + + + + + + \ No newline at end of file diff --git a/src/atbridge/atbridge.vcxproj.filters b/src/atbridge/atbridge.vcxproj.filters new file mode 100644 index 0000000..ce93137 --- /dev/null +++ b/src/atbridge/atbridge.vcxproj.filters @@ -0,0 +1,69 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + + \ No newline at end of file diff --git a/src/atbridge/elap.c b/src/atbridge/elap.c new file mode 100644 index 0000000..c21c710 --- /dev/null +++ b/src/atbridge/elap.c @@ -0,0 +1,396 @@ +/* +GSport - an Apple //gs Emulator +Copyright (C) 2013-2014 by Peter Neubauer + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/** This module implements the ELAP port of the bridge. **/ + +#include +#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 +#include +#endif +#ifdef WIN32 +#include +#include +#endif +#ifdef __linux__ +#include +#include +#endif + +extern int g_ethernet_interface; + +static pcap_t* pcap_session; + +static struct packet_port_t elap_port; +static struct ether_addr_t HW_LOCAL; + +/*static void dump_device_list(pcap_if_t* devices) +{ + int i = 0; + for(pcap_if_t* device = devices; device; device = device->next) + { + printf("%d. %s", ++i, device->name); + if (device->description) + printf(" (%s)\n", device->description); + else + printf(" (No description available)\n"); + } +}*/ + +static void elap_clone_host_mac(pcap_if_t* device) +{ + if (!device) + return; + +#ifdef WIN32 + //// + // Extract the device GUID, which Windows uses to identify the device. + char* name = device->name; + while (name && *name != '{') + name++; + size_t guidLen = strlen(name); + + //// + // Find and copy the device MAC address. + ULONG size = sizeof(IP_ADAPTER_ADDRESSES) * 15; + IP_ADAPTER_ADDRESSES* addresses = malloc(size); + + ULONG result = GetAdaptersAddresses(AF_UNSPEC, + GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, NULL, addresses, &size); + + if (result == ERROR_BUFFER_OVERFLOW) + { + // The addresses buffer is too small. Allocate a bigger buffer. + free(addresses); + addresses = malloc(size); + result = GetAdaptersAddresses(AF_UNSPEC, + GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, NULL, addresses, &size); + } + + if (result == NO_ERROR) + { + // Search for the desired adapter address. + IP_ADAPTER_ADDRESSES* current = addresses; + while (current) + { + if (current->PhysicalAddressLength == ETHER_ADDR_LEN && memcmp(current->AdapterName, name, guidLen) == 0) + { + memcpy(&HW_LOCAL.mac, ¤t->PhysicalAddress, sizeof(HW_LOCAL.mac)); + break; + } + current = current->Next; + } + } + else + { + halt_printf("ATBridge: Failed to find host MAC address (%d).", result); + } + + free(addresses); +#else + struct pcap_addr* address; + for (address = device->addresses; address != 0; address = address->next) + if (address->addr->sa_family == AF_PACKET) + { + struct sockaddr_ll* ll = (struct sockaddr_ll*)address->addr; + memcpy(&HW_LOCAL.mac, ll->sll_addr, sizeof(HW_LOCAL.mac)); + } +#endif +} + +const struct ether_addr_t* elap_get_mac() +{ + return &HW_LOCAL; +} + +bool elap_init() +{ + port_init(&elap_port); + + memcpy(&HW_LOCAL, &HW_LOCAL_DEFAULT, sizeof(HW_LOCAL)); + + pcap_if_t* device; + pcap_if_t* alldevs; + int i = 0; + + char errbuf[PCAP_ERRBUF_SIZE]; + + // Load the PCAP library. + if (!pcapdelay_load()) + { + halt_printf("ATBridge: PCAP not available.\n"); + return false; + } + + // Retrieve the device list. + if(pcapdelay_findalldevs(&alldevs, errbuf) == -1) + { + atbridge_printf("ATBridge: Error enumerating PCAP devices: %s\n", errbuf); + return false; + } + //dump_device_list(alldevs); + + // Jump to the selected adapter. + for (device = alldevs, i = 0; i < g_ethernet_interface; device = device->next, i++); + if (!device) + { + halt_printf("ATBridge: PCAP device not found. Check interface number in settings.\n"); + return false; + } + + // Clone the MAC address of the underlying interface. In certain configurations (e.g. Windows with an MS Loopback + // interface), the interface, even in promiscous mode, filters foreign MAC addresses. + elap_clone_host_mac(device); + + // Open the adapter, + if ((pcap_session = pcapdelay_open_live(device->name, // name of the device + 65536, // portion of the packet to capture. + // 65536 grants that the whole packet will be captured on all the MACs. + 1, // promiscuous mode (nonzero means promiscuous) + 1, // read timeout + errbuf // error buffer + )) == NULL) + { + halt_printf("ATBridge: Unable to open the adapter. Pcap does not support %s.\n", device->name); + pcapdelay_freealldevs(alldevs); + return false; + } + + // The device must support Ethernet because the bridge "speaks" EtherTalk. + if (pcapdelay_datalink(pcap_session) == DLT_EN10MB) + { + pcapdelay_setnonblock(pcap_session, 1, errbuf); + + atbridge_printf("ATBridge: AppleTalk bridging using network device '%s' with Ethernet address %.2X:%.2X:%.2X:%.2X:%.2X:%.2X.\n", + device->description, HW_LOCAL.mac[0], HW_LOCAL.mac[1], HW_LOCAL.mac[2], HW_LOCAL.mac[3], HW_LOCAL.mac[4], HW_LOCAL.mac[5]); + + pcapdelay_freealldevs(alldevs); + return true; + } + else + { + pcapdelay_close(pcap_session); + pcap_session = 0; + halt_printf("ATBridge: Selected network device %s must support Ethernet.\n", device->description); + pcapdelay_freealldevs(alldevs); + return false; + } +} + +void elap_shutdown() +{ + port_shutdown(&elap_port); + if (pcap_session) + { + pcapdelay_close(pcap_session); + pcap_session = 0; + } + pcapdelay_unload(); +} + +void elap_enqueue_out(struct packet_t* packet) +{ + enqueue_packet(&elap_port.out, packet); +} + +struct packet_t* elap_dequeue_in() +{ + return dequeue(&elap_port.in); +} + +void elap_send(const struct ether_addr_t* dest, const struct snap_discriminator_t* discriminator, size_t size, byte data[]) +{ + if (pcap_session && dest && discriminator) + { + // Allocate heap space for the frame. + const size_t frame_size = sizeof(struct ethernet_header_t) + sizeof(struct snap_header_t) + size; + u_char* frame_data; + size_t pad = 0; + if (frame_size < ETHER_MIN_SIZE) + pad = ETHER_MIN_SIZE - frame_size; + frame_data = (u_char*)malloc(frame_size + pad); + + // Build the 802.3 header. + struct ethernet_header_t* ether = (struct ethernet_header_t*)frame_data; + memcpy(ether->dest.mac, dest, sizeof(ether->dest.mac)); + memcpy(ether->source.mac, HW_LOCAL.mac, sizeof(ether->source.mac)); + ether->length = htons(frame_size - sizeof(struct ethernet_header_t)); + + // Build the 802.2 header. + struct snap_header_t* snap = (struct snap_header_t*)(frame_data + sizeof(struct ethernet_header_t)); + snap->dsap = SNAP_DSAP; + snap->ssap = SNAP_SSAP; + snap->control = SNAP_CONTROL; + memcpy(&snap->discriminator, discriminator, sizeof(snap->discriminator)); + + // Add the data payload. + struct snap_header_t* payload = (struct snap_header_t*)(frame_data + sizeof(struct ethernet_header_t) + sizeof(struct snap_header_t)); + memcpy(payload, data, size); + + // Add padding to meet minimum Ethernet frame size. + if (pad > 0) + memset(frame_data + frame_size, 0, pad); + + pcapdelay_sendpacket(pcap_session, frame_data, frame_size + pad); + } +} + +static bool elap_send_one_queued() +{ + // Attempt to send one packet out the host network interface. + struct packet_t* packet = queue_peek(&elap_port.out); + if (packet) + { + // Find the MAC address. + const struct ether_addr_t* dest; + if (packet->dest.node == at_broadcast_node) + dest = &HW_APPLETALK_BROADCAST; + else + dest = aarp_request_hardware(&packet->dest); + + // Send it. + if (dest) + { + elap_send(dest, &SNAP_APPLETALK, packet->size, packet->data); + + dequeue(&elap_port.out); + free(packet->data); + free(packet); + } + else + { + // AARP does not have the needed hardware address. Give AARP time to obtain the address and keep the current packet. + if (!aarp_retry()) + { + // However, if AARP has reached the retry limit, the packet is undeliverable. Discard the packet and move on. + atbridge_printf("ATBridge: AARP failed to find MAC address for network %d node %d. Dropping packet.\n", packet->dest.network, packet->dest.node); + aarp_retry_reset(); + dequeue(&elap_port.out); + free(packet->data); + free(packet); + } + } + return true; + } + else + return false; +} + +static void elap_send_all_queued() +{ + while (elap_send_one_queued()); +} + +static bool elap_receive_one() +{ + if (!pcap_session) + return false; + + struct pcap_pkthdr header; + const u_char* packet = pcapdelay_next(pcap_session, &header); + if (packet) + { + // Receive and process one packet from the host network interface. + //// + + // Check the Ethernet 802.3 header. + const struct ethernet_header_t* ether = (struct ethernet_header_t*)packet; + if (header.len > sizeof(struct ethernet_header_t) && + ntohs(ether->length) <= ETHER_MAX_SIZE && + ntohs(ether->length) > sizeof(struct snap_header_t) && + (memcmp(ðer->source, &HW_LOCAL, sizeof(ether->source)) != 0) && /* Ignore packets sent from our node. */ + (memcmp(ðer->dest, &HW_LOCAL, sizeof(ether->dest)) == 0 || /* Accept packets destined for our node ... */ + memcmp(ðer->dest, &HW_APPLETALK_BROADCAST, sizeof(ether->dest)) == 0 /* ... or for broadcast. */) + ) + { + // Check the 802.2 SNAP header. + const struct snap_header_t* snap = (struct snap_header_t*)(packet + sizeof(struct ethernet_header_t)); + if (snap->dsap == SNAP_DSAP && + snap->ssap == SNAP_SSAP && + snap->control == SNAP_CONTROL) + { + if (memcmp(&snap->discriminator, &SNAP_APPLETALK, sizeof(snap->discriminator)) == 0) + { + // Handle an AppleTalk packet. + const size_t payload_size = ntohs(ether->length) - sizeof(struct snap_header_t); + const u_char* payload = packet + sizeof(struct ethernet_header_t) + sizeof(struct snap_header_t); + + byte* copy = (byte*)malloc(payload_size); + memcpy(copy, payload, payload_size); + + // ELAP does not support short-form DDP, so this must be a long-form DDP packet. + struct at_addr_t source, dest; + if (payload_size >= sizeof(struct DDP_LONG)) + { + // Extract the protocol address from the header. + // + // ELAP really shouldn't be looking at the header for the next level of the protocol stack, + // but this is a convenient place to glean addresses for the AMT, a process that needs both + // hardware and protocol addresses. + struct DDP_LONG* header = (struct DDP_LONG*)copy; + dest.network = (at_network_t)ntohs(header->dest_net); + source.network = (at_network_t)ntohs(header->source_net); + dest.node = header->dest_node; + source.node = header->source_node; + + enqueue(&elap_port.in, dest, source, LAP_DDP_LONG, payload_size, copy); + + aarp_glean(&source, ðer->source); + } + else + atbridge_printf("ATBridge: Dropping invalid short ELAP frame.\n"); + } + else if (memcmp(&snap->discriminator, &SNAP_AARP, sizeof(snap->discriminator)) == 0) + { + // Handle an AARP packet. + struct aarp_header_t* aarp = (struct aarp_header_t*)(packet + sizeof(struct ethernet_header_t) + sizeof(struct snap_header_t)); + aarp->dest_proto_addr.addr.network = ntohs(aarp->dest_proto_addr.addr.network); + aarp->source_proto_addr.addr.network = ntohs(aarp->source_proto_addr.addr.network); + aarp->function = ntohs(aarp->function); + aarp->hardware_type = ntohs(aarp->hardware_type); + aarp->protocol_type = ntohs(aarp->protocol_type); + aarp_handle_packet(aarp); + } + } + } + + return true; + } + else + return false; +} + +static void elap_receive_all() +{ + while (elap_receive_one()); +} + +void elap_process() +{ + elap_receive_all(); + elap_send_all_queued(); +} \ No newline at end of file diff --git a/src/atbridge/elap.h b/src/atbridge/elap.h new file mode 100644 index 0000000..1a89474 --- /dev/null +++ b/src/atbridge/elap.h @@ -0,0 +1,36 @@ +/* +GSport - an Apple //gs Emulator +Copyright (C) 2013-2014 by Peter Neubauer + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/** ELAP port of the AppleTalk Bridge **/ + +bool elap_init(); +void elap_shutdown(); +void elap_process(); + +struct packet_t; + +void elap_enqueue_out(struct packet_t* packet); +struct packet_t* elap_dequeue_in(); + +struct ether_addr_t; +struct snap_discriminator_t; + +void elap_send(const struct ether_addr_t* dest, const struct snap_discriminator_t* discriminator, size_t size, byte data[]); + +const struct ether_addr_t* elap_get_mac(); \ No newline at end of file diff --git a/src/atbridge/elap_defs.h b/src/atbridge/elap_defs.h new file mode 100644 index 0000000..8ce656e --- /dev/null +++ b/src/atbridge/elap_defs.h @@ -0,0 +1,117 @@ +/* +GSport - an Apple //gs Emulator +Copyright (C) 2013-2014 by Peter Neubauer + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* Ethernet addresses are 6 bytes */ +#define ETHER_ADDR_LEN 6 + +static const word16 ETHER_MAX_SIZE = 0x5DC; +static const word16 ETHER_MIN_SIZE = 60; +static const byte SNAP_DSAP = 0xAA; +static const byte SNAP_SSAP = 0xAA; +static const byte SNAP_CONTROL = 0x03; + +#define OUI_APPLETALK_1 0x08 +#define OUI_APPLETALK_2 0x00 +#define OUI_APPLETALK_3 0x07 +#define TYPE_APPLETALK_1 0x80 +#define TYPE_APPLETALK_2 0x9B + +#define OUI_AARP_1 0x00 +#define OUI_AARP_2 0x00 +#define OUI_AARP_3 0x00 +#define TYPE_AARP_1 0x80 +#define TYPE_AARP_2 0xF3 + +static const byte AARP_HARDWARE_ETHER = 0x01; +static const word16 AARP_PROTOCOL_TYPE = 0x809B; +static const byte AARP_HW_ADDR_LEN = 6; +static const byte AARP_PROTOCOL_ADDR_LEN = 4; +enum AARP_FUNCTION +{ + AARP_FUNCTION_REQUEST = 0x01, + AARP_FUNCTION_RESPONSE = 0x02, + AARP_FUNCTION_PROBE = 0x03 +}; + +// reference C-4 +static const long AARP_PROBE_INTERVAL = 200; /* milliseconds */ +static const unsigned int AARP_PROBE_COUNT = 10; + +// reference 2-9 and 3-9, optional and at developer discretion +static const long AARP_REQUEST_INTERVAL = 200; /* milliseconds */ +static const unsigned int AARP_REQUEST_COUNT = 10; + +#pragma pack(push, 1) +struct ether_addr_t +{ + byte mac[ETHER_ADDR_LEN]; +}; + +/* Ethernet 802.2/802.3/SNAP header */ +struct ethernet_header_t +{ + // 802.3 header + struct ether_addr_t dest; + struct ether_addr_t source; + word16 length; +}; + +struct snap_discriminator_t +{ + byte oui[3]; + byte type[2]; +}; + +struct snap_header_t +{ + // 802.2 header + byte dsap; + byte ssap; + byte control; + + // SNAP header + struct snap_discriminator_t discriminator; +}; + +struct protocol_addr_t +{ + byte zero; // Reference C-4 and 3-11: The protocol address is four bytes with the high byte zero. + struct at_addr_t addr; +}; + +struct aarp_header_t +{ + word16 hardware_type; + word16 protocol_type; + byte hw_addr_len; + byte protocol_addr_len; + word16 function; + struct ether_addr_t source_hw_addr; + struct protocol_addr_t source_proto_addr; + struct ether_addr_t dest_hw_addr; + struct protocol_addr_t dest_proto_addr; +}; +#pragma pack(pop) + +static const struct ether_addr_t HW_APPLETALK_BROADCAST = {{ 0x09, 0x00, 0x07, 0xff, 0xff, 0xff }}; +static const struct ether_addr_t HW_LOCAL_DEFAULT = {{0x02 /* unicast, locally administered */, 'A', '2', 'G', 'S', 0x00}}; +//static const struct ether_addr_t HW_LOCAL = {{ 0x02 /* unicast, locally administered */, 0x00, 0x4c, 0x4f, 0x4f, 0x50 }}; +static const struct ether_addr_t HW_ZERO = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; +static const struct snap_discriminator_t SNAP_APPLETALK = {{ OUI_APPLETALK_1, OUI_APPLETALK_2, OUI_APPLETALK_3}, {TYPE_APPLETALK_1, TYPE_APPLETALK_2 }}; +static const struct snap_discriminator_t SNAP_AARP = {{ OUI_AARP_1, OUI_AARP_2, OUI_AARP_3}, {TYPE_AARP_1, TYPE_AARP_2 }}; \ No newline at end of file diff --git a/src/atbridge/llap.c b/src/atbridge/llap.c new file mode 100644 index 0000000..d11233d --- /dev/null +++ b/src/atbridge/llap.c @@ -0,0 +1,332 @@ +/* +GSport - an Apple //gs Emulator +Copyright (C) 2013-2014 by Peter Neubauer + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/** This module implements the LLAP port of the bridge. **/ + +#include +#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); + } +} diff --git a/src/atbridge/llap.h b/src/atbridge/llap.h new file mode 100644 index 0000000..341f457 --- /dev/null +++ b/src/atbridge/llap.h @@ -0,0 +1,37 @@ +/* +GSport - an Apple //gs Emulator +Copyright (C) 2013-2014 by Peter Neubauer + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +struct packet_t; + +/** LLAP port of the AppleTalk Bridge **/ + +void llap_init(); +void llap_shutdown(); + +/** Send one LLAP packet from the GS + */ +void llap_enqueue_in(double dcycs, size_t size, byte data[]); + +/** Receive one LLAP packet from the world to the GS and return the size + */ +void llap_dequeue_out(double dcycs, size_t* size, byte* data[]); + + +void llap_enqueue_out(struct packet_t* packet); +struct packet_t* llap_dequeue_in(); diff --git a/src/atbridge/notes.txt b/src/atbridge/notes.txt new file mode 100644 index 0000000..4692d53 --- /dev/null +++ b/src/atbridge/notes.txt @@ -0,0 +1,61 @@ +AppleTalk Emulation and Bridging for GSport +Peter Neubauer, March 2014 + +Introduction +============ +The Apple IIgs shipped with a fantastic but often overlooked feature -- AppleTalk networking. AppleTalk is a low-cost, easy-to-maintain network technology that +enables network booting, file sharing, and printer sharing between your Apple IIgs, Workstation Card equipped Apple IIe, and classic Macintosh machines. As of +March 2014, GSport is the first modern emulator with support for AppleTalk. Now, you can just "drag and drop" files between your machines without thinking about +disk images, FTP, or serial cables. You can develop new software with an emulator and test on real hardware. You don't need to think much about version control. +Rather, you can update the file once on your file server, and all of your emulated and real machines have the new file. You can download the latest Apple II +software and immediately run it. + +Using It +======== +First, you need an AppleShare-compatible file server. A classic Mac or "netatalk" server could provide this function. For simplicity, I recommend Ivan Drucker's +A2SERVER (http://appleii.ivanx.com/a2server/), which is a pre-configured and easy-to-use package with "netatalk" and other useful tools. You may run A2SERVER in +a VirtualBox virtual machine, on an existing Linux server, or on a Raspberry Pi. + +Second, you need GSport 0.3 or later with ROM03 running on a Windows or Linux (x86 or Raspberry Pi) machine. Start GSport and press F4 to access the configuration +menu. If necessary, select a ROM03 image. Select the "Ethernet Card Configuration" menu option. Change "AppleTalk Bridging" to "On". Change "Use Interface +Number" to select the network where you have attached your AppleShare server or A2SERVER. Exit the GSport configuration menu. + +Third, party like it is 1989. Your GS is now connected to the AppleTalk network. Refer to Apple's documentation included with GS/OS System 5 and 6 for further +directions. + +Internal Overview +================= +GSport with AppleTalk networking is functionally equivalent to a real Apple IIgs with a LocalTalk/EtherTalk bridge. Originally, AppleTalk employed the "LocalTalk" +physical layer, which requires special hardware not found on modern computers. GSport converts LocalTalk to EtherTalk, a somewhat more modern physical layer using +familiar Ethernet cabling. Internally, GSport emulates the Zilog SCC chip in the IIgs, communicates with unmodified Apple-provided networking software built-in to +the IIgs and GS/OS, and converts the network traffic to EtherTalk. + +Limitations +=========== +- A wireless network may not work because many wireless adapters drop EtherTalk packets. Instead, use a wired Ethernet connection. + +- AppleTalk bridging has been tested with System 6.0.1 and System 5.0.2 on ROM03. Booting from a local disk or from the network works. Other ROM revisions and +system software may not work. + +- The SCC baud rate is incorrect because the SCC does not emulate line coding. Still, emulated network speed should be close to the 230.4kbps speed of a real +LocalTalk network. + +- The bridge supports Windows using Visual Studio, Cygwin, and Linux (x86 and Raspberry Pi). Other platforms should be straightforward, but I do not have a suitable +build environment. + +- The bridge requires a router on the network. The bridge should work in both a routerless and router-filled network. + +- The bridge works with simple networks consisting of a single network on a single segment with a single zone. The bridge should function with all valid network +configurations and hardware routers. Other configurations might not work, and I welcome reports. + +- The bridge implements the non-extended method for acquiring the network number. Interoperability would likely be better using the extended method, but this method +is much more complex. + +Credits +======= +Thanks to Gursharan Sidhu, Richard Andrews, and Alan Oppenheimer for creating and documenting AppleTalk. +Thanks to Kent Dickey and the GSport contributors for GSport and the original SCC emulation. +Thanks to David Schmenk for testing, encouragement, and Raspberry Pi support. +Thanks to Ivan Drucker for A2SERVER. +Thanks to the Gus emulator engineers for showing that AppleTalk emulation is possible. +Thanks to James Littlejohn for discussions about extending the capabilities of the Apple IIgs. \ No newline at end of file diff --git a/src/atbridge/pcap_delay.c b/src/atbridge/pcap_delay.c new file mode 100644 index 0000000..fff92d8 --- /dev/null +++ b/src/atbridge/pcap_delay.c @@ -0,0 +1,170 @@ +/* +GSport - an Apple //gs Emulator +Copyright (C) 2014 by Peter Neubauer + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include "pcap_delay.h" + +#ifdef WIN32 +#include +static HMODULE module = NULL; +#elif __linux__ +#include +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; +} \ No newline at end of file diff --git a/src/atbridge/pcap_delay.h b/src/atbridge/pcap_delay.h new file mode 100644 index 0000000..139f4aa --- /dev/null +++ b/src/atbridge/pcap_delay.h @@ -0,0 +1,46 @@ +/* +GSport - an Apple //gs Emulator +Copyright (C) 2014 by Peter Neubauer + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/* +This interface provides a thin, delay-loaded wrapper around the PCAP library so that +you may start GSport without intalling PCAP. Of course, some features that require +PCAP won't be available. + +This wrapper provides a subset of the available PCAP APIs necessary for ATBridge. +Feel free to extend the wrapper. +*/ + +#ifdef WIN32 +#include "../arch/win32/pcap.h" +#elif __linux__ +#include +#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 *); \ No newline at end of file diff --git a/src/atbridge/port.c b/src/atbridge/port.c new file mode 100644 index 0000000..b47639c --- /dev/null +++ b/src/atbridge/port.c @@ -0,0 +1,139 @@ +/* +GSport - an Apple //gs Emulator +Copyright (C) 2013-2014 by Peter Neubauer + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/** This module implements queues for storing and transferring packets within the bridge. **/ + +#include "../defc.h" +#include "atalk.h" +#include "port.h" + +void port_init(struct packet_port_t* port) +{ + if (port) + { + queue_init(&port->in); + queue_init(&port->out); + } +} + +void port_shutdown(struct packet_port_t* port) +{ + if (port) + { + queue_shutdown(&port->in); + queue_shutdown(&port->out); + } +} + +void queue_init(struct packet_queue_t* queue) +{ + if (queue) + { + queue->head = queue->tail = 0; + } +} + +void queue_shutdown(struct packet_queue_t* queue) +{ + if (queue) + { + struct packet_t* packet = dequeue(queue); + while (packet) + { + if (packet->data) + free(packet->data); + free(packet); + packet = dequeue(queue); + } + } +} + +void enqueue(struct packet_queue_t* queue, struct at_addr_t dest, struct at_addr_t source, byte type, size_t size, byte data[]) +{ + if (!queue) + return; + + struct packet_t* packet = (struct packet_t*)malloc(sizeof(struct packet_t)); + packet->dest.network = dest.network; + packet->dest.node = dest.node; + packet->source.network = source.network; + packet->source.node = source.node; + packet->type = type; + packet->size = size; + packet->data = data; + enqueue_packet(queue, packet); +} + +void enqueue_packet(struct packet_queue_t* queue, struct packet_t* packet) +{ + packet->next = 0; + + if (queue->tail) + queue->tail->next = packet; + else + queue->head = packet; + queue->tail = packet; +} + +void insert(struct packet_queue_t* queue, struct at_addr_t dest, struct at_addr_t source, byte type, size_t size, byte data[]) +{ + if (!queue) + return; + + struct packet_t* packet = (struct packet_t*)malloc(sizeof(struct packet_t)); + packet->dest.network = dest.network; + packet->dest.node = dest.node; + packet->source.network = source.network; + packet->source.node = source.node; + packet->type = type; + packet->size = size; + packet->data = data; + insert_packet(queue, packet); +} + +void insert_packet(struct packet_queue_t* queue, struct packet_t* packet) +{ + packet->next = queue->head; + queue->head = packet; + if (!queue->tail) + queue->tail = queue->head; +} + +struct packet_t* dequeue(struct packet_queue_t* queue) +{ + if (queue && queue->head) + { + struct packet_t* packet = queue->head; + if (queue->tail == queue->head) + queue->tail = queue->head = 0; + else + queue->head = packet->next; + return packet; + } + else + return 0; +} + +struct packet_t* queue_peek(struct packet_queue_t* queue) +{ + if (queue) + return queue->head; + else + return 0; +} diff --git a/src/atbridge/port.h b/src/atbridge/port.h new file mode 100644 index 0000000..650b743 --- /dev/null +++ b/src/atbridge/port.h @@ -0,0 +1,58 @@ +/* +GSport - an Apple //gs Emulator +Copyright (C) 2013-2014 by Peter Neubauer + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +struct packet_t +{ + struct at_addr_t dest; + struct at_addr_t source; + byte type; + size_t size; + byte* data; + struct packet_t* next; +}; + +struct packet_queue_t +{ + struct packet_t* head; + struct packet_t* tail; +}; + +void queue_init(struct packet_queue_t* queue); +void queue_shutdown(struct packet_queue_t* queue); + +void enqueue(struct packet_queue_t* queue, struct at_addr_t dest, struct at_addr_t source, byte type, size_t size, byte data[]); +void enqueue_packet(struct packet_queue_t* queue, struct packet_t* packet); + +struct packet_t* dequeue(struct packet_queue_t* queue); +struct packet_t* queue_peek(struct packet_queue_t* queue); + +// Insert the packet at the head of the queue, contrary to normal FIFO operation. +void insert(struct packet_queue_t* queue, struct at_addr_t dest, struct at_addr_t source, byte type, size_t size, byte data[]); +void insert_packet(struct packet_queue_t* queue, struct packet_t* packet); + + + +struct packet_port_t +{ + struct packet_queue_t in; + struct packet_queue_t out; +}; + +void port_init(struct packet_port_t* port); +void port_shutdown(struct packet_port_t* port); \ No newline at end of file diff --git a/src/config.c b/src/config.c index 712000f..61d5ae7 100644 --- a/src/config.c +++ b/src/config.c @@ -1,97 +1,101 @@ -/* - GSport - an Apple //gs Emulator - Copyright (C) 2010 - 2013 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 -#include "config.h" -#if defined(__OS2__) -#include "arch\os2\src\dirport.h" -#elif defined(_MSC_VER) -#include "arch\win32\dirent-win32.h" -#else -#include -#endif - -#ifdef HAVE_TFE -#include "tfe/tfesupp.h" -#include "tfe/protos_tfe.h" -#endif - -#if defined _MSC_VER -#include -#define snprintf _snprintf -typedef unsigned int mode_t; -#endif - -extern int Verbose; -extern word32 g_vbl_count; -extern Iwm iwm; - -extern int g_track_bytes_35[]; -extern int g_track_nibs_35[]; -extern int g_c031_disk35; - -extern int g_cur_a2_stat; -extern byte *g_slow_memory_ptr; -extern byte *g_rom_fc_ff_ptr; -extern byte *g_rom_cards_ptr; -extern double g_cur_dcycs; -extern int g_rom_version; -extern int g_fatal_log; - -extern word32 g_adb_repeat_vbl; - -extern int halt_sim; -extern int g_limit_speed; -extern int g_force_depth; -extern int g_serial_type[]; -extern int g_serial_out_masking; -extern int g_serial_modem[]; -extern word32 g_mem_size_base; -extern word32 g_mem_size_exp; -extern int g_video_line_update_interval; -extern int g_video_extra_check_inputs; -extern int g_user_halt_bad; -extern int g_joystick_type; -extern int g_joystick_scale_factor_x; -extern int g_joystick_scale_factor_y; -extern int g_joystick_trim_amount_x; -extern int g_joystick_trim_amount_y; -extern int g_swap_paddles; -extern int g_invert_paddles; -extern int g_ethernet; -extern int g_ethernet_interface; -extern int g_parallel; -extern int g_parallel_out_masking; -extern int g_printer; -extern int g_printer_dpi; -extern char* g_printer_output; -extern int g_printer_multipage; -extern char* g_printer_font_roman; -extern char* g_printer_font_sans; -extern char* g_printer_font_prestige; -extern char* g_printer_font_courier; -extern char* g_printer_font_script; -extern char* g_printer_font_ocra; -extern int g_printer_timeout; +/* + GSport - an Apple //gs Emulator + Copyright (C) 2010 - 2014 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 +#include "config.h" +#if defined(__OS2__) +#include "arch\os2\src\dirport.h" +#elif defined(_MSC_VER) +#include "arch\win32\dirent-win32.h" +#else +#include +#endif + +#ifdef HAVE_TFE +#include "tfe/tfesupp.h" +#include "tfe/protos_tfe.h" +#endif + +#if defined _MSC_VER +#include +#define snprintf _snprintf +typedef unsigned int mode_t; +#endif + +extern int Verbose; +extern word32 g_vbl_count; +extern Iwm iwm; + +extern int g_track_bytes_35[]; +extern int g_track_nibs_35[]; +extern int g_c031_disk35; + +extern int g_cur_a2_stat; +extern byte *g_slow_memory_ptr; +extern byte *g_rom_fc_ff_ptr; +extern byte *g_rom_cards_ptr; +extern double g_cur_dcycs; +extern int g_rom_version; +extern int g_fatal_log; + +extern word32 g_adb_repeat_vbl; + +extern int halt_sim; +extern int g_limit_speed; +extern int g_force_depth; +extern int g_serial_type[]; +extern int g_serial_out_masking; +extern int g_serial_modem[]; +extern word32 g_mem_size_base; +extern word32 g_mem_size_exp; +extern int g_video_line_update_interval; +extern int g_video_extra_check_inputs; +extern int g_user_halt_bad; +extern int g_joystick_type; +extern int g_joystick_scale_factor_x; +extern int g_joystick_scale_factor_y; +extern int g_joystick_trim_amount_x; +extern int g_joystick_trim_amount_y; +extern int g_swap_paddles; +extern int g_invert_paddles; +extern int g_ethernet; +extern int g_ethernet_interface; +extern int g_appletalk_bridging; +extern int g_appletalk_turbo; +extern int g_appletalk_diagnostics; +extern int g_appletalk_network_hint; +extern int g_parallel; +extern int g_parallel_out_masking; +extern int g_printer; +extern int g_printer_dpi; +extern char* g_printer_output; +extern int g_printer_multipage; +extern char* g_printer_font_roman; +extern char* g_printer_font_sans; +extern char* g_printer_font_prestige; +extern char* g_printer_font_courier; +extern char* g_printer_font_script; +extern char* g_printer_font_ocra; +extern int g_printer_timeout; extern int g_imagewriter; extern int g_imagewriter_dpi; @@ -101,3267 +105,3282 @@ extern int g_imagewriter_timeout; extern char* g_imagewriter_fixed_font; extern char* g_imagewriter_prop_font; -#if defined(_WIN32) || defined(__CYGWIN__) -extern int g_win_show_console_request; -extern int g_win_status_debug_request; -#endif - -extern int g_screen_index[]; -extern word32 g_full_refresh_needed; -extern word32 g_a2_screen_buffer_changed; -extern int g_a2_new_all_stat[]; -extern int g_new_a2_stat_cur_line; -extern byte g_bram[2][256]; -extern byte* g_bram_ptr; -extern byte g_temp_boot_slot; -extern byte g_orig_boot_slot; - -extern int g_key_down; -extern const char g_gsport_version_str[]; -int g_config_control_panel = 0; -char g_config_gsport_name[1024]; -char g_cfg_cwd_str[CFG_PATH_MAX] = { 0 }; - -int g_config_gsport_auto_update = 1; -int g_config_gsport_update_needed = 0; - -const char *g_config_gsport_name_list[] = { - "config.txt", "config.gsport", "gsport_conf", ".config.gsport", 0 -}; - -int g_highest_smartport_unit = -1; -int g_reparse_delay = 0; -int g_user_page2_shadow = 1; - -byte g_save_text_screen_bytes[0x800]; -int g_save_cur_a2_stat = 0; -char g_cfg_printf_buf[CFG_PRINTF_BUFSIZE]; -char g_config_gsport_buf[CONF_BUF_LEN]; - -word32 g_cfg_vbl_count = 0; - -int g_cfg_curs_x = 0; -int g_cfg_curs_y = 0; -int g_cfg_curs_inv = 0; -int g_cfg_curs_mousetext = 0; - -#define CFG_MAX_OPTS 16 -#define CFG_OPT_MAXSTR 100 - -int g_cfg_opts_vals[CFG_MAX_OPTS]; -char g_cfg_opts_strs[CFG_MAX_OPTS][CFG_OPT_MAXSTR]; -char g_cfg_opts_strvals[CFG_MAX_OPTS][CFG_OPT_MAXSTR]; -char g_cfg_opt_buf[CFG_OPT_MAXSTR]; - -char *g_cfg_rom_path = "ROM"; -char *g_cfg_file_def_name = "Undefined"; -char **g_cfg_file_strptr = 0; -int g_cfg_file_min_size = 1024; -int g_cfg_file_max_size = 2047*1024*1024; - -#define MAX_PARTITION_BLK_SIZE 65536 - -extern Cfg_menu g_cfg_main_menu[]; - -#define KNMP(a) &a, #a, 0 - -Cfg_menu g_cfg_disk_menu[] = { -{ "Disk Configuration", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU }, -{ "s5d1 = ", 0, 0, 0, CFGTYPE_DISK + 0x5000 }, -{ "s5d2 = ", 0, 0, 0, CFGTYPE_DISK + 0x5010 }, -{ "", 0, 0, 0, 0 }, -{ "s6d1 = ", 0, 0, 0, CFGTYPE_DISK + 0x6000 }, -{ "s6d2 = ", 0, 0, 0, CFGTYPE_DISK + 0x6010 }, -{ "", 0, 0, 0, 0 }, -{ "s7d1 = ", 0, 0, 0, CFGTYPE_DISK + 0x7000 }, -{ "s7d2 = ", 0, 0, 0, CFGTYPE_DISK + 0x7010 }, -{ "s7d3 = ", 0, 0, 0, CFGTYPE_DISK + 0x7020 }, -{ "s7d4 = ", 0, 0, 0, CFGTYPE_DISK + 0x7030 }, -{ "s7d5 = ", 0, 0, 0, CFGTYPE_DISK + 0x7040 }, -{ "s7d6 = ", 0, 0, 0, CFGTYPE_DISK + 0x7050 }, -{ "s7d7 = ", 0, 0, 0, CFGTYPE_DISK + 0x7060 }, -{ "s7d8 = ", 0, 0, 0, CFGTYPE_DISK + 0x7070 }, -{ "s7d9 = ", 0, 0, 0, CFGTYPE_DISK + 0x7080 }, -{ "s7d10 = ", 0, 0, 0, CFGTYPE_DISK + 0x7090 }, -{ "s7d11 = ", 0, 0, 0, CFGTYPE_DISK + 0x70a0 }, -{ "", 0, 0, 0, 0 }, -{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, -{ 0, 0, 0, 0, 0 }, -}; - +#if defined(_WIN32) || defined(__CYGWIN__) +extern int g_win_show_console_request; +extern int g_win_status_debug_request; +#endif + +extern int g_screen_index[]; +extern word32 g_full_refresh_needed; +extern word32 g_a2_screen_buffer_changed; +extern int g_a2_new_all_stat[]; +extern int g_new_a2_stat_cur_line; +extern byte g_bram[2][256]; +extern byte* g_bram_ptr; +extern byte g_temp_boot_slot; +extern byte g_orig_boot_slot; + +extern int g_key_down; +extern const char g_gsport_version_str[]; +int g_config_control_panel = 0; +char g_config_gsport_name[1024]; +char g_cfg_cwd_str[CFG_PATH_MAX] = { 0 }; + +int g_config_gsport_auto_update = 1; +int g_config_gsport_update_needed = 0; + +const char *g_config_gsport_name_list[] = { + "config.txt", "config.gsport", "gsport_conf", ".config.gsport", 0 +}; + +int g_highest_smartport_unit = -1; +int g_reparse_delay = 0; +int g_user_page2_shadow = 1; + +byte g_save_text_screen_bytes[0x800]; +int g_save_cur_a2_stat = 0; +char g_cfg_printf_buf[CFG_PRINTF_BUFSIZE]; +char g_config_gsport_buf[CONF_BUF_LEN]; + +word32 g_cfg_vbl_count = 0; + +int g_cfg_curs_x = 0; +int g_cfg_curs_y = 0; +int g_cfg_curs_inv = 0; +int g_cfg_curs_mousetext = 0; + +#define CFG_MAX_OPTS 16 +#define CFG_OPT_MAXSTR 100 + +int g_cfg_opts_vals[CFG_MAX_OPTS]; +char g_cfg_opts_strs[CFG_MAX_OPTS][CFG_OPT_MAXSTR]; +char g_cfg_opts_strvals[CFG_MAX_OPTS][CFG_OPT_MAXSTR]; +char g_cfg_opt_buf[CFG_OPT_MAXSTR]; + +char *g_cfg_rom_path = "ROM"; +char *g_cfg_file_def_name = "Undefined"; +char **g_cfg_file_strptr = 0; +int g_cfg_file_min_size = 1024; +int g_cfg_file_max_size = 2047*1024*1024; + +#define MAX_PARTITION_BLK_SIZE 65536 + +extern Cfg_menu g_cfg_main_menu[]; + +#define KNMP(a) &a, #a, 0 + +Cfg_menu g_cfg_disk_menu[] = { +{ "Disk Configuration", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU }, +{ "s5d1 = ", 0, 0, 0, CFGTYPE_DISK + 0x5000 }, +{ "s5d2 = ", 0, 0, 0, CFGTYPE_DISK + 0x5010 }, +{ "", 0, 0, 0, 0 }, +{ "s6d1 = ", 0, 0, 0, CFGTYPE_DISK + 0x6000 }, +{ "s6d2 = ", 0, 0, 0, CFGTYPE_DISK + 0x6010 }, +{ "", 0, 0, 0, 0 }, +{ "s7d1 = ", 0, 0, 0, CFGTYPE_DISK + 0x7000 }, +{ "s7d2 = ", 0, 0, 0, CFGTYPE_DISK + 0x7010 }, +{ "s7d3 = ", 0, 0, 0, CFGTYPE_DISK + 0x7020 }, +{ "s7d4 = ", 0, 0, 0, CFGTYPE_DISK + 0x7030 }, +{ "s7d5 = ", 0, 0, 0, CFGTYPE_DISK + 0x7040 }, +{ "s7d6 = ", 0, 0, 0, CFGTYPE_DISK + 0x7050 }, +{ "s7d7 = ", 0, 0, 0, CFGTYPE_DISK + 0x7060 }, +{ "s7d8 = ", 0, 0, 0, CFGTYPE_DISK + 0x7070 }, +{ "s7d9 = ", 0, 0, 0, CFGTYPE_DISK + 0x7080 }, +{ "s7d10 = ", 0, 0, 0, CFGTYPE_DISK + 0x7090 }, +{ "s7d11 = ", 0, 0, 0, CFGTYPE_DISK + 0x70a0 }, +{ "", 0, 0, 0, 0 }, +{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; + // OG Use define instead of const for joystick_types #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) -Cfg_menu g_cfg_joystick_menu[] = { -{ "Joystick Configuration", g_cfg_joystick_menu, 0, 0, CFGTYPE_MENU }, +Cfg_menu g_cfg_joystick_menu[] = { +{ "Joystick Configuration", g_cfg_joystick_menu, 0, 0, CFGTYPE_MENU }, { "Joystick Emulation,"TOSTRING(JOYSTICK_TYPE_KEYPAD)",Keypad Joystick,"TOSTRING(JOYSTICK_TYPE_MOUSE)",Mouse Joystick,"TOSTRING(JOYSTICK_TYPE_NATIVE_1)",Native Joystick 1," TOSTRING(JOYSTICK_TYPE_NATIVE_2)",Native Joystick 2,"TOSTRING(JOYSTICK_TYPE_NONE)",No Joystick", KNMP(g_joystick_type), CFGTYPE_INT }, -{ "Joystick Scale X,0x100,Standard,0x119,+10%,0x133,+20%," - "0x150,+30%,0xb0,-30%,0xcd,-20%,0xe7,-10%", - KNMP(g_joystick_scale_factor_x), CFGTYPE_INT }, -{ "Joystick Scale Y,0x100,Standard,0x119,+10%,0x133,+20%," - "0x150,+30%,0xb0,-30%,0xcd,-20%,0xe7,-10%", - KNMP(g_joystick_scale_factor_y), CFGTYPE_INT }, -{ "Joystick Trim X", KNMP(g_joystick_trim_amount_x), CFGTYPE_INT }, -{ "Joystick Trim Y", KNMP(g_joystick_trim_amount_y), CFGTYPE_INT }, -{ "Swap Joystick X and Y,0,Normal operation,1,Paddle 1 and Paddle 0 swapped", - KNMP(g_swap_paddles), CFGTYPE_INT }, -{ "Invert Joystick,0,Normal operation,1,Left becomes right and up becomes down", - KNMP(g_invert_paddles), CFGTYPE_INT }, -{ "", 0, 0, 0, 0 }, -{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, -{ 0, 0, 0, 0, 0 }, -}; - -Cfg_menu g_cfg_rom_menu[] = { -{ "ROM File Selection", g_cfg_rom_menu, 0, 0, CFGTYPE_MENU }, -{ "ROM File", KNMP(g_cfg_rom_path), CFGTYPE_FILE }, -{ "", 0, 0, 0, 0 }, -{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, -{ 0, 0, 0, 0, 0 }, -}; - -Cfg_menu g_cfg_serial_menu[] = { -{ "Serial Port Configuration", g_cfg_serial_menu, 0, 0, CFGTYPE_MENU }, -{ "Port 0 (slot 1),0,Only use socket 6501,1,Use real port if avail,2,Virtual Imagewriter", - KNMP(g_serial_type[0]), CFGTYPE_INT }, -{ "Port 1 (slot 2),0,Only use socket 6502,1,Use real port if avail,2,Virtual Imagewriter", - KNMP(g_serial_type[1]), CFGTYPE_INT }, -{ "Serial Output,0,Send full 8-bit data,1,Mask off high bit", - KNMP(g_serial_out_masking), CFGTYPE_INT }, -{ "Modem on port 0 (slot 1),0,Simple socket emulation mode,1,Modem with " - "incoming and outgoing emulation", KNMP(g_serial_modem[0]), - CFGTYPE_INT }, -{ "Modem on port 1 (slot 2),0,Simple socket emulation mode,1,Modem with " - "incoming and outgoing emulation", KNMP(g_serial_modem[1]), - CFGTYPE_INT }, -{ "", 0, 0, 0, 0 }, -{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, -{ 0, 0, 0, 0, 0 }, -}; - -Cfg_menu g_cfg_parallel_menu[] = { -{ "Parallel Card Configuration", g_cfg_parallel_menu, 0, 0, CFGTYPE_MENU }, -{ "Parallel Card in Slot 1,0,Off,1,On", - KNMP(g_parallel), CFGTYPE_INT }, -{ "Parallel Output,0,Send full 8-bit data,1,Mask off high bit", - KNMP(g_parallel_out_masking), CFGTYPE_INT }, -{ "", 0, 0, 0, 0 }, -{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, -{ 0, 0, 0, 0, 0 }, -}; - -Cfg_menu g_cfg_ethernet_menu[] = { -{ "Ethernet Card Configuration", g_cfg_ethernet_menu, 0, 0, CFGTYPE_MENU }, -{ "Uthernet Card in Slot 3,0,Off,1,On", - KNMP(g_ethernet), CFGTYPE_INT }, -{ "Use Interface Number,0,0,1,1,2,2,3,3,4,4", - KNMP(g_ethernet_interface), CFGTYPE_INT }, -{ "", 0, 0, 0, 0 }, -{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, -{ 0, 0, 0, 0, 0 }, -}; - -Cfg_menu g_cfg_printer_menu[] = { -{ "Virtual Printer Configuration", g_cfg_printer_menu, 0, 0, CFGTYPE_MENU }, -{ "Virtual Printer Type,0,Epson LQ", - KNMP(g_printer), CFGTYPE_INT }, -{ "Printer DPI,60,60x60 dpi,180,180x180 dpi,360,360x360 dpi", - KNMP(g_printer_dpi), CFGTYPE_INT }, -{ "Printer Output Type,bmp,Windows Bitmap,ps,Postscript (B&W),printer,Direct to host printer,text,Text file", - KNMP(g_printer_output), CFGTYPE_STR }, -{ "Multipage Files? (PS Only),0,No,1,Yes", - KNMP(g_printer_multipage), CFGTYPE_INT }, -{ "Printer Timeout,0,Never,2,2 sec.,15,15 sec.,30,30 sec.,60, 1 min.", - KNMP(g_printer_timeout), CFGTYPE_INT }, -{ "", 0, 0, 0, 0 }, -{ "Epson LQ Fonts", 0, 0, 0, 0 }, -{ "--------------", 0, 0, 0, 0 }, -{ "", 0, 0, 0, 0 }, -{ "Roman", KNMP(g_printer_font_roman), CFGTYPE_FILE }, -{ "Sans Serif", KNMP(g_printer_font_sans), CFGTYPE_FILE }, -{ "Courier", KNMP(g_printer_font_courier), CFGTYPE_FILE }, -{ "Prestige", KNMP(g_printer_font_prestige), CFGTYPE_FILE }, -{ "Script", KNMP(g_printer_font_script), CFGTYPE_FILE }, -{ "OCR A/B", KNMP(g_printer_font_ocra), CFGTYPE_FILE }, -{ "", 0, 0, 0, 0 }, -{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, -{ 0, 0, 0, 0, 0 }, -}; - -Cfg_menu g_cfg_imagewriter_menu[] = { -{ "Virtual Imagewriter Configuration", g_cfg_imagewriter_menu, 0, 0, CFGTYPE_MENU }, -{ "Virtual Printer Type,0,Imagewriter II,1,Imagewriter LQ", - KNMP(g_imagewriter), CFGTYPE_INT }, -{ "Printer DPI,360,360x360 dpi (Best for 8-bit software),720,720x720 dpi (Best for GS/OS & IW LQ Modes),1440,1440x1440 dpi", - KNMP(g_imagewriter_dpi), CFGTYPE_INT }, -{ "Printer Output Type,bmp,Windows Bitmap,ps,Postscript (B&W),colorps,Postscript (Color),printer,Direct to host printer,text,Text file", - KNMP(g_imagewriter_output), CFGTYPE_STR }, -{ "Multipage Files? (PS Only),0,No,1,Yes", - KNMP(g_imagewriter_multipage), CFGTYPE_INT }, -{ "Printer Timeout,0,Never,2,2 sec.,15,15 sec.,30,30 sec.,60, 1 min.", - KNMP(g_imagewriter_timeout), CFGTYPE_INT }, -{ "", 0, 0, 0, 0 }, -{ "Imagewriter Fonts", 0, 0, 0, 0 }, -{ "-----------------", 0, 0, 0, 0 }, -{ "", 0, 0, 0, 0 }, -{ "Fixed Width Font", KNMP(g_imagewriter_fixed_font), CFGTYPE_FILE }, -{ "", 0, 0, 0, 0 }, -{ "Proportional Font", KNMP(g_imagewriter_prop_font), CFGTYPE_FILE }, +{ "Joystick Scale X,0x100,Standard,0x119,+10%,0x133,+20%," + "0x150,+30%,0xb0,-30%,0xcd,-20%,0xe7,-10%", + KNMP(g_joystick_scale_factor_x), CFGTYPE_INT }, +{ "Joystick Scale Y,0x100,Standard,0x119,+10%,0x133,+20%," + "0x150,+30%,0xb0,-30%,0xcd,-20%,0xe7,-10%", + KNMP(g_joystick_scale_factor_y), CFGTYPE_INT }, +{ "Joystick Trim X", KNMP(g_joystick_trim_amount_x), CFGTYPE_INT }, +{ "Joystick Trim Y", KNMP(g_joystick_trim_amount_y), CFGTYPE_INT }, +{ "Swap Joystick X and Y,0,Normal operation,1,Paddle 1 and Paddle 0 swapped", + KNMP(g_swap_paddles), CFGTYPE_INT }, +{ "Invert Joystick,0,Normal operation,1,Left becomes right and up becomes down", + KNMP(g_invert_paddles), CFGTYPE_INT }, { "", 0, 0, 0, 0 }, { "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, { 0, 0, 0, 0, 0 }, -}; - -#if defined(_WIN32) || defined(__CYGWIN__) -Cfg_menu g_cfg_debug_menu[] = { -{ "Debugging Options", g_cfg_debug_menu, 0, 0, CFGTYPE_MENU }, -{ "Status lines,0,Hide,1,Show", KNMP(g_win_status_debug_request), CFGTYPE_INT }, -{ "Console,0,Hide,1,Show", KNMP(g_win_show_console_request), CFGTYPE_INT }, +}; + +Cfg_menu g_cfg_rom_menu[] = { +{ "ROM File Selection", g_cfg_rom_menu, 0, 0, CFGTYPE_MENU }, +{ "ROM File", KNMP(g_cfg_rom_path), CFGTYPE_FILE }, +{ "", 0, 0, 0, 0 }, +{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; + +Cfg_menu g_cfg_serial_menu[] = { +{ "Serial Port Configuration", g_cfg_serial_menu, 0, 0, CFGTYPE_MENU }, +{ "Port 0 (slot 1),0,Only use socket 6501,1,Use real port if avail,2,Virtual Imagewriter", + KNMP(g_serial_type[0]), CFGTYPE_INT }, +{ "Port 1 (slot 2),0,Only use socket 6502,1,Use real port if avail,2,Virtual Imagewriter", + KNMP(g_serial_type[1]), CFGTYPE_INT }, +{ "Serial Output,0,Send full 8-bit data,1,Mask off high bit", + KNMP(g_serial_out_masking), CFGTYPE_INT }, +{ "Modem on port 0 (slot 1),0,Simple socket emulation mode,1,Modem with " + "incoming and outgoing emulation", KNMP(g_serial_modem[0]), + CFGTYPE_INT }, +{ "Modem on port 1 (slot 2),0,Simple socket emulation mode,1,Modem with " + "incoming and outgoing emulation", KNMP(g_serial_modem[1]), + CFGTYPE_INT }, +{ "", 0, 0, 0, 0 }, +{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; + +Cfg_menu g_cfg_parallel_menu[] = { +{ "Parallel Card Configuration", g_cfg_parallel_menu, 0, 0, CFGTYPE_MENU }, +{ "Parallel Card in Slot 1,0,Off,1,On", + KNMP(g_parallel), CFGTYPE_INT }, +{ "Parallel Output,0,Send full 8-bit data,1,Mask off high bit", + KNMP(g_parallel_out_masking), CFGTYPE_INT }, +{ "", 0, 0, 0, 0 }, +{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; + +Cfg_menu g_cfg_ethernet_menu[] = { +{ "Ethernet Card Configuration", g_cfg_ethernet_menu, 0, 0, CFGTYPE_MENU }, +{ "Use Interface Number,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10", + KNMP(g_ethernet_interface), CFGTYPE_INT }, +{ "", 0, 0, 0, 0 }, +{ "Uthernet Card in Slot 3,0,Off,1,On", + KNMP(g_ethernet), CFGTYPE_INT }, +#ifdef HAVE_ATBRIDGE +{ "", 0, 0, 0, 0 }, +{ "AppleTalk Bridging,0,Off,1,On", + KNMP(g_appletalk_bridging), CFGTYPE_INT }, +{ "AppleTalk Speed,0,Normal (230.4 kbps),1,Turbo", + KNMP(g_appletalk_turbo), CFGTYPE_INT }, +#endif +{ "", 0, 0, 0, 0 }, +{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; + +Cfg_menu g_cfg_printer_menu[] = { +{ "Virtual Printer Configuration", g_cfg_printer_menu, 0, 0, CFGTYPE_MENU }, +{ "Virtual Printer Type,0,Epson LQ", + KNMP(g_printer), CFGTYPE_INT }, +{ "Printer DPI,60,60x60 dpi,180,180x180 dpi,360,360x360 dpi", + KNMP(g_printer_dpi), CFGTYPE_INT }, +{ "Printer Output Type,bmp,Windows Bitmap,ps,Postscript (B&W),printer,Direct to host printer,text,Text file", + KNMP(g_printer_output), CFGTYPE_STR }, +{ "Multipage Files? (PS Only),0,No,1,Yes", + KNMP(g_printer_multipage), CFGTYPE_INT }, +{ "Printer Timeout,0,Never,2,2 sec.,15,15 sec.,30,30 sec.,60, 1 min.", + KNMP(g_printer_timeout), CFGTYPE_INT }, +{ "", 0, 0, 0, 0 }, +{ "Epson LQ Fonts", 0, 0, 0, 0 }, +{ "--------------", 0, 0, 0, 0 }, +{ "", 0, 0, 0, 0 }, +{ "Roman", KNMP(g_printer_font_roman), CFGTYPE_FILE }, +{ "Sans Serif", KNMP(g_printer_font_sans), CFGTYPE_FILE }, +{ "Courier", KNMP(g_printer_font_courier), CFGTYPE_FILE }, +{ "Prestige", KNMP(g_printer_font_prestige), CFGTYPE_FILE }, +{ "Script", KNMP(g_printer_font_script), CFGTYPE_FILE }, +{ "OCR A/B", KNMP(g_printer_font_ocra), CFGTYPE_FILE }, +{ "", 0, 0, 0, 0 }, +{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; + +Cfg_menu g_cfg_imagewriter_menu[] = { +{ "Virtual Imagewriter Configuration", g_cfg_imagewriter_menu, 0, 0, CFGTYPE_MENU }, +{ "Virtual Printer Type,0,Imagewriter II,1,Imagewriter LQ", + KNMP(g_imagewriter), CFGTYPE_INT }, +{ "Printer DPI,360,360x360 dpi (Best for 8-bit software),720,720x720 dpi (Best for GS/OS & IW LQ Modes),1440,1440x1440 dpi", + KNMP(g_imagewriter_dpi), CFGTYPE_INT }, +{ "Printer Output Type,bmp,Windows Bitmap,ps,Postscript (B&W),colorps,Postscript (Color),printer,Direct to host printer,text,Text file", + KNMP(g_imagewriter_output), CFGTYPE_STR }, +{ "Multipage Files? (PS Only),0,No,1,Yes", + KNMP(g_imagewriter_multipage), CFGTYPE_INT }, +{ "Printer Timeout,0,Never,2,2 sec.,15,15 sec.,30,30 sec.,60, 1 min.", + KNMP(g_imagewriter_timeout), CFGTYPE_INT }, +{ "", 0, 0, 0, 0 }, +{ "Imagewriter Fonts", 0, 0, 0, 0 }, +{ "-----------------", 0, 0, 0, 0 }, +{ "", 0, 0, 0, 0 }, +{ "Fixed Width Font", KNMP(g_imagewriter_fixed_font), CFGTYPE_FILE }, +{ "", 0, 0, 0, 0 }, +{ "Proportional Font", KNMP(g_imagewriter_prop_font), CFGTYPE_FILE }, { "", 0, 0, 0, 0 }, { "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, { 0, 0, 0, 0, 0 }, }; -#endif -Cfg_menu g_cfg_main_menu[] = { -{ "GSport Configuration", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, -{ "Disk Configuration", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU }, -{ "Joystick Configuration", g_cfg_joystick_menu, 0, 0, CFGTYPE_MENU }, -{ "ROM File Selection", g_cfg_rom_menu, 0, 0, CFGTYPE_MENU }, -{ "Serial Port Configuration", g_cfg_serial_menu, 0, 0, CFGTYPE_MENU }, -{ "Ethernet Card Configuration", g_cfg_ethernet_menu, 0, 0, CFGTYPE_MENU }, -{ "Parallel Card Configuration", g_cfg_parallel_menu, 0, 0, CFGTYPE_MENU }, -{ "Virtual Printer Configuration", g_cfg_printer_menu, 0, 0, CFGTYPE_MENU }, +#if defined(HAVE_ATBRIDGE) || defined(_WIN32) || defined(__CYGWIN__) +Cfg_menu g_cfg_debug_menu[] = { +{ "Debugging Options", g_cfg_debug_menu, 0, 0, CFGTYPE_MENU }, +#if defined(_WIN32) || defined(__CYGWIN__) +{ "Status lines,0,Hide,1,Show", KNMP(g_win_status_debug_request), CFGTYPE_INT }, +{ "Console,0,Hide,1,Show", KNMP(g_win_show_console_request), CFGTYPE_INT }, +#endif +#ifdef HAVE_ATBRIDGE +{ "", 0, 0, 0, 0 }, +{ "Show AppleTalk Diagnostics,0,No,1,Yes", KNMP(g_appletalk_diagnostics), CFGTYPE_INT }, +{ "AppleTalk Network Hint", KNMP(g_appletalk_network_hint), CFGTYPE_INT }, +#endif +{ "", 0, 0, 0, 0 }, +{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; +#endif + +Cfg_menu g_cfg_main_menu[] = { +{ "GSport Configuration", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ "Disk Configuration", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU }, +{ "Joystick Configuration", g_cfg_joystick_menu, 0, 0, CFGTYPE_MENU }, +{ "ROM File Selection", g_cfg_rom_menu, 0, 0, CFGTYPE_MENU }, +{ "Serial Port Configuration", g_cfg_serial_menu, 0, 0, CFGTYPE_MENU }, +{ "Ethernet Card Configuration", g_cfg_ethernet_menu, 0, 0, CFGTYPE_MENU }, +{ "Parallel Card Configuration", g_cfg_parallel_menu, 0, 0, CFGTYPE_MENU }, +{ "Virtual Printer Configuration", g_cfg_printer_menu, 0, 0, CFGTYPE_MENU }, { "Virtual Imagewriter Configuration", g_cfg_imagewriter_menu, 0, 0, CFGTYPE_MENU }, -#ifndef _WIN32 -{ "Force X-windows display depth", KNMP(g_force_depth), CFGTYPE_INT }, -#endif -#if defined(_WIN32) || defined(__CYGWIN__) -{ "Debugging Options", g_cfg_debug_menu, 0, 0, CFGTYPE_MENU }, -#endif -{ "Auto-update configuration file,0,Manual,1,Immediately", - KNMP(g_config_gsport_auto_update), CFGTYPE_INT }, -{ "Speed,0,Unlimited,1,1.0MHz,2,2.8MHz,3,8.0MHz (Zip)", - KNMP(g_limit_speed), CFGTYPE_INT }, -{ "Expansion Mem Size,0,0MB,0x100000,1MB,0x200000,2MB,0x300000,3MB," - "0x400000,4MB,0x600000,6MB,0x800000,8MB,0xa00000,10MB,0xc00000,12MB," - "0xe00000,14MB", KNMP(g_mem_size_exp), CFGTYPE_INT }, -{ "3200 Color Enable,0,Auto (Full if fast enough),1,Full (Update every line)," - "8,Off (Update video every 8 lines)", - KNMP(g_video_line_update_interval), CFGTYPE_INT }, -{ "Keyboard and mouse poll rate,0,60 times per second,1,240 times per second", - KNMP(g_video_extra_check_inputs), CFGTYPE_INT }, -{ "Code Red Halts,0,Do not stop on bad accesses,1,Enter debugger on bad " - "accesses", KNMP(g_user_halt_bad), CFGTYPE_INT }, -{ "Enable Text Page 2 Shadow,0,Disabled on ROM 01 (matches real hardware)," - "1,Enabled on ROM 01 and 03", - KNMP(g_user_page2_shadow), CFGTYPE_INT }, -{ "Dump text screen to file", (void *)cfg_text_screen_dump, 0, 0, CFGTYPE_FUNC}, -{ "", 0, 0, 0, 0 }, -{ "Save changes to configuration file", (void *)config_write_config_gsport_file, 0, 0, - CFGTYPE_FUNC }, -{ "", 0, 0, 0, 0 }, -{ "Exit Config (or press F4)", (void *)cfg_exit, 0, 0, CFGTYPE_FUNC }, -{ 0, 0, 0, 0, 0 }, -}; - - -#define CFG_MAX_DEFVALS 128 -Cfg_defval g_cfg_defvals[CFG_MAX_DEFVALS]; -int g_cfg_defval_index = 0; - -int g_cfg_slotdrive = -1; -int g_cfg_select_partition = -1; -char g_cfg_tmp_path[CFG_PATH_MAX]; -char g_cfg_file_path[CFG_PATH_MAX]; -char g_cfg_file_cachedpath[CFG_PATH_MAX]; -char g_cfg_file_cachedreal[CFG_PATH_MAX]; -char g_cfg_file_curpath[CFG_PATH_MAX]; -char g_cfg_file_shortened[CFG_PATH_MAX]; -char g_cfg_file_match[CFG_PATH_MAX]; - -Cfg_listhdr g_cfg_dirlist = { 0 }; -Cfg_listhdr g_cfg_partitionlist = { 0 }; - -int g_cfg_file_pathfield = 0; - -const char *g_gsport_rom_names[] = { "ROM", "ROM", "ROM.01", "ROM.03", 0 }; - /* First entry is special--it will be overwritten by g_cfg_rom_path */ - -const char *g_gsport_c1rom_names[] = { "parallel.rom", 0 }; -const char *g_gsport_c2rom_names[] = { 0 }; -const char *g_gsport_c3rom_names[] = { 0 }; -const char *g_gsport_c4rom_names[] = { 0 }; -const char *g_gsport_c5rom_names[] = { 0 }; -const char *g_gsport_c6rom_names[] = { "c600.rom", "controller.rom", "disk.rom", - "DISK.ROM", "diskII.prom", 0 }; -const char *g_gsport_c7rom_names[] = { 0 }; - -const char **g_gsport_rom_card_list[8] = { - 0, g_gsport_c1rom_names, - g_gsport_c2rom_names, g_gsport_c3rom_names, - g_gsport_c4rom_names, g_gsport_c5rom_names, - g_gsport_c6rom_names, g_gsport_c7rom_names }; - -byte g_rom_c600_rom01_diffs[256] = { - 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0xe2, 0x00, - 0xd0, 0x50, 0x0f, 0x77, 0x5b, 0xb9, 0xc3, 0xb1, - 0xb1, 0xf8, 0xcb, 0x4e, 0xb8, 0x60, 0xc7, 0x2e, - 0xfc, 0xe0, 0xbf, 0x1f, 0x66, 0x37, 0x4a, 0x70, - 0x55, 0x2c, 0x3c, 0xfc, 0xc2, 0xa5, 0x08, 0x29, - 0xac, 0x21, 0xcc, 0x09, 0x55, 0x03, 0x17, 0x35, - 0x4e, 0xe2, 0x0c, 0xe9, 0x3f, 0x9d, 0xc2, 0x06, - 0x18, 0x88, 0x0d, 0x58, 0x57, 0x6d, 0x83, 0x8c, - 0x22, 0xd3, 0x4f, 0x0a, 0xe5, 0xb7, 0x9f, 0x7d, - 0x2c, 0x3e, 0xae, 0x7f, 0x24, 0x78, 0xfd, 0xd0, - 0xb5, 0xd6, 0xe5, 0x26, 0x85, 0x3d, 0x8d, 0xc9, - 0x79, 0x0c, 0x75, 0xec, 0x98, 0xcc, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x39, 0x00, 0x35, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, - 0x6c, 0x44, 0xce, 0x4c, 0x01, 0x08, 0x00, 0x00 -}; - - -void -config_init_menus(Cfg_menu *menuptr) -{ - void *voidptr; - const char *name_str; - Cfg_defval *defptr; - char **str_ptr; - char *str; - int type; - int pos; - int val; - - if(menuptr[0].defptr != 0) { - return; - } - menuptr[0].defptr = (void *)1; - pos = 0; - while(pos < 100) { - type = menuptr->cfgtype; - voidptr = menuptr->ptr; - name_str = menuptr->name_str; - if(menuptr->str == 0) { - break; - } - if(name_str != 0) { - defptr = &(g_cfg_defvals[g_cfg_defval_index++]); - if(g_cfg_defval_index >= CFG_MAX_DEFVALS) { - fatal_printf("CFG_MAX_DEFVAL overflow\n"); - my_exit(5); - } - defptr->menuptr = menuptr; - defptr->intval = 0; - defptr->strval = 0; - switch(type) { - case CFGTYPE_INT: - val = *((int *)voidptr); - defptr->intval = val; - menuptr->defptr = &(defptr->intval); - break; - case CFGTYPE_STR: - str_ptr = (char **)menuptr->ptr; - str = *str_ptr; - // We need to malloc this string since all - // string values must be dynamically alloced - defptr->strval = str; // this can have a copy - *str_ptr = gsport_malloc_str(str); - menuptr->defptr = &(defptr->strval); - break; - case CFGTYPE_FILE: - str_ptr = (char **)menuptr->ptr; - str = *str_ptr; - // We need to malloc this string since all - // string values must be dynamically alloced - defptr->strval = str; // this can have a copy - *str_ptr = gsport_malloc_str(str); - menuptr->defptr = &(defptr->strval); - break; - default: - fatal_printf("name_str is %p = %s, but type: " - "%d\n", name_str, name_str, type); - my_exit(5); - } - } - if(type == CFGTYPE_MENU) { - config_init_menus((Cfg_menu *)voidptr); - } - pos++; - menuptr++; - } -} - -void -config_init() -{ - int can_create; - - config_init_menus(g_cfg_main_menu); - - // Find the configuration file - g_config_gsport_name[0] = 0; - can_create = 1; - setup_gsport_file(&g_config_gsport_name[0], sizeof(g_config_gsport_name), 0, - can_create, &g_config_gsport_name_list[0]); - - config_parse_config_gsport_file(); -} - -void -cfg_exit() -{ - /* printf("In cfg exit\n"); */ - if(g_rom_version >= 1) { - g_config_control_panel = 0; - } -} - -void -cfg_toggle_config_panel() -{ - g_config_control_panel = !g_config_control_panel; - if(g_rom_version < 0) { - g_config_control_panel = 1; /* Stay in config mode */ - } -} - -void -cfg_text_screen_dump() -{ - char buf[85]; - char *filename; - FILE *ofile; - int offset; - int c; - int pos; - int i, j; - - filename = "gsport.screen.dump"; - printf("Writing text screen to the file %s\n", filename); - ofile = fopen(filename, "w"); - if(ofile == 0) { - fatal_printf("Could not write to file %s, (%d)\n", filename, - errno); - return; - } - - for(i = 0; i < 24; i++) { - pos = 0; - for(j = 0; j < 40; j++) { - offset = g_screen_index[i] + j; - if(g_save_cur_a2_stat & ALL_STAT_VID80) { - c = g_save_text_screen_bytes[0x400+offset]; - c = c & 0x7f; - if(c < 0x20) { - c += 0x40; - } - buf[pos++] = c; - } - c = g_save_text_screen_bytes[offset] & 0x7f; - if(c < 0x20) { - c += 0x40; - } - buf[pos++] = c; - } - while((pos > 0) && (buf[pos-1] == ' ')) { - /* try to strip out trailing spaces */ - pos--; - } - buf[pos++] = '\n'; - buf[pos++] = 0; - fputs(buf, ofile); - } - fclose(ofile); -} - -#ifdef HAVE_TFE -void -cfg_get_tfe_name() -{ - int i = 0; - char *ppname = NULL; - char *ppdes = NULL; - cfg_htab_vtab(0,9); - if (tfe_enumadapter_open()) - { - cfg_printf("Interface List:\n---------------"); - while(tfe_enumadapter(&ppname,&ppdes)) - { - cfg_htab_vtab(0, 11+i); - cfg_printf("%2d: %s",i,ppdes); - i++; - lib_free(ppname); - lib_free(ppdes); - } - tfe_enumadapter_close(); - } - else - { - #if defined(_WIN32) || defined(__CYGWIN__) - cfg_printf("ERROR: Install/Enable WinPcap for Ethernet Support!!"); - #else - cfg_printf("ERROR: Install/Enable LibPcap for Ethernet Support!!"); - #endif - } - return; -} -#endif - -void -config_vbl_update(int doit_3_persec) -{ - if(doit_3_persec) { - if(g_config_gsport_auto_update && g_config_gsport_update_needed) { - config_write_config_gsport_file(); - } - } - return; -} - -void -config_parse_option(char *buf, int pos, int len, int line) -{ - Cfg_menu *menuptr; - Cfg_defval *defptr; - char *nameptr; - char **strptr; - int *iptr; - int num_equals; - int type; - int val; - int c; - int i; - -// warning: modifies buf (turns spaces to nulls) -// parse buf from pos into option, "=" and then "rest" - if(pos >= len) { - /* blank line */ - return; - } - - if(strncmp(&buf[pos], "bram", 4) == 0) { - config_parse_bram(buf, pos+4, len); - return; - } - - // find "name" as first contiguous string - printf("...parse_option: line %d, %p,%p = %s (%s) len:%d\n", line, - &buf[pos], buf, &buf[pos], buf, len); - - nameptr = &buf[pos]; - while(pos < len) { - c = buf[pos]; - if(c == 0 || c == ' ' || c == '\t' || c == '\n') { - break; - } - pos++; - } - buf[pos] = 0; - pos++; - - // Eat up all whitespace and '=' - num_equals = 0; - while(pos < len) { - c = buf[pos]; - if((c == '=') && num_equals == 0) { - pos++; - num_equals++; - } else if(c == ' ' || c == '\t') { - pos++; - } else { - break; - } - } - - /* Look up nameptr to find type */ - type = -1; - defptr = 0; - menuptr = 0; - for(i = 0; i < g_cfg_defval_index; i++) { - defptr = &(g_cfg_defvals[i]); - menuptr = defptr->menuptr; - if(strcmp(menuptr->name_str, nameptr) == 0) { - type = menuptr->cfgtype; - break; - } - } - - switch(type) { - case CFGTYPE_INT: - /* use strtol */ - val = (int)strtol(&buf[pos], 0, 0); - iptr = (int *)menuptr->ptr; - *iptr = val; - break; - case CFGTYPE_STR: - strptr = (char **)menuptr->ptr; - if(strptr && *strptr) { - free(*strptr); - } - *strptr = gsport_malloc_str(&buf[pos]); - break; - case CFGTYPE_FILE: - strptr = (char **)menuptr->ptr; - if(strptr && *strptr) { - free(*strptr); - } - *strptr = gsport_malloc_str(&buf[pos]); - break; - default: - printf("Config file variable %s is unknown type: %d\n", - nameptr, type); - } - -} - -void -config_parse_bram(char *buf, int pos, int len) -{ - int bram_num; - int offset; - int val; - - if((len < (pos+5)) || (buf[pos+1] != '[') || (buf[pos+4] != ']')) { - fatal_printf("While reading configuration file, found malformed bram " - "statement: %s\n", buf); - return; - } - bram_num = buf[pos] - '0'; - if(bram_num != 1 && bram_num != 3) { - fatal_printf("While reading configuration file, found bad bram " - "num: %s\n", buf); - return; - } - - bram_num = bram_num >> 1; // turn 3->1 and 1->0 - - offset = strtoul(&(buf[pos+2]), 0, 16); - pos += 5; - while(pos < len) { - while(buf[pos] == ' ' || buf[pos] == '\t' || buf[pos] == 0x0a || - buf[pos] == 0x0d || buf[pos] == '=') { - pos++; - } - val = strtoul(&buf[pos], 0, 16); - clk_bram_set(bram_num, offset, val); - offset++; - pos += 2; - } -} - -void -config_load_roms() -{ - struct stat stat_buf; - const char **names_ptr; - int more_than_8mb; - int changed_rom; - int len; - FILE *file; - int ret; - int i; - - g_rom_version = -1; - - /* set first entry of g_gsport_rom_names[] to g_cfg_rom_path so that */ - /* it becomes the first place searched. */ - g_gsport_rom_names[0] = g_cfg_rom_path; - setup_gsport_file(&g_cfg_tmp_path[0], CFG_PATH_MAX, -1, 0, - &g_gsport_rom_names[0]); - - if(g_cfg_tmp_path[0] == 0) { - // Just get out, let config interface select ROM - g_config_control_panel = 1; - return; - } - file = fopen(&g_cfg_tmp_path[0], "rb"); - if(!file) { - fatal_printf("Open ROM file %s failed; errno:%d\n", - &g_cfg_tmp_path[0], errno); - g_config_control_panel = 1; - return; - } - - ret = stat(&g_cfg_tmp_path[0], &stat_buf); - if(ret != 0) { - fatal_printf("stat returned %d; errno: %d\n", - ret, errno); - g_config_control_panel = 1; - return; - } - - len = stat_buf.st_size; - if(len == 128*1024) { - g_rom_version = 1; - g_mem_size_base = 256*1024; - memset(&g_rom_fc_ff_ptr[0], 0, 2*65536); - /* Clear banks fc and fd to 0 */ - ret = fread(&g_rom_fc_ff_ptr[2*65536], 1, len, file); - } else if(len == 256*1024) { - g_rom_version = 3; - g_mem_size_base = 1024*1024; - ret = fread(&g_rom_fc_ff_ptr[0], 1, len, file); - } else { - fatal_printf("The ROM size should be 128K or 256K, this file " - "is %d bytes\n", len); - g_config_control_panel = 1; - return; - } - - printf("Read: %d bytes of ROM\n", ret); - if(ret != len) { - fatal_printf("errno: %d\n", errno); - g_config_control_panel = 1; - return; - } - fclose(file); - - memset(&g_rom_cards_ptr[0], 0, 256*16); - - /* initialize c600 rom to be diffs from the real ROM, to build-in */ - /* Apple II compatibility without distributing ROMs */ - for(i = 0; i < 256; i++) { - g_rom_cards_ptr[0x600 + i] = g_rom_fc_ff_ptr[0x3c600 + i] ^ - g_rom_c600_rom01_diffs[i]; - } - if(g_rom_version >= 3) { - /* some patches */ - g_rom_cards_ptr[0x61b] ^= 0x40; - g_rom_cards_ptr[0x61c] ^= 0x33; - g_rom_cards_ptr[0x632] ^= 0xc0; - g_rom_cards_ptr[0x633] ^= 0x33; - } - - for(i = 1; i < 8; i++) { - names_ptr = g_gsport_rom_card_list[i]; - if(names_ptr == 0) { - continue; - } - if(*names_ptr == 0) { - continue; - } - setup_gsport_file(&g_cfg_tmp_path[0], CFG_PATH_MAX, 1, 0, - names_ptr); - if(g_cfg_tmp_path[0] != 0) { - file = fopen(&(g_cfg_tmp_path[0]), "rb"); - if(!file) { - fatal_printf("Open card ROM file %s failed; errno:%d\n", - &g_cfg_tmp_path[0], errno); - continue; - } - - len = 256; - ret = fread(&g_rom_cards_ptr[i*0x100], 1, len, file); - - if(ret != len) { - fatal_printf("While reading card ROM %s, file " - "is too short. (%d) Expected %d bytes, " - "read %d bytes\n", &g_cfg_tmp_path[0], errno, len, ret); - continue; - } - printf("Read: %d bytes of ROM in slot %d from file %s.\n", ret, i, &g_cfg_tmp_path[0]); - fclose(file); - } - } - more_than_8mb = (g_mem_size_exp > 0x800000); - /* Only do the patch if users wants more than 8MB of expansion mem */ - - changed_rom = 0; - if(g_rom_version == 1) { - /* make some patches to ROM 01 */ -#if 0 - /* 1: Patch ROM selftest to not do speed test */ - printf("Patching out speed test failures from ROM 01\n"); - g_rom_fc_ff_ptr[0x3785a] = 0x18; - changed_rom = 1; -#endif - -#if 0 - /* 2: Patch ROM selftests not to do tests 2,4 */ - /* 0 = skip, 1 = do it, test 1 is bit 0 of LSByte */ - g_rom_fc_ff_ptr[0x371e9] = 0xf5; - g_rom_fc_ff_ptr[0x371ea] = 0xff; - changed_rom = 1; -#endif - - if(more_than_8mb) { - /* Geoff Weiss patch to use up to 14MB of RAM */ - g_rom_fc_ff_ptr[0x30302] = 0xdf; - g_rom_fc_ff_ptr[0x30314] = 0xdf; - g_rom_fc_ff_ptr[0x3031c] = 0x00; - changed_rom = 1; - } - - /* Patch ROM selftest to not do ROM cksum if any changes*/ - if(changed_rom) { - g_rom_fc_ff_ptr[0x37a06] = 0x18; - g_rom_fc_ff_ptr[0x37a07] = 0x18; - } - } else if(g_rom_version == 3) { - /* patch ROM 03 */ - printf("Patching ROM 03 smartport bug\n"); - /* 1: Patch Smartport code to fix a stupid bug */ - /* that causes it to write the IWM status reg into c036, */ - /* which is the system speed reg...it's "safe" since */ - /* IWM status reg bit 4 must be 0 (7MHz)..., otherwise */ - /* it might have turned on shadowing in all banks! */ - g_rom_fc_ff_ptr[0x357c9] = 0x00; - changed_rom = 1; - -#if 0 - /* patch ROM 03 to not to speed test */ - /* skip fast speed test */ - g_rom_fc_ff_ptr[0x36ad7] = 0x18; - g_rom_fc_ff_ptr[0x36ad8] = 0x18; - changed_rom = 1; -#endif - -#if 0 - /* skip slow speed test */ - g_rom_fc_ff_ptr[0x36ae7] = 0x18; - g_rom_fc_ff_ptr[0x36ae8] = 0x6b; - changed_rom = 1; -#endif - -#if 0 - /* 4: Patch ROM 03 selftests not to do tests 1-4 */ - g_rom_fc_ff_ptr[0x364a9] = 0xf0; - g_rom_fc_ff_ptr[0x364aa] = 0xff; - changed_rom = 1; -#endif - - /* ROM tests are in ff/6403-642x, where 6403 = addr of */ - /* test 1, etc. */ - - if(more_than_8mb) { - /* Geoff Weiss patch to use up to 14MB of RAM */ - g_rom_fc_ff_ptr[0x30b] = 0xdf; - g_rom_fc_ff_ptr[0x31d] = 0xdf; - g_rom_fc_ff_ptr[0x325] = 0x00; - changed_rom = 1; - } - - if(changed_rom) { - /* patch ROM 03 selftest to not do ROM cksum */ - g_rom_fc_ff_ptr[0x36cb0] = 0x18; - g_rom_fc_ff_ptr[0x36cb1] = 0x18; - } - - } -} - -void -config_parse_config_gsport_file() -{ - FILE *fconf; - char *buf; - char *ptr; - char *name_ptr; - char *partition_name; - int part_num; - int ejected; - int line; - int pos; - int slot; - int drive; - int size; - int len; - int ret; - int i; - - printf("Parsing configuration file\n"); - - clk_bram_zero(); - - g_highest_smartport_unit = -1; - - cfg_get_base_path(&g_cfg_cwd_str[0], g_config_gsport_name, 0); -#ifndef __OS2__ - if(g_cfg_cwd_str[0] != 0) { - ret = chdir(&g_cfg_cwd_str[0]); - if(ret != 0) { - printf("chdir to %s, errno:%d\n", g_cfg_cwd_str, errno); - } - } - /* In any case, copy the directory path to g_cfg_cwd_str */ - (void)getcwd(&g_cfg_cwd_str[0], CFG_PATH_MAX); -#endif - - fconf = fopen(g_config_gsport_name, "r"); - if(fconf == 0) { - fatal_printf("cannot open configuration file at %s! Stopping!\n", - g_config_gsport_name); - my_exit(3); - } - - line = 0; - while(1) { - buf = &g_config_gsport_buf[0]; - ptr = fgets(buf, CONF_BUF_LEN, fconf); - if(ptr == 0) { - iwm_printf("Done reading disk_conf\n"); - break; - } - - line++; - /* strip off newline(s) */ - len = strlen(buf); - for(i = len - 1; i >= 0; i--) { - if((buf[i] != 0x0d) && (buf[i] != 0x0a)) { - break; - } - len = i; - buf[i] = 0; - } - - iwm_printf("disk_conf[%d]: %s\n", line, buf); - if(len > 0 && buf[0] == '#') { - iwm_printf("Skipping comment\n"); - continue; - } - - /* determine what this is */ - pos = 0; - - while(pos < len && (buf[pos] == ' ' || buf[pos] == '\t') ) { - pos++; - } - if((pos + 4) > len || buf[pos] != 's' || buf[pos+2] != 'd' || - buf[pos+1] > '9' || buf[pos+1] < '0') { - config_parse_option(buf, pos, len, line); - continue; - } - - slot = buf[pos+1] - '0'; - drive = buf[pos+3] - '0'; - - /* skip over slot, drive */ - pos += 4; - if(buf[pos] >= '0' && buf[pos] <= '9') { - drive = drive * 10 + buf[pos] - '0'; - pos++; - } - - /* make s6d1 mean index 0 */ - drive--; - - while(pos < len && (buf[pos] == ' ' || buf[pos] == '\t' || - buf[pos] == '=') ) { - pos++; - } - - ejected = 0; - if(buf[pos] == '#') { - /* disk is ejected, but read all the info anyway */ - ejected = 1; - pos++; - } - - size = 0; - if(buf[pos] == ',') { - /* read optional size parameter */ - pos++; - while(pos < len && buf[pos] >= '0' && buf[pos] <= '9'){ - size = size * 10 + buf[pos] - '0'; - pos++; - } - size = size * 1024; - if(buf[pos] == ',') { - pos++; /* eat trailing ',' */ - } - } - - /* see if it has a partition name */ - partition_name = 0; - part_num = -1; - if(buf[pos] == ':') { - pos++; - /* yup, it's got a partition name! */ - partition_name = &buf[pos]; - while((pos < len) && (buf[pos] != ':')) { - pos++; - } - buf[pos] = 0; /* null terminate partition name */ - pos++; - } - if(buf[pos] == ';') { - pos++; - /* it's got a partition number */ - part_num = 0; - while((pos < len) && (buf[pos] != ':')) { - part_num = (10*part_num) + buf[pos] - '0'; - pos++; - } - pos++; - } - - /* Get filename */ - name_ptr = &(buf[pos]); - if(name_ptr[0] == 0) { - continue; - } - - insert_disk(slot, drive, name_ptr, ejected, size, - partition_name, part_num); - - } - - ret = fclose(fconf); - if(ret != 0) { - fatal_printf("Closing configuration file ret: %d, errno: %d\n", ret, - errno); - my_exit(4); - } - - iwm_printf("Done parsing disk_conf file\n"); -} - - -Disk * -cfg_get_dsk_from_slot_drive(int slot, int drive) -{ - Disk *dsk; - int max_drive; - - /* Get dsk */ - max_drive = 2; - switch(slot) { - case 5: - dsk = &(iwm.drive35[drive]); - break; - case 6: - dsk = &(iwm.drive525[drive]); - break; - default: - max_drive = MAX_C7_DISKS; - dsk = &(iwm.smartport[drive]); - } - - if(drive >= max_drive) { - dsk -= drive; /* move back to drive 0 effectively */ - } - - return dsk; -} - -void -config_generate_config_gsport_name(char *outstr, int maxlen, Disk *dsk, - int with_extras) -{ - char *str; - - str = outstr; - - if(with_extras && (!dsk->file)) { - snprintf(str, maxlen - (str - outstr), "#"); - str = &outstr[strlen(outstr)]; - } - if(with_extras && dsk->force_size > 0) { - snprintf(str, maxlen - (str - outstr), ",%d,", dsk->force_size); - str = &outstr[strlen(outstr)]; - } - if(with_extras && dsk->partition_name != 0) { - snprintf(str, maxlen - (str - outstr), ":%s:", - dsk->partition_name); - str = &outstr[strlen(outstr)]; - } else if(with_extras && dsk->partition_num >= 0) { - snprintf(str, maxlen - (str - outstr), ";%d:", - dsk->partition_num); - str = &outstr[strlen(outstr)]; - } - snprintf(str, maxlen - (str - outstr), "%s", dsk->name_ptr); -} - -void -config_write_config_gsport_file() -{ - FILE *fconf; - Disk *dsk; - Cfg_defval *defptr; - Cfg_menu *menuptr; - char *curstr, *defstr; - int defval, curval; - int type; - int slot, drive; - int i; - - printf("Writing configuration file to %s\n", g_config_gsport_name); - - fconf = fopen(g_config_gsport_name, "w+"); - if(fconf == 0) { - halt_printf("cannot open %s! Stopping!\n",g_config_gsport_name); - return; - } - - fprintf(fconf, "# GSport configuration file version %s\n", - g_gsport_version_str); - - for(i = 0; i < MAX_C7_DISKS + 4; i++) { - slot = 7; - drive = i - 4; - if(i < 4) { - slot = (i >> 1) + 5; - drive = i & 1; - } - if(drive == 0) { - fprintf(fconf, "\n"); /* an extra blank line */ - } - - dsk = cfg_get_dsk_from_slot_drive(slot, drive); - if(dsk->name_ptr == 0 && (i > 4)) { - /* No disk, not even ejected--just skip */ - continue; - } - fprintf(fconf, "s%dd%d = ", slot, drive + 1); - if(dsk->name_ptr == 0) { - fprintf(fconf, "\n"); - continue; - } - config_generate_config_gsport_name(&g_cfg_tmp_path[0], - CFG_PATH_MAX, dsk, 1); - fprintf(fconf, "%s\n", &g_cfg_tmp_path[0]); - } - - fprintf(fconf, "\n"); - - /* See if any variables are different than their default */ - for(i = 0; i < g_cfg_defval_index; i++) { - defptr = &(g_cfg_defvals[i]); - menuptr = defptr->menuptr; - defval = defptr->intval; - type = menuptr->cfgtype; - if(type == CFGTYPE_INT) { - curval = *((int *)menuptr->ptr); - if(curval != defval) { - fprintf(fconf, "%s = %d\n", menuptr->name_str, - curval); - } - } - if(type == CFGTYPE_STR) { - curstr = *((char **)menuptr->ptr); - defstr = *((char **)menuptr->defptr); - if(strcmp(curstr, defstr) != 0) { - fprintf(fconf, "%s = %s\n", menuptr->name_str, - curstr); - } - } - if(type == CFGTYPE_FILE) { - curstr = *((char **)menuptr->ptr); - defstr = *((char **)menuptr->defptr); - if(strcmp(curstr, defstr) != 0) { - fprintf(fconf, "%s = %s\n", menuptr->name_str, - curstr); - } - } - } - - fprintf(fconf, "\n"); - - /* write bram state */ - clk_write_bram(fconf); - - fclose(fconf); - - g_config_gsport_update_needed = 0; -} - -void -insert_disk(int slot, int drive, const char *name, int ejected, int force_size, - const char *partition_name, int part_num) -{ - byte buf_2img[512]; - Disk *dsk; - char *name_ptr, *uncomp_ptr, *system_str; - char *part_ptr; - int size; - int system_len; - int part_len; - int cmp_o, cmp_p, cmp_dot; - int cmp_b, cmp_i, cmp_n; - int can_write; - int len; - int nibs; - int unix_pos; - int name_len; - int image_identified; - int exp_size; - int save_track; - int ret; - int tmp; - int i; - - g_config_gsport_update_needed = 1; - - if((slot < 5) || (slot > 7)) { - fatal_printf("Invalid slot for inserting disk: %d\n", slot); - return; - } - if(drive < 0 || ((slot == 7) && (drive >= MAX_C7_DISKS)) || - ((slot < 7) && (drive > 1))) { - fatal_printf("Invalid drive for inserting disk: %d\n", drive); - return; - } - - dsk = cfg_get_dsk_from_slot_drive(slot, drive); - -#if 0 - printf("Inserting disk %s (%s or %d) in slot %d, drive: %d\n", name, - partition_name, part_num, slot, drive); -#endif - - dsk->just_ejected = 0; - dsk->force_size = force_size; - - if(!dsk->file) { - eject_disk(dsk); - } - - /* Before opening, make sure no other mounted disk has this name */ - /* If so, unmount it */ - if(!ejected) { - for(i = 0; i < 2; i++) { - eject_named_disk(&iwm.drive525[i], name,partition_name); - eject_named_disk(&iwm.drive35[i], name, partition_name); - } - for(i = 0; i < MAX_C7_DISKS; i++) { - eject_named_disk(&iwm.smartport[i],name,partition_name); - } - } - - if(dsk->name_ptr != 0) { - /* free old name_ptr */ - free(dsk->name_ptr); - } - - name_len = strlen(name); - name_ptr = (char *)malloc(name_len + 1); -#if defined(_WIN32) || defined(__CYGWIN__) - // On Windows, we need to change backslashes to forward slashes. - for (i = 0; i < name_len; i++) { - if (name[i] == '\\') { - name_ptr[i] = '/'; - } else { - name_ptr[i] = name[i]; - } - } - name_ptr[name_len] = 0; -#else - strncpy(name_ptr, name, name_len + 1); -#endif - dsk->name_ptr = name_ptr; - - dsk->partition_name = 0; - if(partition_name != 0) { - part_len = strlen(partition_name) + 1; - part_ptr = (char *)malloc(part_len); - strncpy(part_ptr, partition_name, part_len); - dsk->partition_name = part_ptr; - } - dsk->partition_num = part_num; - - iwm_printf("Opening up disk image named: %s\n", name_ptr); - - if(ejected) { - /* just get out of here */ - dsk->file = 0; - return; - } - - dsk->file = 0; - can_write = 1; - - if((name_len > 3) && (strcmp(&name_ptr[name_len - 3], ".gz") == 0)) { - - /* it's gzip'ed, try to gunzip it, then unlink the */ - /* uncompressed file */ - - can_write = 0; - - uncomp_ptr = (char *)malloc(name_len + 1); - strncpy(uncomp_ptr, name_ptr, name_len + 1); - uncomp_ptr[name_len - 3] = 0; - - system_len = 2*name_len + 100; - system_str = (char *)malloc(system_len + 1); - snprintf(system_str, system_len, - "set -o noclobber;gunzip -c %c%s%c > %c%s%c", - 0x22, name_ptr, 0x22, - 0x22, uncomp_ptr, 0x22); - /* 0x22 are " to allow spaces in filenames */ - printf("I am uncompressing %s into %s for mounting\n", - name_ptr, uncomp_ptr); - ret = system(system_str); - if(ret == 0) { - /* successfully ran */ - dsk->file = fopen(uncomp_ptr, "rb"); - iwm_printf("Opening .gz file %s\n", uncomp_ptr); - - /* and, unlink the temporary file */ - (void)unlink(uncomp_ptr); - } - free(system_str); - free(uncomp_ptr); - /* Reduce name_len by 3 so that subsequent compares for .po */ - /* look at the correct chars */ - name_len -= 3; - } - - if((!dsk->file) && can_write) { - dsk->file = fopen(name_ptr, "rb+"); - } - - if((!dsk->file) && can_write) { - printf("Trying to open %s read-only, errno: %d\n", name_ptr, - errno); - dsk->file = fopen(name_ptr, "rb"); - can_write = 0; - } - - if(!dsk->file) { - fatal_printf("Disk image %s does not exist!\n", name_ptr); - return; - } - - if(can_write != 0) { - dsk->write_prot = 0; - dsk->write_through_to_unix = 1; - } else { - dsk->write_prot = 1; - dsk->write_through_to_unix = 0; - } - - save_track = dsk->cur_qtr_track; /* save arm position */ - dsk->image_type = DSK_TYPE_PRODOS; - dsk->image_start = 0; - - /* See if it is in 2IMG format */ - ret = fread((char *)&buf_2img[0], 1, 512, dsk->file); - size = force_size; - if(size <= 0) { - size = cfg_get_fd_size(name_ptr); - } - - /* Try to guess that there is a Mac Binary header of 128 bytes */ - /* See if image size & 0xfff = 0x080 which indicates extra 128 bytes */ - if((size & 0xfff) == 0x080) { - printf("Assuming Mac Binary header on %s\n", dsk->name_ptr); - dsk->image_start += 0x80; - } - image_identified = 0; - if(buf_2img[0] == '2' && buf_2img[1] == 'I' && buf_2img[2] == 'M' && - buf_2img[3] == 'G') { - /* It's a 2IMG disk */ - printf("Image named %s is in 2IMG format\n", dsk->name_ptr); - image_identified = 1; - - if(buf_2img[12] == 0) { - printf("2IMG is in DOS 3.3 sector order\n"); - dsk->image_type = DSK_TYPE_DOS33; - } - if(buf_2img[19] & 0x80) { - /* disk is locked */ - printf("2IMG is write protected\n"); - dsk->write_prot = 1; - dsk->write_through_to_unix = 0; - } - if((buf_2img[17] & 1) && (dsk->image_type == DSK_TYPE_DOS33)) { - dsk->vol_num = buf_2img[16]; - printf("Setting DOS 3.3 vol num to %d\n", dsk->vol_num); - } - // Some 2IMG archives have the size byte reversed - size = (buf_2img[31] << 24) + (buf_2img[30] << 16) + - (buf_2img[29] << 8) + buf_2img[28]; - unix_pos = (buf_2img[27] << 24) + (buf_2img[26] << 16) + - (buf_2img[25] << 8) + buf_2img[24]; - if(size == 0x800c00) { - // Byte reversed 0x0c8000 - size = 0x0c8000; - } - dsk->image_start = unix_pos; - dsk->image_size = size; - } - exp_size = 800*1024; - if(dsk->disk_525) { - exp_size = 140*1024; - } - if(!image_identified) { - /* See if it might be the Mac diskcopy format */ - tmp = (buf_2img[0x40] << 24) + (buf_2img[0x41] << 16) + - (buf_2img[0x42] << 8) + buf_2img[0x43]; - if((size >= (exp_size + 0x54)) && (tmp == exp_size)) { - /* It's diskcopy since data size field matches */ - printf("Image named %s is in Mac diskcopy format\n", - dsk->name_ptr); - image_identified = 1; - dsk->image_start += 0x54; - dsk->image_size = exp_size; - dsk->image_type = DSK_TYPE_PRODOS; /* ProDOS */ - } - } - if(!image_identified) { - /* Assume raw image */ - dsk->image_size = size; - dsk->image_type = DSK_TYPE_PRODOS; - if(dsk->disk_525) { - dsk->image_type = DSK_TYPE_DOS33; - if(name_len >= 4) { - cmp_o = dsk->name_ptr[name_len-1]; - cmp_p = dsk->name_ptr[name_len-2]; - cmp_dot = dsk->name_ptr[name_len-3]; - if(cmp_dot == '.' && - (cmp_p == 'p' || cmp_p == 'P') && - (cmp_o == 'o' || cmp_o == 'O')) { - dsk->image_type = DSK_TYPE_PRODOS; - } - - cmp_b = dsk->name_ptr[name_len-1]; - cmp_i = dsk->name_ptr[name_len-2]; - cmp_n = dsk->name_ptr[name_len-3]; - cmp_dot = dsk->name_ptr[name_len-4]; - if(cmp_dot == '.' && - (cmp_n == 'n' || cmp_n == 'N') && - (cmp_i == 'i' || cmp_i == 'I') && - (cmp_b == 'b' || cmp_b == 'B')) { - dsk->image_type = DSK_TYPE_NIB; - dsk->write_prot = 1; - dsk->write_through_to_unix = 0; - } - } - } - } - - dsk->disk_dirty = 0; - dsk->nib_pos = 0; - dsk->trks = 0; - - if(dsk->smartport) { - g_highest_smartport_unit = MAX(dsk->drive, - g_highest_smartport_unit); - - if(partition_name != 0 || part_num >= 0) { - ret = cfg_partition_find_by_name_or_num(dsk->file, - partition_name, part_num, dsk); - printf("partition %s (num %d) mounted, wr_prot: %d\n", - partition_name, part_num, dsk->write_prot); - - if(ret < 0) { - fclose(dsk->file); - dsk->file = 0; - return; - } - } - iwm_printf("adding smartport device[%d], size:%08x, " - "img_sz:%08x\n", dsk->drive, dsk->trks[0].unix_len, - dsk->image_size); - } else if(dsk->disk_525) { - unix_pos = dsk->image_start; - size = dsk->image_size; - disk_set_num_tracks(dsk, 4*35); - len = 0x1000; - nibs = NIB_LEN_525; - if(dsk->image_type == DSK_TYPE_NIB) { - len = dsk->image_size / 35;; - nibs = len; - } - if(size != 35*len) { - fatal_printf("Disk 5.25 error: size is %d, not 140K. " - "Will try to mount anyway\n", size, 35*len); - } - for(i = 0; i < 35; i++) { - iwm_move_to_track(dsk, 4*i); - disk_unix_to_nib(dsk, 4*i, unix_pos, len, nibs); - unix_pos += len; - } - } else { - /* disk_35 */ - unix_pos = dsk->image_start; - size = dsk->image_size; - if(size != 800*1024) { - fatal_printf("Disk 3.5 error: size is %d, not 800K. " - "Will try to mount anyway\n", size); - } - disk_set_num_tracks(dsk, 2*80); - for(i = 0; i < 2*80; i++) { - iwm_move_to_track(dsk, i); - len = g_track_bytes_35[i >> 5]; - nibs = g_track_nibs_35[i >> 5]; - iwm_printf("Trk: %d.%d = unix: %08x, %04x, %04x\n", - i>>1, i & 1, unix_pos, len, nibs); - disk_unix_to_nib(dsk, i, unix_pos, len, nibs); - unix_pos += len; - - iwm_printf(" trk_len:%05x\n", dsk->trks[i].track_len); - } - } - - iwm_move_to_track(dsk, save_track); - -} - -void -eject_named_disk(Disk *dsk, const char *name, const char *partition_name) -{ - - if(!dsk->file) { - return; - } - - /* If name matches, eject the disk! */ - if(!strcmp(dsk->name_ptr, name)) { - /* It matches, eject it */ - if((partition_name != 0) && (dsk->partition_name != 0)) { - /* If both have partitions, and they differ, then */ - /* don't eject. Otherwise, eject */ - if(strcmp(dsk->partition_name, partition_name) != 0) { - /* Don't eject */ - return; - } - } - eject_disk(dsk); - } -} - -void -eject_disk_by_num(int slot, int drive) -{ - Disk *dsk; - - dsk = cfg_get_dsk_from_slot_drive(slot, drive); - - eject_disk(dsk); -} - -void -eject_disk(Disk *dsk) -{ - int motor_on; - int i; - - if(!dsk->file) { - return; - } - - g_config_gsport_update_needed = 1; - - motor_on = iwm.motor_on; - if(g_c031_disk35 & 0x40) { - motor_on = iwm.motor_on35; - } - if(motor_on) { - halt_printf("Try eject dsk:%s, but motor_on!\n", dsk->name_ptr); - } - - iwm_flush_disk_to_unix(dsk); - - printf("Ejecting disk: %s\n", dsk->name_ptr); - - /* Free all memory, close file */ - - /* free the tracks first */ - if(dsk->trks != 0) { - for(i = 0; i < dsk->num_tracks; i++) { - if(dsk->trks[i].nib_area) { - free(dsk->trks[i].nib_area); - } - dsk->trks[i].nib_area = 0; - dsk->trks[i].track_len = 0; - } - free(dsk->trks); - } - dsk->num_tracks = 0; - dsk->trks = 0; - - /* close file, clean up dsk struct */ - fclose(dsk->file); - - dsk->image_start = 0; - dsk->image_size = 0; - dsk->nib_pos = 0; - dsk->disk_dirty = 0; - dsk->write_through_to_unix = 0; - dsk->write_prot = 1; - dsk->file = 0; - dsk->just_ejected = 1; - - /* Leave name_ptr valid */ -} - -int -cfg_get_fd_size(char *filename) -{ - struct stat stat_buf; - int ret; - - ret = stat(filename, &stat_buf); - if(ret != 0) { - fprintf(stderr,"stat %s returned errno: %d\n", - filename, errno); - stat_buf.st_size = 0; - } - - return stat_buf.st_size; -} - -int -cfg_partition_read_block(FILE *file, void *buf, int blk, int blk_size) -{ - int ret; - - ret = fseek(file, blk * blk_size, SEEK_SET); - if(ret != 0) { - printf("fseek: wanted: %08x, errno: %d\n", - blk * blk_size, errno); - return 0; - } - - ret = fread((char *)buf, 1, blk_size, file); - if(ret != blk_size) { - printf("ret: %08x, wanted %08x, errno: %d\n", ret, blk_size, - errno); - return 0; - } - return ret; -} - -int -cfg_partition_find_by_name_or_num(FILE *file, const char *partnamestr, int part_num, - Disk *dsk) -{ - Cfg_dirent *direntptr; - int match; - int num_parts; - int i; - - num_parts = cfg_partition_make_list(dsk->name_ptr, file); - - if(num_parts <= 0) { - return -1; - } - - for(i = 0; i < g_cfg_partitionlist.last; i++) { - direntptr = &(g_cfg_partitionlist.direntptr[i]); - match = 0; - if((strncmp(partnamestr, direntptr->name, 32) == 0) && - (part_num < 0)) { - //printf("partition, match1, name:%s %s, part_num:%d\n", - // partnamestr, direntptr->name, part_num); - - match = 1; - } - if((partnamestr == 0) && (direntptr->part_num == part_num)) { - //printf("partition, match2, n:%s, part_num:%d == %d\n", - // direntptr->name, direntptr->part_num, part_num); - match = 1; - } - if(match) { - dsk->image_start = direntptr->image_start; - dsk->image_size = direntptr->size; - //printf("match with image_start: %08x, image_size: " - // "%08x\n", dsk->image_start, dsk->image_size); - - return i; - } - } - - return -1; -} - -int -cfg_partition_make_list(char *filename, FILE *file) -{ - Driver_desc *driver_desc_ptr; - Part_map *part_map_ptr; - word32 *blk_bufptr; - word32 start; - word32 len; - word32 data_off; - word32 data_len; - word32 sig; - int size; - int image_start, image_size; - int is_dir; - int block_size; - int map_blks; - int cur_blk; - - block_size = 512; - - cfg_free_alldirents(&g_cfg_partitionlist); - - blk_bufptr = (word32 *)malloc(MAX_PARTITION_BLK_SIZE); - - cfg_partition_read_block(file, blk_bufptr, 0, block_size); - - driver_desc_ptr = (Driver_desc *)blk_bufptr; - sig = GET_BE_WORD16(driver_desc_ptr->sig); - block_size = GET_BE_WORD16(driver_desc_ptr->blk_size); - if(block_size == 0) { - block_size = 512; - } - if(sig != 0x4552 || block_size < 0x200 || - (block_size > MAX_PARTITION_BLK_SIZE)) { - cfg_printf("Partition error: No driver descriptor map found\n"); - free(blk_bufptr); - return 0; - } - - map_blks = 1; - cur_blk = 0; - size = cfg_get_fd_size(filename); - cfg_file_add_dirent(&g_cfg_partitionlist, "None - Whole image", - is_dir=0, size, 0, -1); - - while(cur_blk < map_blks) { - cur_blk++; - cfg_partition_read_block(file, blk_bufptr, cur_blk, block_size); - part_map_ptr = (Part_map *)blk_bufptr; - sig = GET_BE_WORD16(part_map_ptr->sig); - if(cur_blk <= 1) { - map_blks = MIN(20, - GET_BE_WORD32(part_map_ptr->map_blk_cnt)); - } - if(sig != 0x504d) { - printf("Partition entry %d bad signature:%04x\n", - cur_blk, sig); - free(blk_bufptr); - return g_cfg_partitionlist.last; - } - - /* found it, check for consistency */ - start = GET_BE_WORD32(part_map_ptr->phys_part_start); - len = GET_BE_WORD32(part_map_ptr->part_blk_cnt); - data_off = GET_BE_WORD32(part_map_ptr->data_start); - data_len = GET_BE_WORD32(part_map_ptr->data_cnt); - if(data_off + data_len > len) { - printf("Poorly formed entry\n"); - continue; - } - - if(data_len < 10 || start < 1) { - printf("Poorly formed entry %d, datalen:%d, " - "start:%08x\n", cur_blk, data_len, start); - continue; - } - - image_size = data_len * block_size; - image_start = (start + data_off) * block_size; - is_dir = 2*(image_size < 800*1024); -#if 0 - printf(" partition add entry %d = %s %d %08x %08x\n", - cur_blk, part_map_ptr->part_name, is_dir, - image_size, image_start); -#endif - - cfg_file_add_dirent(&g_cfg_partitionlist, - part_map_ptr->part_name, is_dir, image_size, - image_start, cur_blk); - } - - free(blk_bufptr); - return g_cfg_partitionlist.last; -} - -int -cfg_maybe_insert_disk(int slot, int drive, const char *namestr) -{ - int num_parts; - FILE *file; - - file = fopen(namestr, "rb"); - if(!file) { - fatal_printf("Cannot open disk image: %s\n", namestr); - return 0; - } - - num_parts = cfg_partition_make_list((char*)namestr, file); - fclose(file); - - if(num_parts > 0) { - printf("Choose a partition\n"); - g_cfg_select_partition = 1; - } else { - insert_disk(slot, drive, namestr, 0, 0, 0, -1); - return 1; - } - return 0; -} - -int -cfg_stat(char *path, struct stat *sb) -{ - int removed_slash; - int len; - int ret; - - removed_slash = 0; - len = 0; - -#ifdef _WIN32 - /* Windows doesn't like to stat paths ending in a /, so remove it */ - len = strlen(path); - if((len > 1) && (path[len - 1] == '/') ) { - path[len - 1] = 0; /* remove the slash */ - removed_slash = 1; - } -#endif - - ret = stat(path, sb); - -#ifdef _WIN32 - /* put the slash back */ - if(removed_slash) { - path[len - 1] = '/'; - } -#endif - - return ret; -} - -void -cfg_htab_vtab(int x, int y) -{ - if(x > 79) { - x = 0; - } - if(y > 23) { - y = 0; - } - g_cfg_curs_x = x; - g_cfg_curs_y = y; - g_cfg_curs_inv = 0; - g_cfg_curs_mousetext = 0; -} - -void -cfg_home() -{ - int i; - - cfg_htab_vtab(0, 0); - for(i = 0; i < 24; i++) { - cfg_cleol(); - } -} - -void -cfg_cleol() -{ - g_cfg_curs_inv = 0; - g_cfg_curs_mousetext = 0; - cfg_putchar(' '); - while(g_cfg_curs_x != 0) { - cfg_putchar(' '); - } -} - -void -cfg_putchar(int c) -{ - int offset; - int x, y; - - if(c == '\n') { - cfg_cleol(); - return; - } - if(c == '\b') { - g_cfg_curs_inv = !g_cfg_curs_inv; - return; - } - if(c == '\t') { - g_cfg_curs_mousetext = !g_cfg_curs_mousetext; - return; - } - y = g_cfg_curs_y; - x = g_cfg_curs_x; - - offset = g_screen_index[g_cfg_curs_y]; - if((x & 1) == 0) { - offset += 0x10000; - } - if(g_cfg_curs_inv) { - if(c >= 0x40 && c < 0x60) { - c = c & 0x1f; - } - } else { - c = c | 0x80; - } - if(g_cfg_curs_mousetext) { - c = (c & 0x1f) | 0x40; - } - set_memory_c(0xe00400 + offset + (x >> 1), c, 0); - x++; - if(x >= 80) { - x = 0; - y++; - if(y >= 24) { - y = 0; - } - } - g_cfg_curs_y = y; - g_cfg_curs_x = x; -} - -void -cfg_printf(const char *fmt, ...) -{ - va_list ap; - int c; - int i; - - va_start(ap, fmt); - (void)vsnprintf(g_cfg_printf_buf, CFG_PRINTF_BUFSIZE, fmt, ap); - va_end(ap); - - for(i = 0; i < CFG_PRINTF_BUFSIZE; i++) { - c = g_cfg_printf_buf[i]; - if(c == 0) { - return; - } - cfg_putchar(c); - } -} - -void -cfg_print_num(int num, int max_len) -{ - char buf[64]; - char buf2[64]; - int len; - int cnt; - int c; - int i, j; - - /* Prints right-adjusted "num" in field "max_len" wide */ - snprintf(&buf[0], 64, "%d", num); - len = strlen(buf); - for(i = 0; i < 64; i++) { - buf2[i] = ' '; - } - j = max_len + 1; - buf2[j] = 0; - j--; - cnt = 0; - for(i = len - 1; (i >= 0) && (j >= 1); i--) { - c = buf[i]; - if(c >= '0' && c <= '9') { - if(cnt >= 3) { - buf2[j--] = ','; - cnt = 0; - } - cnt++; - } - buf2[j--] = c; - } - cfg_printf(&buf2[1]); -} - -void -cfg_get_disk_name(char *outstr, int maxlen, int type_ext, int with_extras) -{ - Disk *dsk; - int slot, drive; - - slot = type_ext >> 8; - drive = type_ext & 0xff; - dsk = cfg_get_dsk_from_slot_drive(slot, drive); - - outstr[0] = 0; - if(dsk->name_ptr == 0) { - return; - } - - config_generate_config_gsport_name(outstr, maxlen, dsk, with_extras); -} - -void -cfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int change) -{ - char valbuf[CFG_OPT_MAXSTR]; - char **str_ptr; - const char *menustr; - char *curstr, *defstr; - char *str; - char *outstr; - int *iptr; - int val; - int num_opts; - int opt_num; - int bufpos, outpos; - int curval, defval; - int type; - int type_ext; - int opt_get_str; - int separator; - int len; - int c; - int i; - - g_cfg_opt_buf[0] = 0; - - num_opts = 0; - opt_get_str = 0; - separator = ','; - - menuptr += menu_pos; /* move forward to entry menu_pos */ - - menustr = menuptr->str; - type = menuptr->cfgtype; - type_ext = (type >> 4); - type = type & 0xf; - len = strlen(menustr) + 1; - - bufpos = 0; - outstr = &(g_cfg_opt_buf[0]); - - outstr[bufpos++] = ' '; - outstr[bufpos++] = ' '; - outstr[bufpos++] = '\t'; - outstr[bufpos++] = '\t'; - outstr[bufpos++] = ' '; - outstr[bufpos++] = ' '; - - if(menu_pos == highlight_pos) { - outstr[bufpos++] = '\b'; - } - - opt_get_str = 2; - i = -1; - outpos = bufpos; -#if 0 - printf("cfg menu_pos: %d str len: %d\n", menu_pos, len); -#endif - while(++i < len) { - c = menustr[i]; - if(c == separator) { - if(i == 0) { - continue; - } - c = 0; - } - outstr[outpos++] = c; - outstr[outpos] = 0; - if(outpos >= CFG_OPT_MAXSTR) { - fprintf(stderr, "CFG_OPT_MAXSTR exceeded\n"); - my_exit(1); - } - if(c == 0) { - if(opt_get_str == 2) { - outstr = &(valbuf[0]); - bufpos = outpos - 1; - opt_get_str = 0; - } else if(opt_get_str) { -#if 0 - if(menu_pos == highlight_pos) { - printf("menu_pos %d opt %d = %s=%d\n", - menu_pos, num_opts, - g_cfg_opts_strs[num_opts], - g_cfg_opts_vals[num_opts]); - } -#endif - num_opts++; - outstr = &(valbuf[0]); - opt_get_str = 0; - if(num_opts >= CFG_MAX_OPTS) { - fprintf(stderr, "CFG_MAX_OPTS oflow\n"); - my_exit(1); - } - } else { - if (type == CFGTYPE_INT) - { - val = strtoul(valbuf, 0, 0); - g_cfg_opts_vals[num_opts] = val; - } - - if (type == CFGTYPE_STR) - { - strncpy(&(g_cfg_opts_strvals[num_opts][0]),&(valbuf[0]),CFG_OPT_MAXSTR); - } - outstr = &(g_cfg_opts_strs[num_opts][0]); - opt_get_str = 1; - } - outpos = 0; - outstr[0] = 0; - } - } - - if(menu_pos == highlight_pos) { - g_cfg_opt_buf[bufpos++] = '\b'; - } - - g_cfg_opt_buf[bufpos] = 0; - - // Figure out if we should get a checkmark - curval = -1; - defval = -1; - curstr = 0; - if(type == CFGTYPE_INT) { +#ifndef _WIN32 +{ "Force X-windows display depth", KNMP(g_force_depth), CFGTYPE_INT }, +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +{ "Debugging Options", g_cfg_debug_menu, 0, 0, CFGTYPE_MENU }, +#endif +{ "Auto-update configuration file,0,Manual,1,Immediately", + KNMP(g_config_gsport_auto_update), CFGTYPE_INT }, +{ "Speed,0,Unlimited,1,1.0MHz,2,2.8MHz,3,8.0MHz (Zip)", + KNMP(g_limit_speed), CFGTYPE_INT }, +{ "Expansion Mem Size,0,0MB,0x100000,1MB,0x200000,2MB,0x300000,3MB," + "0x400000,4MB,0x600000,6MB,0x800000,8MB,0xa00000,10MB,0xc00000,12MB," + "0xe00000,14MB", KNMP(g_mem_size_exp), CFGTYPE_INT }, +{ "3200 Color Enable,0,Auto (Full if fast enough),1,Full (Update every line)," + "8,Off (Update video every 8 lines)", + KNMP(g_video_line_update_interval), CFGTYPE_INT }, +{ "Keyboard and mouse poll rate,0,60 times per second,1,240 times per second", + KNMP(g_video_extra_check_inputs), CFGTYPE_INT }, +{ "Code Red Halts,0,Do not stop on bad accesses,1,Enter debugger on bad " + "accesses", KNMP(g_user_halt_bad), CFGTYPE_INT }, +{ "Enable Text Page 2 Shadow,0,Disabled on ROM 01 (matches real hardware)," + "1,Enabled on ROM 01 and 03", + KNMP(g_user_page2_shadow), CFGTYPE_INT }, +{ "Dump text screen to file", (void *)cfg_text_screen_dump, 0, 0, CFGTYPE_FUNC}, +{ "", 0, 0, 0, 0 }, +{ "Save changes to configuration file", (void *)config_write_config_gsport_file, 0, 0, + CFGTYPE_FUNC }, +{ "", 0, 0, 0, 0 }, +{ "Exit Config (or press F4)", (void *)cfg_exit, 0, 0, CFGTYPE_FUNC }, +{ 0, 0, 0, 0, 0 }, +}; + + +#define CFG_MAX_DEFVALS 128 +Cfg_defval g_cfg_defvals[CFG_MAX_DEFVALS]; +int g_cfg_defval_index = 0; + +int g_cfg_slotdrive = -1; +int g_cfg_select_partition = -1; +char g_cfg_tmp_path[CFG_PATH_MAX]; +char g_cfg_file_path[CFG_PATH_MAX]; +char g_cfg_file_cachedpath[CFG_PATH_MAX]; +char g_cfg_file_cachedreal[CFG_PATH_MAX]; +char g_cfg_file_curpath[CFG_PATH_MAX]; +char g_cfg_file_shortened[CFG_PATH_MAX]; +char g_cfg_file_match[CFG_PATH_MAX]; + +Cfg_listhdr g_cfg_dirlist = { 0 }; +Cfg_listhdr g_cfg_partitionlist = { 0 }; + +int g_cfg_file_pathfield = 0; + +const char *g_gsport_rom_names[] = { "ROM", "ROM", "ROM.01", "ROM.03", 0 }; + /* First entry is special--it will be overwritten by g_cfg_rom_path */ + +const char *g_gsport_c1rom_names[] = { "parallel.rom", 0 }; +const char *g_gsport_c2rom_names[] = { 0 }; +const char *g_gsport_c3rom_names[] = { 0 }; +const char *g_gsport_c4rom_names[] = { 0 }; +const char *g_gsport_c5rom_names[] = { 0 }; +const char *g_gsport_c6rom_names[] = { "c600.rom", "controller.rom", "disk.rom", + "DISK.ROM", "diskII.prom", 0 }; +const char *g_gsport_c7rom_names[] = { 0 }; + +const char **g_gsport_rom_card_list[8] = { + 0, g_gsport_c1rom_names, + g_gsport_c2rom_names, g_gsport_c3rom_names, + g_gsport_c4rom_names, g_gsport_c5rom_names, + g_gsport_c6rom_names, g_gsport_c7rom_names }; + +byte g_rom_c600_rom01_diffs[256] = { + 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0xe2, 0x00, + 0xd0, 0x50, 0x0f, 0x77, 0x5b, 0xb9, 0xc3, 0xb1, + 0xb1, 0xf8, 0xcb, 0x4e, 0xb8, 0x60, 0xc7, 0x2e, + 0xfc, 0xe0, 0xbf, 0x1f, 0x66, 0x37, 0x4a, 0x70, + 0x55, 0x2c, 0x3c, 0xfc, 0xc2, 0xa5, 0x08, 0x29, + 0xac, 0x21, 0xcc, 0x09, 0x55, 0x03, 0x17, 0x35, + 0x4e, 0xe2, 0x0c, 0xe9, 0x3f, 0x9d, 0xc2, 0x06, + 0x18, 0x88, 0x0d, 0x58, 0x57, 0x6d, 0x83, 0x8c, + 0x22, 0xd3, 0x4f, 0x0a, 0xe5, 0xb7, 0x9f, 0x7d, + 0x2c, 0x3e, 0xae, 0x7f, 0x24, 0x78, 0xfd, 0xd0, + 0xb5, 0xd6, 0xe5, 0x26, 0x85, 0x3d, 0x8d, 0xc9, + 0x79, 0x0c, 0x75, 0xec, 0x98, 0xcc, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x39, 0x00, 0x35, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, + 0x6c, 0x44, 0xce, 0x4c, 0x01, 0x08, 0x00, 0x00 +}; + + +void +config_init_menus(Cfg_menu *menuptr) +{ + void *voidptr; + const char *name_str; + Cfg_defval *defptr; + char **str_ptr; + char *str; + int type; + int pos; + int val; + + if(menuptr[0].defptr != 0) { + return; + } + menuptr[0].defptr = (void *)1; + pos = 0; + while(pos < 100) { + type = menuptr->cfgtype; + voidptr = menuptr->ptr; + name_str = menuptr->name_str; + if(menuptr->str == 0) { + break; + } + if(name_str != 0) { + defptr = &(g_cfg_defvals[g_cfg_defval_index++]); + if(g_cfg_defval_index >= CFG_MAX_DEFVALS) { + fatal_printf("CFG_MAX_DEFVAL overflow\n"); + my_exit(5); + } + defptr->menuptr = menuptr; + defptr->intval = 0; + defptr->strval = 0; + switch(type) { + case CFGTYPE_INT: + val = *((int *)voidptr); + defptr->intval = val; + menuptr->defptr = &(defptr->intval); + break; + case CFGTYPE_STR: + str_ptr = (char **)menuptr->ptr; + str = *str_ptr; + // We need to malloc this string since all + // string values must be dynamically alloced + defptr->strval = str; // this can have a copy + *str_ptr = gsport_malloc_str(str); + menuptr->defptr = &(defptr->strval); + break; + case CFGTYPE_FILE: + str_ptr = (char **)menuptr->ptr; + str = *str_ptr; + // We need to malloc this string since all + // string values must be dynamically alloced + defptr->strval = str; // this can have a copy + *str_ptr = gsport_malloc_str(str); + menuptr->defptr = &(defptr->strval); + break; + default: + fatal_printf("name_str is %p = %s, but type: " + "%d\n", name_str, name_str, type); + my_exit(5); + } + } + if(type == CFGTYPE_MENU) { + config_init_menus((Cfg_menu *)voidptr); + } + pos++; + menuptr++; + } +} + +void +config_init() +{ + int can_create; + + config_init_menus(g_cfg_main_menu); + + // Find the configuration file + g_config_gsport_name[0] = 0; + can_create = 1; + setup_gsport_file(&g_config_gsport_name[0], sizeof(g_config_gsport_name), 0, + can_create, &g_config_gsport_name_list[0]); + + config_parse_config_gsport_file(); +} + +void +cfg_exit() +{ + /* printf("In cfg exit\n"); */ + if(g_rom_version >= 1) { + g_config_control_panel = 0; + } +} + +void +cfg_toggle_config_panel() +{ + g_config_control_panel = !g_config_control_panel; + if(g_rom_version < 0) { + g_config_control_panel = 1; /* Stay in config mode */ + } +} + +void +cfg_text_screen_dump() +{ + char buf[85]; + char *filename; + FILE *ofile; + int offset; + int c; + int pos; + int i, j; + + filename = "gsport.screen.dump"; + printf("Writing text screen to the file %s\n", filename); + ofile = fopen(filename, "w"); + if(ofile == 0) { + fatal_printf("Could not write to file %s, (%d)\n", filename, + errno); + return; + } + + for(i = 0; i < 24; i++) { + pos = 0; + for(j = 0; j < 40; j++) { + offset = g_screen_index[i] + j; + if(g_save_cur_a2_stat & ALL_STAT_VID80) { + c = g_save_text_screen_bytes[0x400+offset]; + c = c & 0x7f; + if(c < 0x20) { + c += 0x40; + } + buf[pos++] = c; + } + c = g_save_text_screen_bytes[offset] & 0x7f; + if(c < 0x20) { + c += 0x40; + } + buf[pos++] = c; + } + while((pos > 0) && (buf[pos-1] == ' ')) { + /* try to strip out trailing spaces */ + pos--; + } + buf[pos++] = '\n'; + buf[pos++] = 0; + fputs(buf, ofile); + } + fclose(ofile); +} + +#ifdef HAVE_TFE +void +cfg_get_tfe_name() +{ + int i = 0; + char *ppname = NULL; + char *ppdes = NULL; + cfg_htab_vtab(0,10); + if (tfe_enumadapter_open()) + { + cfg_printf("Interface List:\n---------------"); + while(tfe_enumadapter(&ppname,&ppdes)) + { + cfg_htab_vtab(0, 12+i); + cfg_printf("%2d: %s",i,ppdes); + i++; + lib_free(ppname); + lib_free(ppdes); + } + tfe_enumadapter_close(); + } + else + { + #if defined(_WIN32) || defined(__CYGWIN__) + cfg_printf("ERROR: Install/Enable WinPcap for Ethernet Support!!"); + #else + cfg_printf("ERROR: Install/Enable LibPcap for Ethernet Support!!"); + #endif + } + return; +} +#endif + +void +config_vbl_update(int doit_3_persec) +{ + if(doit_3_persec) { + if(g_config_gsport_auto_update && g_config_gsport_update_needed) { + config_write_config_gsport_file(); + } + } + return; +} + +void +config_parse_option(char *buf, int pos, int len, int line) +{ + Cfg_menu *menuptr; + Cfg_defval *defptr; + char *nameptr; + char **strptr; + int *iptr; + int num_equals; + int type; + int val; + int c; + int i; + +// warning: modifies buf (turns spaces to nulls) +// parse buf from pos into option, "=" and then "rest" + if(pos >= len) { + /* blank line */ + return; + } + + if(strncmp(&buf[pos], "bram", 4) == 0) { + config_parse_bram(buf, pos+4, len); + return; + } + + // find "name" as first contiguous string + printf("...parse_option: line %d, %p,%p = %s (%s) len:%d\n", line, + &buf[pos], buf, &buf[pos], buf, len); + + nameptr = &buf[pos]; + while(pos < len) { + c = buf[pos]; + if(c == 0 || c == ' ' || c == '\t' || c == '\n') { + break; + } + pos++; + } + buf[pos] = 0; + pos++; + + // Eat up all whitespace and '=' + num_equals = 0; + while(pos < len) { + c = buf[pos]; + if((c == '=') && num_equals == 0) { + pos++; + num_equals++; + } else if(c == ' ' || c == '\t') { + pos++; + } else { + break; + } + } + + /* Look up nameptr to find type */ + type = -1; + defptr = 0; + menuptr = 0; + for(i = 0; i < g_cfg_defval_index; i++) { + defptr = &(g_cfg_defvals[i]); + menuptr = defptr->menuptr; + if(strcmp(menuptr->name_str, nameptr) == 0) { + type = menuptr->cfgtype; + break; + } + } + + switch(type) { + case CFGTYPE_INT: + /* use strtol */ + val = (int)strtol(&buf[pos], 0, 0); + iptr = (int *)menuptr->ptr; + *iptr = val; + break; + case CFGTYPE_STR: + strptr = (char **)menuptr->ptr; + if(strptr && *strptr) { + free(*strptr); + } + *strptr = gsport_malloc_str(&buf[pos]); + break; + case CFGTYPE_FILE: + strptr = (char **)menuptr->ptr; + if(strptr && *strptr) { + free(*strptr); + } + *strptr = gsport_malloc_str(&buf[pos]); + break; + default: + printf("Config file variable %s is unknown type: %d\n", + nameptr, type); + } + +} + +void +config_parse_bram(char *buf, int pos, int len) +{ + int bram_num; + int offset; + int val; + + if((len < (pos+5)) || (buf[pos+1] != '[') || (buf[pos+4] != ']')) { + fatal_printf("While reading configuration file, found malformed bram " + "statement: %s\n", buf); + return; + } + bram_num = buf[pos] - '0'; + if(bram_num != 1 && bram_num != 3) { + fatal_printf("While reading configuration file, found bad bram " + "num: %s\n", buf); + return; + } + + bram_num = bram_num >> 1; // turn 3->1 and 1->0 + + offset = strtoul(&(buf[pos+2]), 0, 16); + pos += 5; + while(pos < len) { + while(buf[pos] == ' ' || buf[pos] == '\t' || buf[pos] == 0x0a || + buf[pos] == 0x0d || buf[pos] == '=') { + pos++; + } + val = strtoul(&buf[pos], 0, 16); + clk_bram_set(bram_num, offset, val); + offset++; + pos += 2; + } +} + +void +config_load_roms() +{ + struct stat stat_buf; + const char **names_ptr; + int more_than_8mb; + int changed_rom; + int len; + FILE *file; + int ret; + int i; + + g_rom_version = -1; + + /* set first entry of g_gsport_rom_names[] to g_cfg_rom_path so that */ + /* it becomes the first place searched. */ + g_gsport_rom_names[0] = g_cfg_rom_path; + setup_gsport_file(&g_cfg_tmp_path[0], CFG_PATH_MAX, -1, 0, + &g_gsport_rom_names[0]); + + if(g_cfg_tmp_path[0] == 0) { + // Just get out, let config interface select ROM + g_config_control_panel = 1; + return; + } + file = fopen(&g_cfg_tmp_path[0], "rb"); + if(!file) { + fatal_printf("Open ROM file %s failed; errno:%d\n", + &g_cfg_tmp_path[0], errno); + g_config_control_panel = 1; + return; + } + + ret = stat(&g_cfg_tmp_path[0], &stat_buf); + if(ret != 0) { + fatal_printf("stat returned %d; errno: %d\n", + ret, errno); + g_config_control_panel = 1; + return; + } + + len = stat_buf.st_size; + if(len == 128*1024) { + g_rom_version = 1; + g_mem_size_base = 256*1024; + memset(&g_rom_fc_ff_ptr[0], 0, 2*65536); + /* Clear banks fc and fd to 0 */ + ret = fread(&g_rom_fc_ff_ptr[2*65536], 1, len, file); + } else if(len == 256*1024) { + g_rom_version = 3; + g_mem_size_base = 1024*1024; + ret = fread(&g_rom_fc_ff_ptr[0], 1, len, file); + } else { + fatal_printf("The ROM size should be 128K or 256K, this file " + "is %d bytes\n", len); + g_config_control_panel = 1; + return; + } + + printf("Read: %d bytes of ROM\n", ret); + if(ret != len) { + fatal_printf("errno: %d\n", errno); + g_config_control_panel = 1; + return; + } + fclose(file); + + memset(&g_rom_cards_ptr[0], 0, 256*16); + + /* initialize c600 rom to be diffs from the real ROM, to build-in */ + /* Apple II compatibility without distributing ROMs */ + for(i = 0; i < 256; i++) { + g_rom_cards_ptr[0x600 + i] = g_rom_fc_ff_ptr[0x3c600 + i] ^ + g_rom_c600_rom01_diffs[i]; + } + if(g_rom_version >= 3) { + /* some patches */ + g_rom_cards_ptr[0x61b] ^= 0x40; + g_rom_cards_ptr[0x61c] ^= 0x33; + g_rom_cards_ptr[0x632] ^= 0xc0; + g_rom_cards_ptr[0x633] ^= 0x33; + } + + for(i = 1; i < 8; i++) { + names_ptr = g_gsport_rom_card_list[i]; + if(names_ptr == 0) { + continue; + } + if(*names_ptr == 0) { + continue; + } + setup_gsport_file(&g_cfg_tmp_path[0], CFG_PATH_MAX, 1, 0, + names_ptr); + if(g_cfg_tmp_path[0] != 0) { + file = fopen(&(g_cfg_tmp_path[0]), "rb"); + if(!file) { + fatal_printf("Open card ROM file %s failed; errno:%d\n", + &g_cfg_tmp_path[0], errno); + continue; + } + + len = 256; + ret = fread(&g_rom_cards_ptr[i*0x100], 1, len, file); + + if(ret != len) { + fatal_printf("While reading card ROM %s, file " + "is too short. (%d) Expected %d bytes, " + "read %d bytes\n", &g_cfg_tmp_path[0], errno, len, ret); + continue; + } + printf("Read: %d bytes of ROM in slot %d from file %s.\n", ret, i, &g_cfg_tmp_path[0]); + fclose(file); + } + } + more_than_8mb = (g_mem_size_exp > 0x800000); + /* Only do the patch if users wants more than 8MB of expansion mem */ + + changed_rom = 0; + if(g_rom_version == 1) { + /* make some patches to ROM 01 */ +#if 0 + /* 1: Patch ROM selftest to not do speed test */ + printf("Patching out speed test failures from ROM 01\n"); + g_rom_fc_ff_ptr[0x3785a] = 0x18; + changed_rom = 1; +#endif + +#if 0 + /* 2: Patch ROM selftests not to do tests 2,4 */ + /* 0 = skip, 1 = do it, test 1 is bit 0 of LSByte */ + g_rom_fc_ff_ptr[0x371e9] = 0xf5; + g_rom_fc_ff_ptr[0x371ea] = 0xff; + changed_rom = 1; +#endif + + if(more_than_8mb) { + /* Geoff Weiss patch to use up to 14MB of RAM */ + g_rom_fc_ff_ptr[0x30302] = 0xdf; + g_rom_fc_ff_ptr[0x30314] = 0xdf; + g_rom_fc_ff_ptr[0x3031c] = 0x00; + changed_rom = 1; + } + + /* Patch ROM selftest to not do ROM cksum if any changes*/ + if(changed_rom) { + g_rom_fc_ff_ptr[0x37a06] = 0x18; + g_rom_fc_ff_ptr[0x37a07] = 0x18; + } + } else if(g_rom_version == 3) { + /* patch ROM 03 */ + printf("Patching ROM 03 smartport bug\n"); + /* 1: Patch Smartport code to fix a stupid bug */ + /* that causes it to write the IWM status reg into c036, */ + /* which is the system speed reg...it's "safe" since */ + /* IWM status reg bit 4 must be 0 (7MHz)..., otherwise */ + /* it might have turned on shadowing in all banks! */ + g_rom_fc_ff_ptr[0x357c9] = 0x00; + changed_rom = 1; + +#if 0 + /* patch ROM 03 to not to speed test */ + /* skip fast speed test */ + g_rom_fc_ff_ptr[0x36ad7] = 0x18; + g_rom_fc_ff_ptr[0x36ad8] = 0x18; + changed_rom = 1; +#endif + +#if 0 + /* skip slow speed test */ + g_rom_fc_ff_ptr[0x36ae7] = 0x18; + g_rom_fc_ff_ptr[0x36ae8] = 0x6b; + changed_rom = 1; +#endif + +#if 0 + /* 4: Patch ROM 03 selftests not to do tests 1-4 */ + g_rom_fc_ff_ptr[0x364a9] = 0xf0; + g_rom_fc_ff_ptr[0x364aa] = 0xff; + changed_rom = 1; +#endif + + /* ROM tests are in ff/6403-642x, where 6403 = addr of */ + /* test 1, etc. */ + + if(more_than_8mb) { + /* Geoff Weiss patch to use up to 14MB of RAM */ + g_rom_fc_ff_ptr[0x30b] = 0xdf; + g_rom_fc_ff_ptr[0x31d] = 0xdf; + g_rom_fc_ff_ptr[0x325] = 0x00; + changed_rom = 1; + } + + if(changed_rom) { + /* patch ROM 03 selftest to not do ROM cksum */ + g_rom_fc_ff_ptr[0x36cb0] = 0x18; + g_rom_fc_ff_ptr[0x36cb1] = 0x18; + } + + } +} + +void +config_parse_config_gsport_file() +{ + FILE *fconf; + char *buf; + char *ptr; + char *name_ptr; + char *partition_name; + int part_num; + int ejected; + int line; + int pos; + int slot; + int drive; + int size; + int len; + int ret; + int i; + + printf("Parsing configuration file\n"); + + clk_bram_zero(); + + g_highest_smartport_unit = -1; + + cfg_get_base_path(&g_cfg_cwd_str[0], g_config_gsport_name, 0); +#ifndef __OS2__ + if(g_cfg_cwd_str[0] != 0) { + ret = chdir(&g_cfg_cwd_str[0]); + if(ret != 0) { + printf("chdir to %s, errno:%d\n", g_cfg_cwd_str, errno); + } + } + /* In any case, copy the directory path to g_cfg_cwd_str */ + (void)getcwd(&g_cfg_cwd_str[0], CFG_PATH_MAX); +#endif + + fconf = fopen(g_config_gsport_name, "r"); + if(fconf == 0) { + fatal_printf("cannot open configuration file at %s! Stopping!\n", + g_config_gsport_name); + my_exit(3); + } + + line = 0; + while(1) { + buf = &g_config_gsport_buf[0]; + ptr = fgets(buf, CONF_BUF_LEN, fconf); + if(ptr == 0) { + iwm_printf("Done reading disk_conf\n"); + break; + } + + line++; + /* strip off newline(s) */ + len = strlen(buf); + for(i = len - 1; i >= 0; i--) { + if((buf[i] != 0x0d) && (buf[i] != 0x0a)) { + break; + } + len = i; + buf[i] = 0; + } + + iwm_printf("disk_conf[%d]: %s\n", line, buf); + if(len > 0 && buf[0] == '#') { + iwm_printf("Skipping comment\n"); + continue; + } + + /* determine what this is */ + pos = 0; + + while(pos < len && (buf[pos] == ' ' || buf[pos] == '\t') ) { + pos++; + } + if((pos + 4) > len || buf[pos] != 's' || buf[pos+2] != 'd' || + buf[pos+1] > '9' || buf[pos+1] < '0') { + config_parse_option(buf, pos, len, line); + continue; + } + + slot = buf[pos+1] - '0'; + drive = buf[pos+3] - '0'; + + /* skip over slot, drive */ + pos += 4; + if(buf[pos] >= '0' && buf[pos] <= '9') { + drive = drive * 10 + buf[pos] - '0'; + pos++; + } + + /* make s6d1 mean index 0 */ + drive--; + + while(pos < len && (buf[pos] == ' ' || buf[pos] == '\t' || + buf[pos] == '=') ) { + pos++; + } + + ejected = 0; + if(buf[pos] == '#') { + /* disk is ejected, but read all the info anyway */ + ejected = 1; + pos++; + } + + size = 0; + if(buf[pos] == ',') { + /* read optional size parameter */ + pos++; + while(pos < len && buf[pos] >= '0' && buf[pos] <= '9'){ + size = size * 10 + buf[pos] - '0'; + pos++; + } + size = size * 1024; + if(buf[pos] == ',') { + pos++; /* eat trailing ',' */ + } + } + + /* see if it has a partition name */ + partition_name = 0; + part_num = -1; + if(buf[pos] == ':') { + pos++; + /* yup, it's got a partition name! */ + partition_name = &buf[pos]; + while((pos < len) && (buf[pos] != ':')) { + pos++; + } + buf[pos] = 0; /* null terminate partition name */ + pos++; + } + if(buf[pos] == ';') { + pos++; + /* it's got a partition number */ + part_num = 0; + while((pos < len) && (buf[pos] != ':')) { + part_num = (10*part_num) + buf[pos] - '0'; + pos++; + } + pos++; + } + + /* Get filename */ + name_ptr = &(buf[pos]); + if(name_ptr[0] == 0) { + continue; + } + + insert_disk(slot, drive, name_ptr, ejected, size, + partition_name, part_num); + + } + + ret = fclose(fconf); + if(ret != 0) { + fatal_printf("Closing configuration file ret: %d, errno: %d\n", ret, + errno); + my_exit(4); + } + + iwm_printf("Done parsing disk_conf file\n"); +} + + +Disk * +cfg_get_dsk_from_slot_drive(int slot, int drive) +{ + Disk *dsk; + int max_drive; + + /* Get dsk */ + max_drive = 2; + switch(slot) { + case 5: + dsk = &(iwm.drive35[drive]); + break; + case 6: + dsk = &(iwm.drive525[drive]); + break; + default: + max_drive = MAX_C7_DISKS; + dsk = &(iwm.smartport[drive]); + } + + if(drive >= max_drive) { + dsk -= drive; /* move back to drive 0 effectively */ + } + + return dsk; +} + +void +config_generate_config_gsport_name(char *outstr, int maxlen, Disk *dsk, + int with_extras) +{ + char *str; + + str = outstr; + + if(with_extras && (!dsk->file)) { + snprintf(str, maxlen - (str - outstr), "#"); + str = &outstr[strlen(outstr)]; + } + if(with_extras && dsk->force_size > 0) { + snprintf(str, maxlen - (str - outstr), ",%d,", dsk->force_size); + str = &outstr[strlen(outstr)]; + } + if(with_extras && dsk->partition_name != 0) { + snprintf(str, maxlen - (str - outstr), ":%s:", + dsk->partition_name); + str = &outstr[strlen(outstr)]; + } else if(with_extras && dsk->partition_num >= 0) { + snprintf(str, maxlen - (str - outstr), ";%d:", + dsk->partition_num); + str = &outstr[strlen(outstr)]; + } + snprintf(str, maxlen - (str - outstr), "%s", dsk->name_ptr); +} + +void +config_write_config_gsport_file() +{ + FILE *fconf; + Disk *dsk; + Cfg_defval *defptr; + Cfg_menu *menuptr; + char *curstr, *defstr; + int defval, curval; + int type; + int slot, drive; + int i; + + printf("Writing configuration file to %s\n", g_config_gsport_name); + + fconf = fopen(g_config_gsport_name, "w+"); + if(fconf == 0) { + halt_printf("cannot open %s! Stopping!\n",g_config_gsport_name); + return; + } + + fprintf(fconf, "# GSport configuration file version %s\n", + g_gsport_version_str); + + for(i = 0; i < MAX_C7_DISKS + 4; i++) { + slot = 7; + drive = i - 4; + if(i < 4) { + slot = (i >> 1) + 5; + drive = i & 1; + } + if(drive == 0) { + fprintf(fconf, "\n"); /* an extra blank line */ + } + + dsk = cfg_get_dsk_from_slot_drive(slot, drive); + if(dsk->name_ptr == 0 && (i > 4)) { + /* No disk, not even ejected--just skip */ + continue; + } + fprintf(fconf, "s%dd%d = ", slot, drive + 1); + if(dsk->name_ptr == 0) { + fprintf(fconf, "\n"); + continue; + } + config_generate_config_gsport_name(&g_cfg_tmp_path[0], + CFG_PATH_MAX, dsk, 1); + fprintf(fconf, "%s\n", &g_cfg_tmp_path[0]); + } + + fprintf(fconf, "\n"); + + /* See if any variables are different than their default */ + for(i = 0; i < g_cfg_defval_index; i++) { + defptr = &(g_cfg_defvals[i]); + menuptr = defptr->menuptr; + defval = defptr->intval; + type = menuptr->cfgtype; + if(type == CFGTYPE_INT) { + curval = *((int *)menuptr->ptr); + if(curval != defval) { + fprintf(fconf, "%s = %d\n", menuptr->name_str, + curval); + } + } + if(type == CFGTYPE_STR) { + curstr = *((char **)menuptr->ptr); + defstr = *((char **)menuptr->defptr); + if(strcmp(curstr, defstr) != 0) { + fprintf(fconf, "%s = %s\n", menuptr->name_str, + curstr); + } + } + if(type == CFGTYPE_FILE) { + curstr = *((char **)menuptr->ptr); + defstr = *((char **)menuptr->defptr); + if(strcmp(curstr, defstr) != 0) { + fprintf(fconf, "%s = %s\n", menuptr->name_str, + curstr); + } + } + } + + fprintf(fconf, "\n"); + + /* write bram state */ + clk_write_bram(fconf); + + fclose(fconf); + + g_config_gsport_update_needed = 0; +} + +void +insert_disk(int slot, int drive, const char *name, int ejected, int force_size, + const char *partition_name, int part_num) +{ + byte buf_2img[512]; + Disk *dsk; + char *name_ptr, *uncomp_ptr, *system_str; + char *part_ptr; + int size; + int system_len; + int part_len; + int cmp_o, cmp_p, cmp_dot; + int cmp_b, cmp_i, cmp_n; + int can_write; + int len; + int nibs; + int unix_pos; + int name_len; + int image_identified; + int exp_size; + int save_track; + int ret; + int tmp; + int i; + + g_config_gsport_update_needed = 1; + + if((slot < 5) || (slot > 7)) { + fatal_printf("Invalid slot for inserting disk: %d\n", slot); + return; + } + if(drive < 0 || ((slot == 7) && (drive >= MAX_C7_DISKS)) || + ((slot < 7) && (drive > 1))) { + fatal_printf("Invalid drive for inserting disk: %d\n", drive); + return; + } + + dsk = cfg_get_dsk_from_slot_drive(slot, drive); + +#if 0 + printf("Inserting disk %s (%s or %d) in slot %d, drive: %d\n", name, + partition_name, part_num, slot, drive); +#endif + + dsk->just_ejected = 0; + dsk->force_size = force_size; + + if(!dsk->file) { + eject_disk(dsk); + } + + /* Before opening, make sure no other mounted disk has this name */ + /* If so, unmount it */ + if(!ejected) { + for(i = 0; i < 2; i++) { + eject_named_disk(&iwm.drive525[i], name,partition_name); + eject_named_disk(&iwm.drive35[i], name, partition_name); + } + for(i = 0; i < MAX_C7_DISKS; i++) { + eject_named_disk(&iwm.smartport[i],name,partition_name); + } + } + + if(dsk->name_ptr != 0) { + /* free old name_ptr */ + free(dsk->name_ptr); + } + + name_len = strlen(name); + name_ptr = (char *)malloc(name_len + 1); +#if defined(_WIN32) || defined(__CYGWIN__) + // On Windows, we need to change backslashes to forward slashes. + for (i = 0; i < name_len; i++) { + if (name[i] == '\\') { + name_ptr[i] = '/'; + } else { + name_ptr[i] = name[i]; + } + } + name_ptr[name_len] = 0; +#else + strncpy(name_ptr, name, name_len + 1); +#endif + dsk->name_ptr = name_ptr; + + dsk->partition_name = 0; + if(partition_name != 0) { + part_len = strlen(partition_name) + 1; + part_ptr = (char *)malloc(part_len); + strncpy(part_ptr, partition_name, part_len); + dsk->partition_name = part_ptr; + } + dsk->partition_num = part_num; + + iwm_printf("Opening up disk image named: %s\n", name_ptr); + + if(ejected) { + /* just get out of here */ + dsk->file = 0; + return; + } + + dsk->file = 0; + can_write = 1; + + if((name_len > 3) && (strcmp(&name_ptr[name_len - 3], ".gz") == 0)) { + + /* it's gzip'ed, try to gunzip it, then unlink the */ + /* uncompressed file */ + + can_write = 0; + + uncomp_ptr = (char *)malloc(name_len + 1); + strncpy(uncomp_ptr, name_ptr, name_len + 1); + uncomp_ptr[name_len - 3] = 0; + + system_len = 2*name_len + 100; + system_str = (char *)malloc(system_len + 1); + snprintf(system_str, system_len, + "set -o noclobber;gunzip -c %c%s%c > %c%s%c", + 0x22, name_ptr, 0x22, + 0x22, uncomp_ptr, 0x22); + /* 0x22 are " to allow spaces in filenames */ + printf("I am uncompressing %s into %s for mounting\n", + name_ptr, uncomp_ptr); + ret = system(system_str); + if(ret == 0) { + /* successfully ran */ + dsk->file = fopen(uncomp_ptr, "rb"); + iwm_printf("Opening .gz file %s\n", uncomp_ptr); + + /* and, unlink the temporary file */ + (void)unlink(uncomp_ptr); + } + free(system_str); + free(uncomp_ptr); + /* Reduce name_len by 3 so that subsequent compares for .po */ + /* look at the correct chars */ + name_len -= 3; + } + + if((!dsk->file) && can_write) { + dsk->file = fopen(name_ptr, "rb+"); + } + + if((!dsk->file) && can_write) { + printf("Trying to open %s read-only, errno: %d\n", name_ptr, + errno); + dsk->file = fopen(name_ptr, "rb"); + can_write = 0; + } + + if(!dsk->file) { + fatal_printf("Disk image %s does not exist!\n", name_ptr); + return; + } + + if(can_write != 0) { + dsk->write_prot = 0; + dsk->write_through_to_unix = 1; + } else { + dsk->write_prot = 1; + dsk->write_through_to_unix = 0; + } + + save_track = dsk->cur_qtr_track; /* save arm position */ + dsk->image_type = DSK_TYPE_PRODOS; + dsk->image_start = 0; + + /* See if it is in 2IMG format */ + ret = fread((char *)&buf_2img[0], 1, 512, dsk->file); + size = force_size; + if(size <= 0) { + size = cfg_get_fd_size(name_ptr); + } + + /* Try to guess that there is a Mac Binary header of 128 bytes */ + /* See if image size & 0xfff = 0x080 which indicates extra 128 bytes */ + if((size & 0xfff) == 0x080) { + printf("Assuming Mac Binary header on %s\n", dsk->name_ptr); + dsk->image_start += 0x80; + } + image_identified = 0; + if(buf_2img[0] == '2' && buf_2img[1] == 'I' && buf_2img[2] == 'M' && + buf_2img[3] == 'G') { + /* It's a 2IMG disk */ + printf("Image named %s is in 2IMG format\n", dsk->name_ptr); + image_identified = 1; + + if(buf_2img[12] == 0) { + printf("2IMG is in DOS 3.3 sector order\n"); + dsk->image_type = DSK_TYPE_DOS33; + } + if(buf_2img[19] & 0x80) { + /* disk is locked */ + printf("2IMG is write protected\n"); + dsk->write_prot = 1; + dsk->write_through_to_unix = 0; + } + if((buf_2img[17] & 1) && (dsk->image_type == DSK_TYPE_DOS33)) { + dsk->vol_num = buf_2img[16]; + printf("Setting DOS 3.3 vol num to %d\n", dsk->vol_num); + } + // Some 2IMG archives have the size byte reversed + size = (buf_2img[31] << 24) + (buf_2img[30] << 16) + + (buf_2img[29] << 8) + buf_2img[28]; + unix_pos = (buf_2img[27] << 24) + (buf_2img[26] << 16) + + (buf_2img[25] << 8) + buf_2img[24]; + if(size == 0x800c00) { + // Byte reversed 0x0c8000 + size = 0x0c8000; + } + dsk->image_start = unix_pos; + dsk->image_size = size; + } + exp_size = 800*1024; + if(dsk->disk_525) { + exp_size = 140*1024; + } + if(!image_identified) { + /* See if it might be the Mac diskcopy format */ + tmp = (buf_2img[0x40] << 24) + (buf_2img[0x41] << 16) + + (buf_2img[0x42] << 8) + buf_2img[0x43]; + if((size >= (exp_size + 0x54)) && (tmp == exp_size)) { + /* It's diskcopy since data size field matches */ + printf("Image named %s is in Mac diskcopy format\n", + dsk->name_ptr); + image_identified = 1; + dsk->image_start += 0x54; + dsk->image_size = exp_size; + dsk->image_type = DSK_TYPE_PRODOS; /* ProDOS */ + } + } + if(!image_identified) { + /* Assume raw image */ + dsk->image_size = size; + dsk->image_type = DSK_TYPE_PRODOS; + if(dsk->disk_525) { + dsk->image_type = DSK_TYPE_DOS33; + if(name_len >= 4) { + cmp_o = dsk->name_ptr[name_len-1]; + cmp_p = dsk->name_ptr[name_len-2]; + cmp_dot = dsk->name_ptr[name_len-3]; + if(cmp_dot == '.' && + (cmp_p == 'p' || cmp_p == 'P') && + (cmp_o == 'o' || cmp_o == 'O')) { + dsk->image_type = DSK_TYPE_PRODOS; + } + + cmp_b = dsk->name_ptr[name_len-1]; + cmp_i = dsk->name_ptr[name_len-2]; + cmp_n = dsk->name_ptr[name_len-3]; + cmp_dot = dsk->name_ptr[name_len-4]; + if(cmp_dot == '.' && + (cmp_n == 'n' || cmp_n == 'N') && + (cmp_i == 'i' || cmp_i == 'I') && + (cmp_b == 'b' || cmp_b == 'B')) { + dsk->image_type = DSK_TYPE_NIB; + dsk->write_prot = 1; + dsk->write_through_to_unix = 0; + } + } + } + } + + dsk->disk_dirty = 0; + dsk->nib_pos = 0; + dsk->trks = 0; + + if(dsk->smartport) { + g_highest_smartport_unit = MAX(dsk->drive, + g_highest_smartport_unit); + + if(partition_name != 0 || part_num >= 0) { + ret = cfg_partition_find_by_name_or_num(dsk->file, + partition_name, part_num, dsk); + printf("partition %s (num %d) mounted, wr_prot: %d\n", + partition_name, part_num, dsk->write_prot); + + if(ret < 0) { + fclose(dsk->file); + dsk->file = 0; + return; + } + } + iwm_printf("adding smartport device[%d], size:%08x, " + "img_sz:%08x\n", dsk->drive, dsk->trks[0].unix_len, + dsk->image_size); + } else if(dsk->disk_525) { + unix_pos = dsk->image_start; + size = dsk->image_size; + disk_set_num_tracks(dsk, 4*35); + len = 0x1000; + nibs = NIB_LEN_525; + if(dsk->image_type == DSK_TYPE_NIB) { + len = dsk->image_size / 35;; + nibs = len; + } + if(size != 35*len) { + fatal_printf("Disk 5.25 error: size is %d, not 140K. " + "Will try to mount anyway\n", size, 35*len); + } + for(i = 0; i < 35; i++) { + iwm_move_to_track(dsk, 4*i); + disk_unix_to_nib(dsk, 4*i, unix_pos, len, nibs); + unix_pos += len; + } + } else { + /* disk_35 */ + unix_pos = dsk->image_start; + size = dsk->image_size; + if(size != 800*1024) { + fatal_printf("Disk 3.5 error: size is %d, not 800K. " + "Will try to mount anyway\n", size); + } + disk_set_num_tracks(dsk, 2*80); + for(i = 0; i < 2*80; i++) { + iwm_move_to_track(dsk, i); + len = g_track_bytes_35[i >> 5]; + nibs = g_track_nibs_35[i >> 5]; + iwm_printf("Trk: %d.%d = unix: %08x, %04x, %04x\n", + i>>1, i & 1, unix_pos, len, nibs); + disk_unix_to_nib(dsk, i, unix_pos, len, nibs); + unix_pos += len; + + iwm_printf(" trk_len:%05x\n", dsk->trks[i].track_len); + } + } + + iwm_move_to_track(dsk, save_track); + +} + +void +eject_named_disk(Disk *dsk, const char *name, const char *partition_name) +{ + + if(!dsk->file) { + return; + } + + /* If name matches, eject the disk! */ + if(!strcmp(dsk->name_ptr, name)) { + /* It matches, eject it */ + if((partition_name != 0) && (dsk->partition_name != 0)) { + /* If both have partitions, and they differ, then */ + /* don't eject. Otherwise, eject */ + if(strcmp(dsk->partition_name, partition_name) != 0) { + /* Don't eject */ + return; + } + } + eject_disk(dsk); + } +} + +void +eject_disk_by_num(int slot, int drive) +{ + Disk *dsk; + + dsk = cfg_get_dsk_from_slot_drive(slot, drive); + + eject_disk(dsk); +} + +void +eject_disk(Disk *dsk) +{ + int motor_on; + int i; + + if(!dsk->file) { + return; + } + + g_config_gsport_update_needed = 1; + + motor_on = iwm.motor_on; + if(g_c031_disk35 & 0x40) { + motor_on = iwm.motor_on35; + } + if(motor_on) { + halt_printf("Try eject dsk:%s, but motor_on!\n", dsk->name_ptr); + } + + iwm_flush_disk_to_unix(dsk); + + printf("Ejecting disk: %s\n", dsk->name_ptr); + + /* Free all memory, close file */ + + /* free the tracks first */ + if(dsk->trks != 0) { + for(i = 0; i < dsk->num_tracks; i++) { + if(dsk->trks[i].nib_area) { + free(dsk->trks[i].nib_area); + } + dsk->trks[i].nib_area = 0; + dsk->trks[i].track_len = 0; + } + free(dsk->trks); + } + dsk->num_tracks = 0; + dsk->trks = 0; + + /* close file, clean up dsk struct */ + fclose(dsk->file); + + dsk->image_start = 0; + dsk->image_size = 0; + dsk->nib_pos = 0; + dsk->disk_dirty = 0; + dsk->write_through_to_unix = 0; + dsk->write_prot = 1; + dsk->file = 0; + dsk->just_ejected = 1; + + /* Leave name_ptr valid */ +} + +int +cfg_get_fd_size(char *filename) +{ + struct stat stat_buf; + int ret; + + ret = stat(filename, &stat_buf); + if(ret != 0) { + fprintf(stderr,"stat %s returned errno: %d\n", + filename, errno); + stat_buf.st_size = 0; + } + + return stat_buf.st_size; +} + +int +cfg_partition_read_block(FILE *file, void *buf, int blk, int blk_size) +{ + int ret; + + ret = fseek(file, blk * blk_size, SEEK_SET); + if(ret != 0) { + printf("fseek: wanted: %08x, errno: %d\n", + blk * blk_size, errno); + return 0; + } + + ret = fread((char *)buf, 1, blk_size, file); + if(ret != blk_size) { + printf("ret: %08x, wanted %08x, errno: %d\n", ret, blk_size, + errno); + return 0; + } + return ret; +} + +int +cfg_partition_find_by_name_or_num(FILE *file, const char *partnamestr, int part_num, + Disk *dsk) +{ + Cfg_dirent *direntptr; + int match; + int num_parts; + int i; + + num_parts = cfg_partition_make_list(dsk->name_ptr, file); + + if(num_parts <= 0) { + return -1; + } + + for(i = 0; i < g_cfg_partitionlist.last; i++) { + direntptr = &(g_cfg_partitionlist.direntptr[i]); + match = 0; + if((strncmp(partnamestr, direntptr->name, 32) == 0) && + (part_num < 0)) { + //printf("partition, match1, name:%s %s, part_num:%d\n", + // partnamestr, direntptr->name, part_num); + + match = 1; + } + if((partnamestr == 0) && (direntptr->part_num == part_num)) { + //printf("partition, match2, n:%s, part_num:%d == %d\n", + // direntptr->name, direntptr->part_num, part_num); + match = 1; + } + if(match) { + dsk->image_start = direntptr->image_start; + dsk->image_size = direntptr->size; + //printf("match with image_start: %08x, image_size: " + // "%08x\n", dsk->image_start, dsk->image_size); + + return i; + } + } + + return -1; +} + +int +cfg_partition_make_list(char *filename, FILE *file) +{ + Driver_desc *driver_desc_ptr; + Part_map *part_map_ptr; + word32 *blk_bufptr; + word32 start; + word32 len; + word32 data_off; + word32 data_len; + word32 sig; + int size; + int image_start, image_size; + int is_dir; + int block_size; + int map_blks; + int cur_blk; + + block_size = 512; + + cfg_free_alldirents(&g_cfg_partitionlist); + + blk_bufptr = (word32 *)malloc(MAX_PARTITION_BLK_SIZE); + + cfg_partition_read_block(file, blk_bufptr, 0, block_size); + + driver_desc_ptr = (Driver_desc *)blk_bufptr; + sig = GET_BE_WORD16(driver_desc_ptr->sig); + block_size = GET_BE_WORD16(driver_desc_ptr->blk_size); + if(block_size == 0) { + block_size = 512; + } + if(sig != 0x4552 || block_size < 0x200 || + (block_size > MAX_PARTITION_BLK_SIZE)) { + cfg_printf("Partition error: No driver descriptor map found\n"); + free(blk_bufptr); + return 0; + } + + map_blks = 1; + cur_blk = 0; + size = cfg_get_fd_size(filename); + cfg_file_add_dirent(&g_cfg_partitionlist, "None - Whole image", + is_dir=0, size, 0, -1); + + while(cur_blk < map_blks) { + cur_blk++; + cfg_partition_read_block(file, blk_bufptr, cur_blk, block_size); + part_map_ptr = (Part_map *)blk_bufptr; + sig = GET_BE_WORD16(part_map_ptr->sig); + if(cur_blk <= 1) { + map_blks = MIN(20, + GET_BE_WORD32(part_map_ptr->map_blk_cnt)); + } + if(sig != 0x504d) { + printf("Partition entry %d bad signature:%04x\n", + cur_blk, sig); + free(blk_bufptr); + return g_cfg_partitionlist.last; + } + + /* found it, check for consistency */ + start = GET_BE_WORD32(part_map_ptr->phys_part_start); + len = GET_BE_WORD32(part_map_ptr->part_blk_cnt); + data_off = GET_BE_WORD32(part_map_ptr->data_start); + data_len = GET_BE_WORD32(part_map_ptr->data_cnt); + if(data_off + data_len > len) { + printf("Poorly formed entry\n"); + continue; + } + + if(data_len < 10 || start < 1) { + printf("Poorly formed entry %d, datalen:%d, " + "start:%08x\n", cur_blk, data_len, start); + continue; + } + + image_size = data_len * block_size; + image_start = (start + data_off) * block_size; + is_dir = 2*(image_size < 800*1024); +#if 0 + printf(" partition add entry %d = %s %d %08x %08x\n", + cur_blk, part_map_ptr->part_name, is_dir, + image_size, image_start); +#endif + + cfg_file_add_dirent(&g_cfg_partitionlist, + part_map_ptr->part_name, is_dir, image_size, + image_start, cur_blk); + } + + free(blk_bufptr); + return g_cfg_partitionlist.last; +} + +int +cfg_maybe_insert_disk(int slot, int drive, const char *namestr) +{ + int num_parts; + FILE *file; + + file = fopen(namestr, "rb"); + if(!file) { + fatal_printf("Cannot open disk image: %s\n", namestr); + return 0; + } + + num_parts = cfg_partition_make_list((char*)namestr, file); + fclose(file); + + if(num_parts > 0) { + printf("Choose a partition\n"); + g_cfg_select_partition = 1; + } else { + insert_disk(slot, drive, namestr, 0, 0, 0, -1); + return 1; + } + return 0; +} + +int +cfg_stat(char *path, struct stat *sb) +{ + int removed_slash; + int len; + int ret; + + removed_slash = 0; + len = 0; + +#ifdef _WIN32 + /* Windows doesn't like to stat paths ending in a /, so remove it */ + len = strlen(path); + if((len > 1) && (path[len - 1] == '/') ) { + path[len - 1] = 0; /* remove the slash */ + removed_slash = 1; + } +#endif + + ret = stat(path, sb); + +#ifdef _WIN32 + /* put the slash back */ + if(removed_slash) { + path[len - 1] = '/'; + } +#endif + + return ret; +} + +void +cfg_htab_vtab(int x, int y) +{ + if(x > 79) { + x = 0; + } + if(y > 23) { + y = 0; + } + g_cfg_curs_x = x; + g_cfg_curs_y = y; + g_cfg_curs_inv = 0; + g_cfg_curs_mousetext = 0; +} + +void +cfg_home() +{ + int i; + + cfg_htab_vtab(0, 0); + for(i = 0; i < 24; i++) { + cfg_cleol(); + } +} + +void +cfg_cleol() +{ + g_cfg_curs_inv = 0; + g_cfg_curs_mousetext = 0; + cfg_putchar(' '); + while(g_cfg_curs_x != 0) { + cfg_putchar(' '); + } +} + +void +cfg_putchar(int c) +{ + int offset; + int x, y; + + if(c == '\n') { + cfg_cleol(); + return; + } + if(c == '\b') { + g_cfg_curs_inv = !g_cfg_curs_inv; + return; + } + if(c == '\t') { + g_cfg_curs_mousetext = !g_cfg_curs_mousetext; + return; + } + y = g_cfg_curs_y; + x = g_cfg_curs_x; + + offset = g_screen_index[g_cfg_curs_y]; + if((x & 1) == 0) { + offset += 0x10000; + } + if(g_cfg_curs_inv) { + if(c >= 0x40 && c < 0x60) { + c = c & 0x1f; + } + } else { + c = c | 0x80; + } + if(g_cfg_curs_mousetext) { + c = (c & 0x1f) | 0x40; + } + set_memory_c(0xe00400 + offset + (x >> 1), c, 0); + x++; + if(x >= 80) { + x = 0; + y++; + if(y >= 24) { + y = 0; + } + } + g_cfg_curs_y = y; + g_cfg_curs_x = x; +} + +void +cfg_printf(const char *fmt, ...) +{ + va_list ap; + int c; + int i; + + va_start(ap, fmt); + (void)vsnprintf(g_cfg_printf_buf, CFG_PRINTF_BUFSIZE, fmt, ap); + va_end(ap); + + for(i = 0; i < CFG_PRINTF_BUFSIZE; i++) { + c = g_cfg_printf_buf[i]; + if(c == 0) { + return; + } + cfg_putchar(c); + } +} + +void +cfg_print_num(int num, int max_len) +{ + char buf[64]; + char buf2[64]; + int len; + int cnt; + int c; + int i, j; + + /* Prints right-adjusted "num" in field "max_len" wide */ + snprintf(&buf[0], 64, "%d", num); + len = strlen(buf); + for(i = 0; i < 64; i++) { + buf2[i] = ' '; + } + j = max_len + 1; + buf2[j] = 0; + j--; + cnt = 0; + for(i = len - 1; (i >= 0) && (j >= 1); i--) { + c = buf[i]; + if(c >= '0' && c <= '9') { + if(cnt >= 3) { + buf2[j--] = ','; + cnt = 0; + } + cnt++; + } + buf2[j--] = c; + } + cfg_printf(&buf2[1]); +} + +void +cfg_get_disk_name(char *outstr, int maxlen, int type_ext, int with_extras) +{ + Disk *dsk; + int slot, drive; + + slot = type_ext >> 8; + drive = type_ext & 0xff; + dsk = cfg_get_dsk_from_slot_drive(slot, drive); + + outstr[0] = 0; + if(dsk->name_ptr == 0) { + return; + } + + config_generate_config_gsport_name(outstr, maxlen, dsk, with_extras); +} + +void +cfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int change) +{ + char valbuf[CFG_OPT_MAXSTR]; + char **str_ptr; + const char *menustr; + char *curstr, *defstr; + char *str; + char *outstr; + int *iptr; + int val; + int num_opts; + int opt_num; + int bufpos, outpos; + int curval, defval; + int type; + int type_ext; + int opt_get_str; + int separator; + int len; + int c; + int i; + + g_cfg_opt_buf[0] = 0; + + num_opts = 0; + opt_get_str = 0; + separator = ','; + + menuptr += menu_pos; /* move forward to entry menu_pos */ + + menustr = menuptr->str; + type = menuptr->cfgtype; + type_ext = (type >> 4); + type = type & 0xf; + len = strlen(menustr) + 1; + + bufpos = 0; + outstr = &(g_cfg_opt_buf[0]); + + outstr[bufpos++] = ' '; + outstr[bufpos++] = ' '; + outstr[bufpos++] = '\t'; + outstr[bufpos++] = '\t'; + outstr[bufpos++] = ' '; + outstr[bufpos++] = ' '; + + if(menu_pos == highlight_pos) { + outstr[bufpos++] = '\b'; + } + + opt_get_str = 2; + i = -1; + outpos = bufpos; +#if 0 + printf("cfg menu_pos: %d str len: %d\n", menu_pos, len); +#endif + while(++i < len) { + c = menustr[i]; + if(c == separator) { + if(i == 0) { + continue; + } + c = 0; + } + outstr[outpos++] = c; + outstr[outpos] = 0; + if(outpos >= CFG_OPT_MAXSTR) { + fprintf(stderr, "CFG_OPT_MAXSTR exceeded\n"); + my_exit(1); + } + if(c == 0) { + if(opt_get_str == 2) { + outstr = &(valbuf[0]); + bufpos = outpos - 1; + opt_get_str = 0; + } else if(opt_get_str) { +#if 0 + if(menu_pos == highlight_pos) { + printf("menu_pos %d opt %d = %s=%d\n", + menu_pos, num_opts, + g_cfg_opts_strs[num_opts], + g_cfg_opts_vals[num_opts]); + } +#endif + num_opts++; + outstr = &(valbuf[0]); + opt_get_str = 0; + if(num_opts >= CFG_MAX_OPTS) { + fprintf(stderr, "CFG_MAX_OPTS oflow\n"); + my_exit(1); + } + } else { + if (type == CFGTYPE_INT) + { + val = strtoul(valbuf, 0, 0); + g_cfg_opts_vals[num_opts] = val; + } + + if (type == CFGTYPE_STR) + { + strncpy(&(g_cfg_opts_strvals[num_opts][0]),&(valbuf[0]),CFG_OPT_MAXSTR); + } + outstr = &(g_cfg_opts_strs[num_opts][0]); + opt_get_str = 1; + } + outpos = 0; + outstr[0] = 0; + } + } + + if(menu_pos == highlight_pos) { + g_cfg_opt_buf[bufpos++] = '\b'; + } + + g_cfg_opt_buf[bufpos] = 0; + + // Figure out if we should get a checkmark + curval = -1; + defval = -1; + curstr = 0; + if(type == CFGTYPE_INT) { iptr = (int*)menuptr->ptr; // OG Added cast - curval = *iptr; + curval = *iptr; iptr = (int*)menuptr->defptr; // OG Added cast - defval = *iptr; - if(curval == defval) { - g_cfg_opt_buf[3] = 'D'; /* checkmark */ - g_cfg_opt_buf[4] = '\t'; - } - } - if(type == CFGTYPE_STR) { - str_ptr = (char **)menuptr->ptr; - curstr = *str_ptr; - str_ptr = (char **)menuptr->defptr; - defstr = *str_ptr; - if(strcmp(curstr,defstr) == 0) { - g_cfg_opt_buf[3] = 'D'; /* checkmark */ - g_cfg_opt_buf[4] = '\t'; - } - } - if(type == CFGTYPE_FILE) { - str_ptr = (char **)menuptr->ptr; - curstr = *str_ptr; - str_ptr = (char **)menuptr->defptr; - defstr = *str_ptr; - if(strcmp(curstr,defstr) == 0) { - g_cfg_opt_buf[3] = 'D'; /* checkmark */ - g_cfg_opt_buf[4] = '\t'; - } - } - - // If it's a menu, give it a special menu indicator - if(type == CFGTYPE_MENU) { - g_cfg_opt_buf[1] = '\t'; - g_cfg_opt_buf[2] = 'M'; /* return-like symbol */ - g_cfg_opt_buf[3] = '\t'; - g_cfg_opt_buf[4] = ' '; - } - - // Decide what to display on the "right" side - str = 0; - opt_num = -1; - if(type == CFGTYPE_INT || type == CFGTYPE_FILE) { - g_cfg_opt_buf[bufpos++] = ' '; - g_cfg_opt_buf[bufpos++] = '='; - g_cfg_opt_buf[bufpos++] = ' '; - g_cfg_opt_buf[bufpos] = 0; - for(i = 0; i < num_opts; i++) { - if(curval == g_cfg_opts_vals[i]) { - opt_num = i; - break; - } - } - } - if(type == CFGTYPE_STR) { - g_cfg_opt_buf[bufpos++] = ' '; - g_cfg_opt_buf[bufpos++] = '='; - g_cfg_opt_buf[bufpos++] = ' '; - g_cfg_opt_buf[bufpos] = 0; - for(i = 0; i < num_opts; i++) { - if(!strcmp(curstr,g_cfg_opts_strvals[i])) { - opt_num = i; - break; - } - } - } - - if(change != 0) { - if(type == CFGTYPE_INT) { - if(num_opts > 0) { - opt_num += change; - if(opt_num >= num_opts) { - opt_num = 0; - } - if(opt_num < 0) { - opt_num = num_opts - 1; - } - curval = g_cfg_opts_vals[opt_num]; - } else { - curval += change; - /* HACK: min_val, max_val testing here */ - } - iptr = (int *)menuptr->ptr; - *iptr = curval; - } - if(type == CFGTYPE_STR) { - if(num_opts > 0) { - opt_num += change; - if(opt_num >= num_opts) { - opt_num = 0; - } - if(opt_num < 0) { - opt_num = num_opts - 1; - } - curstr = g_cfg_opts_strvals[opt_num]; - } else { - //curstr += change; - /* HACK: min_val, max_val testing here */ - } - str_ptr = (char **)menuptr->ptr; - *str_ptr = curstr; - } - g_config_gsport_update_needed = 1; - } - -#if 0 - if(menu_pos == highlight_pos) { - printf("menu_pos %d opt_num %d\n", menu_pos, opt_num); - } -#endif - - if(opt_num >= 0) { - str = &(g_cfg_opts_strs[opt_num][0]); - } else { - if(type == CFGTYPE_INT) { - str = &(g_cfg_opts_strs[0][0]); - snprintf(str, CFG_OPT_MAXSTR, "%d", curval); - } else if (type == CFGTYPE_STR) { - str = &(g_cfg_opts_strs[0][0]); - printf("curstr is: %s str is: %s\n", curstr,str); - snprintf(str, CFG_OPT_MAXSTR, "%s", curstr); - } else if (type == CFGTYPE_DISK) { - str = &(g_cfg_opts_strs[0][0]), - cfg_get_disk_name(str, CFG_OPT_MAXSTR, type_ext, 1); - str = cfg_shorten_filename(str, 68); - } else if (type == CFGTYPE_FILE) { - str = &(g_cfg_opts_strs[0][0]); - snprintf(str, CFG_OPT_MAXSTR, "%s", curstr); - str = cfg_shorten_filename(str, 68); - } else { - str = ""; - } - } - -#if 0 - if(menu_pos == highlight_pos) { - printf("menu_pos %d buf_pos %d, str is %s, %02x, %02x, " - "%02x %02x\n", - menu_pos, bufpos, str, g_cfg_opt_buf[bufpos-1], - g_cfg_opt_buf[bufpos-2], - g_cfg_opt_buf[bufpos-3], - g_cfg_opt_buf[bufpos-4]); - } -#endif - - g_cfg_opt_buf[bufpos] = 0; - strncpy(&(g_cfg_opt_buf[bufpos]), str, CFG_OPT_MAXSTR - bufpos - 1); - g_cfg_opt_buf[CFG_OPT_MAXSTR-1] = 0; -} - -void -cfg_get_base_path(char *pathptr, const char *inptr, int go_up) -{ - const char *tmpptr; - char *slashptr; - char *outptr; - int add_dotdot, is_dotdot; - int len; - int c; - - /* Take full filename, copy it to pathptr, and truncate at last slash */ - /* inptr and pathptr can be the same */ - /* if go_up is set, then replace a blank dir with ".." */ - /* but first, see if path is currently just ../ over and over */ - /* if so, just tack .. onto the end and return */ - //printf("cfg_get_base start with %s\n", inptr); - - g_cfg_file_match[0] = 0; - tmpptr = inptr; - is_dotdot = 1; - while(1) { - if(tmpptr[0] == 0) { - break; - } - if(tmpptr[0] == '.' && tmpptr[1] == '.' && tmpptr[2] == '/') { - tmpptr += 3; - } else { - is_dotdot = 0; - break; - } - } - slashptr = 0; - outptr = pathptr; - c = -1; - while(c != 0) { - c = *inptr++; - if(c == '/') { - if(*inptr != 0) { /* if not a trailing slash... */ - slashptr = outptr; - } - } - *outptr++ = c; - } - if(!go_up) { - /* if not go_up, copy chopped part to g_cfg_file_match*/ - /* copy from slashptr+1 to end */ - tmpptr = slashptr+1; - if(slashptr == 0) { - tmpptr = pathptr; - } - strncpy(&g_cfg_file_match[0], tmpptr, CFG_PATH_MAX); - /* remove trailing / from g_cfg_file_match */ - len = strlen(&g_cfg_file_match[0]); - if((len > 1) && (len < (CFG_PATH_MAX - 1)) && - g_cfg_file_match[len - 1] == '/') { - g_cfg_file_match[len - 1] = 0; - } - //printf("set g_cfg_file_match to %s\n", &g_cfg_file_match[0]); - } - if(!is_dotdot && (slashptr != 0)) { - slashptr[0] = '/'; - slashptr[1] = 0; - outptr = slashptr + 2; - } - add_dotdot = 0; - if(pathptr[0] == 0 || is_dotdot) { - /* path was blank, or consisted of just ../ pattern */ - if(go_up) { - add_dotdot = 1; - } - } else if(slashptr == 0) { - /* no slashes found, but path was not blank--make it blank */ - if(pathptr[0] == '/') { - pathptr[1] = 0; - } else { - pathptr[0] = 0; - } - } - - if(add_dotdot) { - --outptr; - outptr[0] = '.'; - outptr[1] = '.'; - outptr[2] = '/'; - outptr[3] = 0; - } - - //printf("cfg_get_base end with %s, is_dotdot:%d, add_dotdot:%d\n", - // pathptr, is_dotdot, add_dotdot); -} - -void -cfg_file_init() -{ - int slot, drive; - int i; - - if(g_cfg_slotdrive < 0xfff) { - cfg_get_disk_name(&g_cfg_tmp_path[0], CFG_PATH_MAX, - g_cfg_slotdrive, 0); - - slot = g_cfg_slotdrive >> 8; - drive = g_cfg_slotdrive & 1; - for(i = 0; i < 6; i++) { - if(g_cfg_tmp_path[0] != 0) { - break; - } - /* try to get a starting path from some mounted drive */ - drive = !drive; - if(i & 1) { - slot++; - if(slot >= 8) { - slot = 5; - } - } - cfg_get_disk_name(&g_cfg_tmp_path[0], CFG_PATH_MAX, - (slot << 8) + drive, 0); - } - } else { - // Just use g_cfg_file_def_name - strncpy(&g_cfg_tmp_path[0], g_cfg_file_def_name, CFG_PATH_MAX); - } - - cfg_get_base_path(&g_cfg_file_curpath[0], &g_cfg_tmp_path[0], 0); - g_cfg_dirlist.invalid = 1; -} - -void -cfg_free_alldirents(Cfg_listhdr *listhdrptr) -{ - int i; - - if(listhdrptr->max > 0) { - // Free the old directory listing - for(i = 0; i < listhdrptr->last; i++) { - free(listhdrptr->direntptr[i].name); - } - free(listhdrptr->direntptr); - } - - listhdrptr->direntptr = 0; - listhdrptr->last = 0; - listhdrptr->max = 0; - listhdrptr->invalid = 0; - - listhdrptr->topent = 0; - listhdrptr->curent = 0; -} - - -void -cfg_file_add_dirent(Cfg_listhdr *listhdrptr, const char *nameptr, int is_dir, - int size, int image_start, int part_num) -{ - Cfg_dirent *direntptr; - char *ptr; - int inc_amt; - int namelen; - - namelen = strlen(nameptr); - if(listhdrptr->last >= listhdrptr->max) { - // realloc - inc_amt = MAX(64, listhdrptr->max); - inc_amt = MIN(inc_amt, 1024); - listhdrptr->max += inc_amt; + defval = *iptr; + if(curval == defval) { + g_cfg_opt_buf[3] = 'D'; /* checkmark */ + g_cfg_opt_buf[4] = '\t'; + } + } + if(type == CFGTYPE_STR) { + str_ptr = (char **)menuptr->ptr; + curstr = *str_ptr; + str_ptr = (char **)menuptr->defptr; + defstr = *str_ptr; + if(strcmp(curstr,defstr) == 0) { + g_cfg_opt_buf[3] = 'D'; /* checkmark */ + g_cfg_opt_buf[4] = '\t'; + } + } + if(type == CFGTYPE_FILE) { + str_ptr = (char **)menuptr->ptr; + curstr = *str_ptr; + str_ptr = (char **)menuptr->defptr; + defstr = *str_ptr; + if(strcmp(curstr,defstr) == 0) { + g_cfg_opt_buf[3] = 'D'; /* checkmark */ + g_cfg_opt_buf[4] = '\t'; + } + } + + // If it's a menu, give it a special menu indicator + if(type == CFGTYPE_MENU) { + g_cfg_opt_buf[1] = '\t'; + g_cfg_opt_buf[2] = 'M'; /* return-like symbol */ + g_cfg_opt_buf[3] = '\t'; + g_cfg_opt_buf[4] = ' '; + } + + // Decide what to display on the "right" side + str = 0; + opt_num = -1; + if(type == CFGTYPE_INT || type == CFGTYPE_FILE) { + g_cfg_opt_buf[bufpos++] = ' '; + g_cfg_opt_buf[bufpos++] = '='; + g_cfg_opt_buf[bufpos++] = ' '; + g_cfg_opt_buf[bufpos] = 0; + for(i = 0; i < num_opts; i++) { + if(curval == g_cfg_opts_vals[i]) { + opt_num = i; + break; + } + } + } + if(type == CFGTYPE_STR) { + g_cfg_opt_buf[bufpos++] = ' '; + g_cfg_opt_buf[bufpos++] = '='; + g_cfg_opt_buf[bufpos++] = ' '; + g_cfg_opt_buf[bufpos] = 0; + for(i = 0; i < num_opts; i++) { + if(!strcmp(curstr,g_cfg_opts_strvals[i])) { + opt_num = i; + break; + } + } + } + + if(change != 0) { + if(type == CFGTYPE_INT) { + if(num_opts > 0) { + opt_num += change; + if(opt_num >= num_opts) { + opt_num = 0; + } + if(opt_num < 0) { + opt_num = num_opts - 1; + } + curval = g_cfg_opts_vals[opt_num]; + } else { + curval += change; + /* HACK: min_val, max_val testing here */ + } + iptr = (int *)menuptr->ptr; + *iptr = curval; + } + if(type == CFGTYPE_STR) { + if(num_opts > 0) { + opt_num += change; + if(opt_num >= num_opts) { + opt_num = 0; + } + if(opt_num < 0) { + opt_num = num_opts - 1; + } + curstr = g_cfg_opts_strvals[opt_num]; + } else { + //curstr += change; + /* HACK: min_val, max_val testing here */ + } + str_ptr = (char **)menuptr->ptr; + *str_ptr = curstr; + } + g_config_gsport_update_needed = 1; + } + +#if 0 + if(menu_pos == highlight_pos) { + printf("menu_pos %d opt_num %d\n", menu_pos, opt_num); + } +#endif + + if(opt_num >= 0) { + str = &(g_cfg_opts_strs[opt_num][0]); + } else { + if(type == CFGTYPE_INT) { + str = &(g_cfg_opts_strs[0][0]); + snprintf(str, CFG_OPT_MAXSTR, "%d", curval); + } else if (type == CFGTYPE_STR) { + str = &(g_cfg_opts_strs[0][0]); + printf("curstr is: %s str is: %s\n", curstr,str); + snprintf(str, CFG_OPT_MAXSTR, "%s", curstr); + } else if (type == CFGTYPE_DISK) { + str = &(g_cfg_opts_strs[0][0]), + cfg_get_disk_name(str, CFG_OPT_MAXSTR, type_ext, 1); + str = cfg_shorten_filename(str, 68); + } else if (type == CFGTYPE_FILE) { + str = &(g_cfg_opts_strs[0][0]); + snprintf(str, CFG_OPT_MAXSTR, "%s", curstr); + str = cfg_shorten_filename(str, 68); + } else { + str = ""; + } + } + +#if 0 + if(menu_pos == highlight_pos) { + printf("menu_pos %d buf_pos %d, str is %s, %02x, %02x, " + "%02x %02x\n", + menu_pos, bufpos, str, g_cfg_opt_buf[bufpos-1], + g_cfg_opt_buf[bufpos-2], + g_cfg_opt_buf[bufpos-3], + g_cfg_opt_buf[bufpos-4]); + } +#endif + + g_cfg_opt_buf[bufpos] = 0; + strncpy(&(g_cfg_opt_buf[bufpos]), str, CFG_OPT_MAXSTR - bufpos - 1); + g_cfg_opt_buf[CFG_OPT_MAXSTR-1] = 0; +} + +void +cfg_get_base_path(char *pathptr, const char *inptr, int go_up) +{ + const char *tmpptr; + char *slashptr; + char *outptr; + int add_dotdot, is_dotdot; + int len; + int c; + + /* Take full filename, copy it to pathptr, and truncate at last slash */ + /* inptr and pathptr can be the same */ + /* if go_up is set, then replace a blank dir with ".." */ + /* but first, see if path is currently just ../ over and over */ + /* if so, just tack .. onto the end and return */ + //printf("cfg_get_base start with %s\n", inptr); + + g_cfg_file_match[0] = 0; + tmpptr = inptr; + is_dotdot = 1; + while(1) { + if(tmpptr[0] == 0) { + break; + } + if(tmpptr[0] == '.' && tmpptr[1] == '.' && tmpptr[2] == '/') { + tmpptr += 3; + } else { + is_dotdot = 0; + break; + } + } + slashptr = 0; + outptr = pathptr; + c = -1; + while(c != 0) { + c = *inptr++; + if(c == '/') { + if(*inptr != 0) { /* if not a trailing slash... */ + slashptr = outptr; + } + } + *outptr++ = c; + } + if(!go_up) { + /* if not go_up, copy chopped part to g_cfg_file_match*/ + /* copy from slashptr+1 to end */ + tmpptr = slashptr+1; + if(slashptr == 0) { + tmpptr = pathptr; + } + strncpy(&g_cfg_file_match[0], tmpptr, CFG_PATH_MAX); + /* remove trailing / from g_cfg_file_match */ + len = strlen(&g_cfg_file_match[0]); + if((len > 1) && (len < (CFG_PATH_MAX - 1)) && + g_cfg_file_match[len - 1] == '/') { + g_cfg_file_match[len - 1] = 0; + } + //printf("set g_cfg_file_match to %s\n", &g_cfg_file_match[0]); + } + if(!is_dotdot && (slashptr != 0)) { + slashptr[0] = '/'; + slashptr[1] = 0; + outptr = slashptr + 2; + } + add_dotdot = 0; + if(pathptr[0] == 0 || is_dotdot) { + /* path was blank, or consisted of just ../ pattern */ + if(go_up) { + add_dotdot = 1; + } + } else if(slashptr == 0) { + /* no slashes found, but path was not blank--make it blank */ + if(pathptr[0] == '/') { + pathptr[1] = 0; + } else { + pathptr[0] = 0; + } + } + + if(add_dotdot) { + --outptr; + outptr[0] = '.'; + outptr[1] = '.'; + outptr[2] = '/'; + outptr[3] = 0; + } + + //printf("cfg_get_base end with %s, is_dotdot:%d, add_dotdot:%d\n", + // pathptr, is_dotdot, add_dotdot); +} + +void +cfg_file_init() +{ + int slot, drive; + int i; + + if(g_cfg_slotdrive < 0xfff) { + cfg_get_disk_name(&g_cfg_tmp_path[0], CFG_PATH_MAX, + g_cfg_slotdrive, 0); + + slot = g_cfg_slotdrive >> 8; + drive = g_cfg_slotdrive & 1; + for(i = 0; i < 6; i++) { + if(g_cfg_tmp_path[0] != 0) { + break; + } + /* try to get a starting path from some mounted drive */ + drive = !drive; + if(i & 1) { + slot++; + if(slot >= 8) { + slot = 5; + } + } + cfg_get_disk_name(&g_cfg_tmp_path[0], CFG_PATH_MAX, + (slot << 8) + drive, 0); + } + } else { + // Just use g_cfg_file_def_name + strncpy(&g_cfg_tmp_path[0], g_cfg_file_def_name, CFG_PATH_MAX); + } + + cfg_get_base_path(&g_cfg_file_curpath[0], &g_cfg_tmp_path[0], 0); + g_cfg_dirlist.invalid = 1; +} + +void +cfg_free_alldirents(Cfg_listhdr *listhdrptr) +{ + int i; + + if(listhdrptr->max > 0) { + // Free the old directory listing + for(i = 0; i < listhdrptr->last; i++) { + free(listhdrptr->direntptr[i].name); + } + free(listhdrptr->direntptr); + } + + listhdrptr->direntptr = 0; + listhdrptr->last = 0; + listhdrptr->max = 0; + listhdrptr->invalid = 0; + + listhdrptr->topent = 0; + listhdrptr->curent = 0; +} + + +void +cfg_file_add_dirent(Cfg_listhdr *listhdrptr, const char *nameptr, int is_dir, + int size, int image_start, int part_num) +{ + Cfg_dirent *direntptr; + char *ptr; + int inc_amt; + int namelen; + + namelen = strlen(nameptr); + if(listhdrptr->last >= listhdrptr->max) { + // realloc + inc_amt = MAX(64, listhdrptr->max); + inc_amt = MIN(inc_amt, 1024); + listhdrptr->max += inc_amt; listhdrptr->direntptr = (Cfg_dirent*)realloc(listhdrptr->direntptr, - listhdrptr->max * sizeof(Cfg_dirent)); - } + listhdrptr->max * sizeof(Cfg_dirent)); + } ptr = (char*)malloc(namelen+1+is_dir); // OG Added cast - strncpy(ptr, nameptr, namelen+1); - if(is_dir) { - strcat(ptr, "/"); - } -#if 0 - printf("...file entry %d is %s\n", g_cfg_dirlist.last, ptr); -#endif - direntptr = &(listhdrptr->direntptr[listhdrptr->last]); - direntptr->name = ptr; - direntptr->is_dir = is_dir; - direntptr->size = size; - direntptr->image_start = image_start; - direntptr->part_num = part_num; - listhdrptr->last++; -} - -int -cfg_dirent_sortfn(const void *obj1, const void *obj2) -{ - const Cfg_dirent *direntptr1, *direntptr2; - int ret; - - /* Called by qsort to sort directory listings */ - direntptr1 = (const Cfg_dirent *)obj1; - direntptr2 = (const Cfg_dirent *)obj2; + strncpy(ptr, nameptr, namelen+1); + if(is_dir) { + strcat(ptr, "/"); + } +#if 0 + printf("...file entry %d is %s\n", g_cfg_dirlist.last, ptr); +#endif + direntptr = &(listhdrptr->direntptr[listhdrptr->last]); + direntptr->name = ptr; + direntptr->is_dir = is_dir; + direntptr->size = size; + direntptr->image_start = image_start; + direntptr->part_num = part_num; + listhdrptr->last++; +} + +int +cfg_dirent_sortfn(const void *obj1, const void *obj2) +{ + const Cfg_dirent *direntptr1, *direntptr2; + int ret; + + /* Called by qsort to sort directory listings */ + direntptr1 = (const Cfg_dirent *)obj1; + direntptr2 = (const Cfg_dirent *)obj2; #if defined(MAC) || defined(_WIN32) // OG ret = 0; // ret = strcasecmp(direntptr1->name, direntptr2->name); -#else - ret = strcmp(direntptr1->name, direntptr2->name); -#endif - return ret; -} - -int -cfg_str_match(const char *str1, const char *str2, int len) -{ - const byte *bptr1, *bptr2; - int c, c2; - int i; - - /* basically, work like strcmp, except if str1 ends first, return 0 */ - - bptr1 = (const byte *)str1; - bptr2 = (const byte *)str2; - for(i = 0; i < len; i++) { - c = *bptr1++; - c2 = *bptr2++; - if(c == 0) { - if(i > 0) { - return 0; - } else { - return c - c2; - } - } - if(c != c2) { - return c - c2; - } - } - - return 0; -} - -void -cfg_file_readdir(const char *pathptr) -{ -#ifndef __OS2__ - struct dirent *direntptr; - struct stat stat_buf; - DIR *dirptr; - mode_t fmt; - char *str; - const char *tmppathptr; - int size; - int ret; - int is_dir, is_gz; - int len; - int i; - - if(!strncmp(pathptr, &g_cfg_file_cachedpath[0], CFG_PATH_MAX) && - (g_cfg_dirlist.last > 0) && (g_cfg_dirlist.invalid==0)){ - return; - } - // No match, must read new directory - - // Free all dirents that were cached previously - cfg_free_alldirents(&g_cfg_dirlist); - - strncpy(&g_cfg_file_cachedpath[0], pathptr, CFG_PATH_MAX); - strncpy(&g_cfg_file_cachedreal[0], pathptr, CFG_PATH_MAX); - - str = &g_cfg_file_cachedreal[0]; - - for(i = 0; i < 200; i++) { - len = strlen(str); - if(len <= 0) { - break; - } else if(len < CFG_PATH_MAX-2) { - if(str[len-1] != '/') { - // append / to make various routines work - str[len] = '/'; - str[len+1] = 0; - } - } - ret = cfg_stat(str, &stat_buf); - is_dir = 0; - if(ret == 0) { - fmt = stat_buf.st_mode & S_IFMT; - if(fmt == S_IFDIR) { - /* it's a directory */ - is_dir = 1; - } - } - if(is_dir) { - break; - } else { - // user is entering more path, use base for display - cfg_get_base_path(str, str, 0); - } - } - tmppathptr = str; - if(str[0] == 0) { - tmppathptr = "."; - } - cfg_file_add_dirent(&g_cfg_dirlist, "..", 1, 0, -1, -1); - dirptr = opendir(tmppathptr); - if(dirptr == 0) { - printf("Could not open %s as a directory\n", tmppathptr); - return; - } - while(1) { - direntptr = readdir(dirptr); - if(direntptr == 0) { - break; - } - if(!strcmp(".", direntptr->d_name)) { - continue; - } - if(!strcmp("..", direntptr->d_name)) { - continue; - } - /* Else, see if it is a directory or a file */ - snprintf(&g_cfg_tmp_path[0], CFG_PATH_MAX, "%s%s", - &g_cfg_file_cachedreal[0], direntptr->d_name); - ret = cfg_stat(&g_cfg_tmp_path[0], &stat_buf); - len = strlen(g_cfg_tmp_path); - is_dir = 0; - is_gz = 0; - if((len > 3) && (strcmp(&g_cfg_tmp_path[len - 3], ".gz") == 0)){ - is_gz = 1; - } - if(ret != 0) { - printf("stat %s ret %d, errno:%d\n", &g_cfg_tmp_path[0], - ret, errno); - stat_buf.st_size = 0; - continue; /* skip it */ - } else { - fmt = stat_buf.st_mode & S_IFMT; - size = stat_buf.st_size; - if(fmt == S_IFDIR) { - /* it's a directory */ - is_dir = 1; - } else if((fmt == S_IFREG) && (is_gz == 0)) { - if(g_cfg_slotdrive < 0xfff) { - if(size < 140*1024) { - continue; /* skip it */ - } - } else { - /* see if there are size limits */ - if((size < g_cfg_file_min_size) || - (size > g_cfg_file_max_size)) { - continue; /* skip it */ - } - } - } - } - cfg_file_add_dirent(&g_cfg_dirlist, direntptr->d_name, is_dir, - stat_buf.st_size, -1, -1); - } - /* then sort the results (Mac's HFS+ is sorted, but other FS won't be)*/ - qsort(&(g_cfg_dirlist.direntptr[0]), g_cfg_dirlist.last, - sizeof(Cfg_dirent), cfg_dirent_sortfn); - g_cfg_dirlist.curent = g_cfg_dirlist.last - 1; - for(i = g_cfg_dirlist.last - 1; i >= 0; i--) { - ret = cfg_str_match(&g_cfg_file_match[0], - g_cfg_dirlist.direntptr[i].name, CFG_PATH_MAX); - if(ret <= 0) { - /* set cur ent to closest filename to the match name */ - g_cfg_dirlist.curent = i; - } - } -#endif -} - -void -cfg_inspect_maybe_insert_file(char *filename, int should_boot) -{ -/* -Take a look at a file. Based on its size, guess a slot/drive to insert it into. -Used for blind operations like dragging/dropping files. -Optionally boot from that slot. -*/ - int rc = 0; - int slot = 0; - rc = cfg_guess_image_size(filename); - switch (rc) - { - case 0: slot = 7; break; - case 1: slot = 6; break; - case 2: slot = 5; break; - case 3: slot = 7; break; - default: break; - } - if (slot > 0) - { - insert_disk(slot,0,filename,0,0,0,-1); - printf("Inserted disk in slot %d, drive 1. Filename: %s\n", slot, filename); - if (should_boot) { - g_temp_boot_slot = slot; - printf("That slot has been set to boot.\n"); - } - } - else - printf("Unable to determine appropriate place to insert file %s.\n",filename); -} - -int -cfg_guess_image_size(char *filename) -{ -/* -Guess the image size. Return values: - -1 : invalid/unknown. Can't guess. - 0 : Less than 140k; might be ram disk image. - 1 : 140k, 5.25" image. - 2 : 800k, 3.5" image. - 3 : Something bigger. -*/ - struct stat stat_buf; - int rc = -1; - int len; - rc = stat(filename, &stat_buf); - if(rc < 0) - { - printf("Can't get statistics on file %s; errno: %d\n", - filename, errno); - rc = -1; - } else { - len = stat_buf.st_size; - printf("Found file %s, size %d; guessing ", - filename, len); - if (len < 140 * 1024) { - /* Not enough for a 140k image */ - printf("small ProDOS image.\n"); - rc = 0; - } else if (len < 140 * 1024 + 256 + 1) { - /* Reasonable size for 140k image, maybe in 2mg format */ - printf("a 5-1/4\" image.\n"); - rc = 1; - } else if (len < 800 * 1024 + 256 + 1) { - /* Reasonable size for 800k image, maybe in 2mg format */ - printf("a 3-1/2\" image.\n"); - rc = 2; - } else { - /* Let's pretend it's an HDV image */ - printf("a hard drive image.\n"); - rc = 3; - } - } - return rc; -} - -char * -cfg_shorten_filename(const char *in_ptr, int maxlen) -{ - char *out_ptr; - int len; - int c; - int i; - /* Warning: uses a static string, not reentrant! */ - out_ptr = &(g_cfg_file_shortened[0]); - len = strlen(in_ptr); - maxlen = MIN(len, maxlen); - for(i = 0; i < maxlen; i++) { - c = in_ptr[i] & 0x7f; - if(c < 0x20) { - c = '*'; - } - out_ptr[i] = c; - } - out_ptr[maxlen] = 0; - if(len > maxlen) { - for(i = 0; i < (maxlen/2); i++) { - c = in_ptr[len-i-1] & 0x7f; - if(c < 0x20) { - c = '*'; - } - out_ptr[maxlen-i-1] = c; - } - out_ptr[(maxlen/2) - 1] = '.'; - out_ptr[maxlen/2] = '.'; - out_ptr[(maxlen/2) + 1] = '.'; - } - return out_ptr; -} -void -cfg_fix_topent(Cfg_listhdr *listhdrptr) -{ - int num_to_show; - num_to_show = listhdrptr->num_to_show; - /* Force curent and topent to make sense */ - if(listhdrptr->curent >= listhdrptr->last) { - listhdrptr->curent = listhdrptr->last - 1; - } - if(listhdrptr->curent < 0) { - listhdrptr->curent = 0; - } - if(abs(listhdrptr->curent - listhdrptr->topent) >= num_to_show) { - listhdrptr->topent = listhdrptr->curent - (num_to_show/2); - } - if(listhdrptr->topent > listhdrptr->curent) { - listhdrptr->topent = listhdrptr->curent - (num_to_show/2); - } - if(listhdrptr->topent < 0) { - listhdrptr->topent = 0; - } -} -void -cfg_file_draw() -{ - Cfg_listhdr *listhdrptr; - Cfg_dirent *direntptr; - char *str, *fmt; - int num_to_show; - int yoffset; - int x, y; - int i; - cfg_file_readdir(&g_cfg_file_curpath[0]); - for(y = 0; y < 21; y++) { - cfg_htab_vtab(0, y); - cfg_printf("\tZ\t"); - for(x = 1; x < 79; x++) { - cfg_htab_vtab(x, y); - cfg_putchar(' '); - } - cfg_htab_vtab(79, y); - cfg_printf("\t_\t"); - } - cfg_htab_vtab(1, 0); - cfg_putchar('\b'); - for(x = 1; x < 79; x++) { - cfg_putchar(' '); - } - if(g_cfg_slotdrive < 0xfff) { - cfg_htab_vtab(30, 0); - cfg_printf("\bSelect image for s%dd%d\b", - (g_cfg_slotdrive >> 8), (g_cfg_slotdrive & 0xff) + 1); - } else { - cfg_htab_vtab(5, 0); - cfg_printf("\bSelect file to use as %-40s\b", - cfg_shorten_filename(g_cfg_file_def_name, 40)); - } - cfg_htab_vtab(2, 1); - cfg_printf("Configuration file path: %-56s", - cfg_shorten_filename(&g_config_gsport_name[0], 56)); - cfg_htab_vtab(2, 2); - cfg_printf("Current directory: %-50s", - cfg_shorten_filename(&g_cfg_cwd_str[0], 50)); - cfg_htab_vtab(2, 3); - str = ""; - if(g_cfg_file_pathfield) { - str = "\b \b"; - } - cfg_printf("Path: %s%s", - cfg_shorten_filename(&g_cfg_file_curpath[0], 68), str); - cfg_htab_vtab(0, 4); - cfg_printf(" \t"); - for(x = 1; x < 79; x++) { - cfg_putchar('\\'); - } - cfg_printf("\t "); - - /* Force curent and topent to make sense */ - listhdrptr = &g_cfg_dirlist; - num_to_show = CFG_NUM_SHOWENTS; - yoffset = 5; - if(g_cfg_select_partition > 0) { - listhdrptr = &g_cfg_partitionlist; - num_to_show -= 2; - cfg_htab_vtab(2, yoffset); - cfg_printf("Select partition of %-50s\n", - cfg_shorten_filename(&g_cfg_file_path[0], 50), str); - yoffset += 2; - } - listhdrptr->num_to_show = num_to_show; - cfg_fix_topent(listhdrptr); - for(i = 0; i < num_to_show; i++) { - y = i + yoffset; - if(listhdrptr->last > (i + listhdrptr->topent)) { - direntptr = &(listhdrptr-> - direntptr[i + listhdrptr->topent]); - cfg_htab_vtab(2, y); - if(direntptr->is_dir) { - cfg_printf("\tXY\t "); - } else { - cfg_printf(" "); - } - if(direntptr->part_num >= 0) { - cfg_printf("%3d: ", direntptr->part_num); - } - str = cfg_shorten_filename(direntptr->name, 45); - fmt = "%-45s"; - if(i + listhdrptr->topent == listhdrptr->curent) { - if(g_cfg_file_pathfield == 0) { - fmt = "\b%-45s\b"; - } else { - fmt = "%-44s\b \b"; - } - } - cfg_printf(fmt, str); - if(!direntptr->is_dir) { - cfg_print_num(direntptr->size, 13); - } - } - } - cfg_htab_vtab(1, 5 + CFG_NUM_SHOWENTS); - cfg_putchar('\t'); - for(x = 1; x < 79; x++) { - cfg_putchar('L'); - } - cfg_putchar('\t'); -} -void -cfg_partition_selected() -{ - char *str; - const char *part_str; - char *part_str2; - int pos; - int part_num; - pos = g_cfg_partitionlist.curent; - str = g_cfg_partitionlist.direntptr[pos].name; - part_num = -2; - part_str = 0; - if(str[0] == 0 || (str[0] >= '0' && str[0] <= '9')) { - part_num = g_cfg_partitionlist.direntptr[pos].part_num; - } else { - part_str = str; - } - part_str2 = 0; - if(part_str != 0) { - part_str2 = (char *)malloc(strlen(part_str)+1); - strcpy(part_str2, part_str); - } - insert_disk(g_cfg_slotdrive >> 8, g_cfg_slotdrive & 0xff, - &(g_cfg_file_path[0]), 0, 0, part_str2, part_num); - if(part_str2 != 0) { - free(part_str2); - } - g_cfg_slotdrive = -1; - g_cfg_select_partition = -1; -} -void -cfg_file_update_ptr(char *str) -{ - char *newstr; - int len; - len = strlen(str) + 1; +#else + ret = strcmp(direntptr1->name, direntptr2->name); +#endif + return ret; +} + +int +cfg_str_match(const char *str1, const char *str2, int len) +{ + const byte *bptr1, *bptr2; + int c, c2; + int i; + + /* basically, work like strcmp, except if str1 ends first, return 0 */ + + bptr1 = (const byte *)str1; + bptr2 = (const byte *)str2; + for(i = 0; i < len; i++) { + c = *bptr1++; + c2 = *bptr2++; + if(c == 0) { + if(i > 0) { + return 0; + } else { + return c - c2; + } + } + if(c != c2) { + return c - c2; + } + } + + return 0; +} + +void +cfg_file_readdir(const char *pathptr) +{ +#ifndef __OS2__ + struct dirent *direntptr; + struct stat stat_buf; + DIR *dirptr; + mode_t fmt; + char *str; + const char *tmppathptr; + int size; + int ret; + int is_dir, is_gz; + int len; + int i; + + if(!strncmp(pathptr, &g_cfg_file_cachedpath[0], CFG_PATH_MAX) && + (g_cfg_dirlist.last > 0) && (g_cfg_dirlist.invalid==0)){ + return; + } + // No match, must read new directory + + // Free all dirents that were cached previously + cfg_free_alldirents(&g_cfg_dirlist); + + strncpy(&g_cfg_file_cachedpath[0], pathptr, CFG_PATH_MAX); + strncpy(&g_cfg_file_cachedreal[0], pathptr, CFG_PATH_MAX); + + str = &g_cfg_file_cachedreal[0]; + + for(i = 0; i < 200; i++) { + len = strlen(str); + if(len <= 0) { + break; + } else if(len < CFG_PATH_MAX-2) { + if(str[len-1] != '/') { + // append / to make various routines work + str[len] = '/'; + str[len+1] = 0; + } + } + ret = cfg_stat(str, &stat_buf); + is_dir = 0; + if(ret == 0) { + fmt = stat_buf.st_mode & S_IFMT; + if(fmt == S_IFDIR) { + /* it's a directory */ + is_dir = 1; + } + } + if(is_dir) { + break; + } else { + // user is entering more path, use base for display + cfg_get_base_path(str, str, 0); + } + } + tmppathptr = str; + if(str[0] == 0) { + tmppathptr = "."; + } + cfg_file_add_dirent(&g_cfg_dirlist, "..", 1, 0, -1, -1); + dirptr = opendir(tmppathptr); + if(dirptr == 0) { + printf("Could not open %s as a directory\n", tmppathptr); + return; + } + while(1) { + direntptr = readdir(dirptr); + if(direntptr == 0) { + break; + } + if(!strcmp(".", direntptr->d_name)) { + continue; + } + if(!strcmp("..", direntptr->d_name)) { + continue; + } + /* Else, see if it is a directory or a file */ + snprintf(&g_cfg_tmp_path[0], CFG_PATH_MAX, "%s%s", + &g_cfg_file_cachedreal[0], direntptr->d_name); + ret = cfg_stat(&g_cfg_tmp_path[0], &stat_buf); + len = strlen(g_cfg_tmp_path); + is_dir = 0; + is_gz = 0; + if((len > 3) && (strcmp(&g_cfg_tmp_path[len - 3], ".gz") == 0)){ + is_gz = 1; + } + if(ret != 0) { + printf("stat %s ret %d, errno:%d\n", &g_cfg_tmp_path[0], + ret, errno); + stat_buf.st_size = 0; + continue; /* skip it */ + } else { + fmt = stat_buf.st_mode & S_IFMT; + size = stat_buf.st_size; + if(fmt == S_IFDIR) { + /* it's a directory */ + is_dir = 1; + } else if((fmt == S_IFREG) && (is_gz == 0)) { + if(g_cfg_slotdrive < 0xfff) { + if(size < 140*1024) { + continue; /* skip it */ + } + } else { + /* see if there are size limits */ + if((size < g_cfg_file_min_size) || + (size > g_cfg_file_max_size)) { + continue; /* skip it */ + } + } + } + } + cfg_file_add_dirent(&g_cfg_dirlist, direntptr->d_name, is_dir, + stat_buf.st_size, -1, -1); + } + /* then sort the results (Mac's HFS+ is sorted, but other FS won't be)*/ + qsort(&(g_cfg_dirlist.direntptr[0]), g_cfg_dirlist.last, + sizeof(Cfg_dirent), cfg_dirent_sortfn); + g_cfg_dirlist.curent = g_cfg_dirlist.last - 1; + for(i = g_cfg_dirlist.last - 1; i >= 0; i--) { + ret = cfg_str_match(&g_cfg_file_match[0], + g_cfg_dirlist.direntptr[i].name, CFG_PATH_MAX); + if(ret <= 0) { + /* set cur ent to closest filename to the match name */ + g_cfg_dirlist.curent = i; + } + } +#endif +} + +void +cfg_inspect_maybe_insert_file(char *filename, int should_boot) +{ +/* +Take a look at a file. Based on its size, guess a slot/drive to insert it into. +Used for blind operations like dragging/dropping files. +Optionally boot from that slot. +*/ + int rc = 0; + int slot = 0; + rc = cfg_guess_image_size(filename); + switch (rc) + { + case 0: slot = 7; break; + case 1: slot = 6; break; + case 2: slot = 5; break; + case 3: slot = 7; break; + default: break; + } + if (slot > 0) + { + insert_disk(slot,0,filename,0,0,0,-1); + printf("Inserted disk in slot %d, drive 1. Filename: %s\n", slot, filename); + if (should_boot) { + g_temp_boot_slot = slot; + printf("That slot has been set to boot.\n"); + } + } + else + printf("Unable to determine appropriate place to insert file %s.\n",filename); +} + +int +cfg_guess_image_size(char *filename) +{ +/* +Guess the image size. Return values: + -1 : invalid/unknown. Can't guess. + 0 : Less than 140k; might be ram disk image. + 1 : 140k, 5.25" image. + 2 : 800k, 3.5" image. + 3 : Something bigger. +*/ + struct stat stat_buf; + int rc = -1; + int len; + rc = stat(filename, &stat_buf); + if(rc < 0) + { + printf("Can't get statistics on file %s; errno: %d\n", + filename, errno); + rc = -1; + } else { + len = stat_buf.st_size; + printf("Found file %s, size %d; guessing ", + filename, len); + if (len < 140 * 1024) { + /* Not enough for a 140k image */ + printf("small ProDOS image.\n"); + rc = 0; + } else if (len < 140 * 1024 + 256 + 1) { + /* Reasonable size for 140k image, maybe in 2mg format */ + printf("a 5-1/4\" image.\n"); + rc = 1; + } else if (len < 800 * 1024 + 256 + 1) { + /* Reasonable size for 800k image, maybe in 2mg format */ + printf("a 3-1/2\" image.\n"); + rc = 2; + } else { + /* Let's pretend it's an HDV image */ + printf("a hard drive image.\n"); + rc = 3; + } + } + return rc; +} + +char * +cfg_shorten_filename(const char *in_ptr, int maxlen) +{ + char *out_ptr; + int len; + int c; + int i; + /* Warning: uses a static string, not reentrant! */ + out_ptr = &(g_cfg_file_shortened[0]); + len = strlen(in_ptr); + maxlen = MIN(len, maxlen); + for(i = 0; i < maxlen; i++) { + c = in_ptr[i] & 0x7f; + if(c < 0x20) { + c = '*'; + } + out_ptr[i] = c; + } + out_ptr[maxlen] = 0; + if(len > maxlen) { + for(i = 0; i < (maxlen/2); i++) { + c = in_ptr[len-i-1] & 0x7f; + if(c < 0x20) { + c = '*'; + } + out_ptr[maxlen-i-1] = c; + } + out_ptr[(maxlen/2) - 1] = '.'; + out_ptr[maxlen/2] = '.'; + out_ptr[(maxlen/2) + 1] = '.'; + } + return out_ptr; +} +void +cfg_fix_topent(Cfg_listhdr *listhdrptr) +{ + int num_to_show; + num_to_show = listhdrptr->num_to_show; + /* Force curent and topent to make sense */ + if(listhdrptr->curent >= listhdrptr->last) { + listhdrptr->curent = listhdrptr->last - 1; + } + if(listhdrptr->curent < 0) { + listhdrptr->curent = 0; + } + if(abs(listhdrptr->curent - listhdrptr->topent) >= num_to_show) { + listhdrptr->topent = listhdrptr->curent - (num_to_show/2); + } + if(listhdrptr->topent > listhdrptr->curent) { + listhdrptr->topent = listhdrptr->curent - (num_to_show/2); + } + if(listhdrptr->topent < 0) { + listhdrptr->topent = 0; + } +} +void +cfg_file_draw() +{ + Cfg_listhdr *listhdrptr; + Cfg_dirent *direntptr; + char *str, *fmt; + int num_to_show; + int yoffset; + int x, y; + int i; + cfg_file_readdir(&g_cfg_file_curpath[0]); + for(y = 0; y < 21; y++) { + cfg_htab_vtab(0, y); + cfg_printf("\tZ\t"); + for(x = 1; x < 79; x++) { + cfg_htab_vtab(x, y); + cfg_putchar(' '); + } + cfg_htab_vtab(79, y); + cfg_printf("\t_\t"); + } + cfg_htab_vtab(1, 0); + cfg_putchar('\b'); + for(x = 1; x < 79; x++) { + cfg_putchar(' '); + } + if(g_cfg_slotdrive < 0xfff) { + cfg_htab_vtab(30, 0); + cfg_printf("\bSelect image for s%dd%d\b", + (g_cfg_slotdrive >> 8), (g_cfg_slotdrive & 0xff) + 1); + } else { + cfg_htab_vtab(5, 0); + cfg_printf("\bSelect file to use as %-40s\b", + cfg_shorten_filename(g_cfg_file_def_name, 40)); + } + cfg_htab_vtab(2, 1); + cfg_printf("Configuration file path: %-56s", + cfg_shorten_filename(&g_config_gsport_name[0], 56)); + cfg_htab_vtab(2, 2); + cfg_printf("Current directory: %-50s", + cfg_shorten_filename(&g_cfg_cwd_str[0], 50)); + cfg_htab_vtab(2, 3); + str = ""; + if(g_cfg_file_pathfield) { + str = "\b \b"; + } + cfg_printf("Path: %s%s", + cfg_shorten_filename(&g_cfg_file_curpath[0], 68), str); + cfg_htab_vtab(0, 4); + cfg_printf(" \t"); + for(x = 1; x < 79; x++) { + cfg_putchar('\\'); + } + cfg_printf("\t "); + + /* Force curent and topent to make sense */ + listhdrptr = &g_cfg_dirlist; + num_to_show = CFG_NUM_SHOWENTS; + yoffset = 5; + if(g_cfg_select_partition > 0) { + listhdrptr = &g_cfg_partitionlist; + num_to_show -= 2; + cfg_htab_vtab(2, yoffset); + cfg_printf("Select partition of %-50s\n", + cfg_shorten_filename(&g_cfg_file_path[0], 50), str); + yoffset += 2; + } + listhdrptr->num_to_show = num_to_show; + cfg_fix_topent(listhdrptr); + for(i = 0; i < num_to_show; i++) { + y = i + yoffset; + if(listhdrptr->last > (i + listhdrptr->topent)) { + direntptr = &(listhdrptr-> + direntptr[i + listhdrptr->topent]); + cfg_htab_vtab(2, y); + if(direntptr->is_dir) { + cfg_printf("\tXY\t "); + } else { + cfg_printf(" "); + } + if(direntptr->part_num >= 0) { + cfg_printf("%3d: ", direntptr->part_num); + } + str = cfg_shorten_filename(direntptr->name, 45); + fmt = "%-45s"; + if(i + listhdrptr->topent == listhdrptr->curent) { + if(g_cfg_file_pathfield == 0) { + fmt = "\b%-45s\b"; + } else { + fmt = "%-44s\b \b"; + } + } + cfg_printf(fmt, str); + if(!direntptr->is_dir) { + cfg_print_num(direntptr->size, 13); + } + } + } + cfg_htab_vtab(1, 5 + CFG_NUM_SHOWENTS); + cfg_putchar('\t'); + for(x = 1; x < 79; x++) { + cfg_putchar('L'); + } + cfg_putchar('\t'); +} +void +cfg_partition_selected() +{ + char *str; + const char *part_str; + char *part_str2; + int pos; + int part_num; + pos = g_cfg_partitionlist.curent; + str = g_cfg_partitionlist.direntptr[pos].name; + part_num = -2; + part_str = 0; + if(str[0] == 0 || (str[0] >= '0' && str[0] <= '9')) { + part_num = g_cfg_partitionlist.direntptr[pos].part_num; + } else { + part_str = str; + } + part_str2 = 0; + if(part_str != 0) { + part_str2 = (char *)malloc(strlen(part_str)+1); + strcpy(part_str2, part_str); + } + insert_disk(g_cfg_slotdrive >> 8, g_cfg_slotdrive & 0xff, + &(g_cfg_file_path[0]), 0, 0, part_str2, part_num); + if(part_str2 != 0) { + free(part_str2); + } + g_cfg_slotdrive = -1; + g_cfg_select_partition = -1; +} +void +cfg_file_update_ptr(char *str) +{ + char *newstr; + int len; + len = strlen(str) + 1; newstr = (char*)malloc(len); - memcpy(newstr, str, len); - if(g_cfg_file_strptr) { - if(*g_cfg_file_strptr) { - free(*g_cfg_file_strptr); - } - } - *g_cfg_file_strptr = newstr; - if(g_cfg_file_strptr == &(g_cfg_rom_path)) { - printf("Updated ROM file\n"); - load_roms_init_memory(); - } - g_config_gsport_update_needed = 1; -} -void -cfg_file_selected() -{ -#ifndef __OS2__ - struct stat stat_buf; - char *str; - int fmt; - int ret; - if(g_cfg_select_partition > 0) { - cfg_partition_selected(); - return; - } - if(g_cfg_file_pathfield == 0) { - // in file section area of window - str = g_cfg_dirlist.direntptr[g_cfg_dirlist.curent].name; - if(!strcmp(str, "../")) { - /* go up one directory */ - cfg_get_base_path(&g_cfg_file_curpath[0], - &g_cfg_file_curpath[0], 1); - return; - } - snprintf(&g_cfg_file_path[0], CFG_PATH_MAX, "%s%s", - &g_cfg_file_cachedreal[0], str); - } else { - // just use cfg_file_curpath directly - strncpy(&g_cfg_file_path[0], &g_cfg_file_curpath[0], - CFG_PATH_MAX); - } - ret = cfg_stat(&g_cfg_file_path[0], &stat_buf); - fmt = stat_buf.st_mode & S_IFMT; - cfg_printf("Stat'ing %s, st_mode is: %08x\n", &g_cfg_file_path[0], - (int)stat_buf.st_mode); - if(ret != 0) { - printf("stat %s returned %d, errno: %d\n", &g_cfg_file_path[0], - ret, errno); - } else { - if(fmt == S_IFDIR) { - /* it's a directory */ - strncpy(&g_cfg_file_curpath[0], &g_cfg_file_path[0], - CFG_PATH_MAX); - } else { - /* select it */ - if(g_cfg_slotdrive < 0xfff) { - ret = cfg_maybe_insert_disk(g_cfg_slotdrive>>8, - g_cfg_slotdrive & 0xff, - &g_cfg_file_path[0]); - if(ret > 0) { - g_cfg_slotdrive = -1; - } - } else { - cfg_file_update_ptr(&g_cfg_file_path[0]); - g_cfg_slotdrive = -1; - } - } - } -#endif -} - -void -cfg_file_handle_key(int key) -{ - Cfg_listhdr *listhdrptr; - int len; - if(g_cfg_file_pathfield) { - if(key >= 0x20 && key < 0x7f) { - len = strlen(&g_cfg_file_curpath[0]); - if(len < CFG_PATH_MAX-4) { - g_cfg_file_curpath[len] = key; - g_cfg_file_curpath[len+1] = 0; - } - return; - } - } - listhdrptr = &g_cfg_dirlist; - if(g_cfg_select_partition > 0) { - listhdrptr = &g_cfg_partitionlist; - } - if( (g_cfg_file_pathfield == 0) && - ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z')) ) { - /* jump to file starting with this letter */ - g_cfg_file_match[0] = key; - g_cfg_file_match[1] = 0; - g_cfg_dirlist.invalid = 1; /* re-read directory */ - } - switch(key) { - case 0x1b: - if(g_cfg_slotdrive < 0xfff) { - eject_disk_by_num(g_cfg_slotdrive >> 8, - g_cfg_slotdrive & 0xff); - } - g_cfg_slotdrive = -1; - g_cfg_select_partition = -1; - g_cfg_dirlist.invalid = 1; - break; - case 0x0a: /* down arrow */ - if(g_cfg_file_pathfield == 0) { - listhdrptr->curent++; - cfg_fix_topent(listhdrptr); - } - break; - case 0x0b: /* up arrow */ - if(g_cfg_file_pathfield == 0) { - listhdrptr->curent--; - cfg_fix_topent(listhdrptr); - } - break; - case 0x0d: /* return */ - printf("handling return press\n"); - cfg_file_selected(); - break; - case 0x09: /* tab */ - g_cfg_file_pathfield = !g_cfg_file_pathfield; - break; - case 0x08: /* left arrow */ - case 0x7f: /* delete key */ - if(g_cfg_file_pathfield) { - // printf("left arrow/delete\n"); - len = strlen(&g_cfg_file_curpath[0]) - 1; - if(len >= 0) { - g_cfg_file_curpath[len] = 0; - } - } - break; - default: - printf("key: %02x\n", key); - } -#if 0 - printf("curent: %d, topent: %d, last: %d\n", - g_cfg_dirlist.curent, g_cfg_dirlist.topent, g_cfg_dirlist.last); -#endif -} -void -config_control_panel() -{ - void (*fn_ptr)(); - const char *str; - Cfg_menu *menuptr; - void *ptr; - int print_eject_help; - int line; - int type; - int match_found; - int menu_line; - int menu_inc; - int max_line; - int key; - int i, j; - // First, save important text screen state - g_save_cur_a2_stat = g_cur_a2_stat; - for(i = 0; i < 0x400; i++) { - g_save_text_screen_bytes[i] = g_slow_memory_ptr[0x400+i]; - g_save_text_screen_bytes[0x400+i] =g_slow_memory_ptr[0x10400+i]; - } - g_cur_a2_stat = ALL_STAT_TEXT | ALL_STAT_VID80 | ALL_STAT_ANNUNC3 | - (0xf << BIT_ALL_STAT_TEXT_COLOR) | ALL_STAT_ALTCHARSET; - g_a2_new_all_stat[0] = g_cur_a2_stat; - g_new_a2_stat_cur_line = 0; - cfg_printf("In config_control_panel\n"); - for(i = 0; i < 20; i++) { - // Toss any queued-up keypresses - if(adb_read_c000() & 0x80) { - (void)adb_access_c010(); - } - } - g_adb_repeat_vbl = 0; - g_cfg_vbl_count = 0; - // HACK: Force adb keyboard (and probably mouse) to "normal"... - g_full_refresh_needed = -1; - g_a2_screen_buffer_changed = -1; - cfg_home(); - j = 0; - menuptr = g_cfg_main_menu; - if(g_rom_version < 0) { - /* Must select ROM file */ - menuptr = g_cfg_rom_menu; - } - menu_line = 1; - menu_inc = 1; - g_cfg_slotdrive = -1; - g_cfg_select_partition = -1; - while(g_config_control_panel & !(halt_sim&HALT_WANTTOQUIT)) { - if(g_fatal_log > 0) { - x_show_alert(0, 0); - } - cfg_home(); - line = 1; - max_line = 1; - match_found = 0; - print_eject_help = 0; - cfg_printf("%s\n\n", menuptr[0].str); - while(line < 24) { - str = menuptr[line].str; - type = menuptr[line].cfgtype; - ptr = menuptr[line].ptr; - if(str == 0) { - break; - } - if((type & 0xf) == CFGTYPE_DISK) { - print_eject_help = 1; - } - cfg_parse_menu(menuptr, line, menu_line, 0); - if(line == menu_line) { - if(type != 0) { - match_found = 1; - } else if(menu_inc) { - menu_line++; - } else { - menu_line--; - } - } - if(line > max_line) { - max_line = line; - } - cfg_printf("%s\n", g_cfg_opt_buf); - line++; - } - if((menu_line < 1) && !match_found) { - menu_line = 1; - } - if((menu_line >= max_line) && !match_found) { - menu_line = max_line; - } - if(g_rom_version < 0) { - cfg_htab_vtab(0, 21); - cfg_printf("\bYOU MUST SELECT A VALID ROM FILE\b\n"); - } - cfg_htab_vtab(0, 23); - cfg_printf("Move: \tJ\t \tK\t Change: \tH\t \tU\t \tM\t"); - if(print_eject_help) { - cfg_printf(" Eject: "); - if(g_cfg_slotdrive >= 0) { - cfg_printf("\bESC\b"); - } else { - cfg_printf("E"); - } - } -#if 0 - cfg_htab_vtab(0, 22); - cfg_printf("menu_line: %d line: %d, vbl:%d, adb:%d key_dn:%d\n", - menu_line, line, g_cfg_vbl_count, g_adb_repeat_vbl, - g_key_down); -#endif - if(g_cfg_slotdrive >= 0) { - cfg_file_draw(); - } -#ifdef HAVE_TFE - /*HACK eh, at least I think it is. Display the available ethernet interfaces - when in the ethernet control panel. This is the only way one can customize a menu pane. - Kent did it with the directory browser, so why not.*/ - if(menuptr == g_cfg_ethernet_menu) - { - cfg_get_tfe_name(); - } -#endif - key = -1; - while(g_config_control_panel & !(halt_sim&HALT_WANTTOQUIT)) { - video_update(); - key = adb_read_c000(); - if(key & 0x80) { - key = key & 0x7f; - (void)adb_access_c010(); - break; - } else { - key = -1; - } - micro_sleep(1.0/60.0); - g_cfg_vbl_count++; - if(!match_found) { - break; - } - } - if((key >= 0) && (g_cfg_slotdrive < 0)) { - // Normal menu system - switch(key) { - case 0x0a: /* down arrow */ - menu_line++; - menu_inc = 1; - break; - case 0x0b: /* up arrow */ - menu_line--; - menu_inc = 0; - if(menu_line < 1) { - menu_line = 1; - } - break; - case 0x15: /* right arrow */ - cfg_parse_menu(menuptr, menu_line,menu_line,1); - break; - case 0x08: /* left arrow */ - cfg_parse_menu(menuptr,menu_line,menu_line,-1); - break; - case 0x0d: - type = menuptr[menu_line].cfgtype; - ptr = menuptr[menu_line].ptr; - switch(type & 0xf) { - case CFGTYPE_MENU: - menuptr = (Cfg_menu *)ptr; - menu_line = 1; - break; - case CFGTYPE_DISK: - g_cfg_slotdrive = type >> 4; - cfg_file_init(); - break; - case CFGTYPE_FUNC: - fn_ptr = (void (*)())ptr; - (*fn_ptr)(); - break; - case CFGTYPE_FILE: - g_cfg_slotdrive = 0xfff; - g_cfg_file_def_name = *((char **)ptr); - g_cfg_file_strptr = (char **)ptr; - cfg_file_init(); - } - break; - case 0x1b: - // Jump to last menu entry - menu_line = max_line; - break; - case 'e': - case 'E': - type = menuptr[menu_line].cfgtype; - if((type & 0xf) == CFGTYPE_DISK) { - eject_disk_by_num(type >> 12, - (type >> 4) & 0xff); - } - break; - default: - printf("key: %02x\n", key); - } - } else if(key >= 0) { - cfg_file_handle_key(key); - } - } - for(i = 0; i < 0x400; i++) { - set_memory_c(0xe00400+i, g_save_text_screen_bytes[i], 0); - set_memory_c(0xe10400+i, g_save_text_screen_bytes[0x400+i], 0); - } - // And quit - g_config_control_panel = 0; - g_adb_repeat_vbl = g_vbl_count + 60; - g_cur_a2_stat = g_save_cur_a2_stat; - change_display_mode(g_cur_dcycs); - g_full_refresh_needed = -1; - g_a2_screen_buffer_changed = -1; -} + memcpy(newstr, str, len); + if(g_cfg_file_strptr) { + if(*g_cfg_file_strptr) { + free(*g_cfg_file_strptr); + } + } + *g_cfg_file_strptr = newstr; + if(g_cfg_file_strptr == &(g_cfg_rom_path)) { + printf("Updated ROM file\n"); + load_roms_init_memory(); + } + g_config_gsport_update_needed = 1; +} +void +cfg_file_selected() +{ +#ifndef __OS2__ + struct stat stat_buf; + char *str; + int fmt; + int ret; + if(g_cfg_select_partition > 0) { + cfg_partition_selected(); + return; + } + if(g_cfg_file_pathfield == 0) { + // in file section area of window + str = g_cfg_dirlist.direntptr[g_cfg_dirlist.curent].name; + if(!strcmp(str, "../")) { + /* go up one directory */ + cfg_get_base_path(&g_cfg_file_curpath[0], + &g_cfg_file_curpath[0], 1); + return; + } + snprintf(&g_cfg_file_path[0], CFG_PATH_MAX, "%s%s", + &g_cfg_file_cachedreal[0], str); + } else { + // just use cfg_file_curpath directly + strncpy(&g_cfg_file_path[0], &g_cfg_file_curpath[0], + CFG_PATH_MAX); + } + ret = cfg_stat(&g_cfg_file_path[0], &stat_buf); + fmt = stat_buf.st_mode & S_IFMT; + cfg_printf("Stat'ing %s, st_mode is: %08x\n", &g_cfg_file_path[0], + (int)stat_buf.st_mode); + if(ret != 0) { + printf("stat %s returned %d, errno: %d\n", &g_cfg_file_path[0], + ret, errno); + } else { + if(fmt == S_IFDIR) { + /* it's a directory */ + strncpy(&g_cfg_file_curpath[0], &g_cfg_file_path[0], + CFG_PATH_MAX); + } else { + /* select it */ + if(g_cfg_slotdrive < 0xfff) { + ret = cfg_maybe_insert_disk(g_cfg_slotdrive>>8, + g_cfg_slotdrive & 0xff, + &g_cfg_file_path[0]); + if(ret > 0) { + g_cfg_slotdrive = -1; + } + } else { + cfg_file_update_ptr(&g_cfg_file_path[0]); + g_cfg_slotdrive = -1; + } + } + } +#endif +} + +void +cfg_file_handle_key(int key) +{ + Cfg_listhdr *listhdrptr; + int len; + if(g_cfg_file_pathfield) { + if(key >= 0x20 && key < 0x7f) { + len = strlen(&g_cfg_file_curpath[0]); + if(len < CFG_PATH_MAX-4) { + g_cfg_file_curpath[len] = key; + g_cfg_file_curpath[len+1] = 0; + } + return; + } + } + listhdrptr = &g_cfg_dirlist; + if(g_cfg_select_partition > 0) { + listhdrptr = &g_cfg_partitionlist; + } + if( (g_cfg_file_pathfield == 0) && + ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z')) ) { + /* jump to file starting with this letter */ + g_cfg_file_match[0] = key; + g_cfg_file_match[1] = 0; + g_cfg_dirlist.invalid = 1; /* re-read directory */ + } + switch(key) { + case 0x1b: + if(g_cfg_slotdrive < 0xfff) { + eject_disk_by_num(g_cfg_slotdrive >> 8, + g_cfg_slotdrive & 0xff); + } + g_cfg_slotdrive = -1; + g_cfg_select_partition = -1; + g_cfg_dirlist.invalid = 1; + break; + case 0x0a: /* down arrow */ + if(g_cfg_file_pathfield == 0) { + listhdrptr->curent++; + cfg_fix_topent(listhdrptr); + } + break; + case 0x0b: /* up arrow */ + if(g_cfg_file_pathfield == 0) { + listhdrptr->curent--; + cfg_fix_topent(listhdrptr); + } + break; + case 0x0d: /* return */ + printf("handling return press\n"); + cfg_file_selected(); + break; + case 0x09: /* tab */ + g_cfg_file_pathfield = !g_cfg_file_pathfield; + break; + case 0x08: /* left arrow */ + case 0x7f: /* delete key */ + if(g_cfg_file_pathfield) { + // printf("left arrow/delete\n"); + len = strlen(&g_cfg_file_curpath[0]) - 1; + if(len >= 0) { + g_cfg_file_curpath[len] = 0; + } + } + break; + default: + printf("key: %02x\n", key); + } +#if 0 + printf("curent: %d, topent: %d, last: %d\n", + g_cfg_dirlist.curent, g_cfg_dirlist.topent, g_cfg_dirlist.last); +#endif +} +void +config_control_panel() +{ + void (*fn_ptr)(); + const char *str; + Cfg_menu *menuptr; + void *ptr; + int print_eject_help; + int line; + int type; + int match_found; + int menu_line; + int menu_inc; + int max_line; + int key; + int i, j; + // First, save important text screen state + g_save_cur_a2_stat = g_cur_a2_stat; + for(i = 0; i < 0x400; i++) { + g_save_text_screen_bytes[i] = g_slow_memory_ptr[0x400+i]; + g_save_text_screen_bytes[0x400+i] =g_slow_memory_ptr[0x10400+i]; + } + g_cur_a2_stat = ALL_STAT_TEXT | ALL_STAT_VID80 | ALL_STAT_ANNUNC3 | + (0xf << BIT_ALL_STAT_TEXT_COLOR) | ALL_STAT_ALTCHARSET; + g_a2_new_all_stat[0] = g_cur_a2_stat; + g_new_a2_stat_cur_line = 0; + cfg_printf("In config_control_panel\n"); + for(i = 0; i < 20; i++) { + // Toss any queued-up keypresses + if(adb_read_c000() & 0x80) { + (void)adb_access_c010(); + } + } + g_adb_repeat_vbl = 0; + g_cfg_vbl_count = 0; + // HACK: Force adb keyboard (and probably mouse) to "normal"... + g_full_refresh_needed = -1; + g_a2_screen_buffer_changed = -1; + cfg_home(); + j = 0; + menuptr = g_cfg_main_menu; + if(g_rom_version < 0) { + /* Must select ROM file */ + menuptr = g_cfg_rom_menu; + } + menu_line = 1; + menu_inc = 1; + g_cfg_slotdrive = -1; + g_cfg_select_partition = -1; + while(g_config_control_panel & !(halt_sim&HALT_WANTTOQUIT)) { + if(g_fatal_log > 0) { + x_show_alert(0, 0); + } + cfg_home(); + line = 1; + max_line = 1; + match_found = 0; + print_eject_help = 0; + cfg_printf("%s\n\n", menuptr[0].str); + while(line < 24) { + str = menuptr[line].str; + type = menuptr[line].cfgtype; + ptr = menuptr[line].ptr; + if(str == 0) { + break; + } + if((type & 0xf) == CFGTYPE_DISK) { + print_eject_help = 1; + } + cfg_parse_menu(menuptr, line, menu_line, 0); + if(line == menu_line) { + if(type != 0) { + match_found = 1; + } else if(menu_inc) { + menu_line++; + } else { + menu_line--; + } + } + if(line > max_line) { + max_line = line; + } + cfg_printf("%s\n", g_cfg_opt_buf); + line++; + } + if((menu_line < 1) && !match_found) { + menu_line = 1; + } + if((menu_line >= max_line) && !match_found) { + menu_line = max_line; + } + if(g_rom_version < 0) { + cfg_htab_vtab(0, 21); + cfg_printf("\bYOU MUST SELECT A VALID ROM FILE\b\n"); + } + cfg_htab_vtab(0, 23); + cfg_printf("Move: \tJ\t \tK\t Change: \tH\t \tU\t \tM\t"); + if(print_eject_help) { + cfg_printf(" Eject: "); + if(g_cfg_slotdrive >= 0) { + cfg_printf("\bESC\b"); + } else { + cfg_printf("E"); + } + } +#if 0 + cfg_htab_vtab(0, 22); + cfg_printf("menu_line: %d line: %d, vbl:%d, adb:%d key_dn:%d\n", + menu_line, line, g_cfg_vbl_count, g_adb_repeat_vbl, + g_key_down); +#endif + if(g_cfg_slotdrive >= 0) { + cfg_file_draw(); + } +#ifdef HAVE_TFE + /*HACK eh, at least I think it is. Display the available ethernet interfaces + when in the ethernet control panel. This is the only way one can customize a menu pane. + Kent did it with the directory browser, so why not.*/ + if(menuptr == g_cfg_ethernet_menu) + { + cfg_get_tfe_name(); + } +#endif + key = -1; + while(g_config_control_panel & !(halt_sim&HALT_WANTTOQUIT)) { + video_update(); + key = adb_read_c000(); + if(key & 0x80) { + key = key & 0x7f; + (void)adb_access_c010(); + break; + } else { + key = -1; + } + micro_sleep(1.0/60.0); + g_cfg_vbl_count++; + if(!match_found) { + break; + } + } + if((key >= 0) && (g_cfg_slotdrive < 0)) { + // Normal menu system + switch(key) { + case 0x0a: /* down arrow */ + menu_line++; + menu_inc = 1; + break; + case 0x0b: /* up arrow */ + menu_line--; + menu_inc = 0; + if(menu_line < 1) { + menu_line = 1; + } + break; + case 0x15: /* right arrow */ + cfg_parse_menu(menuptr, menu_line,menu_line,1); + break; + case 0x08: /* left arrow */ + cfg_parse_menu(menuptr,menu_line,menu_line,-1); + break; + case 0x0d: + type = menuptr[menu_line].cfgtype; + ptr = menuptr[menu_line].ptr; + switch(type & 0xf) { + case CFGTYPE_MENU: + menuptr = (Cfg_menu *)ptr; + menu_line = 1; + break; + case CFGTYPE_DISK: + g_cfg_slotdrive = type >> 4; + cfg_file_init(); + break; + case CFGTYPE_FUNC: + fn_ptr = (void (*)())ptr; + (*fn_ptr)(); + break; + case CFGTYPE_FILE: + g_cfg_slotdrive = 0xfff; + g_cfg_file_def_name = *((char **)ptr); + g_cfg_file_strptr = (char **)ptr; + cfg_file_init(); + } + break; + case 0x1b: + // Jump to last menu entry + menu_line = max_line; + break; + case 'e': + case 'E': + type = menuptr[menu_line].cfgtype; + if((type & 0xf) == CFGTYPE_DISK) { + eject_disk_by_num(type >> 12, + (type >> 4) & 0xff); + } + break; + default: + printf("key: %02x\n", key); + } + } else if(key >= 0) { + cfg_file_handle_key(key); + } + } + for(i = 0; i < 0x400; i++) { + set_memory_c(0xe00400+i, g_save_text_screen_bytes[i], 0); + set_memory_c(0xe10400+i, g_save_text_screen_bytes[0x400+i], 0); + } + // And quit + g_config_control_panel = 0; + g_adb_repeat_vbl = g_vbl_count + 60; + g_cur_a2_stat = g_save_cur_a2_stat; + change_display_mode(g_cur_dcycs); + g_full_refresh_needed = -1; + g_a2_screen_buffer_changed = -1; +} void x_clk_setup_bram_version() { diff --git a/src/engine_c.c b/src/engine_c.c index e8bc0dc..0abf736 100644 --- a/src/engine_c.c +++ b/src/engine_c.c @@ -784,7 +784,13 @@ void fixed_memory_ptrs_shut() word32 get_itimer() { -#if defined(__i386) && defined(__GNUC__) +#if defined(_WIN32) + LARGE_INTEGER count; + if (QueryPerformanceCounter(&count)) + return count.LowPart; + else + return 0; +#elif defined(__i386) && defined(__GNUC__) /* Here's my bad ia32 asm code to do rdtsc */ /* Linux source uses: */ /* asm volatile("rdtsc" : "=a"(ret) : : "edx"); */ @@ -805,12 +811,6 @@ get_itimer() asm volatile ("mftb %0" : "=r"(ret)); return ret; -#elif defined(_WIN32) - LARGE_INTEGER count; - if (QueryPerformanceCounter(&count)) - return count.LowPart; - else - return 0; #else return 0; #endif diff --git a/src/gsport.sln b/src/gsport.sln index 8569592..2b6c137 100644 --- a/src/gsport.sln +++ b/src/gsport.sln @@ -1,13 +1,16 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual C++ Express 2010 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Express 2012 for Windows Desktop Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gsport", "gsport.vcxproj", "{0B4E527A-DB50-4B5F-9B08-303ABAF7356A}" ProjectSection(ProjectDependencies) = postProject + {2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B} = {2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B} {E810477A-E004-4308-A58A-21393213EF89} = {E810477A-E004-4308-A58A-21393213EF89} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tfe", "tfe\tfe.vcxproj", "{E810477A-E004-4308-A58A-21393213EF89}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "atbridge", "atbridge\atbridge.vcxproj", "{2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -22,6 +25,10 @@ Global {E810477A-E004-4308-A58A-21393213EF89}.Debug|Win32.Build.0 = Debug|Win32 {E810477A-E004-4308-A58A-21393213EF89}.Release|Win32.ActiveCfg = Release|Win32 {E810477A-E004-4308-A58A-21393213EF89}.Release|Win32.Build.0 = Release|Win32 + {2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B}.Debug|Win32.ActiveCfg = Debug|Win32 + {2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B}.Debug|Win32.Build.0 = Debug|Win32 + {2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B}.Release|Win32.ActiveCfg = Release|Win32 + {2C88133A-7CB8-4C03-AF4D-4ECFC6F8500B}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/gsport.vcxproj b/src/gsport.vcxproj index 8d98eec..270c7d8 100644 --- a/src/gsport.vcxproj +++ b/src/gsport.vcxproj @@ -50,12 +50,12 @@ NotUsing Level3 Disabled - WIN32;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN_SOUND;GSPORT_LITTLE_ENDIAN;HAVE_TFE;%(PreprocessorDefinitions) - CompileAsCpp + WIN32;_DEBUG;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN_SOUND;GSPORT_LITTLE_ENDIAN;HAVE_TFE;HAVE_ATBRIDGE;TOGGLE_STATUS;%(PreprocessorDefinitions) + CompileAsC Speed - false + true ProgramDatabase - Default + EnableFastChecks Prompt true true @@ -64,7 +64,7 @@ Console true - Winmm.lib;Ws2_32.lib;Shlwapi.lib;$(SolutionDir)$(Configuration)\tfe.lib;%(AdditionalDependencies) + IPHLPAPI.lib;Winmm.lib;Ws2_32.lib;Shlwapi.lib;$(SolutionDir)$(Configuration)\tfe.lib;;$(SolutionDir)$(Configuration)\atbridge.lib;%(AdditionalDependencies) MachineX86 @@ -75,10 +75,10 @@ Full true true - WIN32;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN_SOUND;GSPORT_LITTLE_ENDIAN;HAVE_TFE;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN_SOUND;GSPORT_LITTLE_ENDIAN;HAVE_ATBRIDGE;HAVE_TFE;TOGGLE_STATUS;%(PreprocessorDefinitions) Speed false - CompileAsCpp + CompileAsC Prompt StreamingSIMDExtensions2 AnySuitable @@ -87,10 +87,10 @@ Console - false + true true true - Winmm.lib;Ws2_32.lib;Shlwapi.lib;$(SolutionDir)$(Configuration)\tfe.lib;%(AdditionalDependencies) + IPHLPAPI.lib;Winmm.lib;Ws2_32.lib;Shlwapi.lib;$(SolutionDir)$(Configuration)\tfe.lib;;$(SolutionDir)$(Configuration)\atbridge.lib;%(AdditionalDependencies) @@ -118,6 +118,7 @@ perl make_inst s 16 instable.h > 16inst_s.h 8inst_c.h 16inst_c.h 8inst_s.h 16inst_s.h false + @@ -171,10 +172,17 @@ perl make_size c size_tab.h > size_c.h - + + CompileAsCpp + CompileAsCpp + - + + CompileAsCpp + CompileAsCpp + + diff --git a/src/gsport.vcxproj.filters b/src/gsport.vcxproj.filters index 734b40e..bb358ff 100644 --- a/src/gsport.vcxproj.filters +++ b/src/gsport.vcxproj.filters @@ -117,6 +117,9 @@ Header Files + + Header Files + @@ -194,6 +197,9 @@ Source Files + + Source Files + diff --git a/src/moremem.c b/src/moremem.c index 14604ae..8ca3bce 100644 --- a/src/moremem.c +++ b/src/moremem.c @@ -1,6 +1,6 @@ /* GSport - an Apple //gs Emulator - Copyright (C) 2010 by GSport contributors + Copyright (C) 2010 - 2014 by GSport contributors Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey @@ -1351,9 +1351,12 @@ io_read(word32 loc, double *cyc_ptr) return 0; case 0x41: /* 0xc041 */ return g_c041_val; + case 0x44: /* 0xc044 */ + // SCC LAD A + return scc_read_lad(0); case 0x45: /* 0xc045 */ - halt_printf("Mega II mouse read: c045\n"); - return 0; + // SCC LAD B + return scc_read_lad(1); case 0x46: /* 0xc046 */ tmp = g_c046_val; g_c046_val = (tmp & 0xbf) + ((tmp & 0x80) >> 1); @@ -1385,7 +1388,6 @@ io_read(word32 loc, double *cyc_ptr) g_em_emubyte_cnt = 0; return 0; } - case 0x44: /* 0xc044 */ case 0x48: /* 0xc048 */ case 0x49: /* 0xc049 */ case 0x4a: /* 0xc04a */ @@ -2033,9 +2035,14 @@ io_write(word32 loc, int val, double *cyc_ptr) /* 0xc040 - 0xc04f */ case 0x41: /* c041 */ g_c041_val = val & 0x1f; - if((val & 0xe7) != 0) { + if((val & 0xe6) != 0) { halt_printf("write c041: %02x\n", val); } + + if (val & C041_EN_MOUSE) + { + // Enable Mega II mouse + } if(!(val & C041_EN_VBL_INTS)) { /* no more vbl interrupt */ @@ -2331,7 +2338,8 @@ io_write(word32 loc, int val, double *cyc_ptr) case 0x9c: case 0x9d: case 0x9e: case 0x9f: if (g_parallel) { - return parallel_write((word16)loc & 0xf, (byte)val); + parallel_write((word16)loc & 0xf, (byte)val); + return; } else { @@ -2379,7 +2387,8 @@ io_write(word32 loc, int val, double *cyc_ptr) case 0xbf: if (tfe_enabled) { - return tfe_store((word16)loc & 0xf, (byte)val); + tfe_store((word16)loc & 0xf, (byte)val); + return; } else { diff --git a/src/printer.h b/src/printer.h index 7b08c65..6923d75 100644 --- a/src/printer.h +++ b/src/printer.h @@ -1,6 +1,6 @@ /* GSport - an Apple //gs Emulator - Copyright (C) 2010 - 2011 by GSport contributors + Copyright (C) 2010 - 2014 by GSport contributors Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey @@ -289,12 +289,18 @@ private: Bit8u ASCII85CurCol; // Columns printed so far in the current lines }; +#endif + //Interfaces to C code #ifdef __cplusplus extern "C" { +#else +#include +typedef unsigned char Bit8u; #endif + void printer_init(int pdpi, int pwidth, int pheight, char* poutput, bool mpage); void printer_loop(Bit8u pchar); void printer_close(); @@ -304,4 +310,3 @@ void printer_feed(); #endif #endif -#endif diff --git a/src/protos.h b/src/protos.h index 6e34724..f648a7e 100644 --- a/src/protos.h +++ b/src/protos.h @@ -249,6 +249,7 @@ void show_scc_state(void); void scc_log(int regnum, word32 val, double dcycs); void show_scc_log(void); word32 scc_read_reg(int port, double dcycs); +word16 scc_read_lad(int port); void scc_write_reg(int port, word32 val, double dcycs); void scc_maybe_br_event(int port, double dcycs); void scc_evaluate_ints(int port); diff --git a/src/scc.c b/src/scc.c index 753b411..b52dd47 100644 --- a/src/scc.c +++ b/src/scc.c @@ -1,1309 +1,1627 @@ -/* - GSport - an Apple //gs Emulator - Copyright (C) 2010 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" - -#ifdef UNDER_CE -#define vsnprintf _vsnprintf -#endif - -extern int Verbose; -extern int g_code_yellow; -extern double g_cur_dcycs; -extern int g_serial_type[]; -extern int g_serial_out_masking; -extern int g_irq_pending; - -/* my scc port 0 == channel A = slot 1 = c039/c03b */ -/* port 1 == channel B = slot 2 = c038/c03a */ - -#include "scc.h" -#define SCC_R14_DPLL_SOURCE_BRG 0x100 -#define SCC_R14_FM_MODE 0x200 - -#define SCC_DCYCS_PER_PCLK ((DCYCS_1_MHZ) / ((DCYCS_28_MHZ) /8)) -#define SCC_DCYCS_PER_XTAL ((DCYCS_1_MHZ) / 3686400.0) - -#define SCC_BR_EVENT 1 -#define SCC_TX_EVENT 2 -#define SCC_RX_EVENT 3 -#define SCC_MAKE_EVENT(port, a) (((a) << 1) + (port)) - -Scc scc_stat[2]; - -int g_baud_table[] = { - 110, 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200 , 230400 -}; - -int g_scc_overflow = 0; - -void -scc_init() -{ - Scc *scc_ptr; - int i, j; - - for(i = 0; i < 2; i++) { - scc_ptr = &(scc_stat[i]); - scc_ptr->accfd = -1; - scc_ptr->sockfd = -1; - scc_ptr->socket_state = -1; - scc_ptr->rdwrfd = -1; - scc_ptr->state = 0; - scc_ptr->host_handle = 0; - scc_ptr->host_handle2 = 0; - scc_ptr->br_event_pending = 0; - scc_ptr->rx_event_pending = 0; - scc_ptr->tx_event_pending = 0; - scc_ptr->char_size = 8; +/* + GSport - an Apple //gs Emulator + Copyright (C) 2010 - 2014 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 "scc_llap.h" + +#ifdef UNDER_CE +#define vsnprintf _vsnprintf +#endif + +extern int Verbose; +extern int g_code_yellow; +extern double g_cur_dcycs; +extern int g_serial_type[]; +extern int g_serial_out_masking; +extern int g_irq_pending; +extern int g_c041_val; +extern int g_appletalk_bridging; +extern int g_appletalk_turbo; + +/* my scc port 0 == channel A = slot 1 = c039/c03b */ +/* port 1 == channel B = slot 2 = c038/c03a */ + +#include "scc.h" +#define SCC_R14_DPLL_SOURCE_BRG 0x100 +#define SCC_R14_FM_MODE 0x200 + +#define SCC_DCYCS_PER_PCLK ((DCYCS_1_MHZ) / ((DCYCS_28_MHZ) /8)) +#define SCC_DCYCS_PER_XTAL ((DCYCS_1_MHZ) / 3686400.0) + +#define SCC_BR_EVENT 1 +#define SCC_TX_EVENT 2 +#define SCC_RX_EVENT 3 +#define SCC_MAKE_EVENT(port, a) (((a) << 1) + (port)) + +Scc scc_stat[2]; + +int g_baud_table[] = { + 110, 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400 +}; + +static char* wr_names[] = { + "command", // 0 + "interrupt and transfer mode", // 1 + "interrupt vector", // 2 + "receive params", // 3 + "misc params", // 4 + "trasmit params", // 5 + "sync/addr field", // 6 + "sync/flag", // 7 + "transmit", // 8 + "master interrupt", // 9 + "trans/recv control", // 10 + "clock mode", // 11 + "baud rate (lower)", // 12 + "baud rate (upper)", // 13 + "misc control", // 14 + "ext/status interrupt" // 15 +}; +static char* rr_names[] = { + "status", + "special condition", + "interrupt vector", + "pending", + "RR4", + "RR5", + "RR6", + "RR7", + "receive data", + "RR9", + "misc status", + "time constant (lower)", + "time constant (upper)", + "RR14", + "ext/status interrupt" +}; + +int g_scc_overflow = 0; + +void +scc_init() +{ + Scc *scc_ptr; + int i, j; + + for(i = 0; i < 2; i++) { + scc_ptr = &(scc_stat[i]); + scc_ptr->accfd = -1; + scc_ptr->sockfd = -1; + scc_ptr->socket_state = -1; + scc_ptr->rdwrfd = -1; + scc_ptr->state = 0; + scc_ptr->host_handle = 0; + scc_ptr->host_handle2 = 0; + scc_ptr->br_event_pending = 0; + scc_ptr->rx_event_pending = 0; + scc_ptr->tx_event_pending = 0; + scc_ptr->char_size = 8; scc_ptr->baud_rate = 115200; - scc_ptr->telnet_mode = 0; - scc_ptr->telnet_iac = 0; - scc_ptr->out_char_dcycs = 0.0; - scc_ptr->socket_num_rings = 0; - scc_ptr->socket_last_ring_dcycs = 0; - scc_ptr->modem_mode = 0; - scc_ptr->modem_dial_or_acc_mode = 0; - scc_ptr->modem_plus_mode = 0; - scc_ptr->modem_s0_val = 0; - scc_ptr->modem_cmd_len = 0; - scc_ptr->modem_cmd_str[0] = 0; - for(j = 0; j < 2; j++) { - scc_ptr->telnet_local_mode[j] = 0; - scc_ptr->telnet_remote_mode[j] = 0; - scc_ptr->telnet_reqwill_mode[j] = 0; - scc_ptr->telnet_reqdo_mode[j] = 0; - } - } - - scc_reset(); - // Initialize ports right away - enable incoming data before PR#x - scc_port_init(0); - scc_port_init(1); -} - -void -scc_reset() -{ - Scc *scc_ptr; - int i; - - for(i = 0; i < 2; i++) { - scc_ptr = &(scc_stat[i]); - - scc_ptr->port = i; - scc_ptr->mode = 0; - scc_ptr->reg_ptr = 0; - scc_ptr->in_rdptr = 0; - scc_ptr->in_wrptr = 0; - scc_ptr->out_rdptr = 0; - scc_ptr->out_wrptr = 0; - scc_ptr->dcd = 0; - scc_ptr->wantint_rx = 0; - scc_ptr->wantint_tx = 0; - scc_ptr->wantint_zerocnt = 0; - scc_ptr->read_called_this_vbl = 0; - scc_ptr->write_called_this_vbl = 0; - scc_evaluate_ints(i); - scc_hard_reset_port(i); - } -} - -void -scc_hard_reset_port(int port) -{ - Scc *scc_ptr; - - scc_reset_port(port); - - scc_ptr = &(scc_stat[port]); - scc_ptr->reg[14] = 0; /* zero bottom two bits */ - scc_ptr->reg[13] = 0; - scc_ptr->reg[12] = 0; - scc_ptr->reg[11] = 0x08; - scc_ptr->reg[10] = 0; - scc_ptr->reg[7] = 0; - scc_ptr->reg[6] = 0; - scc_ptr->reg[5] = 0; - scc_ptr->reg[4] = 0x04; - scc_ptr->reg[3] = 0; - scc_ptr->reg[2] = 0; - scc_ptr->reg[1] = 0; - - /* HACK HACK: */ - scc_stat[0].reg[9] = 0; /* Clear all interrupts */ - - scc_evaluate_ints(port); - - scc_regen_clocks(port); -} - -void -scc_reset_port(int port) -{ - Scc *scc_ptr; - - scc_ptr = &(scc_stat[port]); - scc_ptr->reg[15] = 0xf8; - scc_ptr->reg[14] &= 0x03; /* 0 most (including >= 0x100) bits */ - scc_ptr->reg[10] = 0; - scc_ptr->reg[5] &= 0x65; /* leave tx bits and sdlc/crc bits */ - scc_ptr->reg[4] |= 0x04; /* Set async mode */ - scc_ptr->reg[3] &= 0xfe; /* clear receiver enable */ - scc_ptr->reg[1] &= 0xfe; /* clear ext int enable */ - - scc_ptr->br_is_zero = 0; - scc_ptr->tx_buf_empty = 1; - - scc_ptr->wantint_rx = 0; - scc_ptr->wantint_tx = 0; - scc_ptr->wantint_zerocnt = 0; - - scc_ptr->rx_queue_depth = 0; - - scc_evaluate_ints(port); - - scc_regen_clocks(port); - - scc_clr_tx_int(port); - scc_clr_rx_int(port); -} - -void -scc_regen_clocks(int port) -{ - Scc *scc_ptr; - double br_dcycs, tx_dcycs, rx_dcycs; - double rx_char_size, tx_char_size; - double clock_mult; - word32 reg4; - word32 reg14; - word32 reg11; - word32 br_const; - word32 baud; - word32 max_diff; - word32 diff; - int state; - int baud_entries; - int pos; - int i; - - /* Always do baud rate generator */ - scc_ptr = &(scc_stat[port]); - br_const = (scc_ptr->reg[13] << 8) + scc_ptr->reg[12]; - br_const += 2; /* counts down past 0 */ - - reg4 = scc_ptr->reg[4]; - clock_mult = 1.0; - switch((reg4 >> 6) & 3) { - case 0: /* x1 */ - clock_mult = 1.0; - break; - case 1: /* x16 */ - clock_mult = 16.0; - break; - case 2: /* x32 */ - clock_mult = 32.0; - break; - case 3: /* x64 */ - clock_mult = 64.0; - break; - } - - br_dcycs = 0.01; - reg14 = scc_ptr->reg[14]; - if(reg14 & 0x1) { - br_dcycs = SCC_DCYCS_PER_XTAL; - if(reg14 & 0x2) { - br_dcycs = SCC_DCYCS_PER_PCLK; - } - } - - br_dcycs = br_dcycs * (double)br_const; - - tx_dcycs = 1; - rx_dcycs = 1; - reg11 = scc_ptr->reg[11]; - if(((reg11 >> 3) & 3) == 2) { - tx_dcycs = 2.0 * br_dcycs * clock_mult; - } - if(((reg11 >> 5) & 3) == 2) { - rx_dcycs = 2.0 * br_dcycs * clock_mult; - } - - tx_char_size = 8.0; - switch((scc_ptr->reg[5] >> 5) & 0x3) { - case 0: // 5 bits - tx_char_size = 5.0; - break; - case 1: // 7 bits - tx_char_size = 7.0; - break; - case 2: // 6 bits - tx_char_size = 6.0; - break; - } - - scc_ptr->char_size = (int)tx_char_size; - - switch((scc_ptr->reg[4] >> 2) & 0x3) { - case 1: // 1 stop bit - tx_char_size += 2.0; // 1 stop + 1 start bit - break; - case 2: // 1.5 stop bit - tx_char_size += 2.5; // 1.5 stop + 1 start bit - break; - case 3: // 2 stop bits - tx_char_size += 3.0; // 2.0 stop + 1 start bit - break; - } - - if(scc_ptr->reg[4] & 1) { - // parity enabled - tx_char_size += 1.0; - } - - if(scc_ptr->reg[14] & 0x10) { - /* loopback mode, make it go faster...*/ - rx_char_size = 1.0; - tx_char_size = 1.0; - } - - rx_char_size = tx_char_size; /* HACK */ - - baud = (int)(DCYCS_1_MHZ / tx_dcycs); - max_diff = 5000000; - pos = 0; - baud_entries = sizeof(g_baud_table)/sizeof(g_baud_table[0]); - for(i = 0; i < baud_entries; i++) { - diff = abs((int)(g_baud_table[i] - baud)); - if(diff < max_diff) { - pos = i; - max_diff = diff; - } - } - - scc_ptr->baud_rate = g_baud_table[pos]; - - scc_ptr->br_dcycs = br_dcycs; - scc_ptr->tx_dcycs = tx_dcycs * tx_char_size; - scc_ptr->rx_dcycs = rx_dcycs * rx_char_size; - - state = scc_ptr->state; + scc_ptr->telnet_mode = 0; + scc_ptr->telnet_iac = 0; + scc_ptr->out_char_dcycs = 0.0; + scc_ptr->socket_num_rings = 0; + scc_ptr->socket_last_ring_dcycs = 0; + scc_ptr->modem_mode = 0; + scc_ptr->modem_dial_or_acc_mode = 0; + scc_ptr->modem_plus_mode = 0; + scc_ptr->modem_s0_val = 0; + scc_ptr->modem_cmd_len = 0; + scc_ptr->modem_cmd_str[0] = 0; + for(j = 0; j < 2; j++) { + scc_ptr->telnet_local_mode[j] = 0; + scc_ptr->telnet_remote_mode[j] = 0; + scc_ptr->telnet_reqwill_mode[j] = 0; + scc_ptr->telnet_reqdo_mode[j] = 0; + } + } + + scc_reset(); +} + +void +scc_reset() +{ + Scc *scc_ptr; + int i; + + for(i = 0; i < 2; i++) { + scc_ptr = &(scc_stat[i]); + + scc_ptr->port = i; + scc_ptr->mode = 0; + scc_ptr->reg_ptr = 0; + scc_ptr->in_rdptr = 0; + scc_ptr->in_wrptr = 0; + scc_ptr->lad = 0; + scc_ptr->out_rdptr = 0; + scc_ptr->out_wrptr = 0; + scc_ptr->dcd = 0; + scc_ptr->wantint_rx = 0; + scc_ptr->wantint_tx = 0; + scc_ptr->wantint_zerocnt = 0; + scc_ptr->did_int_rx_first = 0; + scc_ptr->irq_pending = 0; + scc_ptr->read_called_this_vbl = 0; + scc_ptr->write_called_this_vbl = 0; + scc_evaluate_ints(i); + scc_hard_reset_port(i); + } +} + +void +scc_hard_reset_port(int port) +{ + Scc *scc_ptr; + + scc_reset_port(port); + + scc_ptr = &(scc_stat[port]); + scc_ptr->reg[14] = 0; /* zero bottom two bits */ + scc_ptr->reg[13] = 0; + scc_ptr->reg[12] = 0; + scc_ptr->reg[11] = 0x08; + scc_ptr->reg[10] = 0; + scc_ptr->reg[7] = 0; + scc_ptr->reg[6] = 0; + scc_ptr->reg[5] = 0; + scc_ptr->reg[4] = 0x04; + scc_ptr->reg[3] = 0; + scc_ptr->reg[2] = 0; + scc_ptr->reg[1] = 0; + + /* HACK HACK: */ + scc_stat[0].reg[9] = 0; /* Clear all interrupts */ + + scc_evaluate_ints(port); + + scc_regen_clocks(port); +} + +void +scc_reset_port(int port) +{ + Scc *scc_ptr; + + scc_ptr = &(scc_stat[port]); + scc_ptr->reg[15] = 0xf8; + scc_ptr->reg[14] &= 0x03; /* 0 most (including >= 0x100) bits */ + scc_ptr->reg[10] = 0; + scc_ptr->reg[5] &= 0x65; /* leave tx bits and sdlc/crc bits */ + scc_ptr->reg[4] |= 0x04; /* Set async mode */ + scc_ptr->reg[3] &= 0xfe; /* clear receiver enable */ + scc_ptr->reg[1] &= 0xfe; /* clear ext int enable */ + + scc_ptr->br_is_zero = 0; + scc_ptr->tx_buf_empty = 1; + + scc_ptr->wantint_rx = 0; + scc_ptr->wantint_tx = 0; + scc_ptr->wantint_zerocnt = 0; + + scc_ptr->rx_queue_depth = 0; + scc_ptr->sdlc_eof = 0; + scc_ptr->eom = 1; + + scc_evaluate_ints(port); + + scc_regen_clocks(port); + + scc_clr_tx_int(port); + scc_clr_rx_int(port); +} + +void +scc_regen_clocks(int port) +{ + Scc *scc_ptr; + double br_dcycs, tx_dcycs, rx_dcycs; + double rx_char_size, tx_char_size; + double clock_mult; + word32 reg4; + word32 reg14; + word32 reg11; + word32 br_const; + word32 baud; + word32 max_diff; + word32 diff; + int sync_mode = 0; + int baud_entries; + int pos; + int i; + + /* Always do baud rate generator */ + scc_ptr = &(scc_stat[port]); + br_const = (scc_ptr->reg[13] << 8) + scc_ptr->reg[12]; + br_const += 2; /* counts down past 0 */ + + reg4 = scc_ptr->reg[4]; + clock_mult = 1.0; + switch((reg4 >> 6) & 3) { + case 0: /* x1 */ + clock_mult = 1.0; + break; + case 1: /* x16 */ + clock_mult = 16.0; + break; + case 2: /* x32 */ + clock_mult = 32.0; + break; + case 3: /* x64 */ + clock_mult = 64.0; + break; + } + + br_dcycs = 0.01; + reg14 = scc_ptr->reg[14]; + if(reg14 & 0x1) { + br_dcycs = SCC_DCYCS_PER_XTAL; + if(reg14 & 0x2) { + br_dcycs = SCC_DCYCS_PER_PCLK; + } + } + + br_dcycs = br_dcycs * (double)br_const; + + tx_dcycs = 1; + rx_dcycs = 1; + reg11 = scc_ptr->reg[11]; + if(((reg11 >> 3) & 3) == 2) { + tx_dcycs = 2.0 * br_dcycs * clock_mult; + } + switch ((reg11 >> 5) & 3) { + case 0: + // Receive clock = RTxC pin (not emulated) + case 1: + // Receive clock = TRxC pin (not emulated) + // The real SCC has external pins that could provide the clock. But, this is not emulated. + break; + case 3: + // Receive clock = DPLL output + // Only LocalTalk uses the DPLL receive clock. We do not, however, emulate the DPLL. + // In this case, the receive clock should be about the same as the transmit clock. + rx_dcycs = tx_dcycs; + break; + case 2: + // Receive clock = BRG output + rx_dcycs = 2.0 * br_dcycs * clock_mult; + break; + } + + tx_char_size = 8.0; + switch((scc_ptr->reg[5] >> 5) & 0x3) { + case 0: // 5 bits + tx_char_size = 5.0; + break; + case 1: // 7 bits + tx_char_size = 7.0; + break; + case 2: // 6 bits + tx_char_size = 6.0; + break; + } + + scc_ptr->char_size = (int)tx_char_size; + + switch((scc_ptr->reg[4] >> 2) & 0x3) { + case 0: // sync mode (no start or stop bits) + sync_mode = 1; + break; + case 1: // 1 stop bit + tx_char_size += 2.0; // 1 stop + 1 start bit + break; + case 2: // 1.5 stop bit + tx_char_size += 2.5; // 1.5 stop + 1 start bit + break; + case 3: // 2 stop bits + tx_char_size += 3.0; // 2.0 stop + 1 start bit + break; + } + + if((scc_ptr->reg[4] & 1) && !sync_mode) { + // parity enabled + tx_char_size += 1.0; + } + + if(scc_ptr->reg[14] & 0x10) { + /* loopback mode, make it go faster...*/ + rx_char_size = 1.0; + tx_char_size = 1.0; + } + + rx_char_size = tx_char_size; /* HACK */ + + baud = (int)(DCYCS_1_MHZ / tx_dcycs); + max_diff = 5000000; + pos = 0; + baud_entries = sizeof(g_baud_table)/sizeof(g_baud_table[0]); + for(i = 0; i < baud_entries; i++) { + diff = abs((int)(g_baud_table[i] - baud)); + if(diff < max_diff) { + pos = i; + max_diff = diff; + } + } + + scc_ptr->baud_rate = g_baud_table[pos]; + + scc_ptr->br_dcycs = br_dcycs; + scc_ptr->tx_dcycs = tx_dcycs * tx_char_size; + scc_ptr->rx_dcycs = rx_dcycs * rx_char_size; + switch (scc_ptr->state) { case 1: /* socket */ scc_socket_change_params(port); break; case 2: /* real serial ports */ -#ifdef MAC - scc_serial_mac_change_params(port); -#endif -#ifdef _WIN32 - scc_serial_win_change_params(port); -#endif +#ifdef MAC + scc_serial_mac_change_params(port); +#endif +#ifdef _WIN32 + scc_serial_win_change_params(port); +#endif + break; + case 3: /* localtalk */ + if (g_appletalk_turbo) + { + // If the user has selected AppleTalk "turbo" mode, increase the baud + // rate to be as fast as possible, limited primarily by the ability of + // the emulated GS to handle data. + scc_ptr->baud_rate = 0; + scc_ptr->br_dcycs = 1; + scc_ptr->tx_dcycs = 1; + scc_ptr->rx_dcycs = 1; + } break; case 4: /* Imagewriter */ scc_ptr->baud_rate = 230400; scc_ptr->tx_dcycs = tx_dcycs * 1.2; //Somehow this speeds up serial transfer without overrunning the buffer scc_ptr->rx_dcycs = rx_dcycs * 1.2; break; - } -} + } -void -scc_port_init(int port) -{ - int state; - state = 0; - switch (g_serial_type[port]) { - case 0: - break; - case 1: - #ifdef MAC - state = scc_serial_mac_init(port); - #endif - #ifdef _WIN32 - state = scc_serial_win_init(port); - #endif - break; +} + +void +scc_port_init(int port) +{ + int state; + state = 0; + switch (g_serial_type[port]) { + case 0: + break; + case 1: + #ifdef MAC + state = scc_serial_mac_init(port); + #endif + #ifdef _WIN32 + state = scc_serial_win_init(port); + #endif + break; case 2: state = scc_imagewriter_init(port); break; - default: + default: + break; + } + + if(state <= 0) { + scc_socket_init(port); + } +} + +void +scc_try_to_empty_writebuf(int port, double dcycs) +{ + Scc *scc_ptr; + int state; + + scc_ptr = &(scc_stat[port]); + state = scc_ptr->state; + if(scc_ptr->write_called_this_vbl) { + return; + } + + scc_ptr->write_called_this_vbl = 1; + + switch (state) + { + case 2: +#if defined(MAC) + scc_serial_mac_empty_writebuf(port); +#endif +#if defined(_WIN32) + scc_serial_win_empty_writebuf(port); +#endif + break; + + case 1: + scc_socket_empty_writebuf(port, dcycs); break; - } - if(state <= 0) { - scc_socket_init(port); - } -} - -void -scc_try_to_empty_writebuf(int port, double dcycs) -{ - Scc *scc_ptr; - int state; - - scc_ptr = &(scc_stat[port]); - state = scc_ptr->state; - if(scc_ptr->write_called_this_vbl) { - return; - } - - scc_ptr->write_called_this_vbl = 1; - - switch (state) - { - case 2: -#if defined(MAC) - scc_serial_mac_empty_writebuf(port); -#endif -#if defined(_WIN32) - scc_serial_win_empty_writebuf(port); -#endif - break; - - case 1: - scc_socket_empty_writebuf(port, dcycs); + case 3: + // When we're doing LocalTalk, the write buffer gets emptied at the end of the frame and does not use this function. break; case 4: scc_imagewriter_empty_writebuf(port, dcycs); break; - } -} - -void -scc_try_fill_readbuf(int port, double dcycs) -{ - Scc *scc_ptr; - int space_used, space_left; - int state; - - scc_ptr = &(scc_stat[port]); - state = scc_ptr->state; - - space_used = scc_ptr->in_wrptr - scc_ptr->in_rdptr; - if(space_used < 0) { - space_used += SCC_INBUF_SIZE; - } - space_left = (7*SCC_INBUF_SIZE/8) - space_used; - if(space_left < 1) { - /* Buffer is pretty full, don't try to get more */ - return; - } - -#if 0 - if(scc_ptr->read_called_this_vbl) { - return; - } -#endif - - scc_ptr->read_called_this_vbl = 1; - - switch (state) - { - case 2: -#if defined(MAC) - scc_serial_mac_fill_readbuf(port, space_left, dcycs); -#endif -#if defined(_WIN32) - scc_serial_win_fill_readbuf(port, space_left, dcycs); -#endif + } +} + +void +scc_try_fill_readbuf(int port, double dcycs) +{ + Scc *scc_ptr; + int space_used_before_rx, space_left; + int space_used_after_rx; + int state; + + scc_ptr = &(scc_stat[port]); + state = scc_ptr->state; + + space_used_before_rx = scc_ptr->in_wrptr - scc_ptr->in_rdptr; + if(space_used_before_rx < 0) { + space_used_before_rx += SCC_INBUF_SIZE; + } + space_left = (7*SCC_INBUF_SIZE/8) - space_used_before_rx; + if(space_left < 1) { + /* Buffer is pretty full, don't try to get more */ + return; + } + +#if 0 + if(scc_ptr->read_called_this_vbl) { + return; + } +#endif + + scc_ptr->read_called_this_vbl = 1; + + switch (state) + { + case 2: +#if defined(MAC) + scc_serial_mac_fill_readbuf(port, space_left, dcycs); +#endif +#if defined(_WIN32) + scc_serial_win_fill_readbuf(port, space_left, dcycs); +#endif + break; + + case 1: + scc_socket_fill_readbuf(port, space_left, dcycs); break; - - case 1: - scc_socket_fill_readbuf(port, space_left, dcycs); + + case 3: + // LLAP deals with packets, and we only allow one packet in the read buffer at a time. + // If the buffer is empty, try to fill it with another packet. + if (g_appletalk_bridging && (space_used_before_rx == 0) && (scc_ptr->rx_queue_depth == 0) && !(scc_ptr->sdlc_eof)) + { + scc_llap_fill_readbuf(port, space_left, dcycs); + //scc_maybe_rx_event(port, dcycs); + scc_ptr->sdlc_eof = 0; break; case 4: scc_imagewriter_fill_readbuf(port, space_left, dcycs); break; - } -} - -void -scc_update(double dcycs) -{ - /* called each VBL update */ - scc_stat[0].write_called_this_vbl = 0; - scc_stat[1].write_called_this_vbl = 0; - scc_stat[0].read_called_this_vbl = 0; - scc_stat[1].read_called_this_vbl = 0; - - scc_try_to_empty_writebuf(0, dcycs); - scc_try_to_empty_writebuf(1, dcycs); - scc_try_fill_readbuf(0, dcycs); - scc_try_fill_readbuf(1, dcycs); - - scc_stat[0].write_called_this_vbl = 0; - scc_stat[1].write_called_this_vbl = 0; - scc_stat[0].read_called_this_vbl = 0; - scc_stat[1].read_called_this_vbl = 0; -} - -void -do_scc_event(int type, double dcycs) -{ - Scc *scc_ptr; - int port; - - port = type & 1; - type = (type >> 1); - - scc_ptr = &(scc_stat[port]); - if(type == SCC_BR_EVENT) { - /* baud rate generator counted down to 0 */ - scc_ptr->br_event_pending = 0; - scc_set_zerocnt_int(port); - scc_maybe_br_event(port, dcycs); - } else if(type == SCC_TX_EVENT) { - scc_ptr->tx_event_pending = 0; - scc_ptr->tx_buf_empty = 1; - scc_handle_tx_event(port, dcycs); - } else if(type == SCC_RX_EVENT) { - scc_ptr->rx_event_pending = 0; - scc_maybe_rx_event(port, dcycs); - } else { - halt_printf("do_scc_event: %08x!\n", type); - } - return; -} - -void -show_scc_state() -{ - Scc *scc_ptr; - int i, j; - - for(i = 0; i < 2; i++) { - scc_ptr = &(scc_stat[i]); - printf("SCC port: %d\n", i); - for(j = 0; j < 16; j += 4) { - printf("Reg %2d-%2d: %02x %02x %02x %02x\n", j, j+3, - scc_ptr->reg[j], scc_ptr->reg[j+1], - scc_ptr->reg[j+2], scc_ptr->reg[j+3]); - } - printf("state: %d, accfd: %d, rdwrfd: %d, host:%p, host2:%p\n", - scc_ptr->state, scc_ptr->accfd, scc_ptr->rdwrfd, - scc_ptr->host_handle, scc_ptr->host_handle2); - printf("in_rdptr: %04x, in_wr:%04x, out_rd:%04x, out_wr:%04x\n", - scc_ptr->in_rdptr, scc_ptr->in_wrptr, - scc_ptr->out_rdptr, scc_ptr->out_wrptr); - printf("rx_queue_depth: %d, queue: %02x, %02x, %02x, %02x\n", - scc_ptr->rx_queue_depth, scc_ptr->rx_queue[0], - scc_ptr->rx_queue[1], scc_ptr->rx_queue[2], - scc_ptr->rx_queue[3]); - printf("want_ints: rx:%d, tx:%d, zc:%d\n", - scc_ptr->wantint_rx, scc_ptr->wantint_tx, - scc_ptr->wantint_zerocnt); - printf("ev_pendings: rx:%d, tx:%d, br:%d\n", - scc_ptr->rx_event_pending, - scc_ptr->tx_event_pending, - scc_ptr->br_event_pending); - printf("br_dcycs: %f, tx_dcycs: %f, rx_dcycs: %f\n", - scc_ptr->br_dcycs, scc_ptr->tx_dcycs, - scc_ptr->rx_dcycs); - printf("char_size: %d, baud_rate: %d, mode: %d\n", - scc_ptr->char_size, scc_ptr->baud_rate, - scc_ptr->mode); - printf("modem_dial_mode:%d, telnet_mode:%d iac:%d, " - "modem_cmd_len:%d\n", scc_ptr->modem_dial_or_acc_mode, - scc_ptr->telnet_mode, scc_ptr->telnet_iac, - scc_ptr->modem_cmd_len); - printf("telnet_loc_modes:%08x %08x, telnet_rem_motes:" - "%08x %08x\n", scc_ptr->telnet_local_mode[0], - scc_ptr->telnet_local_mode[1], - scc_ptr->telnet_remote_mode[0], - scc_ptr->telnet_remote_mode[1]); - printf("modem_mode:%08x plus_mode: %d, out_char_dcycs: %f\n", - scc_ptr->modem_mode, scc_ptr->modem_plus_mode, - scc_ptr->out_char_dcycs); - } - -} - -#define LEN_SCC_LOG 50 -STRUCT(Scc_log) { - int regnum; - word32 val; - double dcycs; -}; - -Scc_log g_scc_log[LEN_SCC_LOG]; -int g_scc_log_pos = 0; - -#define SCC_REGNUM(wr,port,reg) ((wr << 8) + (port << 4) + reg) - -void -scc_log(int regnum, word32 val, double dcycs) -{ - int pos; - - pos = g_scc_log_pos; - g_scc_log[pos].regnum = regnum; - g_scc_log[pos].val = val; - g_scc_log[pos].dcycs = dcycs; - pos++; - if(pos >= LEN_SCC_LOG) { - pos = 0; - } - g_scc_log_pos = pos; -} - -void -show_scc_log(void) -{ - double dcycs; - int regnum; - int pos; - int i; - - pos = g_scc_log_pos; - dcycs = g_cur_dcycs; - printf("SCC log pos: %d, cur dcycs:%f\n", pos, dcycs); - for(i = 0; i < LEN_SCC_LOG; i++) { - pos--; - if(pos < 0) { - pos = LEN_SCC_LOG - 1; - } - regnum = g_scc_log[pos].regnum; - printf("%d:%d: port:%d wr:%d reg: %d val:%02x at t:%f\n", - i, pos, (regnum >> 4) & 0xf, (regnum >> 8), - (regnum & 0xf), - g_scc_log[pos].val, - g_scc_log[pos].dcycs - dcycs); - } -} - -word32 -scc_read_reg(int port, double dcycs) -{ - Scc *scc_ptr; - word32 ret; - int regnum; - - scc_ptr = &(scc_stat[port]); - scc_ptr->mode = 0; - regnum = scc_ptr->reg_ptr; - - /* port 0 is channel A, port 1 is channel B */ - switch(regnum) { - case 0: - case 4: - ret = 0x60; /* 0x44 = no dcd, no cts,0x6c = dcd ok, cts ok*/ - if(scc_ptr->dcd) { - ret |= 0x08; - } - ret |= 0x8; /* HACK HACK */ - if(scc_ptr->rx_queue_depth) { - ret |= 0x01; - } - if(scc_ptr->tx_buf_empty) { - ret |= 0x04; - } - if(scc_ptr->br_is_zero) { - ret |= 0x02; - } - //printf("Read scc[%d] stat: %f : %02x\n", port, dcycs, ret); - break; - case 1: - case 5: - /* HACK: residue codes not right */ - ret = 0x07; /* all sent */ - break; - case 2: - case 6: - if(port == 0) { - ret = scc_ptr->reg[2]; - } else { - - halt_printf("Read of RR2B...stopping\n"); - ret = 0; -#if 0 - ret = scc_stat[0].reg[2]; - wr9 = scc_stat[0].reg[9]; - for(i = 0; i < 8; i++) { - if(ZZZ){}; - } - if(wr9 & 0x10) { - /* wr9 status high */ - - } -#endif - } - break; - case 3: - case 7: - if(port == 0) { - ret = (g_irq_pending & 0x3f); - } else { - ret = 0; - } - break; - case 8: - ret = scc_read_data(port, dcycs); - break; - case 9: - case 13: - ret = scc_ptr->reg[13]; - break; - case 10: - case 14: - ret = 0; - break; - case 11: - case 15: - ret = scc_ptr->reg[15]; - break; - case 12: - ret = scc_ptr->reg[12]; - break; - default: - halt_printf("Tried reading c03%x with regnum: %d!\n", 8+port, - regnum); - ret = 0; - } - - scc_ptr->reg_ptr = 0; - scc_printf("Read c03%x, rr%d, ret: %02x\n", 8+port, regnum, ret); - if(regnum != 0 && regnum != 3) { - scc_log(SCC_REGNUM(0,port,regnum), ret, dcycs); - } - - return ret; -} - -void -scc_write_reg(int port, word32 val, double dcycs) -{ - Scc *scc_ptr; - word32 old_val; - word32 changed_bits; - word32 irq_mask; - int regnum; - int mode; - int tmp1; - - scc_ptr = &(scc_stat[port]); - regnum = scc_ptr->reg_ptr & 0xf; - mode = scc_ptr->mode; - - if(mode == 0) { - if((val & 0xf0) == 0) { - /* Set reg_ptr */ - scc_ptr->reg_ptr = val & 0xf; - regnum = 0; - scc_ptr->mode = 1; - } else { - scc_log(SCC_REGNUM(1,port,0), val, dcycs); - } - } else { - scc_ptr->reg_ptr = 0; - scc_ptr->mode = 0; - } - - if(regnum != 0) { - scc_log(SCC_REGNUM(1,port,regnum), val, dcycs); - } - - changed_bits = (scc_ptr->reg[regnum] ^ val) & 0xff; - - /* Set reg reg */ - switch(regnum) { - case 0: /* wr0 */ - tmp1 = (val >> 3) & 0x7; - switch(tmp1) { - case 0x0: - case 0x1: - break; - case 0x2: /* reset ext/status ints */ - /* should clear other ext ints */ - scc_clr_zerocnt_int(port); - break; - case 0x5: /* reset tx int pending */ - scc_clr_tx_int(port); - break; - case 0x6: /* reset rr1 bits */ - break; - case 0x7: /* reset highest pri int pending */ - irq_mask = g_irq_pending; - if(port == 0) { - /* Move SCC0 ints into SCC1 positions */ - irq_mask = irq_mask >> 3; - } - if(irq_mask & IRQ_PENDING_SCC1_RX) { - scc_clr_rx_int(port); - } else if(irq_mask & IRQ_PENDING_SCC1_TX) { - scc_clr_tx_int(port); - } else if(irq_mask & IRQ_PENDING_SCC1_ZEROCNT) { - scc_clr_zerocnt_int(port); - } - break; - case 0x4: /* enable int on next rx char */ - default: - halt_printf("Wr c03%x to wr0 of %02x, bad cmd cd:%x!\n", - 8+port, val, tmp1); - } - tmp1 = (val >> 6) & 0x3; - switch(tmp1) { - case 0x0: /* null code */ - break; - case 0x1: /* reset rx crc */ - case 0x2: /* reset tx crc */ - printf("Wr c03%x to wr0 of %02x!\n", 8+port, val); - break; - case 0x3: /* reset tx underrun/eom latch */ - /* if no extern status pending, or being reset now */ - /* and tx disabled, ext int with tx underrun */ - /* ah, just do nothing */ - break; - } - return; - case 1: /* wr1 */ - /* proterm sets this == 0x10, which is int on all rx */ - scc_ptr->reg[regnum] = val; - return; - case 2: /* wr2 */ - /* All values do nothing, let 'em all through! */ - scc_ptr->reg[regnum] = val; - return; - case 3: /* wr3 */ - if((val & 0x1e) != 0x0) { - halt_printf("Wr c03%x to wr3 of %02x!\n", 8+port, val); - } - scc_ptr->reg[regnum] = val; - return; - case 4: /* wr4 */ - if((val & 0x30) != 0x00 || (val & 0x0c) == 0) { - halt_printf("Wr c03%x to wr4 of %02x!\n", 8+port, val); - } - scc_ptr->reg[regnum] = val; - if(changed_bits) { - scc_regen_clocks(port); - } - return; - case 5: /* wr5 */ - if((val & 0x15) != 0x0) { - halt_printf("Wr c03%x to wr5 of %02x!\n", 8+port, val); - } - scc_ptr->reg[regnum] = val; - if(changed_bits & 0x60) { - scc_regen_clocks(port); - } - return; - case 6: /* wr6 */ - if(val != 0) { - halt_printf("Wr c03%x to wr6 of %02x!\n", 8+port, val); - } - scc_ptr->reg[regnum] = val; - return; - case 7: /* wr7 */ - if(val != 0) { - halt_printf("Wr c03%x to wr7 of %02x!\n", 8+port, val); - } - scc_ptr->reg[regnum] = val; - return; - case 8: /* wr8 */ - scc_write_data(port, val, dcycs); - return; - case 9: /* wr9 */ - if((val & 0xc0)) { - if(val & 0x80) { - scc_reset_port(0); - } - if(val & 0x40) { - scc_reset_port(1); - } - if((val & 0xc0) == 0xc0) { - scc_hard_reset_port(0); - scc_hard_reset_port(1); - } - } - if((val & 0x35) != 0x00) { - printf("Write c03%x to wr9 of %02x!\n", 8+port, val); - halt_printf("val & 0x35: %02x\n", (val & 0x35)); - } - old_val = scc_stat[0].reg[9]; - scc_stat[0].reg[regnum] = val; - scc_evaluate_ints(0); - scc_evaluate_ints(1); - return; - case 10: /* wr10 */ - if((val & 0xff) != 0x00) { - printf("Wr c03%x to wr10 of %02x!\n", 8+port, val); - } - scc_ptr->reg[regnum] = val; - return; - case 11: /* wr11 */ - scc_ptr->reg[regnum] = val; - if(changed_bits) { - scc_regen_clocks(port); - } - return; - case 12: /* wr12 */ - scc_ptr->reg[regnum] = val; - if(changed_bits) { - scc_regen_clocks(port); - } - return; - case 13: /* wr13 */ - scc_ptr->reg[regnum] = val; - if(changed_bits) { - scc_regen_clocks(port); - } - return; - case 14: /* wr14 */ - old_val = scc_ptr->reg[regnum]; - val = val + (old_val & (~0xff)); - switch((val >> 5) & 0x7) { - case 0x0: - case 0x1: - case 0x2: - case 0x3: - break; - - case 0x4: /* DPLL source is BR gen */ - val |= SCC_R14_DPLL_SOURCE_BRG; - break; - default: - halt_printf("Wr c03%x to wr14 of %02x, bad dpll cd!\n", - 8+port, val); - } - if((val & 0x0c) != 0x0) { - halt_printf("Wr c03%x to wr14 of %02x!\n", 8+port, val); - } - scc_ptr->reg[regnum] = val; - if(changed_bits) { - scc_regen_clocks(port); - } - scc_maybe_br_event(port, dcycs); - return; - case 15: /* wr15 */ - /* ignore all accesses since IIgs self test messes with it */ - if((val & 0xff) != 0x0) { - scc_printf("Write c03%x to wr15 of %02x!\n", 8+port, - val); - } - if((scc_stat[0].reg[9] & 0x8) && (val != 0)) { - printf("Write wr15:%02x and master int en = 1!\n",val); - /* set_halt(1); */ - } - scc_ptr->reg[regnum] = val; - scc_maybe_br_event(port, dcycs); - scc_evaluate_ints(port); - return; - default: - halt_printf("Wr c03%x to wr%d of %02x!\n", 8+port, regnum, val); - return; - } -} - -void -scc_maybe_br_event(int port, double dcycs) -{ - Scc *scc_ptr; - double br_dcycs; - - scc_ptr = &(scc_stat[port]); - - if(((scc_ptr->reg[14] & 0x01) == 0) || scc_ptr->br_event_pending) { - return; - } - /* also, if ext ints not enabled, don't do baud rate ints */ - if((scc_ptr->reg[15] & 0x02) == 0) { - return; - } - - br_dcycs = scc_ptr->br_dcycs; - if(br_dcycs < 1.0) { - halt_printf("br_dcycs: %f!\n", br_dcycs); - } - - scc_ptr->br_event_pending = 1; - add_event_scc(dcycs + br_dcycs, SCC_MAKE_EVENT(port, SCC_BR_EVENT)); -} - -void -scc_evaluate_ints(int port) -{ - Scc *scc_ptr; - word32 irq_add_mask, irq_remove_mask; - int mie; - - scc_ptr = &(scc_stat[port]); - mie = scc_stat[0].reg[9] & 0x8; /* Master int en */ - - if(!mie) { - /* There can be no interrupts if MIE=0 */ - remove_irq(IRQ_PENDING_SCC1_RX | IRQ_PENDING_SCC1_TX | - IRQ_PENDING_SCC1_ZEROCNT | - IRQ_PENDING_SCC0_RX | IRQ_PENDING_SCC0_TX | - IRQ_PENDING_SCC0_ZEROCNT); - return; - } - - irq_add_mask = 0; - irq_remove_mask = 0; - if(scc_ptr->wantint_rx) { - irq_add_mask |= IRQ_PENDING_SCC1_RX; - } else { - irq_remove_mask |= IRQ_PENDING_SCC1_RX; - } - if(scc_ptr->wantint_tx) { - irq_add_mask |= IRQ_PENDING_SCC1_TX; - } else { - irq_remove_mask |= IRQ_PENDING_SCC1_TX; - } - if(scc_ptr->wantint_zerocnt) { - irq_add_mask |= IRQ_PENDING_SCC1_ZEROCNT; - } else { - irq_remove_mask |= IRQ_PENDING_SCC1_ZEROCNT; - } - if(port == 0) { - /* Port 1 is in bits 0-2 and port 0 is in bits 3-5 */ - irq_add_mask = irq_add_mask << 3; - irq_remove_mask = irq_remove_mask << 3; - } - if(irq_add_mask) { - add_irq(irq_add_mask); - } - if(irq_remove_mask) { - remove_irq(irq_remove_mask); - } -} - - -void -scc_maybe_rx_event(int port, double dcycs) -{ - Scc *scc_ptr; - double rx_dcycs; - int in_rdptr, in_wrptr; - int depth; - - scc_ptr = &(scc_stat[port]); - - if(scc_ptr->rx_event_pending) { - /* one pending already, wait for the event to arrive */ - return; - } - - in_rdptr = scc_ptr->in_rdptr; - in_wrptr = scc_ptr->in_wrptr; - depth = scc_ptr->rx_queue_depth; - if((in_rdptr == in_wrptr) || (depth >= 3)) { - /* no more chars or no more space, just get out */ - return; - } - - if(depth < 0) { - depth = 0; - } - - /* pull char from in_rdptr into queue */ - scc_ptr->rx_queue[depth] = scc_ptr->in_buf[in_rdptr]; - scc_ptr->in_rdptr = (in_rdptr + 1) & (SCC_INBUF_SIZE - 1); - scc_ptr->rx_queue_depth = depth + 1; - scc_maybe_rx_int(port, dcycs); - rx_dcycs = scc_ptr->rx_dcycs; - scc_ptr->rx_event_pending = 1; - add_event_scc(dcycs + rx_dcycs, SCC_MAKE_EVENT(port, SCC_RX_EVENT)); -} - -void -scc_maybe_rx_int(int port, double dcycs) -{ - Scc *scc_ptr; - int depth; - int rx_int_mode; - - scc_ptr = &(scc_stat[port]); - - depth = scc_ptr->rx_queue_depth; - if(depth <= 0) { - /* no more chars, just get out */ - scc_clr_rx_int(port); - return; - } - rx_int_mode = (scc_ptr->reg[1] >> 3) & 0x3; - if(rx_int_mode == 1 || rx_int_mode == 2) { - scc_ptr->wantint_rx = 1; - } - scc_evaluate_ints(port); -} - -void -scc_clr_rx_int(int port) -{ - scc_stat[port].wantint_rx = 0; - scc_evaluate_ints(port); -} - -void -scc_handle_tx_event(int port, double dcycs) -{ - Scc *scc_ptr; - int tx_int_mode; - - scc_ptr = &(scc_stat[port]); - - /* nothing pending, see if ints on */ - tx_int_mode = (scc_ptr->reg[1] & 0x2); - if(tx_int_mode) { - scc_ptr->wantint_tx = 1; - } - scc_evaluate_ints(port); -} - -void -scc_maybe_tx_event(int port, double dcycs) -{ - Scc *scc_ptr; - double tx_dcycs; - - scc_ptr = &(scc_stat[port]); - - if(scc_ptr->tx_event_pending) { - /* one pending already, tx_buf is full */ - scc_ptr->tx_buf_empty = 0; - } else { - /* nothing pending, see ints on */ - scc_evaluate_ints(port); - tx_dcycs = scc_ptr->tx_dcycs; - scc_ptr->tx_event_pending = 1; - add_event_scc(dcycs + tx_dcycs, - SCC_MAKE_EVENT(port, SCC_TX_EVENT)); - } -} - -void -scc_clr_tx_int(int port) -{ - scc_stat[port].wantint_tx = 0; - scc_evaluate_ints(port); -} - -void -scc_set_zerocnt_int(int port) -{ - Scc *scc_ptr; - - scc_ptr = &(scc_stat[port]); - - if(scc_ptr->reg[15] & 0x2) { - scc_ptr->wantint_zerocnt = 1; - } - scc_evaluate_ints(port); -} - -void -scc_clr_zerocnt_int(int port) -{ - scc_stat[port].wantint_zerocnt = 0; - scc_evaluate_ints(port); -} - -void -scc_add_to_readbuf(int port, word32 val, double dcycs) -{ - Scc *scc_ptr; - int in_wrptr; - int in_wrptr_next; - int in_rdptr; - - scc_ptr = &(scc_stat[port]); - - in_wrptr = scc_ptr->in_wrptr; - in_rdptr = scc_ptr->in_rdptr; - in_wrptr_next = (in_wrptr + 1) & (SCC_INBUF_SIZE - 1); - if(in_wrptr_next != in_rdptr) { - scc_ptr->in_buf[in_wrptr] = val; - scc_ptr->in_wrptr = in_wrptr_next; - scc_printf("scc in port[%d] add char 0x%02x, %d,%d != %d\n", - scc_ptr->port, val, - in_wrptr, in_wrptr_next, in_rdptr); - g_scc_overflow = 0; - } else { - if(g_scc_overflow == 0) { - g_code_yellow++; - printf("scc inbuf overflow port %d\n", port); - } - g_scc_overflow = 1; - } - - scc_maybe_rx_event(port, dcycs); -} - -void -scc_add_to_readbufv(int port, double dcycs, const char *fmt, ...) -{ - va_list ap; - char *bufptr; - int ret, len, c; - int i; - - va_start(ap, fmt); - bufptr = (char*)malloc(4096); // OG cast added - bufptr[0] = 0; - ret = vsnprintf(bufptr, 4090, fmt, ap); - len = strlen(bufptr); - for(i = 0; i < len; i++) { - c = bufptr[i]; - if(c == 0x0a) { - scc_add_to_readbuf(port, 0x0d, dcycs); - } - scc_add_to_readbuf(port, c, dcycs); - } - va_end(ap); -} - -void -scc_transmit(int port, word32 val, double dcycs) -{ - Scc *scc_ptr; - int out_wrptr; - int out_rdptr; - - scc_ptr = &(scc_stat[port]); - - /* See if port initialized, if not, do so now */ - if(scc_ptr->state == 0) { - scc_port_init(port); - } - if(scc_ptr->state < 0) { - /* No working serial port, just toss it and go */ - return; - } - - if(!scc_ptr->tx_buf_empty) { - /* toss character! */ - printf("Tossing char\n"); - return; - } - - out_wrptr = scc_ptr->out_wrptr; - out_rdptr = scc_ptr->out_rdptr; - if(scc_ptr->tx_dcycs < 1.0) { - if(out_wrptr != out_rdptr) { - /* do just one char, then get out */ - printf("tx_dcycs < 1\n"); - return; - } - } - if(g_serial_out_masking) { - val = val & 0x7f; - } - - scc_add_to_writebuf(port, val, dcycs); -} - -void -scc_add_to_writebuf(int port, word32 val, double dcycs) -{ - Scc *scc_ptr; - int out_wrptr; - int out_wrptr_next; - int out_rdptr; - - scc_ptr = &(scc_stat[port]); - - /* See if port initialized, if not, do so now */ - if(scc_ptr->state == 0) { - scc_port_init(port); - } - if(scc_ptr->state < 0) { - /* No working serial port, just toss it and go */ - return; - } - - out_wrptr = scc_ptr->out_wrptr; - out_rdptr = scc_ptr->out_rdptr; - - out_wrptr_next = (out_wrptr + 1) & (SCC_OUTBUF_SIZE - 1); - if(out_wrptr_next != out_rdptr) { - scc_ptr->out_buf[out_wrptr] = val; - scc_ptr->out_wrptr = out_wrptr_next; - scc_printf("scc wrbuf port %d had char 0x%02x added\n", - scc_ptr->port, val); - g_scc_overflow = 0; - } else { - if(g_scc_overflow == 0) { - g_code_yellow++; - printf("scc outbuf overflow port %d\n", port); - } - g_scc_overflow = 1; - } -} - -word32 -scc_read_data(int port, double dcycs) -{ - Scc *scc_ptr; - word32 ret; - int depth; - int i; - - scc_ptr = &(scc_stat[port]); - - scc_try_fill_readbuf(port, dcycs); - - depth = scc_ptr->rx_queue_depth; - - ret = 0; - if(depth != 0) { - ret = scc_ptr->rx_queue[0]; - for(i = 1; i < depth; i++) { - scc_ptr->rx_queue[i-1] = scc_ptr->rx_queue[i]; - } - scc_ptr->rx_queue_depth = depth - 1; - scc_maybe_rx_event(port, dcycs); - scc_maybe_rx_int(port, dcycs); - } - - scc_printf("SCC read %04x: ret %02x, depth:%d\n", 0xc03b-port, ret, - depth); - - scc_log(SCC_REGNUM(0,port,8), ret, dcycs); - - return ret; -} - - -void -scc_write_data(int port, word32 val, double dcycs) -{ - Scc *scc_ptr; - - scc_printf("SCC write %04x: %02x\n", 0xc03b-port, val); - scc_log(SCC_REGNUM(1,port,8), val, dcycs); - - scc_ptr = &(scc_stat[port]); - if(scc_ptr->reg[14] & 0x10) { - /* local loopback! */ - scc_add_to_readbuf(port, val, dcycs); - } else { - scc_transmit(port, val, dcycs); - } - scc_try_to_empty_writebuf(port, dcycs); - - scc_maybe_tx_event(port, dcycs); + } + break; + } + + // Update the LAD (link activity detector), which LocalTalk uses in the CSMA/CA algorithm. + // The real LAD depends on the line coding and data, but the "get bigger when data on RX line" + // emulation is good enough since no software depends on the specific value of the LAD counter. + // Practically, the emulated LLAP interface never has collisions and the LAD, therefore, is not + // useful, but, for sake of correctness and more realisitic timing, emulate the LAD anyway. + space_used_after_rx = scc_ptr->in_wrptr - scc_ptr->in_rdptr; + if(space_used_after_rx < 0) { + space_used_after_rx += SCC_INBUF_SIZE; + } + scc_ptr->lad += space_used_after_rx - space_used_before_rx; +} + +void +scc_update(double dcycs) +{ + if (g_appletalk_bridging && (scc_stat[0].state == 3 || scc_stat[1].state == 3)) + scc_llap_update(); + + /* called each VBL update */ + scc_stat[0].write_called_this_vbl = 0; + scc_stat[1].write_called_this_vbl = 0; + scc_stat[0].read_called_this_vbl = 0; + scc_stat[1].read_called_this_vbl = 0; + + scc_try_fill_readbuf(0, dcycs); + scc_try_fill_readbuf(1, dcycs); + scc_stat[0].read_called_this_vbl = 0; + scc_stat[1].read_called_this_vbl = 0; + + /* LLAP mode only transfers complete packets. Retain the data in the + transmit and receive buffers until the buffers contain one complete packet */ + if (scc_stat[0].state != 3) + { + scc_try_to_empty_writebuf(0, dcycs); + scc_stat[0].write_called_this_vbl = 0; + } + if (scc_stat[1].state != 3) + { + scc_try_to_empty_writebuf(1, dcycs); + scc_stat[1].write_called_this_vbl = 0; + } +} + +void +do_scc_event(int type, double dcycs) +{ + Scc *scc_ptr; + int port; + + port = type & 1; + type = (type >> 1); + + scc_ptr = &(scc_stat[port]); + if(type == SCC_BR_EVENT) { + /* baud rate generator counted down to 0 */ + scc_ptr->br_event_pending = 0; + scc_set_zerocnt_int(port); + scc_maybe_br_event(port, dcycs); + } else if(type == SCC_TX_EVENT) { + scc_ptr->tx_event_pending = 0; + scc_ptr->tx_buf_empty = 1; + scc_handle_tx_event(port, dcycs); + } else if(type == SCC_RX_EVENT) { + scc_ptr->rx_event_pending = 0; + scc_maybe_rx_event(port, dcycs); + } else { + halt_printf("do_scc_event: %08x!\n", type); + } + return; +} + +void +show_scc_state() +{ + Scc *scc_ptr; + int i, j; + + for(i = 0; i < 2; i++) { + scc_ptr = &(scc_stat[i]); + printf("SCC port: %d\n", i); + for(j = 0; j < 16; j += 4) { + printf("Reg %2d-%2d: %02x %02x %02x %02x\n", j, j+3, + scc_ptr->reg[j], scc_ptr->reg[j+1], + scc_ptr->reg[j+2], scc_ptr->reg[j+3]); + } + printf("state: %d, accfd: %d, rdwrfd: %d, host:%p, host2:%p\n", + scc_ptr->state, scc_ptr->accfd, scc_ptr->rdwrfd, + scc_ptr->host_handle, scc_ptr->host_handle2); + printf("in_rdptr: %04x, in_wr:%04x, out_rd:%04x, out_wr:%04x\n", + scc_ptr->in_rdptr, scc_ptr->in_wrptr, + scc_ptr->out_rdptr, scc_ptr->out_wrptr); + printf("rx_queue_depth: %d, queue: %02x, %02x, %02x, %02x\n", + scc_ptr->rx_queue_depth, scc_ptr->rx_queue[0], + scc_ptr->rx_queue[1], scc_ptr->rx_queue[2], + scc_ptr->rx_queue[3]); + printf("want_ints: rx:%d, tx:%d, zc:%d\n", + scc_ptr->wantint_rx, scc_ptr->wantint_tx, + scc_ptr->wantint_zerocnt); + printf("ev_pendings: rx:%d, tx:%d, br:%d\n", + scc_ptr->rx_event_pending, + scc_ptr->tx_event_pending, + scc_ptr->br_event_pending); + printf("br_dcycs: %f, tx_dcycs: %f, rx_dcycs: %f\n", + scc_ptr->br_dcycs, scc_ptr->tx_dcycs, + scc_ptr->rx_dcycs); + printf("char_size: %d, baud_rate: %d, mode: %d\n", + scc_ptr->char_size, scc_ptr->baud_rate, + scc_ptr->mode); + printf("modem_dial_mode:%d, telnet_mode:%d iac:%d, " + "modem_cmd_len:%d\n", scc_ptr->modem_dial_or_acc_mode, + scc_ptr->telnet_mode, scc_ptr->telnet_iac, + scc_ptr->modem_cmd_len); + printf("telnet_loc_modes:%08x %08x, telnet_rem_motes:" + "%08x %08x\n", scc_ptr->telnet_local_mode[0], + scc_ptr->telnet_local_mode[1], + scc_ptr->telnet_remote_mode[0], + scc_ptr->telnet_remote_mode[1]); + printf("modem_mode:%08x plus_mode: %d, out_char_dcycs: %f\n", + scc_ptr->modem_mode, scc_ptr->modem_plus_mode, + scc_ptr->out_char_dcycs); + } + +} + +#define LEN_SCC_LOG 5000 +STRUCT(Scc_log) { + int regnum; + word32 val; + double dcycs; +}; + +Scc_log g_scc_log[LEN_SCC_LOG]; +int g_scc_log_pos = 0; + +#define SCC_REGNUM(wr,port,reg) ((wr << 8) + (port << 4) + reg) + +void +scc_log(int regnum, word32 val, double dcycs) +{ + int pos; + + pos = g_scc_log_pos; + g_scc_log[pos].regnum = regnum; + g_scc_log[pos].val = val; + g_scc_log[pos].dcycs = dcycs; + pos++; + if(pos >= LEN_SCC_LOG) { + pos = 0; + } + g_scc_log_pos = pos; +} + +void +show_scc_log(void) +{ + double dcycs; + int regnum; + int pos; + int i; + char* name; + + pos = g_scc_log_pos; + dcycs = g_cur_dcycs; + printf("SCC log pos: %d, cur dcycs:%f\n", pos, dcycs); + for(i = 0; i < LEN_SCC_LOG; i++) { + pos--; + if(pos < 0) { + pos = LEN_SCC_LOG - 1; + } + regnum = g_scc_log[pos].regnum; + if (regnum >> 8) + name = wr_names[regnum & 0xf]; + else + name = rr_names[regnum & 0xf]; + + printf("%d:%d:\tport:%d wr:%d reg: %d (%s)\t\tval:%02x \tat t:%f\n", + i, pos, (regnum >> 4) & 0xf, (regnum >> 8), + (regnum & 0xf), + name, + g_scc_log[pos].val, + g_scc_log[pos].dcycs /*- dcycs*/); + } +} + +word16 scc_read_lad(int port) +{ + // The IIgs provides a "LocalTalk link activity detector (LAD)" through repurposing the + // MegaII mouse interface. Per the IIgs schematic, the MegaII mouse inputs connect via + // the MSEX and MSEY lines to the RX lines of the SCC between the SCC and the line drivers. + // So, if there's activity on the RX lines, the mouse counters increment. The firmware + // uses this for the "carrier sense" part of the CSMA/CA algorithm. Typical firmware usage + // is to (1) reset the mouse counter, (2) wait a bit, and (3) take action if some activity + // is present. The firmware does not appear to use the specific value of the LAD counter; + // rather, the firmware only considers "zero" and "not zero". + // + // Apple engineers invented the term LAD, and there are references to it in Gus. + + if (port != 0 && port != 1) + { + halt_printf("Invalid SCC port.\n"); + return 0; + } + + Scc* scc_ptr = &(scc_stat[port]); + if (g_c041_val & C041_EN_MOUSE) + { + unsigned int temp = scc_ptr->lad; + scc_ptr->lad = 0; + return temp; + } + else + return 0; +} + +word32 +scc_read_reg(int port, double dcycs) +{ + Scc *scc_ptr; + word32 ret; + int regnum; + + scc_ptr = &(scc_stat[port]); + scc_ptr->mode = 0; + regnum = scc_ptr->reg_ptr; + + /* port 0 is channel A, port 1 is channel B */ + switch(regnum) { + case 0: + case 4: + ret = 0x20; + if (scc_ptr->eom) + ret |= 0x40; + if(scc_ptr->dcd) { + ret |= 0x08; + } + ret |= 0x8; /* HACK HACK */ + if(scc_ptr->rx_queue_depth) { + ret |= 0x01; + } + if(scc_ptr->tx_buf_empty) { + ret |= 0x04; + } + if(scc_ptr->br_is_zero) { + ret |= 0x02; + } + //printf("Read scc[%d] stat: %f : %02x\n", port, dcycs, ret); + break; + case 1: + case 5: + /* HACK: residue codes not right */ + ret = 0x07; /* all sent */ + if (scc_ptr->state == 3 && scc_ptr->sdlc_eof) + ret |= 0x80; + break; + case 2: + case 6: + if(port == 0) { + ret = scc_ptr->reg[2]; + } else { + + halt_printf("Read of RR2B...stopping\n"); + ret = 0; +#if 0 + ret = scc_stat[0].reg[2]; + wr9 = scc_stat[0].reg[9]; + for(i = 0; i < 8; i++) { + if(ZZZ){}; + } + if(wr9 & 0x10) { + /* wr9 status high */ + + } +#endif + } + break; + case 3: + case 7: + if(port == 0) { + // The interrupt pending register only exists in channel A. + ret = (scc_stat[0].irq_pending << 3) | scc_stat[1].irq_pending; + } else { + ret = 0; + } + break; + case 8: + ret = scc_read_data(port, dcycs); + break; + case 9: + case 13: + ret = scc_ptr->reg[13]; + break; + case 10: + case 14: + ret = 0; + break; + case 11: + case 15: + ret = scc_ptr->reg[15]; + break; + case 12: + ret = scc_ptr->reg[12]; + break; + default: + halt_printf("Tried reading c03%x with regnum: %d!\n", 8+port, + regnum); + ret = 0; + } + + scc_ptr->reg_ptr = 0; + scc_printf("Read c03%x, rr%d, ret: %02x\n", 8+port, regnum, ret); + //if(regnum != 0 && regnum != 3) { + scc_log(SCC_REGNUM(0,port,regnum), ret, dcycs); + //} + + return ret; +} + +void +scc_write_reg(int port, word32 val, double dcycs) +{ + Scc *scc_ptr; + word32 old_val; + word32 changed_bits; + word32 irq_mask; + int regnum; + int mode; + int tmp1; + + scc_ptr = &(scc_stat[port]); + regnum = scc_ptr->reg_ptr & 0xf; + mode = scc_ptr->mode; + + // The SCC has more internal registers than memory locations mapped into the CPU's address space. + // To access alternate registers, the CPU writes a register selection code to WR0. The next write + // goes to the selected register. WR0 also contains several command and reset codes, and it is + // possible to write command, reset, and register selection in a single WR0 write. + if(mode == 0) { + // WR0 is selected, and this write goes to WR0. + // Extract the register selection code, which determines the next register access in conjunction with the "point high" command. + scc_ptr->reg_ptr = val & 0x07; + if (((val >> 3) & 0x07) == 0x01) + scc_ptr->reg_ptr |= 0x08; + + // But, this write goes to WR0. + regnum = 0; + if (scc_ptr->reg_ptr) + scc_ptr->mode = 1; + } else { + // Some other register is selected, but the next access goes to register 0. + scc_ptr->reg_ptr = 0; + scc_ptr->mode = 0; + } + + if ((regnum != 0) || // accesses to registers other than WR0 + ((regnum == 0) && (val & 0xf8)) || // accesses to WR0 only for selecting a register + ((regnum == 0) && ((val & 0x38) == 0x80)) // access to WR0 with a point high register selection + ) + // To keep the log shorter and easier to read, omit register selection code write to WR0. Log everything else. + scc_log(SCC_REGNUM(1,port,regnum), val, dcycs); + + changed_bits = (scc_ptr->reg[regnum] ^ val) & 0xff; + + /* Set reg reg */ + switch(regnum) { + case 0: /* wr0 */ + tmp1 = (val >> 3) & 0x7; + switch(tmp1) { + case 0x0: /* null code */ + break; + case 0x1: /* point high */ + break; + case 0x2: /* reset ext/status ints */ + /* should clear other ext ints */ + scc_clr_zerocnt_int(port); + break; + case 0x3: /* send abort (sdlc) */ + halt_printf("Wr c03%x to wr0 of %02x, bad cmd cd:%x!\n", + 8+port, val, tmp1); + scc_ptr->eom = 1; + break; + case 0x4: /* enable int on next rx char */ + scc_ptr->did_int_rx_first = 0; + break; + case 0x5: /* reset tx int pending */ + scc_clr_tx_int(port); + break; + case 0x6: /* reset rr1 bits */ + // Per Section 5.2.1 of the SCC User's Manual, issuing an Error Reset when + // a special condition exists (e.g. EOF) while using "interrupt on first RX" + // mode causes loss of the the data with the special condition from the receive + // FIFO. In some cases, the GS relies on this behavior to clear the final CRC + // byte from the RX FIFO. + // + // Based on experimentation, checking for an active first RX interrupt is incorrect. + // System 5 fails to operate correctly with this check. Anyway, skipping this check + // seems to correct operation, but more investigation is necessary. + if ((scc_ptr->sdlc_eof == 1) /*&& (scc_ptr->did_int_rx_first == 1)*/) + { + // Remove and discard one byte (the one causing the current special condition) from the RX FIFO. + int depth = scc_ptr->rx_queue_depth; + if (depth != 0) { + for (int i = 1; i < depth; i++) { + scc_ptr->rx_queue[i - 1] = scc_ptr->rx_queue[i]; + } + scc_ptr->rx_queue_depth = depth - 1; + scc_maybe_rx_event(port, dcycs); + scc_maybe_rx_int(port, dcycs); + } + } + + // Reset emulated error bits. Note that we don't emulate all the bits. + scc_ptr->sdlc_eof = 0; + break; + case 0x7: /* reset highest pri int pending */ + irq_mask = g_irq_pending; + if(port == 0) { + /* Move SCC0 ints into SCC1 positions */ + irq_mask = irq_mask >> 3; + } + if(irq_mask & IRQ_PENDING_SCC1_RX) { + scc_clr_rx_int(port); + scc_ptr->irq_pending &= ~IRQ_PENDING_SCC1_RX; + } else if(irq_mask & IRQ_PENDING_SCC1_TX) { + scc_clr_tx_int(port); + scc_ptr->irq_pending &= ~IRQ_PENDING_SCC1_TX; + } else if(irq_mask & IRQ_PENDING_SCC1_ZEROCNT) { + scc_clr_zerocnt_int(port); + scc_ptr->irq_pending &= ~IRQ_PENDING_SCC1_ZEROCNT; + } + break; + default: + halt_printf("Wr c03%x to wr0 of %02x, bad cmd cd:%x!\n", + 8+port, val, tmp1); + } + tmp1 = (val >> 6) & 0x3; + switch(tmp1) { + case 0x0: /* null code */ + break; + case 0x1: /* reset rx crc */ + // Do nothing. Emulated packets never have CRC errors. + break; + case 0x2: /* reset tx crc */ + // Do nothing. Emulated packets never have CRC errors. + break; + case 0x3: /* reset tx underrun/eom latch */ + /* if no extern status pending, or being reset now */ + /* and tx disabled, ext int with tx underrun */ + /* ah, just do nothing */ + //if (!(scc_ptr->reg[5] & 0x08)) + // First, this command has no effect unless the transmitter is disabled. + //scc_ptr->eom = 0; + break; + } + return; + case 1: /* wr1 */ + /* proterm sets this == 0x10, which is int on all rx */ + scc_ptr->reg[regnum] = val; + return; + case 2: /* wr2 */ + /* All values do nothing, let 'em all through! */ + scc_ptr->reg[regnum] = val; + return; + case 3: /* wr3 */ + if((scc_ptr->state != 3) && ((val & 0x0e) != 0x0)) { + halt_printf("Wr c03%x to wr3 of %02x!\n", 8+port, val); + } + old_val = scc_ptr->reg[regnum]; + scc_ptr->reg[regnum] = val; + + if (!(old_val & 0x01) && (val & 0x01)) + { + // If the receiver transitions from disabled to enabled, try to pull data into the FIFO. + scc_try_fill_readbuf(port, dcycs); + scc_maybe_rx_event(port, dcycs); + } + + return; + case 4: /* wr4 */ + if((val & 0x30) != 0x00 && (val & 0x30) != 0x20) { + halt_printf("Wr c03%x to wr4 of %02x!\n", 8+port, val); + } + + if (((val >> 4) & 0x3) == 0x02 /* SDLC */ && + ((val >> 2) & 0x3) == 0x00 /* enable sync modes */) + { + if (g_appletalk_bridging) + { + // SDLC mode enabled. Redirect such data to the LocalTalk driver. + scc_ptr->state = 3; + scc_llap_init(); + printf("Enabled LocalTalk on port %d.\n", port); + } + else + printf("Attempted to enable LocalTalk on port %d but bridging is disabled.\n", port); + } + + scc_ptr->reg[regnum] = val; + if(changed_bits) { + scc_regen_clocks(port); + } + return; + case 5: /* wr5 */ + if((val & 0x10) != 0x0) { + halt_printf("Wr c03%x to wr5 of %02x!\n", 8+port, val); + } + + // Since we don't emulate the SDLC frame, ignore the CRC polynomial type (bit 2). + // Since the emulated link never has CRC errors, silently accept Tx CRC enable. + if (g_appletalk_bridging && scc_ptr->state == 3) + { + if ((scc_ptr->reg[regnum] & 0x08) && !(val & 0x08)) + { + // When the TX enable changes from enabled to disabled, the GS is about to finish a frame. + // The GS will wait a bit longer for the hardware to finish sending the abort sequence, but + // this is of little concern since we don't have a "real line". + scc_llap_empty_writebuf(port, dcycs); + //scc_ptr->eom = 1; + } + } + + scc_ptr->reg[regnum] = val; + if(changed_bits & 0x60) { + scc_regen_clocks(port); + } + return; + case 6: /* wr6 */ + if (scc_ptr->state == 3) { + // In SDLC mode (state 3), WR6 contains the node ID for hardware address filtering. + printf("Trying LocalTalk node ID %d.\n", val); + scc_llap_set_node(val); + } + else if(val != 0) { + halt_printf("Wr c03%x to wr6 of %02x!\n", 8+port, val); + } + + scc_ptr->reg[regnum] = val; + return; + case 7: /* wr7 */ + if (((scc_ptr->state == 3) && (val != 0x7e)) || (scc_ptr->state != 3)) + // SDLC requires a sync character of 0x7e, per the SDLC spec. + halt_printf("Wr c03%x to wr7 of %02x!\n", 8+port, val); + + scc_ptr->reg[regnum] = val; + return; + case 8: /* wr8 */ + scc_write_data(port, val, dcycs); + return; + case 9: /* wr9 */ + if((val & 0xc0)) { + if(val & 0x80) { + scc_reset_port(0); + } + if(val & 0x40) { + scc_reset_port(1); + } + if((val & 0xc0) == 0xc0) { + scc_hard_reset_port(0); + scc_hard_reset_port(1); + } + } + if((val & 0x35) != 0x00) { + printf("Write c03%x to wr9 of %02x!\n", 8+port, val); + halt_printf("val & 0x35: %02x\n", (val & 0x35)); + } + old_val = scc_stat[0].reg[9]; + scc_stat[0].reg[regnum] = val; + scc_evaluate_ints(0); + scc_evaluate_ints(1); + return; + case 10: /* wr10 */ + if(((val & 0xff) != 0x00) && + ((val & 0xe0) != 0xe0 && scc_ptr->state == 3) /* Allow FM0 */) { + printf("Wr c03%x to wr10 of %02x!\n", 8+port, val); + } + scc_ptr->reg[regnum] = val; + return; + case 11: /* wr11 */ + scc_ptr->reg[regnum] = val; + if(changed_bits) { + scc_regen_clocks(port); + } + return; + case 12: /* wr12 */ + scc_ptr->reg[regnum] = val; + if(changed_bits) { + scc_regen_clocks(port); + } + return; + case 13: /* wr13 */ + scc_ptr->reg[regnum] = val; + if(changed_bits) { + scc_regen_clocks(port); + } + return; + case 14: /* wr14 */ + old_val = scc_ptr->reg[regnum]; + val = val + (old_val & (~0xff)); + switch((val >> 5) & 0x7) { + case 0x0: + // Null command. + case 0x1: + // Enter search mode command. + case 0x2: + // Reset clock missing command + case 0x3: + // Disable PLL command. + break; + + case 0x4: /* DPLL source is BR gen */ + val |= SCC_R14_DPLL_SOURCE_BRG; + break; + + case 0x6: + // Set FM mode. + // + // LocalTalk uses this mode. + // Ignore this command because we don't emulate line conding. + if (scc_ptr->state != 3) + halt_printf("Wr c03%x to wr14 of %02x, FM mode!\n", + 8+port, val); + val |= SCC_R14_FM_MODE; + break; + + case 0x5: + // Set source = /RTxC. + case 0x7: + // Set NRZI mode. + default: + halt_printf("Wr c03%x to wr14 of %02x, bad dpll cd!\n", + 8+port, val); + } + if((val & 0x0c) != 0x0) { + halt_printf("Wr c03%x to wr14 of %02x!\n", 8+port, val); + } + scc_ptr->reg[regnum] = val; + if(changed_bits) { + scc_regen_clocks(port); + } + scc_maybe_br_event(port, dcycs); + return; + case 15: /* wr15 */ + /* ignore all accesses since IIgs self test messes with it */ + if((val & 0xff) != 0x0) { + scc_printf("Write c03%x to wr15 of %02x!\n", 8+port, + val); + } + if((scc_stat[0].reg[9] & 0x8) && (val != 0)) { + printf("Write wr15:%02x and master int en = 1!\n",val); + /* set_halt(1); */ + } + scc_ptr->reg[regnum] = val; + scc_maybe_br_event(port, dcycs); + scc_evaluate_ints(port); + return; + default: + halt_printf("Wr c03%x to wr%d of %02x!\n", 8+port, regnum, val); + return; + } +} + +void +scc_maybe_br_event(int port, double dcycs) +{ + Scc *scc_ptr; + double br_dcycs; + + scc_ptr = &(scc_stat[port]); + + if(((scc_ptr->reg[14] & 0x01) == 0) || scc_ptr->br_event_pending) { + return; + } + /* also, if ext ints not enabled, don't do baud rate ints */ + if((scc_ptr->reg[15] & 0x02) == 0) { + return; + } + + br_dcycs = scc_ptr->br_dcycs; + if(br_dcycs < 1.0) { + halt_printf("br_dcycs: %f!\n", br_dcycs); + } + + scc_ptr->br_event_pending = 1; + add_event_scc(dcycs + br_dcycs, SCC_MAKE_EVENT(port, SCC_BR_EVENT)); +} + +void +scc_evaluate_ints(int port) +{ + Scc *scc_ptr; + word32 irq_add_mask, irq_remove_mask; + int mie; + + scc_ptr = &(scc_stat[port]); + mie = scc_stat[0].reg[9] & 0x8; /* Master int en */ + + // The master interrupt enable (MIE) gates assertion of the interrupt line. + // Even if the MIE is disabled, the interrupt pending bits still reflect + // what interrupt would occur if MIE was enabled. Software could poll the + // pending bits, and AppleTalk does exactly this to detect the start of + // a packet. So, we must always calculate the pending interrupts. + irq_add_mask = 0; + irq_remove_mask = 0; + if(scc_ptr->wantint_rx) { + irq_add_mask |= IRQ_PENDING_SCC1_RX; + } else { + irq_remove_mask |= IRQ_PENDING_SCC1_RX; + } + if(scc_ptr->wantint_tx) { + irq_add_mask |= IRQ_PENDING_SCC1_TX; + } else { + irq_remove_mask |= IRQ_PENDING_SCC1_TX; + } + if(scc_ptr->wantint_zerocnt) { + irq_add_mask |= IRQ_PENDING_SCC1_ZEROCNT; + } else { + irq_remove_mask |= IRQ_PENDING_SCC1_ZEROCNT; + } + scc_stat[port].irq_pending &= ~irq_remove_mask; + scc_stat[port].irq_pending |= irq_add_mask; + + + if(!mie) { + /* There can be no interrupts if MIE=0 */ + remove_irq(IRQ_PENDING_SCC1_RX | IRQ_PENDING_SCC1_TX | + IRQ_PENDING_SCC1_ZEROCNT | + IRQ_PENDING_SCC0_RX | IRQ_PENDING_SCC0_TX | + IRQ_PENDING_SCC0_ZEROCNT); + return; + } + if(port == 0) { + /* Port 1 is in bits 0-2 and port 0 is in bits 3-5 */ + irq_add_mask = irq_add_mask << 3; + irq_remove_mask = irq_remove_mask << 3; + } + if(irq_add_mask) { + add_irq(irq_add_mask); + } + if(irq_remove_mask) { + remove_irq(irq_remove_mask); + } +} + + +void +scc_maybe_rx_event(int port, double dcycs) +{ + Scc *scc_ptr; + double rx_dcycs; + int in_rdptr, in_wrptr; + int depth; + + scc_ptr = &(scc_stat[port]); + + if(scc_ptr->rx_event_pending) { + /* one pending already, wait for the event to arrive */ + return; + } + + if (!(scc_ptr->reg[3] & 0x01)) { + // If the receiver is disabled, don't transfer data into the RX FIFO. + return; + } + + in_rdptr = scc_ptr->in_rdptr; + in_wrptr = scc_ptr->in_wrptr; + depth = scc_ptr->rx_queue_depth; + if((in_rdptr == in_wrptr) || (depth >= 3)) { + /* no more chars or no more space, just get out */ + return; + } + + if(depth < 0) { + depth = 0; + } + + /* pull char from in_rdptr into queue */ + scc_ptr->rx_queue[depth] = scc_ptr->in_buf[in_rdptr]; + scc_ptr->in_rdptr = (in_rdptr + 1) & (SCC_INBUF_SIZE - 1); + scc_ptr->rx_queue_depth = depth + 1; + scc_maybe_rx_int(port, dcycs); + rx_dcycs = scc_ptr->rx_dcycs; + scc_ptr->rx_event_pending = 1; + add_event_scc(dcycs + rx_dcycs, SCC_MAKE_EVENT(port, SCC_RX_EVENT)); +} + +void +scc_maybe_rx_int(int port, double dcycs) +{ + Scc *scc_ptr; + int depth; + int rx_int_mode; + + scc_ptr = &(scc_stat[port]); + + depth = scc_ptr->rx_queue_depth; + if(depth <= 0) { + /* no more chars, just get out */ + scc_clr_rx_int(port); + return; + } + rx_int_mode = (scc_ptr->reg[1] >> 3) & 0x3; + switch (rx_int_mode) + { + case 0: + break; + case 1: /* Rx Int On First Characters or Special Condition */ + // Based on experimentation, there's a delay in SDLC mode before the RX on first interrupt goes active. + // Most likely, this delay is due to the address matching requiring complete reception of the destination address field. + if (!scc_ptr->did_int_rx_first && ((scc_ptr->state != 3) || ((scc_ptr->state == 3) && (depth == 2)))) + { + scc_ptr->did_int_rx_first = 1; + scc_ptr->wantint_rx = 1; + } + break; + case 2: /* Int On All Rx Characters or Special Condition */ + scc_ptr->wantint_rx = 1; + break; + case 3: + halt_printf("Unsupported SCC RX interrupt mode 3 (Rx Int On Special Condition Only)."); + break; + } + scc_evaluate_ints(port); +} + +void +scc_clr_rx_int(int port) +{ + scc_stat[port].wantint_rx = 0; + scc_evaluate_ints(port); +} + +void +scc_handle_tx_event(int port, double dcycs) +{ + Scc *scc_ptr; + int tx_int_mode; + + scc_ptr = &(scc_stat[port]); + + /* nothing pending, see if ints on */ + tx_int_mode = (scc_ptr->reg[1] & 0x2); + if(tx_int_mode) { + scc_ptr->wantint_tx = 1; + } + scc_evaluate_ints(port); +} + +void +scc_maybe_tx_event(int port, double dcycs) +{ + Scc *scc_ptr; + double tx_dcycs; + + scc_ptr = &(scc_stat[port]); + + if(scc_ptr->tx_event_pending) { + /* one pending already, tx_buf is full */ + scc_ptr->tx_buf_empty = 0; + } else { + /* nothing pending, see ints on */ + scc_evaluate_ints(port); + tx_dcycs = scc_ptr->tx_dcycs; + scc_ptr->tx_event_pending = 1; + add_event_scc(dcycs + tx_dcycs, + SCC_MAKE_EVENT(port, SCC_TX_EVENT)); + } +} + +void +scc_clr_tx_int(int port) +{ + scc_stat[port].wantint_tx = 0; + scc_evaluate_ints(port); +} + +void +scc_set_zerocnt_int(int port) +{ + Scc *scc_ptr; + + scc_ptr = &(scc_stat[port]); + + if(scc_ptr->reg[15] & 0x2) { + scc_ptr->wantint_zerocnt = 1; + } + scc_evaluate_ints(port); +} + +void +scc_clr_zerocnt_int(int port) +{ + scc_stat[port].wantint_zerocnt = 0; + scc_evaluate_ints(port); +} + +void +scc_add_to_readbuf(int port, word32 val, double dcycs) +{ + Scc *scc_ptr; + int in_wrptr; + int in_wrptr_next; + int in_rdptr; + + scc_ptr = &(scc_stat[port]); + + in_wrptr = scc_ptr->in_wrptr; + in_rdptr = scc_ptr->in_rdptr; + in_wrptr_next = (in_wrptr + 1) & (SCC_INBUF_SIZE - 1); + if(in_wrptr_next != in_rdptr) { + scc_ptr->in_buf[in_wrptr] = val; + scc_ptr->in_wrptr = in_wrptr_next; + scc_printf("scc in port[%d] add char 0x%02x, %d,%d != %d\n", + scc_ptr->port, val, + in_wrptr, in_wrptr_next, in_rdptr); + g_scc_overflow = 0; + } else { + if(g_scc_overflow == 0) { + g_code_yellow++; + printf("scc inbuf overflow port %d\n", port); + } + g_scc_overflow = 1; + } + + scc_maybe_rx_event(port, dcycs); +} + +void +scc_add_to_readbufv(int port, double dcycs, const char *fmt, ...) +{ + va_list ap; + char *bufptr; + int len, c; + int i; + + va_start(ap, fmt); + bufptr = (char*)malloc(4096); // OG cast added + bufptr[0] = 0; + vsnprintf(bufptr, 4090, fmt, ap); + len = strlen(bufptr); + for(i = 0; i < len; i++) { + c = bufptr[i]; + if(c == 0x0a) { + scc_add_to_readbuf(port, 0x0d, dcycs); + } + scc_add_to_readbuf(port, c, dcycs); + } + va_end(ap); +} + +void +scc_transmit(int port, word32 val, double dcycs) +{ + Scc *scc_ptr; + int out_wrptr; + int out_rdptr; + + scc_ptr = &(scc_stat[port]); + + /* See if port initialized, if not, do so now */ + if(scc_ptr->state == 0) { + scc_port_init(port); + } + if(scc_ptr->state < 0) { + /* No working serial port, just toss it and go */ + return; + } + + if(!scc_ptr->tx_buf_empty) { + /* toss character! */ + printf("Tossing char\n"); + return; + } + + out_wrptr = scc_ptr->out_wrptr; + out_rdptr = scc_ptr->out_rdptr; + if(scc_ptr->tx_dcycs < 1.0) { + if(out_wrptr != out_rdptr) { + /* do just one char, then get out */ + printf("tx_dcycs < 1\n"); + return; + } + } + if(g_serial_out_masking && + (scc_ptr->state != 3 /* never mask LLAP data */)) { + val = val & 0x7f; + } + + scc_add_to_writebuf(port, val, dcycs); +} + +void +scc_add_to_writebuf(int port, word32 val, double dcycs) +{ + Scc *scc_ptr; + int out_wrptr; + int out_wrptr_next; + int out_rdptr; + + scc_ptr = &(scc_stat[port]); + + /* See if port initialized, if not, do so now */ + if(scc_ptr->state == 0) { + scc_port_init(port); + } + if(scc_ptr->state < 0) { + /* No working serial port, just toss it and go */ + return; + } + + out_wrptr = scc_ptr->out_wrptr; + out_rdptr = scc_ptr->out_rdptr; + + out_wrptr_next = (out_wrptr + 1) & (SCC_OUTBUF_SIZE - 1); + if(out_wrptr_next != out_rdptr) { + scc_ptr->out_buf[out_wrptr] = val; + scc_ptr->out_wrptr = out_wrptr_next; + scc_printf("scc wrbuf port %d had char 0x%02x added\n", + scc_ptr->port, val); + g_scc_overflow = 0; + } else { + if(g_scc_overflow == 0) { + g_code_yellow++; + printf("scc outbuf overflow port %d\n", port); + } + g_scc_overflow = 1; + } +} + +word32 +scc_read_data(int port, double dcycs) +{ + Scc *scc_ptr; + word32 ret; + int depth; + int i; + + scc_ptr = &(scc_stat[port]); + + scc_try_fill_readbuf(port, dcycs); + + depth = scc_ptr->rx_queue_depth; + + ret = 0; + if(depth != 0) { + ret = scc_ptr->rx_queue[0]; + for(i = 1; i < depth; i++) { + scc_ptr->rx_queue[i-1] = scc_ptr->rx_queue[i]; + } + scc_ptr->rx_queue_depth = depth - 1; + scc_maybe_rx_event(port, dcycs); + scc_maybe_rx_int(port, dcycs); + + int buffered_rx = scc_ptr->in_wrptr - scc_ptr->in_rdptr; + if(buffered_rx < 0) { + buffered_rx += SCC_INBUF_SIZE; + } + + int bytes_left = buffered_rx + scc_ptr->rx_queue_depth; + if (scc_ptr->state == 3 /* SDLC mode */ && bytes_left == 1) + { + // Flag an end of frame. + scc_ptr->sdlc_eof = 1; + } + + //printf("SCC read %04x: ret %02x, depth:%d, buffered: %d\n", 0xc03b - port, ret, scc_ptr->rx_queue_depth, buffered_rx); + } + + scc_printf("SCC read %04x: ret %02x, depth:%d\n", 0xc03b-port, ret, depth); + scc_log(SCC_REGNUM(0,port,8), ret, dcycs); + + return ret; +} + + +void +scc_write_data(int port, word32 val, double dcycs) +{ + Scc *scc_ptr; + + scc_printf("SCC write %04x: %02x\n", 0xc03b-port, val); + scc_log(SCC_REGNUM(1,port,8), val, dcycs); + + scc_ptr = &(scc_stat[port]); + if(scc_ptr->reg[14] & 0x10) { + /* local loopback! */ + scc_add_to_readbuf(port, val, dcycs); + } else { + scc_transmit(port, val, dcycs); + } + if (scc_ptr->state != 3) { + // If we're doing LLAP, empty the writebuf at the end of the packet. + // Otherwise, empty as soon as possible. + scc_try_to_empty_writebuf(port, dcycs); + } + + scc_maybe_tx_event(port, dcycs); } \ No newline at end of file diff --git a/src/scc.h b/src/scc.h index 39db3fb..fb32af8 100644 --- a/src/scc.h +++ b/src/scc.h @@ -36,8 +36,9 @@ /* my scc port 0 == channel A, port 1 = channel B */ -#define SCC_INBUF_SIZE 512 /* must be a power of 2 */ -#define SCC_OUTBUF_SIZE 512 /* must be a power of 2 */ +// LLAP may have packets up to 603 bytes, and the buffers must be large enough to contain a single packet. +#define SCC_INBUF_SIZE 1024 /* must be a power of 2 */ +#define SCC_OUTBUF_SIZE 1024 /* must be a power of 2 */ #define SCC_MODEM_MAX_CMD_STR 128 @@ -47,7 +48,7 @@ STRUCT(Scc) { int port; - int state; + int state /* 0 == disconnected, 1 == real serial port, 2 == socket, 3 == LocalTalk */; int accfd; SOCKET sockfd; int socket_state; @@ -64,6 +65,7 @@ STRUCT(Scc) { int rx_queue_depth; byte rx_queue[4]; + unsigned int lad; int in_rdptr; int in_wrptr; @@ -78,7 +80,10 @@ STRUCT(Scc) { int wantint_rx; int wantint_tx; int wantint_zerocnt; + int did_int_rx_first; int dcd; + int sdlc_eof; + int eom; double br_dcycs; double tx_dcycs; @@ -87,6 +92,7 @@ STRUCT(Scc) { int br_event_pending; int rx_event_pending; int tx_event_pending; + byte irq_pending; int char_size; int baud_rate; diff --git a/src/scc_llap.c b/src/scc_llap.c new file mode 100644 index 0000000..91ced9b --- /dev/null +++ b/src/scc_llap.c @@ -0,0 +1,156 @@ +/* + GSport - an Apple //gs Emulator + Copyright (C) 2013 - 2014 by GSport contributors + Originally authored by Peter Neubauer + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This is an interface between the SCC emulation and the LAP bridge. */ + +#include +#include "defc.h" +#include "scc.h" + +int g_appletalk_bridging; +int g_appletalk_turbo = 0; +int g_appletalk_diagnostics = 0; +int g_appletalk_network_hint = 0; + +#ifdef HAVE_ATBRIDGE +#include "atbridge/atbridge.h" +#include "atbridge/llap.h" + +extern Scc scc_stat[2]; + +extern int g_config_gsport_update_needed; +static bool bridge_initialized = false; + +void scc_llap_init() +{ + bridge_initialized = atbridge_init(); + atbridge_set_net(g_appletalk_network_hint); +} + +void scc_llap_set_node(byte val) +{ + atbridge_set_node(val); +} + +void scc_llap_update() +{ + if (bridge_initialized) + { + atbridge_process(); + + if (g_appletalk_network_hint != atbridge_get_net()) + { + g_appletalk_network_hint = atbridge_get_net(); + g_config_gsport_update_needed = 1; + } + } +} + +/** Transfer one packet from the bridge to the SCC **/ +void scc_llap_fill_readbuf(int port, int space_left, double dcycs) +{ + atbridge_set_diagnostics(g_appletalk_diagnostics); + + byte* data; + + if (!bridge_initialized) + return; + + data = NULL; + size_t bytes_read; + size_t i; + + llap_dequeue_out(dcycs, &bytes_read, &data); + + for(i = 0; i < bytes_read; i++) { + scc_add_to_readbuf(port, data[i], dcycs); + } + + free(data); +} + +/** Transfer one packet from the SCC to the AppleTalk bridge. **/ +void scc_llap_empty_writebuf(int port, double dcycs) +{ + atbridge_set_diagnostics(g_appletalk_diagnostics); + + Scc* scc_ptr; + + if (!bridge_initialized) + return; + + int rdptr; + int wrptr; + int len; + + scc_ptr = &(scc_stat[port]); + + // If there's data in the output buffer, send it. + rdptr = scc_ptr->out_rdptr; + wrptr = scc_ptr->out_wrptr; + if(rdptr == wrptr) + return; + + len = wrptr - rdptr; + if (len < 0) + { + // The data is not contiguous since it wraps around the end of the buffer. + // But, this should never happen since this function always empties the entire + // buffer, and the buffer is large enough to hold the maximum packet size. + halt_printf("SCC LLAP: Unexpected non-contiguous data. Dropping packet.\n"); + } + else + { + // The data is contiguous, so read the data directly from the buffer. + llap_enqueue_in(dcycs, len, &scc_ptr->out_buf[rdptr]); + } + + // Remove the sent data from the output buffer. Since the buffer contains + // one complete packet, always send all of the data. + scc_ptr->out_rdptr = 0; + scc_ptr->out_wrptr = 0; + + // Latch EOM to indicate that the SCC has sent the message. + // The timing will be a bit off from the real hardware since we're not + // emulating the sending hardware timing and CRC generation. + scc_ptr->eom = 1; +} + +#else +void scc_llap_init() +{ +} + +void scc_llap_set_node(byte val) +{ +} + +void scc_llap_update() +{ +} + +void scc_llap_fill_readbuf(int port, int space_left, double dcycs) +{ +} + +void scc_llap_empty_writebuf(int port, double dcycs) +{ +} +#endif \ No newline at end of file diff --git a/src/scc_llap.h b/src/scc_llap.h new file mode 100644 index 0000000..63cc700 --- /dev/null +++ b/src/scc_llap.h @@ -0,0 +1,26 @@ +/* +GSport - an Apple //gs Emulator +Copyright (C) 2013 by GSport contributors +Originally authored by Peter Neubauer + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +void scc_llap_init(); +void scc_llap_shutdown(); +void scc_llap_update(); +void scc_llap_fill_readbuf(int port, int space_left, double dcycs); +void scc_llap_empty_writebuf(int port, double dcycs); +void scc_llap_set_node(byte val); \ No newline at end of file diff --git a/src/scc_socket_driver.c b/src/scc_socket_driver.c index 755a7e5..96dba63 100644 --- a/src/scc_socket_driver.c +++ b/src/scc_socket_driver.c @@ -25,7 +25,10 @@ #include "scc.h" #ifndef UNDER_CE //OG #include -#endif +#endif +#ifdef __CYGWIN__ +#include +#endif extern Scc scc_stat[2]; extern int g_serial_modem[]; diff --git a/src/scc_windriver.c b/src/scc_windriver.c index a88ce59..5f84ad9 100644 --- a/src/scc_windriver.c +++ b/src/scc_windriver.c @@ -24,6 +24,11 @@ #include "defc.h" #include "scc.h" +#ifdef __CYGWIN__ +#include +#include +#endif + #ifdef UNDER_CE #define vsnprintf _vsnprintf #endif diff --git a/src/sim65816.c b/src/sim65816.c index 2667c71..e53aa74 100644 --- a/src/sim65816.c +++ b/src/sim65816.c @@ -1,151 +1,150 @@ -/* - GSport - an Apple //gs Emulator - Copyright (C) 2010 - 2013 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 - -#include "defc.h" -#ifdef HAVE_TFE - #include "tfe/tfesupp.h" - #include "tfe/protos_tfe.h" -#endif - #include "printer.h" +/* + GSport - an Apple //gs Emulator + Copyright (C) 2010 - 2013 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 + +#include "defc.h" +#ifdef HAVE_TFE + #include "tfe/tfesupp.h" + #include "tfe/protos_tfe.h" +#endif + #include "printer.h" #include "imagewriter.h" - -#ifdef UNDER_CE -#define vsnprintf _vsnprintf -#endif - -#if defined(__CYGWIN__) -#define WIN32_LEAN_AND_MEAN /* Tell windows we want less header gunk */ -#define STRICT /* Tell Windows we want compile type checks */ -#include /* Need a definition for LPTSTR in CYGWIN */ -#endif -#if defined (_WIN32) || defined(__CYGWIN__) -extern void get_cwd(LPTSTR buffer, int size); -#endif - -#define PC_LOG_LEN (8*1024) - + +#ifdef UNDER_CE +#define vsnprintf _vsnprintf +#endif + +#if defined (_WIN32) || defined(__CYGWIN__) +#define WIN32_LEAN_AND_MEAN /* Tell windows we want less header gunk */ +#define STRICT /* Tell Windows we want compile type checks */ +#include /* Need a definition for LPTSTR in CYGWIN */ + +extern void get_cwd(LPTSTR buffer, int size); +#endif + +#define PC_LOG_LEN (8*1024) + int g_speed_fast ; // OG Expose fast parameter int g_initialized = 0; // OG To know if the emulator has finalized its initialization int g_accept_events = 0; // OG To know if the emulator is ready to accept external events - -char g_argv0_path[256] = "./"; - -const char *g_gsport_default_paths[] = { "", "./", "${HOME}/","${PWD}/", -#ifdef MAC - "${0}/../", -#endif - "${HOME}/Library/GSport/", - "${0}/Contents/Resources/", "/usr/local/lib/", - "/usr/local/gsport/", "/usr/local/lib/gsport/", "/usr/share/gsport/", - "/var/lib/", "/usr/lib/gsport/", "${0}/", 0 }; - -#define MAX_EVENTS 64 - -/* All EV_* must be less than 256, since upper bits reserved for other use */ -/* e.g., DOC_INT uses upper bits to encode oscillator */ -#define EV_60HZ 1 -#define EV_STOP 2 -#define EV_SCAN_INT 3 -#define EV_DOC_INT 4 -#define EV_VBL_INT 5 -#define EV_SCC 6 -#define EV_VID_UPD 7 - -extern int g_stepping; - -extern int g_c068_statereg; -extern int g_cur_a2_stat; - -extern int g_c08x_wrdefram; -extern int g_c02d_int_crom; - -extern int g_c035_shadow_reg; -extern int g_c036_val_speed; - -extern int g_c023_val; -extern int g_c041_val; -extern int g_c046_val; -extern int g_zipgs_reg_c059; -extern int g_zipgs_reg_c05a; -extern int g_zipgs_reg_c05b; -extern int g_zipgs_unlock; - -extern int g_engine_c_mode; -extern int defs_instr_start_8; -extern int defs_instr_start_16; -extern int defs_instr_end_8; -extern int defs_instr_end_16; -extern int op_routs_start; -extern int op_routs_end; - -Engine_reg engine; -extern word32 table8[]; -extern word32 table16[]; - -extern byte doc_ram[]; - -extern int g_iwm_motor_on; -extern int g_fast_disk_emul; -extern int g_slow_525_emul_wr; -extern int g_c031_disk35; -extern int g_config_control_panel; - -extern int g_audio_enable; -extern int g_preferred_rate; - -void U_STACK_TRACE(); - -double g_fcycles_stop = 0.0; -int halt_sim = 0; -int enter_debug = 0; -int g_rom_version = -1; -int g_user_halt_bad = 0; -int g_halt_on_bad_read = 0; -int g_ignore_bad_acc = 1; -int g_ignore_halts = 1; -int g_code_red = 0; -int g_code_yellow = 0; -int g_use_alib = 0; -int g_serial_type[2]; -int g_iw2_emul = 0; -int g_serial_out_masking = 0; -int g_serial_modem[2] = { 0, 1 }; -int g_ethernet = 0; -int g_ethernet_interface = 0; -int g_parallel = 0; -int g_parallel_out_masking = 0; -int g_printer = 0; -int g_printer_dpi = 360; -char* g_printer_output = "bmp"; -int g_printer_multipage = 0; -int g_printer_timeout = 2; -char* g_printer_font_roman = "lib/letgothl.ttf"; -char* g_printer_font_sans = "sansserif.ttf"; -char* g_printer_font_courier = "courier.ttf"; -char* g_printer_font_prestige = "prestige.ttf"; -char* g_printer_font_script = "script.ttf"; -char* g_printer_font_ocra = "ocra.ttf"; - + +char g_argv0_path[256] = "./"; + +const char *g_gsport_default_paths[] = { "", "./", "${HOME}/","${PWD}/", +#ifdef MAC + "${0}/../", +#endif + "${HOME}/Library/GSport/", + "${0}/Contents/Resources/", "/usr/local/lib/", + "/usr/local/gsport/", "/usr/local/lib/gsport/", "/usr/share/gsport/", + "/var/lib/", "/usr/lib/gsport/", "${0}/", 0 }; + +#define MAX_EVENTS 64 + +/* All EV_* must be less than 256, since upper bits reserved for other use */ +/* e.g., DOC_INT uses upper bits to encode oscillator */ +#define EV_60HZ 1 +#define EV_STOP 2 +#define EV_SCAN_INT 3 +#define EV_DOC_INT 4 +#define EV_VBL_INT 5 +#define EV_SCC 6 +#define EV_VID_UPD 7 + +extern int g_stepping; + +extern int g_c068_statereg; +extern int g_cur_a2_stat; + +extern int g_c08x_wrdefram; +extern int g_c02d_int_crom; + +extern int g_c035_shadow_reg; +extern int g_c036_val_speed; + +extern int g_c023_val; +extern int g_c041_val; +extern int g_c046_val; +extern int g_zipgs_reg_c059; +extern int g_zipgs_reg_c05a; +extern int g_zipgs_reg_c05b; +extern int g_zipgs_unlock; + +extern int g_engine_c_mode; +extern int defs_instr_start_8; +extern int defs_instr_start_16; +extern int defs_instr_end_8; +extern int defs_instr_end_16; +extern int op_routs_start; +extern int op_routs_end; + +Engine_reg engine; +extern word32 table8[]; +extern word32 table16[]; + +extern byte doc_ram[]; + +extern int g_iwm_motor_on; +extern int g_fast_disk_emul; +extern int g_slow_525_emul_wr; +extern int g_c031_disk35; +extern int g_config_control_panel; + +extern int g_audio_enable; +extern int g_preferred_rate; + +void U_STACK_TRACE(); + +double g_fcycles_stop = 0.0; +int halt_sim = 0; +int enter_debug = 0; +int g_rom_version = -1; +int g_user_halt_bad = 0; +int g_halt_on_bad_read = 0; +int g_ignore_bad_acc = 1; +int g_ignore_halts = 1; +int g_code_red = 0; +int g_code_yellow = 0; +int g_use_alib = 0; +int g_serial_type[2]; +int g_iw2_emul = 0; +int g_serial_out_masking = 0; +int g_serial_modem[2] = { 0, 1 }; +int g_ethernet = 0; +int g_ethernet_interface = 0; +int g_parallel = 0; +int g_parallel_out_masking = 0; +int g_printer = 0; +int g_printer_dpi = 360; +char* g_printer_output = "bmp"; +int g_printer_multipage = 0; +int g_printer_timeout = 2; +char* g_printer_font_roman = "lib/letgothl.ttf"; +char* g_printer_font_sans = "sansserif.ttf"; +char* g_printer_font_courier = "courier.ttf"; +char* g_printer_font_prestige = "prestige.ttf"; +char* g_printer_font_script = "script.ttf"; +char* g_printer_font_ocra = "ocra.ttf"; + int g_imagewriter = 0; int g_imagewriter_dpi = 360; char* g_imagewriter_output = "bmp"; @@ -154,65 +153,65 @@ int g_imagewriter_timeout = 2; char* g_imagewriter_fixed_font = "roman.ttf"; char* g_imagewriter_prop_font = "roman.ttf"; -int g_config_iwm_vbl_count = 0; -extern const char g_gsport_version_str[] = "0.2"; -int g_pause=0; // OG Added pause - -#define START_DCYCS (0.0) - -double g_last_vbl_dcycs = START_DCYCS; -double g_cur_dcycs = START_DCYCS; - -double g_last_vbl_dadjcycs = 0.0; -double g_dadjcycs = 0.0; - - -int g_wait_pending = 0; -int g_stp_pending = 0; -extern int g_irq_pending; - -int g_num_irq = 0; -int g_num_brk = 0; -int g_num_cop = 0; -int g_num_enter_engine = 0; -int g_io_amt = 0; -int g_engine_action = 0; -int g_engine_halt_event = 0; -int g_engine_scan_int = 0; -int g_engine_doc_int = 0; - -int g_testing = 0; -int g_testing_enabled = 0; - -#define MAX_FATAL_LOGS 20 - -int g_debug_file_fd = -1; -int g_fatal_log = -1; -char *g_fatal_log_strs[MAX_FATAL_LOGS]; - -word32 stop_run_at; - -int g_25sec_cntr = 0; -int g_1sec_cntr = 0; - -double g_dnatcycs_1sec = 0.0; -word32 g_natcycs_lastvbl = 0; - -int Verbose = 0; -int Halt_on = 0; - -word32 g_mem_size_base = 256*1024; /* size of motherboard memory */ -word32 g_mem_size_exp = 8*1024*1024; /* size of expansion RAM card */ -word32 g_mem_size_total = 256*1024; /* Total contiguous RAM from 0 */ - -extern word32 slow_mem_changed[]; - -byte *g_slow_memory_ptr = 0; -byte *g_memory_ptr = 0; -byte *g_dummy_memory1_ptr = 0; -byte *g_rom_fc_ff_ptr = 0; -byte *g_rom_cards_ptr = 0; - +int g_config_iwm_vbl_count = 0; +extern const char g_gsport_version_str[] = "0.2"; +int g_pause=0; // OG Added pause + +#define START_DCYCS (0.0) + +double g_last_vbl_dcycs = START_DCYCS; +double g_cur_dcycs = START_DCYCS; + +double g_last_vbl_dadjcycs = 0.0; +double g_dadjcycs = 0.0; + + +int g_wait_pending = 0; +int g_stp_pending = 0; +extern int g_irq_pending; + +int g_num_irq = 0; +int g_num_brk = 0; +int g_num_cop = 0; +int g_num_enter_engine = 0; +int g_io_amt = 0; +int g_engine_action = 0; +int g_engine_halt_event = 0; +int g_engine_scan_int = 0; +int g_engine_doc_int = 0; + +int g_testing = 0; +int g_testing_enabled = 0; + +#define MAX_FATAL_LOGS 20 + +int g_debug_file_fd = -1; +int g_fatal_log = -1; +char *g_fatal_log_strs[MAX_FATAL_LOGS]; + +word32 stop_run_at; + +int g_25sec_cntr = 0; +int g_1sec_cntr = 0; + +double g_dnatcycs_1sec = 0.0; +word32 g_natcycs_lastvbl = 0; + +int Verbose = 0; +int Halt_on = 0; + +word32 g_mem_size_base = 256*1024; /* size of motherboard memory */ +word32 g_mem_size_exp = 8*1024*1024; /* size of expansion RAM card */ +word32 g_mem_size_total = 256*1024; /* Total contiguous RAM from 0 */ + +extern word32 slow_mem_changed[]; + +byte *g_slow_memory_ptr = 0; +byte *g_memory_ptr = 0; +byte *g_dummy_memory1_ptr = 0; +byte *g_rom_fc_ff_ptr = 0; +byte *g_rom_cards_ptr = 0; + // OG Added allocated pointers byte *g_slow_memory_ptr_allocated = 0; byte *g_memory_ptr_allocated = 0; @@ -220,25 +219,25 @@ byte *g_dummy_memory1_ptr_allocated = 0; byte *g_rom_fc_ff_ptr_allocated = 0; byte *g_rom_cards_ptr_allocated = 0; -void *g_memory_alloc_ptr = 0; /* for freeing memory area */ - -Page_info page_info_rd_wr[2*65536 + PAGE_INFO_PAD_SIZE]; - -Pc_log g_pc_log_array[PC_LOG_LEN + 2]; -Data_log g_data_log_array[PC_LOG_LEN + 2]; - -Pc_log *g_log_pc_ptr = &(g_pc_log_array[0]); -Pc_log *g_log_pc_start_ptr = &(g_pc_log_array[0]); -Pc_log *g_log_pc_end_ptr = &(g_pc_log_array[PC_LOG_LEN]); - -Data_log *g_log_data_ptr = &(g_data_log_array[0]); -Data_log *g_log_data_start_ptr = &(g_data_log_array[0]); -Data_log *g_log_data_end_ptr = &(g_data_log_array[PC_LOG_LEN]); - +void *g_memory_alloc_ptr = 0; /* for freeing memory area */ + +Page_info page_info_rd_wr[2*65536 + PAGE_INFO_PAD_SIZE]; + +Pc_log g_pc_log_array[PC_LOG_LEN + 2]; +Data_log g_data_log_array[PC_LOG_LEN + 2]; + +Pc_log *g_log_pc_ptr = &(g_pc_log_array[0]); +Pc_log *g_log_pc_start_ptr = &(g_pc_log_array[0]); +Pc_log *g_log_pc_end_ptr = &(g_pc_log_array[PC_LOG_LEN]); + +Data_log *g_log_data_ptr = &(g_data_log_array[0]); +Data_log *g_log_data_start_ptr = &(g_data_log_array[0]); +Data_log *g_log_data_end_ptr = &(g_data_log_array[PC_LOG_LEN]); + // OG Added sim65816_initglobals() void sim65816_initglobals() { - + g_fcycles_stop = 0.0; halt_sim = 0; enter_debug = 0; @@ -298,429 +297,429 @@ void sim65816_initglobals() g_mem_size_total = 256*1024; /* Total contiguous RAM from 0 */ } -void -show_pc_log() -{ - FILE *pcfile; - Pc_log *log_pc_ptr; - Data_log *log_data_ptr; - double dcycs; - double start_dcycs; - word32 instr; - word32 psr; - word32 acc, xreg, yreg; - word32 stack, direct; - word32 dbank; - word32 kpc; - int data_wrap; - int accsize, xsize; - int num; - int i; - - pcfile = fopen("pc_log_out", "w"); - if(pcfile == 0) { - fprintf(stderr,"fopen failed...errno: %d\n", errno); - exit(2); - } - - log_pc_ptr = g_log_pc_ptr; - log_data_ptr = g_log_data_ptr; -#if 0 - fprintf(pcfile, "current pc_log_ptr: %p, start: %p, end: %p\n", - log_pc_ptr, log_pc_start_ptr, log_pc_end_ptr); -#endif - - start_dcycs = log_pc_ptr->dcycs; - dcycs = start_dcycs; - - data_wrap = 0; - /* find first data entry */ - while(data_wrap < 2 && (log_data_ptr->dcycs < dcycs)) { - log_data_ptr++; - if(log_data_ptr >= g_log_data_end_ptr) { - log_data_ptr = g_log_data_start_ptr; - data_wrap++; - } - } - fprintf(pcfile, "start_dcycs: %9.2f\n", start_dcycs); - - for(i = 0; i < PC_LOG_LEN; i++) { - dcycs = log_pc_ptr->dcycs; - while((data_wrap < 2) && (log_data_ptr->dcycs <= dcycs) && - (log_data_ptr->dcycs >= start_dcycs)) { - fprintf(pcfile, "DATA set %06x = %06x (%d) %9.2f\n", - log_data_ptr->addr, log_data_ptr->val, - log_data_ptr->size, - log_data_ptr->dcycs - start_dcycs); - log_data_ptr++; - if(log_data_ptr >= g_log_data_end_ptr) { - log_data_ptr = g_log_data_start_ptr; - data_wrap++; - } - } - dbank = (log_pc_ptr->dbank_kpc >> 24) & 0xff; - kpc = log_pc_ptr->dbank_kpc & 0xffffff; - instr = log_pc_ptr->instr; - psr = (log_pc_ptr->psr_acc >> 16) & 0xffff;; - acc = log_pc_ptr->psr_acc & 0xffff;; - xreg = (log_pc_ptr->xreg_yreg >> 16) & 0xffff;; - yreg = log_pc_ptr->xreg_yreg & 0xffff;; - stack = (log_pc_ptr->stack_direct >> 16) & 0xffff;; - direct = log_pc_ptr->stack_direct & 0xffff;; - - num = log_pc_ptr - g_log_pc_start_ptr; - - accsize = 2; - xsize = 2; - if(psr & 0x20) { - accsize = 1; - } - if(psr & 0x10) { - xsize = 1; - } - - fprintf(pcfile, "%04x: A:%04x X:%04x Y:%04x P:%03x " - "S:%04x D:%04x B:%02x %9.2f ", i, - acc, xreg, yreg, psr, stack, direct, dbank, - (dcycs-start_dcycs)); - - do_dis(pcfile, kpc, accsize, xsize, 1, instr); - log_pc_ptr++; - if(log_pc_ptr >= g_log_pc_end_ptr) { - log_pc_ptr = g_log_pc_start_ptr; - } - } - - fclose(pcfile); -} - - -#define TOOLBOX_LOG_LEN 64 - -int g_toolbox_log_pos = 0; -word32 g_toolbox_log_array[TOOLBOX_LOG_LEN][8]; - -word32 -toolbox_debug_4byte(word32 addr) -{ - word32 part1, part2; - - /* If addr looks safe, use it */ - if(addr > 0xbffc) { - return (word32)-1; - } - - part1 = get_memory16_c(addr, 0); - part1 = (part1 >> 8) + ((part1 & 0xff) << 8); - part2 = get_memory16_c(addr+2, 0); - part2 = (part2 >> 8) + ((part2 & 0xff) << 8); - - return (part1 << 16) + part2; -} - -void -toolbox_debug_c(word32 xreg, word32 stack, double *cyc_ptr) -{ - int pos; - - pos = g_toolbox_log_pos; - - stack += 9; +void +show_pc_log() +{ + FILE *pcfile; + Pc_log *log_pc_ptr; + Data_log *log_data_ptr; + double dcycs; + double start_dcycs; + word32 instr; + word32 psr; + word32 acc, xreg, yreg; + word32 stack, direct; + word32 dbank; + word32 kpc; + int data_wrap; + int accsize, xsize; + int num; + int i; + + pcfile = fopen("pc_log_out", "w"); + if(pcfile == 0) { + fprintf(stderr,"fopen failed...errno: %d\n", errno); + exit(2); + } + + log_pc_ptr = g_log_pc_ptr; + log_data_ptr = g_log_data_ptr; +#if 0 + fprintf(pcfile, "current pc_log_ptr: %p, start: %p, end: %p\n", + log_pc_ptr, log_pc_start_ptr, log_pc_end_ptr); +#endif + + start_dcycs = log_pc_ptr->dcycs; + dcycs = start_dcycs; + + data_wrap = 0; + /* find first data entry */ + while(data_wrap < 2 && (log_data_ptr->dcycs < dcycs)) { + log_data_ptr++; + if(log_data_ptr >= g_log_data_end_ptr) { + log_data_ptr = g_log_data_start_ptr; + data_wrap++; + } + } + fprintf(pcfile, "start_dcycs: %9.2f\n", start_dcycs); + + for(i = 0; i < PC_LOG_LEN; i++) { + dcycs = log_pc_ptr->dcycs; + while((data_wrap < 2) && (log_data_ptr->dcycs <= dcycs) && + (log_data_ptr->dcycs >= start_dcycs)) { + fprintf(pcfile, "DATA set %06x = %06x (%d) %9.2f\n", + log_data_ptr->addr, log_data_ptr->val, + log_data_ptr->size, + log_data_ptr->dcycs - start_dcycs); + log_data_ptr++; + if(log_data_ptr >= g_log_data_end_ptr) { + log_data_ptr = g_log_data_start_ptr; + data_wrap++; + } + } + dbank = (log_pc_ptr->dbank_kpc >> 24) & 0xff; + kpc = log_pc_ptr->dbank_kpc & 0xffffff; + instr = log_pc_ptr->instr; + psr = (log_pc_ptr->psr_acc >> 16) & 0xffff;; + acc = log_pc_ptr->psr_acc & 0xffff;; + xreg = (log_pc_ptr->xreg_yreg >> 16) & 0xffff;; + yreg = log_pc_ptr->xreg_yreg & 0xffff;; + stack = (log_pc_ptr->stack_direct >> 16) & 0xffff;; + direct = log_pc_ptr->stack_direct & 0xffff;; + + num = log_pc_ptr - g_log_pc_start_ptr; + + accsize = 2; + xsize = 2; + if(psr & 0x20) { + accsize = 1; + } + if(psr & 0x10) { + xsize = 1; + } + + fprintf(pcfile, "%04x: A:%04x X:%04x Y:%04x P:%03x " + "S:%04x D:%04x B:%02x %9.2f ", i, + acc, xreg, yreg, psr, stack, direct, dbank, + (dcycs-start_dcycs)); + + do_dis(pcfile, kpc, accsize, xsize, 1, instr); + log_pc_ptr++; + if(log_pc_ptr >= g_log_pc_end_ptr) { + log_pc_ptr = g_log_pc_start_ptr; + } + } + + fclose(pcfile); +} + + +#define TOOLBOX_LOG_LEN 64 + +int g_toolbox_log_pos = 0; +word32 g_toolbox_log_array[TOOLBOX_LOG_LEN][8]; + +word32 +toolbox_debug_4byte(word32 addr) +{ + word32 part1, part2; + + /* If addr looks safe, use it */ + if(addr > 0xbffc) { + return (word32)-1; + } + + part1 = get_memory16_c(addr, 0); + part1 = (part1 >> 8) + ((part1 & 0xff) << 8); + part2 = get_memory16_c(addr+2, 0); + part2 = (part2 >> 8) + ((part2 & 0xff) << 8); + + return (part1 << 16) + part2; +} + +void +toolbox_debug_c(word32 xreg, word32 stack, double *cyc_ptr) +{ + int pos; + + pos = g_toolbox_log_pos; + + stack += 9; g_toolbox_log_array[pos][0] = (word32)(g_last_vbl_dcycs + *cyc_ptr); - g_toolbox_log_array[pos][1] = stack+1; - g_toolbox_log_array[pos][2] = xreg; - g_toolbox_log_array[pos][3] = toolbox_debug_4byte(stack+1); - g_toolbox_log_array[pos][4] = toolbox_debug_4byte(stack+5); - g_toolbox_log_array[pos][5] = toolbox_debug_4byte(stack+9); - g_toolbox_log_array[pos][6] = toolbox_debug_4byte(stack+13); - g_toolbox_log_array[pos][7] = toolbox_debug_4byte(stack+17); - - pos++; - if(pos >= TOOLBOX_LOG_LEN) { - pos = 0; - } - - g_toolbox_log_pos = pos; -} - -void -show_toolbox_log() -{ - int pos; - int i; - - pos = g_toolbox_log_pos; - - for(i = TOOLBOX_LOG_LEN - 1; i >= 0; i--) { - printf("%2d:%2d: %08x %06x %04x: %08x %08x %08x %08x %08x\n", - i, pos, - g_toolbox_log_array[pos][0], - g_toolbox_log_array[pos][1], - g_toolbox_log_array[pos][2], - g_toolbox_log_array[pos][3], - g_toolbox_log_array[pos][4], - g_toolbox_log_array[pos][5], - g_toolbox_log_array[pos][6], - g_toolbox_log_array[pos][7]); - pos++; - if(pos >= TOOLBOX_LOG_LEN) { - pos = 0; - } - } -} - -#if 0 -/* get_memory_c is not used, get_memory_asm is, but this does what the */ -/* assembly language would do */ -word32 -get_memory_c(word32 loc, int diff_cycles) -{ - byte *addr; - word32 result; - int index; - -#ifdef CHECK_BREAKPOINTS - check_breakpoints_c(loc); -#endif - - index = loc >> 8; - result = page_info[index].rd; - if(result & BANK_IO_BIT) { - return get_memory_io(loc, diff_cycles); - } - - addr = (byte *)((result & 0xffffff00) + (loc & 0xff)); - - return *addr; -} -#endif - - -word32 -get_memory_io(word32 loc, double *cyc_ptr) -{ - int tmp; - - if(loc > 0xffffff) { - halt_printf("get_memory_io:%08x out of range==halt!\n", loc); - return 0; - } - tmp = loc & 0xfef000; - if(tmp == 0xc000 || tmp == 0xe0c000) { - return(io_read(loc & 0xfff, cyc_ptr)); - } - - /* Else it's an illegal addr...skip if memory sizing */ - if(loc >= g_mem_size_total) { - if((loc & 0xfffe) == 0) { -#if 0 - printf("get_io assuming mem sizing, not halting\n"); -#endif - return 0; - } - } - - /* Skip reads to f80000 and f00000, just return 0 */ - if((loc & 0xf70000) == 0xf00000) { - return 0; - } - - if((loc & 0xff0000) == 0xef0000) { - /* DOC RAM */ - return (doc_ram[loc & 0xffff]); - } - - g_code_yellow++; - if(g_ignore_bad_acc && !g_user_halt_bad) { - /* print no message, just get out. User doesn't want */ - /* to be bothered by buggy programs */ - return 0; - } - - printf("get_memory_io for addr: %06x\n", loc); - printf("stat for addr: %06x = %p\n", loc, - GET_PAGE_INFO_RD((loc >> 8) & 0xffff)); - set_halt(g_halt_on_bad_read | g_user_halt_bad); - - return 0; -} - -#if 0 -word32 -get_memory16_pieces(word32 loc, int diff_cycles) -{ - return(get_memory_c(loc, diff_cycles) + - (get_memory_c(loc+1, diff_cycles) << 8)); -} - -word32 -get_memory24(word32 loc, int diff_cycles) -{ - return(get_memory_c(loc, diff_cycles) + - (get_memory_c(loc+1, diff_cycles) << 8) + - (get_memory_c(loc+2, diff_cycles) << 16)); -} -#endif - -#if 0 -void -set_memory(word32 loc, int val, int diff_cycles) -{ - byte *ptr; - word32 new_addr; - word32 tmp; - word32 or_val; - int or_pos; - int old_slow_val; - -#ifdef CHECK_BREAKPOINTS - check_breakpoints_c(loc); -#endif - - tmp = GET_PAGE_INFO_WR((loc>>8) & 0xffff); - if(tmp & BANK_IO) { - set_memory_io(loc, val, diff_cycles); - return; - } - - if((loc & 0xfef000) == 0xe0c000) { - printf("set_memory_special: non-io for addr %08x, %02x, %d\n", - loc, val, diff_cycles); - halt_printf("tmp: %08x\n", tmp); - } - - ptr = (byte *)(tmp & (~0xff)); - - new_addr = loc & 0xffff; - old_slow_val = val; - - if(tmp & BANK_SHADOW) { - old_slow_val = g_slow_memory_ptr[new_addr]; - } else if(tmp & BANK_SHADOW2) { - new_addr += 0x10000; - old_slow_val = g_slow_memory_ptr[new_addr]; - } - - if(old_slow_val != val) { - g_slow_memory_ptr[new_addr] = val; - or_pos = (new_addr >> SHIFT_PER_CHANGE) & 0x1f; - or_val = DEP1(1, or_pos, 0); - if((new_addr >> CHANGE_SHIFT) >= SLOW_MEM_CH_SIZE) { - printf("new_addr: %08x\n", new_addr); - exit(12); - } - slow_mem_changed[(new_addr & 0xffff) >> CHANGE_SHIFT] |= or_val; - } - - ptr[loc & 0xff] = val; - -} -#endif - -void -set_memory_io(word32 loc, int val, double *cyc_ptr) -{ - word32 tmp; - tmp = loc & 0xfef000; - if(tmp == 0xc000 || tmp == 0xe0c000) { - io_write(loc, val, cyc_ptr); - return; - } - - /* Else it's an illegal addr */ - if(loc >= g_mem_size_total) { - if((loc & 0xfffe) == 0) { -#if 0 - printf("set_io assuming mem sizing, not halting\n"); -#endif - return; - } - } - - /* ignore writes to ROM */ - if((loc & 0xfc0000) == 0xfc0000) { - return; - } - - if((loc & 0xff0000) == 0xef0000) { - /* DOC RAM */ - doc_ram[loc & 0xffff] = val; - return; - } - - if(g_ignore_bad_acc && !g_user_halt_bad) { - /* print no message, just get out. User doesn't want */ - /* to be bothered by buggy programs */ - return; - } - - if((loc & 0xffc000) == 0x00c000) { - printf("set_memory %06x = %02x, warning\n", loc, val); - return; - } - - halt_printf("set_memory %06x = %02x, stopping\n", loc, val); - - return; -} - - -#if 0 -void -check_breakpoints_c(word32 loc) -{ - int index; - int count; - int i; - - index = (loc & (MAX_BP_INDEX-1)); - count = breakpoints[index].count; - if(count) { - for(i = 0; i < count; i++) { - if(loc == breakpoints[index].addrs[i]) { - halt_printf("Write hit breakpoint %d!\n", i); - } - } - } -} -#endif - - -void -show_regs_act(Engine_reg *eptr) -{ - int tmp_acc, tmp_x, tmp_y, tmp_psw; - int kpc; - int direct_page, dbank; - int stack; - - kpc = eptr->kpc; - tmp_acc = eptr->acc; - direct_page = eptr->direct; - dbank = eptr->dbank; - stack = eptr->stack; - - tmp_x = eptr->xreg; - tmp_y = eptr->yreg; - - tmp_psw = eptr->psr; - - printf(" PC=%02x.%04x A=%04x X=%04x Y=%04x P=%03x", - kpc>>16, kpc & 0xffff ,tmp_acc,tmp_x,tmp_y,tmp_psw); - printf(" S=%04x D=%04x B=%02x,cyc:%.3f\n", stack, direct_page, - dbank, g_cur_dcycs); -} - -void -show_regs() -{ - show_regs_act(&engine); -} - + g_toolbox_log_array[pos][1] = stack+1; + g_toolbox_log_array[pos][2] = xreg; + g_toolbox_log_array[pos][3] = toolbox_debug_4byte(stack+1); + g_toolbox_log_array[pos][4] = toolbox_debug_4byte(stack+5); + g_toolbox_log_array[pos][5] = toolbox_debug_4byte(stack+9); + g_toolbox_log_array[pos][6] = toolbox_debug_4byte(stack+13); + g_toolbox_log_array[pos][7] = toolbox_debug_4byte(stack+17); + + pos++; + if(pos >= TOOLBOX_LOG_LEN) { + pos = 0; + } + + g_toolbox_log_pos = pos; +} + +void +show_toolbox_log() +{ + int pos; + int i; + + pos = g_toolbox_log_pos; + + for(i = TOOLBOX_LOG_LEN - 1; i >= 0; i--) { + printf("%2d:%2d: %08x %06x %04x: %08x %08x %08x %08x %08x\n", + i, pos, + g_toolbox_log_array[pos][0], + g_toolbox_log_array[pos][1], + g_toolbox_log_array[pos][2], + g_toolbox_log_array[pos][3], + g_toolbox_log_array[pos][4], + g_toolbox_log_array[pos][5], + g_toolbox_log_array[pos][6], + g_toolbox_log_array[pos][7]); + pos++; + if(pos >= TOOLBOX_LOG_LEN) { + pos = 0; + } + } +} + +#if 0 +/* get_memory_c is not used, get_memory_asm is, but this does what the */ +/* assembly language would do */ +word32 +get_memory_c(word32 loc, int diff_cycles) +{ + byte *addr; + word32 result; + int index; + +#ifdef CHECK_BREAKPOINTS + check_breakpoints_c(loc); +#endif + + index = loc >> 8; + result = page_info[index].rd; + if(result & BANK_IO_BIT) { + return get_memory_io(loc, diff_cycles); + } + + addr = (byte *)((result & 0xffffff00) + (loc & 0xff)); + + return *addr; +} +#endif + + +word32 +get_memory_io(word32 loc, double *cyc_ptr) +{ + int tmp; + + if(loc > 0xffffff) { + halt_printf("get_memory_io:%08x out of range==halt!\n", loc); + return 0; + } + tmp = loc & 0xfef000; + if(tmp == 0xc000 || tmp == 0xe0c000) { + return(io_read(loc & 0xfff, cyc_ptr)); + } + + /* Else it's an illegal addr...skip if memory sizing */ + if(loc >= g_mem_size_total) { + if((loc & 0xfffe) == 0) { +#if 0 + printf("get_io assuming mem sizing, not halting\n"); +#endif + return 0; + } + } + + /* Skip reads to f80000 and f00000, just return 0 */ + if((loc & 0xf70000) == 0xf00000) { + return 0; + } + + if((loc & 0xff0000) == 0xef0000) { + /* DOC RAM */ + return (doc_ram[loc & 0xffff]); + } + + g_code_yellow++; + if(g_ignore_bad_acc && !g_user_halt_bad) { + /* print no message, just get out. User doesn't want */ + /* to be bothered by buggy programs */ + return 0; + } + + printf("get_memory_io for addr: %06x\n", loc); + printf("stat for addr: %06x = %p\n", loc, + GET_PAGE_INFO_RD((loc >> 8) & 0xffff)); + set_halt(g_halt_on_bad_read | g_user_halt_bad); + + return 0; +} + +#if 0 +word32 +get_memory16_pieces(word32 loc, int diff_cycles) +{ + return(get_memory_c(loc, diff_cycles) + + (get_memory_c(loc+1, diff_cycles) << 8)); +} + +word32 +get_memory24(word32 loc, int diff_cycles) +{ + return(get_memory_c(loc, diff_cycles) + + (get_memory_c(loc+1, diff_cycles) << 8) + + (get_memory_c(loc+2, diff_cycles) << 16)); +} +#endif + +#if 0 +void +set_memory(word32 loc, int val, int diff_cycles) +{ + byte *ptr; + word32 new_addr; + word32 tmp; + word32 or_val; + int or_pos; + int old_slow_val; + +#ifdef CHECK_BREAKPOINTS + check_breakpoints_c(loc); +#endif + + tmp = GET_PAGE_INFO_WR((loc>>8) & 0xffff); + if(tmp & BANK_IO) { + set_memory_io(loc, val, diff_cycles); + return; + } + + if((loc & 0xfef000) == 0xe0c000) { + printf("set_memory_special: non-io for addr %08x, %02x, %d\n", + loc, val, diff_cycles); + halt_printf("tmp: %08x\n", tmp); + } + + ptr = (byte *)(tmp & (~0xff)); + + new_addr = loc & 0xffff; + old_slow_val = val; + + if(tmp & BANK_SHADOW) { + old_slow_val = g_slow_memory_ptr[new_addr]; + } else if(tmp & BANK_SHADOW2) { + new_addr += 0x10000; + old_slow_val = g_slow_memory_ptr[new_addr]; + } + + if(old_slow_val != val) { + g_slow_memory_ptr[new_addr] = val; + or_pos = (new_addr >> SHIFT_PER_CHANGE) & 0x1f; + or_val = DEP1(1, or_pos, 0); + if((new_addr >> CHANGE_SHIFT) >= SLOW_MEM_CH_SIZE) { + printf("new_addr: %08x\n", new_addr); + exit(12); + } + slow_mem_changed[(new_addr & 0xffff) >> CHANGE_SHIFT] |= or_val; + } + + ptr[loc & 0xff] = val; + +} +#endif + +void +set_memory_io(word32 loc, int val, double *cyc_ptr) +{ + word32 tmp; + tmp = loc & 0xfef000; + if(tmp == 0xc000 || tmp == 0xe0c000) { + io_write(loc, val, cyc_ptr); + return; + } + + /* Else it's an illegal addr */ + if(loc >= g_mem_size_total) { + if((loc & 0xfffe) == 0) { +#if 0 + printf("set_io assuming mem sizing, not halting\n"); +#endif + return; + } + } + + /* ignore writes to ROM */ + if((loc & 0xfc0000) == 0xfc0000) { + return; + } + + if((loc & 0xff0000) == 0xef0000) { + /* DOC RAM */ + doc_ram[loc & 0xffff] = val; + return; + } + + if(g_ignore_bad_acc && !g_user_halt_bad) { + /* print no message, just get out. User doesn't want */ + /* to be bothered by buggy programs */ + return; + } + + if((loc & 0xffc000) == 0x00c000) { + printf("set_memory %06x = %02x, warning\n", loc, val); + return; + } + + halt_printf("set_memory %06x = %02x, stopping\n", loc, val); + + return; +} + + +#if 0 +void +check_breakpoints_c(word32 loc) +{ + int index; + int count; + int i; + + index = (loc & (MAX_BP_INDEX-1)); + count = breakpoints[index].count; + if(count) { + for(i = 0; i < count; i++) { + if(loc == breakpoints[index].addrs[i]) { + halt_printf("Write hit breakpoint %d!\n", i); + } + } + } +} +#endif + + +void +show_regs_act(Engine_reg *eptr) +{ + int tmp_acc, tmp_x, tmp_y, tmp_psw; + int kpc; + int direct_page, dbank; + int stack; + + kpc = eptr->kpc; + tmp_acc = eptr->acc; + direct_page = eptr->direct; + dbank = eptr->dbank; + stack = eptr->stack; + + tmp_x = eptr->xreg; + tmp_y = eptr->yreg; + + tmp_psw = eptr->psr; + + printf(" PC=%02x.%04x A=%04x X=%04x Y=%04x P=%03x", + kpc>>16, kpc & 0xffff ,tmp_acc,tmp_x,tmp_y,tmp_psw); + printf(" S=%04x D=%04x B=%02x,cyc:%.3f\n", stack, direct_page, + dbank, g_cur_dcycs); +} + +void +show_regs() +{ + show_regs_act(&engine); +} + //OG for regular exit, use quitEmulator() void quitEmulator() -{ +{ printf("set_halt(HALT_WANTTOQUIT)\n"); set_halt(HALT_WANTTOQUIT); } @@ -732,164 +731,164 @@ void quitEmulator() #define fatalExit exit #else extern void fatalExit(int); -#endif +#endif void my_exit(int ret) { end_screen(); imagewriter_close(); - printer_close(); + printer_close(); printf("exiting (ret=%d)\n",ret); fatalExit(ret); -} - - -void -do_reset() -{ - - // OG Cleared remaining IRQS on RESET - extern int g_irq_pending; - extern int g_scan_int_events ; - extern int g_c023_val; - - g_c068_statereg = 0x08 + 0x04 + 0x01; /* rdrom, lcbank2, intcx */ - g_c035_shadow_reg = 0; - - g_c08x_wrdefram = 1; - g_c02d_int_crom = 0; - g_c023_val = 0; - g_c041_val = 0; - - engine.psr = (engine.psr | 0x134) & ~(0x08); - engine.stack = 0x100 + (engine.stack & 0xff); - engine.dbank = 0; - engine.direct = 0; - engine.xreg &= 0xff; - engine.yreg &= 0xff; - g_wait_pending = 0; - g_stp_pending = 0; - - - video_reset(); - adb_reset(); - iwm_reset(); - scc_reset(); - sound_reset(g_cur_dcycs); - setup_pageinfo(); - change_display_mode(g_cur_dcycs); - - g_irq_pending = 0; - - engine.kpc = get_memory16_c(0x00fffc, 0); - - g_stepping = 0; - +} + + +void +do_reset() +{ + + // OG Cleared remaining IRQS on RESET + extern int g_irq_pending; + extern int g_scan_int_events ; + extern int g_c023_val; + + g_c068_statereg = 0x08 + 0x04 + 0x01; /* rdrom, lcbank2, intcx */ + g_c035_shadow_reg = 0; + + g_c08x_wrdefram = 1; + g_c02d_int_crom = 0; + g_c023_val = 0; + g_c041_val = 0; + + engine.psr = (engine.psr | 0x134) & ~(0x08); + engine.stack = 0x100 + (engine.stack & 0xff); + engine.dbank = 0; + engine.direct = 0; + engine.xreg &= 0xff; + engine.yreg &= 0xff; + g_wait_pending = 0; + g_stp_pending = 0; + + + video_reset(); + adb_reset(); + iwm_reset(); + scc_reset(); + sound_reset(g_cur_dcycs); + setup_pageinfo(); + change_display_mode(g_cur_dcycs); + + g_irq_pending = 0; + + engine.kpc = get_memory16_c(0x00fffc, 0); + + g_stepping = 0; + if (g_irq_pending) halt_printf("*** irq remainings...\n"); -} - -#define CHECK(start, var, value, var1, var2) \ - var2 = PTR2WORD(&(var)); \ - var1 = PTR2WORD((start)); \ - if((var2 - var1) != value) { \ - printf("CHECK: " #var " is 0x%x, but " #value " is 0x%x\n", \ - (var2 - var1), value); \ - exit(5); \ - } - -void -check_engine_asm_defines() -{ - Fplus fplus; - Fplus *fplusptr; - Pc_log pclog; - Pc_log *pcptr; - Engine_reg ereg; - Engine_reg *eptr; - word32 val1; - word32 val2; - - eptr = &ereg; - CHECK(eptr, eptr->fcycles, ENGINE_FCYCLES, val1, val2); - CHECK(eptr, eptr->fplus_ptr, ENGINE_FPLUS_PTR, val1, val2); - CHECK(eptr, eptr->acc, ENGINE_REG_ACC, val1, val2); - CHECK(eptr, eptr->xreg, ENGINE_REG_XREG, val1, val2); - CHECK(eptr, eptr->yreg, ENGINE_REG_YREG, val1, val2); - CHECK(eptr, eptr->stack, ENGINE_REG_STACK, val1, val2); - CHECK(eptr, eptr->dbank, ENGINE_REG_DBANK, val1, val2); - CHECK(eptr, eptr->direct, ENGINE_REG_DIRECT, val1, val2); - CHECK(eptr, eptr->psr, ENGINE_REG_PSR, val1, val2); - CHECK(eptr, eptr->kpc, ENGINE_REG_KPC, val1, val2); - - pcptr = &pclog; - CHECK(pcptr, pcptr->dbank_kpc, LOG_PC_DBANK_KPC, val1, val2); - CHECK(pcptr, pcptr->instr, LOG_PC_INSTR, val1, val2); - CHECK(pcptr, pcptr->psr_acc, LOG_PC_PSR_ACC, val1, val2); - CHECK(pcptr, pcptr->xreg_yreg, LOG_PC_XREG_YREG, val1, val2); - CHECK(pcptr, pcptr->stack_direct, LOG_PC_STACK_DIRECT, val1, val2); - if(LOG_PC_SIZE != sizeof(pclog)) { - printf("LOG_PC_SIZE: %d != sizeof=%d\n", LOG_PC_SIZE, - (int)sizeof(pclog)); - exit(2); - } - - fplusptr = &fplus; - CHECK(fplusptr, fplusptr->plus_1, FPLUS_PLUS_1, val1, val2); - CHECK(fplusptr, fplusptr->plus_2, FPLUS_PLUS_2, val1, val2); - CHECK(fplusptr, fplusptr->plus_3, FPLUS_PLUS_3, val1, val2); - CHECK(fplusptr, fplusptr->plus_x_minus_1, FPLUS_PLUS_X_M1, val1, val2); -} - -byte * -memalloc_align(int size, int skip_amt, void **alloc_ptr) -{ - byte *bptr; - word32 addr; - word32 offset; - - skip_amt = MAX(256, skip_amt); +} + +#define CHECK(start, var, value, var1, var2) \ + var2 = PTR2WORD(&(var)); \ + var1 = PTR2WORD((start)); \ + if((var2 - var1) != value) { \ + printf("CHECK: " #var " is 0x%x, but " #value " is 0x%x\n", \ + (var2 - var1), value); \ + exit(5); \ + } + +void +check_engine_asm_defines() +{ + Fplus fplus; + Fplus *fplusptr; + Pc_log pclog; + Pc_log *pcptr; + Engine_reg ereg; + Engine_reg *eptr; + word32 val1; + word32 val2; + + eptr = &ereg; + CHECK(eptr, eptr->fcycles, ENGINE_FCYCLES, val1, val2); + CHECK(eptr, eptr->fplus_ptr, ENGINE_FPLUS_PTR, val1, val2); + CHECK(eptr, eptr->acc, ENGINE_REG_ACC, val1, val2); + CHECK(eptr, eptr->xreg, ENGINE_REG_XREG, val1, val2); + CHECK(eptr, eptr->yreg, ENGINE_REG_YREG, val1, val2); + CHECK(eptr, eptr->stack, ENGINE_REG_STACK, val1, val2); + CHECK(eptr, eptr->dbank, ENGINE_REG_DBANK, val1, val2); + CHECK(eptr, eptr->direct, ENGINE_REG_DIRECT, val1, val2); + CHECK(eptr, eptr->psr, ENGINE_REG_PSR, val1, val2); + CHECK(eptr, eptr->kpc, ENGINE_REG_KPC, val1, val2); + + pcptr = &pclog; + CHECK(pcptr, pcptr->dbank_kpc, LOG_PC_DBANK_KPC, val1, val2); + CHECK(pcptr, pcptr->instr, LOG_PC_INSTR, val1, val2); + CHECK(pcptr, pcptr->psr_acc, LOG_PC_PSR_ACC, val1, val2); + CHECK(pcptr, pcptr->xreg_yreg, LOG_PC_XREG_YREG, val1, val2); + CHECK(pcptr, pcptr->stack_direct, LOG_PC_STACK_DIRECT, val1, val2); + if(LOG_PC_SIZE != sizeof(pclog)) { + printf("LOG_PC_SIZE: %d != sizeof=%d\n", LOG_PC_SIZE, + (int)sizeof(pclog)); + exit(2); + } + + fplusptr = &fplus; + CHECK(fplusptr, fplusptr->plus_1, FPLUS_PLUS_1, val1, val2); + CHECK(fplusptr, fplusptr->plus_2, FPLUS_PLUS_2, val1, val2); + CHECK(fplusptr, fplusptr->plus_3, FPLUS_PLUS_3, val1, val2); + CHECK(fplusptr, fplusptr->plus_x_minus_1, FPLUS_PLUS_X_M1, val1, val2); +} + +byte * +memalloc_align(int size, int skip_amt, void **alloc_ptr) +{ + byte *bptr; + word32 addr; + word32 offset; + + skip_amt = MAX(256, skip_amt); bptr = (byte*)calloc(size + skip_amt + 256, 1); // OG Added cast - if(alloc_ptr) { - /* Save allocation address */ - *alloc_ptr = bptr; - } - - addr = PTR2WORD(bptr) & 0xff; - - /* must align bptr to be 256-byte aligned */ - /* this code should work even if ptrs are > 32 bits */ - - offset = ((addr + skip_amt - 1) & (~0xff)) - addr; - - return (bptr + offset); -} - -void -memory_ptr_init() -{ - word32 mem_size; - - /* This routine may be called several times--each time the ROM file */ - /* changes this will be called */ - mem_size = MIN(0xdf0000, g_mem_size_base + g_mem_size_exp); - g_mem_size_total = mem_size; + if(alloc_ptr) { + /* Save allocation address */ + *alloc_ptr = bptr; + } + + addr = PTR2WORD(bptr) & 0xff; + + /* must align bptr to be 256-byte aligned */ + /* this code should work even if ptrs are > 32 bits */ + + offset = ((addr + skip_amt - 1) & (~0xff)) - addr; + + return (bptr + offset); +} + +void +memory_ptr_init() +{ + word32 mem_size; + + /* This routine may be called several times--each time the ROM file */ + /* changes this will be called */ + mem_size = MIN(0xdf0000, g_mem_size_base + g_mem_size_exp); + g_mem_size_total = mem_size; // OG using memory_ptr_shut() instead memory_ptr_shut(); /* - if(g_memory_alloc_ptr) { - free(g_memory_alloc_ptr); - g_memory_alloc_ptr = 0; - } + if(g_memory_alloc_ptr) { + free(g_memory_alloc_ptr); + g_memory_alloc_ptr = 0; + } */ - g_memory_ptr = memalloc_align(mem_size, 256, &g_memory_alloc_ptr); - - printf("RAM size is 0 - %06x (%.2fMB)\n", mem_size, - (double)mem_size/(1024.0*1024.0)); -} - + g_memory_ptr = memalloc_align(mem_size, 256, &g_memory_alloc_ptr); + + printf("RAM size is 0 - %06x (%.2fMB)\n", mem_size, + (double)mem_size/(1024.0*1024.0)); +} + // OG Added memory_ptr_shut void memory_ptr_shut() @@ -903,24 +902,24 @@ memory_ptr_shut() } -extern int g_screen_redraw_skip_amt; -extern int g_use_shmem; -extern int g_use_dhr140; -extern int g_use_bw_hires; - -char g_display_env[512]; -int g_force_depth = -1; -int g_screen_depth = 8; - - -int -gsportmain(int argc, char **argv) -{ - int diff; - int skip_amt; - int tmp1; - int i; - char *final_arg = 0; +extern int g_screen_redraw_skip_amt; +extern int g_use_shmem; +extern int g_use_dhr140; +extern int g_use_bw_hires; + +char g_display_env[512]; +int g_force_depth = -1; +int g_screen_depth = 8; + + +int +gsportmain(int argc, char **argv) +{ + int diff; + int skip_amt; + int tmp1; + int i; + char *final_arg = 0; // OG Restoring globals sim65816_initglobals(); @@ -929,219 +928,219 @@ gsportmain(int argc, char **argv) //OG Disabling argument parsing #ifndef ACTIVEGS - /* parse args */ + /* parse args */ for(i = 1; i < argc; i++) { - if(!strcmp("-badrd", argv[i])) { - printf("Halting on bad reads\n"); - g_halt_on_bad_read = 2; - } else if(!strcmp("-noignbadacc", argv[i])) { - printf("Not ignoring bad memory accesses\n"); - g_ignore_bad_acc = 0; - } else if(!strcmp("-noignhalt", argv[i])) { - printf("Not ignoring code red halts\n"); - g_ignore_halts = 0; - } else if(!strcmp("-test", argv[i])) { - printf("Allowing testing\n"); - g_testing_enabled = 1; - } else if(!strcmp("-hpdev", argv[i])) { - printf("Using /dev/audio\n"); - g_use_alib = 0; - } else if(!strcmp("-alib", argv[i])) { - printf("Using Aserver audio server\n"); - g_use_alib = 1; - } else if(!strcmp("-24", argv[i])) { - printf("Using 24-bit visual\n"); - g_force_depth = 24; - } else if(!strcmp("-16", argv[i])) { - printf("Using 16-bit visual\n"); - g_force_depth = 16; - } else if(!strcmp("-15", argv[i])) { - printf("Using 15-bit visual\n"); - g_force_depth = 15; - } else if(!strcmp("-mem", argv[i])) { - if((i+1) >= argc) { - printf("Missing argument\n"); - exit(1); - } - g_mem_size_exp = strtol(argv[i+1], 0, 0) & 0x00ff0000; - printf("Using %d as memory size\n", g_mem_size_exp); - i++; - } else if(!strcmp("-skip", argv[i])) { - if((i+1) >= argc) { - printf("Missing argument\n"); - exit(1); - } - skip_amt = strtol(argv[i+1], 0, 0); - printf("Using %d as skip_amt\n", skip_amt); - g_screen_redraw_skip_amt = skip_amt; - i++; - } else if(!strcmp("-audio", argv[i])) { - if((i+1) >= argc) { - printf("Missing argument\n"); - exit(1); - } - tmp1 = strtol(argv[i+1], 0, 0); - printf("Using %d as audio enable val\n", tmp1); - g_audio_enable = tmp1; - i++; - } else if(!strcmp("-arate", argv[i])) { - if((i+1) >= argc) { - printf("Missing argument\n"); - exit(1); - } - tmp1 = strtol(argv[i+1], 0, 0); - printf("Using %d as preferred audio rate\n", tmp1); - g_preferred_rate = tmp1; - i++; - } else if(!strcmp("-v", argv[i])) { - if((i+1) >= argc) { - printf("Missing argument\n"); - exit(1); - } - tmp1 = strtol(argv[i+1], 0, 0); - printf("Setting Verbose = 0x%03x\n", tmp1); - Verbose = tmp1; - i++; -#ifndef __NeXT__ - } else if(!strcmp("-display", argv[i])) { - if((i+1) >= argc) { - printf("Missing argument\n"); - exit(1); - } - printf("Using %s as display\n", argv[i+1]); - sprintf(g_display_env, "DISPLAY=%s", argv[i+1]); - putenv(&g_display_env[0]); - i++; -#endif - } else if(!strcmp("-noshm", argv[i])) { - printf("Not using X shared memory\n"); - g_use_shmem = 0; - } else if(!strcmp("-joystick", argv[i])) { - printf("Ignoring -joystick option\n"); - } else if(!strcmp("-dhr140", argv[i])) { - printf("Using simple dhires color map\n"); - g_use_dhr140 = 1; - } else if(!strcmp("-bw", argv[i])) { - printf("Forcing black-and-white hires modes\n"); - g_cur_a2_stat |= ALL_STAT_COLOR_C021; - g_use_bw_hires = 1; - } else if(!strcmp("-enet", argv[i])) { - if((i+1) >= argc) { - printf("Missing argument\n"); - exit(1); - } - tmp1 = strtol(argv[i+1], 0, 0); - printf("Using %d as ethernet enable val\n", tmp1); - g_ethernet = tmp1; - i++; - } else { - if ((i == (argc - 1)) && (strncmp("-", argv[i], 1) != 0)) { - final_arg = argv[i]; - } else { - printf("Bad option: %s\n", argv[i]); - exit(3); - } - } + if(!strcmp("-badrd", argv[i])) { + printf("Halting on bad reads\n"); + g_halt_on_bad_read = 2; + } else if(!strcmp("-noignbadacc", argv[i])) { + printf("Not ignoring bad memory accesses\n"); + g_ignore_bad_acc = 0; + } else if(!strcmp("-noignhalt", argv[i])) { + printf("Not ignoring code red halts\n"); + g_ignore_halts = 0; + } else if(!strcmp("-test", argv[i])) { + printf("Allowing testing\n"); + g_testing_enabled = 1; + } else if(!strcmp("-hpdev", argv[i])) { + printf("Using /dev/audio\n"); + g_use_alib = 0; + } else if(!strcmp("-alib", argv[i])) { + printf("Using Aserver audio server\n"); + g_use_alib = 1; + } else if(!strcmp("-24", argv[i])) { + printf("Using 24-bit visual\n"); + g_force_depth = 24; + } else if(!strcmp("-16", argv[i])) { + printf("Using 16-bit visual\n"); + g_force_depth = 16; + } else if(!strcmp("-15", argv[i])) { + printf("Using 15-bit visual\n"); + g_force_depth = 15; + } else if(!strcmp("-mem", argv[i])) { + if((i+1) >= argc) { + printf("Missing argument\n"); + exit(1); + } + g_mem_size_exp = strtol(argv[i+1], 0, 0) & 0x00ff0000; + printf("Using %d as memory size\n", g_mem_size_exp); + i++; + } else if(!strcmp("-skip", argv[i])) { + if((i+1) >= argc) { + printf("Missing argument\n"); + exit(1); + } + skip_amt = strtol(argv[i+1], 0, 0); + printf("Using %d as skip_amt\n", skip_amt); + g_screen_redraw_skip_amt = skip_amt; + i++; + } else if(!strcmp("-audio", argv[i])) { + if((i+1) >= argc) { + printf("Missing argument\n"); + exit(1); + } + tmp1 = strtol(argv[i+1], 0, 0); + printf("Using %d as audio enable val\n", tmp1); + g_audio_enable = tmp1; + i++; + } else if(!strcmp("-arate", argv[i])) { + if((i+1) >= argc) { + printf("Missing argument\n"); + exit(1); + } + tmp1 = strtol(argv[i+1], 0, 0); + printf("Using %d as preferred audio rate\n", tmp1); + g_preferred_rate = tmp1; + i++; + } else if(!strcmp("-v", argv[i])) { + if((i+1) >= argc) { + printf("Missing argument\n"); + exit(1); + } + tmp1 = strtol(argv[i+1], 0, 0); + printf("Setting Verbose = 0x%03x\n", tmp1); + Verbose = tmp1; + i++; +#ifndef __NeXT__ + } else if(!strcmp("-display", argv[i])) { + if((i+1) >= argc) { + printf("Missing argument\n"); + exit(1); + } + printf("Using %s as display\n", argv[i+1]); + sprintf(g_display_env, "DISPLAY=%s", argv[i+1]); + putenv(&g_display_env[0]); + i++; +#endif + } else if(!strcmp("-noshm", argv[i])) { + printf("Not using X shared memory\n"); + g_use_shmem = 0; + } else if(!strcmp("-joystick", argv[i])) { + printf("Ignoring -joystick option\n"); + } else if(!strcmp("-dhr140", argv[i])) { + printf("Using simple dhires color map\n"); + g_use_dhr140 = 1; + } else if(!strcmp("-bw", argv[i])) { + printf("Forcing black-and-white hires modes\n"); + g_cur_a2_stat |= ALL_STAT_COLOR_C021; + g_use_bw_hires = 1; + } else if(!strcmp("-enet", argv[i])) { + if((i+1) >= argc) { + printf("Missing argument\n"); + exit(1); + } + tmp1 = strtol(argv[i+1], 0, 0); + printf("Using %d as ethernet enable val\n", tmp1); + g_ethernet = tmp1; + i++; + } else { + if ((i == (argc - 1)) && (strncmp("-", argv[i], 1) != 0)) { + final_arg = argv[i]; + } else { + printf("Bad option: %s\n", argv[i]); + exit(3); + } + } } -#endif - check_engine_asm_defines(); - fixed_memory_ptrs_init(); - - if(sizeof(word32) != 4) { - printf("sizeof(word32) = %d, must be 4!\n", - (int)sizeof(word32)); - exit(1); - } - - if(!g_engine_c_mode) { - diff = &defs_instr_end_8 - &defs_instr_start_8; - if(diff != 1) { - printf("defs_instr_end_8 - start is %d\n",diff); - exit(1); - } - - diff = &defs_instr_end_16 - &defs_instr_start_16; - if(diff != 1) { - printf("defs_instr_end_16 - start is %d\n", diff); - exit(1); - } - - diff = &op_routs_end - &op_routs_start; - if(diff != 1) { - printf("op_routs_end - start is %d\n", diff); - exit(1); - } - } - - iwm_init(); - config_init(); - // If the final argument was not a switch, then treat it like a disk image filename to insert - if (final_arg) { - // ...and flag it to boot - cfg_inspect_maybe_insert_file(final_arg, 1); - } - printer_init(g_printer_dpi,85,110,g_printer_output,g_printer_multipage != 0); - //If ethernet is enabled in config.gsport, let's initialize it -#ifdef HAVE_TFE - if (g_ethernet == 1) - { - int i = 0; - char *ppname = NULL; - char *ppdes = NULL; - if (tfe_enumadapter_open()) - { - //Loop through the available adapters until we reach the interface number specified in config.gsport - while(tfe_enumadapter(&ppname,&ppdes)) - { - if (i == g_ethernet_interface) break; - i++; - } - tfe_enumadapter_close(); - printf("Using host ethernet interface: %s\nUthernet support is ON.\n",ppdes); - } - else - { - printf("No ethernet host adapters found. Do you have PCap installed/enabled?\nUthernet support is OFF.\n"); - } - set_tfe_interface(ppname); //Connect the emulated ethernet device with the selected host adapter - lib_free(ppname); - lib_free(ppdes); - tfe_init(); - } -#endif - - load_roms_init_memory(); - - init_reg(); - clear_halt(); - - initialize_events(); - - video_init(); - -#ifndef _WIN32 - //sleep(1); -#endif - sound_init(); - scc_init(); - adb_init(); - joystick_init(); - if(g_rom_version >= 3) { - g_c036_val_speed |= 0x40; /* set power-on bit */ - } - - do_reset(); - g_stepping = 0; +#endif + check_engine_asm_defines(); + fixed_memory_ptrs_init(); + + if(sizeof(word32) != 4) { + printf("sizeof(word32) = %d, must be 4!\n", + (int)sizeof(word32)); + exit(1); + } + + if(!g_engine_c_mode) { + diff = &defs_instr_end_8 - &defs_instr_start_8; + if(diff != 1) { + printf("defs_instr_end_8 - start is %d\n",diff); + exit(1); + } + + diff = &defs_instr_end_16 - &defs_instr_start_16; + if(diff != 1) { + printf("defs_instr_end_16 - start is %d\n", diff); + exit(1); + } + + diff = &op_routs_end - &op_routs_start; + if(diff != 1) { + printf("op_routs_end - start is %d\n", diff); + exit(1); + } + } + + iwm_init(); + config_init(); + // If the final argument was not a switch, then treat it like a disk image filename to insert + if (final_arg) { + // ...and flag it to boot + cfg_inspect_maybe_insert_file(final_arg, 1); + } + printer_init(g_printer_dpi,85,110,g_printer_output,g_printer_multipage != 0); + //If ethernet is enabled in config.gsport, let's initialize it +#ifdef HAVE_TFE + if (g_ethernet == 1) + { + int i = 0; + char *ppname = NULL; + char *ppdes = NULL; + if (tfe_enumadapter_open()) + { + //Loop through the available adapters until we reach the interface number specified in config.gsport + while(tfe_enumadapter(&ppname,&ppdes)) + { + if (i == g_ethernet_interface) break; + i++; + } + tfe_enumadapter_close(); + printf("Using host ethernet interface: %s\nUthernet support is ON.\n",ppdes); + } + else + { + printf("No ethernet host adapters found. Do you have PCap installed/enabled?\nUthernet support is OFF.\n"); + } + set_tfe_interface(ppname); //Connect the emulated ethernet device with the selected host adapter + lib_free(ppname); + lib_free(ppdes); + tfe_init(); + } +#endif + + load_roms_init_memory(); + + init_reg(); + clear_halt(); + + initialize_events(); + + video_init(); + +#ifndef _WIN32 + //sleep(1); +#endif + sound_init(); + scc_init(); + adb_init(); + joystick_init(); + if(g_rom_version >= 3) { + g_c036_val_speed |= 0x40; /* set power-on bit */ + } + + do_reset(); + g_stepping = 0; // OG Notify emulator has been initialized and ready to accept external events g_initialized = 1; g_accept_events = 1; - do_go(); - - /* If we get here, we hit a breakpoint, call debug intfc */ - do_debug_intfc(); - + do_go(); + + /* If we get here, we hit a breakpoint, call debug intfc */ + do_debug_intfc(); + // OG Notify emulator is being closed, and cannot accept events anymore g_accept_events = 0; @@ -1159,30 +1158,30 @@ gsportmain(int argc, char **argv) //my_exit(0); end_screen(); - return 0; -} - -void -load_roms_init_memory() -{ - config_load_roms(); - memory_ptr_init(); - clk_setup_bram_version(); /* Must be after config_load_roms */ - if(g_rom_version >= 3) { - g_c036_val_speed |= 0x40; /* set power-on bit */ - } else { - g_c036_val_speed &= (~0x40); /* clear the bit */ - } - do_reset(); - - /* if user booted ROM 01, switches to ROM 03, then switches back */ - /* to ROM 01, then the reset routines call to Tool $0102 looks */ - /* at uninitialized $e1/15fe and if it is negative it will JMP */ - /* through $e1/1688 which ROM 03 left pointing to fc/0199 */ - /* So set e1/15fe = 0 */ - set_memory16_c(0xe115fe, 0, 0); -} - + return 0; +} + +void +load_roms_init_memory() +{ + config_load_roms(); + memory_ptr_init(); + clk_setup_bram_version(); /* Must be after config_load_roms */ + if(g_rom_version >= 3) { + g_c036_val_speed |= 0x40; /* set power-on bit */ + } else { + g_c036_val_speed &= (~0x40); /* clear the bit */ + } + do_reset(); + + /* if user booted ROM 01, switches to ROM 03, then switches back */ + /* to ROM 01, then the reset routines call to Tool $0102 looks */ + /* at uninitialized $e1/15fe and if it is negative it will JMP */ + /* through $e1/1688 which ROM 03 left pointing to fc/0199 */ + /* So set e1/15fe = 0 */ + set_memory16_c(0xe115fe, 0, 0); +} + // OG Added load_roms_shut_memory void load_roms_shut_memory() { @@ -1191,939 +1190,939 @@ void load_roms_shut_memory() #ifndef ACTIVEGS -void -gsport_expand_path(char *out_ptr, const char *in_ptr, int maxlen) -{ - char name_buf[256]; - char *tmp_ptr; - int name_len; - int in_char; - int state; - - out_ptr[0] = 0; - - name_len = 0; - state = 0; - - /* See if in_ptr has ${} notation, replace with getenv or argv0 */ - while(maxlen > 0) { - in_char = *in_ptr++; - *out_ptr++ = in_char; - maxlen--; - if(state == 0) { - /* No $ seen yet, look for it */ - if(in_char == '$') { - state = 1; - } - } else if(state == 1) { - /* See if next char is '{' (dummy }) */ - if(in_char == '{') { /* add dummy } */ - state = 2; - name_len = 0; - out_ptr -= 2; - } else { - state = 0; - } - } else if(state == 2) { - /* fill name_buf ... dummy '{' */ - out_ptr--; - if(in_char == '}') { - name_buf[name_len] = 0; - - /* got token, now look it up */ - tmp_ptr = ""; - if(!strncmp("0", name_buf, 128)) { - /* Replace ${0} with g_argv0_path */ - tmp_ptr = &(g_argv0_path[0]); -#if defined (_WIN32) || defined(__CYGWIN__) - } else if(!strncmp("PWD", name_buf, 128)) { - /* Replace ${PWD} with cwd in Windows */ - get_cwd(out_ptr,128); - tmp_ptr = out_ptr; -#endif - } else { - tmp_ptr = getenv(name_buf); - if(tmp_ptr == 0) { - tmp_ptr = ""; - } - } - strncpy(out_ptr, tmp_ptr, maxlen); - out_ptr += strlen(tmp_ptr); - maxlen -= strlen(tmp_ptr); - state = 0; - } else { - name_buf[name_len++] = in_char; - } - } - if(in_char == 0) { - /* make sure its null terminated */ - *out_ptr++ = 0; - break; - } - } -} - -void -setup_gsport_file(char *outname, int maxlen, int ok_if_missing, - int can_create_file, const char **name_ptr) -{ - char local_path[256]; - struct stat stat_buf; - const char **path_ptr; - const char **cur_name_ptr, **save_path_ptr; - int ret; - - outname[0] = 0; - - path_ptr = &g_gsport_default_paths[0]; - - save_path_ptr = path_ptr; - while(*path_ptr) { - gsport_expand_path(&(local_path[0]), *path_ptr, 250); - cur_name_ptr = name_ptr; - while(*cur_name_ptr) { - strcpy(outname, &(local_path[0])); - strncat(outname, *cur_name_ptr, 255-strlen(outname)); - if(!ok_if_missing) { - printf("Trying '%s'\n", outname); - } - ret = stat(outname, &stat_buf); - if(ret == 0) { - /* got it! */ - return; - } - cur_name_ptr++; - } - path_ptr++; - } - - outname[0] = 0; - if(ok_if_missing > 0) { - return; - } - - /* couldn't find it, print out all the attempts */ - path_ptr = save_path_ptr; - fatal_printf("Could not find required file \"%s\" in any of these " - "directories:\n", *name_ptr); - while(*path_ptr) { - fatal_printf(" %s\n", *path_ptr++); - } - - if(can_create_file) { - // If we didn't find a file, pick a place to put it. - // Default is the current working directory. -#ifdef MAC - gsport_expand_path(&(local_path[0]), "${0}/../config.txt", 250); -#else - gsport_expand_path(&(local_path[0]), "${PWD}/config.txt", 250); -#endif - strcpy(outname, &(local_path[0])); - // Ask user if it's OK to create the file (or just create it) - x_dialog_create_gsport_conf(*name_ptr); - can_create_file = 0; - - // But clear out the fatal_printfs first - clear_fatal_logs(); - setup_gsport_file(outname, maxlen, ok_if_missing, - can_create_file, name_ptr); - // It's one-level of recursion--it cannot loop since we - // clear can_create_file. - // If it returns, then there was succes and we should get out - return; - } else if(ok_if_missing) { - /* Just show an alert and return if ok_if_missing < 0 */ - x_show_alert(0, 0); - return; - } - - my_exit(2); -} - +void +gsport_expand_path(char *out_ptr, const char *in_ptr, int maxlen) +{ + char name_buf[256]; + char *tmp_ptr; + int name_len; + int in_char; + int state; + + out_ptr[0] = 0; + + name_len = 0; + state = 0; + + /* See if in_ptr has ${} notation, replace with getenv or argv0 */ + while(maxlen > 0) { + in_char = *in_ptr++; + *out_ptr++ = in_char; + maxlen--; + if(state == 0) { + /* No $ seen yet, look for it */ + if(in_char == '$') { + state = 1; + } + } else if(state == 1) { + /* See if next char is '{' (dummy }) */ + if(in_char == '{') { /* add dummy } */ + state = 2; + name_len = 0; + out_ptr -= 2; + } else { + state = 0; + } + } else if(state == 2) { + /* fill name_buf ... dummy '{' */ + out_ptr--; + if(in_char == '}') { + name_buf[name_len] = 0; + + /* got token, now look it up */ + tmp_ptr = ""; + if(!strncmp("0", name_buf, 128)) { + /* Replace ${0} with g_argv0_path */ + tmp_ptr = &(g_argv0_path[0]); +#if defined (_WIN32) || defined(__CYGWIN__) + } else if(!strncmp("PWD", name_buf, 128)) { + /* Replace ${PWD} with cwd in Windows */ + get_cwd(out_ptr,128); + tmp_ptr = out_ptr; +#endif + } else { + tmp_ptr = getenv(name_buf); + if(tmp_ptr == 0) { + tmp_ptr = ""; + } + } + strncpy(out_ptr, tmp_ptr, maxlen); + out_ptr += strlen(tmp_ptr); + maxlen -= strlen(tmp_ptr); + state = 0; + } else { + name_buf[name_len++] = in_char; + } + } + if(in_char == 0) { + /* make sure its null terminated */ + *out_ptr++ = 0; + break; + } + } +} + +void +setup_gsport_file(char *outname, int maxlen, int ok_if_missing, + int can_create_file, const char **name_ptr) +{ + char local_path[256]; + struct stat stat_buf; + const char **path_ptr; + const char **cur_name_ptr, **save_path_ptr; + int ret; + + outname[0] = 0; + + path_ptr = &g_gsport_default_paths[0]; + + save_path_ptr = path_ptr; + while(*path_ptr) { + gsport_expand_path(&(local_path[0]), *path_ptr, 250); + cur_name_ptr = name_ptr; + while(*cur_name_ptr) { + strcpy(outname, &(local_path[0])); + strncat(outname, *cur_name_ptr, 255-strlen(outname)); + if(!ok_if_missing) { + printf("Trying '%s'\n", outname); + } + ret = stat(outname, &stat_buf); + if(ret == 0) { + /* got it! */ + return; + } + cur_name_ptr++; + } + path_ptr++; + } + + outname[0] = 0; + if(ok_if_missing > 0) { + return; + } + + /* couldn't find it, print out all the attempts */ + path_ptr = save_path_ptr; + fatal_printf("Could not find required file \"%s\" in any of these " + "directories:\n", *name_ptr); + while(*path_ptr) { + fatal_printf(" %s\n", *path_ptr++); + } + + if(can_create_file) { + // If we didn't find a file, pick a place to put it. + // Default is the current working directory. +#ifdef MAC + gsport_expand_path(&(local_path[0]), "${0}/../config.txt", 250); +#else + gsport_expand_path(&(local_path[0]), "${PWD}/config.txt", 250); +#endif + strcpy(outname, &(local_path[0])); + // Ask user if it's OK to create the file (or just create it) + x_dialog_create_gsport_conf(*name_ptr); + can_create_file = 0; + + // But clear out the fatal_printfs first + clear_fatal_logs(); + setup_gsport_file(outname, maxlen, ok_if_missing, + can_create_file, name_ptr); + // It's one-level of recursion--it cannot loop since we + // clear can_create_file. + // If it returns, then there was succes and we should get out + return; + } else if(ok_if_missing) { + /* Just show an alert and return if ok_if_missing < 0 */ + x_show_alert(0, 0); + return; + } + + my_exit(2); +} + #endif -Event g_event_list[MAX_EVENTS]; -Event g_event_free; -Event g_event_start; - -void -initialize_events() -{ - int i; - - for(i = 1; i < MAX_EVENTS; i++) { - g_event_list[i-1].next = &g_event_list[i]; - } - g_event_free.next = &g_event_list[0]; - g_event_list[MAX_EVENTS-1].next = 0; - - g_event_start.next = 0; - g_event_start.dcycs = 0.0; - - add_event_entry(DCYCS_IN_16MS, EV_60HZ); -} - -void -check_for_one_event_type(int type) -{ - Event *ptr; - int count; - int depth; - - count = 0; - depth = 0; - ptr = g_event_start.next; - while(ptr != 0) { - depth++; - if(ptr->type == type) { - count++; - if(count != 1) { - halt_printf("in check_for_1, type %d found at " - "depth: %d, count: %d, at %f\n", - type, depth, count, ptr->dcycs); - } - } - ptr = ptr->next; - } -} - - -void -add_event_entry(double dcycs, int type) -{ - Event *this_event; - Event *ptr, *prev_ptr; - int tmp_type; - int done; - - this_event = g_event_free.next; - if(this_event == 0) { - halt_printf("Out of queue entries!\n"); - show_all_events(); - return; - } - g_event_free.next = this_event->next; - - this_event->type = type; - - tmp_type = type & 0xff; - if((dcycs < 0.0) || (dcycs > (g_cur_dcycs + 50*1000*1000.0)) || - ((dcycs < g_cur_dcycs) && (tmp_type != EV_SCAN_INT))) { - halt_printf("add_event: dcycs: %f, type:%05x, cur_dcycs: %f!\n", - dcycs, type, g_cur_dcycs); - dcycs = g_cur_dcycs + 1000.0; - } - - ptr = g_event_start.next; - if(ptr && (dcycs < ptr->dcycs)) { - /* create event before next expected event */ - /* do this by setting HALT_EVENT */ - set_halt(HALT_EVENT); - } - - prev_ptr = &g_event_start; - ptr = g_event_start.next; - - done = 0; - while(!done) { - if(ptr == 0) { - this_event->next = ptr; - this_event->dcycs = dcycs; - prev_ptr->next = this_event; - return; - } else { - if(ptr->dcycs < dcycs) { - /* step across this guy */ - prev_ptr = ptr; - ptr = ptr->next; - } else { - /* go in front of this guy */ - this_event->dcycs = dcycs; - this_event->next = ptr; - prev_ptr->next = this_event; - return; - } - } - } -} - -extern int g_doc_saved_ctl; - -double -remove_event_entry(int type) -{ - Event *ptr, *prev_ptr; - Event *next_ptr; - - ptr = g_event_start.next; - prev_ptr = &g_event_start; - - while(ptr != 0) { - if((ptr->type & 0xffff) == type) { - /* got it, remove it */ - next_ptr = ptr->next; - prev_ptr->next = next_ptr; - - /* Add ptr to free list */ - ptr->next = g_event_free.next; - g_event_free.next = ptr; - - return ptr->dcycs; - } - prev_ptr = ptr; - ptr = ptr->next; - } - - halt_printf("remove event_entry: %08x, but not found!\n", type); - if((type & 0xff) == EV_DOC_INT) { - printf("DOC, g_doc_saved_ctl = %02x\n", g_doc_saved_ctl); - } -#ifdef HPUX - U_STACK_TRACE(); -#endif - show_all_events(); - - return 0.0; -} - -void -add_event_stop(double dcycs) -{ - add_event_entry(dcycs, EV_STOP); -} - -void -add_event_doc(double dcycs, int osc) -{ - if(dcycs < g_cur_dcycs) { - dcycs = g_cur_dcycs; -#if 0 - halt_printf("add_event_doc: dcycs: %f, cur_dcycs: %f\n", - dcycs, g_cur_dcycs); -#endif - } - - add_event_entry(dcycs, EV_DOC_INT + (osc << 8)); -} - -void -add_event_scc(double dcycs, int type) -{ - if(dcycs < g_cur_dcycs) { - dcycs = g_cur_dcycs; - } - - add_event_entry(dcycs, EV_SCC + (type << 8)); -} - -void -add_event_vbl() -{ - double dcycs; - - dcycs = g_last_vbl_dcycs + (DCYCS_IN_16MS * (192.0/262.0)); - add_event_entry(dcycs, EV_VBL_INT); -} - -void -add_event_vid_upd(int line) -{ - double dcycs; - - dcycs = g_last_vbl_dcycs + ((DCYCS_IN_16MS * line) / 262.0); - add_event_entry(dcycs, EV_VID_UPD + (line << 8)); -} - -double -remove_event_doc(int osc) -{ - return remove_event_entry(EV_DOC_INT + (osc << 8)); -} - -double -remove_event_scc(int type) -{ - return remove_event_entry(EV_SCC + (type << 8)); -} - -void -show_all_events() -{ - Event *ptr; - int count; - double dcycs; - - count = 0; - ptr = g_event_start.next; - while(ptr != 0) { - dcycs = ptr->dcycs; - printf("Event: %02x: type: %05x, dcycs: %f (%f)\n", - count, ptr->type, dcycs, dcycs - g_cur_dcycs); - ptr = ptr->next; - count++; - } - -} - -word32 g_vbl_count = 0; -int g_vbl_index_count = 0; -double dtime_array[60]; -double g_dadjcycs_array[60]; -double g_dtime_diff3_array[60]; -double g_dtime_this_vbl_array[60]; -double g_dtime_exp_array[60]; -double g_dtime_pmhz_array[60]; -double g_dtime_eff_pmhz_array[60]; -int g_limit_speed = 2; -double sim_time[60]; -double g_sim_sum = 0.0; - -double g_cur_sim_dtime = 0.0; -double g_projected_pmhz = 1.0; -double g_zip_pmhz = 8.0; -double g_sim_mhz = 100.0; -int g_line_ref_amt = 1; -int g_video_line_update_interval = 0; - -Fplus g_recip_projected_pmhz_slow; -Fplus g_recip_projected_pmhz_fast; -Fplus g_recip_projected_pmhz_zip; -Fplus g_recip_projected_pmhz_unl; - -void -show_pmhz() -{ - printf("Pmhz: %f, c036:%02x, limit: %d\n", - g_projected_pmhz, g_c036_val_speed, g_limit_speed); - -} - -void -setup_zip_speeds() -{ - double frecip; - double fmhz; - int mult; - - mult = 16 - ((g_zipgs_reg_c05a >> 4) & 0xf); - // 16 = full speed, 1 = 1/16th speed - fmhz = (8.0 * mult) / 16.0; -#if 0 - if(mult == 16) { - /* increase full speed by 19% to make zipgs freq measuring */ - /* programs work correctly */ - fmhz = fmhz * 1.19; - } -#endif - frecip = 1.0 / fmhz; - g_zip_pmhz = fmhz; - g_recip_projected_pmhz_zip.plus_1 = frecip; - g_recip_projected_pmhz_zip.plus_2 = 2.0 * frecip; - g_recip_projected_pmhz_zip.plus_3 = 3.0 * frecip; - if(frecip >= 0.5) { - g_recip_projected_pmhz_zip.plus_x_minus_1 = 1.01; - } else { - g_recip_projected_pmhz_zip.plus_x_minus_1 = 1.01 - frecip; - } -} - -void -run_prog() -{ - Fplus *fplus_ptr; - Event *this_event; - Event *db1; - double dcycs; - double now_dtime; - double prev_dtime; - double prerun_fcycles; - double fspeed_mult; - double fcycles_stop; - word32 ret; - word32 zip_speed_0tof, zip_speed_0tof_new; - int zip_en, zip_follow_cps; - int type; - int motor_on; - int iwm_1; - int iwm_25; - int limit_speed; - int apple35_sel; - int fast, zip_speed, faster_than_28, unl_speed; - int this_type; - - fflush(stdout); - - g_cur_sim_dtime = 0.0; - - g_recip_projected_pmhz_slow.plus_1 = 1.0; - g_recip_projected_pmhz_slow.plus_2 = 2.0; - g_recip_projected_pmhz_slow.plus_3 = 3.0; - g_recip_projected_pmhz_slow.plus_x_minus_1 = 0.9; - - g_recip_projected_pmhz_fast.plus_1 = (1.0 / 2.5); - g_recip_projected_pmhz_fast.plus_2 = (2.0 / 2.5); - g_recip_projected_pmhz_fast.plus_3 = (3.0 / 2.5); - g_recip_projected_pmhz_fast.plus_x_minus_1 = (1.98 - (1.0/2.5)); - - zip_speed_0tof = g_zipgs_reg_c05a & 0xf0; - setup_zip_speeds(); - - if(engine.fplus_ptr == 0) { - g_recip_projected_pmhz_unl = g_recip_projected_pmhz_slow; - } - - while(1) { - fflush(stdout); +Event g_event_list[MAX_EVENTS]; +Event g_event_free; +Event g_event_start; + +void +initialize_events() +{ + int i; + + for(i = 1; i < MAX_EVENTS; i++) { + g_event_list[i-1].next = &g_event_list[i]; + } + g_event_free.next = &g_event_list[0]; + g_event_list[MAX_EVENTS-1].next = 0; + + g_event_start.next = 0; + g_event_start.dcycs = 0.0; + + add_event_entry(DCYCS_IN_16MS, EV_60HZ); +} + +void +check_for_one_event_type(int type) +{ + Event *ptr; + int count; + int depth; + + count = 0; + depth = 0; + ptr = g_event_start.next; + while(ptr != 0) { + depth++; + if(ptr->type == type) { + count++; + if(count != 1) { + halt_printf("in check_for_1, type %d found at " + "depth: %d, count: %d, at %f\n", + type, depth, count, ptr->dcycs); + } + } + ptr = ptr->next; + } +} + + +void +add_event_entry(double dcycs, int type) +{ + Event *this_event; + Event *ptr, *prev_ptr; + int tmp_type; + int done; + + this_event = g_event_free.next; + if(this_event == 0) { + halt_printf("Out of queue entries!\n"); + show_all_events(); + return; + } + g_event_free.next = this_event->next; + + this_event->type = type; + + tmp_type = type & 0xff; + if((dcycs < 0.0) || (dcycs > (g_cur_dcycs + 50*1000*1000.0)) || + ((dcycs < g_cur_dcycs) && (tmp_type != EV_SCAN_INT))) { + halt_printf("add_event: dcycs: %f, type:%05x, cur_dcycs: %f!\n", + dcycs, type, g_cur_dcycs); + dcycs = g_cur_dcycs + 1000.0; + } + + ptr = g_event_start.next; + if(ptr && (dcycs < ptr->dcycs)) { + /* create event before next expected event */ + /* do this by setting HALT_EVENT */ + set_halt(HALT_EVENT); + } + + prev_ptr = &g_event_start; + ptr = g_event_start.next; + + done = 0; + while(!done) { + if(ptr == 0) { + this_event->next = ptr; + this_event->dcycs = dcycs; + prev_ptr->next = this_event; + return; + } else { + if(ptr->dcycs < dcycs) { + /* step across this guy */ + prev_ptr = ptr; + ptr = ptr->next; + } else { + /* go in front of this guy */ + this_event->dcycs = dcycs; + this_event->next = ptr; + prev_ptr->next = this_event; + return; + } + } + } +} + +extern int g_doc_saved_ctl; + +double +remove_event_entry(int type) +{ + Event *ptr, *prev_ptr; + Event *next_ptr; + + ptr = g_event_start.next; + prev_ptr = &g_event_start; + + while(ptr != 0) { + if((ptr->type & 0xffff) == type) { + /* got it, remove it */ + next_ptr = ptr->next; + prev_ptr->next = next_ptr; + + /* Add ptr to free list */ + ptr->next = g_event_free.next; + g_event_free.next = ptr; + + return ptr->dcycs; + } + prev_ptr = ptr; + ptr = ptr->next; + } + + halt_printf("remove event_entry: %08x, but not found!\n", type); + if((type & 0xff) == EV_DOC_INT) { + printf("DOC, g_doc_saved_ctl = %02x\n", g_doc_saved_ctl); + } +#ifdef HPUX + U_STACK_TRACE(); +#endif + show_all_events(); + + return 0.0; +} + +void +add_event_stop(double dcycs) +{ + add_event_entry(dcycs, EV_STOP); +} + +void +add_event_doc(double dcycs, int osc) +{ + if(dcycs < g_cur_dcycs) { + dcycs = g_cur_dcycs; +#if 0 + halt_printf("add_event_doc: dcycs: %f, cur_dcycs: %f\n", + dcycs, g_cur_dcycs); +#endif + } + + add_event_entry(dcycs, EV_DOC_INT + (osc << 8)); +} + +void +add_event_scc(double dcycs, int type) +{ + if(dcycs < g_cur_dcycs) { + dcycs = g_cur_dcycs; + } + + add_event_entry(dcycs, EV_SCC + (type << 8)); +} + +void +add_event_vbl() +{ + double dcycs; + + dcycs = g_last_vbl_dcycs + (DCYCS_IN_16MS * (192.0/262.0)); + add_event_entry(dcycs, EV_VBL_INT); +} + +void +add_event_vid_upd(int line) +{ + double dcycs; + + dcycs = g_last_vbl_dcycs + ((DCYCS_IN_16MS * line) / 262.0); + add_event_entry(dcycs, EV_VID_UPD + (line << 8)); +} + +double +remove_event_doc(int osc) +{ + return remove_event_entry(EV_DOC_INT + (osc << 8)); +} + +double +remove_event_scc(int type) +{ + return remove_event_entry(EV_SCC + (type << 8)); +} + +void +show_all_events() +{ + Event *ptr; + int count; + double dcycs; + + count = 0; + ptr = g_event_start.next; + while(ptr != 0) { + dcycs = ptr->dcycs; + printf("Event: %02x: type: %05x, dcycs: %f (%f)\n", + count, ptr->type, dcycs, dcycs - g_cur_dcycs); + ptr = ptr->next; + count++; + } + +} + +word32 g_vbl_count = 0; +int g_vbl_index_count = 0; +double dtime_array[60]; +double g_dadjcycs_array[60]; +double g_dtime_diff3_array[60]; +double g_dtime_this_vbl_array[60]; +double g_dtime_exp_array[60]; +double g_dtime_pmhz_array[60]; +double g_dtime_eff_pmhz_array[60]; +int g_limit_speed = 2; +double sim_time[60]; +double g_sim_sum = 0.0; + +double g_cur_sim_dtime = 0.0; +double g_projected_pmhz = 1.0; +double g_zip_pmhz = 8.0; +double g_sim_mhz = 100.0; +int g_line_ref_amt = 1; +int g_video_line_update_interval = 0; + +Fplus g_recip_projected_pmhz_slow; +Fplus g_recip_projected_pmhz_fast; +Fplus g_recip_projected_pmhz_zip; +Fplus g_recip_projected_pmhz_unl; + +void +show_pmhz() +{ + printf("Pmhz: %f, c036:%02x, limit: %d\n", + g_projected_pmhz, g_c036_val_speed, g_limit_speed); + +} + +void +setup_zip_speeds() +{ + double frecip; + double fmhz; + int mult; + + mult = 16 - ((g_zipgs_reg_c05a >> 4) & 0xf); + // 16 = full speed, 1 = 1/16th speed + fmhz = (8.0 * mult) / 16.0; +#if 0 + if(mult == 16) { + /* increase full speed by 19% to make zipgs freq measuring */ + /* programs work correctly */ + fmhz = fmhz * 1.19; + } +#endif + frecip = 1.0 / fmhz; + g_zip_pmhz = fmhz; + g_recip_projected_pmhz_zip.plus_1 = frecip; + g_recip_projected_pmhz_zip.plus_2 = 2.0 * frecip; + g_recip_projected_pmhz_zip.plus_3 = 3.0 * frecip; + if(frecip >= 0.5) { + g_recip_projected_pmhz_zip.plus_x_minus_1 = 1.01; + } else { + g_recip_projected_pmhz_zip.plus_x_minus_1 = 1.01 - frecip; + } +} + +void +run_prog() +{ + Fplus *fplus_ptr; + Event *this_event; + Event *db1; + double dcycs; + double now_dtime; + double prev_dtime; + double prerun_fcycles; + double fspeed_mult; + double fcycles_stop; + word32 ret; + word32 zip_speed_0tof, zip_speed_0tof_new; + int zip_en, zip_follow_cps; + int type; + int motor_on; + int iwm_1; + int iwm_25; + int limit_speed; + int apple35_sel; + int fast, zip_speed, faster_than_28, unl_speed; + int this_type; + + fflush(stdout); + + g_cur_sim_dtime = 0.0; + + g_recip_projected_pmhz_slow.plus_1 = 1.0; + g_recip_projected_pmhz_slow.plus_2 = 2.0; + g_recip_projected_pmhz_slow.plus_3 = 3.0; + g_recip_projected_pmhz_slow.plus_x_minus_1 = 0.9; + + g_recip_projected_pmhz_fast.plus_1 = (1.0 / 2.5); + g_recip_projected_pmhz_fast.plus_2 = (2.0 / 2.5); + g_recip_projected_pmhz_fast.plus_3 = (3.0 / 2.5); + g_recip_projected_pmhz_fast.plus_x_minus_1 = (1.98 - (1.0/2.5)); + + zip_speed_0tof = g_zipgs_reg_c05a & 0xf0; + setup_zip_speeds(); + + if(engine.fplus_ptr == 0) { + g_recip_projected_pmhz_unl = g_recip_projected_pmhz_slow; + } + + while(1) { + fflush(stdout); // OG Disabling control panel #ifndef ACTIVEGS - if(g_config_control_panel) { - config_control_panel(); - } + if(g_config_control_panel) { + config_control_panel(); + } #endif - if(g_irq_pending && !(engine.psr & 0x4)) { - irq_printf("taking an irq!\n"); - take_irq(0); - /* Interrupt! */ - } - - motor_on = g_iwm_motor_on; - limit_speed = g_limit_speed; - apple35_sel = g_c031_disk35 & 0x40; - zip_en = ((g_zipgs_reg_c05b & 0x10) == 0); - zip_follow_cps = ((g_zipgs_reg_c059 & 0x8) != 0); - zip_speed_0tof_new = g_zipgs_reg_c05a & 0xf0; - fast = (g_c036_val_speed & 0x80) || (zip_en && !zip_follow_cps); + if(g_irq_pending && !(engine.psr & 0x4)) { + irq_printf("taking an irq!\n"); + take_irq(0); + /* Interrupt! */ + } + + motor_on = g_iwm_motor_on; + limit_speed = g_limit_speed; + apple35_sel = g_c031_disk35 & 0x40; + zip_en = ((g_zipgs_reg_c05b & 0x10) == 0); + zip_follow_cps = ((g_zipgs_reg_c059 & 0x8) != 0); + zip_speed_0tof_new = g_zipgs_reg_c05a & 0xf0; + fast = (g_c036_val_speed & 0x80) || (zip_en && !zip_follow_cps); // OG Make fast parameter public g_speed_fast = fast; - if(zip_speed_0tof_new != zip_speed_0tof) { - zip_speed_0tof = zip_speed_0tof_new; - setup_zip_speeds(); - } - - iwm_1 = motor_on && !apple35_sel && - (g_c036_val_speed & 0x4) && - (g_slow_525_emul_wr || !g_fast_disk_emul); - iwm_25 = (motor_on && apple35_sel) && !g_fast_disk_emul; - faster_than_28 = fast && (!iwm_1 && !iwm_25) && zip_en && - ((limit_speed == 0) || (limit_speed == 3)); - zip_speed = faster_than_28 && - ((zip_speed_0tof != 0) || (limit_speed == 3) || - (g_zipgs_unlock >= 4) ); + if(zip_speed_0tof_new != zip_speed_0tof) { + zip_speed_0tof = zip_speed_0tof_new; + setup_zip_speeds(); + } + + iwm_1 = motor_on && !apple35_sel && + (g_c036_val_speed & 0x4) && + (g_slow_525_emul_wr || !g_fast_disk_emul); + iwm_25 = (motor_on && apple35_sel) && !g_fast_disk_emul; + faster_than_28 = fast && (!iwm_1 && !iwm_25) && zip_en && + ((limit_speed == 0) || (limit_speed == 3)); + zip_speed = faster_than_28 && + ((zip_speed_0tof != 0) || (limit_speed == 3) || + (g_zipgs_unlock >= 4) ); // OG unlimited speed should not be affected by zip. // unl_speed = faster_than_28 && !zip_speed; unl_speed = (limit_speed == 0) && faster_than_28; - if(unl_speed) { - /* use unlimited speed */ - fspeed_mult = g_projected_pmhz; - fplus_ptr = &g_recip_projected_pmhz_unl; - } else if(zip_speed) { - fspeed_mult = g_zip_pmhz; - fplus_ptr = &g_recip_projected_pmhz_zip; - } else if(fast && !iwm_1 && !(limit_speed == 1)) { - fspeed_mult = 2.5; - fplus_ptr = &g_recip_projected_pmhz_fast; - } else { - /* else run slow */ - fspeed_mult = 1.0; - fplus_ptr = &g_recip_projected_pmhz_slow; - } - - engine.fplus_ptr = fplus_ptr; - - this_type = g_event_start.next->type; - - prerun_fcycles = g_cur_dcycs - g_last_vbl_dcycs; - engine.fcycles = prerun_fcycles; - fcycles_stop = (g_event_start.next->dcycs - g_last_vbl_dcycs) + - 0.001; - if(g_stepping) { - fcycles_stop = prerun_fcycles; - } - g_fcycles_stop = fcycles_stop; - -#if 0 - printf("Enter engine, fcycs: %f, stop: %f\n", - prerun_fcycles, fcycles_stop); - printf("g_cur_dcycs: %f, last_vbl_dcyc: %f\n", g_cur_dcycs, - g_last_vbl_dcycs); -#endif - - g_num_enter_engine++; - prev_dtime = get_dtime(); - - ret = enter_engine(&engine); - - now_dtime = get_dtime(); - - g_cur_sim_dtime += (now_dtime - prev_dtime); - - dcycs = g_last_vbl_dcycs + (double)(engine.fcycles); - - g_dadjcycs += (engine.fcycles - prerun_fcycles) * - fspeed_mult; - -#if 0 - printf("...back, engine.fcycles: %f, dcycs: %f\n", - (double)engine.fcycles, dcycs); -#endif - - g_cur_dcycs = dcycs; - - if(ret != 0) { - g_engine_action++; - handle_action(ret); - } - - if(halt_sim == HALT_EVENT) { - g_engine_halt_event++; - /* if we needed to stop to check for interrupts, */ - /* clear halt */ - halt_sim = 0; - } - -#if 0 - if(!g_testing && run_cycles < -2000000) { - halt_printf("run_cycles: %d, cycles: %d\n", run_cycles, - cycles); - printf("this_type: %05x\n", this_type); - printf("duff_cycles: %d\n", duff_cycles); - printf("start.next->rel_time: %d, type: %05x\n", - g_event_start.next->rel_time, - g_event_start.next->type); - } -#endif - - this_event = g_event_start.next; - while(dcycs >= this_event->dcycs) { - /* Pop this guy off of the queue */ - g_event_start.next = this_event->next; - - type = this_event->type; - this_event->next = g_event_free.next; - g_event_free.next = this_event; - switch(type & 0xff) { - case EV_60HZ: - update_60hz(dcycs, now_dtime); - break; - case EV_STOP: - printf("type: EV_STOP\n"); - printf("next: %p, dcycs: %f\n", - g_event_start.next, dcycs); - db1 = g_event_start.next; - halt_printf("next.dcycs: %f\n", db1->dcycs); - break; - case EV_SCAN_INT: - g_engine_scan_int++; - irq_printf("type: scan int\n"); - do_scan_int(dcycs, type >> 8); - break; - case EV_DOC_INT: - g_engine_doc_int++; - doc_handle_event(type >> 8, dcycs); - break; - case EV_VBL_INT: - do_vbl_int(); - break; - case EV_SCC: - do_scc_event(type >> 8, dcycs); - break; - case EV_VID_UPD: - video_update_event_line(type >> 8); - break; - default: - printf("Unknown event: %d!\n", type); - exit(3); - } - - this_event = g_event_start.next; - - } - - if(g_event_start.next == 0) { - halt_printf("ERROR...run_prog, event_start.n=0!\n"); - } - -#if 0 - if(!g_testing && g_event_start.next->rel_time > 2000000) { - printf("Z:start.next->rel_time: %d, duff_cycles: %d\n", - g_event_start.next->rel_time, duff_cycles); - halt_printf("Zrun_cycles:%d, cycles:%d\n", run_cycles, - cycles); - - show_all_events(); - } -#endif - - if(halt_sim != 0 && halt_sim != HALT_EVENT) { - break; - } - if(g_stepping) { - break; - } - } - - if(!g_testing) { - printf("leaving run_prog, halt_sim:%d\n", halt_sim); - } - - x_auto_repeat_on(0); -} - -void -add_irq(word32 irq_mask) -{ - if(g_irq_pending & irq_mask) { - /* Already requested, just get out */ - return; - } - g_irq_pending |= irq_mask; - set_halt(HALT_EVENT); -} - -void -remove_irq(word32 irq_mask) -{ - g_irq_pending = g_irq_pending & (~irq_mask); -} - -void -take_irq(int is_it_brk) -{ - word32 new_kpc; - word32 va; - - irq_printf("Taking irq, at: %02x/%04x, psw: %02x, dcycs: %f\n", - engine.kpc>>16, engine.kpc & 0xffff, engine.psr, - g_cur_dcycs); - - g_num_irq++; - if(g_wait_pending) { - /* step over WAI instruction */ - engine.kpc++; - g_wait_pending = 0; - } - - if(engine.psr & 0x100) { - /* Emulation */ - set_memory_c(engine.stack, (engine.kpc >> 8) & 0xff, 0); - engine.stack = ((engine.stack -1) & 0xff) + 0x100; - - set_memory_c(engine.stack, engine.kpc & 0xff, 0); - engine.stack = ((engine.stack -1) & 0xff) + 0x100; - - set_memory_c(engine.stack, - (engine.psr & 0xef)|(is_it_brk<<4),0); - /* Clear B bit in psr on stack */ - engine.stack = ((engine.stack -1) & 0xff) + 0x100; - - va = 0xfffffe; - if(g_c035_shadow_reg & 0x40) { - /* I/O shadowing off...use ram locs */ - va = 0x00fffe; - } - - } else { - /* native */ - set_memory_c(engine.stack, (engine.kpc >> 16) & 0xff, 0); - engine.stack = ((engine.stack -1) & 0xffff); - - set_memory_c(engine.stack, (engine.kpc >> 8) & 0xff, 0); - engine.stack = ((engine.stack -1) & 0xffff); - - set_memory_c(engine.stack, engine.kpc & 0xff, 0); - engine.stack = ((engine.stack -1) & 0xffff); - - set_memory_c(engine.stack, engine.psr & 0xff, 0); - engine.stack = ((engine.stack -1) & 0xffff); - - if(is_it_brk) { - /* break */ - va = 0xffffe6; - if(g_c035_shadow_reg & 0x40) { - va = 0xffe6; - } - } else { - /* irq */ - va = 0xffffee; - if(g_c035_shadow_reg & 0x40) { - va = 0xffee; - } - } - - } - - new_kpc = get_memory_c(va, 0); - new_kpc = new_kpc + (get_memory_c(va+1, 0) << 8); - - engine.psr = ((engine.psr & 0x1f3) | 0x4); - - engine.kpc = new_kpc; - HALT_ON(HALT_ON_IRQ, "Halting on IRQ\n"); - -} - -double g_dtime_last_vbl = 0.0; -double g_dtime_expected = (1.0/60.0); - -int g_scan_int_events = 0; - - - -void -show_dtime_array() -{ - double dfirst_time; - double first_total_cycs; - int i; - int pos; - - dfirst_time = 0.0; - first_total_cycs = 0.0; - - - for(i = 0; i < 60; i++) { - pos = (g_vbl_index_count + i) % 60; - printf("%2d:%2d dt:%.5f adjc:%9.1f this_vbl:%.6f " - "exp:%.5f p:%2.2f ep:%2.2f\n", - i, pos, - dtime_array[pos] - dfirst_time, - g_dadjcycs_array[pos] - first_total_cycs, - g_dtime_this_vbl_array[pos], - g_dtime_exp_array[pos] - dfirst_time, - g_dtime_pmhz_array[pos], - g_dtime_eff_pmhz_array[pos]); - dfirst_time = dtime_array[pos]; - first_total_cycs = g_dadjcycs_array[pos]; - } -} - -extern word32 g_cycs_in_40col; -extern word32 g_cycs_in_xredraw; -extern word32 g_cycs_in_check_input; -extern word32 g_cycs_in_refresh_line; -extern word32 g_cycs_in_refresh_ximage; -extern word32 g_cycs_in_io_read; -extern word32 g_cycs_in_sound1; -extern word32 g_cycs_in_sound2; -extern word32 g_cycs_in_sound3; -extern word32 g_cycs_in_sound4; -extern word32 g_cycs_in_start_sound; -extern word32 g_cycs_in_est_sound; -extern word32 g_refresh_bytes_xfer; - -extern int g_num_snd_plays; -extern int g_num_doc_events; -extern int g_num_start_sounds; -extern int g_num_scan_osc; -extern int g_num_recalc_snd_parms; -extern float g_fvoices; - -extern int g_doc_vol; -extern int g_a2vid_palette; - -extern int g_status_refresh_needed; - - -void -update_60hz(double dcycs, double dtime_now) -{ - register word32 end_time; - char status_buf[1024]; - char sim_mhz_buf[128]; - char total_mhz_buf[128]; - char *sim_mhz_ptr, *total_mhz_ptr; - char *code_str1, *code_str2, *sp_str; - double eff_pmhz; - double planned_dcycs; - double predicted_pmhz; - double recip_predicted_pmhz; - double dtime_this_vbl_sim; - double dtime_diff_1sec; - double dratio; - double dtime_till_expected; - double dtime_diff; - double dtime_this_vbl; - double dadjcycs_this_vbl; - double dadj_cycles_1sec; - double dtmp1, dtmp2, dtmp3, dtmp4, dtmp5; - double dnatcycs_1sec; - int tmp; - int doit_3_persec; - int cur_vbl_index; - int prev_vbl_index; - - g_vbl_count++; - - /* NOTE: this event is defined to occur before line 0 */ - /* It's actually happening at the start of the border for line (-1) */ - /* All other timings should be adjusted for this */ - - irq_printf("vbl_60hz: vbl: %d, dcycs: %f, last_vbl_dcycs: %f\n", - g_vbl_count, dcycs, g_last_vbl_dcycs); - - planned_dcycs = DCYCS_IN_16MS; - - g_last_vbl_dcycs = g_last_vbl_dcycs + planned_dcycs; - - add_event_entry(g_last_vbl_dcycs + planned_dcycs, EV_60HZ); - check_for_one_event_type(EV_60HZ); - - cur_vbl_index = g_vbl_index_count; - - /* figure out dtime spent running SIM, not all the overhead */ - dtime_this_vbl_sim = g_cur_sim_dtime; - g_cur_sim_dtime = 0.0; - g_sim_sum = g_sim_sum - sim_time[cur_vbl_index] + dtime_this_vbl_sim; - sim_time[cur_vbl_index] = dtime_this_vbl_sim; - - dadj_cycles_1sec = g_dadjcycs - g_dadjcycs_array[cur_vbl_index]; - - /* dtime_diff_1sec is dtime total spent over the last 60 ticks */ - dtime_diff_1sec = dtime_now - dtime_array[cur_vbl_index]; - - dtime_array[cur_vbl_index] = dtime_now; - g_dadjcycs_array[cur_vbl_index] = g_dadjcycs; - - prev_vbl_index = cur_vbl_index; - cur_vbl_index = prev_vbl_index + 1; - if(cur_vbl_index >= 60) { - cur_vbl_index = 0; - } - g_vbl_index_count = cur_vbl_index; - - GET_ITIMER(end_time); - g_dnatcycs_1sec += (double)(end_time - g_natcycs_lastvbl); - g_natcycs_lastvbl = end_time; - - if(prev_vbl_index == 0) { - if(g_sim_sum < (1.0/250.0)) { - sim_mhz_ptr = "???"; - g_sim_mhz = 250.0; - } else { - g_sim_mhz = (dadj_cycles_1sec / g_sim_sum) / - (1000.0*1000.0); - sprintf(sim_mhz_buf, "%6.2f", g_sim_mhz); - sim_mhz_ptr = sim_mhz_buf; - } - if(dtime_diff_1sec < (1.0/250.0)) { - total_mhz_ptr = "???"; - } else { - sprintf(total_mhz_buf, "%6.2f", - (dadj_cycles_1sec / dtime_diff_1sec) / - (1000000.0)); - total_mhz_ptr = total_mhz_buf; - } - - switch(g_limit_speed) { - case 1: sp_str = "1Mhz"; break; - case 2: sp_str = "2.8Mhz"; break; - case 3: sp_str = "8.0Mhz"; break; - default: sp_str = "Unlimited"; break; - } - + if(unl_speed) { + /* use unlimited speed */ + fspeed_mult = g_projected_pmhz; + fplus_ptr = &g_recip_projected_pmhz_unl; + } else if(zip_speed) { + fspeed_mult = g_zip_pmhz; + fplus_ptr = &g_recip_projected_pmhz_zip; + } else if(fast && !iwm_1 && !(limit_speed == 1)) { + fspeed_mult = 2.5; + fplus_ptr = &g_recip_projected_pmhz_fast; + } else { + /* else run slow */ + fspeed_mult = 1.0; + fplus_ptr = &g_recip_projected_pmhz_slow; + } + + engine.fplus_ptr = fplus_ptr; + + this_type = g_event_start.next->type; + + prerun_fcycles = g_cur_dcycs - g_last_vbl_dcycs; + engine.fcycles = prerun_fcycles; + fcycles_stop = (g_event_start.next->dcycs - g_last_vbl_dcycs) + + 0.001; + if(g_stepping) { + fcycles_stop = prerun_fcycles; + } + g_fcycles_stop = fcycles_stop; + +#if 0 + printf("Enter engine, fcycs: %f, stop: %f\n", + prerun_fcycles, fcycles_stop); + printf("g_cur_dcycs: %f, last_vbl_dcyc: %f\n", g_cur_dcycs, + g_last_vbl_dcycs); +#endif + + g_num_enter_engine++; + prev_dtime = get_dtime(); + + ret = enter_engine(&engine); + + now_dtime = get_dtime(); + + g_cur_sim_dtime += (now_dtime - prev_dtime); + + dcycs = g_last_vbl_dcycs + (double)(engine.fcycles); + + g_dadjcycs += (engine.fcycles - prerun_fcycles) * + fspeed_mult; + +#if 0 + printf("...back, engine.fcycles: %f, dcycs: %f\n", + (double)engine.fcycles, dcycs); +#endif + + g_cur_dcycs = dcycs; + + if(ret != 0) { + g_engine_action++; + handle_action(ret); + } + + if(halt_sim == HALT_EVENT) { + g_engine_halt_event++; + /* if we needed to stop to check for interrupts, */ + /* clear halt */ + halt_sim = 0; + } + +#if 0 + if(!g_testing && run_cycles < -2000000) { + halt_printf("run_cycles: %d, cycles: %d\n", run_cycles, + cycles); + printf("this_type: %05x\n", this_type); + printf("duff_cycles: %d\n", duff_cycles); + printf("start.next->rel_time: %d, type: %05x\n", + g_event_start.next->rel_time, + g_event_start.next->type); + } +#endif + + this_event = g_event_start.next; + while(dcycs >= this_event->dcycs) { + /* Pop this guy off of the queue */ + g_event_start.next = this_event->next; + + type = this_event->type; + this_event->next = g_event_free.next; + g_event_free.next = this_event; + switch(type & 0xff) { + case EV_60HZ: + update_60hz(dcycs, now_dtime); + break; + case EV_STOP: + printf("type: EV_STOP\n"); + printf("next: %p, dcycs: %f\n", + g_event_start.next, dcycs); + db1 = g_event_start.next; + halt_printf("next.dcycs: %f\n", db1->dcycs); + break; + case EV_SCAN_INT: + g_engine_scan_int++; + irq_printf("type: scan int\n"); + do_scan_int(dcycs, type >> 8); + break; + case EV_DOC_INT: + g_engine_doc_int++; + doc_handle_event(type >> 8, dcycs); + break; + case EV_VBL_INT: + do_vbl_int(); + break; + case EV_SCC: + do_scc_event(type >> 8, dcycs); + break; + case EV_VID_UPD: + video_update_event_line(type >> 8); + break; + default: + printf("Unknown event: %d!\n", type); + exit(3); + } + + this_event = g_event_start.next; + + } + + if(g_event_start.next == 0) { + halt_printf("ERROR...run_prog, event_start.n=0!\n"); + } + +#if 0 + if(!g_testing && g_event_start.next->rel_time > 2000000) { + printf("Z:start.next->rel_time: %d, duff_cycles: %d\n", + g_event_start.next->rel_time, duff_cycles); + halt_printf("Zrun_cycles:%d, cycles:%d\n", run_cycles, + cycles); + + show_all_events(); + } +#endif + + if(halt_sim != 0 && halt_sim != HALT_EVENT) { + break; + } + if(g_stepping) { + break; + } + } + + if(!g_testing) { + printf("leaving run_prog, halt_sim:%d\n", halt_sim); + } + + x_auto_repeat_on(0); +} + +void +add_irq(word32 irq_mask) +{ + if(g_irq_pending & irq_mask) { + /* Already requested, just get out */ + return; + } + g_irq_pending |= irq_mask; + set_halt(HALT_EVENT); +} + +void +remove_irq(word32 irq_mask) +{ + g_irq_pending = g_irq_pending & (~irq_mask); +} + +void +take_irq(int is_it_brk) +{ + word32 new_kpc; + word32 va; + + irq_printf("Taking irq, at: %02x/%04x, psw: %02x, dcycs: %f\n", + engine.kpc>>16, engine.kpc & 0xffff, engine.psr, + g_cur_dcycs); + + g_num_irq++; + if(g_wait_pending) { + /* step over WAI instruction */ + engine.kpc++; + g_wait_pending = 0; + } + + if(engine.psr & 0x100) { + /* Emulation */ + set_memory_c(engine.stack, (engine.kpc >> 8) & 0xff, 0); + engine.stack = ((engine.stack -1) & 0xff) + 0x100; + + set_memory_c(engine.stack, engine.kpc & 0xff, 0); + engine.stack = ((engine.stack -1) & 0xff) + 0x100; + + set_memory_c(engine.stack, + (engine.psr & 0xef)|(is_it_brk<<4),0); + /* Clear B bit in psr on stack */ + engine.stack = ((engine.stack -1) & 0xff) + 0x100; + + va = 0xfffffe; + if(g_c035_shadow_reg & 0x40) { + /* I/O shadowing off...use ram locs */ + va = 0x00fffe; + } + + } else { + /* native */ + set_memory_c(engine.stack, (engine.kpc >> 16) & 0xff, 0); + engine.stack = ((engine.stack -1) & 0xffff); + + set_memory_c(engine.stack, (engine.kpc >> 8) & 0xff, 0); + engine.stack = ((engine.stack -1) & 0xffff); + + set_memory_c(engine.stack, engine.kpc & 0xff, 0); + engine.stack = ((engine.stack -1) & 0xffff); + + set_memory_c(engine.stack, engine.psr & 0xff, 0); + engine.stack = ((engine.stack -1) & 0xffff); + + if(is_it_brk) { + /* break */ + va = 0xffffe6; + if(g_c035_shadow_reg & 0x40) { + va = 0xffe6; + } + } else { + /* irq */ + va = 0xffffee; + if(g_c035_shadow_reg & 0x40) { + va = 0xffee; + } + } + + } + + new_kpc = get_memory_c(va, 0); + new_kpc = new_kpc + (get_memory_c(va+1, 0) << 8); + + engine.psr = ((engine.psr & 0x1f3) | 0x4); + + engine.kpc = new_kpc; + HALT_ON(HALT_ON_IRQ, "Halting on IRQ\n"); + +} + +double g_dtime_last_vbl = 0.0; +double g_dtime_expected = (1.0/60.0); + +int g_scan_int_events = 0; + + + +void +show_dtime_array() +{ + double dfirst_time; + double first_total_cycs; + int i; + int pos; + + dfirst_time = 0.0; + first_total_cycs = 0.0; + + + for(i = 0; i < 60; i++) { + pos = (g_vbl_index_count + i) % 60; + printf("%2d:%2d dt:%.5f adjc:%9.1f this_vbl:%.6f " + "exp:%.5f p:%2.2f ep:%2.2f\n", + i, pos, + dtime_array[pos] - dfirst_time, + g_dadjcycs_array[pos] - first_total_cycs, + g_dtime_this_vbl_array[pos], + g_dtime_exp_array[pos] - dfirst_time, + g_dtime_pmhz_array[pos], + g_dtime_eff_pmhz_array[pos]); + dfirst_time = dtime_array[pos]; + first_total_cycs = g_dadjcycs_array[pos]; + } +} + +extern word32 g_cycs_in_40col; +extern word32 g_cycs_in_xredraw; +extern word32 g_cycs_in_check_input; +extern word32 g_cycs_in_refresh_line; +extern word32 g_cycs_in_refresh_ximage; +extern word32 g_cycs_in_io_read; +extern word32 g_cycs_in_sound1; +extern word32 g_cycs_in_sound2; +extern word32 g_cycs_in_sound3; +extern word32 g_cycs_in_sound4; +extern word32 g_cycs_in_start_sound; +extern word32 g_cycs_in_est_sound; +extern word32 g_refresh_bytes_xfer; + +extern int g_num_snd_plays; +extern int g_num_doc_events; +extern int g_num_start_sounds; +extern int g_num_scan_osc; +extern int g_num_recalc_snd_parms; +extern float g_fvoices; + +extern int g_doc_vol; +extern int g_a2vid_palette; + +extern int g_status_refresh_needed; + + +void +update_60hz(double dcycs, double dtime_now) +{ + register word32 end_time; + char status_buf[1024]; + char sim_mhz_buf[128]; + char total_mhz_buf[128]; + char *sim_mhz_ptr, *total_mhz_ptr; + char *code_str1, *code_str2, *sp_str; + double eff_pmhz; + double planned_dcycs; + double predicted_pmhz; + double recip_predicted_pmhz; + double dtime_this_vbl_sim; + double dtime_diff_1sec; + double dratio; + double dtime_till_expected; + double dtime_diff; + double dtime_this_vbl; + double dadjcycs_this_vbl; + double dadj_cycles_1sec; + double dtmp1, dtmp2, dtmp3, dtmp4, dtmp5; + double dnatcycs_1sec; + int tmp; + int doit_3_persec; + int cur_vbl_index; + int prev_vbl_index; + + g_vbl_count++; + + /* NOTE: this event is defined to occur before line 0 */ + /* It's actually happening at the start of the border for line (-1) */ + /* All other timings should be adjusted for this */ + + irq_printf("vbl_60hz: vbl: %d, dcycs: %f, last_vbl_dcycs: %f\n", + g_vbl_count, dcycs, g_last_vbl_dcycs); + + planned_dcycs = DCYCS_IN_16MS; + + g_last_vbl_dcycs = g_last_vbl_dcycs + planned_dcycs; + + add_event_entry(g_last_vbl_dcycs + planned_dcycs, EV_60HZ); + check_for_one_event_type(EV_60HZ); + + cur_vbl_index = g_vbl_index_count; + + /* figure out dtime spent running SIM, not all the overhead */ + dtime_this_vbl_sim = g_cur_sim_dtime; + g_cur_sim_dtime = 0.0; + g_sim_sum = g_sim_sum - sim_time[cur_vbl_index] + dtime_this_vbl_sim; + sim_time[cur_vbl_index] = dtime_this_vbl_sim; + + dadj_cycles_1sec = g_dadjcycs - g_dadjcycs_array[cur_vbl_index]; + + /* dtime_diff_1sec is dtime total spent over the last 60 ticks */ + dtime_diff_1sec = dtime_now - dtime_array[cur_vbl_index]; + + dtime_array[cur_vbl_index] = dtime_now; + g_dadjcycs_array[cur_vbl_index] = g_dadjcycs; + + prev_vbl_index = cur_vbl_index; + cur_vbl_index = prev_vbl_index + 1; + if(cur_vbl_index >= 60) { + cur_vbl_index = 0; + } + g_vbl_index_count = cur_vbl_index; + + GET_ITIMER(end_time); + g_dnatcycs_1sec += (double)(end_time - g_natcycs_lastvbl); + g_natcycs_lastvbl = end_time; + + if(prev_vbl_index == 0) { + if(g_sim_sum < (1.0/250.0)) { + sim_mhz_ptr = "???"; + g_sim_mhz = 250.0; + } else { + g_sim_mhz = (dadj_cycles_1sec / g_sim_sum) / + (1000.0*1000.0); + sprintf(sim_mhz_buf, "%6.2f", g_sim_mhz); + sim_mhz_ptr = sim_mhz_buf; + } + if(dtime_diff_1sec < (1.0/250.0)) { + total_mhz_ptr = "???"; + } else { + sprintf(total_mhz_buf, "%6.2f", + (dadj_cycles_1sec / dtime_diff_1sec) / + (1000000.0)); + total_mhz_ptr = total_mhz_buf; + } + + switch(g_limit_speed) { + case 1: sp_str = "1Mhz"; break; + case 2: sp_str = "2.8Mhz"; break; + case 3: sp_str = "8.0Mhz"; break; + default: sp_str = "Unlimited"; break; + } + // OG Pass speed info to the control (ActiveX specific) #ifdef ACTIVEGS { @@ -2131,238 +2130,238 @@ update_60hz(double dcycs, double dtime_now) updateInfo(sp_str,total_mhz_ptr); } #endif - sprintf(status_buf, "dcycs:%9.1f sim MHz:%s " - "Eff MHz:%s, sec:%1.3f vol:%02x pal:%x, Limit:%s", - dcycs/(1000.0*1000.0), sim_mhz_ptr, total_mhz_ptr, - dtime_diff_1sec, g_doc_vol, g_a2vid_palette, - sp_str); - video_update_status_line(0, status_buf); - - if(g_video_line_update_interval == 0) { - if(g_sim_mhz > 12.0) { - /* just set video line_ref_amt to 1 */ - g_line_ref_amt = 1; - } else if(g_line_ref_amt == 1 && g_sim_mhz < 4.0) { - g_line_ref_amt = 8; - } - } else { - g_line_ref_amt = g_video_line_update_interval; - } - - if(g_dnatcycs_1sec < (1000.0*1000.0)) { - /* make it so large that all %'s become 0 */ - g_dnatcycs_1sec = 800.0*1000.0*1000.0*1000.0; - } - dnatcycs_1sec = g_dnatcycs_1sec / 100.0; /* eff mult by 100 */ - - dtmp2 = (double)(g_cycs_in_check_input) / dnatcycs_1sec; - dtmp3 = (double)(g_cycs_in_refresh_line) / dnatcycs_1sec; - dtmp4 = (double)(g_cycs_in_refresh_ximage) / dnatcycs_1sec; - sprintf(status_buf, "xfer:%08x, %5.1f ref_amt:%d " - "ch_in:%4.1f%% ref_l:%4.1f%% ref_x:%4.1f%%", - g_refresh_bytes_xfer, g_dnatcycs_1sec/(1000.0*1000.0), - g_line_ref_amt, dtmp2, dtmp3, dtmp4); - video_update_status_line(1, status_buf); - - sprintf(status_buf, "Ints:%3d I/O:%4dK BRK:%3d COP:%2d " - "Eng:%3d act:%3d hev:%3d esi:%3d edi:%3d", - g_num_irq, g_io_amt>>10, g_num_brk, g_num_cop, - g_num_enter_engine, g_engine_action, - g_engine_halt_event, g_engine_scan_int, - g_engine_doc_int); - video_update_status_line(2, status_buf); - - dtmp1 = (double)(g_cycs_in_sound1) / dnatcycs_1sec; - dtmp2 = (double)(g_cycs_in_sound2) / dnatcycs_1sec; - dtmp3 = (double)(g_cycs_in_sound3) / dnatcycs_1sec; - dtmp4 = (double)(g_cycs_in_start_sound) / dnatcycs_1sec; - dtmp5 = (double)(g_cycs_in_est_sound) / dnatcycs_1sec; - sprintf(status_buf, "snd1:%4.1f%%, 2:%4.1f%%, " - "3:%4.1f%%, st:%4.1f%% est:%4.1f%% %4.2f", - dtmp1, dtmp2, dtmp3, dtmp4, dtmp5, g_fvoices); - video_update_status_line(3, status_buf); - - code_str1 = ""; - code_str2 = ""; - if(g_code_yellow) { - code_str1 = "Code: Yellow"; - code_str2 = "Emulated system state suspect, save work"; - } - if(g_code_red) { - code_str1 = "Code: RED"; - code_str2 = "Emulated system state probably corrupt"; - } - sprintf(status_buf, "snd_plays:%4d, doc_ev:%4d, st_snd:%4d " - "snd_parms: %4d %s", - g_num_snd_plays, g_num_doc_events, g_num_start_sounds, - g_num_recalc_snd_parms, code_str1); - video_update_status_line(4, status_buf); - - draw_iwm_status(5, status_buf); - - sprintf(status_buf, "GSport v%-6s " - "Press F4 for Config Menu %s", - g_gsport_version_str, code_str2); - video_update_status_line(6, status_buf); - - g_status_refresh_needed = 1; - - g_num_irq = 0; - g_num_brk = 0; - g_num_cop = 0; - g_num_enter_engine = 0; - g_io_amt = 0; - g_engine_action = 0; - g_engine_halt_event = 0; - g_engine_scan_int = 0; - g_engine_doc_int = 0; - - g_cycs_in_40col = 0; - g_cycs_in_xredraw = 0; - g_cycs_in_check_input = 0; - g_cycs_in_refresh_line = 0; - g_cycs_in_refresh_ximage = 0; - g_cycs_in_io_read = 0; - g_cycs_in_sound1 = 0; - g_cycs_in_sound2 = 0; - g_cycs_in_sound3 = 0; - g_cycs_in_sound4 = 0; - g_cycs_in_start_sound = 0; - g_cycs_in_est_sound = 0; - g_dnatcycs_1sec = 0.0; - g_refresh_bytes_xfer = 0; - - g_num_snd_plays = 0; - g_num_doc_events = 0; - g_num_start_sounds = 0; - g_num_scan_osc = 0; - g_num_recalc_snd_parms = 0; - - g_fvoices = (float)0.0; - } - - dtime_this_vbl = dtime_now - g_dtime_last_vbl; - if(dtime_this_vbl < 0.001) { - dtime_this_vbl = 0.001; - } - - g_dtime_last_vbl = dtime_now; - - dadjcycs_this_vbl = g_dadjcycs - g_last_vbl_dadjcycs; - g_last_vbl_dadjcycs = g_dadjcycs; - - g_dtime_expected += (1.0/60.0); - - eff_pmhz = ((dadjcycs_this_vbl) / (dtime_this_vbl)) / - DCYCS_1_MHZ; - - /* using eff_pmhz, predict how many cycles can be run by */ - /* g_dtime_expected */ - - dtime_till_expected = g_dtime_expected - dtime_now; - - dratio = 60.0 * dtime_till_expected; - - predicted_pmhz = eff_pmhz * dratio; - - if(! (predicted_pmhz < (1.4 * g_projected_pmhz))) { - predicted_pmhz = 1.4 * g_projected_pmhz; - } - - if(! (predicted_pmhz > (0.7 * g_projected_pmhz))) { - predicted_pmhz = 0.7 * g_projected_pmhz; - } - - if(!(predicted_pmhz >= 1.0)) { - irq_printf("predicted: %f, setting to 1.0\n", predicted_pmhz); - predicted_pmhz = 1.0; - } - - if(!(predicted_pmhz < 250.0)) { - irq_printf("predicted: %f, setting to 250.0\n", predicted_pmhz); - predicted_pmhz = 250.0; - } - - recip_predicted_pmhz = 1.0/predicted_pmhz; - g_projected_pmhz = predicted_pmhz; - - g_recip_projected_pmhz_unl.plus_1 = 1.0*recip_predicted_pmhz; - g_recip_projected_pmhz_unl.plus_2 = 2.0*recip_predicted_pmhz; - g_recip_projected_pmhz_unl.plus_3 = 3.0*recip_predicted_pmhz; - g_recip_projected_pmhz_unl.plus_x_minus_1 = 1.01 - recip_predicted_pmhz; - - if(dtime_till_expected < -0.125) { - /* If we were way off, get back on track */ - /* this happens because our sim took much longer than */ - /* expected, so we're going to skip some VBL */ - irq_printf("adj1: dtexp:%f, dt_new:%f\n", - g_dtime_expected, dtime_now); - - dtime_diff = -dtime_till_expected; - - irq_printf("dtime_till_exp: %f, dtime_diff: %f, dcycs: %f\n", - dtime_till_expected, dtime_diff, dcycs); - - g_dtime_expected += dtime_diff; - } - - if(dtime_till_expected > (3/60.0)) { - /* we're running fast, usleep */ - micro_sleep(dtime_till_expected - (1/60.0)); - } - - g_dtime_this_vbl_array[prev_vbl_index] = dtime_this_vbl; - g_dtime_exp_array[prev_vbl_index] = g_dtime_expected; - g_dtime_pmhz_array[prev_vbl_index] = predicted_pmhz; - g_dtime_eff_pmhz_array[prev_vbl_index] = eff_pmhz; - - - if(g_c041_val & C041_EN_VBL_INTS) { - add_event_vbl(); - } - - g_25sec_cntr++; - if(g_25sec_cntr >= 16) { - g_25sec_cntr = 0; - if(g_c041_val & C041_EN_25SEC_INTS) { - add_irq(IRQ_PENDING_C046_25SEC); - g_c046_val |= 0x10; - irq_printf("Setting c046 .25 sec int, g_irq_pend:%d\n", - g_irq_pending); - } - } - - g_1sec_cntr++; - if(g_1sec_cntr >= 60) { - g_1sec_cntr = 0; - tmp = g_c023_val; - tmp |= 0x40; /* set 1sec int */ - if(tmp & 0x04) { - tmp |= 0x80; - add_irq(IRQ_PENDING_C023_1SEC); - irq_printf("Setting c023 to %02x irq_pend: %d\n", - tmp, g_irq_pending); - } - g_c023_val = tmp; - } - - if(!g_scan_int_events) { - check_scan_line_int(dcycs, 0); - } - - doit_3_persec = 0; - if(g_config_iwm_vbl_count > 0) { - g_config_iwm_vbl_count--; - } else { - g_config_iwm_vbl_count = 20; - doit_3_persec = 1; - } - - iwm_vbl_update(doit_3_persec); + sprintf(status_buf, "dcycs:%9.1f sim MHz:%s " + "Eff MHz:%s, sec:%1.3f vol:%02x pal:%x, Limit:%s", + dcycs/(1000.0*1000.0), sim_mhz_ptr, total_mhz_ptr, + dtime_diff_1sec, g_doc_vol, g_a2vid_palette, + sp_str); + video_update_status_line(0, status_buf); + + if(g_video_line_update_interval == 0) { + if(g_sim_mhz > 12.0) { + /* just set video line_ref_amt to 1 */ + g_line_ref_amt = 1; + } else if(g_line_ref_amt == 1 && g_sim_mhz < 4.0) { + g_line_ref_amt = 8; + } + } else { + g_line_ref_amt = g_video_line_update_interval; + } + + if(g_dnatcycs_1sec < (1000.0*1000.0)) { + /* make it so large that all %'s become 0 */ + g_dnatcycs_1sec = 800.0*1000.0*1000.0*1000.0; + } + dnatcycs_1sec = g_dnatcycs_1sec / 100.0; /* eff mult by 100 */ + + dtmp2 = (double)(g_cycs_in_check_input) / dnatcycs_1sec; + dtmp3 = (double)(g_cycs_in_refresh_line) / dnatcycs_1sec; + dtmp4 = (double)(g_cycs_in_refresh_ximage) / dnatcycs_1sec; + sprintf(status_buf, "xfer:%08x, %5.1f ref_amt:%d " + "ch_in:%4.1f%% ref_l:%4.1f%% ref_x:%4.1f%%", + g_refresh_bytes_xfer, g_dnatcycs_1sec/(1000.0*1000.0), + g_line_ref_amt, dtmp2, dtmp3, dtmp4); + video_update_status_line(1, status_buf); + + sprintf(status_buf, "Ints:%3d I/O:%4dK BRK:%3d COP:%2d " + "Eng:%3d act:%3d hev:%3d esi:%3d edi:%3d", + g_num_irq, g_io_amt>>10, g_num_brk, g_num_cop, + g_num_enter_engine, g_engine_action, + g_engine_halt_event, g_engine_scan_int, + g_engine_doc_int); + video_update_status_line(2, status_buf); + + dtmp1 = (double)(g_cycs_in_sound1) / dnatcycs_1sec; + dtmp2 = (double)(g_cycs_in_sound2) / dnatcycs_1sec; + dtmp3 = (double)(g_cycs_in_sound3) / dnatcycs_1sec; + dtmp4 = (double)(g_cycs_in_start_sound) / dnatcycs_1sec; + dtmp5 = (double)(g_cycs_in_est_sound) / dnatcycs_1sec; + sprintf(status_buf, "snd1:%4.1f%%, 2:%4.1f%%, " + "3:%4.1f%%, st:%4.1f%% est:%4.1f%% %4.2f", + dtmp1, dtmp2, dtmp3, dtmp4, dtmp5, g_fvoices); + video_update_status_line(3, status_buf); + + code_str1 = ""; + code_str2 = ""; + if(g_code_yellow) { + code_str1 = "Code: Yellow"; + code_str2 = "Emulated system state suspect, save work"; + } + if(g_code_red) { + code_str1 = "Code: RED"; + code_str2 = "Emulated system state probably corrupt"; + } + sprintf(status_buf, "snd_plays:%4d, doc_ev:%4d, st_snd:%4d " + "snd_parms: %4d %s", + g_num_snd_plays, g_num_doc_events, g_num_start_sounds, + g_num_recalc_snd_parms, code_str1); + video_update_status_line(4, status_buf); + + draw_iwm_status(5, status_buf); + + sprintf(status_buf, "GSport v%-6s " + "Press F4 for Config Menu %s", + g_gsport_version_str, code_str2); + video_update_status_line(6, status_buf); + + g_status_refresh_needed = 1; + + g_num_irq = 0; + g_num_brk = 0; + g_num_cop = 0; + g_num_enter_engine = 0; + g_io_amt = 0; + g_engine_action = 0; + g_engine_halt_event = 0; + g_engine_scan_int = 0; + g_engine_doc_int = 0; + + g_cycs_in_40col = 0; + g_cycs_in_xredraw = 0; + g_cycs_in_check_input = 0; + g_cycs_in_refresh_line = 0; + g_cycs_in_refresh_ximage = 0; + g_cycs_in_io_read = 0; + g_cycs_in_sound1 = 0; + g_cycs_in_sound2 = 0; + g_cycs_in_sound3 = 0; + g_cycs_in_sound4 = 0; + g_cycs_in_start_sound = 0; + g_cycs_in_est_sound = 0; + g_dnatcycs_1sec = 0.0; + g_refresh_bytes_xfer = 0; + + g_num_snd_plays = 0; + g_num_doc_events = 0; + g_num_start_sounds = 0; + g_num_scan_osc = 0; + g_num_recalc_snd_parms = 0; + + g_fvoices = (float)0.0; + } + + dtime_this_vbl = dtime_now - g_dtime_last_vbl; + if(dtime_this_vbl < 0.001) { + dtime_this_vbl = 0.001; + } + + g_dtime_last_vbl = dtime_now; + + dadjcycs_this_vbl = g_dadjcycs - g_last_vbl_dadjcycs; + g_last_vbl_dadjcycs = g_dadjcycs; + + g_dtime_expected += (1.0/60.0); + + eff_pmhz = ((dadjcycs_this_vbl) / (dtime_this_vbl)) / + DCYCS_1_MHZ; + + /* using eff_pmhz, predict how many cycles can be run by */ + /* g_dtime_expected */ + + dtime_till_expected = g_dtime_expected - dtime_now; + + dratio = 60.0 * dtime_till_expected; + + predicted_pmhz = eff_pmhz * dratio; + + if(! (predicted_pmhz < (1.4 * g_projected_pmhz))) { + predicted_pmhz = 1.4 * g_projected_pmhz; + } + + if(! (predicted_pmhz > (0.7 * g_projected_pmhz))) { + predicted_pmhz = 0.7 * g_projected_pmhz; + } + + if(!(predicted_pmhz >= 1.0)) { + irq_printf("predicted: %f, setting to 1.0\n", predicted_pmhz); + predicted_pmhz = 1.0; + } + + if(!(predicted_pmhz < 250.0)) { + irq_printf("predicted: %f, setting to 250.0\n", predicted_pmhz); + predicted_pmhz = 250.0; + } + + recip_predicted_pmhz = 1.0/predicted_pmhz; + g_projected_pmhz = predicted_pmhz; + + g_recip_projected_pmhz_unl.plus_1 = 1.0*recip_predicted_pmhz; + g_recip_projected_pmhz_unl.plus_2 = 2.0*recip_predicted_pmhz; + g_recip_projected_pmhz_unl.plus_3 = 3.0*recip_predicted_pmhz; + g_recip_projected_pmhz_unl.plus_x_minus_1 = 1.01 - recip_predicted_pmhz; + + if(dtime_till_expected < -0.125) { + /* If we were way off, get back on track */ + /* this happens because our sim took much longer than */ + /* expected, so we're going to skip some VBL */ + irq_printf("adj1: dtexp:%f, dt_new:%f\n", + g_dtime_expected, dtime_now); + + dtime_diff = -dtime_till_expected; + + irq_printf("dtime_till_exp: %f, dtime_diff: %f, dcycs: %f\n", + dtime_till_expected, dtime_diff, dcycs); + + g_dtime_expected += dtime_diff; + } + + if(dtime_till_expected > (3/60.0)) { + /* we're running fast, usleep */ + micro_sleep(dtime_till_expected - (1/60.0)); + } + + g_dtime_this_vbl_array[prev_vbl_index] = dtime_this_vbl; + g_dtime_exp_array[prev_vbl_index] = g_dtime_expected; + g_dtime_pmhz_array[prev_vbl_index] = predicted_pmhz; + g_dtime_eff_pmhz_array[prev_vbl_index] = eff_pmhz; + + + if(g_c041_val & C041_EN_VBL_INTS) { + add_event_vbl(); + } + + g_25sec_cntr++; + if(g_25sec_cntr >= 16) { + g_25sec_cntr = 0; + if(g_c041_val & C041_EN_25SEC_INTS) { + add_irq(IRQ_PENDING_C046_25SEC); + g_c046_val |= 0x10; + irq_printf("Setting c046 .25 sec int, g_irq_pend:%d\n", + g_irq_pending); + } + } + + g_1sec_cntr++; + if(g_1sec_cntr >= 60) { + g_1sec_cntr = 0; + tmp = g_c023_val; + tmp |= 0x40; /* set 1sec int */ + if(tmp & 0x04) { + tmp |= 0x80; + add_irq(IRQ_PENDING_C023_1SEC); + irq_printf("Setting c023 to %02x irq_pend: %d\n", + tmp, g_irq_pending); + } + g_c023_val = tmp; + } + + if(!g_scan_int_events) { + check_scan_line_int(dcycs, 0); + } + + doit_3_persec = 0; + if(g_config_iwm_vbl_count > 0) { + g_config_iwm_vbl_count--; + } else { + g_config_iwm_vbl_count = 20; + doit_3_persec = 1; + } + + iwm_vbl_update(doit_3_persec); // OG Disabling config update #ifndef ACTIVEGS - config_vbl_update(doit_3_persec); + config_vbl_update(doit_3_persec); #else // OG Added disk update { @@ -2370,377 +2369,377 @@ update_60hz(double dcycs, double dtime_now) checkImages(); } #endif - - video_update(); - sound_update(dcycs); - clock_update(); - scc_update(dcycs); - //Check and see if virtual printer timeout has been reached. - if (g_printer_timeout) - { - printer_update(); - } + + video_update(); + sound_update(dcycs); + clock_update(); + scc_update(dcycs); + //Check and see if virtual printer timeout has been reached. + if (g_printer_timeout) + { + printer_update(); + } if (g_imagewriter_timeout) { imagewriter_update(); } - paddle_update_buttons(); -} - -void -do_vbl_int() -{ - if(g_c041_val & C041_EN_VBL_INTS) { - g_c046_val |= 0x08; - add_irq(IRQ_PENDING_C046_VBL); - irq_printf("Setting c046 vbl_int_status to 1, irq_pend: %d\n", - g_irq_pending); - } -} - - -void -do_scan_int(double dcycs, int line) -{ - int c023_val; - g_scan_int_events = 0; - - c023_val = g_c023_val; - if(c023_val & 0x20) { - halt_printf("c023 scan_int and another on line %03x\n", line); - } - - /* make sure scan int is still enabled for this line */ - if((g_slow_memory_ptr[0x19d00 + line] & 0x40) && - (g_cur_a2_stat & ALL_STAT_SUPER_HIRES)) { - /* valid interrupt, do it */ - c023_val |= 0xa0; /* vgc_int and scan_int */ - if(c023_val & 0x02) { - add_irq(IRQ_PENDING_C023_SCAN); - irq_printf("Setting c023 to %02x, irq_pend: %d\n", - c023_val, g_irq_pending); - } - g_c023_val = c023_val; - HALT_ON(HALT_ON_SCAN_INT, "In do_scan_int\n"); - } else { - /* scan int bit cleared on scan line control byte */ - /* look for next line, if any */ - check_scan_line_int(dcycs, line+1); - } -} - -void -check_scan_line_int(double dcycs, int cur_video_line) -{ - int delay; - int start; - int line; - int i; - /* Called during VBL interrupt phase */ - - if(!(g_cur_a2_stat & ALL_STAT_SUPER_HIRES)) { - return; - } - - if(g_c023_val & 0x20) { - /* don't check for any more */ - return; - } - - start = cur_video_line; - if(start < 0) { - halt_printf("check_scan_line_int: cur_video_line: %d\n", - cur_video_line); - start = 0; - } - - for(line = start; line < 200; line++) { - i = line; - - if(i < 0 || i >= 200) { - halt_printf("check_new_scan_int:i:%d, line:%d, st:%d\n", - i, line, start); - i = 0; - } - if(g_slow_memory_ptr[0x19d00+i] & 0x40) { - irq_printf("Adding scan_int for line %d\n", i); + paddle_update_buttons(); +} + +void +do_vbl_int() +{ + if(g_c041_val & C041_EN_VBL_INTS) { + g_c046_val |= 0x08; + add_irq(IRQ_PENDING_C046_VBL); + irq_printf("Setting c046 vbl_int_status to 1, irq_pend: %d\n", + g_irq_pending); + } +} + + +void +do_scan_int(double dcycs, int line) +{ + int c023_val; + g_scan_int_events = 0; + + c023_val = g_c023_val; + if(c023_val & 0x20) { + halt_printf("c023 scan_int and another on line %03x\n", line); + } + + /* make sure scan int is still enabled for this line */ + if((g_slow_memory_ptr[0x19d00 + line] & 0x40) && + (g_cur_a2_stat & ALL_STAT_SUPER_HIRES)) { + /* valid interrupt, do it */ + c023_val |= 0xa0; /* vgc_int and scan_int */ + if(c023_val & 0x02) { + add_irq(IRQ_PENDING_C023_SCAN); + irq_printf("Setting c023 to %02x, irq_pend: %d\n", + c023_val, g_irq_pending); + } + g_c023_val = c023_val; + HALT_ON(HALT_ON_SCAN_INT, "In do_scan_int\n"); + } else { + /* scan int bit cleared on scan line control byte */ + /* look for next line, if any */ + check_scan_line_int(dcycs, line+1); + } +} + +void +check_scan_line_int(double dcycs, int cur_video_line) +{ + int delay; + int start; + int line; + int i; + /* Called during VBL interrupt phase */ + + if(!(g_cur_a2_stat & ALL_STAT_SUPER_HIRES)) { + return; + } + + if(g_c023_val & 0x20) { + /* don't check for any more */ + return; + } + + start = cur_video_line; + if(start < 0) { + halt_printf("check_scan_line_int: cur_video_line: %d\n", + cur_video_line); + start = 0; + } + + for(line = start; line < 200; line++) { + i = line; + + if(i < 0 || i >= 200) { + halt_printf("check_new_scan_int:i:%d, line:%d, st:%d\n", + i, line, start); + i = 0; + } + if(g_slow_memory_ptr[0x19d00+i] & 0x40) { + irq_printf("Adding scan_int for line %d\n", i); delay = (int)( (DCYCS_IN_16MS/262.0) * ((double)line) ); - add_event_entry(g_last_vbl_dcycs + delay, EV_SCAN_INT + - (line << 8)); - g_scan_int_events = 1; - check_for_one_event_type(EV_SCAN_INT); - break; - } - } -} - -void -check_for_new_scan_int(double dcycs) -{ - int cur_video_line; - - cur_video_line = get_lines_since_vbl(dcycs) >> 8; - - check_scan_line_int(dcycs, cur_video_line); -} - -void -init_reg() -{ - engine.acc = 0; - engine.xreg = 0; - engine.yreg = 0; - engine.stack = 0x1ff; - engine.direct = 0; - engine.psr = 0x134; - engine.fplus_ptr = 0; - -} - - -void -handle_action(word32 ret) -{ - int type; - - type = EXTRU(ret,3,4); - switch(type) { - case RET_BREAK: - do_break(ret & 0xff); - break; - case RET_COP: - do_cop(ret & 0xff); - break; -#if 0 - case RET_MVN: - do_mvn(ret & 0xffff); - break; -#endif - case RET_C700: - do_c700(ret); - break; - case RET_C70A: - do_c70a(ret); - break; - case RET_C70D: - do_c70d(ret); - break; -#if 0 - case RET_ADD_DEC_8: - do_add_dec_8(ret); - break; - case RET_ADD_DEC_16: - do_add_dec_16(ret); - break; -#endif - case RET_IRQ: - irq_printf("Special fast IRQ response. irq_pending: %x\n", - g_irq_pending); - break; - case RET_WDM: - do_wdm(ret & 0xff); - break; - case RET_STP: - do_stp(); - break; - default: - halt_printf("Unknown special action: %08x!\n", ret); - } - -} - -#if 0 -void -do_add_dec_8(word32 ret) -{ - halt_printf("do_add_dec_8 called, ret: %08x\n", ret); -} - -void -do_add_dec_16(word32 ret) -{ - halt_printf("do_add_dec_16 called, ret: %08x\n", ret); -} -#endif - -void -do_break(word32 ret) -{ - if(!g_testing) { - printf("I think I got a break, second byte: %02x!\n", ret); - printf("kpc: %06x\n", engine.kpc); - } - - halt_printf("do_break, kpc: %06x\n", engine.kpc); - enter_debug = 1; -} - -void -do_cop(word32 ret) -{ - halt_printf("COP instr %02x!\n", ret); - fflush(stdout); -} - -#if 0 -void -do_mvn(word32 banks) -{ - int src_bank, dest_bank; - int dest, src; - int num; - int i; - int val; - - halt_printf("In MVN...just quitting\n"); - return; - printf("MVN instr with %04x, cycles: %08x\n", banks, engine.cycles); - src_bank = banks >> 8; - dest_bank = banks & 0xff; - printf("psr: %03x\n", engine.psr); - if((engine.psr & 0x30) != 0) { - halt_printf("MVN in non-native mode unimplemented!\n"); - } - - dest = dest_bank << 16 | engine.yreg; - src = src_bank << 16 | engine.xreg; - num = engine.acc; - printf("Moving %08x+1 bytes from %08x to %08x\n", num, src, dest); - - for(i = 0; i <= num; i++) { - val = get_memory_c(src, 0); - set_memory_c(dest, val, 0); - src = (src_bank << 16) | ((src + 1) & 0xffff); - dest = (dest_bank << 16) | ((dest + 1) & 0xffff); - } - engine.dbank = dest_bank; - engine.acc = 0xffff; - engine.yreg = dest & 0xffff; - engine.xreg = src & 0xffff; - engine.kpc = (engine.kpc + 3); - printf("move done. db: %02x, acc: %04x, y: %04x, x: %04x, num: %08x\n", - engine.dbank, engine.acc, engine.yreg, engine.xreg, num); -} -#endif - -void -do_wdm(word32 arg) -{ - switch(arg) { - case 0x8d: /* Bouncin Ferno does WDM 8d */ - break; - default: - halt_printf("do_wdm: %02x!\n", arg); - } -} - -void -do_wai() -{ - halt_printf("do_wai!\n"); -} - -void -do_stp() -{ - if(!g_stp_pending) { - g_stp_pending = 1; - halt_printf("Hit STP instruction at: %06x, press RESET to " - "continue\n", engine.kpc); - } -} - -void -size_fail(int val, word32 v1, word32 v2) -{ - halt_printf("Size failure, val: %08x, %08x %08x\n", val, v1, v2); -} - -int -fatal_printf(const char *fmt, ...) -{ - va_list ap; - int ret; - - va_start(ap, fmt); - - if(g_fatal_log < 0) { - g_fatal_log = 0; - } - ret = gsport_vprintf(fmt, ap); - va_end(ap); - - return ret; -} - -int -gsport_vprintf(const char *fmt, va_list ap) -{ - char *bufptr, *buf2ptr; - int len; - int ret; - + add_event_entry(g_last_vbl_dcycs + delay, EV_SCAN_INT + + (line << 8)); + g_scan_int_events = 1; + check_for_one_event_type(EV_SCAN_INT); + break; + } + } +} + +void +check_for_new_scan_int(double dcycs) +{ + int cur_video_line; + + cur_video_line = get_lines_since_vbl(dcycs) >> 8; + + check_scan_line_int(dcycs, cur_video_line); +} + +void +init_reg() +{ + engine.acc = 0; + engine.xreg = 0; + engine.yreg = 0; + engine.stack = 0x1ff; + engine.direct = 0; + engine.psr = 0x134; + engine.fplus_ptr = 0; + +} + + +void +handle_action(word32 ret) +{ + int type; + + type = EXTRU(ret,3,4); + switch(type) { + case RET_BREAK: + do_break(ret & 0xff); + break; + case RET_COP: + do_cop(ret & 0xff); + break; +#if 0 + case RET_MVN: + do_mvn(ret & 0xffff); + break; +#endif + case RET_C700: + do_c700(ret); + break; + case RET_C70A: + do_c70a(ret); + break; + case RET_C70D: + do_c70d(ret); + break; +#if 0 + case RET_ADD_DEC_8: + do_add_dec_8(ret); + break; + case RET_ADD_DEC_16: + do_add_dec_16(ret); + break; +#endif + case RET_IRQ: + irq_printf("Special fast IRQ response. irq_pending: %x\n", + g_irq_pending); + break; + case RET_WDM: + do_wdm(ret & 0xff); + break; + case RET_STP: + do_stp(); + break; + default: + halt_printf("Unknown special action: %08x!\n", ret); + } + +} + +#if 0 +void +do_add_dec_8(word32 ret) +{ + halt_printf("do_add_dec_8 called, ret: %08x\n", ret); +} + +void +do_add_dec_16(word32 ret) +{ + halt_printf("do_add_dec_16 called, ret: %08x\n", ret); +} +#endif + +void +do_break(word32 ret) +{ + if(!g_testing) { + printf("I think I got a break, second byte: %02x!\n", ret); + printf("kpc: %06x\n", engine.kpc); + } + + halt_printf("do_break, kpc: %06x\n", engine.kpc); + enter_debug = 1; +} + +void +do_cop(word32 ret) +{ + halt_printf("COP instr %02x!\n", ret); + fflush(stdout); +} + +#if 0 +void +do_mvn(word32 banks) +{ + int src_bank, dest_bank; + int dest, src; + int num; + int i; + int val; + + halt_printf("In MVN...just quitting\n"); + return; + printf("MVN instr with %04x, cycles: %08x\n", banks, engine.cycles); + src_bank = banks >> 8; + dest_bank = banks & 0xff; + printf("psr: %03x\n", engine.psr); + if((engine.psr & 0x30) != 0) { + halt_printf("MVN in non-native mode unimplemented!\n"); + } + + dest = dest_bank << 16 | engine.yreg; + src = src_bank << 16 | engine.xreg; + num = engine.acc; + printf("Moving %08x+1 bytes from %08x to %08x\n", num, src, dest); + + for(i = 0; i <= num; i++) { + val = get_memory_c(src, 0); + set_memory_c(dest, val, 0); + src = (src_bank << 16) | ((src + 1) & 0xffff); + dest = (dest_bank << 16) | ((dest + 1) & 0xffff); + } + engine.dbank = dest_bank; + engine.acc = 0xffff; + engine.yreg = dest & 0xffff; + engine.xreg = src & 0xffff; + engine.kpc = (engine.kpc + 3); + printf("move done. db: %02x, acc: %04x, y: %04x, x: %04x, num: %08x\n", + engine.dbank, engine.acc, engine.yreg, engine.xreg, num); +} +#endif + +void +do_wdm(word32 arg) +{ + switch(arg) { + case 0x8d: /* Bouncin Ferno does WDM 8d */ + break; + default: + halt_printf("do_wdm: %02x!\n", arg); + } +} + +void +do_wai() +{ + halt_printf("do_wai!\n"); +} + +void +do_stp() +{ + if(!g_stp_pending) { + g_stp_pending = 1; + halt_printf("Hit STP instruction at: %06x, press RESET to " + "continue\n", engine.kpc); + } +} + +void +size_fail(int val, word32 v1, word32 v2) +{ + halt_printf("Size failure, val: %08x, %08x %08x\n", val, v1, v2); +} + +int +fatal_printf(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + + if(g_fatal_log < 0) { + g_fatal_log = 0; + } + ret = gsport_vprintf(fmt, ap); + va_end(ap); + + return ret; +} + +int +gsport_vprintf(const char *fmt, va_list ap) +{ + char *bufptr, *buf2ptr; + int len; + int ret; + bufptr = (char*)malloc(4096); // OG Added Cast - ret = vsnprintf(bufptr, 4090, fmt, ap); - + ret = vsnprintf(bufptr, 4090, fmt, ap); + // OG Display warning printf("Warning:%s",bufptr); - len = strlen(bufptr); - if(g_fatal_log >= 0 && g_fatal_log < MAX_FATAL_LOGS) { + len = strlen(bufptr); + if(g_fatal_log >= 0 && g_fatal_log < MAX_FATAL_LOGS) { buf2ptr = (char*)malloc(len+1); // OG Added Cast - memcpy(buf2ptr, bufptr, len+1); - g_fatal_log_strs[g_fatal_log++] = buf2ptr; - } - must_write(1, bufptr, len); - if(g_debug_file_fd >= 0) { - must_write(g_debug_file_fd, bufptr, len); - } - free(bufptr); - - return ret; -} - -void -must_write(int fd, char *bufptr, int len) -{ - int ret; -#ifndef __OS2__ - while(len > 0) { - ret = write(fd, bufptr, len); - if(ret >= 0) { - len -= ret; - bufptr += ret; - } else if(errno != EAGAIN && errno != EINTR) { - return; // just get out - } - } -#else - printf("%s\n",bufptr); -#endif -} - -void -clear_fatal_logs() -{ - int i; - - for(i = 0; i < g_fatal_log; i++) { - free(g_fatal_log_strs[i]); - g_fatal_log_strs[i] = 0; - } - g_fatal_log = -1; -} - -char * -gsport_malloc_str(char *in_str) -{ - char *str; - int len; - - len = strlen(in_str) + 1; + memcpy(buf2ptr, bufptr, len+1); + g_fatal_log_strs[g_fatal_log++] = buf2ptr; + } + must_write(1, bufptr, len); + if(g_debug_file_fd >= 0) { + must_write(g_debug_file_fd, bufptr, len); + } + free(bufptr); + + return ret; +} + +void +must_write(int fd, char *bufptr, int len) +{ + int ret; +#ifndef __OS2__ + while(len > 0) { + ret = write(fd, bufptr, len); + if(ret >= 0) { + len -= ret; + bufptr += ret; + } else if(errno != EAGAIN && errno != EINTR) { + return; // just get out + } + } +#else + printf("%s\n",bufptr); +#endif +} + +void +clear_fatal_logs() +{ + int i; + + for(i = 0; i < g_fatal_log; i++) { + free(g_fatal_log_strs[i]); + g_fatal_log_strs[i] = 0; + } + g_fatal_log = -1; +} + +char * +gsport_malloc_str(char *in_str) +{ + char *str; + int len; + + len = strlen(in_str) + 1; str = (char*)malloc(len); // OG Added cast - memcpy(str, in_str, len); - - return str; -} + memcpy(str, in_str, len); + + return str; +} diff --git a/src/tfe/tfe.vcxproj b/src/tfe/tfe.vcxproj index 34d35ed..ea326ee 100644 --- a/src/tfe/tfe.vcxproj +++ b/src/tfe/tfe.vcxproj @@ -55,7 +55,7 @@ false ProgramDatabase Default - CompileAsCpp + CompileAsC true true false @@ -74,7 +74,7 @@ true WIN32;NDEBUG;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;TFE_EXPORTS;%(PreprocessorDefinitions) Speed - CompileAsCpp + CompileAsC false StreamingSIMDExtensions AnySuitable @@ -98,8 +98,8 @@ - + diff --git a/src/tfe/tfe.vcxproj.filters b/src/tfe/tfe.vcxproj.filters index aec989f..1157911 100644 --- a/src/tfe/tfe.vcxproj.filters +++ b/src/tfe/tfe.vcxproj.filters @@ -47,7 +47,7 @@ Source Files - + Source Files diff --git a/src/arch/unix/tfearch.c b/src/tfe/tfearch.c old mode 100755 new mode 100644 similarity index 92% rename from src/arch/unix/tfearch.c rename to src/tfe/tfearch.c index 7dba105..8a5dc8a --- a/src/arch/unix/tfearch.c +++ b/src/tfe/tfearch.c @@ -26,16 +26,16 @@ */ -#include -#include "../../tfe/tfesupp.h" - #include #include #include #include +#include -#include "../../defc.h" -#include "../../tfe/protos_tfe.h" +#include "../atbridge/pcap_delay.h" +#include "tfesupp.h" +#include "../defc.h" +#include "protos_tfe.h" /** #define TFE_DEBUG_ARCH 1 **/ /** #define TFE_DEBUG_PKTDUMP 1 **/ @@ -51,7 +51,6 @@ //static log_t tfe_arch_log = LOG_ERR; - static pcap_if_t *TfePcapNextDev = NULL; static pcap_if_t *TfePcapAlldevs = NULL; static pcap_t *TfePcapFP = NULL; @@ -82,16 +81,7 @@ void debug_output( const char *text, unsigned char *what, int count ) } while (len1>0); } #endif // #ifdef TFE_DEBUG_PKTDUMP -/* -static -void TfePcapCloseAdapter(void) -{ - if (TfePcapAlldevs) { - (*p_pcap_freealldevs)(TfePcapAlldevs); - TfePcapAlldevs = NULL; - } -} -*/ + /* These functions let the UI enumerate the available interfaces. @@ -115,7 +105,7 @@ void TfePcapCloseAdapter(void) */ int tfe_arch_enumadapter_open(void) { - if (pcap_findalldevs(&TfePcapAlldevs, TfePcapErrbuf) == -1) + if (pcapdelay_findalldevs(&TfePcapAlldevs, TfePcapErrbuf) == -1) { #ifdef TFE_DEBUG_ARCH log_message(tfe_arch_log, "ERROR in TfeEnumAdapterOpen: pcap_findalldevs: '%s'", TfePcapErrbuf); @@ -154,7 +144,7 @@ int tfe_arch_enumadapter(char **ppname, char **ppdescription) int tfe_arch_enumadapter_close(void) { if (TfePcapAlldevs) { - pcap_freealldevs(TfePcapAlldevs); + pcapdelay_freealldevs(TfePcapAlldevs); TfePcapAlldevs = NULL; } return 1; @@ -195,7 +185,7 @@ int TfePcapOpenAdapter(const char *interface_name) } } - TfePcapFP = pcap_open_live(TfePcapDevice->name, 1700, 1, 20, TfePcapErrbuf); + TfePcapFP = pcapdelay_open_live(TfePcapDevice->name, 1700, 1, 20, TfePcapErrbuf); if ( TfePcapFP == NULL) { #ifdef TFE_DEBUG_ARCH @@ -205,7 +195,7 @@ int TfePcapOpenAdapter(const char *interface_name) return FALSE; } - if (pcap_setnonblock(TfePcapFP, 1, TfePcapErrbuf)<0) + if (pcapdelay_setnonblock(TfePcapFP, 1, TfePcapErrbuf)<0) { #ifdef TFE_DEBUG_ARCH log_message(tfe_arch_log, "WARNING: Setting PCAP to non-blocking failed: '%s'", TfePcapErrbuf); @@ -213,7 +203,7 @@ int TfePcapOpenAdapter(const char *interface_name) } /* Check the link layer. We support only Ethernet for simplicity. */ - if(pcap_datalink(TfePcapFP) != DLT_EN10MB) + if(pcapdelay_datalink(TfePcapFP) != DLT_EN10MB) { #ifdef TFE_DEBUG_ARCH log_message(tfe_arch_log, "ERROR: TFE works only on Ethernet networks."); @@ -278,24 +268,6 @@ void tfe_arch_set_mac( const unsigned char mac[6] ) #endif } -/* -void tfe_arch_set_hashfilter(const DWORD hash_mask[2]) -{ -#if defined(TFE_DEBUG_ARCH) || defined(TFE_DEBUG_FRAMES) - log_message( tfe_arch_log, "New hash filter set: %08X:%08X.", - hash_mask[1], hash_mask[0]); -#endif -} -*/ - -/* -void tfe_arch_receive_remove_committed_frame(void) -{ -#ifdef TFE_DEBUG_ARCH - log_message( tfe_arch_log, "tfe_arch_receive_remove_committed_frame()." ); -#endif -} -*/ void tfe_arch_recv_ctl( int bBroadcast, /* broadcast */ int bIA, /* individual address (IA) */ @@ -364,7 +336,7 @@ int tfe_arch_receive_frame(TFE_PCAP_INTERNAL *pinternal) int ret = -1; /* check if there is something to receive */ - if (pcap_dispatch(TfePcapFP, 1, (pcap_handler)TfePcapPacketHandler, (unsigned char*)pinternal)!=0) { + if (pcapdelay_dispatch(TfePcapFP, 1, (pcap_handler)TfePcapPacketHandler, (unsigned char*)pinternal)!=0) { /* Something has been received */ ret = pinternal->len; } @@ -399,7 +371,7 @@ void tfe_arch_transmit(int force, /* FORCE: Delete waiting frames in trans debug_output( "Transmit frame: ", txframe, txlength); #endif // #ifdef TFE_DEBUG_PKTDUMP - if (pcap_sendpacket(TfePcapFP, txframe, txlength) == -1) { + if (pcapdelay_sendpacket(TfePcapFP, txframe, txlength) == -1) { //log_message(tfe_arch_log, "WARNING! Could not send packet!"); } } diff --git a/src/vars_fbrpilinux b/src/vars_fbrpilinux index 78122b8..b02a332 100644 --- a/src/vars_fbrpilinux +++ b/src/vars_fbrpilinux @@ -1,15 +1,14 @@ TARGET = gsportfb -TFEOBJ = tfe/tfe.o arch/unix/tfearch.o tfe/tfesupp.o -OBJECTS = $(OBJECTS1) $(TFEOBJ) fbdriver.o +OBJECTS = $(OBJECTS1) $(TFEOBJ) $(ATOBJ) $(TFEOBJ) fbdriver.o CC = g++ -CCOPTS = -O2 -Wall -fomit-frame-pointer -march=armv6 -OPTS = -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE +CCOPTS = -O2 -Wall -fomit-frame-pointer -std=gnu99 -march=armv6 +OPTS = -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DHAVE_ATBRIDGE SUFFIX = NAME = gsportfb LDFLAGS = LDOPTS = LD = $(CC) -EXTRA_LIBS = -lpcap +EXTRA_LIBS = EXTRA_SPECIALS = AS = cc diff --git a/src/vars_pi b/src/vars_pi index 842a129..df69d12 100644 --- a/src/vars_pi +++ b/src/vars_pi @@ -1,15 +1,14 @@ TARGET = gsportx -TFEOBJ = tfe/tfe.o arch/unix/tfearch.o tfe/tfesupp.o -OBJECTS = $(OBJECTS1) $(TFEOBJ) xdriver.o +OBJECTS = $(OBJECTS1) $(TFEOBJ) $(ATOBJ) $(PCAPOBJ) xdriver.o CC = g++ -CCOPTS = -O2 -Wall -fomit-frame-pointer -march=armv6 -OPTS = -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DTOGGLE_STATUS +CCOPTS = -O2 -Wall -fomit-frame-pointer -std=gnu99 -march=armv6 +OPTS = -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DTOGGLE_STATUS -DHAVE_ATBRIDGE SUFFIX = NAME = gsportx LDFLAGS = LDOPTS = LD = $(CC) -EXTRA_LIBS = -lXext -lpcap +EXTRA_LIBS = -lXext EXTRA_SPECIALS = AS = cc diff --git a/src/vars_win32 b/src/vars_win32 index 28f14d8..d784d55 100644 --- a/src/vars_win32 +++ b/src/vars_win32 @@ -1,11 +1,10 @@ TARGET = gsport.exe -TFEOBJ = tfe/tfe.o arch/win32/tfearch.o tfe/tfesupp.o -OBJECTS = $(OBJECTS1) $(TFEOBJ) scc_windriver.o win32snd_driver.o win_console.o win_generic.o gsport32.o -CCOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DWIN_SOUND -DTOGGLE_STATUS -CPPOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DTOGGLE_STATUS +OBJECTS = $(OBJECTS1) $(TFEOBJ) $(ATOBJ) $(PCAPOBJ) scc_windriver.o win32snd_driver.o win_console.o win_generic.o gsport32.o +CCOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DWIN_SOUND -DTOGGLE_STATUS -DWIN32 -D_WIN32 -D__USE_W32_SOCKETS -D_WINSOCK2API_ -std=gnu99 -DHAVE_ATBRIDGE +CPPOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DTOGGLE_STATUS -DWIN32 -D_WIN32 -D__USE_W32_SOCKETS -D_WINSOCK2API_ -DHAVE_ATBRIDGE SUFFIX = ".exe" NAME = gsport -EXTRA_LIBS = -lcomdlg32 -lShlwapi +EXTRA_LIBS = -Larch/win32 -lcomdlg32 -lShlwapi -lIPHlpApi -XOPTS = -Wall -fomit-frame-pointer -march=pentium -XLIBS = +XOPTS = -Wall -fomit-frame-pointer -march=i686 +XLIBS = \ No newline at end of file diff --git a/src/vars_win32_sdl b/src/vars_win32_sdl index c767a58..98e353d 100644 --- a/src/vars_win32_sdl +++ b/src/vars_win32_sdl @@ -1,11 +1,11 @@ TARGET = gsport.exe -TFEOBJ = tfe/tfe.o arch/win32/tfearch.o tfe/tfesupp.o -OBJECTS = $(OBJECTS1) $(TFEOBJ) scc_windriver.o win32snd_driver.o win_console.o win_generic.o gsport32.o -CCOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DWIN_SOUND -DHAVE_SDL -DWIN32 -D_WIN32 -D__USE_W32_SOCKETS -DTOGGLE_STATUS -CPPOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DHAVE_SDL -DWIN32 -D_WIN32 -D__USE_W32_SOCKETS -I /usr/include/freetype2 -I/usr/include/SDL -DTOGGLE_STATUS +OBJECTS = $(OBJECTS1) $(TFEOBJ) $(ATOBJ) $(PCAPOBJ) scc_windriver.o win32snd_driver.o win_console.o win_generic.o gsport32.o +CCOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DWIN_SOUND -DHAVE_SDL -DTOGGLE_STATUS -DWIN32 -D_WIN32 -D__USE_W32_SOCKETS -D_WINSOCK2API_ -std=gnu99 -DHAVE_ATBRIDGE +CPPOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DHAVE_SDL -DTOGGLE_STATUS -DWIN32 -D_WIN32 -D__USE_W32_SOCKETS -D_WINSOCK2API_ -DHAVE_ATBRIDGE -I /usr/include/freetype2 -I/usr/include/SDL + SUFFIX = ".exe" NAME = gsport -EXTRA_LIBS = -lSDL -lfreetype -lcomdlg32 -lShlwapi +EXTRA_LIBS = -Larch/win32 -lSDL -lfreetype -lcomdlg32 -lShlwapi -lIPHlpApi -XOPTS = -Wall -fomit-frame-pointer -march=pentium -XLIBS = +XOPTS = -Wall -fomit-frame-pointer -march=i686 +XLIBS = \ No newline at end of file diff --git a/src/vars_x86linux b/src/vars_x86linux index ac736cd..6ad1bae 100644 --- a/src/vars_x86linux +++ b/src/vars_x86linux @@ -1,8 +1,7 @@ - TARGET = gsportx -OBJECTS = $(OBJECTS1) xdriver.o +OBJECTS = $(OBJECTS1) $(TFEOBJ) $(ATOBJ) $(PCAPOBJ) xdriver.o CC = g++ -CCOPTS = -O2 -Wall -fomit-frame-pointer -march=pentium +CCOPTS = -O2 -Wall -fomit-frame-pointer -std=gnu99 -march=i686 -DHAVE_TFE -DHAVE_ATBRIDGE -DTOGGLE_STATUS OPTS = -DGSPORT_LITTLE_ENDIAN SUFFIX = NAME = gsportx @@ -15,5 +14,4 @@ EXTRA_SPECIALS = AS = cc PERL = perl -XOPTS = -I/usr/X11R6/include - +XOPTS = -I/usr/X11R6/include \ No newline at end of file