diff --git a/NOTICE b/NOTICE index e47f1de..02189d2 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ - C64Net WiFi Firmware - Copyright 2016-2017 Bo Zimmerman + C64Net WiFi Firmware / Zimodem + Copyright 2016-2021 Bo Zimmerman This product includes software developed by Bo Zimmerman diff --git a/README b/README index 1660408..01cdcd0 100644 --- a/README +++ b/README @@ -1,35 +1,84 @@ -C64Net WiFi Firmware 3.3 (C)2016-2017 Bo Zimmerman +Zimodem 3.6.4 (C)2016-2021 Bo Zimmerman Please read the LICENSE file for license information Please read the NOTICE file for credits information Intro: ------ -This firmware code, "C64Net WiFi Firmware", provides an api for communication between a serial terminal and the ESP8266. It simulates the old-style Hayes "AT" commands, so that the ESP8266 appears to the terminal as if it were a Hayes modem. These "AT" commands may then be issued to the ESP8266 in order to force it to connect to a wireless access point, and from there to make one or more manageable connections to the internet. It also includes a streaming serial "telnet" mode, and server/port listener capabilities. +This firmware code provides an api for communication between a serial terminal and the ESP8266 or ESP32. It simulates the old-style Hayes "AT" commands, so that the ESP8266 appears to the terminal as if it were a Hayes modem. These "AT" commands may then be issued to the ESP8266 in order to force it to connect to a wireless access point, and from there to make one or more manageable connections to the internet. It also includes a streaming serial "telnet" mode, and server/port listener capabilities. The default baud rate that the firmware establishes is 1200. Be sure to use the commands below to set this to the proper baud rate for your terminal/host computer. If you plan on using the Commodore 8-bit utilities, keep the firmware baud rate saved at 1200. Building: --------- -This firmware was built using Arduino IDE rev1.6.11 with the Arduino Generic ESP8266 Module installed from the head dev at https://github.com/esp8266/Arduino. Using that version of the library not the official Boards Manager distribution (via Additional Boards Manager URL http://arduino.esp8266.com/stable/package_esp8266com_index.json) was important to resolving several problems (specifically 300ms/byte socket write times). How you get there from here was tricky. I followed the standard instructions for installing an ESP board, found where it dropped the package (on Windows 10, it was in: -c:\users\\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\... I dropped the new stuff from github in there instead of the old stuff, and then went into the new "tools" directory and ran get.py. +To build this firmware for the ESP-01 or ESP-12, I used the Arduino IDE rev1.8.10 with the 2.7.4 Arduino ESP8266 libraries using the Generic ESP8266 Module. Versions newer than 2.7.4 may not work. Install the libraries from the Boards Manager using http://arduino.esp8266.com/stable/package_esp8266com_index.json. Both versions of the firmware are built with SPIFFS enabled. On the ESP8266, I used settings of 1M/160k, but you may want to alter that depending on your hardware and needs. + +To build this firmware for the ESP-32, I also used the Arduino IDE rev 1.8.10 with the Arduino ESP32 Dev Module installed from the board manager * , with the following settings: QIO, 4MB, 80MHZ, 921600, NONE, Avrisp Mk II **. I found that baud changing corrupts the serial bus as of 1/13/2018, so the following changes were made to the base libraries: +> Added to esp32-hal-uart.c: +void uartChangeBaudRate(uint8_t uart_nr, uint32_t baudrate) +{ + uartSetBaudRate(&_uart_bus_array[uart_nr], baudrate); +} +void uartChangeConfig(uint8_t uart_nr, uint32_t config) +{ + uart_t* uart = &_uart_bus_array[uart_nr]; + UART_MUTEX_LOCK(); + uart->dev->conf0.val = config; + #define TWO_STOP_BITS_CONF 0x3 + #define ONE_STOP_BITS_CONF 0x1 + + if ( uart->dev->conf0.stop_bit_num == TWO_STOP_BITS_CONF) { + uart->dev->conf0.stop_bit_num = ONE_STOP_BITS_CONF; + uart->dev->rs485_conf.dl1_en = 1; + } + UART_MUTEX_UNLOCK(); +} +size_t uartWritesRemaining(uart_t* uart) +{ + if(uart == NULL) { + return 0; + } + size_t remain; + UART_MUTEX_LOCK(); + remain = 0x7F - uart->dev->status.txfifo_cnt; + UART_MUTEX_UNLOCK(); + return remain; +} +> Added to esp32-hal-uart.h: +void uartChangeBaudRate(uint8_t uart_nr, uint32_t baudrate); +void uartChangeConfig(uint8_t uart_nr, uint32_t config); +size_t uartWritesRemaining(uart_t* uart); +> Added to HardwareSerial.cpp: +void HardwareSerial::changeBaudRate(int baudRate) { uartChangeBaudRate(_uart_nr, baudRate); } +void HardwareSerial::changeConfig(uint32_t config) { uartChangeConfig(_uart_nr, config); } +> Added to HardwareSerial.h: +void changeBaudRate(int baudRate); +void changeConfig(uint32_t config); + +* On the ESP32, if you want to build firmware that is Update compatible with the official production zimodem, you'll need to install the exact libraries I use from http://www.zimmers.net/otherprojs/esp32_v0.zip . If you don't care about that, using the latest libs work just fine. Using: ------ -Upon initialization, or any time the ESP8266 is reset, the modem will display its version, and some information about the host hardware, and then read a configuration file from the internal SPIFFS to re-establish the previously set baud rate, and to attempt to re-connect to the previously connected wireless router. The first time it is run, the firmware will set a baud rate of 1200 and display INITALIZED to let you know that no previous wifi configuration was found. Once the serial terminal displays READY, it is ready to receive commands. +Upon initialization, or any time the ESP module is reset, the modem will display its version, and some information about the host hardware, and then read a configuration file from the internal SPIFFS to re-establish the previously set baud rate, and to attempt to re-connect to the previously connected wireless router. The first time it is run, the firmware will set a baud rate of 1200 and display INITALIZED to let you know that no previous wifi configuration was found. Once the serial terminal displays READY, it is ready to receive commands. -The first command you'll probably want to enter is ATW to list your wireless routers, and then ATW"<>,<>" to connect to the router, then AT&W to save it. +The first command you'll probably want to enter is AT+CONFIG to connect to a wireless router, and set your flow control and other command mode settings. -If you plan to use this primarily from a computer that doesn't need linefeeds, such as the C64, you'll want to enter ATR0 to go into carriage-return only mode, and then at&w to save this setting as well. +Afterwards, if you plan to use this primarily from a computer that doesn't need linefeeds, such as the C64, you'll want to enter ATR0 to go into carriage-return only mode, and then at&w to save this setting as well. -If you want to operate at a higher baud, you'll want to enter ATB9600 (or whatever baud rate you want to try), and then reconnect your terminal program to the modem at that new baud rate. If everything looks good, and you want to keep the new baud rate across restarts, don't forget to save the new baud rate with AT&W. +If you want to operate at a higher baud, you'll want to enter ATB9600 (or whatever baud rate you want to try), and then reconnect your terminal program to the modem at that new baud rate. If everything looks good, and you want to keep the new baud rate across restarts, save the new baud rate with AT&W. Warning though: Most of the example C64 programs assume the modem defaults to 1200 baud. If you want to connect to a remote telnet server, eg coffeemud.net, port 23, you'll want to enter ATDT"coffeemud.net:23". Don't forget to set your terminal program to the proper translation mode (ANSI, ASCII, or whatever). If you are using a Commodore Graphics terminal program and want to connect to a Commodore BBS, eg cottonwoodbbs.dyndns.org port 6502, you'll want to enter ATD"cottonwoodbbs.dyndns.org:6502". -If you want to use Q-Link, you need to add a phone number alias first. To do this, enter ATP"5551212=q-link.net:5190". Then select "Hayes compatible" 1200 baud modem in the Q-Link client. +If you want to use Q-Link, you need to add a phone number alias first. To do this, enter ATP"5551212=q-link.net:5190" or enter it from the config menu AT+CONFIG. From the C64 Q-Link client, select "Hayes compatible" 1200 baud modem when prompted. -If you want to run a Commodore BBS program using the modem, you'll want to configure the BBS program to the same idle baud rate that your modem is using (1200 baud by default), configure it for a Hayes style modem (or the C=1670), and use an initialization string of "ATR0E0S0=1S41=1A6400" plus any other recommended settings from the BBS program. This creates a listener at port 6400 that switches directly to stream mode on the first ring, with no linefeed carriage returns, and no keystroke echo. Your BBS program may require you add certain other settings, such as V0 or X1.. which you should also do. +If you want to run a Commodore BBS program using the modem, you'll want to configure the BBS program to the same idle baud rate that your modem is using (1200 baud by default), configure it for a Hayes style modem (or the C=1670), and either create a persistant listener using AT+CONFIG, or use an initialization string of "ATR0E0S0=1S41=1A6400" plus any other recommended settings from the BBS program. This creates a listener at port 6400 that switches directly to stream mode on the first ring, with no linefeed carriage returns, and no keystroke echo. Your BBS program may require you add certain other settings, such as V0 or X1.. which you should also do. + +If you want to try printing to a CUPS/IPP-printer, enter AT+PRINT?::/, followed by your data. Where ? is A)scii, P)etscii, or R)aw. A 5 second pause in incoming data completes the document and returns to command mode. Example: AT+PRINTR:192.168.1.10:631/ipp/printer -- followed by ENTER and then the data to print, without any pause longer than 5 seconds. Subsequent to doing this, using AT+PRINT will repeat the previous URL. + +If you want to chat on IRC, enter AT+IRC, enter a nickname and a irc host as a phone number entry, then /join channels, chat, or /quit. + +ESP32 Guru Modem users with SD-cards can enter AT+SHELL to get a shell command prompt. Enter ? to get a list of shell commands. Command Set: ----------- @@ -38,6 +87,8 @@ The command set is as follows (not case sensitive): ATZ : closes all open socket connections (preserving the Access Point connection), stops all listeners, and resets the state of the Command processor to the saved configuration, preserving the current baud rate and wifi connection. +A/ : Repeats the previous command + ATI : re-shows the startup message, including wifi connection information. ATI0 : same as ATI ATI1 : Shows the current common variable settings, common 'S' registers. @@ -46,15 +97,18 @@ ATI3 : Shows the modem's current Wireless Router connection ATI4 : Shows only the firmware current version ATI5 : Shows all the current variable settings, all 'S' registers. ATI6 : Shows the current mac address. +ATI7 : Shows the current formatted time (see AT&T). +ATI8 : Shows the firmware build date/time +ATI9 : Same as I3, but also includes any static settings, same order as ATW +ATI10: Shows the last printer url used. ATA : If a server listener has generated a RING, then ATA will switch the last rung connection to Stream mode (see ATD). -ATA/ : Repeats the previous command (no idea why...) ATAn : Causes the modem to create a server listening on port n. When a connection is received, the terminal will generate 1 or more RINGs according to the ATS0 register, followed by a normal CONNECT respose. At this point, all other commands related to connections may be used normally, unless ATS41 is > 0, in which case incoming connections are automatically sent to Stream mode as per ATD or ATA. Listeners are listed along with other connections using ATC0. -ATAPn"[HOSTNAME]:[PORT]" : Adding a P modifier causes all incoming connection input to be translated to PETSCII -ATATn"[HOSTNAME]:[PORT]" : Adding a T modifier causes connection streaming input to be translated per TELNET when the changed to Stream mode -ATAEn"[HOSTNAME]:[PORT]" : Adding a E modifier causes connection terminal echo to be enabled when the changed to Stream mode -ATAXn"[HOSTNAME]:[PORT]" : Adding a X modifier causes connection XON/XOFF flow control to be enabled when the changed to Stream mode +ATAPn : Adding a P modifier causes all incoming connection input to be translated to PETSCII +ATATn : Adding a T modifier causes connection streaming input to be translated per TELNET when the changed to Stream mode +ATAEn : Adding a E modifier causes connection terminal echo to be enabled when the changed to Stream mode +ATAXn : Adding a X modifier causes connection XON/XOFF flow control to be enabled when the changed to Stream mode. ATN0 : Shuts down all listeners, leaving client connections open ATNn : if n > 0 then same as ATAn @@ -88,6 +142,7 @@ ATB"n,xYz" : Sets baud rate n, bits x, parity (E,O,M, or N) for Y, and stop bits ATW : List all wireless network access points scanned within range. The response for each entry is the SSID, following by the RSSI, followed by an * character is the connection is encrypted. ATWn : Where n > 0, this lists up to n wireless network access points scanned within range. The response for each entry is the SSID, following by the RSSI, followed by an * character is the connection is encrypted. ATW"[SSI],[PASSWORD]" : Connects to the wireless access point with the given SSI, using the given password. +ATW"[SSI],[PASSWORD],[IP],[DNS],[GATEWAY],[SUBNET]" : as ATW, but with more options ATWP : Adding a P modifier is the same as all forms of ATW, with both arguments and results presented in PETSCII. ATD : Start a streaming connection between the current opened connection. Use "+++" to exit back to Command mode. @@ -114,33 +169,43 @@ ATHn : Hangs up (disconnects and deletes) the open connection with the given id. ATO : Re-enters a Streaming session (see ATD) under the previous settings, with the current (previous) connection. -ATP : Lists all existing phonebook entries, with the format phone number followed by ATD modifiers, followed by the host and port -ATP"[NUMBER]=[HOSTNAME]:[PORT]" : Adds or Modifies an entry to the phonebook with the given 7 digit number, host, and port. Use ATDnnnnn.. to connect. -ATPP"[NUMBER]=[HOSTNAME]:[PORT]" : Adding a P modifier causes connection input to be translated to PETSCII when connected to that entry. -ATPT"[NUMBER]=[HOSTNAME]:[PORT]" : Adding a T modifier causes connection input to be translated per TELNET when connected to that entry. -ATPE"[NUMBER]=[HOSTNAME]:[PORT]" : Adding a E modifier causes terminal echo to be enabled when connected to that entry. -ATPX"[NUMBER]=[HOSTNAME]:[PORT]" : Adding a X modifier causes XON/XOFF flow control to be enabled when connected to that entry. +ATP : Lists all existing phonebook entries, with the format phone number followed by ATD modifiers, followed by the host and port. Add ? to also get notes. +ATP"[NUMBER]=[HOSTNAME]:[PORT],[NOTES]" : Adds or Modifies an entry to the phonebook with the given 7 digit number, host, port, and notes. Use ATDnnnnn.. to connect. +ATPP"[NUMBER]=[HOSTNAME]:[PORT],[NOTES]" : Adding a P modifier causes connection input to be translated to PETSCII when connected to that entry. +ATPT"[NUMBER]=[HOSTNAME]:[PORT],[NOTES]" : Adding a T modifier causes connection input to be translated per TELNET when connected to that entry. +ATPE"[NUMBER]=[HOSTNAME]:[PORT],[NOTES]" : Adding a E modifier causes terminal echo to be enabled when connected to that entry. +ATPX"[NUMBER]=[HOSTNAME]:[PORT],[NOTES]" : Adding a X modifier causes XON/XOFF flow control to be enabled when connected to that entry. ATP"[NUMBER]=DELETE" : Removes the phonebook entry with the given number. ATS0=n : Changes the number of RING messages received before a CONNECT response is sent, on incoming Server listeners. ATS1=n : Unimplemented, always returns OK ATS2=n : Change the escape character (n = 0-255), Defaults to ASCII decimal 43 ("+") -ATS3=n : Change the Carriage Return Character (n = 0-127), Defaults to ASCII decimal 13 (Carriage Return) -ATS4=n : Change the Line Feed Character (0-127), Defaults ASCII decimal 10 (Line Feed) -ATS5=n : Change the Backspace Character (0-32), ASCII decimal 8 (Backspace) +ATS3=n : Change the Carriage Return Character (n = 0-127), Defaults to ASCII decimal 13 (Carriage Return) +ATS4=n : Change the Line Feed Character (0-127), Defaults ASCII decimal 10 (Line Feed) +ATS5=n : Change the Backspace Character (0-32), ASCII decimal 8 (Backspace) ATS6 ... 39=n : Unimplemented, always returns OK ATS40=n : Change the size of the connection packets (n > 0), Defaults to 127 bytes ATS41=n : When n > 0, all incoming Server listener connections are immediately sent to Stream mode. If n=0, connections remain in normal command mode (default). ATS42=n : Set the CRC8 for an attached Transmit command. e.g. ATS42=123T"[MESSAGE]" returns error unless 123 is CRC8 of "[MESSAGE]". ATS43=n : Sets a standby baud rate n for the next incoming or outgoing connection only. ATZ clears. ATS44=n : Sets an automatic delay of n milliseconds after most bytes written to the Serial port. This is for computers that support a baud rate, but can't really keep up, and you don't want to use flow control. -ATS45=n : Changes how packet and at&g data is delivered. 0 is normal binary with normal headers, 1 is 78 char HEX digit streams followed by EOLN with hex digit headers, 2 is decimal digits followed by EOLN, with decimal digit headers. -ATS46=n : Changes DCD status. n=0 is default DCD=HIGH=online. n=1 is DCD=LOW=online -ATS47=n : Changes CTS status. n=0 is default CTS=HIGH=active. n=1 is CTS=LOW=active -ATS48=n : Changes RTS status. n=0 is default RTS=HIGH=active. n=1 is RTS=LOW=active (N/A on ESP01) -ATS49=n : Changes DCD pin number, n=2 is default -ATS50=n : Changes CTS pin number, n=0 is default on ESP01, and default is 5 otherwise +ATS45=n : Changes how packet and at&g data is delivered. 0 is normal binary with normal headers, 1 is 78 char HEX digit streams followed by EOLN with hex digit headers, 2 is decimal digits followed by EOLN, with decimal digit headers, 3 is normal without SUM header. Add 4 to add packet numbers. +ATS46=n : Changes DCD status. n=0 is default DCD=HIGH=online. n=1 is DCD=LOW=online. n=2 always HIGH. n=3 always LOW. +ATS47=n : Changes DCD pin number, n=2 is default +ATS48=n : Changes CTS status. n=0 is default CTS=HIGH=active. n=1 is CTS=LOW=active. n=2 always HIGH. n=3 always LOW. +ATS49=n : Changes CTS pin number, n=0 is default on ESP01, and default is 5 otherwise +ATS50=n : Changes RTS status. n=0 is default RTS=HIGH=active. n=1 is RTS=LOW=active. n=2 always HIGH. n=3 always LOW. (N/A on ESP01) ATS51=n : Changes RTS pin number, n=4 is default (N/A on ESP01) +ATS52=n : Changes RI status. n=0 is default RI=HIGH=active. n=1 is RTS=LOW=active. n=2 always HIGH. n=3 always LOW. (N/A on ESP01) +ATS53=n : Changes RI pin number, n=14 is default (N/A on ESP01) +ATS54=n : Changes DTR status. n=0 is default DTR=HIGH=active. n=1 is RTS=LOW=active. n=2 always HIGH. n=3 always LOW. (N/A on ESP01) +ATS55=n : Changes DTR pin number, n=12 is default (N/A on ESP01) +ATS56=n : Changes DSR status. n=0 is default DSR=HIGH=active. n=1 is RTS=LOW=active. n=2 always HIGH. n=3 always LOW. (N/A on ESP01) +ATS57=n : Changes DSR pin number, n=13 is default (N/A on ESP01) +ATS60=n : When n > 0, immediately saves existing listeners and automatically restores them later. n=0 to clear. +ATS61=n : When n > 0, sets the number of seconds to timeout a print job stream (AT+PRINT). Default is 5 seconds +ATS62=n : When n > 0, enables/disables telnet support in the ATD (Dial) command. Default is 1 (enabled). + +++ : With a 1 second pause with no other characters afterwards, this will disconnect the current opened connection. ATT"[MESSAGE]" : Transmit the given text string, with \r\n at the end, on the current connection. @@ -150,22 +215,31 @@ ATTPn : Where n > 0, this starts a transmit of exactly n bytes to the current co ATT+"[MESSAGE] : A + argument may be used to force the 'T' command to return the CRC8 of the message instead of OK, when successful. ATL0 : Re-sends the most recently sent data packet again -ATLn : Re-sends the most recently sent data packet for connection id n. +ATLn : Re-sends the most recently sent data packet for connection id n. Prefix n with 0 for earlier packet. +AT&H : Shows a help file from the web, or brief help otherwise. Use &H6502 to reiforce web download. AT&L : Reloads the saved configuration. -AT&W : Saves the current configuration: WiFi settings(ATW), baud rate (ATB), end of line (ATR) settings, flow control (ATF), echo mode (ATE), extended responses (ATX), verbose responses (ATV), quiet responses (ATQ), PETSCII mode (AT&P1), DCD status (ATS46), CTS status (ATS47) -AT&F : Restores the modem to factory default settings. -AT&On : n is 1 to turn on internal serial-reception log, n is 0 to turn off or view a previously turned-on log. -AT&H : Shows a help file from the web, or brief help otherwise. +AT&W : Saves the current configuration: WiFi settings(ATW), baud rate (ATB), end of line (ATR) settings, flow control (ATF), echo mode (ATE), extended responses (ATX), verbose responses (ATV), quiet responses (ATQ), PETSCII mode (AT&P1), pin statuses (ATS46 - S58), Rings (ATS0), Listener Stream-mode (ATS41), and Listener restore (ATS60), printer spec (AT+PRINT), and busy message. +AT&F : Restores the modem to factory default settings. Use &F86 to reformat the SPIFFS. +AT&On : n is 1 to turn on internal serial-reception log, n is 0 to turn off or view a previously turned-on log, n is 88 to turn on ESP32 debug port. AT&U : Checks the firmware home page to see if a new version is available. AT&U6502 : Will update the firmware from the home page on the web. AT&U=x: Will update the firmware from the web to custom version x. +AT&Kn : Flow Control, similar to ATFn, n=0,1,2: disable, n=3,6: rts/cts, n=4,5: Xon/Xoff AT&Pn : Where n > 0, all command mode input and output will be translated to/from PETSCII before internal processing. This will not affect received packet data, or the stream mode. -AT&Nx : Shows the status of ESP8266 I/O pin x - +AT&Nx : Shows the status of ESP module I/O pin x AT&Mn : Adds the byte denoted by n to a list of mask-out bytes. These are bytes that are not transmitted to the serial port in command mode incoming packets. If this command is followed by a C, N, or A command on the SAME LINE, then the setting will apply ONLY to that connection or listener. AT&M : Resets the mask-out bytes list. No bytes will be masked-out. If this command is followed by a C, N, or A command on the SAME LINE, then the setting will apply ONLY to that connection or listener. AT&Dn : Adds the byte denoted by n to a list of delimiter bytes. These are bytes that will compose the last byte in a command-mode incoming packet that is still shorter than the limit set by ATS40. This is useful for CR-LF formatted data. If this command is followed by a C, N, or A command on the SAME LINE, then the setting will apply ONLY to that connection or listener. -AT&D : Resets the delimiter bytes lis. No bytes will be delimited, and packets will contain as many bytes as are received and allowed by ATS40. If this command is followed by a C, N, or A command on the SAME LINE, then the setting will apply ONLY to that connection or listener. +AT&D : Resets the delimiter bytes list. No bytes will be delimited, and packets will contain as many bytes as are received and allowed by ATS40. If this command is followed by a C, N, or A command on the SAME LINE, then the setting will apply ONLY to that connection or listener. + +AT&S"40=[HOSTNAME]" : Change the modem hostname +AT&S"41=[TERMTYPE]" : Change the telnet 'termtype' response string +AT&S"42=[BUSYMSG]" : Change the stream connection 'busy message' + +AT&T"[TIMEZONE],[TIME FORMAT],[NTP URL]" : set up the NTP clock. DISABLE to disable. Format is like Java SimpleDateFormat, but with % escapes. Each argument is optional. +AT&G"[HOSTNAME]:[PORT]/[FILENAME]" : Streams a file from an HTTP source on the internet. The header contains channel 0, file length, and an 8-bit sum of all the bytes in the forthcoming file, followed by the bytes of the file, all formatted as a normal packet. An ASCII 3 (CNTRL-C) received during the transfer will abort. The S44 register can be used to create artificial delays in this output. XON/XOFF Flow control also remains in effect with, on a byte-by-byte basis for the auto and manual flow control systems. Requires flash space for caching, or S45=3 to eliminate the SUM header. +AT&Y : Resets the state machine string. No state machine will be executed. +AT&Yn : Change the current state (for command mode AND current connection) to state n, where n is a decimal number. +AT&Y"[CODED STATE MACHINE]" : Adds the coded format string to a state machine. If this command is followed by a C, N, or A command on the SAME LINE, then the setting will apply ONLY to that connection or listener. State Machine Format: MMcCCNN ... States are numbered by their order in the list starting with 00. Non-matches automatically go to the next state until a match is made. 'MM' is hex byte to match (or 00 to match all). 'c' is one of these commands :e=eat byte, p=push byte to que, d=send byte, q=send all queued, x=flush queue, r=replace with byte represented by hex CC. 'C' is either '-', one of the command letters above, or a hex byte value if the first command was 'r'. 'NN' is the next state to go to, with 00 being the first state. -AT&G"[HOSTNAME]:[PORT]/[FILENAME]" : Streams a file from an HTTP source on the internet. The header contains channel 0, file length, and an 8-bit sum of all the bytes in the forthcoming file, followed by the bytes of the file, all formatted as a normal packet. An ASCII 3 (CNTRL-C) received during the transfer will abort. The S44 register can be used to create artificial delays in this output. XON/XOFF Flow control also remains in effect with, on a byte-by-byte basis for the auto and manual flow control systems. diff --git a/TODO b/TODO index af5f11f..a9c1f0b 100644 --- a/TODO +++ b/TODO @@ -1,11 +1,21 @@ +Bugs Reported (nor reproduced): + +New Features: + *. Ability to use protocols for at&g, and maybe have alternative at+web + *. make a persistent-ish ftp shell + *. Ability to ftp file streamdown, like at&g.. maybe at+ftp *. Support switching between CONNECT 1 and CONNECT 1200 messages - *. Support a larger incoming network buffer. *. Baud rate test commend *. ATW0 should be same as ATW - *. Hardware flow control *. SLIP support? *. PPP support? - *. Network printer support? - - - \ No newline at end of file + +New hardware hopes: +C+B 0 FLAG2+PB0 RxD Receive Data (Must be applied to both pins!) +D! 1 RTS Ready To Send +F! 3 RI Ring Indicator (1650 ring indicator - output of modem -> input of computer ) +H! 4 DCD Data Carrier Detect +J! 5 1650 Pulse Dial Pin (output of computer -> input to modem) ++K 6 CTS Clear To Send +M! 8 TxD Transmit Data + diff --git a/cbm8bit/64net_apps.d64 b/cbm8bit/64net_apps.d64 index 495b383..1470ee8 100644 --- a/cbm8bit/64net_apps.d64 +++ b/cbm8bit/64net_apps.d64 @@ -1,6 +1,6 @@ -KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK -UFXLPMNJSR RETIMERNLDY BUF1DX:LDA BUF1,Y:DEC NUMX4\NCMP #$0D:BNE BUFXNCROfNLDX CRXFLG:BNE BUFXNCRipNSTY CRXFLG:INC CRXFLGzNBUFXNCR INC BUF1DXNLDA BUF1DX:CMP #$FD:BCS BUFXDUNNBUF1NI LDA NUMX:BNE BUFXLPNJMP BUF1DUNNBUFXDUN JMKKKKKP BUF1DUN-0uBUFAP LDX BUFAPX:JSR $FFC6:LDY #$00:TYA:STA BUF1DX:STA CRXFLGXDuBUFALP JSR $FFE4:LDY BUF1DX:STA BUF1,YyNuJSR $FFB7:CMP #$08:BEQ BUFAXXuCMP #$42:BEQ BUFAXbuCMP #$00:BEQ BUFAP1:INC BUF1DXluBUFAX STA CRXFLG:JMP BUF1DUNuBUFAP1 KKKKKINC BUF1DX:LDA BUF1DX:CMP #$FE:BCC BUFALP:JMP BUF1DUN(BUFTMR .BYTE 0;BUF1DX .BYTE 0NBUFX .BYTE 0 0_BUF1 .BYTE 0ٙ NOP;:F$"PMLVIC.BAS":8,8,15,"S0:PMLVIC*":F$,8:F$,8){צzt>JtYs^+]'yw^){i{+]gy{g{)wT)JKKKK " ";:P$P$A$:5010P$""5010#P$(P$,(P$)1):"  ";:50101pTTTI100HzML12:TITT6010N^Pß5,2,0,(8)vZá#5,A$:A$""A$;dáA$:A$""5,A$;nÉ 50010U8:F$"D64WGET":1,U,15,"S0:"F$:1:(F$),U:(F$),U - 22273!.D PMLVIC.BINH; PACKET ML VIC20 BY BO ZIMMERMANh; UPDATED 20170814 10:14P; 56,87 PROTECT IT!dJMP BUF1LINnJMP BUFXLINxJMP PACKETJMP CRCPJMP BUFAPNOP:NOP:NOPNUMX .BYTE 0 CRXFLG .BY P$)3,(P$,OS);:BRBR((P$)OS1) 930:P0PP10P002320: P1BR3,P$;:BRBRP1:OS1:2320_$ 3,(P$,BR);:OSBR1:P1BROS1. 1,"U2";3;0;T;S:1,E,E$,E1,E2:SS18 1,"U1";3;0;T;S:1,E,E$,E1,E2:E66S2552300B E70S0TT1:S5010 pTTTI100 zML12:TITT6010  !Pß5,2,0,(8)%!Zá#5,A$:A$""A$;JtYs^+]'yw^){i{+]gy{g{)wT)JKKKK A$;" ";:P$P$A$:5010P$""5010&P$(P$,(P$)1):"  ";:50104pTTTI100KzML12:TITT6010QaPß5,2,0,(8)yZá#5,A$:A$""A$;dáA$:A$""5,A$;nÉ 50010U8:F$"D64WGET":1,U,15,"S0:"F$:1:(F$),U:(F$ + 22273!.D PMLVIC.BINH; PACKET ML VIC20 BY BO ZIMMERMANh; UPDATED 20170814 10:14P; 56,87 PROTECT IT!dJMP BUF1LINnJMP BUFXLINxJMP PACKETJMP CRCPJMP BUFAPNOP:NOP:NOPNUMX .BYTE 0 CRXFLG .BY PUT4 RTS W;;;;;;;;;;;;;;;;;;XDO JSR $F291'bPHP:PHAGLDA $BA:CMP #$02:BNE NOCLOSeDOCLOS2 LDA #3 ; 00000011STA COMM ; DISABLE STUFFLDA NMINV1:CMP #NMI:BNE NOCLOSSEILDA OLDNMISTA NMINVLDA OLDNMI1STA  10 11 12 14 15 130 129 128 0 0 0 0 0 0GEVECS .BYTE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0_FOLDNMI .BYTE 71 254rJRCOUNT .BYTE 0TERRS .BYTE 0^ZPXMO .BYTE 0hZPXMF .BYTE 0ٙ NOP;:1,8,15,"S0:SWIFTDRVR*":1:"SWIFTDRVR.BAS",8XA:PHA LDY #$00:LDA ($FD),Y:STA $FB7 INY:LDA ($FD),Y:STA $FCD LDX #$09{( DIGLP LDY #$00:LDA ($FD),Y:CLC:ADC $FB:STA ($FD),Y2 @@ -8,7 +8,8 @@ INY:LDA ($FD),Y:ADC $FC:STA ($FD),Y DEX:BNE DIGLPF LDY #$00:PLA:SEC:SBC #48:CLC:ADC ($FD),Y:STA ($FD),YP:INY:CMP #91:BNE PCKLP16 LDA BUF1,Y:INY:CMP #32:BNE PCKERR1` LDA #PEE0:STA $FD:LDA #PEE0:STA $FE{ JSR PCKDIG:BNE PCKERR1 LDA #PEE1:STA $FD:LDA #PEE1:STA $FE JSR PCKDIG:BNE PCKERR1 LDA #PEE2:STA $FD:LDA #PEE2:STA $FE JSR PCKLDA #$00:STA CRCX+DOCRC0 LDA CRCX:CMP BUF1DX:BCC DOCRC13RTSVDOCRC1 TAX:LDA BUF1,X:STA CRCEcLDY #$08DOCRC2 LDA CRC8:E CRCE #$01:STA CRCSLDA CRC8:CLC:R:STA CRC8LDA CRCS:BEQ DOCRC3LDA CRC8:E #$8C:STA CRC8 DOCTE 0 PEE0 .BYTE 0 0( PEE1 .BYTE 0 0; PEE2 .BYTE 0 0L CRC8 .BYTE 0] CRCX .BYTE 0n CRCE .BYTE 0 CRCS .BYTE 0 "TIMEOUT .BYTE 0 0 ,BUFAPX .BYTE 5 DEBUG .BYTE 0 0 SETPSTR LDA $2D:STA $FB:LDA $2E:STA $FC -SETPLP LDY #$00LDA ($FD),Y:STA BUF1,YINY:BNE CRCPL1,CRCPDUN JMP DOCRC8f(#RETIME LDA $A1:STA TIMEOUT:LDA #$00:STA TIMEOUT1:RTS%CHKTIM LDA $A1:CMP TIMEOUT:BNE CHKTIM2:LDX #$00:RTS&%CHKTIM2 STA TIMEOUT:INC TIMEOUT10%LDA TIMEOUT1:CMP #$04:BCS TIMBADUF1LPL'CMP #$0A:BEQ BUF1LPV'CMP #$0D:BEQ BUF1DUN-['JSR RETIME[`'INC BUF1DX:LDA BUF1DX:CMP #$FE:BCC BUF1LPst'BUF1DUN JSR SETPSTR~'LDY #$02:LDA BUF1DX:STA ($FB),Y'LDY #$03:LDA #BUF1:STA ($FB),Y'LDY #$04:LDA #BUF1:STA ($FB),Y*JSP BUF1DUN(uBUFAP1 INC BUF1DX:LDA BUF1DX:CMP #$FE:BCC BUFALP:JMP BUF1DUN<PBACK .BYTE 0 0OBUFTMR .BYTE 0bBUF1DX .BYTE 0uBUFX .BYTE 0 0BUF1 .BYTE 0ٙ NOP;:F$"PML128.BAS":1,8,15,"S0:PML128*":F$,8:F$,80TT1:S0:2360L #5,A$:A$""A$;"dáA$:A$""5,A$;#nÉ 500104#U8:F$"IRC":1,U,15,"S0:"F$:1:F$,U:F$,UU8:F$"D64WGET64-128":1,U,15,"S0:"F$:1:(F$),U:(F$),UONTENT. ONE.";CO$:"EADERS COMPLETE."*"ECEIVING"(CL)" BYTES";CO$d1,UN,15,"I0:":3,UN,3,"#":T1:S0:OS1:1,E,E$,E1,E2j1,"B-P";3;0:TB0:BR256 " "(T)","(S):""; OS1OS(0:2360L :"ONE.";CO$:3:1 V 5,"ATH0":5,"ATZ":TTTI100% ` TITT2400. j 5:8 P$""E " ";Y A$:A$""5010o A$(13)" ": A$(20)A$;" ";:P$P$A$:5010 P$""5010 P$(P$,(P$)1):"  ";::LDA ($FB),Y! +SETPLP LDY #$00LDA ($FD),Y:STA BUF1,YINY:BNE CRCPL1,CRCPDUN JMP DOCRC8f(#RETIME LDA $A1:STA TIMEOUT:LDA #$00:STA TIMEOUT1:RTS%CHKTIM LDA $A1:CMP TIMEOUT:BNE CHKTIM2:LDX #$00:RTS&%CHKTIM2 STA TIMEOUT:INC TIMEOUT10%LDA TIMEOUT1:CMP #$04:BCS TIMBADUF1LPL'CMP #$0A:BEQ BUF1LPV'CMP #$0D:BEQ BUF1DUN-['JSR RETIME[`'INC BUF1DX:LDA BUF1DX:CMP #$FE:BCC BUF1LPst'BUF1DUN JSR SETPSTR~'LDY #$02:LDA BUF1DX:STA ($FB),Y'LDY #$03:LDA #BUF1:STA ($FB),Y'LDY #$04:LDA #BUF1:STA ($FB),Y*JSP BUF1DUN(uBUFAP1 INC BUF1DX:LDA BUF1DX:CMP #$FE:BCC BUFALP:JMP BUF1DUN<PBACK .BYTE 0 0OBUFTMR .BYTE 0bBUF1DX .BYTE 0uBUFX .BYTE 0 0BUF1 .BYTE 0ٙ NOP;:F$"PML128.BAS":1,8,15,"S0:PML128*":F$,8:F$,80TT1:S0:2360L ),U,A$:A$""A$;"dáA$:A$""5,A$;#nÉ 500104#U8:F$"IRC":1,U,15,"S0:"F$:1:F$,U:F$,UU8:F$"D64WGET64-128":1,U,15,"S0:"F$:1:(F$),U:(F$),UDD01: #$EF:STA $DD01OPLA:PLP:RTS(o;;;;;;;;;;;;;;;;;;6pDOPUT PHABzLDA $9AZCMP #$02:BEQ DOPUT3bPLAwDOPUT2 JMP $F1CADOPUT3 LDA STATUS #16:;%00010000BEQ DOPUT3CLC:PLA:STA PORTBCC DOPUT4 +LDA #$00DONMINV1TYA:PHA:LDY #20$INCLO LDA VECS,Y:STA $0319,Y>DEY:BNE INCLO:PLA:TAY\ JSR NOINIT: ; DO, CLI!}NOCLOS JSR UPDCD:PLA:PLP:RTSLDOCLAL JSR $F32F:PHP:PHA:JMP DOCLOS2?;;;;;;;;;;;;;;;;;; @BAUDS .BYTE 9 0 0 1 2 2 5 6 7 8 8 9 :LDA ($FB),Y! CMP #$50:BNE SETPNXT5 INY:LDA ($FB),YN CMP #$80:BEQ SETPDUNw @@ -33,7 +34,7 @@ DIGERR2 LDA #$FF:RTS  ȅ h`h`ʹmʍ`ʌʹMʪ Mʍʽ ȍ`L˩ʩ ʢ _ʥ oL˩C oʭ ʭLḼ  -LoLͩ gʢ _ʩ o o oʮ ʎ ˩ gʩ* oʭʅ` KLͩ`` Wʠ ʙϥLέ ʍI S˭ʍϥ ʍϭʍ gʢ _ʠʹ oʬȌʐ gʢ W )C1 8 1,"U1";3;0;T;S:1,E,E$,E1,E2:E66S2552300)B E70S0TT1:S0:2360EL :"ONE.";CO$:3:1fV 5,"ATH0":5,"ATZ":TTTI100v` TITT2400j 5:P$""" ";A$:A$""5010A$(13)" ":A$(20)A$;LE:BNE CHRO(4NJSR DCDCHK:BEQ CHRO(>NTXA:PHA:TYA:PHA!)RNLDA #TBUF:STA $F9:LDA #TBUF:STA $FAC)\NLDA SAVABYT:STA $9E:JSR $F014W)pNPLA:TAY:PLA:TAXu)zNCHRO PLA:JMP (SAVVTR18))P; ; ; ; ; T FF30 ; ; ; ; ; ;)PT LDA FLG:BNE E)PTNO JMPA:TAX*0RLDA $029B:CMP $029C:BEQ CHRINO+:RLDA $C6:CMP $0289:BCS CHRINO+DRTXA:PHA:TYA:PHA:SEI>+NRLDX $029C:LDA RBUF,X:INC $029C^+XRLDX $C6:STA $0277,X:INC $C6v+bRCLI:PLA:TAY:PLA:TAX+RCHRINO JMP (SAVVTR16)+U; ; ; ; ; ERRUPT ; ; ; ; ; ;+U,^VINC LASTDCD:INC $D020,hVJSR TIMEIN,rVLDA #$7F:STA PAUSETM-|VJSR WELCOME&-VRPT2 JSR CHKIDLE:BCC RPT3E-VRTMO JSR TIMEOUT:JMP RNOe-VRPT3 JSR CHKTMO2:BCS RTMO~-VLDA TIMEFLG:BNE RNO-WLDA PAUSETM:BEQ RPT9-&WDEC PAUSETM:BNE RNO-; ; ; BRKOUT ; ; ; ; ; ;.YBRKOUT NOP.NTXA:PHA:TYA:PHA!)RNLDA #TBUF:STA $F9:LDA #TBUF:STA $FAC)\NLDA SAVABYT:STA $9E:JSR $F014W)pNPLA:TAY:PLA:TAXu)zNCHRO PLA:JMP (SAVVTR18))P; ; ; ; ; T FF30 ; ; ; ; ; ;)PT LDA FLG:BNE E)PTNO JMPA:TAX*0RLDA $029B:CMP $029C:BEQ CHRINO+:RLDA $C6:CMP $0289:BCS CHRINO+DRTXA:PHA:TYA:PHA:SEI>+NRLDX $029C:LDA RBUF,X:INC $029C^+XRLDX $C6:STA $0277,X:INC $C6v+bRCLI:PLA:TAY:PLA:TAX+RCHRINO JMP (SAVVTR16)+U; ; ; ; ; ERRUPT ; ; ; ; ; ;+U,^VINC LASTDCD:INC $D020,hVJSR TIMEIN,rVLDA #$7F:STA PAUSETM-|VJSR WELCOME&-VRPT2 JSR CHKIDLE:BCC RPT3E-VRTMO JSR TIMEOUT:JMP RNOe-VRPT3 JSR CHKTMO2:BCS RTMO~-VLDA TIMEFLG:BNE RNO-WLDA PAUSETM:BEQ RPT9-&WDEC PAUSETM:BNE RNO-; ; ; BRKOUT ; ; ; ; ; ;.YBRKOUT NOP. ?` ʝ` ` 'ʩLK 'ʩLK 'ʩLK 'ʩLK 'ʩLK 'ʩLKʘHH݅ hh` ʍʍʍʍʍʍʍʍʍ`Lwʩʥ`Lʥ )`L̢ Wʭ /˥ ʬʥʐ %˭Lx̭ .I' S˥ ̀Lx̭ ʭ3 gʢ _ʠ o gʩ- oʢ _ʩ o g L˭L gʩ: oʢ _ʥ oḼ C o gL gʢ _ʩ e6 LL KL Aͭ  gʩ: o %L gʩ- oʭ _ʠ oL % LU (SAVVTR20))(PE DEC FLG)!LDY #$00kH!BUFCMP1 LDA ($FB),Y:BEQ BUFCMPYR!CMP BUF1,Y:BNE BUFCMPN\!INY:BNE BUFCMP1f!BUFCMPN LDY #$FFp!BUFCMPY RTS#; ; ; ; SCN OUT STRING ;FFCC$; ; ; ; MDM OUT STRING ; ; ; ; ; $OUTMSTR STX $FB:STY $FC7$LDA #$02:JSR $EFE1L$OUTMST0 LDY #$00p$OUTMST1 LDA ($FB),Y:BEQ OUTMST2~$JSR $FFD2$INY:BNE OUTMST1$OUTMST2 RTS%; ; ; ; DELAY TIMERS ; ; ; ; ;%RETIMS LDA $A'; ; ; ; ; INIT ; ; ; ; ; ;'INIT LDA #$00:STA STAT1S<'LDX #STRINIT0:LDY #STRINIT0:JSR OUTSSTRe'LDA #$05:LDX #$02:LDY #$00:JSR $FFBA$'LDA #$01:LDX #EIGHT:LDY #EIGHT:JSR $FFBD.'JSR $F409;; FFC08'; CHECK ERRB'; ERR, SET STAUTSSTR'LDX #STRINIT1:LDY #STRINIT1:JSR OUTMSTR,'JSR BUFLALN:LDA BUF1DX:BEQ INITB2H'LDX #STROK:LDY #STROKb'JSR BUFCMP:BNE INITB2'LDX #STRINITX:LDY #STRINITX:JSR OUTSSTR'INITB3 JSR RECB'LDX #STRINIT2:LDY #STRINIT2:JSR OUTM #STRINIT5:LDY #STRINIT5:JSR OUTSSTR !(LDX #STRIPIS:LDY #STRIPIS:JSR OUTSSTRF!(LDX #BUF1:LDY #BUF1:JSR OUTSSTRt!((LDX #STRINIT4:LDY #STRINIT4:JSR OUTSSTR!2(LDX #STR2:LDY #STR2:JSR OUTSSTR!)LDY #$00!)INITL1 LDA $0314,Y:STA SAVVTR,STA $0316"J)LDA #BRKOUT:STA $0317#T)LDA #OT:STA $031A#^)LDA #OT:STA $031B5#h)LDA #T:STA $031CL#r)LDA #T:STA $031Dg#|)LDA #CHKIN :STA $031E#)LDA #CHKIN :STA $031F#)LDA #CHKOUT:STA $0320#)LDA #CHKOUT:STA $0321#)LDA #CHN:TA $032C$)LDA #CLALL:STA $032D$*LDA #L:STA $0330%*LDA #L:STA $0331-%*LDA #S:STA $0332D%&*LDA #S:STA $0333r%0*LDY #$00:LDA #SAVBTR:CMP #$FF:BNE INITL3%5*LDA #$02:STA $D020:STA $D021:RTS%:*INITL3 LDA $0308,Y:STA SAVBTR,Y%D*INY:LDA #NMIRTN:STA $0318&*;LDA #NMIRTN:STA $0319'*CLI:JSR $F409:JMP $FFCC5':; ; ; ; ; KEIN 0308 ; ; ; ; ; ;V':KEIN LDA SECFLAG:BNE KEIN0g':JMP (SAVBTR)':KEIN0 JSR $FFD2:BNE KEIN1':KE JMP $FFD2':KEIN1 PHA:SBC #$80:BCS KEIN2'XBUFFILL JSR BUF1:LDY #$00:TYA:STA BUF1DX+bBUFFAL0 JSR RETIMSDlLDA #$01:STA TIMSWAThvBUFFAL1 JSR CHKTIMS:BNE BUFFADNBUFFAL2 LDA $029B:CMP $029CBEQ BUFFAL1LDA #$02:JSR $F1B8LDY BUF1DX:STA BUF1,YCMP #$00:BEQ BUFFAL1A BUF1,X:STA BUF1,YCPY BUF1DX:BEQ BUFFIL2*INX:INY:BNE BUFFIL1MBUFFIL2 DEC BUF1DX:BEQ BUFFIDNfLDY #$00:BEQ BUFFIL0BUFFIL3 INY:CPY BUF1DX:BEQ BUFFNLDA BUF1,Y:CMP #$0D:BEQ BUFFNSEC:BCS BUFFIL3BUFFN LDA #$00:STA BUF1,Y#$00:STA BUF1DX:RTSN BUFLAD4 DEY'X LDA BUF1,Y:CMP #$0D:BNE BUFLAD5:b INY:BNE BUFLAZl BUFLAD5 CPY #$00:BEQ BUFLArv DEY:SEC:BCS BUFLAD4 BUFLA LDX #$00 BUFLAF1 LDA BUF1,Y:STA BUF1,X INX:INY:CPY BUF1DX:BNE BUFLAF1 BUFLAOT LDA #$ ; ; ; ;#OUTSSTR STX $FB:STY $FC#JSR $FFCC:JMP OUTMST01"$BUF1 LDX #$00:TXAT,$BUFL STA BUF1,X:DEX:BNE BUFL\6$RTS}S$; ; ; ; E LINERS ; ; ; ; ;T$RECB LDA $029C:STA $029B:RTS^$DCDCHK LDA $DD01: #$10:RTSh$RESMIO JSR $F04F:JMP $1:STA TIMSBYT&%LDA #$04:STA TIMSWAT0%LDA #$00:STA TIMSBYT1:RTS1:%CHKTIMS LDA $A1LD%CMP TIMSBYT:BEQ TIMSDsN%CHKTIMS2 STA TIMSBYT:INC TIMSBYT1X%LDA TIMSBYT1b%CMP TIMSWAT:BCS TIMSBADl%TIMSD LDX #$00:RTSv%TIMSBAD LDX #$FF:RTST1S, RTSL'LDA #RBUF:STA $F7:LDA #RBUF:STA $F80V'LDA #TBUF:STA $F9:LDA #TBUF:STA $FADj'; BAUD CRECTI[t'LDA 678:BNE INITB1z~'LDA #73:STA 665:BNE INITB2'INITB1 LDA #43:STA 665'INITB2 JSR RECB'LDX #STRINITX:LDY #STRINITX:JSR OSTR'JSR BUFLALN:LDA BUF1DX:BEQ INITB3 'LDX #STROK:LDY #STROK/ 'JSR BUFCMP:BNE INITB3] 'LDX #STRINITX:LDY #STRINITX:JSR OUTSSTRr 'INITB4 JSR RECB (LDX #STRINIT3:LDY #STRINIT3:JSR OUTMSTR @@ -64,21 +65,23 @@ Y:LDA PBACK +; ; ; ; ; STRING TABLE ; ; ; ; ; ; ,EIGHT .BYTE 8 0 6STRINIT1 .BYTE 13:.BYTE "ATHZQ0V1X1F0E0N0R0":.ORT ":.BYTE 0 mSTRINIT5 .BYTE 13 0D rSTRINIT0 .BYTE "TELNETD 1.0 INIT."c |STRINITX .BYTE ".":.BYTE 0 STRATHA0 .BYTE "+++":.BYTE 0 STRATHA1 .BYTE "ATH":.BYTE 13 0 ; ; ; ; CHECK METHODS ; ; ; ; ; CLK1 LDA $DC0F: #$7F STA $DC0FP IDLMINS:RTS/CHKTMO2 LDA $DD0B:CMP TMOHS:BEQ CHKTMO2M]PHP:LDA $DD0A:LDA $DD09:LDA $DD08:PLP:RTSxCHKTMO2M LDA $DD0A:PHALDA $DD09:LDA $DD08PLA:CMP TMOMINS:RTSCHKPAS1 LDA $DC0B:LDA $DC0A"LDA $DC09:PHA:LDA $DC08,PLA:CMP # 9E:STA TBUF,X:INC $029EJSR $F028-PLA:TAX:PLA:TAY:RTSH8OUTMXP STX $FB:STY $FCULLDY #$00lVOUTMXL LDA ($FB),Y{`BEQ OUTMXDjLDX $029E:STA TBUF,X:INC $029EtINY:BNE OUTMXL~OUTMXD RTS; ; ; ; TIMEOUT SETUP ; ; ; ; ; TIMEI UF1DUNNBUFXDUN JMP BUF1DUN@0uBUFAP LDX BUFAPX:JSR $FFC6:LDY #$00:TYA:STA BUF1DX:STA CRXFLGkDuBUFALP JSR $FFE4:LDY BUF1DX:STA BUF1,YNuJSR $FFB7:CMP #$08:BEQ BUFAXXuCMP #$42:BEQ BUFAXbuCMP #$00:BEQ BUFAP1:INC BUF1DXluBUFAX STA CRXFLG:JM -AA:BB0:FFTT:TWW:I0:W116:WWW:R,W15:S,30(3W)%*Q,17:*O1DD1Q,16N*TWWW16:WW0d*AA(40E):870:r*O1840*(C1)93(C1)93840*C1,GG:C1,GG* (C2)93(C2)93840*C2,GG:C2,GG* E0E0 $: .t1260:1250:C$:826:826:1250:D$:1270.~A101200+.A101230Z.Z12:1250:E$:826:1290:826:1250:F$f.AAAA1q.:300.Z12:1250:G$:826:1290:826:1250:H$.AAAA1.:300.Z12:1250:I$:826:1290:8 ":NN1:0ZX110:V1020025:S,V:::S,0:*0nA$;" ";BL$:BL$:BL$>0x826:X1500:e0V200501:S,V::S,0:R,0:Q,00826:B$WP$:PF3.6:G0B$EP$:PG3.60T7((EP$)2):T0T00"";T);EP$0T33.5(WP$)2 ANT TO PLAY AGAIN? ";:600002(IN$,1)"N"1600!2T$EP$:EP$WP$:WP$T$+2 230X2@"":5,"ATZ":I01000:#5,A$:I:5:p2#5,P$:P$""2000~25,(17);2ML6:I082:PR(I)(MVI):I:PR(1)(MV1)2PR(0)0PR(6)PR(8)5,"ATL $:ML9:PR$(1)(((MV8)),2)4 5,"ATS42=";PR$(1);"T";(34);P$;(34)C4 ML:P$"OK"P$""P$PR$(0):3000I4 u4OA0RN(TI):P$"RAND:"(RN):3000:4P$"RAND: ":2200:RN(P$):4OA0RN(1):P$"RAND:"(RN):3000:4P$"RAND 5IN$IN$Z$:Z$;ZD$;Z$;6Z13IN$(IN$,2):CR$;:26Z20ZL1IN$(IN$,ZL1):"";:60010e6Z141Z$(20(ZL1)):Z2ZL:Z$;:Z:60000p66001060"";9);"CURSOR #";NM$;" ";PG$;(142)6::9);"C64NET WIFI 1200 BAUD VERSION" 5:7"CONNECTING TO ";IN$;"...";85,"AT&M13&M10&D13C";(34);IN$;":6502";(34)885,P$:P$"OK"62130e8(P$,8)"CONNECT "CP((P$,9)):62150}8"FAILED."::621008"CONNECT!"8P$"PROTOCOL: WEATHER1":30008:"HANDSHAKE..."  FORWARD"9`"PORT 6502 TO THE ABOVE IP ADDRESS." :j"THEN TELL YOUR FRIEND TO CONNECT."A:t"WAITING FOR CONNECTION..."X:5,P$:P$""62600w:(P$,8)"CONNECT "62600:"CONNECT!":CP((P$,9)):5,"ATC";(P$,9):5,P$:P$"OK"P$"  " "(T)","(S):"";F OS1OS(P$)3,(P$,OS);:BRBR((P$)OS1)e 930:P0PP10P002320 P1BR3,P$;:BRBRP1:OS1:2320$ 3,(P$,BR);:OSBR1:P1BROS1. 1,"U2";3;0;T;S:1,E,E$,E1,E2:SS "OK""IMODEM COMMAND FAILED: ";P$::CL0"EADERS COMPLETE. O CONTENT. ONE.";CO$:U"EADERS COMPLETE."{"ECEIVING"(CL)" BYTES";CO$1,UN,15,"I0:":3,UN,3,"#":T1:S0:OS1:1,E,E$,E1,E21,"B-P";3;0:TB0:BR256 C38,32:C39,32+*E0E0C41,32:C42,32'+4DD01370:8407+9DD1Q,17g+>DD1R,51:V100102:S,V::R,15:Q,16x+HNN11270+RDD0DD1:CC32:GGCC:710+\300+a 300+fCA:Z1FF:CC1:D(C):O1E0D(C1)+p 14:X17:HH1:D(H):D32PP1-:HH47:5-G1FP:G0:P0:HH187:980D-GP:H1786J-d-.A1A133:A14A14s-8A14A14-BA11A1A11-LAAAAA17-VAA33AA33-`AA6AA6-jZ13:1250:C$:826:826:1250:D 26:1250:J$.:300/""AA);:V125:: /V1500::2/V11000::W/Q,17:S,M50:V1100::Q,16:y/ -Q,129:V130::Q,128:R,15//NN1/2"";13);"OUT OF BOUNDS!":1370:V12500:/F"";13)" ROUND"MM" :T(WP$)39T40(WP$)1"";T);WP$(1 "";(F3.6);"% LEFT"K1 "";29);(G3.6);"% LEFT"k1"";1 FG " IT'S A TIE!": 15001"THE WINNER IS ";: FG WP$: 15001 EP$1A$;"W 0":20202PR(4)0P$""3PR(1)255P$"":PR(2)0:2060 PR(2)CP#339342000:P$""2100?3>`3PR$(0)P$:2100:PR$(0)""3(P$,(PR$(0)))PR$(0)P$(P$,(PR$(0))1):3" :";PR$(0),P$:5,"ATZ":5:3 PR$(0)P : ":2200:RN(P$):4' 5`IN$" ":ZTTI:ZC2:ZD$(20)5jZ$:Z$""60070O5tZTTI(" ",ZC,1);"";:ZC3ZC:ZTTI15Z5~600105Z(Z$):ZL(IN$):(Z127)32" ";:601105FL(Z127)64(Z127)91Z$((Z128)255)5ZL25460010 6DI110:"";:I7N"TAKE THE ENEMY BY STORM#7X"PRESS A TO BE THE HOST"R7]"PRESS O TO CONNECT TO THE REMOTE HOST"q7bT$:T$"A"T$"O"620507l"":CR$(13):OA0:T$"A"OA1:625007"ENTER THE HOST IP: ";:60000:IN$"" 821009P$"READY: WEATHER1""BAD RESP";:5,"ATZ":5: 99$5,"ATI2"89.5,P$:P$""P$"OK"62500R98IP$P$:(P$)8625009B5,"AT&M10&M13&D13A6502":5,P$:P$"OK"625309L"YOUR IP ADDRESS IS ";IP$9V"PLEASE SET YOUR ROUTER TO"62630::"HANDSHAKE...";2100:P$"PROTOCOL: WEATHER1""BAD HANDSHAKE":5:8;P$"READY: WEATHER1":3000>; RESPONSE CODE:";CO$;RC:4800:P$""E02200=H(P$)17(P$,1)"C"(P$,9,1)"L"2100WR(P$,15,1)":"2100`\I15~pII1:(P$,I,1)" "2160zX((P$,I)):X0CLX2100TL0:5,CR$;"AT&M&DC";((P),2);CR$;900:P$ D931310:W16:O0: +AA:BB0:FFTT:TWW:I0:W116:WWW:R,W15:S,30(3W)%*Q,17:*O1DD1Q,16N*TWWW16:WW0d*AA(40E):870:r*O1840*(C1)93(C1)93840*C1,GG:C1,GG* (C2)93(C2)93840*C2,GG:C2,GG* E0E0 $: .t1260:1250:C$:826:826:1250:D$:1270.~A101200+.A101230Z.Z12:1250:E$:826:1290:826:1250:F$f.AAAA1q.:300.Z12:1250:G$:826:1290:826:1250:H$.AAAA1.:300.Z12:1250:I$:826:1290:8 ":NN1:0ZX110:V1020025:S,V:::S,0:*0nA$;" ";BL$:BL$:BL$>0x826:X1500:e0V200501:S,V::S,0:R,0:Q,00826:B$WP$:PF3.6:G0B$EP$:PG3.60T7((EP$)2):T0T00"";T);EP$0T33.5(WP$)2 ANT TO PLAY AGAIN? ";:600002(IN$,1)"N"1600!2T$EP$:EP$WP$:WP$T$+2 230X2@"":5,"ATZ":I01000:#5,A$:I:5:p2#5,P$:P$""2000~25,(17);2ML6:I082:PR(I)(MVI):I:PR(1)(MV1)2PR(0)0PR(6)PR(8)5,"ATL $:ML9:PR$(1)(((MV8)),2)4 5,"ATS42=";PR$(1);"T";(34);P$;(34)C4 ML:P$"OK"P$""P$PR$(0):3000I4 u4OA0RN(TI):P$"RAND:"(RN):3000:4P$"RAND: ":2200:RN(P$):4OA0RN(1):P$"RAND:"(RN):3000:4P$"RAND 5IN$IN$Z$:Z$;ZD$;Z$;6Z13IN$(IN$,2):CR$;:26Z20ZL1IN$(IN$,ZL1):"";:60010e6Z141Z$(20(ZL1)):Z2ZL:Z$;:Z:60000p66001060"";9);"CURSOR #";NM$;" ";PG$;(142)6::9);"C64NET WIFI 1200 BAUD VERSION" 5:7"CONNECTING TO ";IN$;"...";85,"AT&M13&M10&D13C";(34);IN$;":6502";(34)885,P$:P$"OK"62130e8(P$,8)"CONNECT "CP((P$,9)):62150}8"FAILED."::621008"CONNECT!"8P$"PROTOCOL: WEATHER1":30008:"HANDSHAKE..."  FORWARD"9`"PORT 6502 TO THE ABOVE IP ADDRESS." :j"THEN TELL YOUR FRIEND TO CONNECT."A:t"WAITING FOR CONNECTION..."X:5,P$:P$""62600w:(P$,8)"CONNECT "62600:"CONNECT!":CP((P$,9)):5,"ATC";(P$,9):5,P$:P$"OK"P$" 256 " "(T)","(S):"";I OS1OS(P$)3,(P$,OS);:BRBR((P$)OS1)h 930:P0PP10P002320 P1BR3,P$;:BRBRP1:OS1:2320$ 3,(P$,BR);:OSBR1:P1BROS1. 1,"U2";3;0;T;S:1,E,E$,E1,E2: P$"OK""IMODEM COMMAND FAILED: ";P$:=CL0"EADERS COMPLETE. O CONTENT. ONE.";CO$:X"EADERS COMPLETE."~"ECEIVING"(CL)" BYTES";CO$1,UN,15,"I0:":3,UN,3,"#":T1:S0:OS1:1,E,E$,E1,E21,"B-P";3;0:TB0:BR C38,32:C39,32+*E0E0C41,32:C42,32'+4DD01370:8407+9DD1Q,17g+>DD1R,51:V100102:S,V::R,15:Q,16x+HNN11270+RDD0DD1:CC32:GGCC:710+\300+a 300+fCA:Z1FF:CC1:D(C):O1E0D(C1)+p 14:X17:HH1:D(H):D32PP1-:HH47:5-G1FP:G0:P0:HH187:980D-GP:H1786J-d-.A1A133:A14A14s-8A14A14-BA11A1A11-LAAAAA17-VAA33AA33-`AA6AA6-jZ13:1250:C$:826:826:1250:D 26:1250:J$.:300/""AA);:V125:: /V1500::2/V11000::W/Q,17:S,M50:V1100::Q,16:y/ +Q,129:V130::Q,128:R,15//NN1/2"";13);"OUT OF BOUNDS!":1370:V12500:/F"";13)" ROUND"MM" :T(WP$)39T40(WP$)1"";T);WP$(1 "";(F3.6);"% LEFT"K1 "";29);(G3.6);"% LEFT"k1"";1 FG " IT'S A TIE!": 15001"THE WINNER IS ";: FG WP$: 15001 EP$1A$;"W 0":20202PR(4)0P$""3PR(1)255P$"":PR(2)0:2060 PR(2)CP#339342000:P$""2100?3>`3PR$(0)P$:2100:PR$(0)""3(P$,(PR$(0)))PR$(0)P$(P$,(PR$(0))1):3" :";PR$(0),P$:5,"ATZ":5:3 PR$(0)P : ":2200:RN(P$):4' 5`IN$" ":ZTTI:ZC2:ZD$(20)5jZ$:Z$""60070O5tZTTI(" ",ZC,1);"";:ZC3ZC:ZTTI15Z5~600105Z(Z$):ZL(IN$):(Z127)32" ";:601105FL(Z127)64(Z127)91Z$((Z128)255)5ZL25460010 6DI110:"";:I7N"TAKE THE ENEMY BY STORM#7X"PRESS A TO BE THE HOST"R7]"PRESS O TO CONNECT TO THE REMOTE HOST"q7bT$:T$"A"T$"O"620507l"":CR$(13):OA0:T$"A"OA1:625007"ENTER THE HOST IP: ";:60000:IN$"" 821009P$"READY: WEATHER1""BAD RESP";:5,"ATZ":5: 99$5,"ATI2"89.5,P$:P$""P$"OK"62500R98IP$P$:(P$)8625009B5,"AT&M10&M13&D13A6502":5,P$:P$"OK"625309L"YOUR IP ADDRESS IS ";IP$9V"PLEASE SET YOUR ROUTER TO"62630::"HANDSHAKE...";2100:P$"PROTOCOL: WEATHER1""BAD HANDSHAKE":5:8;P$"READY: WEATHER1":3000>; AD RESPONSE CODE:";CO$;RC:4800:P$""E02200@H(P$)17(P$,1)"C"(P$,9,1)"L"2100ZR(P$,15,1)":"2100c\I15pII1:(P$,I,1)" "2160zX((P$,I)):X0CLX2100TL0:5,CR$;"AT&M&DC";((P),2);CR$;900: D931310:W16:O0: ,zD127DD0940,I2W16>,C,CC:CC58Q,17:S,FF10:Q,16a,:BB1BB:BB0B$"T"FFFF1g,,X13:C,170:960:C,58:960::II1:O0W16,,Q,17:S,Z10:V150::Q,16:,F0:G1:P0-Y -5,(17);!ML6:P0(MV2):P1(MV4):P2(MV6)FPL(MV0):CR(MV1):C8(MV8)[P00P2C8985kP10P$""|P00CR0#5,P$:P$""985"-";CO$:5,"ATL":945"XPECTED ";E$;", GOT ";A$: --- T - UM:UM3:(789)234UM9:1210.BAXB:UM19,1:5,"AT":6000:6000AXB24001210[NP0:(2614)0NP20BAXB:2576,10:2578,(59490NP):2579,(59491NP)2582,170:2583,1:NP02582,154"ENDING REQUEST...";CO$:5,"AT 2IP$(13)(10):650+"EQUEST SENT. EADING RESPONSE...";CO$EP90:800:P$""2000SLE0:FB0(P$)13"AD RESPONSE: ";CO$;P$:(P$,5)"HTTP/""AD RESPONSE";CO$;P$:RC((P$,10,3))RC200"AD +05,(17);$ML6:P0(MV2):P1(MV4):P2(MV6)IPL(MV0):CR(MV1):C8(MV8)^P00P2C8985nP10P$""P00CR0#5,P$:P$""985"-";CO$:5,"ATL":945"XPECTED ";E$;", GOT ";A$: + -- + 05 +UM:UM3:(789)234UM9:12101BAXB:UM19,1:5,"AT":6000:6000DXB24001210^NP0:(2614)0NP20BAXB:2576,10:2578,(59490NP):2579,(59491NP)2582,170:2583,1:NP02582,154"ENDING REQUEST...";CO$:5, 02IP$(13)(10):650."EQUEST SENT. EADING RESPONSE...";CO$HP90:800:P$""2000VLE0:FB0(P$)13"AD RESPONSE: ";CO$;P$:(P$,5)"HTTP/""AD RESPONSE";CO$;P$:RC((P$,10,3))RC200" 0:(ML)765:"PML64.BIN",(186),1:E MVML18:#5,A$:A$""12y 5,"ATHZ0E0R0":5,P$:P$"OK"TRTR1:TR8125,"ATN0V1X1F3Q0S40=250";(19):5,P$:P$"OK"20TRTR1:TR14,14,14,14,14,14,14,14,14,17"NO MODEM DETECTED?!":5: 160,0,132,33,134,34,177,33,73,128,145,33Lx200,208,247,232,224,8 ,208,240,96,0Q542724:R54272:S542721:R,195:S,16:H1786542725,0:542726,240:5427224,14A$""BL$"     "6!J$D$"       "W!"";:V138:"";::"";n!F28:G28:M3:MM0!V119:"";BL$;"";:!" "20)" "!" "20)" "!" "20)" ""  0:RN.33310%#@"";:V13:"";BL$;"";:V:R#JA1068:4000:AARN:AAAA26:AAAA:B1B#TA,104:V14:AA1:A,102::V15:AA1:A,104:#^AA27:A,104:V111:AA1:A,102::A,104:AA33#hA,104:V16:AA1:A,102::A,104:NN0 $ BL$:BL$;"" %M3490+%X14:BL$;"":1280:1260O%13)"ACT OF NATURE":1280:d%1270:1270:BL$%4100:O1:B$("HLRT",(RN4)1,O):IN$B$:520%M11280:505%Q,33:"";15)" ROUND"MM" ":X12:1260::Q,32%T$E TTFF:O2:590-'W$"HAIL":(W$,O)IN$CC58:FF4:TTFF:O0:590f'W$"TORNADO":(W$,O)IN$CC102:FF7:TTFF:O1:590'&W$"LIGHTNING":(W$,O)IN$O0:590'0W$"QUIT":(W$,O)IN$1390'D"":430'N" WEAPON IS ";W$'SO1"";: " CHARGE IS";A1 )lA1150A1150)vA1150A11505)B$"H"1270:670M)B$"L"1270:1070|)O1"";:V120:"";:::EE1.5EE)E(A1EE)50:DD0:GG104)O2E0CC47:710)O2E0CC77:710)O2CC66*A1148 -HE MAIN LOOP !(UR$)0300KI0:"HE DISK IN DRIVE";UN;"WILL BE OVER-"x"WRITTEN. RE YOU SURE (Y/N)? ";:5000(P$,1)"Y"(P$,1)""1020(P$,1)"N"(P$,1)""10105:(UR$,I1,1)"/"II1: -AT$"&D10&M10&M13C";QU$;H1$;QU$ Q900?V(P$)8(P$,8)"CONNECT "P((P$,9)):1200o["NABLE TO CONNECT TO ";H1$;"";CO$:300|`ITTI40jP90:800:P$""ITTI10tTIIT1130"ONNECTED TO "HO$"";CO$XB96001205 -":6000:6000P$" "P1$" /1.1":600)P$"OST: "HO$:600NP$"ONNECTION: EEP-LIVE":600oP$"SER-GENT: =":600P$"ONTENT-ENGTH: 0":600P$"CCEPT: */*":600I020HH$(I)""1330(P$HH$(I):900 +- THE MAIN LOOP !(UR$)0300NI0:"HE DISK IN DRIVE";UN;"WILL BE OVER-"{"WRITTEN. RE YOU SURE (Y/N)? ";:5000(P$,1)"Y"(P$,1)""1020(P$,1)"N"(P$,1)""10105:(UR$,I1,1)"/"II +H"AT$"&D10&M10&M13C";QU$;H1$;QU$Q900BV(P$)8(P$,8)"CONNECT "P((P$,9)):1200r["NABLE TO CONNECT TO ";H1$;"";CO$:300`ITTI40jP90:800:P$""ITTI10tTIIT1130"ONNECTED TO "HO$"";CO$XB960012 +"AT":6000:6000P$" "P1$" /1.1":600,P$"OST: "HO$:600QP$"ONNECTION: EEP-LIVE":600rP$"SER-GENT: =":600P$"ONTENT-ENGTH: 0":600P$"CCEPT: */*":600I020HH$(I)""1330(P$HH$(I):90 :  140 DOUGHERTY AVE4 HOLBROOK,N.Y.::Z CURSOR #1, JUNE-JULY 1984| ADAPTED TO C64 IN APR, 2017 BY BO ZIMMERMAN X(186):254,X:X8254,8 5,2,0,(8):PP$(25):P$"OK":665,73:666,3:186,(254)& ML49152:TR !"C64NET WIFI MODEM INITIALIZED"CZPG$"WEATHER!":NM$"1":62000adAA,BB:ZAABB:W:Z,W:i"WHAT'S YOUR NAME? ";:60000:EP$IN$:EP$""105jP$"NAME: "EP$:3000:P$"NAME: ":2200:WP$P$kOA1WP$EP$:EP$P$"n826,849,162,4 , @@ -87,20 +90,21 @@ AA:BB mA$"";BL$;" ":A$;"";0$rM3"TARGET"21)"TARGET":400W$|B0"TARGET"21)"ATTACKER":400t$"ATTACKER"20)"TARGET$4100:EE(RN100)$EE50EE(EE2):A$13)" WIND <"(EE)" ":430$A$13)" WIND >"EE" "$ P$:B0T$WP$-&A$"";T$;": WEAPON (H,L,R,T,Q) ";:BOA513_&": ";:P$"WEAPON: ":2200:IN$P$:B$P$:O1:q&"";BL$:520&"? ";:60000&"";BL$:B$(IN$,1):O(IN$):O0510&P$"WEAPON: "B$:3000&W$"RAIN":(W$,O)IN$FF5: V140:"";::A$;"(XM3A10:(]BOA610Z(^M3P$"CHARGE: ":2200:IN$P$:BB$P$:A1(BB$)::615(bM3"CHARGE? ";:60000:BB$IN$:A1(BB$)(dM3A10P$"CHARGE: "((A1),2):3000(eM3A10P$"CHARGE: "(A1):3000(g - I(UR$)1020#H1$(UR$,I):P1$(UR$,I1):P1$""P1$"/"+I0W(H1$,I1,1)":"II1:I(H1$)1050$HO$(H1$,I):I(H1$)H1$H1$":80".AT$"":XB0BA0XBBAAT$"S43="((XB),2)8#5,A$:A$""1080LQU$(34):5,"ATH" -430C460"AD ICK GIVEN(";C;") : "MA$:440/C400"RROR: "MA$:1000d(MA$)N11(MA$,1,N1)NI$MA$(MA$,(NI$)2)t MA$:1000 CMDSMC$""1400(MC$""10002MC$""2000<MC$""2100F )3300 A$(20)(IN$)03230' IN$(IN$,(IN$)1):"  ";:3100Q A(A$):A32(A95A193)A2183100r IN$IN$A$:A$;" ";:3100 IN$""" "::1000 IN$"?"" ":3400 (IN$,1,1)"/"IN$(IN$,2):3500 CC$"" ."f PRINT"/LIST ";CO$;"IST CHANNELS."-p PRINT"/WHO MASK* ";CO$;"SER INFO."Y "NYTHING ELSE ";CO$;"END MESSAGE" CO$;"------------------------------":1000 X0:I2(IN$):X0(IN$,I,1)" "XI :A$"":X1A$(IN$,X P$" :"AA$:600E$"":CC$AA$:1000 "?  ";:P$""& A$:A$""5010< A$(13)" ":f A$(20)A$;" ";:P$P$A$:5010v P$""5010 P$(P$,(P$)1):"  ";:5010 (#TTTI100 2#ML12:TITT9010 <# -4P00E1:: FAIL>. --- GET E$ FROM MODEM, OR ERROR8E$""@MLE$""P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;"" --- GET PACKET HEADER, SETS P0,P1,P2, RETURNS P0=0 IF NADAPR0:#5,P$:P$""930 (254):XB1200:BA1200L CR$(13):(14);:SY(65532):53280,254:53281,246 CO$"":SY226ML49152:665,73((678)30):UMML2048:XB9600 #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: &SY3436879,27:CO$(31) -(SY226(ML UM:UM3:X(789):UM9:X234XB12006 dSY3456579,0< eB fM nP$"A" xCO$;"64 V1.5":"EQUIRES 64ET II FIRMWARE 2.0+" "Y O IMMERMAN (BO@ZIMMERS.NET)":: --- MODEM INIT UN(254) PH0:PT0:MVML18" . 900:VR(P$):VR2.0"IMODEM INIT FAILED: ";P$:E 900:P$"OK"203Z HH$(20):OT$"" UR$"COFFEEMUD.NET:8080/CTCUG/FIRMWARE/C64NET_APPS_LATEST.D64" , GET INFO 6:"EQUEST ARMS:" @ " *) YPE : "A " 1) RL 00X(P$):X1XLH300@X1"NTER : HTTP://";:5000:UR$P$:300|X2"NTER OUTPUT DEVICE/UNIT: ";:5000:UN(P$):300XLHP$HH$(XLW):"ODIFY: ";P$:5005:HH$(XLW)P$:300II1:I020:HH$(I)""II0III:II0 $(((MV8)),2):PN$(((P$)),2)*E$"OK":VR3E$C8$M5,"ATS42=";C8$;"T+";PN$:5,P$~ML:P$E$P$OP$:"ETRYING..";CO$:650 --- GET P$ FROM SOCKET P P$"":E0*930:P0PP00"NEXPECTED PACKET ID: ";P0;"/";P: MC$""2200PMC$""2300ZMC$""1000n-uLP$:1000Qv"NKNOWN: '"MC$"' '"MA$"'"[w1000ex ...OM$MA$:P$" :"MA$:600:E$"":1000I1:MA$""1000(MA$,I,2)" :"2050II1:20102900 II1:2910 MS$(MS$,1,II): A$(13)10009 "TREAM PAUSED. NTER ? FOR HELP."Z OM$""P$" "OM$:600 CO$;"> ";:IN$"":ITTI1000:3200 TIIT"ANCELLED"::1000N A$:A$""3100b ITTI1000 A$(13 :"O HANNEL ELECTED! RY ?";CO$:1000" P$" "CC$" :"IN$C 600:" ":"";NI$;": ";IN$U E$""::1000H CO$;"------------------------------"R "/JOIN #C-64 ";CO$;"HANGE CHANNELS."\ "/QUIT ";CO$;"OGOUT AND EXIT 1):IN$(IN$,1,X1)  IN$"JOIN"4000( IN$"QUIT"P$" :":600:9100M IN$"LIST"P$"":600:1000v IN$"WHO"P$" :"AA$:600:1000"NKNOWN OMMAND: ";IN$;". RY ?";CO$:1000"OINING ";QU$;A$;QU$:AA$A$#5,"ATZ":9000:9000:5: Pß5,2,0,(8)!Zá#5,A$:A$""A$;'!dáA$:A$""5,A$;3!nÉ 50010d!U8:F$"IRC":1,U,15,"S0:"F$:1:F$,U:F$,U"IRC":1,U,15,"S0:"F$:1:F$,U:F$,UP$" :"AA$:600!P$E$4100!E$"":CC$AA$:100   D64WGET4/128 1200B 2.0+@ UPDATED 08/19/2017 02:54Aa + 1:I(UR$)1020&H1$(UR$,I):P1$(UR$,I1):P1$""P1$"/".I0Z(H1$,I1,1)":"II1:I(H1$)1050$HO$(H1$,I):I(H1$)H1$H1$":80".AT$"":XB0BA0XBBAAT$"S43="((XB),2)8#5,A$:A$""1080LQU$(34):5,"AT +MA$:440C400"RROR: "MA$:1000=(MA$)N11(MA$,1,N1)NI$MA$(MA$,(NI$)2)M MA$:1000X CMDSmMC$""1400(MC$""10002MC$""2000<MC$""2100FMC$""2200PMC$""2300 IN$(IN$,(IN$)1):"  ";:3100* A(A$):A32(A95A193)A2183100K IN$IN$A$:A$;" ";:3100d IN$""" "::1000| IN$"?"" ":3400 (IN$,1,1)"/"IN$(IN$,2):3500 CC$"":"O HANNEL ELECTED! RY ?";CO$ NELS."p PRINT"/WHO MASK* ";CO$;"SER INFO."2 "NYTHING ELSE ";CO$;"END MESSAGE"c CO$;"------------------------------":1000 X0:I2(IN$):X0(IN$,I,1)" "XI :A$"":X1A$(IN$,X1):IN$(IN$,1,X1)  IN$"JO :1000"?  ";:P$""A$:A$""5010 A$(13)" ":? A$(20)A$;" ";:P$P$A$:5010O P$""5010x P$(P$,(P$)1):"  ";:5010 (#TTTI100 2#ML12:TITT9010 <# #5,"ATZ":9000:9000:5: Pß5,2,0, +:4P00E1:: FAIL >1 --- GET E$ FROM MODEM, OR ERROR;E$""CMLE$""P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;"" --- GET PACKET HEADER, SETS P0,P1,P2, RETURNS P0=0 IF NADAPR0:#5,P$:P$""93 (254):XB1200:BA1200L CR$(13):(14);:SY(65532):53280,254:53281,246 CO$"":SY226ML49152:665,73((678)30):UMML2048:XB9600 #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: &SY3436879,27:CO$(31) +(SY226(ML UM:UM3:X(789):UM9:X234XB12006 dSY3456579,0< eB fM nP$"A" xCO$;"64 V1.5":"EQUIRES 64ET II FIRMWARE 2.0+" "Y O IMMERMAN (BO@ZIMMERS.NET)":: --- MODEM INIT UN(254) PH0:PT0:MVML18" 9);1 900:VR(P$):VR2.0"IMODEM INIT FAILED: ";P$:H 900:P$"OK"203] HH$(20):OT$"" UR$"COFFEEMUD.NET:8080/CTCUG/FIRMWARE/C64NET_APPS_LATEST.D64" , GET INFO 6:"EQUEST ARMS:" @ " *) YPE : " +A " 1) RL 1000X(P$):X1XLH300CX1"NTER : HTTP://";:5000:UR$P$:300X2"NTER OUTPUT DEVICE/UNIT: ";:5000:UN(P$):300XLHP$HH$(XLW):"ODIFY: ";P$:5005:HH$(XLW)P$:300II1:I020:HH$(I)""II0III:II :C8$(((MV8)),2):PN$(((P$)),2)-E$"OK":VR3E$C8$P5,"ATS42=";C8$;"T+";PN$:5,P$ML:P$E$P$OP$:"ETRYING..";CO$:650 --- GET P$ FROM SOCKET P P$"":E0*930:P0PP00"NEXPECTED PACKET ID: ";P0;"/";P ZMC$""1000nuLP$:1000*v"NKNOWN: '"MC$"' '"MA$"'"4w1000>x ...kOM$MA$:P$" :"MA$:600:E$"":1000I1:MA$""1000(MA$,I,2)" :"2050II1:20102900*"";MS$;": ";(MA$,I2):10004 A$(13)1000 "TREAM PAUSED. NTER ? FOR HELP."3 OM$""P$" "OM$:600] CO$;"> ";:IN$"":ITTI1000:3200 TIIT"ANCELLED"::1000N A$:A$""3100b ITTI1000 A$(13)3300 A$(20)(IN$)03230 :1000 P$" "CC$" :"IN$ 600:" ":"";NI$;": ";IN$. E$""::1000YH CO$;"------------------------------"R "/JOIN #C-64 ";CO$;"HANGE CHANNELS."\ "/QUIT ";CO$;"OGOUT AND EXIT."f PRINT"/LIST ";CO$;"IST CHAN IN"4000 IN$"QUIT"P$" :":600:9100& IN$"LIST"P$"":600:1000O IN$"WHO"P$" :"AA$:600:1000"NKNOWN OMMAND: ";IN$;". RY ?";CO$:1000"OINING ";QU$;A$;QU$:AA$A$P$" :"AA$:600E$"":CC$AA$s(8) Zá#5,A$:A$""A$;!dáA$:A$""5,A$; !nÉ 50010=!U8:F$"IRC":1,U,15,"S0:"F$:1:F$,U:F$,U$"IRC":1,U,15,"S0:"F$:1:F$,U:F$,URC":1,U,15,"S0:"F$:1:F$,U:F$,UP$" :"AA$:600!P$E$4100!E$"":CC$AA$:100   D64WGET4/128 1200B 2.0+@ UPDATED 10/13/2021 12:54Aa 254,(186):(254)8254,8 SY(65532):SY6158,254: SY34X23777:X,170:(X)170"<16K":5,2,0,(8):(65532)3456,87:54,87:52,87 PP$(25):P$"OK":186, 1)2095:"PML64.BIN",(254),1:W -SY226UM0(UM1)245:"UP9600.BIN",(254),1: 2SY61ML4864:981,15:S8(215)128:S812830643 7SY61S8128XB2400:CO$(159) -<SY61(ML1)2175:"PML128.BIN",(254),1:! PSY226UM0  "NITIALIZING MODEM...":CR$(13)(10)9 #5,A$:A$""203h 5,CR$;"ATHZ0F0E0";CR$;:900:P$"OK"203 #5,A$:A$""208 5,CR$;"ATE0N0R0V1Q0";CR$; 900:P$"OK"208 #5,A$:A$""225 5,"ATE0V1X1F3Q0S40=248I4";CR$;(19);  : HTTP://";UR$#B " 2) UTPUT NIT:";UN,ILW2jJNH0:I020:(HH$(I))0NHNH1:(LWNH)") "HH$(I):TLHLWNH1:(LW1NH)") DD EW EADER"rUR$"":P$"1":400|:"YPE A NUMBER OR ΠTO BEGIN:";5000:P$""10 "EW EADER: ";:5000:HH$(II)P$ 300DV --- TRANSMIT P$ TO THE OPEN SOCKET~XOP$P$:ML9:C8$(((MV8)),2):E$"OK":VR3E$C8$b5,"ATS42=";C8$;"TP+";QU$;P$;QU$lML:P$E$P$OP$:"ETRYING..";CO$:600vOP$P$:ML9:C8 *"";MS$;": ";(MA$,I2):10004"";LP$;"":1000N2900:"";MS$;" HAS LEFT THE CHANNEL.";CO$:10002900:"";MS$;" HAS JOINED THE CHANNEL.";CO$:1000T II2^ II(MS$)II(MS$):2950h (MS$,II,1)"!"IIII1:2950r II +<SY61(ML1)2175:"PML128.BIN",(254),1:! PSY226UM0  "NITIALIZING MODEM...":CR$(13)(10)9 #5,A$:A$""203k 5,CR$;"ATHZ0&P0F0E0";CR$;:900:P$"OK"203 #5,A$:A$""208 5,CR$;"ATE0N0R0V1Q0";CR$; 900:P$"OK"208 #5,A$:A$""225 5,"ATE0V1X1F3Q0S40=248I4";CR$;(1  : HTTP://";UR$&B " 2) UTPUT NIT:";UN/ILW2mJNH0:I020:(HH$(I))0NHNH1:(LWNH)") "HH$(I):TLHLWNH1:(LW1NH)") DD EW EADER"rUR$"":P$"1":400|:"YPE A NUMBER OR ΠTO BEGIN:";5000:P$"" 0"EW EADER: ";:5000:HH$(II)P$ 300GV --- TRANSMIT P$ TO THE OPEN SOCKETXOP$P$:ML9:C8$(((MV8)),2):E$"OK":VR3E$C8$b5,"ATS42=";C8$;"TP+";QU$;P$;QU$lML:P$E$P$OP$:"ETRYING..";CO$:600vOP$P$:ML9 "";LP$;"":1000'2900:"";MS$;" HAS LEFT THE CHANNEL.";CO$:1000a2900:"";MS$;" HAS JOINED THE CHANNEL.";CO$:1000jT II2^ II(MS$)II(MS$):2950h (MS$,II,1)"!"IIII1:2950r IIII1:2910 MS$(MS$,1,II): H8 ?8> 8 .8hhL.. S 8>>L;. H 8>L;LDALDYJSRRTSBCSBEQBCCCMPBNELDXJMPSTASTYSTXINYDEYDEXDECINXINCCPYCPXSBCSECADCCLCTAXTAYTXATYAPHAPLABRKBMIBPLANDORAEORBITBVCBVSROLRORLSRCLDCLIASLPHPPLPRTISEDSEITSXTXSCLVNOP  S`W ' % 1(  Y! /)Vn See Y! Q VFW ( H) *` ' )V ` d(K |!K /)L` Lh t! (' (e ) Y o(֝e ) g( J %e J h! /) |! U.T*i? LH) ' #,L1%/ `  Ĥ>å ͽ  &   ݩ L=# BLh ,HwHHHHl`KH ( )hILr" $V  # L" %.: ( H) ( ( # H)  H) h 1(M/Uɝ o*S8`ʊMVAw % o" x} %yz %{|Lh e%JKe hhVL$L` KJ`0G`8`N`HJJJJ '(h)L'( ,  `?h)>@Ahi=hi<H ( hAh@h? ܎ܭ) ܩߢ%DCBxDC (<H=H>H?@A@L` A(Z[\] R(\]Lh 'bc R(OP R(QR ) WN 1(V N ". Lh M3M8OȱPQȭRmbȱmc /)0 A( R(TU ( =<`K H) ( /)K` g(i /)K`>` V`H %h. ( L (v/ ; =)< (= ( H)C (D ( $) (Lh L` A( ( R(DC $)K ( )  ( `e 'IS ) ' ))"У "  H)Lͽ +A H) ( *Lh  + ++ "+ =+ H)(` ) 0 :)`hh`L` HH&&hehe&ee` +UHH H) H)h ( H)h * H) *Lh + H) ( * *Lh  + ++ + "+ +LH)J&&` ( `ԩԩDԎԌLe  - o"N, "L"L /) }*L,ɑх҅^ T.:,$^i(ݍI .LI:$' -8 # .Lp - *Lh- -^8^ - o" ^MM #N, C) "Lh-L" -膭 YMJ,),#($YX$$#]#)i#$S#$S[[i$$)|mi)S4i#bZH&bTDThDt(ntJrtttrDh2"&&rr&HDD PC IRQ SR AC XR YR SPABCDFGHLMNQR(TWX,:;$#"+-OIJ%&EV)>)(2$/'!!!&!)#L'&P)+!&*, # -$ S$(P$,1,I1):P$(P$,I1)LI1:P$""1000#VI(P$)1200C`(P$,I,1)" "II1:1110djMC$(P$,1,I1):P$(P$,I1)|MA$P$:MC$""1000A((MC$,1,I)):A48A571300(MC$)1(MC$,1,1)"0"MC$(MC$,2):1220C(MC$):C  `ТLȈ8aHh0!A$Pp"bBX(@x0123456789ABCDEFNO START ADDRESS----------  xL  `  ߢ%ɑ# `a (u `.HdX=< %B*LM)? , %. NVd (. $ݐ/I +$ ""1000VI(P$)1200`(P$,I,1)" "II1:1110=jMC$(P$,1,I1):P$(P$,I1)UMA$P$:MC$""1000yA((MC$,1,I)):A48A571300(MC$)1(MC$,1,1)"0"MC$(MC$,2):1220C(MC$):C430C460"AD ICK GIVEN(";C;") : "  `ТLȈ8aHh0!A$Pp"bBX(@x0123456789ABCDEFNO START ADDRESS----------  xL  `  ߢ%ɑ# `a (u `.HdX=< %B*LM)? , %. NVd (. $ݐ/I //lL`  V`N " H)`HShS`TUL 8S  $ , o" %Њ =) ( H) -H "h "MX e"X. %. %` {"Ș e"J (J`M z"`8e`J J") J.JJJJ).X)M) JJJ Ȉ` e" MȐ`.T6/U . /)L* ' # % #:wL=% ' ) 'L% A(f ( O0O8?Jnfne )": % |(0eeَTVKV "XU6/ e%. e%MX0 b%X. e%. e% b% b%TK h>h=h<DCBX>)LN ,HP<[k=Zc^[_SH0NHҮB%HHL' % $)K )=< H)$N " Lh JMHF_^:SL[ \]^_@H ) n U( ( )c %H7Ш)) xT  U(` A( U( R(` (HJJJJ '(h) '(H hLiii:`Hh`Y ( |( ( g( g(`L` t!Y ( ( ` ( @@ -113,39 +117,49 @@ $ Y T. ( Y`)  @`- `  MICROMON-64 COMPUTE! BOOKS @E@ 0"E3@ @E3@ @E@ "D3ЌD"D3ЌD"D3@ "D3@ bx!#)t**h++,E*` <*+7,!,),#` ` ---------- BRANCH OUT OF RANGEUNDEFINED LABEL NAKED LABEL <<<<<<<< DISK ERROR >>>>>>>>  -- DUPLICATED LABEL --  -- SYNTAX ERROR --  -$:600v --- GET NEXT FROM SOCKET INTO PPW930:P0PP00"NEXPECTED PACKET ID: ";P0;"/";P:cP00PP$(PE)P$:PEPE1:PE25PE0P$"": --- GET P$ FROM SOCKET P E0:P$"":PH25PH0*PHPEP$PP$(PH):PH t>  ; 4hhw>L+> 32>  :L; 4hhw>L+. O 8>L;> P O:SjHL. N P 8Θ>    L;. N O 8>L;. N H 8>L;. N S 8>L;` .= ȹD==L->=D=D=L,D=L+ 0 $0L+Y=@Z=>Ix> 0L,>G= LG/H=A>Y=ȹH=Y=A>ȹH=Y=Lp,~>>@>ТY=Y=0) , L,HH 2hhY=#?(r>7qmq>q>L-~>Y=) .>L.>>>*8Ӎ>  >L.  ө= .88Ӎ>ӭ>  > +CKET INTO PP0930:P0PP00"NEXPECTED PACKET ID: ";P0;"/";P:<P00`PP$(PE)P$:PEPE1:PE25PE0lP$"": --- GET P$ FROM SOCKET P E0:P$"":PH25PH0*PHPEP$PP$(PH):PHPH1:4700:P00E1:: FAIL> t>  ; 4hhw>L+> 32>  :L; 4hhw>L+. O 8>L;> P O:SjHL. N P 8Θ>    L;. N O 8>L;. N H 8>L;. N S 8>L;` .= ȹD==L->=D=D=L,D=L+ 0 $0L+Y=@Z=>Ix> 0L,>G= LG/H=A>Y=ȹH=Y=A>ȹH=Y=Lp,~>>@>ТY=Y=0) , L,HH 2hhY=#?(r>7qmq>q>L-~>Y=) .>L.>>>*8Ӎ>  >L.  ө= .88Ӎ>ӭ>  > 0  8> 8> ?8;  .8 8w>L+>>s>t>  1L=+   >   HH ͽ>>>`D=P` 8 8 8ƅ= .8 8L~-ȹD=V Ȍ>8>D=IȹD= LL0ȹD==;ȥ>ʠD=D=Lq0D=` 8 8 8= .8 8L0>>Ȍy>Dmy>=i 2>z>{>ȑhhL->> 1>8岥 89> .8 8`  = `= ` ` = u +,`L+  ɬ= .8L.`L2 8>L4>> >` 7L3 L4 4>hhL+`ɱ[ɳ_ɪ>ɬL4.$ ^4D=L3>`L5D=L58>΃>轞0轞0D=Ls4)`>L3>L3 3> * 8 8> D= L4ȄDe=i 2>> 6z>{> "E L6:L6; 3>>L6"L5> 8L5L9D=> 7>L5>=譑>u C:?; 3>>L~6m=> m= 8L16m=D= :=L16>n=LO6=> 2z> 7>=L16> 8n=: 4>>hh>>Ll.L+>`  7>> ?8{> H8{>L7y>>>  y>  ee` - 8L08` 8`>> r9 8>` ͽ 8>`> r9 r9 9` ͽ 9` 8`u>v> ͽ L9`D= .8`  8 `>`>`>  > 8 8W> .8 8L; L9  ^4D=L9D==L:> g8 ?8 8 8 1 4w>`. E N D 9>w>>s>t> h3`> D=  ^4D==L:L;,=ȩP=ȩ,=ȩW=Ȅ 8 8> 2 s> PH1: -4700:P00E1:: FAIL+>PHPEP$PP$(PH):PHPH1:5HE1:\ ---- GET E$ FROM MODEM, OR ERRORfE$""nMLE$""P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;"" GET PACKET HEADER, SETS P0,P1,P2, RETU "" "-";CO$:5,"ATL":945/"XPECTED ";E$;", GOT ";A$:C THE MAIN LOOPXA$:A$""3000m800:P$""1000MS$"":MC$"":MA$"":I1:LP$P$(P$,1,1)":"1100I(P$)1100$(P$,I,1)" "II1:1050.M *0q>7>*8>> i@=L+=ȹ ∄ 1 2w> h3>? 8 L A D S 8> D= 2z>s>{>t> w>L. h3>>>L+>> 8 ?8 g8 ?8> ;7Lj2r>rr>G=hmq>q>L->9ȹD= r> mq>q>r>SL~-L->L~-8z>H{>hL + 8L08` 8`>> r9 8>` ͽ 8>`> r9 r9 9` ͽ 9` 8`u>v> ͽ L9`D= .8`  8 `>`>`>  > 8 8W> .8 8L; L9  ^4D=L9D==L:> g8 ?8 8 8 1 4w>`. E N D 9>w>>s>t> h3`> D=  ^4D==L:L;,=ȩP=ȩ,=ȩW=Ȅ 8 8> 2 s> PHPEP$PP$(PH):PHPH1:HE1:5 ---- GET E$ FROM MODEM, OR ERROR?E$""GMLE$""P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;"" GET PACKET HEADER, SETS P0,P1,P2, RETURNS P0=0 IF NADAML12:E0:P00:P1 45"XPECTED ";E$;", GOT ";A$: THE MAIN LOOP1A$:A$""3000F800:P$""1000jMS$"":MC$"":MA$"":I1:LP$P$(P$,1,1)":"1100I(P$)1100$(P$,I,1)" "II1:1050.MS$(P$,1,I1):P$(P$,I1)LI1:P$ *0q>7>*8>> i@=L+=ȹ ∄ 1 2w> h3>? 8 L A D S 8> D= 2z>s>{>t> w>L. h3>>>L+>> 8 ?8 g8 ?8> ;7Lj2r>rr>G=hmq>q>L->9ȹD= r> mq>q>r>SL~-L->L~-8z>H{>hL 0h L>-hL 0hL 08z>{>L~-~>Y=,L.q>LL-{>Ur>  mq>q> 7 7L-~>Y=)lq>L-Z="[=z>r>ѩmq>q>L~- 7L-r> q>iq>L- q>i q> 7 7>L LtY=XeY=)L,{>r>RNzr> q>iq>L-r>1 /LG/q>iq>L- 8 8W> .8 8L-{>Dr> mq>q>L~- 2 /LG/mq>q>[=Y q>ɶL/L~-r> mq>q>L- 3 /LG/mq>q>L->>> hh c80 ƳƲL0>>x>L?1y>> 1ȹY=S0OѲ>> 1L0>0`> 8 8 ?8= .8 8hhq>)>L-L~-y>L?1> 1y>>ȱz>ȱ{>> -{>z>>>mz>z>>m{>{>>`L?1ƳƲ` =z>{>>)==== -3====L3>> T3β=`=.==.==m===m===.=`=mz>z>=m{>{>` />>>>> u> v>  4hhL+ L3 L4:LP4;s>>U>> 3L4  ^4D=L3 8 ?8 8 hhL+D=PD= >`w>` ,:( ; ,) =D=L5>D= M5L3>>D= M5>L3z>{>z>.{>z>.{>z>.{>z>.{>=A) z>z>>>`> HH $0hhD= D=Bh>>>> 8 ?8 g8 ?8> D= D=  8z>x>{>y> x>y>x>y>  `8>΃>轞0轞0L+7)`D=+L?7ȹD= Z7=LJ7:808`== 2z>>{>>`> 8`>  q> H8 ?8q> 7`> 8`>z> H8z>L7> 8 8`>z> H8z>  >`>`>`  > > r9L 9> ͽ  `>`>`  > r9 r9LC9 ͽ  `>`>`  v>u> ͽ  `H)4=hJJJJ4= `F 9hhL+ɀ G:L9DL:PL:NL5;OL ;SL;HLSTA $DC0C;; SP1 AULTS 1Z*HSECi*RLDA WRRPTRx*\SBC RDRPTR*fCMP #200*pBCS ENABLE2;; D'T ENABLE RTS RECBUFFER IS FU +Ԋ;; DISABLE SERIAL ERFACE,@DISABLE PHA:TXA:PHA:TYA:PHA8,ALDA IRQVECT:CMP #IRQ:BNE NODISa,BLDA IRQVECT1:CMP #IRQ:BEQ DISABL2,CNODIS PLA:TAY:PLA:TAX:PLA:RTS,IDISABL2 SEI,JLDA $DD01;; DISABLE RTS,T IRQ;; OLD IRQHLER-STA IRQVECT-œLDA #IGIRQ-̜STA IRQVECT1.֜LDA #IGNMI;; OLD NMIHLER/.STA NMIVECT@.LDA #IGNMIR.STA NMIVECT1Z.CLIv.PLA:TAY:PLA:TAX:PLA:RTS}.;;.;;.PREVTAB .BUF 128.ٙ NOP;:F$"UP9600.BAS ;CO$ 1000: GO START MAIN LP8V --- TRANSMIT P$ TO THE OPEN SOCKETdX(P$)0((P$,1))10P$(P$,(P$)1)]OP$P$:ML9:C8$(((MV8)),2)::E$"OK":VR3E$C8$b5,"ATS42=";C8$;"T+";QU$;P$;QU$lML:P$E$P$OP$:"XMIT FAIL";CC54):BA1200:XB1200I CR$(13):(14);:SY(65532):53280,254:53281,246 CO$"":SY226ML49152:665,73((678)30):UMML2048:XB9600 #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: &SY3436879,27:CO$(31) -(SY226(ML1):UM3:X(789):UM9:X234XB1200H ZSY3456579,0: WHY DOES THIS WORK` d GET THE BAUD RATEk nP$"A" xCO$;" V1.5":"EQUIRES 64ET II FIRMWARE 1.8+" "Y O IMMERMAN (BO@ZIMMERS.NET)":: ------------------------A$:A$""208# 5,CR$;"ATE0N0R0V1Q0";CR$;: 900:P$"OK"208d 5,"ATE0V1X1F3Q0S40=250I4";CR$;(19); 900:VR(P$):VR1.8"IMODEM INIT FAILED: ";P$: 900:P$"OK"203 , GET THE SERVER 6:"OME SERVERS:"@"IRC.NLNOG.NET)0SE$"":360|SE$SE$":"PO$-#5,A$:A$""390G MAKE THE CONNECTIONzAT$"":XB0BA0XBBAAT$"S43="((XB),2)#5,A$:A$""406QU$(34):5,"AT"AT$"H&D13&M13&M10CP";QU$;SE$;QU$900(P$,8)"CONNECT ""N82,170:2583,1:NP02582,154$5,"AT":9000:9000[:"HAT IS YOUR NICKNAME";:5000:NI$P$:N1(NI$)yNI$""" GUESS NOT.":P$" "NI$:600P$" GUEST 0 * :OE NONYMOUS":600"ONNECTED, WAIT FOR . ? FOR HELP"IA1)&DTXA&NASL&XE #$33;; TIME CST SER +'bLDX #0;; 51 55 DEPING PALNTSC VERSIX'lSTA $DC04;; START UE TIMERA (OF CIA1)'vSTX $DC05;; (TIME IS AROUND 1(2BAUDRATE) )'ASL;; TIME CST RECEIVER 'A #1;; 103 ЉSTA $DC0D;; DISABLE TIMERA (CIA1) ERRUPT&)ډSTA $DC0F;; START TIMERB OF CIA1 (GENERATES KEYSCAN IRQ)])LDA #$92;; TIMERB OF CIA2 (ENABLE SIGNAL AT PB7)k)STA $DD0Fx)LDA #$98)BIT $DD0D;; CLEAR PING NMIS) STA $DD0D;; ENABLE NMI (SDR LL*zLDA #2;; ENABLE RTS+STA $DD03;; (THE RTS LINE IS THE LY OUTPUT)+ENABLE2 CLI1+PLA:TAY:PLA:TAX:PLA:RTS`+;; TABLE OF TIMER UES PAL NTSC VERSIx+ILOTAB .BYTE 149 37+;;+IHITAB .BYTE 66 64+ʊ;; #$FD,^STA $DD01,hLDA #$7F,rSTA $DD0D;; DISABLE ALL CIA ERRUPTS -|STA $DC0D>-LDA #$41;; QUICK ( DIRTY) HACK SWITCH BACKk-STA $DC05;; THE AULT CIA1 CFIGURATIx-LDA #$81-STA $DC0D;; ENABLE TIMER1 (THIS IS AULT)-LDA #IG#":1,8,15,"S0:UP9600*":1:F$,8"UP9600.BAS":1,8,15,"S0:UP9600*":1:F$,8EQ SETPDUN +{>z>>>mz>z>>m{>{>>`L?1ƳƲ` =z>{>>)==== -3====L3>> T3β=`=.==.==m===m===.=`=mz>z>=m{>{>` />>>>> u> v>  4hhL+ L3 L4:LP4;s>>U>> 3L4  ^4D=L3 8 ?8 8 hhL+D=PD= >`w>` ,:( ; ,) =D=L5>D= M5L3>>D= M5>L3z>{>z>.{>z>.{>z>.{>z>.{>=A) z>z>>>`> HH $0hhD= D=Bh>>>> 8 ?8 g8 ?8> D= D=  8z>x>{>y> x>y>x>y>  `8>΃>轞0轞0L+7)`D=+L?7ȹD= Z7=LJ7:808`== 2z>>{>>`> 8`>  q> H8 ?8q> 7`> 8`>z> H8z>L7> 8 8`>z> H8z>  >`>`>`  > > r9L 9> ͽ  `>`>`  > r9 r9LC9 ͽ  `>`>`  v>u> ͽ  `H)4=hJJJJ4= `F 9hhL+ɀ G:L9DL:PL:NL5;OL ;SL;HLSTA $DC0C;; SP1 AULTS 1Z*HSECi*RLDA WRRPTRx*\SBC RDRPTR*fCMP #200*pBCS ENABLE2;; D'T ENABLE RTS RECBUFFER IS FU +Ԋ;; DISABLE SERIAL ERFACE,@DISABLE PHA:TXA:PHA:TYA:PHA8,ALDA IRQVECT:CMP #IRQ:BNE NODISa,BLDA IRQVECT1:CMP #IRQ:BEQ DISABL2,CNODIS PLA:TAY:PLA:TAX:PLA:RTS,IDISABL2 SEI,JLDA $DD01;; DISABLE RTS,T IRQ;; OLD IRQHLER-STA IRQVECT-œLDA #IGIRQ-̜STA IRQVECT1.֜LDA #IGNMI;; OLD NMIHLER/.STA NMIVECT@.LDA #IGNMIR.STA NMIVECT1Z.CLIv.PLA:TAY:PLA:TAX:PLA:RTS}.;;.;;.PREVTAB .BUF 128.ٙ NOP;:F$"UP9600.BAS  --- TRANSMIT P$ TO THE OPEN SOCKET=X(P$)0((P$,1))10P$(P$,(P$)1)x]OP$P$:ML9:C8$(((MV8)),2)::E$"OK":VR3E$C8$b5,"ATS42=";C8$;"T+";QU$;P$;QU$lML:P$E$P$OP$:"XMIT FAIL";CC$:600v --- GET NEXT FROM SO54):BA1200:XB1200I CR$(13):(14);:SY(65532):53280,254:53281,246 CO$"":SY226ML49152:665,73((678)30):UMML2048:XB9600 #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: &SY3436879,27:CO$(31) +(SY226(ML1):UM3:X(789):UM9:X234XB1200H ZSY3456579,0: WHY DOES THIS WORK` d GET THE BAUD RATEk nP$"A" xCO$;" V1.5":"EQUIRES 64ET II FIRMWARE 1.8+" "Y O IMMERMAN (BO@ZIMMERS.NET)":: ------------------------#5,A$:A$""208& 5,CR$;"ATE0N0R0V1Q0";CR$;= 900:P$"OK"208g 5,"ATE0V1X1F3Q0S40=250I4";CR$;(19); 900:VR(P$):VR1.8"IMODEM INIT FAILED: ";P$: 900:P$"OK"203 , GET THE SERVER 6:"OME SERVERS:" @"IRC.NLNOG.#5,A$:A$""390  MAKE THE CONNECTIONSAT$"":XB0BA0XBBAAT$"S43="((XB),2)j#5,A$:A$""406QU$(34):5,"AT"AT$"H&D13&M13&M10CP";QU$;SE$;QU$900(P$,8)"CONNECT ""NABLE TO CONNECT TO ";SE$;":";P$:300AT":9000:90004:"HAT IS YOUR NICKNAME";:5000:NI$P$:N1(NI$)RNI$""" GUESS NOT.":jP$" "NI$:600P$" GUEST 0 * :OE NONYMOUS":600"ONNECTED, WAIT FOR . ? FOR HELP";CO$ 1000: GO START MAIN LPVIA1)&DTXA&NASL&XE #$33;; TIME CST SER +'bLDX #0;; 51 55 DEPING PALNTSC VERSIX'lSTA $DC04;; START UE TIMERA (OF CIA1)'vSTX $DC05;; (TIME IS AROUND 1(2BAUDRATE) )'ASL;; TIME CST RECEIVER 'A #1;; 103 ЉSTA $DC0D;; DISABLE TIMERA (CIA1) ERRUPT&)ډSTA $DC0F;; START TIMERB OF CIA1 (GENERATES KEYSCAN IRQ)])LDA #$92;; TIMERB OF CIA2 (ENABLE SIGNAL AT PB7)k)STA $DD0Fx)LDA #$98)BIT $DD0D;; CLEAR PING NMIS) STA $DD0D;; ENABLE NMI (SDR LL*zLDA #2;; ENABLE RTS+STA $DD03;; (THE RTS LINE IS THE LY OUTPUT)+ENABLE2 CLI1+PLA:TAY:PLA:TAX:PLA:RTS`+;; TABLE OF TIMER UES PAL NTSC VERSIx+ILOTAB .BYTE 149 37+;;+IHITAB .BYTE 66 64+ʊ;; #$FD,^STA $DD01,hLDA #$7F,rSTA $DD0D;; DISABLE ALL CIA ERRUPTS -|STA $DC0D>-LDA #$41;; QUICK ( DIRTY) HACK SWITCH BACKk-STA $DC05;; THE AULT CIA1 CFIGURATIx-LDA #$81-STA $DC0D;; ENABLE TIMER1 (THIS IS AULT)-LDA #IG#":1,8,15,"S0:UP9600*":1:F$,8"UP9600.BAS":1,8,15,"S0:UP9600*":1:F$,8EQ SETPDUN LSETPNXT LDA $FB:CLC:ADC #$07:STA $FB VLDA $FC:ADC #$00:STA $FC `LDA $FB:CMP $31:BCC SETPLP:BNE SETPDUN jLDA $FC:CMP $32:BCC SETPLP -SETPDUN RTS  IRC64/128 1200B 1.8+= UPDATED 08/19/2017 02:58A^ +SETPDUN RTS  IRC64/128 1200B 1.8+= UPDATED 10/13/2021 02:58A^ 254,(186):(254)8254,8 SY(65532):SY6158,254: SY34X23777:X,170:(X)170"<16K":5,2,0,(8):(65532)3456,87:54,87:52,87 PP$(25):P$"OK":186,(22095:"PML64.BIN",(254),1:T -SY226UM0(UM1)245:"UP9600.BIN",(254),1: 2SY61ML4864:981,15:S8(215)128:S812830643 <SY61(ML1)2175:"PML128.BIN",(254),1: -FSY61S8128XB2400:CO$(159) PSY226UM0UM--------' GET STARTED !M -------------------------------r UN(254):IP$"":CR$(13)(10) PH0:PT0:MVML18 "NITIALIZING MODEM..." #5,A$:A$""203 5,CR$;"ATHZ0F0E0";CR$;:900:P$"OK"203 #5, PORT 6667, #C-64"5E"IRC.FREENODE.NET PORT 6667, #C64FRIENDS"_J"IRC.US.IRCNET.NET PORT 6667, #C-64"^SE$""::"HAT IS YOUR SERVER HOST":5000:SE$P$hSE$""" GUESS YOU'RE DONE THEN":5:r"HAT IS THE PORT":5000:PO$P$:(PO$ ABLE TO CONNECT TO ";SE$;":";P$:300%P((P$,8))7XB9600430\UM:UM3:(789)234UM9:435BAXB:UM19,1:5,"AT":9000:9000XB2400435NP0:(2614)0NP20BAXB:2576,10:2578,(59490NP):2579,(59491NP) 25 111 DEPING PALNTSC VERSI (STA $DD06;; START UE TIMERB (OF CIA2)9(STX $DD07;; (TIME IS AROUND 1BAUDRATE )q(LDA #$41;; START TIMERA OF CIA1, SP1 USED AS OUTPUT(STA $DC0E;; GENERATES THE SER'S BIT CLOCK(LDA #1(ƉSTA OUTSTAT( +FSY61S8128XB2400:CO$(159) PSY226UM0UM--------' GET STARTED !M -------------------------------r UN(254):IP$"":CR$(13)(10) PH0:PT0:MVML18 "NITIALIZING MODEM..." #5,A$:A$""203 5,CR$;"ATHZ0&P0F0E0";CR$;:900:P$"OK"203 NET PORT 6667, #C-64"8E"IRC.FREENODE.NET PORT 6667, #C64FRIENDS"r^SE$""::"HAT IS YOUR SERVER HOST":5000:SE$P$hSE$""" GUESS YOU'RE DONE THEN":5:r"HAT IS THE PORT":5000:PO$P$:(PO$)0SE$"":360|SE$SE$":"PO$ P((P$,8))XB96004305UM:UM3:(789)234UM9:435\BAXB:UM19,1:5,"AT":9000:9000nXB2400435NP0:(2614)0NP20BAXB:2576,10:2578,(59490NP):2579,(59491NP)2582,170:2583,1:NP02582,1545," 111 DEPING PALNTSC VERSI (STA $DD06;; START UE TIMERB (OF CIA2)9(STX $DD07;; (TIME IS AROUND 1BAUDRATE )q(LDA #$41;; START TIMERA OF CIA1, SP1 USED AS OUTPUT(STA $DC0E;; GENERATES THE SER'S BIT CLOCK(LDA #1(ƉSTA OUTSTAT( UT2 NOPUT1 PLA:JMP $F1CADOPUT2 CLC:PLA+JSR RSOUTBLDA #$00:STA $0297JCLCYDOPUT4 RTSV;; W;;;;;;;;;;;;;;;;;; XDO PHA:JSR DISABLE:JSR $F314bBEQ DOCLO2lPLA:CLC:RTSvDOCLO2 JSR $F3 'NMIDOBIT PHA''BIT $DD0D;; CHECK BIT 7 (STARTBIT )W$'BPL NMIDOBI2;; NO STARTBIT RECEIVED, SKIPd.'LDA #$138'STA $DD0F;; START TIMER B (CED RE, SIGNAL AT PB7)B'STA $DD0D;; DISABLE TIMER FLAG ERRUPTSL'LDA #NMIBYTRY;;  SKIP (EG. KEY)+LDA #$923 +STA $DD0F;; TIMER B (KEEP SIGNALLING AT PB7!)b*+STA $DD0D;; ENABLE FLAG ( TIMER) ERRUPTS4+LDA #NMIDOBIT;; NMI CALL NMIDOBIT>+STA NMIVECT;; (TRIGGERED BY A STARTBIT)C+LDA #NMIDOBIT:STA NMIVECT1H+TA (RECPTR),Y+INY+STY WRRPTR +SEC;;START BUFFER FULL CHK(+TYA7+SBC RDRPTRD+CMP #200U+BCC NMIBYTR2+LDA $DD01;; ME THAN 200 BYTES IN THE RECEIVE BUFFER+ #$FD;; DISABLE RTS,STA $DD01,NMIBYTR2 PLA:TAY:PLA:TAX$,PLAIP THE FIRST PARTRNLDX OUTSTAT/\NBEQ IRQ2;; SKIP, WE'RE ING AN EMPTY SDR7fNDEXGpNSTX OUTSTAT[zNIRQ2 BCC IRQ6iNIRQ3 CLIwNJSR $FFEANLDA $CC:BNE IRQ5NDEC $CD:BNE IRQ5NLDA #$14:STA $CDNLDY $D3:LSR $CFNLDX $02EUR LDY RDRPTRUCPY WRRPTR,VBEQ R3;; SKIP (EMPTY BUFFER, WITH CARRY SET)?VLDA (RECPTR),YGVINYV"VSTY RDRPTRy,VPHA;;BEGIN BUFFER EMPTYING CHK6VTYA@VSECJVSBC WRRPTRTVCMP #206;;25650^VBCC R2hVLDA #2:A $DD01:STA $D$DC0C:LDA #$02:STA OUTSTAT:R:A #$7F:STA $DC0C FZCLI%PZLDX $A8:LDY $A7:PLA:RTS4ZZRSOUTX CLIIdZLDA #$FD:STA $A2mnZRSOUTX2 LDA OUTSTAT:BEQ RSOUTX3xZBIT $A2:BMI RSOUTX2ZRSOUTX3 JMP $F490o];; p];; INSTALLR ALY CHANGED vuLDA NMIVECT uCMP #IGNMI'!uBNE INSTERR;; NMIVECR ALY CHANGED9!uLDA NMIVECT1J!uCMP #IGNMIs!uBNE INSTERR;; NMIVECR ALY CHANGED~!uLDY #0!uSTY WRSPTR!uSTY RDSPTR!uSTY WRRPTR!uSTY RDRPTR!u;; PROBE RS $DD01;; LOOK IT TRIGGERS AN #\vDEX;; SHTREGISTER ERRUPT#fvBNE INSTALL2C#pvLDA $DD0D;; CHECK BIT3 (SDRFLAG)L#zv #8r#vBEQ INSTERR;; NO ERFACE DETECTED#v;; GENERATE LOOKUP TABLE#vLDX #0#vINSTALL3 STX OUTSTAT;; OUTSTAT USED AS TE#IRQ:BNE ENABL2$LDA IRQVECT1:CMP #IRQ:BNE ENABL2%PLA:TAY:PLA:TAX:PLA:RTS*%ENABL2 SEIO%ˆLDX #IRQ;; INSTALL IRQHLER^%̈LDY #IRQn%ֈSTX IRQVECT%STY IRQVECT1%LDX #NMIDOBIT;; INSTALL NMIHLER%LDY #NMIDOBIT%STX1F;; SET BALDA $BA:CMP #$02:BEQ DOCLO40DOCLO3 PLA:JMP $F291SDOCLO4 LDA #$00:STA UPFLAG:PLAhLDX #$00:CLC:RTS>;; @DOCHKIN PHA:LDA UPFLAG:BNE DOCHKI1:PLA:JMP $F20EEDOCHKI1 PLA:JSR $F30F:BEQ DOCHKI2OCHKO1 PLA:JSR $F30F:BEQ DOCHKO2:JMP $F701(DOCHKO2 JSR $F31FILDA $BA:CMP #$02:BEQ DOCHKO4cCMP #$04:BCC NOCHKOUTDOCHKO3 JSR DISABLE:JMP NOCHKOUTDOCHKO4 STA $9A:JSR ENABLE:CLC:RTSNOCHKOUT JMP $F25B';; NMI CALL NMIBYTRYV'STA NMIVECT;; (TRIGGERED BY SDR FULL)@['LDA #NMIBYTRY:STA NMIVECT1w`'NMIDOBI2 PLA;; IGNE, NMI WAS TRIGGERED BY KEYj'RTIt';;*NMIBYTRY PHA+BIT $DD0D;; CHECK BIT 7 (SDR FULL ) +BPL NMIDOBI2;; SDR FULL, XA:PHA:TYA:PHA#\+LDA $DD0C;; SDR (BIT0BIT7,...,BIT7=DATABIT0)Jf+CMP #128;; MOVE BIT7 O CARRYFLAGUp+ #127]z+TAX+LDA REVTAB,X;; BITS 1-7 FROM LOOKUP TABLE+ADC #0;; ADD BIT0+LDY WRRPTR;; WRITE IT O THE RECEIVE BUFFER+ST.,RTI#N;; 3N;; IRQ PARTF NIRQ LDA $DC0Da*NIRQ1 LSR;; IRQMASK4NLSR;; MOVE BIT1 O CARRYFLAG (TIMER B FLAG)>N #$02;; TEST BIT3 (SDR FLAG)HNBEQ IRQ3;; SDR EMPTY, SK87NLDA ($D1),Y:BCS IRQ4NINC $CF:STA $CENJSR $EA240NLDA ($F3),Y:STA $0287FNLDX $0286:LDA $CEXNIRQ4 E #$80fOJSR $EA1CzOIRQ5 JSR $EA87OIRQ6 JMP $EA81U;; U;; BYTE FROM SERIAL ERFACD01;; ENABLE RTSVCLCVR2 PLAVR3 RTS*Y;; KY;; PUT BYTE SERIAL ERFACEqYRSOUT PHA:STA $9E:CMP #$80: #$7FYSTX $A8:STY $A7:TAXYJSR RSOUTXZRSOUT3 LDA REVTAB,X(ZADC #$00:LSR2ZSEIvLDX #8"HvINSTALL2 STX $DD01;; GGLE TXD"RvSTA MPARY VARIABLE#vLDY #8#vINSTALL4 ASL OUTSTAT$vR $vDEY$vBNE INSTALL4,$vSTA REVTAB,X4$vINXE$vBPL INSTALL3k$;; $;; ENABLE SERIAL ERFACE (IRQNMI)$ENABLE PHA:TXA:PHA:TYA:PHA$LDA IRQVECT:CMP  :JMP $F701 -FDOCHKI2 JSR $F31F+JLDA $BA:CMP #$02:BEQ DOCHKI4DOCMP #$04:BCC NOCHKINhTDOCHKI3 JSR DISABLE:JMP NOCHKIN^DOCHKI4 STA $99:JSR ENABLE:CLC:RTShNOCHKIN JMP $F219;;DOCHKOUT PHA:LDA UPFLAG:BNE DOCHKO1:PLA:JMP $F250D -OP2 NOP N LDA #$00 X STA RHEAD( b STA RTAIL7 l STA RCOUNTE v STA ERRSc ;LDA #RECVBUFF:;TA RBUFF ;;DA #RECVBUFF:;TA RBUFF1 ; ERNAL CLOCK, 8N1, BAUD BELOW LDY #$00: LDA ($BB),Y A #%00010000 STA ROL ; NO PAR, NO ECHO A:PHALDA #$00:STA SAVBYTE&LDA RHEAD:CMP RTAIL3BEQ DO4@PDO3 TAYRLDA (RBUFF),Y`INC RHEADpSTA SAVBYTEDO4 PLA:TAX:PLA:TAYLDA SAVBYTE:CLC:RTSo;;;;;;;;;;;;;;;;;;pDOPUT PHAzLDA $9ACMP #$02:BEQ DOPUT2 P #$02:BEQ DOCLO3PLA:JMP $F291(DOCLO3 PLA:JSR $F291ULDX #%00000011:STX COMM ; DISABLE STUFF]SEIjLDA #$47xSTA NMINVLDA #$FESTA NMINV1CLILDX #$00:RTS?;;;;;;;;;;;;;;;;;;ٙ NOP;:1,8,15,"S0:SWIFTDRVR*":JSR R:BCC DOTIN:PLA:TAX:PLA:TAY:LDA #$08:STA $0297:LDA #$00:CLC:RTSQDOTIN STA SAVBYTEjDO4 PLA:TAX:PLA:TAYLDA SAVBYTE:CLC:RTSn;; pDOPUT PHAzLDA UPFLAG:BEQ NOPUT1LDA $9A:CMP #$02:BEQ DOP +FDOCHKI2 JSR $F31F+JLDA $BA:CMP #$02:BEQ DOCHKI4DOCMP #$04:BCC NOCHKINhTDOCHKI3 JSR DISABLE:JMP NOCHKIN^DOCHKI4 STA $99:JSR ENABLE:CLC:RTShNOCHKIN JMP $F219;;DOCHKOUT PHA:LDA UPFLAG:BNE DOCHKO1:PLA:JMP $F250D ; ERNAL CLOCK, 8N1, BAUD BELOW2 STY ESR232:LDA ($BB),YP TAY:LDA BAUDS,Y:BPL DOOP3e LDY #16:STY ROL #$7F:STA ESR232:LDA #$00 DOOP3 A #16:;%00010000 STA ROL ; NO PAR, NO ECHO, XMIT , RECV LDA #9:; %00001001 ; STA NV1 NVEC CLI LDA #DO:STA $031C4 LDA #DO:STA $031DN LDA #DOPUT:STA $0326hLDA #DOPUT:STA $0327LDA #DOCLAL:STA $032CLDA #DOCLAL:STA $032D<E JSR UPDCDPLA:TAY:PLA:PLP:RTS;;;;;;;;;;;;NMI PHA:TXA:PHA:TY .JSR $FFE1:BNE NMOUT8JMP $FE664VNMOUT PLA:TAY:PLA:TAX:PLA:RTII;;;;;;;;;;;;;;;;]SAVBYTE .BYTE 0oUPDCD PHP:PHALDA $DD03:A #$10:STA $DD03LDA STATUS: #64:BEQ DCDLDA $DD01:A #$10:STA $DD01PLA:PLP:RTSDCD LDA $JSR R:BCC DOTIN:PLA:TAX:PLA:TAY:LDA #$08:STA $0297:LDA #$00:CLC:RTSQDOTIN STA SAVBYTEjDO4 PLA:TAX:PLA:TAYLDA SAVBYTE:CLC:RTSn;; pDOPUT PHAzLDA UPFLAG:BEQ NOPUT1LDA $9A:CMP #$02:BEQ DOP ­ #‘̑‘ 4L¢ ™ L ) ¹ ­лLL®  ™  B L­L``   L  eh80qȩqh° в````M)jIjо` # °L4```` L͜ ™600 (CERR)/ 2JMP ENABLE;; (RE)ENABLE ERFACEi <JMP DISABLE;; DISABLE ERFACE (EG. FLOPPY ACCESSES) F;; RSOUT R BOTH MODY A X REGISTER PJMP RSOUT;; PUT BYTE RS232 (BLOCKING) ZJMP R;; BYTE FROM RS232 (CTRYAGAIN)# -JFIES $EPOER O RECEIVE BUFFER@ 8RDRPTR 668;; POER O RECEIVE BUFFERX L;; STATIC VARIABLES VSTIME .BYTE 0;; COPY OF $A2JFIES DETECT TIMEOUTS `OUTSTAT 169 jUPFLAG .BYTE 0 tSAVBYTE .BYTE 0 0 0 0 ~;;JFIES .BYTE 0 RECPTR 247;; LDA #DOCHKOUT:STA $0320, LDA #DOCHKOUT:STA $0321H LDA #DOCHRIN:STA $0324d LDA #DOCHRIN:STA $0325~ LDA #DOIN:STA $032A LDA #DOIN:STA $032B LDA #DOPUT:STA $0326  LDA #DOPUT:STA $0327  CLI:RTS  ;; +DOCHRIN LDA UPFLAG:BEQ NOCHRINILDA $99:CMP #$02:BEQ DO2_NOCHRIN JMP $F157~DOIN LDA UPFLAG:BEQ NOINLDA $99:CMP #$02:BEQ DO2NOIN JMP $F13EDO2 TYA:PHA:TXA:PHALDA #$00:STA SAVBYTE:STA $0297, NO XMIT , YES RECV " LDA #%00001001 ;0 STA COMM> STA ZPXMFb #%11110000 ; KEEP PARITYECHO A #%00001001 ; SET RECV BUF LY STA ZPXMO VEC SEI LDA #NMI STA NMINV LDA #NMI STA NMINV1 CLI <E PLA:TAINC RTAIL INC RCOUNTLDA ZPXMF'"STA COMME$NREVD PLA:TAY:PLA:TAX:PLAM.RTIb;;;;;;;;;;;;;;;;vSAVBYTE .BYTE 0DOCHRIN LDA $99:CMP #$02:BEQ DO2JMP $F157DOIN LDA $99:CMP #$02:BEQ DO2JMP $F13EDO2 TYA:PHA:TXPLA:JMP $F1CADOPUT2 LDA STATUS# #%000100002BEQ DOPUT2HCLC:PLA:STA PORTWBCC DOPUT3d -LDA #$00sDOPUT3 RTSW;;;;;;;;;;;;;;;;;;XDO PHA:JSR $F314bBEQ DOCLO2lPLA:CLC:RTSvDOCLO2 JSR $F31F ; SET BALDA $BA:CM1:"SWIFTDRVR",8OK"203) #5,A$:A$""208H 5,CR$;"ATE0N0R0V1Q0";CR$;_ 900:P$"OK"208 5,"ATE0V1X1F3Q0S40=128S0=1S41=0";CR$;(19);: A50764 900:P$"OK""IMODEM INIT FAILED: ";P$: #5,A$:A$""245 5,"ATI2";CR$;:2'  B $L$$L tP ȩ tɀii12`    LL[  ۩ Ω мL`0:HHȱ LL0LVLL-.Pȱɀii/0`    LL[  ۩ Ω мL0`0:HHȱ eȱ +JFIES $EPOER O RECEIVE BUFFER@ 8RDRPTR 668;; POER O RECEIVE BUFFERX L;; STATIC VARIABLES VSTIME .BYTE 0;; COPY OF $A2JFIES DETECT TIMEOUTS `OUTSTAT 169 jUPFLAG .BYTE 0 tSAVBYTE .BYTE 0 0 0 0 ~;;JFIES .BYTE 0 RECPTR 247;; LDA #DOCHKOUT:STA $0320, LDA #DOCHKOUT:STA $0321H LDA #DOCHRIN:STA $0324d LDA #DOCHRIN:STA $0325~ LDA #DOIN:STA $032A LDA #DOIN:STA $032B LDA #DOPUT:STA $0326  LDA #DOPUT:STA $0327  CLI:RTS  ;; +DOCHRIN LDA UPFLAG:BEQ NOCHRINILDA $99:CMP #$02:BEQ DO2_NOCHRIN JMP $F157~DOIN LDA UPFLAG:BEQ NOINLDA $99:CMP #$02:BEQ DO2NOIN JMP $F13EDO2 TYA:PHA:TXA:PHALDA #$00:STA SAVBYTE:STA $0297$F7 +NMINV $0318! +3;;;;;;;;;;;. + JMP INITD + ; INITCHIP ; VECS + ; RECVBYTEb + ; XMITBYTEr +' +;;;;;;;;;;; + INIT SEI + LDA $031B:CMP #DO:BEQ NOINIT + LDY #20 + INSAV LDA $0319,Y:STA VECS,Y + DEY:BNE INSAV LDA $031A:STA DO1: JSR $F34A:; CALL I ! PHP:PHA- & LDA $BA:CMP #$02; 0 BEQ DOOP2W : PLA:PLP:RTS:;EARLY EXITi D DOOP2 TYA:PHAv N LDY #$00 X STY RHEAD b STY RTAIL l STY RCOUNT v STY ERRS ; ERNAL CLOCK, 8N1, BAUD BELOW ;;DA #RECVBUFF:;TA RBUFF1  COMM STA ZPXMF, #%11110000 ; KEEP PARITYECHOT A #9:;%00001001 ; SET RECV BUF LYb STA ZPXMOo VEC SEI LDA NMINV1:CMP #NMI:BEQ NVEC STA OLDNMI1:LDA NMINV:STA OLDNMI LDA #NMI STA NMINV LDA #NMI STA NMIA:PHA#;LDX #%00000011:STX COMM ; DISABLE STUFF6CLD:LDA STATUSR #8:; MASK OUT NINDI`BEQ NREVDo;STA ERRS}LDY RTAILLDA PORTSTA (RBUFF),YINC RTAILINC RCOUNTNREVD NOP:;LDA ZPXMF:STA COMM$;JMP $FEBC2'  B $L$$L tP ȩ tɀii12`    LL[  ۩ Ω мL`0:HHȱ LL0LVLL-.Pȱɀii/0`    LL[  ۩ Ω мL0`0:HHȱ eȱ $C800+ ;;UP9600 KERNAL ADAPTERM ;;IGINALLY BY DANIAL DALLMANy ;;ADAPTED 2017 KERNAL BY BO ZIMMERMAN;;MODIED 2142017 1:26A.D @0:UP9600.BIN;; PROVIDED FUNCTISJMP INIT (JMP INSTALL;; INSTALL (PROBE ) UP9A2; LOWEST BYTE OF TEM'S JFIE COUNTERW IGIRQ $EA31;; (MUST INCEASE JFIECOUNTER !)j @@ -153,15 +167,16 @@ OP2 NOP N LDA #$00 X STA RHEAD( b STA RTAIL7 l STA RCOUNTE v STA ERR NMIVECT 792 IRQVECT 788 WRSPTR 670;; WRITEPOER O S BUFFER -$RDSPTR 669;; POER O S BUFFER .WRRPTR 667;; WRITRECBUF $CB00;; 247 248? SNDPTR 249;;SNDBUF $CC00;; 249 250F ;;M ;;Z INIT SEIr LDA #DO:STA $031A LDA #DO:STA $031B LDA #DO:STA $031C LDA #DO:STA $031D LDA #DOCHKIN:STA $031E LDA #DOCHKIN:STA $031F   DO PHA:TYA:PHA:JSR DISABLE6! JSR $F34A;;CALL IT& LDY #$00:LDA $BA:CMP #$02_0 BNE Ey LDY #$00: LDA ($BB),Y CMP #$0C BCC E LDA #$01:STA UPFLAG JSR INSTALL<E PLA:TAY:PLALDX #$00:CLC:RTS;;  Y:PLA:RTSLDX #$00:RTS;;;;;;;;;;;;,NMI NOPDPHA:TXA:PHA:TYA:PHASLDA STATUSLDX #%00000011:STX COMM ; DISABLE STUFFSTA ERRS #%00001000 ; MASK OUT NINDIBEQ NREVDLDA PORTLDY RTAILSTA (RBUFF),Y +$RDSPTR 669;; POER O S BUFFER .WRRPTR 667;; WRITRECBUF $CB00;; 247 248? SNDPTR 249;;SNDBUF $CC00;; 249 250F ;;M ;;Z INIT SEIr LDA #DO:STA $031A LDA #DO:STA $031B LDA #DO:STA $031C LDA #DO:STA $031D LDA #DOCHKIN:STA $031E LDA #DOCHKIN:STA $031F   DO PHA:TYA:PHA:JSR DISABLE6! JSR $F34A;;CALL IT& LDY #$00:LDA $BA:CMP #$02_0 BNE Ey LDY #$00: LDA ($BB),Y CMP #$0C BCC E LDA #$01:STA UPFLAG JSR INSTALL<E PLA:TAY:PLALDX #$00:CLC:RTS;;  LDA $031B:STA DO29 LDA $031C:STA DO1:LDA $031D:STA DO2k LDA $0326:STA DOPUT21:LDA $0327:STA DOPUT22 LDA $032C:STA DOCLAL1:LDA $032D:STA DOCLAL2 NOINIT LDA #DO:STA $031A  LDA #DO:STA $031B  CLI:RTS  ;;;;;;;;;;;  DO LLL`LL/0 tP ȩ tɀii12`   $ LL'['  ۩ Ω мL$`'0:HHȱ  PEE1 .BYTE 0 0" PEE2 .BYTE 0 03 CRC8 .BYTE 0D CRCX .BYTE 0U CRCE .BYTE 0f CRCS .BYTE 0| "TIMEOUT .BYTE 0 0 ,BUFAPX .BYTE 5 DEBUG .BYTE 0 0 SETPSTR LDA $2F:STA $FB:LDA $30:STA $FC SETPLP LDY #$00:LDA #$FB:LDX #$01:JSR $ PACKET LDX #$0C@ PACK LDA #$00:STA NUMX,X:DEX:BNE PACK:STA NUMXN JSR $FFCCe 4LDX #$05:JSR $FFC6u >JSR BUF1LIN HLDY #$00:STY CRC8 RPCKLP1 CPY BUF1DX:BCC PCKC1 \PCKDUN1 JMP $FFCC fPCKERR1 LDX #$FF:STX CRXFLG:JMP $FFCC PCKC1 LDJSR PCKDIG:BNE PCKERR1( LDA PEE11:BNE PCKERR1J LDA PEE1:STA NUMX:BEQ PCKDUN1` PCKP JMP BUFXLIN PCKDIG CPY BUF1DX:BCC PCKDIG2 DIGERR1 LDX #$FF:RTS PCKDIG2 LDA BUF1,Y:INY PCKDIG3 CMP #48:BCC DIGERR1:CMP #58:BCS DIGERR1 TAX:TD),YP INY:LDA #$00:ADC ($FD),Y:STA ($FD),YBZ PLA:TAY:CPY BUF1DX:BCS DIGERR2id LDA BUF1,Y:INY:CMP #32:BNE PCKDIG3zn LDA #$00:RTSx -DIGERR2 LDA #$FF:RTS BUF LDA #$00:TAY BUFCLP STA BUF1,Y:INY:BNE BUFCLP RTSDOCRC8 LDA #$00:STA CDOCRC3 LDA CRCE:CLC:R:STA CRCE%DEY:BNE DOCRC2="INC CRCX:BNE DOCRC0E,RTSZCRCP JSR SETPSTRLDY #$02:LDA #$FB:LDX #$01:JSR $FF74:STA BUF1DXINY:LDA #$FB:LDX #$01:JSR $FF74:STA $FDINY:LDA #$FB:LDX #$01:JSR $FF74:STA $FEL LDX #$05:JSR $FFC6:LDY #$00:TYA:STA BUF1DX:JSR RETIMEV'BUF1LP JSR CHKTIM:BEQ BUF1L2:STX CRXFLG:JMP $FFCC'BUF1L2 LDA $0A18:CMP $0A19:BEQ BUF1LP$'JSR $FFE4:LDY BUF1DX:STA BUF1,YB'CMP #$00:BEQ BUF1LPL'CMP #$0A:BEQ BUF1LPV'CMP #$0D:BEQ BUDA #$00:JSR $FF77 'LDY #$04:LDX #$01:LDA #$FE:JSR $FF77F'LDY #$00:STY $FB:LDA #$FE:STA $FCi'BUF1OL1 CPY BUF1DX:BCS BUF1OU0'LDA BUF1,Y:LDX #$01:JSR $FF77'INY:BNE BUF1OL1:JMP BUF1OU1'BUF1OU0 LDX #$01:LDA PBACK:JSR $FF77'LDX #$01:IN20015 ;;;;;;;;;;;% BASE $DE007 PORT = $DE00J STATUS $DE01\ COMM $DE02m ROL $DE03s ; RHEAD $A7 RTAIL $A8 RBUFF $F7 RCOUNT $B4 ERRS $B5 ZPXMO $B6 ZPXMF $BD NMINV $0318 -3; LDA #DOCHRIN:STA $03250 LDA #DOIN:STA $032BJ LDA #DOPUT:STA $0326d  LDA #DOPUT:STA $0327l  CLIt  RTS  ;;;;;;;;;;;  DO JSR $F34A; CALL I ! PHA:TYA:PHA & LDA $BA:CMP #$02 0 BEQ DOOP2 : PLA:TAY:PLA:RTS;EARLY EXIT D DOeȱeh80qȩqh$ ' в``'`$`'M)jIjо` # t$ȩ tȩ t$  t'L>L `` $  L +DIGERR2 LDA #$FF:RTS BUF LDA #$00:TAY BUFCLP STA BUF1,Y:INY:BNE BUFCLP RTSDOCRC8 LDA #$00:STA CDOCRC3 LDA CRCE:CLC:R:STA CRCE%DEY:BNE DOCRC2="INC CRCX:BNE DOCRC0E,RTSZCRCP JSR SETPSTRLDY #$02:LDA #$FB:LDX #$01:JSR $FF74:STA BUF1DXINY:LDA #$FB:LDX #$01:JSR $FF74:STA $FDINY:LDA #$FB:LDX #$01:JSR $FF74:STA $FEL LDX #$05:JSR $FFC6:LDY #$00:TYA:STA BUF1DX:JSR RETIMEV'BUF1LP JSR CHKTIM:BEQ BUF1L2:STX CRXFLG:JMP $FFCC'BUF1L2 LDA $0A18:CMP $0A19:BEQ BUF1LP$'JSR $FFE4:LDY BUF1DX:STA BUF1,YB'CMP #$00:BEQ BUF1LPL'CMP #$0A:BEQ BUF1LPV'CMP #$0D:BEQ BUDA #$00:JSR $FF77 'LDY #$04:LDX #$01:LDA #$FE:JSR $FF77F'LDY #$00:STY $FB:LDA #$FE:STA $FCi'BUF1OL1 CPY BUF1DX:BCS BUF1OU0'LDA BUF1,Y:LDX #$01:JSR $FF77'INY:BNE BUF1OL1:JMP BUF1OU1'BUF1OU0 LDX #$01:LDA PBACK:JSR $FF77'LDX #$01:INLfhhh@H ݭ)@  h(`)h(`HhL)hސ` H+ޭxɍɍHəh F h(` /HLV  + G; B480012; B720013; B960014 ; B19 + $C800#.D SWTDRVR.BIN;; KERNAL BAUD RATESG; B501S; B752`; B1103m; B1354z; B1505; B3006; B6007; B12008; B18009; B240010; B360011; B480012; B720013; B960014 eȱeh80qȩqh$ ' в``'`$`'M)jIjо` # t$ȩ tȩ t$  t'L>L `` $  L   4864 .D PML128.BIND; PACKET ML 128 BY BO ZIMMERMANd; UPDATED 20170728 10:14Pj;zdJMP BUF1LINnJMP BUFXLINxJMP PACKETJMP CRCPJMP BUFAPNOP:NOP:NOPNUMX .BYTE 0CRXFLG .BYTE 0PEE0 .BYTE 0 0FF74 @@ -178,27 +193,17 @@ LDX #$09 DIGLP LDY #$00:LDA ($FD),Y:CLC:ADC $FB:STA ($FD),Y2 INY:LDA ($FD),Y:ADC $FC:STA ($FD),Y< DEX:BNE DIGLPF -LDY #$00:PLA:SEC:SBC #48:CLC:ADC ($FD),Y:STA ($FRC8 LDA #$00:STA CRCX3DOCRC0 LDA CRCX:CMP BUF1DX:BCC DOCRC1;RTS^DOCRC1 TAX:LDA BUF1,X:STA CRCEkLDY #$08DOCRC2 LDA CRC8:E CRCE #$01:STA CRCSLDA CRC8:CLC:R:STA CRC8LDA CRCS:BEQ DOCRC3LDA CRC8:E #$8C:STA CRC8DY #$00CRCP11 CPY BUF1DX:BCS CRCPDUN@LDA #$FD:LDX #$01:JSR $FF74:STA BUF1,YSINY:BNE CRCP11jCRCPDUN JMP DOCRC8(#RETIME LDA #$00:TAX:TAY:JMP $FFDB%CHKTIM JSR $FFDE:CPX #$03:BCS TIMBAD:LDX #$00:RTS:%TIMBAD LDX #$FF:RTS 'BUF1LINF1DUN['JSR RETIME*`'INC BUF1DX:LDA BUF1DX:CMP #$FE:BCC BUF1LPct'BUF1DUN LDA $02B9:PHA:JSR SETPSTR:LDA #$FB:STA $02B9~'LDA $FB:CLC:ADC #$02:STA PBACK:LDA $FC:ADC #$00:STA PBACK1'LDY #$02:LDX #$01:LDA BUF1DX:JSR $FF77'LDY #$03:LDX #$01:L - 49152#.D SWTDRVR.BIN4; BAUD RATES@; B501L; B752Y; B1103f; B1354s; B1505; B3006; B6007; B12008; B18009; B240010; B360011; B480012; B720013; B960014 ; B19 ;;;;;;;;;; - JMP INIT) - ; INITCHIP ; VEC8 - ; RECVBYTEG - ; XMITBYTEW -' -;;;;;;;;;;;d - INIT SEI| - LDA #DO:STA $031A - LDA #DO:STA $031B - LDA #DO:STA $031C - LDA #DO:STA $031D - LDA #DOCHRIN:STA $0324 - LDA #DOIN:STA $032A $' - $$˭H #i!i"$ w w w$' wL! wȭ" wh >L$  $' L ) $' $$лLL  $ $A 0 l"64NET APPS2ACONFIGURE,8: FTP,8:&IRC,8: WGET,8: D64WGET,8: TELNET,8: CBMTERM,8TELNETD64,8:KK COMETMODE64,8: - WEATHER64,8: ----REQ ML----PML64.BINPML128.BINPMLVIC.BIN UP9600.BIN RDS64.BINKK -TELNETML.BINV-1541 X-XFER64.BINX-XFER128.BIN----SOURCE----PML64.BAS PML128.BASPMLVIC.BASKK SWIFTDRVR201608 UP9600.BAS(RDS64.BAS- TELNETML.BAS----TOOLS---- EMUTIL LADS MONITOR.49152KK MONITOR.8192KKKKK +LDY #$00:PLA:SEC:SBC #48:CLC:ADC ($FD),Y:STA ($FRC8 LDA #$00:STA CRCX3DOCRC0 LDA CRCX:CMP BUF1DX:BCC DOCRC1;RTS^DOCRC1 TAX:LDA BUF1,X:STA CRCEkLDY #$08DOCRC2 LDA CRC8:E CRCE #$01:STA CRCSLDA CRC8:CLC:R:STA CRC8LDA CRCS:BEQ DOCRC3LDA CRC8:E #$8C:STA CRC8DY #$00CRCP11 CPY BUF1DX:BCS CRCPDUN@LDA #$FD:LDX #$01:JSR $FF74:STA BUF1,YSINY:BNE CRCP11jCRCPDUN JMP DOCRC8(#RETIME LDA #$00:TAX:TAY:JMP $FFDB%CHKTIM JSR $FFDE:CPX #$03:BCS TIMBAD:LDX #$00:RTS:%TIMBAD LDX #$FF:RTS 'BUF1LINF1DUN['JSR RETIME*`'INC BUF1DX:LDA BUF1DX:CMP #$FE:BCC BUF1LPct'BUF1DUN LDA $02B9:PHA:JSR SETPSTR:LDA #$FB:STA $02B9~'LDA $FB:CLC:ADC #$02:STA PBACK:LDA $FC:ADC #$00:STA PBACK1'LDY #$02:LDX #$01:LDA BUF1DX:JSR $FF77'LDY #$03:LDX #$01:LLx;ɈSȭTȭLɭMɭ&8ɭ'9ɭ,ɭ-ɩRȍX` JHh(`HɌɌޱ )ީ ީ ލ)p xɭɩݍȍXKɍ/&ɍ'ɍ,- hh(`HHHح)ޑ  +; B1920015 ; B3840016+ ; B5760017< ; B11520018M ; B23040019] ;;;;;;;;;;;n BASE $DE00 PORT = $DE00 STATUS $DE01 COMM $DE02 ROL $DE03 ESR232 $DE07 ; RHEAD 668 RTAIL 667 RBUFF  $' + $$˭H #i!i"$ w w w$' wL! wȭ" wh >L$  $' L ) $' $$лLL  $ $A l 64NET APPS2A +CONFIGURE,8: FTP,8:&IRC,8: +IRC 64,8: WGET,8: D64WGET,8: TELNET,8: CBMTERM,8:KKTELNETD64,8:COMETMODE64,8: + WEATHER64,8: ----REQ ML----PML64.BINPML128.BINPMLVIC.BIN UP9600.BINKK + RDS64.BINTELNETML.BINV-1541 X-XFER64.BINX-XFER128.BINSWIFTDRVR.BIN----SOURCE----PML64.BASKK  PML128.BASPMLVIC.BASUP9600.BAS(RDS64.BAS- TELNETML.BAS SWIFTDRVR.BAS----TOOLS---- EMUTILKK LADS MONITOR.49152 MONITOR.8192KKKKK  - 49152 .D PML64.BINC; PACKET ML 64 BY BO ZIMMERMANc; UPDATED 20170728 10:14PsdJMP BUF1LINnJMP BUFXLINxJMP PACKETJMP CRCPJMP BUFAPNOP:NOP:NOPNUMX .BYTE 0CRXFLG .BYTE 0PEE0 .BYTE 0 0 PEE1 # CONFIGURE64/128 1200B 1.8+C UPDATED 08/14/2017 02:54Ad254,(186):(254)8254,8 -SY(65532):SY6158,254: SY34X23777:X,170:(X)170"<16K":5,2,0,(8):(65532)3456,87:54,87:52,87 WF$(100):WF0:P$" TPNXT + 49152 .D PML64.BINC; PACKET ML 64 BY BO ZIMMERMANc; UPDATED 20170728 10:14PsdJMP BUF1LINnJMP BUFXLINxJMP PACKETJMP CRCPJMP BUFAPNOP:NOP:NOPNUMX .BYTE 0CRXFLG .BYTE 0PEE0 .BYTE 0 0 PEE1 # +(49153)54"V-1541",8,1:E5,2,0,(8):TI$"000000":T10t5,"ATR0":TTTI100:900:"CONNECTING...";(920:".";:T1T11:T120"FAIL.":5:25,"AT":TTTI100:900<5,A$:(A$,2)"AT"(A$,2)""5,A$ + F(A$,2)"OK"(A$ TPNXT INY:LDA ($FB),Y. CMP #$80:BEQ SETPDUNW LSETPNXT LDA $FB:CLC:ADC #$07:STA $FBt @@ -213,25 +218,27 @@ DIGLP LDY #$00:LDA ($FD),Y:CLC:ADC $FB:STA ($FD),Y INY:LDA ($FD),Y:ADC $FC:STA ($FD),Y< DEX:BNE DIGLPF LDY #$00:PLA:SEC:SBC #48:CLC:ADC ($FD),Y:STA ($FD),YP -INY:LDA #$00:ADC ($FD),Y:STA ($X:STA CRXFLGDuBUFALP JSR $FFE4:LDY BUF1DX:STA BUF1,Y:NuJSR $FFB7:CMP #$08:BEQ BUFAXQXuCMP #$42:BEQ BUFAXtbuCMP #$00:BEQ BUFAP1:INC BUF1DXluBUFAX STA CRXFLG:JMP BUF1DUNuBUFAP1 INC BUF1DX:LDA BUF1DX:CMP #$FE:BCC BUFALP:JMP BUF1DUNBUFTM CRCX:CMP BUF1DX:BCC DOCRC1RTS6DOCRC1 TAX:LDA BUF1,X:STA CRCECLDY #$08_DOCRC2 LDA CRC8:E CRCEs #$01:STA CRCSLDA CRC8:CLC:R:STA CRC8LDA CRCS:BEQ DOCRC3LDA CRC8:E #$8C:STA CRC8DOCRC3 LDA CRCE:CLC:R:STA CRCEOK":PB$(50):186,(254)N CR$(13):(14);:SY(65532):53280,254:53281,246 CO$"":SY226ML49152:665,73((678)30) #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: &SY3436879,27:CO$(31) -(SY226(ML1)2095:"PML6 .BYTE 0 0 PEE2 .BYTE 0 0, CRC8 .BYTE 0= CRCX .BYTE 0N CRCE .BYTE 0_ CRCS .BYTE 0u "TIMEOUT .BYTE 0 0 ,BUFAPX .BYTE 5 DEBUG .BYTE 0 0 SETPSTR LDA $2D:STA $FB:LDA $2E:STA $FC SETPLP LDY #$00:LDA ($FB),Y +INY:LDA #$00:ADC ($FD),Y:STA ($X:STA CRXFLGDuBUFALP JSR $FFE4:LDY BUF1DX:STA BUF1,Y:NuJSR $FFB7:CMP #$08:BEQ BUFAXQXuCMP #$42:BEQ BUFAXtbuCMP #$00:BEQ BUFAP1:INC BUF1DXluBUFAX STA CRXFLG:JMP BUF1DUNuBUFAP1 INC BUF1DX:LDA BUF1DX:CMP #$FE:BCC BUFALP:JMP BUF1DUNBUFTM CRCX:CMP BUF1DX:BCC DOCRC1RTS6DOCRC1 TAX:LDA BUF1,X:STA CRCECLDY #$08_DOCRC2 LDA CRC8:E CRCEs #$01:STA CRCSLDA CRC8:CLC:R:STA CRC8LDA CRCS:BEQ DOCRC3LDA CRC8:E #$8C:STA CRC8DOCRC3 LDA CRCE:CLC:R:STA CRCE,2)""40 PQ$(34):920[ Z5,"ATF0C";Q$;"COMMODORESERVER.COM:1541";Q$:TTTI400:900 d5,A$:(A$,2)"AT"(A$,2)""5,A$ n(A$,8)"CONNECT "(A$,8)" "40 x5,"ATB2400O0":TTTI100:900 "DONE." +"* RESET MODEM TO .BYTE 0 0 PEE2 .BYTE 0 0, CRC8 .BYTE 0= CRCX .BYTE 0N CRCE .BYTE 0_ CRCS .BYTE 0u "TIMEOUT .BYTE 0 0 ,BUFAPX .BYTE 5 DEBUG .BYTE 0 0 SETPSTR LDA $2D:STA $FB:LDA $2E:STA $FC SETPLP LDY #$00:LDA ($FB),Y CMP #$50:BNE SENE CRCPL1 CRCPDUN JMP DOCRC8F(#RETIME LDA $A1:STA TIMEOUT:LDA #$00:STA TIMEOUT1:RTS~%CHKTIM LDA $A1:CMP TIMEOUT:BNE CHKTIM2:LDX #$00:RTS&%CHKTIM2 STA TIMEOUT:INC TIMEOUT10%LDA TIMEOUT1:CMP #$04:BCS TIMBAD:LDX #$00:RTS:%TIMBAD LDX #$F NUMX,X:DEX:BNE PACK:STA NUMX& JSR $FFCC= 4LDX #$05:JSR $FFC6M >JSR BUF1LINc HLDY #$00:STY CRC8 RPCKLP1 CPY BUF1DX:BCC PCKC1 \PCKDUN1 JMP $FFCC fPCKERR1 LDX #$FF:STX CRXFLG:JMP $FFCC PCKC1 LDA BUF1,Y:INY:CMP #91:BNE PCKLP1 LDA V'CMP #$0D:BEQ BUF1DUN ['JSR RETIME;`'INC BUF1DX:LDA BUF1DX:CMP #$FE:BCC BUF1LPSt'BUF1DUN JSR SETPSTRw~'LDY #$02:LDA BUF1DX:STA ($FB),Y'LDY #$03:LDA #BUF1:STA ($FB),Y'LDY #$04:LDA #BUF1:STA ($FB),Y*JSR DOCRC8:JMP $FFCC NBUFXLIN LNE PCKERR1" LDA PEE1:STA NUMX:BEQ PCKDUN18 PCKP JMP BUFXLINZ PCKDIG CPY BUF1DX:BCC PCKDIG2s DIGERR1 LDX #$FF:RTS PCKDIG2 LDA BUF1,Y:INY PCKDIG3 CMP #48:BCC DIGERR1:CMP #58:BCS DIGERR1 TAX:TYA:PHA:TXA:PHA LDY #$00:LDA ($FD),Y: #$0D:BNE BUFXNCRfNLDX CRXFLG:BNE BUFXNCR*pNSTY CRXFLG:INC CRXFLGAzNBUFXNCR INC BUF1DXeNLDA BUF1DX:CMP #$FD:BCS BUFXDUNNBUF1NI LDA NUMX:BNE BUFXLPNJMP BUF1DUNNBUFXDUN JMP BUF1DUN0uBUFAP LDX BUFAPX:JSR $FFC6:LDY #$00:TYA:STA BUF1DFD),YZ PLA:TAY:CPY BUF1DX:BCS DIGERR2Ad LDA BUF1,Y:INY:CMP #32:BNE PCKDIG3Rn LDA #$00:RTSkx -DIGERR2 LDA #$FF:RTS BUF LDA #$00:TAY BUFCLP STA BUF1,Y:INY:BNE BUFCLP RTSDOCRC8 LDA #$00:STA CRC8LDA #$00:STA CRCX DOCRC0 LDA~R .BYTE 0BUF1DX .BYTE 0BUFX .BYTE 0 0 BUF1 .BYTE 0Zٙ NOP;:F$"PML64.BAS":8,8,15,"S0:PML64*":F$,8:F$,81DX:STA BUF1,YNuJSR $FFB7:CMP #$00:BEQ BUFAP1XuINC BUF1DXbuSTA CRXFLG:JMP BUF1DUNuBUFAP1 INC BUF1DX:LDA BUF1DX:CMP # DEY:BNE DOCRC2"INC CRCX:BNE DOCRC0,RTS2CRCP JSR SETPSTRVLDY #$02:LDA ($FB),Y:STA BUF1DXwLDY #$03:LDA ($FB),Y:STA $FDLDY #$04:LDA ($FB),Y:STA $FELDY #$00CRCPL1 CPY BUF1DX:BCS CRCPDUNLDA ($FD),Y:STA BUF1,YINY:B -4.BIN",(254),1:C -2SY61ML4864:981,15:P(215)128:P12830643w -<SY61(ML1)2175:"PML128.BIN",(254),1: -FSY61CO$(159) -d -nP$"A" -xCO$;" V1.4":"EQUIRES 64ET II IRMWARE 1.5+" -"1200 BAUD VERSION"1  LPWF:LPPG15LPPG15-& IPGLP:(I)") ";WF$(I):IP0 :"NTER TO UIT TO ."t: LPWF"NTER FOR EXT AGE"D PG15"NTER FOR REV AGE"N "NTER A NUMBER TO ONNECT TO THAT " "? ";:5000:A$P$:A$""3100 #5,A$:A$""203+ 5,CR$;"ATHZ0F0E0";CR$;Q 900:P$"OK"CTCT1:CT10203o CT10#5,A$:A$""207 CT105,"ATHF3E0";CR$; CT10900:P$"OK"CTCT1:207 CT10"NITIALIZATION FAILED.":"S YOUR MODEM SET TO 1200B? hLh`HhLh L  +DIGERR2 LDA #$FF:RTS BUF LDA #$00:TAY BUFCLP STA BUF1,Y:INY:BNE BUFCLP RTSDOCRC8 LDA #$00:STA CRC8LDA #$00:STA CRCX DOCRC0 LDA~R .BYTE 0BUF1DX .BYTE 0BUFX .BYTE 0 0 BUF1 .BYTE 0Zٙ NOP;:F$"PML64.BAS":8,8,15,"S0:PML64*":F$,8:F$,81DX:STA BUF1,YNuJSR $FFB7:CMP #$00:BEQ BUFAP1XuINC BUF1DXbuSTA CRXFLG:JMP BUF1DUNuBUFAP1 INC BUF1DX:LDA BUF1DX:CMP # DEY:BNE DOCRC2"INC CRCX:BNE DOCRC0,RTS2CRCP JSR SETPSTRVLDY #$02:LDA ($FB),Y:STA BUF1DXwLDY #$03:LDA ($FB),Y:STA $FDLDY #$04:LDA ($FB),Y:STA $FELDY #$00CRCPL1 CPY BUF1DX:BCS CRCPDUNLDA ($FD),Y:STA BUF1,YINY:B\ EXIT COMET MODE." +5:49152" +1 +TITT9007 +N +#5,A$:A$""920T +00:900:"CONNECTING...";(920:".";:T1T11:T120"FAIL.":5:25,"AT":TTTI100:900<5,A$:(A$,2)"AT"(A$,2)""5,A$ + F(A$,2)"OK"(A$C10:1010*LC$P$:CT0:C1C11:C115P$"":41010:_WI$P$:"URRENT II : ";WI$uCD0:WI$""2000 1050:CD020004000:"ESTING CONNECTION...";:TTTI200TITT1055$CT0:#5,A$:A$""1060%.OK":PB$(50):186,(254)N CR$(13):(14);:SY(65532):53280,254:53281,246 CO$"":SY226ML49152:665,73((678)30) #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: &SY3436879,27:CO$(31) +(SY226(ML1)2095:"PML6 hLh`HhLh L   sL;Ʌ `LHhLPh L   sLkɅ `L[H, ݍ ݩɍh@H, ݍ ݩnɍHH ɀ)iȌ8Ȑ)hhh@ JJ) ʆ4X )%ͤFϮ $029B:CMP $029C:BEQ BUFUP:BCS BUFL1Y zLDA #$FF:SEC:SBC $029C:CLC:ADC $029B:SEC:BCS BUFL2q {BUFL1 SEC:SBC $029C BUFL2 CMP #200:BCC BUFL3 LDA $DD01: #$FD:STA $DD01:SEC:BCS TERMK BUFL3 CMP #20:BCS TERMK BUFUP LDA $DD01:A #$02:STA $DD0ѠɎnɎoˍܽqˍ܊ I3܎ - ݎݩAܩ ܍ܩݩ, ݍ ݩ ܩݍ 8ȰXhhh`%B@HHHhhh`x)ݩ ݍ ܩAܩ ܩ1GXhhh`900:P$"OK"208 ".";:5,"ATE0V1X1Q0F3";CR$ A$"X"A$""5:) A$"N"A$""LPWFPGPG15:3100R A$"P"A$""PG15PGPG15:3100u A((A$,1,1)):A48A573100 A(A$):APG APG15 AWF3100 WIA:"TTEMPT TO CONNECT TO: ";WF$(A) "NTER II ASSWORD: ";:5000:PA$P$-H ML12:5,CR$;"ATW";QU$;WF$(WI);",";PA$;QU$;CR$;:900[R P$"ERROR""ONNECT AIL!";CO$:3100mW P$"OK"3420~X P$""3400Y 900:3410\ "ONNECT SUCCESS!";CO$f g TTTI300h TITT3432p CD0:1050:CD0-----------------------------8 THE MAIN LOOP !^ -------------------------------CT0:C10:CD0:LC$"1":1010:WI$P$:1010:P$WI$10301000ML12:5,CR$;"ATI3";CR$;900:P$LC$1025CTCT1:CT3CUCCESS!";CO$8ML12:ML12::"CANNING FOR II HOTSPOTS...";N5,CR$;"ATW";CR$;}900:I1:P$""P$"OK""ONE!":::3000I(P$)2100(P$,I,1)" "P$(P$,I1):2100II1:20304WF$(WF1)P$:WFWF1:2020 PG& "Y O IMMERMAN (BO@ZIMMERS.NET)"::M --------------------------------t GET STARTED ! ------------------------------- PH0:PT0:MVML18:CR$(13)(10):QU$(34):MV14,5 "NITIALIZING MODEM...";:CT0LLzLLsL=Lxaȍȍɍ> ɍ!$ȍ%*ȍ+ύ&ȍ'X`HH s J  zhh`LWL>HHȍ ʐ hh`hh`HhLh =ʩ`H s h`   + ݎݩAܩ ܍ܩݩ, ݍ ݩ ܩݍ 8ȰXhhh`%B@HHHhhh`x)ݩ ݍ ܩAܩ ܩ1GXhhh`900:P$"OK"208 ".";:5,"ATE0V1X1Q0F3";CR$ML12:5,CR$;"ATC";QU$;"132.148.138.66:80";QU$;CR$;A8900:CD1:P$""1080=(P$,8)" "CD0:CTCT1:CT1070,1070,1070,1070,1200BI13:ML12:5,CR$;"ATH0";CR$;:900:IL5,CR$;"ATH0";CR$;:900CD0"AIL!";CO$CD1"UCCESS!";CO$ +@ML12:ML12::"CANNING FOR II HOTSPOTS...";V5,CR$;"ATW";CR$;900:I1:P$""P$"""ONE!":::3000I(P$)2100(P$,I,1)" "P$(P$,I1):2100II1:20304WF$(WF1)P$:WFWF1:202& "Y O IMMERMAN (BO@ZIMMERS.NET)"::M --------------------------------t GET STARTED ! ------------------------------- PH0:PT0:MVML18:CR$(13)(10):QU$(34):MV14,5 "NITIALIZING MODEM...";:CT0 --------------------------------@ THE MAIN LOOP !f -------------------------------CT0:C10:CD0:LC$"1":1010:WI$P$:1010:P$WI$10301000ML12:5,CR$;"ATI3";CR$;900:P$LC$1025CTCT1:CT3# CONFIGURE64/128 1200B 2.0+C UPDATED 04/26/2018 02:54Pd254,(186):(254)8254,8 +SY(65532):SY6158,254: SY34X23777:X,170:(X)170"<16K":5,2,0,(8):(65532)3456,87:54,87:52,87 WF$(100):WF0:P$"LLzLLsL=Lxaȍȍɍ> ɍ!$ȍ%*ȍ+ύ&ȍ'X`HH s J  zhh`LWL>HHȍ ʐ hh`hh`HhLh =ʩ`H s h`   0.D TELNETML.BIND#;;TERMINAL ML 64 BY BO ZIMMERMANd$;;UPDATED 20170613 07:44Pd;;TERMSETUP LDA $DD01: #$10:STA DCDCHKiTERMINAL LDX #$05:JSR $FFC6nJSR $FFE4:CMP #$00:BEQ TERMKsCMP #$0A:BEQ TERMKxJSR $FFD2" yLDAѰυ $I  L꬜̛ȌH8ΐ  ݍh`Hɀ) gʽiJx ܩj  Xh`X$0LX8`x1GX ݩݍݍ, ݢݍ )jHHH hhh`x1 TERMK LDX #$00:JSR $FFC6> JSR $FFE4:CMP #$00:BEQ TERMINALk @@ -240,31 +247,40 @@ I3 TERMOUT PHA:LDX #$05:JSR $FFC9:PLA JSR $FFD2:LDX #$00:JSR $FFC9 SEC:BCS TERMINAL:RTS:53280,254:53281v = -9 ͜& 8m88Ȑ )8  ݢ Ʌ `H h 8HZ1 1,UN,15:8,UN,8,"TELNETPHONEBOOK,S,R" 1,E:E08:1:300 8,HZ:I1HZ:8,HO$(I1):8,PO(I1): "8:1 , 1000" CT10 ,:: 1000:  --------------------------------a  GET E$ FROM MODEM, OR ERROR !  ------------------------------- E$"":P$"" ML E$""P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;""  --- 5,CR$;"ATC";QU$;"98.138.253.109:80";QU$;CR$;98900:CD1:P$"OK"1080|=(P$,8)"CONNECT "CD0:CTCT1:CT1070,1070,1070,1070,1200BI13:ML12:5,CR$;"ATH0";CR$;:900:IL5,CR$;"ATH0";CR$;:900CD0"AIL!";CO$CD1"10:1010"LC$P$:CT0:C1C11:C115P$"":,10102WWI$P$:"URRENT II : ";WI$mCD0:WI$""2000 1050:CD020004000:"ESTING CONNECTION...";:TTTI200TITT1055$CT0:#5,A$:A$""1060.ML12: -3100 z "AVING NEW OPTIONS..."" ML12:ML12:ML12< 5,CR$;"ATZ0&WE0";CR$T 900:P$"OK"3470r P$"":ML12:P$""3490 ML12:5:"HANGE II CONNECTION (Y/)? ";A$:A$"Y"A$"""":2000A$"N"A$""A$//";:5000:HO$P$:3006X2"NTER SERNAME: ";:5000:UN$P$:300fX3"NTER ASSWORD: ";:5000:PA$P$:300X4"NTER OUTPUT DEVICE/UNIT: ";:5000:UN(P$):300 300V --- TRANSMIT P$ TO THE OPEN SOCKET !XOP$P$:ML9:C8$ ";P$:4120h" ) DD NEW":" ) UIT TO "Ar:"NTER YOUR CHOICE: ";:5000:Q$P$g|QQ$(Q$,1):QQ$"Q"QQ$""5:QQ(Q$):(QQ0QQPB)QQ$"A"QQ$"":4100QQ04300")DIT OR )ELETE? ";A$:A$"E"A$"" 00:XB1200@ CR$(13):(14);:SY(65532):53280,254:53281,246 CO$"":SY226ML49152:665,73((678)30):UMML2048:XB9600 #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: &SY3436879,27:CO$(31) -(SY226(ML1)2095: DIGITS.":4100"ARGET HOST: ";:5000:B2$P$.(B2$)4"RONG.":4100S"ARGET PORT: ";:5000:B2(P$)nB20"RONG.":4100B3$"":"O TRANSLATION (Y/N)? ";4980:A1B3$B3$"P""O TRANSLATION (Y/N)? "3:X(789):UM9:X234XB1200D ZSY3456579,0: WHY DOES THIS WORK dMVML18:MV14,8:DD56577:SY34DD37136: FIX BUFAP e f n xCO$;" V1.7":"EQUIRES 64ET II FIRMWARE 2.0+" "Y O IMMERMAN (BO@ZIMMERS.NET)":A$"Y"A$""A$"N"A$""4980*~A0:AA$"":A$"Y"A$""AA$"":A15AA$:?P$""L" ";`A$:A$""5010vA$(13)" ":A$(20)A$;" ";:P$P$A$:5010P$""5010P$(P$,(P$)1):"  ";:501"OK"208: 5,"ATE0V1X1F3Q0S40=248S0=1S41=0I4";CR$;(19);:L9248r 900:VR(P$):VR2.0"IMODEM INIT FAILED: ";P$: 900:P$"OK"203 #5,A$:A$""245 5,"ATI2";CR$;:900:IP$P$:P$"OK"(P$)8245 P$"":I1(IP$)+IFMI"XERR:";P$:P$OP$:650' --- GET P$ FROM SOCKET P5 P$"":E0o*930:P0PP00"NEXPECTED PACKET ID: ";P0;"/";P:4P00E1:: FAIL>RPC0\PCPC1:800:P$""E1PC60860f(P$)4(P$,4,1)"-"pEC$(P$,3)TP://";HO$B " 2) SERNAME : ";UN$8C " 3) ASSWORD : ";PA$TD " 4) ISK EVICE:";UN]ILH4wrHO$"":P$"1":400|:"YPE A NUMBER OR ΠTO CONNECT:";5000:P$""1000X(P$):X1XLH300X1"NTER : FTP:(13)4010"":"URRENT 'HONE' BOOK:":PB015,CR$;"ATP";CR$?TTTI100Z"900:P$""TITT4200j,P$""41306I1:PP$P$:P$"OK"P$""4200@(PP$,I,1)" "II1:4160JPP$(PP$,I1)^PB$(PB)PP$:PBPB1:PB;") FTP64/128 1200B 2.0+= UPDATED 08/19/2017 02:39A^254,8:(186)7254,(186) -SY(65532):SY6158,254: SY34X23777:X,170:(X)170"<16K":5,2,0,(8):(65532)3456,87:54,87:52,87 P$"OK":186,(254):BA12"":B1$PB$(QQ1):4310A$"D"A$""4260""R5,"ATP";(34);PB$(QQ1);"=DELETE";(34):900:4100v"HONE NUMBER: ";:5000:B1$P$(B1$)3"RONG.":4100I1(B1$):B1((B1$,I,1)):B148B157B1$""I:B1$"""AD"PML64.BIN",(254),1:K +9 ͜& 8m88Ȑ )8  ݢ Ʌ `H h 8HZ1 1,UN,15:8,UN,8,"TELNETPHONEBOOK,S,R" 1,E:E08:1:300 8,HZ:I1HZ:8,HO$(I1):8,PO(I1): "8:1 , 10004.BIN",(254),1:C +2SY61ML4864:981,15:P(215)128:P12830643w +<SY61(ML1)2175:"PML128.BIN",(254),1: +FSY61CO$(159) +d +nP$"A" +xCO$;" V1.6":"EQUIRES 64ET II IRMWARE 2.0+" +"1200 BAUD VERSION" O 1200B?" CT10 ,:: 1000B  --------------------------------i  GET E$ FROM MODEM, OR ERROR !  ------------------------------- E$"":P$"" ML E$""P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;""  #5,A$:A$""2030 5,CR$;"ATHZ0F0R0E0&P1";CR$;V 900:P$""CTCT1:CT10203t CT10#5,A$:A$""207 CT105,"ATHF3E0&P1";CR$; CT10900:P$""CTCT1:207 CT10"NITIALIZATION FAILED.":"S YOUR MODEM SET T +0 PG1 LPWF:LPPG15LPPG155& IPGLP:(I)") ";WF$(I):IX0 :"NTER TO UIT TO ."|: LPWF"NTER FOR EXT AGE"D PG15"NTER FOR REV AGE"N "NTER A NUMBER TO ONNECT TO THAT " "? ";:5000:A$P$:A$"TP://";:5000:HO$P$:3009X2"NTER SERNAME: ";:5000:UN$P$:300iX3"NTER ASSWORD: ";:5000:PA$P$:300X4"NTER OUTPUT DEVICE/UNIT: ";:5000:UN(P$):300 300V --- TRANSMIT P$ TO THE OPEN SOCKET !XOP$P$:ML9:C8 RD: ";:5000:PA$P$5H ML12:5,CR$;"ATW";QU$;WF$(WI);",";PA$;QU$;CR$;:900cR P$"""ONNECT AIL!";CO$:3100uW P$""3420X P$""3400Y 900:3410\ "ONNECT SUCCESS!";CO$f g TTTI300h TITT3432p CD0:105 00:XB1200@ CR$(13):(14);:SY(65532):53280,254:53281,246 CO$"":SY226ML49152:665,73((678)30):UMML2048:XB9600 #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: &SY3436879,27:CO$(31) +(SY226(ML1)2095:A$"N"A$""A$(13)4010"":0"URRENT 'HONE' BOOK:":PB0E5,CR$;"ATP";CR$STTTI100n"900:P$""TITT4200~,P$""41306I1:PP$P$:P$"OK"P$""4200@(PP$,I,1)" "II1:4160JPP$(PP$,I1)^PB$(PB3:X(789):UM9:X234XB1200D ZSY3456579,0: WHY DOES THIS WORK dMVML18:MV14,8:DD56577:SY34DD37136: FIX BUFAP e f n xCO$;" V1.8":"EQUIRES 64ET II FIRMWARE 2.0+" "Y O IMMERMAN (BO@ZIMMERS.NET)":A$:A$"E"A$"""":B1$PB$(QQ1):4310%A$"D"A$""4260.""f5,"ATP";(34);PB$(QQ1);"=DELETE";(34):900:4100"HONE NUMBER: ";:5000:B1$P$(B1$)3"RONG.":4100I1(B1$):B1((B1$,I,1)):B148B157B1$""P$"OK"208= 5,"ATE0V1X1F3Q0S40=248S0=1S41=0I4";CR$;(19);:L9248u 900:VR(P$):VR2.0"IMODEM INIT FAILED: ";P$: 900:P$"OK"203 #5,A$:A$""245 5,"ATI2";CR$;:900:IP$P$:P$"OK"(P$)8245 P$"":I1(IP$).I$"XERR:";P$:P$OP$:650 * --- GET P$ FROM SOCKET P8 P$"":E0r*930:P0PP00"NEXPECTED PACKET ID: ";P0;"/";P:4P00E1:: FAIL>RPC0\PCPC1:800:P$""E1PC60860f(P$)4(P$,4,1)"-"pEC$(P$: FTP://";HO$B " 2) SERNAME : ";UN$;C " 3) ASSWORD : ";PA$WD " 4) ISK EVICE:";UN`ILH4zrHO$"":P$"1":400|:"YPE A NUMBER OR ΠTO CONNECT:";5000:P$""1000X(P$):X1XLH300 X1"NTER : F"3100 A$"X"A$""5:1 A$"N"A$""LPWFPGPG15:3100Z A$"P"A$""PG15PGPG15:3100} A((A$,1,1)):A48A573100 A(A$):APG APG15 AWF3100 WIA:"TTEMPT TO CONNECT TO: ";WF$(A) "NTER II ASSWO FTP64/128 1200B 2.0+= UPDATED 09/02/2021 02:39A^254,8:(186)7254,(186) +SY(65532):SY6158,254: SY34X23777:X,170:(X)170"<16K":5,2,0,(8):(65532)3456,87:54,87:52,87 P$"OK":186,(254):BA120:CD03100z "AVING NEW OPTIONS..."* ML12:ML12:ML12G 5,CR$;"ATZ0&WE0&P1";CR$h 900:P$""P$"OK"3470 P$"":ML12:P$""3490 ML12:5:"HANGE II CONNECTION (Y/)? ";A$:A$"Y"A$"""":2000"PML64.BIN",(254),1:K -SY226UM0(UM1)245:"UP9600.BIN",(254),1: 2S80:SY61ML4864:981,15:S8(215)128:S812830643 <SY61(ML1)2175:"PML128.BIN",(254),1: -FSY61S8128XB2400:CO$(159) PSY226UM0UM:UM;4980:A1B3$B3$"T"&"O TERMINAL CHO (Y/N)? ";404980:A1B3$B3$"E"]:"O / LOW ONTROL (Y/N)? ";xD4980:A1B3$B3$"X"NB4$((B2),2)X5,"ATP";B3$;(34);B1$;"=";B2$;":";B4$;(34):900b4100tA$:: ---- ZIMODEM SETUP8 UN(254):IP$"":CR$(13)(10)O PH0:PT0:MVML18m "NITIALIZING MODEM..." #5,A$:A$""203 5,CR$;"ATHZ0F0E0";CR$;:900:P$"OK"203 #5,A$:A$""208 5,CR$;"ATE0N0R0V1Q0";CR$; 900:P$0OÐPß5,2,0,(8)Zá#5,A$:A$""A$;dáA$:A$""5,A$;*nÉ 50010aF$"CONFIGURE":1,8,15,"S0:"F$:1:(F$),8:(F$),8TT4200,P$""41306I1:PP$P$:P$"OK"P$""4200@(PP$,I,1)" "II1:4160JPP$(PP$,I1)  D$(IP$,I,1)="."THENIP$=LEFT$(IP$,I-1)+","+MID$(IP$,I+1)N I:"OUR IP ADDRESS IS: ";IP$*HO$"FTP.ZIMMERS.NET":PO21:UN$"ANONYMOUS":PA$"MY@EMAIL.COM"+HO$="192.168.1.112":PO=21, GET INFO6:"EQUEST ARMS:"A " 1) RL : F(((MV8)),2):E$"OK":VR3E$C8$6b5,"ATS42=";C8$;"TP+";QU$;P$;QU$elML:P$E$"XERR:";CO$;P$:P$OP$:600kvOP$P$:ML9:C8$(((MV8)),2):PN$(((P$)),2)5,"ATS42=";C8$;"T+";PN$:5,P$:E$"OK":VR3E$C8$ML:P$E$ -:EC(EC$)qPC0r800:P$""E1PC60882/sP$""E1870Jt(P$,3)EC$P$:881au(P$,4,1)"-"881gv --- GET E$ FROM MODEM, OR ERRORE$""MLE$""P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;""$=PL06100$BY00:Y1PL0Y$P$:6100%GLP$(P$,4):LP$"550 "P$:62509%LLP$"226 "Y1:Y$P$:6100h%QLP$"150 "("",(TT))P$::6400:6100%VP0SPPL08,P$;:TTTTPL:6500:Y00:6100%[P0P6100%`Y0Y01:Y0Y0Y1TT CO$:5,"ATL":945"XPECTED ";E$;", GOT ";A$:1 --- THE MAIN LOOPHQU$(34): BEGIN!{AT$"":XB0BA0XBBAAT$"S43="((XB),2)#5,A$:A$""1020L5,"ATH"AT$"&D10&M13&M10CP";QU$;HO$;":";((PO),2);QU$Q900:SP0:P$" ,4)"LDIR"8200 (P$,4)"GET "F$(P$,5):6000:20009(P$,4)"PUT "F$(P$,5):7000:2000OP$"?"P$"HELP"y P$"HELP""GET PUT LS CD DIR DEL"!P$"HELP""LCD LDIR LDEL QUIT""P$"HELP""SE ,S AND ,P IN GET/PUT FILEN01205UM:UM3:(789)234UM9:12105BAXB:UM19,1:5,"AT":9000:9000HXB24001210bNP0:(2614)0NP20BAXB:2576,10:2578,(59490NP):2579,(59491NP)2582,170:2583,1:NP02582,1545,"AT":9000:9000850UB850:IFP$=""THEN4020 #5,A$:ST0A$""900:4025 P$:P$"":600K GOSUB900:IFLEFT$(P$,5)="RING "THEN4040w Y0:Y$"":Y10:Y20:Y35:BA4800Y310 GET#5,A$:IFST=0ANDA$<>""THENGOSUB900:GOTO4100 A$:A$" "A$"":"BORTED.P$""1270P$:(P$,3)"230"2000 -UN$"":PA$"":12305#5,A$:A$""1300LP$"":600:A0lAA1:850:P$""A601305|P$""1300P$::(P$,3)"227"1300(A((P$,1)):A48A57P$(P$,(P$)1):13202X0(P$)Y2Y21:Y0(Y12Y2Y3)4100!Y$"#5,A$:A$""ST04244+"800:P$""P$:4245W"5,"ATH"((SP),2):900:P$"OK"4250]"g"P$""t"" ";"A$:A$""5010"A$(13)" ":"A$(20)A$;" ";:P$P$A$:501080nI1(P$):(P$,I,1)","P$(P$,I1)"."(P$,I1)+s:H0$P$H2$:R00Cx#5,P$:P$""1400d}5,"AT";CC$;QU$;H0$;QU$:900(P$)8(P$,8)"CONNECT "SP((P$,9)): 1420R011300R0R01:"RROR: ";P$:"ETRY TO CONNECT#P$" "F$:600$Y0:Y$"":TB0:TT0:Y00:Y110#$#5,A$:ST0A$""900:6100,$800G$FX0P0SPP106200h$1,UN,15:8,UN,8,"@0:"F$X$$$1,E:E08:1:"AILED TO OPEN "F$"";CO$:6250$.FX1$8PL(P$):P0P6230  ---- LOW LVL PACKET READ%PR0:#5,P$:P$""93035,(17);^ML6:P0(MV2):P1(MV4):P2(MV6)PL(MV0):CR(MV1):C8(MV8)P00P2C8985P10P$""P00CR0#5,P$:P$""985"-";P$"LS"(P$,3)"LS "4000:2000 P$"DIR"(P$,4)"DIR "4000:2000LP$"EXIT"P$"QUIT"5,"ATZ":900:5:r(P$,4)"DEL "P$"DELE"(P$,4)(P$,3)"CD "P$" "(P$,4)(P$,4)"LCD "8000(P$,5)"LDEL "8100 (P$OK"1020V(P$)8(P$,8)"CONNECT "P((P$,9)):1200Z["NABLE TO CONNECT TO ";HO$;" PORT";PO;"";CO$:300g`ITTI40jP90:800:P$""ITTI10tTIIT1130"ONNECTED TO ";HO$;" ON CHANNEL";P;", TANDBY...";CO$XB960AMES!" #P$"HELP""ELOW ARE SERVER COMMANDS:";CO$$4600:850:P$:2000* PP$" ":600:850:P$""4000zP$:CC$"&M10&D10&M13CP":1300:E1P$"":600:850:P$""4010PRINTP$:P$=" "+IP$+",198,76":GOSUB600:GOS:P$""1240P$(UN$"""SERNAME: ";:5000:UN$P$:P$""1230QP$" "UN$:600:850:P$""1240P$:(P$,3)"331""SERNAME REJECTED?!";CO$:5:PA$"""ASSWORD: ";:5000:PA$P$:P$""1260P$" "PA$:600:850:":4250 6800 hP0PY1P$""Y$P$:4100!rP0PP$""4100=!|P0P(P$,4)"226 "Y1:Y$P$:4100m!P0P(P$,4)"150 "4100:PRINTP$:GOTO4100!P0SPP$""4235!Y20:Y1Y11:S80(P$,34):4100!P$:4100!P0P4100!<A((P$,X0,1)):A47A58X0X01:13404FX2((P$,X01)):P$(P$,X01):X0(P$)aPA((P$,X0,1)):A47A58X0X01:1360ZX1((P$,X01)):P$(P$,X01):H2$":"(((X1256)X2),2)_P$(P$,5)dA((P$,1)):A48A57P$(P$,2):13 "P$""5010#P$(P$,(P$)1):"  ";:50106#pFX0:X$",P,W":P$" ":600:850:P$""6000S#uP$:CC$"C":1300:E1c#zFX$(F$,2)#(F$,1)" "F$(F$,2):6015#FX$",P"FX$",S"F$(F$,(F$)2):X$FX$",W"#F$"" TO ";H0$;"";CO$:1400#5,A$:A$""142025,"ATC";((P),2):900:P$"OK"E0:f"ETRY TO CHANGE BACK TO ";H0$;"";CO$:1420"OMMAND (?): ";:5000((DD)16)0"OST CONNECTION";CO$:P$"QUIT"P$""800:P$:2000 -TB6100%e#5,A$:ST0A$""900:6245#&gY$: "";((TT),2);" BYTES TRANSFERRED.";CO$U&j8:1:5,"ATH"((SP),2):900:P$"OK"6250p&t800:P$""P$:6260v&&I(P$)1:I00:TB0& -A$(P$,I,1):A$"("6450&A$" "I0I& Uȅ` AȰ UȰ Rȅ` ȥHJJJJ 'Ȫh) 'H hLiii:`Hh`Y |Ȱ gȐ gȐ`L` tY ` +FSY61S8128XB2400:CO$(159) PSY226UM0UM:UM)PP$:PBPB1:PB;") ";P$:4120+h" ) DD NEW":" ) UIT TO "Ur:"NTER YOUR CHOICE: ";:5000:Q$P${|QQ$(Q$,1):QQ$"Q"QQ$""5:QQ(Q$):(QQ0QQPB)QQ$"A"QQ$"":4100QQ04300")DIT OR )ELETE? "; : ---- ZIMODEM SETUP8 UN(254):IP$"":CR$(13)(10)O PH0:PT0:MVML18m "NITIALIZING MODEM..." #5,A$:A$""203 5,CR$;"ATHZ0&P0F0E0";CR$;:900:P$"OK"203 #5,A$:A$""208 5,CR$;"ATE0N0R0V1Q0";CR$; 900:I:B1$"""AD DIGITS.":4100$"ARGET HOST: ";:5000:B2$P$B(B2$)4"RONG.":4100g"ARGET PORT: ";:5000:B2(P$)B20"RONG.":4100B3$"":"O TRANSLATION (Y/N)? ";4980:A1B3$B3$"P""O  FMID$(IP$,I,1)="."THENIP$=LEFT$(IP$,I-1)+","+MID$(IP$,I+1)Q I:"OUR IP ADDRESS IS: ";IP$*HO$"FTP.ZIMMERS.NET":PO21:UN$"ANONYMOUS":PA$"MY@EMAIL.COM"+HO$="192.168.1.112":PO=21, GET INFO6:"EQUEST ARMS:"A " 1) RL $(((MV8)),2):E$"OK":VR3E$C8$9b5,"ATS42=";C8$;"TP+";QU$;P$;QU$hlML:P$E$"XERR:";CO$;P$:P$OP$:600nvOP$P$:ML9:C8$(((MV8)),2):PN$(((P$)),2)5,"ATS42=";C8$;"T+";PN$:5,P$:E$"OK":VR3E$C8$ML:P$E +,3):EC(EC$)qPC0r800:P$""E1PC608822sP$""E1870Mt(P$,3)EC$P$:881du(P$,4,1)"-"881jv --- GET E$ FROM MODEM, OR ERRORE$""MLE$""P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;""";CO$:6250$.FX1$8PL(P$):P0P6230%=PL06100!%BY00:Y1PL0Y$P$:6100G%GLP$(P$,4):LP$"550 "P$:6250g%LLP$"226 "Y1:Y$P$:6100%QLP$"150 "("",(TT))P$::6400:6100%VP0SPPL08,P$;:TTTTPL:6500:Y00:6 ";CO$:5,"ATL":945"XPECTED ";E$;", GOT ";A$:4 --- THE MAIN LOOPKQU$(34): BEGIN!~AT$"":XB0BA0XBBAAT$"S43="((XB),2)#5,A$:A$""1020L5,"ATH"AT$"&D10&M13&M10CP";QU$;HO$;":";((PO),2);QU$:E0Q900:SP (P$,5)"LDEL "8100 (P$,4)"LDIR"82002 (P$,4)"GET "F$(P$,5):6000:2000](P$,4)"PUT "F$(P$,5):7000:2000sP$"?"P$"HELP" P$"HELP""GET PUT LS CD DIR DEL"!P$"HELP""LCD LDIR LDEL QUIT""P$"HELP";P;", TANDBY...";CO$ XB960012052UM:UM3:(789)234UM9:1210YBAXB:UM19,1:5,"AT":9000:9000lXB24001210NP0:(2614)0NP20BAXB:2576,10:2578,(59490NP):2579,(59491NP)2582,170:2583,1:NP02582,$=" "+IP$+",198,76":GOSUB600:GOSUB850:IFP$=""THEN4020, #5,A$:ST0A$""900:4025C P$:P$"":600o GOSUB900:IFLEFT$(P$,5)="RING "THEN4040 Y0:Y$"":Y10:Y20:Y35:BA4800Y310 GET#5,A$:IFST=0ANDA$<>""THENGOSUB900:GOTO4100"1260 P$" "PA$:600:850:P$""1270)P$:(P$,3)"230"2000A +UN$"":PA$"":1230Y#5,A$:A$""1300pP$"":600:A0AA1:850:P$""A601305P$""1300P$::(P$,3)"227"1300(A((P$,1)):A48A57(P$,34):4100!P$:4100!P0P4100"Y2Y21:Y0(Y12Y2Y3)4100!"Y$>"#5,A$:A$""ST04244Y"800:P$""P$:4245"5,"ATH"((SP),2):900:P$"OK"4250""P$"""" ";"A$:A$""5010"A$(13)" "((P$,1)):A48A57P$(P$,2):13809nI1(P$):(P$,I,1)","P$(P$,I1)"."(P$,I1)Os:H0$P$H2$:R00gx#5,P$:P$""1400}5,"AT";CC$;QU$;H0$;QU$:900(P$)8(P$,8)"CONNECT "SP((P$,9)): 1420R011300R0R01S"F$(F$,(F$)2):X$FX$",W"#F$"" $P$" "F$:600.$Y0:Y$"":TB0:TT0:Y00:Y110Q$#5,A$:ST0A$""900:6100Z$800u$FX0P0SPP106200$1,UN,15:8,UN,8,"@0:"F$X$$$1,E:E08:1:"AILED TO OPEN "F$"  ---- LOW LVL PACKET READ(PR0:#5,P$:P$""93065,(17);aML6:P0(MV2):P1(MV4):P2(MV6)PL(MV0):CR(MV1):C8(MV8)P00P2C8985P10P$""P00CR0#5,P$:P$""985"-UIT"P$""800:P$:2000P$"LS"(P$,3)"LS "4000:2000DP$"DIR"(P$,4)"DIR "4000:2000pP$"EXIT"P$"QUIT"5,"ATZ":900:5:(P$,4)"DEL "P$"DELE"(P$,4)(P$,3)"CD "P$" "(P$,4)(P$,4)"LCD "80000:P$"OK"1020&V(P$)8(P$,8)"CONNECT "P((P$,9)):1200CX(P$)0E3EE1:1105~["NABLE TO CONNECT TO ";HO$;" PORT";PO;"";CO$:300`ITTI40jP90:800:P$""ITTI10tTIIT1130"ONNECTED TO ";HO$;" ON CHANNEL""SE ,S AND ,P IN GET/PUT FILENAMES!"0#P$"HELP""ELOW ARE SERVER COMMANDS:";CO$H4600:850:P$:2000N tP$" ":600:850:P$""4000P$:CC$"&M10&D10&M13CP":1300:E1P$"":600:850:P$""4010 PRINTP$:P1545,"AT":9000:9000850:P$""1240P$LUN$"""SERNAME: ";:5000:UN$P$:P$""1230uP$" "UN$:600:850:P$""1240P$:(P$,3)"331""SERNAME REJECTED?!";CO$:5:PA$"""ASSWORD: ";:5000:PA$P$:P$" A$:A$" "A$"":"BORTED.":4250!6800#!hP0PY1P$""Y$P$:4100B!rP0PP$""Y14100,4240k!|P0P(P$,4)"226 "Y1:Y$P$:4100!P0P(P$,4)"150 "4100:PRINTP$:GOTO4100!P0SPP$""4235!Y20:Y1Y11:S80P$(P$,(P$)1):13202X0(P$),<A((P$,X0,1)):A47A58X0X01:1340XFX2((P$,X01)):P$(P$,X01):X0(P$)PA((P$,X0,1)):A47A58X0X01:1360ZX1((P$,X01)):P$(P$,X01):H2$":"(((X1256)X2),2)_P$(P$,5)dA :"A$(20)A$;" ";:P$P$A$:5010#P$""5010/#P$(P$,(P$)1):"  ";:5010d#pFX0:X$",P,W":P$" ":600:850:P$""6000#uP$:CC$"C":1300:E1#zFX$(F$,2)#(F$,1)" "F$(F$,2):6015#FX$",P"FX$", :"RROR: ";P$:"ETRY TO CONNECT TO ";H0$;"";CO$:1400+#5,A$:A$""1420V5,"ATC";((P),2):900:P$"OK"E0:"ETRY TO CHANGE BACK TO ";H0$;"";CO$:1420"OMMAND (?): ";:5000((DD)16)0"OST CONNECTION";CO$:P$"Q +100%[P0P6100%`Y0Y01:Y0Y0Y1TTTB6100&e#5,A$:ST0A$""900:6245Q&gY$: "";((TT),2);" BYTES TRANSFERRED.";CO$&j8:1:5,"ATH"((SP),2):900:P$"OK"6250&t800:P$""P$:6260&&I(P$)1:I00:TB0& +A Uȅ` AȰ UȰ Rȅ` ȥHJJJJ 'Ȫh) 'H hLiii:`Hh`Y |Ȱ gȐ gȐ`L` tY ` -Y Y8`:)(i` LeÍ` 7 ǥ"О  Lu Lh FX$",P"FX$",S"F$(F$,(F$)2):X$FX$",R"'qF$""(s1,UN,15:8,UN,8,F$X$O(t1,E:E08:1:"AILED TO OPEN "F$"";CO$:n(uP$::CC$"C":1300:E1(vP$" "F$:600(5,"ATC";((SP),2):900:P$"OK"7040(Y0:Y$" S`W 1Ȑ  Y /ɬVn See Y Q VFW  H *` V ` dȍK |K /L`Lh t ' ȝe Y oȐ֝e gȐ J Ţe -J h / | $::7100)>P0SP"?!":7100)H7100 *R#5,A$:ST0A$""900:7250:*\ "";((TT),2);" BYTES TRANSFERRED.";CO$R*a#5,A$:A$""7265*f8:1:5,"ATH"((SP),2):900:P$"OK"7265*hTTTI200*k800:P$""TITT7275*pP$:800U.T*i? LH ǩ à,L1ũ `  Ĥ>å ͽ  &   ݩ L= BLhHwHHHHl`KH hILr ĮV  L Ţ.:  H ȩ ȩ H H) h:E,E$,E1,E2:1:2000+ P$(P$,6):(P$)0P$":"P$, 8,UN,0,"$"P$, #8,A$,A$6,& #8,A$,A$:ST0X(0):8::2000d,0 #8,A$,B$:X(A$(0))256(B$(0)):X;,: #8,A$:A$""(13);:8230,D B$:B$" "8::2000,N A$;:8250,(#TT1ȬM/Uɝ o*S8`ʊMVAw o© x} Ŏyz Ŏ{|Lh eŎJKe hhVLL`KJ`0G`8`N`HJJJJ 'Ȫh)L'ȩ +Y Y8`:)(i` LeÍ` 7 ǥ"О  Lu Lh (F$,2)'g(F$,1)" "F$(F$,2):7015(lFX$",P"FX$",S"F$(F$,(F$)2):X$FX$",R"+(qF$""F(s1,UN,15:8,UN,8,F$X$}(t1,E:E08:1:"AILED TO OPEN "F$"";CO$:(uP$::CC$"C":1300:E1(vP$" "F$:600(5,"AT S`W 1Ȑ  Y /ɬVn See Y Q VFW  H *` V ` dȍK |K /L`Lh t ' ȝe Y oȐ֝e gȐ J Ţe +J h / | )"150 "("",(TT))P$::7100)4P0PP$::7100 +*>P0SP"?!":7100*H71007*R#5,A$:ST0A$""900:7250h*\ "";((TT),2);" BYTES TRANSFERRED.";CO$*a#5,A$:A$""7265*f8:1:5,"ATH"((SP),2):900:P$"OK"7265*hTTU.T*i? LH ǩ à,L1ũ `  Ĥ>å ͽ  &   ݩ L= BLhHwHHHHl`KH hILr ĮV  L Ţ.:  H ȩ ȩ H H) hP$(P$,6)+1,UN,15,"S0:"P$:1,E,E$,E1,E2:E,E$,E1,E2:1:2000, P$(P$,6):(P$)0P$":"P$0, 8,UN,0,"$"P$>, #8,A$,A$d,& #8,A$,A$:ST0X(0):8::2000,0 #8,A$,B$:X(A$(0))256(B$(0)):X;,: #8,A$:A$""(13);:8230,D1ȬM/Uɝ o*S8`ʊMVAw o© x} Ŏyz Ŏ{|Lh eŎJKe hhVLL`KJ`0G`8`N`HJJJJ 'Ȫh)L'ȩ ,  `?h)>@Ahi=hi<H hAh@h?Q  g) ٩IS LhIL )i L` Lh / / H S0 S LhL` Lh Ţ.$   H Hɢ0T.Ui `UT Hɥ ʥ HɊ)   i@ (ʩL܎ܭ) ܩߢōDCBxDC ȭ<H=H>H?@A@L` AȍZ[\] Rȍ\]Lh Ǎbc RȍOP RȍQR - WN 1ȮV N ª LhM3M8OȱPQȭRmbȱmc /Ɉ0 Aȅ RȍTU II1:I06410&(&2I00'<((P$,I01),4)"BYTE"&'FTB((P$,I1,I0I)):Y1TB10:U'd" "(TT):'XFX0:X$",P,R":P$" ":600:850:P$""7000'bFX$(F$,2)'g(F$,1)" "F$(F$,2):7015'lxL `  ߢōɑ `a ȩu `ΎHdX=< ŢB*LMɩ? , ũ. NVd . $ݐI -υυlL` V`N ¢ H`HShS`TUL8S":TT0(#5,A$:ST0A$""900:7100)ML12:X(MV):XE(MV1))(P$)07100=)TTTT(P$):6500:650:XE07250F)800h) P0P(P$,3)"550"P$:7250})*P0PP$""7100)/P0P(P$,4)"150 "("",(TT))P$::7100)4P0PP , o Њ =  H H h ¢MX eˆX Ž ` {ªȘ eŠJ ȮJ`M z…`8e`J J") JΰJJJJ)΍X)M) JJJ Ȉ` e¢ MȐ`΍T6ύU:P$""7280*z5,"ATC";((P),2):900:P$"OK"7290*+@P$(P$,5):(P$)8(P$)168020L+JUN(P$):"URRENT DRIVE IS NOW";UN:2000+T1,UN,15,"CD "P$:1,E,E$,E1,E2:E,E$,E1,E2:1:2000+P$(P$,6)+1,UN,15,"S0:"P$:1,E,E$,E1,E2. /ɈL ǩ é:wL= Dž  L Aȅf O0O8?Jnfne ": Ű |Ȥ0eeَTVKV ®XU6 eŽ eŢMX0 bňX eŽ e b bŭTK TI100,2#ML12:TITT9010,<#,Pß5,2,0,(8)-Zá#5,A$:A$""A$;-dáA$:A$""5,A$;$-nÉ 50010Y-U8:F$"FTP":1,U,15,"S0:"F$:1:(F$),U:(F$),U:800:P$""7280-z5,"ATC";((P),2):900:P$"OK"7290--@P$(P$,5):(P$ h>h=h<DCBX>)LN,HP<[k=Zc^[_SH0NHҮBHHL $ɍK ɭ=< Hɩ$N  LhJMHF_^:SL[\]^_@H  n U c ŭH7Ш)) xT=<`K Hɱ  /K` gȐi /K`>` V`H h. ȩ L Ȣv ; =ɭ< ȭ=  HɭC ȭD  $ LhL` A RȍDC $ɍK   `e ǭIS )"У "  - HɦLͽ ˰A H  Lh +˅ " = H(`  0 :)`hh`L`HH&&hehe&ee` ˍUHH H Hh  Hh H Lh H  Lh + " LHJ&&` `ԩԩDԎԌLe$:300II1:I020:HH$(I)""II0IIIL:II0"EW EADER: ";:5000:HH$(II)P$V 300V --- TRANSMIT P$ TO THE OPEN SOCKETXOP$P$:ML9:C8$(((MV8)),2):E$"OK":VR3E$C8$b5,"ATS42=";C8$;"TP+";QU$;P$;QU$lML:P$E$ o©N, ©LL / }Lɑх҅^ T:,$^i(ݍI ΐLI:$' 8 é Lp Lh ͥ^8^ o ^MM âN, C Lh͢L¦ ͦ膭 00:BA1200@ CR$(13):(14);:SY(65532):53280,254:53281,246 CO$"":SY226ML49152:665,73((678)30):UMML2048:XB9600 #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: &SY3436879,27:CO$(31) + WN 1ȮV N ª LhM3M8OȱPQȭRmbȱmc /Ɉ0 Aȅ RȍTU $(P$,I,1):A$"("6450&A$" "I0I&II1:I06410'('2I000'<((P$,I01),4)"BYTE"T'FTB((P$,I1,I0I)):Y1TB10:'d" "(TT):'XFX0:X$",P,R":P$" ":600:850:P$""7000'bFX$xL `  ߢōɑ `a ȩu `ΎHdX=< ŢB*LMɩ? , ũ. NVd . $ݐI +υυlL` V`N ¢ H`HShS`TUL8SC";((SP),2):900:P$"OK"7040(Y0:Y$"":TT0)#5,A$:ST0A$""900:71003)ML12:X(MV):XE(MV1)E)(P$)07100k)TTTT(P$):6500:650:XE07250t)800) P0P(P$,3)"550"P$:7250)*P0PP$""7100)/P0P(P$,4 , o Њ =  H H h ¢MX eˆX Ž ` {ªȘ eŠJ ȮJ`M z…`8e`J J") JΰJJJJ)΍X)M) JJJ Ȉ` e¢ MȐ`΍T6ύUTI200*k800:P$""TITT7275*pP$:800:P$""7280 +z5,"ATC";((P),2):900:P$"OK"7290&+L+@P$(P$,5):(P$)8(P$)168020z+JUN(P$):"URRENT DRIVE IS NOW";UN:2000+T1,UN,15,"CD "P$:1,E,E$,E1,E2:E,E$,E1,E2:1:2000+. /ɈL ǩ é:wL= Dž  L Aȅf O0O8?Jnfne ": Ű |Ȥ0eeَTVKV ®XU6 eŽ eŢMX0 bňX eŽ e b bŭTK B$:B$" "8::2000,N A$;:8250,(#TTTI100-2#ML12:TITT9010-<#-Pß5,2,0,(8)/-Zá#5,A$:A$""A$;F-dáA$:A$""5,A$;R-nÉ 50010-U8:F$"FTP":1,U,15,"S0:"F$:1:(F$),U:(F$),U:P$"OK"7290--@P$(P$,5):(P$ h>h=h<DCBX>)LN,HP<[k=Zc^[_SH0NHҮBHHL $ɍK ɭ=< Hɩ$N  LhJMHF_^:SL[\]^_@H  n U c ŭH7Ш)) xT=<`K Hɱ  /K` gȐi /K`>` V`H h. ȩ L Ȣv ; =ɭ< ȭ=  HɭC ȭD  $ LhL` A RȍDC $ɍK   `e ǭIS )"У "  + HɦLͽ ˰A H  Lh +˅ " = H(`  0 :)`hh`L`HH&&hehe&ee` ˍUHH H Hh  Hh H Lh H  Lh + " LHJ&&` `ԩԩDԎԌLe)P$:300 II1:I020:HH$(I)""II0IIIO:II0"EW EADER: ";:5000:HH$(II)P$Y 300V --- TRANSMIT P$ TO THE OPEN SOCKETXOP$P$:ML9:C8$(((MV8)),2):E$"OK":VR3E$C8$b5,"ATS42=";C8$;"TP+";QU$;P$;QU$lML:P$ o©N, ©LL / }Lɑх҅^ T:,$^i(ݍI ΐLI:$' 8 é Lp Lh ͥ^8^ o ^MM âN, C Lh͢L¦ ͦ膭 00:BA1200@ CR$(13):(14);:SY(65532):53280,254:53281,246 CO$"":SY226ML49152:665,73((678)30):UMML2048:XB9600 #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: &SY3436879,27:CO$(31) (SY226(ML1)2095:YMJ,),#($YX$$#]#)i#$S#$S[[i$$)|mi)S4i#bZH&bTDThDt(ntJrtttrDh2"&&rr&HDD PC IRQ SR AC XR YR SPABCDFGHLMNQR(TWX,:;$#"+-OIJ%&EV)>ɯ2/ƠɰLǠP* -ĉ(789):UM9:X234XB1200* dSY3456579,00 e6 fA nP$"A" xCO$;" V1.5":"EQUIRES 64ET II FIRMWARE 2.0+" "Y O IMMERMAN (BO@ZIMMERS.NET)":: --- MODEM INIT UN(254) PH0:PT0:MVML18 "NITIALIZIN5,"ATL":945"XPECTED ";E$;", GOT ";A$:B --- THE MAIN LOOP !](UR$)0(OF$)0300eI0(UR$,I1,1)"/"II1:I(UR$)1020H1$(UR$,I):P1$(UR$,I1):P1$""P1$"/"I0(H1$,I1,1)":"II1R(P$):VR2.0"IMODEM INIT FAILED: ";P$:: 900:P$"OK"203 HH$(20):UR$"WWW.ZIMMERS.NET/INDEX.HTML":OT$"":OF$"@0:WGET-OUTPUT,S" , GET INFO 6:"EQUEST ARMS:" @ " *) YPE : " A " 1) RL : HTTP://";UR$ &*930:P0PP00"NEXPECTED PACKET ID: ";P0;"/";P:=4P00E1:: FAILC>i --- GET E$ FROM MODEM, OR ERRORsE$""{MLE$""P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;"" --- GET PACKET HEADER, SETS PP$""1000X(P$):X1XLH300HX1"NTER : HTTP://";:5000:UR$P$:300X2"NTER OUTPUT DEVICE/UNIT: ";:5000:UN(P$):300X3"NTER OUTPUT FILE: ";:5000:OF$P$:300XLHP$HH$(XLW):"ODIFY: ";P$:5005:HH$(XLW)P L eeL S H LhLx Xl --- GET E$ FROM MODEM, OR ERRORvE$""~MLE$""P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;"" + --- GET PACKET HEADER, SET0:P$""1000X(P$):X1XLH300KX1"NTER : HTTP://";:5000:UR$P$:300X2"NTER OUTPUT DEVICE/UNIT: ";:5000:UN(P$):300X3"NTER OUTPUT FILE: ";:5000:OF$P$:300XLHP$HH$(XLW):"ODIFY: ";P$:5005:HH$(XLW L eeL S H LhLx X fI nP$"A" xCO$;" V1.5":"EQUIRES 64ET II FIRMWARE 1.8+" "1200 BAUD VERSION" "Y O IMMERMAN (BO@ZIMMERS.NET)":: -------------------------------- 1,UN,15:8,UN,8,OF$",W"*1,E:E0"NABLE TO OPEN "OF$": "(E)"";CO$:0E930:P0P2300Y P10P002300i TLTL(P$)}$ (P$)08,P$;. TP(TLCL100.0)8 " "(TL)" ("(TP)"% )""EADERS COMPLETE.""ECEIVING"(CL)" BYTES";CO$71,UN,15:8,UN,8,OF$",W"r1,E:E0"NABLE TO OPEN "OF$": "(E)"";CO$:x930:P0P2300 P10P002300 TLTL(P$)$ (P$)08,P$;. TP(TLCL100NABLE TO CONNECT TO ";H1$;"";CO$:300`ITTI409jP90:800:P$""ITTI10ItTIIT1130l"ONNECTED TO "HO$"";CO$XB96001205UM:UM3:(789)234UM9:1210BAXB:UM19,1:5,"AT":6000:6000XB24001210  TELNET64/128 1200B 1.8+@ UPDATED 08/15/2017 12:54Aa -254,(186):(254)8254,8 SY(65532):SY6158,254: SY34X23777:X,170:(X)170"<16K":5,2,0,(8):(65532)3456,87:54,87:52,87 PP$(25):P$"OK":186,A$;" ";:P$P$A$:5010P$""5010(P$(P$,(P$)1):"  ";:50106pTTTI100MzML12:TITT6010ScPß5,2,0,(8){Zá#5,A$:A$""A$;dáA$:A$""5,A$;nÉ 50010D8:F$"WGET":1,D,15,"S0:"F$:1:(F$),D:(F$),D-LIVE":600 P$"SER-GENT: =":600-P$"ONTENT-LENGTH: 0":600GP$"CCEPT: */*":600SI020gHH$(I)""1330z(P$HH$(I):9002IP$(13)(10):650"EQUEST SENT. EADING RESPONSE...";CO$P90:800:P$""BIN",(254),1:C +<SY61(ML1)2175:"PML128.BIN",(254),1: PSY226UM0UM:UM3:X#thE`<ʍ7!̺``G MODEM...":CR$(13)(10)* #5,A$:A$""203` 5,CR$;CR$;"ATHZ0&P0F0E0";CR$;:900:P$"OK"203w #5,A$:A$""208 5,CR$;"ATE0N0R0V1Q0";CR$; 900:P$"OK"208 #5,A$:A$""225 5,"ATE0V1X1F3Q0S40=248I4";CR$;(19);& 90E$P$OP$:"ETRYING..";CO$:600vOOP$P$:ML9:C8$(((MV8)),2):PN$(((P$)),2)hE$"OK":VR3E$C8$5,"ATS42=";C8$;"T+";PN$:5,P$ML:P$E$P$OP$:"ETRYING..";CO$:650 --- GET P$ FROM SOCKET P P$"": B " 2) UTPUT NIT:";UN.C " 3) UTPUT ILE: ";OF$7ILW3uJNH0:I020:(HH$(I))0NHNH1:(LWNH)") "HH$(I):TLHLWNH1:(LW1NH)") DD EW EADER"rUR$"":P$"1":400|:"YPE A NUMBER OR ΠTO BEGIN:";500S P0,P1,P2, RETURNS P0=0 IF NADA&PR0:#5,P$:P$""93045,(17);_ML6:P0(MV2):P1(MV4):P2(MV6)PL(MV0):CR(MV1):C8(MV8)P00P2C8985P10P$""P00CR0#5,P$:P$""985"-";C +I1:I(H1$)1050"$HO$(H1$,I):I(H1$)H1$H1$":80"U.AT$"":XB0BA0XBBAAT$"S43="((XB),2)m8#5,A$:A$""1080LQU$(34):5,"ATH"AT$"&D10&M10&M13C";QU$;H1$;QU$Q900V(P$)8(P$,8)"CONNECT "P((P$,9)):1200[):"  ";:5010OÐPß5,2,0,(8)Zá#5,A$:A$""A$;2dáA$:A$""5,A$;>nÉ 50010uF$"CONFIGURE":1,8,15,"S0:"F$:1:(F$),8:(F$),8(X)170"<16K":5,2,0,(8):(65532)3456,87:54,87:52,87 WF$(100):WF0:P$" 100.0)8 " "(TL)" ("(TP)"% )"1B "";:TLCL2300ML :"ONE.";CO$:8:1nV 5,"ATH0":5,"ATZ":TTTI100~` TITT2400j 5:P$""" ";A$:A$""5010A$(13)" ":A$(20 NP0:(2614)0NP203BAXB:2576,10:2578,(59490NP):2579,(59491NP)Y2582,170:2583,1:NP02582,154"ENDING REQUEST...";CO$:5,"AT":6000:6000P$" "P1$" /1.1":600P$"OST: "HO$:600P$"ONNECTION: (254):BA1200:XB1200L CR$(13):(14);:SY(65532):53280,254:53281,246} CO$"":SY226ML49152:665,73((678)30) #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: &SY3436879,27:CO$(31) +(SY226(ML1)2095:"PML64.),D$,5)"HTTP/""AD RESPONSE";CO$;P$:RC((P$,10,3))KRC200"AD RESPONSE CODE:";CO$;RC:d4800:P$""E02200H(P$)17(P$,1)"C"(P$,9,1)"L"2100R(P$,15,1)":"2100\I15pII1:(P$,I,1)" "2160""2000LE0:FB0 (P$)13"AD RESPONSE: ";CO$;P$:R(P$,5)"HTTP/""AD RESPONSE";CO$;P$:gRC((P$,10,3))RC200"AD RESPONSE CODE:";CO$;RC:4800:P$""E02200H(P$)17(P$,1)"C"(P$,9,1)"L"21%:I,A%131:II1:A%10, _A%0I,A%:II1:902 d8 e> fI nP$"A" xCO$;" V1.6":"EQUIRES 64ET II FIRMWARE 1.8+" "1200 BAUD VERSION" "Y O IMMERMAN (BO@ZIMMERS.NET)":: -------------------------------- 4100tA$:A$"Y"A$""A$"N"A$""4980>~A0:AA$"":A$"Y"A$""AA$"":A1IAA$:SP$""`" ";tA$:A$""5010A$(13)" ":A$(20)A$;" ";:P$P$A$:5010P$""5010P$(P$,(P$)1"EADERS COMPLETE.""ECEIVING"(CL)" BYTES";CO$:1,UN,15:8,UN,8,OF$",W"u1,E:E0"NABLE TO OPEN "OF$": "(E)"";CO$:{930:P0P2300 P10P002300 TLTL(P$)$ (P$)08,P$;. TP(TLCL"NABLE TO CONNECT TO ";H1$;"";CO$:300`ITTI40<jP90:800:P$""ITTI10LtTIIT1130o"ONNECTED TO "HO$"";CO$XB96001205UM:UM3:(789)234UM9:1210BAXB:UM19,1:5,"AT":6000:6000XB24001210  TELNET64/128 1200B 1.8+@ UPDATED 09/02/2022 12:54Aa +254,(186):(254)8254,8 SY(65532):SY6158,254: SY34X23777:X,170:(X)170"<16K":5,2,0,(8):(65532)3456,87:54,87:52,87 PP$(25):P$"OK":186,)A$;" ";:P$P$A$:5010P$""5010+P$(P$,(P$)1):"  ";:50109pTTTI100PzML12:TITT6010VfPß5,2,0,(8)~Zá#5,A$:A$""A$;dáA$:A$""5,A$;nÉ 50010D8:F$"WGET":1,D,15,"S0:"F$:1:(F$),D:(F$EEP-LIVE":600P$"SER-GENT: =":6000P$"ONTENT-LENGTH: 0":600JP$"CCEPT: */*":600VI020jHH$(I)""1330}(P$HH$(I):9002IP$(13)(10):650"EQUEST SENT. EADING RESPONSE...";CO$P90:800:P$BIN",(254),1:C 2SY61ML4864:981,15:S8(215)128:S812830643w <SY61(ML1)2175:"PML128.BIN",(254),1: =SY61S8128XB2400:CO$(159) FTM828:SY61TM2816 -PITM:DD56577:SY34DD37136 ZA%:SY61(A%155A%156)BzX((P$,I)):X0CLX2100"TL0:5,CR$;"AT&M&DC";((P),2);CR$;V900:P$"OK""IMODEM COMMAND FAILED: ";P$:CL0"EADERS COMPLETE. O CONTENT. ONE.";CO$:"EADERS COMPLETE.""ECEIVING"(CL)" BYTES";CO$ R(P$,15,1)":"2100\I15pII1:(P$,I,1)" "21607zX((P$,I)):X0CLXA2100jTL0:5,CR$;"AT&M&DC";((P),2);CR$;900:P$"OK""IMODEM COMMAND FAILED: ";P$:CL0"EADERS COMPLETE. O CONTENT. ONE.";CO$: GET STARTED !B -------------------------------P UN(254) PH0:PT0:MVML18:CR$(13)(10):QU$(34) "NITIALIZING MODEM...";:6000 #5,A$:A$""203 5,CR$;"ATHZ0F0E0";CR$; 900:P$"OK"203  -#5,A$:A$""208, ".";:5,CR$;"ATE0N0R0V1F0";CR$;C 900:P$"OK"208j ".";:5,"ATE0V1X1Q0F0";CR$;(19); 900:P$"OK""IMODEM INIT FAILED: ";P$: "!": HO$(30): PO(30): HM$(30) HO$(0)"COFFEEMUD.NET":PO(0)23:HZ11,UN`Lʢ`ҍ&©'L©3&©'  3L ` 3ķL.` ,© 3` ** 0  } Lt W` © L * ;z ,Ls <` *   UP...";:6000 :I1100:#5,A$:I& #5,A$:A$""2480, H :"ODIFY HONEBOOK:"T I1HZ (I)") ";HO$(I1);":";((PO(I1)),2):I (HZ1)") DD EW NTRY" :"NTER A NUMBER OR : "; 5000:P$"" X(P$ ------------- E$""MLVE$""P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;""\ -------------------------------- THE MAIN LOOP ! -------------------------------:CO$;"AIN ENU:"" 5010A(A$):A13" ":)A34A$;A$;" ";:P$P$A$:5010OA20A$;" ";:P$P$A$:5010_P$""5010P$(P$,(P$)1):"  ";:5010p((DD)16)0z61005,"+++";:".";6100".";:5,"ATH":TTTI15F ǩ 0P +PITM:DD56577:SY34DD37136 ZA%:SY61(A%155A%156)BTRANSLATION (Y/N)? "; 4980:A1B3$B3$"T"-&"O TERMINAL CHO (Y/N)? ";H04980:A1B3$B3$"E"q:"O / LOW ONTROL (Y/N)? ";D4980:A1B3$B3$"X"NB4$((B2),2)X5,"ATP";B3$;(34);B1$;"=";B2$;":";B4$;(34):900b 00R(P$,15,1)":"2100\I15pII1:(P$,I,1)" "2160:zX((P$,I)):X0CLXD2100mTL0:5,CR$;"AT&M&DC";((P),2);CR$;900:P$"OK""IMODEM COMMAND FAILED: ";P$:CL0"EADERS COMPLETE. O CONTENT. ONE.";CO$: GET STARTED !B -------------------------------P UN(254) PH0:PT0:MVML18:CR$(13)(10):QU$(34) "NITIALIZING MODEM...";:6000 #5,A$:A$""203 5,CR$;"ATHZ0&P0F0E0";CR$; 900:P$"OK"203 +  +#5,A$:A$""208/ ".";:5,CR$;"ATE0N0R0V1F0";CR$;F 900:P$"OK"208m ".";:5,"ATE0V1X1Q0F0";CR$;(19); 900:P$"OK""IMODEM INIT FAILED: ";P$: "!": HO$(30): PO(30): HM$(30) HO$(0)"COFFEEMUD.NET":PO(0)23:HZ11`Lʢ`ҍ&©'L©3&©'  3L ` 3ķL.` ,© 3` ** 0  } Lt W` © L * ;z ,Ls <` *   27 :CO$;(14);"ANGING UP...";:6000, :I1100:#5,A$:ID #5,A$:A$""2480J f :"ODIFY HONEBOOK:"r I1HZ (I)") ";HO$(I1);":";((PO(I1)),2):I (HZ1)") DD EW NTRY" :"NTER A NUMBER OR : "; ---------------- E$""MLYE$""P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;""_ -------------------------------- THE MAIN LOOP ! -------------------------------:CO$;"AIN ENU:" +"" ";A$:A$""5010A(A$):A13" ":GA34A$;A$;" ";:P$P$A$:5010mA20A$;" ";:P$P$A$:5010}P$""5010P$(P$,(P$)1):"  ";:5010p((DD)16)0z61005,"+++";:".";61F ǩ 0P -3LČ ,ĭ34 LØI#3"m#34#i4"3`L7 , ȬIJǭ $ ©`5 0 L| ĐLgƮ 0 0 8` W``  0 }VP23000:1000`P34000:1000CdP4"ERMINAL MODE. ";:2430:1000Sj"?!":1000r:"IAL FROM HONEBOOK:"~I1HZ (I)") ";HO$(I1);":";((PO(I1)),2) I::"NTER A NUMBER OR : ";*5000:P$""4X }ŭ   0 } LũLNŘȩ)-./0127384````ɍ  ``ɍ  `` ǩɍ ¥ < ``@#!&#>$>%3J`Hͺ hL lh΍ɍ ` * HHH -hhh`ͺ +3LČ ,ĭ34 LØI#3"m#34#i4"3`L7 , ȬIJǭ $ ©`5 0 L| ĐLgƮ 0 0 8` W``  0 }000VP23000:1000`P34000:1000DdP45,CR$;"AT&P1";CR$;:2430:1000Tj"?!":1000s:"IAL FROM HONEBOOK:"I1HZ (I)") ";HO$(I1);":";((PO(I1)),2) I::"NTER A NUMBER OR : ";*5000:P$""4 }ŭ   0 } LũLNŘȩ)-./0127384````ɍ  ``ɍ  `` ǩɍ ¥ < ``@#!&#>$>%3J`Hͺ hL lh΍ɍ ` * HHH -hhh`ͺ L l * -` JHh`   `L - 7384`  ed`  `  ;HO$;":";((PO),2). ` "* ONNECTED. ";3t 5,CR$;"ATF0O";CR$;S~ SY226"IT 1 TO EXIT."s SY61"IT TO EXIT." 53280,0:53281,0:SY3436879,8 TM 53280,254:53281,246:SY3436879,27 :CO$;(14);"ANGING ,15:8,UN,8,"TELNETPHONEBOOK,S,R"/1,E:E08:1:300X8,HZ:I1HZ:8,HO$(I1):8,PO(I1)s8,H$:HM$(I1)(H$,2)"I:8:1, 1000 -------------------------------- GET E$ FROM MODEM, OR ERROR ! ------------------HZ:I1HZ:8,HO$(I1):8,PO(I1)] 8,"-"HM$(I1).b I:8:1: 3000a:"NTER HOSTNAME/IP: ";:5000:H$P$:P$"""NTER PORT NUMBER (23): ";:5000:HP(P$):HP0HP23HO$H$:POHP:2300P$""" ";A$:A$"" ):X1XHZ13000,& "NTER HOSTNAME/IP: ";:5000:H$P$:P$""3000i0 "NTER PORT NUMBER (23): ";:5000:HP(P$):HP0HP23: HO$(X1)H$:PO(X1)HP:XHZ1HZHZ1D 1,UN,15:8,UN,8,"@0:TELNETPHONEBOOK,S,W"N 1,E:E08:1:300X 8,1) IAL FROM HONEBOOK"#" 2) ODIFY HONEBOOK"<" 3) UICK ONNECT"U " 4) ERMINAL MODE"e" 9) UIT":"NTER A NUMBER:  ";$5000:P$""1000.P(P$):P95:1,UN,15,"I0":1:8P1P41050BLP12000:1000 0ML12:TITT6050 TTTI150TITT6110"(?\@1,8,15:8,8,8,"TELNETML.BIN,P,R":LN41000:DN0sJ1,E:E08:1:T#8,A$:ST08:^A$""A$(0)cA(A$):A$((A),2)hDN0((LN),2);" DATA ";A$;:DNDNL6LLLLLLELL,LHO$HO$(X1):POPO(X1)S"ONNECTING TO ";HO$;":";((PO),2);"...";CO$k #5,A$:A$""2310 5,CR$;"ATF3HCTEP";QU$;HO$;":";((PO),2);QU$;CR$; 900:(P$)7(P$,7)"CONNECT"2400$ "NABLE TO CONNECT TO "0L7 GG 0꘠G W`܌ 0 0 8LgƬL î Lå. 0- + 7384`  ed`  `  ";HO$;":";((PO),2). ` "* ONNECTED. ";3t 5,CR$;"AT0O";CR$;S~ SY226"IT 1 TO EXIT."s SY61"IT TO EXIT." "ERMINAL MODE.";CR$; 53280,0:53281,0:SY3436879,8 TM 53280,254:53281,246:SY3436879,,UN,15:8,UN,8,"TELNETPHONEBOOK,S,R"21,E:E08:1:300[8,HZ:I1HZ:8,HO$(I1):8,PO(I1)v8,H$:HM$(I1)(H$,2)"I:8:1, 1000 -------------------------------- GET E$ FROM MODEM, OR ERROR ! --------------- 1,E:E08:1:300#X 8,HZ:I1HZ:8,HO$(I1):8,PO(I1)7] 8,"-"HM$(I1)Lb I:8:1: 3000:"NTER HOSTNAME/IP: ";:5000:H$P$:P$"""NTER PORT NUMBER (23): ";:5000:HP(P$):HP0HP23HO$H$:POHP:2300P$"  5000:P$"" X(P$):X1XHZ13000J& "NTER HOSTNAME/IP: ";:5000:H$P$:P$""30000 "NTER PORT NUMBER (23): ";:5000:HP(P$):HP0HP23: HO$(X1)H$:PO(X1)HP:XHZ1HZHZ1D 1,UN,15:8,UN,8,"@0:TELNETPHONEBOOK,S,W"N" 1) IAL FROM HONEBOOK"&" 2) ODIFY HONEBOOK"?" 3) UICK ONNECT"X " 4) ERMINAL MODE"h" 9) UIT":"NTER A NUMBER:  ";$5000:P$""1000.P(P$):P95:1,UN,15,"I0":1:8P1P41050BLP12000:1 00".";:5,"ATH&P0":TTTI150ML12:TITT6050-TTTI150=TITT6110CI?}@1,8,15:8,8,8,"TELNETML.BIN,P,R":LN41000:DN0J1,E:E08:1:T#8,A$:ST08:^A$""A$(0)cA(A$):A$((A),2)hDNL6LLLLLLELL,LHO$HO$(X1):POPO(X1)T"ONNECTING TO ";HO$;":";((PO),2);"...";CO$l #5,A$:A$""2310 5,CR$;"ATF3HCTEP";QU$;HO$;":";((PO),2);QU$;CR$; 900:(P$)7(P$,7)"CONNECT"2400$ "NABLE TO CONNECT TO 0L7 GG 0꘠G W`܌ 0 0 8LgƬL î Lå. 0- ! Lm S Lé 3LméS 3 W` ĐLg  0 L7 L7EF  GHE ->>/=T<L5Lu |` ` ` ǥ < `` ` ` `ɍ ¥ < ` `-.`` -.` ǩǍɍ ¥ < `` ǩɍ ¥ ,© 3 `` ǩɍ ¥ ,©ɍ  ``V1.5":"EQUIRES 64ET II FIRMWARE 1.8+"< "1200 BAUD VERSION"h "Y O IMMERMAN (BO@ZIMMERS.NET)":: -------------------------------- GET STARTED ! ------------------------------- UN(254) PH0:PTR ERROR !" -------------------------------,E$""4MLyE$""P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;"" -------------------------------- THE MAIN LOOP ! -------------------------- H`HGGhGGiGH`12GHǮ +>>/=T<L5Lu |` ` ` ǥ < `` ` ` `ɍ ¥ < ` `-.`` -.` ǩǍɍ ¥ < `` ǩɍ ¥ ,© 3 `` ǩɍ ¥ ,©ɍ  ``V1.5":"EQUIRES 64ET II FIRMWARE 1.8+"< "1200 BAUD VERSION"h "Y O IMMERMAN (BO@ZIMMERS.NET)":: -------------------------------- GET STARTED ! ------------------------------- UN(254) PH0:PT, OR ERROR !% -------------------------------/E$""7ML|E$""P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;"" -------------------------------- THE MAIN LOOP ! ----------------------- H`HGGhGGiGH`12GHǮ *mǨmǪimG1mH2GȑGȑGȑGȑG`LHE0F hH"# -.#m"#i"#12GHm"m# 12"#mHHGHHEGȭFGȭ"4,11,173,1&F 221,41,253,141,1,221,56,176,12,201,20,176,8,173,1,221,9,2lP 141,1,221,162,0,32,198,255,32,228,255,201,0,240,171,201,133,240Z 4,201,27,208,4,32,204,255,96,72,162,5,32,201,255,104,32,210d 255,162,0,32,201,255,56,176,141,(254):BA1200:XB1200M CR$(13):(14);:SY(65532):53280,254:53281,246~ CO$"":SY226ML49152:665,73((678)30) #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: &SY3436879,27:CO$(31) +.#m"#i"#12GHm"m# 12"#mHHGHHEGȭFGȭ",56,176,4,56,237,156,2,201,200,144,11,173,1GF 221,41,253,141,1,221,56,176,12,201,20,176,8,173,1,221,9,2P 141,1,221,162,0,32,198,255,32,228,255,201,0,240,171,201,133,240Z 4,201,27,208,4,32,204,255,96,72,162,5,32,201,255,104,32,210d 255,,(254):BA1200:XB1200M CR$(13):(14);:SY(65532):53280,254:53281,246~ CO$"":SY226ML49152:665,73((678)30) #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: &SY3436879,27:CO$(31) (SY226(ML1)2095:"PML640-TIMEOUT[2400 BAUD]LLLLL˩.ʍˍˍ&'`HHHخݩ ݭ Mݬݰ  -J -)݊)/BݩݩݩM ݩMݩݩ<LV ݊),Jfƨ"ݩ ݌ M ݊Jƴ0 fL̞ V1X1Q0F0";CR$;(19);9900:P$"OK""IMODEM INIT FAILED: ";P$:_"!": HO$(30): PO(30): HM$(30)HO$(0)"COTTONWOODBBS.DYNDNS.ORG":PO(0)6502:HZ1HO$(1)"BORDERLINEBBS.DYNDNS.ORG":PO(1)6400:HZ2HO$(2)"CENTRONIAN.SERVEBEER.COM":IN",(254),1:' =S8128XB2400:CO$(159)A FTM828:SY61TM2816q PITM:XM51200:DD56577:SY34XM0:DD37136 ZA%:SY61(A%155A%156)B%:I,A%131:II1:A%10 _A%0I,A%:II1:90 d e f nP$"A"# xCO$;"= MR ɍ  ``ɍ ¥ ,©, 38ii 3 3ɍ  cǰ 3 3 ǰ `` 0 0 ``8`(+ 3/ 3ͯܭͮ8`8`,z`Ʒ` ɭ12GHi njHEGȭFGhG1:40020|",";A$;:DNDN1DN18:DN0:LNLN10# 40020`( 162,5,32,198,255,32,228,255,201,0,240,61,201,10,240,572 32,210,255,173,155,2,205,156,2,240,38,176,13,169,255,56,237,156< 2,24,109,155,2,56,176,4,56,237,156,2,201,200,14! CBMTERM64/128 1200B 1.8+A UPDATED 08/15/2017 12:54Ab -254,(186):(254)8254,8 SY(65532):SY6158,254: SY34X23777:X,170:(X)170"<16K":5,2,0,(8):(65532)3456,87:54,87:52,87 PP$(25):P$"OK":186Gȭ#GȩGȩGhG` F= tɍ"#"G"#GH"1#2ܭGH12`/0GHGEȱGF t12`GHqGHqGh`DISKS 22 INSERT $ 22 LOAD CF FIND , 22 NAME SAVE ,PRG LOGIN ?NOT SUPPORTED IN V-1541?100:MVML18:CR$(13)(10):QU$(34)@ "NITIALIZING MODEM...";:6000W #5,A$:A$""203w 5,CR$;CR$;"ATHZ0F0E0";CR$; 900:P$"OK"203 #5,A$:A$""208 ".";:5,CR$;"ATE0N0R0V1F0";CR$; 900:P$"OK"208".";:5,"ATE0.BIN",(254),1:D +)݊)/BݩݩݩM ݩMݩݩ<LV ݊),Jfƨ"ݩ ݌ M ݊Jƴ0 fL̞ TE0V1X1Q0F0";CR$;(19);<900:P$"OK""IMODEM INIT FAILED: ";P$:b"!": HO$(30): PO(30): HM$(30)HO$(0)"COTTONWOODBBS.DYNDNS.ORG":PO(0)6502:HZ1HO$(1)"BORDERLINEBBS.DYNDNS.ORG":PO(1)6400:HZ2HO$(2)"CENTRONIAN.SERVEBEER.COIN",(254),1:' =S8128XB2400:CO$(159)A FTM828:SY61TM2816q PITM:XM51200:DD56577:SY34XM0:DD37136 ZA%:SY61(A%155A%156)B%:I,A%131:II1:A%10 _A%0I,A%:II1:90 d e f nP$"A"# xCO$;"= MR ɍ  ``ɍ ¥ ,©, 38ii 3 3ɍ  cǰ 3 3 ǰ `` 0 0 ``8`(+ 3/ 3ͯܭͮ8`8`,z`Ʒ` ɭ12GHi njHEGȭFGhG0((LN),2);" DATA ";A$;:DNDN1:40020|",";A$;:DNDN18DN18:DN0:LNLN10D 40020( 162,5,32,198,255,32,228,255,201,0,240,61,201,10,240,572 32,210,255,173,155,2,205,156,2,240,38,176,13,169,255,56,237,156< 2,24,109,155,2! CBMTERM64/128 1200B 1.8+A UPDATED 10/13/2021 12:54Ab +254,(186):(254)8254,8 SY(65532):SY6158,254: SY34X23777:X,170:(X)170"<16K":5,2,0,(8):(65532)3456,87:54,87:52,87 PP$(25):P$"OK":186Gȭ#GȩGȩGhG` F= tɍ"#"G"#GH"1#2ܭGH12`/0GHGEȱGF t12`GHqGHqGh`DISKS 22 INSERT $ 22 LOAD CF FIND , 22 NAME SAVE ,PRG LOGIN ?NOT SUPPORTED IN V-1541?100:MVML18:CR$(13)(10):QU$(34)@ "NITIALIZING MODEM...";:6000W #5,A$:A$""203z 5,CR$;CR$;"ATHZ0&P0F0E0";CR$; 900:P$"OK"203 #5,A$:A$""208 ".";:5,CR$;"ATE0N0R0V1F0";CR$; 900:P$"OK"208 ".";:5,"A.BIN",(254),1:D -SY226(51201)1615:"X-XFER64.BIN",(254),1:S 1SY6170 2ML4864:981,15:S8(215)128:S812830643 7981,0:X(51201):981,15:X4960 -8981,0:"X-XFER128.BIN",(254),1:981,15: <(ML1)2175:"PML128.B  ݩмH) ݩ-퍡h`H`h̝J): ݭݩݩ x ݌  (` .L hL < 6)ˍeʹˍjʹˍʹˍʭ)#ݩLXLL򅞄̛`B3PO(2)6400:HZ3)1,UN,15:8,UN,8,"BBSPHONEBOOK,S,R"C1,E:E08:1:300M8,HZYI1HZi8,HO$(I1)x8,PO(I1)8,H$:HM$(I1)(H$,2)!"8:1, 1000 -------------------------------- GET E$ FROM MODEM, O ------ :CO$;"AIN ENU:"*" 1) IAL FROM HONEBOOK"F" 2) ODIFY HONEBOOK"_" 3) UICK ONNECT"x " 4) ERMINAL MODE"" 9) UIT":"NTER A NUMBER:  ";$5000:P$""1000.P(P$):P95:1,UN,15,"I0":1:8P A$;" ";:P$P$A$:5010A20A$;" ";:P$P$A$:5010&P$""5010OP$(P$,(P$)1):"  ";:5010cp((DD)16)0wz6100:5,"+++";".";:61005,"ATH":TTTI150ML12:TITT6040TTTI150TITT 000:P$""2430% -F$P$:"NTER A DEVICE (";((UN),2);"): ";:5000> -DV(P$):DV0DVUNr -(F$)3(F$,2)",P"(F$,2)",S"F$F$",P" -1,DV,15:2,DV,2,"@0:"F$",W":1,E:E02760 -2:1:E,E$,E1,E2:E0:2430 -SY61981,0 -51 R : "; *5000:P$""$4X(P$):X1XHZ2000M>HO$HO$(X1):POPO(X1):HA$HM$(X1)"ONNECTING TO ";HO$;":";((PO),2);"...";CO$ #5,A$:A$""2310 AT$"":IFXB>0ANDBA>0ANDXB<>BATHENAT$="S43="+MID$(STR$(XB,2) 5,CR$;10 40020( 162,5,32,198,255,32,228,255,201,0,240,61,201,10,240,57c2 32,210,255,173,155,2,205,156,2,240,38,176,13,169,255,56,237,156< 2,24,109,155,2,56,176,4,56,237,156,2,201,200,144,11,173,1F 221,41,253,141,1,221,56,176,12,201,20,& "NTER HOSTNAME/IP: ";:5000:H$P$:P$""3000L0 "NTER PORT NUMBER (23): ";:5000:HP(P$):HP0HP23g5 "OCAL ECHO (/)? ";6 A$:A$"Y"A$""A$"N"A$""31267 A$:HM$(X1)"":A$"Y"A$""HM$(X1)"E": HO$(X1)H$:PO(XXIT." 53280,0:53281,0:SY3436879,8 TME 53280,254:53281,246:SY3436879,27a XM0((DD)16)02500 :CO$;(14);")PLOAD, )OWNLOAD, )ANGUP, OR )ONT: "; A$:A$"U"A$""2600 A$"D"A$""2700 A$"C"A$" TELNETD64 1200B 2.7+= UPDATED 07/07/2017 03:39P^254,8:(186)7254,(186)(49153)43"RDS64.BIN",(254),1:53280,14:53281,6 -(14);"TELNETD64 V1.0":"EQUIRES 64ET II FIRMWARE 2.7+""1200 BAUD VERSION"& ";:5000:HP(P$):HP0HP23"OCAL ECHO (/)? ";?A$:A$"Y"A$""A$"N"A$""4030eA$:HA$"":A$"Y"A$""HA$"E"|HO$H$:POHP:2300P$""" ";A$:A$""5010A(A$):A13" ":A34A$;$):DV0DVUN$F -(F$)3(F$,2)",P"(F$,2)",S"F$F$",P"PP -1,DV,15:2,DV,2,F$",R":1,E:E02660pZ -2:1:E,E$,E1,E2:E0:2430d -SY61981,0n -51200x -SY61981,15 -2:1::"ONE!":2430 -"D":"NTER NEW FILENAME: ";:51P41050BLP12000:1000&VP23000:1000;`P34000:1000fdP4"ERMINAL MODE. ";:2430:1000vj"?!":1000:"IAL FROM HONEBOOK:"I1HZ (I)") ";HO$(I1);":";((PO(I1)),2) I::"NTER A NUMBER O6110?@1,8,15:8,8,8,"TELNETML.BIN,P,R":LN41000:DN00J1,E:E08:1:FT#8,A$:ST08:Y^A$""A$(0)rcA(A$):A$((A),2)hDN0((LN),2);" DATA ";A$;:DNDN1:40020|",";A$;:DNDN1DN18:DN0:LNLN209 -SY61981,15 -2:1::"ONE!":2430+ :"ODIFY HONEBOOK:"7 I1HZg (I)") ";HO$(I1);":";((PO(I1)),2):I (HZ1)") DD EW NTRY" :"NTER A NUMBER OR : "; 5000:P$"" X(P$):X1XHZ13000"ATH"AT$"F3C";HA$;QU$;HO$;":";((PO),2);QU$;CR$;D 900:(P$)7(P$,7)"CONNECT"2400v$ "NABLE TO CONNECT TO ";HO$;":";((PO),2)|. ` "* ONNECTED. ";t 5,CR$;"ATF0O";CR$;~ SY226"IT 1 TO EXIT." SY61"IT TO E176,8,173,1,221,9,2) P 141,1,221,162,0,32,198,255,32,228,255,201,0,240,171,201,133,240k Z 4,201,27,208,4,32,204,255,96,72,162,5,32,201,255,104,32,210 d 255,162,0,32,201,255,56,176,141  -1 OÐ Pß5,2,0,(8) Zá#5,A$:A$""A$; dá1)HP:XHZ1HZHZ1D 1,UN,15:8,UN,8,"@0:BBSPHONEBOOK,S,W"5N 1,E:E08:1:300GX 8,HZ:I1HZr] 8,HO$(I1):8,PO(I1):8,"-"HM$(I1)b I:8:1: 3000:"NTER HOSTNAME/IP: ";:5000:H$P$:P$"""NTER PORT NUMBER (23):  "2430 A$"H"A$""2480  "H"6 :CO$;(14);"ANGING UP...";:6000O :I1100:#5,A$:Ig #5,A$:A$""2520m ( -"U":"NTER THE FILENAME: ";:5000:P$""24302 -F$P$:"NTER A DEVICE (";((UN),2);"): ";:5000< -DV(P"Y O IMMERMAN (BO@ZIMMERS.NET)"::A "ONFIGURE ERVER:":p "IME IMIT: HRS, MINS 0, 55";H,M XH:5000:HX XM:5000:MX 4915222,H:4915223,M "DLE IME/INS 4";X $5000:4915221,X +8981,0:"X-XFER128.BIN",(254),1:981,15: <(ML1)2175:"PML128.B  ݩмH) ݩ-퍡h`H`h̝J): ݭݩݩ x ݌  (` .L hL < 6)ˍeʹˍjʹˍʹˍʭ)#ݩLXLL򅞄̛`B3M":PO(2)6400:HZ3,1,UN,15:8,UN,8,"BBSPHONEBOOK,S,R"F1,E:E08:1:300P8,HZ\I1HZl8,HO$(I1){8,PO(I1)8,H$:HM$(I1)(H$,2)!"8:1, 1000 -------------------------------- GET E$ FROM MODEM +--------:CO$;"AIN ENU:"-" 1) IAL FROM HONEBOOK"I" 2) ODIFY HONEBOOK"b" 3) UICK ONNECT"{ " 4) ERMINAL MODE"" 9) UIT":"NTER A NUMBER:  ";$5000:P$""1000.P(P$):P95:1,UN,15,"I0":1:8 A$;A$;" ";:P$P$A$:5010A20A$;" ";:P$P$A$:5010)P$""5010RP$(P$,(P$)1):"  ";:5010fp((DD)16)0zz6100:5,"+++";".";:61005,"ATH":TTTI150ML12:TITT6040TTTI150TI :5000:P$""2430( +F$P$:"NTER A DEVICE (";((UN),2);"): ";:5000A +DV(P$):DV0DVUNu +(F$)3(F$,2)",P"(F$,2)",S"F$F$",P" +1,DV,15:2,DV,2,"@0:"F$",W":1,E:E02760 +2:1:E,E$,E1,E2:E0:2430 +SY61981,0 + R OR : "; *5000:P$""'4X(P$):X1XHZ2000P>HO$HO$(X1):POPO(X1):HA$HM$(X1)"ONNECTING TO ";HO$;":";((PO),2);"...";CO$ #5,A$:A$""2310 AT$"":IFXB>0ANDBA>0ANDXB<>BATHENAT$="S43="+MID$(STR$(XB,2) 5,CLN10 40020 ( 162,5,32,198,255,32,228,255,201,0,240,61,201,10,240,57f2 32,210,255,173,155,2,205,156,2,240,38,176,13,169,255,56,237,156< 2,24,109,155,2,56,176,4,56,237,156,2,201,200,144,11,173,1F 221,41,253,141,1,221,56,176,12,201,00& "NTER HOSTNAME/IP: ";:5000:H$P$:P$""3000O0 "NTER PORT NUMBER (23): ";:5000:HP(P$):HP0HP23j5 "OCAL ECHO (/)? ";6 A$:A$"Y"A$""A$"N"A$""31267 A$:HM$(X1)"":A$"Y"A$""HM$(X1)"E": HO$(X1)H$:PO EXIT." 53280,0:53281,0:SY3436879,8 TMH 53280,254:53281,246:SY3436879,27d XM0((DD)16)02500 :CO$;(14);")PLOAD, )OWNLOAD, )ANGUP, OR )ONT: "; A$:A$"U"A$""2600 A$"D"A$""2700 A$"C"A$ TELNETD64 1200B 2.7+= UPDATED 07/07/2017 03:39P^254,8:(186)7254,(186)(49153)43"RDS64.BIN",(254),1:53280,14:53281,6 +(14);"TELNETD64 V1.0":"EQUIRES 64ET II FIRMWARE 2.7+""1200 BAUD VERSION"& ): ";:5000:HP(P$):HP0HP23"OCAL ECHO (/)? ";BA$:A$"Y"A$""A$"N"A$""4030hA$:HA$"":A$"Y"A$""HA$"E"HO$H$:POHP:2300P$""" ";A$:A$""5010A(A$):A13" ":A34(P$):DV0DVUN'F +(F$)3(F$,2)",P"(F$,2)",S"F$F$",P"SP +1,DV,15:2,DV,2,F$",R":1,E:E02660sZ +2:1:E,E$,E1,E2:E0:2430d +SY61981,0n +51200x +SY61981,15 +2:1::"ONE!":2430 +"D":"NTER NEW FILENAME: ";P1P41050BLP12000:1000)VP23000:1000>`P34000:1000idP4"ERMINAL MODE. ";:2430:1000yj"?!":1000:"IAL FROM HONEBOOK:"I1HZ (I)") ";HO$(I1);":";((PO(I1)),2) I::"NTER A NUMBETT6110?@1,8,15:8,8,8,"TELNETML.BIN,P,R":LN41000:DN03J1,E:E08:1:IT#8,A$:ST08:\^A$""A$(0)ucA(A$):A$((A),2)hDN0((LN),2);" DATA ";A$;:DNDN1:40020|",";A$;:DNDN1DN18:DN0:LN51209 +SY61981,15 +2:1::"ONE!":2430. :"ODIFY HONEBOOK:": I1HZj (I)") ";HO$(I1);":";((PO(I1)),2):I (HZ1)") DD EW NTRY" :"NTER A NUMBER OR : "; 5000:P$"" X(P$):X1XHZ130R$;"ATH"AT$"F3C";HA$;QU$;HO$;":";((PO),2);QU$;CR$;G 900:(P$)7(P$,7)"CONNECT"2400y$ "NABLE TO CONNECT TO ";HO$;":";((PO),2). ` "* ONNECTED. ";t 5,CR$;"ATF0O";CR$;~ SY226"IT 1 TO EXIT." SY61"IT T20,176,8,173,1,221,9,2, P 141,1,221,162,0,32,198,255,32,228,255,201,0,240,171,201,133,240n Z 4,201,27,208,4,32,204,255,96,72,162,5,32,201,255,104,32,210 d 255,162,0,32,201,255,56,176,141  -1 OÐ Pß5,2,0,(8) Zá#5,A$:A$""A$; O(X1)HP:XHZ1HZHZ1D 1,UN,15:8,UN,8,"@0:BBSPHONEBOOK,S,W"8N 1,E:E08:1:300JX 8,HZ:I1HZu] 8,HO$(I1):8,PO(I1):8,"-"HM$(I1)b I:8:1: 3000:"NTER HOSTNAME/IP: ";:5000:H$P$:P$"""NTER PORT NUMBER (23 ""2430 A$"H"A$""2480 "H"9 :CO$;(14);"ANGING UP...";:6000R :I1100:#5,A$:Ij #5,A$:A$""2520p ( +"U":"NTER THE FILENAME: ";:5000:P$""24302 +F$P$:"NTER A DEVICE (";((UN),2);"): ";:5000< +DV"Y O IMMERMAN (BO@ZIMMERS.NET)"::A "ONFIGURE ERVER:":p "IME IMIT: HRS, MINS 0, 55";H,M XH:5000:HX XM:5000:MX 4915222,H:4915223,M "DLE IME/INS 4";X $5000:4915221,X ."ECURITY (0/1=ON)A$:A$(13)2040  20000 :"TARTING SERVER:"; 49152j ((53280)15)14"! EE MANUAL.": "ET ROUTER TO REDIRECT":"TO THE ABOVE ADDRESS AND PORT." "ERVER IS AWAITING CONNECTION..." I49152 (((I))(, ߯n~N^.> ʍʍʍʍʍʍʍʍʍ`Lʥ`L:ʭ)`Lʠƍʍ :` :ʥ`H͜h`h`ʹmʍ`ʌʹMʪ Mʍʽ ȍ`L˩ʩ  p ʢ  L8˩C  V AʭLiLY̭  -L?˭L(̢  ʥ :ʬʥʐ ʭL˭ .I' ʥ ̀L˭ ʭ3  ZA$:A$""5,A$; nÉ 50010$!U8:F$"CBMTERM":1,U,15,"S0:"F$:1:(F$),U:(F$),U"! EE MANUAL.": "ET ROUTER TO REDIRECT":"TO THE ABOVE ADDRESS AND PORT." "ERVER IS AWAITING CONNECTION..." I49152 (((I))(()""40 PQ$(34):920Y Z5,"ATF0C";Q$;"COMMODORESERVER.COM:1541";Q$:TTTI400:900 d5,A$:(A$,2)"AT"(A$,2)""5,A$ n(A$,8)"CONNECT "(A$,8)" "40 x5,"ATB2400O0":TTTI100:900 "DONE." -"* RESET MODEM TO E p -   L8˭Li p :   LP̭ C  L8   L̩    ʎ pʩ * ʅ`  p L?ͩ``  ΥL 1";I +L?˭L(̢  ʥ :ʬʥʐ ʭL˭ .I' ʥ ̀L˭ ʭ3  ]dáA$:A$""5,A$; nÉ 50010'!U8:F$"CBMTERM":1,U,15,"S0:"F$:1:(F$),U:(F$),U! EE MANUAL.": "ET ROUTER TO REDIRECT":"TO THE ABOVE ADDRESS AND PORT." "ERVER IS AWAITING CONNECTION..." I49152 (((I))((56,87:54,87:52,87B PP$(25):P$"OK":186,(254):BA1200:XB1200w CR$(13):(14);:SY(65532):53280,4:53281,15 CO$"":SY226ML49152:665,73((678)30):UMML2048:XB9600 #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: +& p -   L8˭Li p :   LP̭ C  L8   L̩    ʎ pʩ * ʅ`  p L?ͩ``  ΥL 1";I 84915224,I6 L"ISTEN ORT 6400";PL VP0P655351100c @@ -336,18 +354,19 @@ tA I$"":4000:AA2:OAA (A)13I$I$((A)):AA1:1210 PR$"ODEM INI (I1)))"AT"4030# II1:I491522564010- AI:7 4000A "IA1d ,(((I))((I1)))"AT"4160 6II1:I491522564140 @II1 J((I))"A"4190 TII1:I491522564170 ^II1:AI g X0(X10):XX(10X0):X(X016>]|6Ut 0@P`p2"RBrb$4dtDT6&vfVFHXhx(8ZJzj :*뛋l|L\,< ݭ~n^N>.Ͽ0 P@p`"2BRbr4$tdTD&6fvFV陉XHxh8(JZjz -*:ͽ|l\L<өͭ ʍI ʭʍΥ ʍέʍ {  ʹ Ȍʐ  : V A )C6 LJLi L ̭  : L -   L pLT:A$""5,A$;/nÉ 500100U8:F$"FTP":1,U,15,"S0:"F$:1:(F$),U:(F$),UOAI1,((I$,I,1)):I OAOA(I$):OA,13:OA1,0 ((56577)16)03000 "ARRIER DETECTED." "ESET MODEM, WAIT 30 SECONDS," "THEN HIT ENTER:" - # -(49153)54"V-1541",8,1:E5,2,0,(8):TI$"000000":T10r5,"AT":TTTI100:900:"CONNECTING...";(920:".";:T1T11:T120"FAIL.":5:25,"AT":TTTI100:900<5,A$:(A$,2)"AT"(A$,2)""5,A$ F(A$,2)"OK"(A$,2T "I$:I1(I$)2:PR$PR$"":I+ PR$;:I$> (I$)281220e I1(I$):OAI1,((I$,I,1)):I OAOA(I$):OA,13:OA1,0 ((56577)16)03000 "ARRIER DETECTED." "ESET MODEM, WAIT 30 SECONDS," "THEN HIT ENTER:" @)X:0U8:F$"TELNETD64":1,U,15,"S0:"F$:1:F$,U:F$,UrseJzʔiM핼RJ|Ws))udstJze'Ւ%ϒI})s|9)w)u_'^MRs))|-rݗwy)u]ytewz^MIJ)Uvw)yJUiUNIlfYuIl)JYXIT COMET MODE." -5:49152 -/ -TITT9005 -L -#5,A$:A$""920R -_cA(A$):A$((A),2)vhDN0((LN),2);" DATA ";A$;:DNDN1:40020|",";A$;:DNDN1DN18:DN0:LNLN10 40020( 162,5,32,198,255,32,228,255LLLL !Bc)Jk1sR9{ZbC ǤjK( ϬSr0[z8冧@a#펯Hi -+ԷqP3ܿyX;"`A* hI2Qp:Yx -No%Fg=^5wV˨nO, àfG$_~ (I$)281220e I1(I$):OAI1,((I$,I,1)):I OAOA(I$):OA,13:OA1,0 ((56577)16)03000 "ARRIER DETECTED." "ESET MODEM, WAIT 30 SECONDS," "THEN HIT ENTER:" @)X:0U8:F$"TELNETD64":1,U,15,"S0:"F$:1:F$,U:F$,UrseJzʔiM핼RJ|Ws))udstJze'Ւ%ϒI})s|9)w)u_'^MRs))|-rݗwy)u]ytewz^MIJ)Uvw)yJUiUNIlfYuIl)J SY3436879,27:CO$(31)I +(SY226(ML1)2095:"PML64.BIN",(254),1: +-SY226UM0(UM1)245:"UP9600.BIN",(254),1: +2SY61ML4864:981,15:S8(215)128:S812830643 +<SY61(ML1)2175:"PML128.BIN",(254),1: FSY61LLLL !Bc)Jk1sR9{ZbC ǤjK( ϬSr0[z8冧@a#펯Hi ++ԷqP3ܿyX;"`A* hI2Qp:Yx -No%Fg=^5wV˨nO, àfG$_~"$A$:A$""940$""SP$"":820$#2,A$,A$$#2,A$,A$:ST0X(0):%#,"I"DD$":":15,"I"SD$)1:15:2:3@)T00"ALIDATED";(T0T1);"/";T0;" SECTORS"Z):"IT RETURN:  ";r)A$:A$(13)1740{)100)VL$"":E0:VLVL1:(F1$,1)""IW$((VL),2)"-")(F1$,1)""VL11850)(F$,1)"R"185eh80qȩqhY Y в``Y`WWWY`YWWMW)WWjWWWIWWjWWо` $WYYYL5XW W`W`W W W`` Y X XWL͜ Y83400:P0P1,(ML1536)256::3400:P,0::3400j MVML(35):SP$" ":3300SU8:SD$"0":DU8:DD$"0":F1$"INGLE ":F2$"ORMAL ":MN1"MUTIL V4.0":"O IMMERMAN":"NDRE ACHAT" ("...LANET NK.":!D:X$(((A$)),2):B!XB$"EST. ":XDU:X$DD$:550:DUX:DD$X$:100}!"[]INGLE OR []ULTI-ILE:  ";:X$""(13):400!F1$("INGLE ULTIPLE"F1$" ",((O1)8)1,8):F1$:!"[]ORMAL OR []OMPRESSED:  ";:X$""(13):400U,15,"I"DD$":":15,SU,15,"I"SD$":">&T00:T10:D0SU:D0$SD$:1800:E01700i&3,DU,3,"#":V10:EF0:T1:S0:TT0:E0&EF:"ONE.":50:1600&V11200&$EF0::"";SP$:"RACK";T;" ECTOR";S;&.VM01100'82,"U1";3;(DD$);T;S: NPACK ALIDATE: ";VM$(VM)V"[] NPACK AN IMAGE":"[] ACK AN IMAGE":M1$""(13)"[] ISK INTERFACE":"[] XIT TO ":MN$"":M1(MN$)200:220"";:I1M1:O0:IMNO("")"";(O);(MN$,I,1);"":I: ;"> ";:CO$""#>A$:A$""830,#HA(A$):A13900f#RA20CO$""" "A$A$" ";:CO$(CO$,(CO$)1):830#\A31A96CO$CO$A$:A$;" ";:830#fA191A218CO$CO$A$:A$;" ";:830#p830#1::""SP$:"";#CO$""100 (2,"B-P";3;0:MV1,3:(ML(33)):V11G(2,"U2";3;(DD$);T;S:2,E,E$,E1,E2:E0V10:1250j(E66E,E$,E1,E2:NENE1:V10(TTTT1:TT200(F1$,1)""1800:TT0:EF0(E66S255SS1:1040(E70S0TT1:S0:1040(1700(@2 $):B((X$,I,1)):((AB)((A 128)B))(O0)OI: I:O0400@ s B$"OURCE ":XSU:X$SD$:550:SUX:SD$X$:100 &""B$"NIT :"(X):""14);:1,0:1,A$:1 0:X(A$):X7X30"";:550 :""B$"RIVE: "X$:""14);:1,0:1,A$:1 -ENING ARCHIVE.":50*X1+lF$"":SUDUSD$DD$"RIVES MUST DIFFER!":50:`+v"ILENAME: ";:X$(F1$,1):X$"""?-";|+1,0:1,F$:1:X(F$):+X16(X$""X14)"ILENAME TOO LONG!":"";:1900++1900:VL0:F$""100,F$K GJIBINBECAEMGOCALIFALGMJACNABA"C0 "KNBHCAMJACLAOGOOBCCAOOBECAEMGOCAKNBHCAMJACJAPAMOBECAMOBECAMOBCCAMOBC"0 "CAMOBBCAMOBBCALIFAMNKAAAKNBECAJBPOOOBCCAKNBCCABIGFPOIFPOKNBDCAGFPPIF"0 "PPKFPPINBGCAKFPOINBFCACAEDCBEMMMPPKJCDIFPPKJLMIFPOKJAAINBCCAKAAAK00-MV1,3:(F2$,1)""MV1,255'- 2,"B-P";3;0:MV,3:(ML(03))O-%(MV)255"OMPRESS RROR!":1700g-4MV1,1:(ML(13))->TTTT1:TT200(F1$,1)""1800:TT0-HE66S255SS1:2040-RE70S0TT1:S0:2040-\:"ONE!A(A$):LA033505 I1LA2:X16(((A$,I,1))65)(((A$,I1,1))65)05 P,X:PP1::3310?5 ML8192[5 D((ML8192)256):PMLt5* A$:LA(A$):LA054 I1LA:X((A$,I,1))65:PPX:Y(P):Y32Y365> P,(P)D:I:33705H P(LAADLIFAIKGAKJPPINAPCAKNBCCAINBACAKNBDCAINBGCAKNBCCAIN"W2D "BFCAGAKOBACACAMJPPKJCDIFPPKJLMIFPOKAAALBPOCANCPPOGPONAACOGPPKFPPMNBG"2N "CANAAFKFPOMNBFCAJAOFEMMMPPKOAPCACAMGPPKJCCIFPPKJLMIFPOKOBACAOAPPNAAO"2X "KAAACAMPPPJJLMCCMINAPHEMMMPPCAMPPPINBECAKBKCBKMBBCANAADEMBKCBOOBBCALJLMCCKMBCCAMIJBPOIINBPOPADIKJ"_/ "AAINBHCAKNBECAMJIALAANMJIAPAAJOOBCCAOOBECAEMGOCAKAAAKNBECAJBPOMOBBCA"/ "OOBCCAKNBCCABIGFPOIFPOKNBDCAGFPPIFPPEMFBCAOOBHCAKNBECAMJIAPANHMJIBJA"/ "AKMJPPPAMPOOBECAEMGOCAMJABNAAPKNBECABIKF$",S,W":2,SU,15,"I"SD$":":15,DU,15,"I"DD$":"4,D0DU:D0$DD$:1800:E1700_,3,SU,3,"#":V10:EF0:T1:S0:TT0:E0x,2,"U1";3;(SD$);T;S,EF0::"";SP$:"RACK";T;" ECTOR";S;, 2,E,E$,E1,E2:E662110,E:E,E$,E1,E2::21KC"4 "AAIOBBCALNLMCCNNLMCDNAAEOINAPFGAOOBBCAGA" 4 ""U4 "CDDDDNLDGGDFFLDFDDHDDFDFDDDNDLDDFFDDIHDDPDHGDKHDDDHDDDDDIFDIJFDFLN"4 "RDDFDHHIGNDDFDHHLDDDDDDEFYHIFJMMUGSDEGGNGQIODDJ"4 ""4 PML::(P)764 "READING ML DATA..."4 A$:LLBPO"'1 "OGPONAACOGPPINBECAMJIBLACOKAAALBPOOGPONAACOGPPKMBCCAOOBCCANJLMCCPAAD"s1& "EMMHCBMOBECANAODKFPPMNBGCANAAFKFPOMNBFCALADKLIFAMBKNBECADIOJIAINBECA"10 "KAAALBPOOGPONAACOGPPKMBCCAOOBCCANJLMCCPAADEMMHCBMOBECANAONKFPPMNBGCA" 2: "NAAFKFPOMNBFCAK":1700/. "EMBJCAEMNPCBEMALCCEMJDCCEMKHCCAAAAAAAAAAAAAAAAAAAAKOAPCACAMGPPKAAACA"{. "MPPPJJLMCCJJLMCDMINAPEKOBACAOAPPNAANKJCEINBGCAKJLMINBFCAEMMMPPKJCDIF". "PPKJLMIFPOKJAAINBBCAINBHCAKJAAINBHCAKMBBCALJLMCCKAABJBPOIMBECAIMBCCA"/ "OOBBCANAADEM65532):P226ML9216:P45:6R P22ML8192:P42:6\ P61ML4864:P0:15:96f P246ML13312:P45:S6p P34ML13824:P45:r6 "UNSUPPORTED COMPUTER.": MJIBLACMCAMPPPKAAAJBPOOGPONA";3b "ACOGPPMOBECANAOOKFPPMJCDNAAEKFPOMJLMJANIPADICAMMPPKJPPINAPCAINBACAGA"3l "KNBECADIOJIAINBECACAMPPPINBCCAKAAAJBPOOGPONAACOGPPMOBECANAPDKFPPMJCD"3v "NAAEKFPOMJLMJAKANAMIEMMMPPKOBACACAMJPPKAAALJLMCCCANCPPMINAPHEMMMPPKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK \ No newline at end of file +ENING ARCHIVE.":50*X1+lF$"":SUDUSD$DD$"RIVES MUST DIFFER!":50:`+v"ILENAME: ";:X$(F1$,1):X$"""?-";|+1,0:1,F$:1:X(F$):+X16(X$""X14)"ILENAME TOO LONG!":"";:1900++1900:VL0:F$""100,F$hSE$""" GUESS YOU'RE DONE THEN":5:[r"HAT IS THE PORT":5000:PO$P$:(PO$)0SE$"":360o|SE$SE$":"PO$#5,A$:A$""390 MAKE THE CONNECTIONAT$"":XB0BA0XBBAAT$"S43="((XB),2)#5,A$:A$""406"Q GJIBINBECAEMGOCALIFALGMJACNABA"C0 "KNBHCAMJACLAOGOOBCCAOOBECAEMGOCAKNBHCAMJACJAPAMOBECAMOBECAMOBCCAMOBC"0 "CAMOBBCAMOBBCALIFAMNKAAAKNBECAJBPOOOBCCAKNBCCABIGFPOIFPOKNBDCAGFPPIF"0 "PPKFPPINBGCAKFPOINBFCACAEDCBEMMMPPKJCDIFPPKJLMIFPOKJAAINBCCAKAAA U$(34):5,"AT"AT$"H&D13&M13&M10CP";QU$;SE$;QU$+900m(P$,8)"CONNECT ""NABLE TO CONNECT TO ";SE$;":";P$:300~P((P$,8))XB9600430UM:UM3:(789)234UM9:435BAXB:UM19,1:5,"AT":9000:9000XB240043500-MV1,3:(F2$,1)""MV1,255'- 2,"B-P";3;0:MV,3:(ML(03))O-%(MV)255"OMPRESS RROR!":1700g-4MV1,1:(ML(13))->TTTT1:TT200(F1$,1)""1800:TT0-HE66S255SS1:2040-RE70S0TT1:S0:2040-\:"ONE!A(A$):LA033505 I1LA2:X16(((A$,I,1))65)(((A$,I1,1))65)05 P,X:PP1::3310?5 ML8192[5 D((ML8192)256):PMLt5* A$:LA(A$):LA054 I1LA:X((A$,I,1))65:PPX:Y(P):Y32Y365> P,(P)D:I:33705H P(LAADLIFAIKGAKJPPINAPCAKNBCCAINBACAKNBDCAINBGCAKNBCCAIN"W2D "BFCAGAKOBACACAMJPPKJCDIFPPKJLMIFPOKAAALBPOCANCPPOGPONAACOGPPKFPPMNBG"2N "CANAAFKFPOMNBFCAJAOFEMMMPPKOAPCACAMGPPKJCCIFPPKJLMIFPOKOBACAOAPPNAAO"2X "KAAACAMPPPJJLMCCMINAPHEMMMPPCAMPPPINBECA 900:P$"OK"203!, GET THE SERVER96:"OME SERVERS:"_@"IRC.NLNOG.NET PORT 6667, #C-64"E"IRC.FREENODE.NET PORT 6667, #C64FRIENDS"J"IRC.US.IRCNET.NET PORT 6667, #C-64"^SE$""::"HAT IS YOUR SERVER HOST":5000:SE$P$BKCBKMBBCANAADEMBKCBOOBBCALJLMCCKMBCCAMIJBPOIINBPOPADIKJ"_/ "AAINBHCAKNBECAMJIALAANMJIAPAAJOOBCCAOOBECAEMGOCAKAAAKNBECAJBPOMOBBCA"/ "OOBCCAKNBCCABIGFPOIFPOKNBDCAGFPPIFPPEMFBCAOOBHCAKNBECAMJIAPANHMJIBJA"/ "AKMJPPPAMPOOBECAEMGOCAMJABNAAPKNBECABI NP0:(2614)0NP20?BAXB:2576,10:2578,(59490NP):2579,(59491NP)e2582,170:2583,1:NP02582,154}5,"AT":9000:9000:"HAT IS YOUR NICKNAME";:5000:NI$P$:N1(NI$)NI$""" GUESS NOT.":P$" "NI$:600F$",S,W":2,SU,15,"I"SD$":":15,DU,15,"I"DD$":"4,D0DU:D0$DD$:1800:E1700_,3,SU,3,"#":V10:EF0:T1:S0:TT0:E0x,2,"U1";3;(SD$);T;S,EF0::"";SP$:"RACK";T;" ECTOR";S;, 2,E,E$,E1,E2:E662110,E:E,E$,E1,E2::21KC"4 "AAIOBBCALNLMCCNNLMCDNAAEOINAPFGAOOBBCAGA" 4 ""U4 "CDDDDNLDGGDFFLDFDDHDDFDFDDDNDLDDFFDDIHDDPDHGDKHDDDHDDDDDIFDIJFDFLN"4 "RDDFDHHIGNDDFDHHLDDDDDDEFYHIFJMMUGSDEGGNGQIODDJ"4 ""4 PML::(P)764 "READING ML DATA..."4 A$:LLBPO"'1 "OGPONAACOGPPINBECAMJIBLACOKAAALBPOOGPONAACOGPPKMBCCAOOBCCANJLMCCPAAD"s1& "EMMHCBMOBECANAODKFPPMNBGCANAAFKFPOMNBFCALADKLIFAMBKNBECADIOJIAINBECA"10 "KAAALBPOOGPONAACOGPPKMBCCAOOBCCANJLMCCPAADEMMHCBMOBECANAONKFPPMNBGCA" 2: "NAAFKFPOMNBFCA MODEM..." #5,A$:A$""203F 5,CR$;"ATHZ0F0E0";CR$;:900:P$"OK"203] #5,A$:A$""208| 5,CR$;"ATE0N0R0V1Q0";CR$; 900:P$"OK"208 5,"ATE0V1X1F3Q0S40=250I4";CR$;(19); 900:VR(P$):VR1.8"IMODEM INIT FAILED: ";P$:":1700/. "EMBJCAEMNPCBEMALCCEMJDCCEMKHCCAAAAAAAAAAAAAAAAAAAAKOAPCACAMGPPKAAACA"{. "MPPPJJLMCCJJLMCDMINAPEKOBACAOAPPNAANKJCEINBGCAKJLMINBFCAEMMMPPKJCDIF". "PPKJLMIFPOKJAAINBBCAINBHCAKJAAINBHCAKMBBCALJLMCCKAABJBPOIMBECAIMBCCA"/ "OOBBCANAADEM65532):P226ML9216:P45:6R P22ML8192:P42:6\ P61ML4864:P0:15:96f P246ML13312:P45:S6p P34ML13824:P45:r6 "UNSUPPORTED COMPUTER.": MJIBLACMCAMPPPKAAAJBPOOGPONA";3b "ACOGPPMOBECANAOOKFPPMJCDNAAEKFPOMJLMJANIPADICAMMPPKJPPINAPCAINBACAGA"3l "KNBECADIOJIAINBECACAMPPPINBCCAKAAAJBPOOGPONAACOGPPMOBECANAPDKFPPMJCD"3v "NAAEKFPOMJLMJAKANAMIEMMMPPKOBACACAMJPPKAAALJLMCCCANCPPMINAPHEMMMPP +P$" GUEST 0 * :OE NONYMOUS":600TPS$"ONNECTED, WAIT FOR . ? FOR HELP"CO$: 10000s 1000: GO START MAIN LPV --- TRANSMIT P$ TO THE OPEN SOCKETX(P$)0((P$,1))10P$(P$,(P$)1)]OP$P$:ML9:C8$(((MV8))!3)" ":"A$(20)A$;" ";:P$P$A$:5010"P$""50109"P$(P$,(P$)1):"  ";:5010T"p:: NEW INPUT ROUTINEj"z A$(13) 6250" A$(20) 6500" A$(34) 1000" (IS$)78 1000"A(A$): (A31 A127) IN LOOP +A$:A$"" 6000: WAS 30004 GOTO 1000 : REM ADDED FOR DEBUGGINGI800:P$""1000mMS$"":MC$"":MA$"":I1:LP$P$(P$,1,1)":"1100I(P$)1100$(P$,I,1)" "II1:1050.MS$(P$,1,I1):P$(P$,I1)LI -":10000R PS$"/JOIN #C-64 "CO$"HANGE CHANNELS.":10000E\ PS$"/QUIT "CO$"OGOUT AND EXIT.":10000pf PRINT"/LIST ";CO$;"IST CHANNELS."p PRINT"/WHO MASK* ";CO$;"SER INFO." PS$"NYTHING ELSE "CO$"END MESSAGE":100 PE)P$:PEPE1:PE25PE0P$"":. --- GET P$ FROM SOCKET PI E0:P$"":PH25PH0j*PHPEP$PP$(PH):PHPH1:4700:P00E1:: FAIL>PHPEP$PP$(PH):PHPH1:HE1: ---- GET E$ FROM MODEM, OR ERRORE$"" $)II(MS$):2950 h (MS$,II,1)"!"IIII1:2950r IIII1:29105 MS$(MS$,1,II):I A$(13)10000t "TREAM PAUSED. NTER ? FOR HELP." OM$""P$" "OM$:600 CO$;"> ";:IN$"":ITTI1000:3200 TIIT"ANCELL ("C") : "MA$:10000:440#C400PS$"RROR: "MA$:10000:6080X(MA$)N11(MA$,1,N1)NI$MA$(MA$,(NI$)2)rPS$MA$: 10000:6080} CMDSMC$""1400(MC$""10002MC$""2000<MC$""2100F 6080 IN$"WHO"P$" :"AA$:600:6080:!PS$"NKNOWN OMMAND: "IN$". RY ?"CO$:10000:6080f!PS$"OINING "QU$A$QU$:10000:AA$A$!P$" :"AA$:600!E$"":CC$AA$:1000!"?  ";:P$""!A$:A$""5010!A$(1 ML6:P0(MV2):P1(MV4):P2(MV6)1PL(MV0):CR(MV1):C8(MV8)FP00P2C8985VP10P$""gP00CR0xML12:P$""PS$"-"CO$:10000:5,"ATL":945PS$"XPECTED "E$", GOT "A$:10000: THE MA  3400 (IN$,1,1)"/"IN$(IN$,2):3500B CC$""PS$"O HANNEL ELECTED! RY ?"CO$:10000:1000a P$" "CC$" :"IN$ 600:PS$""NI$": "IN$:10000:6080 E$"":1000H PS$CO$"-------------------------------------- ,2)::E$"OK":VR3E$C8$'b5,"ATS42=";C8$;"T+";QU$;P$;QU$^lML:P$E$P$OP$:PS$"XMIT FAIL"CC$:10000:600dv --- GET NEXT FROM SOCKET INTO PP930P0PP00PS$"NEXPECTED PACKET ID: "P0"/"P:10000:P00PP$( I1:20102900*PS$""MS$": "(MA$,I2):10000:6080<4PS$""LP$"":10000:6080~2900:PS$""MS$" HAS LEFT THE CHANNEL."CO$:10000:60802900:PS$""MS$" HAS JOINED THE CHANNEL."CO$:10000:6080T II2^ II(MS 1:P$""1000VI(P$)1200`(P$,I,1)" "II1:1110@jMC$(P$,1,I1):P$(P$,I1)XMA$P$:MC$""1000|A((MC$,1,I)):A48A571300(MC$)1(MC$,1,1)"0"MC$(MC$,2):1220C(MC$)C430C460PS$"AD ICK GIVEN 00 PS$CO$"---------------------------------------":10000:6080B X0:I2(IN$):X0(IN$,I,1)" "XIr :A$"":X1A$(IN$,X1):IN$(IN$,1,X1) IN$"JOIN"4000 IN$"QUIT"P$" :":600:9100 IN$"LIST"P$"":600: MLE$""P$E$ 927 KPS$"OMM ERROR. XPECTED "E$", OT "P$CO$"":10000: GET PACKET HEADER, SETS P0,P1,P2, RETURNS P0=0 IF NADAML12:E0:P00:P10:P20:PR0:I0:I00:I10:CR0:P40:P50:C80P$"":5,(17);  ED"::1000N A$:A$""3100b ITTI1000 A$(13)33006 A$(20)(IN$)03230b IN$(IN$,(IN$)1):"  ";:3100 A(A$):A32(A95A193)A2183100 IN$IN$A$:A$;" ";:3100 IN$""" "::1000 IN$"?" MC$""2200PMC$""2300ZMC$""1000n5uPS$LP$:10000:6080cvPS$"NKNOWN: '"MC$"' '"MA$"'":10000mw6080wx ...OM$MA$:P$" :"MA$:600:E$"":1000I1:MA$""1000(MA$,I,2)" :"2050I! + (A192 A219) 6070" 1000"IS$IS$A$3#"";IS$;"";CO$;: 1000N#j:: RETURN IS PRESSEDY#t 6750m#~ IS$"" 1000#IN$IS$:IS$"": 3305#d:: DELETE KEY PRESSED#n (IS$)0 1000#xIS$(IS$,KKK!$#5,"ATZ":9000:9000:5:$%' ** CLEAR BTM 2 LINES, ADJUST LINE LINK TBL, ADJ PS$, ? PS$F%' I1 2: 781,22I:59903:r%$' ::: FOR I=1 TO 7:POKE216+I,132:NEXT I%.'PS$PS$(13)%8' (PS$)255 PS$(PS$,254)(13)%B' CO$"KKKKK!(IS$)1)# 6750: 6080#^:: SETUP BTM 2 LINESI$h" ";}$r" ?=HELP ";CO$;$| 2023,160: 56295,14:$(#TTTI100$2#ML12:TITT9010$<#KKK";PS$%L'6750:%Pß5,2,0,(8) +&Zá#5,A$:A$""A$;!&dáA$:A$""5,A$;-&nÉ 50010^&U8:F$"IRC":1,U,15,"S0:"F$:1:F$,U:F$,UKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK \ No newline at end of file diff --git a/cbm8bit/prgdocs.txt b/cbm8bit/64net_prgdocs.txt similarity index 94% rename from cbm8bit/prgdocs.txt rename to cbm8bit/64net_prgdocs.txt index 79dc197..ca83512 100644 --- a/cbm8bit/prgdocs.txt +++ b/cbm8bit/64net_prgdocs.txt @@ -1,8 +1,8 @@ -/******************************** -/* C64Net WiFi Firmware Software * -/* for the Commodore 64 and 128 * -/******************************** -///////////////////////////////// +/***************************************** +/* C64Net WiFi Firmware Software * +/* for the Commodore 64, 128, and VIC-20 * +/***************************************** +////////////////////////////////////////// This disk includes applications written to work with the C64Net WiFi modem running firmware version 2.0 or better. If this is your first time using the modem, you should start with the configure program to get your modem connected to the internet. You can then use the FTP, TELNET, IRC, WGET, and other programs whenever you wish to communicate with the internet. The disk includes numerous machine language binaries (pml64.bin, pml128.bin, pmlvic.bin, x-xfer64.bin, x-xfer128.bin, rds64.bin, and up9600.bin) which are required for some the several programs provided. @@ -460,3 +460,29 @@ $c803 Receive Checksum (download) $c806 SendFile (upload) $c809 Receive CRC (download) All functions require the modem opened to channel 5, and the appropriate file on channel 2. Also be aware that it will copy a small 16 byte bit of bank-switch code to $1BE0. During transfers, the C= key can be used to abort. + +SWIFTDRVR.BIN +************************************* + +This file is the SwiftLink/Turbo232 BASIC wedge driver for the C64. Use SYS51200 to activate. After this point, all device 2 access will go to the SwiftLink/Turbo232 cartridge instead of the user port. +The baud rate codes used in the OPEN command are compatible with the C64 KERNAL, with additions: +00000 (bit value of 0) = Nonstandard (User-Defined) Rate (Not Implemented) +00001 (bit value of 1) = 50 Baud (Not supported) +00010 (bit value of 2) = 75 Baud (Not supported) +00011 (bit value of 3) = 110 Baud (Not supported) +00100 (bit value of 4) = 134.5 Baud (Not supported) +00101 (bit value of 5) = 150 Baud (Not supported) +00110 (bit value of 6) = 300 Baud +00111 (bit value of 7) = 600 Baud +01000 (bit value of 8) = 1200 Baud +01001 (bit value of 9) = 1800 Baud (Not supported) +01010 (bit value of 10) = 2400 Baud +01011 (bit value of 11) = 3600 Baud +01100 (bit value of 12) = 4800 Baud +01101 (bit value of 13) = 7200 Baud +01110 (bit value of 14) = 9600 Baud +01111 (bit value of 15) = 19200 Baud +10000 (bit value of 16) = 38400 Baud +10001 (bit value of 17) = 57600 Baud (Turbo232 only) +10010 (bit value of 18) = 115200 Baud (Turbo232 only) +10011 (bit value of 19) = 230400 Baud (Turbo232 only) diff --git a/cbm8bit/c64net_geos.d64 b/cbm8bit/c64net_geos.d64 new file mode 100644 index 0000000..18fde5e --- /dev/null +++ b/cbm8bit/c64net_geos.d64 @@ -0,0 +1,349 @@ +KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK"@0   12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 0--------1--------2--------3--------4---------5--------6--------7--------8--------9--------A---- Hy0xhy yH y ݩݩ yyyNyyy yy0h y(``x yH yH7ЩЩЭ0)JzJzhh yXLIz "yyy yy5y`z`z +{ y |z My+{ y z` + +ate0r0qy`H yH yܭ ܅ܭ h yh`PZ` /y Myz X| y`z`x| y |z Myz` + +ate0r0q1s43=4800 +at+printa + +ate0r0q1 +at+printa `l0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345  em from http://electronicsisfun.com or a suitable facsimile running Zimodem firmware version 3.5.4 or better. Use AT&U to make sure you are running the latest version of the firmware.  @  Lastly, the driver requires a CUPs server that permits remo  terminal program to prepare your modem to use the driver. The first step is to make sure your modem is set to 1200 baud. Use  @ATB1200 to ensure this. Next you will tell the modem your printers host, port, and url and also print a test page. To  If a page does not appear, or you see ERROR instead of OK after 5 seconds of waiting, then you should check your CUPs or printer settings. @  Once your print test is successful, save your printer settings to the modem with  @AT&W .  @Using  which is why you must save your printer url to the modem before using the driver. It then converts the GEOS image into a 640 x 720 Sun Raster (RAS) file and transmits the data to the modem. The modem then converts the image file to an IPP request anH0 @This Page Intentionally Left Blank  @ ?@! က͉ݐ=yqa y~ras4c64net 1.0 Bo Zimmerman RAS CUPS printer driver for c64net-wifi modem http://ctcug.coffeemud.net LzLzLL{L{L.|L5|LR|L|`~`ɀ`@OP `O``z py ݩ ݩy) ݍ` +yyy Izy yH yyyhLyT4)6y0"yݭyNyyyPyݸPݩ ݩy` 1 +at+printr + +ate0r0q1s43=4800 +at+printr Yj /y My{{{{m{{m{{z zyy{e{eI ye{{{{yy /yz` /yzz My | /AWrite Image V2.0geoWrite V2.1  ...݅ ( >1 59 +..  9 < <` " . +r " `(@1ʎsAڢ9 V&  Hras4c64net printer driver  @(c) 2020 Bo Zimmerman  Requirements:  This driver works in the C64 and C128 versions of GEOS, Wheels, MP3, and gateWay. The driver also requires a C64Net WiFi mod te IPP connections, or a printer which supports both IPP and Sun Raster (ras) image printing. You will need your IPP host, port, and url below. The url might look something like 192.168.1.50:631/printers/hp_laserjet  @ Modem Setup:  You will need a  do this, enter the following command, substituting your CUPs/IPP printer url where appropriate:  @AT+PRINTP: host @: port @/ my-printer/url @  Now @  just enter some text and wait a few seconds and a test page should be printed.  the Driver:  The driver has been tested with geoWrite and geoPaint (monochrome only), but should work fine in all standard GEOS applications that use your normal printer driver. The driver works by sending  @ATR0Q1E0 and then  @AT+PRINTR d transmits to the CUPs server (or IPP printer). This process starts at 1200 baud, but peaks out at 4800 baud -- faster a normal IEC printer. Contact me at bo@zimmers.net with any questions. @  0 0--------1--------2--------3--------4---------5--------6--------7--------8--------9--------A---- 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345 3 4 5 6 7 8 9 10 11 + ' '  C(-qSp : # L'(-qfp :  - - .-? NL +atb1200 + + + + +Exit to deskTop?Disconnect and Exit? ' '  - - . L'(u C(`u --Å Nu --Dž N`+++ath0 +vH5܅wwvh |>~ffcff~ f<80 ~~ v f|<>` n~|`fxxn~ p`k~f|f|<ffk<0> ~|`>f>f|8lfffff`ffkf`f~ffxffff`<fff f|fff0f`fffffbfffl``ffll`  "$&(*,.02468:<>@BDFHJLNPRT\dlt| $,5=EMU]emu} %-5=EE3 ?f 0`` > Ì7acð`gx~ap3À 7a?`gx|`~ 3s`xya< 00` o$ J©` pp #Lȱprɠr`ȱpɠaz8 pP`(( `'`'`%؅ x%`i؍(i&(&` L File Open Failure.Load Complete.| `` `A[i`a{8 `\[ ^م V­ n`\[ Т$wv $Ѣ`  xL(=6 5  ^$wv}$yx V­ n` v 5Ч`x  xɠ $ɠ$`  Cx " &vG @ =@G GGSX\`sX{`SX\`sX{`PRGSEQansipetscii  P `+ V `  B G"  + qPp \ `nwv  q:pPש qdpP̢`Please select an option:new work fileexisting fileto deskTopOn Disk:Please enter new filename:Ent  rsL `  LperrqessrrsLB!  !L  pqspr pqP`  B!L%% `$ `$$$ Յ + `$$$$$`( 6$qpn  ?`  (% ؅ + Щ(( {$L" p "pLqsprr " rsL<#`  4#LperrqessrrsL#  ^#Lp "qspr pqP`  #L T$((% ؅ + <(u(tpt(tȭ(t((& ؅ + `/ K _On`Pm|aDisconnecting...`  On_`  .qp { L' '( A   P LBuffer Empty?!  .qp { L' '(  D !H!  \Ph! <~<<p<<|ff ffff`ffffff``f|<|> cff`flkff|>`f<><>00 fF? 0`<~~<<<<<p|<><f<f6f ~00|<`xJ xLJa3sa`xLJ77`cxLJal7f`gLJ31x3x{30 LL qp { nL | `$qpr !qp \ nL 5 "P쩁p #qp \ nL Buffer Empty?!File Create Failure.Save Complete. gL $qpr } qp \ nL   P󩁅p #q%p \ n 8 7 + `0- P&m[\8[i  *\`xxi  G +  *x`xxxͩ`$2@N\jyx\exxyxvv`Lڍލ$wvv ٞٳٳ?03 `| A   + `|  6`   + `|  6`  L©L¬ H h er new file name:Insert new disk into disk drive.ERROR: File exists!ERROR: Disk full!ERROR: Cannot create!qprn `%%%$ Յ +  !`  } L v!$$H$ !PȌ$h`qspr  r ݭ(( {$ å(( $r(s(r(( xrȭ(rȭ(r((errs $ c$((& ؅ + ` rr !L% %%` T$ o$m(((ą c$%%(  La (08@HPX`hpx (08@HPX`hpx (08@HPX`hpxffb< 0<<<~ + )L,© Z     L `-=L` P deab`ȝP `8 +MN `Ǎ  ? +  +  zb1200  `x\!uX)  `` C(ͧ  +5" )#Ӯ!H  hH hH h` +P`Hh +` xPp @ {}@ {8) u`H(h`L `  +8)8(α8 L <  ee eee@88 ~~8(8  ©ee`P ` ~ m~~L; 8~~`B!MMM `T! 2!`M`M MM ` " ' `vL! 'Lh"~ H~H0H  h +hPhv OPQRSTUVWXYZ  !"#$%&'()*+,-./:;<=>?@ + [\]^_`ABCDEFGHIJKL  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ %  `` + + + +``L'L'L'L'L'L'L'L' L' +L' L' L' L'L'L' geoCBMTerm 1.0  For C64Net WiFi Modem By Bo Zimmerman bo@zimmers.netbL L'` [ L'( ratetermx-modembufferexit }( +` }( N( )`)Ņ` }( N( *9`)݅` }(*E` }( C( *`*` }( +`*х` }(+"`+q)y+)+connectphonebookq)}-disconnectUFn*,*,* .startloadsaveviewdumpstop.+,#.+,to deskTop ***!!+** `!` L L L  L L  L  +L +L a*l**a*`*l*` +L 8    8 )  D K/ 34 65 8 +7`P `_pqrsM  $ م N` atze0v1x1f0q0r1&p1i0  @  qp,sr,ut'wfv # Y( ,/E/E$   +%    + + 8!!   ~`ɀ`   'L,­~ ' ' ` &` ` at $10) +&?&@) +O&?O&@`LH),h0HE/ PHE2֍ h) E~  +` L ~` 2)eɏ(m(m~~` 8@8 ~~8(8 8 ­8~~`  +` <  e ` 34 65 8 +7``8(α~8(~8)`L  `~Lg ee~  ` 9 `  +8~' L!`ܭܪ)I5) `ܭ) ܭ)$  %  ``~~܅܅~~~ V!`  !"#$%&'()*+,-./0123456789:;<=>?@[\]^_`ABCDEFGHIJKLMN MNOPQRSTUVWXYZ !"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\]^_ABCDEFGHIJKLMNOPQRSTUVWXYZݡ ABCDEFGHIJKLMNOPQRSTUVWXYZݡ '&&&&& '&/'4'>'&&'&&&C'H'M'R'W'\'a'*'&9'%' L& A LLL; LLj L )JJJJ`) + + + +`$  ` P P {`P `L`L`()(((LQL ' ' (((`   '(((`(@)(@ )(@:)(@T)(@`))@z))@)geosconnectbaud ,*,*#,$*(,30012002400480072009600*****$*Fn*z(0w`*+k*+w*+*+ echo on echo off ANSI term ASCII term+*+*+uploaddownload*z(not connectedT*:+*n+*c++,. ++  +L x***x*`**`L-,L-,L-,L-,L-,L-,͛Lp {, +, ,  *,݅ N  , *  L ++*+*` ' '   ` '(  `+*+* +P뭛i + {,A*@*` +atb2400 + + +W L dMWWC W#LTJLSJLSJLSJ`WW MM MWLgKL_KW WڭWL:KW MW MWWWWW ~MWLKWWIW MW WzXLKzXW{XWWLKW)WWW JJWWɀ dM MJ W# Sr0[z8冧@a#펯Hi ++ԷqP3ܿyX;"`A* hI2Qp:Yx -No%Fg=^5wV˨nO, àfG$_~]|6Ut 0@P`p2"RBrb$4dtDTdem status message. Also, by default, the program starts in DCD-active mode, and local echo turned on. If your modem does not support the DCD signal, you will need to select the geoTelnet application in deskTop and bring up the Information page for the app. At the bottom is the information area which, in some versions of GEOS, can be edited. If you can edit this area, add the -D characters to this area. This will de-activate DCD signal-sensitive menus.  MegaPatch 3.0 64, MegaPatch 3.0 128, gateWay 64, and gateWay 128. It requires a 40 column composite monitor, C64Net WiFi Modem, disk drive, and mouse or joystick. A GEOS compatible ram expansion unit (REU) is optional, but beneficial.  @Features inctems) desk accessory access internal Phonebook for storing favorite telnet hosts quick connect option Echo (duplex) toggle, and on-the-fly term-type changing. X-Modem upload and download. 64K input buffer support, with LOAD, SAVE (as ANSI or PETSCII), DUgithub.com/bozimmerman/geoModules You can link directly from the given object code, or built from scratch, provided the necessary geoModules include files and object files are present, and the F/geoTelnet.rel font file.  @Configureing and Using:  t is a wifi modem that supports AT commands, a DCD signal that goes high when the modem is online, and defaults to RTS/CTS flow control. However, the program can also be used with any C= user port wifi modem for the C64 or C128 that defaults at 1200 bauset the baud rate to 1200 and issue the following commands: "atze0v1x1f0q0r1&p0i0". This command resets the modem, turns off modem echo, turns on verbose messages, RTS/CTS flow control, standard cariage returns, ASCII command mode, and then sends the moWLTJWLgK dM PJW W#P WC W#WLTJ W#L~KW W# W# W#L~KW LW dMWWW`W L dM KqKpKsKrKuRJtQJwKvKLqLLSJLSJLSJWW`WW` KWW KWWWW +WɀPWWWWɀ W ܍ܭhW`WWLM K 5"`W`WW !WW M`H5ܭ h`HW $h`WWh`WWWmWW`WWWMWNMWWMW`!Bc)Jk1sR9{ZbC ǤjK( Ϭ6&vfVFHXhx(8ZJzj +:*뛋l|L\,< ݭ~n^N>.Ͽ0 P@p`"2BRbr4$tdTD&6fvFV陉XHxh8(JZjz +*:ͽ|l\L<, ߯n~N^.> +? '$ $$$$4$$$4$$$$$$<Write Image V2.0Red StormgeoWrite V1.1 This file was created with Wrong is Write. Written by Joe Buckley.@0 @geoTelnet  (C)2017 Bo Zimmerman geoTelnet is an ansi terminal program for connecting to telnet servers. It is compatible with the following operating systems: GEOS 64 2.0, GEOS 128 2.0, Wheels 64 4.0+, Wheels 128 4.0+,lude:  80 column text output in a 40 column monitor screen. ANSI X3.64 terminal support, or as much I could manage. ASCII terminal support (no Extended ASCII) TELNET code support. Native support for 300-4800 baud (up to 7200 for SuperCPU enhanced sysMP, and VIEW.  @Building:  geoTelnet was built using Concept for Wheels 64. It might also build using geoProgrammer, but hasn't been tested. In addition to the given source, you'll require several components from the geoModules project: https:// geoTelnet is optimized to work with the C64Net WiFi modem from www.electronicsisfun.com. The next best option is a user port wifi modem that uses the "Zimodem" firmware. Information about this can be found at github.com/bozimmerman/Zimodem . Next bes d. The C64Net WiFi Modem can be constructed from schematics by going to: http://coffeemud.net:8080/ctcug/index.php/Inexpensive_Wireless_Internet_Modem It can also be purchased from: https://electronicsisfun.com/c64net-wifi At startup, the program will  WWWIW MWzXW WzXW{X oMWW W#WȌWWLqL M MM M $(C0W WWLgK KP KW W PJ ~MLTLWWH W#hL~K ~M dMLKWWWWWWWWWW`H5W ܍h`WH5  +;f5W@~1;`i f5~~~6; ;; ; + *;W~:W ;W:W`;W:W30V;3BW;iWB~ v<WP96V~~7V~:m7Vi` ~ML!V`V  S` + }+  =`! OLS?E4>V4>\[ 2E> V­ >`\[ EНWwv &?WѢ` c= L+> F x>L(8>=@>Dxvv`W`WW`WWWWWWqpr g ]=@q.p TL ]= K A@qOpAsrAuitBwZv K ]=!  ]@qp TP ]=!  ]@qp T c= `Upload Complete!Upload Cancelled.File failure. UploadWxWWWWWWWx`W B` [ByS<pP8q`h`h`hkeErrors: kVBytes:` pA yDUploading... B` pA yDDownloading... B` 9  +Ph $WWW Ч``WW`WWWWWWqpGr Dq

DSX\`sX{`SX\`sX{`PRGSEQansipetscii`GAFFInsert new disk into disk drive.ERROR: File exists!ERROR: Disk full!ERROR: Cannot create!1  $(,048<@DHLPTX\`dhlptx|  "@Dꨨ*ꊢJHBJLNJJD Z`B".J" +NJJJN ^J@$,ΎꮎNJ&BJJnJ P@ PWWLJWWqLJpKJsOJrNJuRJtQJwKvK``<3fc33>c3?fc0fc3fc3?9     p 0{6͙6͙36͙x + Y  +e + + LV9 +e + + L:9 +e + + L8 +e + + L9`cWWbWd`bW8bWcW 8L;cWbW6Vȱ7V`W` 9L;mbWbWcWcWbW6Vȭ7V`cWWbWd`7VU : E;`` x:` :S ; 8L;` 9;W:W `V: =`;W:WV:ee` x: :ȢU w " 3 L+;qqiq7V6V v< ;*`#qi qq; 9;qqiq v&m[\8[ ?i  *\`xx ?i  G +  *x`xxxͩ`$2@N\jyx\exxy cancelled BWpWW`WsmWWW`WWWW߭WyWx`{zxzxyz{yWxW`WWW8WWWWWWqWpxsr ,qWpWWWW ]=`WWWWW` O@LAWyeW ``h Ѕ ؅ + "`h Ѕ ؅ + *8`` + }+ GFE EFE E B`! OLBCWwvvEVE 2EWwvEWyɅx V­ >` Ev QEFile failure. Download cancelledHWyWxWWWWhx`W`W``qpWsWrrrs m +`WW ]=` BWwW D`W B` D DL©L¬ H h͉P `E( V ` 4GBx  F FF F + `GEFE 6`EEE EE E + `GFEE 6`Please select an option:new work fileexisting fileto deskTopOn Disk:Please enter new filename:Enter new file name: $(,048<@DHLPTX\`dhlptx||JD$ή*ꪪ `H@@ PdB *J(@ @@@ZBd"@Bꨨ*ꪨJHB@.JJꪪM^`BF̎$Ȭ,JNHBJJNJ, Z$Bd ?!#?' ? ? ? ? ? ?@ +cc=gycgycgcgxpcyc=o3  +OO $MO ¥~~܅܅~~~ X OOO`~H0H h hvL OOO`?8 !r s#s$xH5V# U#V#"$݈ ݩ Ȍ$)$ $ݭ)  )h,$P J"h` + +QP TcdiDG14  4B`eAD/2P`UUU`UUUUUUU UUUUUU`UUUUUL$U[ UL$`;U`0:L%8 U PP.L&%&UUL&%&UUL&L&&L&(L&2L&8HUh 'Ph'HP)PhmPPL&8('HP)PhmPPL&p@0 P`@L*' UL&%U U [UUUU UL&%UL&%UUF ( F L&%UU0 B)O!)e! !! ! + $L&%' )!O! !! ! + $L&% ! L&%L&%L&% 9OO)e! !O O +L$ 9OO)eO O! ! +L$! <?e e q pO < Oe)!L&% <  qp8 =PPPP(e8 = +UL*L&%P( 9!!8)! !! ! +L$UUL&%Ob,@&-geostelnetbaud ratetermx-modembufferexit ,)` , + -2`-W` , + -˅`-o` ,-ׅ` , + .$`.G` ,U .`.c` ,.`+qC-3L-4connectphonebookqc-3disco.../.'/.j0.0startloadsaveviewdumpstop.q2.q2to deskTop .k.j.  .k.j. ` `G`` `A[i`a{8 ` + }+  /qp sL >GFEEFEE B`plete. + }+  /qp sL > + !   POOO GOOOL + }+  /qp sL > + !  H  Ph OOO GOOOLO H1LO H1L --O*-`*-`` +atb2400 + + + }+  +2qمp 2# L+2qp 2 q3 3 32Ņ ' L +atb1200 + + + + +Exit to deskTop?Disconnect and Exit? + }+  q3 3 3 L+u +`u T33I ' u T33M ' `+++ath0 +vH54Enter the host:port to connect A7 +F` + }+ ! Y t$7V6V4 V­ w " L+ b9 8P 9 8PL:6t 5]5/6F6t,64695Q5Telnet Server Phonebook =0> ;``hX`xH5U#V# ݭ hX`$$$`xH5U#V#hX$r$sqpL! m$$0H5 h$`H5$ݭ$ݭ$ݍݍ$ h I$`H5)H"h`H$0xh$H5 ݩݩ $$$N$$ #$ÅH5$h``xH5HTT$7ЩЩЭ0)##hЭTThXL#@$͓$8`H$T$$$$͓$ I$h$`H,$H5 h,$P J"h`H,$H50HUhUUUUU`UUH + + +UhHyUUhyUUhyUUU` &ȹ &UL$ & +$&%ȹ$&%UOOO $OOOOU`mHfABCDsuJKhlpEFGdLMPnP&''*'''''(.((q)w)z)})'''c')d*P+[(UL&%U PPL&%U [UL&%','L''['L'''L''э'L'UUU ,L&% #''L' #','L' #UL&%U POUOUOUPUPU&U%UL&%UOUOUOUPUPU&U%L&%UL&%U (  B)L&%L&%L&%L&%UU! <?e e q pO < Oe)!L&% <  qp8 =PPPP(e8 = 2*UL)L&% " 9OO)e! !! ! +L$UUL=OL]+`PP =PP {`PP =`wL5"`wL5"`UL,,g,f,F,LQL }+ + 2,1,,0`   +2,1,`p,@,v,@,~,@,,@,,@,,@ -nnectU;c-1-1-1-1-1-130012002400480072009600------;c- ,Gl-21-=1 .a1.l1 echo on echo off ANSI term ASCII term+5.v==.BuploaddownloadS. ,not connectedTWqpr /q…p TL > . P쩁p +/q؅p T ]=L >Buffer Empty?!File Create Failure.Save Complete. + }+  =`Wqpr g0qFp T ]=L >  P󩁅p +0qZp T ]=L >File Open Failure.Load ComP w1LP w1L +..P* +.`*.`L1L1L1L1L1L1͢$Lp 2 +h2 h2  !2c ' 2c  q# )2 ! L +-载-` }+ +  ! !` + ! `-载- +P뭢$i +O$ 2-- ܅wwvh`/ K _On`Pm|aDisconnecting...`  On_` + }+ UqppP4D V­U 3 L+PP49 ' 49 U ' U 4@ ' 4@ ` +atd"" +d6 _4,, pP $h(`xHH5 C)B)UΙ$0&$ݭ$N$$$hh@$hh@ݩ ݍ$L<#L<#ݩ ݩ ݩ$L<#Jn$Θ$L<# ݩݘH$$T$h$͔$,$ ),$P hh@>hh~`LHH5,hLL"p$ + +$ݢ ɨ ɀJݒNNɒJjɀgeoTelnet 1.3 Bo Zimmerman Telnet: c64net-wifi modem http://electronicsisfun.com http://ctcug.coffeemud.net   ? + -L` ' &' =' P' O]5.J?JPJaJrJJJJprog infoqprn ` ą +  `  gL `H oPȌh`qspr  r rs  e  å | rsr xrȭrȭrerrs  M  Dž + ` rr L ` > Y m M  Dž + ` pp +Lȱprɠr`ȱpɠaz8 pP` ```Dž x`iǍi` `$  `  `qm$ p L   Ϋ ` $ Ν `` $ `1 H N hk    $ i$ `LN ¢r  rmpYVqp ;`Y tXW҅ ~ w©T`  9 LqpT`V  Sr)pT8 + F LQo F LQLO H1Ѕ F LQo F LQo  F LQL O H1Ѕ F LQ W#`D SSS m$`V  4 `S`S W#̟SS I$`OvO7`HUU 9h!! !! ! +/ $/pPPP {`G &` geoTelnet 1.3 For C64Net WiFi Modem By Bo Zimmerman bo@zimmers.netpqrsS ! ~M ' ` ! !` atze0v1x1f0 POOO`HOOOh OOO`HUhL9%!hL<hL$ LM +LdL ` EP)%&O!!` MLd!!P&P%`!2)ePɏP(mPP`P?!! /8)P8(PPP&P%(m%%&8 ©8) %%&O`!O`P8(PP%8(%&8O)O`O!`)mOOPɏP(mPPPȱp!)LQp)!L}} }ԟ `ԟ ԟo!!LQE 4 +LQ P 4 LQLQansi 1200,1200p 4 LQPo >L,© ) O 1 L Lɀ`geoTelnet 1.3 -ȱdwDw`h`H(` x +o` xe==>i>>H=H +oL hhh=h>HH + +=oL8 7  ;©8iH /h + + + +88i*` / VLH hLR + + + +eiiHH f ©g ~hh5HІ ¦5hІg ~ {DžL`  LperrqessrrsL,  L  pqspr pqP`  ,L ` ` ą + `` qpn  ?` Ѕ ݭЩ e L p pLqsprr rsL& +`   +LperrqessrrsLm +  H +Lp qspr pqP`  m +L >  Dž + <utpttȭt Dž +  Y J©`È ` L g Lj!)Lj}J g iÈ N ` L  H х  ©k h ©`   ` N Hh  `` Ϋ  $ Ϋ Ν ` չ rq r`͉ L  LptJJJJp tp`)iQiRhhLr  rm`qpXW + ҅`qp 9 qp` U O H1P w1 U )2 `  | +L,©!Ǎ!!!!?!!!!!!8!!!!!!!!8!)!P  Kqp2sr2utwv / + PP OOO3!4!6!5!8!q0r1&p0i0 atzb1200 OOx wX) OOO `xOOX ©`` H hO   +   ;`PuuH W#[ W#hH W#h`ABDCOOOP(m%%&` =xPp =@ {}@ {OPP!8) "`HPPP(h`"ȸP`ee (m%%&`O!O!`mOOmOOO)%&`!O!O`O8OOOO) %%&`!OPPP #`!O!OP&P%`Ro! !!`!q!pp!LQɀeppqL^ȱp! L6?LV pGG  L=LV pGG  2L_LV p@  TL~LV p@  sLfq +qIpHs +sr u +untmvwvHvwh `  L.AA2A geoTelnetcgeoTelnet-Docc:geoCBMTerm +c] +geoCBMTerm-Docc  ras4c64net c , ras4c64net.doc c4KKKKKKKKKKKKKKKKK + BV pP 1qP 1 U 2 `  | +L,©!Ǎ!!!!?!!!!!!8!!!!!!!!8!)!rP  Kqp2sr2utwv / ], sPtP nPmPlP3!4!6!5!8!! L%L%L% 9nPnP)e! !mP lP +L$ 9nPnP)emP lP! ! +L$ )L%L%L%L%3V4V! <?e e q pnP < nPe)!L% <  qp8 =vPuPvPu q0r1&p0i0 atzb1200 kPjPx wX) mPlPnP `xkPjPX ©`` H hpP   +   ;`qPuuH #[ #hH #h`ABDCnPmPlP ݩݩ +%%%N%%%h(`xHH~ E)D)W +%0(%ݭ%N%%%hh@%hh@ݩ ݍ%Lc#Lc#ݩ ݩ ݩ %Lc#Jn% %Lc# ݩݘH%%U%h%%,% ),%P hh@>hh~`LP(m%%&` =xPp =@ {}@ {oPvPuP!8) "`HvPuPsP(h`"ȸP`ee,%H~ h,%P j"h`H,%H~)h,%P j"h`""܅ `HHLi#~#H}#HLi# + +QP TcdiDG14  4B`eAD/2qP`V3V8V`-VVVV(m%%&`mP!lP!`mlPlPmmPmPlP)%&`!mP!lP`lP8lPmPmPlP) %%&`!nPvPPuP #`!mP!lPvP&uP%`Ro! !!`!q!pp!LQɀeppqL^ȱp!oP-V`mHfABCDsuJKhlpEFGdLMPn&'''' ((r(()))));(K([(')*+(3VL%4V sPtPL%4V5V sPtP.L%'9V3VL%'4V9VL'L'&L9'(L'2Lc'8H9Vh~'Ph'HtP)tPhmtPtPL'8('HtP)tPh F LQo F LQLpP 1Ѕ F LQo F LQo  F LQL pP 1Ѕ F LQ #`D TTT $`V  4 `T`T #TT z$`iPvnPmmP;VlP:VnPVuP=V&@V%?VL%;VmP:VlPVvP=VuP@V&?V%L%3VL%4V ( F ( F L%3V4V0 )nP!)e! !! ! + $L%' )!lP! !! ! + $L% 7`HV3V 9h!! !! ! +/ $/psPPsP {`H  &` geoTelnet 1.3 For C64Net WiFi Modem By Bo Zimmerman bo@zimmers.netpqrsT ! M ' ` " !` atze0v1x1f0) ݍhX`xH~}#~# $ ݭ hX`%%%`xH~}#~# $ $hX%r%sqpL! $%0H~ h%`H~ %ݭ %ݭ %ݍݍ% h z$`H~)h"h`H%0xh%H~ PmPlPnP`HnPmPlPh mPlPnP`HVhL%!hL<hLT% LM +LdL ` EtP)%&oP!!` MLd!!vP&uP%`!2)evPɏuP(muPuPvHH~,hLl"p% + +% #%4H~ %h``xH~HUU$J7ЩЩЭ0)##hЭUUhXL#@%%8`H%U%%%%% z$h%`H`P?!! /8)uP8(uPvPvP&uP%(m%%&8 ©8) %%&oP`!nP`uP8(uPvP%8(%&8nP)nP`nP!`)mnPnPvPɏuP(muPuPvP,V+VV ,V+V-VV3V8V`-VVVV-VLj%-V[ VLj%`;8V`0:L*&80H8Vh3V3V3V8V-V`3V3VH + + +3VhHy3V3Vhy3V3Vhy3V3V-V`}&ȹ~&-VLj%}& +&e&ȹ&f&-VmPlPnP j%mPlPnPȱp!)LQp)!L}} }ԟ `ԟ ԟo!!LQE 4 +LQ P 4 LQLQansi 1200,1200p 4 LQPo mtPtPL'p@0 P`@L' 3VL%4V 4V [4V3V5V5V 5VL%3VL%4V4V [4VL%4(,3(L%(4([3(L%(4(3(L%(4(э3(L%(3V4V4V ,L% #4(3(L%( #4(,3(L%( #3VL%4V P PlP K$MoP ¥~~܅܅~~~ X nPmPlP`~H~H h hvL nPmPlP`?8 !r #s%xH~~# $}#~#" $ %݈ ݩ Ȍ%)% %ݭ +P(e8 = *4VL*L% " 9nPnP)e! !! ! +L$3V4V! <?e e q pnP < nPe)!L% <  qp8 =vPuPvPuP(e89 1>WW3VP<3WP<iW~ <%XP9㭧V~~V~~? Hi Q<V3`V V .= +VV`V͖ VH W=hVVɪ iTVL= L=`VVWWՍWW ,,,LQL + + ,,,`   +,,,`,@-,@#-,@=-,@W--@c- -@}--@-geostelnetbaud ratetermx-modembufferexit ,)` , R, -`-ȅ` , R, .<`-` ,.H` , G, a9PL:;6t 5]f6/v6F6t6655Telnet Server Phonebook =0> E<``P<5W~1Q<`i 5~~~6S< R<U< T< + *ͬW~ͫW WW`WW *ho on echo off ANSI term ASCII term+.=.Cuploaddownload.~,not connectedT.=/.Y0///0 /71startloadsaveviewdumpstop1/21/2to deskTop ...  /.. `  0{6͙6͙36͙x + ?!#?' ? ? ? ? ? >`Xq&pr g0qp T =L}>  P󩁅p +0q˅p T =L}>File Open Failure.Load Complete. + +  0q#p sL}> +, !   PnPmPlP GnPmPlPL + +  0q#p sL}> +, ! WW a9LE:,, p4Enter a name for  # 2 ! L +....` + +  " !` +, ! `.... +P%i +iP% r2D.C.` +atb2400 + + + +  G,3qJp 2# L+,3q]p 2 3 3 436 ' L +atb1200 + + + + +Exit to deskTop?ȢaV w $" a4 L+,Pc3?fc0fc3fc3?9     p` +H`` `A[i`a{8 ` + +  0q#p sL}> +HF8F7FF=F f/ P쩁p +0qIp T =L}>Buffer Empty?!File Create Failure.Save Complete. + +  ?@ +cc=gycgycgcgxpcyc=o3  Y  +e + + L9 +e + + L9 +e + + L>9 +e + + Ly9`WWW`W8 H  Ph nPmPlP GnPmPlPLpP 1LpP 1L d.o.pP*d.`*o.`qP 1LqP 1L {..qP*{.`*.`L2L2L2L2L2L2%Lp r2 +2 2  !2ԅ ' 2ԅ this connection :`CVȢaVVqCp:# V­+CV&Vqap4 V­ aV ; ;`` :` ;S ; a9LE<` :WW `V =`WWVee` : ; Disconnect and Exit? + +  3 3 4 L+,u G,`u 33 ' u 33 ' `+++ath0 +vH5܅wwvh`/ K _On`Pm|aDisconnecting...`  On_` + + VqappP4 V­aV a4 +WVV ΖΧV` =ӭVV =`V VPVH``VmVi` ML!V`V  S` + +  >`! oPL?DF>V>\[ E> V­Wv4Lm/銫De'Ⴃ}\?ػuT7г.lM&dE>]|6Ut 0@P`p2"RBrb$4dtDT6&vfVFHXhx(8ZJzj +:*뛋l|L\,< ݭ~n^N>.Ͽ0 P@p`"2BRbr4$tdTD&6 ?i  *\`xx?i  G +  *x`xxxͩ`$2@N\jyx\exxyxvv`RX`OXNX`QXPXTXSXUXXq&pr g =@qp TL = K 9B@qpAsrAuڅtBw˅v L = @DHLPTX\`dhlptx|  $(,048<@DHLPTX\`dhlptx||JD$ή*ꪪ `H@@ PdB *J(@ @@@NXOXOXRXRXOXqNXpxsr ,qOXpNXNXOXRX =`QXOXPXNXRX` @LAQXyPXxPXQXRXQXOXPXNXx`UX uB` [ByS<pP8q`h`h`hkeErrors: kVBytes:` A @ PeX\XLtJeX\XqJpJsJrJuJtJwILvHLfX TM MVXeXC #LJLJLJLJ`XXYX M MYXLKLKVX ZXڭ`XLKhX NcX uM[XdXhX[X\X MhXoPLCXw&vvIFVIF EXw&vFXy:x V­ }>` Ev EЧ``OXNX`QXPXTXSXUXXq&p +Hr Dqp T =L = K VBDqЅpEs-rEuBtBw˅v jJ' D +H p + =uJtJwILvHLLLLJLJLJeX\X`eX\X` ALbXgX DLgXkXbXgX +gXɀPgXkXgXgXɀhXVXiXIjX +N]XXeX ^XX_XX M[XhX #[XȌ[X\XLL uM M M K$(C0XX YX`XLK ULP JLVX XX X D`UX uB` cE cEL©L¬ H h͉P `E V ` GBx  xɠ :Xɠ:X` G"  +Gqޅp T`nwv  GqȅpPשGqpP̢` fG Cx "qG &vUF @Xh`eX]XkXm]X]X`^X_XkXM^XiOM_X^XiN_X`!Bc)Jk1sR9{ZbC ǤjK( ϬSr0[z8冧@a#펯Hi ++ԷqP3ܿyX;"`A* hI2Qp:Yx -No%Fg=^5wV˨nO, àfG$_~< }>`\[ EНXw&v ?&XѢ` = L+,? fG x$?L(>=>IEٞٳٳ?03 6 58 7 + `0- W?&m[\8[n:new work fileexisting fileto deskTopOn Disk:Please enter new filename:Enter new file name:Insert new disk into disk drive.ERROR: File exists!ERROR: Disk full!ERROR: Cannot create!1  $(,048<!  ]@qxp TP =!  ]@qp T = `Upload Complete!Upload Cancelled.File failure. Upload cancelled uBQXpPXOX`NXsmSXSXTX`QXOXPXNX߭QXyPXx`{zxzxyz{yOXxNX`QXPXNX8ZBd"@Bꨨ*ꪨJHB@.JJꪪM^`BF̎$Ȭ,JNHBJJNJ, Z$Bd"@Dꨨ*ꊢJHBJLNJJD Z`B".J" +NJJJN ^J@$,ΎꮎNJ&BJJnJ PyDUploading... uB` A yDDownloading... uB` 9  +Ph $WTXSX eUX ``h Ѕ ؅ + "`h Ѕ ؅ + *8`` + +  +HF8Fz7FF=F~IESX\`sX{`SX\`sX{`PRGSEQansipetscii` +HAtFxFsF rFwF vF + ` +HmFuFFU 6`lFpFkF jFoF nF + ` +HuFmFFU 6`Please select an optioGfvFV陉XHxh8(JZjz +*:ͽ|l\L<, ߯n~N^.> ``È ` L l Lj!)Lj}O l iÈ S ` L  H   ©p h ©`   `͘ S Hh  `` ΰ  , ΰ ΢ 7ЩЩЭ0)hЭhXL@H5ܭ h`8`H_ h`_`H,H5 h,P nh`H,H5)h,P nh` + +QP   LhXhrdsq`pL0 W0xH~ hWX`H~bݭ`ݭaݍݍW h `H~)h`HW0xh]H~ ݩݩ _WfN]geh(`xHH~ E)D)W_0(eݭgN]L "ȸP`ee`P ``  )` [LtP `ɀ` L  L  H~H0ܭ h`h`x``xH~Hjin7ЩЩЭ0)hЭjihXL@H~ܭ h`XY8`HX_[ZXXY h[`Y_Y`H,dH~ h,dP h`H,`͉ L  LptJJJJp tp`)i'i(hhLr  r`qp + M`qp qp`L hhh=h>HH + +=oL<q +q p s +s +r +u +u t v +w +vHvwh `  # L ;©8iH +h + + + +88i +` / + VL +H hL  + + + +eiisq9pL0 0xH5 hX`H5ݭݭݍݍ h `H5)lh`H0xhH5 ݩݩ Nh(`xHH5 C)B)U0&ݭNhh@hh@ݩ ݍL` info8 7  L6? #LV pGG   L ELV pGG  : L gLV p@  \ L LV p@  { L `, `  `qm, p L  ΰ ` , ΢ݢ ɨ ɀlJlJj geoCBMTerm 1.0 Bo Zimmerman CBMTerm: c64net-wifi modem http://ctcug.coffeemud.net - A,B,E,D or (equal)init str L0LLL%LHLLLwLLLLLoLLLp`pp r sdxH~ ΍ A`݈ ݩ ȌW)f gݭ) ݍhX`xH~ L ݭ hX`XYZ`xH~` `  , `9 H͘ S hp   , i, `LS H 9h    + +/ $/pP {`  L, LgeoCBMTerm 1.0  LH   hLH   hL H   hfehh@ghh@ݩ ݍWLLݩ ݩ ݩ^LJn\^L ݩݘHY\_YhYZ,d ),dP hh@>hh~`LHH~,hLph + +k H~`hX ©`?8 ¢r  rpԅqp ;`ԅ tM ~ w©`  Lqp`  )r)p8 +MN rq rdH~)h,dP h`΅΅܅ `HHLHHL + +QP TcdiDG14  4B`eAD/2h`H(`  +o` e==>i>>H=H +oL0LLL L-LLLYLnL{LLL>LbLLp9pup r sxH5z yz݈ ݩ Ȍ) ݭ) ݍhX`xH5yz ݭ hX``xH5yzhXr HH +< ©= ~hh5HІ ¦5hІ= ~ {Dž  ? + -L +`  + & + = + P + O +   & 7 H Y j { progL`ݩ ݩ ݩL`JnL` ݩݘH_h, ),P hh@>hh~`LHH5,hLpp + + H5h``xH5H=@ TcdiDG14  4B`eAD/2 + `0- B &m[\8[| i  *\`xx| i  G +  *x`xxxͩ`$2@N\jyx\exxyxvv`!!!ፉ!!!ፎ!L !!!̍!!!Ѝ!.wvv! X P nLP!6t M]#/3FItZbgTelnet Server Phonebook =0> i"``t"%a~1u"`i ~~~6w" v"y" x" + *\%~[% \%[%`]%\% *! P!.wv!o.yx V­ n` +#v '#Ч`x  xɠ .ɠ.` # Cx "# &vG @! =@!!9" G!G!"SX\`sX{`SX\`sX{`PRGSEQansipetscii p 0{6͙6͙36͙x + ?!#?' ? ? ? ?q2p \ [L Upload Complete!Upload Cancelled.File failure. Upload cancelled .p..`.sm...`....߭.y.x`{zxzxyz{y.x.`...8......q.pxs`%8%΄% Li"%%W$ȱX$`%` BLi"m%%%%%W$ȭX$`%%%Ʌ`X$W$Li"%` 6L  V­A#<$qp V­"$P$ q8p : P$ !`#qpp^`d6 wnloading... ` 9  +Ph $W.. e. ``h Ѕ ؅ + "`h Ѕ ؅ + *8`` n$ L A L`..`......qpn$r +\%[% `$[ #`\%[%$[ee`  .!Ȣ$ȱ w H HL t"qqiqX$W$ # 8"*`#qi qqu" Bt"qqiq #Pͩ#P$.``qp.s.rrrs -`.. S` .w. `. `\[ P!˅ V­ n`\[ +#Т.wv .Ѣ` # x L(=!"6 58 7LL$qppP V­$ H nL  v N$ N} N` +atd"" +d6 ,, pPEnter the host:port to connect A7 +F`ԅ t$X$W$ V­ w nL  PQ$3`W$ X$ R# +X$W$`W$͖ X$H {#hX$X$ɪ iTX$L# p#`X$W$%%%%%W$W$ ΖW$` #ӭW$X$ #`$[ [$P\$H``$[mX$i`]%\%~[%`x+L;\%`L&Q\% `| v!j\%L```<3fc33>c3?fc0fc3fc3?9     L\Lq$p$)t$(s$w$v$&z$%y$,}$+|$` x$ L*  YL A Lr.`..`......qpr *qFp \ nL S K  -qgps2rutwrv )& S A qp \ P S A  ? ?@ +cc=gycgycgcgxpcyc=o3  Y  +e + + L +e + + Lh +e + + L +e + + L6`%%%r p+q.p.... S`.....` gL2.y.x.......x`. ` [ByS<pP8q`h`h`hkeErrors: kVBytes:`  yDUploading... `  yDDo ,, pEnter a name for this connectionEnable Local Echo on connect? `#Ȣ$ȱP$#qp V­D#?$qp V­%$ P$ q8p : P$ .! !` ` .!S " Li"`  qp \ nL S K qps=ruRtwrv $! n$ p - S A qp \ P#p -. 8 S A qp \ [L Download Complete!Download Cancelled.File failure. Download cancelledH.y.x....hx`.` PP$$[ ` ` `[$P[$H\$W$W$$]`% 9 1>\%[%3Q$t"3c%t"i%c~ #%P9W$~~X$~~? Hi u" +ٞٳٳ?03 `!n$A!!! !! ! + `n$!!! 6`!!! !! ! + `n$!!! 6` " "L©L¬ H h͉@0 @geoCBMTer @m  (C)2017-2020 Bo Zimmerman geoCBMTerm is an color petscii terminal program for connecting to Commodore BBS servers. It is compatible with the following operating systems: GEOS 64 2.0, GEOS 128 2.0, Wheel file name:Insert new disk into disk drive.ERROR: File exists!ERROR: Disk full!ERROR: Cannot create!LLLwLoL//L$//q$p$s$r$u$t$wg&vf&/ ' >( //C o$L$L$L$L$` / / '( '   . .m333ą .00330 + Щ33 .L, p ,pLqsprr , rsLj-`  b-LperrqessrrsL-  -Lp ,qspr pqP`  -L .33& / o$ o$ o$L& / ' / >(// /`/ ' s&qa&p`&sd&rc&u$t$wg&vf&L'L$L$L$//`//` _&// b&//// +/ɀP////ɀ/ //I/ m(/// //// I(// o$/Ȍs 64 4.0+, Wheels 128 4.0+, MegaPatch 3.0 64, MegaPatch 3.0 128, gateWay 64, and gateWay 128. It requires a 40 column composite monitor, C64Net WiFi Modem (preferred, or similar user-port modem), disk drive, and mouse or joystick. A GEOS compatible ram  (hP //L' / ܍ܭ`/ ܍` e& u$` /` /// +/ '`H/ r$h`//h`///m//`///M/)M//(/`!Bc)Jk1sR9{ZbC ǤjK( ϬSr0[z8冧@a expansion unit (REU) is optional, but beneficial. The C64Net WiFi Modem can be constructed from schematics by going to: http://coffeemud.net:8080/ctcug/index.php/Inexpensive_Wireless_Internet_Modem It can also be purchased from: https://electronicsisfun(8ZJzj +:*뛋l|L\,< ݭ~n^N>.Ͽ0 P@p`"2BRbr4$tdTD&6fvFV陉XHxh8(JZjz +*:ͽ|l\L<, ߯n~N^.>qprn `000/ +  +.com/c64net-wifi  @Features include:  40 column text output in C64 font. PETSCII color and graphics terminal support PETSCII keyboard entry support Native support for 300-4800 baud (up to 7200 for SuperCPU enhanced systems) desk accessory access iP `# V `  $B G"  +$qBp \ `nwv  $q,pPש$qVpP̢`Please select an option:new work fileexisting fileto deskTopOn Disk:Please enter new filename:Enter new`3 d.qpn  ?`  ݭ33 . å33 .r3s3r33 xrȭ3rȭ3r33errs K. .331 + ` rr +L0 00`/L%L% / /ڭ/L%/ U(/ '///// x$/L%/ /I/ m(/ /͞/L%//// +/L% +/)/// $//ɀ >( $ o$ /L$ /L% >( $/ o$P /C o$ /L$ o$L0 + <3u3tpt3tȭ3t331 +  . J©` pp -Lȱprɠr`ȱpɠaz8 pP`ͭ33 `2`2`0 x0`i3i131`//L' ' '( ' r$(C8 / //L% s&P h& / / $ x$ / {$L& / / {$ //H o$hL& x$ >(L~& / +/ / ///// //` H5 (h`H~ (h`/ H5 (h/`H~   + #펯Hi ++ԷqP3ܿyX;"`A* hI2Qp:Yx -No%Fg=^5wV˨nO, àfG$_~]|6Ut 0@P`p2"RBrb$4dtDT6&vfVFHXhx? '$ $$$$4$$$4$$$$$$<Write Image V2.0Red StormgeoWrite V1.1 This file was created with Wrong is Write. Written by Joe Buckley. `  *L +//H̡/ +PȌ/h`qspr * r rsL!+`  +LperrqessrrsLp+  K+L * pqspr pqP`  p+L͢00 `/ `/// + `///// +nternal Phonebook for storing favorite hosts quick connect option Echo (duplex) toggle X-Modem upload and download. 64K input buffer support, with LOAD, SAVE, DUMP, and VIEW.  @Building:  geoCBMTerm was built using Concept for Wheels 64. It mightK of GEOS, can be edited. If you can edit this area, you can add one of more of the following switches to the information text area:  @-D  : turn off DCD active mode. If your modem does not support the standard Commodore "Data Carrier Detect" sig t from scratch, provided the necessary geoModules include files and object files are present, and the F/CBMFont.rel and F/C=GFXFont.rel font files.  @Configuring and Using: geoCBMTerm is optimized to work with the C64Net WiFi modem from www.electK -B. To default at 4800, use -B -B.  @=  : If an equal sign appears in the information text area, then every character after that will be sent to your modem at application start time, instead of the sequence listed above. For example,when the modem is online, and defaults to RTS/CTS flow control. However, the program can also be used with any C= user port wifi modem for the C64 or C128 that defaults at 1200 baud. At startup, the program will set the baud rate to 1200 and issue the KKogram starts in DCD-active mode, and local echo turned on. To change the startup default settings, select the geoCBMTerm application in deskTop and bring up the Information page for the app. At the bottom is the information area which, in some versions also build using geoProgrammer, but hasn't been tested. In addition to the given source, you'll require several components from the geoModules project: https://github.com/bozimmerman/geoModules You can link directly from the given object code, or buil   nal, and does not make that signal go active whenever remotely connected, you should use this switch.  @-E  : turn off local echo mode by default  @-B  : increment the default baud rate. If you want the program to default at 2400 baud, useronicsisfun.com. The next best option is a user port wifi modem that uses the "Zimodem" firmware. Information about this can be found at github.com/bozimmerman/Zimodem . Next best is a wifi modem that supports AT commands, a DCD signal that goes high Km if you  are happy with your modems default settings, then you may end the entire info text area with =Z  following commands: "atze0v1x1f0q0r1&p1i0". This command resets the modem, turns off modem echo, turns on verbose messages, RTS/CTS flow control, C= cariage returns, PETSCII command mode, and then sends the modem status message. Also, by default, the prKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK \ No newline at end of file diff --git a/cbm8bit/link232_apps.txt b/cbm8bit/link232_apps.txt new file mode 100644 index 0000000..eb26f9a --- /dev/null +++ b/cbm8bit/link232_apps.txt @@ -0,0 +1,266 @@ +/************************** +/* Link232 WiFi Software * +/* for the Commodore 64 * +/************************** +/////////////////////////// + +This disk includes applications written to work with the Link232 WiFi modem running firmware version 2.0 or better. If this is your first time using the modem, you should start with the configure program to get your modem connected to the internet. You can then use the FTP, TELNET, IRC, WGET, and other programs whenever you wish to communicate with the internet. The disk includes numerous machine language binaries (pml64.bin, and swiftdrvr.bin) which are required for some the several programs provided. + +The disk includes the source assembly code for the pml64 and the SwiftLink Kernal Wedge driver binary. These source files are in LADS format. The disk also includes the LADS assembler for the C64 to re-assemble the binaries from the source files. To use the LADS assembler, you should use a C64 or C128 in C64 mode, and load the LADS assembler using LOAD"LADS",8,1. Once the assembler is loaded, clear the screen and type the name of the source file to assemble in the upper left-hand corner of the screen. Once that is done, use SYS11000 to begin the assembly, which will create the .bin files. + +The disk lastly includes the program "emutil", which is a C64, C128, or VIC-20 program to creating and dissolving .D64 images, such as those downloaded by the d64wget program discussed below. + +Below you will find information on the application programs included on this disk, as well as instructions on using the PML64, PML128, PMLVIC, and UP9600 machine language libraries from BASIC or machine language. + +CONFIGURE +************************************* + +The Configure program is for setting up or altering the WiFi Hotspot, also known as a Wireless Internet Router, that your modem uses to communicate with the internet. It also allows you to modify the modems built in phone book. It is written in BASIC, but uses the PML64.BIN libraries for speed (see below). This program requires a C64 or C128 in 64 mode and your Link232 WiFi Modem configured for 19200 baud, which is the default factory configuration. To run the program, enter LOAD"CONFIGURE",8 and then RUN. + +When the program runs, it will initialize the Link232 modem with certain commands. If you receive an error at this point, or if the program hangs for longer than a minute, try turning your computer off and on again to clear memory and reset the modem, and then load and run it again. + +After initialization, the program will scan the area for WiFi Hotspots and provide you a list to select from. If you do not see your own wireless router listed, or do not know the name, consult your ISP's internet equipment documentation. After you have selected the appropriate hotspot, you will be prompted for the password. After the password is entered, the program will test the settings and, if successful, save the settings to the flash memory in your modem it will be remembered even after the computer is turned off. When wifi configuration is complete, and the modem passes the connection test, you can modify the modems internal phone book. + +The modems internal phone book maintains a list of numbers and associated internet addresses and configurations. These numbers can then be used by third party software to 'dial' to those sites using the modems ATD command. A phone number, in this case, is any large number, preferably 7 digits or more, that is assigned to a particuler internet host and port. Each host can then be configured for ASCII-PETSCII translation, TELNET code translation, XON/XOFF flow control, and/or local ECHO (half vs full duplex). Every modification you make is saved to the modem and available after reboots. This system can be used to connect to the online Q-Link service online using the existing Q-Link software. + + +FTP +************************************* + +File Transfer Protocol program supports a minimal number of features to connect to FTP Servers all over the internet in order to download and/or upload programs. It is written in BASIC, but uses PML64.BIN and SWIFTDRVR.BIN. + +This program requires a C64 or C128 in 64 mode, and your Link232 WiFi Modem configured for 19200 baud, which is the default factory configuration. To run the program, enter LOAD"FTP",8 and then RUN. + +When you start the program, it will initialize your Link232 modem with certain commands. If you receive an error at this point, or if the program hangs for longer than a minute, try turning your computer off and on again to clear memory and reset the modem, and then load and run it again. + +After initialization, you will be given a menu where you can enter the host name of the ftp server to connect to, and the login and password to use. If you plan to login with the name "anonymous", you should use your email address as your password. The last option on the menu is the drive number to upload files from and download files to. When all options are correct, hit RETURN to connect. + +At the FTP prompt, you may enter one of the following commands: + +DIR : list the files in the current directory +CD [DIRECTORY OR PATH] : change to a new directory +QUIT : exit the ftp program +HELP : show the commands supported by the client and the server +GET [FILENAME,P] : download the program with the name filename as a PRG. Use ,S for SEQ. +PUT [FILENAME,P] : upload the program with the name filename which is a PRG file. Use ,S for SEQ. +DEL [FILENAME] : delete the given file from the server +LCD [DEVICE NUMBER or CMD Hard Drive PATH] : change the active local upload/download drive, or change which directory you are using if the active drive is a UIEC or CMD Hard Drive compatible. +LDIR [MASK] : get a directory of the active local drive. +LDEL [FILENAME] : delete the given local file from the active drive. + + +WGET +************************************* + +WGET program is for downloading files and pages from a standard HTTP web address (URL). It is written in BASIC, but uses PML64.BIN and SWIFTDRVR.BIN. + +This program requires a C64 or C128 in 64 mode, and your Link232 WiFi Modem configured for 19200 baud, which is the default factory configuration. To run the program, enter LOAD"WGET",8 and then RUN. + +When you start the program, it will initialize your Link232 modem with certain commands. If you receive an error at this point, or if the program hangs for longer than a minute, try turning your computer off and on again to clear memory and reset the modem, and then load and run it again. + +After initialization, you will be given a menu where you can enter the url of the file or page to download, the unit number of the disk drive to download the file or page to, and the name of the file to write to the disk. Your filename should end with ,P to create a PRG file, and ,S to create a SEQ file. For example, a filename of "mypage,s" will create a file called "mypage" of type SEQ. When all options are correct, hit RETURN to connect and begin the download. When the download completes, the program will exit. + + +IRC +************************************* + +IRC program is for chatting with people on free internet IRC Servers. It is written in BASIC, but uses the PML64.BIN, PMLVIC.BIN, or PML128.BIN libraries for speed (see below). This program requires a C64 or C128 in 64 mode, and your Link232 WiFi Modem configured for 19200 baud, which is the default factory configuration. To run the program, enter LOAD"IRC",8 and then RUN. + +When you start the program, it will initialize your Link232 modem with certain commands. If you receive an error at this point, or if the program hangs for longer than a minute, try turning your computer off and on again to clear memory and reset the modem, and then load and run it again. + +After initialization, you will be asked to enter the host name of the IRC Server to connect to. Next you will be prompted for the port number that the server listens on. The program will then attempt to connect to the host and port. If successful, you will be prompted to enter a nickname, which is the name you will be known by during chatting. If your nickname is already in use by someone else, you will eventually be prompted to enter a new one. Either way, the program will display the servers MOTD and other information. This could take several minutes. It's best to just wait until you see the message "End of MOTD" to begin typing. + +After the MOTD message, you may type at any time. If you have joined a chat channel, you will automatically see messages as they appear in the channel. When you begin typing, all messages from the server are suspended until you hit RETURN. Since the IRC client will appear to the server to be idle while you are typing, it is best to spend as little time as necessary in messaage and command entry mode. You may enter ? followed by RETURN to see the IRC commands supported by this client. + +Commands include: +/QUIT : exit the IRC chat program +/JOIN #channelname : join the new channel named "#channelname" +* Anything else entered will be assumed to be a message to sent to the channel you have already joined. + +TELNET +************************************* + +TELNET program is for communicating with a TELNET server as a terminal, or with a TELNET-like server such as a Multi-User Dungeon game (a MUD). It is written in BASIC, but uses the PML64.BIN, PMLVIC.BIN, or PML128.BIN libraries for speed (see below). This program requires a C64 or C128 in 64 mode, and your Link232 WiFi Modem configured for 19200 baud, which is the default factory configuration. To run the program, enter LOAD"TELNET",8 and then RUN. + +When you start the program, it will initialize your Link232 modem with certain commands. If you receive an error at this point, or if the program hangs for longer than a minute, try turning your computer off and on again to clear memory and reset the modem, and then load and run it again. + +After initialization, you will be shown a menu with the following options: + +Dial from Phonebook: +This will let you connect to a server saved in your phonebook on disk. See below for information about terminal mode while connected to a server + +Modify Phonebook: +This will let you add or modify servers to save in your phonebook on disk. These can be then "Dialed" to quickly later on. + +Quick Connect: +This will let you manually enter a server host name and port to immediately connect to. See below for information about terminal mode while connected to a server. + +Terminal Mode: +This will put you into terminal mode without connecting to any server. In this case, you would be in the modem firmware's Command Mode. You should consult the documentation for the Link232 WiFi Firmware for more information on this mode. Use the F1 key or the C128's ESC key to exit terminal mode. + +Quit: +Exit the TELNET program + +When you have connected to a server, you will be in ANSI terminal mode. You can exit the server and disconnect at any time by hitting the F1 key on your C64, or the ESC key on your C128. When connected to a server, the modem will translate as much ASCII/ANSI into mostly viewable PETSCII for your Commodore computer. However, you will notice that underscores look like back-arrows. ANSI colors will be translated to Commodore PETSCII colors wherever possible. Your own keystrokes will also be translated to ASCII/ANSI as you type. + +CBMTERM +************************************* + +C= TERM program is for communicating with a telnet-style Commodore Graphics BBS servers as a terminal. It is written in BASIC, but uses PML64.BIN and SWIFTDRVR.BIN. This program requires a C64 or C128 in 64 mode, and your Link232 WiFi Modem configured for 19200 baud, which is the default factory configuration. To run the program, enter LOAD"CBMTERM",8 and then RUN. + +When you start the program, it will initialize your Link232 modem with certain commands. If you receive an error at this point, or if the program hangs for longer than a minute, try turning your computer off and on again to clear memory and reset the modem, and then load and run it again. + +After initialization, you will be shown a menu with the following options: + +Dial from Phonebook: +This will let you connect to a server saved in your phonebook on disk. See below for information about terminal mode while connected to a server + +Modify Phonebook: +This will let you add or modify servers to save in your phonebook on disk. These can be then "Dialed" to quickly later on. You will receive an option for a Host Name, a Port Number, and Local Echo. Consult your online BBS list for hosts and ports of the BBSes you wish to connect to. The Local Echo option should usually be answered with NO, as most BBSes will echo for you, and having the modem do local echo will corrupt X-Modem transfers. + +Quick Connect: +This will let you manually enter a server host name and port to immediately connect to. See below for information about terminal mode while connected to a server. + +Terminal Mode: +This will put you into terminal mode without connecting to any server. In this case, you would be in the modem firmware's Command Mode. You will be prompted as to whether you would like Local Echo turned on. If you plan to remain in Command Mode and not connect to anything, you can say Yes. However, if you plan to connect to a BBS, or especially if you plan to do any file transfers, you should definitely answer NO. You should consult the documentation for the Link232 WiFi Firmware for more information on this mode. Use the F1 key or the C128's ESC key to exit terminal mode. See below for more you can do after exiting terminal mode. + +Quit: +Exit the CBMBBS program + +When you have connected to a server, you will be in CBM PETSCII/Graphics mode. You can exit the terminal at any time by hitting the F1 key on your C64, or the ESC key on your C128. If you are offline, you will immediately return to the main menu. However, if you are still online, you will have the option to hangup and return to the main menu, upload a file using X-Modem CRC, download a file, or continue back to terminal mode. + +D64WGET +************************************* + +D64WGET program is for downloading and writing full emulator disk images (such as .D64, .D71, .D81, .DNP, etc) files from a standard HTTP web address (URL) and write them to a formatted, blank diskette. It is written in BASIC, but uses PML64.BIN and SWIFTDRVR.BIN. + +This program requires a C64 or C128 in 64 mode, and your Link232 WiFi Modem configured for 19200 baud, which is the default factory configuration. To run the program, enter LOAD"D64WGET",8 and then RUN. + +When you start the program, it will initialize your Link232 modem with certain commands. If you receive an error at this point, or if the program hangs for longer than a minute, try turning your computer off and on again to clear memory and reset the modem, and then load and run it again. + +After initialization, you will be given a menu where you can enter the url of the disk image file to download and write, and the unit number of the disk drive containing the *formatted* blank disk to write the image to. When all options are correct, hit RETURN and answer Y to the confirmation to connect and begin the download and disk write. When the download completes, which could take awhile, the program will exit. + +PML64.BIN +************************************* + +This file for the C64 in 128 mode is a machine language support file for performing certain common Link232 modem functions from BASIC. PML64.BIN loads to $C000 (49152). + +To use them from BASIC, make sure to follow two important rules: 1. Load the appropriate binary into memory (pml64.bin). and 2. Initialize the variable p$, by giving it some value, as early in your program as possible. For example: P$="" would be fine. + +Throughout this section, I will refer to the variable ML to refer to the starting address of the library. For the C64, this is always 49152. So, when I say ML+3, I mean 49155 on the C64. + +--------- +BUF1LIN + +Usage: +sys ML + +Pre-requisites: +P$ must be initialized in BASIC +Channel 5 must be OPEN to the modem + +Outputs: +P$ contains the string line read from the modem, if any +ML + 19 is 0 to denote success, or 255 for a timeout +ML + 26 contains the CRC8 value of the received string + +Description: +Reads from the open modem channel until a carriage return (13) is seen, or until 255 characters are read, or until a timeout occurs. The characters are returned in the BASIC variable P$, although character values 10 and 0 are always ignored. The timeout is about 12 seconds. + +--------- +BUFXLIN + +Usage: +sys ML+3 + +Pre-requisites: +P$ must be initialized in BASIC +Channel 5 must be OPEN to the modem +ML + 18 contains the number of bytes to read + +Outputs: +P$ contains the X bytes read from the modem if any +ML + 18 contains the number of bytes NOT read (0 on success) +ML + 19 is 255 for a timeout, or any other value for the index of the last carriage return (13) in the returned buffer. +ML + 26 contains the CRC8 value of the received bytes + +Reads from the open modem channel until the number of bytes in ML+18 have been read, or until 253 bytes are read, or until a timeout occurs. The bytes are returned in the BASIC variable P$. The timeout is about 12 seconds. + +--------- +GETPACKET + +Usage: +sys ML+6 + +Pre-requisites: +P$ must be initialized in BASIC +Channel 5 must be OPEN to the modem + +Outputs: +ML + 18 contains the number of bytes read +ML + 19 is 255 for a timeout or packet formatting error +ML + 20 is the packet/channel ID that sent the data (0=none) +ML + 22 is the size of the packet received +ML + 24 is the EXPECTED CRC8 of the packet data sent by the modem +ML + 26 contains the CRC8 value of the received string + +Reads a special Link232 WiFi Firmware formatted Packet from modem channel. This function will block until it times out, or a packet is received, so it's best used with the ATF3 flow control to ensure that every time XON is sent to the modem, a single packet header is guarenteed to be sent to the computer. When this method returns, ML+19 should be checked for an error, ML+22 for a non-0 to see if any data was received, or ML+20 to make sure it matches the expected channel (this can be 0 for a non-response when using ATF3). You can also optionally compare ML+26 to ML+24 to check for RS232 errors. + +--------- +CRCP + +Usage: +sys ML+9 + +Pre-requisites: +P$ contains the string to calculate the CRC of + +Outputs: +ML + 26 contains the CRC8 value of the received string + +Description: +Calculates the CRC8 of P$ and returns it. + +--------- +BUFAP + +Usage: +sys ML+12 + +Pre-requisites: +P$ must be initialized in BASIC +ML + 32 contains the channel number to read from +The above channel must be open + +Outputs: +P$ contains the bytes read from the channel, if any +ML + 26 contains the CRC8 value of the received bytes + +Description: +Reads from the open channel whose number is contained in ML+32 until no more bytes are available, or until 254 bytes are read. The bytes are returned in the BASIC variable P$. + +SWIFTDRVR.BIN +************************************* + +This file is the SwiftLink/Turbo232 BASIC wedge driver for the C64. Use SYS51200 to activate. After this point, all device 2 access will go to the SwiftLink/Turbo232 cartridge instead of the user port. +The baud rate codes used in the OPEN command are compatible with the C64 KERNAL, with additions: +00000 (bit value of 0) = Nonstandard (User-Defined) Rate (Not Implemented) +00001 (bit value of 1) = 50 Baud (Not supported) +00010 (bit value of 2) = 75 Baud (Not supported) +00011 (bit value of 3) = 110 Baud (Not supported) +00100 (bit value of 4) = 134.5 Baud (Not supported) +00101 (bit value of 5) = 150 Baud (Not supported) +00110 (bit value of 6) = 300 Baud +00111 (bit value of 7) = 600 Baud +01000 (bit value of 8) = 1200 Baud +01001 (bit value of 9) = 1800 Baud (Not supported) +01010 (bit value of 10) = 2400 Baud +01011 (bit value of 11) = 3600 Baud +01100 (bit value of 12) = 4800 Baud +01101 (bit value of 13) = 7200 Baud +01110 (bit value of 14) = 9600 Baud +01111 (bit value of 15) = 19200 Baud +10000 (bit value of 16) = 38400 Baud +10001 (bit value of 17) = 57600 Baud (Turbo232 only) +10010 (bit value of 18) = 115200 Baud (Turbo232 only) +10011 (bit value of 19) = 230400 Baud (Turbo232 only) diff --git a/cbm8bit/link232_slapps.d64 b/cbm8bit/link232_slapps.d64 new file mode 100644 index 0000000..04422ae --- /dev/null +++ b/cbm8bit/link232_slapps.d64 @@ -0,0 +1,350 @@ +KKKKKKKKKKKKKKKKKKKKK + +READY. + 10 * = $C800 14 .D SWIFTDRVR.BIN 1000 ; KERNAL BAUD RATES 1010 ; B50=1 1011 ; B75=2 1012 ; B110=3 1013 ; B135=4 1014 ; B150=5 1015 ; B300=6 1016 ; B600=7 1017 ; B1200=8 1018 ; B1800=9 1019 ; B2400=10 1020 ; B3600=11 1021 ; B4800=12 1022 ; B72 UT PHA 6010 LDA $9A 6020 CMP #$02:BEQ DOPUT3 6030 PLA 6040 DOPUT2 JMP $F1CA 6320 DOPUT3 LDA STATUS 6330 AND #16:;%00010000 6340 BEQ DOPUT3 6350 CLC:PLA:STA DATAPORT 6400 BCC DOPUT4 6410 LDA #$00 6420 DOPUT4 RTS 6999 ;;;;;;;;;;;;;;;;;; 7000 DOCLOSE JSR $F NREVD 4060 ;STA ERRORS 4070 LDY RTAIL 4080 LDA DATAPORT 4090 STA (RBUFF),Y 4100 INC RTAIL 4110 INC RCOUNT 4500 NREVD NOP:;LDA ZPXMF:STA COMMAND 4900 ;JMP $FEBC 4910 JSR $FFE1:BNE NMOUT 4920 JMP $FE66 4950 NMOUT PLA:TAY:PLA:TAX:PLA:RTI 4998 ;;;;;;;;;;;;; 0 INCLO LDA VECS,Y:STA $0319,Y 7170 DEY:BNE INCLO:PLA:TAY 7180 JSR NOINIT: ; DOOPEN, AND CLI! 7190 NOCLOS JSR UPDCD:PLA:PLP:RTS 7500 DOCLAL JSR $F32F:PHP:PHA:JMP DOCLOS2 7999 ;;;;;;;;;;;;;;;;;; 8000 BAUDS .BYTE 9 0 0 1 2 2 5 6 7 8 8 9 10 11 12 14 15 130 :BEQ NONVEC 3502 STA OLDNMI+1:LDA NMINV:STA OLDNMI 3510 LDA #NEWNMI 3540 STA NMINV+1 3550 NONVEC CLI 3560 LDA #DOCLOSE:STA $031D 3580 LDA #DOPUT:STA $0327 3600 LD",8 55556 PRINT2 NOP;:VERIFY"SWIFTDRVR.BAS",8 55557 PRINT3 NOP;"SWIFTDRVR.BAS":PRINT"%LADS":PRINT"SYS11000" +READY. +OCLAL JSR $F32F:PHP:PHA:JMP DOCLOS2 7999 ;;;;;;;;;;;;;;;;;; 8000 BAUDS .BYTE 9 0 0 1 2 2 5 6 7 8 8 9 10 11 12 14 15 130 RNAL CLOCK, 8N1, BAUD BELOW 3210 ;;DA #>RECVBUFF:;TA RBUFF+1 3219 ; INTERNAL CLOCK, 8N1, BAUD BELOW 3220 STY ESR232:LDA ($BB),Y 3225 TAY:LDA BAUDS,Y:BPL DOOP3 3226 LDY #16:STY CONTROL 3228 AND #$7F:STA ESR232:LDA #$00 3230 DOOP3 ORA #16:;%00010000 3240 SK31A:STA DOOPEN+1:LDA $031B:STA DOOPEN+2 3040 LDA $031C:STA DOCLOSE+1:LDA $031D:STA DOCLOSE+2 3050 LDA $0326:STA DOPUT2+1:LDA $0327:STA DOPUT2+2 3060 LDA $032C:STA DOCLAL+1:LDA $032D:STA DOCLAL+2 3070 NOINIT LDA #DOOPEN:STA $0K00=13 1023 ; B9600=14 1024 ; B19200=15 1025 ; B230400=19 2000 ;;;;;;;;;;; 2010 BASE = $DE00 2011 DATAPORT = $DE00 2012 STATUS = $DE01 2013 COMMAND = $DE02 2014 CONTROL = $DE03 2015 ESR232 = $DE07 2020 ; 2021 RHEAD = 668 2022 RTAIL = 667 2030 RBUFF = $F7 291 7010 PHP:PHA 7040 LDA $BA:CMP #$02:BNE NOCLOS 7060 DOCLOS2 LDA #3 ; 00000011 7070 STA COMMAND ; DISABLE STUFF 7080 LDA NMINV+1:CMP #>NEWNMI:BNE NOCLOS 7100 SEI 7110 LDA OLDNMI 7120 STA NMINV 7130 LDA OLDNMI+1 7140 STA NMINV+1 7150 TYA:PHA:LDY #20 716;;; 4999 SAVBYTE .BYTE 0 5000 UPDCD PHP:PHA 5010 LDA $DD03:ORA #$10:STA $DD03 5020 LDA STATUS:AND #64:BEQ CLRDCD 5030 LDA $DD01:ORA #$10:STA $DD01 5040 PLA:PLP:RTS 5050 CLRDCD LDA $DD01:AND #$EF:STA $DD01 5199 PLA:PLP:RTS 5999 ;;;;;;;;;;;;;;;;;; 6000 DOP129 128 0 0 0 0 0 0 8005 VECS .BYTE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8006 OLDNMI .BYTE 71 254 8010 RCOUNT .BYTE 0 8020 ERRORS .BYTE 0 8030 ZPXMO .BYTE 0 8040 ZPXMF .BYTE 0 55555 PRINT NOP;:OPEN1,8,15,"S0:SWIFTDRVR*":CLOSE1:SAVE"SWIFTDRVR.BASA #>DOCLAL:STA $032C 3610 LDA #NEWNMI),U:(F$),U_WiYҦ*Ru)\ީ;r%*n_9)+2e)JrWsܹ:zNܹ;r-^oJsNܹ;rm䙮su|RJzO~՗4˓=g)Y.N%J̳J{;rzi˓oJWsNީ;r-^oJjszɩ^:Ml䗪NuYJJs+=\=9橮jRU]r{jk+_e)J31B 3090 CLI:RTS 3099 ;;;;;;;;;;; 3100 DOOPEN JSR $F34A:; CALL IOPEN 3105 PHP:PHA 3110 LDA $BA:CMP #$02 3120 BEQ DOOP2 3130 PLA:PLP:RTS:;EARLY EXIT 3140 DOOP2 TYA:PHA 3150 LDY #$00 3160 STY RHEAD 3170 STY RTAIL 3180 STY RCOUNT 3190 STY ERRORS 3200 ; INTEK2040 NMINV = $0318 2099 ;;;;;;;;;;; 2500 JMP INIT 2510 ; INITCHIP ; NEWVEC 2520 ; RECVBYTE 2530 ; XMITBYTE 2599 ;;;;;;;;;;; 3000 INIT SEI 3005 LDA $031B:CMP #>DOOPEN:BEQ NOINIT 3009 LDY #20 3010 INSAV LDA $0319,Y:STA VECS,Y 3020 DEY:BNE INSAV 3030 LDA $0Lx;ɈSȭTȭLɭMɭ&8ɭ'9ɭ,ɭ-ɩRȍX` JHh(`HɌɌޱ )ީ ީ ލ)p xɭɩݍȍXKɍ/&ɍ'ɍ,- hh(`HHHح)ޑ  + $C800#.D SWTDRVR.BIN;; KERNAL BAUD RATESG; B501S; B752`; B1103m; B1354z; B1505; B3006; B6007; B12008; B18009; B240010; B360011; B480012; B720013; B960014 A #DO:STA $031D LDA #DOPUT:STA $03267LDA #DOPUT:STA $0327RLDA #DOCLAL:STA $032CmLDA #DOCLAL:STA $032D~<E JSR UPDCDPLA:TAY:PLA:PLP:RTS;;;;;;;;;;;;NMI PHA:TXA:PHA:TYA:PHA;LDX #%00000011:STX COMM ; DISABLE STU BYTE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.FOLDNMI .BYTE 71 254AJRCOUNT .BYTE 0STERRS .BYTE 0f^ZPXMO .BYTE 0yhZPXMF .BYTE 0ٙ NOP;:1,8,15,"S0:SWIFTDRVR*":1:"SWIFTDRVR.BAS",8ٙ2 NOP;:"SWIFTDRVR.BAS",8ٙ3 NOP;"SWIFT +UFXLPMNJSR RETIMERNLDY BUF1DX:LDA BUF1,Y:DEC NUMX4\NCMP #$0D:BNE BUFXNCROfNLDX CRXFLG:BNE BUFXNCRipNSTY CRXFLG:INC CRXFLGzNBUFXNCR INC BUF1DXNLDA BUF1DX:CMP #$FD:BCS BUFXDUNNBUF1NI LDA NUMX:BNE BUFXLPNJMP BUF1DUNNBUFXDUN JM #$02 + 0 BEQ DOOP2& : PLA:PLP:RTS:;EARLY EXIT8 D DOOP2 TYA:PHAE N LDY #$00S X STY RHEADa b STY RTAILp l STY RCOUNT~ v STY ERRS ; ERNAL CLOCK, 8N1, BAUD BELOW ;;DA #RECVBUFF:;TA RBUFF1 ; ERNAL CLOCK, 8N1, BAUD BELOW STY ESR232:Lfhhh@H ݭ)@  h(`)h(`HhL)hސ` H+ޭxɍɍHəh F h(` /HLV  + Gꠠ PML128.BAS ; B1920015 ; B23040019, ;;;;;;;;;;;= BASE $DE00O PORT = $DE00b STATUS $DE01t COMM $DE02 ROL $DE03 ESR232 $DE07 ; RHEAD 668 RTAIL 667 RBUFF $F7 NMINV $0318 3;;;;;;;;;;; JMP INIFFCLD:LDA STATUS! #8:; MASK OUT NINDI/BEQ NREVD>;STA ERRSLLDY RTAILZLDA PORTlSTA (RBUFF),YzINC RTAILINC RCOUNTNREVD NOP:;LDA ZPXMF:STA COMM$;JMP $FEBC.JSR $FFE1:BNE NMOUT8JMP $FE66VNMOUT PLbPHP:PHALDA $BA:CMP #$02:BNE NOCLOS4DOCLOS2 LDA #3 ; 00000011RSTA COMM ; DISABLE STUFFxLDA NMINV1:CMP #NMI:BNE NOCLOSSEILDA OLDNMISTA NMINVLDA OLDNMI1STA NMINV1TYA:PHA:LDY #20INCLO LDA VECS,Y:P BUF1DUN-0uBUFAP LDX BUFAPX:JSR $FFC6:LDY #$00:TYA:STA BUF1DX:STA CRXFLGXDuBUFALP JSR $FFE4:LDY BUF1DX:STA BUF1,YyNuJSR $FFB7:CMP #$08:BEQ BUFAXXuCMP #$42:BEQ BUFAXbuCMP #$00:BEQ BUFAP1:INC BUF1DXluBUFAX STA CRXFLG:JMP BUF1DUNuBUFAP1 LDA ($BB),Y TAY:LDA BAUDS,Y:BPL DOOP34 LDY #16:STY ROLS #$7F:STA ESR232:LDA #$00o DOOP3 A #16:;%00010000| STA ROL ; NO PAR, NO ECHO, XMIT , RECV LDA #9:; %00001001 ; STA COMM STA ZPXMF #%11110000 ; KEEP PARI2DRVR.BAS":"%LADS":"SYS11000"NIT: ; DO, CLI!LNOCLOS JSR UPDCD:PLA:PLP:RTSuLDOCLAL JSR $F32F:PHP:PHA:JMP DOCLOS2?;;;;;;;;;;;;;;;;;;@BAUDS .BYTE 9 0 0 1 2 2 5 6 7 8 8 9 10 11 12 14 15 130 129 128 0 0 0 0 0 0EVECS .T + ; INITCHIP ; VEC" + ; RECVBYTE1 + ; XMITBYTEA +' +;;;;;;;;;;;N + INIT SEIq + LDA $031B:CMP #DO:BEQ NOINIT} + LDY #20 + INSAV LDA $0319,Y:STA VECS,Y + DEY:BNE INSAV + LDA $031A:STA DO1:LDA $031B:STA DO2 LDA $031C:STA DO1:LDA $A:TAY:PLA:TAX:PLA:RTI;;;;;;;;;;;;;;;;,SAVBYTE .BYTE 0>UPDCD PHP:PHA^LDA $DD03:A #$10:STA $DD03|LDA STATUS: #64:BEQ DCDLDA $DD01:A #$10:STA $DD01PLA:PLP:RTSDCD LDA $DD01: #$EF:STA $DD01OPLA:PLP:RTSo;;;;;;;STA $0319,Y DEY:BNE INCLO:PLA:TAY+ JSR NOINIT: ; DO, CLI!LNOCLOS JSR UPDCD:PLA:PLP:RTSuLDOCLAL JSR $F32F:PHP:PHA:JMP DOCLOS2?;;;;;;;;;;;;;;;;;;@BAUDS .BYTE 9 0 0 1 2 2 5 6 7 8 8 9 10 11 12 14 15 130 129 128 0 0 0 0 0 0EVECS .INC BUF1DX:LDA BUF1DX:CMP #$FE:BCC BUFALP:JMP BUF1DUN(BUFTMR .BYTE 0;BUF1DX .BYTE 0NBUFX .BYTE 0 0_BUF1 .BYTE 0ٙ NOP;:F$"PMLVIC.BAS":8,8,15,"S0:PMLVIC*":F$,8:F$,8){צzt>JtYs^+]'yw^){i{+]gy{g{)wT)JTYECHO# A #9:;%00001001 ; SET RECV BUF LY1 STA ZPXMO> VEC SEIc LDA NMINV1:CMP #NMI:BEQ NVEC STA OLDNMI1:LDA NMINV:STA OLDNMI LDA #NMI STA NMINV LDA #NMI STA NMINV1 NVEC CLI LDA #DO:STA $031C LD4)X1!$BC15:SY227:56834,X:"Pß5,2,0,(8)"Zá#5,A$:A$""A$;0"dáA$:A$""5,A$;<"nÉ 50010m"U8:F$"IRC":1,U,15,"S0:"F$:1:F$,U:F$,U)JR7NNܜ.Nj+j9Y)r,R秪Nܥ)IJksܹ:zNܹ;r-^oJsNܹ;rme)J031D:STA DO2: LDA $0326:STA DOPUT21:LDA $0327:STA DOPUT22l LDA $032C:STA DOCLAL1:LDA $032D:STA DOCLAL2 NOINIT LDA #DO:STA $031A  LDA #DO:STA $031B  CLI:RTS  ;;;;;;;;;;;  DO JSR $F34A:; CALL I ! PHP:PHA & LDA $BA:CMP  ;;;;;;;;;;;pDOPUT PHAzLDA $9A)CMP #$02:BEQ DOPUT31PLAFDOPUT2 JMP $F1CA\DOPUT3 LDA STATUSq #16:;%00010000BEQ DOPUT3CLC:PLA:STA PORTBCC DOPUT4 +LDA #$00DOPUT4 RTSW;;;;;;;;;;;;;;;;;;XDO JSR $F291 NE.";CO$:3:1V 5,"ATH0":5,"ATZ":TTTI100` TITT2400j 5:'P$""4" ";HA$:A$""5010^A$(13)" ":A$(20)A$;" ";:P$P$A$:5010P$""5010P$(P$,(P$)1):"  ";:5010pTTTI100 + 22273!.D PMLVIC.BINH; PACKET ML VIC20 BY BO ZIMMERMANh; UPDATED 20170814 10:14P; 56,87 PROTECT IT!dJMP BUF1LINnJMP BUFXLINxJMP PACKETJMP CRCPJMP BUFAPNOP:NOP:NOPNUMX .BYTE 0 CRXFLG .BY BYTE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.FOLDNMI .BYTE 71 254AJRCOUNT .BYTE 0STERRS .BYTE 0f^ZPXMO .BYTE 0yhZPXMF .BYTE 0ٙ NOP;:1,8,15,"S0:SWIFTDRVR*":1:"SWIFTDRVR.BAS",8ٙ2 NOP;:"SWIFTDRVR.BAS",8ٙ3 NOP;"SWIFTl# +A0A1:"SWIFTDRVR.BIN",8,1;51200:5,2,0,(15)Y#5,A$:A$;:A$:A$""30h(5,A$;:30.BINX-XFER128.BINSWIFTDRVR.BINPML64.BAS PML128.BASXA:PHA LDY #$00:LDA ($FD),Y:STA $FB7 +INY:LDA ($FD),Y:STA $FCD +LDX #$09{( +DIGLP LDY #$00:LDA ($FD),Y:CLC:ADC $FB:STA ($FD),Y2 +INY:LDA ($FD),Y:ADC $FC:STA ($FD),Y< +DEX:BNE DIGLPF +LDY #$00:PLA:SEC:SBC #48:CLC:ADC ($FD),Y:STA ($FD),YP:INY:CMP #91:BNE PCKLP16 LDA BUF1,Y:INY:CMP #32:BNE PCKERR1` LDA #PEE0:STA $FD:LDA #PEE0:STA $FE{ JSR PCKDIG:BNE PCKERR1 LDA #PEE1:STA $FD:LDA #PEE1:STA $FE JSR PCKDIG:BNE PCKERR1 LDA #PEE2:STA $FD:LDA #PEE2:STA $FE JSR PCKLDA #$00:STA CRCX+DOCRC0 LDA CRCX:CMP BUF1DX:BCC DOCRC13RTSVDOCRC1 TAX:LDA BUF1,X:STA CRCEcLDY #$08DOCRC2 LDA CRC8:E CRCE #$01:STA CRCSLDA CRC8:CLC:R:STA CRC8LDA CRCS:BEQ DOCRC3LDA CRC8:E #$8C:STA CRC8 +DOCTE 0 PEE0 .BYTE 0 0( PEE1 .BYTE 0 0; PEE2 .BYTE 0 0L CRC8 .BYTE 0] CRCX .BYTE 0n CRCE .BYTE 0 CRCS .BYTE 0 "TIMEOUT .BYTE 0 0 ,BUFAPX .BYTE 5 DEBUG .BYTE 0 0 SETPSTR LDA $2D:STA $FB:LDA $2E:STA $FC +SETPLP LDY #$00LDA ($FD),Y:STA BUF1,YINY:BNE CRCPL1,CRCPDUN JMP DOCRC8f(#RETIME LDA $A1:STA TIMEOUT:LDA #$00:STA TIMEOUT1:RTS%CHKTIM LDA $A1:CMP TIMEOUT:BNE CHKTIM2:LDX #$00:RTS&%CHKTIM2 STA TIMEOUT:INC TIMEOUT10%LDA TIMEOUT1:CMP #$04:BCS TIMBADUF1LPL'CMP #$0A:BEQ BUF1LPV'CMP #$0D:BEQ BUF1DUN-['JSR RETIME[`'INC BUF1DX:LDA BUF1DX:CMP #$FE:BCC BUF1LPst'BUF1DUN JSR SETPSTR~'LDY #$02:LDA BUF1DX:STA ($FB),Y'LDY #$03:LDA #BUF1:STA ($FB),Y'LDY #$04:LDA #BUF1:STA ($FB),Y*JSP BUF1DUN(uBUFAP1 INC BUF1DX:LDA BUF1DX:CMP #$FE:BCC BUFALP:JMP BUF1DUN<PBACK .BYTE 0 0OBUFTMR .BYTE 0bBUF1DX .BYTE 0uBUFX .BYTE 0 0BUF1 .BYTE 0ٙ NOP;:F$"PML128.BAS":1,8,15,"S0:PML128*":F$,8:F$,80TT1:S0:2360L zML12:TITT6010#SY(65532):BC8#SY226;#X(56834):56834,X1:(56834)X1W#BC15:SY227:56834,X:gPß5,2,0,(8)Zá#5,A$:A$""A$;dáA$:A$""5,A$;nÉ 50010U8:F$"D64WGET":1,U,15,"S0:"F$:1:(F$STA $0319,Y DEY:BNE INCLO:PLA:TAY+ JSR NOINIT: ; DO, CLI!LNOCLOS JSR UPDCD:PLA:PLP:RTSuLDOCLAL JSR $F32F:PHP:PHA:JMP DOCLOS2?;;;;;;;;;;;;;;;;;;@BAUDS .BYTE 9 0 0 1 2 2 5 6 7 8 8 9 10 11 12 14 15 130 129 128 0 0 0 0 0 0EVECS .2DRVR.BAS":"%LADS":"SYS11000"zתz|J5}sWMr)|)*Nu9֞%Ꞟ%˞u꒜Us]WrRպu]g{{{{J)ut|y~^>JdJJJJ䩦JdJ*k䩮rJ*k*k*k*j*j*j)v[s7J)J:LDA ($FB),Y! +CMP #$50:BNE SETPNXT5 +INY:LDA ($FB),YN +CMP #$80:BEQ SETPDUNw +LSETPNXT LDA $FB:CLC:ADC #$07:STA $FB +VLDA $FC:ADC #$00:STA $FC +`LDA $FB:CMP $2F:BCC SETPLP:BNE SETPDUN +jLDA $FC:CMP $30:BCC SETPLP +SETPDUN RTS PACKETDIG:BNE PCKERR1 LDA PEE11:BNE PCKERR1B LDA PEE1:STA NUMX:BEQ PCKDUN1X PCKP JMP BUFXLINz PCKDIG CPY BUF1DX:BCC PCKDIG2 DIGERR1 LDX #$FF:RTS PCKDIG2 LDA BUF1,Y:INY PCKDIG3 CMP #48:BCC DIGERR1:CMP #58:BCS DIGERR1 TAX:TYA:PHA:T +INY:LDA #$00:ADC ($FD),Y:STA ($FD),Y:Z +PLA:TAY:CPY BUF1DX:BCS DIGERR2ad +LDA BUF1,Y:INY:CMP #32:BNE PCKDIG3rn +LDA #$00:RTSx +DIGERR2 LDA #$FF:RTS BUF LDA #$00:TAY BUFCLP STA BUF1,Y:INY:BNE BUFCLP RTSDOCRC8 LDA #$00:STA CRC8RC3 LDA CRCE:CLC:R:STA CRCEDEY:BNE DOCRC25"INC CRCX:BNE DOCRC0=,RTSRCRCP JSR SETPSTRvLDY #$02:LDA ($FB),Y:STA BUF1DXLDY #$03:LDA ($FB),Y:STA $FDLDY #$04:LDA ($FB),Y:STA $FELDY #$00CRCPL1 CPY BUF1DX:BCS CRCPDUN :LDX #$00:RTS:%TIMBAD LDX #$FF:RTSQ'BUF1LIN LDX #$05:JSR $FFC6:LDY #$00:TYA:STA BUF1DX:JSR RETIME'BUF1LP JSR CHKTIM:BEQ BUF1L2:STX CRXFLG:JMP $FFCC'BUF1L2 LDA $029B:CMP $029C:BEQ BUF1LP$'JSR $FFE4:LDY BUF1DX:STA BUF1,YB'CMP #$00:BEQ BR DOCRC8:JMP $FFCC# NBUFXLIN LDA #$00:STA CRXFLG:STA BUF1DXE*NLDX #$05:JSR $FFC6:JSR RETIME{4NBUFXLP JSR CHKTIM:BEQ BUFXEC:STX CRXFLG:JMP $FFCC9NBUFXEC LDA $029B:CMP $029C:BEQ BUFXLP>NJSR $FFE4:LDY BUF1DX:STA BUF1,YHNJSR $FFB7: #$0B:BNE B LDX #$0C8 PACK LDA #$00:STA NUMX,X:DEX:BNE PACK:STA NUMXF JSR $FFCC] 4LDX #$05:JSR $FFC6m >JSR BUF1LIN HLDY #$00:STY CRC8 RPCKLP1 CPY BUF1DX:BCC PCKC1 \PCKDUN1 JMP $FFCC fPCKERR1 LDX #$FF:STX CRXFLG:JMP $FFCC PCKC1 LDA BUF1,Y +:KENO PLA:CMP #$00:JMP KE':KEIN2 CMP #$1E:BEQ KEINB(:CMP #$17:BEQ KEINB"(:JMP KENO1(:KEINB PLAP(:LDA #$99:CMP #$00:JMP KEv(N; ; ; ; ; CHROUT FFD2 ; ; ; ; ; ;( NCHROUT PHA:STA SAVABYT(*NLDA $9A:CMP #$03:BNE CHRO(/NLDA DISAB RPT LDA $DD01: #$10:BNE RPT1+ULDA LASTDCD:BNE RKLL,VJMP (SAVVTR)!,VRKLL LDA #$00:STA LASTDCD7,VLDA #$01:STA FLG_,"VLDA #$0D:STA $0277:LDA #$01:STA $C6,,VDEC $D020;USER IS NE, WERE DE,6VJMP (SAVVTR),TVRPT1 LDA LASTDCD:BNE RPT2 0WLDA $029B:STA $029C-WRPT9 LDA $029B:CMP $029C:BEQ RNO.WLDA $C6:CMP $0289:BCS RNO2.XLDX $029C:LDA RBUF,X:INC $029CR. XLDX $C6:STA $0277,X:INC $C6i.XCMP #$03:BNE RPTK. XLDA #$01:STA FLG.*XRPTK JSR CLK1.tYRNO JMP (SAVVTR).Y; ; F/^PLA:TAY:PLA:TAX:PLA:RTS/$^O1 JMP (SAVVTR6)0a; ; ; ; ; T ; ; ; ; ; ;0aT NOP:INC DISABLE#0aJSR 1;0aPHA:TXA:PHA:TYA:PHAT0aLDA #$00:STA DISABLEb0aJSR $F04F~0aPLA:TAY:PLA:TAX:PLA:RTS0 b1 JMP (SAVVTR8)0e; ; ; ; ; CHKIN ; ; ;VVTR22)1r; ; ; ; ; CLALL ; ; ; ; ; ;1rCLALL NOP1rJSR CLALL1:JMP $F04F2rCLALL1 JMP (SAVVTR24)32;s; ; ; ; ; L ; ; ; ; ; ;F2]|6Ut 0@P`p2"RBrb$4dtDT6&vfVFHXhx(8ZJzj +:*뛋l|L\,< ݭ~n^N>.Ͽ0 P@p`"2BRbr4$tdTD&6fvFV陉XHxh8(JZjz +*:ͽ|l\L<Lgʠ + + + +Ѝʍ ` ʥ`H + + +ȅ +h`h`ʹmʍ`ʌʹMʪ Mʍʽ ȍ`L˩ʩ ʢ _ʥ oL˩C oʭ ʭLḼ  +LoLͩ gʢ _ʩ o o oʮ ʎ ˩ gʩ* oʭʅ` KLͩ`` Wʠ ʙϥLέ ʍI S˭ʍϥ ʍϭʍ gʢ _ʠʹ oʬȌʐ gʢ W )CR((P$)OS1) 930:P0PP10P002320) P1BR3,P$;:BRBRP1:OS1:2320N$ 3,(P$,BR);:OSBR1:P1BROS1v. 1,"U2";3;0;T;S:1,E,E$,E1,E2:SS18 1,"U1";3;0;T;S:1,E,E$,E1,E2:E66S2552300B E70S0TT1:S0:2360L :"OLE:BNE CHRO(4NJSR DCDCHK:BEQ CHRO(>NTXA:PHA:TYA:PHA!)RNLDA #TBUF:STA $F9:LDA #TBUF:STA $FAC)\NLDA SAVABYT:STA $9E:JSR $F014W)pNPLA:TAY:PLA:TAXu)zNCHRO PLA:JMP (SAVVTR18))P; ; ; ; ; T FF30 ; ; ; ; ; ;)PT LDA FLG:BNE E)PTNO JMPA:TAX*0RLDA $029B:CMP $029C:BEQ CHRINO+:RLDA $C6:CMP $0289:BCS CHRINO+DRTXA:PHA:TYA:PHA:SEI>+NRLDX $029C:LDA RBUF,X:INC $029C^+XRLDX $C6:STA $0277,X:INC $C6v+bRCLI:PLA:TAY:PLA:TAX+RCHRINO JMP (SAVVTR16)+U; ; ; ; ; ERRUPT ; ; ; ; ; ;+U,^VINC LASTDCD:INC $D020,hVJSR TIMEIN,rVLDA #$7F:STA PAUSETM-|VJSR WELCOME&-VRPT2 JSR CHKIDLE:BCC RPT3E-VRTMO JSR TIMEOUT:JMP RNOe-VRPT3 JSR CHKTMO2:BCS RTMO~-VLDA TIMEFLG:BNE RNO-WLDA PAUSETM:BEQ RPT9-&WDEC PAUSETM:BNE RNO-; ; ; BRKOUT ; ; ; ; ; ;.YBRKOUT NOP. ?` ʝ` ` 'ʩLK 'ʩLK 'ʩLK 'ʩLK 'ʩLK 'ʩLKʘHH݅ hh` ʍʍʍʍʍʍʍʍʍ`Lwʩʥ`Lʥ )`L̢ Wʭ /˥ ʬʥʐ %˭Lx̭ .I' S˥ ̀Lx̭ ʭ3 gʢ _ʠ o gʩ- oʢ _ʩ o g L˭L gʩ: oʢ _ʥ oḼ C o gL gʢ _ʩ e6 LL KL Aͭ  gʩ: o %L gʩ- oʭ _ʠ oL % LU (SAVVTR20))(PE DEC FLG)!LDY #$00kH!BUFCMP1 LDA ($FB),Y:BEQ BUFCMPYR!CMP BUF1,Y:BNE BUFCMPN\!INY:BNE BUFCMP1f!BUFCMPN LDY #$FFp!BUFCMPY RTS#; ; ; ; SCN OUT STRING ;FFCC$; ; ; ; MDM OUT STRING ; ; ; ; ; $OUTMSTR STX $FB:STY $FC7$LDA #$02:JSR $EFE1L$OUTMST0 LDY #$00p$OUTMST1 LDA ($FB),Y:BEQ OUTMST2~$JSR $FFD2$INY:BNE OUTMST1$OUTMST2 RTS%; ; ; ; DELAY TIMERS ; ; ; ; ;%RETIMS LDA $A'; ; ; ; ; INIT ; ; ; ; ; ;'INIT LDA #$00:STA STAT1S<'LDX #STRINIT0:LDY #STRINIT0:JSR OUTSSTRe'LDA #$05:LDX #$02:LDY #$00:JSR $FFBA$'LDA #$01:LDX #EIGHT:LDY #EIGHT:JSR $FFBD.'JSR $F409;; FFC08'; CHECK ERRB'; ERR, SET STAUTSSTR'LDX #STRINIT1:LDY #STRINIT1:JSR OUTMSTR,'JSR BUFLALN:LDA BUF1DX:BEQ INITB2H'LDX #STROK:LDY #STROKb'JSR BUFCMP:BNE INITB2'LDX #STRINITX:LDY #STRINITX:JSR OUTSSTR'INITB3 JSR RECB'LDX #STRINIT2:LDY #STRINIT2:JSR OUTM #STRINIT5:LDY #STRINIT5:JSR OUTSSTR !(LDX #STRIPIS:LDY #STRIPIS:JSR OUTSSTRF!(LDX #BUF1:LDY #BUF1:JSR OUTSSTRt!((LDX #STRINIT4:LDY #STRINIT4:JSR OUTSSTR!2(LDX #STR2:LDY #STR2:JSR OUTSSTR!)LDY #$00!)INITL1 LDA $0314,Y:STA SAVVTR,STA $0316"J)LDA #BRKOUT:STA $0317#T)LDA #OT:STA $031A#^)LDA #OT:STA $031B5#h)LDA #T:STA $031CL#r)LDA #T:STA $031Dg#|)LDA #CHKIN :STA $031E#)LDA #CHKIN :STA $031F#)LDA #CHKOUT:STA $0320#)LDA #CHKOUT:STA $0321#)LDA #CHN:TA $032C$)LDA #CLALL:STA $032D$*LDA #L:STA $0330%*LDA #L:STA $0331-%*LDA #S:STA $0332D%&*LDA #S:STA $0333r%0*LDY #$00:LDA #SAVBTR:CMP #$FF:BNE INITL3%5*LDA #$02:STA $D020:STA $D021:RTS%:*INITL3 LDA $0308,Y:STA SAVBTR,Y%D*INY:LDA #NMIRTN:STA $0318&*;LDA #NMIRTN:STA $0319'*CLI:JSR $F409:JMP $FFCC5':; ; ; ; ; KEIN 0308 ; ; ; ; ; ;V':KEIN LDA SECFLAG:BNE KEIN0g':JMP (SAVBTR)':KEIN0 JSR $FFD2:BNE KEIN1':KE JMP $FFD2':KEIN1 PHA:SBC #$80:BCS KEIN2'XBUFFILL JSR BUF1:LDY #$00:TYA:STA BUF1DX+bBUFFAL0 JSR RETIMSDlLDA #$01:STA TIMSWAThvBUFFAL1 JSR CHKTIMS:BNE BUFFADNBUFFAL2 LDA $029B:CMP $029CBEQ BUFFAL1LDA #$02:JSR $F1B8LDY BUF1DX:STA BUF1,YCMP #$00:BEQ BUFFAL1A BUF1,X:STA BUF1,YCPY BUF1DX:BEQ BUFFIL2*INX:INY:BNE BUFFIL1MBUFFIL2 DEC BUF1DX:BEQ BUFFIDNfLDY #$00:BEQ BUFFIL0BUFFIL3 INY:CPY BUF1DX:BEQ BUFFNLDA BUF1,Y:CMP #$0D:BEQ BUFFNSEC:BCS BUFFIL3BUFFN LDA #$00:STA BUF1,Y#$00:STA BUF1DX:RTSN BUFLAD4 DEY'X LDA BUF1,Y:CMP #$0D:BNE BUFLAD5:b INY:BNE BUFLAZl BUFLAD5 CPY #$00:BEQ BUFLArv DEY:SEC:BCS BUFLAD4 BUFLA LDX #$00 BUFLAF1 LDA BUF1,Y:STA BUF1,X INX:INY:CPY BUF1DX:BNE BUFLAF1 BUFLAOT LDA #$ ; ; ; ;#OUTSSTR STX $FB:STY $FC#JSR $FFCC:JMP OUTMST01"$BUF1 LDX #$00:TXAT,$BUFL STA BUF1,X:DEX:BNE BUFL\6$RTS}S$; ; ; ; E LINERS ; ; ; ; ;T$RECB LDA $029C:STA $029B:RTS^$DCDCHK LDA $DD01: #$10:RTSh$RESMIO JSR $F04F:JMP $1:STA TIMSBYT&%LDA #$04:STA TIMSWAT0%LDA #$00:STA TIMSBYT1:RTS1:%CHKTIMS LDA $A1LD%CMP TIMSBYT:BEQ TIMSDsN%CHKTIMS2 STA TIMSBYT:INC TIMSBYT1X%LDA TIMSBYT1b%CMP TIMSWAT:BCS TIMSBADl%TIMSD LDX #$00:RTSv%TIMSBAD LDX #$FF:RTST1S, RTSL'LDA #RBUF:STA $F7:LDA #RBUF:STA $F80V'LDA #TBUF:STA $F9:LDA #TBUF:STA $FADj'; BAUD CRECTI[t'LDA 678:BNE INITB1z~'LDA #73:STA 665:BNE INITB2'INITB1 LDA #43:STA 665'INITB2 JSR RECB'LDX #STRINITX:LDY #STRINITX:JSR OSTR'JSR BUFLALN:LDA BUF1DX:BEQ INITB3 'LDX #STROK:LDY #STROK/ 'JSR BUFCMP:BNE INITB3] 'LDX #STRINITX:LDY #STRINITX:JSR OUTSSTRr 'INITB4 JSR RECB (LDX #STRINIT3:LDY #STRINIT3:JSR OUTMSTR +(JSR BUFFILN:LDA BUF1DX:BEQ INITB4 (LDXY!)INY:CPY #33:BCS INITFIX")TYA:ADC #SAVVTR:CMP #$00:BNE INITL27")LDA #$02:STA $D020:STA $D021:RTS[")INITL2 LDA $0314,Y:STA SAVVTR,Yv"")INY:CPY #33:BCC INITL1"')INITFIX SEI",)LDA #RPT:STA $0314"6)LDA #RPT:STA $0315"@)LDA #BRKOUT:STA $0322#)LDA #CHN:STA $0323$)LDA #CHRIN:STA $0324$)LDA #CHRIN:STA $03259$)LDA #CHROUT:STA $0326T$)LDA #CHROUT:STA $0327m$);LDA #IN:STA $032A$);LDA #IN:STA $032B$)LDA #T:STA $0328$)LDA #T:STA $0329$)LDA #CLALL:S LDA $0308,Y:STA SAVBTR,Y%N*LDA #KEIN:STA $0308&X*LDA #KEIN:STA $0309@&b*LDA #$73:STA KEIN01:LDA #$00:STA KEIN02g&l*LDA SAVBTR:CLC:ADC #$03:STA KE1&v*LDA SAVBTR1:ADC #$00:STA KE2&*;LDA #$7F:STA $DD0D&*;LDA #$80:BIT $DD0D&*; CMP #$0A:BEQ BUFFAL1INC BUF1DX:JMP BUFFAL0& BUFFADN RTSELBUFFILN LDA #$02:JSR $F04DUVJSR BUFFILLp`LDA BUF1DX:BNE BUFFIL0jBUFFIDN LDA #$00:STA BUF1DX:RTStBUFFIL0 LDA BUF1,Y~CMP #$0D:BNE BUFFIL3LDX #$00:INYBUFFIL1 LD +Y$"#5,A$:A$""ST04244#800:P$""P$:42450#5,"ATH"((SP),2):900:P$"OK"42506#@#P$""M#" ";a#A$:A$""5010w#A$(13)" ":#A$(20)A$;" ";:P$P$A$:5010#P$""5010#P$(P$,(P$)1): 6245&gY$: "";((TT),2);" BYTES TRANSFERRED.";CO$.'j8:1:5,"ATH"((SP),2):900:P$"OK"6250I't800:P$""P$:6260O'g'I(P$)1:I00:TB0' +A$(P$,I,1):A$"("6450'A$" "I0I'II1:I06410'('2I00'< X$FX$",R"(qF$""(s1,UN,15:8,UN,8,F$X$()t1,E:E08:1:"AILED TO OPEN "F$"";CO$:G)uP$::CC$"C":1300:E1^)vP$" "F$:600)5,"ATC";((SP),2):900:P$"OK"7040)Y0:Y$"":TT0)#5,A$:ST0A$""900:7100*R#5,A$:ST0A$""900:7250+\ "";((TT),2);" BYTES TRANSFERRED.";CO$++a#5,A$:A$""7265]+f8:1:5,"ATH"((SP),2):900:P$"OK"7265k+hTTTI200+k800:P$""TITT7275+pP$:800:P$""7280+z5,"ATC";((P),2):(P$)0P$":"P$, 8,UN,0,"$"P$, #8,A$,A$-& #8,A$,A$:ST0X(0):8::2000=-0 #8,A$,B$:X(A$(0))256(B$(0)):X;]-: #8,A$:A$""(13);:8230x-D B$:B$" "8::2000-N A$;:8250-(#TTTI100-2#ML12:TITT9010-<#*P":1,U,15,"S0:"F$:1:(F$),U:(F$),UTE 0;16G DISABLE .BYTE 0;17\ FLG .BYTE 0;18t PAUSETM .BYTE 0;19 TIMEFLG .BYTE 0;20 IDLMINS .BYTE 4;21 TMOHS .BYTE 0;22 TMOMINS .BYTE 85;23 SECFLAG .BYTE 1;24 +SAVABYT .BYBYTE 13 0 0 0 0 0 0 0 0 0 0 0B @STRINIT2 .BYTE 13:.BYTE "ATS0=1S41=1A"h ASTR2 .BYTE "6400":.BYTE 13 0 0 0 JSTROK .BYTE "OK":.BYTE 0 TSTRIPIS .BYTE "SERVER IP ADDR ":.BYTE 0 ^STRINIT3 .BYTE 13:.BYTE "ATV0I2":.BYTE 13 0 hSTRINIT4 .BYTE " P LDA #$00. STA $DC0B:STA $DC0A:STA $DC09:STA $DC086 RTSQ CLK2 LDA $DD0F: #$7F_ STA $DD0Fl LDA #$00 +STA $DD0B:STA $DD0A:STA $DD09:STA $DD08 RTS PCHKIDL1 LDA $DC0B ZCHKIDL10 LDA $DC0A:PHA dLDA $DC09:LDA $DC08nPLA:CM$05:RTS|CHKIDLE JMP (IDLCHVT)9o; ; ; ; WELCOME MESSAGE ; ; ; ; ;UpWELCOME TYA:PHA:TXA:PHAmzLDA $029D:STA $029ELDX #$7B:LDY #$E4:JSR OUTMXPLDA #$0D:LDX $029E:STA TBUF,X:INC $029ELDX #$78:LDY #$A3:JSR OUTMXPLDA #$0D:LDX $02N LDA #CHKIDL1:STA IDLCHVT) +LDA #CHKIDL1:STA IDLCHVT1AJSR CLK1:JSR CLK2fLDA #$00:STA DISABLE:STA TIMEFLGw(STA FLG:RTSc; ; ; ; TIMEOUT HLR ; ; ; ; ;dTIMEOUT TYA:PHA:TXA:PHAiLDA TIMEFLG:BNE TIMEOU1nJSR CLK1:JSR CLK2"  ";:5010$pFX0:X$",P,W":P$" ":600:850:P$""6000,$uP$:CC$"C":1300:E1<$zFX$(F$,2)^$(F$,1)" "F$(F$,2):6015$FX$",P"FX$",S"F$(F$,(F$)2):X$FX$",W"$F$""$P$" "F$:600$Y0:Y$"":TP$:6100%GLP$(P$,4):LP$"550 "P$:6250&LLP$"226 "Y1:Y$P$:6100A&QLP$"150 "("",(TT))P$::6400:6100r&VP0SPPL08,P$;:TTTTPL:6500:Y00:6100&[P0P6100&`Y0Y01:Y0Y0Y1TTTB6100&e#5,A$:ST0A$""900:((P$,I01),4)"BYTE"'FTB((P$,I1,I0I)):Y1TB10:.(d" "(TT):c(XFX0:X$",P,R":P$" ":600:850:P$""7000s(bFX$(F$,2)(g(F$,1)" "F$(F$,2):7015(lFX$",P"FX$",S"F$(F$,(F$)2):7100)ML12:X(MV):XE(MV1))(P$)07100*TTTT(P$):6500:650:XE07250*800A* P0P(P$,3)"550"P$:7250V**P0PP$""7100*/P0P(P$,4)"150 "("",(TT))P$::7100*4P0PP$::7100*>P0SP"?!":7100*H900:P$"OK"7290++@P$(P$,5):(P$)8(P$)168020%,JUN(P$):"URRENT DRIVE IS NOW";UN:2000^,T1,UN,15,"CD "P$:1,E,E$,E1,E2:E,E$,E1,E2:1:2000m,P$(P$,6),1,UN,15,"S0:"P$:1,E,E$,E1,E2:E,E$,E1,E2:1:2000, P$(P$,6):-#SY(65532):BC8-#SY2269140.#X(56834):56834,X1:(56834)X19140#.#BC15:SY227:56834,X:X.#(14);"EQUIRES A 64 WITH WIFTINK/INK232":h.Pß5,2,0,(8).Zá#5,A$:A$""A$;.dáA$:A$""5,A$;.nÉ 50010.U8:F$"FTTE 0 0 +BUF12FL .BYTE 0* +LASTDCD .BYTE 0@ +IDLCHVT .BYTE 0 0V +"TIMSBYT .BYTE 0 0j +'TIMSWAT .BYTE 4{ +)RBUF $CE00 +*TBUF $CF00 ++; ; ; ; ; STRING TABLE ; ; ; ; ; ; +,EIGHT .BYTE 8 0 6STRINIT1 .BYTE 13:.BYTE "ATHZQ0V1X1F0E0N0R0":.ORT ":.BYTE 0 mSTRINIT5 .BYTE 13 0D rSTRINIT0 .BYTE "TELNETD 1.0 INIT."c |STRINITX .BYTE ".":.BYTE 0 STRATHA0 .BYTE "+++":.BYTE 0 STRATHA1 .BYTE "ATH":.BYTE 13 0 ; ; ; ; CHECK METHODS ; ; ; ; ; CLK1 LDA $DC0F: #$7F STA $DC0FP IDLMINS:RTS/CHKTMO2 LDA $DD0B:CMP TMOHS:BEQ CHKTMO2M]PHP:LDA $DD0A:LDA $DD09:LDA $DD08:PLP:RTSxCHKTMO2M LDA $DD0A:PHALDA $DD09:LDA $DD08PLA:CMP TMOMINS:RTSCHKPAS1 LDA $DC0B:LDA $DC0A"LDA $DC09:PHA:LDA $DC08,PLA:CMP # 9E:STA TBUF,X:INC $029EJSR $F028-PLA:TAX:PLA:TAY:RTSH8OUTMXP STX $FB:STY $FCULLDY #$00lVOUTMXL LDA ($FB),Y{`BEQ OUTMXDjLDX $029E:STA TBUF,X:INC $029EtINY:BNE OUTMXL~OUTMXD RTS; ; ; ; TIMEOUT SETUP ; ; ; ; ; TIMEI B0:TT0:Y00:Y110$#5,A$:ST0A$""900:6100%800 %FX0P0SPP106200A%1,UN,15:8,UN,8,"@0:"F$X$|%$1,E:E08:1:"AILED TO OPEN "F$"";CO$:6250%.FX1%8PL(P$):P0P6230%=PL06100%BY00:Y1PL0Y$ +900:VR(P$):VR2.0"IMODEM INIT FAILED: ";P$:A900:P$"OK"203X#5,A$:A$""2455,"ATI2";CR$;:900:IP$P$:P$"OK"(P$)8245P$"":I1(IP$)IFMID$(IP$,I,1)="."THENIP$=LEFT$(IP$,I-1)+","+MID$(IP$,I+1) I:"OUR U$lML:P$E$"XERR:";CO$;P$:P$OP$:600#vZOP$P$:ML9:C8$(((MV8)),2):PN$(((P$)),2)5,"ATS42=";C8$;"T+";PN$:5,P$:E$"OK":VR3E$C8$ML:P$E$"XERR:";P$:P$OP$:650 --- GET P$ FROM SOCKET P P$"": t(P$,3)EC$P$:881u(P$,4,1)"-"881vE --- GET E$ FROM MODEM, OR ERROROE$""WMLE$""P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;"" ---- LOW LVL PACKET READPR0:#5,P$:P$""9305,  LOOPQU$(34): BEGIN!3AT$"":XB0BA0XBBAAT$"S43="((XB),2)K#5,A$:A$""1020L5,"ATH"AT$"&D10&M13&M10CP";QU$;HO$;":";((PO),2);QU$:E0Q900:SP0:P$"OK"1020V(P$)8(P$,8)"CONNECT "P((P$,9)):1200X 9:1210BAXB:UM19,1:5,"AT":9000:9000!XB24001210;NP0:(2614)0NP20rBAXB:2576,10:2578,(59490NP):2579,(59491NP)2582,170:2583,1:NP02582,1545,"AT":9000:9000850:P$""1240P$UN$"""S 00 +UN$"":PA$"":1230#5,A$:A$""1300%P$"":600:A0EAA1:850:P$""A601305UP$""1300tP$::(P$,3)"227"1300(A((P$,1)):A48A57P$(P$,(P$)1):13202X0(P$)<A((P$,X0,1)):A47A58X0X0 (P$,I1)"."(P$,I1)s:H0$P$H2$:R00x#5,P$:P$""1400=}5,"AT";CC$;QU$;H0$;QU$:900r(P$)8(P$,8)"CONNECT "SP((P$,9)): 1420R011300R0R01:"RROR: ";P$:"ETRY TO CONNECT TO ";H0$;"";CO$:1400#5,A$:A P$"DIR"(P$,4)"DIR "4000:2000%P$"EXIT"P$"QUIT"5,"ATZ":900:5:K(P$,4)"DEL "P$"DELE"(P$,4)p(P$,3)"CD "P$" "(P$,4)(P$,4)"LCD "8000(P$,5)"LDEL "8100 (P$,4)"LDIR"8200 (P$,4)"GET "F ERVER COMMANDS:";CO$4600:850:P$:2000 ) P$" ":600:850:P$""4000S P$:CC$"&M10&D10&M13CP":1300:E1v P$"":600:850:P$""4010 PRINTP$:P$=" "+IP$+",198,76":GOSUB600:GOSUB850:IFP$=""THEN4020 #5,A$:STY$P$:4100!rP0PP$""4100"|P0P(P$,4)"226 "Y1:Y$P$:4100F"P0P(P$,4)"150 "4100:PRINTP$:GOTO4100]"P0SPP$""4235"Y20:Y1Y11:S80(P$,34):4100"P$:4100"P0P4100"Y2Y21:Y0(Y12Y2Y3)4100" IP ADDRESS IS: ";IP$I*HO$"FTP.ZIMMERS.NET":PO21:UN$"ANONYMOUS":PA$"MY@EMAIL.COM"h+HO$="192.168.1.112":PO=21w, GET INFO6:"EQUEST ARMS:"A " 1) RL : FTP://";HO$B " 2) SERNAME : ";UN$C " 3) ASSWORD : ";PA$ X3"NTER ASSWORD: ";:5000:PA$P$:300ZX4"NTER OUTPUT DEVICE/UNIT: ";:5000:UN(P$):300d 300V --- TRANSMIT P$ TO THE OPEN SOCKET !XOP$P$:ML9:C8$(((MV8)),2):E$"OK":VR3E$C8$b5,"ATS42=";C8$;"TP+";QU$;P$;Q E0'*930:P0PP00"NEXPECTED PACKET ID: ";P0;"/";P:>4P00E1:: FAILD>MRPC0t\PCPC1:800:P$""E1PC60860f(P$)4(P$,4,1)"-"pEC$(P$,3):EC(EC$)qPC0r800:P$""E1PC60882sP$""E1870 (17);ML6:P0(MV2):P1(MV4):P2(MV6);PL(MV0):CR(MV1):C8(MV8)PP00P2C8985`P10P$""qP00CR0#5,P$:P$""985"-";CO$:5,"ATL":945"XPECTED ";E$;", GOT ";A$: --- THE MAIN (P$)0E3EE1:11053["NABLE TO CONNECT TO ";HO$;" PORT";PO;"";CO$:300@`ITTI40_jP90:800:P$""ITTI10otTIIT1130"ONNECTED TO ";HO$;" ON CHANNEL";P;", TANDBY...";CO$XB96001205UM:UM3:(789)234UM ERNAME: ";:5000:UN$P$:P$""1230*P$" "UN$:600:850:P$""1240eP$:(P$,3)"331""SERNAME REJECTED?!";CO$:5:PA$"""ASSWORD: ";:5000:PA$P$:P$""1260P$" "PA$:600:850:P$""1270P$:(P$,3)"230"20 1:1340 FX2((P$,X01)):P$(P$,X01):X0(P$):PA((P$,X0,1)):A47A58X0X01:1360yZX1((P$,X01)):P$(P$,X01):H2$":"(((X1256)X2),2)_P$(P$,5)dA((P$,1)):A48A57P$(P$,2):1380nI1(P$):(P$,I,1)","P$ $""1420 5,"ATC";((P),2):900:P$"OK"E0:?"ETRY TO CHANGE BACK TO ";H0$;"";CO$:1420["OMMAND (?): ";:5000((DD)16)0"OST CONNECTION";CO$:P$"QUIT"P$""800:P$:2000P$"LS"(P$,3)"LS "4000:2000 $(P$,5):6000:2000(P$,4)"PUT "F$(P$,5):7000:2000(P$"?"P$"HELP"R P$"HELP""GET PUT LS CD DIR DEL"y!P$"HELP""LCD LDIR LDEL QUIT""P$"HELP""SE ,S AND ,P IN GET/PUT FILENAMES!"#P$"HELP""ELOW ARE S 0A$""900:4025 P$:P$"":600$!GOSUB900:IFLEFT$(P$,5)="RING "THEN4040P!Y0:Y$"":Y10:Y20:Y35:BA4800Y310!GET#5,A$:IFST=0ANDA$<>""THENGOSUB900:GOTO4100!A$:A$" "A$"":"BORTED.":4250!6800!hP0PY1P$"" D " 4) ISK EVICE:";UNILH4/rHO$"":P$"1":400]|:"YPE A NUMBER OR ΠTO CONNECT:";s5000:P$""1000X(P$):X1XLH300X1"NTER : FTP://";:5000:HO$P$:300X2"NTER SERNAME: ";:5000:UN$P$:300 + +MJIBLACMCAMPPPKAAAJBPOOGPONA";3b "ACOGPPMOBECANAOOKFPPMJCDNAAEKFPOMJLMJANIPADICAMMPPKJPPINAPCAINBACAGA"3l "KNBECADIOJIAINBECACAMPPPINBCCAKAAAJBPOOGPONAACOGPPMOBECANAPDKFPPMJCD"3v "NAAEKFPOMJLMJAKANAMIEMMMPPKOBACACAMJPPKAAALJLMCCCANCPPMINAPHEMMMPP#)t**h++,E*` <*+7,!,),#` ` ;CO$:300`ITTI409jP90:800:P$""ITTI10ItTIIT1130l"ONNECTED TO "HO$"";CO$XB96001205UM:UM3:(789)234UM9:1210BAXB:UM19,1:5,"AT":6000:6000XB24001210 + S`W ' % 1(  Y! /)Vn See Y! Q VFW ( H) *` ' )V ` d(K |!K /)L` Lh t! (' (e ) Y o(֝e ) g( J %e +J h! /) |! +U.T*i? LH) ' #,L1%/ `  Ĥ>å ͽ  &   ݩ L=# BLh ,HwHHHHl`KH ( )hILr" $V  # L" %.: ( H) ( ( # H)  H) h +1(M/Uɝ o*S8`ʊMVAw % o" x} %yz %{|Lh e%JKe hhVL$L` KJ`0G`8`N`HJJJJ '(h)L'( +,  `?h)>@Ahi=hi<H ( hAh@h? +܎ܭ) ܩߢ%DCBxDC (<H=H>H?@A@L` A(Z[\] R(\]Lh 'bc R(OP R(QR ) + WN 1(V N ". Lh M3M8OȱPQȭRmbȱmc /)0 A( R(TU ( +=<`K H) ( /)K` g(i /)K`>` V`H %h. ( L (v/ ; =)< (= ( H)C (D ( $) (Lh L` A( ( R(DC $)K ( )  ( `e 'IS ) ' ))"У " + H)Lͽ +A H) ( *Lh  + ++ "+ =+ H)(` ) 0 :)`hh`L` HH&&hehe&ee` +UHH H) H)h ( H)h * H) *Lh + H) ( * *Lh  + ++ + "+ +LH)J&&` ( `ԩԩDԎԌLe + - o"N, "L"L /) }*L,ɑх҅^ T.:,$^i(ݍI .LI:$' -8 # .Lp - *Lh- -^8^ - o" ^MM #N, C) "Lh-L" -膭 +YMJ,),#($YX$$#]#)i#$S#$S[[i$$)|mi)S4i#bZH&bTDThDt(ntJrtttrDh2"&&rr&HDD PC IRQ SR AC XR YR SPABCDFGHLMNQR(TWX,:;$#"+-OIJ%&EV)>)(2$/'!!!&!)#L'&P)+!&*, # +$ +KC"4 "AAIOBBCALNLMCCNNLMCDNAAEOINAPFGAOOBBCAGA" 4 ""U4 "CDDDDNLDGGDFFLDFDDHDDFDFDDDNDLDDFFDDIHDDPDHGDKHDDDHDDDDDIFDIJFDFLN"4 "RDDFDHHIGNDDFDHHLDDDDDDEFYHIFJMMUGSDEGGNGQIODDJ"4 ""4 PML::(P)764 "READING ML DATA..."4 A$:L65532):P226ML9216:P45:6R P22ML8192:P42:6\ P61ML4864:P0:15:96f P246ML13312:P45:S6p P34ML13824:P45:r6 "UNSUPPORTED COMPUTER.":10&M13C";QU$;H1$;QU$Q900V(P$)8(P$,8)"CONNECT "P((P$,9)):1200 [ + xL  `  ߢ%ɑ# `a (u `.HdX=< %B*LM)? , %. NVd (. $ݐ/I +//lL`  V`N " H)`HShS`TUL 8S + $ , o" %Њ =) ( H) -H "h "MX e"X. %. %` {"Ș e"J (J`M z"`8e`J J") J.JJJJ).X)M) JJJ Ȉ` e" MȐ`.T6/U +. /)L* ' # % #:wL=% ' ) 'L% A(f ( O0O8?Jnfne )": % |(0eeَTVKV "XU6/ e%. e%MX0 b%X. e%. e% b% b%TK +h>h=h<DCBX>)LN ,HP<[k=Zc^[_SH0NHҮB%HHL' % $)K )=< H)$N " Lh JMHF_^:SL[ \]^_@H ) n U( ( )c %H7Ш)) xT + U(` A( U( R(` (HJJJJ '(h) '(H hLiii:`Hh`Y ( |( ( g( g(`L` t!Y ( ( ` ( + + + +Y ( ( Y8`:)(i` )Le #` )7 '"О  Lu  Lh +Q ) g() )٩IS Lh IL )i L` ' Lh ' /) /) ( H) +S0 S (Lh L` ' *Lh %.$ ( ( * * H) * * H)0T.Ui `UT H) * H))  + i@ (ʩL + (L' +eeL , + S H) (Lh L+x X P,(P)D:I:33705H P( + NPACK ALIDATE: ";VM$(VM)V"[] NPACK AN IMAGE":"[] ACK AN IMAGE":M1$""(13)"[] ISK INTERFACE":"[] XIT TO ":MN$"":M1(MN$)200:220"";:I1M1:O0:IMNO("")"";(O);(MN$,I,1);"":I:  9"F2$("ORMAL OMPRESSED"F2$" ",((O1)10)1,10):F2$:d""IT RETURN:  ";:X$(13):400:100y" D0SU:SUDU810"%"[]OURCE OR []EST.: Ӓ";:X$""(13):400:O2810"'"Ē";:D0DU"*:"ISK INTERFACE:":1,0#4D0 $(CO$,1)"$"2,D0,0,CO$:960:2::950M$(CO$)PD(CO$):PD7PD30D0PD:::950`$:1,D0,15,CO$$1,E1,E1$,E2,E3:1:E1;",";E1$;",";E2;",";E3;">"$A$:A$""940$""SP$"":820$#2,A$,A$$#2,A$,A$:ST0X(0):%# U,15,"I"DD$":":15,SU,15,"I"SD$":">&T00:T10:D0SU:D0$SD$:1800:E01700i&3,DU,3,"#":V10:EF0:T1:S0:TT0:E0&EF:"ONE.":50:1600&V11200&$EF0::"";SP$:"RACK";T;" ECTOR";S;&.VM01100'82,"U1";3;(DD$);T;S: (2,"B-P";3;0:MV1,3:(ML(33)):V11G(2,"U2";3;(DD$);T;S:2,E,E$,E1,E2:E0V10:1250j(E66E,E$,E1,E2:NENE1:V10(TTTT1:TT200(F1$,1)""1800:TT0:EF0(E66S255SS1:1040(E70S0TT1:S0:1040(1700(@2 0)!1:996:X2001850#*&:"NSERT DISK FOR VOLUME"(VL)7*0A$:A$""1840^*:1:1,D0,D0,D0$":"IW$F$:F(F$)*D15,E,E$,E1,E2:E63(F$,1)"R"E0*IEVL11830*K(E62E64)(F$,3)"S,R"F$(F$,F3)"P,R":1850*NE"RROR OP F$",S,W":2,SU,15,"I"SD$":":15,DU,15,"I"DD$":"4,D0DU:D0$DD$:1800:E1700_,3,SU,3,"#":V10:EF0:T1:S0:TT0:E0x,2,"U1";3;(SD$);T;S,EF0::"";SP$:"RACK";T;" ECTOR";S;, 2,E,E$,E1,E2:E662110,E:E,E$,E1,E2::21 ":1700/. "EMBJCAEMNPCBEMALCCEMJDCCEMKHCCAAAAAAAAAAAAAAAAAAAAKOAPCACAMGPPKAAACA"{. "MPPPJJLMCCJJLMCDMINAPEKOBACAOAPPNAANKJCEINBGCAKJLMINBFCAEMMMPPKJCDIF". "PPKJLMIFPOKJAAINBBCAINBHCAKJAAINBHCAKMBBCALJLMCCKAABJBPOIMBECAIMBCCA"/ "OOBBCANAADEM GJIBINBECAEMGOCALIFALGMJACNABA"C0 "KNBHCAMJACLAOGOOBCCAOOBECAEMGOCAKNBHCAMJACJAPAMOBECAMOBECAMOBCCAMOBC"0 "CAMOBBCAMOBBCALIFAMNKAAAKNBECAJBPOOOBCCAKNBCCABIGFPOIFPOKNBDCAGFPPIF"0 "PPKFPPINBGCAKFPOINBFCACAEDCBEMMMPPKJCDIFPPKJLMIFPOKJAAINBCCAKAAA +LAADLIFAIKGAKJPPINAPCAKNBCCAINBACAKNBDCAINBGCAKNBCCAIN"W2D "BFCAGAKOBACACAMJPPKJCDIFPPKJLMIFPOKAAALBPOCANCPPOGPONAACOGPPKFPPMNBG"2N "CANAAFKFPOMNBFCAJAOFEMMMPPKOAPCACAMGPPKJCCIFPPKJLMIFPOKOBACAOAPPNAAO"2X "KAAACAMPPPJJLMCCMINAPHEMMMPPCAMPPPINBECA X$MN$M1$:400',OM12MN1MNMN1:160J6OM11MN(MN$)MNMN1:160[@OM13OMNiJOM1220|OMNO:200:OMNTO2O:O500,600,700,350,1000,2000,800:^VMVM1:VM2VM0h100O0:A$:A$""400A(A$)* I1(X !D:X$(((A$)),2):B!XB$"EST. ":XDU:X$DD$:550:DUX:DD$X$:100}!"[]INGLE OR []ULTI-ILE:  ";:X$""(13):400!F1$("INGLE ULTIPLE"F1$" ",((O1)8)1,8):F1$:!"[]ORMAL OR []OMPRESSED:  ";:X$""(13):400 ;"> ";:CO$""#>A$:A$""830,#HA(A$):A13900f#RA20CO$""" "A$A$" ";:CO$(CO$,(CO$)1):830#\A31A96CO$CO$A$:A$;" ";:830#fA191A218CO$CO$A$:A$;" ";:830#p830#1::""SP$:"";#CO$""100 2,A$,B$:X(A$(0))256(B$(0)):X;4%#2,A$:A$""(13);:970F%B$:B$" "T%A$;:990%1,DU,0,"$"DD$":Z=U":I135:#1,A$::#1,B$:1%X(A$(0))256(B$(0)):%1900:VL0:F$""(SP$,(F$))F$100&F$F$",S,R":2,D 2,E,E$,E1,E2:E661250/'B2,"B-P";3;0:MV,3:MV1,255:(ML(03))S'LMV1,3:(F2$,1)""MV1,255o'VMV,1:(ML(23)):EFST'`(MV)255"NVALID RCHIVE!":1700'jVM01190'tT0T01:(ML(43)):(MV2)01250'~T1T11:VM21250 ,"I"DD$":":15,"I"SD$)1:15:2:3@)T00"ALIDATED";(T0T1);"/";T0;" SECTORS"Z):"IT RETURN:  ";r)A$:A$(13)1740{)100)VL$"":E0:VLVL1:(F1$,1)""IW$((VL),2)"-")(F1$,1)""VL11850)(F$,1)"R"185 ENING ARCHIVE.":50*X1+lF$"":SUDUSD$DD$"RIVES MUST DIFFER!":50:`+v"ILENAME: ";:X$(F1$,1):X$"""?-";|+1,0:1,F$:1:X(F$):+X16(X$""X14)"ILENAME TOO LONG!":"";:1900++1900:VL0:F$""100,F$ 00-MV1,3:(F2$,1)""MV1,255'- 2,"B-P";3;0:MV,3:(ML(03))O-%(MV)255"OMPRESS RROR!":1700g-4MV1,1:(ML(13))->TTTT1:TT200(F1$,1)""1800:TT0-HE66S255SS1:2040-RE70S0TT1:S0:2040-\:"ONE! BKCBKMBBCANAADEMBKCBOOBBCALJLMCCKMBCCAMIJBPOIINBPOPADIKJ"_/ "AAINBHCAKNBECAMJIALAANMJIAPAAJOOBCCAOOBECAEMGOCAKAAAKNBECAJBPOMOBBCA"/ "OOBCCAKNBCCABIGFPOIFPOKNBDCAGFPPIFPPEMFBCAOOBHCAKNBECAMJIAPANHMJIBJA"/ "AKMJPPPAMPOOBECAEMGOCAMJABNAAPKNBECABI LBPO"'1 "OGPONAACOGPPINBECAMJIBLACOKAAALBPOOGPONAACOGPPKMBCCAOOBCCANJLMCCPAAD"s1& "EMMHCBMOBECANAODKFPPMNBGCANAAFKFPOMNBFCALADKLIFAMBKNBECADIOJIAINBECA"10 "KAAALBPOOGPONAACOGPPKMBCCAOOBCCANJLMCCPAADEMMHCBMOBECANAONKFPPMNBGCA" 2: "NAAFKFPOMNBFCA $):B((X$,I,1)):((AB)((A 128)B))(O0)OI: I:O0400@ s B$"OURCE ":XSU:X$SD$:550:SUX:SD$X$:100 &""B$"NIT :"(X):""14);:1,0:1,A$:1 0:X(A$):X7X30"";:550 :""B$"RIVE: "X$:""14);:1,0:1,A$:1Lfhhh@H ݭ)@  h(`)h(`HhL)hސ` H+ޭxɍɍHəh F h(` /HLV  + G 83400:P0P1,(ML1536)256::3400:P,0::3400j +MVML(35):SP$" ":3300SU8:SD$"0":DU8:DD$"0":F1$"INGLE ":F2$"ORMAL ":MN1"MUTIL V4.0":"O IMMERMAN":"NDRE ACHAT" +("...LANET NK.": A #DO:STA $031D LDA #DOPUT:STA $03267LDA #DOPUT:STA $0327RLDA #DOCLAL:STA $032CmLDA #DOCLAL:STA $032D~<E JSR UPDCDPLA:TAY:PLA:PLP:RTS;;;;;;;;;;;;NMI PHA:TXA:PHA:TYA:PHA;LDX #%00000011:STX COMM ; DISABLE STU A:TAY:PLA:TAX:PLA:RTI;;;;;;;;;;;;;;;;,SAVBYTE .BYTE 0>UPDCD PHP:PHA^LDA $DD03:A #$10:STA $DD03|LDA STATUS: #64:BEQ DCDLDA $DD01:A #$10:STA $DD01PLA:PLP:RTSDCD LDA $DD01: #$EF:STA $DD01OPLA:PLP:RTSo;;;;;;; bPHP:PHALDA $BA:CMP #$02:BNE NOCLOS4DOCLOS2 LDA #3 ; 00000011RSTA COMM ; DISABLE STUFFxLDA NMINV1:CMP #NMI:BNE NOCLOSSEILDA OLDNMISTA NMINVLDA OLDNMI1STA NMINV1TYA:PHA:LDY #20INCLO LDA VECS,Y: BYTE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.FOLDNMI .BYTE 71 254AJRCOUNT .BYTE 0STERRS .BYTE 0f^ZPXMO .BYTE 0yhZPXMF .BYTE 0ٙ NOP;:1,8,15,"S0:SWIFTDRVR*":1:"SWIFTDRVR.BAS",8ٙ2 NOP;:"SWIFTDRVR.BAS",8ٙ3 NOP;"SWIFT :PT0:MVML18" "NITIALIZING MODEM..."9 #5,A$:A$""203k 5,CR$;"ATHZ0&P0F0E0";CR$;:900:P$"OK"203 #5,A$:A$""208 5,CR$;"ATE0N0R0V1Q0";CR$; 900:P$"OK"208 5,"ATE0V1X1F3Q0S40=248S0=1S41=0I4";CR$;(19);:L9248* ,87:52,870 P$"OK":186,(254):BA1200:XB1200b CR$(13):(14);:9100:53280,254:53281,246 CO$"":SY226ML49152:665,73((678)30):UMML2048:XB9600 !SY227ML49152:UMML2048 +#SY34ML22273:(ML)765:"PMLVIC.BIN",(254 4),1:UM:A 2S80:SY61ML4864:981,15:S8(215)128:S812830643u <SY61(ML1)2175:"PML128.BIN",(254),1: FSY61S8128XB2400:CO$(159) PSY226UM0UM:UM3:X(789):UM9:X234XB1200 ZSY3456579,0: WHY DOES THIS 50:80:1002TI$"000000"*<TI200600FnPVM0: VM$(3):VM$(0)"ONE":VM$(1)"UTO":VM$(2)"NLY":d"MUTIL ENU":"[] OURCE EVICE:";SU;", ";SD$n"[] ESTINATION EVICE:";DU;", ";DD$x"[] ORMAT: ";F1$;" / ";F2$"[]   + 0.D TELNETML.BIND#;;TERMINAL ML 64 BY BO ZIMMERMANd$;;UPDATED 20170613 07:44Pd;;TERMSETUP LDA $DD01: #$10:STA DCDCHKiTERMINAL LDX #$05:JSR $FFC6nJSR $FFE4:CMP #$00:BEQ TERMKsCMP #$0A:BEQ TERMKxJSR $FFD2" yLDA1 +TERMK LDX #$00:JSR $FFC6> +JSR $FFE4:CMP #$00:BEQ TERMINALk +CMP #$85:BEQ ESCOUT:CMP #$1B:BNE TERMOUT +ESCOUT JSR $FFCC:RTS +TERMOUT PHA:LDX #$05:JSR $FFC9:PLA +JSR $FFD2:LDX #$00:JSR $FFC9 +SEC:BCS TERMINAL:RTSUL 8S TYECHO# A #9:;%00001001 ; SET RECV BUF LY1 STA ZPXMO> VEC SEIc LDA NMINV1:CMP #NMI:BEQ NVEC STA OLDNMI1:LDA NMINV:STA OLDNMI LDA #NMI STA NMINV LDA #NMI STA NMINV1 NVEC CLI LDA #DO:STA $031C LD FFCLD:LDA STATUS! #8:; MASK OUT NINDI/BEQ NREVD>;STA ERRSLLDY RTAILZLDA PORTlSTA (RBUFF),YzINC RTAILINC RCOUNTNREVD NOP:;LDA ZPXMF:STA COMM$;JMP $FEBC.JSR $FFE1:BNE NMOUT8JMP $FE66VNMOUT PL ;;;;;;;;;;;pDOPUT PHAzLDA $9A)CMP #$02:BEQ DOPUT31PLAFDOPUT2 JMP $F1CA\DOPUT3 LDA STATUSq #16:;%00010000BEQ DOPUT3CLC:PLA:STA PORTBCC DOPUT4 +LDA #$00DOPUT4 RTSW;;;;;;;;;;;;;;;;;;XDO JSR $F291 STA $0319,Y DEY:BNE INCLO:PLA:TAY+ JSR NOINIT: ; DO, CLI!LNOCLOS JSR UPDCD:PLA:PLP:RTSuLDOCLAL JSR $F32F:PHP:PHA:JMP DOCLOS2?;;;;;;;;;;;;;;;;;;@BAUDS .BYTE 9 0 0 1 2 2 5 6 7 8 8 9 10 11 12 14 15 130 129 128 0 0 0 0 0 0EVECS .2DRVR.BAS":"%LADS":"SYS11000"/BEQ NREVD>;STA ERRSLLDY RTAILZLDA PORTlSTA (RBUFF),YzINC RTAILINC RCOUNTNREVD NOP:;LDA ZPXMF:STA COMM$;JMP $FEBC.JSR $FFE1:BNE NMOUT8JMP $FE66VNMOUT PL  FTP64SW 19200B 2.0+< UPDATED 10/13/2021 12:54A]254,8:(186)7254,(186)s9100: SET SY,BC +SY6158,254: SY34X23777:X,170:(X)170"<16K": SY227(51201)351200 5,2,0,(BC):(65532)3456,87:54 ),1: +&SY3436879,27:CO$(31)0 +(SY22645] +*(ML1)2095:"PML64.BIN",(254),1: +,UM0(UM1)245:"UP9600.BIN",(254),1: +-SY22750 +/(ML1)2095:"PML64.BIN",(254),1: 1UM0(UM1)35:"SWIFTDRVR.BIN",(25  WORK8 dMVML18:MV14,8:DD56577:SY34DD37136: FIX BUFAP> eD fJ n xCO$;" V1.7":"EQUIRES IMODEM FIRMWARE 2.0+" "Y O IMMERMAN (BO@ZIMMERS.NET)":: ---- ZIMODEM SETUP UN(254):IP$"":CR$(13)(10) PH0 $029B:CMP $029C:BEQ BUFUP:BCS BUFL1Y zLDA #$FF:SEC:SBC $029C:CLC:ADC $029B:SEC:BCS BUFL2q {BUFL1 SEC:SBC $029C BUFL2 CMP #200:BCC BUFL3 LDA $DD01: #$FD:STA $DD01:SEC:BCS TERMK BUFL3 CMP #20:BCS TERMK BUFUP LDA $DD01:A #$02:STA $DD0 +#5,A$:A$""23104 5,CR$;"ATF3HCTEP";QU$;HO$;":";((PO),2);QU$;CR$;] 900:(P$)7(P$,7)"CONNECT"2400$ "NABLE TO CONNECT TO ";HO$;":";((PO),2). ` "* ONNECTED. ";t 5,CR$;"ATF0O";CR$;~ SY226"IT 1 TO EXIT." 00:H$P$:P$""0"NTER PORT NUMBER (23): ";:5000:HP(P$):HP0HP23GHO$H$:POHP:2300MWP$""d" ";xA$:A$""5010A(A$):A13" ":A34A$;A$;" ";:P$P$A$:5010A20A$;" ";:P$P$A$:501 #X(56834):56834,X1:(56834)X19140%#BC15:SY227:56834,X:Z#(14);"EQUIRES A 64 WITH WIFTINK/INK232":`?@1,8,15:8,8,8,"TELNETML.BIN,P,R":LN41000:DN0J1,E:E08:1:T#8,A$:ST08:^A$""A$(0)cA ,156< 2,24,109,155,2,56,176,4,56,237,156,2,201,200,144,11,173,1^F 221,41,253,141,1,221,56,176,12,201,20,176,8,173,1,221,9,2P 141,1,221,162,0,32,198,255,32,228,255,201,0,240,171,201,133,240Z 4,201,27,208,4,32,204,255,96,72,162,5,32,201, LDA ($BB),Y TAY:LDA BAUDS,Y:BPL DOOP34 LDY #16:STY ROLS #$7F:STA ESR232:LDA #$00o DOOP3 A #16:;%00010000| STA ROL ; NO PAR, NO ECHO, XMIT , RECV LDA #9:; %00001001 ; STA COMM STA ZPXMF #%11110000 ; KEEP PARI ; B1920015 ; B23040019, ;;;;;;;;;;;= BASE $DE00O PORT = $DE00b STATUS $DE01t COMM $DE02 ROL $DE03 ESR232 $DE07 ; RHEAD 668 RTAIL 667 RBUFF $F7 NMINV $0318 3;;;;;;;;;;; JMP INI 031D:STA DO2: LDA $0326:STA DOPUT21:LDA $0327:STA DOPUT22l LDA $032C:STA DOCLAL1:LDA $032D:STA DOCLAL2 NOINIT LDA #DO:STA $031A  LDA #DO:STA $031B  CLI:RTS  ;;;;;;;;;;;  DO JSR $F34A:; CALL I ! PHP:PHA & LDA $BA:CMP v = +9 ͜& 8m88Ȑ )8  ݢ Ʌ `H h 8 m= 8L16m=D= :=L16>n=LO6=> 2z> 7>=L16> 8n=: 4>>hh>>Ll.L+>` eh80qȩqh° в````M)jIjо` # °L4```` L͜ ™ Lx;ɈSȭTȭLɭMɭ&8ɭ'9ɭ,ɭ-ɩRȍX` JHh(`HɌɌޱ )ީ ީ ލ)p xɭɩݍȍXKɍ/&ɍ'ɍ,- hh(`HHHح)ޑ  SY61"IT TO EXIT."+ 53280,0:53281,0:SY3436879,83 TM^ 53280,254:53281,246:SY3436879,27 :CO$;(14);"ANGING UP...";:6000 :I1100:#5,A$:I #5,A$:A$""2480  :"ODIFY HONEBOOK:" I1HZ 0:HP(P$):HP0HP23&: HO$(X1)H$:PO(X1)HP:XHZ1HZHZ1TD 1,UN,15:8,UN,8,"@0:TELNETPHONEBOOK,S,W"nN 1,E:E08:1:300X 8,HZ:I1HZ:8,HO$(I1):8,PO(I1)] 8,"-"HM$(I1)b I:8:1: 3000:"NTER HOSTNAME/IP: ";:50 0P$""5010P$(P$,(P$)1):"  ";:5010.p((DD)16)08z6100L5,"+++";:".";V6100s".";:5,"ATH":TTTI150ML12:TITT6050TTTI150TITT6110#SY(65532):BC8#SY2269140  (A$):A$((A),2)hDN0((LN),2);" DATA ";A$;:DNDN1:400204|",";A$;:DNDN1ODN18:DN0:LNLN10[ 40020( 162,5,32,198,255,32,228,255,201,0,240,61,201,10,240,572 32,210,255,173,155,2,205,156,2,240,38,176,13,169,255,56,237255,104,32,210 d 255,162,0,32,201,255,56,176,141 -1OÐ+Pß5,2,0,(8)CZá#5,A$:A$""A$;ZdáA$:A$""5,A$;fnÉ 50010U8:F$"TELNET":1,U,15,"S0:"F$:1:(F$),U:(F$),U)? ";A$:A$"Y"A$""A$"N"A$""4030  + $C800#.D SWTDRVR.BIN;; KERNAL BAUD RATESG; B501S; B752`; B1103m; B1354z; B1505; B3006; B6007; B12008; B18009; B240010; B360011; B480012; B720013; B960014 T + ; INITCHIP ; VEC" + ; RECVBYTE1 + ; XMITBYTEA +' +;;;;;;;;;;;N + INIT SEIq + LDA $031B:CMP #DO:BEQ NOINIT} + LDY #20 + INSAV LDA $0319,Y:STA VECS,Y + DEY:BNE INSAV + LDA $031A:STA DO1:LDA $031B:STA DO2 LDA $031C:STA DO1:LDA $ #$02 + 0 BEQ DOOP2& : PLA:PLP:RTS:;EARLY EXIT8 D DOOP2 TYA:PHAE N LDY #$00S X STY RHEADa b STY RTAILp l STY RCOUNT~ v STY ERRS ; ERNAL CLOCK, 8N1, BAUD BELOW ;;DA #RECVBUFF:;TA RBUFF1 ; ERNAL CLOCK, 8N1, BAUD BELOW STY ESR232: LL0LVLL-.Pȱɀii/0`    LL[  ۩ Ω мL0`0:HHȱ eȱ + ­ #‘̑‘ 4L¢ ™ L ) ¹ ­лLL®  ™  B L­L:NL5;OL ;SL;HLHO$HO$(X1):POPO(X1)"ONNECTING TO ";HO$;":";((PO),2);"...";CO$ PP$(25):P$"OK":186,(254):BA1200:XB1200^ CR$(13):(14);:9100:53280,254:53281,246 CO$"":SY226ML49152:665,73((678)30) !SY227ML49152 #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: &SY3436879,27:CO$(3BIN",(254),1:. =SY61S8128XB2400:CO$(159)H FTM828:SY61TM2816j PITM:DD56577:SY34DD37136 ZA%:SY61(A%155A%156)B%:I,A%131:II1:A%10 _A%0I,A%:II1:90 d e f nP$"A" xCO$;" V1.5":":CR$(13)(10):QU$(34)5 "NITIALIZING MODEM...";:6000L #5,A$:A$""203k 5,CR$;"ATHZ0&P0F0E0";CR$; 900:P$"OK"203 #5,A$:A$""208 ".";:5,CR$;"ATE0N0R0V1F0";CR$; 900:P$"OK"208 ".";:5,"ATE0V1X1Q0F0";CRH$:HM$(I1)(H$,2)"I:8:1, 1000D --------------------------------k GET E$ FROM MODEM, OR ERROR ! -------------------------------E$""MLE$""P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;"" 9) UIT":"NTER A NUMBER:  ";,$5000:P$""1000S.P(P$):P95:1,UN,15,"I0":1:e8P1P41050kBLP12000:1000VP23000:1000`P34000:1000dP4"ERMINAL MODE. ";:2430:1000j"?!":1000:130"ONNECTED TO "HO$"";CO$XB96001205AUM:UM3:(789)234UM9:1210hBAXB:UM19,1:5,"AT":6000:6000{XB24001210NP0:(2614)0NP20BAXB:2576,10:2578,(59490NP):2579,(59491NP)2582,170:2583:600I020HH$(I)""1330(P$HH$(I):9002I5P$(13)(10):650e"EQUEST SENT. EADING RESPONSE...";CO$P90:800:P$""2000LE0:FB0(P$)13"AD RESPONSE: ";CO$;P$:(P$,5)"HTTP/""AD RESP100TL0:5,CR$;"AT&M&DC";((P),2);CR$;:900:P$"OK""IMODEM COMMAND FAILED: ";P$:tCL0"EADERS COMPLETE. O CONTENT. ONE.";CO$:"EADERS COMPLETE.""ECEIVING"(CL)" BYTES";CO$1,UN,15:8,UN,8,OF$",W"ONE.";CO$:8:1V 5,"ATH0":5,"ATZ":TTTI100` TITT2400 j 5:*P$""7" ";KA$:A$""5010aA$(13)" ":A$(20)A$;" ";:P$P$A$:5010P$""5010P$(P$,(P$)1):"  ";:5010pTTTI100H$;nÉ 50010D8:F$"WGET":1,D,15,"S0:"F$:1:(F$),D:(F$),D:F$,8EQ SETPDUN +LSETPNXT LDA $FB:CLC:ADC #$07:STA $FB +VLDA $FC:ADC #$00:STA $FC +`LDA $FB:CMP $31:BCC SETPLP:BNE SETPDUN +jLDA $FC:CMP $32:BCC SETPLP +SETPDUN RTS  TELNET64SW 19200B 1.8+? UPDATED 10/13/2021 12:54A` +254,(186):(254)8254,8{ 9100:SY6158,254: SY34X23777:X,170:(X)170"<16K":SY227(51201)3512005,2,0,(BC):(65532)3456,87:54,87:52,87, 1)2 +(SY226(ML1)2095:"PML64.BIN",(254),1:f ++SY227(ML1)2095:"PML64.BIN",(254),1: +-SY227(51201)35:"SWIFTDRVR.BIN",(254),1: +2SY61ML4864:981,15:S8(215)128:S812830643 <SY61(ML1)2175:"PML128.EQUIRES IMODEM FIRMWARE 1.8+"1 "19200 BAUD VERSION"] "Y O IMMERMAN (BO@ZIMMERS.NET)":: -------------------------------- GET STARTED ! ------------------------------- UN(254) PH0:PT0:MVML18$;(19);-900:P$"OK""IMODEM INIT FAILED: ";P$:S"!": HO$(30): PO(30): HM$(30)|HO$(0)"COFFEEMUD.NET":PO(0)23:HZ11,UN,15:8,UN,8,"TELNETPHONEBOOK,S,R"1,E:E08:1:3008,HZ:I1HZ:8,HO$(I1):8,PO(I1)8,  --------------------------------< THE MAIN LOOP !b -------------------------------z:CO$;"AIN ENU:"" 1) IAL FROM HONEBOOK"" 2) ODIFY HONEBOOK"" 3) UICK ONNECT" " 4) ERMINAL MODE"" ,1:NP02582,154'"ENDING REQUEST...";CO$:5,"AT":6000:6000JP$" "P1$" /1.1":600cP$"OST: "HO$:600P$"ONNECTION: EEP-LIVE":600P$"SER-GENT: =":600P$"ONTENT-LENGTH: 0":600P$"CCEPT: */*" + OM$""P$" "OM$:600  CO$;"> ";:IN$"":ITTI1000:3200D TIIT"ANCELLED"::1000XN A$:A$""3100gb ITTI1000z A$(13)3300 A$(20)(IN$)03230 IN$(IN$,(IN$)1):"  ";:3100 A(A$):A32 IST"P$"":600:1000 IN$"WHO"P$" :"AA$:600:1000F "NKNOWN OMMAND: ";IN$;". RY ?";CO$:1000h "OINING ";QU$;A$;QU$:AA$A$ P$" :"AA$:600 E$"":CC$AA$:1000 "?  ";:P$"" A$:A$""5010 A$(134)X19240!$BC15:SY227:56834,X:*"$(14);"EQUIRES A 64 WITH WIFTINK/INK232"::"Pß5,2,0,(8)R"Zá#5,A$:A$""A$;i"dáA$:A$""5,A$;u"nÉ 50010"U8:F$"IRC":1,U,15,"S0:"F$:1:F$,U:F$,UDA #NMIDOBIT:STA NMIVECT1H+T$"OK":186,(254):XB1200:BA1200R CR$(13):(14);:9100:53280,254:53281,246 CO$"":SY226ML49152:665,73((678)30):UMML2048:XB9600 !SY227ML49152:UMML2048 #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: +&SY344:981,15:S8(215)128:S812830643E 7SY61S8128XB2400:CO$(159)y <SY61(ML1)2175:"PML128.BIN",(254),1: PSY226UM0UM:UM3:X(789):UM9:X234XB1200 dSY3456579,0 e f nP$"A" xCO$;" V1.5":203 #5,A$:A$""208/ 5,CR$;"ATE0N0R0V1Q0";CR$;F 900:P$"OK"208] #5,A$:A$""225 5,"ATE0V1X1F3Q0S40=248I4";CR$;(19); 900:VR(P$):VR2.0"IMODEM INIT FAILED: ";P$: 900:P$"OK"203# HH$(20):UR$"WWW.ZIMMERNH1:(LWNH)") "HH$(I):<TLHLWNH1:(LW1NH)") DD EW EADER"VrUR$"":P$"1":400|:"YPE A NUMBER OR ΠTO BEGIN:";5000:P$""1000X(P$):X1XLH300X1"NTER : HTTP://";:5000:UR$P$:300 X2" 300V --- TRANSMIT P$ TO THE OPEN SOCKETUXOP$P$:ML9:C8$(((MV8)),2):E$"OK":VR3E$C8$zb5,"ATS42=";C8$;"TP+";QU$;P$;QU$lML:P$E$P$OP$:"ETRYING..";CO$:600vOP$P$:ML9:C8$(((MV8)),2):PN$(((P$)),2)GET E$ FROM MODEM, OR ERRORE$""ML\E$""P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;""b --- GET PACKET HEADER, SETS P0,P1,P2, RETURNS P0=0 IF NADAPR0:#5,P$:P$""9305,(17);ML6:P0(MV2):P1(UR$)0(OF$)0300I0-(UR$,I1,1)"/"II1:I(UR$)1020]H1$(UR$,I):P1$(UR$,I1):P1$""P1$"/"eI0(H1$,I1,1)":"II1:I(H1$)1050$HO$(H1$,I):I(H1$)H1$H1$":80".AT$"":XB0BA0XBBAAT$"S43="(A95A193)A2183100 IN$IN$A$:A$;" ";:3100' IN$""" "::1000? IN$"?"" ":3400f (IN$,1,1)"/"IN$(IN$,2):3500 CC$"":"O HANNEL ELECTED! RY ?";CO$:1000 P$" "CC$" :"IN$ 600:" ":"";NINYTHING ELSE ";CO$;"END MESSAGE"& CO$;"------------------------------":1000R X0:I2(IN$):X0(IN$,I,1)" "XI :A$"":X1A$(IN$,X1):IN$(IN$,1,X1)  IN$"JOIN"4000 IN$"QUIT"P$" :":600:9100 IN$"L3)" ":!A$(20)A$;" ";:P$P$A$:5010!P$""5010;!P$(P$,(P$)1):"  ";:5010I!(#TTTI100`!2#ML12:TITT9010f!<#!#5,"ATZ":9000:9000:5:!#SY(65532):BC8!#SY2269240!$X(56834):56834,X1:(568 WGET4SW 19200B 2.0+< UPDATED 10/13/2021 12:54A] +254,(186):(254)8254,8x 9100:SY6158,254: SY34X23777:X,170:(X)170"<16K":SY227(51201)3512005,2,0,(BC):(65532)3456,87:54,87:52,87 P36879,27:CO$(31)D +(SY226(ML1)2095:"PML64.BIN",(254),1:x ++SY227(ML1)2095:"PML64.BIN",(254),1: +-SY226UM0(UM1)245:"UP9600.BIN",(254),1: +/SY227(51201)35:"SWIFTDRVR.BIN",(254),1: 2SY61ML486"EQUIRES IMODEM FIRMWARE 2.0+"D "Y O IMMERMAN (BO@ZIMMERS.NET)"::Y --- MODEM INITg UN(254)~ PH0:PT0:MVML18 "NITIALIZING MODEM...":CR$(13)(10) #5,A$:A$""203 5,CR$;CR$;"ATHZ0&P0F0E0";CR$;:900:P$"OK"S.NET/INDEX.HTML":OT$"":OF$"@0:WGET-OUTPUT,S"2, GET INFOK6:"EQUEST ARMS:"h@ " *) YPE : "A " 1) RL : HTTP://";UR$B " 2) UTPUT NIT:";UNC " 3) UTPUT ILE: ";OF$ILW3JNH0:I020:(HH$(I))0NHNTER OUTPUT DEVICE/UNIT: ";:5000:UN(P$):300SX3"NTER OUTPUT FILE: ";:5000:OF$P$:300XLHP$HH$(XLW):"ODIFY: ";P$:5005:HH$(XLW)P$:300II1:I020:HH$(I)""II0III:II0"EW EADER: ";:5000:HH$(II)P$E$"OK":VR3E$C8$$5,"ATS42=";C8$;"T+";PN$:5,P$UML:P$E$P$OP$:"ETRYING..";CO$:650[z --- GET P$ FROM SOCKET P P$"":E0*930:P0PP00"NEXPECTED PACKET ID: ";P0;"/";P:4P00E1:: FAIL> ---  MV4):P2(MV6)PL(MV0):CR(MV1):C8(MV8)2P00P2C8985BP10P$""SP00CR0j#5,P$:P$""985"-";CO$:5,"ATL":945"XPECTED ";E$;", GOT ";A$: --- THE MAIN LOOP !( $;": ";IN$ E$""::1000H CO$;"------------------------------"JR "/JOIN #C-64 ";CO$;"HANGE CHANNELS."r\ "/QUIT ";CO$;"OGOUT AND EXIT."f PRINT"/LIST ";CO$;"IST CHANNELS."p PRINT"/WHO MASK* ";CO$;"SER INFO." "56834,X:#(14);"EQUIRES A 64 WITH WIFTINK/INK232":OÐ*Pß5,2,0,(8)BZá#5,A$:A$""A$;YdáA$:A$""5,A$;enÉ 50010F$"CONFIGURE":1,8,15,"S0:"F$:1:(F$),8:(F$),8$P$A$:5010P$""5010P$(P$,(P$)1):" 61ML4864:981,15:S8(215)128:S812830643] <SY61(ML1)2175:"PML128.BIN",(254),1: FSY61S8128XB2400:CO$(159) PSY226UM0UM:UM3:X(789):UM9:X234XB1200 ZSY3456579,0: WHY DOES THIS WORK d GET THE BAUD P$"":CR$(13)(10)" PH0:PT0:MVML18@ "NITIALIZING MODEM..."W #5,A$:A$""203 5,CR$;"ATHZ0&P0F0E0";CR$;:900:P$"OK"203 #5,A$:A$""208 5,CR$;"ATE0N0R0V1Q0";CR$; 900:P$"OK"2085,"ATE0V1X1F3Q0S40=250I47, #C-64"5^SE$""::"HAT IS YOUR SERVER HOST":5000:SE$P$bhSE$""" GUESS YOU'RE DONE THEN":5:r"HAT IS THE PORT":5000:PO$P$:(PO$)0SE$"":360|SE$SE$":"PO$#5,A$:A$""390 MAKE THE CONNECTIONAT$"":UM9:435BAXB:UM19,1:5,"AT":9000:90001XB2400435KNP0:(2614)0NP20BAXB:2576,10:2578,(59490NP):2579,(59491NP)2582,170:2583,1:NP02582,1545,"AT":9000:9000:"HAT IS YOUR NICKNAME";:5000:NI$P))10P$(P$,(P$)1);]OP$P$:ML9:C8$(((MV8)),2)::E$"OK":VR3E$C8$_b5,"ATS42=";C8$;"T+";QU$;P$;QU$lML:P$E$P$OP$:"XMIT FAIL";CC$:600v --- GET NEXT FROM SOCKET INTO PP930:P0PP00"NEXPECTED PACKET ID: "OM MODEM, OR ERRORE$"" +MLOE$""P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;""U GET PACKET HEADER, SETS P0,P1,P2, RETURNS P0=0 IF NADAML12:E0:P00:P10:P20:PR0:I0:I00:I10:CR0:P40:P50:C80P$"":5A$:A$""3000 800:P$""1000-MS$"":MC$"":MA$"":I1:LP$P$F(P$,1,1)":"1100XI(P$)1100x$(P$,I,1)" "II1:1050.MS$(P$,1,I1):P$(P$,I1)LI1:P$""1000VI(P$)1200`(P$,I,1)" "II1:1110(MA$,1,N1)NI$MA$(MA$,(NI$)2) MA$:1000 CMDS0MC$""1400E(MC$""1000]2MC$""2000u<MC$""2100FMC$""2200PMC$""2300ZMC$""1000nuLP$:1000v"NKNOWN: '"NEL.";CO$:1000$2900:"";MS$;" HAS JOINED THE CHANNEL.";CO$:1000-T II2L^ II(MS$)II(MS$):2950oh (MS$,II,1)"!"IIII1:2950r IIII1:2910 MS$(MS$,1,II): A$(13)1000 "TREAM PAUSED. NTER ? FOR HELP." IRC64SW 19200B 1.8+< UPDATED 10/13/2021 12:54A] +254,(186):(254)8254,8x 9200:SY6158,254: SY34X23777:X,170:(X)170"<16K":SY227(51201)3512005,2,0,(BC):(65532)3456,87:54,87:52,87)  +&SY3436879,27:CO$(31)M +(SY226(ML1)2095:"PML64.BIN",(254),1: ++SY227(ML1)2095:"PML64.BIN",(254),1: +,SY227(51201)35:"SWIFTDRVR.BIN",(254),1: +-SY226UM0(UM1)245:"UP9600.BIN",(254),1:) 2SY RATE nP$"A"F xCO$;" V1.5":"EQUIRES IMODEM FIRMWARE 1.8+"r "Y O IMMERMAN (BO@ZIMMERS.NET)":: -------------------------------- GET STARTED ! ------------------------------- UN(254):I";CR$;(19);8900:VR(P$):VR1.8"IMODEM INIT FAILED: ";P$:O900:P$"OK"203d, GET THE SERVER|6:"OME SERVERS:"@"IRC.NLNOG.NET PORT 6667, #C-64"E"IRC.FREENODE.NET PORT 6667, #C64FRIENDS"J"IRC.US.IRCNET.NET PORT 666XB0BA0XBBAAT$"S43="((XB),2)-#5,A$:A$""406eQU$(34):5,"AT"AT$"H&D13&M13&M10CP";QU$;SE$;QU$n900(P$,8)"CONNECT ""NABLE TO CONNECT TO ";SE$;":";P$:300P((P$,8))XB9600430UM:UM3:(789)234$:N1(NI$)NI$""" GUESS NOT.":-P$" "NI$:600YP$" GUEST 0 * :OE NONYMOUS":600"ONNECTED, WAIT FOR . ? FOR HELP";CO$ 1000: GO START MAIN LPV --- TRANSMIT P$ TO THE OPEN SOCKETX(P$)0((P$,1;P0;"/";P:P00#PP$(PE)P$:PEPE1:PE25PE0/P$"":N --- GET P$ FROM SOCKET Pi E0:P$"":PH25PH0*PHPEP$PP$(PH):PHPH1:4700:P00E1:: FAIL>PHPEP$PP$(PH):PHPH1:HE1: ---- GET E$ FR,(17);ML6:P0(MV2):P1(MV4):P2(MV6);PL(MV0):CR(MV1):C8(MV8)PP00P2C8985`P10P$""qP00CR0ML12:P$"""-";CO$:5,"ATL":945"XPECTED ";E$;", GOT ";A$: THE MAIN LOOPjMC$(P$,1,I1):P$(P$,I1)MA$P$:MC$""1000<A((MC$,1,I)):A48A571300l(MC$)1(MC$,1,1)"0"MC$(MC$,2):1220C(MC$):C430C460"AD ICK GIVEN(";C;") : "MA$:440C400"RROR: "MA$:1000(MA$)N11 MC$"' '"MA$"'"w1000x ....OM$MA$:P$" :"MA$:600:E$"":1000CI1:MA$""1000](MA$,I,2)" :"2050mII1:2010w2900*"";MS$;": ";(MA$,I2):10004"";LP$;"":10002900:"";MS$;" HAS LEFT THE CHAN PP$(25):P$"OK":186,(254):BA1200:XB1200[ CR$(13):(14);:9200:53280,254:53281,246 CO$"":SY226ML49152:665,73((678)30):UMML2048:XB9600 !SY227ML49152:UMML2048 #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: +" CONFIGURE64SW 19200B 2.0+B UPDATED 08/15/2021 12:54Pc254,(186):(254)8254,8~ +9100:SY6158,254: SY34X23777:X,170:(X)170"<16K":SY227(51201)3512005,2,0,(BC):(65532)3456,87:54,87:52,87 -------------------------------U PH0:PT0:MVML18:CR$(13)(10):QU$(34):MV14,5y "NITIALIZING MODEM...";:CT0 #5,A$:A$""203 5,CR$;"ATHZ0F0R0E0&P1";CR$; 900:P$""CTCT1:CT10203 CT10#5,A$:A$""207 ---------------------------- E$"":P$""(MLmE$""P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;""s -------------------------------- THE MAIN LOOP ! -------------------------------"CT0:C"2000  1050:CD020004000@:"ESTING CONNECTION...";:TTTI200PTITT1055m$CT0:#5,A$:A$""1060.ML12:5,CR$;"ATC";QU$;"132.148.138.66:80";QU$;CR$;8900:CD1:P$""1080=(P$,8)" "CD0:CTCT1:CT1P$"""ONE!":::3000I(P$)2100?(P$,I,1)" "P$(P$,I1):2100OII1:2030n4WF$(WF1)P$:WFWF1:2020w PG1 LPWF:LPPG15LPPG15& IPGLP:(I)") ";WF$(I):I0 :"NTER TO UIT TO .": LPWF"NT,1,1)):A48A573100& A(A$):APG APG15 AWF3100Q WIA:"TTEMPT TO CONNECT TO: ";WF$(A)| "NTER II ASSWORD: ";:5000:PA$P$H ML12:5,CR$;"ATW";QU$;WF$(WI);",";PA$;QU$;CR$;:900R P$"""ONNECT AIL!";CO$:31003470 P$"":ML12:P$""3490 ML12:5:>"HANGE II CONNECTION (Y/)? ";`A$:A$"Y"A$"""":2000A$"N"A$""A$(13)4010"":"URRENT 'HONE' BOOK:":PB05,CR$;"ATP";CR$TTTI100"900:P$(Q$,1):QQ$"Q"QQ$""5:1QQ(Q$):(QQ0QQPB)QQ$"A"QQ$"":4100AQQ04300\")DIT OR )ELETE? ";A$:A$"E"A$"""":B1$PB$(QQ1):4310A$"D"A$""4260""5,"ATP";(34);PB$(QQ1);"=DELETE";(34):000:B2(P$)B20"RONG.":41001B3$"":"O TRANSLATION (Y/N)? ";L4980:A1B3$B3$"P"r"O TRANSLATION (Y/N)? ";4980:A1B3$B3$"T"&"O TERMINAL CHO (Y/N)? ";04980:A1B3$B3$"E":"O /" ";A$:A$""5010 A$(13)" ":5A$(20)A$;" ";:P$P$A$:5010EP$""5010nP$(P$,(P$)1):"  ";:5010#SY(65532):BC8#SY2269140#X(56834):56834,X1:(56834)X19140#BC15:SY227:. WF$(100):WF0:P$"OK":PB$(50):186,(254)` CR$(13):(14);:9100:53280,254:53281,246 CO$"":SY226SY227ML49152:665,73((678)30) #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: &SY3436879,27:CO$(31)& +'SY2 FSY61CO$(159) d" nP$"A"a xCO$;" V1.6":"EQUIRES IMODEM IRMWARE 2.0+"{ "19200 BAUD VERSION" "Y O IMMERMAN (BO@ZIMMERS.NET)":: -------------------------------- GET STARTED !  CT105,"ATHF3E0&P1";CR$;< CT10900:P$""CTCT1:207 CT10"NITIALIZATION FAILED.":"S YOUR MODEM SET TO 1200B?" CT10 ,:: 1000  --------------------------------  GET E$ FROM MODEM, OR ERROR ! ---10:CD0:LC$"1":1010:WI$P$:1010:P$WI$1030,1000JML12:5,CR$;"ATI3";CR$;a900:P$LC$1025uCTCT1:CT3C10:1010LC$P$:CT0:C1C11:C115P$"":1010WI$P$:"URRENT II : ";WI$CD0:WI$"070,1070,1070,1070,12002BI13:ML12:5,CR$;"ATH0";CR$;:900:INL5,CR$;"ATH0";CR$;:900hCD0"AIL!";CO$CD1"UCCESS!";CO$ML12:ML12::"CANNING FOR II HOTSPOTS...";5,CR$;"ATW";CR$;900:I1:P$""ER FOR EXT AGE"!D PG15"NTER FOR REV AGE"NN "NTER A NUMBER TO ONNECT TO THAT "q "? ";:5000:A$P$:A$""3100 A$"X"A$""5: A$"N"A$""LPWFPGPG15:3100 A$"P"A$""PG15PGPG15:3100 A((A$W P$""3420X P$""3400Y 900:34105\ "ONNECT SUCCESS!";CO$;f Ig TTTI300Yh TITT3432sp CD0:1050:CD03100z "AVING NEW OPTIONS..." ML12:ML12:ML12 5,CR$;"ATZ0&WE0&P1";CR$ 900:P$""P$"OK"""TITT4200,P$""4130$6I1:PP$P$:P$"OK"P$""4200E@(PP$,I,1)" "II1:4160XJPP$(PP$,I1)^PB$(PB)PP$:PBPB1:PB;") ";P$:4120h" ) DD NEW":" ) UIT TO "r:"NTER YOUR CHOICE: ";:5000:Q$P$|QQ$900:4100 "HONE NUMBER: ";:5000:B1$P$)(B1$)3"RONG.":4100^I1(B1$):B1((B1$,I,1)):B148B157B1$""I:B1$"""AD DIGITS.":4100"ARGET HOST: ";:5000:B2$P$(B2$)4"RONG.":4100"ARGET PORT: ";:5 LOW ONTROL (Y/N)? "; D4980:A1B3$B3$"X" NB4$((B2),2)VX5,"ATP";B3$;(34);B1$;"=";B2$;":";B4$;(34):900`b4100ftA$:A$"Y"A$""A$"N"A$""4980~A0:AA$"":A$"Y"A$""AA$"":A1AA$:P$"" 26(ML1)2095:"PML64.BIN",(254),1:Z +(SY227(ML1)2095:"PML64.BIN",(254),1: +-SY227(51201)35:"SWIFTDRVR.BIN",(254),1: +2SY61ML4864:981,15:P(215)128:P12830643 +<SY61(ML1)2175:"PML128.BIN",(254),1: l"LINK232 APPS2ACONFIGURE,8:FTP,8:( +IRC,8: WGET,8: D64WGET,8:TELNET,8: CBMTERM,8: CBMTERM,8KK----REQ ML---- PML64.BIN TELNETML.BIN SWIFTDRVR.BIN----SOURCE---- PML64.BAS +TELNETML.BAS SWIFTDRVR.BASKK +----TOOLS---- EMUTIL LADS MONITOR.49152 + MONITOR.8192SWIFTDRVR.BINPML64.BAS PML128.BASKK PMLVIC.BASSWALMOST.BASUP9600.BAS(RDS64.BAS- TELNETML.BASSW.ASM EMUTIL LADSKK MONITOR.49152 MONITOR.8192SW TESTSW FTDRVR.BINKKKKK + FTP64SW 19200B 2.0+< UPDATED 10/13/2021 12:54A]254,8:(186)7254,(186)s9100: SET SY,BC +SY6158,254: SY34X23777:X,170:(X)170"<16K": SY227(51201)351200 5,2,0,(BC):(65532)3456,87:5400 +UN$"":PA$"":1230#5,A$:A$""1300%P$"":600:A0EAA1:850:P$""A601305UP$""1300tP$::(P$,3)"227"1300(A((P$,1)):A48A57P$(P$,(P$)1):13202X0(P$)<A((P$,X0,1)):A47A58X0X0 ),1: +&SY3436879,27:CO$(31)0 +(SY22645] +*(ML1)2095:"PML64.BIN",(254),1: +,UM0(UM1)245:"UP9600.BIN",(254),1: +-SY22750 +/(ML1)2095:"PML64.BIN",(254),1: 1UM0(UM1)35:"SWIFTDRVR.BIN",(25 E0'*930:P0PP00"NEXPECTED PACKET ID: ";P0;"/";P:>4P00E1:: FAILD>MRPC0t\PCPC1:800:P$""E1PC60860f(P$)4(P$,4,1)"-"pEC$(P$,3):EC(EC$)qPC0r800:P$""E1PC60882sP$""E1870 WORK8 dMVML18:MV14,8:DD56577:SY34DD37136: FIX BUFAP> eD fJ n xCO$;" V1.7":"EQUIRES IMODEM FIRMWARE 2.0+" "Y O IMMERMAN (BO@ZIMMERS.NET)":: ---- ZIMODEM SETUP UN(254):IP$"":CR$(13)(10) PH0(17);ML6:P0(MV2):P1(MV4):P2(MV6);PL(MV0):CR(MV1):C8(MV8)PP00P2C8985`P10P$""qP00CR0#5,P$:P$""985"-";CO$:5,"ATL":945"XPECTED ";E$;", GOT ";A$: --- THE MAIN900:VR(P$):VR2.0"IMODEM INIT FAILED: ";P$:A900:P$"OK"203X#5,A$:A$""2455,"ATI2";CR$;:900:IP$P$:P$"OK"(P$)8245P$"":I1(IP$)IFMID$(IP$,I,1)="."THENIP$=LEFT$(IP$,I-1)+","+MID$(IP$,I+1) I:"OUR (P$)0E3EE1:11053["NABLE TO CONNECT TO ";HO$;" PORT";PO;"";CO$:300@`ITTI40_jP90:800:P$""ITTI10otTIIT1130"ONNECTED TO ";HO$;" ON CHANNEL";P;", TANDBY...";CO$XB96001205UM:UM3:(789)234UM D " 4) ISK EVICE:";UNILH4/rHO$"":P$"1":400]|:"YPE A NUMBER OR ΠTO CONNECT:";s5000:P$""1000X(P$):X1XLH300X1"NTER : FTP://";:5000:HO$P$:300X2"NTER SERNAME: ";:5000:UN$P$:300ERNAME: ";:5000:UN$P$:P$""1230*P$" "UN$:600:850:P$""1240eP$:(P$,3)"331""SERNAME REJECTED?!";CO$:5:PA$"""ASSWORD: ";:5000:PA$P$:P$""1260P$" "PA$:600:850:P$""1270P$:(P$,3)"230"20,87:52,870 P$"OK":186,(254):BA1200:XB1200b CR$(13):(14);:9100:53280,254:53281,246 CO$"":SY226ML49152:665,73((678)30):UMML2048:XB9600 !SY227ML49152:UMML2048 +#SY34ML22273:(ML)765:"PMLVIC.BIN",(254U$lML:P$E$"XERR:";CO$;P$:P$OP$:600#vZOP$P$:ML9:C8$(((MV8)),2):PN$(((P$)),2)5,"ATS42=";C8$;"T+";PN$:5,P$:E$"OK":VR3E$C8$ML:P$E$"XERR:";P$:P$OP$:650 --- GET P$ FROM SOCKET P P$"":4),1:UM:A 2S80:SY61ML4864:981,15:S8(215)128:S812830643u <SY61(ML1)2175:"PML128.BIN",(254),1: FSY61S8128XB2400:CO$(159) PSY226UM0UM:UM3:X(789):UM9:X234XB1200 ZSY3456579,0: WHY DOES THISt(P$,3)EC$P$:881u(P$,4,1)"-"881vE --- GET E$ FROM MODEM, OR ERROROE$""WMLE$""P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;"" ---- LOW LVL PACKET READPR0:#5,P$:P$""9305,:PT0:MVML18" "NITIALIZING MODEM..."9 #5,A$:A$""203k 5,CR$;"ATHZ0&P0F0E0";CR$;:900:P$"OK"203 #5,A$:A$""208 5,CR$;"ATE0N0R0V1Q0";CR$; 900:P$"OK"208 5,"ATE0V1X1F3Q0S40=248S0=1S41=0I4";CR$;(19);:L9248* LOOPQU$(34): BEGIN!3AT$"":XB0BA0XBBAAT$"S43="((XB),2)K#5,A$:A$""1020L5,"ATH"AT$"&D10&M13&M10CP";QU$;HO$;":";((PO),2);QU$:E0Q900:SP0:P$"OK"1020V(P$)8(P$,8)"CONNECT "P((P$,9)):1200XIP ADDRESS IS: ";IP$I*HO$"FTP.ZIMMERS.NET":PO21:UN$"ANONYMOUS":PA$"MY@EMAIL.COM"h+HO$="192.168.1.112":PO=21w, GET INFO6:"EQUEST ARMS:"A " 1) RL : FTP://";HO$B " 2) SERNAME : ";UN$C " 3) ASSWORD : ";PA$ 9:1210BAXB:UM19,1:5,"AT":9000:9000!XB24001210;NP0:(2614)0NP20rBAXB:2576,10:2578,(59490NP):2579,(59491NP)2582,170:2583,1:NP02582,1545,"AT":9000:9000850:P$""1240P$UN$"""S X3"NTER ASSWORD: ";:5000:PA$P$:300ZX4"NTER OUTPUT DEVICE/UNIT: ";:5000:UN(P$):300d 300V --- TRANSMIT P$ TO THE OPEN SOCKET !XOP$P$:ML9:C8$(((MV8)),2):E$"OK":VR3E$C8$b5,"ATS42=";C8$;"TP+";QU$;P$;Q +1:1340 FX2((P$,X01)):P$(P$,X01):X0(P$):PA((P$,X0,1)):A47A58X0X01:1360yZX1((P$,X01)):P$(P$,X01):H2$":"(((X1256)X2),2)_P$(P$,5)dA((P$,1)):A48A57P$(P$,2):1380nI1(P$):(P$,I,1)","P$(P$)0P$":"P$, 8,UN,0,"$"P$, #8,A$,A$-& #8,A$,A$:ST0X(0):8::2000=-0 #8,A$,B$:X(A$(0))256(B$(0)):X;]-: #8,A$:A$""(13);:8230x-D B$:B$" "8::2000-N A$;:8250-(#TTTI100-2#ML12:TITT9010-<# $""1420 5,"ATC";((P),2):900:P$"OK"E0:?"ETRY TO CHANGE BACK TO ";H0$;"";CO$:1420["OMMAND (?): ";:5000((DD)16)0"OST CONNECTION";CO$:P$"QUIT"P$""800:P$:2000P$"LS"(P$,3)"LS "4000:2000 P$:6100%GLP$(P$,4):LP$"550 "P$:6250&LLP$"226 "Y1:Y$P$:6100A&QLP$"150 "("",(TT))P$::6400:6100r&VP0SPPL08,P$;:TTTTPL:6500:Y00:6100&[P0P6100&`Y0Y01:Y0Y0Y1TTTB6100&e#5,A$:ST0A$""900:$(P$,5):6000:2000(P$,4)"PUT "F$(P$,5):7000:2000(P$"?"P$"HELP"R P$"HELP""GET PUT LS CD DIR DEL"y!P$"HELP""LCD LDIR LDEL QUIT""P$"HELP""SE ,S AND ,P IN GET/PUT FILENAMES!"#P$"HELP""ELOW ARE S((P$,I01),4)"BYTE"'FTB((P$,I1,I0I)):Y1TB10:.(d" "(TT):c(XFX0:X$",P,R":P$" ":600:850:P$""7000s(bFX$(F$,2)(g(F$,1)" "F$(F$,2):7015(lFX$",P"FX$",S"F$(F$,(F$)2):0A$""900:4025 P$:P$"":600$!GOSUB900:IFLEFT$(P$,5)="RING "THEN4040P!Y0:Y$"":Y10:Y20:Y35:BA4800Y310!GET#5,A$:IFST=0ANDA$<>""THENGOSUB900:GOTO4100!A$:A$" "A$"":"BORTED.":4250!6800!hP0PY1P$""7100)ML12:X(MV):XE(MV1))(P$)07100*TTTT(P$):6500:650:XE07250*800A* P0P(P$,3)"550"P$:7250V**P0PP$""7100*/P0P(P$,4)"150 "("",(TT))P$::7100*4P0PP$::7100*>P0SP"?!":7100*HY$"#5,A$:A$""ST04244#800:P$""P$:42450#5,"ATH"((SP),2):900:P$"OK"42506#@#P$""M#" ";a#A$:A$""5010w#A$(13)" ":#A$(20)A$;" ";:P$P$A$:5010#P$""5010#P$(P$,(P$)1):900:P$"OK"7290++@P$(P$,5):(P$)8(P$)168020%,JUN(P$):"URRENT DRIVE IS NOW";UN:2000^,T1,UN,15,"CD "P$:1,E,E$,E1,E2:E,E$,E1,E2:1:2000m,P$(P$,6),1,UN,15,"S0:"P$:1,E,E$,E1,E2:E,E$,E1,E2:1:2000, P$(P$,6):(P$,I1)"."(P$,I1)s:H0$P$H2$:R00x#5,P$:P$""1400=}5,"AT";CC$;QU$;H0$;QU$:900r(P$)8(P$,8)"CONNECT "SP((P$,9)): 1420R011300R0R01:"RROR: ";P$:"ETRY TO CONNECT TO ";H0$;"";CO$:1400#5,A$:AB0:TT0:Y00:Y110$#5,A$:ST0A$""900:6100%800 %FX0P0SPP106200A%1,UN,15:8,UN,8,"@0:"F$X$|%$1,E:E08:1:"AILED TO OPEN "F$"";CO$:6250%.FX1%8PL(P$):P0P6230%=PL06100%BY00:Y1PL0Y$P$"DIR"(P$,4)"DIR "4000:2000%P$"EXIT"P$"QUIT"5,"ATZ":900:5:K(P$,4)"DEL "P$"DELE"(P$,4)p(P$,3)"CD "P$" "(P$,4)(P$,4)"LCD "8000(P$,5)"LDEL "8100 (P$,4)"LDIR"8200 (P$,4)"GET "F6245&gY$: "";((TT),2);" BYTES TRANSFERRED.";CO$.'j8:1:5,"ATH"((SP),2):900:P$"OK"6250I't800:P$""P$:6260O'g'I(P$)1:I00:TB0' +A$(P$,I,1):A$"("6450'A$" "I0I'II1:I06410'('2I00'<ERVER COMMANDS:";CO$4600:850:P$:2000 ) P$" ":600:850:P$""4000S P$:CC$"&M10&D10&M13CP":1300:E1v P$"":600:850:P$""4010 PRINTP$:P$=" "+IP$+",198,76":GOSUB600:GOSUB850:IFP$=""THEN4020 #5,A$:STX$FX$",R"(qF$""(s1,UN,15:8,UN,8,F$X$()t1,E:E08:1:"AILED TO OPEN "F$"";CO$:G)uP$::CC$"C":1300:E1^)vP$" "F$:600)5,"ATC";((SP),2):900:P$"OK"7040)Y0:Y$"":TT0)#5,A$:ST0A$""900:Y$P$:4100!rP0PP$""4100"|P0P(P$,4)"226 "Y1:Y$P$:4100F"P0P(P$,4)"150 "4100:PRINTP$:GOTO4100]"P0SPP$""4235"Y20:Y1Y11:S80(P$,34):4100"P$:4100"P0P4100"Y2Y21:Y0(Y12Y2Y3)4100" 7100*R#5,A$:ST0A$""900:7250+\ "";((TT),2);" BYTES TRANSFERRED.";CO$++a#5,A$:A$""7265]+f8:1:5,"ATH"((SP),2):900:P$"OK"7265k+hTTTI200+k800:P$""TITT7275+pP$:800:P$""7280+z5,"ATC";((P),2): "  ";:5010$pFX0:X$",P,W":P$" ":600:850:P$""6000,$uP$:CC$"C":1300:E1<$zFX$(F$,2)^$(F$,1)" "F$(F$,2):6015$FX$",P"FX$",S"F$(F$,(F$)2):X$FX$",W"$F$""$P$" "F$:600$Y0:Y$"":T +-#SY(65532):BC8-#SY2269140.#X(56834):56834,X1:(56834)X19140#.#BC15:SY227:56834,X:X.#(14);"EQUIRES A 64 WITH WIFTINK/INK232":h.Pß5,2,0,(8).Zá#5,A$:A$""A$;.dáA$:A$""5,A$;.nÉ 50010.U8:F$"FT UTPUT DEVICE/UNIT: ";:5000:UN(P$):300XXLHP$HH$(XLW):"ODIFY: ";P$:5005:HH$(XLW)P$:300II1:I020:HH$(I)""II0III:II0"EW EADER: ";:5000:HH$(II)P$ 300V --- TRANSMIT P$ TO THE OPEN SOCKETXOP$P$:NP0:(2614)0NP20/BAXB:2576,10:2578,(59490NP):2579,(59491NP)U2582,170:2583,1:NP02582,154"ENDING REQUEST...";CO$:5,"AT":6000:6000P$" "P1$" /1.1":600P$"OST: "HO$:600P$"ONNECTION: EE PP$(25):P$"OK":186,(254):XB1200:BA1200_ CR$(13):(14);:9100:53280,254:53281,246 CO$"":SY226ML49152:665,73((678)30):UMML2048:XB9600 !SY227ML49152:UMML2048 #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1ML:P$E$P$OP$:"ETRYING..";CO$:650!@ --- GET P$ FROM SOCKET PN P$"":E0*930:P0PP00"NEXPECTED PACKET ID: ";P0;"/";P:4P00E1:: FAIL> --- GET E$ FROM MODEM, OR ERRORE$""ML"E$""SY61ML4864:981,15:S8(215)128:S812830643R 7SY61S8128XB2400:CO$(159) <SY61(ML1)2175:"PML128.BIN",(254),1: PSY226UM0UM:UM3:X(789):UM9:X234XB1200 dSY3456579,0 e f nP$"A"( xCO$;"P00P2C8985P10P$""P00CR00#5,P$:P$""985W"-";CO$:5,"ATL":945y"XPECTED ";E$;", GOT ";A$: --- THE MAIN LOOP !(UR$)0300I0:"HE DISK IN DRIVE";UN;"WILL BE OVER0:P$"OK"203 #5,A$:A$""208; 5,CR$;"ATE0N0R0V1Q0";CR$;R 900:P$"OK"208i #5,A$:A$""225 5,"ATE0V1X1F3Q0S40=248I4";CR$;(19); 900:VR(P$):VR2.0"IMODEM INIT FAILED: ";P$: 900:P$"OK"203 HH$(20):OT$1:I(H1$)1050$HO$(H1$,I):I(H1$)H1$H1$":80"Q.AT$"":XB0BA0XBBAAT$"S43="((XB),2)i8#5,A$:A$""1080LQU$(34):5,"ATH"AT$"&D10&M10&M13C";QU$;H1$;QU$Q900V(P$)8(P$,8)"CONNECT "P((P$,9)):1200 [(LWNH)") "HH$(I):5TLHLWNH1:(LW1NH)") DD EW EADER"OrUR$"":P$"1":400{|:"YPE A NUMBER OR ΠTO BEGIN:";5000:P$""1000X(P$):X1XLH300X1"NTER : HTTP://";:5000:UR$P$:300X2"NTER O*P":1,U,15,"S0:"F$:1:(F$),U:(F$),UTO CONNECT TO: ";WF$(A)S "NTER II ASSWORD: ";:5000:PA$P$H ML12:5,CR$;"ATW";QU$;WF$(WI);",";PA$;QU$;CR$;:900R P$"""ONNECT AIL!";CO$:3100W P$""3420X P$""3400Y 90  D64WGET64SW 19200B 2.0+@ UPDATED 10/13/2021 12:54Aa +254,(186):(254)8254,8| 9100:SY6158,254: SY34X23777:X,170:(X)170"<16K":SY227(51201)3512005,2,0,(BC):(65532)3456,87:54,87:52,87-ML9:C8$(((MV8)),2):E$"OK":VR3E$C8$@b5,"ATS42=";C8$;"TP+";QU$;P$;QU$qlML:P$E$P$OP$:"ETRYING..";CO$:600wvOP$P$:ML9:C8$(((MV8)),2):PN$(((P$)),2)E$"OK":VR3E$C8$5,"ATS42=";C8$;"T+";PN$:5,P$: +&SY3436879,27:CO$(31)Q +(SY226(ML1)2095:"PML64.BIN",(254),1: ++SY227(ML1)2095:"PML64.BIN",(254),1: +-SY226UM0(UM1)245:"UP9600.BIN",(254),1: +/SY227(51201)35:"SWIFTDRVR.BIN",(254),1:- 2P$E$"OMM ERROR. XPECTED ";E$;", OT ";P$;CO$;""(i --- GET PACKET HEADER, SETS P0,P1,P2, RETURNS P0=0 IF NADAPR0:#5,P$:P$""9305,(17);ML6:P0(MV2):P1(MV4):P2(MV6)PL(MV0):CR(MV1):C8(MV8)64 V1.5":"EQUIRES IMODEM FIRMWARE 2.0+"T "Y O IMMERMAN (BO@ZIMMERS.NET)"::i --- MODEM INITw UN(254) PH0:PT0:MVML18 "NITIALIZING MODEM...":CR$(13)(10) #5,A$:A$""203 5,CR$;"ATHZ0&P0F0E0";CR$;:90-""WRITTEN. RE YOU SURE (Y/N)? ";:50007(P$,1)"Y"(P$,1)""1020[(P$,1)"N"(P$,1)""1010d5:(UR$,I1,1)"/"II1:I(UR$)1020H1$(UR$,I):P1$(UR$,I1):P1$""P1$"/"I0(H1$,I1,1)":"II "":UR$"COFFEEMUD.NET:8080/CTCUG/FIRMWARE/C64NET_APPS_LATEST.D64"I, GET INFOb6:"EQUEST ARMS:"@ " *) YPE : "A " 1) RL : HTTP://";UR$B " 2) UTPUT NIT:";UNILW2JNH0:I020:(HH$(I))0NHNH1:"NABLE TO CONNECT TO ";H1$;"";CO$:300`ITTI408jP90:800:P$""ITTI10HtTIIT1130k"ONNECTED TO "HO$"";CO$~XB96001205UM:UM3:(789)234UM9:1210BAXB:UM19,1:5,"AT":6000:6000XB24001210 +P-LIVE":600 P$"SER-GENT: =":600,P$"ONTENT-ENGTH: 0":600FP$"CCEPT: */*":600RI020fHH$(I)""1330y(P$HH$(I):9002IP$(13)(10):650"EQUEST SENT. EADING RESPONSE...";CO$P90:800:P$"HZ8,HO$(I1)8,PO(I1),8,H$:HM$(I1)(H$,2)2!<"8:1G, 1000n -------------------------------- GET E$ FROM MODEM, OR ERROR ! -------------------------------E$""MLE$""P$E$"OMM ERRO R(P$,15,1)":"2100\I15pII1:(P$,I,1)" "21606zX((P$,I)):X0CLX@2100iTL0:5,CR$;"AT&M&DC";((P),2);CR$;900:P$"OK""IMODEM COMMAND FAILED: ";P$:CL0"EADERS COMPLETE. O CONTENT. ONE.";CO$: PP$(25):P$"OK":186,(254):BA1200:XB1200_ CR$(13):(14);:9100:53280,254:53281,246 CO$"":SY226ML49152:665,73((678)30) !SY227ML49152 #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: &SY3436879,27:CO$(((P$)OS1) 930:P0PP10P002320( P1BR3,P$;:BRBRP1:OS1:2320M$ 3,(P$,BR);:OSBR1:P1BROS1u. 1,"U2";3;0;T;S:1,E,E$,E1,E2:SS18 1,"U1";3;0;T;S:1,E,E$,E1,E2:E66S2552300B E70S0TT1:S0:2360L :"ONS8(215)128:S812830643= 7981,0:X(51201):981,15:X4960l 8981,0:"X-XFER128.BIN",(254),1:981,15: <(ML1)2175:"PML128.BIN",(254),1: =S8128XB2400:CO$(159) FTM828:SY61TM2816 + PITM:XM51200:DD56577:SY34SYML12:TITT6010#SY(65532):BC8#SY2269140@#X(56834):56834,X1:(56834)X19140\#BC15:SY227:56834,X:#(14);"EQUIRES A 64 WITH WIFTINK/INK232":Pß5,2,0,(8)Zá#5,A$:A$""A$;dáA$:A$""5,A$;ET)"::% --------------------------------L GET STARTED !r ------------------------------- UN(254) PH0:PT0:MVML18:CR$(13)(10):QU$(34) "NITIALIZING MODEM...";:6000 #5,A$:A$""2035ICK ONNECT" " 4) ERMINAL MODE"!" 9) UIT"@:"NTER A NUMBER:  ";V$5000:P$""1000}.P(P$):P95:1,UN,15,"I0":1:8P1P41050BLP12000:1000VP23000:1000`P34000:1000dP4"ERMINALM$(30).HO$(0)"COTTONWOODBBS.DYNDNS.ORG":PO(0)6502:HZ1dHO$(1)"BORDERLINEBBS.DYNDNS.ORG":PO(1)6400:HZ2HO$(2)"CENTRONIAN.SERVEBEER.COM":PO(2)6400:HZ31,UN,15:8,UN,8,"BBSPHONEBOOK,S,R"1,E:E08:1:3008,HZI1"2000LE0:FB0(P$)13"AD RESPONSE: ";CO$;P$:N(P$,5)"HTTP/""AD RESPONSE";CO$;P$:cRC((P$,10,3))RC200"AD RESPONSE CODE:";CO$;RC:4800:P$""E02200H(P$)17(P$,1)"C"(P$,9,1)"L"2100  CBMTERM64SW 19200B 1.8+@ UPDATED 10/13/2021 12:54Aa +254,(186):(254)8254,8| 9100:SY6158,254: SY34X23777:X,170:(X)170"<16K":SY227(51201)3512005,2,0,(BC):(65532)3456,87:54,87:52,87-"EADERS COMPLETE.""ECEIVING"(CL)" BYTES";CO$R1,UN,15,"I0:":3,UN,3,"#":T1:S0:OS1:1,E,E$,E1,E2Xu1,"B-P";3;0:TB0:BR256 " "(T)","(S):""; OS1OS(P$)3,(P$,OS);:BRBR31)3 +(SY226(ML1)2095:"PML64.BIN",(254),1:g ++SY227(ML1)2095:"PML64.BIN",(254),1: +-SY226(51201)1615:"X-XFER64.BIN",(254),1: +/SY227(51201)35:"SWIFTDRVR.BIN",(254),1: +1SY6170 2ML4864:981,15:E.";CO$:3:1V 5,"ATH0":5,"ATZ":TTTI100` TITT2400j 5:&P$""3" ";GA$:A$""5010]A$(13)" ":A$(20)A$;" ";:P$P$A$:5010P$""5010P$(P$,(P$)1):"  ";:5010pTTTI100z227XM0:DD37136C ZA%:SY61(A%155A%156)B%:I,A%131:II1:A%10^ _A%0I,A%:II1:90d dj ep f{ nP$"A" xCO$;"= V1.5":"EQUIRES IMODEM FIRMWARE 1.8+" "19200 BAUD VERSION" "Y O IMMERMAN (BO@ZIMMERS.NInÉ 50010 U8:F$"D64WGET":1,U,15,"S0:"F$:1:(F$),U:(F$),UNP0:(2614)0NP20tBAXB:2576,10:2578,(59490NP):2579,(59491NP)2582,170:2583,1:NP02582,1545,"AT":9000:9000850:P$""1240P$UN$""" ,CR$;CR$;"ATHZ0&P0F0E0";CR$;'900:P$"OK"203>#5,A$:A$""208c".";:5,CR$;"ATE0N0R0V1F0";CR$;z900:P$"OK"208".";:5,"ATE0V1X1Q0F0";CR$;(19);900:P$"OK""IMODEM INIT FAILED: ";P$:"!": HO$(30): PO(30): HR. XPECTED ";E$;", OT ";P$;CO$;""? --------------------------------f THE MAIN LOOP ! -------------------------------:CO$;"AIN ENU:"" 1) IAL FROM HONEBOOK"" 2) ODIFY HONEBOOK"" 3) U + MODE. ";:2430:1000j"?!":1000.:"IAL FROM HONEBOOK:":I1HZg (I)") ";HO$(I1);":";((PO(I1)),2) I::"NTER A NUMBER OR : ";*5000:P$""4X(P$):X1XHZ2000>HO$HO$(X1):POPO(X1):HA$HM$(X1) CRCX:CMP BUF1DX:BCC DOCRC1RTS6DOCRC1 TAX:LDA BUF1,X:STA CRCECLDY #$08_DOCRC2 LDA CRC8:E CRCEs #$01:STA CRCSLDA CRC8:CLC:R:STA CRC8LDA CRCS:BEQ DOCRC3LDA CRC8:E #$8C:STA CRC8DOCRC3 LDA CRCE:CLC:R:STA CRCE NABLE TO CONNECT TO ";HO$;":";((PO),2). -` "* ONNECTED. ";Et 5,CR$;"ATF0O";CR$;e~ SY226"IT 1 TO EXIT." SY61"IT TO EXIT." 53280,0:53281,0:SY3436879,8 TM 53280,254:53281,246:SY3436879,27  .BYTE 0 0 PEE2 .BYTE 0 0, CRC8 .BYTE 0= CRCX .BYTE 0N CRCE .BYTE 0_ CRCS .BYTE 0u "TIMEOUT .BYTE 0 0 ,BUFAPX .BYTE 5 DEBUG .BYTE 0 0 SETPSTR LDA $2D:STA $FB:LDA $2E:STA $FC SETPLP LDY #$00:LDA ($FB),Y +CMP #$50:BNE SE,A$:I #5,A$:A$""2520 :( +"U":"NTER THE FILENAME: ";:5000:P$""2430p2 +F$P$:"NTER A DEVICE (";((UN),2);"): ";:5000< +DV(P$):DV0DVUNF +(F$)3(F$,2)",P"(F$,2)",S"F$F$",P"P +1,DV,15:2,DV,2,F$",R":1,E: NUMX,X:DEX:BNE PACK:STA NUMX& JSR $FFCC= 4LDX #$05:JSR $FFC6M >JSR BUF1LINc HLDY #$00:STY CRC8 RPCKLP1 CPY BUF1DX:BCC PCKC1 \PCKDUN1 JMP $FFCC fPCKERR1 LDX #$FF:STX CRXFLG:JMP $FFCC PCKC1 LDA BUF1,Y:INY:CMP #91:BNE PCKLP1 LDA (F$)3(F$,2)",P"(F$,2)",S"F$F$",P"= +1,DV,15:2,DV,2,"@0:"F$",W":1,E:E02760] +2:1:E,E$,E1,E2:E0:2430o +SY61981,0z +51209 +SY61981,15 +2:1::"ONE!":2430 :"ODIFY HONEBOOK:" I1HZ (I)NE PCKERR1" LDA PEE1:STA NUMX:BEQ PCKDUN18 PCKP JMP BUFXLINZ PCKDIG CPY BUF1DX:BCC PCKDIG2s DIGERR1 LDX #$FF:RTS PCKDIG2 LDA BUF1,Y:INY PCKDIG3 CMP #48:BCC DIGERR1:CMP #58:BCS DIGERR1 TAX:TYA:PHA:TXA:PHA LDY #$00:LDA ($FD),Y:HP0HP235 "OCAL ECHO (/)? ";.6 A$:A$"Y"A$""A$"N"A$""3126^7 A$:HM$(X1)"":A$"Y"A$""HM$(X1)"E": HO$(X1)H$:PO(X1)HP:XHZ1HZHZ1D 1,UN,15:8,UN,8,"@0:BBSPHONEBOOK,S,W"N 1,E:E08:1:300X 8,HZFD),YZ +PLA:TAY:CPY BUF1DX:BCS DIGERR2Ad +LDA BUF1,Y:INY:CMP #32:BNE PCKDIG3Rn +LDA #$00:RTSkx +DIGERR2 LDA #$FF:RTS BUF LDA #$00:TAY BUFCLP STA BUF1,Y:INY:BNE BUFCLP RTSDOCRC8 LDA #$00:STA CRC8LDA #$00:STA CRCX DOCRC0 LDA"ONNECTING TO ";HO$;":";((PO),2);"...";CO$4 #5,A$:A$""2310u AT$"":IFXB>0ANDBA>0ANDXB<>BATHENAT$="S43="+MID$(STR$(XB,2) 5,CR$;"ATH"AT$"F3C";HA$;QU$;HO$;":";((PO),2);QU$;CR$; 900:(P$)7(P$,7)"CONNECT"2400$ " + 49152 .D PML64.BINC; PACKET ML 64 BY BO ZIMMERMANc; UPDATED 20170728 10:14PsdJMP BUF1LINnJMP BUFXLINxJMP PACKETJMP CRCPJMP BUFAPNOP:NOP:NOPNUMX .BYTE 0CRXFLG .BYTE 0PEE0 .BYTE 0 0 PEE1XM0((DD)16)025007 :CO$;(14);")PLOAD, )OWNLOAD, )ANGUP, OR )ONT: ";S A$:A$"U"A$""2600k A$"D"A$""2700 A$"C"A$""2430 A$"H"A$""2480 "H" :CO$;(14);"ANGING UP...";:6000 :I1100:#5TPNXT +INY:LDA ($FB),Y. +CMP #$80:BEQ SETPDUNW +LSETPNXT LDA $FB:CLC:ADC #$07:STA $FBt +VLDA $FC:ADC #$00:STA $FC +`LDA $FB:CMP $2F:BCC SETPLP:BNE SETPDUN +jLDA $FC:CMP $30:BCC SETPLP +SETPDUN RTS +PACKET LDX #$0C PACK LDA #$00:STAE02660 Z +2:1:E,E$,E1,E2:E0:2430d +SY61981,0&n +512009x +SY61981,15T +2:1::"ONE!":2430 +"D":"NTER NEW FILENAME: ";:5000:P$""2430 +F$P$:"NTER A DEVICE (";((UN),2);"): ";:5000 +DV(P$):DV0DVUN  +BUF1,Y:INY:CMP #32:BNE PCKERR1@ LDA #PEE0:STA $FD:LDA #PEE0:STA $FE[ JSR PCKDIG:BNE PCKERR1 LDA #PEE1:STA $FD:LDA #PEE1:STA $FE JSR PCKDIG:BNE PCKERR1 LDA #PEE2:STA $FD:LDA #PEE2:STA $FE JSR PCKDIG:BNE PCKERR1 LDA PEE11:B") ";HO$(I1);":";((PO(I1)),2):I (HZ1)") DD EW NTRY"D :"NTER A NUMBER OR : ";W 5000:P$""t X(P$):X1XHZ13000& "NTER HOSTNAME/IP: ";:5000:H$P$:P$""30000 "NTER PORT NUMBER (23): ";:5000:HP(P$): STA $FB +INY:LDA ($FD),Y:STA $FC$ +LDX #$09[( +DIGLP LDY #$00:LDA ($FD),Y:CLC:ADC $FB:STA ($FD),Y2 +INY:LDA ($FD),Y:ADC $FC:STA ($FD),Y< +DEX:BNE DIGLPF +LDY #$00:PLA:SEC:SBC #48:CLC:ADC ($FD),Y:STA ($FD),YP +INY:LDA #$00:ADC ($FD),Y:STA ($ :I1HZ ] 8,HO$(I1):8,PO(I1):8,"-"HM$(I1)b I:8:1: 3000R:"NTER HOSTNAME/IP: ";:5000:H$P$:P$"""NTER PORT NUMBER (23): ";:5000:HP(P$):HP0HP23"OCAL ECHO (/)? ";A$:A$"Y"A$""A$"N"A$""40 +DEY:BNE DOCRC2"INC CRCX:BNE DOCRC0,RTS2CRCP JSR SETPSTRVLDY #$02:LDA ($FB),Y:STA BUF1DXwLDY #$03:LDA ($FB),Y:STA $FDLDY #$04:LDA ($FB),Y:STA $FELDY #$00CRCPL1 CPY BUF1DX:BCS CRCPDUNLDA ($FD),Y:STA BUF1,YINY:B=z>{>>)==== -3====L3>> T3β=`=.==.==m===m===.=`=mz>z>=m{>{>` />>>>> u> v>  4hhL+ L3 L4:LP4;s>>U>> 3L4  ^4D=L3 8 ?8 8  F:RTS1'BUF1LIN LDX #$05:JSR $FFC6:LDY #$00:TYA:STA BUF1DX:JSR RETIMEg'BUF1LP JSR CHKTIM:BEQ BUF1L2:STX CRXFLG:JMP $FFCC'BUF1L2 LDA $029B:CMP $029C:BEQ BUF1LP$'JSR $FFE4:LDY BUF1DX:STA BUF1,YB'CMP #$00:BEQ BUF1LPL'CMP #$0A:BEQ BUF1LP .= ȹD==L->=D=D=L,D=L+ 0 $0L+Y=@Z=>Ix> 0L,>G= LG/H=A>Y=ȹH=Y=A>ȹH=Y=Lp,~>>@>ТY=Y=0) , L,HH 2hhY=#?(r>7qmq>q>L-~>Y=)DA #$00:STA CRXFLG:STA BUF1DX%*NLDX #$05:JSR $FFC6:JSR RETIME:4NBUFXLP JSR $FFE4T>NLDY BUF1DX:STA BUF1,YCNJSR CHKTIM:BEQ BUFXEC:STX CRXFLG:JMP $FFCCHNBUFXEC JSR $FFB7: #$0B:BNE BUFXLPMNJSR RETIMERNLDY BUF1DX:LDA BUF1,Y:DEC NUMX\NCMP.>L.>>>*8Ӎ>  >L.  ө= .88Ӎ>ӭ>  > +0  8> 8> ?8;  .8 8w>L+>>s>t>  1L=+   >   X:STA CRXFLGDuBUFALP JSR $FFE4:LDY BUF1DX:STA BUF1,Y:NuJSR $FFB7:CMP #$08:BEQ BUFAXQXuCMP #$42:BEQ BUFAXtbuCMP #$00:BEQ BUFAP1:INC BUF1DXluBUFAX STA CRXFLG:JMP BUF1DUNuBUFAP1 INC BUF1DX:LDA BUF1DX:CMP #$FE:BCC BUFALP:JMP BUF1DUNBUFTMHH ͽ>>>`D=P` 8 8 8ƅ= .8 8L~-ȹD=V Ȍ>8>D=IȹD= LL0ȹD==;ȥ>ʠD=D=Lq0D=` 8 8 8= .8 8L0>>Ȍy>Dmy>=i 2>z>{>ȑhhL->> 1>8岥hhL+D=PD= >`w>` ,:( ; ,) =D=L5>D= M5L3>>D= M5>L3z>{>z>.{>z>.{>z>.{>z>.{>=A) z>z>>>`> HH $0hhD= D=Bh>>>> 8 ?8 g8 ?8> D= D=89> .8 8`  = `= ` ` = u +,`L+  ɬ= .8L.`L2NE CRCPL1 CRCPDUN JMP DOCRC8F(#RETIME LDA $A1:STA TIMEOUT:LDA #$00:STA TIMEOUT1:RTS~%CHKTIM LDA $A1:CMP TIMEOUT:BNE CHKTIM2:LDX #$00:RTS&%CHKTIM2 STA TIMEOUT:INC TIMEOUT10%LDA TIMEOUT1:CMP #$04:BCS TIMBAD:LDX #$00:RTS:%TIMBAD LDX #$F*0q>7>*8>> i@=L+=ȹ ∄ 1 2w> h3>? 8 L A D S 8> D= 2z>s>{>t> w>L. h3>>>L+>> 8 ?8 g8 ?8> ;7Lj2r>rr>G=hmq>q>L->9ȹD=V'CMP #$0D:BEQ BUF1DUN ['JSR RETIME;`'INC BUF1DX:LDA BUF1DX:CMP #$FE:BCC BUF1LPSt'BUF1DUN JSR SETPSTRw~'LDY #$02:LDA BUF1DX:STA ($FB),Y'LDY #$03:LDA #BUF1:STA ($FB),Y'LDY #$04:LDA #BUF1:STA ($FB),Y*JSR DOCRC8:JMP $FFCC NBUFXLIN Lr> mq>q>r>SL~-L->L~-8z>H{>hL +0h L>-hL +0hL +08z>{>L~-~>Y=,L.q>LL-{>Ur>  mq>q> 7 7L-~>Y=)lq>L-Z="[=z>r>ѩmq>q>L~- 7L-r> q>iq>L- q>i q> 7 7>L #$0D:BNE BUFXNCRfNLDX CRXFLG:BNE BUFXNCR*pNSTY CRXFLG:INC CRXFLGAzNBUFXNCR INC BUF1DXeNLDA BUF1DX:CMP #$FD:BCS BUFXDUNNBUF1NI LDA NUMX:BNE BUFXLPNJMP BUF1DUNNBUFXDUN JMP BUF1DUN0uBUFAP LDX BUFAPX:JSR $FFC6:LDY #$00:TYA:STA BUF1DLtY=XeY=)L,{>r>RNzr> q>iq>L-r>1 /LG/q>iq>L- 8 8W> .8 8L-{>Dr> mq>q>L~- 2 /LG/mq>q>[=Y +q>ɶL/L~-r> mq>q>L- 3 /LG/mq>q>L->>> hh~R .BYTE 0BUF1DX .BYTE 0BUFX .BYTE 0 0 BUF1 .BYTE 0Zٙ NOP;:F$"PML64.BAS":8,8,15,"S0:PML64*":F$,8:F$,8ET P$ FROM SOCKET P P$"":E0*930:P0PP00"NEXPECTED PACKET ID: ";P0;"/";P:4P00E1:: FAIL> -- c80 ƳƲL0>>x>L?1y>> 1ȹY=S0OѲ>> 1L0>0`> 8 8 ?8= .8 8hhq>)>L-L~-y>L?1> 1y>>ȱz>ȱ{>> +{>z>>>mz>z>>m{>{>>`L?1ƳƲ` 8>L4>> >` 7L3 L4 4>hhL+`ɱ[ɳ_ɪ>ɬL4.$ ^4D=L3>`L5D=L58>΃>轞0轞0D=Ls4)`>L3>L3 3> * 8 8> D= L4ȄDe=i 2>> 6z>{> +"E L6:L6; 3>>L6"L5> 8L5L9D=> 7>L5>=譑>u C:?; 3>>L~6m=> m= 8L16m=D= :=L16>n=LO6=> 2z> 7>=L16> 8n=: 4>>hh>>Ll.L+>` ܎ܭ) ܩߢōDCBxDC ȭ<H=H>H?@A@L` AȍZ[\] Rȍ\]Lh Ǎbc RȍOP RȍQR + WN 1ȮV N ª LhM3M8OȱPQȭRmbȱmc /Ɉ0 Aȅ RȍTU   `ТLȈ8aHh0!A$Pp"bBX(@x0123456789ABCDEFNO START ADDRESS---------- 7>> ?8{> H8{>L7y>>>  y>  ee` + 8L08` 8`>> r9 8>` ͽ 8>`> r9 r9 9` ͽ 9` 8`u>v> ͽ L9`D= .8`  8 `>`>`>  >  S`W 1Ȑ  Y /ɬVn See Y Q VFW  H *` V ` dȍK |K /L`Lh t ' ȝe Y oȐ֝e gȐ J Ţe +J h / |  Uȅ` AȰ UȰ Rȅ` ȥHJJJJ 'Ȫh) 'H hLiii:`Hh`Y |Ȱ gȐ gȐ`L` tY ` + + + +Y Y8`:)(i` LeÍ` 7 ǥ"О  Lu Lh8 8W> .8 8L; L9  ^4D=L9D==L:> g8 ?8 8 8 1 4w>`. E N D 9>w>>s>t> h3`> D=  ^4D==L:L;,=ȩP=ȩ,=ȩW=Ȅ 8 8> 2 s> U.T*i? LH ǩ à,L1ũ `  Ĥ>å ͽ  &   ݩ L= BLhHwHHHHl`KH hILr ĮV  L Ţ.:  H ȩ ȩ H H) hh>h=h<DCBX>)LN,HP<[k=Zc^[_SH0NHҮBHHL $ɍK ɭ=< Hɩ$N  LhJMHF_^:SL[\]^_@H  n U c ŭH7Ш)) xT H8 ?8> 8 .8hhL.. S 8>>L;. H 8>L;LDALDYJSRRTSBCSBEQBCCCMPBNELDXJMPSTASTYSTXINYDEYDEXDECINXINCCPYCPXSBCSECADCCLCTAXTAYTXATYAPHAPLABRKBMIBPLANDORAEORBITBVCBVSROLRORLSRCLDCLIASLPHPPLPRTISEDSEITSXTXSCLVNOP  8z>x>{>y> x>y>x>y>  `8>΃>轞0轞0L+7)`D=+L?7ȹD= Z7=LJ7:808`== 2z>>{>>`> 8`>  q> H8 ?8q> 7`> 8`>z> H8z>L7> 8 8`>z> H8z>xL `  ߢōɑ `a ȩu `ΎHdX=< ŢB*LMɩ? , ũ. NVd . $ݐI +υυlL` V`N ¢ H`HShS`TUL8S---------- BRANCH OUT OF RANGEUNDEFINED LABEL NAKED LABEL <<<<<<<< DISK ERROR >>>>>>>>  -- DUPLICATED LABEL --  -- SYNTAX ERROR -- O CONNECT TO ";H1$;"";CO$:300`ITTI40jP90:800:P$""ITTI10tTIIT >`>`>`  > > r9L 9> ͽ  `>`>`  > r9 r9LC9 ͽ  `>`>`  v>u> ͽ  `H)4=hJJJJ4= `F 9hhL+ɀ G:L9DL:PL:NL5;OL ;SL;HL@Ahi=hi<H hAh@h? t>  ; 4hhw>L+> 32>  :L; 4hhw>L+. O 8>L;> P O:SjHL. N P 8Θ>    L;. N O 8>L;. N H 8>L;. N S 8>L;`. /ɈL ǩ é:wL= Dž  L Aȅf O0O8?Jnfne ": Ű |Ȥ0eeَTVKV ®XU6 eŽ eŢMX0 bňX eŽ e b bŭTK  +=<`K Hɱ  /K` gȐi /K`>` V`H h. ȩ L Ȣv ; =ɭ< ȭ=  HɭC ȭD  $ LhL` A RȍDC $ɍK   `e ǭIS )"У " `Lʢ`ҍ&©'L©3&©'  3L ` 3ķL.` ,© 3` ** 0  } Lt W` © L * ;z ,Ls <` *  #thE`<ʍ7!̺`` "NITIALIZING MODEM...";:6000P #5,A$:A$""203l 5,CR$;"ATHZ0F0E0";CR$; 900:P$"OK"203 #5,A$:A$""208 ".";:5,CR$;"ATE0N0R0V1F0";CR$; 900:P$"OK"208 ".";:5,"ATE0V1X1Q0F0";C HɦLͽ ˰A H  Lh +˅ " = H(`  0 :)`hh`L`HH&&hehe&ee` ˍUHH H Hh  Hh H Lh H  Lh + " LHJ&&` `ԩԩDԎԌLe:"  ";:5010p((DD)16)0z6100:5,"+++"; ".";:610075,"ATH":TTTI150NML12:TITT6040TbTTTI150rTITT6110x#SY(65532):BC8#SY2269140#X(56834):56834,X1:(56834)X19140#F ǩ 0P + +3LČ ,ĭ34 LØI#3"m#34#i4"3`L7 , ȬIJǭ $ ©`5 0 L| ĐLgƮ 0 0 8` W``  0 } o©N, ©LL / }Lɑх҅^ T:,$^i(ݍI ΐLI:$' 8 é Lp Lh ͥ^8^ o ^MM âN, C Lh͢L¦ ͦ膭;A$;:DNDN1:40020|",";A$;:DNDN1 DN18:DN0:LNLN10 40020\ ( 162,5,32,198,255,32,228,255,201,0,240,61,201,10,240,57 2 32,210,255,173,155,2,205,156,2,240,38,176,13,169,255,56,237,156 < 2,24,109,155,2,56,176,4,56,237,156,2,3J`Hͺ hL lh΍ɍ ` * HHH -hhh`ͺ +L l * -` JHh`   `L + 7384`  ed`  `  YMJ,),#($YX$$#]#)i#$S#$S[[i$$)|mi)S4i#bZH&bTDThDt(ntJrtttrDh2"&&rr&HDD PC IRQ SR AC XR YR SPABCDFGHLMNQR(TWX,:;$#"+-OIJ%&EV)>ɯ2/ƠɰLǠP* +ĉQ  g) ٩IS LhIL )i L` Lh / / H +S0 S LhL` Lh Ţ.$   H Hɢ0T.Ui `UT Hɥ ʥ HɊ)  + i@ (ʩL30A$:HA$"":A$"Y"A$""HA$"E"HO$H$:POHP:2300%P$""2" ";FA$:A$""5010`A(A$):A13" ":A34A$;A$;" ";:P$P$A$:5010A20A$;" ";:P$P$A$:5010P$""5010P$(P$,(P$)1),141! -1!OÐ!Pß5,2,0,(8)"Zá#5,A$:A$""A$;"dáA$:A$""5,A$;*"nÉ 50010c"U8:F$"CBMTERM":1,U,15,"S0:"F$:1:(F$),U:(F$),U:6000 :I1100:#5,A$:I #5,A$:A$""2480  :"ODIFY HONEBOOK:" I1H L eeL S H LhLx X>/=T<L5Lu |` ` ` ǥ < `` ` ` `ɍ ¥ < ` `-.`` -.` ǩǍɍ ¥ < `` ǩɍ ¥ ,© 3 `` ǩɍ ¥ ,©ɍ  ``! CBMTERM64/128 1200B 1.8+A UPDATED 08/15/2017 12:54Ab +254,(186):(254)8254,8} 9100:SY6158,254: SY34X23777:X,170:(X)170"<16K":SY227(51201)3512005,2,0,(BC):(65532)3456,87:54,87:52,87:S8(215)128:S812830643> 7981,0:X(51201):981,15:X4960m 8981,0:"X-XFER128.BIN",(254),1:981,15: <(ML1)2175:"PML128.BIN",(254),1: =S8128XB2400:CO$(159) FTM828:SY61TM2816 PITM:XM51200:DD56577:SY34S H`HGGhGGiGH`12GHǮ +*mǨmǪimG1mH2GȑGȑGȑGȑG`LHE0F hH"# +.#m"#i"#12GHm"m# 12"#mHHGHHEGȭFGȭ" X(56834):56834,X1:(56834)X1 #BC15:SY227:56834,X:&?Z@1,8,15:8,8,8,"TELNETML.BIN,P,R":LN41000:DN0qJ1,E:E08:1:T#8,A$:ST08:^A$""A$(0)cA(A$):A$((A),2)hDN0((LN),2);" DATA ";A$;:DNDN 5,CR$;CR$;"ATHZ0F0E0";CR$;(900:P$"OK"203?#5,A$:A$""208d".";:5,CR$;"ATE0N0R0V1F0";CR$;{900:P$"OK"208".";:5,"ATE0V1X1Q0F0";CR$;(19);900:P$"OK""IMODEM INIT FAILED: ";P$:"!": HO$(30): PO(30): 0-TIMEOUT[2400 BAUD]LLLLL˩.ʍˍˍ&'`HHHخݩ ݭ Mݬݰ  -J +)݊)/BݩݩݩM ݩMݩݩ<LV ݊),Jfƨ"ݩ ݌ M ݊Jƴ0 fL̞ . PP$(25):P$"OK":186,(254):BA1200:XB1200` CR$(13):(14);:9100:53280,254:53281,246 CO$"":SY226ML49152:665,73((678)30) !SY227ML49152 #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: +&SY3436879,27:CO$Y227XM0:DD37136D ZA%:SY61(A%155A%156)B%:I,A%131:II1:A%10_ _A%0I,A%:II1:90e dk eq f| nP$"A" xCO$;"= V1.5":"EQUIRES 64ET II FIRMWARE 1.8+" "1200 BAUD VERSION" "Y O IMMERMAN (BO@ZIMMEMR ɍ  ``ɍ ¥ ,©, 38ii 3 3ɍ  cǰ 3 3 ǰ `` 0 0 ``8`(+ 3/ 3ͯܭͮ8`8`,z`Ʒ` ɭ12GHi njHEGȭFGhG1:40020|",";A$;:DNDN1DN18:DN0:LNLN10! 40020^( 162,5,32,198,255,32,228,255,201,0,240,61,201,10,240,572 32,210,255,173,155,2,205,156,2,240,38,176,13,169,255,56,237,156< 2,24,109,155,2,56,176,4,56,237,156,2,201,200,14HM$(30)/HO$(0)"COTTONWOODBBS.DYNDNS.ORG":PO(0)6502:HZ1eHO$(1)"BORDERLINEBBS.DYNDNS.ORG":PO(1)6400:HZ2HO$(2)"CENTRONIAN.SERVEBEER.COM":PO(2)6400:HZ31,UN,15:8,UN,8,"BBSPHONEBOOK,S,R"1,E:E08:1:3008,HZI1Gȭ#GȩGȩGhG` F= tɍ"#"G"#GH"1#2ܭGH12`/0GHGEȱGF t12`GHqGHqGh`DISKS 22 INSERT $ 22 LOAD CF FIND , 22 NAME SAVE ,PRG LOGIN ?NOT SUPPORTED IN V-1541?10(31)4 +(SY226(ML1)2095:"PML64.BIN",(254),1:h ++SY227(ML1)2095:"PML64.BIN",(254),1: +-SY226(51201)1615:"X-XFER64.BIN",(254),1: +/SY227(51201)35:"SWIFTDRVR.BIN",(254),1: +1SY6170 2ML4864:981,15RS.NET)"::) --------------------------------P GET STARTED !v ------------------------------- UN(254) PH0:PT0:MVML18:CR$(13)(10):QU$(34) "NITIALIZING MODEM...";:6000 #5,A$:A$""203  ݩмH) ݩ-퍡h`H`h̝J): ݭݩݩ x ݌  (` .L hL < 6)ˍeʹˍjʹˍʹˍʭ)#ݩLXLL򅞄̛`B3HZ8,HO$(I1)8,PO(I1)-8,H$:HM$(I1)(H$,2)3!="8:1H, 1000o -------------------------------- GET E$ FROM MODEM, OR ERROR ! -------------------------------E$""MLE$""P$E$"OMM ERRL MODE. ";:2430:1000j"?!":1000/:"IAL FROM HONEBOOK:";I1HZh (I)") ";HO$(I1);":";((PO(I1)),2) I::"NTER A NUMBER OR : ";*5000:P$""4X(P$):X1XHZ2000>HO$HO$(X1):POPO(X1):HA$HM$(X1)XM0((DD)16)025008 :CO$;(14);")PLOAD, )OWNLOAD, )ANGUP, OR )ONT: ";T A$:A$"U"A$""2600l A$"D"A$""2700 A$"C"A$""2430 A$"H"A$""2480 "H" :CO$;(14);"ANGING UP...";:6000 :I1100:# (F$)3(F$,2)",P"(F$,2)",S"F$F$",P"> +1,DV,15:2,DV,2,"@0:"F$",W":1,E:E02760^ +2:1:E,E$,E1,E2:E0:2430p +SY61981,0{ +51209 +SY61981,15 +2:1::"ONE!":2430 :"ODIFY HONEBOOK:" I1HZ (I) +Z:I1HZ ] 8,HO$(I1):8,PO(I1):8,"-"HM$(I1) b I:8:1: 3000S:"NTER HOSTNAME/IP: ";:5000:H$P$:P$"""NTER PORT NUMBER (23): ";:5000:HP(P$):HP0HP23"OCAL ECHO (/)? ";A$:A$"Y"A$""A$"N"A$""4 :SY227:56834,X:?@1,8,15:8,8,8,"TELNETML.BIN,P,R":LN41000:DN05J1,E:E08:1:KT#8,A$:ST08:^^A$""A$(0)wcA(A$):A$((A),2)hDN0((LN),2);" DATA ";A$;:DNDN1:40020|",";A$;:DNDN1DN18:DN0 OR. XPECTED ";E$;", OT ";P$;CO$;""@ --------------------------------g THE MAIN LOOP ! -------------------------------:CO$;"AIN ENU:"" 1) IAL FROM HONEBOOK"" 2) ODIFY HONEBOOK"" 3)  "ONNECTING TO ";HO$;":";((PO),2);"...";CO$5 #5,A$:A$""2310v AT$"":IFXB>0ANDBA>0ANDXB<>BATHENAT$="S43="+MID$(STR$(XB,2) 5,CR$;"ATH"AT$"F3C";HA$;QU$;HO$;":";((PO),2);QU$;CR$; 900:(P$)7(P$,7)"CONNECT"2400$ " TELNETD64 1200B 2.7+= UPDATED 07/07/2017 03:39P^254,8:(186)7254,(186)(49153)43"RDS64.BIN",(254),1:53280,14:53281,6 +(14);"TELNETD64 V1.0":"EQUIRES 64ET II FIRMWARE 2.7+""1200 BAUD VERSION"& 5,A$:I #5,A$:A$""2520 ;( +"U":"NTER THE FILENAME: ";:5000:P$""2430q2 +F$P$:"NTER A DEVICE (";((UN),2);"): ";:5000< +DV(P$):DV0DVUNF +(F$)3(F$,2)",P"(F$,2)",S"F$F$",P"P +1,DV,15:2,DV,2,F$",R":1,E") ";HO$(I1);":";((PO(I1)),2):I  (HZ1)") DD EW NTRY"E :"NTER A NUMBER OR : ";X 5000:P$""u X(P$):X1XHZ13000& "NTER HOSTNAME/IP: ";:5000:H$P$:P$""30000 "NTER PORT NUMBER (23): ";:5000:HP(P$):030A$:HA$"":A$"Y"A$""HA$"E"HO$H$:POHP:2300&P$""3" ";GA$:A$""5010aA(A$):A13" ":A34A$;A$;" ";:P$P$A$:5010A20A$;" ";:P$P$A$:5010P$""5010P$(P$,(P$)1UICK ONNECT" " 4) ERMINAL MODE""" 9) UIT"A:"NTER A NUMBER:  ";W$5000:P$""1000~.P(P$):P95:1,UN,15,"I0":1:8P1P41050BLP12000:1000VP23000:1000`P34000:1000dP4"ERMINANABLE TO CONNECT TO ";HO$;":";((PO),2). .` "* ONNECTED. ";Ft 5,CR$;"ATF0O";CR$;f~ SY226"IT 1 TO EXIT." SY61"IT TO EXIT." 53280,0:53281,0:SY3436879,8 TM 53280,254:53281,246:SY3436879,27 :LNLN10 40020" ( 162,5,32,198,255,32,228,255,201,0,240,61,201,10,240,57h 2 32,210,255,173,155,2,205,156,2,240,38,176,13,169,255,56,237,156 < 2,24,109,155,2,56,176,4,56,237,156,2,201,200,144,11,173,1 F 221,41,253,141,1,221,56,176,12,:E02660 +Z +2:1:E,E$,E1,E2:E0:2430d +SY61981,0'n +51200:x +SY61981,15U +2:1::"ONE!":2430 +"D":"NTER NEW FILENAME: ";:5000:P$""2430 +F$P$:"NTER A DEVICE (";((UN),2);"): ";:5000 +DV(P$):DV0DVUN  +HP0HP235 "OCAL ECHO (/)? ";/6 A$:A$"Y"A$""A$"N"A$""3126_7 A$:HM$(X1)"":A$"Y"A$""HM$(X1)"E": HO$(X1)H$:PO(X1)HP:XHZ1HZHZ1D 1,UN,15:8,UN,8,"@0:BBSPHONEBOOK,S,W"N 1,E:E08:1:300X 8,H):"  ";:5010p((DD)16)0z6100:5,"+++";!".";:610085,"ATH":TTTI150OML12:TITT6040UcTTTI150sTITT6110y#SY(65532):BC8#SY226#X(56834):56834,X1:(56834)X1#BC15"Y O IMMERMAN (BO@ZIMMERS.NET)"::A "ONFIGURE ERVER:":p "IME IMIT: HRS, MINS 0, 55";H,M XH:5000:HX XM:5000:MX 4915222,H:4915223,M "DLE IME/INS 4";X $5000:4915221,X +."ECURITY (0/1=ON)A$:A$(13)2040  20000 :"TARTING SERVER:"; 49152j ((53280)15)14"! EE MANUAL.": "ET ROUTER TO REDIRECT":"TO THE ABOVE ADDRESS AND PORT." "ERVER IS AWAITING CONNECTION..." I49152 (((I))(, ߯n~N^.> ʍʍʍʍʍʍʍʍʍ`Lʥ`L:ʭ)`Lʠƍʍ :` :ʥ`H͜h`h`ʹmʍ`ʌʹMʪ Mʍʽ ȍ`L˩ʩ  p ʢ  L8˩C  V AʭLiLY̭  +L?˭L(̢  ʥ :ʬʥʐ ʭL˭ .I' ʥ ̀L˭ ʭ3  ! 201,20,176,8,173,1,221,9,2.!P 141,1,221,162,0,32,198,255,32,228,255,201,0,240,171,201,133,240p!Z 4,201,27,208,4,32,204,255,96,72,162,5,32,201,255,104,32,210!d 255,162,0,32,201,255,56,176,141! -1!OÐ!Pß5,2,0,(8)!Zá#5,A$:A$""A$56,87:54,87:52,87B PP$(25):P$"OK":186,(254):BA1200:XB1200w CR$(13):(14);:SY(65532):53280,4:53281,15 CO$"":SY226ML49152:665,73((678)30):UMML2048:XB9600 #SY34ML22273:(ML)765:"PMLVIC.BIN",(254),1: +& p -   L8˭Li p :   LP̭ C  L8   L̩    ʎ pʩ * ʅ`  p L?ͩ``  ΥL 1";I +84915224,I6 +L"ISTEN ORT 6400";PL +VP0P655351100c +`4100:P$((P),2) +jI1(P$):AI1,((P$,I,1)):I +tAA(P$):A,13:A1,0 +I$"":4000:AA2:OAA +(A)13I$I$((A)):AA1:1210 PR$"ODEM INI (I1)))"AT"4030# II1:I491522564010- AI:7 4000A "IA1d ,(((I))((I1)))"AT"4160 6II1:I491522564140 @II1 J((I))"A"4190 TII1:I491522564170 ^II1:AI g X0(X10):XX(10X0):X(X016>]|6Ut 0@P`p2"RBrb$4dtDT6&vfVFHXhx(8ZJzj +:*뛋l|L\,< ݭ~n^N>.Ͽ0 P@p`"2BRbr4$tdTD&6fvFV陉XHxh8(JZjz +*:ͽ|l\L<өͭ ʍI ʭʍΥ ʍέʍ {  ʹ Ȍʐ  : V A )C6 LJLi L ̭  : L -   L pL IRC64/128 1200B 1.8+= UPDATED 08/19/2017 02:58An UPDATED UI FOR C64 BY JIM HAPPEL 10/6/2020 +254,(186):(254)8254,8 SY(65532):SY6158,254: SY34X23777:X,170:(X)170"<16K": 5,2,0,(8):(65532)34S8128XB2400:CO$(159)L PSY226UM0UM:UM3:X(789):UM9:X234XB1200v ZSY3456579,0: WHY DOES THIS WORK d GET THE BAUD RATE nP$"A" xCO$;" V1.5":"EQUIRES 64ET II FIRMWARE 1.8+" "Y O IMMERMAN (BO@ZIT "I$:I1(I$)2:PR$PR$"":I+ PR$;:I$> (I$)281220e I1(I$):OAI1,((I$,I,1)):I OAOA(I$):OA,13:OA1,0 ((56577)16)03000 "ARRIER DETECTED." "ESET MODEM, WAIT 30 SECONDS," "THEN HIT ENTER:" @)X:0U8:F$"TELNETD64":1,U,15,"S0:"F$:1:F$,U:F$,UrseJzʔiM핼RJ|Ws))udstJze'Ւ%ϒI})s|9)w)u_'^MRs))|-rݗwy)u]ytewz^MIJ)Uvw)yJUiUNIlfYuIl)J SY3436879,27:CO$(31)I +(SY226(ML1)2095:"PML64.BIN",(254),1: +-SY226UM0(UM1)245:"UP9600.BIN",(254),1: +2SY61ML4864:981,15:S8(215)128:S812830643 +<SY61(ML1)2175:"PML128.BIN",(254),1: FSY61LLLL !Bc)Jk1sR9{ZbC ǤjK( ϬSr0[z8冧@a#펯Hi ++ԷqP3ܿyX;"`A* hI2Qp:Yx -No%Fg=^5wV˨nO, àfG$_~"$A$:A$""940$""SP$"":820$#2,A$,A$$#2,A$,A$:ST0X(0):%#,"I"DD$":":15,"I"SD$)1:15:2:3@)T00"ALIDATED";(T0T1);"/";T0;" SECTORS"Z):"IT RETURN:  ";r)A$:A$(13)1740{)100)VL$"":E0:VLVL1:(F1$,1)""IW$((VL),2)"-")(F1$,1)""VL11850)(F$,1)"R"185eh80qȩqhY Y в``Y`WWWY`YWWMW)WWjWWWIWWjWWо` $WYYYL5XW W`W`W W W`` Y X XWL͜ Y83400:P0P1,(ML1536)256::3400:P,0::3400j +MVML(35):SP$" ":3300SU8:SD$"0":DU8:DD$"0":F1$"INGLE ":F2$"ORMAL ":MN1"MUTIL V4.0":"O IMMERMAN":"NDRE ACHAT" +("...LANET NK.":!D:X$(((A$)),2):B!XB$"EST. ":XDU:X$DD$:550:DUX:DD$X$:100}!"[]INGLE OR []ULTI-ILE:  ";:X$""(13):400!F1$("INGLE ULTIPLE"F1$" ",((O1)8)1,8):F1$:!"[]ORMAL OR []OMPRESSED:  ";:X$""(13):400U,15,"I"DD$":":15,SU,15,"I"SD$":">&T00:T10:D0SU:D0$SD$:1800:E01700i&3,DU,3,"#":V10:EF0:T1:S0:TT0:E0&EF:"ONE.":50:1600&V11200&$EF0::"";SP$:"RACK";T;" ECTOR";S;&.VM01100'82,"U1";3;(DD$);T;S: NPACK ALIDATE: ";VM$(VM)V"[] NPACK AN IMAGE":"[] ACK AN IMAGE":M1$""(13)"[] ISK INTERFACE":"[] XIT TO ":MN$"":M1(MN$)200:220"";:I1M1:O0:IMNO("")"";(O);(MN$,I,1);"":I: ;"> ";:CO$""#>A$:A$""830,#HA(A$):A13900f#RA20CO$""" "A$A$" ";:CO$(CO$,(CO$)1):830#\A31A96CO$CO$A$:A$;" ";:830#fA191A218CO$CO$A$:A$;" ";:830#p830#1::""SP$:"";#CO$""100 (2,"B-P";3;0:MV1,3:(ML(33)):V11G(2,"U2";3;(DD$);T;S:2,E,E$,E1,E2:E0V10:1250j(E66E,E$,E1,E2:NENE1:V10(TTTT1:TT200(F1$,1)""1800:TT0:EF0(E66S255SS1:1040(E70S0TT1:S0:1040(1700(@2 $):B((X$,I,1)):((AB)((A 128)B))(O0)OI: I:O0400@ s B$"OURCE ":XSU:X$SD$:550:SUX:SD$X$:100 &""B$"NIT :"(X):""14);:1,0:1,A$:1 0:X(A$):X7X30"";:550 :""B$"RIVE: "X$:""14);:1,0:1,A$:1 +ENING ARCHIVE.":50*X1+lF$"":SUDUSD$DD$"RIVES MUST DIFFER!":50:`+v"ILENAME: ";:X$(F1$,1):X$"""?-";|+1,0:1,F$:1:X(F$):+X16(X$""X14)"ILENAME TOO LONG!":"";:1900++1900:VL0:F$""100,F$hSE$""" GUESS YOU'RE DONE THEN":5:[r"HAT IS THE PORT":5000:PO$P$:(PO$)0SE$"":360o|SE$SE$":"PO$#5,A$:A$""390 MAKE THE CONNECTIONAT$"":XB0BA0XBBAAT$"S43="((XB),2)#5,A$:A$""406"Q GJIBINBECAEMGOCALIFALGMJACNABA"C0 "KNBHCAMJACLAOGOOBCCAOOBECAEMGOCAKNBHCAMJACJAPAMOBECAMOBECAMOBCCAMOBC"0 "CAMOBBCAMOBBCALIFAMNKAAAKNBECAJBPOOOBCCAKNBCCABIGFPOIFPOKNBDCAGFPPIF"0 "PPKFPPINBGCAKFPOINBFCACAEDCBEMMMPPKJCDIFPPKJLMIFPOKJAAINBCCAKAAA U$(34):5,"AT"AT$"H&D13&M13&M10CP";QU$;SE$;QU$+900m(P$,8)"CONNECT ""NABLE TO CONNECT TO ";SE$;":";P$:300~P((P$,8))XB9600430UM:UM3:(789)234UM9:435BAXB:UM19,1:5,"AT":9000:9000XB240043500-MV1,3:(F2$,1)""MV1,255'- 2,"B-P";3;0:MV,3:(ML(03))O-%(MV)255"OMPRESS RROR!":1700g-4MV1,1:(ML(13))->TTTT1:TT200(F1$,1)""1800:TT0-HE66S255SS1:2040-RE70S0TT1:S0:2040-\:"ONE!A(A$):LA033505 I1LA2:X16(((A$,I,1))65)(((A$,I1,1))65)05 P,X:PP1::3310?5 ML8192[5 D((ML8192)256):PMLt5* A$:LA(A$):LA054 I1LA:X((A$,I,1))65:PPX:Y(P):Y32Y365> P,(P)D:I:33705H P(LAADLIFAIKGAKJPPINAPCAKNBCCAINBACAKNBDCAINBGCAKNBCCAIN"W2D "BFCAGAKOBACACAMJPPKJCDIFPPKJLMIFPOKAAALBPOCANCPPOGPONAACOGPPKFPPMNBG"2N "CANAAFKFPOMNBFCAJAOFEMMMPPKOAPCACAMGPPKJCCIFPPKJLMIFPOKOBACAOAPPNAAO"2X "KAAACAMPPPJJLMCCMINAPHEMMMPPCAMPPPINBECA 900:P$"OK"203!, GET THE SERVER96:"OME SERVERS:"_@"IRC.NLNOG.NET PORT 6667, #C-64"E"IRC.FREENODE.NET PORT 6667, #C64FRIENDS"J"IRC.US.IRCNET.NET PORT 6667, #C-64"^SE$""::"HAT IS YOUR SERVER HOST":5000:SE$P$BKCBKMBBCANAADEMBKCBOOBBCALJLMCCKMBCCAMIJBPOIINBPOPADIKJ"_/ "AAINBHCAKNBECAMJIALAANMJIAPAAJOOBCCAOOBECAEMGOCAKAAAKNBECAJBPOMOBBCA"/ "OOBCCAKNBCCABIGFPOIFPOKNBDCAGFPPIFPPEMFBCAOOBHCAKNBECAMJIAPANHMJIBJA"/ "AKMJPPPAMPOOBECAEMGOCAMJABNAAPKNBECABI NP0:(2614)0NP20?BAXB:2576,10:2578,(59490NP):2579,(59491NP)e2582,170:2583,1:NP02582,154}5,"AT":9000:9000:"HAT IS YOUR NICKNAME";:5000:NI$P$:N1(NI$)NI$""" GUESS NOT.":P$" "NI$:600F$",S,W":2,SU,15,"I"SD$":":15,DU,15,"I"DD$":"4,D0DU:D0$DD$:1800:E1700_,3,SU,3,"#":V10:EF0:T1:S0:TT0:E0x,2,"U1";3;(SD$);T;S,EF0::"";SP$:"RACK";T;" ECTOR";S;, 2,E,E$,E1,E2:E662110,E:E,E$,E1,E2::21KC"4 "AAIOBBCALNLMCCNNLMCDNAAEOINAPFGAOOBBCAGA" 4 ""U4 "CDDDDNLDGGDFFLDFDDHDDFDFDDDNDLDDFFDDIHDDPDHGDKHDDDHDDDDDIFDIJFDFLN"4 "RDDFDHHIGNDDFDHHLDDDDDDEFYHIFJMMUGSDEGGNGQIODDJ"4 ""4 PML::(P)764 "READING ML DATA..."4 A$:LLBPO"'1 "OGPONAACOGPPINBECAMJIBLACOKAAALBPOOGPONAACOGPPKMBCCAOOBCCANJLMCCPAAD"s1& "EMMHCBMOBECANAODKFPPMNBGCANAAFKFPOMNBFCALADKLIFAMBKNBECADIOJIAINBECA"10 "KAAALBPOOGPONAACOGPPKMBCCAOOBCCANJLMCCPAADEMMHCBMOBECANAONKFPPMNBGCA" 2: "NAAFKFPOMNBFCA MODEM..." #5,A$:A$""203F 5,CR$;"ATHZ0F0E0";CR$;:900:P$"OK"203] #5,A$:A$""208| 5,CR$;"ATE0N0R0V1Q0";CR$; 900:P$"OK"208 5,"ATE0V1X1F3Q0S40=250I4";CR$;(19); 900:VR(P$):VR1.8"IMODEM INIT FAILED: ";P$:":1700/. "EMBJCAEMNPCBEMALCCEMJDCCEMKHCCAAAAAAAAAAAAAAAAAAAAKOAPCACAMGPPKAAACA"{. "MPPPJJLMCCJJLMCDMINAPEKOBACAOAPPNAANKJCEINBGCAKJLMINBFCAEMMMPPKJCDIF". "PPKJLMIFPOKJAAINBBCAINBHCAKJAAINBHCAKMBBCALJLMCCKAABJBPOIMBECAIMBCCA"/ "OOBBCANAADEM65532):P226ML9216:P45:6R P22ML8192:P42:6\ P61ML4864:P0:15:96f P246ML13312:P45:S6p P34ML13824:P45:r6 "UNSUPPORTED COMPUTER.": MJIBLACMCAMPPPKAAAJBPOOGPONA";3b "ACOGPPMOBECANAOOKFPPMJCDNAAEKFPOMJLMJANIPADICAMMPPKJPPINAPCAINBACAGA"3l "KNBECADIOJIAINBECACAMPPPINBCCAKAAAJBPOOGPONAACOGPPMOBECANAPDKFPPMJCD"3v "NAAEKFPOMJLMJAKANAMIEMMMPPKOBACACAMJPPKAAALJLMCCCANCPPMINAPHEMMMPP +P$" GUEST 0 * :OE NONYMOUS":600TPS$"ONNECTED, WAIT FOR . ? FOR HELP"CO$: 10000s 1000: GO START MAIN LPV --- TRANSMIT P$ TO THE OPEN SOCKETX(P$)0((P$,1))10P$(P$,(P$)1)]OP$P$:ML9:C8$(((MV8))!3)" ":"A$(20)A$;" ";:P$P$A$:5010"P$""50109"P$(P$,(P$)1):"  ";:5010T"p:: NEW INPUT ROUTINEj"z A$(13) 6250" A$(20) 6500" A$(34) 1000" (IS$)78 1000"A(A$): (A31 A127) IN LOOP +A$:A$"" 6000: WAS 30004 GOTO 1000 : REM ADDED FOR DEBUGGINGI800:P$""1000mMS$"":MC$"":MA$"":I1:LP$P$(P$,1,1)":"1100I(P$)1100$(P$,I,1)" "II1:1050.MS$(P$,1,I1):P$(P$,I1)LI -":10000R PS$"/JOIN #C-64 "CO$"HANGE CHANNELS.":10000E\ PS$"/QUIT "CO$"OGOUT AND EXIT.":10000pf PRINT"/LIST ";CO$;"IST CHANNELS."p PRINT"/WHO MASK* ";CO$;"SER INFO." PS$"NYTHING ELSE "CO$"END MESSAGE":100 PE)P$:PEPE1:PE25PE0P$"":. --- GET P$ FROM SOCKET PI E0:P$"":PH25PH0j*PHPEP$PP$(PH):PHPH1:4700:P00E1:: FAIL>PHPEP$PP$(PH):PHPH1:HE1: ---- GET E$ FROM MODEM, OR ERRORE$"" $)II(MS$):2950 h (MS$,II,1)"!"IIII1:2950r IIII1:29105 MS$(MS$,1,II):I A$(13)10000t "TREAM PAUSED. NTER ? FOR HELP." OM$""P$" "OM$:600 CO$;"> ";:IN$"":ITTI1000:3200 TIIT"ANCELL ("C") : "MA$:10000:440#C400PS$"RROR: "MA$:10000:6080X(MA$)N11(MA$,1,N1)NI$MA$(MA$,(NI$)2)rPS$MA$: 10000:6080} CMDSMC$""1400(MC$""10002MC$""2000<MC$""2100F 6080 IN$"WHO"P$" :"AA$:600:6080:!PS$"NKNOWN OMMAND: "IN$". RY ?"CO$:10000:6080f!PS$"OINING "QU$A$QU$:10000:AA$A$!P$" :"AA$:600!E$"":CC$AA$:1000!"?  ";:P$""!A$:A$""5010!A$(1 ML6:P0(MV2):P1(MV4):P2(MV6)1PL(MV0):CR(MV1):C8(MV8)FP00P2C8985VP10P$""gP00CR0xML12:P$""PS$"-"CO$:10000:5,"ATL":945PS$"XPECTED "E$", GOT "A$:10000: THE MA  3400 (IN$,1,1)"/"IN$(IN$,2):3500B CC$""PS$"O HANNEL ELECTED! RY ?"CO$:10000:1000a P$" "CC$" :"IN$ 600:PS$""NI$": "IN$:10000:6080 E$"":1000H PS$CO$"-------------------------------------- ,2)::E$"OK":VR3E$C8$'b5,"ATS42=";C8$;"T+";QU$;P$;QU$^lML:P$E$P$OP$:PS$"XMIT FAIL"CC$:10000:600dv --- GET NEXT FROM SOCKET INTO PP930P0PP00PS$"NEXPECTED PACKET ID: "P0"/"P:10000:P00PP$( I1:20102900*PS$""MS$": "(MA$,I2):10000:6080<4PS$""LP$"":10000:6080~2900:PS$""MS$" HAS LEFT THE CHANNEL."CO$:10000:60802900:PS$""MS$" HAS JOINED THE CHANNEL."CO$:10000:6080T II2^ II(MS 1:P$""1000VI(P$)1200`(P$,I,1)" "II1:1110@jMC$(P$,1,I1):P$(P$,I1)XMA$P$:MC$""1000|A((MC$,1,I)):A48A571300(MC$)1(MC$,1,1)"0"MC$(MC$,2):1220C(MC$)C430C460PS$"AD ICK GIVEN 00 PS$CO$"---------------------------------------":10000:6080B X0:I2(IN$):X0(IN$,I,1)" "XIr :A$"":X1A$(IN$,X1):IN$(IN$,1,X1) IN$"JOIN"4000 IN$"QUIT"P$" :":600:9100 IN$"LIST"P$"":600: MLE$""P$E$ 927 KPS$"OMM ERROR. XPECTED "E$", OT "P$CO$"":10000: GET PACKET HEADER, SETS P0,P1,P2, RETURNS P0=0 IF NADAML12:E0:P00:P10:P20:PR0:I0:I00:I10:CR0:P40:P50:C80P$"":5,(17);  ED"::1000N A$:A$""3100b ITTI1000 A$(13)33006 A$(20)(IN$)03230b IN$(IN$,(IN$)1):"  ";:3100 A(A$):A32(A95A193)A2183100 IN$IN$A$:A$;" ";:3100 IN$""" "::1000 IN$"?" MC$""2200PMC$""2300ZMC$""1000n5uPS$LP$:10000:6080cvPS$"NKNOWN: '"MC$"' '"MA$"'":10000mw6080wx ...OM$MA$:P$" :"MA$:600:E$"":1000I1:MA$""1000(MA$,I,2)" :"2050I! + (A192 A219) 6070" 1000"IS$IS$A$3#"";IS$;"";CO$;: 1000N#j:: RETURN IS PRESSEDY#t 6750m#~ IS$"" 1000#IN$IS$:IS$"": 3305#d:: DELETE KEY PRESSED#n (IS$)0 1000#xIS$(IS$,KKK!$#5,"ATZ":9000:9000:5:$%' ** CLEAR BTM 2 LINES, ADJUST LINE LINK TBL, ADJ PS$, ? PS$F%' I1 2: 781,22I:59903:r%$' ::: FOR I=1 TO 7:POKE216+I,132:NEXT I%.'PS$PS$(13)%8' (PS$)255 PS$(PS$,254)(13)%B' CO$"K -1OÐPß5,2,0,(8) Zá#5,A$:A$""A$; dáA$:A$""5,A$;,nÉ 50010dU8:F$"TELNET":1,U,15,"S0:"F$:1:(F$),U:(F$),Ur6sdٮwٮsfyg)ofM9MJ󗩷JLM6k6k6rfMyMپvg)wfvg)6r6r6k)Jf""A$;dáA$:A$""5,A$;nÉ 500108F$"CONFIGURE":1,8,15,"S0:"F$:1:(F$),8:(F$),8kKWjRe\ܽj.Nܥ&R.Nܥ'Rڜ;rKWrܹ;rJzr)J״s\.N+[^ҕꓷ)JRR=\Ir,RԦ{̗=-˓˓)JRkI{W4˓&Iӕ)J-#SY(65532):BC8-#SY226.#X(56834):56834,X1:(56834)X1.$BC15:SY227:56834,X:/.Pß5,2,0,(8)G.Zá#5,A$:A$""A$;^.dáA$:A$""5,A$;j.nÉ 50010.U8:F$"FTP":1,U,15,"S0:"F$:1:(F$),U:(F$),UJzɺ^:Me)J),D:(F$),D*R_mgJ礩JWԥszR}OWRV\ӷ.NIgW\:{)+2e)zzꓵ.Nj+=_v;zRyj)_wꓷ&Iz5^:Yfze)+=dr,RoJ۩w{vuj)J!(IS$)1)# 6750: 6080#^:: SETUP BTM 2 LINESI$h" ";}$r" ?=HELP ";CO$;$| 2023,160: 56295,14:$(#TTTI100$2#ML12:TITT9010$<#a;!dáA$:A$""5,A$;!nÉ 50010)"U8:F$"CBMTERM":1,U,15,"S0:"F$:1:(F$),U:(F$),Uٮsfyg)ofM9MJdJLM6k6k6rfMyMپvg)wfvg)6r6r6k6kٮw٭Jd[JLrٺkg)sfkf6r6oٮwٮrU)JKK";PS$%L'6750:%Pß5,2,0,(8) +&Zá#5,A$:A$""A$;!&dáA$:A$""5,A$;-&nÉ 50010^&U8:F$"IRC":1,U,15,"S0:"F$:1:F$,U:F$,UKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK \ No newline at end of file diff --git a/cbm8bit/src/64Net Apps.cbmprj b/cbm8bit/src/64Net Apps.cbmprj index 9095b2f..b23e9e5 100644 --- a/cbm8bit/src/64Net Apps.cbmprj +++ b/cbm8bit/src/64Net Apps.cbmprj @@ -1,6 +1,6 @@  - + 4 @@ -14,7 +14,12 @@ disk.d64 + 64net apps.prg + False + + False + False C64 CARTRIDGE @@ -46,8 +51,12 @@ x-xfer128.asm x-xfer64.asm - 1 + 0 234 + + + + cbmterm64-128-vic.bas @@ -58,7 +67,18 @@ False - False + True + + + cbmterm-sw64.bas + cbmterm-sw.prg + 1 + True + + + + False + True cometmode64.bas @@ -69,7 +89,7 @@ False - False + True configure64-128-vic.bas @@ -80,7 +100,18 @@ False - False + True + + + configure-sw64.bas + configure-sw.prg + 1 + True + + + + False + True d64wget64-128-vic.bas @@ -93,6 +124,17 @@ False True + + d64wget-sw64.bas + d64wget-sw.prg + 1 + True + + + + False + True + ftp64-128-vic.bas ftp.prg @@ -104,6 +146,17 @@ False True + + ftp-sw64.bas + ftp-sw.prg + 1 + True + + + + False + True + irc64-128-vic.bas irc.prg @@ -112,6 +165,17 @@ + True + True + + + irc-sw64.bas + irc-sw.prg + 1 + True + + + False True @@ -179,7 +243,7 @@ False - False + True telnetd64.bas @@ -190,7 +254,7 @@ False - False + True telnetml.asm @@ -203,6 +267,17 @@ False False + + telnet-sw64.bas + telnet-sw.prg + 1 + True + + + + False + True + up9600.asm up9600.bin.prg @@ -223,7 +298,7 @@ False - False + True wget64-128-vic.bas @@ -233,7 +308,18 @@ - True + False + True + + + wget-sw64.bas + wget-sw.prg + 1 + True + + + + False True diff --git a/cbm8bit/src/cbmterm-sw64.bas b/cbm8bit/src/cbmterm-sw64.bas new file mode 100644 index 0000000..cf7e060 --- /dev/null +++ b/cbm8bit/src/cbmterm-sw64.bas @@ -0,0 +1,217 @@ +!-------------------------------------------------- +!- Tuesday, August 17, 2021 9:44:15 AM +!- Import of : +!- z:\lost+found\cbmterm.prg +!- Commodore 64 +!-------------------------------------------------- +1 REM CBMTERM64SW 19200B 1.8+ +2 REM UPDATED 10/13/2021 12:54A +10 POKE254,PEEK(186):IFPEEK(254)<8THENPOKE254,8 +12 GOSUB9100:IFSY=61THENPOKE58,254:CLR +13 IFSY=34THENX=23777:POKEX,170:IFPEEK(X)<>170THENPRINT"<16k":STOP +14 IFSY=227ANDPEEK(51201)=3THENSYS51200 +15 OPEN5,2,0,CHR$(BC):IFPEEK(65532)=34THENPOKE56,87:POKE54,87:POKE52,87 +17 DIMPP$(25):P$="ok":POKE186,PEEK(254):BA=1200:XB=1200 +20 CR$=CHR$(13):PRINTCHR$(14);:GOSUB9100:POKE53280,254:POKE53281,246 +30 CO$="{light blue}":IFSY=226THENML=49152:POKE665,73-(PEEK(678)*30) +33 IFSY=227THENML=49152 +35 IFSY=34THENML=22273:IFPEEK(ML)<>76THENCLOSE5:LOAD"pmlvic.bin",PEEK(254),1:RUN +38 IFSY=34THENPOKE36879,27:CO$=CHR$(31) +40 IFSY=226ANDPEEK(ML+1)<>209THENCLOSE5:LOAD"pml64.bin",PEEK(254),1:RUN +43 IFSY=227ANDPEEK(ML+1)<>209THENCLOSE5:LOAD"pml64.bin",PEEK(254),1:RUN +45 IFSY=226ANDPEEK(51201)<>161THENCLOSE5:LOAD"x-xfer64.bin",PEEK(254),1:RUN +47 IFSY=227ANDPEEK(51201)<>3THENCLOSE5:LOAD"swiftdrvr.bin",PEEK(254),1:RUN +49 IFSY<>61THEN70 +50 ML=4864:POKE981,15:S8=PEEK(215)AND128:IFS8=128THENSYS30643 +55 POKE981,0:X=PEEK(51201):POKE981,15:IFX=49THEN60 +56 POKE981,0:LOAD"x-xfer128.bin",PEEK(254),1:POKE981,15:RUN +60 IFPEEK(ML+1)<>217THENCLOSE5:LOAD"pml128.bin",PEEK(254),1:RUN +61 IFS8=128THENXB=2400:CO$=CHR$(159) +70 TM=828:IFSY=61THENTM=2816 +80 I=TM:XM=51200:DD=56577:IFSY=34ORSY=227THENXM=0:DD=37136 +90 READA%:IFSY=61AND(A%=155ORA%=156)THENREADB%:POKEI,A%-131:I=I+1:A%=10 +95 IFA%>=0THENPOKEI,A%:I=I+1:GOTO90 +100 REM +101 REM +102 REM +110 P$="a" +120 PRINTCO$;"{clear}{down*2}C= TERM v1.5":PRINT"Requires Zimodem firmware 1.8+" +130 PRINT"19200 baud version" +140 PRINT"By Bo Zimmerman (bo@zimmers.net)":PRINT:PRINT +197 REM -------------------------------- +198 REM GET STARTED ! +199 REM ------------------------------- +200 UN=PEEK(254) +201 PH=0:PT=0:MV=ML+18:CR$=CHR$(13)+CHR$(10):QU$=CHR$(34) +202 PRINT "Initializing modem...";:GOSUB6000 +203 GET#5,A$:IFA$<>""THEN203 +205 PRINT#5,CR$;CR$;"athz0&p0f0e0";CR$; +206 GOSUB900:IFP$<>"ok"THEN203 +208 GET#5,A$:IFA$<>""THEN208 +210 PRINT".";:PRINT#5,CR$;"ate0n0r0v1f0";CR$; +220 GOSUB900:IFP$<>"ok"THEN208 +230 PRINT".";:PRINT#5,"ate0v1x1q0f0";CR$;CHR$(19); +240 GOSUB900:IFP$<>"ok"THENPRINT"Zimodem init failed: ";P$:STOP +250 PRINT"!":DIM HO$(30):DIM PO(30):DIM HM$(30) +251 HO$(0)="cottonwoodbbs.dyndns.org":PO(0)=6502:HZ=1 +252 HO$(1)="borderlinebbs.dyndns.org":PO(1)=6400:HZ=2 +253 HO$(2)="centronian.servebeer.com":PO(2)=6400:HZ=3 +260 OPEN1,UN,15:OPEN8,UN,8,"bbsphonebook,s,r" +270 INPUT#1,E:IFE<>0THENCLOSE8:CLOSE1:GOTO300 +280 INPUT#8,HZ +281 FORI=1TOHZ +282 INPUT#8,HO$(I-1) +285 INPUT#8,PO(I-1) +287 INPUT#8,H$:HM$(I-1)=MID$(H$,2) +289 NEXT +290 CLOSE8:CLOSE1 +300 GOTO 1000 +897 REM -------------------------------- +898 REM GET E$ FROM MODEM, OR ERROR ! +899 REM ------------------------------- +900 E$="" +910 SYSML +920 IFE$<>""ANDP$<>E$THENPRINT"{reverse on}{red}Comm error. Expected ";E$;", Got ";P$;CO$;"{reverse off}" +925 RETURN +997 REM -------------------------------- +998 REM THE MAIN LOOP ! +999 REM ------------------------------- +1000 PRINT:PRINTCO$;"Main Menu:" +1010 PRINT" 1) Dial from Phonebook" +1020 PRINT" 2) Modify Phonebook" +1030 PRINT" 3) Quick Connect" +1035 PRINT" 4) Terminal mode" +1040 PRINT" 9) Quit" +1050 PRINT:PRINT"Enter a number: {reverse on} {reverse off}{left}"; +1060 GOSUB5000:IFP$=""THEN1000 +1070 P=VAL(P$):IFP=9THENCLOSE5:OPEN1,UN,15,"i0":CLOSE1:END +1080 IFP<1ORP>4THEN1050 +1090 PRINT +1100 IFP=1THENGOSUB2000:GOTO1000 +1110 IFP=2THENGOSUB3000:GOTO1000 +1120 IFP=3THENGOSUB4000:GOTO1000 +1124 IFP=4THENPRINT"{reverse on}{light green}Terminal mode. ";:GOSUB2430:GOTO1000 +1130 PRINT"?!":GOTO1000 +2000 PRINT:PRINT"{down}Dial from Phonebook:" +2020 FORI=1TOHZ +2030 PRINT STR$(I)+") ";HO$(I-1);":";MID$(STR$(PO(I-1)),2) +2080 NEXTI:PRINT:PRINT"Enter a number or RETURN: "; +2090 GOSUB5000:IFP$=""THENRETURN +2100 X=VAL(P$):IFX<1ORX>HZTHEN2000 +2110 HO$=HO$(X-1):PO=PO(X-1):HA$=HM$(X-1) +2300 PRINT"{reverse on}{light green}Connecting to ";HO$;":";MID$(STR$(PO),2);"...{reverse off}";CO$ +2310 GET#5,A$:IFA$<>""THEN2310 +2315 AT$="":REMIFXB>0ANDBA>0ANDXB<>BATHENAT$="s43="+MID$(STR$(XB,2) +2320 PRINT#5,CR$;"ath"+AT$+"f3c";HA$;QU$;HO$;":";MID$(STR$(PO),2);QU$;CR$; +2330 GOSUB900:IFLEN(P$)>7ANDLEFT$(P$,7)="connect"THEN2400 +2340 PRINT"{reverse on}{red}Unable to connect to ";HO$;":";MID$(STR$(PO),2) +2350 RETURN +2400 PRINT"{reverse on}{light green}* Connected. "; +2420 PRINT#5,CR$;"atf0o";CR$; +2430 IFSY=226THENPRINT"Hit F1 to exit.{light gray}" +2440 IFSY=61THENPRINT"Hit ESC to exit.{light gray}" +2450 POKE53280,0:POKE53281,0:IFSY=34THENPOKE36879,8 +2453 SYSTM +2457 POKE53280,254:POKE53281,246:IFSY=34THENPOKE36879,27 +2460 IFXM=0OR(PEEK(DD)AND16)=0THEN2500 +2470 PRINT:PRINTCO$;CHR$(14);"{reverse off}U)pload, D)ownload, H)angup, or C)ont: "; +2480 GETA$:IFA$="u"ORA$="U"THEN2600 +2485 IFA$="d"ORA$="D"THEN2700 +2490 IFA$="c"ORA$="C"THEN2430 +2495 IFA$<>"h"ANDA$<>"H"THEN2480 +2496 PRINT"h" +2500 PRINT:PRINTCO$;CHR$(14);"{reverse off}Hanging up...";:GOSUB6000 +2510 PRINT:FORI=1TO100:GET#5,A$:NEXTI +2520 GET#5,A$:IFA$<>""THEN2520 +2530 RETURN +2600 PRINT"u":PRINT"Enter the filename: ";:GOSUB5000:IFP$=""THEN2430 +2610 F$=P$:PRINT"Enter a device (";MID$(STR$(UN),2);"): ";:GOSUB5000 +2620 DV=VAL(P$):IFDV=0THENDV=UN +2630 IFLEN(F$)<3ORRIGHT$(F$,2)<>",p"ANDRIGHT$(F$,2)<>",s"THENF$=F$+",p" +2640 OPEN1,DV,15:OPEN2,DV,2,F$+",r":INPUT#1,E:IFE=0THEN2660 +2650 CLOSE2:CLOSE1:PRINTE,E$,E1,E2:E=0:GOTO2430 +2660 IFSY=61THENPOKE981,0 +2670 SYS51200 +2680 IFSY=61THENPOKE981,15 +2690 CLOSE2:CLOSE1:PRINT:PRINT"Done!":GOTO2430 +2700 PRINT"d":PRINT"Enter new filename: ";:GOSUB5000:IFP$=""THEN2430 +2710 F$=P$:PRINT"Enter a device (";MID$(STR$(UN),2);"): ";:GOSUB5000 +2720 DV=VAL(P$):IFDV=0THENDV=UN +2730 IFLEN(F$)<3ORRIGHT$(F$,2)<>",p"ANDRIGHT$(F$,2)<>",s"THENF$=F$+",p" +2740 OPEN1,DV,15:OPEN2,DV,2,"@0:"+F$+",w":INPUT#1,E:IFE=0THEN2760 +2750 CLOSE2:CLOSE1:PRINTE,E$,E1,E2:E=0:GOTO2430 +2760 IFSY=61THENPOKE981,0 +2770 SYS51209 +2780 IFSY=61THENPOKE981,15 +2790 CLOSE2:CLOSE1:PRINT:PRINT"Done!":GOTO2430 +3000 PRINT:PRINT"{down}Modify Phonebook:" +3020 FORI=1TOHZ +3030 PRINT STR$(I)+") ";HO$(I-1);":";MID$(STR$(PO(I-1)),2):NEXTI +3040 PRINTSTR$(HZ+1)+") Add New Entry" +3080 PRINT:PRINT"Enter a number or RETURN: "; +3090 GOSUB5000:IFP$=""THENRETURN +3100 X=VAL(P$):IFX<1ORX>HZ+1THEN3000 +3110 PRINT"Enter hostname/ip: ";:GOSUB5000:H$=P$:IFP$=""THEN3000 +3120 PRINT"Enter port number (23): ";:GOSUB5000:HP=VAL(P$):IFHP<=0THENHP=23 +3125 PRINT"Local echo (Y/N)? "; +3126 GETA$:IFA$<>"y"ANDA$<>"Y"ANDA$<>"n"ANDA$<>"N"THEN3126 +3127 PRINTA$:HM$(X-1)="":IFA$="y"ORA$="Y"THENHM$(X-1)="e" +3130 HO$(X-1)=H$:PO(X-1)=HP:IFX=HZ+1THENHZ=HZ+1 +3140 OPEN1,UN,15:OPEN8,UN,8,"@0:bbsphonebook,s,w" +3150 INPUT#1,E:IFE<>0THENCLOSE8:CLOSE1:GOTO300 +3160 PRINT#8,HZ:FORI=1TOHZ +3165 PRINT#8,HO$(I-1):PRINT#8,PO(I-1):PRINT#8,"-"+HM$(I-1) +3170 NEXTI:CLOSE8:CLOSE1:GOTO 3000 +4000 PRINT:PRINT"Enter hostname/ip: ";:GOSUB5000:H$=P$:IFP$=""THENRETURN +4010 PRINT"Enter port number (23): ";:GOSUB5000:HP=VAL(P$):IFHP<=0THENHP=23 +4020 PRINT"Local echo (Y/N)? "; +4030 GETA$:IFA$<>"y"ANDA$<>"Y"ANDA$<>"n"ANDA$<>"N"THEN4030 +4040 PRINTA$:HA$="":IFA$="y"ORA$="Y"THENHA$="e" +4050 HO$=H$:PO=HP:GOTO2300 +4999 STOP +5000 P$="" +5005 PRINT"{reverse on} {reverse off}{left}"; +5010 GETA$:IFA$=""THEN5010 +5020 A=ASC(A$):IFA=13THENPRINT" {left}":RETURN +5025 IFA=34THENPRINTA$;A$;"{left}{reverse on} {reverse off}{left}";:P$=P$+A$:GOTO5010 +5030 IFA<>20THENPRINTA$;"{reverse on} {reverse off}{left}";:P$=P$+A$:GOTO5010 +5040 IFP$=""THEN5010 +5050 P$=LEFT$(P$,LEN(P$)-1):PRINT" {left*2} {left}{reverse on} {reverse off}{left}";:GOTO5010 +6000 IF(PEEK(DD)AND16)=0THENRETURN +6010 GOSUB6100:PRINT#5,"+++"; +6020 PRINT".";:GOSUB6100 +6030 PRINT#5,"ath":TT=TI+150 +6040 SYSML+12:IFTI226THEN9140 +9120 X=PEEK(56834):POKE56834,X+1:IFPEEK(56834)<>X+1THEN9140 +9130 BC=15:SY=227:POKE56834,X:RETURN +9140 PRINTCHR$(14);"Requires a C64 with SwiftLink/Link232":STOP +39999 STOP +40000 OPEN1,8,15:OPEN8,8,8,"telnetml.bin,p,r":LN=41000:DN=0 +40010 INPUT#1,E:IFE<>0THENCLOSE8:CLOSE1:STOP +40020 GET#8,A$:IFST>0THENCLOSE8:END +40030 IFA$=""THENA$=CHR$(0) +40035 A=ASC(A$):A$=MID$(STR$(A),2) +40040 IFDN=0THENPRINTMID$(STR$(LN),2);" data ";A$;:DN=DN+1:GOTO40020 +40060 PRINT",";A$;:DN=DN+1 +40070 IFDN=18THENPRINT:DN=0:LN=LN+10 +40080 GOTO 40020 +41000 DATA 162,5,32,198,255,32,228,255,201,0,240,61,201,10,240,57 +41010 DATA 32,210,255,173,155,2,205,156,2,240,38,176,13,169,255,56,237,156 +41020 DATA 2,24,109,155,2,56,176,4,56,237,156,2,201,200,144,11,173,1 +41030 DATA 221,41,253,141,1,221,56,176,12,201,20,176,8,173,1,221,9,2 +41040 DATA 141,1,221,162,0,32,198,255,32,228,255,201,0,240,171,201,133,240 +41050 DATA 4,201,27,208,4,32,204,255,96,72,162,5,32,201,255,104,32,210 +41060 DATA 255,162,0,32,201,255,56,176,141 +41999 DATA -1 +49999 STOP +50000 OPEN5,2,0,CHR$(8) +50010 GET#5,A$:IFA$<>""THENPRINTA$; +50020 GETA$:IFA$<>""THENPRINT#5,A$; +50030 GOTO 50010 +55555 U=8:F$="cbmterm":OPEN1,U,15,"s0:"+F$:CLOSE1:SAVE(F$),U:VERIFY(F$),U diff --git a/cbm8bit/src/cbmterm64-128-vic-p4.bas b/cbm8bit/src/cbmterm64-128-vic-p4.bas new file mode 100644 index 0000000..0cb27f5 --- /dev/null +++ b/cbm8bit/src/cbmterm64-128-vic-p4.bas @@ -0,0 +1,216 @@ +!-------------------------------------------------- +!- Monday, October 01, 2018 10:29:24 PM +!- Import of : +!- c:\tmp\cbmterm.prg +!- Unexpanded VIC20 / C16 / Plus4 +!-------------------------------------------------- +1 REM CBMTERM64/128 1200B 1.8+ +2 REM UPDATED 08/15/2017 12:54A +10 SY=PEEK(65532):IFSY=61THENPOKE58,254:CLR +11 POKE253,186:IFSY=246THENPOKE253,174:GRAPHIC1:GRAPHIC0 +12 POKE254,PEEK(PEEK(253)):IFPEEK(254)<8THENPOKE254,8 +13 IFSY=34THENX=23777:POKEX,170:IFPEEK(X)<>170THENPRINT"<16k":STOP +15 IFSY=246THENCLR:OPEN5,2,0,CHR$(24)+CHR$(5):COLOR0,2,7:COLOR4,15,6:GOTO17 +16 OPEN5,2,0,CHR$(8):IFPEEK(65532)=34THENPOKE56,87:POKE54,87:POKE52,87 +17 DIMPP$(25):P$="ok":POKE186,PEEK(PEEK(253)):BA=1200:XB=1200 +20 CR$=CHR$(13):PRINTCHR$(14);:SY=PEEK(65532):POKE53280,254:POKE53281,246 +30 CO$="{light blue}":IFSY=226THENML=49152:POKE665,73-(PEEK(678)*30) +33 IFSY=246THENML=4096:IFPEEK(ML)<>76THENCLOSE5:LOAD"pml+4.bin",PEEK(254),1:RUN +35 IFSY=34THENML=22273:IFPEEK(ML)<>76THENCLOSE5:LOAD"pmlvic.bin",PEEK(254),1:RUN +38 IFSY=34THENPOKE36879,27:CO$=CHR$(31) +40 IFSY=226ANDPEEK(ML+1)<>209THENCLOSE5:LOAD"pml64.bin",PEEK(254),1:RUN +45 IFSY=226ANDPEEK(51201)<>161THENCLOSE5:LOAD"x-xfer64.bin",PEEK(254),1:RUN +49 IFSY<>61THEN70 +50 ML=4864:POKE981,15:S8=PEEK(215)AND128:IFS8=128THENSYS30643 +55 POKE981,0:X=PEEK(51201):POKE981,15:IFX=49THEN60 +56 POKE981,0:LOAD"x-xfer128.bin",PEEK(254),1:POKE981,15:RUN +60 IFPEEK(ML+1)<>217THENCLOSE5:LOAD"pml128.bin",PEEK(254),1:RUN +61 IFS8=128THENXB=2400:CO$=CHR$(159) +70 TM=828:IFSY=61THENTM=2816 +71 IFSY=246THENTM=7936 +80 I=TM:XM=51200:DD=56577:IFSY=34THENXM=0:DD=37136 +81 IFSY=246THENDD=64874:XM=0 +90 READA%:IFSY=61AND(A%=155ORA%=156)THENREADB%:POKEI,A%-131:I=I+1:A%=10 +95 IFA%>=0THENPOKEI,A%:I=I+1:GOTO90 +100 REM +101 REM +102 REM +110 P$="a" +120 PRINTCO$;"{clear}{down*2}C= TERM v1.5":PRINT"Requires C64Net WiFi firmware 1.8+" +130 PRINT"1200 baud version" +140 PRINT"By Bo Zimmerman (bo@zimmers.net)":PRINT:PRINT +197 REM -------------------------------- +198 REM GET STARTED ! +199 REM ------------------------------- +200 UN=PEEK(254) +201 PH=0:PT=0:MV=ML+18:CR$=CHR$(13)+CHR$(10):QU$=CHR$(34) +202 PRINT "Initializing modem...";:GOSUB6000 +203 GET#5,A$:IFA$<>""THEN203 +205 PRINT#5,CR$;CR$;"athz0f0e0";CR$; +206 GOSUB900:IFP$<>"ok"THEN203 +208 GET#5,A$:IFA$<>""THEN208 +210 PRINT".";:PRINT#5,CR$;"ate0n0r0v1f0";CR$; +220 GOSUB900:IFP$<>"ok"THEN208 +230 PRINT".";:PRINT#5,"ate0v1x1q0f0";CR$;CHR$(19); +240 GOSUB900:IFP$<>"ok"THENPRINT"Zimodem init failed: ";P$:STOP +250 PRINT"!":DIM HO$(30):DIM PO(30):DIM HM$(30) +251 HO$(0)="cottonwoodbbs.dyndns.org":PO(0)=6502:HZ=1 +252 HO$(1)="borderlinebbs.dyndns.org":PO(1)=6400:HZ=2 +253 HO$(2)="centronian.servebeer.com":PO(2)=6400:HZ=3 +260 OPEN1,UN,15:OPEN8,UN,8,"bbsphonebook,s,r" +270 INPUT#1,E:IFE<>0THENCLOSE8:CLOSE1:GOTO300 +280 INPUT#8,HZ +281 FORI=1TOHZ +282 INPUT#8,HO$(I-1) +285 INPUT#8,PO(I-1) +287 INPUT#8,H$:HM$(I-1)=MID$(H$,2) +289 NEXT +290 CLOSE8:CLOSE1 +300 GOTO 1000 +897 REM -------------------------------- +898 REM GET E$ FROM MODEM, OR ERROR ! +899 REM ------------------------------- +900 E$="" +910 SYSML +920 IFE$<>""ANDP$<>E$THENPRINT"{reverse on}{red}Comm error. Expected ";E$;", Got ";P$;CO$;"{reverse off}" +925 RETURN +997 REM -------------------------------- +998 REM THE MAIN LOOP ! +999 REM ------------------------------- +1000 PRINT:PRINTCO$;"Main Menu:" +1010 PRINT" 1) Dial from Phonebook" +1020 PRINT" 2) Modify Phonebook" +1030 PRINT" 3) Quick Connect" +1035 PRINT" 4) Terminal mode" +1040 PRINT" 9) Quit" +1050 PRINT:PRINT"Enter a number: {reverse on} {reverse off}{left}"; +1060 GOSUB5000:IFP$=""THEN1000 +1070 P=VAL(P$):IFP=9THENCLOSE5:OPEN1,UN,15,"i0":CLOSE1:END +1080 IFP<1ORP>4THEN1050 +1090 PRINT +1100 IFP=1THENGOSUB2000:GOTO1000 +1110 IFP=2THENGOSUB3000:GOTO1000 +1120 IFP=3THENGOSUB4000:GOTO1000 +1124 IFP=4THENPRINT"{reverse on}{light green}Terminal mode. ";:GOSUB2430:GOTO1000 +1130 PRINT"?!":GOTO1000 +2000 PRINT:PRINT"{down}Dial from Phonebook:" +2020 FORI=1TOHZ +2030 PRINT STR$(I)+") ";HO$(I-1);":";MID$(STR$(PO(I-1)),2) +2080 NEXTI:PRINT:PRINT"Enter a number or RETURN: "; +2090 GOSUB5000:IFP$=""THENRETURN +2100 X=VAL(P$):IFX<1ORX>HZTHEN2000 +2110 HO$=HO$(X-1):PO=PO(X-1):HA$=HM$(X-1) +2300 PRINT"{reverse on}{light green}Connecting to ";HO$;":";MID$(STR$(PO),2);"...{reverse off}";CO$ +2310 GET#5,A$:IFA$<>""THEN2310 +2315 AT$="":REMIFXB>0ANDBA>0ANDXB<>BATHENAT$="s43="+MID$(STR$(XB,2) +2320 PRINT#5,CR$;"ath"+AT$+"f3c";HA$;QU$;HO$;":";MID$(STR$(PO),2);QU$;CR$; +2330 GOSUB900:IFLEN(P$)>7ANDLEFT$(P$,7)="connect"THEN2400 +2340 PRINT"{reverse on}{red}Unable to connect to ";HO$;":";MID$(STR$(PO),2) +2350 RETURN +2400 PRINT"{reverse on}{light green}* Connected. "; +2420 PRINT#5,CR$;"atf0o";CR$; +2430 IFSY=226THENPRINT"Hit F1 to exit.{light gray}" +2440 IFSY=61THENPRINT"Hit ESC to exit.{light gray}" +2450 POKE53280,0:POKE53281,0:IFSY=34THENPOKE36879,8 +2451 IFSY=246THENCOLOR0,1,0:COLOR4,1,0 +2453 SYSTM +2457 POKE53280,254:POKE53281,246:IFSY=34THENPOKE36879,27 +2458 IFSY=246THENCOLOR0,2,7:COLOR4,15,6 +2460 IFXM=0OR(PEEK(DD)AND16)=0THEN2500 +2470 PRINT:PRINTCO$;CHR$(14);"{reverse off}U)pload, D)ownload, H)angup, or C)ont: "; +2480 GETA$:IFA$="u"ORA$="U"THEN2600 +2485 IFA$="d"ORA$="D"THEN2700 +2490 IFA$="c"ORA$="C"THEN2430 +2495 IFA$<>"h"ANDA$<>"H"THEN2480 +2496 PRINT"h" +2500 PRINT:PRINTCO$;CHR$(14);"{reverse off}Hanging up...";:GOSUB6000 +2510 PRINT:FORI=1TO100:GET#5,A$:NEXTI +2520 GET#5,A$:IFA$<>""THEN2520 +2530 RETURN +2600 PRINT"u":PRINT"Enter the filename: ";:GOSUB5000:IFP$=""THEN2430 +2610 F$=P$:PRINT"Enter a device (";MID$(STR$(UN),2);"): ";:GOSUB5000 +2620 DV=VAL(P$):IFDV=0THENDV=UN +2630 IFLEN(F$)<3ORRIGHT$(F$,2)<>",p"ANDRIGHT$(F$,2)<>",s"THENF$=F$+",p" +2640 OPEN1,DV,15:OPEN2,DV,2,F$+",r":INPUT#1,E:IFE=0THEN2660 +2650 CLOSE2:CLOSE1:PRINTE,E$,E1,E2:E=0:GOTO2430 +2660 IFSY=61THENPOKE981,0 +2670 SYS51200 +2680 IFSY=61THENPOKE981,15 +2690 CLOSE2:CLOSE1:PRINT:PRINT"Done!":GOTO2430 +2700 PRINT"d":PRINT"Enter new filename: ";:GOSUB5000:IFP$=""THEN2430 +2710 F$=P$:PRINT"Enter a device (";MID$(STR$(UN),2);"): ";:GOSUB5000 +2720 DV=VAL(P$):IFDV=0THENDV=UN +2730 IFLEN(F$)<3ORRIGHT$(F$,2)<>",p"ANDRIGHT$(F$,2)<>",s"THENF$=F$+",p" +2740 OPEN1,DV,15:OPEN2,DV,2,"@0:"+F$+",w":INPUT#1,E:IFE=0THEN2760 +2750 CLOSE2:CLOSE1:PRINTE,E$,E1,E2:E=0:GOTO2430 +2760 IFSY=61THENPOKE981,0 +2770 SYS51209 +2780 IFSY=61THENPOKE981,15 +2790 CLOSE2:CLOSE1:PRINT:PRINT"Done!":GOTO2430 +3000 PRINT:PRINT"{down}Modify Phonebook:" +3020 FORI=1TOHZ +3030 PRINT STR$(I)+") ";HO$(I-1);":";MID$(STR$(PO(I-1)),2):NEXTI +3040 PRINTSTR$(HZ+1)+") Add New Entry" +3080 PRINT:PRINT"Enter a number or RETURN: "; +3090 GOSUB5000:IFP$=""THENRETURN +3100 X=VAL(P$):IFX<1ORX>HZ+1THEN3000 +3110 PRINT"Enter hostname/ip: ";:GOSUB5000:H$=P$:IFP$=""THEN3000 +3120 PRINT"Enter port number (23): ";:GOSUB5000:HP=VAL(P$):IFHP<=0THENHP=23 +3125 PRINT"Local echo (Y/N)? "; +3126 GETA$:IFA$<>"y"ANDA$<>"Y"ANDA$<>"n"ANDA$<>"N"THEN3126 +3127 PRINTA$:HM$(X-1)="":IFA$="y"ORA$="Y"THENHM$(X-1)="e" +3130 HO$(X-1)=H$:PO(X-1)=HP:IFX=HZ+1THENHZ=HZ+1 +3140 OPEN1,UN,15:OPEN8,UN,8,"@0:bbsphonebook,s,w" +3150 INPUT#1,E:IFE<>0THENCLOSE8:CLOSE1:GOTO300 +3160 PRINT#8,HZ:FORI=1TOHZ +3165 PRINT#8,HO$(I-1):PRINT#8,PO(I-1):PRINT#8,"-"+HM$(I-1) +3170 NEXTI:CLOSE8:CLOSE1:GOTO 3000 +4000 PRINT:PRINT"Enter hostname/ip: ";:GOSUB5000:H$=P$:IFP$=""THENRETURN +4010 PRINT"Enter port number (23): ";:GOSUB5000:HP=VAL(P$):IFHP<=0THENHP=23 +4020 PRINT"Local echo (Y/N)? "; +4030 GETA$:IFA$<>"y"ANDA$<>"Y"ANDA$<>"n"ANDA$<>"N"THEN4030 +4040 PRINTA$:HA$="":IFA$="y"ORA$="Y"THENHA$="e" +4050 HO$=H$:PO=HP:GOTO2300 +4999 STOP +5000 P$="" +5005 PRINT"{reverse on} {reverse off}{left}"; +5010 GETA$:IFA$=""THEN5010 +5020 A=ASC(A$):IFA=13THENPRINT" {left}":RETURN +5025 IFA=34THENPRINTA$;A$;"{left}{reverse on} {reverse off}{left}";:P$=P$+A$:GOTO5010 +5030 IFA<>20THENPRINTA$;"{reverse on} {reverse off}{left}";:P$=P$+A$:GOTO5010 +5040 IFP$=""THEN5010 +5050 P$=LEFT$(P$,LEN(P$)-1):PRINT" {left*2} {left}{reverse on} {reverse off}{left}";:GOTO5010 +6000 IF(PEEK(DD)AND16)=0THENRETURN +6010 GOSUB6100:PRINT#5,"+++"; +6020 PRINT".";:GOSUB6100 +6030 PRINT#5,"ath":TT=TI+150 +6040 SYSML+12:IFTI0THENCLOSE8:CLOSE1:STOP +40020 GET#8,A$:IFST>0THENCLOSE8:END +40030 IFA$=""THENA$=CHR$(0) +40035 A=ASC(A$):A$=MID$(STR$(A),2) +40040 IFDN=0THENPRINTMID$(STR$(LN),2);" data ";A$;:DN=DN+1:GOTO40020 +40060 PRINT",";A$;:DN=DN+1 +40070 IFDN=18THENPRINT:DN=0:LN=LN+10 +40080 GOTO 40020 +41000 DATA 162,5,32,198,255,32,228,255,201,0,240,61,201,10,240,57 +41010 DATA 32,210,255,173,155,2,205,156,2,240,38,176,13,169,255,56,237,156 +41020 DATA 2,24,109,155,2,56,176,4,56,237,156,2,201,200,144,11,173,1 +41030 DATA 221,41,253,141,1,221,56,176,12,201,20,176,8,173,1,221,9,2 +41040 DATA 141,1,221,162,0,32,198,255,32,228,255,201,0,240,171,201,133,240 +41050 DATA 4,201,27,208,4,32,204,255,96,72,162,5,32,201,255,104,32,210 +41060 DATA 255,162,0,32,201,255,56,176,141 +41999 DATA -1 +49999 STOP +50000 REMOPEN5,2,0,CHR$(8) +50001 CLR:OPEN5,2,0,CHR$(8+16)+CHR$(5):P$="":ML=4096:SYSML+12 +50010 GET#5,A$:IFA$<>""THENPRINTA$; +50020 GETA$:IFA$<>""THENPRINT#5,A$;:IFA$="{pound}"THENPRINTPEEK(DEC("0314")) +50030 GOTO 50010 +55555 U=8:F$="cbmterm":OPEN1,U,15,"s0:"+F$:CLOSE1:SAVE(F$),U:VERIFY(F$),U diff --git a/cbm8bit/src/cbmterm64-128-vic.bas b/cbm8bit/src/cbmterm64-128-vic.bas index dcba710..32de0ef 100644 --- a/cbm8bit/src/cbmterm64-128-vic.bas +++ b/cbm8bit/src/cbmterm64-128-vic.bas @@ -5,7 +5,7 @@ !- Commodore 64 !-------------------------------------------------- 1 REM CBMTERM64/128 1200B 1.8+ -2 REM UPDATED 08/15/2017 12:54A +2 REM UPDATED 10/13/2021 12:54A 10 POKE254,PEEK(186):IFPEEK(254)<8THENPOKE254,8 12 SY=PEEK(65532):IFSY=61THENPOKE58,254:CLR 13 IFSY=34THENX=23777:POKEX,170:IFPEEK(X)<>170THENPRINT"<16k":STOP @@ -41,7 +41,7 @@ 201 PH=0:PT=0:MV=ML+18:CR$=CHR$(13)+CHR$(10):QU$=CHR$(34) 202 PRINT "Initializing modem...";:GOSUB6000 203 GET#5,A$:IFA$<>""THEN203 -205 PRINT#5,CR$;CR$;"athz0f0e0";CR$; +205 PRINT#5,CR$;CR$;"athz0&p0f0e0";CR$; 206 GOSUB900:IFP$<>"ok"THEN203 208 GET#5,A$:IFA$<>""THEN208 210 PRINT".";:PRINT#5,CR$;"ate0n0r0v1f0";CR$; diff --git a/cbm8bit/src/cometmode64.bas b/cbm8bit/src/cometmode64.bas index 44b832c..a4b0887 100644 --- a/cbm8bit/src/cometmode64.bas +++ b/cbm8bit/src/cometmode64.bas @@ -1,12 +1,12 @@ !-------------------------------------------------- -!- Saturday, June 03, 2017 9:07:31 PM +!- Thursday, April 26, 2018 9:07:31 PM !- Import of : !- c:\src\zimodem\cbm8bit\src\cometmode.prg !- Commodore 64 !-------------------------------------------------- 10 IFPEEK(49153)<>54THENLOAD"v-1541",8,1:RUN 20 OPEN5,2,0,CHR$(8):TI$="000000":T1=0 -30 PRINT#5,"at":TT=TI+100:GOSUB900:PRINT"connecting..."; +30 PRINT#5,"at&p0r0":TT=TI+100:GOSUB900:PRINT"connecting..."; 40 GOSUB920:PRINT".";:T1=T1+1:IFT1>20THENPRINT"fail.":CLOSE5:END 50 PRINT#5,"at":TT=TI+100:GOSUB900 60 INPUT#5,A$:IFLEFT$(A$,2)="at"ORLEFT$(A$,2)="AT"THENINPUT#5,A$ diff --git a/cbm8bit/src/configure-sw64.bas b/cbm8bit/src/configure-sw64.bas new file mode 100644 index 0000000..786cc68 --- /dev/null +++ b/cbm8bit/src/configure-sw64.bas @@ -0,0 +1,179 @@ +!-------------------------------------------------- +!- Tuesday, August 17, 2021 9:44:38 AM +!- Import of : +!- z:\lost+found\configure.prg +!- Commodore 64 +!-------------------------------------------------- +1 REM CONFIGURE64SW 19200B 2.0+ +2 REM UPDATED 08/15/2021 12:54P +5 POKE254,PEEK(186):IFPEEK(254)<8THENPOKE254,8 +10 GOSUB9100:IFSY=61THENPOKE58,254:CLR +13 IFSY=34THENX=23777:POKEX,170:IFPEEK(X)<>170THENPRINT"<16k":STOP +14 IFSY=227ANDPEEK(51201)=3THENSYS51200 +15 OPEN5,2,0,CHR$(BC):IFPEEK(65532)=34THENPOKE56,87:POKE54,87:POKE52,87 +17 DIMWF$(100):WF=0:P$="ok":DIMPB$(50):POKE186,PEEK(254) +20 CR$=CHR$(13):PRINTCHR$(14);:GOSUB9100:POKE53280,254:POKE53281,246 +30 CO$="{light blue}":IFSY=226ORSY=227THENML=49152:POKE665,73-(PEEK(678)*30) +35 IFSY=34THENML=22273:IFPEEK(ML)<>76THENCLOSE5:LOAD"pmlvic.bin",PEEK(254),1:RUN +38 IFSY=34THENPOKE36879,27:CO$=CHR$(31) +39 IFSY=226ANDPEEK(ML+1)<>209THENCLOSE5:LOAD"pml64.bin",PEEK(254),1:RUN +40 IFSY=227ANDPEEK(ML+1)<>209THENCLOSE5:LOAD"pml64.bin",PEEK(254),1:RUN +45 IFSY=227ANDPEEK(51201)<>3THENCLOSE5:LOAD"swiftdrvr.bin",PEEK(254),1:RUN +50 IFSY=61THENML=4864:POKE981,15:P=PEEK(215)AND128:IFP=128THENSYS30643 +60 IFSY=61ANDPEEK(ML+1)<>217THENCLOSE5:LOAD"pml128.bin",PEEK(254),1:RUN +70 IFSY=61THENCO$=CHR$(159) +100 REM +110 P$="a" +120 PRINTCO$;"{clear}{down*2}CONFIGURE v1.6":PRINT"Requires Zimodem Firmware 2.0+" +130 PRINT"19200 baud version" +140 PRINT"By Bo Zimmerman (bo@zimmers.net)":PRINT:PRINT +197 REM -------------------------------- +198 REM GET STARTED ! +199 REM ------------------------------- +200 PH=0:PT=0:MV=ML+18:CR$=CHR$(13)+CHR$(10):QU$=CHR$(34):POKEMV+14,5 +202 PRINT "Initializing modem...";:CT=0 +203 GET#5,A$:IFA$<>""THEN203 +205 PRINT#5,CR$;"athz0f0r0e0&p1";CR$; +206 GOSUB900:IFP$<>"OK"THENCT=CT+1:IFCT<10THEN203 +207 IFCT<10THENGET#5,A$:IFA$<>""THEN207 +208 IFCT<10THENPRINT#5,"athf3e0&p1";CR$; +209 IFCT<10THENGOSUB900:IFP$<>"OK"THENCT=CT+1:GOTO207 +210 IFCT=10THENPRINT"Initialization failed.":PRINT"Is your modem set to 1200b?" +220 IFCT=10THENSTOP +300 PRINT:PRINT:GOTO 1000 +897 REM -------------------------------- +898 REM GET E$ FROM MODEM, OR ERROR ! +899 REM ------------------------------- +900 E$="":P$="" +910 SYSML +920 IFE$<>""ANDP$<>E$THENPRINT"{reverse on}{red}Comm error. Expected ";E$;", Got ";P$;CO$;"{reverse off}" +925 RETURN +997 REM -------------------------------- +998 REM THE MAIN LOOP ! +999 REM ------------------------------- +1000 CT=0:C1=0:CD=0:LC$="1":GOSUB1010:WI$=P$:GOSUB1010:IFP$=WI$THEN1030 +1005 GOTO1000 +1010 SYSML+12:PRINT#5,CR$;"ati3";CR$; +1020 GOSUB900:IFP$<>LC$THEN1025 +1021 CT=CT+1:IFCT>3THENRETURN +1023 C1=0:GOTO1010 +1025 LC$=P$:CT=0:C1=C1+1:IFC1>15THENP$="":RETURN +1027 GOTO1010 +1029 RETURN +1030 WI$=P$:PRINT"Current WiFi SSI: ";WI$ +1040 CD=0:IFWI$=""THEN2000 +1045 GOSUB 1050:IFCD=0THEN2000 +1047 GOTO4000 +1050 PRINT:PRINT"Testing connection...";:TT=TI+200 +1055 IFTI""THEN1060 +1070 SYSML+12:PRINT#5,CR$;"atc";QU$;"132.148.138.66:80";QU$;CR$; +1080 GOSUB900:CD=1:IFP$="OK"THEN1080 +1085 IFLEFT$(P$,8)<>"CONNECT "THENCD=0:CT=CT+1:ONCTGOTO1070,1070,1070,1070,1200 +1090 FORI=1TO3:SYSML+12:PRINT#5,CR$;"ath0";CR$;:GOSUB900:NEXTI +1100 PRINT#5,CR$;"ath0";CR$;:GOSUB900 +1200 IFCD=0THENPRINT"{reverse on}{red}Fail!{reverse off}";CO$ +1210 IFCD=1THENPRINT"{reverse on}{light green}Success!{reverse off}";CO$ +1220 RETURN +2000 SYSML+12:SYSML+12:PRINT:PRINT"Scanning for WiFi hotspots..."; +2010 PRINT#5,CR$;"atw";CR$; +2020 GOSUB900:I=1:IFP$=""ORP$="OK"THENPRINT"Done!":PRINT:PRINT:GOTO3000 +2030 IFI>=LEN(P$)THEN2100 +2040 IFMID$(P$,I,1)=" "THENP$=LEFT$(P$,I-1):GOTO2100 +2050 I=I+1:GOTO2030 +2100 WF$(WF+1)=P$:WF=WF+1:GOTO2020 +3000 PG=1 +3100 LP=WF:IFLP>PG+15THENLP=PG+15 +3110 FORI=PGTOLP:PRINTSTR$(I)+") ";WF$(I):NEXTI +3120 PRINT:PRINT"Enter X to Quit to BASIC." +3130 IFLP15THENPRINT"Enter P for Prev Page" +3150 PRINT"Enter a number to Connect to that SSI" +3200 PRINT"? ";:GOSUB5000:A$=P$:IFA$=""THEN3100 +3210 IFA$="x"ORA$="X"THENCLOSE5:END +3220 IFA$="n"ORA$="N"THENIFLP15THENPG=PG-15:GOTO3100 +3240 A=ASC(MID$(A$,1,1)):IFA<48ORA>57THEN3100 +3250 A=VAL(A$):IFAPG+15 OR A>WFTHEN3100 +3260 WI=A:PRINT"Attempt to connect to: ";WF$(A) +3300 PRINT"Enter WiFi Password: ";:GOSUB5000:PA$=P$ +3400 SYSML+12:PRINT#5,CR$;"atw";QU$;WF$(WI);",";PA$;QU$;CR$;:GOSUB900 +3410 IFP$="ERROR"THENPRINT"{reverse on}{red}Connect Fail!{reverse off}";CO$:GOTO3100 +3415 IFP$="OK"THEN3420 +3416 IFP$<>""THEN3400 +3417 GOSUB900:GOTO3410 +3420 PRINT"{reverse on}{light green}Connect success!{reverse off}";CO$ +3430 PRINT +3431 TT=TI+300 +3432 IFTI"OK"ANDP$<>"ok"THEN3470 +3490 P$="":SYSML+12:IFP$<>""THEN3490 +3500 SYSML+12:CLOSE5:RUN +4000 PRINT"Change WiFi connection (y/N)? "; +4010 GETA$:IFA$="y"ORA$="Y"THENPRINT"Y":GOTO2000 +4020 IFA$<>"n"ANDA$<>"N"ANDA$<>CHR$(13)THEN4010 +4030 PRINT"N":PRINT +4100 PRINT"Current 'Phone' book:":PB=0 +4110 PRINT#5,CR$;"atp";CR$ +4120 TT=TI+100 +4130 GOSUB900:IFP$=""ANDTI>TTTHEN4200 +4140 IFP$=""THEN4130 +4150 I=1:PP$=P$:IFP$="ok"ORP$="OK"THEN4200 +4160 IFMID$(PP$,I,1)<>" "THENI=I+1:GOTO4160 +4170 PP$=LEFT$(PP$,I-1) +4190 PB$(PB)=PP$:PB=PB+1:PRINTPB;") ";P$:GOTO4120 +4200 PRINT" A ) Add new":PRINT" Q ) Quit to BASIC" +4210 PRINT:PRINT"Enter your choice: ";:GOSUB5000:Q$=P$ +4220 QQ$=LEFT$(Q$,1):IFQQ$="q"ORQQ$="Q"THENCLOSE5:END +4230 QQ=VAL(Q$):IF(QQ<0ORQQ>PB)ANDQQ$<>"a"ANDQQ$<>"A"THENPRINT:GOTO4100 +4240 IFQQ<=0THEN4300 +4250 PRINT"E)dit or D)elete? "; +4260 GETA$:IFA$="e"ORA$="E"THENPRINT"E":B1$=PB$(QQ-1):GOTO4310 +4270 IFA$<>"d"ANDA$<>"D"THEN4260 +4280 PRINT"D" +4290 PRINT#5,"atp";CHR$(34);PB$(QQ-1);"=delete";CHR$(34):GOSUB900:GOTO4100 +4300 PRINT"Phone number: ";:GOSUB5000:B1$=P$ +4305 IFLEN(B1$)<3THENPRINT"Wrong.":GOTO4100 +4310 FORI=1TOLEN(B1$):B1=ASC(MID$(B1$,I,1)):IFB1<48ORB1>57THENB1$="" +4320 NEXTI:IFB1$=""THENPRINT"Bad digits.":GOTO4100 +4330 PRINT"Target host: ";:GOSUB5000:B2$=P$ +4335 IFLEN(B2$)<4THENPRINT"Wrong.":GOTO4100 +4340 PRINT"Target port: ";:GOSUB5000:B2=VAL(P$) +4345 IFB2<=0THENPRINT"Wrong.":GOTO4100 +4350 B3$="":PRINT"Do PETSCII translation (y/n)? "; +4360 GOSUB4980:IFA=1THENB3$=B3$+"p" +4370 PRINT"Do TELNET translation (y/n)? "; +4380 GOSUB4980:IFA=1THENB3$=B3$+"t" +4390 PRINT"Do terminal Echo (y/n)? "; +4400 GOSUB4980:IFA=1THENB3$=B3$+"e" +4410 PRINT"Do XON/XOFF Flow Control (y/n)? "; +4420 GOSUB4980:IFA=1THENB3$=B3$+"x" +4430 B4$=MID$(STR$(B2),2) +4440 PRINT#5,"atp";B3$;CHR$(34);B1$;"=";B2$;":";B4$;CHR$(34):GOSUB900 +4450 GOTO4100 +4499 STOP +4980 GETA$:IFA$<>"y"ANDA$<>"Y"ANDA$<>"n"ANDA$<>"N"THEN4980 +4990 A=0:AA$="N":IFA$="y"ORA$="Y"THENAA$="Y":A=1 +4995 PRINTAA$:RETURN +5000 P$="" +5005 PRINT"{reverse on} {reverse off}{left}"; +5010 GETA$:IFA$=""THEN5010 +5020 IFA$=CHR$(13)THENPRINT" {left}":RETURN +5030 IFA$<>CHR$(20)THENPRINTA$;"{reverse on} {reverse off}{left}";:P$=P$+A$:GOTO5010 +5040 IFP$=""THEN5010 +5050 P$=LEFT$(P$,LEN(P$)-1):PRINT" {left*2} {left}{reverse on} {reverse off}{left}";:GOTO5010 +9100 SY=PEEK(65532):BC=8 +9110 IFSY<>226THEN9140 +9120 X=PEEK(56834):POKE56834,X+1:IFPEEK(56834)<>X+1THEN9140 +9130 BC=15:SY=227:POKE56834,X:RETURN +9140 PRINTCHR$(14);"Requires a C64 with SwiftLink/Link232":STOP +49999 STOP +50000 OPEN5,2,0,CHR$(8) +50010 GET#5,A$:IFA$<>""THENPRINTA$; +50020 GETA$:IFA$<>""THENPRINT#5,A$; +50030 GOTO 50010 +55555 F$="configure":OPEN1,8,15,"s0:"+F$:CLOSE1:SAVE(F$),8:VERIFY(F$),8 diff --git a/cbm8bit/src/configure64-128-vic-p4.bas b/cbm8bit/src/configure64-128-vic-p4.bas new file mode 100644 index 0000000..ae41755 --- /dev/null +++ b/cbm8bit/src/configure64-128-vic-p4.bas @@ -0,0 +1,175 @@ +!-------------------------------------------------- +!- Monday, October 01, 2018 10:30:35 PM +!- Import of : +!- c:\tmp\configure.prg +!- Commodore 64 +!-------------------------------------------------- +1 REM CONFIGURE64/128 1200B 2.0+ +2 REM UPDATED 09/01/2018 02:54P +5 SY=PEEK(65532):IFSY=61THENPOKE58,254:CLR +6 POKE253,186:IFSY=246THENPOKE253,174:GRAPHIC1:GRAPHIC0 +10 POKE254,PEEK(PEEK(253)):IFPEEK(254)<8ORPEEK(254)>16THENPOKE254,8 +13 IFSY=34THENX=23777:POKEX,170:IFPEEK(X)<>170THENPRINT"<16k":STOP +14 IFSY=246THENCLR:OPEN5,2,0,CHR$(24)+CHR$(5):GOTO17 +15 OPEN5,2,0,CHR$(8):CLR:IFPEEK(65532)=34THENPOKE56,87:POKE54,87:POKE52,87 +17 DIMWF$(100):WF=0:P$="ok":DIMPB$(50):POKE(PEEK(253)),PEEK(254) +20 CR$=CHR$(13):PRINTCHR$(14);:SY=PEEK(65532):POKE53280,254:POKE53281,246 +30 CO$="{light blue}":IFSY=226THENML=49152:POKE665,73-(PEEK(678)*30) +33 IFSY=246THENML=4096:IFPEEK(ML)<>76THENCLOSE5:LOAD"pml+4.bin",PEEK(254),1:RUN +35 IFSY=34THENML=22273:IFPEEK(ML)<>76THENCLOSE5:LOAD"pmlvic.bin",PEEK(254),1:RUN +38 IFSY=34THENPOKE36879,27:CO$=CHR$(31) +40 IFSY=226ANDPEEK(ML+1)<>209THENCLOSE5:LOAD"pml64.bin",PEEK(254),1:RUN +50 IFSY=61THENML=4864:POKE981,15:P=PEEK(215)AND128:IFP=128THENSYS30643 +60 IFSY=61ANDPEEK(ML+1)<>217THENCLOSE5:LOAD"pml128.bin",PEEK(254),1:RUN +70 IFSY=61THENCO$=CHR$(159) +80 IFSY=246THENREM ----SET CO$,BG COLOR +100 REM +110 P$="a" +120 PRINTCO$;"{clear}{down*2}CONFIGURE v1.7":PRINT"Requires C64Net WiFi Firmware 2.0+" +130 PRINT"1200 baud version" +140 PRINT"By Bo Zimmerman (bo@zimmers.net)":PRINT:PRINT +197 REM -------------------------------- +198 REM GET STARTED ! +199 REM ------------------------------- +200 PH=0:PT=0:MV=ML+18:CR$=CHR$(13)+CHR$(10):QU$=CHR$(34):POKEMV+14,5 +202 PRINT "Initializing modem...";:CT=0 +203 GET#5,A$:IFA$<>""THEN203 +205 PRINT#5,CR$;"athz0f0r0e0&p1";CR$; +206 GOSUB900:IFP$<>"OK"THENCT=CT+1:IFCT<10THEN203 +207 IFCT<10THENGET#5,A$:IFA$<>""THEN207 +208 IFCT<10THENPRINT#5,"athf3e0&p1";CR$; +209 IFCT<10THENGOSUB900:IFP$<>"OK"THENCT=CT+1:GOTO207 +210 IFCT=10THENPRINT"Initialization failed.":PRINT"Is your modem set to 1200b?" +220 IFCT=10THENSTOP +300 PRINT:PRINT:GOTO 1000 +897 REM -------------------------------- +898 REM GET E$ FROM MODEM, OR ERROR ! +899 REM ------------------------------- +900 E$="":P$="" +910 SYSML +920 IFE$<>""ANDP$<>E$THENPRINT"{reverse on}{red}Comm error. Expected ";E$;", Got ";P$;CO$;"{reverse off}" +925 RETURN +997 REM -------------------------------- +998 REM THE MAIN LOOP ! +999 REM ------------------------------- +1000 CT=0:C1=0:CD=0:LC$="1":GOSUB1010:WI$=P$:GOSUB1010:IFP$=WI$THEN1030 +1005 GOTO1000 +1010 SYSML+12:PRINT#5,CR$;"ati3";CR$; +1020 GOSUB900:IFP$<>LC$THEN1025 +1021 CT=CT+1:IFCT>3THENRETURN +1023 C1=0:GOTO1010 +1025 LC$=P$:CT=0:C1=C1+1:IFC1>15THENP$="":RETURN +1027 GOTO1010 +1029 RETURN +1030 WI$=P$:PRINT"Current WiFi SSI: ";WI$ +1040 CD=0:IFWI$=""THEN2000 +1045 GOSUB 1050:IFCD=0THEN2000 +1047 GOTO4000 +1050 PRINT:PRINT"Testing connection...";:TT=TI+200 +1055 IFTI""THEN1060 +1070 SYSML+12:PRINT#5,CR$;"atc";QU$;"132.148.138.66:80";QU$;CR$; +1080 GOSUB900:CD=1:IFP$="OK"THEN1080 +1085 IFLEFT$(P$,8)<>"CONNECT "THENCD=0:CT=CT+1:ONCTGOTO1070,1070,1070,1070,1200 +1090 FORI=1TO3:SYSML+12:PRINT#5,CR$;"ath0";CR$;:GOSUB900:NEXTI +1100 PRINT#5,CR$;"ath0";CR$;:GOSUB900 +1200 IFCD=0THENPRINT"{reverse on}{red}Fail!{reverse off}";CO$ +1210 IFCD=1THENPRINT"{reverse on}{light green}Success!{reverse off}";CO$ +1220 RETURN +2000 SYSML+12:SYSML+12:PRINT:PRINT"Scanning for WiFi hotspots..."; +2010 PRINT#5,CR$;"atw";CR$; +2020 GOSUB900:I=1:IFP$=""ORP$="OK"THENPRINT"Done!":PRINT:PRINT:GOTO3000 +2030 IFI>=LEN(P$)THEN2100 +2040 IFMID$(P$,I,1)=" "THENP$=LEFT$(P$,I-1):GOTO2100 +2050 I=I+1:GOTO2030 +2100 WF$(WF+1)=P$:WF=WF+1:GOTO2020 +3000 PG=1 +3100 LP=WF:IFLP>PG+15THENLP=PG+15 +3110 FORI=PGTOLP:PRINTSTR$(I)+") ";WF$(I):NEXTI +3120 PRINT:PRINT"Enter X to Quit to BASIC." +3130 IFLP15THENPRINT"Enter P for Prev Page" +3150 PRINT"Enter a number to Connect to that SSI" +3200 PRINT"? ";:GOSUB5000:A$=P$:IFA$=""THEN3100 +3210 IFA$="x"ORA$="X"THENCLOSE5:END +3220 IFA$="n"ORA$="N"THENIFLP15THENPG=PG-15:GOTO3100 +3240 A=ASC(MID$(A$,1,1)):IFA<48ORA>57THEN3100 +3250 A=VAL(A$):IFAPG+15 OR A>WFTHEN3100 +3260 WI=A:PRINT"Attempt to connect to: ";WF$(A) +3300 PRINT"Enter WiFi Password: ";:GOSUB5000:PA$=P$ +3400 SYSML+12:PRINT#5,CR$;"atw";QU$;WF$(WI);",";PA$;QU$;CR$;:GOSUB900 +3410 IFP$="ERROR"THENPRINT"{reverse on}{red}Connect Fail!{reverse off}";CO$:GOTO3100 +3415 IFP$="OK"THEN3420 +3416 IFP$<>""THEN3400 +3417 GOSUB900:GOTO3410 +3420 PRINT"{reverse on}{light green}Connect success!{reverse off}";CO$ +3430 PRINT +3431 TT=TI+300 +3432 IFTI"OK"ANDP$<>"ok"THEN3470 +3490 P$="":SYSML+12:IFP$<>""THEN3490 +3500 SYSML+12:CLOSE5:RUN +4000 PRINT"Change WiFi connection (y/N)? "; +4010 GETA$:IFA$="y"ORA$="Y"THENPRINT"Y":GOTO2000 +4020 IFA$<>"n"ANDA$<>"N"ANDA$<>CHR$(13)THEN4010 +4030 PRINT"N":PRINT +4100 PRINT"Current 'Phone' book:":PB=0 +4110 PRINT#5,CR$;"atp";CR$ +4120 TT=TI+100 +4130 GOSUB900:IFP$=""ANDTI>TTTHEN4200 +4140 IFP$=""THEN4130 +4150 I=1:PP$=P$:IFP$="ok"ORP$="OK"THEN4200 +4160 IFMID$(PP$,I,1)<>" "THENI=I+1:GOTO4160 +4170 PP$=LEFT$(PP$,I-1) +4190 PB$(PB)=PP$:PB=PB+1:PRINTPB;") ";P$:GOTO4120 +4200 PRINT" A ) Add new":PRINT" Q ) Quit to BASIC" +4210 PRINT:PRINT"Enter your choice: ";:GOSUB5000:Q$=P$ +4220 QQ$=LEFT$(Q$,1):IFQQ$="q"ORQQ$="Q"THENCLOSE5:END +4230 QQ=VAL(Q$):IF(QQ<0ORQQ>PB)ANDQQ$<>"a"ANDQQ$<>"A"THENPRINT:GOTO4100 +4240 IFQQ<=0THEN4300 +4250 PRINT"E)dit or D)elete? "; +4260 GETA$:IFA$="e"ORA$="E"THENPRINT"E":B1$=PB$(QQ-1):GOTO4310 +4270 IFA$<>"d"ANDA$<>"D"THEN4260 +4280 PRINT"D" +4290 PRINT#5,"atp";CHR$(34);PB$(QQ-1);"=delete";CHR$(34):GOSUB900:GOTO4100 +4300 PRINT"Phone number: ";:GOSUB5000:B1$=P$ +4305 IFLEN(B1$)<3THENPRINT"Wrong.":GOTO4100 +4310 FORI=1TOLEN(B1$):B1=ASC(MID$(B1$,I,1)):IFB1<48ORB1>57THENB1$="" +4320 NEXTI:IFB1$=""THENPRINT"Bad digits.":GOTO4100 +4330 PRINT"Target host: ";:GOSUB5000:B2$=P$ +4335 IFLEN(B2$)<4THENPRINT"Wrong.":GOTO4100 +4340 PRINT"Target port: ";:GOSUB5000:B2=VAL(P$) +4345 IFB2<=0THENPRINT"Wrong.":GOTO4100 +4350 B3$="":PRINT"Do PETSCII translation (y/n)? "; +4360 GOSUB4980:IFA=1THENB3$=B3$+"p" +4370 PRINT"Do TELNET translation (y/n)? "; +4380 GOSUB4980:IFA=1THENB3$=B3$+"t" +4390 PRINT"Do terminal Echo (y/n)? "; +4400 GOSUB4980:IFA=1THENB3$=B3$+"e" +4410 PRINT"Do XON/XOFF Flow Control (y/n)? "; +4420 GOSUB4980:IFA=1THENB3$=B3$+"x" +4430 B4$=MID$(STR$(B2),2) +4440 PRINT#5,"atp";B3$;CHR$(34);B1$;"=";B2$;":";B4$;CHR$(34):GOSUB900 +4450 GOTO4100 +4499 STOP +4980 GETA$:IFA$<>"y"ANDA$<>"Y"ANDA$<>"n"ANDA$<>"N"THEN4980 +4990 A=0:AA$="N":IFA$="y"ORA$="Y"THENAA$="Y":A=1 +4995 PRINTAA$:RETURN +5000 P$="" +5005 PRINT"{reverse on} {reverse off}{left}"; +5010 GETA$:IFA$=""THEN5010 +5020 IFA$=CHR$(13)THENPRINT" {left}":RETURN +5030 IFA$<>CHR$(20)THENPRINTA$;"{reverse on} {reverse off}{left}";:P$=P$+A$:GOTO5010 +5040 IFP$=""THEN5010 +5050 P$=LEFT$(P$,LEN(P$)-1):PRINT" {left*2} {left}{reverse on} {reverse off}{left}";:GOTO5010 +49999 STOP +50000 OPEN5,2,0,CHR$(8) +50010 GET#5,A$:IFA$<>""THENPRINTA$; +50020 GETA$:IFA$<>""THENPRINT#5,A$; +50030 GOTO 50010 +55555 F$="configure":OPEN1,8,15,"s0:"+F$:CLOSE1:SAVE(F$),8:VERIFY(F$),8 diff --git a/cbm8bit/src/configure64-128-vic.bas b/cbm8bit/src/configure64-128-vic.bas index e81e13c..1a40f2b 100644 --- a/cbm8bit/src/configure64-128-vic.bas +++ b/cbm8bit/src/configure64-128-vic.bas @@ -1,11 +1,11 @@ !-------------------------------------------------- -!- Tuesday, July 18, 2017 2:54:09 AM +!- Thursday, April 26, 2018 9:07:31 PM !- Import of : -!- c:\src\zimodem\cbm8bit\src\configure64-128.prg +!- z:\unsorted\configure.prg !- Commodore 64 !-------------------------------------------------- -1 REM CONFIGURE64/128 1200B 1.8+ -2 REM UPDATED 08/14/2017 02:54A +1 REM CONFIGURE64/128 1200B 2.0+ +2 REM UPDATED 04/26/2018 02:54P 5 POKE254,PEEK(186):IFPEEK(254)<8THENPOKE254,8 10 SY=PEEK(65532):IFSY=61THENPOKE58,254:CLR 13 IFSY=34THENX=23777:POKEX,170:IFPEEK(X)<>170THENPRINT"<16k":STOP @@ -13,7 +13,7 @@ 17 DIMWF$(100):WF=0:P$="ok":DIMPB$(50):POKE186,PEEK(254) 20 CR$=CHR$(13):PRINTCHR$(14);:SY=PEEK(65532):POKE53280,254:POKE53281,246 30 CO$="{light blue}":IFSY=226THENML=49152:POKE665,73-(PEEK(678)*30) -35 IFSY=34THENML=22273:IFPEEK(ML)<>76THENCLOSE5:LOAD"pmlvic.bin",peek(254),1:RUN +35 IFSY=34THENML=22273:IFPEEK(ML)<>76THENCLOSE5:LOAD"pmlvic.bin",PEEK(254),1:RUN 38 IFSY=34THENPOKE36879,27:CO$=CHR$(31) 40 IFSY=226ANDPEEK(ML+1)<>209THENCLOSE5:LOAD"pml64.bin",PEEK(254),1:RUN 50 IFSY=61THENML=4864:POKE981,15:P=PEEK(215)AND128:IFP=128THENSYS30643 @@ -21,7 +21,7 @@ 70 IFSY=61THENCO$=CHR$(159) 100 REM 110 P$="a" -120 PRINTCO$;"{clear}{down*2}CONFIGURE v1.4":PRINT"Requires C64Net WiFi Firmware 1.5+" +120 PRINTCO$;"{clear}{down*2}CONFIGURE v1.6":PRINT"Requires C64Net WiFi Firmware 2.0+" 130 PRINT"1200 baud version" 140 PRINT"By Bo Zimmerman (bo@zimmers.net)":PRINT:PRINT 197 REM -------------------------------- @@ -30,11 +30,11 @@ 200 PH=0:PT=0:MV=ML+18:CR$=CHR$(13)+CHR$(10):QU$=CHR$(34):POKEMV+14,5 202 PRINT "Initializing modem...";:CT=0 203 GET#5,A$:IFA$<>""THEN203 -205 PRINT#5,CR$;"athz0f0e0";CR$; -206 GOSUB900:IFP$<>"ok"THENCT=CT+1:IFCT<10THEN203 +205 PRINT#5,CR$;"athz0f0e0r0&p1";CR$; +206 GOSUB900:IFP$<>"OK"THENCT=CT+1:IFCT<10THEN203 207 IFCT<10THENGET#5,A$:IFA$<>""THEN207 -208 IFCT<10THENPRINT#5,"athf3e0";CR$; -209 IFCT<10THENGOSUB900:IFP$<>"ok"THENCT=CT+1:GOTO207 +208 IFCT<10THENPRINT#5,"athf3e0&p1";CR$; +209 IFCT<10THENGOSUB900:IFP$<>"OK"THENCT=CT+1:GOTO207 210 IFCT=10THENPRINT"Initialization failed.":PRINT"Is your modem set to 1200b?" 220 IFCT=10THENSTOP 300 PRINT:PRINT:GOTO 1000 @@ -64,9 +64,9 @@ 1050 PRINT:PRINT"Testing connection...";:TT=TI+200 1055 IFTI""THEN1060 -1070 SYSML+12:PRINT#5,CR$;"atc";QU$;"98.138.253.109:80";QU$;CR$; -1080 GOSUB900:CD=1:IFP$="ok"THEN1080 -1085 IFLEFT$(P$,8)<>"connect "THENCD=0:CT=CT+1:ONCTGOTO1070,1070,1070,1070,1200 +1070 SYSML+12:PRINT#5,CR$;"atc";QU$;"132.148.138.66:80";QU$;CR$; +1080 GOSUB900:CD=1:IFP$="OK"THEN1080 +1085 IFLEFT$(P$,8)<>"CONNECT "THENCD=0:CT=CT+1:ONCTGOTO1070,1070,1070,1070,1200 1090 FORI=1TO3:SYSML+12:PRINT#5,CR$;"ath0";CR$;:GOSUB900:NEXTI 1100 PRINT#5,CR$;"ath0";CR$;:GOSUB900 1200 IFCD=0THENPRINT"{reverse on}{red}Fail!{reverse off}";CO$ @@ -74,7 +74,7 @@ 1220 RETURN 2000 SYSML+12:SYSML+12:PRINT:PRINT"Scanning for WiFi hotspots..."; 2010 PRINT#5,CR$;"atw";CR$; -2020 GOSUB900:I=1:IFP$=""ORP$="ok"THENPRINT"Done!":PRINT:PRINT:GOTO3000 +2020 GOSUB900:I=1:IFP$=""ORP$="OK"THENPRINT"Done!":PRINT:PRINT:GOTO3000 2030 IFI>=LEN(P$)THEN2100 2040 IFMID$(P$,I,1)=" "THENP$=LEFT$(P$,I-1):GOTO2100 2050 I=I+1:GOTO2030 @@ -95,8 +95,8 @@ 3260 WI=A:PRINT"Attempt to connect to: ";WF$(A) 3300 PRINT"Enter WiFi Password: ";:GOSUB5000:PA$=P$ 3400 SYSML+12:PRINT#5,CR$;"atw";QU$;WF$(WI);",";PA$;QU$;CR$;:GOSUB900 -3410 IFP$="error"THENPRINT"{reverse on}{red}Connect Fail!{reverse off}";CO$:GOTO3100 -3415 IFP$="ok"THEN3420 +3410 IFP$="ERROR"THENPRINT"{reverse on}{red}Connect Fail!{reverse off}";CO$:GOTO3100 +3415 IFP$="OK"THEN3420 3416 IFP$<>""THEN3400 3417 GOSUB900:GOTO3410 3420 PRINT"{reverse on}{light green}Connect success!{reverse off}";CO$ @@ -106,8 +106,8 @@ 3440 CD=0:GOSUB1050:IFCD=0THEN3100 3450 PRINT"{reverse off}{light green}Saving new options..." 3460 SYSML+12:SYSML+12:SYSML+12 -3470 PRINT#5,CR$;"atz0&we0";CR$ -3480 GOSUB900:IFP$<>"ok"THEN3470 +3470 PRINT#5,CR$;"atz0&we0&p1";CR$ +3480 GOSUB900:IFP$<>"OK"ANDP$<>"ok"THEN3470 3490 P$="":SYSML+12:IFP$<>""THEN3490 3500 SYSML+12:CLOSE5:RUN 4000 PRINT"Change WiFi connection (y/N)? "; diff --git a/cbm8bit/src/d64wget-sw64.bas b/cbm8bit/src/d64wget-sw64.bas new file mode 100644 index 0000000..9f1a969 --- /dev/null +++ b/cbm8bit/src/d64wget-sw64.bas @@ -0,0 +1,192 @@ +!-------------------------------------------------- +!- Tuesday, August 17, 2021 9:44:52 AM +!- Import of : +!- z:\lost+found\d64wget.prg +!- Commodore 64 +!-------------------------------------------------- +1 REM D64WGET64SW 19200B 2.0+ +2 REM UPDATED 10/13/2021 12:54A +10 POKE254,PEEK(186):IFPEEK(254)<8THENPOKE254,8 +12 GOSUB9100:IFSY=61THENPOKE58,254:CLR +13 IFSY=34THENX=23777:POKEX,170:IFPEEK(X)<>170THENPRINT"<16k":STOP +14 IFSY=227ANDPEEK(51201)=3THENSYS51200 +15 OPEN5,2,0,CHR$(BC):IFPEEK(65532)=34THENPOKE56,87:POKE54,87:POKE52,87 +17 DIMPP$(25):P$="ok":POKE186,PEEK(254):XB=1200:BA=1200 +20 CR$=CHR$(13):PRINTCHR$(14);:GOSUB9100:POKE53280,254:POKE53281,246 +30 CO$="{light blue}":IFSY=226THENML=49152:POKE665,73-(PEEK(678)*30):UM=ML+2048:XB=9600 +33 IFSY=227THENML=49152:UM=ML+2048 +35 IFSY=34THENML=22273:IFPEEK(ML)<>76THENCLOSE5:LOAD"pmlvic.bin",PEEK(254),1:RUN +38 IFSY=34THENPOKE36879,27:CO$=CHR$(31) +40 IFSY=226ANDPEEK(ML+1)<>209THENCLOSE5:LOAD"pml64.bin",PEEK(254),1:RUN +43 IFSY=227ANDPEEK(ML+1)<>209THENCLOSE5:LOAD"pml64.bin",PEEK(254),1:RUN +45 IFSY=226ANDUM>0ANDPEEK(UM+1)<>24THENCLOSE5:LOAD"up9600.bin",PEEK(254),1:RUN +47 IFSY=227ANDPEEK(51201)<>3THENCLOSE5:LOAD"swiftdrvr.bin",PEEK(254),1:RUN +50 IFSY=61THENML=4864:POKE981,15:S8=PEEK(215)AND128:IFS8=128THENSYS30643 +55 IFSY=61ANDS8=128THENXB=2400:CO$=CHR$(159) +60 IFSY=61ANDPEEK(ML+1)<>217THENCLOSE5:LOAD"pml128.bin",PEEK(254),1:RUN +80 IFSY=226ANDUM>0THENSYSUM:SYSUM+3:X=PEEK(789):SYSUM+9:IFX=234THENXB=1200 +100 IFSY<>34THENPOKE56579,0 +101 REM +102 REM +110 P$="a" +120 PRINTCO$;"{clear}{down*2}D64WGET v1.5":PRINT"Requires Zimodem firmware 2.0+" +140 PRINT"By Bo Zimmerman (bo@zimmers.net)":PRINT:PRINT +198 REM --- MODEM INIT +200 UN=PEEK(254) +201 PH=0:PT=0:MV=ML+18 +202 PRINT "Initializing modem...":CR$=CHR$(13)+CHR$(10) +203 GET#5,A$:IFA$<>""THEN203 +205 PRINT#5,CR$;"athz0&p0f0e0";CR$;:GOSUB900:IFP$<>"ok"THEN203 +208 GET#5,A$:IFA$<>""THEN208 +210 PRINT#5,CR$;"ate0n0r0v1q0";CR$; +220 GOSUB900:IFP$<>"ok"THEN208 +225 GET#5,A$:IFA$<>""THEN225 +230 PRINT#5,"ate0v1x1f3q0s40=248i4";CR$;CHR$(19); +235 GOSUB900:VR=VAL(P$):IFVR<2.0THENPRINT"Zimodem init failed: ";P$:STOP +240 GOSUB900:IFP$<>"ok"THEN203 +250 DIM HH$(20):OT$="" +260 UR$="coffeemud.net:8080/ctcug/firmware/c64net{arrow left}apps{arrow left}latest.d64" +300 REM GET INFO +310 PRINT:PRINT"{down}Request Parms:" +320 PRINT " *) Type{space*7}: GET" +321 PRINT " 1) Url{space*8}: http://";UR$ +322 PRINT " 2) Output Unit:";UN +329 LW=2 +330 NH=0:FORI=0TO20:IFLEN(HH$(I))>0THENNH=NH+1:PRINTSTR$(LW+NH)+") "+HH$(I):NEXT +340 LH=LW+NH+1:PRINTSTR$(LW+1+NH)+") Add New Header" +370 IFUR$=""THENPRINT:P$="1":GOTO400 +380 PRINT:PRINT"Type a number or RETURN{sh space}to begin:"; +390 GOSUB5000:IFP$=""THEN1000 +400 X=VAL(P$):IFX<1ORX>LHTHEN300 +410 IFX=1THENPRINT"Enter URL: http://";:GOSUB5000:UR$=P$:GOTO300 +420 IFX=2THENPRINT"Enter output device/unit: ";:GOSUB5000:UN=VAL(P$):GOTO300 +440 IFX<>LHTHENP$=HH$(X-LW):PRINT"Modify: ";P$:GOSUB5005:HH$(X-LW)=P$:GOTO300 +450 II=-1:FORI=0TO20:IFHH$(I)=""ANDII<0THENII=I +460 NEXT:IFII>=0THENPRINT"New Header: ";:GOSUB5000:HH$(II)=P$ +470 GOTO 300 +598 REM --- TRANSMIT P$ TO THE OPEN SOCKET +600 OP$=P$:SYSML+9:C8$=MID$(STR$(PEEK(MV+8)),2):E$="ok":IFVR>3THENE$=C8$ +610 PRINT#5,"ats42=";C8$;"tp+";QU$;P$;QU$ +620 SYSML:IFP$<>E$THENP$=OP$:PRINT"{yellow}{reverse on}Retrying..{reverse off}";CO$:GOTO600 +630 RETURN +650 OP$=P$:SYSML+9:C8$=MID$(STR$(PEEK(MV+8)),2):PN$=MID$(STR$(LEN(P$)),2) +655 E$="ok":IFVR>3THENE$=C8$ +660 PRINT#5,"ats42=";C8$;"t+";PN$:PRINT#5,P$ +670 SYSML:IFP$<>E$THENP$=OP$:PRINT"{yellow}{reverse on}Retrying..{reverse off}";CO$:GOTO650 +680 RETURN +798 REM --- GET P$ FROM SOCKET P +800 P$="":E=0 +810 GOSUB930:IFP0<>PANDP0<0THENPRINT"Unexpected packet id: ";P0;"/";P:STOP +820 IFP0=0THENE=1:RETURN:REM FAIL +830 RETURN +898 REM --- GET E$ FROM MODEM, OR ERROR +900 E$="" +910 SYSML +920 IFE$<>""ANDP$<>E$THENPRINT"{reverse on}{red}Comm error. Expected ";E$;", Got ";P$;CO$;"{reverse off}" +925 RETURN +928 REM --- GET PACKET HEADER, SETS P0,P1,P2, RETURNS P0=0 IF NADA +930 PR=0:GET#5,P$:IFP$<>""THEN930 +940 PRINT#5,CHR$(17); +945 SYSML+6:P0=PEEK(MV+2):P1=PEEK(MV+4):P2=PEEK(MV+6) +950 PL=PEEK(MV+0):CR=PEEK(MV+1):C8=PEEK(MV+8) +960 IFP0>0ANDP2<>C8THEN985 +970 IFP1=0THENP$="" +980 IFP0>0ORCR=0THENRETURN +985 GET#5,P$:IFP$<>""THEN985 +990 PRINT"{yellow}PACKET-RETRY";CO$:PRINT#5,"atl":GOTO945 +995 PRINT"Expected ";E$;", got ";A$:STOP +998 REM --- THE MAIN LOOP ! +1000 IFLEN(UR$)=0THEN300 +1010 I=0:PRINT"{down*2}The disk in drive";UN;"will be over-" +1011 PRINT"written.{space*2}Are you sure (y/n)? ";:GOSUB5000 +1012 IFLEFT$(P$,1)="y"ORLEFT$(P$,1)="Y"THEN1020 +1013 IFLEFT$(P$,1)<>"n"ANDLEFT$(P$,1)<>"N"THEN1010 +1015 CLOSE5:STOP +1020 IFMID$(UR$,I+1,1)<>"/"THENI=I+1:IFI":"THENI=I+1:IFI=LEN(H1$)THENH1$=H1$+":80" +1070 AT$="":IFXB>0ANDBA>0ANDXB<>BATHENAT$="s43="+MID$(STR$(XB),2) +1080 GET#5,A$:IFA$<>""THEN1080 +1100 QU$=CHR$(34):PRINT#5,"ath"+AT$+"&d10&m10&m13c";QU$;H1$;QU$ +1105 GOSUB900 +1110 IFLEN(P$)>8ANDLEFT$(P$,8)="connect "THENP=VAL(MID$(P$,9)):GOTO1200 +1115 PRINT"{pink}{reverse on}Unable to connect to ";H1$;"{reverse off}";CO$:GOTO300 +1120 IT=TI+40 +1130 P9=0:GOSUB800:IFP$<>""THENIT=TI+10 +1140 IFTI9600THEN1205 +1202 SYSUM:SYSUM+3:IFPEEK(789)=234THENSYSUM+9:GOTO1210 +1203 BA=XB:POKEUM+19,1:PRINT#5,"at":GOSUB6000:GOSUB6000 +1205 IFXB<>2400THEN1210 +1206 NP=0:IFPEEK(2614)>0THENNP=20 +1207 BA=XB:POKE2576,10:POKE2578,PEEK(59490+NP):POKE2579,PEEK(59491+NP) +1208 POKE2582,170:POKE2583,1:IFNP>0THENPOKE2582,154 +1210 PRINT"{reverse on}{light green}Sending request...{reverse off}";CO$:PRINT#5,"at":GOSUB6000:GOSUB6000 +1215 P$="GET "+P1$+" HTTP/1.1":GOSUB600 +1220 P$="Host: "+HO$:GOSUB600 +1225 P$="Connection: Keep-Alive":GOSUB600 +1230 P$="User-Agent: C=WGET":GOSUB600 +1235 P$="Content-Length: 0":GOSUB600 +1240 P$="Accept: */*":GOSUB600 +1300 FORI=0TO20 +1310 IFHH$(I)=""THEN1330 +1320 P$=HH$(I):GOSUB900 +1330 NEXTI +1950 P$=CHR$(13)+CHR$(10):GOSUB650 +1970 PRINT"{reverse on}{light green}Request sent. Reading response...{reverse off}";CO$ +2000 P9=0:GOSUB800:IFP$=""THEN2000 +2010 LE=0:FB=0 +2020 IFLEN(P$)<13THENPRINT"{reverse on}{pink}Bad response: {reverse off}";CO$;P$:STOP +2030 IFLEFT$(P$,5)<>"http/"THENPRINT"{reverse on}{red}Bad response{reverse off}";CO$;P$:STOP +2040 RC=VAL(MID$(P$,10,3)) +2050 IFRC<>200THENPRINT"{reverse on}{red}Bad response code:{reverse off}";CO$;RC:STOP +2100 GOSUB800:IFP$=""ANDE=0THEN2200 +2120 IFLEN(P$)<17ORLEFT$(P$,1)<>"c"ORMID$(P$,9,1)<>"l"THEN2100 +2130 IFMID$(P$,15,1)<>":"THEN2100 +2140 I=15 +2160 I=I+1:IFMID$(P$,I,1)=" "THEN2160 +2170 X=VAL(MID$(P$,I)):IFX>0THENCL=X +2180 GOTO2100 +2200 TL=0:PRINT#5,CR$;"at&m&dc";MID$(STR$(P),2);CR$; +2210 GOSUB900:IFP$<>"ok"THENPRINT"Zimodem command failed: ";P$:STOP +2240 IFCL=0THENPRINT"{reverse on}{pink}Headers complete. No content. Done.{reverse off}";CO$:END +2250 PRINT"{reverse on}{light green}Headers complete." +2260 PRINT"{reverse on}Receiving"+STR$(CL)+" bytes{reverse off}";CO$ +2270 OPEN1,UN,15,"i0:":OPEN3,UN,3,"#":T=1:S=0:OS=1:INPUT#1,E,E$,E1,E2 +2290 REM +2300 PRINT#1,"b-p";3;0:TB=0:BR=256 +2305 PRINT"{space*15}{left*15}"+STR$(T)+","+STR$(S):PRINT"{up}"; +2310 IFOS>1ANDOS<=LEN(P$)THENPRINT#3,MID$(P$,OS);:BR=BR-(LEN(P$)-OS+1) +2320 GOSUB930:IFP0<>PORP1=0ORP0=0THEN2320 +2330 IFP10THENT=T+1:S=0:GOTO2360 +2380 PRINT:PRINT"{reverse on}{light green}Done.{reverse off}";CO$:CLOSE3:CLOSE1 +2390 PRINT#5,"ath0":PRINT#5,"atz":TT=TI+100 +2400 IFTICHR$(20)THENPRINTA$;"{reverse on} {reverse off}{left}";:P$=P$+A$:GOTO5010 +5040 IFP$=""THEN5010 +5050 P$=LEFT$(P$,LEN(P$)-1):PRINT" {left*2} {left}{reverse on} {reverse off}{left}";:GOTO5010 +6000 TT=TI+100 +6010 SYSML+12:IFTI226THEN9140 +9120 X=PEEK(56834):POKE56834,X+1:IFPEEK(56834)<>X+1THEN9140 +9130 BC=15:SY=227:POKE56834,X:RETURN +9140 PRINTCHR$(14);"Requires a C64 with SwiftLink/Link232":STOP +50000 OPEN5,2,0,CHR$(8) +50010 GET#5,A$:IFA$<>""THENPRINTA$; +50020 GETA$:IFA$<>""THENPRINT#5,A$; +50030 GOTO 50010 +55555 U=8:F$="d64wget":OPEN1,U,15,"s0:"+F$:CLOSE1:SAVE(F$),U:VERIFY(F$),U diff --git a/cbm8bit/src/d64wget64-128-vic.bas b/cbm8bit/src/d64wget64-128-vic.bas index 3bda4ac..74165c5 100644 --- a/cbm8bit/src/d64wget64-128-vic.bas +++ b/cbm8bit/src/d64wget64-128-vic.bas @@ -5,7 +5,7 @@ !- Commodore 64 !-------------------------------------------------- 1 REM D64WGET4/128 1200B 2.0+ -2 REM UPDATED 08/19/2017 02:54A +2 REM UPDATED 10/13/2021 12:54A 10 POKE254,PEEK(186):IFPEEK(254)<8THENPOKE254,8 12 SY=PEEK(65532):IFSY=61THENPOKE58,254:CLR 13 IFSY=34THENX=23777:POKEX,170:IFPEEK(X)<>170THENPRINT"<16k":STOP @@ -32,7 +32,7 @@ 201 PH=0:PT=0:MV=ML+18 202 PRINT "Initializing modem...":CR$=CHR$(13)+CHR$(10) 203 GET#5,A$:IFA$<>""THEN203 -205 PRINT#5,CR$;"athz0f0e0";CR$;:GOSUB900:IFP$<>"ok"THEN203 +205 PRINT#5,CR$;"athz0&p0f0e0";CR$;:GOSUB900:IFP$<>"ok"THEN203 208 GET#5,A$:IFA$<>""THEN208 210 PRINT#5,CR$;"ate0n0r0v1q0";CR$; 220 GOSUB900:IFP$<>"ok"THEN208 diff --git a/cbm8bit/src/ftp-sw64.bas b/cbm8bit/src/ftp-sw64.bas new file mode 100644 index 0000000..432c356 --- /dev/null +++ b/cbm8bit/src/ftp-sw64.bas @@ -0,0 +1,311 @@ +!-------------------------------------------------- +!- Tuesday, August 17, 2021 9:45:06 AM +!- Import of : +!- z:\lost+found\ftp.prg +!- Commodore 64 +!-------------------------------------------------- +1 REM FTP64SW 19200B 2.0+ +2 REM UPDATED 10/13/2021 12:54A +5 POKE254,8:IFPEEK(186)>7THENPOKE254,PEEK(186) +6 GOSUB9100:REM SET SY,BC +10 IFSY=61THENPOKE58,254:CLR +12 IFSY=34THENX=23777:POKEX,170:IFPEEK(X)<>170THENPRINT"<16k":STOP +13 IFSY=227ANDPEEK(51201)=3THENSYS51200 +15 OPEN5,2,0,CHR$(BC):IFPEEK(65532)=34THENPOKE56,87:POKE54,87:POKE52,87 +17 P$="ok":POKE186,PEEK(254):BA=1200:XB=1200 +20 CR$=CHR$(13):PRINTCHR$(14);:GOSUB9100:POKE53280,254:POKE53281,246 +30 CO$="{light blue}":IFSY=226THENML=49152:POKE665,73-(PEEK(678)*30):UM=ML+2048:XB=9600 +33 IFSY=227THENML=49152:UM=ML+2048 +35 IFSY=34THENML=22273:IFPEEK(ML)<>76THENCLOSE5:LOAD"pmlvic.bin",PEEK(254),1:RUN +38 IFSY=34THENPOKE36879,27:CO$=CHR$(31) +40 IFSY<>226THEN45 +42 IFPEEK(ML+1)<>209THENCLOSE5:LOAD"pml64.bin",PEEK(254),1:RUN +44 IFUM>0ANDPEEK(UM+1)<>24THENCLOSE5:LOAD"up9600.bin",PEEK(254),1:RUN +45 IFSY<>227THEN50 +47 IFPEEK(ML+1)<>209THENCLOSE5:LOAD"pml64.bin",PEEK(254),1:RUN +49 IFUM>0ANDPEEK(UM+1)<>3THENCLOSE5:LOAD"swiftdrvr.bin",PEEK(254),1:SYSUM:RUN +50 S8=0:IFSY=61THENML=4864:POKE981,15:S8=PEEK(215)AND128:IFS8=128THENSYS30643 +60 IFSY=61ANDPEEK(ML+1)<>217THENCLOSE5:LOAD"pml128.bin",PEEK(254),1:RUN +70 IFSY=61ANDS8=128THENXB=2400:CO$=CHR$(159) +80 IFSY=226ANDUM>0THENSYSUM:SYSUM+3:X=PEEK(789):SYSUM+9:IFX=234THENXB=1200 +90 IFSY<>34THENPOKE56579,0:REM WHY DOES THIS WORK +100 MV=ML+18:POKEMV+14,8:DD=56577:IFSY=34THENDD=37136:REM FIX BUFAP +101 REM +102 REM +110 REM +120 PRINTCO$;"{clear}{down*2}FTP v1.7":PRINT"Requires Zimodem firmware 2.0+" +140 PRINT"By Bo Zimmerman (bo@zimmers.net)":PRINT:PRINT +199 REM ---- ZIMODEM SETUP +200 UN=PEEK(254):IP$="":CR$=CHR$(13)+CHR$(10) +201 PH=0:PT=0:MV=ML+18 +202 PRINT "Initializing modem..." +203 GET#5,A$:IFA$<>""THEN203 +205 PRINT#5,CR$;"athz0&p0f0e0";CR$;:GOSUB900:IFP$<>"ok"THEN203 +208 GET#5,A$:IFA$<>""THEN208 +210 PRINT#5,CR$;"ate0n0r0v1q0";CR$; +220 GOSUB900:IFP$<>"ok"THEN208 +230 PRINT#5,"ate0v1x1f3q0s40=248s0=1s41=0i4";CR$;CHR$(19);:L9=248 +235 GOSUB900:VR=VAL(P$):IFVR<2.0THENPRINT"Zimodem init failed: ";P$:STOP +240 GOSUB900:IFP$<>"ok"THEN203 +245 GET#5,A$:IFA$<>""THEN245 +250 PRINT#5,"ati2";CR$;:GOSUB900:IP$=P$:IFP$="ok"ORLEN(P$)<8THEN245 +260 P$="":FORI=1TOLEN(IP$) +263 REMIFMID$(IP$,I,1)="."THENIP$=LEFT$(IP$,I-1)+","+MID$(IP$,I+1) +265 NEXTI:PRINT"Your ip address is: ";IP$ +298 HO$="ftp.zimmers.net":PO=21:UN$="anonymous":PA$="my@email.com" +299 REMHO$="192.168.1.112":PO=21 +300 REM GET INFO +310 PRINT:PRINT"{down}Request Parms:" +321 PRINT " 1) Url{space*8}: ftp://";HO$ +322 PRINT " 2) Username{space*3}: ";UN$ +323 PRINT " 3) Password{space*3}: ";PA$ +324 PRINT " 4) Disk Device:";UN +329 LH=4 +370 IFHO$=""THENPRINT:P$="1":GOTO400 +380 PRINT:PRINT"Type a number or RETURN{sh space}to connect:"; +390 GOSUB5000:IFP$=""THEN1000 +400 X=VAL(P$):IFX<1ORX>LHTHEN300 +410 IFX=1THENPRINT"Enter URL: ftp://";:GOSUB5000:HO$=P$:GOTO300 +413 IFX=2THENPRINT"Enter Username: ";:GOSUB5000:UN$=P$:GOTO300 +416 IFX=3THENPRINT"Enter Password: ";:GOSUB5000:PA$=P$:GOTO300 +420 IFX=4THENPRINT"Enter output device/unit: ";:GOSUB5000:UN=VAL(P$):GOTO300 +470 GOTO 300 +598 REM --- TRANSMIT P$ TO THE OPEN SOCKET ! +600 OP$=P$:SYSML+9:C8$=MID$(STR$(PEEK(MV+8)),2):E$="ok":IFVR>3THENE$=C8$ +610 PRINT#5,"ats42=";C8$;"tp+";QU$;P$;QU$ +620 SYSML:IFP$<>E$THENPRINT"{reverse on}{red}xerr:{reverse off}";CO$;P$:P$=OP$:GOTO600 +630 RETURN +650 OP$=P$:SYSML+9:C8$=MID$(STR$(PEEK(MV+8)),2):PN$=MID$(STR$(LEN(P$)),2) +660 PRINT#5,"ats42=";C8$;"t+";PN$:PRINT#5,P$:E$="ok":IFVR>3THENE$=C8$ +670 SYSML:IFP$<>E$THENPRINT"xerr:";P$:P$=OP$:GOTO650 +680 RETURN +798 REM --- GET P$ FROM SOCKET P +800 P$="":E=0 +810 GOSUB930:IFP0<>PANDP0<0THENPRINT"Unexpected packet id: ";P0;"/";P:STOP +820 IFP0=0THENE=1:RETURN:REM FAIL +830 RETURN +850 PC=0 +860 PC=PC+1:GOSUB800:IFP$=""ORE=1THENIFPC<60THEN860 +870 IFLEN(P$)<4ORMID$(P$,4,1)<>"-"THENRETURN +880 EC$=LEFT$(P$,3):EC=VAL(EC$) +881 PC=0 +882 GOSUB800:IFP$=""ORE=1THENIFPC<60THEN882 +883 IFP$=""ORE=1THEN870 +884 IFLEFT$(P$,3)<>EC$THENPRINTP$:GOTO881 +885 IFMID$(P$,4,1)="-"THEN881 +886 RETURN +898 REM --- GET E$ FROM MODEM, OR ERROR +900 E$="" +910 SYSML +920 IFE$<>""ANDP$<>E$THENPRINT"{reverse on}{red}Comm error. Expected ";E$;", Got ";P$;CO$;"{reverse off}" +925 RETURN +927 REM ---- LOW LVL PACKET READ +930 PR=0:GET#5,P$:IFP$<>""THEN930 +940 PRINT#5,CHR$(17); +945 SYSML+6:P0=PEEK(MV+2):P1=PEEK(MV+4):P2=PEEK(MV+6) +950 PL=PEEK(MV+0):CR=PEEK(MV+1):C8=PEEK(MV+8) +960 IFP0>0ANDP2<>C8THEN985 +970 IFP1=0THENP$="" +980 IFP0>0ORCR=0THENRETURN +985 GET#5,P$:IFP$<>""THEN985 +990 PRINT"{yellow}PACKET-RETRY";CO$:PRINT#5,"atl":GOTO945 +995 PRINT"Expected ";E$;", got ";A$:STOP +998 REM --- THE MAIN LOOP +1000 QU$=CHR$(34):REM BEGIN! +1010 AT$="":IFXB>0ANDBA>0ANDXB<>BATHENAT$="s43="+MID$(STR$(XB),2) +1020 GET#5,A$:IFA$<>""THEN1020 +1100 PRINT#5,"ath"+AT$+"&d10&m13&m10cp";QU$;HO$;":";MID$(STR$(PO),2);QU$:E=0 +1105 GOSUB900:SP=0:IFP$="ok"THEN1020 +1110 IFLEN(P$)>8ANDLEFT$(P$,8)="connect "THENP=VAL(MID$(P$,9)):GOTO1200 +1112 IFLEN(P$)=0ANDE<3THENE=E+1:GOTO1105 +1115 PRINT"{pink}{reverse on}Unable to connect to ";HO$;" port";PO;"{reverse off}";CO$:GOTO300 +1120 IT=TI+40 +1130 P9=0:GOSUB800:IFP$<>""THENIT=TI+10 +1140 IFTI9600THEN1205 +1202 SYSUM:SYSUM+3:IFPEEK(789)=234THENSYSUM+9:GOTO1210 +1203 BA=XB:POKEUM+19,1:PRINT#5,"at":GOSUB9000:GOSUB9000 +1205 IFXB<>2400THEN1210 +1206 NP=0:IFPEEK(2614)>0THENNP=20 +1207 BA=XB:POKE2576,10:POKE2578,PEEK(59490+NP):POKE2579,PEEK(59491+NP) +1208 POKE2582,170:POKE2583,1:IFNP>0THENPOKE2582,154 +1210 PRINT#5,"at":GOSUB9000:GOSUB9000 +1220 GOSUB850:IFP$=""THEN1240 +1225 PRINTP$ +1230 IFUN$=""THENPRINT"Username: ";:GOSUB5000:UN$=P$:IFP$=""THEN1230 +1240 P$="USER "+UN$:GOSUB600:GOSUB850:IFP$=""THEN1240 +1250 PRINTP$:IFLEFT$(P$,3)<>"331"THENPRINT"{reverse on}{red}Username rejected?!{reverse off}";CO$:CLOSE5:END +1260 IFPA$=""THENPRINT"Password: ";:GOSUB5000:PA$=P$:IFP$=""THEN1260 +1270 P$="PASS "+PA$:GOSUB600:GOSUB850:IFP$=""THEN1270 +1280 PRINTP$:IFLEFT$(P$,3)="230"THEN2000 +1290 UN$="":PA$="":GOTO1230 +1300 GET#5,A$:IFA$<>""THEN1300 +1302 P$="PASV":GOSUB600:A=0 +1305 A=A+1:GOSUB850:IFP$=""ANDA<60THEN1305 +1307 IFP$=""THEN1300 +1310 PRINTP$:PRINT:IFLEFT$(P$,3)<>"227"THEN1300 +1320 A=ASC(RIGHT$(P$,1)):IFA<48ORA>57THENP$=LEFT$(P$,LEN(P$)-1):GOTO1320 +1330 X0=LEN(P$) +1340 A=ASC(MID$(P$,X0,1)):IFA>47ANDA<58THENX0=X0-1:GOTO1340 +1350 X2=VAL(MID$(P$,X0+1)):P$=LEFT$(P$,X0-1):X0=LEN(P$) +1360 A=ASC(MID$(P$,X0,1)):IFA>47ANDA<58THENX0=X0-1:GOTO1360 +1370 X1=VAL(MID$(P$,X0+1)):P$=LEFT$(P$,X0-1):H2$=":"+MID$(STR$((X1*256)+X2),2) +1375 P$=MID$(P$,5) +1380 A=ASC(LEFT$(P$,1)):IFA<48ORA>57THENP$=MID$(P$,2):GOTO1380 +1390 FORI=1TOLEN(P$):IFMID$(P$,I,1)=","THENP$=LEFT$(P$,I-1)+"."+MID$(P$,I+1) +1395 NEXT:H0$=P$+H2$:R0=0 +1400 GET#5,P$:IFP$<>""THEN1400 +1405 PRINT#5,"at";CC$;QU$;H0$;QU$:GOSUB900 +1410 IFLEN(P$)>8ANDLEFT$(P$,8)="connect "THENSP=VAL(MID$(P$,9)):GOTO 1420 +1412 IFR0>1THEN1300 +1415 R0=R0+1:PRINT"{red}Error: ";P$:PRINT"{pink}{reverse on}Retry to connect to ";H0$;"{reverse off}";CO$:GOTO1400 +1420 GET#5,A$:IFA$<>""THEN1420 +1425 PRINT#5,"atc";MID$(STR$(P),2):GOSUB900:IFP$="ok"THENE=0:RETURN +1430 PRINT"{pink}{reverse on}Retry to change back to ";H0$;"{reverse off}";CO$:GOTO1420 +2000 PRINT"Command (?): ";:GOSUB5000 +2005 IF(PEEK(DD)AND16)=0THENPRINT"{red}Lost connection";CO$:P$="quit" +2010 IFP$=""THENGOSUB800:PRINTP$:GOTO2000 +2020 IFP$="ls"ORLEFT$(P$,3)="ls "THENGOSUB4000:GOTO2000 +2030 IFP$="dir"ORLEFT$(P$,4)="dir "THENGOSUB4000:GOTO2000 +2040 IFP$="exit"ORP$="quit"THENPRINT#5,"atz":GOSUB900:CLOSE5:END +2045 IFLEFT$(P$,4)="del "THENP$="dele"+MID$(P$,4) +2050 IFLEFT$(P$,3)="cd "THENP$="CWD "+MID$(P$,4) +2055 IFLEFT$(P$,4)="lcd "THEN8000 +2056 IFLEFT$(P$,5)="ldel "THEN8100 +2057 IFLEFT$(P$,4)="ldir"THEN8200 +2060 IFLEFT$(P$,4)="get "THENF$=MID$(P$,5):GOSUB6000:GOTO2000 +2070 IFLEFT$(P$,4)="put "THENF$=MID$(P$,5):GOSUB7000:GOTO2000 +2075 IFP$="?"THENP$="help" +2080 IFP$="help"THENPRINT"{light green}{reverse on}get put ls cd dir del" +2081 IFP$="help"THENPRINT"{light green}{reverse on}lcd ldir ldel quit" +2082 IFP$="help"THENPRINT"{light green}{reverse on}Use ,s and ,p in get/put filenames!" +2083 IFP$="help"THENPRINT"{light green}{reverse on}Below are server commands:{reverse off}";CO$ +2100 GOSUB600:GOSUB850:PRINTP$:GOTO2000 +2999 STOP +4000 P$="TYPE A":GOSUB600:GOSUB850:IFP$=""THEN4000 +4005 PRINTP$:CC$="&m10&d10&m13cp":GOSUB1300:IFE=1THENRETURN +4010 P$="PWD":GOSUB600:GOSUB850:IFP$=""THEN4010 +4020 REMPRINTP$:P$="PORT "+IP$+",198,76":GOSUB600:GOSUB850:IFP$=""THEN4020 +4025 GET#5,A$:IFST=0ANDA$<>""THENGOSUB900:GOTO4025 +4030 PRINTP$:P$="LIST":GOSUB600 +4040 REMGOSUB900:IFLEFT$(P$,5)="ring "THEN4040 +4050 Y=0:Y$="":Y1=0:Y2=0:Y3=5:IFBA>4800THENY3=10 +4100 REMGET#5,A$:IFST=0ANDA$<>""THENGOSUB900:GOTO4100 +4110 GETA$:IFA$=" "ORA$="{ct c}"THENPRINT:PRINT"Aborted.":GOTO4250 +4150 GOSUB800 +4200 IFP0=PANDY=1ANDP$<>""THENY$=P$:GOTO4100 +4210 IFP0=PANDP$=""THEN4100 +4220 IFP0=PANDLEFT$(P$,4)="226 "THENY=1:Y$=P$:GOTO4100 +4225 IFP0=PANDLEFT$(P$,4)="150 "THEN4100:REMPRINTP$:GOTO4100 +4230 IFP0<>SPORP$=""THEN4235 +4231 Y2=0:Y1=Y1+1:IFS8=0THENPRINTMID$(P$,34):GOTO4100 +4232 PRINTP$:GOTO4100 +4235 IFP0=PTHEN4100 +4240 Y2=Y2+1:IFY=0AND(Y1>2ORY2""ANDST=0THEN4244 +4245 GOSUB800:IFP$<>""THENPRINTP$:GOTO4245 +4250 PRINT#5,"ath"+MID$(STR$(SP),2):GOSUB900:IFP$<>"ok"THEN4250 +4299 RETURN +5000 P$="" +5005 PRINT"{reverse on} {reverse off}{left}"; +5010 GETA$:IFA$=""THEN5010 +5020 IFA$=CHR$(13)THENPRINT" {left}":RETURN +5030 IFA$<>CHR$(20)THENPRINTA$;"{reverse on} {reverse off}{left}";:P$=P$+A$:GOTO5010 +5040 IFP$=""THEN5010 +5050 P$=LEFT$(P$,LEN(P$)-1):PRINT" {left*2} {left}{reverse on} {reverse off}{left}";:GOTO5010 +6000 FX=0:X$=",p,w":P$="TYPE I":GOSUB600:GOSUB850:IFP$=""THEN6000 +6005 PRINTP$:CC$="c":GOSUB1300:IFE=1THENRETURN +6010 FX$=RIGHT$(F$,2) +6015 IFLEFT$(F$,1)=" "THENF$=MID$(F$,2):GOTO6015 +6020 IFFX$=",p"ORFX$=",s"THENF$=LEFT$(F$,LEN(F$)-2):X$=FX$+",w" +6025 IFF$=""THENRETURN +6030 P$="RETR "+F$:GOSUB600 +6050 Y=0:Y$="":TB=0:TT=0:Y0=0:Y1=10 +6100 GET#5,A$:IFST=0ANDA$<>""THENGOSUB900:GOTO6100 +6150 GOSUB800 +6160 IFFX>0ORP0<>SPORP1=0THEN6200 +6170 OPEN1,UN,15:OPEN8,UN,8,"@0:"+F$+X$ +6180 INPUT#1,E:IFE<>0THENCLOSE8:CLOSE1:PRINT"{reverse on}{red}Failed to open "+F$+"{reverse off}";CO$:GOTO6250 +6190 FX=1 +6200 PL=LEN(P$):IFP0<>PTHEN6230 +6205 IFPL=0THEN6100 +6210 Y0=0:IFY=1ANDPL>0THENY$=P$:GOTO6100 +6215 LP$=LEFT$(P$,4):IFLP$="550 "THENPRINTP$:GOTO6250 +6220 IFLP$="226 "THENY=1:Y$=P$:GOTO6100 +6225 IFLP$="150 "THENPRINTLEFT$("{up}",SGN(TT))+P$:PRINT:GOSUB6400:GOTO6100 +6230 IFP0=SPANDPL>0THENPRINT#8,P$;:TT=TT+PL:GOSUB6500:Y0=0:GOTO6100 +6235 IFP0=PTHEN6100 +6240 Y0=Y0+1:IFY=0THENIFY0""THENGOSUB900:GOTO6245 +6247 PRINTY$:PRINT "{reverse on}{light green}";MID$(STR$(TT),2);" bytes transferred.{reverse off}";CO$ +6250 CLOSE8:CLOSE1:PRINT#5,"ath"+MID$(STR$(SP),2):GOSUB900:IFP$<>"ok"THEN6250 +6260 GOSUB800:IFP$<>""THENPRINTP$:GOTO6260 +6299 RETURN +6400 I=LEN(P$)-1:I0=0:TB=0 +6410 A$=MID$(P$,I,1):IFA$="("THEN6450 +6420 IFA$=" "THENI0=I +6430 I=I-1:IFI>0THEN6410 +6440 RETURN +6450 IFI0=0THENRETURN +6460 IFLEFT$(MID$(P$,I0+1),4)<>"byte"THENRETURN +6470 TB=VAL(MID$(P$,I+1,I0-I)):Y1=TB+10:RETURN +6500 PRINT"{up}{space*15}{left*15}"+STR$(TT):RETURN +7000 FX=0:X$=",p,r":P$="TYPE I":GOSUB600:GOSUB850:IFP$=""THEN7000 +7010 FX$=RIGHT$(F$,2) +7015 IFLEFT$(F$,1)=" "THENF$=MID$(F$,2):GOTO7015 +7020 IFFX$=",p"ORFX$=",s"THENF$=LEFT$(F$,LEN(F$)-2):X$=FX$+",r" +7025 IFF$=""THENRETURN +7027 OPEN1,UN,15:OPEN8,UN,8,F$+X$ +7028 INPUT#1,E:IFE<>0THENCLOSE8:CLOSE1:PRINT"{reverse on}{red}Failed to open "+F$+"{reverse off}";CO$:RETURN +7029 PRINTP$:PRINT:CC$="c":GOSUB1300:IFE=1THENRETURN +7030 P$="STOR "+F$:GOSUB600 +7040 PRINT#5,"atc";MID$(STR$(SP),2):GOSUB900:IFP$<>"ok"THEN7040 +7050 Y=0:Y$="":TT=0 +7100 GET#5,A$:IFST=0ANDA$<>""THENGOSUB900:GOTO7100 +7110 SYSML+12:X=PEEK(MV):XE=PEEK(MV+1) +7120 IFLEN(P$)=0THEN7100 +7130 TT=TT+LEN(P$):GOSUB6500:GOSUB650:IFXE>0THEN7250 +7150 GOSUB800 +7200 IFP0=PANDLEFT$(P$,3)="550"THENPRINTP$:GOTO7250 +7210 IFP0=PANDP$=""THEN7100 +7215 IFP0=PANDLEFT$(P$,4)="150 "THENPRINTLEFT$("{up}",SGN(TT))+P$:PRINT:GOTO7100 +7220 IFP0=PTHENPRINTP$:PRINT:GOTO7100 +7230 IFP0=SPTHENPRINT"?!":GOTO7100 +7240 GOTO7100 +7250 GET#5,A$:IFST=0ANDA$<>""THENGOSUB900:GOTO7250 +7260 PRINT "{reverse on}{light green}";MID$(STR$(TT),2);" bytes transferred.{reverse off}";CO$ +7265 GET#5,A$:IFA$<>""THEN7265 +7270 CLOSE8:CLOSE1:PRINT#5,"ath"+MID$(STR$(SP),2):GOSUB900:IFP$<>"ok"THEN7265 +7272 TT=TI+200 +7275 GOSUB800:IFP$=""ANDTI""THEN7280 +7290 PRINT#5,"atc";MID$(STR$(P),2):GOSUB900:IFP$<>"ok"THEN7290 +7299 RETURN +8000 P$=MID$(P$,5):IFVAL(P$)<8ORVAL(P$)>16THEN8020 +8010 UN=VAL(P$):PRINT"Current drive is now";UN:GOTO2000 +8020 OPEN1,UN,15,"cd "+P$:INPUT#1,E,E$,E1,E2:PRINTE,E$,E1,E2:CLOSE1:GOTO2000 +8100 P$=MID$(P$,6) +8120 OPEN1,UN,15,"s0:"+P$:INPUT#1,E,E$,E1,E2:PRINTE,E$,E1,E2:CLOSE1:GOTO2000 +8200 P$=MID$(P$,6):IFLEN(P$)>0THENP$=":"+P$ +8210 OPEN8,UN,0,"$"+P$ +8220 GET#8,A$,A$ +8230 GET#8,A$,A$:IFST>0THENX=FRE(0):CLOSE8:PRINT:GOTO2000 +8240 GET#8,A$,B$:X=ASC(A$+CHR$(0))+256*ASC(B$+CHR$(0)):PRINTX; +8250 GET#8,A$:IFA$=""THENPRINTCHR$(13);:GOTO8230 +8260 GETB$:IFB$=" "THENCLOSE8:PRINT:GOTO2000 +8270 PRINTA$;:GOTO8250 +9000 TT=TI+100 +9010 SYSML+12:IFTI226THEN9140 +9120 X=PEEK(56834):POKE56834,X+1:IFPEEK(56834)<>X+1THEN9140 +9130 BC=15:SY=227:POKE56834,X:RETURN +9140 PRINTCHR$(14);"Requires a C64 with SwiftLink/Link232":STOP +50000 OPEN5,2,0,CHR$(8) +50010 GET#5,A$:IFA$<>""THENPRINTA$; +50020 GETA$:IFA$<>""THENPRINT#5,A$; +50030 GOTO 50010 +55555 U=8:F$="ftp":OPEN1,U,15,"s0:"+F$:CLOSE1:SAVE(F$),U:VERIFY(F$),U diff --git a/cbm8bit/src/ftp64-128-vic.bas b/cbm8bit/src/ftp64-128-vic.bas index d6604b1..62a1343 100644 --- a/cbm8bit/src/ftp64-128-vic.bas +++ b/cbm8bit/src/ftp64-128-vic.bas @@ -5,7 +5,7 @@ !- Commodore 64 !-------------------------------------------------- 1 REM FTP64/128 1200B 2.0+ -2 REM UPDATED 08/19/2017 02:39A +2 REM UPDATED 09/02/2021 02:39A 5 POKE254,8:IFPEEK(186)>7THENPOKE254,PEEK(186) 10 SY=PEEK(65532):IFSY=61THENPOKE58,254:CLR 12 IFSY=34THENX=23777:POKEX,170:IFPEEK(X)<>170THENPRINT"<16k":STOP @@ -26,14 +26,14 @@ 101 REM 102 REM 110 REM -120 PRINTCO$;"{clear}{down*2}FTP v1.7":PRINT"Requires C64Net WiFi firmware 2.0+" +120 PRINTCO$;"{clear}{down*2}FTP v1.8":PRINT"Requires C64Net WiFi firmware 2.0+" 140 PRINT"By Bo Zimmerman (bo@zimmers.net)":PRINT:PRINT 199 REM ---- ZIMODEM SETUP 200 UN=PEEK(254):IP$="":CR$=CHR$(13)+CHR$(10) 201 PH=0:PT=0:MV=ML+18 202 PRINT "Initializing modem..." 203 GET#5,A$:IFA$<>""THEN203 -205 PRINT#5,CR$;"athz0f0e0";CR$;:GOSUB900:IFP$<>"ok"THEN203 +205 PRINT#5,CR$;"athz0&p0f0e0";CR$;:GOSUB900:IFP$<>"ok"THEN203 208 GET#5,A$:IFA$<>""THEN208 210 PRINT#5,CR$;"ate0n0r0v1q0";CR$; 220 GOSUB900:IFP$<>"ok"THEN208 @@ -107,9 +107,10 @@ 1000 QU$=CHR$(34):REM BEGIN! 1010 AT$="":IFXB>0ANDBA>0ANDXB<>BATHENAT$="s43="+MID$(STR$(XB),2) 1020 GET#5,A$:IFA$<>""THEN1020 -1100 PRINT#5,"ath"+AT$+"&d10&m13&m10cp";QU$;HO$;":";MID$(STR$(PO),2);QU$ +1100 PRINT#5,"ath"+AT$+"&d10&m13&m10cp";QU$;HO$;":";MID$(STR$(PO),2);QU$:E=0 1105 GOSUB900:SP=0:IFP$="ok"THEN1020 1110 IFLEN(P$)>8ANDLEFT$(P$,8)="connect "THENP=VAL(MID$(P$,9)):GOTO1200 +1112 IFLEN(P$)=0ANDE<3THENE=E+1:GOTO1105 1115 PRINT"{pink}{reverse on}Unable to connect to ";HO$;" port";PO;"{reverse off}";CO$:GOTO300 1120 IT=TI+40 1130 P9=0:GOSUB800:IFP$<>""THENIT=TI+10 diff --git a/cbm8bit/src/irc-sw64.bas b/cbm8bit/src/irc-sw64.bas new file mode 100644 index 0000000..3ac31ea --- /dev/null +++ b/cbm8bit/src/irc-sw64.bas @@ -0,0 +1,213 @@ +!-------------------------------------------------- +!- Tuesday, August 17, 2021 9:45:22 AM +!- Import of : +!- z:\lost+found\irc.prg +!- Commodore 64 +!-------------------------------------------------- +1 REM IRC64SW 19200B 1.8+ +2 REM UPDATED 10/13/2021 12:54A +10 POKE254,PEEK(186):IFPEEK(254)<8THENPOKE254,8 +12 GOSUB9200:IFSY=61THENPOKE58,254:CLR +13 IFSY=34THENX=23777:POKEX,170:IFPEEK(X)<>170THENPRINT"<16k":STOP +14 IFSY=227ANDPEEK(51201)=3THENSYS51200 +15 OPEN5,2,0,CHR$(BC):IFPEEK(65532)=34THENPOKE56,87:POKE54,87:POKE52,87 +17 DIMPP$(25):P$="ok":POKE186,PEEK(254):BA=1200:XB=1200 +20 CR$=CHR$(13):PRINTCHR$(14);:GOSUB9200:POKE53280,254:POKE53281,246 +30 CO$="{light blue}":IFSY=226THENML=49152:POKE665,73-(PEEK(678)*30):UM=ML+2048:XB=9600 +33 IFSY=227THENML=49152:UM=ML+2048 +35 IFSY=34THENML=22273:IFPEEK(ML)<>76THENCLOSE5:LOAD"pmlvic.bin",PEEK(254),1:RUN +38 IFSY=34THENPOKE36879,27:CO$=CHR$(31) +40 IFSY=226ANDPEEK(ML+1)<>209THENCLOSE5:LOAD"pml64.bin",PEEK(254),1:RUN +43 IFSY=227ANDPEEK(ML+1)<>209THENCLOSE5:LOAD"pml64.bin",PEEK(254),1:RUN +44 IFSY=227ANDPEEK(51201)<>3THENCLOSE5:LOAD"swiftdrvr.bin",PEEK(254),1:RUN +45 IFSY=226ANDUM>0ANDPEEK(UM+1)<>24THENCLOSE5:LOAD"up9600.bin",PEEK(254),1:RUN +50 IFSY=61THENML=4864:POKE981,15:S8=PEEK(215)AND128:IFS8=128THENSYS30643 +60 IFSY=61ANDPEEK(ML+1)<>217THENCLOSE5:LOAD"pml128.bin",PEEK(254),1:RUN +70 IFSY=61ANDS8=128THENXB=2400:CO$=CHR$(159) +80 IFSY=226ANDUM>0THENSYSUM:SYSUM+3:X=PEEK(789):SYSUM+9:IFX=234THENXB=1200 +90 IFSY<>34THENPOKE56579,0:REM WHY DOES THIS WORK +100 REM GET THE BAUD RATE +110 P$="a" +120 PRINTCO$;"{clear}{down*2}IRC CHAT v1.5":PRINT"Requires Zimodem firmware 1.8+" +140 PRINT"By Bo Zimmerman (bo@zimmers.net)":PRINT:PRINT +197 REM -------------------------------- +198 REM GET STARTED ! +199 REM ------------------------------- +200 UN=PEEK(254):IP$="":CR$=CHR$(13)+CHR$(10) +201 PH=0:PT=0:MV=ML+18 +202 PRINT "Initializing modem..." +203 GET#5,A$:IFA$<>""THEN203 +205 PRINT#5,CR$;"athz0&p0f0e0";CR$;:GOSUB900:IFP$<>"ok"THEN203 +208 GET#5,A$:IFA$<>""THEN208 +210 PRINT#5,CR$;"ate0n0r0v1q0";CR$; +220 GOSUB900:IFP$<>"ok"THEN208 +230 PRINT#5,"ate0v1x1f3q0s40=250i4";CR$;CHR$(19); +235 GOSUB900:VR=VAL(P$):IFVR<1.8THENPRINT"Zimodem init failed: ";P$:STOP +240 GOSUB900:IFP$<>"ok"THEN203 +300 REM GET THE SERVER +310 PRINT:PRINT"{down}Some servers:" +320 PRINT"irc.nlnog.net port 6667, #c-64" +325 PRINT"irc.freenode.net port 6667, #c64friends" +330 PRINT"irc.us.ircnet.net port 6667, #c-64" +350 SE$="":PRINT:PRINT"What is your IRC server host":GOSUB5000:SE$=P$ +360 IFSE$=""THENPRINT"I guess you're done then":CLOSE5:STOP +370 PRINT"What is the port":GOSUB5000:PO$=P$:IFVAL(PO$)<=0THENSE$="":GOTO360 +380 SE$=SE$+":"+PO$ +390 GET#5,A$:IFA$<>""THEN390 +400 REM MAKE THE CONNECTION +405 AT$="":IFXB>0ANDBA>0ANDXB<>BATHENAT$="s43="+MID$(STR$(XB),2) +406 GET#5,A$:IFA$<>""THEN406 +410 QU$=CHR$(34):PRINT#5,"at"+AT$+"h&d13&m13&m10cp";QU$;SE$;QU$ +415 GOSUB900 +420 IFLEFT$(P$,8)<>"connect "THENPRINT"Unable to connect to ";SE$;":";P$:GOTO300 +425 P=VAL(MID$(P$,8)) +427 IFXB<>9600THEN430 +428 SYSUM:SYSUM+3:IFPEEK(789)=234THENSYSUM+9:GOTO435 +429 BA=XB:POKEUM+19,1:PRINT#5,"at":GOSUB9000:GOSUB9000 +430 IFXB<>2400THEN435 +431 NP=0:IFPEEK(2614)>0THENNP=20 +432 BA=XB:POKE2576,10:POKE2578,PEEK(59490+NP):POKE2579,PEEK(59491+NP) +433 POKE2582,170:POKE2583,1:IFNP>0THENPOKE2582,154 +435 PRINT#5,"at":GOSUB9000:GOSUB9000 +440 PRINT:PRINT"What is your nickname";:GOSUB5000:NI$=P$:N1=LEN(NI$) +450 IFNI$=""THENPRINT"I guess not.":STOP +460 P$="NICK "+NI$:GOSUB600 +470 P$="USER guest 0 * :Joe Anonymous":GOSUB600 +490 PRINT"{light green}{reverse on}Connected, wait for MOTD. ? for help{reverse off}";CO$ +500 GOTO 1000: REM GO START MAIN LP +598 REM --- TRANSMIT P$ TO THE OPEN SOCKET +600 IFLEN(P$)>0ANDASC(RIGHT$(P$,1))=10THENP$=LEFT$(P$,LEN(P$)-1) +605 OP$=P$:SYSML+9:C8$=MID$(STR$(PEEK(MV+8)),2)::E$="ok":IFVR>3THENE$=C8$ +610 PRINT#5,"ats42=";C8$;"t+";QU$;P$;QU$ +620 SYSML:IFP$<>E$THENP$=OP$:PRINT"xmit fail";CC$:GOTO600 +630 RETURN +698 REM --- GET NEXT FROM SOCKET INTO PP +700 GOSUB930:IFP0<>PANDP0>0THENPRINT"Unexpected packet id: ";P0;"/";P:STOP +710 IFP0=0THENRETURN +720 PP$(PE)=P$:PE=PE+1:IFPE>=25THENPE=0 +790 P$="":RETURN +798 REM --- GET P$ FROM SOCKET P +800 E=0:P$="":IFPH>=25THENPH=0 +810 IFPH<>PETHENP$=PP$(PH):PH=PH+1:RETURN +820 GOSUB700:IFP0=0THENE=1:RETURN:REM FAIL +830 IFPH<>PETHENP$=PP$(PH):PH=PH+1:RETURN +840 E=1:RETURN +898 REM ---- GET E$ FROM MODEM, OR ERROR +900 E$="" +910 SYSML +920 IFE$<>""ANDP$<>E$THENPRINT"{reverse on}{red}Comm error. Expected ";E$;", Got ";P$;CO$;"{reverse off}" +925 RETURN +928 REM GET PACKET HEADER, SETS P0,P1,P2, RETURNS P0=0 IF NADA +930 SYSML+12:E=0:P0=0:P1=0:P2=0:PR=0:I=0:I0=0:I1=0:CR=0:P4=0:P5=0:C8=0 +940 P$="":PRINT#5,CHR$(17); +945 SYSML+6:P0=PEEK(MV+2):P1=PEEK(MV+4):P2=PEEK(MV+6) +950 PL=PEEK(MV+0):CR=PEEK(MV+1):C8=PEEK(MV+8) +960 IFP0>0ANDP2<>C8THEN985 +970 IFP1=0THENP$="" +980 IFP0>0ORCR=0THENRETURN +985 SYSML+12:P$="" +990 PRINT"{yellow}PACKET-RETRY";CO$:PRINT#5,"atl":GOTO945 +995 PRINT"Expected ";E$;", got ";A$:STOP +998 REM THE MAIN LOOP +1000 GETA$:IFA$<>""THEN3000 +1020 GOSUB800:IFP$=""THEN1000 +1030 MS$="":MC$="":MA$="":I=1:LP$=P$ +1040 IFMID$(P$,1,1)<>":"THEN1100 +1050 IFI>LEN(P$)THEN1100 +1060 IFMID$(P$,I,1)<>" "THENI=I+1:GOTO1050 +1070 MS$=MID$(P$,1,I-1):P$=MID$(P$,I+1) +1100 I=1:IFP$=""THEN1000 +1110 IFI>LEN(P$)THEN1200 +1120 IFMID$(P$,I,1)<>" "THENI=I+1:GOTO1110 +1130 MC$=MID$(P$,1,I-1):P$=MID$(P$,I+1) +1200 MA$=P$:IFMC$=""THEN1000 +1210 A=ASC(MID$(MC$,1,I)):IFA<48ORA>57THEN1300 +1220 IFLEN(MC$)>1ANDMID$(MC$,1,1)="0"THENMC$=MID$(MC$,2):GOTO1220 +1230 C=VAL(MC$):IFC>430ANDC<460THENPRINT"Bad Nick given(";C;") : "+MA$:GOTO440 +1240 IFC>400THENPRINT"Error: "+MA$:GOTO1000 +1250 IFLEN(MA$)>N1+1ANDMID$(MA$,1,N1)=NI$THENMA$=MID$(MA$,LEN(NI$)+2) +1260 PRINT MA$:GOTO1000 +1300 REM CMDS +1310 IFMC$="PING"THEN1400 +1320 IFMC$="MODE"THEN1000 +1330 IFMC$="PRIVMSG"THEN2000 +1340 IFMC$="VERSION"THEN2100 +1350 IFMC$="QUIT"THEN2200 +1360 IFMC$="JOIN"THEN2300 +1370 IFMC$="PONG"THEN1000 +1390 REM +1397 PRINTLP$:GOTO1000 +1398 PRINT"Unknown: '"+MC$+"' '"+MA$+"'" +1399 GOTO1000 +1400 REM ... +1410 OM$=MA$:P$="PONG :"+MA$:GOSUB600:E$="":GOTO1000 +2000 I=1:IFMA$=""THEN1000 +2010 IFMID$(MA$,I,2)=" :"THEN2050 +2020 I=I+1:GOTO2010 +2050 GOSUB2900 +2090 PRINT"{reverse on}";MS$;"{reverse off}: ";MID$(MA$,I+2):GOTO1000 +2100 PRINT"{reverse on}";LP$;"{reverse off}":GOTO1000 +2200 GOSUB2900:PRINT"{pink}{reverse on}";MS$;" has left the channel.{reverse off}";CO$:GOTO1000 +2300 GOSUB2900:PRINT"{light green}{reverse on}";MS$;" has joined the channel.{reverse off}";CO$:GOTO1000 +2900 II=2 +2910 IFII>LEN(MS$)THENII=LEN(MS$):GOTO2950 +2920 IFMID$(MS$,II,1)="!"THENII=II-1:GOTO2950 +2930 II=II+1:GOTO2910 +2950 MS$=MID$(MS$,1,II):RETURN +3000 IFA$=CHR$(13)THEN1000 +3010 PRINT"{reverse on}{pink}Stream paused. Enter ? for help.{reverse off}" +3020 IFOM$<>""THENP$="PING "+OM$:GOSUB600 +3090 PRINTCO$;">{reverse on} {reverse off}{left}";:IN$="":IT=TI+1000:GOTO3200 +3100 IFTI>ITTHENPRINT"{reverse on}{red}Cancelled{reverse off}{pink}":PRINT:GOTO1000 +3150 GETA$:IFA$=""THEN3100 +3170 IT=TI+1000 +3200 IFA$=CHR$(13)THEN3300 +3210 IFA$<>CHR$(20)ORLEN(IN$)=0THEN3230 +3220 IN$=LEFT$(IN$,LEN(IN$)-1):PRINT" {left*2} {left}{reverse on} {reverse off}{left}";:GOTO3100 +3230 A=ASC(A$):IFA<32OR(A>95ANDA<193)ORA>218THEN3100 +3240 IN$=IN$+A$:PRINTA$;"{reverse on} {reverse off}{left}";:GOTO3100 +3300 IFIN$=""THENPRINT" ":PRINT:GOTO1000 +3305 IFIN$="?"THENPRINT" ":GOTO3400 +3310 IFMID$(IN$,1,1)="/"THENIN$=MID$(IN$,2):GOTO3500 +3320 IFCC$=""THENPRINT:PRINT"{reverse on}{red}No Channel Selected! Try ?{reverse off}";CO$:GOTO1000 +3330 P$="PRIVMSG "+CC$+" :"+IN$ +3340 GOSUB600:PRINT" ":PRINT"{reverse on}";NI$;"{reverse off}: ";IN$ +3350 E$="":PRINT:GOTO1000 +3400 PRINTCO$;"{reverse off}------------------------------" +3410 PRINT"{reverse on}{purple}/join #c-64{reverse off} ";CO$;"Change channels." +3420 PRINT"{reverse on}{purple}/quit{reverse off} ";CO$;"Logout and exit." +3430 REMPRINT"{reverse on}{purple}/list{reverse off} ";CO$;"List channels." +3440 REMPRINT"{reverse on}{purple}/who mask*{reverse off} ";CO$;"User info." +3480 PRINT"{reverse on}{purple}Anything else{reverse off} ";CO$;"Send message" +3490 PRINTCO$;"{reverse off}------------------------------":GOTO1000 +3500 X=0:FORI=2TOLEN(IN$):IFX=0ANDMID$(IN$,I,1)=" "THENX=I +3510 NEXT:A$="":IFX>1THENA$=MID$(IN$,X+1):IN$=MID$(IN$,1,X-1) +3520 PRINT +3530 IFIN$="join"THEN4000 +3540 IFIN$="quit"THENP$="QUIT :":GOSUB600:GOTO9100 +3550 IFIN$="list"THENP$="LIST":GOSUB600:GOTO1000 +3560 IFIN$="who"THENP$="WHO :"+AA$:GOSUB600:GOTO1000 +3999 PRINT"{reverse on}{red}Unknown Command: ";IN$;". Try ?{reverse off}";CO$:GOTO1000 +4000 PRINT"Joining ";QU$;A$;QU$:AA$=A$ +4100 P$="JOIN :"+AA$:GOSUB600 +4120 E$="":CC$=AA$:GOTO1000 +5000 PRINT"? {reverse on} {reverse off}{left}";:P$="" +5010 GETA$:IFA$=""THEN5010 +5020 IFA$=CHR$(13)THENPRINT" {left}":RETURN +5030 IFA$<>CHR$(20)THENPRINTA$;"{reverse on} {reverse off}{left}";:P$=P$+A$:GOTO5010 +5040 IFP$=""THEN5010 +5050 P$=LEFT$(P$,LEN(P$)-1):PRINT" {left*2} {left}{reverse on} {reverse off}{left}";:GOTO5010 +9000 TT=TI+100 +9010 SYSML+12:IFTI226THEN9240 +9220 X=PEEK(56834):POKE56834,X+1:IFPEEK(56834)<>X+1THEN9240 +9230 BC=15:SY=227:POKE56834,X:RETURN +9240 PRINTCHR$(14);"Requires a C64 with SwiftLink/Link232":STOP +50000 OPEN5,2,0,CHR$(8) +50010 GET#5,A$:IFA$<>""THENPRINTA$; +50020 GETA$:IFA$<>""THENPRINT#5,A$; +50030 GOTO 50010 +55555 U=8:F$="irc":OPEN1,U,15,"s0:"+F$:CLOSE1:SAVEF$,U:VERIFYF$,U diff --git a/cbm8bit/src/irc64-128-vic.bas b/cbm8bit/src/irc64-128-vic.bas index 1179bca..e1f7400 100644 --- a/cbm8bit/src/irc64-128-vic.bas +++ b/cbm8bit/src/irc64-128-vic.bas @@ -5,7 +5,7 @@ !- Commodore 64 !-------------------------------------------------- 1 REM IRC64/128 1200B 1.8+ -2 REM UPDATED 08/19/2017 02:58A +2 REM UPDATED 10/13/2021 02:58A 10 POKE254,PEEK(186):IFPEEK(254)<8THENPOKE254,8 12 SY=PEEK(65532):IFSY=61THENPOKE58,254:CLR 13 IFSY=34THENX=23777:POKEX,170:IFPEEK(X)<>170THENPRINT"<16k":STOP @@ -33,7 +33,7 @@ 201 PH=0:PT=0:MV=ML+18 202 PRINT "Initializing modem..." 203 GET#5,A$:IFA$<>""THEN203 -205 PRINT#5,CR$;"athz0f0e0";CR$;:GOSUB900:IFP$<>"ok"THEN203 +205 PRINT#5,CR$;"athz0&p0f0e0";CR$;:GOSUB900:IFP$<>"ok"THEN203 208 GET#5,A$:IFA$<>""THEN208 210 PRINT#5,CR$;"ate0n0r0v1q0";CR$; 220 GOSUB900:IFP$<>"ok"THEN208 diff --git a/cbm8bit/src/pml4.asm b/cbm8bit/src/pml4.asm new file mode 100644 index 0000000..567a52a --- /dev/null +++ b/cbm8bit/src/pml4.asm @@ -0,0 +1,469 @@ + + + +* = 4096 + ;.D PML+4.BIN +; PACKET ML +4 BY BO ZIMMERMAN +; UPDATED 2018/08/25 10:14P + JMP BUF1LIN + JMP BUFXLIN + JMP GETPACKET + JMP CRCP + JMP BUFAP + NOP + NOP + NOP +NUMX + bytes 0 +CRXFLG + bytes 0 +PEE0 + bytes 0,0 +PEE1 + bytes 0,0 +PEE2 + bytes 0,0 +CRC8 + bytes 0 +CRCX + bytes 0 +CRCE + bytes 0 +CRCS + bytes 0 +TIMEOUT + bytes 0,0 +BUFAPX + bytes 5 +DEBUG + bytes 0,0 +SETPSTR + LDA $2D + STA $BA + LDA $2E + STA $BB +SETPLP + LDY #$00 + LDA ($BA),Y + CMP #$50 + BNE SETPNXT + INY + LDA ($BA),Y + CMP #$80 + BEQ SETPDUN +SETPNXT + LDA $BA + CLC + ADC #$07 + STA $BA + LDA $BB + ADC #$00 + STA $BB + LDA $BA + CMP $2F + BCC SETPLP + BNE SETPDUN + LDA $BA + CMP $30 + BCC SETPLP +SETPDUN + RTS +GETPACKET + JSR IRQSET + LDX #$0C +PACKCLR + LDA #$00 + STA NUMX,X + DEX + BNE PACKCLR + STA NUMX + JSR $FFCC + LDX #$05 + JSR $FFC6 + JSR BUF1LIN + LDY #$00 + STY CRC8 +PCKLP1 + CPY BUF1DX + BCC PCKC1 +PCKDUN1 + JMP $FFCC +PCKERR1 + LDX #$FF + STX CRXFLG + JMP $FFCC +PCKC1 + LDA BUF1,Y + INY + CMP #91 + BNE PCKLP1 + LDA BUF1,Y + INY + CMP #32 + BNE PCKERR1 + LDA #PEE0 + STA $BD + JSR PCKDIG + BNE PCKERR1 + LDA #PEE1 + STA $BD + JSR PCKDIG + BNE PCKERR1 + LDA #PEE2 + STA $BD + JSR PCKDIG + BNE PCKERR1 + LDA PEE1+1 + BNE PCKERR1 + LDA PEE1 + STA NUMX + BEQ PCKDUN1 +PCKNEWP + JMP BUFXLIN +PCKDIG + CPY BUF1DX + BCC PCKDIG2 +DIGERR1 + LDX #$FF + RTS +PCKDIG2 + LDA BUF1,Y + INY +PCKDIG3 + CMP #48 + BCC DIGERR1 + CMP #58 + BCS DIGERR1 + TAX + TYA + PHA + TXA + PHA + LDY #$00 + LDA ($BC),Y + STA $BA + INY + LDA ($BC),Y + STA $BB + LDX #$09 +DIGLP + LDY #$00 + LDA ($BC),Y + CLC + ADC $BA + STA ($BC),Y + INY + LDA ($BC),Y + ADC $BB + STA ($BC),Y + DEX + BNE DIGLP + LDY #$00 + PLA + SEC + SBC #48 + CLC + ADC ($BC),Y + STA ($BC),Y + INY + LDA #$00 + ADC ($BC),Y + STA ($BC),Y + PLA + TAY + CPY BUF1DX + BCS DIGERR2 + LDA BUF1,Y + INY + CMP #32 + BNE PCKDIG3 + LDA #$00 + RTS +DIGERR2 + LDA #$FF + RTS +BUFCLR + LDA #$00 + TAY +BUFCLP + STA BUF1,Y + INY + BNE BUFCLP + RTS +DOCRC8 + LDA #$00 + STA CRC8 + LDA #$00 + STA CRCX +DOCRC0 + LDA CRCX + CMP BUF1DX + BCC DOCRC1 + RTS +DOCRC1 + TAX + LDA BUF1,X + STA CRCE + LDY #$08 +DOCRC2 + LDA CRC8 + EOR CRCE + AND #$01 + STA CRCS + LDA CRC8 + CLC + ROR + STA CRC8 + LDA CRCS + BEQ DOCRC3 + LDA CRC8 + EOR #$8C + STA CRC8 +DOCRC3 + LDA CRCE + CLC + ROR + STA CRCE + DEY + BNE DOCRC2 + INC CRCX + BNE DOCRC0 + RTS +CRCP + JSR SETPSTR + LDY #$02 + LDA ($BA),Y + STA BUF1DX + LDY #$03 + LDA ($BA),Y + STA $BC + LDY #$04 + LDA ($BA),Y + STA $BD + LDY #$00 +CRCPL1 + CPY BUF1DX + BCS CRCPDUN + LDA ($BC),Y + STA BUF1,Y + INY + BNE CRCPL1 +CRCPDUN + JMP DOCRC8 +RETIME + LDA $A4 + STA TIMEOUT + LDA #$00 + STA TIMEOUT+1 + RTS +CHKTIM + LDA $A4 + CMP TIMEOUT + BNE CHKTIM2 + LDX #$00 + RTS +CHKTIM2 + STA TIMEOUT + INC TIMEOUT+1 + LDA TIMEOUT+1 + CMP #$04 + BCS TIMBAD + LDX #$00 + RTS +TIMBAD + LDX #$FF + RTS +BUF1LIN + JSR IRQSET + LDX #$05 + JSR $FFC6 + LDY #$00 + TYA + STA BUF1DX + JSR RETIME +BUF1LP + JSR CHKTIM + BEQ BUF1L2 + STX CRXFLG + JMP $FFCC +BUF1L2 + LDA $07D1 + CMP $07D2 + BEQ BUF1LP + JSR $FFE4 + LDY BUF1DX + STA BUF1,Y + CMP #$00 + BEQ BUF1LP + CMP #$0A + BEQ BUF1LP + CMP #$0D + BEQ BUF1DUN + JSR RETIME + INC BUF1DX + LDA BUF1DX + CMP #$FE + BCC BUF1LP +BUF1DUN + JSR SETPSTR + LDY #$02 + LDA BUF1DX + STA ($BA),Y + LDY #$03 + LDA #BUF1 + STA ($BA),Y + JSR DOCRC8 + JMP $FFCC +BUFXLIN + JSR IRQSET + LDA #$00 + STA CRXFLG + STA BUF1DX + LDX #$05 + JSR $FFC6 + JSR RETIME +BUFXLP + JSR $FFE4 + LDY BUF1DX + STA BUF1,Y + JSR CHKTIM + BEQ BUFXEC + STX CRXFLG + JMP $FFCC +BUFXEC + JSR $FFB7 + AND #$0B + BNE BUFXLP + JSR RETIME + LDY BUF1DX + LDA BUF1,Y + DEC NUMX + CMP #$0D + BNE BUFXNCR + LDX CRXFLG + BNE BUFXNCR + STY CRXFLG + INC CRXFLG +BUFXNCR + INC BUF1DX + LDA BUF1DX + CMP #$FD + BCS BUFXDUN +BUF1NI + LDA NUMX + BNE BUFXLP + JMP BUF1DUN +BUFXDUN + JMP BUF1DUN +BUFAP + JSR IRQSET + LDX BUFAPX + JSR $FFC6 + LDY #$00 + TYA + STA BUF1DX + STA CRXFLG +BUFALP + LDA BUFAPX + CMP #$05 + BNE BUFAL2 + LDA $07D3 + BEQ BUFAX +BUFAL2 + JSR $FFE4 + LDY BUF1DX + STA BUF1,Y + LDA BUFAPX + CMP #$05 + BEQ BUFAP1 +BUFAPST + JSR $FFB7 + CMP #$08 + BEQ BUFAX + CMP #$42 + BEQ BUFAX + CMP #$00 + BEQ BUFAP1 + INC BUF1DX +BUFAX + STA CRXFLG + JMP BUF1DUN +BUFAP1 + INC BUF1DX + LDA BUF1DX + CMP #$FE + BCC BUFALP + JMP BUF1DUN +IRQSET + LDA OLDIRQ + BEQ IRQSE1 + JMP PREFLOW +IRQSE1 + SEI + LDA $0314 + STA OLDIRQ + LDA $0315 + STA OLDIRQ+1 + LDA #IRQRTN + STA $0315 + CLI + RTS +IRQRTN + LDA $9A + BEQ IRQRT2 + LDA OLDIRQ + STA $0314 + LDA OLDIRQ+1 + STA $0315 + LDA #$00 + STA OLDIRQ + JMP $CE0E +IRQRT2 + JSR DOFLOW + JMP $CE0E +PREFLOW + LDA #$00 + STA $FC + STA $FD +DOFLOW + LDA $07D3 + BNE DOFLO3 + LDA $FD10 + AND #$02 + BNE DOFLOO + LDA $FD10 + ORA #$02 + STA $FD10 +DOFLOO + RTS +DOFLO3 + CMP #$3C + BCC DOFLOO + LDA $FD10 + AND #$02 + BEQ DOFLOO + LDA $FD10 + AND #$FD + STA $FD10 + RTS +OLDIRQ + bytes 0,0 +BUFTMR + bytes 0 +BUF1DX + bytes 0 +BUFX + bytes 0,0 +BUF1 + bytes 0 diff --git a/cbm8bit/src/swiftdrvr.asm b/cbm8bit/src/swiftdrvr.asm index c64b600..b2546b3 100644 --- a/cbm8bit/src/swiftdrvr.asm +++ b/cbm8bit/src/swiftdrvr.asm @@ -1,8 +1,6 @@ - - -* = 49152 - ; .D SWIFTDRVR.BIN - ; BAUD RATES +* = $C800 + ;.D SWIFTDRVR.BIN + ; KERNAL BAUD RATES ; B50=1 ; B75=2 ; B110=3 @@ -18,210 +16,265 @@ ; B7200=13 ; B9600=14 ; B19200=15 - ; ;;;;;;;;; + ; B38400=16 + ; B57600=17 + ; B115200=18 + ; B230400=19 +;;;;;;;;;;; BASE = $DE00 DATAPORT = $DE00 STATUS = $DE01 COMMAND = $DE02 CONTROL = $DE03 - ; -RHEAD = $A7 -RTAIL = $A8 +ESR232 = $DE07 +; +RHEAD = 668 +RTAIL = 667 RBUFF = $F7 -RCOUNT = $B4 -ERRORS = $B5 -ZPXMO = $B6 -ZPXMF = $BD NMINV = $0318 - ; ;;;;;;;;; +;;;;;;;;;;; JMP INIT - ; INITCHIP ; NEWVEC - ; RECVBYTE - ; XMITBYTE - ; ;;;;;;;;; +; INITCHIP ; NEWVEC +; RECVBYTE +; XMITBYTE +;;;;;;;;;;; INIT SEI + LDA $031B + CMP #>DOOPEN + BEQ NOINIT + LDY #20 +INSAV + LDA $0319,Y + STA VECS,Y + DEY + BNE INSAV + LDA $031A + STA DOOPEN+1 + LDA $031B + STA DOOPEN+2 + LDA $031C + STA DOCLOSE+1 + LDA $031D + STA DOCLOSE+2 + LDA $0326 + STA DOPUT2+1 + LDA $0327 + STA DOPUT2+2 + LDA $032C + STA DOCLAL+1 + LDA $032D + STA DOCLAL+2 +NOINIT LDA #DOOPEN STA $031B - LDA #DOCLOSE - STA $031D - LDA #DOCHRIN - STA $0325 - LDA #>DOGETIN - STA $032B - LDA #DOPUT - STA $0327 CLI RTS - ; ;;;;;;;;; +;;;;;;;;;;; DOOPEN JSR $F34A; CALL IOPEN - PHA - TYA + PHP PHA LDA $BA CMP #$02 BEQ DOOP2 - PLA; EARLY EXIT - TAY - PLA + PLA;EARLY EXIT + PLP RTS DOOP2 - NOP - LDA #$00 - STA RHEAD - STA RTAIL - STA RCOUNT - STA ERRORS - ; LDA #RECVBUFF:;TA RBUFF+1 - ; INTERNAL CLOCK, 8N1, BAUD BELOW + TYA + PHA LDY #$00 + STY RHEAD + STY RTAIL + STY RCOUNT + STY ERRORS +; INTERNAL CLOCK, 8N1, BAUD BELOW +;;DA #>RECVBUFF:;TA RBUFF+1 +; INTERNAL CLOCK, 8N1, BAUD BELOW + STY ESR232 LDA ($BB),Y - ORA #%00010000 + TAY + LDA BAUDS,Y + BPL DOOP3 + LDY #16 + STY CONTROL + AND #$7F + STA ESR232 + LDA #$00 +DOOP3 + ORA #16;%00010000 STA CONTROL - ; NO PAR, NO ECHO, NO XMIT INT, YES RECV INT - LDA #%00001001 ; +; NO PAR, NO ECHO, XMIT INT, RECV INT + LDA #9; %00001001 ; STA COMMAND STA ZPXMF - AND #%11110000 ; KEEP PARITY/ECHO - ORA #%00001001 ; SET RECV BUF ONLY + AND #%11110000; KEEP PARITY/ECHO + ORA #9;%00001001 ; SET RECV BUF ONLY STA ZPXMO NEWVEC SEI + LDA NMINV+1 + CMP #>NEWNMI + BEQ NONVEC + STA OLDNMI+1 + LDA NMINV + STA OLDNMI LDA #NEWNMI STA NMINV+1 +NONVEC CLI + LDA #DOCLOSE + STA $031D + LDA #DOPUT + STA $0327 + LDA #>DOCLAL + STA $032C + LDA #NEWNMI + BNE NOCLOS SEI - LDA #$47 + LDA OLDNMI STA NMINV - LDA #$FE + LDA OLDNMI+1 STA NMINV+1 - CLI - LDX #$00 + TYA + PHA + LDY #20 +INCLO + LDA VECS,Y + STA $0319,Y + DEY + BNE INCLO + PLA + TAY + JSR NOINIT; DOOPEN, AND CLI! +NOCLOS + JSR UPDCD + PLA + PLP RTS - ; ;;;;;;;;;;;;;;;; -PRINT - NOP; :OPEN1,8,15,"S0:SWIFTDRVR*":CLOSE1:SAVE"SWIFTDRVR",8 +DOCLAL + JSR $F32F + PHP + PHA + JMP DOCLOS2 +;;;;;;;;;;;;;;;;;; +BAUDS + BYTE 9,0,0,1,2,2,5,6,7,8,8,9,10,11,12,14,15,130,129,128,0,0,0,0,0,0 +VECS + BYTE 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +OLDNMI + BYTE 71,254 +RCOUNT + BYTE 0 +ERRORS + BYTE 0 +ZPXMO + BYTE 0 +ZPXMF + BYTE 0 diff --git a/cbm8bit/src/telnet-sw.bas b/cbm8bit/src/telnet-sw.bas new file mode 100644 index 0000000..b2b0944 Binary files /dev/null and b/cbm8bit/src/telnet-sw.bas differ diff --git a/cbm8bit/src/telnet-sw64.bas b/cbm8bit/src/telnet-sw64.bas new file mode 100644 index 0000000..0e278d0 --- /dev/null +++ b/cbm8bit/src/telnet-sw64.bas @@ -0,0 +1,174 @@ +!-------------------------------------------------- +!- Tuesday, August 17, 2021 9:45:40 AM +!- Import of : +!- z:\lost+found\telnet.prg +!- Commodore 64 +!-------------------------------------------------- +1 REM TELNET64SW 19200B 1.8+ +2 REM UPDATED 10/13/2021 12:54A +10 POKE254,PEEK(186):IFPEEK(254)<8THENPOKE254,8 +12 GOSUB9100:IFSY=61THENPOKE58,254:CLR +13 IFSY=34THENX=23777:POKEX,170:IFPEEK(X)<>170THENPRINT"<16k":STOP +14 IFSY=227ANDPEEK(51201)=3THENSYS51200 +15 OPEN5,2,0,CHR$(BC):IFPEEK(65532)=34THENPOKE56,87:POKE54,87:POKE52,87 +17 DIMPP$(25):P$="ok":POKE186,PEEK(254):BA=1200:XB=1200 +20 CR$=CHR$(13):PRINTCHR$(14);:GOSUB9100:POKE53280,254:POKE53281,246 +30 CO$="{light blue}":IFSY=226THENML=49152:POKE665,73-(PEEK(678)*30) +33 IFSY=227THENML=49152 +35 IFSY=34THENML=22273:IFPEEK(ML)<>76THENCLOSE5:LOAD"pmlvic.bin",PEEK(254),1:RUN +38 IFSY=34THENPOKE36879,27:CO$=CHR$(31) +40 IFSY=226ANDPEEK(ML+1)<>209THENCLOSE5:LOAD"pml64.bin",PEEK(254),1:RUN +43 IFSY=227ANDPEEK(ML+1)<>209THENCLOSE5:LOAD"pml64.bin",PEEK(254),1:RUN +45 IFSY=227ANDPEEK(51201)<>3THENCLOSE5:LOAD"swiftdrvr.bin",PEEK(254),1:RUN +50 IFSY=61THENML=4864:POKE981,15:S8=PEEK(215)AND128:IFS8=128THENSYS30643 +60 IFSY=61ANDPEEK(ML+1)<>217THENCLOSE5:LOAD"pml128.bin",PEEK(254),1:RUN +61 IFSY=61ANDS8=128THENXB=2400:CO$=CHR$(159) +70 TM=828:IFSY=61THENTM=2816 +80 I=TM:DD=56577:IFSY=34THENDD=37136 +90 READA%:IFSY=61AND(A%=155ORA%=156)THENREADB%:POKEI,A%-131:I=I+1:A%=10 +95 IFA%>=0THENPOKEI,A%:I=I+1:GOTO90 +100 REM +101 REM +102 REM +110 P$="a" +120 PRINTCO$;"{clear}{down*2}TELNET v1.5":PRINT"Requires Zimodem firmware 1.8+" +130 PRINT"19200 baud version" +140 PRINT"By Bo Zimmerman (bo@zimmers.net)":PRINT:PRINT +197 REM -------------------------------- +198 REM GET STARTED ! +199 REM ------------------------------- +200 UN=PEEK(254) +201 PH=0:PT=0:MV=ML+18:CR$=CHR$(13)+CHR$(10):QU$=CHR$(34) +202 PRINT "Initializing modem...";:GOSUB6000 +203 GET#5,A$:IFA$<>""THEN203 +205 PRINT#5,CR$;"athz0&p0f0e0";CR$; +206 GOSUB900:IFP$<>"ok"THEN203 +208 GET#5,A$:IFA$<>""THEN208 +210 PRINT".";:PRINT#5,CR$;"ate0n0r0v1f0";CR$; +220 GOSUB900:IFP$<>"ok"THEN208 +230 PRINT".";:PRINT#5,"ate0v1x1q0f0";CR$;CHR$(19); +240 GOSUB900:IFP$<>"ok"THENPRINT"Zimodem init failed: ";P$:STOP +250 PRINT"!":DIM HO$(30):DIM PO(30):DIM HM$(30) +255 HO$(0)="coffeemud.net":PO(0)=23:HZ=1 +260 OPEN1,UN,15:OPEN8,UN,8,"telnetphonebook,s,r" +270 INPUT#1,E:IFE<>0THENCLOSE8:CLOSE1:GOTO300 +280 INPUT#8,HZ:FORI=1TOHZ:INPUT#8,HO$(I-1):INPUT#8,PO(I-1) +285 INPUT#8,H$:HM$(I-1)=MID$(H$,2) +290 NEXTI:CLOSE8:CLOSE1 +300 GOTO 1000 +897 REM -------------------------------- +898 REM GET E$ FROM MODEM, OR ERROR ! +899 REM ------------------------------- +900 E$="" +910 SYSML +920 IFE$<>""ANDP$<>E$THENPRINT"{reverse on}{red}Comm error. Expected ";E$;", Got ";P$;CO$;"{reverse off}" +925 RETURN +997 REM -------------------------------- +998 REM THE MAIN LOOP ! +999 REM ------------------------------- +1000 PRINT:PRINTCO$;"Main Menu:" +1010 PRINT" 1) Dial from Phonebook" +1020 PRINT" 2) Modify Phonebook" +1030 PRINT" 3) Quick Connect" +1035 PRINT" 4) Terminal mode" +1040 PRINT" 9) Quit" +1050 PRINT:PRINT"Enter a number: {reverse on} {reverse off}{left}"; +1060 GOSUB5000:IFP$=""THEN1000 +1070 P=VAL(P$):IFP=9THENCLOSE5:OPEN1,UN,15,"i0":CLOSE1:END +1080 IFP<1ORP>4THEN1050 +1090 PRINT +1100 IFP=1THENGOSUB2000:GOTO1000 +1110 IFP=2THENGOSUB3000:GOTO1000 +1120 IFP=3THENGOSUB4000:GOTO1000 +1124 IFP=4THENPRINT"{reverse on}{light green}Terminal mode. ";:GOSUB2430:GOTO1000 +1130 PRINT"?!":GOTO1000 +2000 PRINT:PRINT"{down}Dial from Phonebook:" +2020 FORI=1TOHZ +2030 PRINT STR$(I)+") ";HO$(I-1);":";MID$(STR$(PO(I-1)),2) +2080 NEXTI:PRINT:PRINT"Enter a number or RETURN: "; +2090 GOSUB5000:IFP$=""THENRETURN +2100 X=VAL(P$):IFX<1ORX>HZTHEN2000 +2110 HO$=HO$(X-1):PO=PO(X-1) +2300 PRINT"{reverse on}{light green}Connecting to ";HO$;":";MID$(STR$(PO),2);"...{reverse off}";CO$ +2310 GET#5,A$:IFA$<>""THEN2310 +2320 PRINT#5,CR$;"atf3hctep";QU$;HO$;":";MID$(STR$(PO),2);QU$;CR$; +2330 GOSUB900:IFLEN(P$)>7ANDLEFT$(P$,7)="connect"THEN2400 +2340 PRINT"{reverse on}{red}Unable to connect to ";HO$;":";MID$(STR$(PO),2) +2350 RETURN +2400 PRINT"{reverse on}{light green}* Connected. "; +2420 PRINT#5,CR$;"atf0o";CR$; +2430 IFSY=226THENPRINT"Hit F1 to exit.{light gray}" +2440 IFSY=61THENPRINT"Hit ESC to exit.{light gray}" +2450 POKE53280,0:POKE53281,0:IFSY=34THENPOKE36879,8 +2453 SYSTM +2457 POKE53280,254:POKE53281,246:IFSY=34THENPOKE36879,27 +2460 PRINT:PRINTCO$;CHR$(14);"{reverse off}Hanging up...";:GOSUB6000 +2470 PRINT:FORI=1TO100:GET#5,A$:NEXTI +2480 GET#5,A$:IFA$<>""THEN2480 +2490 RETURN +3000 PRINT:PRINT"{down}Modify Phonebook:" +3020 FORI=1TOHZ +3030 PRINT STR$(I)+") ";HO$(I-1);":";MID$(STR$(PO(I-1)),2):NEXTI +3040 PRINTSTR$(HZ+1)+") Add New Entry" +3080 PRINT:PRINT"Enter a number or RETURN: "; +3090 GOSUB5000:IFP$=""THENRETURN +3100 X=VAL(P$):IFX<1ORX>HZ+1THEN3000 +3110 PRINT"Enter hostname/ip: ";:GOSUB5000:H$=P$:IFP$=""THEN3000 +3120 PRINT"Enter port number (23): ";:GOSUB5000:HP=VAL(P$):IFHP<=0THENHP=23 +3130 HO$(X-1)=H$:PO(X-1)=HP:IFX=HZ+1THENHZ=HZ+1 +3140 OPEN1,UN,15:OPEN8,UN,8,"@0:telnetphonebook,s,w" +3150 INPUT#1,E:IFE<>0THENCLOSE8:CLOSE1:GOTO300 +3160 PRINT#8,HZ:FORI=1TOHZ:PRINT#8,HO$(I-1):PRINT#8,PO(I-1) +3165 PRINT#8,"-"+HM$(I-1) +3170 NEXTI:CLOSE8:CLOSE1: GOTO 3000 +4000 PRINT:PRINT"Enter hostname/ip: ";:GOSUB5000:H$=P$:IFP$=""THENRETURN +4010 PRINT"Enter port number (23): ";:GOSUB5000:HP=VAL(P$):IFHP<=0THENHP=23 +4020 HO$=H$:PO=HP:GOTO2300 +4999 STOP +5000 P$="" +5005 PRINT"{reverse on} {reverse off}{left}"; +5010 GETA$:IFA$=""THEN5010 +5020 A=ASC(A$):IFA=13THENPRINT" {left}":RETURN +5025 IFA=34THENPRINTA$;A$;"{left}{reverse on} {reverse off}{left}";:P$=P$+A$:GOTO5010 +5030 IFA<>20THENPRINTA$;"{reverse on} {reverse off}{left}";:P$=P$+A$:GOTO5010 +5040 IFP$=""THEN5010 +5050 P$=LEFT$(P$,LEN(P$)-1):PRINT" {left*2} {left}{reverse on} {reverse off}{left}";:GOTO5010 +6000 IF(PEEK(DD)AND16)=0THENRETURN +6010 GOSUB6100 +6020 PRINT#5,"+++";:PRINT"."; +6030 GOSUB6100 +6040 PRINT".";:PRINT#5,"ath":TT=TI+150 +6050 SYSML+12:IFTI226THEN9140 +9120 X=PEEK(56834):POKE56834,X+1:IFPEEK(56834)<>X+1THEN9140 +9130 BC=15:SY=227:POKE56834,X:RETURN +9140 PRINTCHR$(14);"Requires a C64 with SwiftLink/Link232":STOP +39999 STOP +40000 OPEN1,8,15:OPEN8,8,8,"telnetml.bin,p,r":LN=41000:DN=0 +40010 INPUT#1,E:IFE<>0THENCLOSE8:CLOSE1:STOP +40020 GET#8,A$:IFST>0THENCLOSE8:END +40030 IFA$=""THENA$=CHR$(0) +40035 A=ASC(A$):A$=MID$(STR$(A),2) +40040 IFDN=0THENPRINTMID$(STR$(LN),2);" data ";A$;:DN=DN+1:GOTO40020 +40060 PRINT",";A$;:DN=DN+1 +40070 IFDN=18THENPRINT:DN=0:LN=LN+10 +40080 GOTO 40020 +41000 DATA 162,5,32,198,255,32,228,255,201,0,240,61,201,10,240,57 +41010 DATA 32,210,255,173,155,2,205,156,2,240,38,176,13,169,255,56,237,156 +41020 DATA 2,24,109,155,2,56,176,4,56,237,156,2,201,200,144,11,173,1 +41030 DATA 221,41,253,141,1,221,56,176,12,201,20,176,8,173,1,221,9,2 +41040 DATA 141,1,221,162,0,32,198,255,32,228,255,201,0,240,171,201,133,240 +41050 DATA 4,201,27,208,4,32,204,255,96,72,162,5,32,201,255,104,32,210 +41060 DATA 255,162,0,32,201,255,56,176,141 +41999 DATA -1 +49999 STOP +50000 OPEN5,2,0,CHR$(8) +50010 GET#5,A$:IFA$<>""THENPRINTA$; +50020 GETA$:IFA$<>""THENPRINT#5,A$; +50030 GOTO 50010 +55555 U=8:F$="telnet":OPEN1,U,15,"s0:"+F$:CLOSE1:SAVE(F$),U:VERIFY(F$),U diff --git a/cbm8bit/src/telnet64-128-vic.bas b/cbm8bit/src/telnet64-128-vic.bas index 9a3521d..10383be 100644 --- a/cbm8bit/src/telnet64-128-vic.bas +++ b/cbm8bit/src/telnet64-128-vic.bas @@ -5,7 +5,7 @@ !- Commodore 64 !-------------------------------------------------- 1 REM TELNET64/128 1200B 1.8+ -2 REM UPDATED 08/15/2017 12:54A +2 REM UPDATED 09/02/2022 12:54A 10 POKE254,PEEK(186):IFPEEK(254)<8THENPOKE254,8 12 SY=PEEK(65532):IFSY=61THENPOKE58,254:CLR 13 IFSY=34THENX=23777:POKEX,170:IFPEEK(X)<>170THENPRINT"<16k":STOP @@ -27,7 +27,7 @@ 101 REM 102 REM 110 P$="a" -120 PRINTCO$;"{clear}{down*2}TELNET v1.5":PRINT"Requires C64Net WiFi firmware 1.8+" +120 PRINTCO$;"{clear}{down*2}TELNET v1.6":PRINT"Requires C64Net WiFi firmware 1.8+" 130 PRINT"1200 baud version" 140 PRINT"By Bo Zimmerman (bo@zimmers.net)":PRINT:PRINT 197 REM -------------------------------- @@ -37,7 +37,7 @@ 201 PH=0:PT=0:MV=ML+18:CR$=CHR$(13)+CHR$(10):QU$=CHR$(34) 202 PRINT "Initializing modem...";:GOSUB6000 203 GET#5,A$:IFA$<>""THEN203 -205 PRINT#5,CR$;"athz0f0e0";CR$; +205 PRINT#5,CR$;"athz0&p0f0e0";CR$; 206 GOSUB900:IFP$<>"ok"THEN203 208 GET#5,A$:IFA$<>""THEN208 210 PRINT".";:PRINT#5,CR$;"ate0n0r0v1f0";CR$; @@ -76,7 +76,7 @@ 1100 IFP=1THENGOSUB2000:GOTO1000 1110 IFP=2THENGOSUB3000:GOTO1000 1120 IFP=3THENGOSUB4000:GOTO1000 -1124 IFP=4THENPRINT"{reverse on}{light green}Terminal mode. ";:GOSUB2430:GOTO1000 +1124 IFP=4THENPRINT#5,CR$;"at&p1";CR$;:GOSUB2430:GOTO1000 1130 PRINT"?!":GOTO1000 2000 PRINT:PRINT"{down}Dial from Phonebook:" 2020 FORI=1TOHZ @@ -92,9 +92,10 @@ 2340 PRINT"{reverse on}{red}Unable to connect to ";HO$;":";MID$(STR$(PO),2) 2350 RETURN 2400 PRINT"{reverse on}{light green}* Connected. "; -2420 PRINT#5,CR$;"atf0o";CR$; +2420 PRINT#5,CR$;"at0o";CR$; 2430 IFSY=226THENPRINT"Hit F1 to exit.{light gray}" 2440 IFSY=61THENPRINT"Hit ESC to exit.{light gray}" +2445 PRINT"{reverse on}{light green}Terminal mode.{light gray}";CR$; 2450 POKE53280,0:POKE53281,0:IFSY=34THENPOKE36879,8 2453 SYSTM 2457 POKE53280,254:POKE53281,246:IFSY=34THENPOKE36879,27 @@ -133,7 +134,7 @@ 6010 GOSUB6100 6020 PRINT#5,"+++";:PRINT"."; 6030 GOSUB6100 -6040 PRINT".";:PRINT#5,"ath":TT=TI+150 +6040 PRINT".";:PRINT#5,"ath&p0":TT=TI+150 6050 SYSML+12:IFTI76THENCLOSE5:LOAD"pml64.bin",PEEK(186),1:RUN 12 MV=ML+18:GET#5,A$:IFA$<>""THEN12 -13 PRINT#5,"athz0e0r0":INPUT#5,P$:IFP$<>"ok"THENTR=TR+1:IFTR<8THEN12 +13 PRINT#5,"athz0&p0e0r0":INPUT#5,P$:IFP$<>"ok"THENTR=TR+1:IFTR<8THEN12 14 PRINT#5,"atn0v1x1f3q0s40=250";CHR$(19):INPUT#5,P$:IFP$="ok"THEN20 15 TR=TR+1:ONTRGOTO14,14,14,14,14,14,14,14,14,17 17 PRINT"no modem detected?!":CLOSE5:STOP diff --git a/cbm8bit/src/wget-sw64.bas b/cbm8bit/src/wget-sw64.bas new file mode 100644 index 0000000..ea778ff --- /dev/null +++ b/cbm8bit/src/wget-sw64.bas @@ -0,0 +1,188 @@ +!-------------------------------------------------- +!- Tuesday, August 17, 2021 9:46:11 AM +!- Import of : +!- z:\lost+found\wget.prg +!- Commodore 64 +!-------------------------------------------------- +1 REM WGET4SW 19200B 2.0+ +2 REM UPDATED 10/13/2021 12:54A +10 POKE254,PEEK(186):IFPEEK(254)<8THENPOKE254,8 +12 GOSUB9100:IFSY=61THENPOKE58,254:CLR +13 IFSY=34THENX=23777:POKEX,170:IFPEEK(X)<>170THENPRINT"<16k":STOP +14 IFSY=227ANDPEEK(51201)=3THENSYS51200 +15 OPEN5,2,0,CHR$(BC):IFPEEK(65532)=34THENPOKE56,87:POKE54,87:POKE52,87 +17 P$="ok":POKE186,PEEK(254):XB=1200:BA=1200 +20 CR$=CHR$(13):PRINTCHR$(14);:GOSUB9100:POKE53280,254:POKE53281,246 +30 CO$="{light blue}":IFSY=226THENML=49152:POKE665,73-(PEEK(678)*30):UM=ML+2048:XB=9600 +33 IFSY=227THENML=49152:UM=ML+2048 +35 IFSY=34THENML=22273:IFPEEK(ML)<>76THENCLOSE5:LOAD"pmlvic.bin",PEEK(254),1:RUN +38 IFSY=34THENPOKE36879,27:CO$=CHR$(31) +40 IFSY=226ANDPEEK(ML+1)<>209THENCLOSE5:LOAD"pml64.bin",PEEK(254),1:RUN +43 IFSY=227ANDPEEK(ML+1)<>209THENCLOSE5:LOAD"pml64.bin",PEEK(254),1:RUN +45 IFSY=226ANDUM>0ANDPEEK(UM+1)<>24THENCLOSE5:LOAD"up9600.bin",PEEK(254),1:RUN +47 IFSY=227ANDPEEK(51201)<>3THENCLOSE5:LOAD"swiftdrvr.bin",PEEK(254),1:RUN +50 IFSY=61THENML=4864:POKE981,15:S8=PEEK(215)AND128:IFS8=128THENSYS30643 +55 IFSY=61ANDS8=128THENXB=2400:CO$=CHR$(159) +60 IFSY=61ANDPEEK(ML+1)<>217THENCLOSE5:LOAD"pml128.bin",PEEK(254),1:RUN +80 IFSY=226ANDUM>0THENSYSUM:SYSUM+3:X=PEEK(789):SYSUM+9:IFX=234THENXB=1200 +100 IFSY<>34THENPOKE56579,0 +101 REM +102 REM +110 P$="a" +120 PRINTCO$;"{clear}{down*2}WGET v1.5":PRINT"Requires Zimodem firmware 2.0+" +140 PRINT"By Bo Zimmerman (bo@zimmers.net)":PRINT:PRINT +198 REM --- MODEM INIT +200 UN=PEEK(254) +201 PH=0:PT=0:MV=ML+18 +202 PRINT "Initializing modem...":CR$=CHR$(13)+CHR$(10) +203 GET#5,A$:IFA$<>""THEN203 +205 PRINT#5,CR$;CR$;"athz0&p0f0e0";CR$;:GOSUB900:IFP$<>"ok"THEN203 +208 GET#5,A$:IFA$<>""THEN208 +210 PRINT#5,CR$;"ate0n0r0v1q0";CR$; +220 GOSUB900:IFP$<>"ok"THEN208 +225 GET#5,A$:IFA$<>""THEN225 +230 PRINT#5,"ate0v1x1f3q0s40=248i4";CR$;CHR$(19); +235 GOSUB900:VR=VAL(P$):IFVR<2.0THENPRINT"Zimodem init failed: ";P$:STOP +240 GOSUB900:IFP$<>"ok"THEN203 +250 DIM HH$(20):UR$="www.zimmers.net/index.html":OT$="":OF$="@0:wget-output,s" +300 REM GET INFO +310 PRINT:PRINT"{down}Request Parms:" +320 PRINT " *) Type{space*7}: GET" +321 PRINT " 1) Url{space*8}: http://";UR$ +322 PRINT " 2) Output Unit:";UN +323 PRINT " 3) Output File: ";OF$ +329 LW=3 +330 NH=0:FORI=0TO20:IFLEN(HH$(I))>0THENNH=NH+1:PRINTSTR$(LW+NH)+") "+HH$(I):NEXT +340 LH=LW+NH+1:PRINTSTR$(LW+1+NH)+") Add New Header" +370 IFUR$=""THENPRINT:P$="1":GOTO400 +380 PRINT:PRINT"Type a number or RETURN{sh space}to begin:"; +390 GOSUB5000:IFP$=""THEN1000 +400 X=VAL(P$):IFX<1ORX>LHTHEN300 +410 IFX=1THENPRINT"Enter URL: http://";:GOSUB5000:UR$=P$:GOTO300 +420 IFX=2THENPRINT"Enter output device/unit: ";:GOSUB5000:UN=VAL(P$):GOTO300 +430 IFX=3THENPRINT"Enter output file: ";:GOSUB5000:OF$=P$:GOTO300 +440 IFX<>LHTHENP$=HH$(X-LW):PRINT"Modify: ";P$:GOSUB5005:HH$(X-LW)=P$:GOTO300 +450 II=-1:FORI=0TO20:IFHH$(I)=""ANDII<0THENII=I +460 NEXT:IFII>=0THENPRINT"New Header: ";:GOSUB5000:HH$(II)=P$ +470 GOTO 300 +598 REM --- TRANSMIT P$ TO THE OPEN SOCKET +600 OP$=P$:SYSML+9:C8$=MID$(STR$(PEEK(MV+8)),2):E$="ok":IFVR>3THENE$=C8$ +610 PRINT#5,"ats42=";C8$;"tp+";QU$;P$;QU$ +620 SYSML:IFP$<>E$THENP$=OP$:PRINT"{yellow}{reverse on}Retrying..{reverse off}";CO$:GOTO600 +630 RETURN +650 OP$=P$:SYSML+9:C8$=MID$(STR$(PEEK(MV+8)),2):PN$=MID$(STR$(LEN(P$)),2) +655 E$="ok":IFVR>3THENE$=C8$ +660 PRINT#5,"ats42=";C8$;"t+";PN$:PRINT#5,P$ +670 SYSML:IFP$<>E$THENP$=OP$:PRINT"{yellow}{reverse on}Retrying..{reverse off}";CO$:GOTO650 +680 RETURN +798 REM --- GET P$ FROM SOCKET P +800 P$="":E=0 +810 GOSUB930:IFP0<>PANDP0<0THENPRINT"Unexpected packet id: ";P0;"/";P:STOP +820 IFP0=0THENE=1:RETURN:REM FAIL +830 RETURN +898 REM --- GET E$ FROM MODEM, OR ERROR +900 E$="" +910 SYSML +920 IFE$<>""ANDP$<>E$THENPRINT"{reverse on}{red}Comm error. Expected ";E$;", Got ";P$;CO$;"{reverse off}" +925 RETURN +928 REM --- GET PACKET HEADER, SETS P0,P1,P2, RETURNS P0=0 IF NADA +930 PR=0:GET#5,P$:IFP$<>""THEN930 +940 PRINT#5,CHR$(17); +945 SYSML+6:P0=PEEK(MV+2):P1=PEEK(MV+4):P2=PEEK(MV+6) +950 PL=PEEK(MV+0):CR=PEEK(MV+1):C8=PEEK(MV+8) +960 IFP0>0ANDP2<>C8THEN985 +970 IFP1=0THENP$="" +980 IFP0>0ORCR=0THENRETURN +985 GET#5,P$:IFP$<>""THEN985 +990 PRINT"{yellow}PACKET-RETRY";CO$:PRINT#5,"atl":GOTO945 +995 PRINT"Expected ";E$;", got ";A$:STOP +998 REM --- THE MAIN LOOP ! +1000 IFLEN(UR$)=0ORLEN(OF$)=0THEN300 +1010 I=0 +1020 IFMID$(UR$,I+1,1)<>"/"THENI=I+1:IFI":"THENI=I+1:IFI=LEN(H1$)THENH1$=H1$+":80" +1070 AT$="":IFXB>0ANDBA>0ANDXB<>BATHENAT$="s43="+MID$(STR$(XB),2) +1080 GET#5,A$:IFA$<>""THEN1080 +1100 QU$=CHR$(34):PRINT#5,"ath"+AT$+"&d10&m10&m13c";QU$;H1$;QU$ +1105 GOSUB900 +1110 IFLEN(P$)>8ANDLEFT$(P$,8)="connect "THENP=VAL(MID$(P$,9)):GOTO1200 +1115 PRINT"{pink}{reverse on}Unable to connect to ";H1$;"{reverse off}";CO$:GOTO300 +1120 IT=TI+40 +1130 P9=0:GOSUB800:IFP$<>""THENIT=TI+10 +1140 IFTI9600THEN1205 +1202 SYSUM:SYSUM+3:IFPEEK(789)=234THENSYSUM+9:GOTO1210 +1203 BA=XB:POKEUM+19,1:PRINT#5,"at":GOSUB6000:GOSUB6000 +1205 IFXB<>2400THEN1210 +1206 NP=0:IFPEEK(2614)>0THENNP=20 +1207 BA=XB:POKE2576,10:POKE2578,PEEK(59490+NP):POKE2579,PEEK(59491+NP) +1208 POKE2582,170:POKE2583,1:IFNP>0THENPOKE2582,154 +1210 PRINT"{reverse on}{light green}Sending request...{reverse off}";CO$:PRINT#5,"at":GOSUB6000:GOSUB6000 +1215 P$="GET "+P1$+" HTTP/1.1":GOSUB600 +1220 P$="Host: "+HO$:GOSUB600 +1225 P$="Connection: Keep-Alive":GOSUB600 +1230 P$="User-Agent: C=WGET":GOSUB600 +1235 P$="Content-length: 0":GOSUB600 +1240 P$="Accept: */*":GOSUB600 +1300 FORI=0TO20 +1310 IFHH$(I)=""THEN1330 +1320 P$=HH$(I):GOSUB900 +1330 NEXTI +1950 P$=CHR$(13)+CHR$(10):GOSUB650 +1970 PRINT"{reverse on}{light green}Request sent. Reading response...{reverse off}";CO$ +2000 P9=0:GOSUB800:IFP$=""THEN2000 +2010 LE=0:FB=0 +2020 IFLEN(P$)<13THENPRINT"{reverse on}{pink}Bad response: {reverse off}";CO$;P$:STOP +2030 IFLEFT$(P$,5)<>"http/"THENPRINT"{reverse on}{red}Bad response{reverse off}";CO$;P$:STOP +2040 RC=VAL(MID$(P$,10,3)) +2050 IFRC<>200THENPRINT"{reverse on}{red}Bad response code:{reverse off}";CO$;RC:STOP +2100 GOSUB800:IFP$=""ANDE=0THEN2200 +2120 IFLEN(P$)<17ORLEFT$(P$,1)<>"c"ORMID$(P$,9,1)<>"l"THEN2100 +2130 IFMID$(P$,15,1)<>":"THEN2100 +2140 I=15 +2160 I=I+1:IFMID$(P$,I,1)=" "THEN2160 +2170 X=VAL(MID$(P$,I)):IFX>0THENCL=X +2180 GOTO2100 +2200 TL=0:PRINT#5,CR$;"at&m&dc";MID$(STR$(P),2);CR$; +2210 GOSUB900:IFP$<>"ok"THENPRINT"Zimodem command failed: ";P$:STOP +2240 IFCL=0THENPRINT"{reverse on}{pink}Headers complete. No content. Done.{reverse off}";CO$:END +2250 PRINT"{reverse on}{light green}Headers complete." +2260 PRINT"{reverse on}Receiving"+STR$(CL)+" bytes{reverse off}";CO$ +2270 OPEN1,UN,15:OPEN8,UN,8,OF$+",w" +2280 INPUT#1,E:IFE>0THENPRINT"{reverse on}{red}Unable to open "+OF$+": "+STR$(E)+"{reverse off}";CO$:STOP +2290 REM +2300 GOSUB930:IFP0<>PTHEN2300 +2310 IFP1=0ORP0=0THEN2300 +2330 TL=TL+LEN(P$) +2340 IFLEN(P$)>0THENPRINT#8,P$; +2350 TP=INT(TL/CL*100.0) +2360 PRINT"{space*15}{left*15}"+STR$(TL)+" ("+STR$(TP)+"% )" +2370 PRINT"{up}";:IFTLCHR$(20)THENPRINTA$;"{reverse on} {reverse off}{left}";:P$=P$+A$:GOTO5010 +5040 IFP$=""THEN5010 +5050 P$=LEFT$(P$,LEN(P$)-1):PRINT" {left*2} {left}{reverse on} {reverse off}{left}";:GOTO5010 +6000 TT=TI+100 +6010 SYSML+12:IFTI226THEN9140 +9120 X=PEEK(56834):POKE56834,X+1:IFPEEK(56834)<>X+1THEN9140 +9130 BC=15:SY=227:POKE56834,X:RETURN +9140 PRINTCHR$(14);"Requires a C64 with SwiftLink/Link232":STOP +50000 OPEN5,2,0,CHR$(8) +50010 GET#5,A$:IFA$<>""THENPRINTA$; +50020 GETA$:IFA$<>""THENPRINT#5,A$; +50030 GOTO 50010 +55555 D=8:F$="wget":OPEN1,D,15,"s0:"+F$:CLOSE1:SAVE(F$),D:VERIFY(F$),D diff --git a/cbm8bit/src/wget64-128-vic.bas b/cbm8bit/src/wget64-128-vic.bas index 6090aa0..9ccf43b 100644 --- a/cbm8bit/src/wget64-128-vic.bas +++ b/cbm8bit/src/wget64-128-vic.bas @@ -5,7 +5,7 @@ !- Commodore 64 !-------------------------------------------------- 1 REM WGET4/128 1200B 2.0+ -2 REM UPDATED 08/19/2017 02:54A +2 REM UPDATED 10/13/2021 12:54A 10 POKE254,PEEK(186):IFPEEK(254)<8THENPOKE254,8 12 SY=PEEK(65532):IFSY=61THENPOKE58,254:CLR 13 IFSY=34THENX=23777:POKEX,170:IFPEEK(X)<>170THENPRINT"<16k":STOP @@ -32,7 +32,7 @@ 201 PH=0:PT=0:MV=ML+18 202 PRINT "Initializing modem...":CR$=CHR$(13)+CHR$(10) 203 GET#5,A$:IFA$<>""THEN203 -205 PRINT#5,CR$;CR$;"athz0f0e0";CR$;:GOSUB900:IFP$<>"ok"THEN203 +205 PRINT#5,CR$;CR$;"athz0&p0f0e0";CR$;:GOSUB900:IFP$<>"ok"THEN203 208 GET#5,A$:IFA$<>""THEN208 210 PRINT#5,CR$;"ate0n0r0v1q0";CR$; 220 GOSUB900:IFP$<>"ok"THEN208 diff --git a/esp32_prod_bin/boot_app0.bin b/esp32_prod_bin/boot_app0.bin new file mode 100644 index 0000000..13562ca Binary files /dev/null and b/esp32_prod_bin/boot_app0.bin differ diff --git a/esp32_prod_bin/bootloader_qio_80m.bin b/esp32_prod_bin/bootloader_qio_80m.bin new file mode 100644 index 0000000..12180e8 Binary files /dev/null and b/esp32_prod_bin/bootloader_qio_80m.bin differ diff --git a/esp32_prod_bin/zimodem.ino.partitions.bin b/esp32_prod_bin/zimodem.ino.partitions.bin new file mode 100644 index 0000000..eabbf0c Binary files /dev/null and b/esp32_prod_bin/zimodem.ino.partitions.bin differ diff --git a/logo.jpg b/logo.jpg new file mode 100644 index 0000000..9a3ead4 Binary files /dev/null and b/logo.jpg differ diff --git a/zimodem/connSettings.h b/zimodem/connSettings.h new file mode 100644 index 0000000..4e922b4 --- /dev/null +++ b/zimodem/connSettings.h @@ -0,0 +1,47 @@ +/* + Copyright 2016-2019 Bo Zimmerman + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +enum ConnFlag +{ + FLAG_DISCONNECT_ON_EXIT=1, + FLAG_PETSCII=2, + FLAG_TELNET=4, + FLAG_ECHO=8, + FLAG_XONXOFF=16, + FLAG_SECURE=32, + FLAG_RTSCTS=64 +}; + +class ConnSettings +{ + public: + boolean petscii = false; + boolean telnet = false; + boolean echo = false; + boolean xonxoff = false; + boolean rtscts = false; + boolean secure = false; + + ConnSettings(int flagBitmap); + ConnSettings(const char *dmodifiers); + ConnSettings(String modifiers); + int getBitmap(); + int getBitmap(FlowControlType forceCheck); + void setFlag(ConnFlag flagMask, boolean newVal); + String getFlagString(); + + static void IPtoStr(IPAddress *ip, String &str); + static IPAddress *parseIP(const char *ipStr); +}; diff --git a/zimodem/connSettings.ino b/zimodem/connSettings.ino new file mode 100644 index 0000000..0163833 --- /dev/null +++ b/zimodem/connSettings.ino @@ -0,0 +1,139 @@ +/* + Copyright 2016-2019 Bo Zimmerman + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +ConnSettings::ConnSettings(int flagBitmap) +{ + petscii = (flagBitmap & FLAG_PETSCII) > 0; + telnet = (flagBitmap & FLAG_TELNET) > 0; + echo = (flagBitmap & FLAG_ECHO) > 0; + xonxoff = (flagBitmap & FLAG_XONXOFF) > 0; + rtscts = (flagBitmap & FLAG_RTSCTS) > 0; + secure = (flagBitmap & FLAG_SECURE) > 0; +} + +ConnSettings::ConnSettings(const char *dmodifiers) +{ + petscii=((strchr(dmodifiers,'p')!=null) || (strchr(dmodifiers,'P')!=null)); + telnet=((strchr(dmodifiers,'t')!=null) || (strchr(dmodifiers,'T')!=null)); + echo=((strchr(dmodifiers,'e')!=null) || (strchr(dmodifiers,'E')!=null)); + xonxoff=((strchr(dmodifiers,'x')!=null) || (strchr(dmodifiers,'X')!=null)); + rtscts=((strchr(dmodifiers,'r')!=null) || (strchr(dmodifiers,'R')!=null)); + secure=((strchr(dmodifiers,'s')!=null) || (strchr(dmodifiers,'S')!=null)); +} + +ConnSettings::ConnSettings(String modifiers) : ConnSettings(modifiers.c_str()) +{ +} + +int ConnSettings::getBitmap() +{ + int flagsBitmap = 0; + if(petscii) + flagsBitmap = flagsBitmap | FLAG_PETSCII; + if(telnet) + flagsBitmap = flagsBitmap | FLAG_TELNET; + if(echo) + flagsBitmap = flagsBitmap | FLAG_ECHO; + if(xonxoff) + flagsBitmap = flagsBitmap | FLAG_XONXOFF; + if(rtscts) + flagsBitmap = flagsBitmap | FLAG_RTSCTS; + if(secure) + flagsBitmap = flagsBitmap | FLAG_SECURE; + return flagsBitmap; +} + +int ConnSettings::getBitmap(FlowControlType forceCheck) +{ + int flagsBitmap = getBitmap(); + if(((flagsBitmap & (FLAG_XONXOFF | FLAG_RTSCTS))==0) + &&(forceCheck==FCT_RTSCTS)) + flagsBitmap |= FLAG_RTSCTS; + return flagsBitmap; +} + +String ConnSettings::getFlagString() +{ + String lastOptions =(petscii?"p":""); + lastOptions += (petscii?"p":""); + lastOptions += (telnet?"t":""); + lastOptions += (echo?"e":""); + lastOptions += (xonxoff?"x":""); + lastOptions += (rtscts?"r":""); + lastOptions += (secure?"s":""); + return lastOptions; +} + +void ConnSettings::setFlag(ConnFlag flagMask, boolean newVal) +{ + switch(flagMask) + { + case FLAG_DISCONNECT_ON_EXIT: break; + case FLAG_PETSCII: petscii = newVal; break; + case FLAG_TELNET: telnet = newVal; break; + case FLAG_ECHO: echo = newVal; break; + case FLAG_XONXOFF: xonxoff = newVal; break; + case FLAG_SECURE: secure = newVal; break; + case FLAG_RTSCTS: rtscts = newVal; break; + } +} + +void ConnSettings::IPtoStr(IPAddress *ip, String &str) +{ + if(ip == null) + { + str=""; + return; + } + char temp[20]; + sprintf(temp,"%d.%d.%d.%d",(*ip)[0],(*ip)[1],(*ip)[2],(*ip)[3]); + str = temp; +} + +IPAddress *ConnSettings::parseIP(const char *ipStr) +{ + uint8_t dots[4]; + int dotDex=0; + char *le = (char *)ipStr; + const char *ld = ipStr+strlen(ipStr); + if(strlen(ipStr)<7) + return null; + for(char *e=le;e<=ld;e++) + { + if((*e=='.')||(e==ld)) + { + if(le==e) + break; + *e=0; + String sdot = le; + sdot.trim(); + if((sdot.length()==0)||(dotDex>3)) + { + dotDex=99; + break; + } + dots[dotDex++]=(uint8_t)atoi(sdot.c_str()); + if(e==ld) + le=e; + else + le=e+1; + } + } + if((dotDex!=4)||(*le != 0)) + return null; + return new IPAddress(dots[0],dots[1],dots[2],dots[3]); +} + diff --git a/zimodem/zlog.h b/zimodem/filelog.h similarity index 73% rename from zimodem/zlog.h rename to zimodem/filelog.h index 4120d3c..0706c31 100644 --- a/zimodem/zlog.h +++ b/zimodem/filelog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 Bo Zimmerman + Copyright 2016-2019 Bo Zimmerman Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,18 +14,19 @@ limitations under the License. */ -enum LogMode +enum LogOutputState { - NADA=0, - SocketIn=1, - SocketOut=2, - SerialIn=3, - SerialOut=4 + LOS_NADA=0, + LOS_SocketIn=1, + LOS_SocketOut=2, + LOS_SerialIn=3, + LOS_SerialOut=4 }; static unsigned long expectedSerialTime = 1000; -static bool logFileOpen = false; +static boolean logFileOpen = false; +static bool logFileDebug= false; static File logFile; static void logSerialOut(const uint8_t c); @@ -36,9 +37,12 @@ static void logPrint(const char* msg); static void logPrintln(const char* msg); static void logPrintf(const char* format, ...); static void logPrintfln(const char* format, ...); +static char *TOHEX(const char *s, char *hex, const size_t len); static char *TOHEX(long a); static char *TOHEX(int a); static char *TOHEX(unsigned int a); static char *TOHEX(unsigned long a); +static char *tohex(uint8_t a); static char *TOHEX(uint8_t a); - +static uint8_t FROMHEX(uint8_t a1, uint8_t a2); +static char *FROMHEX(const char *hex, char *s, const size_t len); diff --git a/zimodem/filelog.ino b/zimodem/filelog.ino new file mode 100644 index 0000000..0e36994 --- /dev/null +++ b/zimodem/filelog.ino @@ -0,0 +1,283 @@ +/* + Copyright 2016-2019 Bo Zimmerman + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include + +static char HD[3]; +static char HDL[17]; + +static unsigned long logStartTime = millis(); +static unsigned long lastLogTime = millis(); +static unsigned long logCurCount = 0; +static LogOutputState logOutputState = LOS_NADA; + +static uint8_t FROMHEXDIGIT(uint8_t a1) +{ + a1 = lc(a1); + if((a1 >= '0')&&(a1 <= '9')) + return a1-'0'; + if((a1 >= 'a')&&(a1 <= 'f')) + return 10 + (a1-'a'); + return 0; +} + +static uint8_t FROMHEX(uint8_t a1, uint8_t a2) +{ + return (FROMHEXDIGIT(a1) * 16) + FROMHEXDIGIT(a2); +} + +static char *FROMHEX(const char *hex, char *s, const size_t len) +{ + int i=0; + for(const char *h=hex;*h != 0 && (*(h+1)!=0) && (i> 4) & 0x0f]; + HD[1] = "0123456789ABCDEF"[a & 0x0f]; + HD[2] = 0; + return HD; +} + +static char *tohex(uint8_t a) +{ + HD[0] = "0123456789abcdef"[(a >> 4) & 0x0f]; + HD[1] = "0123456789abcdef"[a & 0x0f]; + HD[2] = 0; + return HD; +} + +static char *TOHEX(unsigned long a) +{ + for(int i=7;i>=0;i--) + { + HDL[i] = "0123456789ABCDEF"[a & 0x0f]; + a = a >> 4; + } + HDL[8] = 0; + char *H=HDL; + if((strlen(H)>2) && (strstr(H,"00")==H)) + H+=2; + return H; +} + +static char *TOHEX(unsigned int a) +{ + for(int i=3;i>=0;i--) + { + HDL[i] = "0123456789ABCDEF"[a & 0x0f]; + a = a >> 4; + } + HDL[4] = 0; + char *H=HDL; + if((strlen(H)>2) && (strstr(H,"00")==H)) + H+=2; + return H; +} + +static char *TOHEX(int a) +{ + return TOHEX((unsigned int)a); +} + +static char *TOHEX(long a) +{ + return TOHEX((unsigned long)a); +} + +static void logInternalOut(const LogOutputState m, const uint8_t c) +{ + if(logFileOpen) + { + if((m != logOutputState) + ||(++logCurCount > DBG_BYT_CTR) + ||((millis()-lastLogTime)>expectedSerialTime)) + { + logCurCount=0; + + logOutputState = m; + rawLogPrintln(""); + switch(m) + { + case LOS_NADA: + break; + case LOS_SocketIn: + rawLogPrintf("%s SocI: ",TOHEX(millis()-logStartTime)); + break; + case LOS_SocketOut: + rawLogPrintf("%s SocO: ",TOHEX(millis()-logStartTime)); + break; + case LOS_SerialIn: + rawLogPrintf("%s SerI: ",TOHEX(millis()-logStartTime)); + break; + case LOS_SerialOut: + rawLogPrintf("%s SerO: ",TOHEX(millis()-logStartTime)); + break; + } + } + lastLogTime=millis(); + rawLogPrint(TOHEX(c)); + rawLogPrint(" "); + } +} + +static void logSerialOut(const uint8_t c) +{ + logInternalOut(LOS_SerialOut,c); +} + +static void logSocketOut(const uint8_t c) +{ + logInternalOut(LOS_SocketOut,c); +} + +static void logSerialIn(const uint8_t c) +{ + logInternalOut(LOS_SerialIn,c); +} + +static void logSocketIn(const uint8_t c) +{ + logInternalOut(LOS_SocketIn,c); +} + +static void logSocketIn(const uint8_t *c, int n) +{ + if(logFileOpen) + { + for(int i=0;i +# define ENC_TYPE_NONE WIFI_AUTH_OPEN +# include +# include +# include +# include "SD.h" +# include "SPI.h" +# include "driver/uart.h" + static HardwareSerial HWSerial(UART_NUM_2); +#else +# include "ESP8266WiFi.h" +# define HWSerial Serial +#endif + #include char petToAsc(char c); diff --git a/zimodem/pet2asc.ino b/zimodem/pet2asc.ino index e9a7552..b2db343 100644 --- a/zimodem/pet2asc.ino +++ b/zimodem/pet2asc.ino @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 Bo Zimmerman + Copyright 2016-2019 Bo Zimmerman Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -23,14 +23,14 @@ unsigned char petToAscTable[256] PROGMEM = { 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, 0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f, 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x5b,0x5c,0x5d,0x5e,0x5f, -0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf, -0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf, +0x60,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f, +0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x7b,0x7c,0x7d,0x7e,0x7f, 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f, 0x90,0x91,0x92,0x0c,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f, 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf, 0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf, 0x60,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f, -0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x7b,0x7c,0x7d,0x7e,0x7f, +0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0xda,0xdb,0xdc,0xdd,0xde,0xdf, 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf, 0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf }; @@ -48,8 +48,8 @@ unsigned char ascToPetTable[256] PROGMEM = { 0x90,0x91,0x92,0x0c,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f, 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf, 0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf, -0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f, -0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f, +0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf, +0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf, 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef, 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff }; @@ -112,11 +112,22 @@ unsigned char ascToPetTable[256] PROGMEM = { /** TELNET CODE: IAC*/ #define TELNET_IAC 255 + +uint8_t streamAvailRead(Stream *stream) +{ + int ct=0; + while((stream->available()==0) + &&(ct++)<250) + delay(1); + return stream->read(); +} + bool handleAsciiIAC(char *c, Stream *stream) { if(*c == 255) { - *c=stream->read(); + *c=streamAvailRead(stream); + logSocketIn(*c); if(*c==TELNET_IAC) { *c = 255; @@ -124,40 +135,69 @@ bool handleAsciiIAC(char *c, Stream *stream) } if(*c==TELNET_WILL) { - char what=stream->read(); + char what=streamAvailRead(stream); + logSocketIn(what); uint8_t iacDont[] = {TELNET_IAC, TELNET_DONT, what}; if(what == TELNET_TERMTYPE) iacDont[1] = TELNET_DO; + for(int i=0;i<3;i++) + logSocketOut(iacDont[i]); stream->write(iacDont,3); return false; } - if((*c==TELNET_WONT)||(*c==TELNET_DONT)) + if(*c==TELNET_DONT) + { + char what=streamAvailRead(stream); + logSocketIn(what); + uint8_t iacWont[] = {TELNET_IAC, TELNET_WONT, what}; + for(int i=0;i<3;i++) + logSocketOut(iacWont[i]); + stream->write(iacWont,3); + return false; + } + if(*c==TELNET_WONT) { - char what=stream->read(); + char what=streamAvailRead(stream); + logSocketIn(what); return false; } if(*c==TELNET_DO) { - char what=stream->read(); + char what=streamAvailRead(stream); + logSocketIn(what); uint8_t iacWont[] = {TELNET_IAC, TELNET_WONT, what}; if(what == TELNET_TERMTYPE) iacWont[1] = TELNET_WILL; + for(int i=0;i<3;i++) + logSocketOut(iacWont[i]); stream->write(iacWont,3); return false; } if(*c==TELNET_SB) { - char what=stream->read(); + char what=streamAvailRead(stream); + logSocketIn(what); char lastC=*c; while(((lastC!=TELNET_IAC)||(*c!=TELNET_SE))&&(*c>=0)) { lastC=*c; - *c=stream->read(); + *c=streamAvailRead(stream); + logSocketIn(*c); } if(what == TELNET_TERMTYPE) { - uint8_t resp[] = {TELNET_IAC,TELNET_SB,TELNET_TERMTYPE,0,'Z','i','m','o','d','e','m',TELNET_IAC,TELNET_SE}; - stream->write(resp,13); + int respLen = termType.length() + 6; + uint8_t buf[respLen]; + buf[0]=TELNET_IAC; + buf[1]=TELNET_SB; + buf[2]=TELNET_TERMTYPE; + buf[3]=(uint8_t)0; + sprintf((char *)buf+4,termType.c_str()); + buf[respLen-2]=TELNET_IAC; + buf[respLen-1]=TELNET_SE; + for(int i=0;iwrite(buf,respLen); return false; } } @@ -170,26 +210,31 @@ bool ansiColorToPetsciiColor(char *c, Stream *stream) { if(*c == 27) { - *c=stream->read(); + *c=streamAvailRead(stream); + logSocketIn(*c); if(*c=='[') { int code1=0; int code2=-1; - *c=stream->read(); + *c=streamAvailRead(stream); + logSocketIn(*c); while((*c>='0')&&(*c<='9')) { code1=(code1*10) + (*c-'0'); - *c=stream->read(); + *c=streamAvailRead(stream); + logSocketIn(*c); } while(*c==';') { - *c=stream->read(); + *c=streamAvailRead(stream); + logSocketIn(*c); if((*c>='0')&&(*c<='9')) code2=0; while((*c>='0')&&(*c<='9')) { code2=(code2*10) + (*c-'0'); - *c=stream->read(); + *c=streamAvailRead(stream); + logSocketIn(*c); } } switch(code1) diff --git a/zimodem/phonebook.h b/zimodem/phonebook.h new file mode 100644 index 0000000..c3471c0 --- /dev/null +++ b/zimodem/phonebook.h @@ -0,0 +1,35 @@ +/* + Copyright 2016-2019 Bo Zimmerman + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +class PhoneBookEntry +{ + public: + unsigned long number; + const char *address; + const char *modifiers; + const char *notes; + PhoneBookEntry *next = null; + + PhoneBookEntry(unsigned long phnum, const char *addr, const char *mod, const char *note); + ~PhoneBookEntry(); + + static void loadPhonebook(); + static void clearPhonebook(); + static void savePhonebook(); + static bool checkPhonebookEntry(String cmd); + static PhoneBookEntry *findPhonebookEntry(long number); + static PhoneBookEntry *findPhonebookEntry(String number); +}; + diff --git a/zimodem/phonebook.ino b/zimodem/phonebook.ino new file mode 100644 index 0000000..57c22ef --- /dev/null +++ b/zimodem/phonebook.ino @@ -0,0 +1,176 @@ +/* + Copyright 2016-2019 Bo Zimmerman + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +PhoneBookEntry::PhoneBookEntry(unsigned long phnum, const char *addr, const char *mod, const char *note) +{ + number=phnum; + address = new char[strlen(addr)+1]; + strcpy((char *)address,addr); + modifiers = new char[strlen(mod)+1]; + strcpy((char *)modifiers,mod); + notes = new char[strlen(note)+1]; + strcpy((char *)notes,note); + + if(phonebook == null) + phonebook = this; + else + { + PhoneBookEntry *last = phonebook; + while(last->next != null) + last = last->next; + last->next = this; + } +} + +PhoneBookEntry::~PhoneBookEntry() +{ + if(phonebook == this) + phonebook = next; + else + { + PhoneBookEntry *last = phonebook; + while((last != null) && (last->next != this)) // don't change this! + last = last->next; + if(last != null) + last->next = next; + } + freeCharArray((char **)&address); + freeCharArray((char **)&modifiers); + freeCharArray((char **)¬es); + next=null; +} + +void PhoneBookEntry::loadPhonebook() +{ + clearPhonebook(); + if(SPIFFS.exists("/zphonebook.txt")) + { + File f = SPIFFS.open("/zphonebook.txt", "r"); + while(f.available()>0) + { + String str=""; + char c=f.read(); + while((c != '\n') && (f.available()>0)) + { + str += c; + c=f.read(); + } + int argn=0; + String configArguments[4]; + for(int i=0;i<4;i++) + configArguments[i]=""; + for(int i=0;i9)) + return false; + return true; +} + +PhoneBookEntry *PhoneBookEntry::findPhonebookEntry(long number) +{ + PhoneBookEntry *p = phonebook; + while(p != null) + { + if(p->number == number) + return p; + p=p->next; + } + return null; +} + +PhoneBookEntry *PhoneBookEntry::findPhonebookEntry(String number) +{ + if(!checkPhonebookEntry(number)) + return null; + return findPhonebookEntry(atol(number.c_str())); +} + +void PhoneBookEntry::clearPhonebook() +{ + PhoneBookEntry *phb = phonebook; + while(phonebook != null) + delete phonebook; +} + +void PhoneBookEntry::savePhonebook() +{ + SPIFFS.remove("/zphonebook.txt"); + delay(500); + File f = SPIFFS.open("/zphonebook.txt", "w"); + int ct=0; + PhoneBookEntry *phb=phonebook; + while(phb != null) + { + f.printf("%ul,%s,%s,%s\n",phb->number,phb->address,phb->modifiers,phb->notes); + phb = phb->next; + ct++; + } + f.close(); + delay(500); + if(SPIFFS.exists("/zphonebook.txt")) + { + File f = SPIFFS.open("/zphonebook.txt", "r"); + while(f.available()>0) + { + String str=""; + char c=f.read(); + while((c != '\n') && (f.available()>0)) + { + str += c; + c=f.read(); + } + int argn=0; + if(str.length()>0) + { + for(int i=0;i0)) + { + if(file[strlen(file)-1]=='/') + setCharArray(&path, file); + else + { + char buf[strlen(file)+2]; + sprintf(buf,"%s/",file); + setCharArray(&path, buf); + } + } + return kaplah; +} + +void FTPHost::fixPath(const char *remotepath) +{ + if(remotepath == 0) + return; + char *end = strrchr(remotepath, '/'); + int buflen = ((path == 0) ? 0 : strlen(path)) + strlen(remotepath) + 3; + char fbuf[buflen]; + char pbuf[buflen]; + if(remotepath[0] == '/') + { + strcpy(fbuf, remotepath); + if(end > 0) + { + *end = 0; + sprintf(pbuf,"%s/",remotepath); + } + else + strcpy(pbuf, "/"); + } + else + { + sprintf(fbuf,"%s%s",path,remotepath); + if(end > 0) + { + *end = 0; + sprintf(pbuf,"%s%s/",path,remotepath); + } + else + strcpy(pbuf, path); + } + setCharArray(&path, pbuf); + setCharArray(&file, fbuf); +} + +bool FTPHost::parseUrl(uint8_t *vbuf, char **req) +{ + char *newHostIp; + int newPort; + bool newDoSSL; + char *newUsername; + char *newPassword; + if(parseFTPUrl(vbuf,&newHostIp,req,&newPort,&newDoSSL,&newUsername,&newPassword)) + { + setCharArray(&hostIp,newHostIp); + port = newPort; + doSSL = newDoSSL; + setCharArray(&username,newUsername); + setCharArray(&pw,newPassword); + setCharArray(&path,"/"); + setCharArray(&file,""); + return true; + } + return false; +} + +FTPHost *makeFTPHost(bool isUrl, FTPHost *host, uint8_t *buf, char **req) +{ + if(isUrl) + { + if(host != 0) + delete host; + host = new FTPHost(); + if(!(host->parseUrl(buf,req))) + { + delete host; + host=0; + *req=0; + } + } + else + *req=(char *)buf; + return host; +} + +bool parseFTPUrl(uint8_t *vbuf, char **hostIp, char **req, int *port, bool *doSSL, char **username, char **pw) +{ + *doSSL = false; + if(strstr((char *)vbuf,"ftp:")==(char *)vbuf) + vbuf = vbuf + 4; + else + if(strstr((char *)vbuf,"ftps:")==(char *)vbuf) + { + vbuf = vbuf + 5; + *doSSL = true; + } + while(*vbuf == '/') + vbuf++; + + *port= 21; + *hostIp = (char *)vbuf; + char *atSign=strchr((char *)vbuf,'@'); + *username=NULL; + *pw = NULL; + if(atSign != NULL) + { + *hostIp = atSign + 1; + *atSign = 0; + *username = (char *)vbuf; + vbuf = (uint8_t *)(atSign + 1); + char *pwB = strchr(*username, ':'); + if(pwB != NULL) + { + *pw = pwB+1; + *pwB=0; + } + } + char *portB=strchr((char *)vbuf,':'); + int len=strlen((char *)vbuf); + *req = strchr((char *)vbuf,'/'); + if(*req != NULL) + { + *(*req)=0; + if((*req) != (char *)(vbuf+len-1)) + (*req)++; + } + else + *req = (char *)(vbuf + len); + if(portB != NULL) + { + *portB = 0; + portB++; + *port = atoi(portB); + if(port <= 0) + return ZERROR; + } + return true; +} + +static bool doFTPQuit(WiFiClient **c) +{ + if((*c)->connected()) + { + (*c)->printf("QUIT\r\n"); + delay(500); + } + (*c)->stop(); + delete (*c); + return false; +} + +static String readLine(WiFiClient *c, int timeout) +{ + unsigned long now=millis(); + String line = ""; + while(((millis()-now < timeout) || (c->available()>0)) + && (c->connected()|| (c->available()>0))) + { + yield(); + if(c->available()>0) + { + char ch=c->read(); + if((ch=='\n')||(ch=='\r')) + { + if(line.length()>0) + return line; + } + else + if((ch >= 32 ) && (ch <= 127)) + line += ch; + now=millis(); + } + } + return line; +} + +static int getFTPResponseCode(WiFiClient *c, char *buf) +{ + String resp = readLine(c,5000); + if(resp.length() == 0) + return -1; // timeout total + while((resp.length()<3) + ||(resp[0] < '0')||(resp[0] > '9') + ||(resp[1] < '0')||(resp[1] > '9') + ||(resp[2] < '0')||(resp[2] > '9')) + { + yield(); + resp = readLine(c,1000); + if(resp.length()==0) + return -1; + } + if((buf != NULL)&&(resp.length()<132)) + strcpy(buf,resp.substring(4).c_str()); + return atoi(resp.substring(0,3).c_str()); +} + +void readBytesToSilence(WiFiClient *cc) +{ + unsigned long now = millis(); // just eat the intro for 1 second of silence + while(millis()-now < 1000) + { + yield(); + if(cc->available()>0) + { + cc->read(); + now=millis(); + } + } +} + +bool doFTPGet(FS *fs, const char *hostIp, int port, const char *localpath, const char *remotepath, const char *username, const char *pw, const bool doSSL) +{ + WiFiClient *cc = createWiFiClient(doSSL); + if(WiFi.status() != WL_CONNECTED) + return false; + if(!cc->connect(hostIp, port)) + return doFTPQuit(&cc); + cc->setNoDelay(DEFAULT_NO_DELAY); + readBytesToSilence(cc); + if(username == NULL) + cc->printf("USER anonymous\r\n"); + else + cc->printf("USER %s\r\n",username); + int respCode = getFTPResponseCode(cc, NULL); + if(respCode != 331) + return doFTPQuit(&cc); + if(pw == NULL) + cc->printf("PASS zimodem@zimtime.net\r\n"); + else + cc->printf("PASS %s\r\n",pw); + respCode = getFTPResponseCode(cc, NULL); + if(respCode != 230) + return doFTPQuit(&cc); + readBytesToSilence(cc); + cc->printf("TYPE I\r\n"); + respCode = getFTPResponseCode(cc, NULL); + if(respCode < 0) + return doFTPQuit(&cc); + char ipbuf[129]; + cc->printf("PASV\r\n"); + respCode = getFTPResponseCode(cc, ipbuf); + if(respCode != 227) + return doFTPQuit(&cc); + // now parse the pasv result in .* (ipv4,ipv4,ipv4,ipv4, + char *ipptr = strchr(ipbuf,'('); + while((ipptr != NULL) && (strchr(ipptr+1,'(')!= NULL)) + ipptr=strchr(ipptr+1,'('); + if(ipptr == NULL) + return doFTPQuit(&cc); + int digitCount=0; + int digits[10]; + char *commaPtr=strchr(ipptr+1,','); + while((commaPtr != NULL)&&(digitCount < 10)) + { + *commaPtr = 0; + digits[digitCount++] = atoi(ipptr+1); + ipptr=commaPtr; + commaPtr=strchr(ipptr+1,','); + if(commaPtr == NULL) + commaPtr=strchr(ipptr+1,')'); + } + if(digitCount < 6) + return doFTPQuit(&cc); + sprintf(ipbuf,"%d.%d.%d.%d",digits[0],digits[1],digits[2],digits[3]); + int dataPort = (256 * digits[4]) + digits[5]; + // ok, now we are ready for DATA! + if(WiFi.status() != WL_CONNECTED) + return doFTPQuit(&cc); + WiFiClient *c = createWiFiClient(doSSL); + if(!c->connect(ipbuf, dataPort)) + { + doFTPQuit(&c); + return doFTPQuit(&cc); + } + c->setNoDelay(DEFAULT_NO_DELAY); + cc->printf("RETR %s\r\n",remotepath); + respCode = getFTPResponseCode(cc, NULL); + if((respCode < 0)||(respCode > 400)) + return doFTPQuit(&cc); + File f = fs->open(localpath, "w"); + unsigned long now=millis(); + while((c->connected()||(c->available()>0)) + && ((millis()-now) < 30000)) // loop for data, with nice long timeout + { + if(c->available()>=0) + { + now=millis(); + uint8_t ch=c->read(); + //logSocketIn(ch); // this is ALSO not socket input! + f.write(ch); + } + else + yield(); + } + f.flush(); + f.close(); + c->stop(); + delete c; + doFTPQuit(&cc); + return true; +} + +bool doFTPPut(File &f, const char *hostIp, int port, const char *remotepath, const char *username, const char *pw, const bool doSSL) +{ + WiFiClient *cc = createWiFiClient(doSSL); + if(WiFi.status() != WL_CONNECTED) + return false; + if(!cc->connect(hostIp, port)) + return doFTPQuit(&cc); + cc->setNoDelay(DEFAULT_NO_DELAY); + readBytesToSilence(cc); + if(username == NULL) + cc->printf("USER anonymous\r\n"); + else + cc->printf("USER %s\r\n",username); + int respCode = getFTPResponseCode(cc, NULL); + if(respCode != 331) + return doFTPQuit(&cc); + if(pw == NULL) + cc->printf("PASS zimodem@zimtime.net\r\n"); + else + cc->printf("PASS %s\r\n",pw); + respCode = getFTPResponseCode(cc, NULL); + if(respCode != 230) + return doFTPQuit(&cc); + readBytesToSilence(cc); + cc->printf("TYPE I\r\n"); + respCode = getFTPResponseCode(cc, NULL); + if(respCode < 0) + return doFTPQuit(&cc); + char ipbuf[129]; + cc->printf("PASV\r\n"); + debugPrintf("PASV\r\n"); + respCode = getFTPResponseCode(cc, ipbuf); + if(respCode != 227) + return doFTPQuit(&cc); + // now parse the pasv result in .* (ipv4,ipv4,ipv4,ipv4, + char *ipptr = strchr(ipbuf,'('); + while((ipptr != NULL) && (strchr(ipptr+1,'(')!= NULL)) + ipptr=strchr(ipptr+1,'('); + if(ipptr == NULL) + return doFTPQuit(&cc); + int digitCount=0; + int digits[10]; + char *commaPtr=strchr(ipptr+1,','); + while((commaPtr != NULL)&&(digitCount < 10)) + { + *commaPtr = 0; + digits[digitCount++] = atoi(ipptr+1); + ipptr=commaPtr; + commaPtr=strchr(ipptr+1,','); + if(commaPtr == NULL) + commaPtr=strchr(ipptr+1,')'); + } + if(digitCount < 6) + return doFTPQuit(&cc); + sprintf(ipbuf,"%d.%d.%d.%d",digits[0],digits[1],digits[2],digits[3]); + debugPrintf(ipbuf,"%d.%d.%d.%d",digits[0],digits[1],digits[2],digits[3]); + int dataPort = (256 * digits[4]) + digits[5]; + // ok, now we are ready for DATA! + if(WiFi.status() != WL_CONNECTED) + return doFTPQuit(&cc); + WiFiClient *c = createWiFiClient(doSSL); + if(!c->connect(ipbuf, dataPort)) + { + doFTPQuit(&c); + return doFTPQuit(&cc); + } + c->setNoDelay(DEFAULT_NO_DELAY); + debugPrintf(" STOR %s\r\n",remotepath); + cc->printf("STOR %s\r\n",remotepath); + respCode = getFTPResponseCode(cc, NULL); + if((respCode < 0)||(respCode > 400)) + return doFTPQuit(&cc); + unsigned long now=millis(); + debugPrintf(" Storing... %d\r\n",f.available()); + while((c->connected()) + && (f.available()>0) && ((millis()-now) < 30000)) // loop for data, with nice long timeout + { + if(f.available()>=0) + { + now=millis(); + uint8_t ch=f.read(); + //logSocketIn(ch); // this is ALSO not socket input! + c->write(ch); + } + else + yield(); + } + debugPrintf("FPUT: Done\r\n"); + c->flush(); + c->stop(); + delete c; + doFTPQuit(&cc); + return true; +} + +bool doFTPLS(ZSerial *serial, const char *hostIp, int port, const char *remotepath, const char *username, const char *pw, const bool doSSL) +{ + WiFiClient *cc = createWiFiClient(doSSL); + if(WiFi.status() != WL_CONNECTED) + return false; + if(!cc->connect(hostIp, port)) + return doFTPQuit(&cc); + cc->setNoDelay(DEFAULT_NO_DELAY); + readBytesToSilence(cc); + if(username == NULL) + cc->printf("USER anonymous\r\n"); + else + cc->printf("USER %s\r\n",username); + int respCode = getFTPResponseCode(cc, NULL); + if(respCode != 331) + return doFTPQuit(&cc); + if(pw == NULL) + cc->printf("PASS zimodem@zimtime.net\r\n"); + else + cc->printf("PASS %s\r\n",pw); + respCode = getFTPResponseCode(cc, NULL); + if(respCode != 230) + return doFTPQuit(&cc); + readBytesToSilence(cc); + cc->printf("TYPE A\r\n"); + respCode = getFTPResponseCode(cc, NULL); + if(respCode < 0) + return doFTPQuit(&cc); + if((remotepath != NULL)&& (*remotepath != NULL)) + { + cc->printf("CWD %s\r\n",remotepath); + respCode = getFTPResponseCode(cc, NULL); + if((respCode < 0)||(respCode > 400)) + return doFTPQuit(&cc); + readBytesToSilence(cc); + } + char ipbuf[129]; + cc->printf("PASV\r\n"); + respCode = getFTPResponseCode(cc, ipbuf); + if(respCode != 227) + return doFTPQuit(&cc); + // now parse the pasv result in .* (ipv4,ipv4,ipv4,ipv4, + char *ipptr = strchr(ipbuf,'('); + while((ipptr != NULL) && (strchr(ipptr+1,'(')!= NULL)) + ipptr=strchr(ipptr+1,'('); + if(ipptr == NULL) + return doFTPQuit(&cc); + int digitCount=0; + int digits[10]; + char *commaPtr=strchr(ipptr+1,','); + while((commaPtr != NULL)&&(digitCount < 10)) + { + *commaPtr = 0; + digits[digitCount++] = atoi(ipptr+1); + ipptr=commaPtr; + commaPtr=strchr(ipptr+1,','); + if(commaPtr == NULL) + commaPtr=strchr(ipptr+1,')'); + } + if(digitCount < 6) + return doFTPQuit(&cc); + sprintf(ipbuf,"%d.%d.%d.%d",digits[0],digits[1],digits[2],digits[3]); + int dataPort = (256 * digits[4]) + digits[5]; + // ok, now we are ready for DATA! + if(WiFi.status() != WL_CONNECTED) + return doFTPQuit(&cc); + WiFiClient *c = createWiFiClient(doSSL); + if(!c->connect(ipbuf, dataPort)) + { + doFTPQuit(&c); + return doFTPQuit(&cc); + } + c->setNoDelay(DEFAULT_NO_DELAY); + cc->printf("LIST\r\n",remotepath); + respCode = getFTPResponseCode(cc, NULL); + if((respCode < 0)||(respCode > 400)) + return doFTPQuit(&cc); + unsigned long now=millis(); + while((c->connected()||(c->available()>0)) + && ((millis()-now) < 30000)) // loop for data, with nice long timeout + { + if(c->available()>=0) + { + now=millis(); + uint8_t ch=c->read(); + //logSocketIn(ch); // this is ALSO not socket input! + serial->print((char)ch); + } + else + yield(); + } + c->stop(); + delete c; + doFTPQuit(&cc); + return true; +} +#endif \ No newline at end of file diff --git a/zimodem/proto_hostcm.h b/zimodem/proto_hostcm.h new file mode 100644 index 0000000..3ace46c --- /dev/null +++ b/zimodem/proto_hostcm.h @@ -0,0 +1,84 @@ +#ifdef INCLUDE_SD_SHELL +#ifdef INCLUDE_HOSTCM +/* Converted from source reverse engineered from SP9000 roms by Rob Ferguson */ +#include + +# define HCM_BUFSIZ 104 +# define HCM_SENDBUF (208/2 - 6) +# define HCM_FNSIZ 32 +# define HCM_MAXFN 16 + +typedef struct _HCMFile +{ + char descriptor; + File f; + uint8_t mode; + uint8_t format; + uint8_t type; + int reclen; + char filename[HCM_FNSIZ+1]; + struct _HCMFile *nxt; +} HCMFile; + +class HostCM +{ +private: + ZSerial hserial; + const struct _HCOpts + { + unsigned int speed = 15; //B9600 + unsigned int parity = 0;// ?! + unsigned int stopb=0; + unsigned char lineend=0xd; + unsigned char prompt=0x11; + unsigned char response=0x13; + unsigned char ext=0; + } opt PROGMEM; + + uint8_t outbuf[HCM_BUFSIZ]; + int odex = 0; + + uint8_t inbuf[HCM_BUFSIZ+1]; + int idex = 0; + + bool aborted = false; + unsigned long lastNonPlusTm = 0; + unsigned int plussesInARow = 0; + unsigned long plusTimeExpire = 0; + HCMFile files[HCM_MAXFN]; + FS *hFS = &SD; + File openDirF = (File)0; + File renameF = (File)0; + + char checksum(uint8_t *b, int n); + void checkDoPlusPlusPlus(const int c, const unsigned long tm); + bool checkPlusPlusPlusExpire(const unsigned long tm); + void sendNAK(); + void sendACK(); + void sendError(const char* format, ...); + bool closeAllFiles(); + HCMFile *addNewFileEntry(); + void delFileEntry(HCMFile *e); + HCMFile *getFileByDescriptor(char c); + int numOpenFiles(); + + void protoOpenFile(); + void protoCloseFile(); + void protoPutToFile(); + void protoGetFileBytes(); + void protoOpenDir(); + void protoNextDirFile(); + void protoCloseDir(); + void protoSetRenameFile(); + void protoFinRenameFile(); + void protoEraseFile(); + void protoSeekFile(); + +public: + void receiveLoop(); + bool isAborted(); + HostCM(FS *fs); + ~HostCM(); +}; +#endif +#endif diff --git a/zimodem/proto_hostcm.ino b/zimodem/proto_hostcm.ino new file mode 100644 index 0000000..8935c3c --- /dev/null +++ b/zimodem/proto_hostcm.ino @@ -0,0 +1,690 @@ +#ifdef INCLUDE_SD_SHELL +#ifdef INCLUDE_HOSTCM +/* Converted from Rob Ferguson's code by Bo Zimmerman + * + * Copyright (c) 2013, Robert Ferguson All rights reserved. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +char *basename(char *path) +{ + char *base = strrchr(path, '/'); + return base ? base+1 : path; +} + +char *dirname(char *path) +{ + char *base = strrchr(path, '/'); + if(base) + { + *base = 0; + return path; + } + return ""; +} + +HostCM::HostCM(FS *fs) +{ + hFS = fs; + for(int i=0;if != 0) + e->f.close(); + char d = e->descriptor; + memset(e,sizeof(struct _HCMFile),0); + e->descriptor = d; +} + + +HCMFile *HostCM::getFileByDescriptor(char c) +{ + for(int i=0;i0)||((tm-lastNonPlusTm)>800)) + { + plussesInARow++; + if(plussesInARow > 2) + plusTimeExpire = tm + 800; + } + } + else + { + plusTimeExpire = 0; + lastNonPlusTm = tm; + plussesInARow = 0; + } +} + +bool HostCM::checkPlusPlusPlusExpire(const unsigned long tm) +{ + if(aborted) + return true; + if((plusTimeExpire>0)&&(tm>plusTimeExpire)&&(plussesInARow>2)) + { + aborted = true; + plusTimeExpire = 0; + lastNonPlusTm = tm; + plussesInARow = 0; + return true; + } + return false; +} + +bool HostCM::isAborted() +{ + return aborted; +} + +void HostCM::protoCloseFile() +{ + HCMFile *h = getFileByDescriptor((char)inbuf[1]); + if (h==0) { + sendError("error: invalid descriptor %c", inbuf[1]); + return; + } + + if (h->f == 0) + { + sendError("error: file not open %c", inbuf[1]); // should this be an error though? + return; + } + delFileEntry(h); + sendACK(); +} + +void HostCM::protoPutToFile() +{ + HCMFile *h = getFileByDescriptor((char)inbuf[1]); + if (h==0) { + sendError("error: invalid descriptor %c", inbuf[1]); + return; + } + uint8_t eor = (uint8_t)lc((char)inbuf[2]); + if (h->f == 0) + { + sendError("error: file not open %c", inbuf[1]); + return; + } + /*if((h->mode)=='r')||(h->mode=='l')) + { + sendError("error: read-only file %c", inbuf[1]); + return; + }*/ + if(h->format == 'b') + { + FROMHEX(&inbuf[3], idex - 4); + idex = ((idex - 4) / 2) + 4; + } + if((eor=='z') + &&((h->format == 't') + ||(h->type != 'f'))) + { + inbuf[idex-1] = opt.lineend; + idex++; + } + if(h->f.write(&inbuf[3],idex-4) != idex-4) + { + sendError("error: write failed to file %c", inbuf[1]); + return; + } + sendACK(); +} + +void HostCM::protoGetFileBytes() +{ + int c; + HCMFile *h = getFileByDescriptor((char)inbuf[1]); + if (h==0) + { + sendError("error: invalid descriptor %c", inbuf[1]); + return; + } + if((h->f == 0) + ||((h->mode != 'r') && (h->mode != 'u') && (h->mode != 'l'))) + { + sendError("error: file not open/readable %c", inbuf[1]); + return; + } + + odex = 0; + outbuf[odex++] = opt.response; + if(h->format == 't') + { + outbuf[odex++] = 'b'; + outbuf[odex] = 'z'; + do + { + odex++; + c = h->f.read(); + outbuf[odex] = (uint8_t)(c & 0xff); + } + while((c >= 0) && (odex < (HCM_SENDBUF - 2)) && (outbuf[odex] != 0xd)); + + if(c<0) + { + outbuf[1] = 'e'; + outbuf[2] = checksum(&outbuf[1], 1); + odex=3; // abandon anything between EOL and EOF + } + else + { + if (odex >= (HCM_SENDBUF - 2)) + outbuf[2] = 'n'; + outbuf[odex] = checksum(&outbuf[1], odex - 1); + odex++; + } + } + else + if (h->format == 'b') + { + int rdcount = HCM_SENDBUF; + char eor = 'n'; + if(h->reclen) + { + if (h->reclen < HCM_SENDBUF) + { + rdcount = h->reclen; + eor = 'z'; + } + else + { + int pos = h->f.position(); + if((pos & h->reclen) > ((pos + rdcount) & h->reclen)) + { + rdcount = ((pos + rdcount) / h->reclen) * h->reclen - pos; + eor = 'z'; + } + } + } + uint8_t rdbuf[rdcount]; + int n = h->f.read(rdbuf, rdcount); + if (n <= 0) + { + outbuf[odex++] = 'e'; + outbuf[odex] = checksum(&outbuf[1], odex - 1); + odex++; + } + else + { + outbuf[odex++] = 'b'; + outbuf[odex++] = eor; + for(int i=0;i= HCM_MAXFN) + { + sendError("error: too many open files"); + return; + } + if(idex<8) + { + sendError("error: command too short"); + return; + } + uint8_t mode = (uint8_t)lc((char)inbuf[1]); + if(strchr("rlwsua",(char)mode)==0) + { + sendError("error: illegal mode %c",(char)mode); + return; + } + bool isRead = strchr("rl",(char)mode)!=0; + uint8_t format = (uint8_t)lc((char)inbuf[2]); + uint8_t *ptr = (uint8_t *)memchr(inbuf+3, '(', idex-3); + if(ptr == 0) + { + sendError("error: missing ("); + return; + } + uint8_t type = (uint8_t)lc((char)ptr[1]); + uint8_t reclen = 0; + if(ptr[2]==':') + reclen=atoi((char *)(ptr+3)); + ptr = (uint8_t *)memchr(ptr+2, ')', eobuf-(ptr+1)); + if(ptr == 0) + { + sendError("error: missing )"); + return; + } + inbuf[idex - 1] = 0; + uint8_t *fnptr = ptr + 1; + HCMFile *newF = addNewFileEntry(); + if (type == 'f') + { + char *bn = basename((char *)ptr+1); + char *dn = dirname((char *)ptr+1); + if(reclen == 0) + reclen = 80; + snprintf(newF->filename, sizeof(newF->filename), "%s/(f:%d)%s", dn, reclen, bn); + } + else + strncpy(newF->filename, (char *)ptr+1, sizeof(newF->filename)); + if(isRead) + { + newF->f = SD.open(newF->filename); + if(newF->f == 0) + { + if(strchr(basename(newF->filename), ',') == 0) + { + if(strlen(newF->filename) + 5 < HCM_FNSIZ) + { + if((mode == 'l') || (mode == 's')) + strcat(newF->filename, ",prg"); + else + { + if(type == 'f') + strcat(newF->filename, ",rel"); + else + strcat(newF->filename, ",seq"); + } + newF->f = SD.open(newF->filename); + if(newF == 0) + { + sendError("error: file '%s' not found", newF->filename); + return; + } + } + } + else + { + sendError("error: file '%s' not found", newF->filename); + return; + } + } + } + else + { + newF->f = SD.open(newF->filename,FILE_WRITE); + if(newF->f == 0) + { + sendError("error: failed to open '%s'", newF->filename); + return; + } + if(newF->f && (mode == 'a')) + newF->f.seek(EOF); + } + newF->mode = mode; + newF->format = format; + newF->type = type; + newF->reclen = reclen; + + odex = 0; + outbuf[odex++] = opt.response; + outbuf[odex++] = 'b'; + outbuf[odex++] = newF->descriptor; + outbuf[odex++] = checksum(&(outbuf[1]), 2); + outbuf[odex++] = opt.lineend; + outbuf[odex++] = opt.prompt; + hserial.write(outbuf, odex); + hserial.flush(); +} + +void HostCM::protoOpenDir() +{ + if(openDirF != 0) + { + sendError("error: directory open"); + return; + } + if(idex > 2) + inbuf[idex - 1] = 0; + else + { + strcpy((char *)&inbuf[1], "/"); + idex++; + } + + openDirF = SD.open((char *)&inbuf[1]); + if((openDirF == 0)||(!openDirF.isDirectory())) + { + sendError("error: directory not found %s",(char *)&inbuf[1]); + return; + } + sendACK(); +} + +void HostCM::protoCloseDir() +{ + if(openDirF == 0) + { + sendError("error: directory not open"); // should this really be an error? + return; + } + openDirF.close(); + openDirF = (File)0; + sendACK(); +} + +void HostCM::protoNextDirFile() +{ + if(openDirF == 0) + { + sendError("error: directory not open"); // should this really be an error? + return; + } + + odex = 0; + outbuf[odex++] = opt.response; + + File nf = openDirF.openNextFile(); + if(nf == 0) + outbuf[odex++] = 'e'; + else + { + char *fname = (char *)nf.name(); + char *paren = strchr(fname,')'); + int reclen = 0; + if((strncmp("(f:", fname, 3) == 0) && (paren != 0)) + { + fname = paren + 1; + reclen = atoi((char *)&fname[3]); + } + outbuf[odex++] = 'b'; + + if(reclen) + odex += snprintf((char *)&outbuf[odex], HCM_BUFSIZ - odex, "%-20s %8llu (%d)", + fname, (unsigned long long)nf.size(), reclen); + else + { + if(nf.isDirectory()) + odex += snprintf((char *)&outbuf[odex], HCM_BUFSIZ - odex, "%s/", fname); + else + { + odex += snprintf((char *)&outbuf[odex], HCM_BUFSIZ - odex, "%-20s %8llu", + fname, (unsigned long long)nf.size()); + } + } + } + nf.close(); + outbuf[odex] = checksum(&(outbuf[1]), odex - 1); + odex++; + outbuf[odex++] = opt.lineend; + outbuf[odex++] = opt.prompt; + hserial.write(outbuf, odex); + hserial.flush(); +} + +void HostCM::protoSetRenameFile() +{ + if (renameF != 0) + { + sendError("error: rename in progress"); + return; + } + + if (idex > 2) + { + inbuf[idex - 1] = 0; + renameF = SD.open((char *)&inbuf[1]); + } + else + { + sendError("error: missing filename"); + return; + } + sendACK(); +} + +void HostCM::protoFinRenameFile() +{ + if (renameF == 0) + { + sendError("error: rename not started"); + return; + } + + if (idex > 2) + inbuf[idex - 1] = 0; + else + { + sendError("error: missing filename"); + return; + } + + String on = renameF.name(); + renameF.close(); + if(!SD.rename(on,(char *)&inbuf[1])) + { + renameF = (File)0; + sendError("error: rename %s failed",on); + return; + } + renameF = (File)0; + sendACK(); +} + +void HostCM::protoEraseFile() +{ + if (idex > 2) + inbuf[idex - 1] = 0; + else + { + sendError("error: missing filename"); + return; + } + if(!SD.remove((char *)&inbuf[1])) + { + sendError("error: erase %s failed",(char *)&inbuf[1] ); + return; + } + sendACK(); +} + +void HostCM::protoSeekFile() +{ + HCMFile *h = getFileByDescriptor((char)inbuf[1]); + if (h==0) + { + sendError("error: invalid descriptor %c", inbuf[1]); + return; + } + if(h->f == 0) + { + sendError("error: file not open/readable %c", inbuf[1]); + return; + } + + inbuf[idex - 1] = 0; + + unsigned long offset = atoi((char *)&inbuf[2]) * ((h->reclen == 0)? 1 : h->reclen); + if(!h->f.seek(offset)) + { + sendError("error: seek failed on %s @ %ul", h->f.name(),offset); + return; + } + sendACK(); +} + +char HostCM::checksum(uint8_t *b, int n) +{ + int i, s = 0; + + for (i = 0; i < n; i++) + s += b[i] & 0xf; + return ('A' + (s&0xf)); +} + +void HostCM::sendError(const char* format, ...) +{ + odex = 0; + outbuf[odex++] = opt.response; + outbuf[odex++] = 'x'; + va_list arglist; + va_start(arglist, format); + odex += vsnprintf((char *)&outbuf[2], 80, format, arglist); + va_end(arglist); + outbuf[odex] = checksum(&outbuf[1], odex - 1); + odex++; + outbuf[odex++] = opt.lineend; + outbuf[odex++] = opt.prompt; + hserial.write(outbuf, odex); + hserial.flush(); +} + +void HostCM::sendNAK() +{ + odex = 0; + outbuf[odex++] = opt.response; + outbuf[odex++] = 'N'; + outbuf[odex++] = opt.lineend; + outbuf[odex++] = opt.prompt; + hserial.write(outbuf, odex); + hserial.flush(); +} + +void HostCM::sendACK() +{ + odex = 0; + outbuf[odex++] = opt.response; + outbuf[odex++] = 'b'; + outbuf[odex++] = checksum(&(outbuf[1]), 1); + outbuf[odex++] = opt.lineend; + outbuf[odex++] = opt.prompt; + hserial.write(outbuf, odex); + hserial.flush(); +} + +void HostCM::receiveLoop() +{ + serialOutDeque(); + unsigned long tm = millis(); + if(checkPlusPlusPlusExpire(tm)) + return; + int c; + while(hserial.available() > 0) + { + c=hserial.read(); + if(idex1)&&(inbuf[idex-1]!=checksum(inbuf,idex-1))) + sendNAK(); + else + { + logPrintf("HOSTCM received: %c\n",inbuf[0]); + switch(inbuf[0]) + { + case 'N': + hserial.write(outbuf, odex); + hserial.flush(); + break; + case 'v': sendACK(); break; + case 'q': aborted = closeAllFiles(); break; + case 'o': protoOpenFile(); break; + case 'c': protoCloseFile(); break; + case 'p': protoPutToFile(); break; + case 'g': protoGetFileBytes(); break; + case 'd': protoOpenDir(); break; + case 'f': protoNextDirFile(); break; + case 'k': protoCloseDir(); break; + case 'w': protoSetRenameFile(); break; + case 'b': protoFinRenameFile(); break; + case 'y': protoEraseFile(); break; + case 'r': protoSeekFile(); break; + default: + sendNAK(); + break; + } + } + idex=0; // we are ready for the next packet! + serialOutDeque(); +} +#endif +#endif \ No newline at end of file diff --git a/zimodem/proto_http.h b/zimodem/proto_http.h new file mode 100644 index 0000000..7f52622 --- /dev/null +++ b/zimodem/proto_http.h @@ -0,0 +1,22 @@ +#ifndef ZIMODEM_PROTO_HTTP +#define ZIMODEM_PROTO_HTTP +/* + Copyright 2016-2019 Bo Zimmerman + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +bool parseWebUrl(uint8_t *vbuf, char **hostIp, char **req, int *port, bool *doSSL); +bool doWebGetBytes(const char *hostIp, int port, const char *req, const bool doSSL, uint8_t *buf, int *bufSize); +WiFiClient *doWebGetStream(const char *hostIp, int port, const char *req, bool doSSL, uint32_t *responseSize); +bool doWebGet(const char *hostIp, int port, FS *fs, const char *filename, const char *req, const bool doSSL); +#endif diff --git a/zimodem/proto_http.ino b/zimodem/proto_http.ino new file mode 100644 index 0000000..172a4a3 --- /dev/null +++ b/zimodem/proto_http.ino @@ -0,0 +1,398 @@ +/* + Copyright 2016-2019 Bo Zimmerman + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +bool parseWebUrl(uint8_t *vbuf, char **hostIp, char **req, int *port, bool *doSSL) +{ + *doSSL = false; + if(strstr((char *)vbuf,"http:")==(char *)vbuf) + vbuf = vbuf + 5; + else + if(strstr((char *)vbuf,"https:")==(char *)vbuf) + { + vbuf = vbuf + 6; + *doSSL = true; + } + while(*vbuf == '/') + vbuf++; + + *port= (*doSSL) ? 443 : 80; + *hostIp = (char *)vbuf; + char *portB=strchr((char *)vbuf,':'); + *req = strchr((char *)vbuf,'/'); + if(*req != NULL) + { + *(*req)=0; + (*req)++; + } + else + { + int len=strlen((char *)vbuf); + *req = (char *)(vbuf + len); + } + if(portB != NULL) + { + *portB = 0; + portB++; + *port = atoi(portB); + if(port <= 0) + return false; + } + return true; +} +/* + * It just breaks too many things to allow a stream to go forward without + * a determined length. For example: firmware updates, and even at&g returns + * a page length for the client. Let true clients use sockets and handle + * their own chunked encoding. +class ChunkedStream : public WiFiClient +{ +private: + WiFiClient *wifi = null; + int chunkCount = 0; + int chunkSize = 0; + uint8_t state = 0; //0 + +public: + + ChunkedStream(WiFiClient *s) + { + wifi = s; + } + + ~ChunkedStream() + { + if(wifi != null) + { + wifi->stop(); + delete wifi; + } + } + + virtual int read() + { + if(available()==0) + return -1; + char c=wifi->read(); + bool gotC = false; + int errors = 0; + while((!gotC) && (errors < 5000)) + { + switch(state) + { + case 0: + if(c=='0') + return '\0'; + if((c>='0')&&(c<='9')) + { + chunkSize = (c - '0'); + state=1; + } + else + if((c>='a')&&(c<='f')) + { + chunkSize = 10 + (c-'a'); + state=1; + } + break; + case 1: + { + if((c>='0')&&(c<='9')) + chunkSize = (chunkSize * 16) + (c - '0'); + else + if((c>='a')&&(c<='f')) + chunkSize = (chunkSize * 16) + (c-'a'); + else + if(c=='\r') + state=2; + break; + } + case 2: + if(c == '\n') + { + state = 3; + chunkCount=0; + } + else + state = 0; + break; + case 3: + if(chunkCount < chunkSize) + { + gotC = true; + chunkCount++; + } + else + if(c == '\r') + state = 4; + else + state = 0; + break; + case 4: + if(c == '\n') + state = 0; + else + state = 0; // what else is there to do?! + break; + } + while((!gotC) && (errors < 5000)) + { + if(available()>0) + { + c=wifi->read(); + break; + } + else + if(++errors > 5000) + break; + else + delay(1); + } + } + return c; + } + virtual int peek() + { + return wifi->peek(); + } + + virtual int read(uint8_t *buf, size_t size) + { + if(size == 0) + return 0; + int num = available(); + if(num > size) + num=size; + for(int i=0;igetNoDelay(); + } + void setNoDelay(bool nodelay) + { + wifi->setNoDelay(nodelay); + } + + virtual int available() + { + return wifi->available(); + } + + virtual void stop() + { + wifi->stop(); + } + virtual uint8_t connected() + { + return wifi->connected(); + } +}; + */ +WiFiClient *doWebGetStream(const char *hostIp, int port, const char *req, bool doSSL, uint32_t *responseSize) +{ + *responseSize = 0; + if(WiFi.status() != WL_CONNECTED) + return null; + + WiFiClient *c = createWiFiClient(doSSL); + if(port == 0) + port = 80; + if(!c->connect(hostIp, port)) + { + c->stop(); + delete c; + return null; + } + c->setNoDelay(DEFAULT_NO_DELAY); + + const char *root = ""; + if(req == NULL) + req=root; + if(*req == '/') + req++; + + c->printf("GET /%s HTTP/1.1\r\n",req); + c->printf("User-Agent: Zimodem Firmware\r\n"); + c->printf("Host: %s\r\n",hostIp); + c->printf("Connection: close\r\n\r\n"); + + String ln = ""; + String reUrl = ""; + uint32_t respLength = 0; + int respCode = -1; + bool chunked = false; + while(c->connected() || (c->available()>0)) + { + yield(); + if(c->available()==0) + continue; + + char ch = (char)c->read(); + logSocketIn(ch); // this is very much socket input! + if(ch == '\r') + continue; + else + if(ch == '\n') + { + if(ln.length()==0) + break; + if(respCode < 0) + { + int sp = ln.indexOf(' '); + if(sp<=0) + break; + ln.remove(0,sp+1); + sp = ln.indexOf(' '); + if(sp<=0) + break; + ln.remove(sp); + respCode = atoi(ln.c_str()); + } + else + { + int x=ln.indexOf(':'); + if(x>0) + { + String header = ln.substring(0,x); + header.toLowerCase(); + if(header == "content-length") + { + ln.remove(0,16); + respLength = atoi(ln.c_str()); + } + else + if(header == "location") + { + reUrl = ln; + reUrl.remove(0,10); + } + else + if(header == "transfer-encoding") + { + ln.toLowerCase(); + chunked = ln.indexOf("chunked") > 0; + } + } + } + ln = ""; + } + else + ln.concat(ch); + } + + if((respCode >= 300) + && (respCode <= 399) + && (reUrl.length() > 0) + && (reUrl.length() < 1024)) + { + char newUrlBuf[reUrl.length()+1]; + strcpy(newUrlBuf,reUrl.c_str()); + char *hostIp2; + char *req2; + int port2; + bool doSSL2; + if(parseWebUrl((uint8_t *)newUrlBuf, &hostIp2,&req2,&port2,&doSSL2)) + { + c->stop(); + delete c; + return doWebGetStream(hostIp2,port2,req2,doSSL2,responseSize); + + } + } + + *responseSize = respLength; + if(((!c->connected())&&(c->available()==0)) + ||(respCode != 200) + ||(respLength <= 0)) + { + c->stop(); + delete c; + return null; + } + //if(chunked) // technically, if a length was returned, chunked would be ok, but that's not in the cards. + // return new ChunkedStream(c); + return c; +} + +bool doWebGet(const char *hostIp, int port, FS *fs, const char *filename, const char *req, const bool doSSL) +{ + uint32_t respLength=0; + WiFiClient *c = doWebGetStream(hostIp, port, req, doSSL, &respLength); + if(c==null) + return false; + uint32_t bytesRead = 0; + File f = fs->open(filename, "w"); + unsigned long now = millis(); + while((bytesRead < respLength) // this can be removed for chunked encoding support + && (c->connected()||(c->available()>0)) + && ((millis()-now)<10000)) + { + if(c->available()>0) + { + now=millis(); + uint8_t ch=c->read(); + logSocketIn(ch); // this is very much socket input! + f.write(ch); + bytesRead++; + } + else + yield(); + } + f.flush(); + f.close(); + c->stop(); + delete c; + return (respLength == 0) || (bytesRead == respLength); +} + +bool doWebGetBytes(const char *hostIp, int port, const char *req, const bool doSSL, uint8_t *buf, int *bufSize) +{ + *bufSize = -1; + uint32_t respLength=0; + WiFiClient *c = doWebGetStream(hostIp, port, req, doSSL, &respLength); + if(c==null) + return false; + if(((!c->connected())&&(c->available()==0)) + ||(respLength > *bufSize)) + { + c->stop(); + delete c; + return false; + } + *bufSize = (int)respLength; + int index=0; + unsigned long now = millis(); + while((index < respLength) // this can be removed for chunked encoding support + &&(c->connected()||(c->available()>0)) + && ((millis()-now)<10000)) + { + if(c->available()>0) + { + uint8_t ch=c->read(); + now = millis(); + logSocketIn(ch); // how is this not socket input -- it's coming from a WiFiClient -- that's THE SOCKET! + buf[index++] = ch; + } + else + yield(); + } + *bufSize = index; + c->stop(); + delete c; + return (respLength == 0) || (index == respLength); +} diff --git a/zimodem/proto_kermit.h b/zimodem/proto_kermit.h new file mode 100644 index 0000000..6dd2bab --- /dev/null +++ b/zimodem/proto_kermit.h @@ -0,0 +1,155 @@ +#include + +class KModem +{ +private: + static const int MAXPACKSIZ = 94; /* Maximum packet size */ + static const int MAXTRY = 20; /* Times to retry a packet */ + static const int MYTIME = 10; /* (10) Seconds after which I should be timed out */ + static const int MAXTIM = 60; /* (60) Maximum timeout interval */ + static const int MINTIM = 2; /* (2) Minumum timeout interval */ + static const char MYQUOTE ='#'; /* Quote character I will use */ + static const int MYPAD = 0; /* Number of padding characters I will need */ + static const int MYPCHAR = 0; /* Padding character I need (NULL) */ + static const char MYEOL = '\n'; /* End-Of-Line character I need */ + static const char SOH = 1; /* Start of header */ + static const char CR = 13; /* ASCII Carriage Return */ + static const char SP = 32; /* ASCII space */ + static const char DEL = 127; /* Delete (rubout) */ + static const char ESCCHR = '^'; /* Default escape character for CONNECT */ + static const char NUL = '\0'; /* Null character */ + static const char FALSE = 0; + static const char TRUE = -1; + + int size=0, /* Size of present data */ + rpsiz=0, /* Maximum receive packet size */ + spsiz=0, /* Maximum send packet size */ + pad=0, /* How much padding to send */ + timint=0, /* Timeout for foreign host on sends */ + n=0, /* Packet number */ + numtry=0, /* Times this packet retried */ + oldtry=0, /* Times previous packet retried */ + image=1, /* -1 means 8-bit mode */ + debug=99, /* indicates level of debugging output (0=none) */ + filecount=0, /* Number of files left to send */ + filenum=0, + mflg=0, /* Flag for MacKermit mode */ + xflg=0; /* flag for xmit directory structure */ + char state, /* Present state of the automaton */ + padchar, /* Padding character to send */ + eol, /* End-Of-Line character to send */ + escchr, /* Connect command escape character */ + quote, /* Quote character in incoming data */ + *filnam, /* Current file name */ + *filnamo, /* File name sent */ + *ttyline, /* Pointer to tty line */ + recpkt[MAXPACKSIZ], /* Receive packet buffer */ + packet[MAXPACKSIZ], /* Packet buffer */ + ldata[1024]; /* First line of data to send over connection */ + String **filelist = 0; + int (*recvChar)(ZSerial *ser, int); + void (*sendChar)(ZSerial *ser, char); + bool (*dataHandler)(File *kfp, unsigned long number, char *buffer, int len); + + void flushinput(); + void rpar(char data[]); + void spar(char data[]); + int gnxtfl(); + void bufemp(char buffer[], int len); + void prerrpkt(char *msg); + int bufill(char buffer[]); + int rpack(int *len, int *num, char *data); + int spack(char type, int num, int len, char *data); + char rdata(); + char rinit(); + char rfile(); + char sfile(); + char sinit(); + char sdata(); + char sbreak(); + char seof(); + + bool kfpClosed = true; + String *errStr = 0; +public: + String rootpath = ""; + FS *kfileSystem = &SD; + File kfp; + ZSerial kserial; + + KModem(FlowControlType commandFlow, + int (*recvChar)(ZSerial *ser, int), + void (*sendChar)(ZSerial *ser, char), + bool (*dataHandler)(File *kfp, unsigned long, char*, int), + String &errors); + void setTransmitList(String **fileList, int numFiles); + bool receive(); + bool transmit(); +}; + +static int kReceiveSerial(ZSerial *ser, int del) +{ + unsigned long end=millis() + (del * 1000L); + while(millis() < end) + { + serialOutDeque(); + if(ser->available() > 0) + { + int c=ser->read(); + logSerialIn(c); + return c; + } + yield(); + } + return -1; +} + +static void kSendSerial(ZSerial *ser, char c) +{ + ser->write((uint8_t)c); + ser->flush(); +} + +static bool kUDataHandler(File *kfp, unsigned long number, char *buf, int sz) +{ + for(int i=0;iwrite((uint8_t)buf[i]); + return true; +} + +static bool kDDataHandler(File *kfp, unsigned long number, char *buf, int sz) +{ + for(int i=0;iread(); + if(c<0) + { + if(i==0) + return false; + buf[i] = (char)26; + } + else + buf[i] = (char)c; + } + return true; +} + +static boolean kDownload(FlowControlType commandFlow, FS &fs, String **fileList, int fileCount, String &errors) +{ + KModem kmo(commandFlow, kReceiveSerial, kSendSerial, kDDataHandler, errors); + kmo.kfileSystem = &fs; + kmo.setTransmitList(fileList,fileCount); + bool result = kmo.transmit(); + return result; +} + +static boolean kUpload(FlowControlType commandFlow, FS &fs, String rootPath, String &errors) +{ + KModem kmo(commandFlow, kReceiveSerial, kSendSerial, kUDataHandler, errors); + kmo.kfileSystem = &fs; + kmo.rootpath = rootPath; + bool result = kmo.receive(); + return result; +} + + diff --git a/zimodem/proto_kermit.ino b/zimodem/proto_kermit.ino new file mode 100644 index 0000000..fb010b6 --- /dev/null +++ b/zimodem/proto_kermit.ino @@ -0,0 +1,817 @@ +#ifdef INCLUDE_SD_SHELL +/* + * K e r m i t File Transfer Utility + * + * UNIX Kermit, Columbia University, 1981, 1982, 1983 + * Bill Catchings, Bob Cattani, Chris Maio, Frank da Cruz, Alan Crosswell + * + * Also: Jim Guyton, Rand Corporation + * Walter Underwood, Ford Aerospace + * Lauren Weinstein + * + * Adapted for Zimodem by Bo Zimmerman + */ + +KModem::KModem(FlowControlType commandFlow, + int (*recvChar)(ZSerial *ser, int msDelay), + void (*sendChar)(ZSerial *ser, char sym), + bool (*dataHandler)(File *kfp, unsigned long number, char *buffer, int len), + String &errors) +{ + this->errStr = &errors; + this->sendChar = sendChar; + this->recvChar = recvChar; + this->dataHandler = dataHandler; + this->kserial.setFlowControlType(FCT_DISABLED); + if(commandFlow==FCT_RTSCTS) + this->kserial.setFlowControlType(FCT_RTSCTS); + this->kserial.setPetsciiMode(false); + this->kserial.setXON(true); +} + +void KModem::flushinput() +{ + while(kserial.available()>0) + logSerialIn(kserial.read()); +} + +bool KModem::receive() +{ + state = 'R'; /* Receive-Init is the start state */ + n = 0; /* Initialize message number */ + numtry = 0; /* Say no tries yet */ + + while(TRUE) + { + if (debug) + debugPrintf(" recsw state: %c\n",state); + switch(state) /* Do until done */ + { + case 'R': + state = rinit(); + break; /* Receive-Init */ + case 'F': + state = rfile(); + break; /* Receive-File */ + case 'D': + state = rdata(); + break; /* Receive-Data */ + case 'C': + kserial.flushAlways(); + return true; /* Complete state */ + case 'A': + kserial.flushAlways(); + return false; /* "Abort" state */ + } + } +} + + +bool KModem::transmit() +{ + if (gnxtfl() == FALSE) /* No more files go? */ + { + kserial.flushAlways(); + return false; /* if not, break, EOT, all done */ + } + state = 'S'; /* Send initiate is the start state */ + n = 0; /* Initialize message number */ + numtry = 0; /* Say no tries yet */ + while(ZTRUE) /* Do this as long as necessary */ + { + if (debug) + debugPrintf("sendsw state: %c\n",state); + switch(state) + { + case 'S': + state = sinit(); + break; /* Send-Init */ + case 'F': + state = sfile(); + break; /* Send-File */ + case 'D': + state = sdata(); + break; /* Send-Data */ + case 'Z': + state = seof(); + break; /* Send-End-of-File */ + case 'B': + state = sbreak(); + break; /* Send-Break */ + case 'C': + kserial.flushAlways(); + return true; /* Complete */ + case 'A': + kserial.flushAlways(); + return false; /* "Abort" */ + default: + kserial.flushAlways(); + return false; /* Unknown, fail */ + } + } +} + +/* + * s i n i t + * + * Send Initiate: send this host's parameters and get other side's back. + */ + +char KModem::sinit() +{ + int num, len; /* Packet number, length */ + + if (numtry++ > MAXTRY) + return('A'); /* If too many tries, give up */ + spar(packet); /* Fill up init info packet */ + + flushinput(); /* Flush pending input */ + + spack('S',n,6,packet); /* Send an S packet */ + switch(rpack(&len,&num,recpkt)) /* What was the reply? */ + { + case 'N': + return(state); /* NAK, try it again */ + + case 'Y': /* ACK */ + if (n != num) /* If wrong ACK, stay in S state */ + return(state); /* and try again */ + rpar(recpkt); /* Get other side's init info */ + + if (eol == 0) + eol = '\n'; /* Check and set defaults */ + if (quote == 0) + quote = '#'; + + numtry = 0; /* Reset try counter */ + n = (n+1)%64; /* Bump packet count */ + return('F'); /* OK, switch state to F */ + + case 'E': /* Error packet received */ + prerrpkt(recpkt); /* Print it out and */ + return('A'); /* abort */ + + case FALSE: + return(state); /* Receive failure, try again */ + + default: + return('A'); /* Anything else, just "abort" */ + } +} + + +/* + * s f i l e + * + * Send File Header. + */ + +char KModem::sfile() +{ + int num, len; /* Packet number, length */ + char filnam1[MAX_PATH],/* Converted file name */ + *newfilnam, /* Pointer to file name to send */ + *cp; /* char pointer */ + if (numtry++ > MAXTRY) + return('A'); /* If too many tries, give up */ + + if (kfpClosed) /* If not already open, */ + { + if (debug) + debugPrintf(" Opening %s for sending.\n",filnam); + kfp = kfileSystem->open(filnam,"r"); /* open the file to be sent */ + if (!kfp) /* If bad file pointer, give up */ + { + debugPrintf("Cannot open file %s",filnam); + return('A'); + } + kfpClosed=false; + } + + strcpy(filnam1, filnamo); /* Copy file name */ + newfilnam = cp = filnam1; + if (!xflg) + while (*cp != '\0') /* Strip off all leading directory */ + if (*cp++ == '/') /* names (ie. up to the last /). */ + newfilnam = cp; + + len = strlen(newfilnam); /* Compute length of new filename */ + + debugPrintf("Sending %s as %s",filnam,newfilnam); + + spack('F',n,len,newfilnam); /* Send an F packet */ + switch(rpack(&len,&num,recpkt)) /* What was the reply? */ + { + case 'N': /* NAK, just stay in this state, */ + num = (--num<0 ? 63:num); /* unless it's NAK for next packet */ + if (n != num) /* which is just like an ACK for */ + return(state); /* this packet so fall thru to... */ + case 'Y': /* ACK */ + if (n != num) + return(state); /* If wrong ACK, stay in F state */ + numtry = 0; /* Reset try counter */ + n = (n+1)%64; /* Bump packet count */ + size = bufill(packet); /* Get first data from file */ + return('D'); /* Switch state to D */ + case 'E': /* Error packet received */ + prerrpkt(recpkt); /* Print it out and */ + return('A'); /* abort */ + case FALSE: + return(state); /* Receive failure, stay in F state */ + default: + return('A'); /* Something else, just "abort" */ + } +} + + +/* + * s d a t a + * + * Send File Data + */ + +char KModem::sdata() +{ + int num, len; /* Packet number, length */ + + if (numtry++ > MAXTRY) + return('A'); /* If too many tries, give up */ + spack('D',n,size,packet); /* Send a D packet */ + switch(rpack(&len,&num,recpkt)) /* What was the reply? */ + { + case 'N': /* NAK, just stay in this state, */ + num = (--num<0 ? 63:num); /* unless it's NAK for next packet */ + if (n != num) /* which is just like an ACK for */ + return(state); /* this packet so fall thru to... */ + case 'Y': /* ACK */ + if (n != num) + return(state); /* If wrong ACK, fail */ + numtry = 0; /* Reset try counter */ + n = (n+1)%64; /* Bump packet count */ + if ((size = bufill(packet)) == EOF) /* Get data from file */ + return('Z'); /* If EOF set state to that */ + return('D'); /* Got data, stay in state D */ + case 'E': /* Error packet received */ + prerrpkt(recpkt); /* Print it out and */ + return('A'); /* abort */ + case FALSE: + return(state); /* Receive failure, stay in D */ + default: + return('A'); /* Anything else, "abort" */ + } +} + + +/* + * s e o f + * + * Send End-Of-File. + */ + +char KModem::seof() +{ + int num, len; /* Packet number, length */ + if (numtry++ > MAXTRY) + return('A'); /* If too many tries, "abort" */ + + spack('Z',n,0,packet); /* Send a 'Z' packet */ + switch(rpack(&len,&num,recpkt)) /* What was the reply? */ + { + case 'N': /* NAK, just stay in this state, */ + num = (--num<0 ? 63:num); /* unless it's NAK for next packet, */ + if (n != num) /* which is just like an ACK for */ + return(state); /* this packet so fall thru to... */ + case 'Y': /* ACK */ + if (n != num) + return(state); /* If wrong ACK, hold out */ + numtry = 0; /* Reset try counter */ + n = (n+1)%64; /* and bump packet count */ + if (debug) + debugPrintf(" Closing input file %s, ",filnam); + kfp.close(); /* Close the input file */ + kfpClosed = true; /* Set flag indicating no file open */ + if (debug) + debugPrintf("looking for next file...\n"); + if (gnxtfl() == FALSE) /* No more files go? */ + return('B'); /* if not, break, EOT, all done */ + if (debug) + debugPrintf(" New file is %s\n",filnam); + return('F'); /* More files, switch state to F */ + case 'E': /* Error packet received */ + prerrpkt(recpkt); /* Print it out and */ + return('A'); /* abort */ + case FALSE: + return(state); /* Receive failure, stay in Z */ + default: + return('A'); /* Something else, "abort" */ + } +} + + +/* + * s b r e a k + * + * Send Break (EOT) + */ + +char KModem::sbreak() +{ + int num, len; /* Packet number, length */ + if (numtry++ > MAXTRY) + return('A'); /* If too many tries "abort" */ + + spack('B',n,0,packet); /* Send a B packet */ + switch (rpack(&len,&num,recpkt)) /* What was the reply? */ + { + case 'N': /* NAK, just stay in this state, */ + num = (--num<0 ? 63:num); /* unless NAK for previous packet, */ + if (n != num) /* which is just like an ACK for */ + return(state); /* this packet so fall thru to... */ + case 'Y': /* ACK */ + if (n != num) + return(state); /* If wrong ACK, fail */ + numtry = 0; /* Reset try counter */ + n = (n+1)%64; /* and bump packet count */ + return('C'); /* Switch state to Complete */ + case 'E': /* Error packet received */ + prerrpkt(recpkt); /* Print it out and */ + return('A'); /* abort */ + case FALSE: + return(state); /* Receive failure, stay in B */ + default: + return ('A'); /* Other, "abort" */ + } +} + +/* + * r i n i t + * + * Receive Initialization + */ + +char KModem::rinit() +{ + int len, num; /* Packet length, number */ + + if (numtry++ > MAXTRY) + return('A'); /* If too many tries, "abort" */ + + char rs=rpack(&len,&num,packet); + if (debug) + debugPrintf(" recsw-rinit state: %c\n",rs); + switch(rs) /* Get a packet */ + { + case 'S': /* Send-Init */ + rpar(packet); /* Get the other side's init data */ + spar(packet); /* Fill up packet with my init info */ + spack('Y',n,6,packet); /* ACK with my parameters */ + oldtry = numtry; /* Save old try count */ + numtry = 0; /* Start a new counter */ + n = (n+1)%64; /* Bump packet number, mod 64 */ + return('F'); /* Enter File-Receive state */ + case 'E': /* Error packet received */ + prerrpkt(recpkt); /* Print it out and */ + return('A'); /* abort */ + case FALSE: /* Didn't get packet */ + spack('N',n,0,0); /* Return a NAK */ + return(state); /* Keep trying */ + default: + return('A'); /* Some other packet type, "abort" */ + } +} + +/* + * r f i l e + * + * Receive File Header + */ + +char KModem::rfile() +{ + int num, len; /* Packet number, length */ + + if (numtry++ > MAXTRY) + return('A'); /* "abort" if too many tries */ + + char rs = rpack(&len,&num,packet); + if (debug) + debugPrintf(" recsw-rfile state: %c\n",rs); + switch(rs) /* Get a packet */ + { + case 'S': /* Send-Init, maybe our ACK lost */ + if (oldtry++ > MAXTRY) + return('A'); /* If too many tries abort */ + if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */ + { /* Yes, ACK it again with */ + spar(packet); /* our Send-Init parameters */ + spack('Y',num,6,packet); + numtry = 0; /* Reset try counter */ + return(state); /* Stay in this state */ + } + else + return('A'); /* Not previous packet, "abort" */ + case 'Z': /* End-Of-File */ + if (oldtry++ > MAXTRY) + return('A'); + if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */ + { /* Yes, ACK it again. */ + spack('Y',num,0,0); + numtry = 0; + return(state); /* Stay in this state */ + } + else + return('A'); /* Not previous packet, "abort" */ + case 'F': /* File Header (just what we want) */ + if (num != n) + return('A'); /* The packet number must be right */ + { + char filnam1[MAX_PATH]; /* Holds the converted file name */ + char *subNam=filnam1; + if(rootpath.length()>0) + { + strcpy(filnam1, rootpath.c_str()); + subNam += rootpath.length(); + if(filnam1[strlen(filnam1)-1]!='/') + { + filnam1[strlen(filnam1)]='/'; + filnam1[strlen(filnam1)+1]=0; + subNam++; + } + } + strcpy(subNam, packet); /* Copy the file name */ + kfp = kfileSystem->open(filnam1,FILE_WRITE); + if (!kfp) /* Try to open a new file */ + { + if(errStr != 0) + (*errStr) += ("Cannot create %s\n",filnam1); /* Give up if can't */ + return('A'); + } + else /* OK, give message */ + { + debugPrintf("Receiving %s as %s\n",packet,filnam1); + kfpClosed=false; + } + } + + spack('Y',n,0,0); /* Acknowledge the file header */ + oldtry = numtry; /* Reset try counters */ + numtry = 0; /* ... */ + n = (n+1)%64; /* Bump packet number, mod 64 */ + return('D'); /* Switch to Data state */ + case 'B': /* Break transmission (EOT) */ + if (num != n) + return ('A'); /* Need right packet number here */ + spack('Y',n,0,0); /* Say OK */ + return('C'); /* Go to complete state */ + case 'E': /* Error packet received */ + prerrpkt(recpkt); /* Print it out and */ + return('A'); /* abort */ + case FALSE: /* Didn't get packet */ + spack('N',n,0,0); /* Return a NAK */ + return(state); /* Keep trying */ + default: + return ('A'); /* Some other packet, "abort" */ + } +} + +/* + * r d a t a + * + * Receive Data + */ + +char KModem::rdata() +{ + int num, len; /* Packet number, length */ + if (numtry++ > MAXTRY) + return('A'); /* "abort" if too many tries */ + + char rs=rpack(&len,&num,packet); + if (debug) + debugPrintf(" recsw-rdata state: %c\n",rs); + switch(rs) /* Get packet */ + { + case 'D': /* Got Data packet */ + if (num != n) /* Right packet? */ + { /* No */ + if (oldtry++ > MAXTRY) + return('A'); /* If too many tries, abort */ + if (num == ((n==0) ? 63:n-1)) /* Else check packet number */ + { /* Previous packet again? */ + spack('Y',num,6,packet); /* Yes, re-ACK it */ + numtry = 0; /* Reset try counter */ + return(state); /* Don't write out data! */ + } + else + return('A'); /* sorry, wrong number */ + } + /* Got data with right packet number */ + bufemp(packet,len); /* Write the data to the file */ + spack('Y',n,0,0); /* Acknowledge the packet */ + oldtry = numtry; /* Reset the try counters */ + numtry = 0; /* ... */ + n = (n+1)%64; /* Bump packet number, mod 64 */ + return('D'); /* Remain in data state */ + case 'F': /* Got a File Header */ + if (oldtry++ > MAXTRY) + return('A'); /* If too many tries, "abort" */ + if (num == ((n==0) ? 63:n-1)) /* Else check packet number */ + { /* It was the previous one */ + spack('Y',num,0,0); /* ACK it again */ + numtry = 0; /* Reset try counter */ + return(state); /* Stay in Data state */ + } + else + return('A'); /* Not previous packet, "abort" */ + case 'Z': /* End-Of-File */ + if (num != n) + return('A'); /* Must have right packet number */ + spack('Y',n,0,0); /* OK, ACK it. */ + kfp.close(); /* Close the file */ + kfpClosed=true; + n = (n+1)%64; /* Bump packet number */ + return('F'); /* Go back to Receive File state */ + case 'E': /* Error packet received */ + prerrpkt(recpkt); /* Print it out and */ + return('A'); /* abort */ + case FALSE: /* Didn't get packet */ + spack('N',n,0,0); /* Return a NAK */ + return(state); /* Keep trying */ + default: + return('A'); /* Some other packet, "abort" */ + } +} + +/* + * s p a c k + * + * Send a Packet + */ + +int KModem::spack(char type, int num, int len, char *data) +{ + int i; /* Character loop counter */ + char chksum, buffer[100]; /* Checksum, packet buffer */ + char *bufp; /* Buffer pointer */ + + if (debug>1) /* Display outgoing packet */ + { + if (data != NULL) + data[len] = '\0'; /* Null-terminate data to print it */ + debugPrintf("\n spack type: %c\n",type); + debugPrintf(" num: %d\n",num); + debugPrintf(" len: %d\n",len); + if (data != NULL) + debugPrintf(" data: \"%s\"\n",data); + } + + bufp = buffer; /* Set up buffer pointer */ + for (i=1; i<=pad; i++) + sendChar(&kserial,padchar); /* Issue any padding */ + + *bufp++ = SOH; /* Packet marker, ASCII 1 (SOH) */ + *bufp++ = (len+3+' '); /* Send the character count */ + chksum = (len+3+' '); /* Initialize the checksum */ + *bufp++ = (num+' '); /* Packet number */ + chksum += (num+' '); /* Update checksum */ + *bufp++ = type; /* Packet type */ + chksum += type; /* Update checksum */ + + for (i=0; i> 6)+chksum)&077; /* Compute final checksum */ + *bufp++ = (chksum+' '); /* Put it in the packet */ + if ( mflg ) + *bufp++ = eol; /* MacKermit needs this */ + *bufp = eol; /* Extra-packet line terminator */ + for(i=0;i MAXTIM) || (timint < MINTIM)) + timint = MYTIME; + + while (t != SOH) /* Wait for packet header */ + { + if((t=recvChar(&kserial,timint))<0) + return('A'); + t &= 0177; /* Handle parity */ + } + + done = FALSE; /* Got SOH, init loop */ + while (!done) /* Loop to get a packet */ + { + if((t=recvChar(&kserial,timint))<0) + return('A'); + if (!image) + t &= 0177; /* Handle parity */ + if (t == SOH) + continue; /* Resynchronize if SOH */ + cchksum = t; /* Start the checksum */ + *len = (t-' ')-3; /* Character count */ + + if((t=recvChar(&kserial,timint))<0) + return('A'); + if (!image) + t &= 0177; /* Handle parity */ + if (t == SOH) + continue; /* Resynchronize if SOH */ + cchksum = cchksum + t; /* Update checksum */ + *num = (t-' '); /* Packet number */ + + if((t=recvChar(&kserial,timint))<0) + return('A'); + if (!image) + t &= 0177; /* Handle parity */ + if (t == SOH) + continue; /* Resynchronize if SOH */ + cchksum = cchksum + t; /* Update checksum */ + type = t; /* Packet type */ + + for (i=0; i<*len; i++) /* The data itself, if any */ + { /* Loop for character count */ + if((t=recvChar(&kserial,timint))<0) + return('A'); + if (!image) + t &= 0177; /* Handle parity */ + if (t == SOH) + continue; /* Resynch if SOH */ + cchksum = cchksum + t; /* Update checksum */ + data[i] = t; /* Put it in the data buffer */ + } + data[*len] = 0; /* Mark the end of the data */ + + if((t=recvChar(&kserial,timint))<0) + return('A'); + rchksum = (t-' '); /* Convert to numeric */ + if((t=recvChar(&kserial,timint))<0) + return('A'); + if (!image) + t &= 0177; /* Handle parity */ + if (t == SOH) + continue; /* Resynchronize if SOH */ + done = TRUE; /* Got checksum, done */ + } + + if (debug>1) /* Display incoming packet */ + { + if (data != NULL) + data[*len] = '\0'; /* Null-terminate data to print it */ + debugPrintf("\n rpack type: %c\n",type); + debugPrintf(" num: %d\n",*num); + debugPrintf(" len: %d\n",*len); + if (data != NULL) + debugPrintf(" data: \"%s\"\n",data); + } + /* Fold in bits 7,8 to compute */ + cchksum = (((cchksum&0300) >> 6)+cchksum)&077; /* final checksum */ + + if (cchksum != rchksum) + return(FALSE); + + return(type); /* All OK, return packet type */ +} + +/* + * b u f i l l + * + * Get a bufferful of data from the file that's being sent. + * Only control-quoting is done; 8-bit & repeat count prefixes are + * not handled. + */ + +int KModem::bufill(char buffer[]) +{ + int i; /* Loop index */ + char t; /* Char read from file */ + char t7; /* 7-bit version of above */ + + i = 0; /* Init data buffer pointer */ + while(dataHandler(&kfp,0,&t,1)) /* Get the next character */ + { + t7 = t & 0177; /* Get low order 7 bits */ + if (t7 < SP || t7==DEL || t7==quote) /* Does this char require */ + { /* special handling? */ + if (t=='\n' && !image) + { /* Do LF->CRLF mapping if !image */ + buffer[i++] = quote; + buffer[i++] = '\r' ^ 64; + } + buffer[i++] = quote; /* Quote the character */ + if (t7 != quote) + { + t = (t ^ 64); /* and uncontrolify */ + t7 = (t7 ^ 64); + } + } + if (image) + buffer[i++] = t; /* Deposit the character itself */ + else + buffer[i++] = t7; + if (i >= spsiz-8) + return(i); /* Check length */ + } + if (i==0) + return(EOF); /* Wind up here only on EOF */ + return(i); /* Handle partial buffer */ +} + +void KModem::bufemp(char buffer[], int len) +{ + int i; /* Counter */ + char t; /* Character holder */ + + for (i=0; ic_str(); + filnamo = filnam; + if (debug) + debugPrintf(" gnxtfl: filelist = \"%s\"\n",filnam); + return TRUE; /* else succeed */ +} + +void KModem::spar(char data[]) +{ + data[0] = (MAXPACKSIZ + ' '); /* Biggest packet I can receive */ + data[1] = (MYTIME + ' '); /* When I want to be timed out */ + data[2] = (MYPAD + ' '); /* How much padding I need */ + data[3] = (MYPCHAR ^ 64); /* Padding character I want */ + data[4] = (MYEOL + ' '); /* End-Of-Line character I want */ + data[5] = MYQUOTE; /* Control-Quote character I send */ +} + + +void KModem::setTransmitList(String **fileList, int numFiles) +{ + filelist = fileList; + filecount = numFiles; + filenum = 0; +} + + +/* r p a r + * + * Get the other host's send-init parameters + * + */ + +void KModem::rpar(char data[]) +{ + spsiz = (data[0]- ' '); /* Maximum send packet size */ + timint = (data[1]- ' '); /* When I should time out */ + pad = (data[2]- ' '); /* Number of pads to send */ + padchar = (data[3] ^ 64); /* Padding character to send */ + eol = (data[4]- ' '); /* EOL character I must send */ + quote = data[5]; /* Incoming data quote character */ +} + +/* + * p r e r r p k t + * + * Print contents of error packet received from remote host. + */ +void KModem::prerrpkt(char *msg) +{ + if(errStr != 0) + { + (*errStr)+=msg; + debugPrintf("kermit: %s\n",msg); + } +} + + +#endif \ No newline at end of file diff --git a/zimodem/proto_xmodem.h b/zimodem/proto_xmodem.h new file mode 100644 index 0000000..3529828 --- /dev/null +++ b/zimodem/proto_xmodem.h @@ -0,0 +1,140 @@ +/* + Copyright 2018-2019 Bo Zimmerman + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#include + +class XModem +{ + typedef enum + { + Crc, + ChkSum + } transfer_t; + + private: + //holds readed byte (due to dataAvail()) + int byte; + //expected block number + unsigned char blockNo; + //extended block number, send to dataHandler() + unsigned long blockNoExt; + //retry counter for NACK + int retries; + //buffer + char buffer[128]; + //repeated block flag + bool repeatedBlock; + File *xfile = null; + ZSerial xserial; + + int (*recvChar)(ZSerial *ser, int); + void (*sendChar)(ZSerial *ser, char); + bool (*dataHandler)(File *xfile, unsigned long number, char *buffer, int len); + unsigned short crc16_ccitt(char *buf, int size); + bool dataAvail(int delay); + int dataRead(int delay); + void dataWrite(char symbol); + bool receiveFrameNo(void); + bool receiveData(void); + bool checkCrc(void); + bool checkChkSum(void); + bool receiveFrames(transfer_t transfer); + bool sendNack(void); + void init(void); + + bool transmitFrames(transfer_t); + unsigned char generateChkSum(void); + + public: + static const unsigned char XMO_NACK = 21; + static const unsigned char XMO_ACK = 6; + + static const unsigned char XMO_SOH = 1; + static const unsigned char XMO_EOT = 4; + static const unsigned char XMO_CAN = 0x18; + + static const int receiveDelay=7000; + static const int rcvRetryLimit = 10; + + + XModem(File &f, + FlowControlType commandFlow, + int (*recvChar)(ZSerial *ser, int), + void (*sendChar)(ZSerial *ser, char), + bool (*dataHandler)(File *xfile, unsigned long, char*, int)); + bool receive(); + bool transmit(); +}; + +static int xReceiveSerial(ZSerial *ser, int del) +{ + unsigned long end=micros() + (del * 1000L); + while(micros() < end) + { + serialOutDeque(); + if(ser->available() > 0) + { + int c=ser->read(); + logSerialIn(c); + return c; + } + yield(); + } + return -1; +} + +static void xSendSerial(ZSerial *ser, char c) +{ + ser->write((uint8_t)c); + ser->flush(); +} + +static bool xUDataHandler(File *xfile, unsigned long number, char *buf, int sz) +{ + for(int i=0;iwrite((uint8_t)buf[i]); + return true; +} + +static bool xDDataHandler(File *xfile, unsigned long number, char *buf, int sz) +{ + for(int i=0;iread(); + if(c<0) + { + if(i==0) + return false; + buf[i] = (char)26; + } + else + buf[i] = (char)c; + } + return true; +} + +static boolean xDownload(FlowControlType commandFlow, File &f, String &errors) +{ + XModem xmo(f,commandFlow, xReceiveSerial, xSendSerial, xDDataHandler); + bool result = xmo.transmit(); + return result; +} + +static boolean xUpload(FlowControlType commandFlow, File &f, String &errors) +{ + XModem xmo(f,commandFlow, xReceiveSerial, xSendSerial, xUDataHandler); + bool result = xmo.receive(); + return result; +} diff --git a/zimodem/proto_xmodem.ino b/zimodem/proto_xmodem.ino new file mode 100644 index 0000000..3369ac7 --- /dev/null +++ b/zimodem/proto_xmodem.ino @@ -0,0 +1,363 @@ +/* + Copyright 2018-2019 Bo Zimmerman + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#ifdef INCLUDE_SD_SHELL + +XModem::XModem(File &f, + FlowControlType commandFlow, + int (*recvChar)(ZSerial *ser, int msDelay), + void (*sendChar)(ZSerial *ser, char sym), + bool (*dataHandler)(File *xfile, unsigned long number, char *buffer, int len)) +{ + this->xfile = &f; + this->sendChar = sendChar; + this->recvChar = recvChar; + this->dataHandler = dataHandler; + this->xserial.setFlowControlType(FCT_DISABLED); + if(commandFlow==FCT_RTSCTS) + this->xserial.setFlowControlType(FCT_RTSCTS); + this->xserial.setPetsciiMode(false); + this->xserial.setXON(true); +} + +bool XModem::dataAvail(int delay) +{ + if (this->byte != -1) + return true; + if ((this->byte = this->recvChar(&xserial,delay)) != -1) + return true; + else + return false; +} + +int XModem::dataRead(int delay) +{ + int b; + if(this->byte != -1) + { + b = this->byte; + this->byte = -1; + return b; + } + return this->recvChar(&xserial,delay); +} + +void XModem::dataWrite(char symbol) +{ + this->sendChar(&xserial,symbol); +} + +bool XModem::receiveFrameNo() +{ + unsigned char num = (unsigned char)this->dataRead(XModem::receiveDelay); + unsigned char invnum = (unsigned char)this-> dataRead(XModem::receiveDelay); + this->repeatedBlock = false; + //check for repeated block + if (invnum == (255-num) && num == this->blockNo-1) { + this->repeatedBlock = true; + return true; + } + + if(num != this-> blockNo || invnum != (255-num)) + return false; + else + return true; +} + +bool XModem::receiveData() +{ + for(int i = 0; i < 128; i++) { + int byte = this->dataRead(XModem::receiveDelay); + if(byte != -1) + this->buffer[i] = (unsigned char)byte; + else + return false; + } + return true; +} + +bool XModem::checkCrc() +{ + unsigned short frame_crc = ((unsigned char)this->dataRead(XModem::receiveDelay)) << 8; + + frame_crc |= (unsigned char)this->dataRead(XModem::receiveDelay); + //now calculate crc on data + unsigned short crc = this->crc16_ccitt(this->buffer, 128); + + if(frame_crc != crc) + return false; + else + return true; +} + +bool XModem::checkChkSum() +{ + unsigned char frame_chksum = (unsigned char)this->dataRead(XModem::receiveDelay); + //calculate chksum + unsigned char chksum = 0; + for(int i = 0; i< 128; i++) { + chksum += this->buffer[i]; + } + if(frame_chksum == chksum) + return true; + else + return false; +} + +bool XModem::sendNack() +{ + this->dataWrite(XModem::XMO_NACK); + this->retries++; + if(this->retries < XModem::rcvRetryLimit) + return true; + else + return false; +} + +bool XModem::receiveFrames(transfer_t transfer) +{ + this->blockNo = 1; + this->blockNoExt = 1; + this->retries = 0; + while (1) { + char cmd = this->dataRead(1000); + switch(cmd){ + case XModem::XMO_SOH: + if (!this->receiveFrameNo()) { + if (this->sendNack()) + break; + else + return false; + } + if (!this->receiveData()) { + if (this->sendNack()) + break; + else + return false; + }; + if (transfer == Crc) { + if (!this->checkCrc()) { + if (this->sendNack()) + break; + else + return false; + } + } else { + if(!this->checkChkSum()) { + if (this->sendNack()) + break; + else + return false; + } + } + //callback + if(this->dataHandler != NULL && this->repeatedBlock == false) + if(!this->dataHandler(xfile,this->blockNoExt, this->buffer, 128)) { + return false; + } + //ack + this->dataWrite(XModem::XMO_ACK); + if(this->repeatedBlock == false) + { + this->blockNo++; + this->blockNoExt++; + } + break; + case XModem::XMO_EOT: + this->dataWrite(XModem::XMO_ACK); + return true; + case XModem::XMO_CAN: + //wait second CAN + if(this->dataRead(XModem::receiveDelay) ==XModem::XMO_CAN) + { + this->dataWrite(XModem::XMO_ACK); + //this->flushInput(); + return false; + } + //something wrong + this->dataWrite(XModem::XMO_CAN); + this->dataWrite(XModem::XMO_CAN); + this->dataWrite(XModem::XMO_CAN); + return false; + default: + //something wrong + this->dataWrite(XModem::XMO_CAN); + this->dataWrite(XModem::XMO_CAN); + this->dataWrite(XModem::XMO_CAN); + return false; + } + + } +} + +void XModem::init() +{ + //set preread byte + this->byte = -1; +} + +bool XModem::receive() +{ + this->init(); + + for (int i =0; i < 16; i++) + { + this->dataWrite('C'); + if (this->dataAvail(1500)) + { + bool ok = receiveFrames(Crc); + xserial.flushAlways(); + return ok; + } + + } + for (int i =0; i < 16; i++) + { + this->dataWrite(XModem::XMO_NACK); + if (this->dataAvail(1500)) + { + bool ok = receiveFrames(ChkSum); + xserial.flushAlways(); + return ok; + } + } +} + +unsigned short XModem::crc16_ccitt(char *buf, int size) +{ + unsigned short crc = 0; + while (--size >= 0) { + int i; + crc ^= (unsigned short) *buf++ << 8; + for (i = 0; i < 8; i++) + if (crc & 0x8000) + crc = crc << 1 ^ 0x1021; + else + crc <<= 1; + } + return crc; +} + +unsigned char XModem::generateChkSum(void) +{ + //calculate chksum + unsigned char chksum = 0; + for(int i = 0; i< 128; i++) { + chksum += this->buffer[i]; + } + return chksum; +} + +bool XModem::transmitFrames(transfer_t transfer) +{ + this->blockNo = 1; + this->blockNoExt = 1; + // use this only in unit tetsing + //memset(this->buffer, 'A', 128); + while(1) + { + //get data + if (this->dataHandler != NULL) + { + if( false == this->dataHandler(xfile,this->blockNoExt, this->buffer, 128)) + { + //end of transfer + this->sendChar(&xserial,XModem::XMO_EOT); + //wait ACK + if (this->dataRead(XModem::receiveDelay) == XModem::XMO_ACK) + return true; + else + return false; + } + } + else + { + //cancel transfer - send CAN twice + this->sendChar(&xserial,XModem::XMO_CAN); + this->sendChar(&xserial,XModem::XMO_CAN); + //wait ACK + if (this->dataRead(XModem::receiveDelay) == XModem::XMO_ACK) + return true; + else + return false; + } + //send SOH + this->sendChar(&xserial,XModem::XMO_SOH); + //send frame number + this->sendChar(&xserial,this->blockNo); + //send inv frame number + this->sendChar(&xserial,(unsigned char)(255-(this->blockNo))); + //send data + for(int i = 0; i <128; i++) + this->sendChar(&xserial,this->buffer[i]); + //send checksum or crc + if (transfer == ChkSum) { + this->sendChar(&xserial,this->generateChkSum()); + } else { + unsigned short crc; + crc = this->crc16_ccitt(this->buffer, 128); + + this->sendChar(&xserial,(unsigned char)(crc >> 8)); + this->sendChar(&xserial,(unsigned char)(crc)); + + } + //TO DO - wait NACK or CAN or ACK + int ret = this->dataRead(XModem::receiveDelay); + switch(ret) + { + case XModem::XMO_ACK: //data is ok - go to next chunk + this->blockNo++; + this->blockNoExt++; + continue; + case XModem::XMO_NACK: //resend data + continue; + case XModem::XMO_CAN: //abort transmision + return false; + } + } + return false; +} + +bool XModem::transmit() +{ + int retry = 0; + int sym; + this->init(); + + //wait for CRC transfer + while(retry < 32) + { + if(this->dataAvail(1000)) + { + sym = this->dataRead(1); //data is here - no delay + if(sym == 'C') + { + bool ok = this->transmitFrames(Crc); + xserial.flushAlways(); + return ok; + } + if(sym == XModem::XMO_NACK) + { + bool ok = this->transmitFrames(ChkSum); + xserial.flushAlways(); + return ok; + } + } + retry++; + } + return false; +} + +#endif diff --git a/zimodem/proto_zmodem.h b/zimodem/proto_zmodem.h new file mode 100644 index 0000000..f99d519 --- /dev/null +++ b/zimodem/proto_zmodem.h @@ -0,0 +1,504 @@ +/* + * zmodem.h + * zmodem constants + * (C) Mattheij Computer Service 1994 + * + * Date: Thu, 19 Nov 2015 10:10:02 +0100 + * From: Jacques Mattheij + * Subject: Re: zmodem license + * To: Stephen Hurd, Fernando Toledo + * CC: Rob Swindell + * + * Hello there to all of you, + * + * So, this email will then signify as the transfer of any and all rights I + * held up to this point with relation to the copyright of the zmodem + * package as released by me many years ago and all associated files to + * Stephen Hurd. Fernando Toledo and Rob Swindell are named as + * witnesses to this transfer. + * + * ... + * + * best regards, + * + * Jacques Mattheij + */ + +/* $Id: zmodem.h,v 1.55 2018/02/01 08:20:19 deuce Exp $ */ + +#ifndef _ZMODEM_H +#define _ZMODEM_H + +#define ZMODEM_FILE_SIZE_MAX 0xffffffff /* 32-bits, blame Chuck */ + +/* + * ascii constants + */ + +#define BOOL bool +#define BYTE uint8_t +#define uchar uint8_t +#define MAX_PATH 253 +#define NOINP -1 /* input buffer empty (incom only) */ +#define LOG_EMERG 0 /* system is unusable */ +#define LOG_ALERT 1 /* action must be taken immediately */ +#define LOG_CRIT 2 /* critical conditions */ +#define LOG_ERR 3 /* error conditions */ +#define LOG_WARNING 4 /* warning conditions */ +#define LOG_NOTICE 5 /* normal but significant condition */ +#define LOG_INFO 6 /* informational */ +#define LOG_DEBUG 7 /* debug-level messages */ + +#define ZMO_DLE 0x10 +#define ZMO_XON 0x11 +#define ZMO_XOFF 0x13 +#define ZMO_CAN 0x18 + +#ifndef INT_TO_BOOL +#define INT_TO_BOOL(x) ((x)?ZTRUE:ZFALSE) +#endif + +#define ZFALSE 0 +#define ZTRUE 1 + +#define TERMINATE(str) str[sizeof(str)-1]=0 + +/* This is a bound-safe version of strcpy basically - only works with fixed-length arrays */ +#ifdef SAFECOPY_USES_SPRINTF +#define SAFECOPY(dst,src) sprintf(dst,"%.*s",(int)sizeof(dst)-1,src) +#else /* strncpy is faster */ +#define SAFECOPY(dst,src) (strncpy(dst,src,sizeof(dst)), TERMINATE(dst)) +#endif + +/* + * zmodem constants + */ + +#define ZBLOCKLEN 1024 /* "true" Zmodem max subpacket length */ + +#define ZMAXHLEN 0x10 /* maximum header information length */ +#define ZMAXSPLEN 0x400 /* maximum subpacket length */ + + +#define ZPAD 0x2a /* pad character; begins frames */ +#define ZDLE 0x18 /* ctrl-x zmodem escape */ +#define ZDLEE 0x58 /* escaped ZDLE */ + +#define ZBIN 0x41 /* binary frame indicator (CRC16) */ +#define ZHEX 0x42 /* hex frame indicator */ +#define ZBIN32 0x43 /* binary frame indicator (CRC32) */ +#define ZBINR32 0x44 /* run length encoded binary frame (CRC32) */ + +#define ZVBIN 0x61 /* binary frame indicator (CRC16) */ +#define ZVHEX 0x62 /* hex frame indicator */ +#define ZVBIN32 0x63 /* binary frame indicator (CRC32) */ +#define ZVBINR32 0x64 /* run length encoded binary frame (CRC32) */ + +#define ZRESC 0x7e /* run length encoding flag / escape character */ + +/* + * zmodem frame types + */ + +#define ZRQINIT 0x00 /* request receive init (s->r) */ +#define ZRINIT 0x01 /* receive init (r->s) */ +#define ZSINIT 0x02 /* send init sequence (optional) (s->r) */ +#define ZACK 0x03 /* ack to ZRQINIT ZRINIT or ZSINIT (s<->r) */ +#define ZFILE 0x04 /* file name (s->r) */ +#define ZSKIP 0x05 /* skip this file (r->s) */ +#define ZNAK 0x06 /* last packet was corrupted (?) */ +#define ZABORT 0x07 /* abort batch transfers (?) */ +#define ZFIN 0x08 /* finish session (s<->r) */ +#define ZRPOS 0x09 /* resume data transmission here (r->s) */ +#define ZDATA 0x0a /* data packet(s) follow (s->r) */ +#define ZEOF 0x0b /* end of file reached (s->r) */ +#define ZFERR 0x0c /* fatal read or write error detected (?) */ +#define ZCRC 0x0d /* request for file CRC and response (?) */ +#define ZCHALLENGE 0x0e /* security challenge (r->s) */ +#define ZCOMPL 0x0f /* request is complete (?) */ +#define ZCAN 0x10 /* pseudo frame; + other end cancelled session with 5* CAN */ +#define ZFREECNT 0x11 /* request free bytes on file system (s->r) */ +#define ZCOMMAND 0x12 /* issue command (s->r) */ +#define ZSTDERR 0x13 /* output data to stderr (??) */ + +/* + * ZDLE sequences + */ + +#define ZCRCE 0x68 /* CRC next, frame ends, header packet follows */ +#define ZCRCG 0x69 /* CRC next, frame continues nonstop */ +#define ZCRCQ 0x6a /* CRC next, frame continuous, ZACK expected */ +#define ZCRCW 0x6b /* CRC next, frame ends, ZACK expected */ +#define ZRUB0 0x6c /* translate to rubout 0x7f */ +#define ZRUB1 0x6d /* translate to rubout 0xff */ + +/* + * frame specific data. + * entries are prefixed with their location in the header array. + */ + +/* + * Byte positions within header array + */ + +#define FTYPE 0 /* frame type */ + +#define ZF0 4 /* First flags byte */ +#define ZF1 3 +#define ZF2 2 +#define ZF3 1 + +#define ZP0 1 /* Low order 8 bits of position */ +#define ZP1 2 +#define ZP2 3 +#define ZP3 4 /* High order 8 bits of file position */ + +/* + * ZRINIT frame + * zmodem receiver capability flags + */ + +#define ZF0_CANFDX 0x01 /* Receiver can send and receive true full duplex */ +#define ZF0_CANOVIO 0x02 /* receiver can receive data during disk I/O */ +#define ZF0_CANBRK 0x04 /* receiver can send a break signal */ +#define ZF0_CANCRY 0x08 /* Receiver can decrypt DONT USE */ +#define ZF0_CANLZW 0x10 /* Receiver can uncompress DONT USE */ +#define ZF0_CANFC32 0x20 /* Receiver can use 32 bit Frame Check */ +#define ZF0_ESCCTL 0x40 /* Receiver expects ctl chars to be escaped */ +#define ZF0_ESC8 0x80 /* Receiver expects 8th bit to be escaped */ + +#define ZF1_CANVHDR 0x01 /* Variable headers OK */ + +/* + * ZSINIT frame + * zmodem sender capability + */ + +#define ZF0_TESCCTL 0x40 /* Transmitter expects ctl chars to be escaped */ +#define ZF0_TESC8 0x80 /* Transmitter expects 8th bit to be escaped */ + +#define ZATTNLEN 0x20 /* Max length of attention string */ +#define ALTCOFF ZF1 /* Offset to alternate canit string, 0 if not used */ + +/* + * ZFILE frame + */ + +/* + * Conversion options one of these in ZF0 + */ + +#define ZF0_ZCBIN 1 /* Binary transfer - inhibit conversion */ +#define ZF0_ZCNL 2 /* Convert NL to local end of line convention */ +#define ZF0_ZCRESUM 3 /* Resume interrupted file transfer */ + +/* + * Management include options, one of these ored in ZF1 + */ + +#define ZF1_ZMSKNOLOC 0x80 /* Skip file if not present at rx */ +#define ZF1_ZMMASK 0x1f /* Mask for the choices below */ +#define ZF1_ZMNEWL 1 /* Transfer if source newer or longer */ +#define ZF1_ZMCRC 2 /* Transfer if different file CRC or length */ +#define ZF1_ZMAPND 3 /* Append contents to existing file (if any) */ +#define ZF1_ZMCLOB 4 /* Replace existing file */ +#define ZF1_ZMNEW 5 /* Transfer if source newer */ +#define ZF1_ZMDIFF 6 /* Transfer if dates or lengths different */ +#define ZF1_ZMPROT 7 /* Protect destination file */ +#define ZF1_ZMCHNG 8 /* Change filename if destination exists */ + +/* + * Transport options, one of these in ZF2 + */ + +#define ZF2_ZTNOR 0 /* no compression */ +#define ZF2_ZTLZW 1 /* Lempel-Ziv compression */ +#define ZF2_ZTRLE 3 /* Run Length encoding */ + +/* + * Extended options for ZF3, bit encoded + */ + +#define ZF3_ZCANVHDR 0x01 /* Variable headers OK */ + /* Receiver window size override */ +#define ZF3_ZRWOVR 0x04 /* byte position for receive window override/256 */ +#define ZF3_ZXSPARS 0x40 /* encoding for sparse file operations */ + +/* + * ZCOMMAND frame + */ + +#define ZF0_ZCACK1 0x01 /* Acknowledge, then do command */ + +typedef struct { + + BYTE rxd_header[ZMAXHLEN]; /* last received header */ + int rxd_header_len; /* last received header size */ + uint32_t rxd_header_pos; /* last received header position value */ + + /* + * receiver capability flags + * extracted from the ZRINIT frame as received + */ + + BOOL can_full_duplex; + BOOL can_overlap_io; + BOOL can_break; + BOOL can_fcs_32; + BOOL want_fcs_16; + BOOL escape_ctrl_chars; + BOOL escape_8th_bit; + + /* + * file management options. + * only one should be on + */ + + int management_newer; + int management_clobber; + int management_protect; + + /* from zmtx.c */ + + BYTE tx_data_subpacket[8192]; + BYTE rx_data_subpacket[8192]; /* zzap = 8192 */ + + char current_file_name[MAX_PATH+1]; + int64_t current_file_size; + int64_t current_file_pos; + time_t current_file_time; + unsigned current_file_num; + unsigned total_files; + int64_t total_bytes; + unsigned files_remaining; + int64_t bytes_remaining; + int64_t transfer_start_pos; + time_t transfer_start_time; + + int receive_32bit_data; + int use_crc16; + int32_t ack_file_pos; /* file position used in acknowledgement of correctly */ + /* received data subpackets */ + + int last_sent; + + int n_cans; + + /* Stuff added by RRS */ + + /* Status */ + BOOL cancelled; + BOOL local_abort; + BOOL file_skipped; + BOOL no_streaming; + BOOL frame_in_transit; + unsigned recv_bufsize; /* Receiver specified buffer size */ + int32_t crc_request; + unsigned errors; + unsigned consecutive_errors; + + /* Configuration */ + BOOL escape_telnet_iac; + unsigned init_timeout; + unsigned send_timeout; + unsigned recv_timeout; + unsigned crc_timeout; + unsigned max_errors; + unsigned block_size; + unsigned max_block_size; + int64_t max_file_size; /* 0 = unlimited */ + int *log_level; + /* error C2520: conversion from unsigned __int64 to double not implemented, use signed __int64 */ + void* cbdata; +} zmodem_t; + +class ZModem +{ +public: + ZModem(FS *zfs, void* cbdata); + ~ZModem(); + BOOL send_file( char* name, File* fp, BOOL request_init, time_t* start, uint64_t* bytes_sent); + int get_zfin(); + int recv_init(); + int lputs(void* unused, int level, const char* str); + int lprintf(int level, const char *fmt, ...); + int send_zabort(); + int send_zfin(); + int recv_files(const char* download_dir, uint64_t* bytes_received); + unsigned recv_file_data( File*, int64_t offset); + zmodem_t *zm=0; + ZSerial zserial; + FS *zfileSystem=0; +private: + char* ver(char *buf); + const char* source(void); + int rx(); + int tx(BYTE ch); + int send_ack( int32_t pos); + int send_nak(); + int send_zskip(); + int send_zrinit(); + int send_pos_header(int type, int32_t pos, BOOL hex); + int get_zrinit(); + BOOL get_crc( int32_t length, uint32_t* crc); + void parse_zrinit(); + void parse_zfile_subpacket(); + int recv_file_frame(File* fp); + int recv_header_and_check(); + int send_hex(uchar val); + int send_padded_zdle(); + int send_hex_header(unsigned char * p); + int send_bin32_header(unsigned char * p); + int send_bin16_header(unsigned char * p); + int send_bin_header(unsigned char * p); + int send_data32(uchar subpkt_type, unsigned char * p, size_t l); + int send_data16(uchar subpkt_type,unsigned char * p, size_t l); + int send_data(uchar subpkt_type, unsigned char * p, size_t l); + int send_data_subpkt(uchar subpkt_type, unsigned char * p, size_t l); + int data_waiting(unsigned timeout); + void recv_purge(); + void flush(); + int send_raw(unsigned char ch); + int send_esc(unsigned char c); + int recv_data32(unsigned char * p, unsigned maxlen, unsigned* l); + int recv_data16(register unsigned char* p, unsigned maxlen, unsigned* l); + int recv_data(unsigned char* p, size_t maxlen, unsigned* l, BOOL ack); + BOOL recv_subpacket(BOOL ack); + int recv_nibble(); + int recv_hex(); + int recv_raw(); + BOOL recv_bin16_header(); + BOOL recv_hex_header(); + BOOL recv_bin32_header(); + int recv_header_raw(int errors); + int recv_header(); + BOOL request_crc(int32_t length); + BOOL recv_crc(uint32_t* crc); + BOOL handle_zrpos(uint64_t* pos); + BOOL handle_zack(); + BOOL is_connected(); + BOOL is_cancelled(); + int send_from(File* fp, uint64_t pos, uint64_t* sent); + int send_znak(); + int send_zeof(uint32_t pos); + void progress(void* cbdata, int64_t current_pos); + int send_byte(void* unused, uchar ch, unsigned timeout); + int recv_byte(void* unused, unsigned timeout); /* seconds */ + ulong frame_pos(int type); + char* getfname(const char* path); +}; + +static ZModem *initZSerial(FS &fs, FlowControlType commandFlow) +{ + ZModem *modem = new ZModem(&SD, NULL); + modem->zserial.setFlowControlType(FCT_DISABLED); + if(commandFlow==FCT_RTSCTS) + modem->zserial.setFlowControlType(FCT_RTSCTS); + else + modem->zserial.setFlowControlType(FCT_NORMAL); + modem->zserial.setPetsciiMode(false); + modem->zserial.setXON(true); + return modem; +} + +static boolean zDownload(FlowControlType flow, FS &fs, String filePath, String &errors) +{ + time_t starttime = 0; + uint64_t bytes_sent=0; + BOOL success=ZFALSE; + char filePathC[MAX_PATH]; + File F; + + ZModem *modem = initZSerial(fs, flow); + + //static int send_files(char** fname, uint fnames) + F=modem->zfileSystem->open(filePath); + modem->zm->files_remaining = 1; + modem->zm->bytes_remaining = F.size(); + strcpy(filePathC,filePath.c_str()); + success=modem->send_file(filePathC, &F, ZTRUE, &starttime, &bytes_sent); + if(success) + modem->get_zfin(); + F.close(); + + modem->zserial.flushAlways(); + delete modem; + return (success==ZTRUE) && (modem->zm->cancelled==ZFALSE); +} + +static boolean zUpload(FlowControlType flow, FS &fs, String dirPath, String &errors) +{ + BOOL success=ZFALSE; + int i; + char str[MAX_PATH]; + File fp; + int err; + + ZModem *modem = initZSerial(fs,flow); + + //static int receive_files(char** fname_list, int fnames) + //TODO: loop might be necc around here, for multiple files? + i=modem->recv_init(); + if(modem->zm->cancelled || (i<0)) + { + delete modem; + return ZFALSE; + } + switch(i) { + case ZFILE: + //SAFECOPY(fname,zm.current_file_name); + //file_bytes = zm.current_file_size; + //ftime = zm.current_file_time; + //total_files = zm.files_remaining; + //total_bytes = zm.bytes_remaining; + break; + case ZFIN: + case ZCOMPL: + delete modem; + return ZTRUE; // was (!success) + default: + delete modem; + return ZFALSE; + } + + strcpy(str,dirPath.c_str()); + if(str[strlen(str)-1]!='/') + { + str[strlen(str)]='/'; + str[strlen(str)+1]=0; + } + strcpy(str+strlen(str),modem->zm->current_file_name); + + fp = modem->zfileSystem->open(str,FILE_WRITE); + if(!fp) + { + modem->lprintf(LOG_ERR,"Error %d creating %s",errno,str); + modem->send_zabort(); + //zmodem_send_zskip(); //TODO: for when we move to multiple files + //continue; + delete modem; + return ZFALSE; + } + err=modem->recv_file_data(&fp,0); + + if(err<=modem->zm->max_errors && !modem->zm->cancelled) + success=ZTRUE; + + if(success) + modem->send_zfin(); + + fp.close(); + if(modem->zm->local_abort) + { + modem->lprintf(LOG_ERR,"Locally aborted, sending cancel to remote"); + modem->send_zabort(); + delete modem; + return ZFALSE; + } + + modem->zserial.flushAlways(); + delete modem; + return (success == ZTRUE); +} +#endif diff --git a/zimodem/proto_zmodem.ino b/zimodem/proto_zmodem.ino new file mode 100644 index 0000000..bfde252 --- /dev/null +++ b/zimodem/proto_zmodem.ino @@ -0,0 +1,2432 @@ +/* zmodem.c */ + +/* Synchronet ZMODEM Functions */ + +/* $Id: zmodem.c,v 1.120 2018/02/01 08:20:19 deuce Exp $ */ + +/******************************************************************************/ +/* Project : Unite! File : zmodem general Version : 1.02 */ +/* */ +/* (C) Mattheij Computer Service 1994 */ +/* + * Date: Thu, 19 Nov 2015 10:10:02 +0100 + * From: Jacques Mattheij + * Subject: Re: zmodem license + * To: Stephen Hurd, Fernando Toledo + * CC: Rob Swindell + * + * Hello there to all of you, + * + * So, this email will then signify as the transfer of any and all rights I + * held up to this point with relation to the copyright of the zmodem + * package as released by me many years ago and all associated files to + * Stephen Hurd. Fernando Toledo and Rob Swindell are named as + * witnesses to this transfer. + * + * ... + * + * best regards, + * + * Jacques Mattheij + ******************************************************************************/ + +#ifdef INCLUDE_SD_SHELL + +/* + * zmodem primitives and other code common to zmtx and zmrx + */ +#define ENDOFFRAME 2 +#define FRAMEOK 1 +#define TIMEOUT -1 /* rx routine did not receive a character within timeout */ +#define INVHDR -2 /* invalid header received; but within timeout */ +#define ABORTED -3 /* Aborted *or* disconnected */ +#define SUBPKTOVERFLOW -4 /* Subpacket received more than block length */ +#define CRCFAILED -5 /* Failed CRC comparison */ +#define INVALIDSUBPKT -6 /* Invalid Subpacket Type */ +#define ZDLEESC 0x8000 /* one of ZCRCE; ZCRCG; ZCRCQ or ZCRCW was received; ZDLE escaped */ + +#define BADSUBPKT 0x80 + +#define HDRLEN 5 /* size of a zmodem header */ + +ZModem::~ZModem() +{ + if(zm != 0) + free(zm); + zm = 0; +} +ZModem::ZModem(FS *zfs, void* cbdata) +{ + zfileSystem=zfs; + zm = (zmodem_t *)malloc(sizeof(zmodem_t)); + memset(zm,0,sizeof(zmodem_t)); + int log_level=LOG_DEBUG; + zm->log_level=&log_level; + zm->recv_bufsize = (ulong)1024; + zm->no_streaming = ZFALSE; + zm->want_fcs_16 =ZFALSE; + zm->escape_telnet_iac = ZTRUE; + zm->escape_8th_bit = ZFALSE; + zm->escape_ctrl_chars = ZFALSE; + + /* Use sane default values */ + zm->init_timeout=10; /* seconds */ + zm->send_timeout=10; /* seconds (reduced from 15) */ + zm->recv_timeout=10; /* seconds (reduced from 20) */ + zm->crc_timeout=120; /* seconds */ + zm->block_size=ZBLOCKLEN; + zm->max_block_size=ZBLOCKLEN; + zm->max_errors=9; + + zm->cbdata=cbdata; +} + +#if 0 + int ZModem::lputs(void* unused, int level, const char* str) + { + debugPrintf("%s\n",str); // debug off -- seems like can't print to both serials too near each other. + return ZTRUE; + } + + int ZModem::lprintf(int level, const char *fmt, ...) + { + char sbuf[1024]; + va_list argptr; + + va_start(argptr,fmt); + vsnprintf(sbuf,sizeof(sbuf),fmt,argptr); + sbuf[sizeof(sbuf)-1]=0; + va_end(argptr); + return(lputs(NULL,level,sbuf)); + } +#else + int ZModem::lputs(void *unused, int level, const char * str) {} + int ZModem::lprintf(int level, const char *fmt, ...) {} + + #define lprintf //lprintf +#endif + +char* ZModem::getfname(const char* path) +{ + const char* fname; + const char* bslash; + + fname=strrchr(path,'/'); + bslash=strrchr(path,'\\'); + if(bslash>fname) + fname=bslash; + if(fname!=NULL) + fname++; + else + fname=(char*)path; + return((char*)fname); +} + +BOOL ZModem::is_connected() +{ + return(ZTRUE); +} + +BOOL ZModem::is_cancelled() +{ + return(zm->cancelled); +} + +int ZModem::data_waiting(unsigned timeout) +{ + timeout *= 1000; + unsigned long startTime = millis(); + while(zserial.available()==0) + { + unsigned long currentTime = millis(); + unsigned long elapsedTime = currentTime - startTime; + if(elapsedTime >= timeout) + return ZFALSE; + delay(1); + } + return ZTRUE; +} + +/* + * show the progress of the transfer like this: + * zmtx: sending file "garbage" 4096 bytes ( 20%) + */ +void ZModem::progress(void* cbdata, int64_t current_pos) +{ + //debugPrintf("POGRESS %lld\n",current_pos); + // do nothing? +} + +int ZModem::send_byte(void* unused, uchar ch, unsigned timeout) +{ + lprintf(LOG_DEBUG, "Send: %d", ch); + zserial.printb(ch); + //zserial.flush(); // safe flush + return(0); +} + +int ZModem::recv_byte(void* unused, unsigned timeout /* seconds */) +{ + unsigned long startTime = millis(); + timeout *= 1000; + while(zserial.available()==0) + { + unsigned long currentTime = millis(); + unsigned long elapsedTime = currentTime - startTime; + if(elapsedTime >= timeout) + return(NOINP); + delay(1); + yield(); + } + int ch = zserial.read(); + lprintf(LOG_DEBUG, "Recvd: %d", ch); + return ch; +} + +static char *chr(int ch) +{ + static char str[25]; + + switch(ch) { + case TIMEOUT: return("TIMEOUT"); + case ABORTED: return("ABORTED"); + case SUBPKTOVERFLOW: return "Subpacket Overflow"; + case CRCFAILED: return "CRC Failure"; + case INVALIDSUBPKT: return "Invalid Subpacket"; + case ZRQINIT: return("ZRQINIT"); + case ZRINIT: return("ZRINIT"); + case ZSINIT: return("ZSINIT"); + case ZACK: return("ZACK"); + case ZFILE: return("ZFILE"); + case ZSKIP: return("ZSKIP"); + case ZCRC: return("ZCRC"); + case ZNAK: return("ZNAK"); + case ZABORT: return("ZABORT"); + case ZFIN: return("ZFIN"); + case ZRPOS: return("ZRPOS"); + case ZDATA: return("ZDATA"); + case ZEOF: return("ZEOF"); + case ZFERR: return("ZFERR"); + case ZPAD: return("ZPAD"); + case ZCAN: return("ZCAN"); + case ZDLE: return("ZDLE"); + case ZDLEE: return("ZDLEE"); + case ZBIN: return("ZBIN"); + case ZHEX: return("ZHEX"); + case ZBIN32: return("ZBIN32"); + case ZRESC: return("ZRESC"); + case ZCRCE: return("ZCRCE"); + case ZCRCG: return("ZCRCG"); + case ZCRCQ: return("ZCRCQ"); + case ZCRCW: return("ZCRCW"); + + } + if(ch<0) + sprintf(str,"%d",ch); + else if(ch>=' ' && ch<='~') + sprintf(str,"'%c' (%02Xh)",(uchar)ch,(uchar)ch); + else + sprintf(str,"%u (%02Xh)",(uchar)ch,(uchar)ch); + return(str); +} + +static char* frame_desc(int frame) +{ + static char str[25]; + + if(frame==TIMEOUT) + return "TIMEOUT"; + + if(frame==INVHDR) + return "Invalid Header"; + + if(frame==ABORTED) + return "Aborted"; + + if(frame >= 0 && (frame&BADSUBPKT)) { + strcpy(str,"BAD "); + switch(frame&~BADSUBPKT) { + case ZRQINIT: strcat(str,"ZRQINIT"); break; + case ZRINIT: strcat(str,"ZRINIT"); break; + case ZSINIT: strcat(str,"ZSINIT"); break; + case ZACK: strcat(str,"ZACK"); break; + case ZFILE: strcat(str,"ZFILE"); break; + case ZSKIP: strcat(str,"ZSKIP"); break; + case ZNAK: strcat(str,"ZNAK"); break; + case ZABORT: strcat(str,"ZABORT"); break; + case ZFIN: strcat(str,"ZFIN"); break; + case ZRPOS: strcat(str,"ZRPOS"); break; + case ZDATA: strcat(str,"ZDATA"); break; + case ZEOF: strcat(str,"ZEOF"); break; + case ZFERR: strcat(str,"ZFERR"); break; + case ZCRC: strcat(str,"ZCRC"); break; + case ZCHALLENGE: strcat(str,"ZCHALLENGE"); break; + case ZCOMPL: strcat(str,"ZCOMPL"); break; + case ZCAN: strcat(str,"ZCAN"); break; + case ZFREECNT: strcat(str,"ZFREECNT"); break; + case ZCOMMAND: strcat(str,"ZCOMMAND"); break; + case ZSTDERR: strcat(str,"ZSTDERR"); break; + default: + sprintf(str,"Unknown (%08X)", frame); + break; + } + } else + sprintf(str,"%d",frame); + return(str); +} + +ulong ZModem::frame_pos(int type) +{ + switch(type) { + case ZRPOS: + case ZACK: + case ZEOF: + case ZDATA: + return(zm->rxd_header_pos); + } + + return 0; +} + +/* + * read bytes as long as rdchk indicates that + * more data is available. + */ + +void ZModem::recv_purge() +{ + while(recv_byte(zm->cbdata,0)>=0); +} + +/* + * Flush the output buffer + */ +void ZModem::flush() +{ + zserial.flush(); +} + +uint32_t ucrc32(unsigned char p, uint32_t crc) +{ + int c; + crc ^= p; + for(int c = 8; c-- > 0; ) + crc = (crc >> 1) ^ (0xEDB88320UL & ~((crc & 1l)-1l)); + return crc; +} + +uint32_t ucrc32(char *buf, int size) +{ + uint32_t crc = 0xffffffffL; + while (--size >= 0) { + crc = ucrc32(*buf++, crc); + } + return crc; +} + +uint16_t ucrc16(unsigned char p, uint16_t crc) +{ + int i; + crc ^= (uint16_t) p << 8; + for (i = 0; i < 8; i++) + if (crc & 0x8000) + crc = crc << 1 ^ 0x1021; + else + crc <<= 1; + return crc; +} + +uint16_t ucrc16(char *buf, int size) +{ + uint16_t crc = 0; + while (--size >= 0) { + crc = ucrc16(*buf++, crc); + } + return crc; +} + +uint32_t fcrc32(File* fp, unsigned long len) +{ + int ch; + uint32_t crc=0xffffffff; + unsigned long l; + + fp->seek(0); + for(l=0;(len==0 || lavailable()>0);l++) { + if((ch=fp->read())<0) + break; + crc=ucrc32(ch,crc); + } + return(~crc); +} + +/* + * transmit a character. + * this is the raw modem interface + */ +/* Returns 0 on success */ +int ZModem::send_raw(unsigned char ch) +{ + int result; + + if((result=send_byte(zm->cbdata,ch,zm->send_timeout))!=0) + { + lprintf(LOG_ERR,"send_raw SEND ERROR: %d",result); + } + + zm->last_sent = ch; + + return result; +} + +/* + * transmit a character ZDLE escaped + */ +int ZModem::send_esc(unsigned char c) +{ + int result; + + if((result=send_raw( ZDLE))!=0) + return(result); + /* + * exclusive or; not an or so ZDLE becomes ZDLEE + */ + return send_raw( (uchar)(c ^ 0x40)); +} + +/* + * transmit a character; ZDLE escaping if appropriate + */ +int ZModem::tx(unsigned char c) +{ + int result; + + switch (c) { + case ZMO_DLE: + case ZMO_DLE|0x80: /* even if high-bit set */ + case ZMO_XON: + case ZMO_XON|0x80: + case ZMO_XOFF: + case ZMO_XOFF|0x80: + case ZDLE: + return send_esc( c); + case 0x0d: + case 0x0d|0x80: + if(zm->escape_ctrl_chars && (zm->last_sent&0x7f) == '@') + return send_esc( c); + break; + case TELNET_IAC: + if(zm->escape_telnet_iac) { + if((result=send_raw( ZDLE))!=0) + return(result); + return send_raw( ZRUB1); + } + break; + default: + if(zm->escape_ctrl_chars && (c&0x60)==0) + return send_esc( c); + break; + } + /* + * anything that ends here is so normal we might as well transmit it. + */ + return send_raw( c); +} + +/**********************************************/ +/* Output single byte as two hex ASCII digits */ +/**********************************************/ +int ZModem::send_hex(uchar val) +{ + char* xdigit="0123456789abcdef"; + int result; + + lprintf(LOG_DEBUG,"send_hex: %02X ",val); + + if((result=send_raw( xdigit[val>>4]))!=0) + return result; + return send_raw( xdigit[val&0xf]); +} + +int ZModem::send_padded_zdle() +{ + int result; + delay(100); //dammit, this might fix something. + if((result=send_raw( ZPAD))!=0) + return result; + if((result=send_raw( ZPAD))!=0) + return result; + return send_raw( ZDLE); +} + +/* + * transmit a hex header. + * these routines use tx_raw because we're sure that all the + * characters are not to be escaped. + */ +int ZModem::send_hex_header(unsigned char * p) +{ + int i; + int result; + uchar type=*p; + unsigned short int crc; + + lprintf(LOG_DEBUG,"send_hex_header: %s", chr(type)); + + if((result=send_padded_zdle())!=0) + return result; + + if((result=send_raw( ZHEX))!=0) + return result; + + /* + * initialise the crc + */ + + crc = 0; + + /* + * transmit the header + */ + + for(i=0;i>8)))!=0) + return result; + if((result=send_hex( (uchar)(crc&0xff)))!=0) + return result; + + /* + * end of line sequence + */ + + if((result=send_raw( '\r'))!=0) + return result; + if((result=send_raw( '\n'))!=0) /* FDSZ sends 0x8a instead of 0x0a */ + return result; + + if(type!=ZACK && type!=ZFIN) + result=send_raw( ZMO_XON); //TODO:BZ:SUSPICIOUS + + flush(); + + return(result); +} + +/* + * Send ZMODEM binary header hdr + */ +int ZModem::send_bin32_header(unsigned char * p) +{ + int i; + int result; + uint32_t crc; + + lprintf(LOG_DEBUG,"send_bin32_header: %s", chr(*p)); + + if((result=send_padded_zdle())!=0) + return result; + + if((result=send_raw( ZBIN32))!=0) + return result; + + crc = 0xffffffffL; + + for(i=0;i> 8) & 0xff)))!=0) + return result; + if((result= tx( (uchar)((crc >> 16) & 0xff)))!=0) + return result; + return tx( (uchar)((crc >> 24) & 0xff)); +} + +int ZModem::send_bin16_header(unsigned char * p) +{ + int i; + int result; + unsigned int crc; + + lprintf(LOG_DEBUG,"send_bin16_header: %s", chr(*p)); + + if((result=send_padded_zdle())!=0) + return result; + + if((result=send_raw( ZBIN))!=0) + return result; + + crc = 0; + + for(i=0;i> 8)))!=0) + return result; + return tx( (uchar)(crc&0xff)); +} + + +/* + * transmit a header using either hex 16 bit crc or binary 32 bit crc + * depending on the receivers capabilities + * we dont bother with variable length headers. I dont really see their + * advantage and they would clutter the code unneccesarily + */ +int ZModem::send_bin_header(unsigned char * p) +{ + if(zm->can_fcs_32 && !zm->want_fcs_16) + return send_bin32_header( p); + return send_bin16_header( p); +} + +/* + * data subpacket transmission + */ +int ZModem::send_data32(uchar subpkt_type, unsigned char * p, size_t l) +{ + int result; + uint32_t crc; + + lprintf(LOG_DEBUG,"send_data32: %s (%u bytes)", chr(subpkt_type), l); + + crc = 0xffffffffl; + + while(l > 0) { + crc = ucrc32(*p,crc); + if((result=tx( *p++))!=0) + return result; + l--; + } + + crc = ucrc32(subpkt_type, crc); + + if((result=send_raw( ZDLE))!=0) + return result; + if((result=send_raw( subpkt_type))!=0) + return result; + + crc = ~crc; + + if((result= tx( (uchar) ((crc ) & 0xff)))!=0) + return result; + if((result= tx( (uchar) ((crc >> 8 ) & 0xff)))!=0) + return result; + if((result= tx( (uchar) ((crc >> 16) & 0xff)))!=0) + return result; + return tx( (uchar) ((crc >> 24) & 0xff)); +} + +int ZModem::send_data16(uchar subpkt_type,unsigned char * p, size_t l) +{ + int result; + unsigned short crc; + + lprintf(LOG_DEBUG,"send_data16: %s (%u bytes)", chr(subpkt_type), l); + + crc = 0; + + while(l > 0) { + crc = ucrc16(*p,crc); + if((result=tx( *p++))!=0) + return result; + l--; + } + + crc = ucrc16(subpkt_type,crc); + + if((result=send_raw( ZDLE))!=0) + return result; + if((result=send_raw( subpkt_type))!=0) + return result; + + if((result= tx( (uchar)(crc >> 8)))!=0) + return result; + return tx( (uchar)(crc&0xff)); +} + +/* + * send a data subpacket using crc 16 or crc 32 as desired by the receiver + */ +int ZModem::send_data_subpkt(uchar subpkt_type, unsigned char * p, size_t l) +{ + int result; + + if(subpkt_type == ZCRCW || subpkt_type == ZCRCE) /* subpacket indicating 'end-of-frame' */ + zm->frame_in_transit=ZFALSE; + else /* other subpacket (mid-frame) */ + zm->frame_in_transit=ZTRUE; + + if(!zm->want_fcs_16 && zm->can_fcs_32) { + if((result=send_data32( subpkt_type,p,l))!=0) + return result; + } + else { + if((result=send_data16( subpkt_type,p,l))!=0) + return result; + } + + if(subpkt_type == ZCRCW) + result=send_raw( 0x11/*XON*/); + + flush(); + + return result; +} + +int ZModem::send_data(uchar subpkt_type, unsigned char * p, size_t l) +{ + if(!zm->frame_in_transit) { /* Start of frame, include ZDATA header */ + lprintf(LOG_DEBUG,"send_data: start of frame, offset %u" ,zm->current_file_pos); + send_pos_header( ZDATA, (uint32_t)zm->current_file_pos, /* Hex? */ ZFALSE); + } + + return send_data_subpkt( subpkt_type, p, l); +} + +int ZModem::send_pos_header(int type, int32_t pos, BOOL hex) +{ + uchar header[5]; + + header[0] = type; + header[ZP0] = (uchar) (pos & 0xff); + header[ZP1] = (uchar)((pos >> 8) & 0xff); + header[ZP2] = (uchar)((pos >> 16) & 0xff); + header[ZP3] = (uchar)((pos >> 24) & 0xff); + + if(hex) + return send_hex_header( header); + else + return send_bin_header( header); +} + +int ZModem::send_ack(int32_t pos) +{ + return send_pos_header( ZACK, pos, /* Hex? */ ZTRUE); +} + +int ZModem::send_zfin() +{ + unsigned char zfin_header[] = { ZFIN, 0, 0, 0, 0 }; + + lprintf(LOG_NOTICE,"Finishing Session (Sending ZFIN)"); + return send_hex_header(zfin_header); +} + +int ZModem::send_zabort() +{ + lprintf(LOG_WARNING,"Aborting Transfer (Sending ZABORT)"); + return send_pos_header( ZABORT, 0, /* Hex? */ ZTRUE); +} + +int ZModem::send_znak() +{ + lprintf(LOG_INFO,"Sending ZNAK"); + return send_pos_header( ZNAK, 0, /* Hex? */ ZTRUE); +} + +int ZModem::send_zskip() +{ + lprintf(LOG_INFO,"Sending ZSKIP"); + return send_pos_header( ZSKIP, 0L, /* Hex? */ ZTRUE); +} + +int ZModem::send_zeof(uint32_t pos) +{ + lprintf(LOG_INFO,"Sending End-of-File (ZEOF) frame (pos=%lu)", pos); + return send_pos_header( ZEOF, pos, /* Hex? */ ZTRUE); +} + + +/* + * rx_raw ; receive a single byte from the line. + * reads as many are available and then processes them one at a time + * check the data stream for 5 consecutive 0x18 CAN characters; + * and if you see them abort. this saves a lot of clutter in + * the rest of the code; even though it is a very strange place + * for an exit. (but that was wat session abort was all about.) + */ + +int ZModem::recv_raw() +{ + int c; + unsigned attempt; + + for(attempt=0;attempt<=zm->recv_timeout;attempt++) { + if((c=recv_byte(zm->cbdata,1)) >= 0) /* second timeout */ + break; + if(is_cancelled()) + return(ZCAN); + if(!is_connected()) + return(ABORTED); + } + if(attempt>zm->recv_timeout) + return(TIMEOUT); + + if(c == ZMO_CAN) { + zm->n_cans++; + if(zm->n_cans == 5) { + zm->cancelled=ZTRUE; + lprintf(LOG_WARNING,"recv_raw: Cancelled remotely"); + /*return(TIMEOUT); removed June-12-2005 */ + } + } + else { + zm->n_cans = 0; + } + + return c; +} + +/* + * rx; receive a single byte undoing any escaping at the + * sending site. this bit looks like a mess. sorry for that + * but there seems to be no other way without incurring a lot + * of overhead. at least like this the path for a normal character + * is relatively short. + */ + +int ZModem::rx() +{ + int c; + + /* + * outer loop for ever so for sure something valid + * will come in; a timeout will occur or a session abort + * will be received. + */ + + while(is_connected() && !is_cancelled()) { + + do { + switch(c = recv_raw()) { + case ZDLE: + break; + case ZMO_XON: + case ZMO_XON|0x80: + case ZMO_XOFF: + case ZMO_XOFF|0x80: + lprintf(LOG_WARNING,"rx: dropping flow ctrl char: %s" ,chr(c)); + continue; + default: + /* + * if all control characters should be escaped and + * this one wasnt then its spurious and should be dropped. + */ + if(zm->escape_ctrl_chars && (c >= 0) && (c & 0x60) == 0) { + lprintf(LOG_WARNING,"rx: dropping unescaped ctrl char: %s" ,chr(c)); + continue; + } + /* + * normal character; return it. + */ + return c; + } + break; + } while(!is_cancelled()); + + /* + * ZDLE encoded sequence or session abort. + * (or something illegal; then back to the top) + */ + + while(!is_cancelled()) { + + switch(c=recv_raw()) { + case ZMO_XON: + case ZMO_XON|0x80: + case ZMO_XOFF: + case ZMO_XOFF|0x80: + case ZDLE: + lprintf(LOG_WARNING,"rx: dropping escaped flow ctrl char: %s" ,chr(c)); + continue; + /* + * these four are really nasty. + * for convenience we just change them into + * special characters by setting a bit outside the + * first 8. that way they can be recognized and still + * be processed as characters by the rest of the code. + */ + case ZCRCE: + case ZCRCG: + case ZCRCQ: + case ZCRCW: + lprintf(LOG_DEBUG,"rx: encoding data subpacket type: %s" ,chr(c)); + return (c | ZDLEESC); + case ZRUB0: + return 0x7f; + case ZRUB1: + return 0xff; + default: + if(c < 0) + return c; + + if(zm->escape_ctrl_chars && (c & 0x60) == 0) { + /* + * a not escaped control character; probably + * something from a network. just drop it. + */ + lprintf(LOG_WARNING,"rx: dropping unescaped ctrl char: %s" ,chr(c)); + continue; + } + /* + * legitimate escape sequence. + * rebuild the orignal and return it. + */ + if((c & 0x60) == 0x40) { + return c ^ 0x40; + } + lprintf(LOG_WARNING,"rx: illegal sequence: ZDLE %s" ,chr(c)); + break; + } + break; + } + } + + /* + * not reached (unless cancelled). + */ + + return ABORTED; +} + +/* + * receive a data subpacket as dictated by the last received header. + * return 2 with correct packet and end of frame + * return 1 with correct packet frame continues + * return 0 with incorrect frame. + * return TIMEOUT with a timeout + * if an acknowledgement is requested it is generated automatically + * here. + */ + +/* + * data subpacket reception + */ + +int ZModem::recv_data32(unsigned char * p, unsigned maxlen, unsigned* l) +{ + int c; + uint32_t rxd_crc; + uint32_t crc; + int subpkt_type; + + lprintf(LOG_DEBUG,"recv_data32"); + + crc = 0xffffffffl; + + do { + c = rx(); + + if(c < 0) + return c; + + if(c > 0xff) + break; + + if(*l >= maxlen) + return SUBPKTOVERFLOW; + crc = ucrc32(c,crc); + *p++ = c; + (*l)++; + } while(1); + + subpkt_type = c & 0xff; + + crc = ucrc32(subpkt_type, crc); + + crc = ~crc; + + rxd_crc = rx(); + rxd_crc |= rx() << 8; + rxd_crc |= rx() << 16; + rxd_crc |= rx() << 24; + + if(rxd_crc != crc) { + lprintf(LOG_WARNING,"CRC32 ERROR (%08lX, expected: %08lX) Bytes=%u, subpacket-type=%s" ,rxd_crc, crc, *l, chr(subpkt_type)); + return CRCFAILED; + } + lprintf(LOG_DEBUG,"GOOD CRC32: %08lX (Bytes=%u, subpacket-type=%s)" ,crc, *l, chr(subpkt_type)); + + zm->ack_file_pos += *l; + + return subpkt_type; +} + +int ZModem::recv_data16(register unsigned char* p, unsigned maxlen, unsigned* l) +{ + int c; + int subpkt_type; + unsigned short crc; + unsigned short rxd_crc; + + lprintf(LOG_DEBUG,"recv_data16"); + + crc = 0; + + do { + c = rx(); + + if(c < 0) + return c; + + if(c > 0xff) + break; + + if(*l >= maxlen) + return SUBPKTOVERFLOW; + crc = ucrc16(c,crc); + *p++ = c; + (*l)++; + } while(1); + + subpkt_type = c & 0xff; + + crc = ucrc16(subpkt_type,crc); + + rxd_crc = rx() << 8; + rxd_crc |= rx(); + + if(rxd_crc != crc) { + lprintf(LOG_WARNING,"CRC16 ERROR (%04hX, expected: %04hX) Bytes=%d" ,rxd_crc, crc, *l); + return CRCFAILED; + } + lprintf(LOG_DEBUG,"GOOD CRC16: %04hX (Bytes=%d)", crc, *l); + + zm->ack_file_pos += *l; + + return subpkt_type; +} + +int ZModem::recv_data(unsigned char* p, size_t maxlen, unsigned* l, BOOL ack) +{ + int subpkt_type; + unsigned n=0; + + if(l==NULL) + l=&n; + + lprintf(LOG_DEBUG,"recv_data (%u-bit)", zm->receive_32bit_data ? 32:16); + + /* + * receive the right type of frame + */ + + *l = 0; + + if(zm->receive_32bit_data) { + subpkt_type = recv_data32( p, maxlen, l); + } + else { + subpkt_type = recv_data16( p, maxlen, l); + } + + if(subpkt_type <= 0) /* e.g. TIMEOUT, SUBPKTOVERFLOW, CRCFAILED */ + return(subpkt_type); + + lprintf(LOG_DEBUG,"recv_data received subpacket-type: %s" ,chr(subpkt_type)); + + switch(subpkt_type) { + /* + * frame continues non-stop + */ + case ZCRCG: + return FRAMEOK; + /* + * frame ends + */ + case ZCRCE: + return ENDOFFRAME; + /* + * frame continues; ZACK expected + */ + case ZCRCQ: + if(ack) + send_ack( zm->ack_file_pos); + return FRAMEOK; + /* + * frame ends; ZACK expected + */ + case ZCRCW: + if(ack) + send_ack( zm->ack_file_pos); + return ENDOFFRAME; + } + + lprintf(LOG_WARNING,"Received invalid subpacket-type: %s", chr(subpkt_type)); + + return INVALIDSUBPKT; +} + +BOOL ZModem::recv_subpacket(BOOL ack) +{ + int type; + + type=recv_data(zm->rx_data_subpacket,sizeof(zm->rx_data_subpacket),NULL,ack); + if(type!=FRAMEOK && type!=ENDOFFRAME) { + send_znak(); + return(ZFALSE); + } + + return(ZTRUE); +} + +int ZModem::recv_nibble() +{ + int c; + + c = rx(); + + if(c < 0) + return c; + + if(c > '9') { + if(c < 'a' || c > 'f') { + /* + * illegal hex; different than expected. + * we might as well time out. + */ + return -1; + } + + c -= 'a' - 10; + } + else { + if(c < '0') { + /* + * illegal hex; different than expected. + * we might as well time out. + */ + return -1; + } + c -= '0'; + } + + return c; +} + +int ZModem::recv_hex() +{ + int n1; + int n0; + int ret; + + n1 = recv_nibble(); + + if(n1 < 0) + return n1; + + n0 = recv_nibble(); + + if(n0 < 0) + return n0; + + ret = (n1 << 4) | n0; + + lprintf(LOG_DEBUG,"recv_hex returning: 0x%02X", ret); + + return ret; +} + +/* + * receive routines for each of the six different styles of header. + * each of these leaves zm->rxd_header_len set to 0 if the end result is + * not a valid header. + */ +BOOL ZModem::recv_bin16_header() +{ + int c; + int n; + unsigned short int crc; + unsigned short int rxd_crc; + + lprintf(LOG_DEBUG,"recv_bin16_header"); + + crc = 0; + + for(n=0;nrxd_header[n] = c; + } + + rxd_crc = rx() << 8; + rxd_crc |= rx(); + + if(rxd_crc != crc) { + lprintf(LOG_WARNING,"CRC16 ERROR: 0x%hX, expected: 0x%hX", rxd_crc, crc); + return(ZFALSE); + } + lprintf(LOG_DEBUG,"GOOD CRC16: %04hX", crc); + + zm->rxd_header_len = 5; + + return(ZTRUE); +} + +BOOL ZModem::recv_hex_header() +{ + int c; + int i; + unsigned short int crc = 0; + unsigned short int rxd_crc; + + lprintf(LOG_DEBUG,"recv_hex_header"); + + for(i=0;irxd_header[i] = c; + } + + /* + * receive the crc + */ + + c = recv_hex(); + + if(c < 0) + return ZFALSE; + + rxd_crc = c << 8; + + c = recv_hex(); + + if(c < 0 ) + return ZFALSE; + + rxd_crc |= c; + + if(rxd_crc == crc) { + lprintf(LOG_DEBUG,"GOOD CRC16: %04hX", crc); + zm->rxd_header_len = 5; + } + else { + lprintf(LOG_WARNING,"CRC16 ERROR: 0x%hX, expected: 0x%hX", rxd_crc, crc); + return ZFALSE; + } + + /* + * drop the end of line sequence after a hex header + */ + c = rx(); + if(c == '\r') { + /* + * both are expected with 0x0d + */ + rx(); /* drop 0x0a */ + } + + return ZTRUE; +} + +BOOL ZModem::recv_bin32_header() +{ + int c; + int n; + uint32_t crc; + uint32_t rxd_crc; + + lprintf(LOG_DEBUG,"recv_bin32_header"); + + crc = 0xffffffffL; + + for(n=0;nrxd_header[n] = c; + } + + crc = ~crc; + + rxd_crc = rx(); + rxd_crc |= rx() << 8; + rxd_crc |= rx() << 16; + rxd_crc |= rx() << 24; + + if(rxd_crc != crc) { + lprintf(LOG_WARNING,"CRC32 ERROR (%08lX, expected: %08lX)" ,rxd_crc, crc); + return(ZFALSE); + } + lprintf(LOG_DEBUG,"GOOD CRC32: %08lX", crc); + + zm->rxd_header_len = 5; + return(ZTRUE); +} + +/* + * receive any style header + * if the errors flag is set than whenever an invalid header packet is + * received INVHDR will be returned. otherwise we wait for a good header + * also; a flag (receive_32bit_data) will be set to indicate whether data + * packets following this header will have 16 or 32 bit data attached. + * variable headers are not implemented. + */ +int ZModem::recv_header_raw(int errors) +{ + int c; + int frame_type; + + lprintf(LOG_DEBUG,"recv_header_raw"); + + zm->rxd_header_len = 0; + + do { + do { + if((c = recv_raw()) < 0) + return(c); + if(is_cancelled()) + return(ZCAN); + } while(c != ZPAD); + + if((c = recv_raw()) < 0) + return(c); + + if(c == ZPAD) { + if((c = recv_raw()) < 0) + return(c); + } + + /* + * spurious ZPAD check + */ + + if(c != ZDLE) { + lprintf(LOG_WARNING,"recv_header_raw: Expected ZDLE, received: %s" ,chr(c)); + continue; + } + + /* + * now read the header style + */ + c = rx(); + switch (c) { + case ZBIN: + if(!recv_bin16_header()) + return INVHDR; + zm->receive_32bit_data = ZFALSE; + break; + case ZHEX: + if(!recv_hex_header()) + return INVHDR; + zm->receive_32bit_data = ZFALSE; + break; + case ZBIN32: + if(!recv_bin32_header()) + return INVHDR; + zm->receive_32bit_data = ZTRUE; + break; + default: + if(c < 0) { + lprintf(LOG_WARNING,"recv_header_raw: %s", chr(c)); + return c; + } + /* + * unrecognized header style + */ + lprintf(LOG_ERR,"recv_header_raw: UNRECOGNIZED header style: %s" ,chr(c)); + if(errors) { + return INVHDR; + } + + continue; + } + if(errors && zm->rxd_header_len == 0) { + return INVHDR; + } + + } while(zm->rxd_header_len == 0 && !is_cancelled()); + + if(is_cancelled()) + return(ZCAN); + + /* + * this appears to have been a valid header. + * return its type. + */ + + frame_type = zm->rxd_header[0]; + + zm->rxd_header_pos = zm->rxd_header[ZP0] | (zm->rxd_header[ZP1] << 8) | + (zm->rxd_header[ZP2] << 16) | (zm->rxd_header[ZP3] << 24); + + switch(frame_type) { + case ZCRC: + zm->crc_request = zm->rxd_header_pos; + break; + case ZDATA: + zm->ack_file_pos = zm->rxd_header_pos; + break; + case ZFILE: + zm->ack_file_pos = 0l; + if(!recv_subpacket(/* ack? */ZFALSE)) + frame_type |= BADSUBPKT; + break; + case ZSINIT: + case ZCOMMAND: + if(!recv_subpacket(/* ack? */ZTRUE)) + frame_type |= BADSUBPKT; + break; + case ZFREECNT: + send_pos_header( ZACK, 999999999L, /* Hex? */ ZTRUE); + break; + } + + lprintf(LOG_DEBUG,"recv_header_raw received header type: %s",frame_desc(frame_type)); + + return frame_type; +} + +int ZModem::recv_header() +{ + int ret; + + switch(ret = recv_header_raw( ZFALSE)) { + case TIMEOUT: + lprintf(LOG_WARNING,"recv_header TIMEOUT"); + break; + case INVHDR: + lprintf(LOG_WARNING,"recv_header detected an invalid header"); + break; + default: + lprintf(LOG_DEBUG,"recv_header returning: %s (pos=%lu)" ,frame_desc(ret), frame_pos( ret)); + + if(ret==ZCAN) + zm->cancelled=ZTRUE; + else if(ret==ZRINIT) + parse_zrinit(); + break; + } + + return ret; +} + +int ZModem::recv_header_and_check() +{ + int type=ABORTED; + + while(is_connected() && !is_cancelled()) { + type = recv_header_raw(ZTRUE); + + if(type == TIMEOUT) + break; + + if(type != INVHDR && (type&BADSUBPKT) == 0) + break; + + send_znak(); + } + + lprintf(LOG_DEBUG,"recv_header_and_check returning: %s (pos=%lu)",frame_desc(type), frame_pos( type)); + + if(type==ZCAN) + zm->cancelled=ZTRUE; + + return type; +} + +BOOL ZModem::request_crc(int32_t length) +{ + recv_purge(); + send_pos_header(ZCRC,length,ZTRUE); + return ZTRUE; +} + +BOOL ZModem::recv_crc(uint32_t* crc) +{ + int type; + + if(!data_waiting(zm->crc_timeout)) { + lprintf(LOG_ERR,"Timeout waiting for response (%u seconds)", zm->crc_timeout); + return(ZFALSE); + } + if((type=recv_header())!=ZCRC) { + lprintf(LOG_ERR,"Received %s instead of ZCRC", frame_desc(type)); + return(ZFALSE); + } + if(crc!=NULL) + *crc = zm->crc_request; + return ZTRUE; +} + +BOOL ZModem::get_crc(int32_t length, uint32_t* crc) +{ + if(request_crc( length)) + return recv_crc( crc); + return ZFALSE; +} + +void ZModem::parse_zrinit() +{ + zm->can_full_duplex = INT_TO_BOOL(zm->rxd_header[ZF0] & ZF0_CANFDX); + zm->can_overlap_io = INT_TO_BOOL(zm->rxd_header[ZF0] & ZF0_CANOVIO); + zm->can_break = INT_TO_BOOL(zm->rxd_header[ZF0] & ZF0_CANBRK); + zm->can_fcs_32 = INT_TO_BOOL(zm->rxd_header[ZF0] & ZF0_CANFC32); + zm->escape_ctrl_chars = INT_TO_BOOL(zm->rxd_header[ZF0] & ZF0_ESCCTL); + zm->escape_8th_bit = INT_TO_BOOL(zm->rxd_header[ZF0] & ZF0_ESC8); + + /* + lprintf(LOG_INFO,"Receiver requested mode (0x%02X):\r\n" + "%s-duplex, %s overlap I/O, CRC-%u, Escape: %s" + ,zm->rxd_header[ZF0] + ,zm->can_full_duplex ? "Full" : "Half" + ,zm->can_overlap_io ? "Can" : "Cannot" + ,zm->can_fcs_32 ? 32 : 16 + ,zm->escape_ctrl_chars ? "ALL" : "Normal" + ); + */ + + if((zm->recv_bufsize = (zm->rxd_header[ZP0] | zm->rxd_header[ZP1]<<8)) != 0) + { + lprintf(LOG_INFO,"Receiver specified buffer size of: %u", zm->recv_bufsize); + } +} + +int ZModem::get_zrinit() +{ + unsigned char zrqinit_header[] = { ZRQINIT, /* ZF3: */0, 0, 0, /* ZF0: */0 }; + /* Note: sz/dsz/fdsz sends 0x80 in ZF3 because it supports var-length headers. */ + /* We do not, so we send 0x00, resulting in a CRC-16 value of 0x0000 as well. */ + + send_raw('r'); + send_raw('z'); + send_raw('\r'); + send_hex_header(zrqinit_header); + + if(!data_waiting(zm->init_timeout)) + return(TIMEOUT); + return recv_header(); +} + +int ZModem::send_zrinit() +{ + unsigned char zrinit_header[] = { ZRINIT, 0, 0, 0, 0 }; + + zrinit_header[ZF0] = ZF0_CANFDX; + + if(!zm->no_streaming) + zrinit_header[ZF0] |= ZF0_CANOVIO; + + if(zm->can_break) + zrinit_header[ZF0] |= ZF0_CANBRK; + + if(!zm->want_fcs_16) + zrinit_header[ZF0] |= ZF0_CANFC32; + + if(zm->escape_ctrl_chars) + zrinit_header[ZF0] |= ZF0_ESCCTL; + + if(zm->escape_8th_bit) + zrinit_header[ZF0] |= ZF0_ESC8; + + if(zm->no_streaming && zm->recv_bufsize==0) + zm->recv_bufsize = sizeof(zm->rx_data_subpacket); + + zrinit_header[ZP0] = zm->recv_bufsize & 0xff; + zrinit_header[ZP1] = zm->recv_bufsize >> 8; + + return send_hex_header( zrinit_header); +} + +/* Returns ZFIN on success */ +int ZModem::get_zfin() +{ + int result; + int type=ZCAN; + unsigned attempts; + + for(attempts=0; attemptsmax_errors && is_connected() && !is_cancelled(); attempts++) { + if(attempts&1) /* Alternate between ZABORT and ZFIN */ + result = send_zabort(); + else + result = send_zfin(); + if(result != 0) + return result; + if((type = recv_header()) == ZFIN) + break; + } + + /* + * these Os are formally required; but they don't do a thing + * unfortunately many programs require them to exit + * (both programs already sent a ZFIN so why bother ?) + */ + + if(type == ZFIN) { + send_raw('O'); + send_raw('O'); + } + + return type; +} + +BOOL ZModem::handle_zrpos(uint64_t* pos) +{ + if(zm->rxd_header_pos <= zm->current_file_size) { + if(*pos != zm->rxd_header_pos) { + *pos = zm->rxd_header_pos; + lprintf(LOG_INFO,"Resuming transfer from offset: %llu", *pos); + } + return ZTRUE; + } + lprintf(LOG_WARNING,"Invalid ZRPOS offset: %lu", zm->rxd_header_pos); + return ZFALSE; +} + +BOOL ZModem::handle_zack() +{ + if(zm->rxd_header_pos == zm->current_file_pos) + return ZTRUE; + lprintf(LOG_WARNING,"ZACK for incorrect offset (%lu vs %lu)" ,zm->rxd_header_pos, (ulong)zm->current_file_pos); + return ZFALSE; +} + +/* + * send from the current position in the file + * all the way to end of file or until something goes wrong. + * (ZNAK or ZRPOS received) + * returns ZRINIT on success. + */ +int ZModem::send_from(File* fp, uint64_t pos, uint64_t* sent) +{ + size_t n; + uchar type; + unsigned buf_sent=0; + unsigned subpkts_sent=0; + + if(sent!=NULL) + *sent=0; + + if(!fp->seek(pos)) { + lprintf(LOG_ERR,"ERROR %d seeking to file offset %llu" ,errno, pos); + send_pos_header( ZFERR, (uint32_t)pos, /* Hex? */ ZTRUE); + return ZFERR; + } + zm->current_file_pos=pos; + + + /* + * send the data in the file + */ + + while(is_connected()) { + + /* + * read a block from the file + */ + + n = fp->read(zm->tx_data_subpacket,zm->block_size); + + progress(zm->cbdata, fp->position()); + + type = ZCRCW; + + /** ZMODEM.DOC: + ZCRCW data subpackets expect a response before the next frame is sent. + If the receiver does not indicate overlapped I/O capability with the + CANOVIO bit, or sets a buffer size, the sender uses the ZCRCW to allow + the receiver to write its buffer before sending more data. + ***/ + /* Note: we always use ZCRCW for the first frame */ + if(subpkts_sent || n < zm->block_size) { + /* ZMODEM.DOC: + In the absence of fatal error, the sender eventually encounters end of + file. If the end of file is encountered within a frame, the frame is + closed with a ZCRCE data subpacket which does not elicit a response + except in case of error. + */ + if(n < zm->block_size) + type = ZCRCE; + else { + if(zm->can_overlap_io && !zm->no_streaming && (zm->recv_bufsize==0 || buf_sent+n < zm->recv_bufsize)) + type = ZCRCG; + else /* Send a ZCRCW frame */ + buf_sent = 0; + } + } + + /* Note: No support for sending ZCRCQ data sub-packets here */ + + if(send_data( type, zm->tx_data_subpacket, n)!=0) + return(TIMEOUT); + + zm->current_file_pos += n; + if(zm->current_file_pos > zm->current_file_size) + zm->current_file_size = zm->current_file_pos; + subpkts_sent++; + + if(type == ZCRCW || type == ZCRCE) { + lprintf(LOG_DEBUG,"Sent end-of-frame (%s sub-packet)", chr(type)); + if(type==ZCRCW) { /* ZACK expected */ + lprintf(LOG_DEBUG,"Waiting for ZACK"); + while(is_connected()) { + int ack; + if((ack = recv_header()) != ZACK) + return(ack); + + if(is_cancelled()) + return(ZCAN); + + if(handle_zack()) + break; + } + } + } + + if(sent!=NULL) + *sent+=n; + + buf_sent+=n; + + if(n < zm->block_size) { + lprintf(LOG_DEBUG,"send_from: end of file (or read error) reached at offset: %lld", zm->current_file_pos); + send_zeof( (uint32_t)zm->current_file_pos); + return recv_header(); /* If this is ZRINIT, Success */ + } + + /* + * characters from the other side + * check out that header + */ + + while(data_waiting( zm->consecutive_errors ? 1:0) + && !is_cancelled() && is_connected()) { + int rx_type; + int c; + lprintf(LOG_DEBUG,"Back-channel traffic detected:"); + if((c = recv_raw()) < 0) + return(c); + if(c == ZPAD) { + /* ZMODEM.DOC: + FULL STREAMING WITH SAMPLING + If one of these characters (0x18 CAN or ZPAD) is seen, an + empty ZCRCE data subpacket is sent. + */ + send_data( ZCRCE, NULL, 0); + rx_type = recv_header(); + lprintf(LOG_DEBUG,"Received back-channel data: %s", chr(rx_type)); + if(rx_type >= 0) { + return rx_type; + } + } + else + { + lprintf(LOG_DEBUG,"Received: %s",chr(c)); + } + } + if(is_cancelled()) + return(ZCAN); + + zm->consecutive_errors = 0; + + if(zm->block_size < zm->max_block_size) { + zm->block_size*=2; + if(zm->block_size > zm->max_block_size) + zm->block_size = zm->max_block_size; + } + } + + lprintf(LOG_DEBUG,"send_from: returning unexpectedly!"); + + /* + * end of file reached. + * should receive something... so fake ZACK + */ + + return ZACK; +} + +/* + * send a file; returns true when session is successful. (or file is skipped) + */ + +BOOL ZModem::send_file(char* fname, File* fp, BOOL request_init, time_t* start, uint64_t* sent) +{ + uint64_t pos=0; + uint64_t sent_bytes; + unsigned char * p; + uchar zfile_frame[] = { ZFILE, 0, 0, 0, 0 }; + int type; + int i; + unsigned attempts; + + if(zm->block_size == 0) + zm->block_size = ZBLOCKLEN; + + if(zm->block_size < 128) + zm->block_size = 128; + + if(zm->block_size > sizeof(zm->tx_data_subpacket)) + zm->block_size = sizeof(zm->tx_data_subpacket); + + if(zm->max_block_size < zm->block_size) + zm->max_block_size = zm->block_size; + + if(zm->max_block_size > sizeof(zm->rx_data_subpacket)) + zm->max_block_size = sizeof(zm->rx_data_subpacket); + + if(sent!=NULL) + *sent=0; + + if(start!=NULL) + *start=time(NULL); + + zm->file_skipped=ZFALSE; + + if(zm->no_streaming) + { + lprintf(LOG_WARNING,"Streaming disabled"); + } + + if(request_init) { + for(zm->errors=0; zm->errors<=zm->max_errors && !is_cancelled() && is_connected(); zm->errors++) { + if(zm->errors) + { + lprintf(LOG_NOTICE,"Sending ZRQINIT (%u of %u)" ,zm->errors+1,zm->max_errors+1); + } + else + { + lprintf(LOG_INFO,"Sending ZRQINIT"); + } + i = get_zrinit(); + if(i == ZRINIT) + break; + lprintf(LOG_WARNING,"send_file: received %s instead of ZRINIT" ,frame_desc(i)); + } + if(zm->errors>=zm->max_errors || is_cancelled() || !is_connected()) + return(ZFALSE); + } + + zm->current_file_size = fp->size(); + SAFECOPY(zm->current_file_name, getfname(fname)); + + /* + * the file exists. now build the ZFILE frame + */ + + /* + * set conversion option + * (not used; always binary) + */ + + zfile_frame[ZF0] = ZF0_ZCBIN; + + /* + * management option + */ + + if(zm->management_protect) { + zfile_frame[ZF1] = ZF1_ZMPROT; + lprintf(LOG_DEBUG,"send_file: protecting destination"); + } + else if(zm->management_clobber) { + zfile_frame[ZF1] = ZF1_ZMCLOB; + lprintf(LOG_DEBUG,"send_file: overwriting destination"); + } + else if(zm->management_newer) { + zfile_frame[ZF1] = ZF1_ZMNEW; + lprintf(LOG_DEBUG,"send_file: overwriting destination if newer"); + } + else + zfile_frame[ZF1] = ZF1_ZMCRC; + + /* + * transport options + * (just plain normal transfer) + */ + + zfile_frame[ZF2] = ZF2_ZTNOR; + + /* + * extended options + */ + + zfile_frame[ZF3] = 0; + + /* + * now build the data subpacket with the file name and lots of other + * useful information. + */ + + /* + * first enter the name and a 0 + */ + + p = zm->tx_data_subpacket; + + strncpy((char *)zm->tx_data_subpacket,getfname(fname),sizeof(zm->tx_data_subpacket)-1); + zm->tx_data_subpacket[sizeof(zm->tx_data_subpacket)-1]=0; + + p += strlen((char*)p) + 1; + + sprintf((char*)p,"%lld"" %llo 0 0 %u %lld"" 0" + ,zm->current_file_size /* use for estimating only, could be zero! */ + ,0 + ,zm->files_remaining + ,zm->bytes_remaining + ); + + p += strlen((char*)p) + 1; + + for(attempts=0;;attempts++) { + + if(attempts > zm->max_errors) + return(ZFALSE); + + /* + * send the header and the data + */ + + lprintf(LOG_DEBUG,"Sending ZFILE frame: '%s'" ,zm->tx_data_subpacket+strlen((char*)zm->tx_data_subpacket)+1); + + if((i=send_bin_header(zfile_frame))!=0) { + lprintf(LOG_DEBUG,"send_bin_header returned %d",i); + continue; + } + if((i=send_data_subpkt(ZCRCW,zm->tx_data_subpacket,p - zm->tx_data_subpacket))!=0) { + lprintf(LOG_DEBUG,"send_data_subpkt returned %d",i); + continue; + } + /* + * wait for anything but an ZACK packet + */ + + do { + type = recv_header(); + if(is_cancelled()) + return(ZFALSE); + } while(type == ZACK && is_connected()); + + if(!is_connected()) + return(ZFALSE); + +#if 0 + lprintf(LOG_INFO,"type : %d",type); +#endif + + if(type == ZSKIP) { + zm->file_skipped=ZTRUE; + lprintf(LOG_WARNING,"File skipped by receiver"); + return(ZTRUE); + } + + if(type == ZCRC) { + if(zm->crc_request==0) + { + lprintf(LOG_NOTICE,"Receiver requested CRC of entire file"); + } + else + { + lprintf(LOG_NOTICE,"Receiver requested CRC of first %lu bytes" ,zm->crc_request); + } + send_pos_header(ZCRC,fcrc32(fp,zm->crc_request),ZTRUE); + type = recv_header(); + } + + if(type == ZRPOS) + break; + } + + if(!handle_zrpos( &pos)) + return(ZFALSE); + + zm->transfer_start_pos = pos; + zm->transfer_start_time = time(NULL); + + if(start!=NULL) + *start=zm->transfer_start_time; + + fp->seek(0); + zm->errors = 0; + zm->consecutive_errors = 0; + + lprintf(LOG_DEBUG,"Sending %s from offset %llu", fname, pos); + do { + /* + * and start sending + */ + + type = send_from( fp, pos, &sent_bytes); + + if(!is_connected()) + return(ZFALSE); + + if(type == ZFERR || type == ZABORT || is_cancelled()) + break; + + if(type == ZSKIP) { + zm->file_skipped=ZTRUE; + lprintf(LOG_WARNING,"File skipped by receiver at offset: %llu", pos + sent_bytes); + /* ZOC sends a ZRINIT after mid-file ZSKIP, so consume the ZRINIT here */ + recv_header(); + return(ZTRUE); + } + + if(sent != NULL) + *sent += sent_bytes; + + if(type == ZRINIT) + return(ZTRUE); /* Success */ + + if(type==ZACK && handle_zack()) { + pos += sent_bytes; + continue; + } + + /* Error of some kind */ + + lprintf(LOG_ERR,"Received %s at offset: %lld", chr(type), zm->current_file_pos); + + if(zm->block_size == zm->max_block_size && zm->max_block_size > ZBLOCKLEN) + zm->max_block_size /= 2; + + if(zm->block_size > 128) + zm->block_size /= 2; + + zm->errors++; + if(++zm->consecutive_errors > zm->max_errors) + break; /* failure */ + + if(type==ZRPOS) { + if(!handle_zrpos( &pos)) + break; + } + } while(ZTRUE); + + lprintf(LOG_WARNING,"Transfer failed on receipt of: %s", chr(type)); + + return(ZFALSE); +} + +int ZModem::recv_files(const char* download_dir, uint64_t* bytes_received) +{ + char fpath[MAX_PATH+1]; + File* fp; + int64_t l; + BOOL skip; + BOOL loop; + uint64_t b; + uint32_t crc; + uint32_t rcrc; + int64_t bytes; + int64_t kbytes; + int64_t start_bytes; + unsigned files_received=0; + time_t t; + unsigned cps; + unsigned timeout; + unsigned errors; + + if(bytes_received!=NULL) + *bytes_received=0; + zm->current_file_num=1; + while(recv_init()==ZFILE) { + bytes=zm->current_file_size; + kbytes=bytes/1024; + if(kbytes<1) + kbytes=0; + lprintf(LOG_INFO,"Downloading %s (%lld"" KBytes) via Zmodem", zm->current_file_name, kbytes); + + do { /* try */ + skip=ZTRUE; + loop=ZFALSE; + + sprintf(fpath,"%s/%s",download_dir,zm->current_file_name); + lprintf(LOG_DEBUG,"fpath=%s",fpath); + File fileF=zfileSystem->open(fpath); + if(fileF) { + l=fileF.size(); + lprintf(LOG_WARNING,"%s already exists (%lld"" bytes)",fpath,l); + if(l>=(int32_t)bytes) { + lprintf(LOG_WARNING,"Local file size >= remote file size (%lld"")" ,bytes); + break; + /* + if(zm->duplicate_filename==NULL) + break; + else { + if(l > (int32_t)bytes) { + if(zm->duplicate_filename(zm->cbdata, zm)) { + loop=ZTRUE; + continue; + } + break; + } + } + */ + } + fp=&fileF; + if(!(*fp)) { + lprintf(LOG_ERR,"Error %d opening %s", errno, fpath); + break; + } + //setvbuf(fp,NULL,_IOFBF,0x10000); + + lprintf(LOG_NOTICE,"Requesting CRC of remote file: %s", zm->current_file_name); + if(!request_crc( (uint32_t)l)) { + fp->close(); + lprintf(LOG_ERR,"Failed to request CRC of remote file"); + break; + } + lprintf(LOG_NOTICE,"Calculating CRC of: %s", fpath); + crc=fcrc32(fp,(uint32_t)l); /* Warning: 4GB limit! */ + fp->close(); + lprintf(LOG_INFO,"CRC of %s (%lu bytes): %08lX" ,getfname(fpath), (ulong)l, crc); + lprintf(LOG_NOTICE,"Waiting for CRC of remote file: %s", zm->current_file_name); + if(!recv_crc(&rcrc)) { + lprintf(LOG_ERR,"Failed to get CRC of remote file"); + break; + } + if(crc!=rcrc) { + lprintf(LOG_WARNING,"Remote file has different CRC value: %08lX", rcrc); + /* + if(zm->duplicate_filename) { + if(zm->duplicate_filename(zm->cbdata, zm)) { + loop=ZTRUE; + continue; + } + } + */ + break; + } + if(l == (int32_t)bytes) { + lprintf(LOG_INFO,"CRC, length, and filename match."); + break; + } + lprintf(LOG_INFO,"Resuming download of %s",fpath); + } + + if(fileF) + fileF.close(); + fileF=zfileSystem->open(fpath, FILE_APPEND); + fp = &fileF; + start_bytes=fp->size(); + + skip=ZFALSE; + errors=recv_file_data(fp,start_bytes); + + l=fp->size(); + fp->close(); + if(errors && l==0) { /* aborted/failed download */ + if(zfileSystem->remove(fpath)) /* don't save 0-byte file */ + { + lprintf(LOG_ERR,"Error %d removing %s",errno,fpath); + } + else + { + lprintf(LOG_INFO,"Deleted 0-byte file %s",fpath); + } + } + else { + if(l!=bytes) { + lprintf(LOG_WARNING,"Incomplete download (%lld"" bytes received, expected %lld"")",l,bytes); + } else { + if((t=time(NULL)-zm->transfer_start_time)<=0) + t=1; + b=l-start_bytes; + if((cps=(unsigned)(b/t))==0) + cps=1; + lprintf(LOG_INFO,"Received %llu"" bytes successfully (%u CPS)",b,cps); + files_received++; + if(bytes_received!=NULL) + *bytes_received+=b; + } + //if(zm->current_file_time) //BZ: unsupported... + // setfdate(fpath,zm->current_file_time); + } + + } while(loop); + /* finally */ + + if(skip) { + lprintf(LOG_WARNING,"Skipping file"); + send_zskip(); + } + zm->current_file_num++; + } + if(zm->local_abort) + send_zabort(); + + /* wait for "over-and-out" */ + timeout=zm->recv_timeout; + zm->recv_timeout=2; + if(rx()=='O') + rx(); + zm->recv_timeout=timeout; + + return(files_received); +} + +int ZModem::recv_init() +{ + int type=0x18/*CAN*/; + unsigned errors; + + lprintf(LOG_DEBUG,"recv_init"); + +#if 0 + while(is_connected() && !is_cancelled() && (ch=recv_byte(zm->cbdata,0))!=NOINP) + { + lprintf(LOG_DEBUG,"Throwing out received: %s",chr((uchar)ch)); + } +#endif + + for(errors=0; errors<=zm->max_errors && !is_cancelled() && is_connected(); errors++) { + if(errors) + { + lprintf(LOG_NOTICE,"Sending ZRINIT (%u of %u)",errors+1, zm->max_errors+1); + } + else + { + lprintf(LOG_INFO,"Sending ZRINIT"); + } + send_zrinit(); + + type = recv_header(); + + if(zm->local_abort) + break; + + if(type==TIMEOUT) + continue; + + lprintf(LOG_DEBUG,"recv_init: Received %s",chr(type)); + + if(type==ZFILE) { + parse_zfile_subpacket(); + return(type); + } + + if(type==ZFIN) { + send_zfin(); /* 0x06 ACK */ + return(type); + } + + lprintf(LOG_WARNING,"recv_init: Received %s instead of ZFILE or ZFIN" ,frame_desc(type)); + lprintf(LOG_DEBUG,"ZF0=%02X ZF1=%02X ZF2=%02X ZF3=%02X" ,zm->rxd_header[ZF0],zm->rxd_header[ZF1],zm->rxd_header[ZF2],zm->rxd_header[ZF3]); + } + + return(type); +} + +void ZModem::parse_zfile_subpacket() +{ + int i; + int mode=0; + long serial=-1L; + ulong tmptime; + + SAFECOPY(zm->current_file_name,getfname((char*)zm->rx_data_subpacket)); + + zm->current_file_size = 0; + zm->current_file_time = 0; + zm->files_remaining = 0; + zm->bytes_remaining = 0; + + i=sscanf((char*)zm->rx_data_subpacket+strlen((char*)zm->rx_data_subpacket)+1,"%lld %lo %o %lo %u %lld" + ,&zm->current_file_size /* file size (decimal) */ + ,&tmptime /* file time (octal unix format) */ + ,&mode /* file mode */ + ,&serial /* program serial number */ + ,&zm->files_remaining /* remaining files to be sent */ + ,&zm->bytes_remaining /* remaining bytes to be sent */ + ); + zm->current_file_time=tmptime; + + lprintf(LOG_DEBUG,"Zmodem file (ZFILE) data (%u fields): %s",i, zm->rx_data_subpacket+strlen((char*)zm->rx_data_subpacket)+1); + + if(!zm->files_remaining) + zm->files_remaining = 1; + if(!zm->bytes_remaining) + zm->bytes_remaining = zm->current_file_size; + + if(!zm->total_files) + zm->total_files = zm->files_remaining; + if(!zm->total_bytes) + zm->total_bytes = zm->bytes_remaining; +} + +/* + * receive file data until the end of the file or until something goes wrong. + * the name is only used to show progress + */ + +unsigned ZModem::recv_file_data(File* fp, int64_t offset) +{ + int type=0; + unsigned errors=0; + off_t pos; + + zm->transfer_start_pos=offset; + zm->transfer_start_time=time(NULL); + + if(!(fp->seek(offset))) { + lprintf(LOG_ERR,"ERROR %d seeking to file offset %lld" ,errno, offset); + ZModem::send_pos_header( ZFERR, (uint32_t)offset, /* Hex? */ ZTRUE); + return 1; /* errors */ + } + + /* zmodem.doc: + + The zmodem receiver uses the file length [from ZFILE data] as an estimate only. + It may be used to display an estimate of the transmission time, + and may be compared with the amount of free disk space. The + actual length of the received file is determined by the data + transfer. A file may grow after transmission commences, and + all the data will be sent. + */ + while(errors<=zm->max_errors && is_connected() && !is_cancelled()) { + + if((pos=fp->position()) > zm->current_file_size) + zm->current_file_size = pos; + + if(zm->max_file_size!=0 && pos >= zm->max_file_size) { + lprintf(LOG_WARNING,"Specified maximum file size (%lld"" bytes) reached at offset %lld" ,zm->max_file_size, pos); + send_pos_header( ZFERR, (uint32_t)pos, /* Hex? */ ZTRUE); + break; + } + + if(type!=ENDOFFRAME) + send_pos_header( ZRPOS, (uint32_t)pos, /* Hex? */ ZTRUE); + + type = recv_file_frame(fp); + if(type == ZEOF || type == ZFIN) + break; + if(type==ENDOFFRAME) + { + lprintf(LOG_DEBUG,"Received complete frame at offset: %lu", (ulong)fp->position()); + } + else { + if(type>0 && !zm->local_abort) + { + lprintf(LOG_ERR,"Received %s at offset: %lu", chr(type), (ulong)fp->position()); + } + errors++; + } + } + + /* + * wait for the eof header + */ + for(;errors<=zm->max_errors && !is_cancelled() && type!=ZEOF && type!=ZFIN; errors++) + type = recv_header_and_check(); + + return(errors); +} + + +int ZModem::recv_file_frame(File* fp) +{ + unsigned n; + int type; + unsigned attempt; + + /* + * wait for a ZDATA header with the right file offset + * or a timeout or a ZFIN + */ + + for(attempt=0;;attempt++) { + if(attempt>=zm->max_errors) + return TIMEOUT; + type = recv_header(); + switch(type) { + case ZEOF: + /* ZMODEM.DOC: + If the receiver has not received all the bytes of the file, + the receiver ignores the ZEOF because a new ZDATA is coming. + */ + if(zm->rxd_header_pos==(uint32_t)fp->position()) + return type; + lprintf(LOG_WARNING,"Ignoring ZEOF as all bytes (%lu) have not been received",zm->rxd_header_pos); + continue; + case ZFIN: + return type; + case TIMEOUT: + return type; + } + if(is_cancelled() || !is_connected()) + return ZCAN; + + if(type==ZDATA) + break; + + lprintf(LOG_WARNING,"Received %s instead of ZDATA frame", frame_desc(type)); + } + + if(zm->rxd_header_pos!=(uint32_t)fp->position()) { + lprintf(LOG_WARNING,"Received wrong ZDATA frame (%lu vs %lu)" ,zm->rxd_header_pos, (ulong)fp->position()); + return ZFALSE; + } + + do { + type = recv_data(zm->rx_data_subpacket,sizeof(zm->rx_data_subpacket),&n,ZTRUE); + + lprintf(LOG_INFO,"packet len %d type %d\n",n,type); + + if (type == ENDOFFRAME || type == FRAMEOK) { + + if(fp->write(zm->rx_data_subpacket,n)!=n) { + lprintf(LOG_ERR,"ERROR %d writing %u bytes at file offset %llu" ,errno, n,(uint64_t)fp->position()); + send_pos_header( ZFERR, (uint32_t)fp->position(), /* Hex? */ ZTRUE); + return ZFALSE; + } + } + + if(type==FRAMEOK) + zm->block_size = n; + + progress(zm->cbdata, fp->position()); + + if(is_cancelled()) + return(ZCAN); + + } while(type == FRAMEOK); + + return type; +} + +const char* ZModem::source(void) +{ + return(__FILE__); +} + +char* ZModem::ver(char *buf) +{ + sscanf("$Revision: 1.120 $", "%*s %s", buf); + + return(buf); +} + +#endif diff --git a/zimodem/rt_clock.h b/zimodem/rt_clock.h new file mode 100644 index 0000000..e612bb5 --- /dev/null +++ b/zimodem/rt_clock.h @@ -0,0 +1,174 @@ +/* + Copyright 2016-2019 Bo Zimmerman + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include + +static const char *TimeZones[243][2] PROGMEM = { {"UTC","0"}, + {"A","1"},{"CDT","10:30"},{"ACST","9:30"},{"ACT","-5"}, + {"ACT","9:30/10:30"},{"ACWST","8:45"},{"ADT","3"},{"ADT","-3"}, + {"AEDT","11"},{"AEST","10"},{"AET","11"},{"AFT","4:30"}, + {"AKDT","-8"},{"AKST","-9"},{"ALMT","6"},{"AMST","-3"},{"AMST","5"}, + {"AMT","-4"},{"AMT","4"},{"ANAST","12"},{"ANAT","12"},{"AQTT","5"}, + {"ART","-3"},{"AST","2"},{"AST","-4"},{"AT","-4/-3"},{"AWDT","9"}, + {"AWST","8"},{"AZOST","0"},{"AZOT","-1"},{"AZST","5"},{"AZT","4"}, + {"AOE","-12"},{"B","2"},{"BNT","8"},{"BOT","-4"},{"BRST","-2"}, + {"BRT","-3"},{"BST","6"},{"BST","11"},{"BST","1"},{"BTT","6"}, + {"C","3"},{"CAST","8"},{"CAT","2"},{"CCT","6:30"},{"CDT","-5"}, + {"CDT","-4"},{"CEST","2"},{"CET","1"},{"CHADT","13:45"}, + {"CHAST","12:45"},{"CHOST","9"},{"CHOT","8"},{"CHUT","10"}, + {"CIDST","-4"},{"CIST","-5"},{"CKT","-10"},{"CLST","-3"}, + {"CLT","-4"},{"COT","-5"},{"CST","-6"},{"CST","8"},{"CST","-5"}, + {"CT","-6/-5"},{"CVT","-1"},{"CXT","7"},{"ChST","10"},{"D","4"}, + {"DAVT","7"},{"DDUT","10"},{"E","5"},{"EASST","-5"},{"EAST","-6"}, + {"EAT","3"},{"ECT","-5"},{"EDT","-4"},{"EEST","3"},{"EET","2"}, + {"EGST","0"},{"EGT","-1"},{"EST","-5"},{"ET","-5/-4"},{"F","6"}, + {"FET","3"},{"FJST","13"},{"FJT","12"},{"FKST","-3"},{"FKT","-4"}, + {"FNT","-2"},{"G","7"},{"GALT","-6"},{"GAMT","-9"},{"GET","4"}, + {"GFT","-3"},{"GILT","12"},{"GMT","0"},{"GST","4"},{"GST","-2"}, + {"GYT","-4"},{"H","8"},{"HADT","-9"},{"HAST","-10"},{"HKT","8"}, + {"HOVST","8"},{"HOVT","7"},{"I","9"},{"ICT","7"},{"IDT","3"}, + {"IOT","6"},{"IRDT","4:30"},{"IRKST","9"},{"IRKT","8"}, + {"IRST","3:30"},{"IST","5:30"},{"IST","1"},{"IST","2"},{"JST","9"}, + {"K","10"},{"KGT","6"},{"KOST","11"},{"KRAST","8"},{"KRAT","7"}, + {"KST","9"},{"KUYT","4"},{"L","11"},{"LHDT","11"},{"LHST","10:30"}, + {"LINT","14"},{"M","12"},{"MAGST","12"},{"MAGT","11"},{"MART","-9:30"}, + {"MAWT","5"},{"MDT","-6"},{"MHT","12"},{"MMT","6:30"},{"MSD","4"}, + {"MSK","3"},{"MST","-7"},{"MT","-7/-6"},{"MUT","4"},{"MVT","5"}, + {"MYT","8"},{"N","-1"},{"NCT","11"},{"NDT","-2:30"},{"NFT","11"}, + {"NOVST","7"},{"NOVT","6"},{"NPT","5:45"},{"NRT","12"},{"NST","-3:30"}, + {"NUT","-11"},{"NZDT","13"},{"NZST","12"},{"O","-2"},{"OMSST","7"}, + {"OMST","6"},{"ORAT","5"},{"P","-3"},{"PDT","-7"},{"PET","-5"}, + {"PETST","12"},{"PETT","12"},{"PGT","10"},{"PHOT","13"},{"PHT","8"}, + {"PKT","5"},{"PMDT","-2"},{"PMST","-3"},{"PONT","11"},{"PST","-8"}, + {"PST","-8"},{"PT","-8/-7"},{"PWT","9"},{"PYST","-3"},{"PYT","-4"}, + {"PYT","8:30"},{"Q","-4"},{"QYZT","6"},{"R","-5"},{"RET","4"}, + {"ROTT","-3"},{"S","-6"},{"SAKT","11"},{"SAMT","4"},{"SAST","2"}, + {"SBT","11"},{"SCT","4"},{"SGT","8"},{"SRET","11"},{"SRT","-3"}, + {"SST","-11"},{"SYOT","3"},{"T","-7"},{"TAHT","-10"},{"TFT","5"}, + {"TJT","5"},{"TKT","13"},{"TLT","9"},{"TMT","5"},{"TOST","14"}, + {"TOT","13"},{"TRT","3"},{"TVT","12"},{"U","-8"},{"ULAST","9"}, + {"ULAT","8"},{"UYST","-2"},{"UYT","-3"},{"UZT","5"}, + {"V","-9"},{"VET","-4"},{"VLAST","11"},{"VLAT","10"},{"VOST","6"}, + {"VUT","11"},{"W","-10"},{"WAKT","12"},{"WARST","-3"},{"WAST","2"}, + {"WAT","1"},{"WEST","1"},{"WET","0"},{"WFT","12"},{"WGST","-2"}, + {"WGT","-3"},{"WIB","7"},{"WIT","9"},{"WITA","8"},{"WST","14"}, + {"WST","1"},{"WT","0"},{"X","-11"},{"Y","-12"},{"YAKST","10"}, + {"YAKT","9"},{"YAPT","10"},{"YEKST","6"},{"YEKT","5"},{"Z","0"} +}; + +class DateTimeClock +{ +public: + DateTimeClock(uint32_t epochSecs); + DateTimeClock(); + DateTimeClock(int year, int month, int day, int hour, int min, int sec, int millis); +protected: + uint16_t year=0; + uint8_t month=0; + uint8_t day=0; + uint8_t hour=0; + uint8_t min=0; + uint8_t sec=0; + uint16_t milsec=0; + char str[55]; + + bool isLeapYear(); + uint8_t getDaysInThisMonth(); +public: + int getYear(); + void setYear(int y); + void addYear(uint32_t y); + int getMonth(); + void setMonth(int m); + void addMonth(uint32_t m); + int getDay(); + void setDay(int d); + void addDay(uint32_t d); + int getHour(); + void setHour(int h); + void addHour(uint32_t h); + int getMinute(); + void setMinute(int mm); + void addMinute(uint32_t mm); + int getSecond(); + void setSecond(int s); + void addSecond(uint32_t s); + int getMillis();; + void setMillis(int s); + void addMillis(uint64_t s); + + void setByUnixEpoch(uint32_t unisex); + uint32_t getUnixEpoch(); + + int getDoWNumber(); + const char *getDoW(); + + bool isInStandardTime(); + bool isInDaylightSavingsTime(); + + void setTime(DateTimeClock &clock); +}; + +class RealTimeClock : DateTimeClock +{ +public: + RealTimeClock(uint32_t epochSecs); + RealTimeClock(); + RealTimeClock(int year, int month, int day, int hour, int min, int sec, int millis); + + void tick(); + + bool isTimeSet(); + + bool reset(); + + int getTimeZoneCode(); + void setTimeZoneCode(int val); + bool setTimeZone(String str); + + String getFormat(); + void setFormat(String fmt); + + String getNtpServerHost(); + void setNtpServerHost(String newHost); + + bool isDisabled(); + void setDisabled(bool tf); + + void forceUpdate(); + + DateTimeClock &getCurrentTime(); + String getCurrentTimeFormatted(); + + // should be private +private: + bool disabled = false; + DateTimeClock adjClock; + WiFiUDP udp; + bool udpStarted = false; + uint32_t lastMillis = 0; + uint32_t nextNTPMillis = 0; + int32_t ntpPeriodMillis = 60 * 1000; // every minute + int32_t ntpPeriodLongMillis = 5 * 60 * 60 * 1000; // every 5 hours + uint8_t tzCode = 0; + String format="%M/%d/%yyyy %h:%mm:%ss%aa %z"; + String ntpServerName = "time.nist.gov"; + + void startUdp(); + bool sendTimeRequest(); +}; + + diff --git a/zimodem/rt_clock.ino b/zimodem/rt_clock.ino new file mode 100644 index 0000000..f74a644 --- /dev/null +++ b/zimodem/rt_clock.ino @@ -0,0 +1,702 @@ +/* + Copyright 2016-2019 Bo Zimmerman + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#include +#define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \ + ((x)<< 8 & 0x00FF0000UL) | \ + ((x)>> 8 & 0x0000FF00UL) | \ + ((x)>>24 & 0x000000FFUL) ) + +const int NTP_PACKET_SIZE = 48; + +uint8_t DAYS_IN_MONTH[13] PROGMEM = { + 31,28,31,30,31,30,31,31,30,31,30,31 +}; + +char *uintToStr( const uint64_t num, char *str ) +{ + uint8_t i = 0; + uint64_t n = num; + do + i++; + while ( n /= 10 ); + str[i] = '\0'; + n = num; + do + str[--i] = ( n % 10 ) + '0'; + while ( n /= 10 ); + return str; +} + +DateTimeClock::DateTimeClock() : DateTimeClock(0) +{ +} + + +DateTimeClock::DateTimeClock(uint32_t epochSecs) +{ + setByUnixEpoch(epochSecs); +} + +DateTimeClock::DateTimeClock(int y, int m, int d, int h, int mn, int s, int mi) +{ + year=y; + month=m; + day=d; + hour=h; + min=mn; + sec=s; + milsec=mi; +} + +RealTimeClock::RealTimeClock(uint32_t epochSecs) : DateTimeClock(epochSecs) +{ + lastMillis = millis(); + nextNTPMillis = millis(); +} + +RealTimeClock::RealTimeClock() : DateTimeClock() +{ + lastMillis = millis(); + nextNTPMillis = millis(); +} + +RealTimeClock::RealTimeClock(int y, int m, int d, int h, int mn, int s, int mi) : + DateTimeClock(y,m,d,h,mn,s,mi) +{ + lastMillis = millis(); + nextNTPMillis = millis(); +} + +void RealTimeClock::startUdp() +{ + if(!udpStarted) + { + udpStarted=udp.begin(2390); + } +} + +void RealTimeClock::tick() +{ + if(disabled) + return; + if(udpStarted) + { + int cb = udp.parsePacket(); + if (cb) + { + // adapted from code by by Michael Margolis, Tom Igoe, and Ivan Grokhotkov + //debugPrint("Packet received, length=%d\n\r",cb); + byte packetBuffer[ NTP_PACKET_SIZE]; + udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer + // combine the four bytes (two words) into a long integer + // this is NTP time (seconds since Jan 1 1900): + uint32_t secsSince1900 = htonl(*((uint32_t *)(packetBuffer + 40))); + // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: + const uint32_t seventyYears = 2208988800UL; + // subtract seventy years: + uint32_t epoch = secsSince1900 - seventyYears; + lastMillis = millis(); + debugPrintf("Received NTP: %d/%d/%d %d:%d:%d\n\r",(int)getMonth(),(int)getDay(),(int)getYear(),(int)getHour(),(int)getMinute(),(int)getSecond()); + // now to apply the timezone. Ugh; + setByUnixEpoch(epoch); + String tz=""; + { + char s=0; + char c=pgm_read_byte_near(&(TimeZones[tzCode][1][s])); + while(c != 0) + { + tz += c; + c=pgm_read_byte_near(&(TimeZones[tzCode][1][++s])); + } + String otz=tz; + int x=tz.indexOf("/"); + if(x > 0) + { + if(isInStandardTime()) + tz = tz.substring(0,x); + else + tz = tz.substring(x+1); + } + x=tz.indexOf(":"); + int mm=0; + if(x > 0) + { + mm = atoi(tz.substring(x+1).c_str()); + tz = tz.substring(0,x); + } + uint32_t echg = (atoi(tz.c_str()) * 3600); + echg += ((echg < 0)?(-(mm * 60)):(mm * 60)); + setByUnixEpoch(epoch + echg); + } + nextNTPMillis = millis() + ntpPeriodLongMillis; // one hour + } + else + { + uint32_t now=millis(); + if(nextNTPMillis >= now) + { + if(((nextNTPMillis - now) > ntpPeriodLongMillis) + ||(nextNTPMillis == now)) + forceUpdate(); + } + else + if((now - nextNTPMillis) > ntpPeriodLongMillis) + forceUpdate(); + } + } +} + +void RealTimeClock::forceUpdate() +{ + if(!disabled) + { + nextNTPMillis = millis() + ntpPeriodMillis; + startUdp(); + sendTimeRequest(); + } +} + +bool RealTimeClock::isTimeSet() +{ + return (year > 1000); +} + +bool RealTimeClock::reset() +{ + year=0; + month=0; + day=0; + hour=0; + min=0; + sec=0; + milsec=0; + lastMillis = millis(); + nextNTPMillis = millis(); + tzCode = 0; + format="%M/%d/%yyyy %h:%mm:%ss%aa %z"; + ntpServerName = "time.nist.gov"; + return true; +} + +int DateTimeClock::getYear() +{ + return year; +} + +void DateTimeClock::setYear(int y) +{ + year=y; +} + +void DateTimeClock::addYear(uint32_t y) +{ + year+=y; +} + +int DateTimeClock::getMonth() +{ + return month + 1; // because 0 based +} + +void DateTimeClock::setMonth(int m) +{ + month = m % 12; +} + +void DateTimeClock::addMonth(uint32_t m) +{ + m = month + m; + if(m > 11) + addYear(floor(m / 12)); + setMonth(m); +} + +int DateTimeClock::getDay() +{ + return day + 1; +} + +void DateTimeClock::setDay(int d) +{ + day = d % getDaysInThisMonth(); +} + +void DateTimeClock::addDay(uint32_t d) +{ + d = day + d; + if(d >= getDaysInThisMonth()) + { + while(d > (isLeapYear()?366:365)) + { + d=d-(isLeapYear()?366:365); + addYear(1); + } + while(d >= getDaysInThisMonth()) + { + d=d-getDaysInThisMonth(); + addMonth(1); + } + } + setDay(d); +} + +int DateTimeClock::getHour() +{ + return hour; +} + +void DateTimeClock::setHour(int h) +{ + hour=h % 24; +} + +void DateTimeClock::addHour(uint32_t h) +{ + h=hour + h; + if(h > 23) + addDay(floor(h / 24)); + setHour(h); +} + +int DateTimeClock::getMinute() +{ + return min; +} + +void DateTimeClock::setMinute(int mm) +{ + min=mm % 60; +} + +void DateTimeClock::addMinute(uint32_t mm) +{ + mm = min+mm; + if(mm > 59) + addHour(floor(mm / 60)); + setMinute(mm); +} + +int DateTimeClock::getSecond() +{ + return sec; +} + +void DateTimeClock::setSecond(int s) +{ + sec=s % 60; +} + +void DateTimeClock::addSecond(uint32_t s) +{ + s = sec + s; + if(s > 59) + addMinute(floor(s / 60)); + setSecond(s); +} + +int DateTimeClock::getMillis() +{ + return milsec; +} + +void DateTimeClock::setMillis(int s) +{ + milsec=s % 1000; +} + +void DateTimeClock::addMillis(uint64_t s) +{ + s = milsec+s; + if(s > 999) + addSecond(floor(s / 1000)); + setMillis(s); +} + +bool DateTimeClock::isLeapYear() +{ + if(year % 4 == 0) + { + if(year % 100 == 0) + { + if(year % 400 == 0) + return true; + return false; + } + return true; + } + return false; +} + +uint8_t DateTimeClock::getDaysInThisMonth() +{ + if(month != 1) // feb exception + return pgm_read_byte_near(DAYS_IN_MONTH + month); + return (isLeapYear() ? 29 : 28); +} + +void DateTimeClock::setTime(DateTimeClock &clock) +{ + year=clock.year; + month=clock.month; + day=clock.day; + hour=clock.hour; + min=clock.min; + sec=clock.sec; + milsec=clock.milsec; +} + + +DateTimeClock &RealTimeClock::getCurrentTime() +{ + adjClock.setTime(*this); + uint32_t now=millis(); + if(lastMillis <= now) + adjClock.addMillis(now - lastMillis); + else + adjClock.addMillis(now + (0xffffffff - lastMillis)); + return adjClock; +} + + +void DateTimeClock::setByUnixEpoch(uint32_t unisex) +{ + setYear(1970); + setMonth(0); + setDay(0); + setHour(0); + setMinute(0); + setSecond(0); + setMillis(0); + uint64_t ms = unisex; + ms *= 1000L; + addMillis(ms); +} + +uint32_t DateTimeClock::getUnixEpoch() +{ + if(year < 1970) + return 0; + uint32_t val = sec + (min * 60) + (hour * 60 * 60); + //TODO: + return val; +} + +bool RealTimeClock::sendTimeRequest() +{ + if((WiFi.status() == WL_CONNECTED)&&(udpStarted)) + { + // adapted from code by by Michael Margolis, Tom Igoe, and Ivan Grokhotkov + debugPrintf("Sending NTP Packet..."); + byte packetBuffer[ NTP_PACKET_SIZE]; + memset(packetBuffer, 0, NTP_PACKET_SIZE); + packetBuffer[0] = 0b11100011; // LI, Version, Mode + packetBuffer[1] = 0; // Stratum, or type of clock + packetBuffer[2] = 6; // Polling Interval + packetBuffer[3] = 0xEC; // Peer Clock Precision + // 8 bytes of zero for Root Delay & Root Dispersion + packetBuffer[12] = 49; + packetBuffer[13] = 0x4E; + packetBuffer[14] = 49; + packetBuffer[15] = 52; + IPAddress timeServerIP; + String host = ntpServerName; + int port=123; + int pi=host.indexOf(':'); + if(pi>0) + { + port=atoi(host.substring(pi+1).c_str()); + host = host.substring(0,pi); + } + WiFi.hostByName(ntpServerName.c_str(), timeServerIP); + udp.beginPacket(timeServerIP, port); //NTP requests are to port 123 + udp.write(packetBuffer, NTP_PACKET_SIZE); + udp.endPacket(); + return true; + } + return false; +} + +int DateTimeClock::getDoWNumber() +{ + uint16_t x= (getMonth() + 9) % 12; + uint16_t y = getYear() - x/10; + uint32_t z= 365*y + y/4 - y/100 + y/400 + (x*306 + 5)/10 + (getDay() - 1); + z=z%7; + if(z>3) + return z-4; + else + return z+3; +} + +const char *DateTimeClock::getDoW() +{ + int num=getDoWNumber(); + switch(num) + { + case 0:return "Sunday"; + case 1:return "Monday"; + case 2:return "Tuesday"; + case 3:return "Wednesday"; + case 4:return "Thursday"; + case 5:return "Friday"; + case 6:return "Saturday"; + default: return "Broken"; + } +} + +bool DateTimeClock::isInStandardTime() +{ + uint8_t m=getMonth(); + if(m<3) + return true; + if(m==3) + { + uint8_t d=getDay(); + uint8_t dow=getDoWNumber(); + while(dow-- > 0) + d--; + if(d<14) + return true; + return false; + } + if((m>3)&&(m<11)) + return false; + if(m==11) + { + uint8_t d=getDay(); + uint8_t dow=getDoWNumber(); + while(dow-- > 0) + d--; + if(d<7) + return false; + return true; + } + return true; +} + +bool DateTimeClock::isInDaylightSavingsTime() +{ + return !isInStandardTime(); +} + +int RealTimeClock::getTimeZoneCode() +{ + return tzCode; +} + +void RealTimeClock::setTimeZoneCode(int val) +{ + if((tzCode >= 0)&&(tzCode < 243)) + { + tzCode = val; + forceUpdate(); + } +} + +bool RealTimeClock::setTimeZone(String str) +{ + str.toUpperCase(); + if(str.length()==0) + return false; + for(int i=0;i<243;i++) + { + for(int s=0;s<=str.length();s++) + { + char c=pgm_read_byte_near(&(TimeZones[i][0][s])); + if(s==str.length()) + { + if(c==0) + { + tzCode = i; + forceUpdate(); + return true; + } + } + else + if((c==0)||(c != str[s])) + break; + } + } + return false; +} + +String RealTimeClock::getFormat() +{ + return format; +} + +void RealTimeClock::setFormat(String fmt) +{ + fmt.replace(',','.'); + format = fmt; +} + +bool RealTimeClock::isDisabled() +{ + return disabled; +} + +void RealTimeClock::setDisabled(bool tf) +{ + disabled=tf; +} + +String RealTimeClock::getCurrentTimeFormatted() +{ + //String format="%M/%D/%YYYY %h:%mm:%ss%a" + DateTimeClock c = getCurrentTime(); + String f=format; + if(f.indexOf("%yyyy")>=0) + { + sprintf(str,"%04d",(int)c.getYear()); + f.replace("%yyyy",str); + } + if(f.indexOf("%yy")>=0) + { + int y=c.getYear(); + y -= (floor(y/100)*100); + sprintf(str,"%02d",y); + f.replace("%yy",str); + } + if(f.indexOf("%y")>=0) + { + sprintf(str,"%d",(int)c.getYear()); + f.replace("%y",str); + } + if(f.indexOf("%MM")>=0) + { + sprintf(str,"%02d",(int)c.getMonth()); + f.replace("%MM",str); + } + if(f.indexOf("%M")>=0) + { + sprintf(str,"%d",(int)c.getMonth()); + f.replace("%M",str); + } + if(f.indexOf("%dd")>=0) + { + sprintf(str,"%02d",(int)c.getDay()); + f.replace("%dd",str); + } + if(f.indexOf("%d")>=0) + { + sprintf(str,"%d",(int)c.getDay()); + f.replace("%d",str); + } + if(f.indexOf("%ee")>=0) + { + f.replace("%ee",c.getDoW()); + } + if(f.indexOf("%e")>=0) + { + String dow=c.getDoW(); + dow = dow.substring(0,3); + sprintf(str,"%d",dow.c_str()); + f.replace("%e",str); + } + if(f.indexOf("%HH")>=0) + { + sprintf(str,"%02d",(int)c.getHour()); + f.replace("%HH",str); + } + if(f.indexOf("%H")>=0) + { + sprintf(str,"%d",(int)c.getHour()); + f.replace("%H",str); + } + if(f.indexOf("%hh")>=0) + { + if((c.getHour()%12)==0) + strcpy(str,"12"); + else + sprintf(str,"%02d",c.getHour()%12); + f.replace("%hh",str); + } + if(f.indexOf("%h")>=0) + { + if((c.getHour()%12)==0) + strcpy(str,"12"); + else + sprintf(str,"%d",(int)(c.getHour() % 12)); + f.replace("%h",str); + } + if(f.indexOf("%mm")>=0) + { + sprintf(str,"%02d",(int)c.getMinute()); + f.replace("%mm",str); + } + if(f.indexOf("%m")>=0) + { + sprintf(str,"%d",(int)c.getMinute()); + f.replace("%m",str); + } + if(f.indexOf("%ss")>=0) + { + sprintf(str,"%02d",(int)c.getSecond()); + f.replace("%ss",str); + } + if(f.indexOf("%s")>=0) + { + sprintf(str,"%d",(int)c.getSecond()); + f.replace("%s",str); + } + if(f.indexOf("%AA")>=0) + f.replace("%AA",(c.getHour()<12)?"AM":"PM"); + if(f.indexOf("%aa")>=0) + f.replace("%aa",(c.getHour()<12)?"am":"pm"); + if(f.indexOf("%A")>=0) + f.replace("%A",(c.getHour()<12)?"A":"P"); + if(f.indexOf("%a")>=0) + f.replace("%a",(c.getHour()<12)?"a":"p"); + if(f.indexOf("%z")>=0) + { + String z=""; + char s=0; + char c=pgm_read_byte_near(&(TimeZones[tzCode][0][s])); + while(c != 0) + { + z += c; + c=pgm_read_byte_near(&(TimeZones[tzCode][0][++s])); + } + z.toLowerCase(); + f.replace("%z",z.c_str()); + } + if(f.indexOf("%Z")>=0) + { + String z=""; + char s=0; + char c=pgm_read_byte_near(&(TimeZones[tzCode][0][s])); + while(c != 0) + { + z += c; + c=pgm_read_byte_near(&(TimeZones[tzCode][0][++s])); + } + f.replace("%Z",z.c_str()); + } + return f; +} + +String RealTimeClock::getNtpServerHost() +{ + return ntpServerName; +} + +void RealTimeClock::setNtpServerHost(String newHost) +{ + newHost.replace(',','.'); + ntpServerName = newHost; +} + diff --git a/zimodem/zserout.h b/zimodem/serout.h similarity index 78% rename from zimodem/zserout.h rename to zimodem/serout.h index 92edba9..56d65cd 100644 --- a/zimodem/zserout.h +++ b/zimodem/serout.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 Bo Zimmerman + Copyright 2016-2019 Bo Zimmerman Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,6 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +#ifndef ZHEADER_SEROUT_H +#define ZHEADER_SEROUT_H #define DBG_BYT_CTR 20 @@ -29,7 +31,11 @@ enum FlowControlType }; static bool enableRtsCts = true; -static int SER_BUFSIZE = 128; +#ifdef ZIMODEM_ESP32 +# define SER_BUFSIZE 0x7F +#else +# define SER_BUFSIZE 128 +#endif static uint8_t TBUF[SER_WRITE_BUFSIZE]; static char FBUF[256]; static int TBUFhead=0; @@ -39,15 +45,15 @@ static int serialDelayMs = 0; static void serialDirectWrite(uint8_t c); static void serialOutDeque(); static int serialOutBufferBytesRemaining(); -static void enqueSerialOut(uint8_t c); static void clearSerialOutBuffer(); -class ZSerial +class ZSerial : public Stream { private: bool petsciiMode = false; - FlowControlType flowControlType=FCT_NORMAL; - bool XON=true; + FlowControlType flowControlType=DEFAULT_FCT; + bool XON_STATE=true; + void enqueByte(uint8_t c); public: ZSerial(); void setPetsciiMode(bool petscii); @@ -60,13 +66,15 @@ class ZSerial bool isSerialHalted(); bool isSerialCancelled(); bool isPacketOut(); + int getConfigFlagBitmap(); void prints(String str); void prints(const char *expr); void printc(const char c); void printc(uint8_t c); + virtual size_t write(uint8_t c); + size_t write(uint8_t *buf, int bufSz); void printb(uint8_t c); - void write(uint8_t c); void printd(double f); void printi(int i); void printf(const char* format, ...); @@ -74,5 +82,10 @@ class ZSerial void flushAlways(); int availableForWrite(); char drainForXonXoff(); + + virtual int available(); + virtual int read(); + virtual int peek(); }; +#endif diff --git a/zimodem/serout.ino b/zimodem/serout.ino new file mode 100644 index 0000000..ee7b372 --- /dev/null +++ b/zimodem/serout.ino @@ -0,0 +1,446 @@ +/* + Copyright 2016-2019 Bo Zimmerman + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include + +static void serialDirectWrite(uint8_t c) +{ + HWSerial.write(c); + if(serialDelayMs > 0) + delay(serialDelayMs); + logSerialOut(c); +} + +static void hwSerialFlush() +{ +#ifdef ZIMODEM_ESP8266 + HWSerial.flush(); +#endif +} + +static void serialOutDeque() +{ +#ifdef ZIMODEM_ESP32 + while((TBUFhead != TBUFtail) + &&((SER_BUFSIZE - HWSerial.availableForWrite())=SER_BUFSIZE)) // necessary for esp8266 flow control +#endif + { + serialDirectWrite(TBUF[TBUFhead]); + TBUFhead++; + if(TBUFhead >= SER_WRITE_BUFSIZE) + TBUFhead = 0; + } +} + +static int serialOutBufferBytesRemaining() +{ + if(TBUFtail == TBUFhead) + return SER_WRITE_BUFSIZE-1; + else + if(TBUFtail > TBUFhead) + { + int used = TBUFtail - TBUFhead; + return SER_WRITE_BUFSIZE - used -1; + } + else + return TBUFhead - TBUFtail - 1; +} + +static void enqueSerialOut(uint8_t c) +{ + TBUF[TBUFtail] = c; + TBUFtail++; + if(TBUFtail >= SER_WRITE_BUFSIZE) + TBUFtail = 0; +} + +static void clearSerialOutBuffer() +{ + TBUFtail=TBUFhead; +} + +static void ensureSerialBytes(int num) +{ + if(serialOutBufferBytesRemaining()<1) + { + serialOutDeque(); + while(serialOutBufferBytesRemaining()<1) + yield(); + } +} + +static void flushSerial() +{ + while(TBUFtail != TBUFhead) + { + serialOutDeque(); + yield(); + } + hwSerialFlush(); +} + +ZSerial::ZSerial() +{ +} + +void ZSerial::setPetsciiMode(bool petscii) +{ + petsciiMode = petscii; +} + +bool ZSerial::isPetsciiMode() +{ + return petsciiMode; +} + +void ZSerial::setFlowControlType(FlowControlType type) +{ + flowControlType = type; +#ifdef ZIMODEM_ESP32 + if(flowControlType == FCT_RTSCTS) + { + uart_set_hw_flow_ctrl(UART_NUM_2,UART_HW_FLOWCTRL_DISABLE,0); + uint32_t invertMask = 0; + if(pinSupport[pinCTS]) + { + uart_set_pin(UART_NUM_2, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, /*cts_io_num*/pinCTS); + // cts is input to me, output to true RS232 + if(ctsActive == HIGH) + invertMask = invertMask | UART_INVERSE_CTS; + } + if(pinSupport[pinRTS]) + { + uart_set_pin(UART_NUM_2, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, /*rts_io_num*/ pinRTS, UART_PIN_NO_CHANGE); + s_pinWrite(pinRTS, rtsActive); + // rts is output to me, input to true RS232 + if(rtsActive == HIGH) + invertMask = invertMask | UART_INVERSE_RTS; + } + //debugPrintf("invert = %d magic values = %d %d, RTS_HIGH=%d, RTS_LOW=%d HIGHHIGH=%d LOWLOW=%d\n",invertMask,ctsActive,rtsActive, DEFAULT_RTS_HIGH, DEFAULT_RTS_LOW, HIGH, LOW); + if(invertMask != 0) + uart_set_line_inverse(UART_NUM_2, invertMask); + const int CUTOFF = 100; + if(pinSupport[pinRTS]) + { + if(pinSupport[pinCTS]) + uart_set_hw_flow_ctrl(UART_NUM_2,UART_HW_FLOWCTRL_CTS_RTS,CUTOFF); + else + uart_set_hw_flow_ctrl(UART_NUM_2,UART_HW_FLOWCTRL_CTS_RTS,CUTOFF); + } + else + if(pinSupport[pinCTS]) + uart_set_hw_flow_ctrl(UART_NUM_2,UART_HW_FLOWCTRL_CTS_RTS,CUTOFF); + } + else + uart_set_hw_flow_ctrl(UART_NUM_2,UART_HW_FLOWCTRL_DISABLE,0); +#endif +} + +FlowControlType ZSerial::getFlowControlType() +{ + return flowControlType; +} + +void ZSerial::setXON(bool isXON) +{ + XON_STATE = isXON; +} + +int ZSerial::getConfigFlagBitmap() +{ + return + (isPetsciiMode()?FLAG_PETSCII:0) + |((getFlowControlType()==FCT_RTSCTS)?FLAG_RTSCTS:0) + |((getFlowControlType()==FCT_NORMAL)?FLAG_XONXOFF:0) + |((getFlowControlType()==FCT_AUTOOFF)?FLAG_XONXOFF:0) + |((getFlowControlType()==FCT_MANUAL)?FLAG_XONXOFF:0); +} + +bool ZSerial::isXON() +{ + return XON_STATE; +} + +bool ZSerial::isSerialOut() +{ + switch(flowControlType) + { + case FCT_RTSCTS: + if(pinSupport[pinCTS]) + { + //debugPrintf("CTS: pin %d (%d == %d)\n",pinCTS,digitalRead(pinCTS),ctsActive); + return (digitalRead(pinCTS) == ctsActive); + } + return true; + case FCT_NORMAL: + case FCT_AUTOOFF: + case FCT_MANUAL: + break; + case FCT_DISABLED: + return true; + case FCT_INVALID: + return true; + } + return XON_STATE; +} + +bool ZSerial::isSerialCancelled() +{ + if(flowControlType == FCT_RTSCTS) + { + if(pinSupport[pinCTS]) + { + //debugPrintf("CTS: pin %d (%d == %d)\n",pinCTS,digitalRead(pinCTS),ctsActive); + return (digitalRead(pinCTS) == ctsInactive); + } + } + return false; +} + +bool ZSerial::isSerialHalted() +{ + return !isSerialOut(); +} + +void ZSerial::enqueByte(uint8_t c) +{ + if(TBUFtail == TBUFhead) + { + switch(flowControlType) + { + case FCT_DISABLED: + case FCT_INVALID: +#ifndef ZIMODEM_ESP32 + if((HWSerial.availableForWrite() > 0) + &&(HWSerial.available() == 0)) +#endif + { + serialDirectWrite(c); + return; + } + break; + case FCT_RTSCTS: +#ifdef ZIMODEM_ESP32 + if(isSerialOut()) +#else + if((HWSerial.availableForWrite() >= SER_BUFSIZE) // necessary for esp8266 flow control + &&(isSerialOut())) +#endif + { + serialDirectWrite(c); + return; + } + break; + case FCT_NORMAL: + case FCT_AUTOOFF: + case FCT_MANUAL: + if((HWSerial.availableForWrite() >= SER_BUFSIZE) + &&(HWSerial.available() == 0) + &&(XON_STATE)) + { + serialDirectWrite(c); + return; + } + break; + } + } + // the car jam of blocked bytes stops HERE + //debugPrintf("%d\n",serialOutBufferBytesRemaining()); + while(serialOutBufferBytesRemaining()<1) + { + if(!isSerialOut()) + delay(1); + else + serialOutDeque(); + yield(); + } + enqueSerialOut(c); +} + +void ZSerial::prints(const char *expr) +{ + if(!petsciiMode) + { + for(int i=0;expr[i]!=0;i++) + { + enqueByte(expr[i]); + } + } + else + { + for(int i=0;expr[i]!=0;i++) + { + enqueByte(ascToPetcii(expr[i])); + } + } +} + +void ZSerial::printi(int i) +{ + char buf[12]; + prints(itoa(i, buf, 10)); +} + +void ZSerial::printd(double f) +{ + char buf[12]; + prints(dtostrf(f, 2, 2, buf)); +} + +void ZSerial::printc(const char c) +{ + if(!petsciiMode) + enqueByte(c); + else + enqueByte(ascToPetcii(c)); +} + +void ZSerial::printc(uint8_t c) +{ + if(!petsciiMode) + enqueByte(c); + else + enqueByte(ascToPetcii(c)); +} + +void ZSerial::printb(uint8_t c) +{ + enqueByte(c); +} + +size_t ZSerial::write(uint8_t c) +{ + enqueByte(c); + return 1; +} + +size_t ZSerial::write(uint8_t *buf, int bufSz) +{ + for(int i=0;i0) + { + ch=HWSerial.read(); + logSerialIn(ch); + if(ch == 3) + break; + switch(flowControlType) + { + case FCT_NORMAL: + if((!XON_STATE) && (ch == 17)) + XON_STATE=true; + else + if((XON_STATE) && (ch == 19)) + XON_STATE=false; + break; + case FCT_AUTOOFF: + case FCT_MANUAL: + if((!XON_STATE) && (ch == 17)) + XON_STATE=true; + else + XON_STATE=false; + break; + case FCT_INVALID: + break; + case FCT_RTSCTS: + break; + } + } + return ch; +} + +int ZSerial::available() +{ + int avail = HWSerial.available(); + if(avail == 0) + { + if((TBUFtail != TBUFhead) && isSerialOut()) + serialOutDeque(); + } + return avail; +} + +int ZSerial::read() +{ + int c=HWSerial.read(); + if(c == -1) + { + if((TBUFtail != TBUFhead) && isSerialOut()) + serialOutDeque(); + } + else + logSerialIn(c); + return c; +} + +int ZSerial::peek() +{ + return HWSerial.peek(); +} diff --git a/zimodem/stringstream.h b/zimodem/stringstream.h new file mode 100644 index 0000000..ecdf342 --- /dev/null +++ b/zimodem/stringstream.h @@ -0,0 +1,27 @@ +#ifndef _STRING_STREAM_H_INCLUDED_ +#define _STRING_STREAM_H_INCLUDED_ + +class StringStream : public Stream +{ +public: + StringStream(const String &s) + { + str = s; + position = 0; + } + + // Stream methods + virtual int available() { return str.length() - position; } + virtual int read() { return position < str.length() ? str[position++] : -1; } + virtual int peek() { return position < str.length() ? str[position] : -1; } + virtual void flush() { }; + // Print methods + virtual size_t write(uint8_t c) { str += (char)c; return 1;}; + +private: + String str; + int length; + int position; +}; + +#endif // _STRING_STREAM_H_INCLUDED_ diff --git a/zimodem/wificlientnode.h b/zimodem/wificlientnode.h index 4894562..b5ff7b5 100644 --- a/zimodem/wificlientnode.h +++ b/zimodem/wificlientnode.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 Bo Zimmerman + Copyright 2016-2019 Bo Zimmerman Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,16 +14,41 @@ limitations under the License. */ -#define LAST_PACKET_BUF_SIZE 256 -#define OVERFLOW_BUF_SIZE 256 +#define PACKET_BUF_SIZE 256 + +#ifdef ZIMODEM_ESP32 +#include +#endif + +static WiFiClient *createWiFiClient(bool SSL) +{ +#ifdef ZIMODEM_ESP32 + if(SSL) + return new WiFiClientSecure(); + else +#endif + return new WiFiClient(); +} + +typedef struct Packet +{ + uint8_t num = 0; + uint16_t len = 0; + uint8_t buf[PACKET_BUF_SIZE] = {0}; +}; class WiFiClientNode : public Stream { private: void finishConnectionLink(); int flushOverflowBuffer(); + void fillUnderflowBuf(); WiFiClient client; WiFiClient *clientPtr; + bool answered=true; + int ringsRemain=0; + unsigned long nextRingMillis = 0; + unsigned long nextDisconnect = 0; public: int id=0; @@ -34,72 +59,51 @@ class WiFiClientNode : public Stream int flagsBitmap = 0; char *delimiters = NULL; char *maskOuts = NULL; - uint8 lastPacketBuf[LAST_PACKET_BUF_SIZE]; - int lastPacketLen=0; - uint8_t overflowBuf[OVERFLOW_BUF_SIZE]; - int overflowBufLen = 0; + char *stateMachine = NULL; + char *machineState = NULL; + String machineQue = ""; + + uint8_t nextPacketNum=1; + uint8_t blankPackets=0; + struct Packet lastPacket[3]; // 0 = current buf, 1&2 are back-cache bufs + //struct Packet underflowBuf; // underflows no longer handled this way WiFiClientNode *next = null; WiFiClientNode(char *hostIp, int newport, int flagsBitmap); - WiFiClientNode(WiFiClient newClient, int flagsBitmap); + WiFiClientNode(WiFiClient newClient, int flagsBitmap, int ringDelay); ~WiFiClientNode(); bool isConnected(); + + FlowControlType getFlowControl(); bool isPETSCII(); bool isEcho(); - bool isXonXoff(); bool isTelnet(); + + bool isAnswered(); + void answer(); + int ringsRemaining(int delta); + unsigned long nextRingTime(long delta); + void markForDisconnect(); + bool isMarkedForDisconnect(); + bool isDisconnectedOnStreamExit(); + void setDisconnectOnStreamExit(bool tf); + + void setNoDelay(bool tf); + size_t write(uint8_t c); size_t write(const uint8_t *buf, size_t size); + void print(String s); int read(); int peek(); void flush(); + void flushAlways(); int available(); int read(uint8_t *buf, size_t size); -}; - -class PhoneBookEntry -{ - public: - unsigned long number; - const char *address; - const char *modifiers; - PhoneBookEntry *next = null; - - PhoneBookEntry(unsigned long phnum, const char *addr, const char *mod); - ~PhoneBookEntry(); - - static void loadPhonebook(); - static void clearPhonebook(); - static void savePhonebook(); -}; - -#ifndef _STRING_STREAM_H_INCLUDED_ -#define _STRING_STREAM_H_INCLUDED_ + String readLine(unsigned int timeout); -class StringStream : public Stream -{ -public: - StringStream(const String &s) - { - str = s; - position = 0; - } - - // Stream methods - virtual int available() { return str.length() - position; } - virtual int read() { return position < str.length() ? str[position++] : -1; } - virtual int peek() { return position < str.length() ? str[position] : -1; } - virtual void flush() { }; - // Print methods - virtual size_t write(uint8_t c) { str += (char)c; }; - -private: - String str; - int length; - int position; + static int getNumOpenWiFiConnections(); + static int checkForAutoDisconnections(); }; -#endif // _STRING_STREAM_H_INCLUDED_ - diff --git a/zimodem/wificlientnode.ino b/zimodem/wificlientnode.ino index bbda583..224df37 100644 --- a/zimodem/wificlientnode.ino +++ b/zimodem/wificlientnode.ino @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 Bo Zimmerman + Copyright 2016-2019 Bo Zimmerman Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -36,41 +36,30 @@ WiFiClientNode::WiFiClientNode(char *hostIp, int newport, int flagsBitmap) strcpy(host,hostIp); id=++WiFiNextClientId; this->flagsBitmap = flagsBitmap; - /* - if((flagsBitmap&FLAG_SECURE)==FLAG_SECURE) - clientPtr = new WiFiClientSecure(); - else - */ - clientPtr = new WiFiClient(); + clientPtr = createWiFiClient((flagsBitmap&FLAG_SECURE)==FLAG_SECURE); client = *clientPtr; - client.setNoDelay(DEFAULT_NO_DELAY); setCharArray(&delimiters,""); setCharArray(&maskOuts,""); + setCharArray(&stateMachine,""); + machineState = stateMachine; if(!client.connect(hostIp, port)) { // deleted when it returns and is deleted } else { - /* - if((flagsBitmap&FLAG_SECURE)==FLAG_SECURE) - { - const char* fingerprint = "CF 05 98 89 CA FF 8E D8 5E 5C E0 C2 E4 F7 E6 C3 C7 50 DD 5C"; - if (((WiFiClientSecure *)clientPtr)->verify(fingerprint, hostIp)) - { - Serial.println("certificate matches"); - } - else - { - Serial.println("certificate doesn't match"); - } - } - */ + client.setNoDelay(DEFAULT_NO_DELAY); finishConnectionLink(); } } - -WiFiClientNode::WiFiClientNode(WiFiClient newClient, int flagsBitmap) + +void WiFiClientNode:: setNoDelay(bool tf) +{ + if(clientPtr != 0) + clientPtr->setNoDelay(tf); +} + +WiFiClientNode::WiFiClientNode(WiFiClient newClient, int flagsBitmap, int ringDelay) { this->flagsBitmap = flagsBitmap; clientPtr=null; @@ -78,19 +67,28 @@ WiFiClientNode::WiFiClientNode(WiFiClient newClient, int flagsBitmap) port=newClient.localPort(); setCharArray(&delimiters,""); setCharArray(&maskOuts,""); + setCharArray(&stateMachine,""); + machineState = stateMachine; String remoteIPStr = newClient.remoteIP().toString(); const char *remoteIP=remoteIPStr.c_str(); host=new char[remoteIPStr.length()+1]; strcpy(host,remoteIP); id=++WiFiNextClientId; client = newClient; + answered=(ringDelay == 0); + if(ringDelay > 0) + { + ringsRemain = ringDelay; + nextRingMillis = millis() + 3000; + } finishConnectionLink(); serverClient=true; } WiFiClientNode::~WiFiClientNode() { - lastPacketLen=0; + lastPacket[0].len=0; + lastPacket[1].len=0; if(host!=null) { client.stop(); @@ -116,8 +114,11 @@ WiFiClientNode::~WiFiClientNode() commandMode.current = conns; if(commandMode.nextConn == this) commandMode.nextConn = conns; + //underflowBuf.len = 0; freeCharArray(&delimiters); freeCharArray(&maskOuts); + freeCharArray(&stateMachine); + machineState = NULL; next=null; checkOpenConnections(); } @@ -137,9 +138,13 @@ bool WiFiClientNode::isEcho() return (flagsBitmap & FLAG_ECHO) == FLAG_ECHO; } -bool WiFiClientNode::isXonXoff() +FlowControlType WiFiClientNode::getFlowControl() { - return (flagsBitmap & FLAG_XONXOFF) == FLAG_XONXOFF; + if((flagsBitmap & FLAG_RTSCTS) == FLAG_RTSCTS) + return FCT_RTSCTS; + if((flagsBitmap & FLAG_XONXOFF) == FLAG_XONXOFF) + return FCT_NORMAL; + return FCT_DISABLED; } bool WiFiClientNode::isTelnet() @@ -152,23 +157,69 @@ bool WiFiClientNode::isDisconnectedOnStreamExit() return (flagsBitmap & FLAG_DISCONNECT_ON_EXIT) == FLAG_DISCONNECT_ON_EXIT; } +void WiFiClientNode::setDisconnectOnStreamExit(bool tf) +{ + if(tf) + flagsBitmap = flagsBitmap | FLAG_DISCONNECT_ON_EXIT; + else + flagsBitmap = flagsBitmap & ~FLAG_DISCONNECT_ON_EXIT; +} + +void WiFiClientNode::fillUnderflowBuf() +{ + /* + //underflow buf is deprecated + int newAvail = client.available(); + if(newAvail > 0) + { + int maxBufAvail = PACKET_BUF_SIZE-underflowBuf.len; + if(newAvail>maxBufAvail) + newAvail=maxBufAvail; + if(newAvail > 0) + underflowBuf.len += client.read(underflowBuf.buf+underflowBuf.len, newAvail); + } + */ +} + int WiFiClientNode::read() { - if(host == null) + if((host == null)||(!answered)) return 0; - return client.read(); + /* + // underflow buf is no longer needed + if(underflowBuf.len > 0) + { + int b = underflowBuf.buf[0]; + memcpy(underflowBuf.buf,underflowBuf.buf+1,--underflowBuf.len); + return b; + } + */ + int c = client.read(); + //fillUnderflowBuf(); + return c; } int WiFiClientNode::peek() { - if(host == null) + if((host == null)||(!answered)) return 0; + /* + // underflow buf is no longer needed + if(underflowBuf.len > 0) + return underflowBuf.buf[0]; + */ return client.peek(); } void WiFiClientNode::flush() { if((host != null)&&(client.available()==0)) + flushAlways(); +} + +void WiFiClientNode::flushAlways() +{ + if(host != null) { flushOverflowBuffer(); client.flush(); @@ -177,20 +228,53 @@ void WiFiClientNode::flush() int WiFiClientNode::available() { - if(host == null) + if((host == null)||(!answered)) return 0; - return client.available(); + return client.available(); // +underflowBuf.len; } int WiFiClientNode::read(uint8_t *buf, size_t size) { - if(host == null) + if((host == null)||(!answered)) return 0; - return client.read(buf,size); + // this whole underflow buf len thing is to get around yet another + // problem in the underlying library where a socket disconnection + // eats away any stray available bytes in their buffers. + /* + // this was changed to be handled a different, so underBuf is also deprecated; + int previouslyRead = 0; + if(underflowBuf.len > 0) + { + if(underflowBuf.len <= size) + { + previouslyRead += underflowBuf.len; + memcpy(buf,underflowBuf.buf,underflowBuf.len); + size -= underflowBuf.len; + underflowBuf.len = 0; + buf += previouslyRead; + } + else + { + memcpy(buf,underflowBuf.buf,size); + underflowBuf.len -= size; + memcpy(underflowBuf.buf,underflowBuf.buf+size,underflowBuf.len); + return size; + } + } + if(size == 0) + return previouslyRead; + */ + + int bytesRead = client.read(buf,size); + //fillUnderflowBuf(); + return bytesRead;// + previouslyRead; } int WiFiClientNode::flushOverflowBuffer() { + /* + * I've never gotten any of this to trigger, and could use those + * extra 260 bytes per connection if(overflowBufLen > 0) { // because overflowBuf is not a const char* for some reason @@ -204,7 +288,7 @@ int WiFiClientNode::flushOverflowBuffer() if(bufWrite >= overflowBufLen) { overflowBufLen = 0; - digitalWrite(pinRTS,rtsActive); + s_pinWrite(pinRTS,rtsActive); // fall-through } else @@ -215,23 +299,27 @@ int WiFiClientNode::flushOverflowBuffer() overflowBuf[i-bufWrite]=overflowBuf[i]; overflowBufLen -= bufWrite; } - digitalWrite(pinRTS,rtsInactive); + s_pinWrite(pinRTS,rtsInactive); return bufWrite; } } + */ return 0; } size_t WiFiClientNode::write(const uint8_t *buf, size_t size) { + int written = 0; + /* overflow buf is pretty much useless now if(host == null) { if(overflowBufLen>0) - digitalWrite(pinRTS,rtsActive); + { + s_pinWrite(pinRTS,rtsActive); + } overflowBufLen=0; return 0; } - int written = 0; written += flushOverflowBuffer(); if(written > 0) { @@ -239,141 +327,121 @@ size_t WiFiClientNode::write(const uint8_t *buf, size_t size) overflowBuf[overflowBufLen]=buf[i]; return written; } + */ written += client.write(buf, size); + /* if(written < size) { - for(int i=written;i0))) { - PhoneBookEntry *last = phonebook; - while(last->next != null) - last = last->next; - last->next = this; + yield(); + if(available()>0) + { + char c=read(); + if((c=='\n')||(c=='\r')) + { + if(line.length()>0) + return line; + } + else + if((c >= 32 ) && (c <= 127)) + line += c; + } } + return line; } -PhoneBookEntry::~PhoneBookEntry() +void WiFiClientNode::answer() { - if(phonebook == this) - phonebook = next; - else - { - PhoneBookEntry *last = phonebook; - while((last != null) && (last->next != this)) // don't change this! - last = last->next; - if(last != null) - last->next = next; - } - freeCharArray((char **)&address); - freeCharArray((char **)&modifiers); - next=null; + answered=true; + ringsRemain=0; + nextRingMillis=0; } -void PhoneBookEntry::loadPhonebook() +bool WiFiClientNode::isAnswered() { - clearPhonebook(); - if(SPIFFS.exists("/zphonebook.txt")) - { - File f = SPIFFS.open("/zphonebook.txt", "r"); - while(f.available()>0) - { - String str=""; - char c=f.read(); - while((c != '\n') && (f.available()>0)) - { - str += c; - c=f.read(); - } - int argn=0; - String configArguments[3]; - for(int i=0;i<3;i++) - configArguments[i]=""; - for(int i=0;inumber,phb->address,phb->modifiers); - phb = phb->next; - ct++; + WiFiClientNode *chkConn = conn; + conn = conn->next; + if((chkConn->nextDisconnect != 0) + &&(millis() > chkConn->nextDisconnect)) + delete(chkConn); + else + if((chkConn->isConnected() + ||(chkConn->available()>0) + ||((chkConn == conns) + &&((serialOutBufferBytesRemaining() isAnswered()) + num++; } - f.close(); - delay(500); - if(SPIFFS.exists("/zphonebook.txt")) + return num; +} + +void WiFiClientNode::markForDisconnect() +{ + if(nextDisconnect == 0) + nextDisconnect = millis() + 5000; // 5 sec +} + +bool WiFiClientNode::isMarkedForDisconnect() +{ + return nextDisconnect != 0; +} + +int WiFiClientNode::checkForAutoDisconnections() +{ + WiFiClientNode *conn = conns; + while(conn != null) { - File f = SPIFFS.open("/zphonebook.txt", "r"); - while(f.available()>0) - { - String str=""; - char c=f.read(); - while((c != '\n') && (f.available()>0)) - { - str += c; - c=f.read(); - } - int argn=0; - if(str.length()>0) - { - for(int i=0;inext; + if((chkConn->nextDisconnect != 0) + &&(millis() > chkConn->nextDisconnect)) + delete(chkConn); } } - - diff --git a/zimodem/wifiservernode.h b/zimodem/wifiservernode.h index 9eab36e..b332e91 100644 --- a/zimodem/wifiservernode.h +++ b/zimodem/wifiservernode.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 Bo Zimmerman + Copyright 2016-2019 Bo Zimmerman Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ static int WiFiNextClientId = 0; -class WiFiServerNode +class WiFiServerSpec { public: int port; @@ -24,11 +24,29 @@ class WiFiServerNode int flagsBitmap = 0; char *delimiters = NULL; char *maskOuts = NULL; + char *stateMachine = NULL; + + WiFiServerSpec(); + WiFiServerSpec(WiFiServerSpec ©); + ~WiFiServerSpec(); + + WiFiServerSpec& operator=(const WiFiServerSpec&); +}; + +class WiFiServerNode : public WiFiServerSpec +{ + public: WiFiServer *server; WiFiServerNode *next = null; WiFiServerNode(int port, int flagsBitmap); bool hasClient(); ~WiFiServerNode(); + + static WiFiServerNode *FindServer(int port); + static void DestroyAllServers(); + static bool ReadWiFiServer(File &f, WiFiServerSpec &node); + static void SaveWiFiServers(); + static void RestoreWiFiServers(); }; diff --git a/zimodem/wifiservernode.ino b/zimodem/wifiservernode.ino index 0dfa8da..925c8c0 100644 --- a/zimodem/wifiservernode.ino +++ b/zimodem/wifiservernode.ino @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 Bo Zimmerman + Copyright 2016-2019 Bo Zimmerman Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,12 +14,46 @@ limitations under the License. */ +WiFiServerSpec::WiFiServerSpec() +{ + setCharArray(&delimiters,""); + setCharArray(&maskOuts,""); + setCharArray(&stateMachine,""); +} + +WiFiServerSpec::~WiFiServerSpec() +{ + freeCharArray(&delimiters); + freeCharArray(&maskOuts); + freeCharArray(&stateMachine); +} + +WiFiServerSpec::WiFiServerSpec(WiFiServerSpec ©) +{ + port=copy.port; + flagsBitmap = copy.flagsBitmap; + setCharArray(&delimiters,copy.delimiters); + setCharArray(&maskOuts,copy.maskOuts); + setCharArray(&stateMachine,copy.stateMachine); +} + +WiFiServerSpec& WiFiServerSpec::operator=(const WiFiServerSpec ©) +{ + if(this != ©) + { + port=copy.port; + flagsBitmap = copy.flagsBitmap; + setCharArray(&delimiters,copy.delimiters); + setCharArray(&maskOuts,copy.maskOuts); + setCharArray(&stateMachine,copy.stateMachine); + } + return *this; +} + WiFiServerNode::WiFiServerNode(int newport, int flagsBitmap) { id=++WiFiNextClientId; port=newport; - setCharArray(&delimiters,""); - setCharArray(&maskOuts,""); this->flagsBitmap = flagsBitmap; server = new WiFiServer(newport); //BZ:server->setNoDelay(DEFAULT_NO_DELAY); @@ -53,8 +87,6 @@ WiFiServerNode::~WiFiServerNode() if(last != null) last->next = next; } - freeCharArray(&delimiters); - freeCharArray(&maskOuts); } bool WiFiServerNode::hasClient() @@ -63,4 +95,196 @@ bool WiFiServerNode::hasClient() return server->hasClient(); return false; } + +bool WiFiServerNode::ReadWiFiServer(File &f, WiFiServerSpec &node) +{ + if(f.available()>0) + { + String str=""; + char c=f.read(); + while((c != ',') && (f.available()>0)) + { + str += c; + c=f.read(); + } + if(str.length()==0) + return false; + node.port = atoi(str.c_str()); + str = ""; + c=f.read(); + while((c != '\n') && (f.available()>0)) + { + str += c; + c=f.read(); + } + if(str.length()==0) + return false; + node.flagsBitmap = atoi(str.c_str()); + str = ""; + c=f.read(); + while((c != ',') && (f.available()>0)) + { + str += c; + c=f.read(); + } + if(str.length()==0) + return false; + int chars=atoi(str.c_str()); + str = ""; + for(int i=0;i0;i++) + { + c=f.read(); + str += c; + } + setCharArray(&node.maskOuts,str.c_str()); + if(f.available()<=0 || f.read()!='\n') + return false; + str = ""; + c=f.read(); + while((c != ',') && (f.available()>0)) + { + str += c; + c=f.read(); + } + if(str.length()==0) + return false; + chars=atoi(str.c_str()); + str = ""; + for(int i=0;i0;i++) + { + c=f.read(); + str += c; + } + setCharArray(&node.delimiters,str.c_str()); + if(f.available()<=0 || f.read()!='\n') + return true; + str = ""; + c=f.read(); + while((c != ',') && (f.available()>0)) + { + str += c; + c=f.read(); + } + if(str.length()==0) + return false; + chars=atoi(str.c_str()); + str = ""; + for(int i=0;i0;i++) + { + str += c; + c=f.read(); + } + setCharArray(&node.stateMachine,str.c_str()); + } + return true; +} + +void WiFiServerNode::SaveWiFiServers() +{ + SPIFFS.remove("/zlisteners.txt"); + delay(500); + File f = SPIFFS.open("/zlisteners.txt", "w"); + int ct=0; + WiFiServerNode *serv = servs; + while(serv != null) + { + f.printf("%d,%d\n",serv->port,serv->flagsBitmap); + if(serv->maskOuts == NULL) + f.printf("0,\n"); + else + f.printf("%d,%s\n",strlen(serv->maskOuts),serv->maskOuts); + if(serv->delimiters == NULL) + f.printf("0,\n"); + else + f.printf("%d,%s\n",strlen(serv->delimiters),serv->delimiters); + if(serv->stateMachine == NULL) + f.printf("0,\n"); + else + f.printf("%d,%s\n",strlen(serv->stateMachine),serv->stateMachine); + ct++; + serv=serv->next; + } + f.close(); + delay(500); + if(SPIFFS.exists("/zlisteners.txt")) + { + File f = SPIFFS.open("/zlisteners.txt", "r"); + bool fail=false; + while(f.available()>5) + { + WiFiServerSpec snode; + if(!ReadWiFiServer(f,snode)) + { + fail=true; + break; + } + } + f.close(); + if(fail) + { + delay(100); + SaveWiFiServers(); + } + } +} + +void WiFiServerNode::DestroyAllServers() +{ + while(servs != null) + { + WiFiServerNode *s=servs; + delete s; + } +} + +WiFiServerNode *WiFiServerNode::FindServer(int port) +{ + WiFiServerNode *s=servs; + while(s != null) + { + if(s->port == port) + return s; + s=s->next; + } + return null; +} + + +void WiFiServerNode::RestoreWiFiServers() +{ + if(SPIFFS.exists("/zlisteners.txt")) + { + File f = SPIFFS.open("/zlisteners.txt", "r"); + bool fail=false; + while(f.available()>0) + { + WiFiServerSpec snode; + if(!ReadWiFiServer(f,snode)) + { + debugPrintf("Server: FAIL\n"); + fail=true; + break; + } + WiFiServerNode *s=servs; + while(s != null) + { + if(s->port == snode.port) + break; + s=s->next; + } + if(s==null) + { + WiFiServerNode *node = new WiFiServerNode(snode.port, snode.flagsBitmap); + setCharArray(&node->delimiters,snode.delimiters); + setCharArray(&node->maskOuts,snode.maskOuts); + setCharArray(&node->stateMachine,snode.stateMachine); + debugPrintf("Server: %d, %d: '%s' '%s'\n",node->port,node->flagsBitmap,node->delimiters,node->maskOuts); + } + else + debugPrintf("Server: DUP %d\n",snode.port); + } + f.close(); + } +} + diff --git a/zimodem/zbrowser.h b/zimodem/zbrowser.h new file mode 100644 index 0000000..e512014 --- /dev/null +++ b/zimodem/zbrowser.h @@ -0,0 +1,61 @@ +/* + Copyright 2016-2019 Bo Zimmerman + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifdef INCLUDE_SD_SHELL +class ZBrowser : public ZMode +{ + private: + enum ZBrowseState + { + ZBROW_MAIN=0, + } currState; + + ZSerial serial; + + void switchBackToCommandMode(); + String makePath(String addendum); + String fixPathNoSlash(String path); + String stripDir(String path); + String stripFilename(String path); + String stripArgs(String line, String &argLetters); + String cleanOneArg(String line); + String cleanFirstArg(String line); + String cleanRemainArg(String line); + bool isMask(String mask); + bool matches(String fname, String mask); + void makeFileList(String ***l, int *n, String p, String mask, bool recurse); + void deleteFile(String fname, String mask, bool recurse); + void showDirectory(String path, String mask, String prefix, bool recurse); + void copyFiles(String source, String mask, String target, bool recurse, bool overwrite); + + FTPHost *ftpHost = 0; + bool showMenu; + bool savedEcho; + String path="/"; + String EOLN; + char EOLNC[5]; + unsigned long lastNumber; + String lastString; + + public: + ~ZBrowser(); + void switchTo(); + void serialIncoming(); + void loop(); + void init(); + void doModeCommand(String &line); +}; +#endif diff --git a/zimodem/zbrowser.ino b/zimodem/zbrowser.ino new file mode 100644 index 0000000..1a84927 --- /dev/null +++ b/zimodem/zbrowser.ino @@ -0,0 +1,1179 @@ +/* + Copyright 2016-2019 Bo Zimmerman + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifdef INCLUDE_SD_SHELL + +static void initSDShell() +{ + if(SD.begin()) + { + if(SD.cardType() != CARD_NONE) + browseEnabled = true; + } +} + +ZBrowser::~ZBrowser() +{ + if(ftpHost != 0) + { + delete ftpHost; + ftpHost = 0; + } +} + +void ZBrowser::switchTo() +{ + currMode=&browseMode; + init(); +} + +void ZBrowser::init() +{ + serial.setFlowControlType(commandMode.serial.getFlowControlType()); + serial.setPetsciiMode(commandMode.serial.isPetsciiMode()); + savedEcho=commandMode.doEcho; + commandMode.doEcho=true; + serial.setXON(true); + showMenu=true; + EOLN=commandMode.EOLN; + strcpy(EOLNC,commandMode.EOLN.c_str()); + currState = ZBROW_MAIN; + lastNumber=0; + lastString=""; +} + +void ZBrowser::serialIncoming() +{ + bool crReceived=commandMode.readSerialStream(); + commandMode.clearPlusProgress(); // re-check the plus-escape mode + if(crReceived) + { + if(commandMode.doEcho) + serial.prints(EOLN); + String line =commandMode.getNextSerialCommand(); + doModeCommand(line); + } +} + +void ZBrowser::switchBackToCommandMode() +{ + commandMode.doEcho=savedEcho; + currMode = &commandMode; +} + +String ZBrowser::fixPathNoSlash(String p) +{ + String finalPath=""; + int lastX=0; + uint16_t backStack[256] = {0}; + backStack[0]=1; + int backX=1; + for(int i=0;ilastX) + { + String sub=p.substring(lastX,i); + if(sub.equals(".")) + { + // do nothing + } + else + if(sub.equals("..")) + { + if(backX > 1) + finalPath = finalPath.substring(0,backStack[--backX]); + } + else + if(sub.length()>0) + { + finalPath += sub; + finalPath += "/"; + backStack[++backX]=finalPath.length(); + } + } + else + if((i==0) && (i 1) + finalPath = finalPath.substring(0,backStack[--backX]); + } + else + { + finalPath += sub; + finalPath += "/"; // why this?! -- oh, so it can be removed below? + } + } + if(finalPath.length()==0) + return "/"; + if(finalPath.length()>1) + finalPath.remove(finalPath.length()-1); + return finalPath; +} + +String ZBrowser::stripDir(String p) +{ + int x=p.lastIndexOf("/"); + if(x<=0) + return "/"; + return p.substring(0,x); +} + +String ZBrowser::stripFilename(String p) +{ + int x=p.lastIndexOf("/"); + if((x<0)||(x==p.length()-1)) + return ""; + return p.substring(x+1); +} + +String ZBrowser::cleanFirstArg(String line) +{ + int state=0; + String arg=""; + for(int i=0;i0) + { + if(addendum.startsWith("/")) + return fixPathNoSlash(addendum); + else + return fixPathNoSlash(path + addendum); + } + return fixPathNoSlash(path); +} + +bool ZBrowser::isMask(String mask) +{ + return (mask.indexOf("*")>=0) || (mask.indexOf("?")>=0); +} + +String ZBrowser::stripArgs(String line, String &argLetters) +{ + while(line.startsWith("-")) + { + int x=line.indexOf(' '); + if(x<0) + { + argLetters = line.substring(1); + return ""; + } + argLetters += line.substring(1,x); + line = line.substring(x+1); + line.trim(); + } + return line; +} + +void ZBrowser::copyFiles(String source, String mask, String target, bool recurse, bool overwrite) +{ + int maskFilterLen = source.length(); + if(!source.endsWith("/")) + maskFilterLen++; + + File root = SD.open(source); + if(!root) + { + serial.printf("Unknown path: %s%s",source.c_str(),EOLNC); + return; + } + + if(root.isDirectory()) + { + if(!SD.exists(target)) // cp d a + { + SD.mkdir(target); + } + else + { + File DD=SD.open(target); //cp d d2, cp d f + if(!DD.isDirectory()) + { + serial.printf("File exists: %s%s",DD.name(),EOLNC); + DD.close(); + return; + } + } + for(File file = root.openNextFile(); file != null; file = root.openNextFile()) + { + if(matches(file.name()+maskFilterLen, mask)) + { + debugPrintf("file matched:%s\n",file.name()); + String tpath = target; + if(file.isDirectory()) + { + if(!recurse) + serial.printf("Skipping: %s%s",file.name(),EOLNC); + else + { + if(!tpath.endsWith("/")) + tpath += "/"; + tpath += stripFilename(file.name()); + } + } + copyFiles(file.name(),"",tpath,false,overwrite); + } + } + } + else + { + String tpath = target; + if(SD.exists(tpath)) + { + File DD=SD.open(tpath); + if(DD.isDirectory()) // cp f d, cp f . + { + if(!tpath.endsWith("/")) + tpath += "/"; + tpath += stripFilename(root.name()); + debugPrintf("file xform to file in dir:%s\n",tpath.c_str()); + } + DD.close(); + } + if(SD.exists(tpath)) + { + File DD=SD.open(tpath); + if(strcmp(DD.name(),root.name())==0) + { + serial.printf("File exists: %s%s",DD.name(),EOLNC); + DD.close(); + return; + } + else + if(!overwrite) // cp f a, cp f e + { + serial.printf("File exists: %s%s",DD.name(),EOLNC); + DD.close(); + return; + } + else + { + DD.close(); + SD.remove(tpath); + } + } + size_t len=root.size(); + File tfile = SD.open(tpath,FILE_WRITE); + for(int i=0;i=fname.length()) + return false; + if(mask[i]=='?') + f++; + else + if(mask[i]=='*') + { + if(i==mask.length()-1) + return true; + int remain=mask.length()-i-1; + f=fname.length()-remain; + } + else + if(mask[i]!=fname[f++]) + return false; + } + return true; +} + +void ZBrowser::showDirectory(String p, String mask, String prefix, bool recurse) +{ + int maskFilterLen = p.length(); + if(!p.endsWith("/")) + maskFilterLen++; + + File root = SD.open(p); + if(!root) + serial.printf("Unknown path: %s%s",p.c_str(),EOLNC); + else + if(root.isDirectory()) + { + File file = root.openNextFile(); + while(file) + { + if(matches(file.name()+maskFilterLen, mask)) + { + debugPrintf("file matched:%s\n",file.name()); + if(file.isDirectory()) + { + serial.printf("%sd %s%s",prefix.c_str(),file.name()+maskFilterLen,EOLNC); + if(recurse) + { + String newPrefix = prefix + " "; + showDirectory(file.name(), mask, newPrefix, recurse); + } + } + else + serial.printf("%s %s %lu%s",prefix.c_str(),file.name()+maskFilterLen,file.size(),EOLNC); + } + else + debugPrintf("file unmatched:%s (%s)\n",file.name(),mask.c_str()); + file = root.openNextFile(); + } + } + else + serial.printf(" %s %lu%s",root.name(),root.size(),EOLNC); +} + +void ZBrowser::doModeCommand(String &line) +{ + char c='?'; + for(int i=0;i 0) + { + cmd=line.substring(0,sp); + cmd.trim(); + line = line.substring(sp+1); + line.trim(); + } + else + line = ""; + switch(currState) + { + case ZBROW_MAIN: + { + if(cmd.length()==0) + showMenu=true; + else + if(cmd.equalsIgnoreCase("exit")||cmd.equalsIgnoreCase("quit")||cmd.equalsIgnoreCase("x")||cmd.equalsIgnoreCase("endshell")) + { + serial.prints("OK"); + serial.prints(EOLN); + //commandMode.showInitMessage(); + switchBackToCommandMode(); + return; + } + else + if(cmd.equalsIgnoreCase("ls")||cmd.equalsIgnoreCase("dir")||cmd.equalsIgnoreCase("$")||cmd.equalsIgnoreCase("list")) + { + String argLetters = ""; + line = stripArgs(line,argLetters); + argLetters.toLowerCase(); + bool recurse=argLetters.indexOf('r')>=0; + String rawPath = makePath(cleanOneArg(line)); + String p; + String mask; + if((line.length()==0)||(line.endsWith("/"))) + { + p=rawPath; + mask=""; + } + else + { + mask=stripFilename(rawPath); + if((mask.length()>0)&&(isMask(mask))) + p=stripDir(rawPath); + else + { + mask=""; + p=rawPath; + } + } + showDirectory(p,mask,"",recurse); + } + else + if(cmd.equalsIgnoreCase("md")||cmd.equalsIgnoreCase("mkdir")||cmd.equalsIgnoreCase("makedir")) + { + String p = makePath(cleanOneArg(line)); + debugPrintf("md:%s\n",p.c_str()); + if((p.length() < 2) || isMask(p) || !SD.mkdir(p)) + serial.printf("Illegal path: %s%s",p.c_str(),EOLNC); + } + else + if(cmd.equalsIgnoreCase("cd")) + { + String p = makePath(cleanOneArg(line)); + debugPrintf("cd:%s\n",p.c_str()); + if(p.length()==0) + serial.printf("Current path: %s%s",p.c_str(),EOLNC); + else + if(p == "/") + path = "/"; + else + if(p.length()>1) + { + File root = SD.open(p); + if(!root) + serial.printf("Unknown path: %s%s",p.c_str(),EOLNC); + else + if(!root.isDirectory()) + serial.printf("Illegal path: %s%s",p.c_str(),EOLNC); + else + path = p + "/"; + } + } + else + if(cmd.equalsIgnoreCase("rd")||cmd.equalsIgnoreCase("rmdir")||cmd.equalsIgnoreCase("deletedir")) + { + String p = makePath(cleanOneArg(line)); + debugPrintf("rd:%s\n",p.c_str()); + File root = SD.open(p); + if(!root) + serial.printf("Unknown path: %s%s",p.c_str(),EOLNC); + else + if(!root.isDirectory()) + serial.printf("Not a directory: %s%s",p.c_str(),EOLNC); + else + if(!SD.rmdir(p)) + serial.printf("Failed to remove directory: %s%s",p.c_str(),EOLNC); + } + else + if(cmd.equalsIgnoreCase("cat")||cmd.equalsIgnoreCase("type")) + { + String p = makePath(cleanOneArg(line)); + debugPrintf("cat:%s\n",p.c_str()); + File root = SD.open(p); + if(!root) + serial.printf("Unknown path: %s%s",p.c_str(),EOLNC); + else + if(root.isDirectory()) + serial.printf("Is a directory: %s%s",p.c_str(),EOLNC); + else + { + root.close(); + File f=SD.open(p, FILE_READ); + for(int i=0;i=0; + String rawPath = makePath(cleanOneArg(line)); + String p=stripDir(rawPath); + String mask=stripFilename(rawPath); + debugPrintf("rm:%s (%s)\n",p.c_str(),mask.c_str()); + deleteFile(p,mask,recurse); + } + else + if(cmd.equalsIgnoreCase("cp")||cmd.equalsIgnoreCase("copy")) + { + String argLetters = ""; + line = stripArgs(line,argLetters); + argLetters.toLowerCase(); + bool recurse=argLetters.indexOf('r')>=0; + bool overwrite=argLetters.indexOf('f')>=0; + String p1=makePath(cleanFirstArg(line)); + String p2=makePath(cleanRemainArg(line)); + String mask; + if((line.length()==0)||(line.endsWith("/"))) + mask=""; + else + { + mask=stripFilename(p1); + if(!isMask(mask)) + mask=""; + else + p1=stripDir(p1); + } + debugPrintf("cp:%s (%s) -> %s\n",p1.c_str(),mask.c_str(), p2.c_str()); + copyFiles(p1,mask,p2,recurse,overwrite); + } + else + if(cmd.equalsIgnoreCase("df")||cmd.equalsIgnoreCase("free")||cmd.equalsIgnoreCase("info")) + { + serial.printf("%llu free of %llu total%s",(SD.totalBytes()-SD.usedBytes()),SD.totalBytes(),EOLNC); + } + else + if(cmd.equalsIgnoreCase("ren")||cmd.equalsIgnoreCase("rename")) + { + + String p1=makePath(cleanFirstArg(line)); + String p2=makePath(cleanRemainArg(line)); + debugPrintf("ren:%s -> %s\n",p1.c_str(), p2.c_str()); + if(p1 == p2) + serial.printf("File exists: %s%s",p1.c_str(),EOLNC); + else + if(SD.exists(p2)) + serial.printf("File exists: %s%s",p2.c_str(),EOLNC); + else + { + if(!SD.rename(p1,p2)) + serial.printf("Failed to rename: %s%s",p1.c_str(),EOLNC); + } + } + else + if(cmd.equalsIgnoreCase("wget")) + { + String p1=cleanFirstArg(line); + String p2=makePath(cleanRemainArg(line)); + debugPrintf("wget:%s -> %s\n",p1.c_str(), p2.c_str()); + if((p1.length()<8) + || ((strcmp(p1.substring(0,7).c_str(),"http://") != 0) + && (strcmp(p1.substring(0,9).c_str(),"https://") != 0))) + serial.printf("Not a url: %s%s",p1.c_str(),EOLNC); + else + if(SD.exists(p2)) + serial.printf("File exists: %s%s",p2.c_str(),EOLNC); + else + { + char buf[p1.length()+1]; + strcpy(buf,p1.c_str()); + char *hostIp; + char *req; + int port; + bool doSSL; + if(!parseWebUrl((uint8_t *)buf,&hostIp,&req,&port,&doSSL)) + serial.printf("Invalid url: %s",p1.c_str()); + else + if(!doWebGet(hostIp, port, &SD, p2.c_str(), req, doSSL)) + serial.printf("Wget failed: %s to file %s",p1.c_str(),p2.c_str()); + } + } + else + if(cmd.equalsIgnoreCase("fget")) + { + String p1=cleanFirstArg(line); + String p2=cleanRemainArg(line); + if(p2.length() == 0) + { + int slash = p1.lastIndexOf('/'); + if(slash < p1.length()-1) + p2 = makePath(p1.substring(slash+1)); + else + p2 = makePath(p1); + } + else + p2=makePath(p2); + debugPrintf("fget:%s -> %s\n",p1.c_str(), p2.c_str()); + char *tmp=0; + bool isUrl = ((p1.length()>=11) + && ((strcmp(p1.substring(0,6).c_str(),"ftp://") == 0) + || (strcmp(p1.substring(0,7).c_str(),"ftps://") == 0))); + if((ftpHost==0)&&(!isUrl)) + serial.printf("Url required: %s%s",p1.c_str(),EOLNC); + else + if(SD.exists(p2)) + serial.printf("File exists: %s%s",p2.c_str(),EOLNC); + else + { + uint8_t buf[p1.length()+1]; + strcpy((char *)buf,p1.c_str()); + char *req; + ftpHost = makeFTPHost(isUrl,ftpHost,buf,&req); + if(req == 0) + serial.printf("Invalid url: %s",p1.c_str()); + else + if(!ftpHost->doGet(&SD, p2.c_str(), req)) + serial.printf("Fget failed: %s to file %s",p1.c_str(),p2.c_str()); + } + } + else + if(cmd.equalsIgnoreCase("fput")) + { + String p1=makePath(cleanFirstArg(line)); + String p2=cleanRemainArg(line); + debugPrintf("fput:%s -> %s\n",p1.c_str(), p2.c_str()); + char *tmp=0; + bool isUrl = ((p2.length()>=11) + && ((strcmp(p2.substring(0,6).c_str(),"ftp://") == 0) + || (strcmp(p2.substring(0,7).c_str(),"ftps://") == 0))); + if((ftpHost==0)&&(!isUrl)) + serial.printf("Url required: %s%s",p2.c_str(),EOLNC); + else + if(!SD.exists(p1)) + serial.printf("File not found: %s%s",p1.c_str(),EOLNC); + else + { + uint8_t buf[p2.length()+1]; + strcpy((char *)buf,p2.c_str()); + File file = SD.open(p1); + char *req; + ftpHost = makeFTPHost(isUrl,ftpHost,buf,&req); + if(req == 0) + serial.printf("Invalid url: %s",p2.c_str()); + else + if(!file) + serial.printf("File not found: %s%s",p1.c_str(),EOLNC); + else + { + if(!ftpHost->doPut(file, req)) + serial.printf("Fput failed: %s from file %s",p2.c_str(),p1.c_str()); + file.close(); + } + } + } + else + if(cmd.equalsIgnoreCase("fls") || cmd.equalsIgnoreCase("fdir")) + { + String p1=cleanOneArg(line); + debugPrintf("fls:%s\n",p1.c_str()); + char *tmp=0; + bool isUrl = ((p1.length()>=11) + && ((strcmp(p1.substring(0,6).c_str(),"ftp://") == 0) + || (strcmp(p1.substring(0,7).c_str(),"ftps://") == 0))); + if((ftpHost==0)&&(!isUrl)) + serial.printf("Url required: %s%s",p1.c_str(),EOLNC); + else + { + uint8_t buf[p1.length()+1]; + strcpy((char *)buf,p1.c_str()); + char *req; + ftpHost = makeFTPHost(isUrl,ftpHost,buf,&req); + if(req == 0) + serial.printf("Invalid url: %s",p1.c_str()); + else + if(!ftpHost->doLS(&serial, req)) + serial.printf("Fls failed: %s",p1.c_str()); + } + } + else + if(cmd.equalsIgnoreCase("mv")||cmd.equalsIgnoreCase("move")) + { + String argLetters = ""; + line = stripArgs(line,argLetters); + argLetters.toLowerCase(); + bool overwrite=argLetters.indexOf('f')>=0; + String p1=makePath(cleanFirstArg(line)); + String p2=makePath(cleanRemainArg(line)); + String mask; + if((line.length()==0)||(line.endsWith("/"))) + mask=""; + else + { + mask=stripFilename(p1); + if((mask.length()>0)&&(isMask(mask))) + p1=stripDir(p1); + else + mask = ""; + } + debugPrintf("mv:%s(%s) -> %s\n",p1.c_str(),mask.c_str(),p2.c_str()); + if((mask.length()==0)||(!isMask(mask))) + { + File root = SD.open(p2); + if(root && root.isDirectory()) + { + if (!p2.endsWith("/")) + p2 += "/"; + p2 += stripFilename(p1); + debugPrintf("mv:%s -> %s\n",p1.c_str(),p2.c_str()); + } + root.close(); + if(p1 == p2) + serial.printf("File exists: %s%s",p1.c_str(),EOLNC); + else + if(SD.exists(p2) && (!overwrite)) + serial.printf("File exists: %s%s",p2.c_str(),EOLNC); + else + { + if(SD.exists(p2)) + SD.remove(p2); + if(!SD.rename(p1,p2)) + serial.printf("Failed to move: %s%s",p1.c_str(),EOLNC); + } + } + else + { + copyFiles(p1,mask,p2,false,overwrite); + deleteFile(p1,mask,false); + } + } + else + if(cmd.equals("?")||cmd.equals("help")) + { + serial.printf("Commands:%s",EOLNC); + serial.printf("ls/dir/list/$ [-r] [/][path] - List files%s",EOLNC); + serial.printf("cd [/][path][..] - Change to new directory%s",EOLNC); + serial.printf("md/mkdir/makedir [/][path] - Create a new directory%s",EOLNC); + serial.printf("rd/rmdir/deletedir [/][path] - Delete a directory%s",EOLNC); + serial.printf("rm/del/delete [-r] [/][path]filename - Delete a file%s",EOLNC); + serial.printf("cp/copy [-r] [-f] [/][path]file [/][path]file - Copy file(s)%s",EOLNC); + serial.printf("ren/rename [/][path]file [/][path]file - Rename a file%s",EOLNC); + serial.printf("mv/move [-f] [/][path]file [/][path]file - Move file(s)%s",EOLNC); + serial.printf("cat/type [/][path]filename - View a file(s)%s",EOLNC); + serial.printf("df/free/info - Show space remaining%s",EOLNC); + serial.printf("xget/zget/kget [/][path]filename - Download a file%s",EOLNC); + serial.printf("xput/zput/kput [/][path]filename - Upload a file%s",EOLNC); + serial.printf("wget [http://url] [/][path]filename - Download url to file%s",EOLNC); + serial.printf("fget [ftp://user:pass@url/file] [/][path]file - FTP get file%s",EOLNC); + serial.printf("fput [/][path]file [ftp://user:pass@url/file] - FTP put file%s",EOLNC); + serial.printf("fdir [ftp://user:pass@url/path] - ftp url dir%s",EOLNC); + serial.printf("exit/quit/x/endshell - Quit to command mode%s",EOLNC); + serial.printf("%s",EOLNC); + } + else + serial.printf("Unknown command: '%s'. Try '?'.%s",cmd.c_str(),EOLNC); + showMenu=true; + break; + } + } +} + +void ZBrowser::loop() +{ + if(showMenu) + { + showMenu=false; + switch(currState) + { + case ZBROW_MAIN: + { + serial.printf("%s%s> ",EOLNC,path.c_str()); + break; + } + } + } + if(commandMode.checkPlusEscape()) + { + switchBackToCommandMode(); + } + else + if(serial.isSerialOut()) + { + serialOutDeque(); + } +} +#else +static void initSDShell() +{} +#endif diff --git a/zimodem/zcommand.h b/zimodem/zcommand.h index e28a45e..2bc4cc9 100644 --- a/zimodem/zcommand.h +++ b/zimodem/zcommand.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 Bo Zimmerman + Copyright 2016-2019 Bo Zimmerman Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,6 +15,11 @@ */ const int MAX_COMMAND_SIZE=256; +static const char *CONFIG_FILE_OLD = "/zconfig.txt"; +static const char *CONFIG_FILE = "/zconfig_v2.txt"; +#define ZI_STATE_MACHINE_LEN 7 +#define DEFAULT_TERMTYPE "Zimodem" +#define DEFAULT_BUSYMSG "\r\nBUSY\r\n7\r\n" enum ZResult { @@ -22,6 +27,7 @@ enum ZResult ZERROR, ZCONNECT, ZNOCARRIER, + ZNOANSWER, ZIGNORE, ZIGNORE_SPECIAL }; @@ -45,36 +51,60 @@ enum ConfigOptions CFG_DCDPIN=14, CFG_CTSPIN=15, CFG_RTSPIN=16, - CFG_LAST=16 + CFG_S0_RINGS=17, + CFG_S41_STREAM=18, + CFG_S60_LISTEN=19, + CFG_RIMODE=20, + CFG_DTRMODE=21, + CFG_DSRMODE=22, + CFG_RIPIN=23, + CFG_DTRPIN=24, + CFG_DSRPIN=25, + CFG_TIMEZONE=26, + CFG_TIMEFMT=27, + CFG_TIMEURL=28, + CFG_HOSTNAME=29, + CFG_PRINTDELAYMS=30, + CFG_PRINTSPEC=31, + CFG_TERMTYPE=32, + CFG_STATIC_IP=33, + CFG_STATIC_DNS=34, + CFG_STATIC_GW=35, + CFG_STATIC_SN=36, + CFG_BUSYMSG=37, + CFG_S62_TELNET=38, + CFG_LAST=38 }; -enum StreamFlag -{ - FLAG_DISCONNECT_ON_EXIT=1, - FLAG_PETSCII=2, - FLAG_TELNET=4, - FLAG_ECHO=8, - FLAG_XONXOFF=16, - FLAG_SECURE=32 -}; +const ConfigOptions v2HexCfgs[] = { CFG_WIFISSI, CFG_WIFIPW, CFG_TIMEZONE, CFG_TIMEFMT, CFG_TIMEURL, + CFG_PRINTSPEC, CFG_BUSYMSG, CFG_HOSTNAME, CFG_TERMTYPE, (ConfigOptions)255 }; enum BinType { BTYPE_NORMAL=0, BTYPE_HEX=1, BTYPE_DEC=2, - BTYPE_INVALID=3 + BTYPE_NORMAL_NOCHK=3, + BTYPE_NORMAL_PLUS=4, + BTYPE_HEX_PLUS=5, + BTYPE_DEC_PLUS=6, + BTYPE_INVALID=7 }; class ZCommand : public ZMode { friend class WiFiClientNode; + friend class ZConfig; + friend class ZBrowser; +#ifdef INCLUDE_IRCC + friend class ZIRCMode; +#endif private: - char *CRLF="\r\n"; - char *LFCR="\r\n"; - char *LF="\n"; - char *CR="\r"; + char CRLF[4]; + char LFCR[4]; + char LF[2]; + char CR[2]; char BS=8; char ringCounter = 1; @@ -87,35 +117,45 @@ class ZCommand : public ZMode int lastServerClientId = 0; WiFiClientNode *current = null; bool autoStreamMode=false; + bool telnetSupport=true; + bool preserveListeners=false; unsigned long lastNonPlusTimeMs = 0; unsigned long currentExpiresTimeMs = 0; char *tempDelimiters = NULL; char *tempMaskOuts = NULL; + char *tempStateMachine = NULL; char *delimiters = NULL; char *maskOuts = NULL; + char *stateMachine = NULL; + char *machineState = NULL; + String machineQue = ""; String previousCommand = ""; - String currentCommand = ""; WiFiClientNode *nextConn=null; int lastPacketId = -1; byte CRC8(const byte *data, byte len); - int makeStreamFlagsBitmap(const char *dmodifiers); void showInitMessage(); bool readSerialStream(); + bool clearPlusProgress(); + bool checkPlusEscape(); + String getNextSerialCommand(); ZResult doSerialCommand(); void setConfigDefaults(); void parseConfigOptions(String configArguments[]); - void setBaseConfigOptions(String configArguments[]); + void setOptionsFromSavedConfig(String configArguments[]); void reSaveConfig(); - void reSendLastPacket(WiFiClientNode *conn); + void reSendLastPacket(WiFiClientNode *conn, uint8_t which); void acceptNewConnection(); - void headerOut(const int channel, const int sz, const int crc8); + void headerOut(const int channel, const int num, const int sz, const int crc8); void sendConnectionNotice(int nodeId); void sendNextPacket(); - bool doWebGet(const char *hostIp, int port, const char *filename, const char *req); - bool doWebGetBytes(const char *hostIp, int port, const char *req, uint8_t *buf, int *bufSize); - bool doWebGetStream(const char *hostIp, int port, const char *req, WiFiClient &c, uint32_t *responseSize); + void connectionArgs(WiFiClientNode *c); + void updateAutoAnswer(); + uint8_t *doStateMachine(uint8_t *buf, uint16_t *bufLen, char **machineState, String *machineQue, char *stateMachine); + uint8_t *doMaskOuts(uint8_t *buf, uint16_t *bufLen, char *maskOuts); + ZResult doWebDump(Stream *in, int len, const bool cacheFlag); + ZResult doWebDump(const char *filename, const bool cache); ZResult doResetCommand(); ZResult doNoListenCommand(); @@ -132,6 +172,7 @@ class ZCommand : public ZMode ZResult doInfoCommand(int vval, uint8_t *vbuf, int vlen, bool isNumber); ZResult doWebStream(int vval, uint8_t *vbuf, int vlen, bool isNumber, const char *filename, bool cache); ZResult doUpdateFirmware(int vval, uint8_t *vbuf, int vlen, bool isNumber); + ZResult doTimeZoneSetupCommand(int vval, uint8_t *vbuf, int vlen, bool isNumber); public: int packetSize = 127; @@ -141,12 +182,18 @@ class ZCommand : public ZMode boolean doEcho; String EOLN; char EC='+'; - char *ECS="+++"; + char ECS[32]; ZCommand(); void loadConfig(); + + FlowControlType getFlowControlType(); + int getConfigFlagBitmap(); + + void sendOfficialResponse(ZResult res); void serialIncoming(); void loop(); + void reset(); }; diff --git a/zimodem/zcommand.ino b/zimodem/zcommand.ino index 569156e..0b36654 100644 --- a/zimodem/zcommand.ino +++ b/zimodem/zcommand.ino @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 Bo Zimmerman + Copyright 2016-2019 Bo Zimmerman Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,20 +18,36 @@ extern "C" void esp_yield(); ZCommand::ZCommand() { + strcpy(CRLF,"\r\n"); + strcpy(LFCR,"\n\r"); + strcpy(LF,"\n"); + strcpy(CR,"\r"); + strcpy(ECS,"+++"); freeCharArray(&tempMaskOuts); freeCharArray(&tempDelimiters); + freeCharArray(&tempStateMachine); setCharArray(&delimiters,""); setCharArray(&maskOuts,""); + setCharArray(&stateMachine,""); + machineState = stateMachine; +} + +void ZCommand::reset() +{ + doResetCommand(); + showInitMessage(); } + byte ZCommand::CRC8(const byte *data, byte len) { byte crc = 0x00; - logPrint("CRC8: "); + //logPrint("CRC8: "); int c=0; while (len--) { byte extract = *data++; + /* save for later debugging if(logFileOpen) { logFile.print(TOHEX(extract)); @@ -43,6 +59,7 @@ byte ZCommand::CRC8(const byte *data, byte len) else logFile.print(" "); } + */ for (byte tempI = 8; tempI; tempI--) { byte sum = (crc ^ extract) & 0x01; @@ -57,45 +74,59 @@ byte ZCommand::CRC8(const byte *data, byte len) return crc; } -int ZCommand::makeStreamFlagsBitmap(const char *dmodifiers) -{ - int flagsBitmap = 0; - if((strchr(dmodifiers,'p')!=null) || (strchr(dmodifiers,'P')!=null)) - flagsBitmap = flagsBitmap | FLAG_PETSCII; - if((strchr(dmodifiers,'t')!=null) || (strchr(dmodifiers,'T')!=null)) - flagsBitmap = flagsBitmap | FLAG_TELNET; - if((strchr(dmodifiers,'e')!=null) || (strchr(dmodifiers,'E')!=null)) - flagsBitmap = flagsBitmap | FLAG_ECHO; - if((strchr(dmodifiers,'x')!=null) || (strchr(dmodifiers,'X')!=null)) - flagsBitmap = flagsBitmap | FLAG_XONXOFF; - if((strchr(dmodifiers,'s')!=null) || (strchr(dmodifiers,'S')!=null)) - flagsBitmap = flagsBitmap | FLAG_SECURE; - return flagsBitmap; -} - void ZCommand::setConfigDefaults() { doEcho=true; - serial.setFlowControlType(FCT_RTSCTS); + autoStreamMode=false; + telnetSupport=true; + preserveListeners=false; + ringCounter=1; + serial.setFlowControlType(DEFAULT_FCT); serial.setXON(true); packetXOn = true; serial.setPetsciiMode(false); binType=BTYPE_NORMAL; serialDelayMs=0; dcdActive=DEFAULT_DCD_HIGH; + dcdInactive=DEFAULT_DCD_LOW; +# ifdef HARD_DCD_HIGH dcdInactive=DEFAULT_DCD_HIGH; +# elif defined(HARD_DCD_LOW) + dcdActive=DEFAULT_DCD_LOW; +# endif ctsActive=DEFAULT_CTS_HIGH; ctsInactive=DEFAULT_CTS_LOW; rtsActive=DEFAULT_RTS_HIGH; rtsInactive=DEFAULT_RTS_LOW; + riActive = DEFAULT_RTS_HIGH; + riInactive = DEFAULT_RTS_LOW; + dtrActive = DEFAULT_RTS_HIGH; + dtrInactive = DEFAULT_RTS_LOW; + dsrActive = DEFAULT_RTS_HIGH; + dsrInactive = DEFAULT_RTS_LOW; pinDCD = DEFAULT_PIN_DCD; pinCTS = getDefaultCtsPin(); pinRTS = DEFAULT_PIN_RTS; - pinMode(pinRTS,OUTPUT); - pinMode(pinCTS,INPUT); - pinMode(pinDCD,OUTPUT); - digitalWrite(pinRTS,rtsActive); - digitalWrite(2,dcdStatus); + pinDTR = DEFAULT_PIN_DTR; + pinDSR = DEFAULT_PIN_DSR; + pinRI = DEFAULT_PIN_RI; + dcdStatus = dcdInactive; + if(pinSupport[pinRTS]) + pinMode(pinRTS,OUTPUT); + if(pinSupport[pinCTS]) + pinMode(pinCTS,INPUT); + if(pinSupport[pinDCD]) + pinMode(pinDCD,OUTPUT); + if(pinSupport[pinDTR]) + pinMode(pinDTR,INPUT); + if(pinSupport[pinDSR]) + pinMode(pinDSR,OUTPUT); + if(pinSupport[pinRI]) + pinMode(pinRI,OUTPUT); + s_pinWrite(pinRTS,rtsActive); + s_pinWrite(pinDCD,dcdStatus); + s_pinWrite(pinDSR,dsrActive); + s_pinWrite(pinRI,riInactive); suppressResponses=false; numericResponses=false; longResponses=true; @@ -111,8 +142,20 @@ void ZCommand::setConfigDefaults() tempBaud = -1; freeCharArray(&tempMaskOuts); freeCharArray(&tempDelimiters); + freeCharArray(&tempStateMachine); setCharArray(&delimiters,""); - setCharArray(&maskOuts,""); + setCharArray(&stateMachine,""); + machineState = stateMachine; + termType = DEFAULT_TERMTYPE; + busyMsg = DEFAULT_BUSYMSG; +#ifdef SUPPORT_LED_PINS + if(pinSupport[DEFAULT_PIN_AA]) + pinMode(DEFAULT_PIN_AA,OUTPUT); + if(pinSupport[DEFAULT_PIN_WIFI]) + pinMode(DEFAULT_PIN_WIFI,OUTPUT); + if(pinSupport[DEFAULT_PIN_HS]) + pinMode(DEFAULT_PIN_HS,OUTPUT); +#endif } char lc(char c) @@ -124,6 +167,17 @@ char lc(char c) return c; } +void ZCommand::connectionArgs(WiFiClientNode *c) +{ + setCharArray(&(c->delimiters),tempDelimiters); + setCharArray(&(c->maskOuts),tempMaskOuts); + setCharArray(&(c->stateMachine),tempStateMachine); + freeCharArray(&tempDelimiters); + freeCharArray(&tempMaskOuts); + freeCharArray(&tempStateMachine); + c->machineState = c->stateMachine; +} + ZResult ZCommand::doResetCommand() { while(conns != null) @@ -133,11 +187,7 @@ ZResult ZCommand::doResetCommand() } current = null; nextConn = null; - while(servs != null) - { - WiFiServerNode *s=servs; - delete s; - } + WiFiServerNode::DestroyAllServers(); setConfigDefaults(); String argv[CFG_LAST+1]; parseConfigOptions(argv); @@ -147,8 +197,8 @@ ZResult ZCommand::doResetCommand() serial.setPetsciiMode(false); serialDelayMs=0; binType=BTYPE_NORMAL; - serial.setFlowControlType(FCT_RTSCTS); - setBaseConfigOptions(argv); + serial.setFlowControlType(DEFAULT_FCT); + setOptionsFromSavedConfig(argv); memset(nbuf,0,MAX_COMMAND_SIZE); return ZOK; } @@ -165,38 +215,130 @@ ZResult ZCommand::doNoListenCommand() c=c2; } */ - while(servs != null) + WiFiServerNode::DestroyAllServers(); + return ZOK; +} + +FlowControlType ZCommand::getFlowControlType() +{ + return serial.getFlowControlType(); +} + +int pinModeCoder(int activeChk, int inactiveChk, int activeDefault) +{ + if(activeChk == activeDefault) { - WiFiServerNode *s=servs; - delete s; + if(inactiveChk == activeDefault) + return 2; + return 0; + } + else + { + if(inactiveChk != activeDefault) + return 3; + return 1; + } +} + +void pinModeDecoder(int mode, int *active, int *inactive, int activeDef, int inactiveDef) +{ + switch(mode) + { + case 0: + *active = activeDef; + *inactive = inactiveDef; + break; + case 1: + *inactive = activeDef; + *active = inactiveDef; + break; + case 2: + *active = activeDef; + *inactive = activeDef; + break; + case 3: + *active = inactiveDef; + *inactive = inactiveDef; + break; + default: + *active = activeDef; + *inactive = inactiveDef; + break; + } +} + +void pinModeDecoder(String newMode, int *active, int *inactive, int activeDef, int inactiveDef) +{ + if(newMode.length()>0) + { + int mode = atoi(newMode.c_str()); + pinModeDecoder(mode,active,inactive,activeDef,inactiveDef); } - return ZOK; } void ZCommand::reSaveConfig() { - SPIFFS.remove("/zconfig.txt"); + char hex[256]; + SPIFFS.remove(CONFIG_FILE_OLD); + SPIFFS.remove(CONFIG_FILE); delay(500); - File f = SPIFFS.open("/zconfig.txt", "w"); const char *eoln = EOLN.c_str(); - int dcdMode = (dcdActive == DEFAULT_DCD_HIGH) ? 0 : 1; - int ctsMode = (ctsActive == DEFAULT_CTS_HIGH) ? 0 : 1; - int rtsMode = (rtsActive == DEFAULT_RTS_HIGH) ? 0 : 1; - f.printf("%s,%s,%d,%s,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", - wifiSSI.c_str(), wifiPW.c_str(), baudRate, eoln, + int dcdMode = pinModeCoder(dcdActive, dcdInactive, DEFAULT_DCD_HIGH); + int ctsMode = pinModeCoder(ctsActive, ctsInactive, DEFAULT_CTS_HIGH); + int rtsMode = pinModeCoder(rtsActive, rtsInactive, DEFAULT_RTS_HIGH); + int riMode = pinModeCoder(riActive, riInactive, DEFAULT_RTS_HIGH); + int dtrMode = pinModeCoder(dtrActive, dtrInactive, DEFAULT_DTR_HIGH); + int dsrMode = pinModeCoder(dsrActive, dsrInactive, DEFAULT_DSR_HIGH); + String wifiSSIhex = TOHEX(wifiSSI.c_str(),hex,256); + String wifiPWhex = TOHEX(wifiPW.c_str(),hex,256); + String zclockFormathex = TOHEX(zclock.getFormat().c_str(),hex,256); + String zclockHosthex = TOHEX(zclock.getNtpServerHost().c_str(),hex,256); + String hostnamehex = TOHEX(hostname.c_str(),hex,256); + String printSpechex = TOHEX(printMode.getLastPrinterSpec(),hex,256); + String termTypehex = TOHEX(termType.c_str(),hex,256); + String busyMsghex = TOHEX(busyMsg.c_str(),hex,256); + String staticIPstr; + ConnSettings::IPtoStr(staticIP,staticIPstr); + String staticDNSstr; + ConnSettings::IPtoStr(staticDNS,staticDNSstr); + String staticGWstr; + ConnSettings::IPtoStr(staticGW,staticGWstr); + String staticSNstr; + ConnSettings::IPtoStr(staticSN,staticSNstr); + + File f = SPIFFS.open(CONFIG_FILE, "w"); + f.printf("%s,%s,%d,%s," + "%d,%d,%d,%d," + "%d,%d,%d,%d,%d," + "%d,%d,%d,%d,%d,%d,%d," + "%d,%d,%d,%d,%d,%d," + "%d," + "%s,%s,%s," + "%d,%s,%s," + "%s,%s,%s,%s," + "%s,%d", + wifiSSIhex.c_str(), wifiPWhex.c_str(), baudRate, eoln, serial.getFlowControlType(), doEcho, suppressResponses, numericResponses, longResponses, serial.isPetsciiMode(), dcdMode, serialConfig, ctsMode, - rtsMode,pinDCD,pinCTS,pinRTS); + rtsMode,pinDCD,pinCTS,pinRTS,autoStreamMode,ringCounter,preserveListeners, + riMode,dtrMode,dsrMode,pinRI,pinDTR,pinDSR, + zclock.isDisabled()?999:zclock.getTimeZoneCode(), + zclockFormathex.c_str(),zclockHosthex.c_str(),hostnamehex.c_str(), + printMode.getTimeoutDelayMs(),printSpechex.c_str(),termTypehex.c_str(), + staticIPstr.c_str(),staticDNSstr.c_str(),staticGWstr.c_str(),staticSNstr.c_str(), + busyMsghex.c_str(),telnetSupport + ); f.close(); delay(500); - if(SPIFFS.exists("/zconfig.txt")) + if(SPIFFS.exists(CONFIG_FILE)) { - File f = SPIFFS.open("/zconfig.txt", "r"); + File f=SPIFFS.open(CONFIG_FILE, "r"); String str=f.readString(); f.close(); int argn=0; if((str!=null)&&(str.length()>0)) { + debugPrintf("Saved Config: %s\n",str.c_str()); for(int i=0;i0) { @@ -240,99 +387,138 @@ void ZCommand::setBaseConfigOptions(String configArguments[]) longResponses = atoi(configArguments[CFG_RESP_LONG].c_str()); if(configArguments[CFG_PETSCIIMODE].length()>0) serial.setPetsciiMode(atoi(configArguments[CFG_PETSCIIMODE].c_str())); - if(configArguments[CFG_DCDMODE].length()>0) - { - int dcdMode = atoi(configArguments[CFG_DCDMODE].c_str()); - if(dcdMode == 1) - { - dcdActive=LOW; - dcdInactive=HIGH; - } - else - { - dcdActive=HIGH; - dcdInactive=LOW; - } - } - if(configArguments[CFG_CTSMODE].length()>0) - { - int cstMode = atoi(configArguments[CFG_CTSMODE].c_str()); - if(cstMode == 1) - { - ctsActive=LOW; - ctsInactive=HIGH; - } - else - { - ctsActive=HIGH; - ctsInactive=LOW; - } - } - if(configArguments[CFG_RTSMODE].length()>0) - { - int rstMode = atoi(configArguments[CFG_RTSMODE].c_str()); - if(rstMode == 1) - { - rtsActive=LOW; - rtsInactive=HIGH; - } - else - { - rtsActive=HIGH; - rtsInactive=LOW; - } - } + pinModeDecoder(configArguments[CFG_DCDMODE],&dcdActive,&dcdInactive,DEFAULT_DCD_HIGH,DEFAULT_DCD_LOW); + pinModeDecoder(configArguments[CFG_CTSMODE],&ctsActive,&ctsInactive,DEFAULT_CTS_HIGH,DEFAULT_CTS_LOW); + pinModeDecoder(configArguments[CFG_RTSMODE],&rtsActive,&rtsInactive,DEFAULT_RTS_HIGH,DEFAULT_RTS_LOW); + pinModeDecoder(configArguments[CFG_RIMODE],&riActive,&riInactive,DEFAULT_RI_HIGH,DEFAULT_RI_LOW); + pinModeDecoder(configArguments[CFG_DTRMODE],&dtrActive,&dtrInactive,DEFAULT_DTR_HIGH,DEFAULT_DTR_LOW); + pinModeDecoder(configArguments[CFG_DSRMODE],&dsrActive,&dsrInactive,DEFAULT_DSR_HIGH,DEFAULT_DSR_LOW); if(configArguments[CFG_DCDPIN].length()>0) { pinDCD = atoi(configArguments[CFG_DCDPIN].c_str()); - pinMode(pinDCD,OUTPUT); + if(pinSupport[pinDCD]) + pinMode(pinDCD,OUTPUT); dcdStatus=dcdInactive; - digitalWrite(2,dcdStatus); } + s_pinWrite(pinDCD,dcdStatus); if(configArguments[CFG_CTSPIN].length()>0) { pinCTS = atoi(configArguments[CFG_CTSPIN].c_str()); - pinMode(pinCTS,INPUT); + if(pinSupport[pinCTS]) + pinMode(pinCTS,INPUT); } if(configArguments[CFG_RTSPIN].length()>0) { pinRTS = atoi(configArguments[CFG_RTSPIN].c_str()); - pinMode(pinRTS,OUTPUT); - digitalWrite(pinRTS,rtsActive); - } + if(pinSupport[pinRTS]) + pinMode(pinRTS,OUTPUT); + } + s_pinWrite(pinRTS,rtsActive); +#ifdef ZIMODEM_ESP32 + serial.setFlowControlType(serial.getFlowControlType()); +#endif + if(configArguments[CFG_RIPIN].length()>0) + { + pinRI = atoi(configArguments[CFG_RIPIN].c_str()); + if(pinSupport[pinRI]) + pinMode(pinRI,OUTPUT); + } + s_pinWrite(pinRI,riInactive); + if(configArguments[CFG_DTRPIN].length()>0) + { + pinDTR = atoi(configArguments[CFG_DTRPIN].c_str()); + if(pinSupport[pinDTR]) + pinMode(pinDTR,INPUT); + } + if(configArguments[CFG_DSRPIN].length()>0) + { + pinDSR = atoi(configArguments[CFG_DSRPIN].c_str()); + if(pinSupport[pinDSR]) + pinMode(pinDSR,OUTPUT); + } + s_pinWrite(pinDSR,dsrActive); + if(configArguments[CFG_S0_RINGS].length()>0) + ringCounter = atoi(configArguments[CFG_S0_RINGS].c_str()); + if(configArguments[CFG_S41_STREAM].length()>0) + autoStreamMode = atoi(configArguments[CFG_S41_STREAM].c_str()); + if(configArguments[CFG_S62_TELNET].length()>0) + telnetSupport = atoi(configArguments[CFG_S62_TELNET].c_str()); + if(configArguments[CFG_S60_LISTEN].length()>0) + { + preserveListeners = atoi(configArguments[CFG_S60_LISTEN].c_str()); + if(preserveListeners) + WiFiServerNode::RestoreWiFiServers(); + } + if(configArguments[CFG_TIMEZONE].length()>0) + { + int tzCode = atoi(configArguments[CFG_TIMEZONE].c_str()); + if(tzCode > 500) + zclock.setDisabled(true); + else + { + zclock.setDisabled(false); + zclock.setTimeZoneCode(tzCode); + } + } + if((!zclock.isDisabled())&&(configArguments[CFG_TIMEFMT].length()>0)) + zclock.setFormat(configArguments[CFG_TIMEFMT]); + if((!zclock.isDisabled())&&(configArguments[CFG_TIMEURL].length()>0)) + zclock.setNtpServerHost(configArguments[CFG_TIMEURL]); + if((!zclock.isDisabled()) && (WiFi.status() == WL_CONNECTED)) + zclock.forceUpdate(); + //if(configArguments[CFG_PRINTDELAYMS].length()>0) // since you can't change it, what's the point? + // printMode.setTimeoutDelayMs(atoi(configArguments[CFG_PRINTDELAYMS].c_str())); + printMode.setLastPrinterSpec(configArguments[CFG_PRINTSPEC].c_str()); + if(configArguments[CFG_TERMTYPE].length()>0) + termType = configArguments[CFG_TERMTYPE]; + if(configArguments[CFG_BUSYMSG].length()>0) + busyMsg = configArguments[CFG_BUSYMSG]; + updateAutoAnswer(); } void ZCommand::parseConfigOptions(String configArguments[]) { delay(500); - File f = SPIFFS.open("/zconfig.txt", "r"); + bool v2=SPIFFS.exists(CONFIG_FILE); + File f; + if(v2) + f = SPIFFS.open(CONFIG_FILE, "r"); + else + f = SPIFFS.open(CONFIG_FILE_OLD, "r"); String str=f.readString(); f.close(); if((str!=null)&&(str.length()>0)) { + debugPrintf("Read Config: %s\n",str.c_str()); int argn=0; for(int i=0;i0) @@ -343,15 +529,28 @@ void ZCommand::loadConfig() serialConfig = (SerialConfig)atoi(argv[CFG_UART].c_str()); if(serialConfig <= 0) serialConfig = DEFAULT_SERIAL_CONFIG; - Serial.begin(baudRate, serialConfig); //Start Serial + changeBaudRate(baudRate); + changeSerialConfig(serialConfig); wifiSSI=argv[CFG_WIFISSI]; wifiPW=argv[CFG_WIFIPW]; + hostname = argv[CFG_HOSTNAME]; + setNewStaticIPs( + ConnSettings::parseIP(argv[CFG_STATIC_IP].c_str()), + ConnSettings::parseIP(argv[CFG_STATIC_DNS].c_str()), + ConnSettings::parseIP(argv[CFG_STATIC_GW].c_str()), + ConnSettings::parseIP(argv[CFG_STATIC_SN].c_str())); if(wifiSSI.length()>0) { - connectWifi(wifiSSI.c_str(),wifiPW.c_str()); + debugPrintf("Connecting to %s\n",wifiSSI.c_str()); + connectWifi(wifiSSI.c_str(),wifiPW.c_str(),staticIP,staticDNS,staticGW,staticSN); + nextReconnectDelay = DEFAULT_RECONNECT_DELAY; + debugPrintf("Done attempting to connect to %s\n",wifiSSI.c_str()); } + debugPrintf("Reset start.\n"); doResetCommand(); + debugPrintf("Reset complete. Init start\n"); showInitMessage(); + debugPrintf("Init complete.\n"); } ZResult ZCommand::doInfoCommand(int vval, uint8_t *vbuf, int vlen, bool isNumber) @@ -361,7 +560,10 @@ ZResult ZCommand::doInfoCommand(int vval, uint8_t *vbuf, int vlen, bool isNumber showInitMessage(); } else - if((vval == 1)||(vval==5)) + switch(vval) + { + case 1: + case 5: { bool showAll = (vval==5); serial.prints("AT"); @@ -482,50 +684,110 @@ ZResult ZCommand::doInfoCommand(int vval, uint8_t *vbuf, int vlen, bool isNumber serial.prints("S45="); serial.printi(binType); } - if((dcdActive != DEFAULT_DCD_HIGH)||(showAll)) - serial.prints("S46=1"); - if((ctsActive != DEFAULT_CTS_HIGH)||(showAll)) - serial.prints("S47=1"); - if((rtsActive != DEFAULT_RTS_HIGH)||(showAll)) - serial.prints("S48=1"); + if((dcdActive != DEFAULT_DCD_HIGH)||(dcdInactive == DEFAULT_DCD_HIGH)||(showAll)) + serial.printf("S46=%d",pinModeCoder(dcdActive,dcdInactive,DEFAULT_DCD_HIGH)); if((pinDCD != DEFAULT_PIN_DCD)||(showAll)) - serial.prints("S49=1"); + serial.printf("S47=%d",pinDCD); + if((ctsActive != DEFAULT_CTS_HIGH)||(ctsInactive == DEFAULT_CTS_HIGH)||(showAll)) + serial.printf("S48=%d",pinModeCoder(ctsActive,ctsInactive,DEFAULT_CTS_HIGH)); if((pinCTS != getDefaultCtsPin())||(showAll)) - serial.prints("S50=1"); + serial.printf("S49=%d",pinCTS); + if((rtsActive != DEFAULT_RTS_HIGH)||(rtsInactive == DEFAULT_RTS_HIGH)||(showAll)) + serial.printf("S50=%d",pinModeCoder(rtsActive,rtsInactive,DEFAULT_RTS_HIGH)); if((pinRTS != DEFAULT_PIN_RTS)||(showAll)) - serial.prints("S51=1"); + serial.printf("S51=%d",pinRTS); + if((riActive != DEFAULT_RI_HIGH)||(riInactive == DEFAULT_RI_HIGH)||(showAll)) + serial.printf("S52=%d",pinModeCoder(riActive,riInactive,DEFAULT_RI_HIGH)); + if((pinRI != DEFAULT_PIN_RI)||(showAll)) + serial.printf("S53=%d",pinRI); + if((dtrActive != DEFAULT_DTR_HIGH)||(dtrInactive == DEFAULT_DTR_HIGH)||(showAll)) + serial.printf("S54=%d",pinModeCoder(dtrActive,dtrInactive,DEFAULT_DTR_HIGH)); + if((pinDTR != DEFAULT_PIN_DTR)||(showAll)) + serial.printf("S55=%d",pinDTR); + if((dsrActive != DEFAULT_DSR_HIGH)||(dsrInactive == DEFAULT_DSR_HIGH)||(showAll)) + serial.printf("S56=%d",pinModeCoder(dsrActive,dsrInactive,DEFAULT_DSR_HIGH)); + if((pinDSR != DEFAULT_PIN_DSR)||(showAll)) + serial.printf("S57=%d",pinDSR); + if(preserveListeners ||(showAll)) + serial.prints(preserveListeners ? "S60=1" : "S60=0"); + if(!telnetSupport ||(showAll)) + serial.prints(telnetSupport ? "S62=1" : "S62=0"); if((serial.isPetsciiMode())||(showAll)) serial.prints(serial.isPetsciiMode() ? "&P1" : "&P0"); - if(logFileOpen || showAll) - serial.prints(logFileOpen ? "&O1" : "&O0"); + if((logFileOpen) || showAll) + serial.prints((logFileOpen && !logFileDebug) ? "&O1" : ((logFileOpen && logFileDebug) ? "&O88" : "&O0")); serial.prints(EOLN); + break; } - else - if(vval == 2) + case 2: { serial.prints(WiFi.localIP().toString().c_str()); serial.prints(EOLN); + break; } - else - if(vval == 3) + case 3: { serial.prints(wifiSSI.c_str()); serial.prints(EOLN); + break; } - else - if(vval == 4) + case 4: { serial.prints(ZIMODEM_VERSION); serial.prints(EOLN); + break; } - else - if(vval == 6) + case 6: { serial.prints(WiFi.macAddress()); serial.prints(EOLN); + break; } - else + case 7: + { + serial.prints(zclock.getCurrentTimeFormatted()); + serial.prints(EOLN); + break; + } + case 8: + { + serial.prints(compile_date); + serial.prints(EOLN); + break; + } + case 9: + { + serial.prints(wifiSSI.c_str()); + serial.prints(EOLN); + if(staticIP != null) + { + String str; + ConnSettings::IPtoStr(staticIP,str); + serial.prints(str.c_str()); + serial.prints(EOLN); + ConnSettings::IPtoStr(staticDNS,str); + serial.prints(str.c_str()); + serial.prints(EOLN); + ConnSettings::IPtoStr(staticGW,str); + serial.prints(str.c_str()); + serial.prints(EOLN); + ConnSettings::IPtoStr(staticSN,str); + serial.prints(str.c_str()); + serial.prints(EOLN); + } + break; + } + case 10: + serial.printf("%s%s",printMode.getLastPrinterSpec(),EOLN.c_str()); + break; + case 11: + { + serial.printf("%d%s",ESP.getFreeHeap(),EOLN.c_str()); + break; + } + default: return ZERROR; + } return ZOK; } @@ -551,7 +813,7 @@ ZResult ZCommand::doBaudCommand(int vval, uint8_t *vbuf, int vlen) if(parPtr==NULL) return ZERROR; char parity=*parPtr; - int configChk=0; + uint32_t configChk=0; switch(conStr[0]) { case '5': @@ -594,10 +856,9 @@ ZResult ZCommand::doBaudCommand(int vval, uint8_t *vbuf, int vlen) { baudRate=vval; } - Serial.flush(); - Serial.begin(baudRate, serialConfig); - //if(!enableRtsCts) - // enableRtsCts=(digitalRead(pinCTS) == ctsActive); + hwSerialFlush(); + changeBaudRate(baudRate); + changeSerialConfig(serialConfig); return ZOK; } @@ -614,25 +875,40 @@ ZResult ZCommand::doConnectCommand(int vval, uint8_t *vbuf, int vlen, bool isNum { if(current->isConnected()) { + current->answer(); serial.prints("CONNECTED "); serial.printf("%d %s:%d",current->id,current->host,current->port); + serial.prints(EOLN); } else + if(current->isAnswered()) { serial.prints("NO CARRIER "); serial.printf("%d %s:%d",current->id,current->host,current->port); + serial.prints(EOLN); + serial.flush(); } - serial.prints(EOLN); return ZIGNORE; } } else if((vval >= 0)&&(isNumber)) { - if(vval == 0) - logPrintln("ConnList0:\r\n"); - else - logPrintfln("ConnSwitchTo: %d",vval); + if((WiFi.status() != WL_CONNECTED) + &&(vval== 1) + &&(conns==null)) + { + if(wifiSSI.length()==0) + return ZERROR; + debugPrintf("Connecting to %s\n",wifiSSI.c_str()); + bool doconn = connectWifi(wifiSSI.c_str(),wifiPW.c_str(),staticIP,staticDNS,staticGW,staticSN); + debugPrintf("Done attempting connect to %s\n",wifiSSI.c_str()); + return doconn ? ZOK : ZERROR; + } + if(vval == 0) + logPrintln("ConnList0:\r\n"); + else + logPrintfln("ConnSwitchTo: %d",vval); if(strlen(dmodifiers)>0) // would be nice to allow petscii/telnet changes here, but need more flags return ZERROR; WiFiClientNode *c=conns; @@ -643,10 +919,7 @@ ZResult ZCommand::doConnectCommand(int vval, uint8_t *vbuf, int vlen, bool isNum if((c!=null)&&(c->id == vval)) { current = c; - setCharArray(&(c->delimiters),tempDelimiters); - setCharArray(&(c->maskOuts),tempMaskOuts); - freeCharArray(&tempDelimiters); - freeCharArray(&tempMaskOuts); + connectionArgs(c); } else return ZERROR; @@ -658,15 +931,19 @@ ZResult ZCommand::doConnectCommand(int vval, uint8_t *vbuf, int vlen, bool isNum { if(c->isConnected()) { + c->answer(); serial.prints("CONNECTED "); serial.printf("%d %s:%d",c->id,c->host,c->port); + serial.prints(EOLN); } else + if(c->isAnswered()) { serial.prints("NO CARRIER "); serial.printf("%d %s:%d",c->id,c->host,c->port); + serial.prints(EOLN); + serial.flush(); } - serial.prints(EOLN); c=c->next; } WiFiServerNode *s=servs; @@ -689,294 +966,260 @@ ZResult ZCommand::doConnectCommand(int vval, uint8_t *vbuf, int vlen, bool isNum (*colon)=0; port=atoi((char *)(++colon)); } - int flagsBitmap = makeStreamFlagsBitmap(dmodifiers); + int flagsBitmap=0; + { + ConnSettings flags(dmodifiers); + flagsBitmap = flags.getBitmap(serial.getFlowControlType()); + } logPrintfln("Connnecting: %s %d %d",(char *)vbuf,port,flagsBitmap); WiFiClientNode *c = new WiFiClientNode((char *)vbuf,port,flagsBitmap); if(!c->isConnected()) { logPrintln("Connnect: FAIL"); delete c; - return ZERROR; + return ZNOANSWER; } else { logPrintfln("Connnect: SUCCESS: %d",c->id); current=c; - setCharArray(&(c->delimiters),tempDelimiters); - setCharArray(&(c->maskOuts),tempMaskOuts); - freeCharArray(&tempDelimiters); - freeCharArray(&tempMaskOuts); + connectionArgs(c); return ZCONNECT; } } return ZOK; } -void ZCommand::headerOut(const int channel, const int sz, const int crc8) +void ZCommand::headerOut(const int channel, const int num, const int sz, const int crc8) { switch(binType) { case BTYPE_NORMAL: sprintf(hbuf,"[ %d %d %d ]%s",channel,sz,crc8,EOLN.c_str()); break; + case BTYPE_NORMAL_PLUS: + sprintf(hbuf,"[ %d %d %d %d ]%s",channel,num,sz,crc8,EOLN.c_str()); + break; case BTYPE_HEX: - sprintf(hbuf,"[ %s %s %s ]%s",String(TOHEX(channel)).c_str(),String(TOHEX(sz)).c_str(),String(TOHEX(crc8)).c_str(),EOLN.c_str()); + sprintf(hbuf,"[ %s %s %s ]%s", + String(TOHEX(channel)).c_str(), + String(TOHEX(sz)).c_str(), + String(TOHEX(crc8)).c_str(),EOLN.c_str()); + break; + case BTYPE_HEX_PLUS: + sprintf(hbuf,"[ %s %s %s %s ]%s", + String(TOHEX(channel)).c_str(), + String(TOHEX(num)).c_str(), + String(TOHEX(sz)).c_str(), + String(TOHEX(crc8)).c_str(),EOLN.c_str()); break; case BTYPE_DEC: sprintf(hbuf,"[%s%d%s%d%s%d%s]%s",EOLN.c_str(),channel,EOLN.c_str(),sz,EOLN.c_str(),crc8,EOLN.c_str(),EOLN.c_str()); break; + case BTYPE_DEC_PLUS: + sprintf(hbuf,"[%s%d%s%d%s%d%s%d%s]%s",EOLN.c_str(), + channel,EOLN.c_str(), + num,EOLN.c_str(), + sz,EOLN.c_str(), + crc8,EOLN.c_str(), + EOLN.c_str()); + break; + case BTYPE_NORMAL_NOCHK: + sprintf(hbuf,"[ %d %d ]%s",channel,sz,EOLN.c_str()); + break; } serial.prints(hbuf); } -bool ZCommand::doWebGetStream(const char *hostIp, int port, const char *req, WiFiClient &c, uint32_t *responseSize) +ZResult ZCommand::doWebStream(int vval, uint8_t *vbuf, int vlen, bool isNumber, const char *filename, bool cache) { - *responseSize = 0; - if(WiFi.status() != WL_CONNECTED) - return false; - c.setNoDelay(DEFAULT_NO_DELAY); - if(!c.connect(hostIp, port)) - { - c.stop(); - return false; - } - c.printf("GET /%s HTTP/1.1\r\n",req); - c.printf("User-Agent: C64Net Firmware\r\n",hostIp); - c.printf("Host: %s\r\n",hostIp); - c.printf("Connection: close\r\n\r\n"); - String ln = ""; - uint32_t respLength = 0; - int respCode = -1; - while(c.connected()) + char *hostIp; + char *req; + int port; + bool doSSL; + if(!parseWebUrl(vbuf,&hostIp,&req,&port,&doSSL)) + return ZERROR; + + if(cache) { - yield(); - if(c.available()<=0) - continue; - - char ch = (char)c.read(); - logSocketIn(ch); - if(ch == '\r') - continue; - else - if(ch == '\n') + if(!SPIFFS.exists(filename)) { - if(ln.length()==0) - break; - if(respCode < 0) - { - int sp = ln.indexOf(' '); - if(sp<=0) - break; - ln.remove(0,sp+1); - sp = ln.indexOf(' '); - if(sp<=0) - break; - ln.remove(sp); - respCode = atoi(ln.c_str()); - } - else - if(ln.startsWith("Content-length: ") - ||ln.startsWith("Content-Length: ")) - { - ln.remove(0,16); - respLength = atoi(ln.c_str()); - } - ln = ""; + if(!doWebGet(hostIp, port, &SPIFFS, filename, req, doSSL)) + return ZERROR; } - else - ln.concat(ch); - } - *responseSize = respLength; - if((!c.connected()) - ||(respCode != 200) - ||(respLength <= 0)) - { - c.stop(); - return false; } - return true; -} - -bool ZCommand::doWebGet(const char *hostIp, int port, const char *filename, const char *req) -{ - uint32_t respLength=0; - WiFiClient c; - if(!doWebGetStream(hostIp, port, req, c, &respLength)) - return false; - - File f = SPIFFS.open(filename, "w"); - while((respLength>0) && (c.connected())) + else + if((binType == BTYPE_NORMAL_NOCHK) + &&(machineQue.length()==0)) { - if(c.available()>=0) + uint32_t respLength=0; + WiFiClient *c = doWebGetStream(hostIp, port, req, doSSL, &respLength); + if(c==null) { - uint8_t ch=c.read(); - logSocketIn(ch); - f.write(ch); - respLength--; + serial.prints(EOLN); + return ZERROR; } - else - yield(); + headerOut(0,1,respLength,0); + serial.flush(); // stupid important because otherwise apps that go xoff miss the header info + ZResult res = doWebDump(c,respLength,false); + c->stop(); + delete c; + serial.prints(EOLN); + return res; } - f.flush(); - f.close(); - c.stop(); - return (respLength == 0); + else + if(!doWebGet(hostIp, port, &SPIFFS, filename, req, doSSL)) + return ZERROR; + return doWebDump(filename, cache); } -bool ZCommand::doWebGetBytes(const char *hostIp, int port, const char *req, uint8_t *buf, int *bufSize) +ZResult ZCommand::doWebDump(Stream *in, int len, const bool cacheFlag) { - WiFiClient c; - uint32_t respLength=0; - if(!doWebGetStream(hostIp, port, req, c, &respLength)) - return false; - if((!c.connected()) - ||(respLength > *bufSize)) - { - c.stop(); - return false; - } - *bufSize = (int)respLength; - int index=0; - while((respLength>0) && (c.connected())) - { - if(c.available()>=0) - { - uint8_t ch=c.read(); - logSocketIn(ch); - buf[index++] = ch; - respLength--; + bool flowControl=!cacheFlag; + BinType streamType = cacheFlag?BTYPE_NORMAL:binType; + uint8_t *buf = (uint8_t *)malloc(1); + uint16_t bufLen = 1; + int bct=0; + unsigned long now = millis(); + while((len>0) + && ((millis()-now)<10000)) + { + if(((!flowControl) || serial.isSerialOut()) + &&(in->available()>0)) + { + now=millis(); + len--; + int c=in->read(); + if(c<0) + break; + buf[0] = (uint8_t)c; + bufLen = 1; + buf = doMaskOuts(buf,&bufLen,maskOuts); + buf = doStateMachine(buf,&bufLen,&machineState,&machineQue,stateMachine); + for(int i=0;i=39) + { + serial.prints(EOLN); + bct=0; + } + break; + } + case BTYPE_DEC: + case BTYPE_DEC_PLUS: + serial.printf("%d%s",c,EOLN.c_str()); + break; + } + } } - else + if(serial.isSerialOut()) + { + serialOutDeque(); yield(); + } + if(serial.drainForXonXoff()==3) + { + serial.setXON(true); + free(buf); + machineState = stateMachine; + return ZOK; + } + while(serial.availableForWrite()<5) + { + if(serial.isSerialOut()) + { + serialOutDeque(); + yield(); + } + if(serial.drainForXonXoff()==3) + { + serial.setXON(true); + free(buf); + machineState = stateMachine; + return ZOK; + } + delay(1); + } + yield(); } - c.stop(); - return (respLength == 0); + free(buf); + machineState = stateMachine; + if(bct > 0) + serial.prints(EOLN); } -ZResult ZCommand::doWebStream(int vval, uint8_t *vbuf, int vlen, bool isNumber, const char *filename, bool cache) +ZResult ZCommand::doWebDump(const char *filename, const bool cache) { - char *portB=strchr((char *)vbuf,':'); - bool success = true; - if(portB == NULL) - success = false; - else + machineState = stateMachine; + int chk8=0; + uint16_t bufLen = 1; + int len = 0; + { - char *hostIp = (char *)vbuf; - *portB = 0; - portB++; - char *req = strchr(portB,'/'); - if(req == NULL) - success = false; - else + File f = SPIFFS.open(filename, "r"); + int flen = f.size(); + if((binType != BTYPE_NORMAL_NOCHK) + &&(machineQue.length()==0)) { - *req = 0; - req++; - int port = atoi(portB); - if(port <=0) - success = false; - else + uint8_t *buf = (uint8_t *)malloc(1); + delay(100); + char *oldMachineState = machineState; + String oldMachineQue = machineQue; + for(int i=0;i255) chk8-=256; } - f.close(); - } - File f = SPIFFS.open(filename, "r"); - int len = f.size(); - if(!cache) - { - headerOut(0,len,chk8); - serial.flush(); // stupid important because otherwise apps that go xoff miss the header info } - bool flowControl=!cache; - BinType streamType = cache?BTYPE_NORMAL:binType; - int bct=0; - while(len>0) - { - if((!flowControl) || serial.isSerialOut()) - { - len--; - int c=f.read(); - if(c<0) - break; - if(cache && serial.isPetsciiMode()) - c=ascToPetcii(c); - switch(streamType) - { - case BTYPE_NORMAL: - serial.write((uint8_t)c); - break; - case BTYPE_HEX: - { - const char *hbuf = TOHEX((uint8_t)c); - serial.printb(hbuf[0]); // prevents petscii - serial.printb(hbuf[1]); - if((++bct)>=39) - { - serial.prints(EOLN); - bct=0; - } - break; - } - case BTYPE_DEC: - serial.printf("%d%s",c,EOLN.c_str()); - break; - } - } - if(serial.isSerialOut()) - { - serialOutDeque(); - yield(); - } - if(serial.drainForXonXoff()==3) - { - serial.setXON(true); - f.close(); - return ZOK; - } - while(serial.availableForWrite()<5) - { - if(serial.isSerialOut()) - { - serialOutDeque(); - yield(); - } - if(serial.drainForXonXoff()==3) - { - serial.setXON(true); - f.close(); - return ZOK; - } - delay(1); - } - yield(); - } - if(bct > 0) - serial.prints(EOLN); - f.close(); } + machineState = oldMachineState; + machineQue = oldMachineQue; + free(buf); } + else + len=flen; + f.close(); } - return ZIGNORE; + File f = SPIFFS.open(filename, "r"); + if(!cache) + { + headerOut(0,1,len,chk8); + serial.flush(); // stupid important because otherwise apps that go xoff miss the header info + } + len = f.size(); + ZResult res = doWebDump(&f, len, cache); + f.close(); + return res; } ZResult ZCommand::doUpdateFirmware(int vval, uint8_t *vbuf, int vlen, bool isNumber) @@ -985,10 +1228,24 @@ ZResult ZCommand::doUpdateFirmware(int vval, uint8_t *vbuf, int vlen, bool isNum serial.prints(ZIMODEM_VERSION); serial.prints("."); serial.prints(EOLN); - + uint8_t buf[255]; int bufSize = 254; - if((!doWebGetBytes("www.zimmers.net", 80, "/otherprojs/c64net-latest-version.txt", buf, &bufSize))||(bufSize<=0)) + char firmwareName[100]; +#ifdef USE_DEVUPDATER + char *updaterHost = "192.168.1.10"; + int updaterPort = 8080; +#else + char *updaterHost = "www.zimmers.net"; + int updaterPort = 80; +#endif +#ifdef ZIMODEM_ESP32 + char *updaterPrefix = "/otherprojs/guru"; +#else + char *updaterPrefix = "/otherprojs/c64net"; +#endif + sprintf(firmwareName,"%s-latest-version.txt",updaterPrefix); + if((!doWebGetBytes(updaterHost, updaterPort, firmwareName, false, buf, &bufSize))||(bufSize<=0)) return ZERROR; if((!isNumber)&&(vlen>2)) @@ -1006,12 +1263,17 @@ ZResult ZCommand::doUpdateFirmware(int vval, uint8_t *vbuf, int vlen, bool isNum while((bufSize>0) &&((buf[bufSize-1]==10)||(buf[bufSize-1]==13))) + { bufSize--; + buf[bufSize] = 0; + } if((strlen(ZIMODEM_VERSION)==bufSize) && memcmp(buf,ZIMODEM_VERSION,strlen(ZIMODEM_VERSION))==0) { serial.prints("Your modem is up-to-date."); serial.prints(EOLN); + if(vval == 6502) + return ZOK; } else { @@ -1026,23 +1288,29 @@ ZResult ZCommand::doUpdateFirmware(int vval, uint8_t *vbuf, int vlen, bool isNum serial.printf("Updating to %s, wait for modem restart...",buf); serial.flush(); + sprintf(firmwareName,"%s-firmware-%s.bin", updaterPrefix, buf); uint32_t respLength=0; - WiFiClient c; - char firmwareName[100]; - sprintf(firmwareName,"/otherprojs/c64net-firmware-%s.bin",buf); - if(!doWebGetStream("www.zimmers.net", 80, firmwareName, c, &respLength)) + WiFiClient *c = doWebGetStream(updaterHost, updaterPort, firmwareName, false, &respLength); + if(c==null) { serial.prints(EOLN); return ZERROR; } - if(!Update.begin(respLength)) + if(!Update.begin((respLength == 0) ? 4096 : respLength)) + { + c->stop(); + delete c; return ZERROR; + } serial.prints("."); serial.flush(); - if(Update.writeStream(c) != respLength) + int writeBytes = Update.writeStream(*c); + if(writeBytes != respLength) { + c->stop(); + delete c; serial.prints(EOLN); return ZERROR; } @@ -1050,12 +1318,16 @@ ZResult ZCommand::doUpdateFirmware(int vval, uint8_t *vbuf, int vlen, bool isNum serial.flush(); if(!Update.end()) { + c->stop(); + delete c; serial.prints(EOLN); return ZERROR; } + c->stop(); + delete c; serial.prints("Done"); serial.prints(EOLN); - serial.prints("Please wait for modem to restart..."); + serial.prints("Modem will now restart, but you should power-cycle or reset your modem."); ESP.restart(); return ZOK; } @@ -1091,38 +1363,95 @@ ZResult ZCommand::doWiFiCommand(int vval, uint8_t *vbuf, int vlen, bool isNumber else { char *x=strstr((char *)vbuf,","); - if(x <= 0) - return ZERROR; - else + char *ssi=(char *)vbuf; + char *pw=ssi + strlen(ssi); + IPAddress *ip[4]; + for(int i=0;i<4;i++) + ip[i]=null; + if(x > 0) { *x=0; - char *ssi=(char *)vbuf; - char *pw=x+1; - bool connSuccess=false; - if((doPETSCII)&&(!serial.isPetsciiMode())) + pw=x+1; + x=strstr(pw,","); + if(x > 0) { - char *ssiP =(char *)malloc(strlen(ssi)+1); - char *pwP = (char *)malloc(strlen(pw)+1); - strcpy(ssiP,ssi); - strcpy(pwP,pw); - for(char *c=ssiP;*c!=0;c++) - *c = ascToPetcii(*c); - for(char *c=pwP;*c!=0;c++) - *c = ascToPetcii(*c); - connSuccess = connectWifi(ssiP,pwP); - free(ssiP); - free(pwP); + int numCommasFound=0; + int numDotsFound=0; + char *comPos[4]; + for(char *e=pw+strlen(pw)-1;e>pw;e--) + { + if(*e==',') + { + if(numDotsFound!=3) + break; + numDotsFound=0; + if(numCommasFound<4) + { + numCommasFound++; + comPos[4-numCommasFound]=e; + } + if(numCommasFound==4) + break; + } + else + if(*e=='.') + numDotsFound++; + else + if(strchr("0123456789 ",*e)==null) + break; + } + if(numCommasFound==4) + { + for(int i=0;i<4;i++) + *(comPos[i])=0; + for(int i=0;i<4;i++) + { + ip[i]=ConnSettings::parseIP(comPos[i]+1); + if(ip[i]==null) + { + while(--i>=0) + { + free(ip[i]); + ip[i]=null; + } + break; + } + } + } } - else - connSuccess = connectWifi(ssi,pw); + } + bool connSuccess=false; + if((doPETSCII)&&(!serial.isPetsciiMode())) + { + char *ssiP =(char *)malloc(strlen(ssi)+1); + char *pwP = (char *)malloc(strlen(pw)+1); + strcpy(ssiP,ssi); + strcpy(pwP,pw); + for(char *c=ssiP;*c!=0;c++) + *c = ascToPetcii(*c); + for(char *c=pwP;*c!=0;c++) + *c = ascToPetcii(*c); + connSuccess = connectWifi(ssiP,pwP,ip[0],ip[1],ip[2],ip[3]); + free(ssiP); + free(pwP); + } + else + connSuccess = connectWifi(ssi,pw,ip[0],ip[1],ip[2],ip[3]); - if(!connSuccess) - return ZERROR; - else + if(!connSuccess) + { + for(int ii=0;ii<4;ii++) { - wifiSSI=ssi; - wifiPW=pw; + if(ip[ii]!=null) + free(ip[ii]); } + return ZERROR; + } + else + { + wifiSSI=ssi; + wifiPW=pw; + setNewStaticIPs(ip[0],ip[1],ip[2],ip[3]); } } return ZOK; @@ -1140,7 +1469,7 @@ ZResult ZCommand::doTransmitCommand(int vval, uint8_t *vbuf, int vlen, bool isNu if(isNumber && (vval>0)) { uint8_t buf[vval]; - int recvd = Serial.readBytes(buf,vval); + int recvd = HWSerial.readBytes(buf,vval); if(logFileOpen) { for(int i=0;iwrite(buf,vlen); - current->write(13); // special case - current->write(10); // special case + buf[vlen]=13; // special case + buf[vlen+1]=10; // special case + current->write(buf,vlen+2); if(logFileOpen) { for(int i=0;inext; } + /* if(vval == 5517545) // slip no login { slipMode.switchTo(); } + */ WiFiClientNode *c=conns; while((c!=null)&&(c->id != vval)) @@ -1234,10 +1565,7 @@ ZResult ZCommand::doDialStreamCommand(unsigned long vval, uint8_t *vbuf, int vle if((c!=null)&&(c->id == vval)&&(c->isConnected())) { current=c; - setCharArray(&(c->delimiters),tempDelimiters); - setCharArray(&(c->maskOuts),tempMaskOuts); - freeCharArray(&tempDelimiters); - freeCharArray(&tempMaskOuts); + connectionArgs(c); streamMode.switchTo(c); return ZCONNECT; } @@ -1246,7 +1574,10 @@ ZResult ZCommand::doDialStreamCommand(unsigned long vval, uint8_t *vbuf, int vle } else { - int flagsBitmap = makeStreamFlagsBitmap(dmodifiers); + ConnSettings flags(dmodifiers); + if(!telnetSupport) + flags.setFlag(FLAG_TELNET, false); + int flagsBitmap = flags.getBitmap(serial.getFlowControlType()); char *colon=strstr((char *)vbuf,":"); int port=23; if(colon != null) @@ -1258,15 +1589,12 @@ ZResult ZCommand::doDialStreamCommand(unsigned long vval, uint8_t *vbuf, int vle if(!c->isConnected()) { delete c; - return ZERROR; + return ZNOANSWER; } else { current=c; - setCharArray(&(c->delimiters),tempDelimiters); - setCharArray(&(c->maskOuts),tempMaskOuts); - freeCharArray(&tempDelimiters); - freeCharArray(&tempMaskOuts); + connectionArgs(c); streamMode.switchTo(c); return ZCONNECT; } @@ -1276,7 +1604,7 @@ ZResult ZCommand::doDialStreamCommand(unsigned long vval, uint8_t *vbuf, int vle ZResult ZCommand::doPhonebookCommand(unsigned long vval, uint8_t *vbuf, int vlen, bool isNumber, const char *dmodifiers) { - if((vlen==0)||(isNumber)) + if((vlen==0)||(isNumber)||((vlen==1)&&(*vbuf='?'))) { PhoneBookEntry *phb=phonebook; char nbuf[30]; @@ -1299,6 +1627,12 @@ ZResult ZCommand::doPhonebookCommand(unsigned long vval, uint8_t *vbuf, int vlen serial.prints(" "); serial.prints(" "); serial.prints(phb->address); + if(!isNumber) + { + serial.prints(" ("); + serial.prints(phb->notes); + serial.prints(")"); + } serial.prints(EOLN.c_str()); serial.flush(); delay(10); @@ -1322,17 +1656,7 @@ ZResult ZCommand::doPhonebookCommand(unsigned long vval, uint8_t *vbuf, int vlen return ZERROR; unsigned long number = atol((char *)vbuf); - PhoneBookEntry *found=null; - PhoneBookEntry *phb=phonebook; - while(phb != null) - { - if(phb->number == number) - { - found=phb; - break; - } - phb = phb->next; - } + PhoneBookEntry *found=PhoneBookEntry::findPhonebookEntry(number); if((strcmp("DELETE",rest)==0) ||(strcmp("delete",rest)==0)) { @@ -1342,42 +1666,27 @@ ZResult ZCommand::doPhonebookCommand(unsigned long vval, uint8_t *vbuf, int vlen PhoneBookEntry::savePhonebook(); return ZOK; } - char *comma = strchr(rest,','); - if(comma != NULL) - return ZERROR; char *colon = strchr(rest,':'); if(colon == NULL) return ZERROR; - for(char *cptr=colon;*cptr!=0;cptr++) + char *comma = strchr(colon,','); + char *notes = ""; + if(comma != NULL) { - if(strchr("0123456789",*cptr) < 0) - return ZERROR; + *comma=0; + notes = comma+1; } + if(!PhoneBookEntry::checkPhonebookEntry(colon)) + return ZERROR; if(found != null) delete found; - PhoneBookEntry *newEntry = new PhoneBookEntry(number,rest,dmodifiers); + PhoneBookEntry *newEntry = new PhoneBookEntry(number,rest,dmodifiers,notes); PhoneBookEntry::savePhonebook(); return ZOK; } ZResult ZCommand::doAnswerCommand(int vval, uint8_t *vbuf, int vlen, bool isNumber, const char *dmodifiers) { - if((vlen == 1)&&(vbuf[0]=='/')) - { - if(previousCommand.length()==0) - return ZERROR; - else - if(previousCommand[previousCommand.length()-1] == '/') - return ZERROR; - else - { - strcpy((char *)nbuf,previousCommand.c_str()); - eon=previousCommand.length(); - doSerialCommand(); - return ZIGNORE_SPECIAL; - } - } - else if(vval <= 0) { WiFiClientNode *c=conns; @@ -1387,23 +1696,26 @@ ZResult ZCommand::doAnswerCommand(int vval, uint8_t *vbuf, int vlen, bool isNumb &&(c->id = lastServerClientId)) { current=c; + checkOpenConnections(); streamMode.switchTo(c); lastServerClientId=0; if(ringCounter == 0) { + c->answer(); sendConnectionNotice(c->id); + checkOpenConnections(); return ZIGNORE; } break; } c=c->next; } - //TODO: possibly go to streaming mode, turn on DCD, and do nothing? return ZOK; // not really doing anything important... } else { - int flagsBitmap = makeStreamFlagsBitmap(dmodifiers); + ConnSettings flags(dmodifiers); + int flagsBitmap = flags.getBitmap(serial.getFlowControlType()); WiFiServerNode *s=servs; while(s != null) { @@ -1414,8 +1726,11 @@ ZResult ZCommand::doAnswerCommand(int vval, uint8_t *vbuf, int vlen, bool isNumb WiFiServerNode *newServer = new WiFiServerNode(vval, flagsBitmap); setCharArray(&(newServer->delimiters),tempDelimiters); setCharArray(&(newServer->maskOuts),tempMaskOuts); + setCharArray(&(newServer->stateMachine),tempStateMachine); freeCharArray(&tempDelimiters); freeCharArray(&tempMaskOuts); + freeCharArray(&tempStateMachine); + updateAutoAnswer(); return ZOK; } } @@ -1468,6 +1783,7 @@ ZResult ZCommand::doHangupCommand(int vval, uint8_t *vbuf, int vlen, bool isNumb if(vval == s->id) { delete s; + updateAutoAnswer(); return ZOK; } s=s->next; @@ -1476,13 +1792,29 @@ ZResult ZCommand::doHangupCommand(int vval, uint8_t *vbuf, int vlen, bool isNumb } } +void ZCommand::updateAutoAnswer() +{ +#ifdef SUPPORT_LED_PINS + bool setPin = (ringCounter>0) && (autoStreamMode) && (servs != NULL); + s_pinWrite(DEFAULT_PIN_AA,setPin?DEFAULT_AA_ACTIVE:DEFAULT_AA_INACTIVE); +#endif + +} + ZResult ZCommand::doLastPacket(int vval, uint8_t *vbuf, int vlen, bool isNumber) { if(!isNumber) return ZERROR; WiFiClientNode *cnode=null; + uint8_t which = 1; if(vval == 0) vval = lastPacketId; + else + { + uint8_t *c = vbuf; + while(*c++ == '0') + which++; + } if(vval <= 0) cnode = current; else @@ -1500,7 +1832,7 @@ ZResult ZCommand::doLastPacket(int vval, uint8_t *vbuf, int vlen, bool isNumber) } if(cnode == null) return ZERROR; - reSendLastPacket(cnode); + reSendLastPacket(cnode,which); return ZIGNORE; } @@ -1534,25 +1866,21 @@ ZResult ZCommand::doEOLNCommand(int vval, uint8_t *vbuf, int vlen, bool isNumber bool ZCommand::readSerialStream() { bool crReceived=false; - while(Serial.available()>0) + while((HWSerial.available()>0) + &&(!crReceived)) { - uint8_t c=Serial.read(); + uint8_t c=HWSerial.read(); logSerialIn(c); if((c==CR[0])||(c==LF[0])) { - if(eon == 0) - continue; - else + if(doEcho) { - if(doEcho) - { - serial.prints(EOLN); - if(serial.isSerialOut()) - serialOutDeque(); - } - crReceived=true; - break; + echoEOLN(c); + if(serial.isSerialOut()) + serialOutDeque(); } + crReceived=true; + break; } if(c>0) @@ -1603,43 +1931,117 @@ bool ZCommand::readSerialStream() continue; } nbuf[eon++]=c; - if(eon>=MAX_COMMAND_SIZE) + if((eon>=MAX_COMMAND_SIZE) + ||((eon==2)&&(nbuf[1]=='/')&&lc(nbuf[0])=='a')) + { + eon--; crReceived=true; + } } } } return crReceived; } -ZResult ZCommand::doSerialCommand() +String ZCommand::getNextSerialCommand() { int len=eon; - uint8_t sbuf[len]; - memcpy(sbuf,nbuf,len); + String currentCommand = (char *)nbuf; + currentCommand.trim(); memset(nbuf,0,MAX_COMMAND_SIZE); if(serial.isPetsciiMode()) { for(int i=0;i0)&&(!zclock.setTimeZone((char *)vbuf))) + return ZERROR; + if(strlen(c1)==0) + return ZOK; + char *c2=strchr(c1,','); + if(c2 == 0) + { + zclock.setFormat(c1); + return ZOK; + } + else + { + *c2=0; + c2++; + if(strlen(c1)>0) + zclock.setFormat(c1); + if(strlen(c2)>0) + zclock.setNtpServerHost(c2); + } + } + return ZOK; +} + +ZResult ZCommand::doSerialCommand() +{ + int len=eon; + String sbuf = getNextSerialCommand(); + + if((sbuf.length()==2) + &&(lc(sbuf[0])=='a') + &&(sbuf[1]=='/')) + { + sbuf = previousCommand; + len=previousCommand.length(); + } + if(logFileOpen) + logPrintfln("Command: %s",sbuf.c_str()); + int crc8=-1; ZResult result=ZOK; + + if((sbuf.length()==4) + &&(strcmp(sbuf.c_str(),"%!PS")==0)) + { + result = printMode.switchToPostScript("%!PS\n"); + sendOfficialResponse(result); + return result; + } + else + if((sbuf.length()==12) + &&(strcmp(sbuf.c_str(),"\x04grestoreall")==0)) + { + result = printMode.switchToPostScript("%!PS\ngrestoreall\n"); + sendOfficialResponse(result); + return result; + } + int index=0; while((index='a')&&(lc(sbuf[index])<='z'))) - &&(sbuf[index]!='&')) + &&(sbuf[index]!='&') + &&(sbuf[index]!='%') + &&(sbuf[index]!=' ')) { char c=sbuf[index]; isNumber = ((c=='-')||((c>='0') && (c<='9'))) && isNumber; @@ -1730,7 +2143,7 @@ ZResult ZCommand::doSerialCommand() memset(vbuf,0,vlen+1); if(vlen>0) { - memcpy(vbuf,sbuf+vstart,vlen); + memcpy(vbuf,sbuf.c_str()+vstart,vlen); if((vlen > 0)&&(isNumber)) { String finalNum=""; @@ -1873,7 +2286,10 @@ ZResult ZCommand::doSerialCommand() if((sval < 0)||(sval>255)) result=ZERROR; else + { ringCounter = sval; + updateAutoAnswer(); + } break; case 2: if((sval < 0)||(sval>255)) @@ -1921,8 +2337,11 @@ ZResult ZCommand::doSerialCommand() packetSize=sval; break; case 41: + { autoStreamMode = (sval > 0); + updateAutoAnswer(); break; + } case 42: crc8=sval; break; @@ -1942,59 +2361,131 @@ ZResult ZCommand::doSerialCommand() result=ZERROR; break; case 46: - if(sval <=0) + { + bool wasActive=(dcdStatus==dcdActive); + pinModeDecoder(sval,&dcdActive,&dcdInactive,DEFAULT_DCD_HIGH,DEFAULT_DCD_LOW); + dcdStatus = wasActive?dcdActive:dcdInactive; + result=ZOK; + s_pinWrite(pinDCD,dcdStatus); + break; + } + case 47: + if((sval >= 0) && (sval <= MAX_PIN_NO) && pinSupport[sval]) { - dcdActive = HIGH; - dcdInactive = LOW; + pinDCD=sval; + pinMode(pinDCD,OUTPUT); + s_pinWrite(pinDCD,dcdStatus); + result=ZOK; + } + else + result=ZERROR; + break; + case 48: + pinModeDecoder(sval,&ctsActive,&ctsInactive,DEFAULT_CTS_HIGH,DEFAULT_CTS_LOW); + result=ZOK; + break; + case 49: + if((sval >= 0) && (sval <= MAX_PIN_NO) && pinSupport[sval]) + { + pinCTS=sval; + pinMode(pinCTS,INPUT); + serial.setFlowControlType(serial.getFlowControlType()); + result=ZOK; } else + result=ZERROR; + break; + case 50: + pinModeDecoder(sval,&rtsActive,&rtsInactive,DEFAULT_RTS_HIGH,DEFAULT_RTS_LOW); + if(pinSupport[pinRTS]) { - dcdActive = LOW; - dcdInactive = HIGH; + serial.setFlowControlType(serial.getFlowControlType()); + s_pinWrite(pinRTS,rtsActive); } + result=ZOK; break; - case 47: - if(sval <=0) + case 51: + if((sval >= 0) && (sval <= MAX_PIN_NO) && pinSupport[sval]) { - ctsActive = HIGH; - ctsInactive = LOW; + pinRTS=sval; + pinMode(pinRTS,OUTPUT); + serial.setFlowControlType(serial.getFlowControlType()); + s_pinWrite(pinRTS,rtsActive); + result=ZOK; } else + result=ZERROR; + break; + case 52: + pinModeDecoder(sval,&riActive,&riInactive,DEFAULT_RI_HIGH,DEFAULT_RI_LOW); + if(pinSupport[pinRI]) { - ctsActive = LOW; - ctsInactive = HIGH; + serial.setFlowControlType(serial.getFlowControlType()); + s_pinWrite(pinRI,riInactive); } + result=ZOK; break; - case 48: - if(sval <=0) + case 53: + if((sval >= 0) && (sval <= MAX_PIN_NO) && pinSupport[sval]) { - rtsActive = HIGH; - rtsInactive = LOW; + pinRI=sval; + pinMode(pinRI,OUTPUT); + s_pinWrite(pinRTS,riInactive); + result=ZOK; } else + result=ZERROR; + break; + case 54: + pinModeDecoder(sval,&dtrActive,&dtrInactive,DEFAULT_DTR_HIGH,DEFAULT_DTR_LOW); + result=ZOK; + break; + case 55: + if((sval >= 0) && (sval <= MAX_PIN_NO) && pinSupport[sval]) { - rtsActive = LOW; - rtsInactive = HIGH; + pinDTR=sval; + pinMode(pinDTR,INPUT); + result=ZOK; } - break; - case 49: - if(sval >= 0) - pinDCD=sval; else result=ZERROR; break; - case 50: - if(sval >= 0) - pinCTS=sval; + case 56: + pinModeDecoder(sval,&dsrActive,&dsrInactive,DEFAULT_DSR_HIGH,DEFAULT_DSR_LOW); + s_pinWrite(pinDSR,dsrActive); + result=ZOK; + break; + case 57: + if((sval >= 0) && (sval <= MAX_PIN_NO) && pinSupport[sval]) + { + pinDSR=sval; + pinMode(pinDSR,OUTPUT); + s_pinWrite(pinDSR,dsrActive); + result=ZOK; + } else result=ZERROR; break; - case 51: + case 60: if(sval >= 0) - pinRTS=sval; + { + preserveListeners=(sval != 0); + if(preserveListeners) + WiFiServerNode::SaveWiFiServers(); + else + SPIFFS.remove("/zlisteners.txt"); + } else result=ZERROR; break; + case 61: + if(sval > 0) + printMode.setTimeoutDelayMs(sval * 1000); + else + result=ZERROR; + break; + case 62: + telnetSupport = (sval > 0); default: break; } @@ -2002,6 +2493,96 @@ ZResult ZCommand::doSerialCommand() } } break; + case '+': + for(int i=0;vbuf[i]!=0;i++) + vbuf[i]=lc(vbuf[i]); + if(strcmp((const char *)vbuf,"config")==0) + { + configMode.switchTo(); + result = ZOK; + } +#ifdef INCLUDE_SD_SHELL + else + if((strstr((const char *)vbuf,"shell")==(char *)vbuf)&&(browseEnabled)) + { + char *colon=strchr((const char*)vbuf,':'); + result = ZOK; + if(colon == 0) + browseMode.switchTo(); + else + { + String line = colon+1; + line.trim(); + browseMode.init(); + browseMode.doModeCommand(line); + } + } +# ifdef INCLUDE_HOSTCM + else + if((strstr((const char *)vbuf,"hostcm")==(char *)vbuf)) + { + result = ZOK; + hostcmMode.switchTo(); + } +# endif +#endif +# ifdef INCLUDE_IRCC + else + if((strstr((const char *)vbuf,"irc")==(char *)vbuf)) + { + result = ZOK; + ircMode.switchTo(); + } +# endif + else + if((strstr((const char *)vbuf,"print")==(char *)vbuf)||(strstr((const char *)vbuf,"PRINT")==(char *)vbuf)) + result = printMode.switchTo((char *)vbuf+5,vlen-5,serial.isPetsciiMode()); + else + result=ZERROR; //todo: branch based on vbuf contents + break; + case '$': + { + int eqMark=0; + for(int i=0;vbuf[i]!=0;i++) + if(vbuf[i]=='=') + { + eqMark=i; + break; + } + else + vbuf[i]=lc(vbuf[i]); + if(eqMark==0) + result=ZERROR; // no EQ means no good + else + { + vbuf[eqMark]=0; + String var=(char *)vbuf; + var.trim(); + String val=(char *)(vbuf+eqMark+1); + val.trim(); + result = ((val.length()==0)&&((strcmp(var.c_str(),"pass")!=0))) ? ZERROR : ZOK; + if(result == ZOK) + { + if(strcmp(var.c_str(),"ssid")==0) + wifiSSI = val; + else + if(strcmp(var.c_str(),"pass")==0) + wifiPW = val; + else + if(strcmp(var.c_str(),"mdns")==0) + hostname = val; + else + if(strcmp(var.c_str(),"sb")==0) + result = doBaudCommand(atoi(val.c_str()),(uint8_t *)val.c_str(),val.length()); + else + result = ZERROR; + } + } + break; + } + case '%': + result=ZERROR; + break; case '&': switch(lc(secCmd)) { @@ -2039,19 +2620,22 @@ ZResult ZCommand::doSerialCommand() if(vval == 86) { loadConfig(); + zclock.reset(); result = SPIFFS.format() ? ZOK : ZERROR; reSaveConfig(); } else { - SPIFFS.remove("/zconfig.txt"); + SPIFFS.remove(CONFIG_FILE); + SPIFFS.remove(CONFIG_FILE_OLD); SPIFFS.remove("/zphonebook.txt"); + SPIFFS.remove("/zlisteners.txt"); PhoneBookEntry::clearPhonebook(); if(WiFi.status() == WL_CONNECTED) WiFi.disconnect(); wifiSSI=""; - wifiConnected=false; delay(500); + zclock.reset(); result=doResetCommand(); showInitMessage(); } @@ -2077,6 +2661,70 @@ ZResult ZCommand::doSerialCommand() } result=ZOK; break; + case 'y': + { + if(isNumber && ((vval > 0)||(vbuf[0]=='0'))) + { + machineState = stateMachine; + machineQue = ""; + if(current != null) + { + current->machineState = current->stateMachine; + current->machineQue = ""; + } + while(vval > 0) + { + vval--; + if((machineState != null)&&(machineState[0]!=0)) + machineState += ZI_STATE_MACHINE_LEN; + if(current != null) + { + if((current->machineState != null)&&(current->machineState[0]!=0)) + current->machineState += ZI_STATE_MACHINE_LEN; + } + } + } + else + if((vlen % ZI_STATE_MACHINE_LEN) != 0) + result=ZERROR; + else + { + bool ok = true; + const char *HEX_DIGITS = "0123456789abcdefABCDEF"; + for(int i=0;ok && (i 0) + memcpy(newStateMachine,vbuf,vlen); + setCharArray(&tempStateMachine,newStateMachine); + result=ZOK; + } + else + { + result=ZERROR; + } + } + } + break; case 'd': if(vval > 0) { @@ -2103,9 +2751,9 @@ ZResult ZCommand::doSerialCommand() { if(logFileOpen) { - logFileOpen = false; logFile.flush(); logFile.close(); + logFileOpen = false; } logFile = SPIFFS.open("/logfile.txt", "r"); int numBytes = logFile.available(); @@ -2127,7 +2775,7 @@ ZResult ZCommand::doSerialCommand() if(serial.isSerialOut()) { serialOutDeque(); - Serial.flush(); + hwSerialFlush(); } delay(1); yield(); @@ -2164,7 +2812,9 @@ ZResult ZCommand::doSerialCommand() { logFileOpen = true; SPIFFS.remove("/logfile.txt"); - logFile = SPIFFS.open("/logfile.txt", "w"); + logFile = SPIFFS.open("/logfile.txt", "w"); + if(vval==88) + logFileDebug=true; result=ZOK; } break; @@ -2203,11 +2853,69 @@ ZResult ZCommand::doSerialCommand() case 'g': result = doWebStream(vval,vbuf,vlen,isNumber,"/temp.web",false); break; + case 's': + if(vlen<3) + result=ZERROR; + else + { + char *eq=strchr((char *)vbuf,'='); + if((eq == null)||(eq == (char *)vbuf)||(eq>=(char *)&(vbuf[vlen-1]))) + result=ZERROR; + else + { + *eq=0; + int snum = atoi((char *)vbuf); + if((snum == 0)&&((vbuf[0]!='0')||(eq != (char *)(vbuf+1)))) + result=ZERROR; + else + { + eq++; + switch(snum) + { + case 40: + if(*eq == 0) + result=ZERROR; + else + { + hostname = eq; + if(WiFi.status()==WL_CONNECTED) + connectWifi(wifiSSI.c_str(),wifiPW.c_str(),staticIP,staticDNS,staticGW,staticSN); + result=ZOK; + } + break; + case 41: + if(*eq == 0) + result=ZERROR; + else + { + termType = eq; + result=ZOK; + } + break; + case 42: + if((*eq == 0)||(strlen(eq)>250)) + result=ZERROR; + else + { + busyMsg = eq; + busyMsg.replace("\\n","\n"); + busyMsg.replace("\\r","\r"); + result=ZOK; + } + break; + default: + result=ZERROR; + break; + } + } + } + } + break; case 'p': serial.setPetsciiMode(vval > 0); break; case 'n': - if(isNumber && (vval >=0) && (vval <=9)) + if(isNumber && (vval >=0) && (vval <= MAX_PIN_NO) && pinSupport[vval]) { int pinNum = vval; int r = digitalRead(pinNum); @@ -2227,16 +2935,28 @@ ZResult ZCommand::doSerialCommand() *eq = 0; int pinNum = atoi((char *)vbuf); int sval = atoi(eq+1); - if((pinNum < 0) || (pinNum > 9) || (sval < 0) || (sval > 1)) + if((pinNum < 0) || (pinNum >= MAX_PIN_NO) || (!pinSupport[pinNum])) result = ZERROR; else { - digitalWrite(pinNum,sval); + s_pinWrite(pinNum,sval); serial.printf("Pin %d FORCED %s.%s",pinNum,(sval==LOW)?"LOW":(sval==HIGH)?"HIGH":"UNK",EOLN.c_str()); } } } + else + result = ZERROR; break; + case 't': + if(vlen == 0) + { + serial.prints(zclock.getCurrentTimeFormatted()); + serial.prints(EOLN); + result = ZIGNORE_SPECIAL; + } + else + result = doTimeZoneSetupCommand(vval, vbuf, vlen, isNumber); + break; case 'u': result=doUpdateFirmware(vval,vbuf,vlen,isNumber); break; @@ -2251,78 +2971,125 @@ ZResult ZCommand::doSerialCommand() } } - setCharArray(&delimiters,tempDelimiters); - freeCharArray(&tempDelimiters); - setCharArray(&maskOuts,tempMaskOuts); - freeCharArray(&tempMaskOuts); - - if(result != ZIGNORE_SPECIAL) - previousCommand = currentCommand; - if(suppressResponses) + if(tempDelimiters != NULL) { - if(result == ZERROR) - { - // on error, cut and run - return ZERROR; - } + setCharArray(&delimiters,tempDelimiters); + freeCharArray(&tempDelimiters); } - else + if(tempMaskOuts != NULL) { - if(crc8 >= 0) - result=ZERROR; // setting S42 without a T command is now Bad. - switch(result) - { - case ZOK: - if(index >= len) - { - logPrintln("Response: OK"); - if(numericResponses) - serial.prints("0"); - else - serial.prints("OK"); - serial.prints(EOLN); - } - break; - case ZERROR: - logPrintln("Response: ERROR"); - if(numericResponses) - serial.prints("4"); - else - serial.prints("ERROR"); - serial.prints(EOLN); - // on error, cut and run - return ZERROR; - case ZCONNECT: - logPrintln("Response: Connected!"); - sendConnectionNotice((current == null) ? baudRate : current->id); - break; - default: - break; - } + setCharArray(&maskOuts,tempMaskOuts); + freeCharArray(&tempMaskOuts); + } + if(tempStateMachine != NULL) + { + setCharArray(&stateMachine,tempStateMachine); + freeCharArray(&tempStateMachine); + machineState = stateMachine; } + if(result != ZIGNORE_SPECIAL) + previousCommand = saveCommand; + if((suppressResponses)&&(result == ZERROR)) + return ZERROR; + if(crc8 >= 0) + result=ZERROR; // setting S42 without a T command is now Bad. + if((result != ZOK)||(index >= len)) + sendOfficialResponse(result); + if(result == ZERROR) // on error, cut and run + return ZERROR; } return result; } +void ZCommand::sendOfficialResponse(ZResult res) +{ + if(!suppressResponses) + { + switch(res) + { + case ZOK: + logPrintln("Response: OK"); + preEOLN(EOLN); + if(numericResponses) + serial.prints("0"); + else + serial.prints("OK"); + serial.prints(EOLN); + break; + case ZERROR: + logPrintln("Response: ERROR"); + preEOLN(EOLN); + if(numericResponses) + serial.prints("4"); + else + serial.prints("ERROR"); + serial.prints(EOLN); + break; + case ZNOANSWER: + logPrintln("Response: NOANSWER"); + preEOLN(EOLN); + if(numericResponses) + serial.prints("8"); + else + serial.prints("NO ANSWER"); + serial.prints(EOLN); + break; + case ZCONNECT: + logPrintln("Response: Connected!"); + sendConnectionNotice((current == null) ? baudRate : current->id); + break; + default: + break; + } + } +} + void ZCommand::showInitMessage() { + serial.prints(commandMode.EOLN); +#ifdef ZIMODEM_ESP32 + int totalSPIFFSSize = SPIFFS.totalBytes(); +#ifdef INCLUDE_SD_SHELL + serial.prints("GuruModem WiFi Firmware v"); +#else + serial.prints("Zimodem32 Firmware v"); +#endif + +#else FSInfo info; SPIFFS.info(info); - serial.prints(commandMode.EOLN); + int totalSPIFFSSize = info.totalBytes; +#ifdef RS232_INVERTED serial.prints("C64Net WiFi Firmware v"); - Serial.setTimeout(60000); +#else + serial.prints("Zimodem Firmware v"); +#endif +#endif + HWSerial.setTimeout(60000); serial.prints(ZIMODEM_VERSION); + //serial.prints(" ("); + //serial.prints(compile_date); + //serial.prints(")"); serial.prints(commandMode.EOLN); char s[100]; +#ifdef ZIMODEM_ESP32 + sprintf(s,"sdk=%s chipid=%d cpu@%d",ESP.getSdkVersion(),ESP.getChipRevision(),ESP.getCpuFreqMHz()); +#else sprintf(s,"sdk=%s chipid=%d cpu@%d",ESP.getSdkVersion(),ESP.getFlashChipId(),ESP.getCpuFreqMHz()); +#endif serial.prints(s); serial.prints(commandMode.EOLN); - sprintf(s,"totsize=%dk ssize=%dk fsize=%dk speed=%dm",(ESP.getFlashChipRealSize()/1024),(ESP.getSketchSize()/1024),info.totalBytes/1024,(ESP.getFlashChipSpeed()/1000000)); +#ifdef ZIMODEM_ESP32 + sprintf(s,"totsize=%dk hsize=%dk fsize=%dk speed=%dm",(ESP.getFlashChipSize()/1024),(ESP.getFreeHeap()/1024),totalSPIFFSSize/1024,(ESP.getFlashChipSpeed()/1000000)); +#else + sprintf(s,"totsize=%dk ssize=%dk fsize=%dk speed=%dm",(ESP.getFlashChipRealSize()/1024),(ESP.getSketchSize()/1024),totalSPIFFSSize/1024,(ESP.getFlashChipSpeed()/1000000)); +#endif + serial.prints(s); serial.prints(commandMode.EOLN); if(wifiSSI.length()>0) { - if(wifiConnected) + if(WiFi.status() == WL_CONNECTED) serial.prints(("CONNECTED TO " + wifiSSI + " (" + WiFi.localIP().toString().c_str() + ")").c_str()); else serial.prints(("ERROR ON " + wifiSSI).c_str()); @@ -2335,38 +3102,142 @@ void ZCommand::showInitMessage() serial.flush(); } -void ZCommand::reSendLastPacket(WiFiClientNode *conn) +uint8_t *ZCommand::doStateMachine(uint8_t *buf, uint16_t *bufLen, char **machineState, String *machineQue, char *stateMachine) +{ + if((stateMachine != NULL) && ((stateMachine)[0] != 0) && (*machineState != NULL) && ((*machineState)[0] != 0)) + { + String newBuf = ""; + for(int i=0;i<*bufLen;) + { + char matchChar = FROMHEX((*machineState)[0],(*machineState)[1]); + if((matchChar == 0)||(matchChar == buf[i])) + { + char c= buf[i++]; + short cmddex=1; + do + { + cmddex++; + switch(lc((*machineState)[cmddex])) + { + case '-': // do nothing + case 'e': // do nothing + break; + case 'p': // push to the que + if(machineQue->length() < 256) + *machineQue += c; + break; + case 'd': // display this char + newBuf += c; + break; + case 'x': // flush queue + *machineQue = ""; + break; + case 'q': // eat this char, but flush the queue + if(machineQue->length()>0) + { + newBuf += *machineQue; + *machineQue = ""; + } + break; + case 'r': // replace this char + if(cmddex == 2) + { + char newChar = FROMHEX((*machineState)[cmddex+1],(*machineState)[cmddex+2]); + newBuf += newChar; + } + break; + default: + break; + } + } + while((cmddex<4) && (lc((*machineState)[cmddex])!='r')); + char *newstate = stateMachine + (ZI_STATE_MACHINE_LEN * FROMHEX((*machineState)[5],(*machineState)[6])); + char *test = stateMachine; + while(test[0] != 0) + { + if(test == newstate) + { + (*machineState) = test; + break; + } + test += ZI_STATE_MACHINE_LEN; + } + } + else + { + *machineState += ZI_STATE_MACHINE_LEN; + if((*machineState)[0] == 0) + { + *machineState = stateMachine; + i++; + } + } + } + if((*bufLen != newBuf.length()) || (memcmp(buf,newBuf.c_str(),*bufLen)!=0)) + { + if(newBuf.length() > 0) + { + if(newBuf.length() > *bufLen) + { + free(buf); + buf = (uint8_t *)malloc(newBuf.length()); + } + memcpy(buf,newBuf.c_str(),newBuf.length()); + } + *bufLen = newBuf.length(); + } + } + return buf; +} + +uint8_t *ZCommand::doMaskOuts(uint8_t *buf, uint16_t *bufLen, char *maskOuts) { - if(conn == NULL) + if(maskOuts[0] != 0) { - headerOut(0,0,0); + uint16_t oldLen=*bufLen; + for(int i=0,o=0;i2)) + { + headerOut(conn->id,0,0,0); + } + else + if(conn->blankPackets>=which) + { + headerOut(conn->id,conn->nextPacketNum-which,0,0); } else - if(conn->lastPacketLen == 0) // never used, or empty + if(conn->lastPacket[which].len == 0) // never used, or empty { - headerOut(conn->id,conn->lastPacketLen,0); + headerOut(conn->id,conn->lastPacket[which].num,0,0); } else { - int bufLen = conn->lastPacketLen; + uint16_t bufLen = conn->lastPacket[which].len; uint8_t *buf = (uint8_t *)malloc(bufLen); - memcpy(buf,conn->lastPacketBuf,bufLen); + uint8_t num = conn->lastPacket[which].num; + memcpy(buf,conn->lastPacket[which].buf,bufLen); - if((conn->maskOuts[0] != 0) || (maskOuts[0] != 0)) - { - int oldLen=bufLen; - for(int i=0,o=0;imaskOuts,buf[i])!=null) - ||(strchr(maskOuts,buf[i])!=null)) - { - o--; - bufLen--; - } - else - buf[o]=buf[i]; - } - } + buf = doMaskOuts(buf,&bufLen,maskOuts); + buf = doMaskOuts(buf,&bufLen,conn->maskOuts); + buf = doStateMachine(buf,&bufLen,&machineState,&machineQue,stateMachine); + buf = doStateMachine(buf,&bufLen,&(conn->machineState),&(conn->machineQue),conn->stateMachine); if(nextConn->isPETSCII()) { int oldLen=bufLen; @@ -2382,7 +3253,7 @@ void ZCommand::reSendLastPacket(WiFiClientNode *conn) } uint8_t crc=CRC8(buf,bufLen); - headerOut(conn->id,bufLen,(int)crc); + headerOut(conn->id,num,bufLen,(int)crc); int bct=0; int i=0; while(i < bufLen) @@ -2391,9 +3262,12 @@ void ZCommand::reSendLastPacket(WiFiClientNode *conn) switch(binType) { case BTYPE_NORMAL: + case BTYPE_NORMAL_NOCHK: + case BTYPE_NORMAL_PLUS: serial.write(c); break; case BTYPE_HEX: + case BTYPE_HEX_PLUS: { const char *hbuf = TOHEX(c); serial.printb(hbuf[0]); // prevents petscii @@ -2406,6 +3280,7 @@ void ZCommand::reSendLastPacket(WiFiClientNode *conn) break; } case BTYPE_DEC: + case BTYPE_DEC_PLUS: serial.printf("%d%s",c,EOLN.c_str()); break; } @@ -2414,7 +3289,7 @@ void ZCommand::reSendLastPacket(WiFiClientNode *conn) if(serial.isSerialOut()) { serialOutDeque(); - Serial.flush(); + hwSerialFlush(); } serial.drainForXonXoff(); delay(1); @@ -2428,18 +3303,50 @@ void ZCommand::reSendLastPacket(WiFiClientNode *conn) } } -void ZCommand::serialIncoming() +bool ZCommand::clearPlusProgress() { - bool crReceived=readSerialStream(); if(currentExpiresTimeMs > 0) currentExpiresTimeMs = 0; if((strcmp((char *)nbuf,ECS)==0)&&((millis()-lastNonPlusTimeMs)>1000)) currentExpiresTimeMs = millis() + 1000; - if(!crReceived) - return; - //delay(200); // give a pause after receiving command before responding - // the delay doesn't affect xon/xoff because its the periodic transmitter that manages that. - doSerialCommand(); +} + +bool ZCommand::checkPlusEscape() +{ + if((currentExpiresTimeMs > 0) && (millis() > currentExpiresTimeMs)) + { + currentExpiresTimeMs = 0; + if(strcmp((char *)nbuf,ECS)==0) + { + if(current != null) + { + if(!suppressResponses) + { + preEOLN(EOLN); + if(numericResponses) + { + serial.prints("3"); + serial.prints(EOLN); + } + else + if(current->isAnswered()) + { + serial.prints("NO CARRIER "); + serial.printf("%d %s:%d",current->id,current->host,current->port); + serial.prints(EOLN); + serial.flush(); + } + } + delete current; + current = conns; + nextConn = conns; + } + memset(nbuf,0,MAX_COMMAND_SIZE); + eon=0; + return true; + } + } + return false; } void ZCommand::sendNextPacket() @@ -2457,21 +3364,21 @@ void ZCommand::sendNextPacket() nextConn = nextConn->next; while(serial.isSerialOut() && (nextConn != null) && (packetXOn)) { - if((nextConn->isConnected()) - && (nextConn->available()>0)) + if(nextConn->available()>0) + //&& (nextConn->isConnected())) // being connected is not required to have buffered bytes waiting! { int availableBytes = nextConn->available(); int maxBytes=packetSize; if(availableBytes Serial.availableForWrite()-15) // how much we read should depend on how much we can IMMEDIATELY write - //maxBytes = Serial.availableForWrite()-15; // .. this is because resendLastPacket ensures everything goes out + // how much we read should depend on how much we can IMMEDIATELY write + // .. this is because resendLastPacket ensures everything goes out if(maxBytes > 0) { if((nextConn->delimiters[0] != 0) || (delimiters[0] != 0)) { - int lastLen = nextConn->lastPacketLen; - uint8_t *lastBuf = nextConn->lastPacketBuf; + uint16_t lastLen = nextConn->lastPacket[0].len; + uint8_t *lastBuf = nextConn->lastPacket[0].buf; if((lastLen >= packetSize) ||((lastLen>0) @@ -2490,7 +3397,7 @@ void ZCommand::sendNextPacket() lastBuf[lastLen++] = c; bytesRemain--; } - nextConn->lastPacketLen = lastLen; + nextConn->lastPacket[0].len = lastLen; if((lastLen >= packetSize) ||((lastLen>0) &&((strchr(nextConn->delimiters,lastBuf[lastLen-1]) != null) @@ -2500,7 +3407,10 @@ void ZCommand::sendNextPacket() { if(serial.getFlowControlType() == FCT_MANUAL) { - headerOut(0,0,0); + if(nextConn->blankPackets == 0) + memcpy(&nextConn->lastPacket[2],&nextConn->lastPacket[1],sizeof(struct Packet)); + nextConn->blankPackets++; + headerOut(nextConn->id,nextConn->nextPacketNum++,0,0); packetXOn = false; } else @@ -2511,12 +3421,22 @@ void ZCommand::sendNextPacket() } else { - maxBytes = nextConn->read(nextConn->lastPacketBuf,maxBytes); - logSocketIn(nextConn->lastPacketBuf,maxBytes); + maxBytes = nextConn->read(nextConn->lastPacket[0].buf,maxBytes); + logSocketIn(nextConn->lastPacket[0].buf,maxBytes); } - nextConn->lastPacketLen=maxBytes; + nextConn->lastPacket[0].num=nextConn->nextPacketNum++; + nextConn->lastPacket[0].len=maxBytes; lastPacketId=nextConn->id; - reSendLastPacket(nextConn); + if(nextConn->blankPackets>0) + { + nextConn->lastPacket[2].num=nextConn->nextPacketNum-1; + nextConn->lastPacket[2].len=0; + } + else + memcpy(&nextConn->lastPacket[2],&nextConn->lastPacket[1],sizeof(struct Packet)); + memcpy(&nextConn->lastPacket[1],&nextConn->lastPacket[0],sizeof(struct Packet)); + nextConn->blankPackets=0; + reSendLastPacket(nextConn,1); if(serial.getFlowControlType() == FCT_AUTOOFF) { packetXOn = false; @@ -2539,13 +3459,20 @@ void ZCommand::sendNextPacket() if(!suppressResponses) { if(numericResponses) + { + preEOLN(EOLN); serial.prints("3"); + serial.prints(EOLN); + } else + if(nextConn->isAnswered()) { + preEOLN(EOLN); serial.prints("NO CARRIER "); serial.printi(nextConn->id); + serial.prints(EOLN); + serial.flush(); } - serial.prints(EOLN); if(serial.getFlowControlType() == FCT_MANUAL) { return; @@ -2572,17 +3499,24 @@ void ZCommand::sendNextPacket() { packetXOn = false; firstConn = conns; + if(firstConn != NULL) + headerOut(firstConn->id,firstConn->nextPacketNum++,0,0); + else + headerOut(0,0,0,0); while(firstConn != NULL) { - firstConn->lastPacketLen = 0; + firstConn->lastPacket[0].len = 0; + if(firstConn->blankPackets == 0) + memcpy(&firstConn->lastPacket[2],&firstConn->lastPacket[1],sizeof(struct Packet)); + firstConn->blankPackets++; firstConn = firstConn->next; } - headerOut(0,0,0); } } void ZCommand::sendConnectionNotice(int id) { + preEOLN(EOLN); if(numericResponses) { if(!longResponses) @@ -2634,7 +3568,7 @@ void ZCommand::acceptNewConnection() if(serv->hasClient()) { WiFiClient newClient = serv->server->available(); - if((newClient != null)&&(newClient.connected())) + if(newClient.connected()) { int port=newClient.localPort(); String remoteIPStr = newClient.remoteIP().toString(); @@ -2652,19 +3586,18 @@ void ZCommand::acceptNewConnection() if(!found) { //BZ:newClient.setNoDelay(true); - WiFiClientNode *newClientNode = new WiFiClientNode(newClient, serv->flagsBitmap); + int futureRings = (ringCounter > 0)?(ringCounter-1):5; + WiFiClientNode *newClientNode = new WiFiClientNode(newClient, serv->flagsBitmap, futureRings * 2); setCharArray(&(newClientNode->delimiters),serv->delimiters); setCharArray(&(newClientNode->maskOuts),serv->maskOuts); - int i=0; - do - { - serial.prints(numericResponses?"2":"RING"); - serial.prints(EOLN); - } - while((++i)stateMachine),serv->stateMachine); + newClientNode->machineState = newClientNode->stateMachine; + s_pinWrite(pinRI,riActive); + preEOLN(EOLN); + serial.prints(numericResponses?"2":"RING"); + serial.prints(EOLN); lastServerClientId = newClientNode->id; - if(ringCounter > 0) + if(newClientNode->isAnswered()) { if(autoStreamMode) { @@ -2680,38 +3613,73 @@ void ZCommand::acceptNewConnection() } serv=serv->next; } -} - -static int lastPinRead = 0; - -void ZCommand::loop() -{ - if((currentExpiresTimeMs > 0) && (millis() > currentExpiresTimeMs)) + // handle rings properly + WiFiClientNode *conn = conns; + unsigned long now=millis(); + while(conn != null) { - currentExpiresTimeMs = 0; - if(strcmp((char *)nbuf,ECS)==0) + WiFiClientNode *nextConn = conn->next; + if((!conn->isAnswered())&&(conn->isConnected())) { - if(current != null) + if(now > conn->nextRingTime(0)) { - if(!suppressResponses) + conn->nextRingTime(3000); + int rings=conn->ringsRemaining(-1); + if(rings <= 0) { - if(numericResponses) - serial.prints("3"); - else + s_pinWrite(pinRI,riInactive); + if(ringCounter > 0) { - serial.prints("NO CARRIER "); - serial.printf("%d %s:%d",current->id,current->host,current->port); + preEOLN(EOLN); + serial.prints(numericResponses?"2":"RING"); + serial.prints(EOLN); + conn->answer(); + if(autoStreamMode) + { + sendConnectionNotice(baudRate); + doAnswerCommand(0, (uint8_t *)"", 0, false, ""); + break; + } + else + sendConnectionNotice(conn->id); } + else + delete conn; + } + else + if((rings % 2) == 0) + { + s_pinWrite(pinRI,riActive); + preEOLN(EOLN); + serial.prints(numericResponses?"2":"RING"); serial.prints(EOLN); } - delete current; - current = conns; - nextConn = conns; + else + s_pinWrite(pinRI,riInactive); } - memset(nbuf,0,MAX_COMMAND_SIZE); - eon=0; } + conn = nextConn; } + if(checkOpenConnections()==0) + { + s_pinWrite(pinRI,riInactive); + } +} + +void ZCommand::serialIncoming() +{ + bool crReceived=readSerialStream(); + clearPlusProgress(); // every serial incoming, without a plus, breaks progress + if((!crReceived)||(eon==0)) + return; + //delay(200); // give a pause after receiving command before responding + // the delay doesn't affect xon/xoff because its the periodic transmitter that manages that. + doSerialCommand(); +} + +void ZCommand::loop() +{ + checkPlusEscape(); acceptNewConnection(); if(serial.isSerialOut()) { diff --git a/zimodem/zconfigmode.h b/zimodem/zconfigmode.h new file mode 100644 index 0000000..e018d32 --- /dev/null +++ b/zimodem/zconfigmode.h @@ -0,0 +1,67 @@ +/* + Copyright 2016-2019 Bo Zimmerman + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +class ZConfig : public ZMode +{ + private: + enum ZConfigMenu + { + ZCFGMENU_MAIN=0, + ZCFGMENU_NUM=1, + ZCFGMENU_ADDRESS=2, + ZCFGMENU_OPTIONS=3, + ZCFGMENU_WIMENU=4, + ZCFGMENU_WIFIPW=5, + ZCFGMENU_WICONFIRM=6, + ZCFGMENU_FLOW=7, + ZCFGMENU_BBSMENU=8, + ZCFGMENU_NEWPORT=9, + ZCFGMENU_NEWHOST=10, + ZCFGMENU_NOTES=11, + ZCFGMENU_NETMENU=12, + ZCFGMENU_SUBNET=13, + ZCFGMENU_NEWPRINT=14 + } currState; + + ZSerial serial; // storage for serial settings only + + void switchBackToCommandMode(); + void doModeCommand(); + bool showMenu; + bool savedEcho; + String EOLN; + const char *EOLNC; + unsigned long lastNumber; + String lastAddress; + String lastOptions; + String lastNotes; + WiFiServerSpec serverSpec; + bool newListen; + bool useDHCP; + bool settingsChanged=false; + char netOpt = ' '; + int lastNumNetworks=0; + IPAddress lastIP; + IPAddress lastDNS; + IPAddress lastGW; + IPAddress lastSN; + + public: + void switchTo(); + void serialIncoming(); + void loop(); +}; + diff --git a/zimodem/zconfigmode.ino b/zimodem/zconfigmode.ino new file mode 100644 index 0000000..b8cc0a3 --- /dev/null +++ b/zimodem/zconfigmode.ino @@ -0,0 +1,877 @@ +/* + Copyright 2016-2019 Bo Zimmerman + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +void ZConfig::switchTo() +{ + currMode=&configMode; + serial.setFlowControlType(commandMode.serial.getFlowControlType()); + serial.setPetsciiMode(commandMode.serial.isPetsciiMode()); + savedEcho=commandMode.doEcho; + newListen=commandMode.preserveListeners; + commandMode.doEcho=true; + serverSpec.port=6502; + serverSpec.flagsBitmap=commandMode.getConfigFlagBitmap(); + if(servs) + serverSpec = *servs; + serial.setXON(true); + showMenu=true; + EOLN=commandMode.EOLN; + EOLNC=EOLN.c_str(); + currState = ZCFGMENU_MAIN; + lastNumber=0; + lastAddress=""; + lastOptions=""; + settingsChanged=false; + lastNumNetworks=0; +} + +void ZConfig::serialIncoming() +{ + bool crReceived=commandMode.readSerialStream(); + commandMode.clearPlusProgress(); // re-check the plus-escape mode + if(crReceived) + { + doModeCommand(); + } +} + +void ZConfig::switchBackToCommandMode() +{ + commandMode.doEcho=savedEcho; + currMode = &commandMode; +} + +void ZConfig::doModeCommand() +{ + String cmd = commandMode.getNextSerialCommand(); + char c='?'; + char sc='?'; + for(int i=0;i32) + { + c=lc(cmd[i]); + if((i32)) + sc=lc(cmd[i+1]); + break; + } + } + switch(currState) + { + case ZCFGMENU_MAIN: + { + if((c=='q')||(cmd.length()==0)) + { + if(settingsChanged) + { + currState=ZCFGMENU_WICONFIRM; + showMenu=true; + } + else + { + commandMode.showInitMessage(); + switchBackToCommandMode(); + return; + } + } + else + if(c=='a') // add to phonebook + { + currState=ZCFGMENU_NUM; + showMenu=true; + } + else + if(c=='w') // wifi + { + currState=ZCFGMENU_WIMENU; + showMenu=true; + } + else + if(c=='h') // host + { + currState=ZCFGMENU_NEWHOST; + showMenu=true; + } + else + if(c=='f') // flow control + { + currState=ZCFGMENU_FLOW; + showMenu=true; + } + else + if((c=='p')&&(sc=='e')) // petscii translation toggle + { + commandMode.serial.setPetsciiMode(!commandMode.serial.isPetsciiMode()); + serial.setPetsciiMode(commandMode.serial.isPetsciiMode()); + settingsChanged=true; + showMenu=true; + } + else + if((c=='p')&&(sc=='r')) // print spec + { + currState=ZCFGMENU_NEWPRINT; + showMenu=true; + } + else + if(c=='e') // echo + { + savedEcho = !savedEcho; + settingsChanged=true; + showMenu=true; + } + else + if(c=='b') // bbs + { + currState=ZCFGMENU_BBSMENU; + showMenu=true; + } + else + if(c>47 && c<58) // its a phonebook entry! + { + PhoneBookEntry *pb=PhoneBookEntry::findPhonebookEntry(cmd); + if(pb == null) + { + serial.printf("%s%sPhone number not found: '%s'.%s%s",EOLNC,EOLNC,cmd.c_str(),EOLNC,EOLNC); + currState=ZCFGMENU_MAIN; + showMenu=true; + } + else + { + lastNumber = pb->number; + lastAddress = pb->address; + lastOptions = pb->modifiers; + lastNotes = pb->notes; + currState=ZCFGMENU_ADDRESS; + showMenu=true; + } + } + else + { + showMenu=true; // re-show the menu + } + break; + } + case ZCFGMENU_WICONFIRM: + { + if((cmd.length()==0)||(c=='n')) + { + commandMode.showInitMessage(); + switchBackToCommandMode(); + return; + } + else + if(c=='y') + { + if(newListen != commandMode.preserveListeners) + { + commandMode.preserveListeners=newListen; + if(!newListen) + { + SPIFFS.remove("/zlisteners.txt"); + WiFiServerNode::DestroyAllServers(); + } + else + { + commandMode.ringCounter=1; + commandMode.autoStreamMode=true; + WiFiServerNode *s=WiFiServerNode::FindServer(serverSpec.port); + if(s != null) + delete s; + s = new WiFiServerNode(serverSpec.port,serverSpec.flagsBitmap); + WiFiServerNode::SaveWiFiServers(); + } + } + else + if(commandMode.preserveListeners) + { + WiFiServerNode *s = WiFiServerNode::FindServer(serverSpec.port); + if( s != null) + { + if(s->flagsBitmap != serverSpec.flagsBitmap) + { + s->flagsBitmap = serverSpec.flagsBitmap; + } + } + else + { + WiFiServerNode::DestroyAllServers(); + s = new WiFiServerNode(serverSpec.port,serverSpec.flagsBitmap); + WiFiServerNode::SaveWiFiServers(); + commandMode.updateAutoAnswer(); + } + } + commandMode.reSaveConfig(); + serial.printf("%sSettings saved.%s",EOLNC,EOLNC); + commandMode.showInitMessage(); + WiFiServerNode::SaveWiFiServers(); + switchBackToCommandMode(); + return; + } + else + showMenu=true; + } + case ZCFGMENU_NUM: + { + PhoneBookEntry *pb=PhoneBookEntry::findPhonebookEntry(cmd); + if(pb != null) + { + serial.printf("%s%sNumber already exists '%s'.%s%s",EOLNC,EOLNC,cmd.c_str(),EOLNC,EOLNC); + currState=ZCFGMENU_MAIN; + showMenu=true; + } + else + if(cmd.length()==0) + { + currState=ZCFGMENU_MAIN; + showMenu=true; + } + else + { + lastNumber = atol((char *)cmd.c_str()); + lastAddress = ""; + ConnSettings flags(commandMode.getConfigFlagBitmap()); + lastOptions = flags.getFlagString(); + lastNotes = ""; + currState=ZCFGMENU_ADDRESS; + showMenu=true; + } + break; + } + case ZCFGMENU_NEWPORT: + { + if(cmd.length()>0) + { + serverSpec.port = atoi((char *)cmd.c_str()); + settingsChanged=true; + } + currState=ZCFGMENU_BBSMENU; + showMenu=true; + break; + } + case ZCFGMENU_ADDRESS: + { + PhoneBookEntry *entry = PhoneBookEntry::findPhonebookEntry(lastNumber); + if(cmd.equalsIgnoreCase("delete") && (entry != null)) + { + delete entry; + currState=ZCFGMENU_MAIN; + serial.printf("%sPhonebook entry deleted.%s%s",EOLNC,EOLNC,EOLNC); + } + else + if((cmd.length()==0) && (entry != null)) + currState=ZCFGMENU_NOTES; // just keep old values + else + { + boolean fail = cmd.indexOf(',') >= 0; + int colonDex=cmd.indexOf(':'); + fail = fail || (colonDex <= 0) || (colonDex == cmd.length()-1); + fail = fail || (colonDex != cmd.lastIndexOf(':')); + if(!fail) + { + for(int i=colonDex+1;i0) + lastNotes=cmd; + currState=ZCFGMENU_OPTIONS; + showMenu=true; // re-show the menu + break; + } + case ZCFGMENU_OPTIONS: + { + if(cmd.length()==0) + { + PhoneBookEntry *entry = PhoneBookEntry::findPhonebookEntry(lastNumber); + if(entry != null) + { + serial.printf("%sPhonebook entry updated.%s%s",EOLNC,EOLNC,EOLNC); + delete entry; + } + else + serial.printf("%sPhonebook entry added.%s%s",EOLNC,EOLNC,EOLNC); + entry = new PhoneBookEntry(lastNumber,lastAddress.c_str(),lastOptions.c_str(),lastNotes.c_str()); + PhoneBookEntry::savePhonebook(); + currState=ZCFGMENU_MAIN; + } + else + { + ConnSettings flags(lastOptions.c_str()); + switch(c) + { + case 'p': + flags.petscii=!flags.petscii; + break; + case 't': + flags.telnet=!flags.telnet; + break; + case 'e': + flags.echo=!flags.echo; + break; + case 'f': + if(flags.xonxoff) + { + flags.xonxoff=false; + flags.rtscts=true; + } + else + if(flags.rtscts) + flags.rtscts=false; + else + flags.xonxoff=true; + break; + case 's': + flags.secure=!flags.secure; + break; + default: + serial.printf("%sInvalid toggle option '%s'.%s%s",EOLNC,cmd.c_str(),EOLNC,EOLNC); + break; + } + lastOptions = flags.getFlagString(); + } + showMenu=true; // re-show the menu + break; + } + case ZCFGMENU_WIMENU: + { + if(cmd.length()==0) + { + currState=ZCFGMENU_MAIN; + showMenu=true; + } + else + { + int num=atoi(cmd.c_str()); + if((num<=0)||(num>lastNumNetworks)) + serial.printf("%sInvalid number. Try again.%s",EOLNC,EOLNC); + else + if(WiFi.encryptionType(num-1) == ENC_TYPE_NONE) + { + if(!connectWifi(WiFi.SSID(num-1).c_str(),"",null,null,null,null)) + { + serial.printf("%sUnable to connect to %s. :(%s",EOLNC,WiFi.SSID(num-1).c_str(),EOLNC); + } + else + { + wifiSSI=WiFi.SSID(num-1); + wifiPW=""; + settingsChanged=true; + serial.printf("%sConnected!%s",EOLNC,EOLNC); + currState=ZCFGMENU_NETMENU; + } + showMenu=true; + } + else + { + lastNumber=num-1; + currState=ZCFGMENU_WIFIPW; + showMenu=true; + } + } + break; + } + case ZCFGMENU_NEWHOST: + if(cmd.length()==0) + currState=ZCFGMENU_WIMENU; + else + { + hostname=cmd; + hostname.replace(',','.'); + if((wifiSSI.length() > 0) && (WiFi.status()==WL_CONNECTED)) + { + if(!connectWifi(wifiSSI.c_str(),wifiPW.c_str(),staticIP,staticDNS,staticGW,staticSN)) + serial.printf("%sUnable to connect to %s. :(%s",EOLNC,wifiSSI.c_str(),EOLNC); + settingsChanged=true; + } + currState=ZCFGMENU_MAIN; + showMenu=true; + } + break; + case ZCFGMENU_NEWPRINT: + if(cmd.length()>0) + { + if(!printMode.testPrinterSpec(cmd.c_str(),cmd.length(),commandMode.serial.isPetsciiMode())) + { + serial.printf("%sBad format. Try ?::/.%s",EOLNC,EOLNC); + serial.printf("? = A)scii P)etscii or R)aw.%s",EOLNC); + serial.printf("Example: R:192.168.1.71:631/ipp/printer%s",EOLNC); + } + else + { + printMode.setLastPrinterSpec(cmd.c_str()); + settingsChanged=true; + } + } + currState=ZCFGMENU_MAIN; + showMenu=true; + break; + case ZCFGMENU_WIFIPW: + if(cmd.length()==0) + { + currState=ZCFGMENU_WIMENU; + showMenu=true; + } + else + { + for(int i=0;i<500;i++) + { + if(serial.isSerialOut()) + serialOutDeque(); + delay(1); + } + if(!connectWifi(WiFi.SSID(lastNumber).c_str(),cmd.c_str(),null,null,null,null)) + serial.printf("%sUnable to connect to %s.%s",EOLNC,WiFi.SSID(lastNumber).c_str(),EOLNC); + else + { + //setNewStaticIPs(null,null,null,null); + useDHCP=(staticIP==null); + lastIP=(staticIP != null)?*staticIP:WiFi.localIP(); + lastDNS=(staticDNS != null)?*staticDNS:IPAddress(192,168,0,1); + lastGW=(staticGW != null)?*staticGW:IPAddress(192,168,0,1); + lastSN=(staticSN != null)?*staticSN:IPAddress(255,255,255,0); + wifiSSI=WiFi.SSID(lastNumber); + wifiPW=cmd; + settingsChanged=true; + currState=ZCFGMENU_NETMENU; + } + showMenu=true; + } + break; + case ZCFGMENU_FLOW: + if(cmd.length()==0) + { + currState=ZCFGMENU_WIMENU; + showMenu=true; + } + else + { + currState=ZCFGMENU_MAIN; + showMenu=true; + if(c=='x') + commandMode.serial.setFlowControlType(FCT_NORMAL); + else + if(c=='r') + commandMode.serial.setFlowControlType(FCT_RTSCTS); + else + if(c=='d') + commandMode.serial.setFlowControlType(FCT_DISABLED); + else + { + serial.printf("%sUnknown flow control type '%s'. Try again.%s",EOLNC,cmd.c_str(),EOLNC); + currState=ZCFGMENU_FLOW; + } + settingsChanged = settingsChanged || (currState ==ZCFGMENU_MAIN); + serial.setFlowControlType(commandMode.serial.getFlowControlType()); + serial.setXON(true); + } + break; + } +} + +void ZConfig::loop() +{ + if(showMenu) + { + showMenu=false; + switch(currState) + { + case ZCFGMENU_MAIN: + { + serial.printf("%sMain Menu%s",EOLNC,EOLNC); + serial.printf("[HOST] name: %s%s",hostname.c_str(),EOLNC); + serial.printf("[WIFI] connection: %s%s",(WiFi.status() == WL_CONNECTED)?wifiSSI.c_str():"Not connected",EOLNC); + String flowName; + switch(commandMode.serial.getFlowControlType()) + { + case FCT_NORMAL: + flowName = "XON/XOFF"; + break; + case FCT_RTSCTS: + flowName = "RTS/CTS"; + break; + case FCT_DISABLED: + flowName = "DISABLED"; + break; + default: + flowName = "OTHER"; + break; + } + String bbsMode = "DISABLED"; + if(newListen) + { + bbsMode = "Port "; + bbsMode += serverSpec.port; + } + serial.printf("[FLOW] control: %s%s",flowName.c_str(),EOLNC); + serial.printf("[ECHO] keystrokes: %s%s",savedEcho?"ON":"OFF",EOLNC); + serial.printf("[BBS] host: %s%s",bbsMode.c_str(),EOLNC); + serial.printf("[PRINT] spec: %s%s",printMode.getLastPrinterSpec(),EOLNC); + serial.printf("[PETSCII] translation: %s%s",commandMode.serial.isPetsciiMode()?"ON":"OFF",EOLNC); + serial.printf("[ADD] new phonebook entry%s",EOLNC); + PhoneBookEntry *p = phonebook; + if(p != null) + { + serial.printf("Phonebook entries:%s",EOLNC); + while(p != null) + { + if(strlen(p->notes)>0) + serial.printf(" [%lu] %s (%s)%s",p->number, p->address, p->notes, EOLNC); + else + serial.printf(" [%lu] %s%s",p->number, p->address, EOLNC); + p=p->next; + } + } + serial.printf("%sEnter command or entry or ENTER to exit: ",EOLNC,EOLNC); + break; + } + case ZCFGMENU_NUM: + serial.printf("%sEnter a new fake phone number (digits ONLY)%s: ",EOLNC,EOLNC); + break; + case ZCFGMENU_NEWPORT: + serial.printf("%sEnter a port number to listen on%s: ",EOLNC,EOLNC); + break; + case ZCFGMENU_ADDRESS: + { + PhoneBookEntry *lastEntry = PhoneBookEntry::findPhonebookEntry(lastNumber); + if(lastEntry == null) + serial.printf("%sEnter a new hostname:port%s: ",EOLNC,EOLNC); + else + serial.printf("%sModify hostname:port, or enter DELETE (%s)%s: ",EOLNC,lastAddress.c_str(),EOLNC); + break; + } + case ZCFGMENU_OPTIONS: + { + ConnSettings flags(lastOptions.c_str()); + serial.printf("%sConnection Options:%s",EOLNC,EOLNC); + serial.printf("[PETSCII] Translation: %s%s",flags.petscii?"ON":"OFF",EOLNC); + serial.printf("[TELNET] Translation: %s%s",flags.telnet?"ON":"OFF",EOLNC); + serial.printf("[ECHO]: %s%s",flags.echo?"ON":"OFF",EOLNC); + serial.printf("[FLOW] Control: %s%s",flags.xonxoff?"XON/XOFF":flags.rtscts?"RTS/CTS":"DISABLED",EOLNC); + serial.printf("%sEnter option to toggle or ENTER to exit%s: ",EOLNC,EOLNC); + break; + } + case ZCFGMENU_NOTES: + { + serial.printf("%sEnter some notes for this entry (%s)%s: ",EOLNC,lastNotes.c_str(),EOLNC); + break; + } + case ZCFGMENU_BBSMENU: + { + serial.printf("%sBBS host settings:%s",EOLNC,EOLNC); + if(newListen) + { + ConnSettings flags(serverSpec.flagsBitmap); + serial.printf("%s[HOST] Listener Port: %d%s",EOLNC,serverSpec.port,EOLNC); + serial.printf("[PETSCII] Translation: %s%s",flags.petscii?"ON":"OFF",EOLNC); + serial.printf("[TELNET] Translation: %s%s",flags.telnet?"ON":"OFF",EOLNC); + serial.printf("[ECHO]: %s%s",flags.echo?"ON":"OFF",EOLNC); + serial.printf("[FLOW] Control: %s%s",flags.xonxoff?"XON/XOFF":flags.rtscts?"RTS/CTS":"DISABLED",EOLNC); + serial.printf("[DISABLE] BBS host listener%s",EOLNC); + } + else + serial.printf("%s[ENABLE] BBS host listener%s",EOLNC,EOLNC); + serial.printf("%sEnter option to toggle or ENTER to exit%s: ",EOLNC,EOLNC); + break; + } + case ZCFGMENU_SUBNET: + { + String str; + switch(netOpt) + { + case 'i': + { + ConnSettings::IPtoStr(&lastIP,str); + serial.printf("%sIP Address (%s)%s: %s",EOLNC,str.c_str(),EOLNC,EOLNC); + break; + } + case 'g': + { + ConnSettings::IPtoStr(&lastGW,str); + serial.printf("%sGateway Address (%s)%s: %s",EOLNC,str.c_str(),EOLNC,EOLNC); + break; + } + case 's': + { + ConnSettings::IPtoStr(&lastSN,str); + serial.printf("%sSubnet Mask Address (%s)%s: %s",EOLNC,str.c_str(),EOLNC,EOLNC); + break; + } + case 'd': + { + ConnSettings::IPtoStr(&lastDNS,str); + serial.printf("%sDNS Address (%s)%s: %s",EOLNC,str.c_str(),EOLNC,EOLNC); + break; + } + } + break; + } + case ZCFGMENU_NETMENU: + { + serial.printf("%sNetwork settings:%s",EOLNC,EOLNC); + if(!useDHCP) + { + String str; + ConnSettings::IPtoStr(&lastIP,str); + serial.printf("%s[IP]: %s%s",EOLNC,str.c_str(),EOLNC); + ConnSettings::IPtoStr(&lastSN,str); + serial.printf("[SUBNET]: %s%s",str.c_str(),EOLNC); + ConnSettings::IPtoStr(&lastGW,str); + serial.printf("[GATEWAY]: %s%s",str.c_str(),EOLNC); + ConnSettings::IPtoStr(&lastDNS,str); + serial.printf("[DNS]: %s%s",str.c_str(),EOLNC); + serial.printf("[ENABLE] Enable DHCP%s",EOLNC); + } + else + serial.printf("%s[DISABLE] DHCP%s",EOLNC,EOLNC); + serial.printf("%sEnter option to toggle or ENTER to exit%s: ",EOLNC,EOLNC); + break; + } + case ZCFGMENU_WIMENU: + { + int n = WiFi.scanNetworks(); + if(n>20) + n=20; + serial.printf("%sWiFi Networks:%s",EOLNC,EOLNC); + lastNumNetworks=n; + for (int i = 0; i < n; ++i) + { + serial.printf("[%d] ",(i+1)); + serial.prints(WiFi.SSID(i).c_str()); + serial.prints(" ("); + serial.printi(WiFi.RSSI(i)); + serial.prints(")"); + serial.prints((WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":"*"); + serial.prints(EOLN.c_str()); + serial.flush(); + delay(10); + } + serial.printf("%sEnter number to connect, or ENTER: ",EOLNC); + break; + } + case ZCFGMENU_NEWHOST: + { + serial.printf("%sEnter a new hostname: ",EOLNC); + break; + } + case ZCFGMENU_NEWPRINT: + { + serial.printf("%sEnter ipp printer spec (?::/path)%s: ",EOLNC,EOLNC); + break; + } + case ZCFGMENU_WIFIPW: + { + serial.printf("%sEnter your WiFi Password: ",EOLNC); + break; + } + case ZCFGMENU_FLOW: + { + serial.printf("%sEnter RTS/CTS, XON/XOFF, or DISABLE flow control%s: ",EOLNC,EOLNC); + break; + } + case ZCFGMENU_WICONFIRM: + { + serial.printf("%sYour setting changed. Save (y/N)?",EOLNC); + break; + } + } + } + if(commandMode.checkPlusEscape()) + { + switchBackToCommandMode(); + } + else + if(serial.isSerialOut()) + { + serialOutDeque(); + } +} + diff --git a/zimodem/zslip.h b/zimodem/zhostcmmode.h similarity index 66% rename from zimodem/zslip.h rename to zimodem/zhostcmmode.h index 1eb9f7d..ed64902 100644 --- a/zimodem/zslip.h +++ b/zimodem/zhostcmmode.h @@ -1,35 +1,34 @@ -/* - Copyright 2016-2016 Bo Zimmerman - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -class ZSlip : public ZMode -{ - private: - unsigned long lastNonPlusTimeMs = 0; - unsigned long currentExpiresTimeMs = 0; - int plussesInARow=0; - ZSerial serial; - long nextAlarm = millis() + 5000; - - void switchBackToCommandMode(bool logout); - void socketWrite(uint8_t c); - - public: - - void switchTo(); - void serialIncoming(); - void loop(); -}; +/* + Copyright 2016-2019 Bo Zimmerman + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifdef INCLUDE_SD_SHELL +#ifdef INCLUDE_HOSTCM +#include "proto_hostcm.h" + +class ZHostCMMode : public ZMode +{ + private: + void switchBackToCommandMode(); + HostCM *proto = 0; + + public: + void switchTo(); + void serialIncoming(); + void loop(); +}; + +#endif +#endif diff --git a/zimodem/zhostcmmode.ino b/zimodem/zhostcmmode.ino new file mode 100644 index 0000000..e410466 --- /dev/null +++ b/zimodem/zhostcmmode.ino @@ -0,0 +1,47 @@ +/* + Copyright 2016-2019 Bo Zimmerman + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#ifdef INCLUDE_SD_SHELL +#ifdef INCLUDE_HOSTCM +void ZHostCMMode::switchBackToCommandMode() +{ + if(proto != 0) + delete proto; + proto = 0; + currMode = &commandMode; +} + +void ZHostCMMode::switchTo() +{ + currMode=&hostcmMode; + if(proto == 0) + proto = new HostCM(&SD); +} + +void ZHostCMMode::serialIncoming() +{ + if(proto != 0) + proto->receiveLoop(); +} + +void ZHostCMMode::loop() +{ + serialOutDeque(); + if((proto != 0) && (proto->isAborted())) + switchBackToCommandMode(); +} + +#endif +#endif diff --git a/zimodem/zimodem.ino b/zimodem/zimodem.ino index 28c9470..e8aa105 100644 --- a/zimodem/zimodem.ino +++ b/zimodem/zimodem.ino @@ -1,35 +1,142 @@ /* - Copyright 2016-2017 Bo Zimmerman + Copyright 2016-2019 Bo Zimmerman Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.https://www.amazon.com/# + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. + limitations under the License. */ - -#define TCP_SND_BUF 4 * TCP_MSS -#define null 0 -#define ZIMODEM_VERSION "3.3" +//#define TCP_SND_BUF 4 * TCP_MSS +#define ZIMODEM_VERSION "3.7.0" +const char compile_date[] = __DATE__ " " __TIME__; #define DEFAULT_NO_DELAY true -#define DEFAULT_PIN_DCD 2 -#define DEFAULT_PIN_CTS 5 -#define DEFAULT_PIN_RTS 4 -#define DEFAULT_DCD_HIGH HIGH -#define DEFAULT_DCD_LOW LOW -#define DEFAULT_CTS_HIGH HIGH -#define DEFAULT_CTS_LOW LOW -#define DEFAULT_RTS_HIGH HIGH -#define DEFAULT_RTS_LOW LOW +#define null 0 +#define INCLUDE_IRCC true +//#define INCLUDE_SLIP true +//# define USE_DEVUPDATER true // only enable this if your name is Bo + +#ifdef ARDUINO_ESP32_DEV +# define ZIMODEM_ESP32 +#elif defined(ESP32) +# define ZIMODEM_ESP32 +#elif defined(ARDUINO_ESP320) +# define ZIMODEM_ESP32 +#elif defined(ARDUINO_NANO32) +# define ZIMODEM_ESP32 +#elif defined(ARDUINO_LoLin32) +# define ZIMODEM_ESP32 +#elif defined(ARDUINO_ESPea32) +# define ZIMODEM_ESP32 +#elif defined(ARDUINO_QUANTUM) +# define ZIMODEM_ESP32 +#else +# define ZIMODEM_ESP8266 +#endif + +#ifdef SUPPORT_LED_PINS +# ifdef GPIO_NUM_0 +# define DEFAULT_PIN_AA GPIO_NUM_16 +# define DEFAULT_PIN_HS GPIO_NUM_15 +# define DEFAULT_PIN_WIFI GPIO_NUM_0 +# else +# define DEFAULT_PIN_AA 16 +# define DEFAULT_PIN_HS 15 +# define DEFAULT_PIN_WIFI 0 +# endif +# define DEFAULT_HS_BAUD 38400 +# define DEFAULT_AA_ACTIVE LOW +# define DEFAULT_AA_INACTIVE HIGH +# define DEFAULT_HS_ACTIVE LOW +# define DEFAULT_HS_INACTIVE HIGH +# define DEFAULT_WIFI_ACTIVE LOW +# define DEFAULT_WIFI_INACTIVE HIGH +#endif + +#ifdef ZIMODEM_ESP32 +# define PIN_FACTORY_RESET GPIO_NUM_0 +# define DEFAULT_PIN_DCD GPIO_NUM_14 +# define DEFAULT_PIN_CTS GPIO_NUM_13 +# define DEFAULT_PIN_RTS GPIO_NUM_15 // unused +# define DEFAULT_PIN_RI GPIO_NUM_32 +# define DEFAULT_PIN_DSR GPIO_NUM_12 +# define DEFAULT_PIN_DTR GPIO_NUM_27 +# define debugPrintf Serial.printf +# define INCLUDE_SD_SHELL true +# define DEFAULT_FCT FCT_DISABLED +# define SerialConfig uint32_t +# define UART_CONFIG_MASK 0x8000000 +# define UART_NB_BIT_MASK 0B00001100 | UART_CONFIG_MASK +# define UART_NB_BIT_5 0B00000000 | UART_CONFIG_MASK +# define UART_NB_BIT_6 0B00000100 | UART_CONFIG_MASK +# define UART_NB_BIT_7 0B00001000 | UART_CONFIG_MASK +# define UART_NB_BIT_8 0B00001100 | UART_CONFIG_MASK +# define UART_PARITY_MASK 0B00000011 +# define UART_PARITY_NONE 0B00000000 +# define UART_NB_STOP_BIT_MASK 0B00110000 +# define UART_NB_STOP_BIT_0 0B00000000 +# define UART_NB_STOP_BIT_1 0B00010000 +# define UART_NB_STOP_BIT_15 0B00100000 +# define UART_NB_STOP_BIT_2 0B00110000 +# define preEOLN serial.prints +# define echoEOLN serial.write +//# define HARD_DCD_HIGH 1 +//# define HARD_DCD_LOW 1 +//# define INCLUDE_HOSTCM true // do this for special SP9000 modems only +#else // ESP-8266, e.g. ESP-01, ESP-12E, inverted for C64Net WiFi Modem +# define DEFAULT_PIN_DSR 13 +# define DEFAULT_PIN_DTR 12 +# define DEFAULT_PIN_RI 14 +# define DEFAULT_PIN_RTS 4 +# define DEFAULT_PIN_CTS 5 // is 0 for ESP-01, see getDefaultCtsPin() below. +# define DEFAULT_PIN_DCD 2 +# define DEFAULT_FCT FCT_RTSCTS +# define RS232_INVERTED 1 +# define debugPrintf doNothing +# define preEOLN(...) +# define echoEOLN(...) serial.prints(EOLN) +#endif + +#ifdef RS232_INVERTED +# define DEFAULT_DCD_HIGH HIGH +# define DEFAULT_DCD_LOW LOW +# define DEFAULT_CTS_HIGH HIGH +# define DEFAULT_CTS_LOW LOW +# define DEFAULT_RTS_HIGH HIGH +# define DEFAULT_RTS_LOW LOW +# define DEFAULT_RI_HIGH HIGH +# define DEFAULT_RI_LOW LOW +# define DEFAULT_DSR_HIGH HIGH +# define DEFAULT_DSR_LOW LOW +# define DEFAULT_DTR_HIGH HIGH +# define DEFAULT_DTR_LOW LOW +#else +# define DEFAULT_DCD_HIGH LOW +# define DEFAULT_DCD_LOW HIGH +# define DEFAULT_CTS_HIGH LOW +# define DEFAULT_CTS_LOW HIGH +# define DEFAULT_RTS_HIGH LOW +# define DEFAULT_RTS_LOW HIGH +# define DEFAULT_RI_HIGH LOW +# define DEFAULT_RI_LOW HIGH +# define DEFAULT_DSR_HIGH LOW +# define DEFAULT_DSR_LOW HIGH +# define DEFAULT_DTR_HIGH LOW +# define DEFAULT_DTR_LOW HIGH +#endif + #define DEFAULT_BAUD_RATE 1200 #define DEFAULT_SERIAL_CONFIG SERIAL_8N1 - +#define MAX_PIN_NO 50 +#define INTERNAL_FLOW_CONTROL_DIV 380 +#define DEFAULT_RECONNECT_DELAY 5000 +#define MAX_RECONNECT_DELAY 1800000 class ZMode { @@ -39,22 +146,64 @@ class ZMode }; #include "pet2asc.h" -#include "zlog.h" -#include "zserout.h" +#include "rt_clock.h" +#include "filelog.h" +#include "serout.h" +#include "connSettings.h" #include "wificlientnode.h" +#include "stringstream.h" +#include "phonebook.h" #include "wifiservernode.h" #include "zstream.h" -#include "zslip.h" +#include "proto_http.h" +#include "proto_ftp.h" +#include "zconfigmode.h" #include "zcommand.h" +#include "zprint.h" + +#ifdef INCLUDE_SD_SHELL +# ifdef INCLUDE_HOSTCM +# include "zhostcmmode.h" +# endif +# include "proto_xmodem.h" +# include "proto_zmodem.h" +# include "proto_kermit.h" +# include "zbrowser.h" +#endif +#ifdef INCLUDE_SLIP +# include "zslipmode.h" +#endif +#ifdef INCLUDE_IRCC +# include "zircmode.h" +#endif static WiFiClientNode *conns = null; static WiFiServerNode *servs = null; static PhoneBookEntry *phonebook = null; +static bool pinSupport[MAX_PIN_NO]; +static bool browseEnabled = false; +static String termType = DEFAULT_TERMTYPE; +static String busyMsg = DEFAULT_BUSYMSG; static ZMode *currMode = null; static ZStream streamMode; -static ZSlip slipMode; +//static ZSlip slipMode; // not yet implemented static ZCommand commandMode; +static ZPrint printMode; +static ZConfig configMode; +static RealTimeClock zclock(0); +#ifdef INCLUDE_SD_SHELL +# ifdef INCLUDE_HOSTCM + static ZHostCMMode hostcmMode; +# endif + static ZBrowser browseMode; +#endif +#ifdef INCLUDE_SLIP + static ZSLIPMode slipMode; +#endif +#ifdef INCLUDE_IRCC + static ZIRCMode ircMode; +#endif enum BaudState { @@ -64,46 +213,136 @@ enum BaudState BS_SWITCH_NORMAL_NEXT }; -static bool wifiConnected =false; static String wifiSSI; static String wifiPW; +static String hostname; +static IPAddress *staticIP = null; +static IPAddress *staticDNS = null; +static IPAddress *staticGW = null; +static IPAddress *staticSN = null; +static unsigned long lastConnectAttempt = 0; +static unsigned long nextReconnectDelay = 0; // zero means don't attempt reconnects static SerialConfig serialConfig = DEFAULT_SERIAL_CONFIG; static int baudRate=DEFAULT_BAUD_RATE; +static int dequeSize=1+(DEFAULT_BAUD_RATE/INTERNAL_FLOW_CONTROL_DIV); static BaudState baudState = BS_NORMAL; +static unsigned long resetPushTimer=0; static int tempBaud = -1; // -1 do nothing static int dcdStatus = LOW; static int pinDCD = DEFAULT_PIN_DCD; static int pinCTS = DEFAULT_PIN_CTS; static int pinRTS = DEFAULT_PIN_RTS; +static int pinDSR = DEFAULT_PIN_DSR; +static int pinDTR = DEFAULT_PIN_DTR; +static int pinRI = DEFAULT_PIN_RI; static int dcdActive = DEFAULT_DCD_HIGH; static int dcdInactive = DEFAULT_DCD_LOW; static int ctsActive = DEFAULT_CTS_HIGH; static int ctsInactive = DEFAULT_CTS_LOW; static int rtsActive = DEFAULT_RTS_HIGH; static int rtsInactive = DEFAULT_RTS_LOW; +static int riActive = DEFAULT_RI_HIGH; +static int riInactive = DEFAULT_RI_LOW; +static int dtrActive = DEFAULT_DTR_HIGH; +static int dtrInactive = DEFAULT_DTR_LOW; +static int dsrActive = DEFAULT_DSR_HIGH; +static int dsrInactive = DEFAULT_DSR_LOW; static int getDefaultCtsPin() { - if((ESP.getFlashChipSize()/1024)>=4096) // assume this is a striketerm/esp12e +#ifdef ZIMODEM_ESP32 + return DEFAULT_PIN_CTS; +#else + if((ESP.getFlashChipRealSize()/1024)>=4096) // assume this is a striketerm/esp12e return DEFAULT_PIN_CTS; else return 0; +#endif } -static bool connectWifi(const char* ssid, const char* password) +static void doNothing(const char* format, ...) { - int WiFiCounter = 0; - if(WiFi.status() == WL_CONNECTED) +} + +static void s_pinWrite(uint8_t pinNo, uint8_t value) +{ + if(pinSupport[pinNo]) + { + digitalWrite(pinNo, value); + } +} + +static void setHostName(const char *hname) +{ +#ifdef ZIMODEM_ESP32 + tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, hname); +#else + WiFi.hostname(hname); +#endif +} + +static void setNewStaticIPs(IPAddress *ip, IPAddress *dns, IPAddress *gateWay, IPAddress *subNet) +{ + if(staticIP != null) + free(staticIP); + staticIP = ip; + if(staticDNS != null) + free(staticDNS); + staticDNS = dns; + if(staticGW != null) + free(staticGW); + staticGW = gateWay; + if(staticSN != null) + free(staticSN); + staticSN = subNet; +} + +static bool connectWifi(const char* ssid, const char* password, IPAddress *ip, IPAddress *dns, IPAddress *gateWay, IPAddress *subNet) +{ + while(WiFi.status() == WL_CONNECTED) + { WiFi.disconnect(); + delay(100); + yield(); + } +#ifndef ZIMODEM_ESP32 + if(hostname.length() > 0) + setHostName(hostname.c_str()); +#endif WiFi.mode(WIFI_STA); + if((ip != null)&&(gateWay != null)&&(dns != null)&&(subNet!=null)) + { + if(!WiFi.config(*ip,*gateWay,*subNet,*dns)) + return false; + } WiFi.begin(ssid, password); - while (WiFi.status() != WL_CONNECTED && WiFiCounter < 30) + if(hostname.length() > 0) + setHostName(hostname.c_str()); + bool amConnected = (WiFi.status() == WL_CONNECTED) && (strcmp(WiFi.localIP().toString().c_str(), "0.0.0.0")!=0); + int WiFiCounter = 0; + while ((!amConnected) && (WiFiCounter < 20)) { - delay(1000); WiFiCounter++; + if(!amConnected) + delay(500); + amConnected = (WiFi.status() == WL_CONNECTED) && (strcmp(WiFi.localIP().toString().c_str(), "0.0.0.0")!=0); + } + lastConnectAttempt = millis(); + if(lastConnectAttempt == 0) + lastConnectAttempt = 1; // 0 is a special case, so skip it + + if(!amConnected) + { + nextReconnectDelay = 0; // assume no retry is desired.. let the caller set it up, as it could be bad PW + WiFi.disconnect(); } - wifiConnected = WiFi.status() == WL_CONNECTED; - return wifiConnected; + else + nextReconnectDelay = DEFAULT_RECONNECT_DELAY; // if connected, we always want to try reconns in the future + +#ifdef SUPPORT_LED_PINS + s_pinWrite(DEFAULT_PIN_WIFI,(WiFi.status() == WL_CONNECTED)?DEFAULT_WIFI_ACTIVE:DEFAULT_WIFI_INACTIVE); +#endif + return (WiFi.status() == WL_CONNECTED); } static void checkBaudChange() @@ -111,15 +350,11 @@ static void checkBaudChange() switch(baudState) { case BS_SWITCH_TEMP_NEXT: - flushSerial(); // blocking, but very very necessary - delay(500); // give the client half a sec to catch up - Serial.begin(tempBaud, serialConfig); //Change baud rate + changeBaudRate(tempBaud); baudState = BS_SWITCHED_TEMP; break; case BS_SWITCH_NORMAL_NEXT: - flushSerial(); // blocking, but very very necessary - delay(500); // give the client half a sec to catch up - Serial.begin(baudRate, serialConfig); //Change baud rate + changeBaudRate(baudRate); baudState = BS_NORMAL; break; default: @@ -127,22 +362,50 @@ static void checkBaudChange() } } +static void changeBaudRate(int baudRate) +{ + flushSerial(); // blocking, but very very necessary + delay(500); // give the client half a sec to catch up + logPrintfln("Baud change to %d.\n",baudRate); + debugPrintf("Baud change to %d.\n",baudRate); + dequeSize=1+(baudRate/INTERNAL_FLOW_CONTROL_DIV); + debugPrintf("Deque constant now: %d\n",dequeSize); +#ifdef ZIMODEM_ESP32 + HWSerial.changeBaudRate(baudRate); +#else + HWSerial.begin(baudRate, serialConfig); //Change baud rate +#endif +#ifdef SUPPORT_LED_PINS + s_pinWrite(DEFAULT_PIN_HS,(baudRate>=DEFAULT_HS_BAUD)?DEFAULT_HS_ACTIVE:DEFAULT_HS_INACTIVE); +#endif +} + +static void changeSerialConfig(SerialConfig conf) +{ + flushSerial(); // blocking, but very very necessary + delay(500); // give the client half a sec to catch up + debugPrintf("Config changing %d.\n",(int)conf); + dequeSize=1+(baudRate/INTERNAL_FLOW_CONTROL_DIV); + debugPrintf("Deque constant now: %d\n",dequeSize); +#ifdef ZIMODEM_ESP32 + HWSerial.changeConfig(conf); +#else + HWSerial.begin(baudRate, conf); //Change baud rate +#endif + debugPrintf("Config changed.\n"); +} + static int checkOpenConnections() { - int num = 0; - WiFiClientNode *conn = conns; - while(conn != null) - { - if(conn->isConnected()) - num++; - conn = conn->next; - } + int num=WiFiClientNode::getNumOpenWiFiConnections(); if(num == 0) { - if(dcdStatus == dcdActive) + if((dcdStatus == dcdActive) + &&(dcdStatus != dcdInactive)) { + logPrintfln("DCD going inactive.\n"); dcdStatus = dcdInactive; - digitalWrite(pinDCD,dcdStatus); + s_pinWrite(pinDCD,dcdStatus); if(baudState == BS_SWITCHED_TEMP) baudState = BS_SWITCH_NORMAL_NEXT; if(currMode == &commandMode) @@ -151,10 +414,12 @@ static int checkOpenConnections() } else { - if(dcdStatus == dcdInactive) + if((dcdStatus == dcdInactive) + &&(dcdStatus != dcdActive)) { + logPrintfln("DCD going active.\n"); dcdStatus = dcdActive; - digitalWrite(pinDCD,dcdStatus); + s_pinWrite(pinDCD,dcdStatus); if((tempBaud > 0) && (baudState == BS_NORMAL)) baudState = BS_SWITCH_TEMP_NEXT; } @@ -164,25 +429,132 @@ static int checkOpenConnections() void setup() { + for(int i=0;i=4096) // assume this is a strykelink/esp12e + { + pinSupport[4]=true; + pinSupport[5]=true; + for(int i=9;i<=16;i++) + pinSupport[i]=true; + pinSupport[11]=false; + } +#endif + initSDShell(); currMode = &commandMode; - SPIFFS.begin(); + if(!SPIFFS.begin()) + { + SPIFFS.format(); + SPIFFS.begin(); + debugPrintf("SPIFFS Formatted."); + } + HWSerial.begin(DEFAULT_BAUD_RATE, DEFAULT_SERIAL_CONFIG); //Start Serial +#ifdef ZIMODEM_ESP8266 + HWSerial.setRxBufferSize(1024); +#endif commandMode.loadConfig(); PhoneBookEntry::loadPhonebook(); dcdStatus = dcdInactive; - pinMode(pinRTS,OUTPUT); - pinMode(pinCTS,INPUT); - pinMode(pinDCD,OUTPUT); - digitalWrite(pinRTS,rtsActive); - digitalWrite(pinDCD,dcdStatus); + s_pinWrite(pinDCD,dcdStatus); flushSerial(); - //enableRtsCts = digitalRead(pinCTS) == ctsActive; +#ifdef SUPPORT_LED_PINS + s_pinWrite(DEFAULT_PIN_WIFI,(WiFi.status() == WL_CONNECTED)?DEFAULT_WIFI_ACTIVE:DEFAULT_WIFI_INACTIVE); + s_pinWrite(DEFAULT_PIN_HS,(baudRate>=DEFAULT_HS_BAUD)?DEFAULT_HS_ACTIVE:DEFAULT_HS_INACTIVE); +#endif +} + +void checkReconnect() +{ + if((WiFi.status() != WL_CONNECTED) + &&(nextReconnectDelay>0) + &&(lastConnectAttempt>0) + &&(wifiSSI.length()>0)) + { + unsigned long now=millis(); + if(lastConnectAttempt > now) + lastConnectAttempt=1; + if(now > lastConnectAttempt + nextReconnectDelay) + { + debugPrintf("Attempting Reconnect to %s\n",wifiSSI.c_str()); + unsigned long oldReconnectDelay = nextReconnectDelay; + if(!connectWifi(wifiSSI.c_str(),wifiPW.c_str(),staticIP,staticDNS,staticGW,staticSN)) + debugPrintf("%sUnable to reconnect to %s.\n",wifiSSI.c_str()); + nextReconnectDelay = oldReconnectDelay * 2; + if(nextReconnectDelay > MAX_RECONNECT_DELAY) + nextReconnectDelay = DEFAULT_RECONNECT_DELAY; + } + } +} + +void checkFactoryReset() +{ +#ifdef ZIMODEM_ESP32 + if(!digitalRead(PIN_FACTORY_RESET)) + { + if(resetPushTimer != 1) + { + if(resetPushTimer==0) + { + resetPushTimer=millis(); + if(resetPushTimer==1) + resetPushTimer++; + } + else + if((millis() - resetPushTimer) > 5000) + { + SPIFFS.remove(CONFIG_FILE); + SPIFFS.remove(CONFIG_FILE_OLD); + SPIFFS.remove("/zphonebook.txt"); + SPIFFS.remove("/zlisteners.txt"); + PhoneBookEntry::clearPhonebook(); + SPIFFS.end(); + SPIFFS.format(); + SPIFFS.begin(); + PhoneBookEntry::clearPhonebook(); + if(WiFi.status() == WL_CONNECTED) + WiFi.disconnect(); + baudRate = DEFAULT_BAUD_RATE; + commandMode.loadConfig(); + PhoneBookEntry::loadPhonebook(); + dcdStatus = dcdInactive; + s_pinWrite(pinDCD,dcdStatus); + wifiSSI=""; + delay(500); + zclock.reset(); + commandMode.reset(); + resetPushTimer=1; + } + } + } + else + if(resetPushTimer != 0) + resetPushTimer=0; +#endif } void loop() { - if(Serial.available()) + checkFactoryReset(); + checkReconnect(); + if(HWSerial.available()) { currMode->serialIncoming(); } currMode->loop(); + zclock.tick(); } diff --git a/zimodem/zircmode.h b/zimodem/zircmode.h new file mode 100644 index 0000000..95dc23d --- /dev/null +++ b/zimodem/zircmode.h @@ -0,0 +1,54 @@ +/* + * zircmode.h + * + * Created on: May 18, 2022 + * Author: Bo Zimmerman + */ + +#ifdef INCLUDE_IRCC +class ZIRCMode: public ZMode +{ +private: + ZSerial serial; // storage for serial settings only + bool showMenu; + bool savedEcho; + String EOLN; + const char *EOLNC; + WiFiClientNode *current = null; + unsigned long lastNumber; + unsigned long timeout=0; + String buf; + String nick; + String lastAddress; + String lastOptions; + String lastNotes; + String channelName; + bool joinReceived; + enum ZIRCMenu + { + ZIRCMENU_MAIN=0, + ZIRCMENU_NICK=1, + ZIRCMENU_ADDRESS=2, + ZIRCMENU_NUM=3, + ZIRCMENU_NOTES=4, + ZIRCMENU_OPTIONS=5, + ZIRCMENU_COMMAND=6 + } currState; + enum ZIRCState + { + ZIRCSTATE_WAIT=0, + ZIRCSTATE_COMMAND=1 + } ircState; + + void switchBackToCommandMode(); + void doIRCCommand(); + void loopMenuMode(); + void loopCommandMode(); + +public: + void switchTo(); + void serialIncoming(); + void loop(); +}; + +#endif /* INCLUDE_IRCC */ diff --git a/zimodem/zircmode.ino b/zimodem/zircmode.ino new file mode 100644 index 0000000..ba0fc1e --- /dev/null +++ b/zimodem/zircmode.ino @@ -0,0 +1,542 @@ +/* + * zircmode.ino + * + * Created on: May 18, 2022 + * Author: Bo Zimmerman + */ +#ifdef INCLUDE_IRCC +//https://github.com/bl4de/irc-client/blob/master/irc_client.py +void ZIRCMode::switchBackToCommandMode() +{ + if(current != null) + { + delete current; + current = null; + } + currMode = &commandMode; +} + +void ZIRCMode::switchTo() +{ + currMode=&ircMode; + savedEcho=commandMode.doEcho; + commandMode.doEcho=true; + serial.setFlowControlType(commandMode.serial.getFlowControlType()); + serial.setPetsciiMode(commandMode.serial.isPetsciiMode()); + showMenu=true; + EOLN=commandMode.EOLN; + EOLNC=EOLN.c_str(); + currState=ZIRCMENU_MAIN; + lastNumber=0; + lastAddress=""; + lastOptions=""; + channelName=""; + joinReceived=false; + if(nick.length()==0) + { + randomSeed(millis()); + char tempNick[50]; + sprintf(tempNick,"ChangeMe#%ld",random(1,999)); + nick = tempNick; + } +} + +void ZIRCMode::doIRCCommand() +{ + String cmd = commandMode.getNextSerialCommand(); + char c='?'; + while((cmd.length()>0) + &&(cmd[0]==32)||(cmd[0]==7)) + cmd = cmd.substring(1); + if(cmd.length()>0) + c=lc(cmd[0]); + switch(currState) + { + case ZIRCMENU_MAIN: + { + if((c=='q')||(cmd.length()==0)) + { + commandMode.showInitMessage(); + switchBackToCommandMode(); + return; + } + else + if(c=='a') // add to phonebook + { + currState=ZIRCMENU_NUM; + showMenu=true; + } + else + if(c=='n') // change nick + { + currState=ZIRCMENU_NICK; + showMenu=true; + } + else + if(c>47 && c<58) // its a phonebook entry! + { + PhoneBookEntry *pb=PhoneBookEntry::findPhonebookEntry(cmd); + if(pb == null) + { + serial.printf("%s%sPhone number not found: '%s'.%s%s",EOLNC,EOLNC,cmd.c_str(),EOLNC,EOLNC); + currState=ZIRCMENU_MAIN; + showMenu=true; + } + else + { + serial.printf("%s%sConnecting to %s...%s%s",EOLNC,EOLNC,pb->address,EOLNC,EOLNC); + String address = pb->address; + char vbuf[address.length()+1]; + strcpy(vbuf,pb->address); + char *colon=strstr((char *)vbuf,":"); + int port=6666; + if(colon != null) + { + (*colon)=0; + port=atoi((char *)(++colon)); + } + int flagsBitmap=0; + { + ConnSettings flags(""); + flagsBitmap = flags.getBitmap(serial.getFlowControlType()); + } + logPrintfln("Connnecting: %s %d %d",(char *)vbuf,port,flagsBitmap); + WiFiClientNode *c = new WiFiClientNode((char *)vbuf,port,flagsBitmap); + if(!c->isConnected()) + { + logPrintln("Connnect: FAIL"); + serial.prints("Connection failed.\n\r"); //TODO: maybe get rid of? + currState=ZIRCMENU_MAIN; + showMenu=true; + } + else + { + logPrintfln("Connnect: SUCCESS: %d",c->id); + serial.prints("Connected.\n\r"); //TODO: maybe get rid of? + current=c; + buf = ""; + currState=ZIRCMENU_COMMAND; + ircState=ZIRCSTATE_WAIT; + timeout=millis()+6000; + showMenu=true; // only wait to get it to act + } + } + } + else + { + showMenu=true; // re-show the menu + } + break; + } + case ZIRCMENU_NUM: + { + PhoneBookEntry *pb=PhoneBookEntry::findPhonebookEntry(cmd); + if(pb != null) + { + serial.printf("%s%sNumber already exists '%s'.%s%s",EOLNC,EOLNC,cmd.c_str(),EOLNC,EOLNC); + currState=ZIRCMENU_MAIN; + showMenu=true; + } + else + if(cmd.length()==0) + { + currState=ZIRCMENU_MAIN; + showMenu=true; + } + else + { + lastNumber = atol((char *)cmd.c_str()); + lastAddress = ""; + ConnSettings flags(commandMode.getConfigFlagBitmap()); + lastOptions = flags.getFlagString(); + lastNotes = ""; + currState=ZIRCMENU_ADDRESS; + showMenu=true; + } + break; + } + case ZIRCMENU_ADDRESS: + { + PhoneBookEntry *entry = PhoneBookEntry::findPhonebookEntry(lastNumber); + if(cmd.equalsIgnoreCase("delete") && (entry != null)) + { + delete entry; + currState=ZIRCMENU_MAIN; + serial.printf("%sPhonebook entry deleted.%s%s",EOLNC,EOLNC,EOLNC); + } + else + if((cmd.length()==0) && (entry != null)) + currState=ZIRCMENU_NOTES; // just keep old values + else + { + boolean fail = cmd.indexOf(',') >= 0; + int colonDex=cmd.indexOf(':'); + fail = fail || (colonDex <= 0) || (colonDex == cmd.length()-1); + fail = fail || (colonDex != cmd.lastIndexOf(':')); + if(!fail) + { + for(int i=colonDex+1;i0) + lastNotes=cmd; + currState=ZIRCMENU_OPTIONS; + showMenu=true; // re-show the menu + break; + } + case ZIRCMENU_NICK: + { + if(cmd.length()>0) + nick=cmd; + currState=ZIRCMENU_MAIN; + showMenu=true; // re-show the menu + break; + } + case ZIRCMENU_OPTIONS: + { + if(cmd.length()==0) + { + serial.printf("%sPhonebook entry added.%s%s",EOLNC,EOLNC,EOLNC); + PhoneBookEntry *entry = new PhoneBookEntry(lastNumber,lastAddress.c_str(),lastOptions.c_str(),lastNotes.c_str()); + PhoneBookEntry::savePhonebook(); + currState=ZIRCMENU_MAIN; + } + else + { + ConnSettings flags(lastOptions.c_str()); + switch(c) + { + case 'p': + flags.petscii=!flags.petscii; + break; + case 't': + flags.telnet=!flags.telnet; + break; + case 'e': + flags.echo=!flags.echo; + break; + case 'f': + if(flags.xonxoff) + { + flags.xonxoff=false; + flags.rtscts=true; + } + else + if(flags.rtscts) + flags.rtscts=false; + else + flags.xonxoff=true; + break; + case 's': + flags.secure=!flags.secure; + break; + default: + serial.printf("%sInvalid toggle option '%s'.%s%s",EOLNC,cmd.c_str(),EOLNC,EOLNC); + break; + } + lastOptions = flags.getFlagString(); + } + showMenu=true; // re-show the menu + break; + } + case ZIRCMENU_COMMAND: + { + if(c=='/') + { + String lccmd = cmd; + lccmd.toLowerCase(); + if(lccmd.startsWith("/join ")) + { + int cs=5; + while((cmd.length()0 && joinReceived) + { + serial.println("* Already in "+channelName+": Not Yet Implemented"); + // we are already joined somewhere + } + else + { + channelName = cmd.substring(cs); + if(current != null) + current->print("JOIN "+channelName+"\r\n"); + } + } + else + serial.println("* A channel name is required *"); + } + else + if(lccmd.startsWith("/quit")) + { + if(current != null) + { + current->print("QUIT Good bye!\r\n"); + current->flush(); + delay(1000); + serial.println("Returning to command mode."); + current->markForDisconnect(); + delete current; + current = null; + } + switchBackToCommandMode(); + } + else + { + serial.println("* Unknown command: "+lccmd); + serial.println("* Try /?"); + } + } + else + if((current != null) + &&(joinReceived)) + { + current->print("PRIVMSG "+channelName+": "+cmd); + } + break; + } + } +} + +void ZIRCMode::loopMenuMode() +{ + if(showMenu) + { + showMenu=false; + switch(currState) + { + case ZIRCMENU_MAIN: + { + if(nick.length()==0) + nick = hostname; + serial.printf("%sInternet Relay Chat (IRC) Main Menu%s",EOLNC,EOLNC); + serial.printf("[NICK] name: %s%s",nick.c_str(),EOLNC); + serial.printf("[ADD] new phonebook entry%s",EOLNC); + PhoneBookEntry *p = phonebook; + if(p != null) + { + serial.printf("Phonebook entries:%s",EOLNC); + while(p != null) + { + if(strlen(p->notes)>0) + serial.printf(" [%lu] %s (%s)%s",p->number, p->address, p->notes, EOLNC); + else + serial.printf(" [%lu] %s%s",p->number, p->address, EOLNC); + p=p->next; + } + } + serial.printf("%sEnter command, number to connect to, or ENTER to exit: ",EOLNC,EOLNC); + break; + } + case ZIRCMENU_NUM: + serial.printf("%sEnter a new fake phone number (digits ONLY)%s: ",EOLNC,EOLNC); + break; + case ZIRCMENU_ADDRESS: + { + serial.printf("%sEnter a new hostname:port%s: ",EOLNC,EOLNC); + break; + } + case ZIRCMENU_NICK: + { + serial.printf("%sEnter a new nick-name%s: ",EOLNC,EOLNC); + break; + } + case ZIRCMENU_OPTIONS: + { + ConnSettings flags(lastOptions.c_str()); + serial.printf("%sConnection Options:%s",EOLNC,EOLNC); + serial.printf("[PETSCII] Translation: %s%s",flags.petscii?"ON":"OFF",EOLNC); + serial.printf("[TELNET] Translation: %s%s",flags.telnet?"ON":"OFF",EOLNC); + serial.printf("[ECHO]: %s%s",flags.echo?"ON":"OFF",EOLNC); + serial.printf("[FLOW] Control: %s%s",flags.xonxoff?"XON/XOFF":flags.rtscts?"RTS/CTS":"DISABLED",EOLNC); + serial.printf("%sEnter option to toggle or ENTER to exit%s: ",EOLNC,EOLNC); + break; + } + case ZIRCMENU_NOTES: + { + serial.printf("%sEnter some notes for this entry (%s)%s: ",EOLNC,lastNotes.c_str(),EOLNC); + break; + } + case ZIRCMENU_COMMAND: + { + showMenu=true; // keep coming back here, over and over and over + if((current==null)||(!current->isConnected())) + { + switchBackToCommandMode(); + } + else + { + String cmd; + while((current->available()>0) && (current->isConnected())) + { + uint8_t c = current->read(); + if((c == '\r')||(c == '\n')||(buf.length()>510)) + { + //serial.prints(buf); + if((c=='\r')||(c=='\n')) + { + cmd=buf; + buf=""; + //serial.prints(EOLNC); + break; + } + buf=""; + } + else + buf += (char)c; + } + if(cmd.length()>0) + { + if(cmd.indexOf("PING :")==0) + { + int x = cmd.indexOf(':'); + if(x>0) + current->print("PONG "+cmd.substring(x+1)+"\r\n"); + } + else + switch(ircState) + { + case ZIRCSTATE_WAIT: + { + if(cmd.indexOf("376")>=0) + { + ircState = ZIRCSTATE_COMMAND; + //TODO: say something? + } + else + if(cmd.indexOf("No Ident response")>=0) + { + current->print("NICK "+nick+"\r\n"); + current->print("USER guest 0 * :"+nick+"\r\n"); + } + else + if(cmd.indexOf("433")>=0) + { + if(nick.indexOf("_____")==0) + { + ircState = ZIRCSTATE_WAIT; + delete current; + current = null; + currState = ZIRCMENU_MAIN; + } + else + { + nick = "_" + nick; + current->print("NICK "+nick+"\r\n"); + current->print("USER guest 0 * :"+nick+"\r\n"); + // above was user nick * * : nick + } + } + else + if(cmd.indexOf(":")==0) + { + int x = cmd.indexOf(":",1); + if(x>1) + { + serial.prints(cmd.substring(x+1)); + serial.prints(EOLNC); + } + } + break; + } + case ZIRCSTATE_COMMAND: + { + if((!joinReceived) + && (channelName.length()>0) + && (cmd.indexOf("366")>=0)) + { + joinReceived=true; + serial.prints("Channel joined. Enter a message to send, or /quit."); + serial.prints(EOLNC); + } + int x0 = cmd.indexOf(":"); + int x1 = (x0>=0)?cmd.indexOf(":", x0+1):-1; + if(x1>0) + { + String msg2=cmd.substring(x1+1); + msg2.trim(); + String msg1=cmd.substring(x0+1,x1); + msg1.trim(); + int x2=msg1.indexOf("!"); + if(x2>=0) + serial.print("< "+msg1.substring(0,x2)+"> "+msg2); + else + serial.prints(msg2); + serial.prints(EOLNC); + } + break; + } + default: + serial.prints("unknown state\n\r"); + switchBackToCommandMode(); + break; + } + } + else + if(ircState == ZIRCSTATE_WAIT) + { + if(millis()>timeout) + { + timeout = millis()+60000; + current->print("NICK "+nick+"\r\n"); + current->print("USER guest 0 * :"+nick+"\r\n"); + } + } + } + break; + } + } + } + if(commandMode.checkPlusEscape()) + { + switchBackToCommandMode(); + } + else + if(serial.isSerialOut()) + { + serialOutDeque(); + } +} + +void ZIRCMode::serialIncoming() +{ + bool crReceived=commandMode.readSerialStream(); + commandMode.clearPlusProgress(); // re-check the plus-escape mode + if(crReceived) + { + doIRCCommand(); + } +} + +void ZIRCMode::loop() +{ + loopMenuMode(); + if(commandMode.checkPlusEscape()) + { + switchBackToCommandMode(); + } + else + if(serial.isSerialOut()) + { + serialOutDeque(); + } +} + +#endif /* INCLUDE_IRCC */ diff --git a/zimodem/zlog.ino b/zimodem/zlog.ino deleted file mode 100644 index c079649..0000000 --- a/zimodem/zlog.ino +++ /dev/null @@ -1,199 +0,0 @@ -/* - Copyright 2016-2017 Bo Zimmerman - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#include - -static char HD[3]; -static char HDL[9]; - -static long logStartTime = millis(); -static long lastLogTime = millis(); -static long logCurCount = 0; -static LogMode logMode = NADA; - -static char *TOHEX(uint8_t a) -{ - HD[0] = "0123456789ABCDEF"[(a >> 4) & 0x0f]; - HD[1] = "0123456789ABCDEF"[a & 0x0f]; - HD[2] = 0; - return HD; -} - -static char *TOHEX(unsigned long a) -{ - for(int i=7;i>=0;i--) - { - HDL[i] = "0123456789ABCDEF"[a & 0x0f]; - a = a >> 4; - } - HDL[8] = 0; - char *H=HDL; - if((strlen(H)>2) && (strstr(H,"00")==H)) - H+=2; - return H; -} - -static char *TOHEX(unsigned int a) -{ - for(int i=3;i>=0;i--) - { - HDL[i] = "0123456789ABCDEF"[a & 0x0f]; - a = a >> 4; - } - HDL[4] = 0; - char *H=HDL; - if((strlen(H)>2) && (strstr(H,"00")==H)) - H+=2; - return H; -} - -static char *TOHEX(int a) -{ - return TOHEX((unsigned int)a); -} - -static char *TOHEX(long a) -{ - return TOHEX((unsigned long)a); -} - -static void logInternalOut(const LogMode m, const uint8_t c) -{ - if(logFileOpen) - { - if((m != logMode) - ||(++logCurCount > DBG_BYT_CTR) - ||((millis()-lastLogTime)>expectedSerialTime)) - { - logCurCount=0; - - logMode = m; - logFile.println(""); - switch(m) - { - case NADA: - break; - case SocketIn: - logFile.printf("%s SocI: ",TOHEX(millis()-logStartTime)); - break; - case SocketOut: - logFile.printf("%s SocO: ",TOHEX(millis()-logStartTime)); - break; - case SerialIn: - logFile.printf("%s SerI: ",TOHEX(millis()-logStartTime)); - break; - case SerialOut: - logFile.printf("%s SerO: ",TOHEX(millis()-logStartTime)); - break; - } - } - lastLogTime=millis(); - logFile.print(TOHEX(c)); - logFile.print(" "); - } -} - -static void logSerialOut(const uint8_t c) -{ - logInternalOut(SerialOut,c); -} - -static void logSocketOut(const uint8_t c) -{ - logInternalOut(SocketOut,c); -} - -static void logSerialIn(const uint8_t c) -{ - logInternalOut(SerialIn,c); -} - -static void logSocketIn(const uint8_t c) -{ - logInternalOut(SocketIn,c); -} - -static void logSocketIn(const uint8_t *c, int n) -{ - if(logFileOpen) - { - for(int i=0;iwrite((uint8_t *)s,len); + if(logFileOpen) + { + for(int i=0;i 50) + timeoutDelayMs = ms; +} + +size_t ZPrint::writeChunk(char *s, int len) +{ + char buf[25]; + sprintf(buf,"%x\r\n",len); + writeStr(buf); + outStream->write((uint8_t *)s,len); + if(logFileOpen) + { + for(int i=0;isetNoDelay(false); // we want a delay in this case + outStream = wifiSock; + result = finishSwitchTo(hostIp, req, port, doSSL); + if(result == ZERROR) + { + outStream = null; + delete wifiSock; + wifiSock = null; + } + yield(); + if(pinSupport[pinRTS]) + s_pinWrite(pinRTS, rtsActive); + if((result != ZERROR) + &&(prefix != 0) + &&(strlen(prefix)>0)) + { + writeChunk(prefix,strlen(prefix)); + serialIncoming(); + } + free(workBuf); + return result; +} + +bool ZPrint::testPrinterSpec(const char *vbuf, int vlen, bool petscii) +{ + char *workBuf = (char *)malloc(vlen+1); + strcpy(workBuf, vbuf); + if(petscii) + { + for(int i=0;isetNoDelay(false); // we want a delay in this case + outStream = wifiSock; + announcePrintJob(hostIp,port,req); + ZResult result = finishSwitchTo(hostIp, req, port, doSSL); + free(workBuf); + if(result == ZERROR) + delete wifiSock; + return result; +} + +ZResult ZPrint::finishSwitchTo(char *hostIp, char *req, int port, bool doSSL) +{ + if((wifiSock != null) && (!wifiSock->isConnected())) + return ZERROR; + char portStr[10]; + sprintf(portStr,"%d",port); + // send the request and http headers: + sprintf(pbuf,"POST /%s HTTP/1.1\r\n",req); + writeStr(pbuf); + writeStr("Transfer-Encoding: chunked\r\n"); + writeStr("Content-Type: application/ipp\r\n"); + sprintf(pbuf,"Host: %s:%d\r\n",hostIp,port); + writeStr(pbuf); + writeStr("Connection: Keep-Alive\r\n"); + writeStr("User-Agent: Zimodem\r\n"); + writeStr("Accept-Encoding: gzip,deflate\r\n"); + writeStr("\r\n"); + outStream->flush(); + // send the ipp header + if((wifiSock != null)&&(!wifiSock->isConnected())) + return ZERROR; + + char jobChar1 = '0' + (jobNum / 10); + char jobChar2 = '0' + (jobNum % 10); + if(++jobNum>94) + jobNum=0; + // version operatid reqid------------------ attribtabid + sprintf(pbuf,"%c%c%c%c%c%c%c%c%c",0x01,0x01,0x00,0x02,0x00,0x00,0x00,jobNum+1,0x01); + writeChunk(pbuf,9); + sprintf(pbuf,"%c%c%cattributes-charset%c%cutf-8",0x47,0x00,0x12,0x00,0x05); + writeChunk(pbuf,28); + sprintf(pbuf,"%c%c%cattributes-natural-language%c%cen-us",0x48,0x00,0x1b,0x00,0x05); + writeChunk(pbuf,37); + + int urllen = strlen(hostIp) + strlen(req)+ strlen(portStr)+9; + sprintf(pbuf,"%c%c%cprinter-uri%c%chttp://%s:%s/%s",0x45,0x00,0x0b,0x00,urllen,hostIp,portStr,req); + writeChunk(pbuf,urllen+16); + sprintf(pbuf,"%c%c%crequesting-user-name%c%czimodem",0x42,0x00,0x14,0x00,0x07); + writeChunk(pbuf,32); + sprintf(pbuf,"%c%c%cjob-name%c%czimodem-j%c%c",0x42,0x00,0x08,0x00,0x0b,jobChar1,jobChar2); + writeChunk(pbuf,24); + sprintf(pbuf,"%c%c%c%ccopies%c%c%c%c%c%c",0x02,0x21,0x00,0x06,0x00,0x04,0x00,0x00,0x00,0x01); + writeChunk(pbuf,16); + sprintf(pbuf,"%c%c%corientation-requested%c%c%c%c%c%c",0x23,0x00,0x15,0x00,0x04,0x00,0x00,0x00,0x03); + writeChunk(pbuf,30); + sprintf(pbuf,"%c%c%coutput-mode%c%cmonochrome%c",0x44,0x00,0x0b,0x00,0x0a, 0x03); + writeChunk(pbuf,27); + outStream->flush(); + if((wifiSock != null)&&(!wifiSock->isConnected())) + return ZERROR; + checkOpenConnections(); + checkBaudChange(); + pdex=0; + coldex=0; + lastNonPlusTimeMs = 0; + plussesInARow=0; + currentExpiresTimeMs = millis()+5000; + currMode=&printMode; + return ZIGNORE; +} + +void ZPrint::serialIncoming() +{ + if(HWSerial.available() > 0) + { + while(HWSerial.available() > 0) + { + uint8_t c=HWSerial.read(); + logSerialIn(c); + if((c==commandMode.EC) + &&(plussesInARow<3) + &&((plussesInARow>0)||((millis()-lastNonPlusTimeMs)>900))) + { + plussesInARow++; + continue; + } + else + { + if(plussesInARow > 0) + { + for(int i=0;i 80) + { + pbuf[pdex++]='\n'; + pbuf[pdex++]='\r'; + coldex=1; + } + else + if((lastC == '\n')&&(lastLastC!='\r')) + pbuf[pdex++]='\r'; + else + if((lastC == '\r')&&(lastLastC!='\n')) + pbuf[pdex++]='\n'; + } + } + pbuf[pdex++]=(char)c; + lastLastC=lastC; + lastC=c; + if(pdex>=250) + { + if(((wifiSock!=null)&&(wifiSock->isConnected())) + ||((wifiSock==null)&&(outStream!=null))) + { + writeChunk(pbuf,pdex); + //wifiSock->flush(); + } + pdex=0; + } + } + if(plussesInARow == 3) + currentExpiresTimeMs = millis()+900; + else + currentExpiresTimeMs = millis()+timeoutDelayMs; + } +} + +void ZPrint::switchBackToCommandMode(bool error) +{ + if((wifiSock != null)||(outStream!=null)) + { + if(error) + commandMode.sendOfficialResponse(ZERROR); + else + commandMode.sendOfficialResponse(ZOK); + if(wifiSock != null) + delete wifiSock; + } + wifiSock = null; + outStream = null; + currMode = &commandMode; +} + +void ZPrint::loop() +{ + if(((wifiSock==null)&&(outStream==null)) + || ((wifiSock!=null)&&(!wifiSock->isConnected()))) + { + debugPrintf("No printer connection\n"); + switchBackToCommandMode(true); + } + else + if(millis()>currentExpiresTimeMs) + { + debugPrintf("Time-out in printing\n"); + if(pdex > 0) + writeChunk(pbuf,pdex); + writeStr("0\r\n\r\n"); + outStream->flush(); + switchBackToCommandMode(false); + } + checkBaudChange(); +} + diff --git a/zimodem/zserout.ino b/zimodem/zserout.ino deleted file mode 100644 index f5e7c08..0000000 --- a/zimodem/zserout.ino +++ /dev/null @@ -1,288 +0,0 @@ -/* - Copyright 2016-2017 Bo Zimmerman - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#include - -static void serialDirectWrite(uint8_t c) -{ - Serial.write(c); - if(serialDelayMs > 0) - delay(serialDelayMs); - logSerialOut(c); -} - -static void serialOutDeque() -{ - if((TBUFhead != TBUFtail)&&(Serial.availableForWrite()>=SER_BUFSIZE)) - { - serialDirectWrite(TBUF[TBUFhead]); - TBUFhead++; - if(TBUFhead >= SER_WRITE_BUFSIZE) - TBUFhead = 0; - } -} - -static int serialOutBufferBytesRemaining() -{ - if(TBUFtail == TBUFhead) - return SER_WRITE_BUFSIZE-1; - else - if(TBUFtail > TBUFhead) - { - int used = TBUFtail - TBUFhead; - return SER_WRITE_BUFSIZE - used -1; - } - else - return TBUFhead - TBUFtail - 1; -} - -static void enqueSerialOut(uint8_t c) -{ - TBUF[TBUFtail] = c; - TBUFtail++; - if(TBUFtail >= SER_WRITE_BUFSIZE) - TBUFtail = 0; -} - -static void clearSerialOutBuffer() -{ - TBUFtail=TBUFhead; -} - -static void ensureSerialBytes(int num) -{ - if(serialOutBufferBytesRemaining()<1) - { - serialOutDeque(); - while(serialOutBufferBytesRemaining()<1) - yield(); - } -} - -static void flushSerial() -{ - while(TBUFtail != TBUFhead) - { - serialOutDeque(); - yield(); - } - Serial.flush(); -} - -ZSerial::ZSerial() -{ -} - -void ZSerial::setPetsciiMode(bool petscii) -{ - petsciiMode = petscii; -} - -bool ZSerial::isPetsciiMode() -{ - return petsciiMode; -} - -void ZSerial::setFlowControlType(FlowControlType type) -{ - flowControlType = type; -} - -FlowControlType ZSerial::getFlowControlType() -{ - return flowControlType; -} - -void ZSerial::setXON(bool isXON) -{ - XON = isXON; -} - -bool ZSerial::isXON() -{ - return XON; -} - -bool ZSerial::isSerialOut() -{ - switch(flowControlType) - { - case FCT_RTSCTS: - //if(enableRtsCts) - return (digitalRead(pinCTS) == ctsActive); - //return true; - case FCT_NORMAL: - case FCT_AUTOOFF: - case FCT_MANUAL: - break; - case FCT_DISABLED: - return true; - case FCT_INVALID: - return true; - } - return XON; -} - -bool ZSerial::isSerialCancelled() -{ - if(flowControlType == FCT_RTSCTS) - { - //if(enableRtsCts) - return (digitalRead(pinCTS) == ctsInactive); - } - return false; -} - -bool ZSerial::isSerialHalted() -{ - return !isSerialOut(); -} - - -void ZSerial::prints(const char *expr) -{ - if(!petsciiMode) - { - for(int i=0;expr[i]!=0;i++) - { - enqueSerialOut(expr[i]); - } - } - else - { - for(int i=0;expr[i]!=0;i++) - { - enqueSerialOut(ascToPetcii(expr[i])); - } - } -} - -void ZSerial::printi(int i) -{ - char buf[12]; - prints(itoa(i, buf, 10)); -} - -void ZSerial::printd(double f) -{ - char buf[12]; - prints(dtostrf(f, 2, 2, buf)); -} - -void ZSerial::printc(const char c) -{ - if(!petsciiMode) - enqueSerialOut(c); - else - enqueSerialOut(ascToPetcii(c)); -} - -void ZSerial::printc(uint8_t c) -{ - if(!petsciiMode) - enqueSerialOut(c); - else - enqueSerialOut(ascToPetcii(c)); -} - -void ZSerial::printb(uint8_t c) -{ - enqueSerialOut(c); -} - -void ZSerial::write(uint8_t c) -{ - enqueSerialOut(c); -} - -void ZSerial::prints(String str) -{ - prints(str.c_str()); -} - - -void ZSerial::printf(const char* format, ...) -{ - int ret; - va_list arglist; - va_start(arglist, format); - vsnprintf(FBUF, sizeof(FBUF), format, arglist); - prints(FBUF); - va_end(arglist); -} - -void ZSerial::flushAlways() -{ - while(TBUFtail != TBUFhead) - { - Serial.flush(); - serialOutDeque(); - yield(); - delay(1); - } - Serial.flush(); -} - -void ZSerial::flush() -{ - while((TBUFtail != TBUFhead) && (isSerialOut())) - { - Serial.flush(); - serialOutDeque(); - yield(); - delay(1); - } - Serial.flush(); -} - -int ZSerial::availableForWrite() -{ - return serialOutBufferBytesRemaining(); -} - -char ZSerial::drainForXonXoff() -{ - char ch = '\0'; - while(Serial.available()>0) - { - ch=Serial.read(); - logSerialIn(ch); - if(ch == 3) - break; - switch(flowControlType) - { - case FCT_NORMAL: - if((!XON) && (ch == 17)) - XON=true; - else - if((XON) && (ch == 19)) - XON=false; - break; - case FCT_AUTOOFF: - case FCT_MANUAL: - if((!XON) && (ch == 17)) - XON=true; - else - XON=false; - break; - case FCT_INVALID: - break; - case FCT_RTSCTS: - break; - } - } - return ch; -} diff --git a/zimodem/zslip.ino b/zimodem/zslip.ino deleted file mode 100644 index 404470f..0000000 --- a/zimodem/zslip.ino +++ /dev/null @@ -1,85 +0,0 @@ -/* - Copyright 2016-2017 Bo Zimmerman - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#include "ESP8266WiFi.h" - -void ZSlip::switchTo() -{ - currentExpiresTimeMs = 0; - lastNonPlusTimeMs = 0; - plussesInARow=0; - serial.setXON(true); - serial.setPetsciiMode(false); - serial.setFlowControlType(FCT_RTSCTS); - currMode=&slipMode; - checkBaudChange(); -} - -void ZSlip::serialIncoming() -{ - int serialAvailable = Serial.available(); - if(serialAvailable == 0) - return; - while(--serialAvailable >= 0) - { - uint8_t c=Serial.read(); - logSerialIn(c); - if((c==commandMode.EC) - &&((plussesInARow>0)||((millis()-lastNonPlusTimeMs)>800))) - plussesInARow++; - else - if(c!=commandMode.EC) - { - plussesInARow=0; - lastNonPlusTimeMs=millis(); - } - //socketWrite(c); *** do the slip packet decode - } - - currentExpiresTimeMs = 0; - if(plussesInARow==3) - currentExpiresTimeMs=millis()+800; -} - -void ZSlip::switchBackToCommandMode(bool logout) -{ - currMode = &commandMode; -} - -void ZSlip::socketWrite(uint8_t c) -{ - // do something special? -} - -void ZSlip::loop() -{ - if((currentExpiresTimeMs > 0) && (millis() > currentExpiresTimeMs)) - { - currentExpiresTimeMs = 0; - if(plussesInARow == 3) - { - plussesInARow=0; - switchBackToCommandMode(false); - } - } - else - if(serial.isSerialOut()) - { - // probably loop through all the open sockets and do something interesting. - } - checkBaudChange(); -} - diff --git a/zimodem/zslipmode.h b/zimodem/zslipmode.h new file mode 100644 index 0000000..f69408e --- /dev/null +++ b/zimodem/zslipmode.h @@ -0,0 +1,21 @@ +/* + * zslipmode.h + * + * Created on: May 17, 2022 + * Author: Bo Zimmerman + */ + +#ifdef INCLUDE_SLIP + +class ZSLIPMode: public ZMode +{ +private: + void switchBackToCommandMode(); + +public: + void switchTo(); + void serialIncoming(); + void loop(); +}; + +#endif /* INCLUDE_SLIP_ */ diff --git a/zimodem/zslipmode.ino b/zimodem/zslipmode.ino new file mode 100644 index 0000000..ffd8562 --- /dev/null +++ b/zimodem/zslipmode.ino @@ -0,0 +1,45 @@ +/* + * zslipmode.ino + * + * Created on: May 17, 2022 + * Author: Bo Zimmerman + */ +#ifdef INCLUDE_SLIP + +#include "zslipmode.h" +#include "lwip/ip.h" +#include "lwip/netif.h" +#include "netif/slipif.h" + +void ZSLIPMode::switchBackToCommandMode() +{ + currMode = &commandMode; +} + +void ZSLIPMode::switchTo() +{ + struct netif sl_netif; + ip_addr_t ipaddr; + ip_addr_t netmask; + ip_addr_t gw; + char int_no = 2; + + IP4_ADDR(&ipaddr, WiFi.localIP()[0], WiFi.localIP()[1], WiFi.localIP()[2], WiFi.localIP()[3]); + IP4_ADDR(&netmask, WiFi.subnetMask()[0], WiFi.subnetMask()[1], WiFi.subnetMask()[2], WiFi.subnetMask()[3]); + IP4_ADDR(&gw, WiFi.gatewayIP()[0], WiFi.gatewayIP()[1], WiFi.gatewayIP()[2], WiFi.gatewayIP()[3]); + netif_add (&sl_netif, &ipaddr, &netmask, &gw, &int_no, slipif_init, ip_input); + netif_set_up(&sl_netif); + //ip_napt_enable(ipaddr.addr, 1); + currMode=&slipMode; +} + +void ZSLIPMode::serialIncoming() +{ +} + +void ZSLIPMode::loop() +{ + serialOutDeque(); + //switchBackToCommandMode(); +} +#endif /* INCLUDE_SLIP_ */ diff --git a/zimodem/zstream.h b/zimodem/zstream.h index 1445b10..47b90eb 100644 --- a/zimodem/zstream.h +++ b/zimodem/zstream.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2016 Bo Zimmerman + Copyright 2016-2019 Bo Zimmerman Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,24 +14,32 @@ limitations under the License. */ +#define ZSTREAM_ESC_BUF_MAX 10 + class ZStream : public ZMode { private: WiFiClientNode *current = null; unsigned long lastNonPlusTimeMs = 0; unsigned long currentExpiresTimeMs = 0; - int plussesInARow=0; + unsigned long nextFlushMs = 0; + int plussesInARow = 0; ZSerial serial; - long nextAlarm = millis() + 5000; + int lastDTR = 0; + uint8_t escBuf[ZSTREAM_ESC_BUF_MAX]; + unsigned long nextAlarm = millis() + 5000; void switchBackToCommandMode(bool logout); void socketWrite(uint8_t c); + void socketWrite(uint8_t *buf, uint8_t len); + void baudDelay(); bool isPETSCII(); bool isEcho(); - bool isXonXoff(); + FlowControlType getFlowControl(); bool isTelnet(); bool isDisconnectedOnStreamExit(); + public: diff --git a/zimodem/zstream.ino b/zimodem/zstream.ino index a931787..8dfb683 100644 --- a/zimodem/zstream.ino +++ b/zimodem/zstream.ino @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 Bo Zimmerman + Copyright 2016-2019 Bo Zimmerman Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -22,9 +22,11 @@ void ZStream::switchTo(WiFiClientNode *conn) plussesInARow=0; serial.setXON(true); serial.setPetsciiMode(isPETSCII()); - serial.setFlowControlType(isXonXoff()?FCT_NORMAL:FCT_RTSCTS); + serial.setFlowControlType(getFlowControl()); currMode=&streamMode; checkBaudChange(); + if(pinSupport[pinDTR]) + lastDTR = digitalRead(pinDTR); } bool ZStream::isPETSCII() @@ -37,9 +39,9 @@ bool ZStream::isEcho() return (current != null) && (current->isEcho()); } -bool ZStream::isXonXoff() +FlowControlType ZStream::getFlowControl() { - return (current != null) && (current->isXonXoff()); + return (current != null) ? (current->getFlowControl()) : FCT_DISABLED; } bool ZStream::isTelnet() @@ -52,14 +54,45 @@ bool ZStream::isDisconnectedOnStreamExit() return (current != null) && (current->isDisconnectedOnStreamExit()); } +void ZStream::baudDelay() +{ + if(baudRate<1200) + delay(5); + else + if(baudRate==1200) + delay(3); + else + delay(1); + yield(); +} + void ZStream::serialIncoming() { - int serialAvailable = Serial.available(); - if(serialAvailable == 0) + int bytesAvailable = HWSerial.available(); + if(bytesAvailable == 0) return; - while(--serialAvailable >= 0) + uint8_t escBufDex = 0; + while(--bytesAvailable >= 0) { - uint8_t c=Serial.read(); + uint8_t c=HWSerial.read(); + if(((c==27)||(escBufDex>0)) + &&(!isPETSCII())) + { + escBuf[escBufDex++] = c; + if(((c>='a')&&(c<='z')) + ||((c>='A')&&(c<='Z')) + ||(escBufDex>=ZSTREAM_ESC_BUF_MAX) + ||((escBufDex==2)&&(c!='['))) + { + logSerialIn(c); + break; + } + if(bytesAvailable==0) + { + baudDelay(); + bytesAvailable=HWSerial.available(); + } + } logSerialIn(c); if((c==commandMode.EC) &&((plussesInARow>0)||((millis()-lastNonPlusTimeMs)>800))) @@ -70,21 +103,24 @@ void ZStream::serialIncoming() plussesInARow=0; lastNonPlusTimeMs=millis(); } - if((c==19)&&(isXonXoff())) + if((c==19)&&(getFlowControl()==FCT_NORMAL)) serial.setXON(false); else - if((c==17)&&(isXonXoff())) + if((c==17)&&(getFlowControl()==FCT_NORMAL)) serial.setXON(true); else { if(isEcho()) - enqueSerialOut(c); + serial.printb(c); if(isPETSCII()) c = petToAsc(c); - socketWrite(c); + if(escBufDex==0) + socketWrite(c); } } - + + if(escBufDex>0) + socketWrite(escBuf,escBufDex); currentExpiresTimeMs = 0; if(plussesInARow==3) currentExpiresTimeMs=millis()+800; @@ -97,10 +133,18 @@ void ZStream::switchBackToCommandMode(bool logout) if(!commandMode.suppressResponses) { if(commandMode.numericResponses) + { + preEOLN(commandMode.EOLN); serial.prints("3"); + serial.prints(commandMode.EOLN); + } else + if(current->isAnswered()) + { + preEOLN(commandMode.EOLN); serial.prints("NO CARRIER"); - serial.prints(commandMode.EOLN); + serial.prints(commandMode.EOLN); + } } delete current; } @@ -108,15 +152,42 @@ void ZStream::switchBackToCommandMode(bool logout) currMode = &commandMode; } +void ZStream::socketWrite(uint8_t *buf, uint8_t len) +{ + if(current->isConnected()) + { + uint8_t escapedBuf[len*2]; + if(isTelnet()) + { + int eDex=0; + for(int i=0;iwrite(buf,len); + nextFlushMs=millis()+250; + } +} + void ZStream::socketWrite(uint8_t c) { if(current->isConnected()) { + if(c == 0xFF && isTelnet()) + current->write(c); current->write(c); logSocketOut(c); - current->flush(); // rendered safe by available check - delay(0); - yield(); + nextFlushMs=millis()+250; + //current->flush(); // rendered safe by available check + //delay(0); + //yield(); } } @@ -128,7 +199,7 @@ void ZStream::loop() if(serv->hasClient()) { WiFiClient newClient = serv->server->available(); - if((newClient != null)&&(newClient.connected())) + if(newClient.connected()) { int port=newClient.localPort(); String remoteIPStr = newClient.remoteIP().toString(); @@ -144,16 +215,47 @@ void ZStream::loop() c=c->next; } if(!found) - { - newClient.write("\r\n\r\n\r\n\r\n\r\nBUSY\r\n7\r\n"); - newClient.flush(); - newClient.stop(); - } + new WiFiClientNode(newClient, serv->flagsBitmap, 5); // constructing is enough + // else // auto disconnect when from same } } serv=serv->next; } + WiFiClientNode *conn = conns; + unsigned long now=millis(); + while(conn != null) + { + WiFiClientNode *nextConn = conn->next; + if((!conn->isAnswered()) + &&(conn->isConnected()) + &&(conn!=current) + &&(!conn->isMarkedForDisconnect())) + { + conn->write((uint8_t *)busyMsg.c_str(), busyMsg.length()); + conn->flushAlways(); + conn->markForDisconnect(); + } + conn = nextConn; + } + + WiFiClientNode::checkForAutoDisconnections(); + + if(pinSupport[pinDTR]) + { + if(lastDTR==dtrActive) + { + lastDTR = digitalRead(pinDTR); + if((lastDTR==dtrInactive) + &&(dtrInactive != dtrActive)) + { + if(current != null) + current->setDisconnectOnStreamExit(true); + switchBackToCommandMode(true); + } + } + lastDTR = digitalRead(pinDTR); + } if((current==null)||(!current->isConnected())) { switchBackToCommandMode(true); @@ -167,6 +269,7 @@ void ZStream::loop() plussesInARow=0; if(current != 0) { + commandMode.sendOfficialResponse(ZOK); switchBackToCommandMode(false); } } @@ -174,7 +277,8 @@ void ZStream::loop() else if(serial.isSerialOut()) { - if((current->isConnected()) && (current->available()>0)) + if(current->available()>0) + //&&(current->isConnected()) // not a requirement to have available bytes to read { int bufferRemaining=serialOutBufferBytesRemaining(); if(bufferRemaining > 0) @@ -192,13 +296,20 @@ void ZStream::loop() logSocketIn(c); if((!isTelnet() || handleAsciiIAC((char *)&c,current)) && (!isPETSCII() || ascToPet((char *)&c,current))) - enqueSerialOut(c); + serial.printb(c); } } } } if(serial.isSerialOut()) + { + if((nextFlushMs > 0) && (millis() > nextFlushMs)) + { + nextFlushMs = 0; + serial.flush(); + } serialOutDeque(); + } } checkBaudChange(); }