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 0000000..77e79f6 Binary files /dev/null and b/lib/arch/win32/cygstdc++-6.dll differ 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 628cdfc..0000000 Binary files a/src/SDL.dll and /dev/null differ 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