* PROGRAM NAME: TERM (TERM II) * AUTHOR: RICHARD CONN * DATE: 6 JUL 81 * VERSION: 4.7 * PREVIOUS VERSION: 1.0 (4 NOV 79), 2.0 (3 FEB 80), 2.1 (16 FEB 80) * 2.2 (20 FEB 80), 2.3 (24 FEB 80), 2.4 (1 MAR 80) * 2.5 (21 SEP 80), 2.6 (19 NOV 80) * 3.0 (1 JAN 81), 3.1 (4 MAR 81), 3.2 (30 MAR 81) * 3.3 (4 APR 81), 3.4 (8 APR 81), 3.5 (18 APR 81) * 3.6 (24 APR 81), 4.0 (11 MAY 81), 4.1 (17 MAY 81) * 4.2 (20 MAY 81), 4.3 (11 JUN 81), 4.4 (15 JUN 81) * 4.5 (20 JUN 81), 4.6 (26 JUN 81) * * NOTE: This program requires Support Package 1.2 (SUPPORT.ASM) * Higher version numbers are acceptable. * VERS EQU 47 ; VERSION NUMBER * CP/M Implementation Equates; If you have a standard (ORG 0) CP/M, * these equates are properly set; otherwise, change CPM$BASE to the * starting address of your CP/M system. CPM$BASE equ 0 ; Base of CP/M PROGRAM$START equ CPM$BASE+100H * **** TERM II **** * TERM II is an inter-computer communications program designed * to be run under the CP/M Operating System. TERM II supports several * different modes of operation -- Terminal Mode, Conversational Mode, * and File Transfer (Send/Receive) Mode. Under Terminal Mode, TERM II * acts as a simple interface between the CON: CP/M Device and the external * modem (and, hence, the external computer). This mode makes the computer * essentially transparent to the user, with the exception that the computer * can be recording the screen display in a memory buffer or printing each * character as it is displayed on the printer. * Under Conversation Mode, TERM II allows two users (or, at least * the current TERM II user) to communicate in a graphically interactive * way with the external user or computer. Two lines are displayed, one * being the transmitted line and the other being the received line, and * the TERM II user can communicate with another TERM II (or similar) user * without encountering the normal problems of mixing the text of the * two conversation sources. * Under File Transfer Mode, CP/M text files can be transmitted or * received between two computers automatically. Automatic error detection * and retry are employed in the protocol 1 and 3 transfer options, and under * all file transfer options any size of CP/M text file may be transferred * between the two machines. The File Transfer Protocols (FTPs) are as * follows: * FTP 1 - TERM II Standard Protocol; this supports automatic error * detection and retry * FTP 2 - TERM II Arbitrary Host Protocol; this supports simple hand- * shaking and no error detection/retry * FTP 3 - CP/M User's Group Protocol; this supports automatic error * detection and retry * * * ******** Usage of TERMINAL Program ******** * * 1. The TERMINAL Program, 'TERM II', is invoked by the general form: * * TERM * * Arguments enclosed in angle brackets are optional. * * 2. If the optional 'filename.ext' is specified, TERM II's default * file name is set to that specified. Wild cards are not permitted in this * specification. The default file name may later be used to specify one of * the following: * A. A file into which is to be stored the contents of a file * transmitted from an external computer, * B. A file whose contents is to be transmitted to an external * computer, * C. A file into which the current memory buffer (from a buffer * load) is to be dumped, and * D. A file whose contents is to be dumped into the memory * buffer. * * 3. If the optional '/Option' is specified, the indicated operation * is performed immediately by TERM II. The valid selections for 'Option' are: * * Option Meaning * C Enter Conversational Mode Immediately * E Enable Echo and Enter Terminal Mode Immediately * R Enter File Transfer Mode, Receive Function, Immediately * S Enter File Transfer Mode, Send Function, Immediately * T Enter the Telephone Subsystem Immediately * X Enter the Exit, or Master Command, Mode Immediately * Print a help message to the user * * If no '/Option' is specified, Terminal Mode is entered immediately with * Echo Off. * * 4. Refer to the TERM II User's Manual for further details on the * usage of TERM II. * * ******** Structure of TERMINAL Program ******** * * TERM II is logically, modularly structured as follows. Each section * within TERM II begins with the comment '*: TERM II, Section' followed by the * section number. * * * Section Function/Contents * ------- ----------------- * * 1 Initialize TERM II * This section contains the telephone directory in the first * two 256-byte pages of memory, the code for initializing the * TERM II buffers, the code for processing the command line, * and the EXIT routine which provides a clean exit to CP/M. * Buffers are present which contain the following: * Exit Mode selection (Master/Abbreviated) * Exit Character * Break Character * Local Echo * Printer * Buffer Load * Translation Table Flags * Translation Table Buffers and Workspace * Telephone Directory Buffer * * 2 Terminal Mode * This section contains the Terminal Mode of TERM II. Terminal * Mode has six entry points: * ENTER$TERM * Select PTERM or OPTD for Entry to TERMINAL * PTERM Display Telephone Directory and enter Terminal * Mode * DECHO Enable Echo and enter Terminal Mode thru OPTD * OPTD Check the status of the selected modem and, if * Modem 1 is selected, check the status of the * Telephone Interface. If the Telephone Inter- * face is supported, the line is checked for a * carrier and the Telephone Subsystem is entered * if there is no carrier * OPTD1 Prints the Terminal Mode Herald, giving Echo, * Printer, Buffer Load, and Modem Status Info * OPTD2 Enter Terminal Mode with no herald * Terminal Mode contains the following Mode-specific supporting * subroutines: * BFLOAD Load character into memory buffer * Q$PR Place character into printer queue * LOS Check for carrier on telephone line * * 3 Exit, or Master Command, Mode * This section contains the Master Command Mode of TERM II. From * this section, the user can perform the following functions: * . Transmit Break * . Enter Translation Table Editor * . Enter Status Editor * . Toggle Buffer Load * . Enter Conversation Mode * . Dump Buffer * . Toggle Echo * . Display/Change the Default File Name * . Load Translation Table and Enable Translation Process * . Hang Up the Telephone and Return to CP/M * . Enter the Telephone Subsystem * . Select the Modem to be used * . Toggle the Printer * . Enter Abbreviated Exit Mode * . Enter File Transfer Mode, Receive Section * . Enter File Transfer Mode, Send Section * . Enter Terminal Mode * . Exit to CP/M * . Run a CP/M Command * . Display the Status of TERM II * . Close the currently-opened file * TERM II also supports an Abbreviated Exit Mode which * provides the more commonly-used functions of Master * Exit Mode * . Toggle Buffer Load * . Dump Buffer * . Toggle Echo * . Hang Up Telephone and Return to CP/M * . Toggle the Printer * . Enter Master Exit Mode * . Enter Terminal Mode * . Run a CP/M Command * . Exit to CP/M * . Display the Status of TERM II * This section contains the following entry points: * INITIAL$TEXIT * Enter Exit Mode from Command Line /X * TEXITXX Enter Exit Mode and delay displaying the menu * in order to give the user time to enter his * command if he doesn't want to see the menu * TEXIT Enter Exit Mode and Display the Menu * TEXIT$ABR * Enter Abbreviated Exit Mode * This section contains the following support routines: * TEXIT0 Subroutine which searches the table pted to * by HL (3 bytes -- command, address) for the * command in Reg A. Executes the command * found or simply returns if not found. * PRINT$ERROR$MESSAGE * Print the error message pted to by the ret * address ending in 0; message is printed at * the error line address * CLEAR$ERROR$MESSAGE * Clear the error message printed by the above * TEST$BELL * Test for Completion Bell and, if set, * ring bell and clear flag * RING$BELL * Ring Bell on user's console * TEXIT$SWITCH * Toggle between Master Exit Mode and * Abbreviated Exit Mode * PRINT$CINDEX * Print Command Index Header * IC$MSG Print Invalid Command Message * ERXIT Display error message pted to by return * address and enter Exit Mode * PAUSE Delay about 2 seconds * INITIALIZE * Initialize the TERM II Interfaces (Support) * * 4 Transmit Break and Load Translation Table Functions * * 5 Telephone Subsystem * This section contains the Telephone Subsystem. From this mode * the user can perform the following functions: * . Enter Answer Mode and Wait for Ring * . Enter the Telephone Directory Editor * . Dial the Telephone * . Toggle the Echo * . Hang up the Telephone and Return to the Telephone * Subsystem * . Quit and return to Exit Mode * . Enter Terminal Mode * . Exit to CP/M * . Display the Status of TERM II * This section contains one entry point, TPHONE. It also * contains the following section-specific routines: * ANSWER$PHONE * Enter Telephone Answering Mode -- Wait for Ring * TPECHO Toggle the Echo * PH$HANG Hang up the Telephone * PH$DIAL Dial the Telephone * * 6 Hang Up Telephone and Exit to CP/M * This section hangs up the telephone and exits to CP/M. * Its entry point is HANG$EXIT. * * 7 Transmit the Exit Character to the External Device (Modem) * The entry point for this section is TROUT. * Allow the user to specify the Modem Device * The entry point for this section is MODEM$SWITCH. * * 8 Allow the user to issue a CP/M Command; a $$$.SUB file * is built which contains (1) the command and (2) the * TERM II reentry command 'TERM * /X'; Entry point is * USER$CMND * * 9 Close the currently-opened file * The entry point for this section is FCLOS. * * 10 Toggle the echo flag * The entry point for this section is TECHO. * * 11 Display the Status of TERM II * The entry point for this section is STATUS$DISPLAY. * Additional entry points are also available: * STATUS1$DISPLAY - Display Status and exit to TEXIT * STATUS2$DISPLAY - Display Status and exit to TPHONE * STATUS3$DISPLAY - Display Status and exit to * STATUS$BUILDER * * 12 Toggle the Buffer Load * This section toggle the buffer load flag, and, if this results * in buffer load being enabled, it asks the user if he wishes a * load from disk or modem. If disk is selected, it loads the * memory buffer. The entry point for this section is TBUF. * * 13 Toggle the Printer * The entry point for this section is TPRNT. * * 14 Conversation Mode * This section initializes the Conversation Mode of TERM II. * Its entry point is TCONV. * * 15 Buffer Dump * This section dumps the memory buffer either to disk or to * the modem. It contains the following general support * routines, and its entry point is DUMP$BUF. * SET$LF$DELAY * Set the delay constant for when a is * transmitted to the modem. This is necessary * to provide the delay sometimes required by * a receiving computer. * SET$DELAY * Set the delay constant for the delay after * each character sent to the external computer. * SET$MONITOR * Set the monitor flag for the user. It enables * the user to watch an inter-system transmission * (such as buffer dump to modem or send/receive) * on his console. * SET$RING * Set the completion bell flag for the user. It * rings the bell (^G or ) on the user's * console at the completion of an operation. * * 16 File Name Display/Set * This section displays the name of the default file and allows * the user to change it if he desires. Its entry point is * FILE$SET. * * 17 Conversation Mode * This section is the complete code for the interactive * Conversation Mode of TERM II; its entry point is CONVERSATION. * * 18 Status Message Display Support Routines * This section contains the Status Display Routine and the other * key status message display routines. The main Status Display * Routine gives the following information: * . Support Package Functions which are enabled -- * CRT Cursor Address and Screen Clear * Printer * Modem 1 * Modem 2 * Telephone Interface * Telephone Carrier Status (Carrier Detect) * . Immediate Function Status -- * Default Exit Mode (Master/Abbreviated) * Echo On or Off * Printer On or Off * Buffer Load On or Off * Modem 1 or Modem 2 Selected * . Default File Specification * . Amount of room left in memory buffer in 256-byte * pages * . Exit Character * . Break Character * . Translation Flag Settings * The entry point for this section is PRINT$STATUS. * This section contains the following support package status * information routines. These routines return the zero flag * set if function not supported. * STAT$CRT * STAT$TTY * STAT$M1 (Modem 1) * STAT$M2 (Modem 2) * STAT$TI (Telephone Interface) * This section also contains the following status print * routines: * ECHOMS Echo On or Off * PRMS Printer On or Off * BUFMS Buffer Load On or Off * MODMS Modem 1 or Modem 2 Selected * XLATEMS Translation Table Enable (Input/Output) * EXITCHMS * Exit Character * BREAKMS Break Character * MASKMS MSB Input/Output Setting * PR$TERM$ENTRY * Print Terminal Mode Entry (w/wo Dir) * PR$INIT$CMND * Print Exit Mode (Master/Abbreviated) * * 19 File Transfer Mode, Send Section * This section supports the routines for file transfer under * transfer Protocols 1 and 2. The entry point is SEND$FILE. * SEND1$FILE, SEND2$FILE, and SEND3$FILE$START are the entry * points for Protocols 1, 2, and 3, resp. This section contains * the following general support routines: * XFER$CPLT * Transfer Complete message and return to Exit * Mode * FLUSH$RESP * Flush chars until the SYNC Char or a 1-sec * timeout occurs. * * 20 File Transfer Mode, Receive Section * This section support the routines for file transfer under * transfer Protocols 1 and 2. The entry point is RECV$FILE. * RECV$HDR, RECV2$FILE, and RECV3$FILE$START are the entry * points for Protocols 1, 2, and 3, resp. This section contains * the following general support routines: * INIT$FCB * Verify the default file name and initialize * the File Control Block for the transfer * PRINT$FN * Print the name of the default file * MONITOR$SET$OFF * Turn off the user Monitor Function and preserve * the current monitor state * MONITOR$RESET * Reset the monitor state from a MONITOR$SET$OFF * call * ERASE$OLD$FILE * Ask the user if he wishes to erase the destina- * tion file; erase and continue if so, abort to * Exit Mode if no * MAKE$NEW$FILE * Create and initialize the destination file on * disk * READ$BLOCK * Read the next block from a disk file * OPEN$FILE * Open the disk file * CLOSE$FILE * Write Last Block and Close the disk file * CLOSE$OUT$FILE * Close the disk file * WRITE$TO$FILE * Write a block to the disk file * WRITE$SECTOR$TO$FILE * Write BUFF to current disk file * READ$SECTOR$FROM$FILE * Read from current disk file to BUFF * * 21 View Mode * This section contains the View Mode Subsystem. Its initial * entry point is VIEW$MODE$ENTRY. * * 22 Print, Screen Display, and I/O Utilities * This section contains the following general support utilities: * SCREEN$CLEAR * Clear the CRT Screen * SCREEN$MESSAGE * Print text at a given screen location * PRINT$ID * Print the name and version number of this pgm * CPM$PRINT * Print text via the standard CP/M print BDOS * call * PRINT$MESSAGE * Print text * PRINT$EXIT$CHAR * Load and print the Exit Character * PRINT$CONTROL$CHAR * Print char in A as a control char if < ' ' * CRLF * Print * OUT$CON * Send char in A to CON: * IN$CON1 * Receive char from CON: in A * IN$CON * Receive char from CON: in A w/translation * ST$CON * Determine CON: Input Status * CON$CHAR * Send char in A to CON: w/Echo and Buffer Load * EXIT$CHECK * Check for Exit Char on input * BREAK$CHECK * Check for Break Char on input * OUT$MOD * Send char in A to Selected Modem (1 or 2) * IN$MOD * Receive char from selected modem in A * w/translation * ST$MOD * Determine input status of selected modem * ABORT * Check to see if EXIT Character was typed and * jump to Exit Mode if so * MASK$IN * Optional Mask on Input from remote * MASK$OUT * Optional Mask on Output to remote * XLATE$IN * Optional Translation on Input from remote * XLATE$OUT * Optional Translation on Output to remote * XLATE$ON * Turn on I/O translation * XLATE$OFF * Turn on I/O translation * HEX$OUT * Convert A to Hexadecimal and print * DECIMAL$OUT * Convert A to Decimal and print * GET$RESPONSE * Input char from CON:, capitalize it, and output * a * RECVNM * Call RECV with Monitor Off * RECV * Receive a character from selected modem in * delay period * SENDNM * Call SEND with Monitor Off * SEND * Send a char to selected modem with monitor and * echo for SEND Section of Transfer Mode * SEND2 * Send a char to selected modem with monitor and * echo for RECEIVE Section of Transfer Mode * * 23 Miscellaneous Utilities * This section contains the following utilities: * BDOS Call CP/M BDOS without affecting BC, DE, HL * CAPS Capitalize char in A reg * GOTOHL Position cursor on CRT * CLEAR Clear line for Conversation Mode * DISPLAY$ERRCT * Display retry count for Protocol 1 Transfer * * 24 Command Tables * This section contains the following command tables: * ICTAB Initial Command Table (Command Line) * CTAB Exit, or Master Command, Table * CTAB$ABR * Exit, or Abbreviated Command, Table * PTAB Telephone Subsystem Command Table * PETAB Telephone Directory Editor Command Table * STAB Status Flag Editor Command Table * XTAB Translation Table Editor Command Table * * 25 Buffer Region * * 26 BDOS Equate Table * * 27 Support Package Entry Addresses and Default Status Routines * * * * SPECIAL VALUES * FALSE EQU 0 TRUE EQU NOT FALSE TENTH$SEC$DELAY EQU 7500 ; 0.1 SEC DELAY CONSTANT FOR 1MHZ ERROR$LIMIT EQU 10 ; MAXIMUM ALLOWABLE ERRORS OPT$CHAR EQU '/' ; CHAR TO INDICATE OPTION SYNC$CH EQU '^' ; PROTOCOL 2 SYNCHRONIZATION CHAR LINE$LEN EQU 80 ; LENGTH OF LINE ON CRT LPS EQU 23 ; NUMBER OF LINES/SCREEN -1 REC$LINE EQU 20 ; LINE RECEIVED MSG IS ON SEND$LINE EQU 22 ; LINE SENT MSG IS ON CPAUS EQU 4000H ; PAUSE BEFORE COMMAND OPTIONS RECV$TIMEOUT$VALUE EQU 5000H ; RECV FUNCTION TIME DELAY CONST LXIB EQU 1 ; LXI B,XXXX INSTR * DEFINE ASCII CONTROL CHARACTERS USED SOH EQU 1 STX EQU 2 ETX EQU 3 EOT EQU 4 ENQ EQU 5 ACK EQU 6 BEL EQU 7 NAK EQU 15H SYN EQU 16H CAN EQU 18H LF EQU 10 CR EQU 13 BS EQU 8 TAB EQU 'I'-'@' ESC EQU 1BH CTRLB EQU 'B'-'@' CTRLC EQU 'C'-'@' CTRLE EQU 'E'-'@' CTRLS EQU 'S'-'@' CTRLX EQU 'X'-'@' CTRLY EQU 'Y'-'@' CTRLZ EQU 'Z'-'@' TILDA EQU 7EH DL EQU 7FH * *: TERM II, Section 1 * * **** START OF TERMINAL PROGRAM **** * ORG PROGRAM$START * JUMP TO START OF PROGRAM JMP START$PROG ; START PROGRAM * * JUMP TABLE FOR OVERLAY INTERFACING * * TEXIT ENTRIES JMP TEXIT JMP TEXIT0 * CHARACTER I/O ENTRIES JMP OUT$CON JMP IN$CON1 JMP PRINT$CONTROL$CHAR JMP C1$FLUSH JMP GET$RESPONSE JMP IC$MSG JMP PRINT$MESSAGE JMP PRINT$ERROR$MESSAGE JMP CLEAR$ERROR$MESSAGE JMP SCREEN$MESSAGE JMP PRINT$CINDEX JMP SCREEN$CLEAR JMP PRINT$ID * SPECIAL MESSAGES JMP EXITCHMS JMP BREAKMS JMP PR$INIT$CMND JMP PR$TERM$ENTRY JMP PRSPDMS JMP ECHOMS JMP PRMS JMP BUFMS JMP XLATEMS * DISK I/O UTILITIES JMP INIT$FCB JMP LOAD$FCB JMP OPEN$FILE JMP CLOSE$OUT$FILE JMP ERASE$OLD$FILE JMP MAKE$NEW$FILE JMP INIT$WRITE$DATA$BUFFER JMP WRITE$HL$TO$FILE JMP INIT$READ$DATA$BUFFER JMP READ$HL$FROM$FILE JMP WRITE$TERM$TO$DISK * MISCELLANEOUS ENTRY POINTS JMP TPHONE JMP PAUSE JMP IN$CON JMP STATUS$DISPLAY JMP SP$END ; GIVES ADDRESS OF DATA$BUFFER * * SPECIAL BUFFER SECTION * * DEFAULT INITIAL COMMAND ENTRY POINT (0=EXIT MODE, 0FFH=ABBREVIATED MODE) INIT$CMND: DB 0FFH ; ENABLE ABBREVIATED COMMAND MODE ON ENTRY * DEFAULT ENTRY POINT FOR TERMINAL MODE (0=TERMINAL, 0FFH=PHONE DIR & TERM) TERM$ENTRY: DB 0 ; SET FOR TERMINAL * DEFAULT VALUE FOR EXIT CHARACTER EXIT$CHAR: DB CTRLE ; SET TO ^E * DEFAULT VALUE FOR PROCESSOR SPEED (1=1MHZ, 2=2MHZ, 3=..., 9=9MHZ) PROCESSOR$SPEED: DB 2 ; SET TO 2MHZ * DEFAULT VALUE FOR BREAK CHARACTER BREAK$CHAR: DB 0 ; SET NO BREAK CHAR * DEFAULT ECHO SETTING (0=NO ECHO, 0FFH=ECHO) ECHOFL: DB 0 ; NO ECHO * DEFAULT PRINTER SETTING (0=NO PRINTER, 0FFH=PRINTER ON) PRFL: DB 0 ; PRINTER OFF * DEFAULT BUFFER LOAD SETTING (0=NO BUFFER LOAD, 0FFH=BUFFER LOAD) BUFLG: DB 0 ; NO BUFFER LOAD * DEFAULT TRANSLATION (INPUT) FLAG (0=TRANSLATION DISABLED, 0FFH=ENABLED) XLATE$IN$FLAG: DB 0 ; NO INPUT TRANSLATION * DEFAULT TRANSLATION (OUTPUT) FLAG XLATE$OUT$FLAG: DB 0 ; NO OUTPUT TRANSLATION * INPUT MASK ENABLE (MASK MSB?); 0=NO, 0FFH=YES MASK$IN$FLAG: DB 0FFH ; YES, MASK MSB * OUTPUT MASK ENABLE (MASK MSB?); 0=NO, 0FFH=YES MASK$OUT$FLAG: DB 0FFH ; YES, MASK MSB * OUTPUT MASK MSB; 07FH=MSB IS 0, 0FFH=MSB IS 1 OUTPUT$MASK: DB 07FH ; SET OUTPUT MASK TO 0 (MSB IS 0) * INPUT TRANSLATION ACTIVE FLAG; 0=NOT ACTIVE, 0FFH=ACTIVE XLATE$IN$ACTIVE: DB 0 ; SET NOT ACTIVE * OUTPUT TRANSLATION ACTIVE FLAG; 0=NOT ACTIVE, 0FFH=ACTIVE XLATE$OUT$ACTIVE: DB 0 ; SET NOT ACTIVE * MODEM 2 ENABLED FLAG; MODEM 2 ON IF 0FFH, OFF IF 0 M2$ON: DB 0 ; INITIALLY OFF * CTRL-S ENABLED FLAG; 0=OFF, 0FFH=ON CTRLS$FLAG: DB 0 ; INITIALLY OFF * BUFFER STATUS DISPLAY FLAG; 0=OFF, 0FFH=ON BFL$FLAG: DB 0 ; INITIALLY OFF * AUTOMATIC INITIALIZATION OF SUPPORT PACKAGE; 0=OFF, 0FFH=ON AUTO$INIT: DB 0 ; INITIALLY OFF * RESERVED FOR FUTURE USE DS 20 * INPUT TRANSLATION TABLE XLATE$IN$TABLE: DS 256 ; 256 ENTRIES (8 BITS) * OUTPUT TRANSLATION TABLE XLATE$OUT$TABLE: DS 256 ; 256 ENTRIES (8 BITS) * BUFFER FOR TELEPHONE NUMBERS PHONE$NUMBERS: DB 0 ; INIT DIR TO EMPTY DS 511 ; REST OF SPACE FOR TELEPHONE NUMBER BUFFER * STACK AREA DS 100 ; 50-ELT STACK STACK: DS 2 ; ORIGINAL STACK PTR * **** MASTER OVERLAY LOADER **** * ORG $/256*256+256 * * TERM II/TERM BLS LOADER * BDOS$ENTRY EQU CPM$BASE+5 TERM$LOADER: LXI SP,STK2 ; LOADER STACK XRA A ; A=0 STA TL$CURDSK STA TL$CURUSR LXI D,BUFF ; SET DMA ADDRESS MVI C,26 CALL BDOS$ENTRY LXI H,FCB+12 MVI B,24 TL$ZEROFILL: MVI M,0 ; ZERO FILL REST OF FCB INX H DCR B JNZ TL$ZEROFILL LXI D,FCB ; OPEN THE FILE MVI C,15 ; OPEN FILE CALL BDOS$ENTRY CPI 0FFH ; ERROR? JNZ TLOAD ; PROCEED IF NOT MVI C,25 ; RETURN CURRENT DISK CALL BDOS$ENTRY ORA A ; ALREADY ON A? JZ TLOAD$USR STA TL$CURDSK ; SET CURRENT DISK MVI E,0 ; LOG IN DRIVE A MVI C,14 ; SELECT DISK CALL BDOS$ENTRY TLOAD$USR: MVI C,12 ; DET VERS NUM CALL BDOS$ENTRY MOV A,H ORA L ; HL=0 IMPLIES CP/M 1.4 JZ TLOAD$2ND MVI E,0FFH ; GET CURRENT USER MVI C,32 ; GET/SET USER CODE CALL BDOS$ENTRY ORA A ; USER ZERO? JZ TLOAD$2ND STA TL$CURUSR ; SET CURRENT USER MVI E,0 ; SELECT USER 0 MVI C,32 ; GET/SET USER CODE CALL BDOS$ENTRY TLOAD$2ND: LXI D,FCB ; 2ND TRY TO OPEN FILE MVI C,15 CALL BDOS$ENTRY CPI 0FFH ; ERROR? JNZ TLOAD ; PROCEED IF NOT * LOADER ERROR -- ABORT TO CP/M MVI E,7 ; RING BELL FOR ERROR MVI C,2 CALL BDOS$ENTRY JMP CPM$BASE ; WARM BOOT * COMPUTE NUMBER OF BLOCKS TO SKIP TLOAD: LXI H,START ; START ADDRESS OF PROGRAM MOV B,H ; COUNT IN B DCR B ; 1 LESS THAN START ADDRESS MOV A,B ; DOUBLE IT FOR SECTOR COUNT ADD B MOV B,A ; RESULT IN B * SKIP JUMP TABLE AND BUFFER AREA TERM$LOADER$SKIP: CALL TL$READ ; READ BLOCK DCR B JNZ TERM$LOADER$SKIP * LOAD REST OF FILE TERM$LOADER$FILE: CALL TL$READ ; READ BLOCK LXI D,BUFF ; COPY BLOCK TO MEMORY TL$COPY: LDAX D ; GET BYTE MOV M,A ; PUT BYTE INX H INX D MOV A,D ; DONE CPI 1 JNZ TL$COPY JMP TERM$LOADER$FILE * READ BLOCK ROUTINE TL$READ: PUSH H PUSH B LXI D,FCB ; READ NEXT BLOCK MVI C,20 CALL BDOS$ENTRY POP B POP H ORA A ; OK? RZ LDA TL$CURDSK ; RESET DISK? ORA A JZ TL$READ1 MOV E,A ; DISK NUMBER IN E MVI C,14 ; SELECT DISK CALL BDOS$ENTRY TL$READ1: LDA TL$CURUSR ; RESET USER? ORA A JZ START MOV E,A ; USER NUMBER IN E MVI C,32 ; GET/SET USER CODE CALL BDOS$ENTRY JMP START ; START LOADED PROGRAM TL$CURDSK: DS 1 ; CURRENT DISK NUMBER TL$CURUSR: DS 1 ; CURRENT USER NUMBER DS 40 ; 20-ELT STACK STK2 EQU $ * **** START OF PROGRAM **** * ORG $/256*256+256 * * START PROGRAM FROM OVERLAY * START: * INIT PRIVATE STACK LXI SP,STACK ;SP=TERM STACK * TURN OFF OVERLAY FACILITY AND ALARM XRA A ; A=0 STA COMPL$BELL ; TURN OFF ALARM STA OVERLAY$LOADED ; SET OVERLAY FACILITY OFF * SET EXIT CHAR IN COMMAND TABLES LDA EXIT$CHAR ; GET IT STA CTAB ; PUT IT STA CTAB$ABR ; PUT IT AGAIN * DETERMINE END ADDRESS OF BUFFER LHLD BDOS+1 ; GET ADDRESS OF FBASE MVI L,0 MOV A,H ; SET END OF BUFFER TO BEFORE THE CCP SUI 9 ; CCP IS 2K, SO SET 9 PAGES (2K + 1 BLOCK) MOV H,A SHLD BUFEND ; SET END OF BUFFER * DETERMINE ADDRESS OF CON: I/O ROUTINES LDA BOOT+2 ; GET HIGH BYTE OF BIOS VECTOR MOV H,A ; ... IN H MVI L,6 ; CONSOLE STATUS ADR SHLD STADR MVI L,9 ; CONSOLE INPUT ADR SHLD INADR MVI L,12 ; CONSOLE OUTPUT ADR SHLD OUTADR * SET CURRENT COMMAND MODE TO BE EQUAL TO THE INITIAL COMMAND ENTRY MODE LDA INIT$CMND ; GET INITIAL FLAG STA CURR$CMND ; SET TO CURRENT COMMAND MODE * DETERMINE START OF BUFFER CALL SP$END ; GET END OF SUPPORT PACKAGE SHLD DATA$BUFFER ; SET DISK I/O BUFFER START ADDRESS LXI D,16*128 ; SIZE OF DISK I/O BUFFER DAD D ; PT TO AFTER DISK I/O BUFFER SHLD BUFFER ; SET BUFFER PTR SHLD BUF$NEXT ; SET NEXT BYTE TO LOAD JMP TEXIT ; REENTRY TO TERM II FROM OVERLAY * * START PROGRAM INITIALLY * START$PROG: LXI H,0 ; SAVE CP/M STACK PTR FOR RETURN DAD SP SHLD STACK * INIT PRIVATE STACK LXI SP,STACK ;SP=TERM STACK * TURN OFF OVERLAY FACILITY XRA A ; A=0 STA OVERLAY$LOADED ; SET FLAG * DETERMINE END ADDRESS OF BUFFER LHLD BDOS+1 ; GET ADDRESS OF FBASE MVI L,0 MOV A,H ; SET END OF BUFFER TO BEFORE THE CCP SUI 9 ; CCP IS 2K, SO SET 9 PAGES (2K + 1 BLOCK) MOV H,A SHLD BUFEND ; SET END OF BUFFER * DETERMINE ADDRESS OF CON: I/O ROUTINES LDA BOOT+2 ; GET HIGH BYTE OF BIOS VECTOR MOV H,A ; ... IN H MVI L,6 ; CONSOLE STATUS ADR SHLD STADR MVI L,9 ; CONSOLE INPUT ADR SHLD INADR MVI L,12 ; CONSOLE OUTPUT ADR SHLD OUTADR * SET CURRENT COMMAND MODE TO BE EQUAL TO THE INITIAL COMMAND ENTRY MODE LDA INIT$CMND ; GET INITIAL FLAG STA CURR$CMND ; SET TO CURRENT COMMAND MODE * DETERMINE START OF BUFFER CALL SP$END ; GET END OF SUPPORT PACKAGE SHLD DATA$BUFFER ; SET DISK I/O BUFFER START ADDRESS LXI D,16*128 ; SIZE OF DISK I/O BUFFER DAD D ; PT TO AFTER DISK I/O BUFFER SHLD BUFFER ; SET BUFFER PTR SHLD BUF$NEXT ; SET NEXT BYTE TO LOAD * CHECK FOR ERROR; END OF BUFFER BEFORE BEGINNING? XCHG ; CHECK FOR END OF BUFFER BEFORE BEGINNING LHLD BUFEND MOV A,H SUB D JNC START0 CALL PRINT$ID CALL PRINT$MESSAGE DB CR,LF,'TERM II Fatal Error -- ' DB 'CP/M System is too small for TERM II',0 CALL PAUSE JMP CPM * PLACE 0 AT END OF INPUT LINE START0: LXI H,BUFF ; PLACE 0 AT END OF INPUT LINE MOV A,M ; GET CHAR CNT ADD L ; PT TO 1 PAST LAST CHAR MOV L,A INR L MVI M,0 ; PLACE ENDING 0 * SEARCH FOR '/?' IN COMMAND LINE (HELP) LXI H,BUFF+1 ; PT TO BEGINNING OF LINE HELP$SEARCH: MOV A,M ; GET BYTE ORA A ; DONE? JZ HELP$SRCH$FAILED INX H ; PT TO NEXT BYTE CPI OPT$CHAR JNZ HELP$SEARCH MOV A,M ; GET BYTE AFTER OPTION CHAR CPI '?' ; HELP? JNZ HELP$SEARCH JMP TERM$HELP HELP$SRCH$FAILED: * CHECK FOR OPTION TRAPPED IN FILE NAME AND CLEAR FILE NAME IF SO LXI H,FCB+1 ; PT TO FILE NAME MOV A,M ; GET BYTE CPI '?' ; WILD CARD CAUGHT? JZ FCB$STAR CPI OPT$CHAR ; OPTION TRAPPED? JNZ TURN$OFF ; CONTINUE IF NOT FCB$STAR: MVI B,11 ; CLEAR 11 BYTES FCB$CLEAR: MVI M,' ' ; STORE INX H ; PT TO NEXT DCR B ; COUNT DOWN JNZ FCB$CLEAR * LOAD EXIT CHAR INTO COMMAND TABLE LDA EXIT$CHAR ; GET EXIT CHAR STA CTAB ; PLACE AS 1ST ENTRY INTO COMMAND TABLE STA CTAB$ABR ; PLACE AS 1ST ENTRY INTO ABBREVIATED CTAB TURN$OFF: XRA A ; A=0 * TURN OFF SKIP FLAG STA LF$SKIP ; ... SKIP ON MODEM OUTPUT * TURN OFF FILE OPEN FLAG -- NO FILE IS OPEN STA FILE$OPEN$FLAG * TURN OFF COMPLETION BELL STA COMPL$BELL * TURN OFF TRANSLATION FOR NOW STA XLATE$IN$ACTIVE ; INPUT TRANSLATION STA XLATE$OUT$ACTIVE ; OUTPUT TRANSLATION * TURN OFF MODEM 2 ENABLED FLAG STA M2$ON ; MODEM 2 NOT ENABLED (MODEM 1 ENABLED) * MUST HAVE MODEM 1 AVAILABLE CALL STAT$M1 ; MODEM 1 THERE? JNZ START1 ; CONTINUE IF SO CALL PRINT$ID CALL PRINT$MESSAGE DB 'TERM II Fatal Error -- Modem 1 Not Supported',0 CALL PAUSE JMP EXIT START1: * INITIALIZE THE INTERFACES IF DESIRED LDA BUFF+2 ; SKIP INTERFACE INIT? CPI '*' ; SKIP IF '*' JNZ START1C XRA A ; SET MASTER COMMAND MODE AND MODEM 1 ON STA CURR$CMND STA M2$ON LDA BUFF+4 ; SET COMMAND MODE CPI 'M' ; MASTER? JZ START1A MVI A,0FFH ; SET ABBREVIATED STA CURR$CMND START1A: LDA BUFF+5 ; SET MODEM CPI '1' ; MODEM 1? JZ START1B MVI A,0FFH ; SET MODEM 2 STA M2$ON START1B: LXI D,SUBFIL ; ERASE $$$.SUB MVI C,ERASE CALL BDOS CALL ZERO$SUBFIL ; INITIALIZE SUBFIL DEFINITION CALL PRINT$MESSAGE DB 'Please Type Any Character to Reenter TERM II -- ',0 CALL GET$RESPONSE CALL PRINT$MESSAGE DB CR,LF,'** Reentrant Status **',CR,LF,0 JMP START2 START1C: CALL INIT$INTERFACES ; INITIALIZE INTERFACES JMP START1D * * INITIALIZE THE HARDWARE-DEPENDENT INTERFACES * INIT$INTERFACES: LDA AUTO$INIT ; AUTOMATICALLY INITIALIZE? ORA A ; 0=NO JNZ II1 CALL SCREEN$CLEAR CALL PRINT$ID CALL PRINT$MESSAGE DB CR,LF,'++ Interface Initialization ++' DB CR,LF,LF,' Initialize Interfaces (Y/N/=Y)? ',0 CALL GET$RESPONSE CPI 'N' RZ II1: CALL STAT$CRT ; CRT CNZ INI$CRT CALL STAT$TTY ; PRINTER CNZ INI$PR CALL STAT$M1 ; MODEM 1 CNZ INI$MOD1 CALL STAT$M2 ; MODEM 2 CNZ INI$MOD2 CALL STAT$TI ; TELEPHONE INTERFACE CNZ INI$TI CALL PRINT$MESSAGE DB CR,LF,' Interfaces Initialized',CR,LF,LF,0 RET * START ROUTINE START1D: CALL PRINT$MESSAGE DB '** Entrant Status **',CR,LF,0 START2: CALL PR$INIT$CMND ; INITIAL EXIT MODE CALL MODMS ; MODEM CALL PAUSE * SCAN FOR OPTION LXI H,BUFF+1 ; PT TO FIRST CHAR OPTL: MOV A,M ; GET CHAR INX H ; PT TO NEXT ORA A ; 0=DONE JZ ENTER$TERM CPI OPT$CHAR ; OPTION CHAR? JNZ OPTL INX H ; PT TO CHAR AFTER OPTION MOV A,M ; GET IT STA OPTC$NEXT ; SAVE IT FOR FURTHER PROCESSING DCX H ; PT TO OPTION MOV A,M ; GET OPTION LXI H,ICTAB ; INITIAL COMMAND TABLE CALL TEXIT0 ; PROCESS DEFAULT OPTIONS * PRINT HELP INFORMATION TERM$HELP: CALL SCREEN$CLEAR CALL PRINT$ID CALL PRINT$MESSAGE DB 'TERM II -- This program is invoked by:',CR,LF DB ' TERM FILENAME.TYP /O',CR,LF DB ' ''FILENAME.TYP'' and ''/O'' are optional. Valid options ' DB 'are --',CR,LF DB ' /C = Invoke Conversational Mode',CR,LF DB ' /E = Set Echo and Enter Terminal Mode',CR,LF DB ' /E or /E+ = Enable Echo, /E- = Disable',CR,LF DB ' /R = Invoke File Receive Mode',CR,LF DB ' /S = Invoke File Send Mode',CR,LF DB ' /T = Invoke Telephone Subsystem',CR,LF DB ' /X = Invoke Exit Command Mode',CR,LF DB ' /XA = Enter Abbreviated Command Level',CR,LF DB ' /XM = Enter Master Command Level',CR,LF DB ' /? = Give this HELP Information',CR,LF DB ' If no option is given, Terminal Mode is entered',CR,LF DB ' The special form:',CR,LF DB ' TERM * /X',CR,LF DB 'is used for reentry to TERM II from TERM II-initiated ' DB 'programs',CR,LF,0 JMP CPM * EXIT TO CP/M * TEXIT ENTRY POINT * TPHONE ENTRY POINT EXIT: CALL SCREEN$CLEAR CALL PRINT$MESSAGE DB CR,LF,LF,'** TERM II Exiting to CP/M **',0 LHLD STACK ; RESTORE SP SPHL RET * PROCESS ECHO OPTION ECHO$OPTION: LDA OPTC$NEXT ; GET CHAR AFTER /E CPI '-' ; DISABLE ECHO? JNZ DECHO ; ENABLE ECHO IF + OR ANYTHING OTHER THAN - XRA A ; A=0 TO DISABLE ECHO STA ECHOFL ; SET FLAG JMP ENTER$TERM ; ENTER TERMINAL MODE * *: TERM II, Section 2 * **** TERMINAL MODE **** * * THIS IS THE TERMINAL MODE SECTION OF TERM. IT SUPPORTS FIVE ENTRY * POINTS: * 0. ENTER$TERM -- DEFAULT ENTRY POINT FOR TERM * 1. PTERM -- DISPLAY TELEPHONE NUMBERS AND ENTER THRU OPTD * 2. DECHO -- ENTER TERMINAL MODE WITH ECHO ENABLED * 3. OPTD -- ENTER TERMINAL MODE AND CHECK TELEPHONE INTERFACE * (ON INITIAL ENTRY, ECHO IS DISABLED) * 4. OPTD1 -- ENTER TERMINAL MODE * 5. OPTD2 -- ENTER TERMINAL MODE WITHOUT OPENING MESSAGE * DETERMINE HOW TO ENTER TERM II ENTER$TERM: LDA TERM$ENTRY ; GET FLAG ORA A ; 0=OPTD, 0FFH=PTERM JZ OPTD * DISPLAY TELEPHONE DIRECTORY AND ENTER TERMINAL MODE VIA NORMAL * CHANNELS PTERM: CALL SCREEN$CLEAR CALL PRINT$ID CALL PRINT$MESSAGE DB CR,LF,LF,' ++ TERM II Telephone Directory Display ++' DB CR,LF,LF,0 CALL EXITCHMS ; TELL USER WHAT EXIT CHAR IS CALL PRINT$PHONE$NUMBERS CALL PRINT$ERROR$MESSAGE DB 'Type Exit Char to Abort or Any Other Char to Enter Terminal ' DB 'Mode - ',0 CALL GET$RESPONSE CALL EXIT$CHECK ; CHECK FOR EXIT CHAR JZ TEXIT JMP OPTD * * PRINT PHONE NUMBERS * PRINT$PHONE$NUMBERS: CALL SCREEN$MESSAGE DB 11,5,'-- Telephone Directory --',0 ; PHONE NUMBER LIST LXI H,PHONE$NUMBERS ; PT TO BUFFER MVI B,'0' ; SET INDEX MOV A,M ; EMPTY DIR? ORA A JNZ PH$LIST0 CALL PRINT$MESSAGE DB CR,LF,' No Entries in Directory',0 RET PH$LIST0: MOV A,M ; GET BYTE ORA A ; 0=DONE RZ CALL PH$LIST$ENTRY JMP PH$LIST0 * PRINT PHONE NUMBER ENTRY PTED TO BY HL WITH INDEX # IN B PH$LIST$ENTRY: CALL CRLF MOV A,B ; GET INDEX CALL OUT$CON INR B ; INCR INDEX MVI A,'.' ; DECIMAL CALL OUT$CON MVI A,' ' ; CALL OUT$CON PH$NUM: MOV A,M ; GET DIGIT INX H ; PT TO NEXT ORA A ; 0=DONE JZ PH$TITLE CALL OUT$CON JMP PH$NUM PH$TITLE: MOV A,M ; GET CHAR INX H ; PT TO NEXT ORA A ; DONE? RZ CALL OUT$CON JMP PH$TITLE * ENABLE ECHO AND GO INTO TERMINAL MODE * INITIAL COMMAND ENTRY POINT DECHO: MVI A,0FFH ; ENABLE ECHO STA ECHOFL * ENTER TERMINAL MODE WITHOUT ECHO * TEXIT ENTRY POINT * TPHONE ENTRY POINT OPTD: * CHECK FOR MODEM 2 ENABLED LDA M2$ON ; GET FLAG ORA A ; 0=NO JNZ OPTD1 ; GO TO TERMINAL MODE IF SO * CHECK FOR TELEPHONE INTERFACE SUPPORT CALL STAT$TI ; TELEPHONE INTERFACE IN SUPPORT PACKAGE? JZ OPTD1 ; GO TO TERMINAL MODE IF NOT * CHECK FOR SIGNAL ON LINE CALL TI$CST ; TELEPHONE CARRIER? JNZ OPTD1 CALL PRINT$MESSAGE DB 'No Signal on Telephone Line -- Entering Telephone ' DB 'Subsystem',0 CALL PAUSE ; 2 SEC DELAY JMP TPHONE * ENTER TERMINAL MODE AND PRINT OPENING MESSAGE OPTD1: CALL TEST$BELL ; TEST AND RING COMPLETION BELL IF SET CALL SCREEN$CLEAR CALL PRINT$ID CALL PRINT$MESSAGE DB CR,LF,'++ Terminal Mode ++',CR,LF,0 CALL ECHOMS ; PRINT ECHO MESSAGE CALL PRMS ; PRINT PRINTER MESSAGE CALL BUFMS ; PRINT BUFFER LOAD MESSAGE CALL MODMS ; PRINT MODEM MESSAGE CALL XLATEMS ; PRINT TRANSLATION MESSAGE CALL BREAKMS ; PRINT BREAK CHAR MESSAGE CALL EXITCHMS ; PRINT EXIT CHAR MESSAGE * ENTER TERMINAL MODE WITHOUT OPENING MESSAGE OPTD2: CALL XLATE$ON ; TURN ON TRANSLATION SYSTEM IF DESIRED * * ** TERMINAL ** * * TERMINAL PROGRAM -- * ALLOWS THE USER TO COMMUNICATE IN A NORMAL TIMESHARING * FASHION WITH AN EXTERNAL COMPUTER. FACILITIES ARE INCLUDED * FOR BUFFER LOAD AND DUMP. * TERMINAL: CALL LOS ; CHECK FOR LOSS OF SIGNAL CALL ST$CON ; CHECK CONSOLE STATUS JNZ TERMC ; GET CONSOLE CHAR IF READY CALL ST$MOD ; CHECK MODEM STATUS JNZ TERMM ; GET MODEM CHAR LDA PRFL ; PRINTER ON? ORA A ; ZERO MEANS NO JZ TERMINAL ** PRINTER OUTPUT SECTION ** * CHECK FOR PRINTER READY CALL ST$PR ; GET PRINTER STATUS JZ TERMINAL ; NOT READY * CHECK PRINTER INPUT QUEUE LHLD QOP ; CHECK PRINTER OUTPUT QUEUE MOV A,M ; ZERO MEANS NO CHAR ORA A JZ TERMINAL * SEND CHAR TO PRINTER MVI M,0 ; CLEAR CHAR FROM QUEUE INR L ; PT TO NEXT SHLD QOP CALL OUT$PR ; PRINT CHAR JMP TERMINAL ** MODEM INPUT/CONSOLE OUTPUT SECTION ** TERMM: CALL IN$MOD ; GET MODEM CHAR CALL OUT$CON ; PRINT CALL Q$PR ; QUEUE PRINTER IF PRINTER ON CALL BFLOAD ; LOAD BUFFER IF FLAG SET JMP TERMINAL ** CONSOLE INPUT/MODEM OUTPUT SECTION ** TERMC: CALL CON$CHAR ; GET CONSOLE CHAR AND ECHO IF SET CALL EXIT$CHECK ; CHECK FOR EXIT CHAR JZ TERMCXX CALL BREAK$CHECK ; CHECK FOR BREAK CHAR JZ TRANSMIT$BREAK CALL OUT$MOD ; SEND TO MODEM MOV C,A ; SAVE CHAR LDA ECHOFL ; ECHO ON? ORA A JZ TERMINAL ; DO NOT PRINT IF ECHO OFF MOV A,C ; GET CHAR CALL Q$PR ; QUEUE PRINTER IF PRINTER ON JMP TERMINAL ** EXIT TERMINAL MODE ** TERMCXX: JMP TEXITXX ; RETURN TO TEXIT (WITH PAUSE) ** LOAD BUFFER IF FLAG SET ** BFLOAD: MOV C,A ; CHAR IN C ORA A ; DON'T SAVE NULL RZ CPI 7FH ; DON'T SAVE RZ LDA BUFLG ; CHECK FLAG ORA A ; 0=NO JZ BFLD PUSH D ! PUSH H LHLD BUFEND ; GET PTR TO END OF BUFFER XCHG LHLD BUF$NEXT ; GET PTR TO CURRENT CHAR MOV A,H ; EQUAL? SUB D JC BFL1 * BUFFER FULL BFL0: XRA A ; TURN OFF BUFFER LOAD STA BUFLG CALL PRINT$MESSAGE DB CR,LF,'Buffer Full -- Load OFF',CR,LF,0 JMP BFL1X * PLACE CHAR IN BUFFER BFL1: MOV M,C ; PUT CHAR INX H ; PT TO NEXT SHLD BUF$NEXT XCHG ; ADDRESS IN DE LHLD BUFEND ; CHECK FOR 1K LESS THAN END ADDRESS DCR H ; 1K LESS DCR H DCR H DCR H DCR H ; 1 MORE MOV A,H ; COMPARE AGAINST CURRENT PAGE CMP D JNC BFL2 ; OK IF D CZ BFL$STAT POP H ! POP D BFLD: MOV A,C ; GET CHAR RET * RING BELL EVERY 10 COUNTS BEEP10: LDA BELLCTR ; DECREMENT COUNTER DCR A STA BELLCTR RNZ CALL RING$BELL MVI A,10 ; RESET COUNTER STA BELLCTR RET * PRINT STATUS LINE FOR BUFFER LOAD IF FLAG SET BFL$STAT: LDA BFL$FLAG ; GET FLAG FOR STATUS DISPLAY ORA A ; 0=NO DISPLAY RZ CALL SCREEN$MESSAGE DB 1,40,'++ Buffer Space Left = ',0 LHLD BUF$NEXT ; GET ADDRESS OF NEXT BYTE XCHG LHLD BUFEND ; GET END ADDRESS OF BUFFER MOV A,H ; SUBTRACT SUB D CALL DECIMAL$OUT ; PRINT IN DECIMAL CALL PRINT$MESSAGE DB ' Pages ++',0 CALL SCREEN$MESSAGE ; GOTO END OF SCREEN DB 24,1,0 RET * PRINTER QUEUE ROUTINE -- PLACE CHAR IN PRINTER QUEUE Q$PR: PUSH B ! PUSH D ! PUSH H MOV C,A ; SAVE CHAR IN C ORA A ; DO NOT QUEUE NULLS JZ Q$PR$DONE LDA PRFL ; PRINT IT? ORA A JZ Q$PR$DONE LHLD QIP ; GET PTR MOV M,C ; PUT CHAR INR L ; PT TO NEXT CIRCULAR SHLD QIP Q$PR$DONE: MOV A,C ; RESTORE CHAR POP H ! POP D ! POP B RET * * LOS -- LOSS OF SIGNAL * THIS ROUTINE CHECKS FOR LOSS OF THE TELEPHONE SIGNAL AND ABORTS * TO THE TERMINAL EXIT ROUTINE IF LOSS OF SIGNAL OCCURS * LOS: LDA M2$ON ; FAKE CARRIER IF MODEM 2 IS ON ORA A ; SET FLAGS RNZ ; MODEM 2 IS ON SO CARRIER PRESENT CALL STAT$TI ; TELEPHONE INTERFACE AVAILABLE? RZ ; RETURN IF NOT CALL TI$CST ; CHECK FOR CARRIER RNZ ; RETURN IF CARRIER CALL PRINT$ERROR$MESSAGE DB BEL,CR,LF,'ERROR -- Loss of Carrier Signal',CR,LF,0 CALL PAUSE ; DELAY JMP TEXIT * *: TERM II, Section 3 * * TERMINAL EXIT ENTRY ROUTINE FROM COMMAND LINE -- * INITIAL$TEXIT: LDA OPTC$NEXT ; GET 2ND OPTION CHAR CPI 'A' ; ABBREVIATED JZ INIT$TEXITA CPI 'M' ; MASTER JZ INIT$TEXITM JMP TEXIT ; DEFAULT INIT$TEXITA: MVI A,0FFH ; SET CURRENT COMMAND STA CURR$CMND JMP TEXIT INIT$TEXITM: XRA A ; 0=MASTER STA CURR$CMND JMP TEXIT * * TERMINAL EXIT ROUTINE -- * THIS ROUTINE GIVES THE OPERATOR CONTROL OF TERM. HE MAY ENTER IT * FROM TERMINAL OR CONVERSATION MODES BY CTRL-E OR HE MAY BE PLACED HERE * BY A NUMBER OF THE INTERNAL ROUTINES (I.E., SEND FILE) WITHIN TERM * TEXITXX: LXI SP,STACK ; RESET STACK CALL TEST$BELL ; TEST AND RING COMPLETION BELL IF SET CALL XLATE$OFF ; TURN OFF TRANSLATION * ** IMMEDIATE INPUT SECTION ** * THIS SECTION DELAYS DISPLAY OF OPTIONS TO GIVE USER TIME TO ENTER * OPTION BEFORE THE DISPLAY; IF HE TYPES A , THE OPTION DISPLAY * IS INVOKED IMMEDIATELY * LXI D,CPAUS ; PROCESS IMMEDIATE INPUT TEXIT$IMMED: CALL ST$CON ; CHECK STATUS JZ TEXIT1$IMMED ; CONTINUE LOOP IF NO CHAR YET CALL IN$CON ; GET CHAR CALL CAPS ; CAPITALIZE JMP TEXIT$ENTRY ; PROCESS OPTION TEXIT1$IMMED: DCX D ; COUNT DOWN MOV A,D ORA E ; DONE? JNZ TEXIT$IMMED ; CONTINUE LOOPING IF NOT DONE ** TERMINAL EXIT OPTION DISPLAY AND PROMPT ** * INITIAL COMMAND ENTRY POINT TEXIT: LXI SP,STACK CALL XLATE$OFF ; TURN OFF TRANSLATION CALL TEST$BELL ; TEST AND RING COMPLETION BELL IF SET CALL SCREEN$CLEAR CALL PRINT$ID LDA CURR$CMND ; DETERMINE CURRENT COMMAND MODE ORA A ; 0=EXIT MODE, 0FFH=ABBREVIATED MODE JNZ TEXIT$ABR CALL SCREEN$MESSAGE DB 4,9,'++ TERM II Exit Mode -- Master Command Level ++',0 CALL PRINT$CINDEX ; PRINT HEADER CALL PRINT$MESSAGE DB CR,LF DB '^B Transmit Break ^X Translation Editor ^Y Status ' DB 'Editor',CR,LF DB ' B Toggle Buffer Load C Conversation Mode D Toggle ' DB 'Buffer Dump',CR,LF DB ' E Toggle Echo F Examine/Set File G Get ' DB 'XLATE Table',CR,LF DB ' H Hang Up/Exit I Init Interfaces L Link ' DB 'to Telephone',CR,LF DB ' M Select Modem P Toggle Printer Q Quit ' DB 'to ACL',CR,LF DB ' R Receive File S Send File T Terminal ' DB 'Mode',CR,LF DB ' U User CP/M Cmnd V Enter View Mode X Exit to ' DB 'CP/M',CR,LF DB ' Y Display Status Z Close File @ Run BLS ' DB 'Overlay',CR,LF DB 0 CALL CLEAR$ERROR$MESSAGE CALL SCREEN$MESSAGE DB 18,15 DB 'TERM II Command -- ',0 CALL GET$RESPONSE ; GET ANS AND CAPITALIZE ** ENTRY POINT FOR TEXIT WITH COMMAND IN REG A USING TEXIT COMMANDS ** TEXIT$ENTRY: LXI SP,STACK LXI H,CTAB ; PT TO FULL COMMAND TABLE PUSH PSW ; SAVE CMND LDA CURR$CMND ; FULL OR ABBREVIATED COMMAND TABLE? ORA A ; 0=FULL JZ TEXIT$ENTRY1 LXI H,CTAB$ABR ; PT TO ABBREVIATED COMMAND TABLE TEXIT$ENTRY1: POP PSW ; GET COMMAND CPI ' ' ; IS VALID -- REPRINT DISPLAY JZ TEXIT CPI CR ; CR IS VALID -- REPRINT DISPLAY JZ TEXIT CALL TEXIT0 ; PROCESS COMMANDS CALL IC$MSG ; INVALID COMMAND JMP TEXIT ; GO BACK IF ERROR IN COMMAND ** ENTRY POINT FOR ABBREVIATED COMMAND MODE ** TEXIT$ABR: CALL SCREEN$MESSAGE DB 4,9,'++ TERM II Exit Mode -- Abbreviated Command Level ++',0 CALL PRINT$CINDEX CALL PRINT$MESSAGE DB CR,LF DB ' B Toggle Buffer Load D Toggle Buffer Dump E Toggle ' DB 'Echo',CR,LF DB ' H Hang Up/Exit L Link to Telephone P Toggle ' DB 'Printer',CR,LF DB ' Q Quit to MCL T Terminal Mode U User ' DB 'CP/M Command',CR,LF DB ' X Exit to CP/M Y Status Display',CR,LF DB 0 CALL SCREEN$MESSAGE DB 16,5,'** Abbreviated Status **',CR,LF,0 CALL SPACE6 CALL EXITCHMS ; EXIT CHAR CALL SPACE6 CALL BREAKMS ; BREAK CHAR CALL SPACE6 CALL ECHOMS ; ECHO ON/OFF CALL SPACE6 CALL PRMS ; PRINTER ON/OFF CALL SPACE6 CALL BUFMS ; BUFFER LOAD ON/OFF CALL CLEAR$ERROR$MESSAGE CALL SCREEN$MESSAGE DB 14,40 DB 'TERM II Command -- ',0 CALL GET$RESPONSE ; INPUT COMMAND JMP TEXIT$ENTRY ** PRINT ERROR MESSAGE AT BOTTOM OF SCREEN ** PRINT$ERROR$MESSAGE: CALL CLEAR$ERROR$MESSAGE ; CLEAR PREVIOUS CALL SCREEN$MESSAGE ; POSITION CURSOR DB 22,1,' ',0 JMP PRINT$MESSAGE ; PRINT NORMAL MESSAGE WITH RETURN ADDRESS ** CLEAR ERROR MESSAGE AT BOTTOM OF SCREEN ** CLEAR$ERROR$MESSAGE: CALL SCREEN$MESSAGE ; POSITION CURSOR DB 22,1,0 PUSH B ; SAVE BC MVI B,78 ; 78 CEM$LOOP: MVI A,' ' ; PRINT CALL OUT$CON DCR B ; COUNT DOWN JNZ CEM$LOOP MVI A,CR ; BEGINNING OF LINE CALL OUT$CON POP B ; RESTORE BC RET ** TEST AND RING COMPLETION BELL IF SET ** TEST$BELL: LDA COMPL$BELL ; CHECK FLAG ORA A ; 0=OFF RZ XRA A ; TURN OFF FLAG STA COMPL$BELL CALL SCREEN$CLEAR CALL PRINT$ID CALL PRINT$MESSAGE DB CR,LF,' ++ TERM II Alarm ++' DB CR,LF,LF,'Operation Complete - Strike Any Char to Terminate ' DB 'Alarm',0 PUSH B TEST$BELL$LOOP: CALL RING$BELL LDA PROCESSOR$SPEED ; TIME DELAY BETWEEN BEEPS MOV B,A ; ... IS PRODUCT OF PROCESSOR SPEED AND 0.1-SEC DELAY TEST$BELL$PAUSE: LXI H,TENTH$SEC$DELAY ; SPEED DELAY FOR CALL TO ST$CON TB$PAUSE2: CALL ST$CON ; GET STATUS? JNZ TEST$BELL$DONE DCX H ; COUNT DOWN MOV A,H ORA L ; ZERO ON HL? JNZ TB$PAUSE2 DCR B ; COUNT DOWN ON PROCESSOR SPEED JNZ TEST$BELL$PAUSE JMP TEST$BELL$LOOP TEST$BELL$DONE: CALL IN$CON ; FLUSH CHAR CALL SCREEN$CLEAR POP B RET * * RING THE BELL ON THE USER'S CONSOLE * RING$BELL: PUSH B PUSH D MVI E,BEL MVI C,2 ; CONSOLE OUTPUT CALL BDOS POP D POP B RET ** SWITCH FUNCTION FOR TEXIT -- TOGGLES BETWEEN ABBREVIATED AND FULL CMNDS ** TEXIT$SWITCH: LDA CURR$CMND ; GET CURRENT SETTING CMA ; FLIP IT STA CURR$CMND ; NEW CURRENT SETTING JMP TEXIT ; DO IT * SPACE OVER 6 SPACES SPACE6: CALL PRINT$MESSAGE DB ' ',0 ; 6 RET * PRINT COMMAND INDEX HEADER PRINT$CINDEX: CALL PRINT$MESSAGE DB CR,LF,LF DB ' -- Command Index --',CR,LF DB 'Cmd Function Cmd Function Cmd Function' DB CR,LF DB '--- -------- --- -------- --- --------' DB 0 RET ** INVALID COMMAND MESSAGE ** IC$MSG: CALL PRINT$ERROR$MESSAGE DB BEL,'-- Invalid Command --',0 CALL PAUSE RET ** ENTRY POINT FOR COMMAND PROCESSING WITH COMMAND IN REG A AND HL=TABLE ** TEXIT0: MOV B,A ; USER COMMAND IN B TEXIT1: MOV A,M ; GET TABLE COMMAND ORA A ; DONE IF ZERO JZ TEXIT3 ; RETURN TO CALLER CMP B ; COMPARE AGAINST COMMAND JZ TEXIT2 INX H ; SKIP ADDRESS INX H INX H JMP TEXIT1 * EXECUTE COMMAND TEXIT2: POP D ; CLEAR RETURN ADDRESS FROM STACK INX H ; PT TO ADDRESS MOV E,M ; GET LOW INX H MOV D,M ; GET HIGH XCHG ; ADDRESS IN HL MOV A,H ; OVERLAY ROUTINE IF HIGH IS 0 ORA A ; HIGH-ORDER ADDRESS = 0? JZ TEXIT2$OVERLAY PCHL ; TRANSFER TO PROGRAM * COMMAND COMES FROM OVERLAY, SO L=OFFSET INTO OVERLAY/3 TEXIT2$OVERLAY: LDA OVERLAY$LOADED ; OVERLAY ALREADY LOADED? ORA A ; 0=NO JNZ TEXIT2$RUN$OVR ; EXECUTE LOADED ROUTINE * LOAD OVERLAY FILE INTO MEMORY PUSH H ; SAVE OFFSET/3 ON STACK LXI H,OVR$FILE+12 ; ZERO SIGNIFICANT FIELDS OF OVR FCB MVI B,24 ; 24 ZEROES T2$OVR$INIT: MVI M,0 ; STORE ZERO INX H DCR B JNZ T2$OVR$INIT MVI C,RETDISK ; RETURN CURRENT DISK CALL BDOS STA CURRENT$DISK CALL DET$VERS ; DETERMINE CURRENT VERSION MVI E,0FFH ; PREPARE TO GET USER CODE MVI C,RETUSER ; BDOS RETURN USER CODE CNZ BDOS ; GET USER CODE IF NOT CP/M 1.4 STA CURRENT$USER CALL PRINT$ERROR$MESSAGE DB 'Loading Overlay File ... ',0 CALL OPEN$OVERLAY ; TRY TO OPEN OVERLAY FILE JNZ T2$OVR1 ; ZERO SET IF ERROR LDA CURRENT$DISK ; SET BUFFER ORA A ; ALREADY ON DRIVE A:? JNZ T2$OVR0 T2$OVR$ERROR: CALL PRINT$MESSAGE DB CR,LF,'Overlay Error -- Overlay File Not Found',0 CALL PAUSE JMP TEXIT T2$OVR0: CALL DET$VERS ; DETERMINE VERSION NUMBER MVI C,RETUSER ; BDOS SET USER CODE MVI E,0 ; SET USER 0 CNZ BDOS ; SET USER 0 IF NOT CP/M 1.4 MVI C,SELDISK ; SELECT DRIVE A: MVI E,0 CALL BDOS CALL OPEN$OVERLAY ; TRY TO OPEN OVERLAY FILE ON DRIVE A:/USER 0 JZ T2$OVR$ERROR T2$OVR1: LHLD BUFFER ; PT TO LOAD ADDRESS T2$OVR2: SHLD BUF$NEXT ; SAVE LOAD ADDRESS XCHG ; DE PTS TO LOAD ADDRESS LHLD BUFEND ; CHECK FOR BUFFER OVERFLOW MOV A,D ; NEXT ADDRESS MUST BE LESS THAN BUFFER END SUB H JC T2$OVR3 CALL PRINT$MESSAGE DB CR,LF,'Overlay Error -- Overlay Buffer Full',0 CALL PAUSE JMP TEXIT T2$OVR3: MVI C,STDMA ; SET DMA ADDRESS FOR NEXT BLOCK CALL BDOS LXI D,OVR$FILE ; READ NEXT BLOCK MVI C,READ CALL BDOS ORA A ; 0=OK -- CONTINUE JNZ T2$OVR4 ; LOAD COMPLETE LHLD BUF$NEXT ; SET PTR TO NEXT ADDRESS LXI D,128 ; 128 BYTES LOADED DAD D JMP T2$OVR2 T2$OVR4: LXI D,OVR$FILE ; CLOSE OVERLAY FILE MVI C,CLOSE CALL BDOS CALL DET$VERS ; DETERMINE IF CP/M 1.4 MVI C,RETUSER ; RESTORE USER CODE LDA CURRENT$USER MOV E,A CNZ BDOS ; SET CODE ONLY IF NOT CP/M 1.4 LDA CURRENT$DISK ; RESET DISK MOV E,A MVI C,SELDISK CALL BDOS LXI H,T2$OVR5 ; SAVE RETURN ADDRESS ON STACK PUSH H LHLD BUFFER ; PT TO OVERLAY INITIALIZATION PCHL ; "CALL" OVERLAY$INIT T2$OVR5: CALL PRINT$MESSAGE DB ' ... Overlay File Loaded',0 MVI A,0FFH ; SET FLAG STA OVERLAY$LOADED CALL PAUSE POP H ; GET OFFSET * RUN OVERLAY ROUTINE WHOSE OFFSET IS IN L (OFFSET=1,2,3,...) TEXIT2$RUN$OVR: MOV A,L ; MULTIPLY OFFSET BY 3 ADD A ; A=A*2 ADD L ; A=L*3 LHLD BUFFER ; GET ADDRESS OF OVERLAY ADD L ; COMPUTE ABSOLUTE ADDRESS MOV L,A MOV A,H ACI 0 MOV H,A ; HL PT TO ADDRESS OF JMP IN OVERLAY PCHL ; "RUN" ROUTINE * COMMAND NOT FOUND ERROR TEXIT3: RET * MODULE TO OPEN THE OVERLAY FILE * RETURN WITH ZERO FLAG SET IF NOT FOUND OPEN$OVERLAY: LXI D,OVR$FILE ; PT TO FCB MVI C,OPEN CALL BDOS INR A ; ZERO FLAG SET MEANS NOT FOUND RET * MODULE TO DETERMINE THE VERSION NUMBER OF CP/M * SUPPORTS VERSION-INDEPENDENT CODE * RETURNS WITH ZERO FLAG SET IF CP/M 1.4 DET$VERS: MVI C,12 ; BDOS RETURN VERSION NUMBER CALL BDOS MOV A,H ; SET ZERO FLAG IF CP/M 1.4 ORA L RET * EXIT PRINTING MESSAGE FOLLOWING 'CALL ERXIT' ERXIT: POP H ; GET PTR TO MESSAGE CALL CRLF ; NEW LINE ERXIT0: MOV A,M ; GET BYTE CALL OUT$CON ; PRINT CHARACTER INX H ; PT TO NEXT ORA A ; END OF MESSAGE? JNZ ERXIT0 CALL PAUSE ; DELAY JMP TEXITXX * BRIEF DELAY PAUSE: PUSH B MVI B,20 ; 2 SEC PAUSE PAUSE1: CALL ST$CON ; ABORT PAUSE? JNZ PAUSE2 CALL TI$TIME$X ; DELAY FOR 0.1 SEC DCR B ; COUNT DOWN JNZ PAUSE1 POP B RET PAUSE2: POP B ; CLEAR STACK CALL IN$CON ; FLUSH CHAR RET * 0.1 SEC DELAY TI$TIME$X: PUSH H PUSH B LDA PROCESSOR$SPEED ; GET SPEED OF PROCESSOR MOV B,A ; ... IN B TI$TIME$X0: LXI H,TENTH$SEC$DELAY TI$TIME$X1: DCX H ; COUNT DOWN MOV A,H ORA L JNZ TI$TIME$X1 DCR B ; COUNT DOWN FOR PROCESSOR JNZ TI$TIME$X0 POP B POP H RET ** INITIALIZE THE INTERFACES OF THE SYSTEM ** INITIALIZE: CALL INIT$INTERFACES CALL PAUSE ; DELAY JMP TEXIT * *: TERM II, Section 4 * **** BREAK and TRANSLATION TABLE LOADER FUNCTIONS **** * TEXIT ENTRY POINT * INITIAL COMMAND ENTRY POINT * * * TRANSMIT BREAK IF SYSTEM CAN * TRANSMIT$BREAK: MVI A,0 ; CAN SYSTEM? CALL SP$BREAK ; SUPPORT PACKAGE BREAK FUNCTION JNZ TRANS$BREAK CALL PRINT$MESSAGE DB CR,LF,'Sorry, BREAK Function Not Supported',0 CALL PAUSE JMP TEXIT TRANS$BREAK: MVI A,0FFH ; TRANSMIT IT CALL SP$BREAK CALL PRINT$MESSAGE DB CR,LF,'** Break Transmitted **',CR,LF,0 JMP OPTD2 ; GOTO TERMINAL MODE SP$BREAK: PUSH PSW ; SAVE FUNCTION CODE LDA M2$ON ; SELECT MODEM ORA A ; 0=1 JZ SP$BR1 POP PSW ; GET CODE JMP BR$MOD2 SP$BR1: POP PSW ; GET CODE JMP BR$MOD1 * * LOAD TRANSLATION TABLE FROM DISK AND SET FLAGS * TEXIT$XLATE: CALL SCREEN$CLEAR CALL PRINT$ID CALL PRINT$MESSAGE DB CR,LF,LF,' ++ Immediate Translation Table Facility ++' DB CR,LF,LF,0 CALL PRINT$MESSAGE DB 'Turn Off Translation (Off) or Load Translation Tables (Load)' DB CR,LF,' (O/L/^C/=L)? ',0 CALL GET$RESPONSE CPI CTRLC ; ABORT? JZ TEXIT CPI 'O' ; TURN OFF TRANSLATION JZ TEXIT$XLATE$OFF LXI H,XLATE$IN$TABLE ; READ INTO IN$TABLE AND OUT$TABLE CALL READ$512$FILE MVI A,0FFH ; TURN ON FLAGS STA XLATE$IN$FLAG STA XLATE$OUT$FLAG CALL PRINT$ERROR$MESSAGE DB ' Translation Tables Loaded and Translation Enabled',0 CALL PAUSE JMP TEXIT TEXIT$XLATE$OFF: XRA A ; TURN OFF FLAGS STA XLATE$IN$FLAG STA XLATE$OUT$FLAG CALL PRINT$ERROR$MESSAGE DB ' Translation Turned Off',0 CALL PAUSE JMP TEXIT * * READ FILE FROM DISK INTO BUFFER PTED TO BY HL; FILE IS 512 BYTES LONG * READ$512$FILE: PUSH H ; SAVE PTR TO BUFFER CALL LOAD$FCB ; INIT FCB CALL OPEN$FILE CALL INIT$READ$DATA$BUFFER ; LOAD BUFFER POP H ; GET PTR TO BUFFER CALL READ$HL$FROM$FILE CALL READ$HL$FROM$FILE CALL READ$HL$FROM$FILE ; XLATE$OUT$TABLE FOLLOWS IN TABLE CALL READ$HL$FROM$FILE RET * *: TERM II, Section 5 * **** TELEPHONE CONTROL SECTION **** * TEXIT ENTRY POINT * INITIAL COMMAND ENTRY POINT * TPHONE: LXI SP,STACK ; RESET STACK XRA A ; TURN ON MODEM 1 STA M2$ON ; MODEM 2 OFF CALL SCREEN$CLEAR ; CLEAR CRT SCREEN CALL PRINT$ID CALL SCREEN$MESSAGE DB 4,9 DB '++ TERM II TELEPHONE SUBSYSTEM -- Modem 1 ++',0 CALL SCREEN$MESSAGE DB 5,11,0 ; POSITION CURSOR CALL ECHOMS ; PRINT ECHO INFORMATION CALL PRINT$CINDEX ; PRINT HEADER CALL PRINT$MESSAGE DB CR,LF DB '^E Directory Editor A Answer Telephone D Dial ' DB 'Telephone',CR,LF DB ' E Toggle Echo H Hang Up Telephone Q Quit to ' DB 'Exit Mode',CR,LF DB ' T Terminal Mode X Exit to CP/M Y Status ' DB 'Display' DB 0 CALL SCREEN$MESSAGE DB 15,15 DB 'TERM II Telephone Subsystem Command -- ',0 CALL GET$RESPONSE ; GET COMMAND AND CAPITALIZE TPHONE$ENTRY: LXI H,PTAB ; PT TO PHONE COMMANDS CALL TEXIT0 ; PROCESS COMMANDS CALL IC$MSG ; INVALID COMMAND JMP TPHONE ; CONTINUE IF ERROR * * ANSWER TELEPHONE MODE * TI$TEST: CALL STAT$TI ; MUST HAVE TELEPHONE INTERFACE RNZ ; CONTINUE IF SO CALL PRINT$ERROR$MESSAGE DB 'Sorry, Telephone Interface Not Supported',0 CALL PAUSE JMP TPHONE ANSWER$PHONE: CALL TI$TEST ; TEST FOR TELEPHONE INTERFACE LXI SP,STACK ; RESET STACK IN CASE OF ATTEMPTED ABORT CALL SCREEN$CLEAR CALL PRINT$ID CALL SCREEN$MESSAGE DB 4,15,'++ Telephone Answer Mode ++',0 CALL SCREEN$MESSAGE DB 6,10,'Select Mode to Enter Upon Answer --',0 CALL PRINT$MESSAGE DB CR,LF,' C - Conversation' DB CR,LF,' T - Terminal' DB CR,LF,' X - Exit' DB CR,LF,LF,' Enter Selection (C/T/X/^C/=C)? ',0 CALL GET$RESPONSE CPI CTRLC ; ABORT? JZ TPHONE STA ANS$ROUTINE ; STORE RESPONSE CALL PRINT$MESSAGE DB CR,LF,LF,'Hanging Up Telephone',CR,LF,0 CALL TI$INI ; INITIALIZE TELEPHONE SUBSYSTEM CALL TI$PREPANS ; PREPARE ANSWER MODE CALL EXITCHMS ; PRINT EXIT CHAR CALL PRINT$MESSAGE DB CR,LF,' Type Exit Char to Abort and Return to TERM II',CR,LF,0 * WAIT FOR RING OR INTERRUPT ANSWER$LOOP: CALL TI$RST ; RING? JNZ ANSWER CALL ABORT ; INTERRUPT CHECK JMP ANSWER$LOOP * * ANSWER TELEPHONE * ANSWER: CALL TI$ANS ; GOTO ANSWER MODE LDA ANS$ROUTINE ; GET ROUTINE SELECTION CPI 'X' ; EXIT MODE? JZ TEXIT CPI 'T' ; TERMINAL MODE? JZ OPTD JMP TCONV ; CONVERSATION MODE OTHERWISE * * TOGGLE ECHO FLAG FOR TELEPHONE SUBSYSTEM * TPHONE ENTRY POINT * TPECHO: LDA ECHOFL ; GET FLAG CMA STA ECHOFL JMP TPHONE * * HANG UP THE PHONE * TPHONE ENTRY POINT * PH$HANG: CALL PH$HANGUP ; HANG UP TELEPHONE JMP TPHONE PH$HANGUP: CALL TI$TEST ; TEST FOR TELEPHONE INTERFACE CALL PRINT$ERROR$MESSAGE DB 'Disconnecting Telephone -- ',0 CALL TI$HANG ; HANG UP PHONE CALL PRINT$MESSAGE DB 'Telephone is Disconnected',0 CALL PAUSE RET * * DIAL THE PHONE * TPHONE ENTRY POINT * PH$DIAL: CALL TI$TEST ; TEST FOR TELEPHONE INTERFACE LXI SP,STACK CALL SCREEN$CLEAR CALL PRINT$ID CALL PRINT$PHONE$NUMBERS CALL SCREEN$MESSAGE DB 3,9 DB '++ Telephone Dialing System ++',CR,LF,0 CALL PRINT$CINDEX CALL PRINT$MESSAGE DB CR,LF DB '0-9 Select Entry N Enter Number Q Quit',0 CALL SCREEN$MESSAGE DB 4,11 DB 'Dialing Command -- ',0 CALL GET$RESPONSE CPI 'N' ; ENTER NUMBER JZ DIAL$NUM CPI 'Q' ; QUIT JZ TPHONE CPI '0' ; VALID DIGIT? JC PH$DIAL$ERROR CPI '9'+1 JNC PH$DIAL$ERROR JMP DIAL$DIR PH$DIAL$ERROR: CALL PRINT$ERROR$MESSAGE DB BEL,'Invalid Response',0 CALL PAUSE JMP PH$DIAL * ENTER OWN NUMBER TO DIAL DIAL$NUM: CALL SCREEN$MESSAGE DB 4,11,'Enter Telephone Number ( to abort) -->' DB '....................',0 CALL SCREEN$MESSAGE DB 4,53,0 ; POSITION CURSOR LXI D,NUM$BUFFER ; SET UP BUFFER MVI C,10 ; READ FROM CON: CALL BDOS LXI H,NUM$BUFFER+1 ; GET CHAR COUNT MOV A,M ; GET COUNT ORA A ; ABORT IF NO NUMBER ENTERED JZ DIAL$NUM$ABORT INR A ; PT TO END OF BUFFER ADD L MOV L,A MOV A,H ACI 0 MOV H,A ; HL PTS TO END MVI M,0 ; PLACE ZERO AT END LXI H,NUM$BUFFER+2 ; PT TO TELEPHONE NUMBER TO DIAL JMP DIALN ; DIAL IT DIAL$NUM$ABORT: CALL PRINT$ERROR$MESSAGE DB CR,LF,'Aborting to Dialing System',0 CALL PAUSE JMP PH$DIAL * * DIAL NUMBER IN DIRECTORY * DIAL$DIR: SUI '0' ; CONVERT TO BINARY MOV B,A ; COUNT IN B LXI H,PHONE$NUMBERS DIAL$DIR1: MOV A,M ; VALID ENTRY? ORA A ; 0 IF NOT JZ DIAL$DIR2 MOV A,B ; ARE WE THERE? ORA A ; 0=YES JZ DIALN DCR B DIAL$DIR$SKIP: MOV A,M ; GET BYTE -- SKIP NUMBER INX H ; PT TO NEXT ORA A ; DONE? JNZ DIAL$DIR$SKIP DD$SKIP1: MOV A,M ; GET BYTE -- SKIP TITLE INX H ; PT TO NEXT ORA A ; DONE? JNZ DD$SKIP1 JMP DIAL$DIR1 * NOT IN TELEPHONE DIRECTORY DIAL$DIR2: CALL PRINT$ERROR$MESSAGE DB BEL,'Invalid Directory Selection',0 CALL PAUSE ; DELAY JMP PH$DIAL * * DIALN -- DIAL THE TELEPHONE NUMBER POINTED TO BY HL AND ENDING IN ZERO * DIALN: CALL TI$HANG ; ENSURE THAT TELEPHONE IS HUNG UP CALL TI$DSTRT ; START DIALING SEQUENCE JNZ DIALN1 CALL PRINT$ERROR$MESSAGE DB CR,LF,'ERROR -- No Dial Tone',0 CALL PAUSE JMP TPHONE * DIAL THE NUMBER DIALN1: CALL SCREEN$MESSAGE DB 5,5,'Dialing -- ',0 DIALN$LOOP: MOV A,M ; GET DIGIT TO DIAL ORA A ; END OF DIGITS? JZ DIALN$DONE ; DONE DIALING CALL OUT$CON ; PRINT DIGIT BEING DIALED CALL TI$DIGIT ; DIAL DIGIT INX H ; PT TO NEXT JMP DIALN$LOOP ; CONTINUE DIALING DIALN$DONE: CALL PRINT$MESSAGE DB ' -- Waiting for Answer',0 CALL TI$DSTOP ; STOP DIALING JNZ DIALN$GO ; GOTO TERMINAL MODE CALL PRINT$ERROR$MESSAGE DB CR,LF,'Timeout - No Answer Received',0 CALL PAUSE CALL PH$HANGUP ; HANG UP TELEPHONE JMP TPHONE DIALN$GO: JMP OPTD1 * *: TERM II, Section 6 * * HANG$EXIT -- HANG UP PHONE AND EXIT TO CP/M * TEXIT ENTRY POINT * HANG$EXIT: CALL PH$HANGUP ; HANG UP TELEPHONE JMP EXIT * *: TERM II, Section 7 * **** TRANSMIT EXIT$CHAR and SWITCH$MODEMS **** * TEXIT ENTRY POINT * TROUT: CALL OUT$MOD ; SEND CHAR TO MODEM JMP OPTD2 * **** SWITCH MODEMS **** * TEXIT ENTRY POINT * MODEM$SWITCH: XRA A ; ENABLE MODEM 1 BY DEFAULT STA M2$ON ; MODEM 2 OFF CALL SP$STAT ; CHECK TO SEE IF MODEM 2 AVAILABLE ANI 1000B ; MODEM 2 ON? JZ MODEM$SW1 CALL PRINT$MESSAGE DB CR,LF,'Enable Modem 1 or Modem 2 (1/2/=1)? ',0 CALL GET$RESPONSE CPI '2' JNZ TEXIT MVI A,0FFH ; TURN ON MODEM 2 STA M2$ON JMP TEXIT * MODEM 2 NOT AVAILABLE MODEM$SW1: CALL PRINT$ERROR$MESSAGE DB CR,LF,'Sorry -- Modem 2 Not Supported -- Modem 1 ON',CR,LF,0 CALL PAUSE ; DELAY JMP TEXIT * *: TERM II, Section 8 **** USER$CMND **** * * USER$CMND -- * USER-DEFINED CP/M COMMAND LINE EXECUTION ROUTINE * THIS FUNCTION ALLOWS THE USER TO ENTER A CP/M COMMAND LINE TO BE * EXECUTED AND THEN EXECUTES IT AS A SUBMIT FILE, RETURNING TO TERM WHEN * DONE * USER$CMND: CALL SCREEN$CLEAR CALL PRINT$ID CALL SCREEN$MESSAGE DB 4,12 DB '++ TERM II CP/M User Command Execution ++',0 CALL SCREEN$MESSAGE DB 6,20 DB 'Please Enter CP/M Command Line or =Abort' DB 0 CALL SCREEN$MESSAGE DB 8,6,'CP/M Command>.........................................',0 CALL SCREEN$MESSAGE DB 8,19,0 ; POSITION CURSOR CALL C1$FLUSH LXI D,NUM$BUFFER MVI C,CREAD ; GET COMMAND CALL BDOS LXI H,NUM$BUFFER+1 MOV A,M ; GET CHAR COUNT ORA A ; ABORT IF NONE JNZ USER$CMND0 USER$CMND$ABORT: CALL PRINT$ERROR$MESSAGE DB 'Aborting to Exit Mode',0 CALL PAUSE JMP TEXIT USER$CMND0: MOV C,A ; CHAR COUNT IN C USER$CMND0$LOOP: INX H ; STRIP OFF LEADING OR LESS MOV A,M ; GET BYTE CPI ' '+1 ; OK IF > JNC USER$CMND0$DONE MVI M,0FFH ; STORE 0FFH DCR C ; DECREMENT CHAR COUNT JNZ USER$CMND0$LOOP JMP USER$CMND$ABORT USER$CMND0$DONE: LXI H,NUM$BUFFER+1 ; GET OLD CHAR COUNT MOV B,M ; SAVE IN B MOV M,C ; STORE NEW CHAR COUNT MOV A,B ; GET OLD CHAR COUNT IN A USER$CMND1: INX H ; PT TO 1ST BYTE ADD L ; MAKE HL PT TO LAST BYTE MOV L,A MOV A,H ACI 0 MOV H,A ; HL PTS TO AFTER LAST BYTE MVI M,0 ; STORE ENDING 0 LXI D,SUBFIL ; OPEN $$$.SUB MVI C,ERASE ; DELETE IT FIRST PUSH D CALL BDOS CALL ZERO$SUBFIL ; INITIALIZE FIELDS POP D MVI C,MAKE ; CREATE IT CALL BDOS CPI 0FFH ; ERROR? JNZ USER$CMND2 CALL PRINT$ERROR$MESSAGE DB 'Error -- Cannot Create $$$.SUB File -- Directory Full',0 CALL PAUSE JMP TEXIT USER$CMND2: LDA CURR$CMND ; PLACE OPTION IN COMMAND LINE ORA A ; 0=MASTER JZ USER$CMND2M MVI A,'A' ; SET ABBREVIATED DB LXIB ; SKIP NEXT INSTR USER$CMND2M: MVI A,'M' ; SET MASTER STA TERMCMND+8 ; SET IT IN COMMAND LINE LDA M2$ON ; PLACE OPTION FROM MODEM IN COMMAND LINE ORA A ; 0=MODEM 2 JZ USER$CMND21 MVI A,'2' ; SET MODEM 2 DB LXIB ; SKIP NEXT INSTR USER$CMND21: MVI A,'1' ; SET MODEM 1 STA TERMCMND+9 LXI H,TERMCMND ; RETURN TO TERM COMMAND CALL WRITE$SUBMIT LXI H,NUM$BUFFER+1 ; WRITE COMMAND CALL WRITE$SUBMIT LXI D,SUBFIL ; CLOSE $$$.SUB AND EXECUTE MVI C,CLOSE CALL BDOS JMP CPM ; WARM BOOT WRITE$SUBMIT: LXI D,BUFF ; WRITE TO BUFFER WSUB1: MOV A,M ; GET BYTE INX H ; PT TO NEXT CPI 0FFH ; DON'T STORE 0FFH JZ WSUB1 STAX D ; PUT BYTE INX D ORA A ; END OF COMMAND? JNZ WSUB1 LXI D,BUFF ; SET DMA ADDRESS MVI C,STDMA CALL BDOS LXI D,SUBFIL ; WRITE FILE MVI C,WRITE CALL BDOS ORA A ; 0=OK RZ CALL PRINT$ERROR$MESSAGE DB 'ERROR -- Cannot Write to $$$.SUB -- Disk Full',0 CALL PAUSE JMP TEXIT * *: TERM II, Section 9 * **** CLOSE CURRENT FILE **** * TEXIT ENTRY POINT * FCLOS: CALL CLOSE$FILE ; CLOSE FILE CALL ERXIT DB '** Direct File Close Complete **',0 * *: TERM II, Section 10 * **** TOGGLE ECHO FLAG **** * TEXIT ENTRY POINT * TECHO: LDA ECHOFL ; SWITCH FLAG CMA STA ECHOFL CALL CRLF CALL ECHOMS JMP OPTD2 * *: TERM II, Section 11 * ** DISPLAY STATUS ** * STATUS$DISPLAY: CALL SCREEN$CLEAR ; CLEAR SCREEN CALL PRINT$STATUS ; PRINT STATUS REPORT RET * **** DISPLAY STATUS FOR TERMINAL EXIT MODE **** * TEXIT ENTRY POINT * STATUS1$DISPLAY: CALL STATUS$DISPLAY JMP TEXIT * **** DISPLAY STATUS FOR TELEPHONE SUBSYSTEM **** * TPHONE ENTRY POINT * STATUS2$DISPLAY: CALL STATUS$DISPLAY JMP TPHONE * *: TERM II, Section 12 * **** TOGGLE BUFFER LOAD FLAG **** * TEXIT ENTRY POINT * TBUF: LDA BUFLG ; LOAD IN PROGRESS? ORA A ; SET FLAGS JNZ TBUF$MODEM ; YES -- TURN OFF MODEM LOAD CALL SCREEN$CLEAR CALL SCREEN$MESSAGE DB 4,9,'++ TERM II Buffer Load Facility ++',0 CALL PRINT$MESSAGE DB CR,LF,LF,'Load Buffer from Modem or Disk (MODEM/DISK/^C/' DB '=MODEM)? ',0 CALL GET$RESPONSE CPI CTRLC ; ABORT? JZ TEXIT PUSH PSW ; SAVE RESPONSE XRA A ; TURN OFF OVERLAY STA OVERLAY$LOADED POP PSW ; GET RESPONSE AND CONTINUE CPI 'D' ; LOAD FROM DISK? JNZ TBUF$MODEM * ** LOAD BUFFER FROM DISK ** * CALL INIT$FCB ; INITIALIZE FCB CALL OPEN$FILE ; OPEN THE FILE CALL INIT$READ$DATA$BUFFER ; LOAD BUFFER LHLD BUFFER ; PT TO START OF BUFFER SHLD BUF$NEXT ; SAVE PTR TO NEXT BYTE IN BUFFER * READ SECTOR OF FILE TBUF1: CALL READ$BLOCK ; READ NEXT BLOCK * STORE SECTOR IN BUFFER TBUF2: LHLD BUFEND ; GET END OF BUFFER XCHG LHLD BUF$NEXT ; GET PTR TO NEXT BYTE IN BUFFER MOV A,H SUB D JC TBUF2A CALL ERXIT DB BEL,'Buffer Overflow -- Load Incomplete',0 TBUF2A: LXI D,BUFF ; PT TO SECTOR IN MEMORY * LOAD SECTOR INTO BUFFER TBUF2B: LDAX D ; GET CHAR CPI CTRLZ ; EOF? JZ TBUF2C MOV M,A INX H ; PT TO NEXT INX D MOV A,D ; DONE? CPI 1 JNZ TBUF2B SHLD BUF$NEXT ; SAVE PTR TO NEXT BYTE OF BUFFER JMP TBUF1 TBUF2C: DCX H ; PT TO LAST BYTE IN BUFFER SHLD BUF$NEXT * CLOSE FILE AND EXIT TBUF3: JMP OPTD1 * ** LOAD BUFFER FROM MODEM -- TOGGLE FLAG SECTION ** * TBUF$MODEM: LDA BUFLG ; GET FLAG CMA STA BUFLG CALL BUFMS ; PRINT MESSAGE LDA BUFLG ; INIT BUFFER IF NECESSARY ORA A JZ OPTD2 ; BUFFER OFF CALL CONTINUE MVI A,10 ; SET ALARM COUNTER STA BELLCTR XRA A ; A=0 STA BFL$FLAG ; SET STATUS FLAG OFF STA CTRLS$FLAG ; SET ^S FLAG OFF CALL PRINT$MESSAGE DB CR,LF,'Transmit ^S if Buffer is Almost Full (Y/N/=N)? ',0 CALL GET$RESPONSE CPI 'Y' JNZ TBUF$MODEM1 MVI A,0FFH ; SET ^S STA CTRLS$FLAG TBUF$MODEM1: CALL PRINT$MESSAGE DB CR,LF,'Clear Memory Buffer (Y/N/=N)? ',0 CALL GET$RESPONSE CPI 'Y' JNZ TBUF$MODEM2 LHLD BUFFER ; BUFFER ON -- SET PTR SHLD BUF$NEXT TBUF$MODEM2: CALL PRINT$MESSAGE DB CR,LF,'Display Buffer Status (Y/N/=N)? ',0 CALL GET$RESPONSE CPI 'Y' JNZ TBUF$MODEM3 MVI A,0FFH ; TURN ON FLAG STA BFL$FLAG ; SET FLAG TBUF$MODEM3: CALL PRINT$MESSAGE DB CR,LF,'Do you wish to invoke the Buffer Load Subsystem' DB ' (Y/N/=N)? ',0 CALL GET$RESPONSE CPI 'Y' JNZ OPTD2 ; TERMINAL MODE IF NOT * * BUFFER LOAD SUBSYSTEM LOADER * CAN BE INVOKED FROM MASTER COMMAND LEVEL (MCL) * BLS$LOADER: LXI H,TERMBLS$FCB ; SET UP FCB LXI D,FCB MVI B,32 CALL MOVE CALL PRINT$MESSAGE DB CR,LF,'++ Loading TERM II BLS ++',CR,LF,0 JMP TERM$LOADER * *: TERM II, Section 13 * **** TOGGLE PRINTER FLAG AND ENGAGE PRINTER IF TOGGLED TO ON **** * TEXIT ENTRY POINT * TPRNT: CALL STAT$TTY ; PRINTER AVAILABLE? JNZ TPRNT0 * PRINTER NOT AVAILABLE THRU SUPPORT PACKAGE CALL PRINT$MESSAGE DB CR,LF,'Sorry -- Printer Not Supported',CR,LF,0 JMP OPTD2 * PRINTER AVAILABLE TPRNT0: LDA PRFL ; GET FLAG CMA STA PRFL CALL PRMS ; PRINT MESSAGE LDA PRFL ; PRINTER ON? ORA A JZ OPTD2 * INITIALIZE PRINTER QUEUE LXI H,QP ; SET QUEUE POINTER SHLD QOP SHLD QIP MVI B,0 ; ZERO QUEUE TPRNT1: MVI M,0 ; STORE ZERO INR L ; PT TO NEXT CIRCULAR DCR B JNZ TPRNT1 JMP OPTD2 * *: TERM II, Section 14 * **** ENGAGE CONVERSATIONAL MODE **** * TEXIT ENTRY POINT * INITIAL COMMAND ENTRY POINT * TCONV: CALL STAT$CRT ; CURSOR ADDRESSING AVAILABLE? JNZ TCONV0 * CURSOR ADDRESSING NOT AVAILABLE THRU SUPPORT PACKAGE CALL PRINT$MESSAGE DB CR,LF,'Sorry -- CRT Cursor Addressing Not Supported',CR,LF,0 CALL PAUSE JMP OPTD2 * CURSOR ADDRESSING AVAILABLE TCONV0: XRA A ; TURN OFF ECHO STA ECHOFL CALL SCREEN$CLEAR ; CLEAR CRT SCREEN CALL SCREEN$MESSAGE DB 5,25,'++ CONVERSATION Mode Version 1.2 ++',0 CALL SCREEN$MESSAGE DB 7,31,'Type Exit Char to Exit',0 CALL SCREEN$MESSAGE DB 8,32,0 ; POSITION CURSOR FOR MSG CALL EXITCHMS ; PRINT EXIT CHAR CALL SCREEN$MESSAGE DB 12,1,0 ; POSITION CURSOR CALL PRINT$MESSAGE DB '** Editing Characters **',CR,LF DB ' - Delete Previous Character',CR,LF DB ' - Erase Line',CR,LF DB ' ^X - Erase SEND Line Locally',CR,LF DB ' ^Y - Erase RECV Line Locally',CR,LF DB ' ^Z - Erase SEND and RECV Lines Locally',0 MVI H,REC$LINE ; SET RECEIVE ON LINE REC$LINE MVI L,1 SHLD NEXTR MVI H,SEND$LINE ; SET SEND ON LINE SEND$LINE SHLD NEXTS CALL GOTOXY ; POSITION CURSOR CALL PRINT$MESSAGE DB 'S:',0 CALL CLEAR ; CLEAR REST OF LINE LHLD NEXTR ; POSITION CURSOR CALL GOTOXY CALL PRINT$MESSAGE DB 'R:',0 CALL CLEAR ; CLEAR REST OF LINE LHLD NEXTR ; RESET POINTER MVI L,3 SHLD NEXTR LHLD NEXTS ; RESET POINTER MVI L,3 SHLD NEXTS CALL GOTOXY JMP CONVERSATION * *: TERM II, Section 15 * **** DUMP STORAGE BUFFER TO MODEM OR DISK **** * TEXIT ENTRY POINT * DUMP$BUF: CALL SCREEN$CLEAR CALL PRINT$ID CALL PRINT$MESSAGE DB CR,LF,LF,' ++ TERM II Buffer Dump Facility ++',CR,LF,0 XRA A ; (A=0) TURN OFF BUFFER LOAD STA BUFLG CALL BUFMS ; PRINT MESSAGE LDA OVERLAY$LOADED ; CHECK FOR OVERLAY IN BUFFER ORA A ; 0=OK JZ DUMP$BUF1 CALL PRINT$MESSAGE DB CR,LF,'ERROR -- Overlay is Loaded in Memory Buffer',0 CALL PAUSE JMP TEXIT * DETERMINE IF DUMP IS TO BE TO MODEM OR DISK DUMP$BUF1: CALL CONTINUE ; CONTINUE OPTION? CALL PRINT$MESSAGE DB CR,LF,'Dump to Modem or Disk (MODEM/DISK/^C/' DB '=MODEM)? ',0 CALL GET$RESPONSE CPI CTRLC ; ABORT? JZ TEXIT CPI 'D' ; DUMP TO DISK JNZ DUMP$TO$MODEM * ** DUMP TO DISK ** * LXI H,BUFF ; PT TO SECTOR BUFFER SHLD BUF$PTR ; SET PTR CALL INIT$FCB ; SET FCB CALL ERASE$OLD$FILE CALL MAKE$NEW$FILE CALL INIT$WRITE$DATA$BUFFER ; INIT DATA BUFFER FOR I/O LHLD BUFFER ; START OF BUFFER * CHECK FOR COMPLETION OF DUMP DISK$DUMP$NEXT: CALL DUMP$COMPLETE ; DONE? JZ DISK$DUMP$BLOCK ; DUMP NEXT BLOCK * CLEAN UP DISK DUMP LHLD BUF$PTR ; PT TO NEXT BYTE IN BUFFER DDN1: MVI M,CTRLZ ; FILL SECTOR W/CTRL-Z'S INR L ; PT TO NEXT JNZ DDN1 * WRITE LAST SECTOR AND CLOSE FILE CALL CLOSE$FILE JMP TEXIT * DUMP BUFFER TO DISK DISK$DUMP$BLOCK: XCHG ; DE PTS TO BYTE LHLD BUF$PTR ; PT TO NEXT BYTE IN DISK BUFFER MOV A,H ; BUFFER FULL? CPI 1 JZ DDB2 LDAX D ; GET BYTE ORA A ; SKIP NULLS JZ DDB1 MOV M,A ; PLACE IN BUFFER INX H ; INCR BUFFER PTR SHLD BUF$PTR DDB1: XCHG ; HL NOW PTS TO CURRENT BYTE CALL ABORT ; CHECK FOR ABORT INX H ; PT TO NEXT BYTE JMP DISK$DUMP$NEXT * WRITE SECTOR TO DISK DDB2: LXI H,BUFF ; RESET BUFFER PTR SHLD BUF$PTR XCHG ; HL PTS TO BYTE PUSH H ; SAVE IT CALL WRITE$TO$FILE ; WRITE BUFF TO DISK POP H ; GET PTR TO CURRENT BYTE JMP DISK$DUMP$BLOCK * ** DUMP TO MODEM ** * DUMP$TO$MODEM: CALL SET$MONITOR ; SET MONITORING OF OUTPUT CALL SET$DELAY ; SET DELAY OF EACH CHAR CALL SET$LF$DELAY ; SET DELAY FOR PROCESSING CALL SET$RING ; SET COMPLETION BELL LHLD BUFFER ; START OF BUFFER * CHECK FOR COMPLETION OF DUMP MODEM$DUMP$NEXT: CALL DUMP$COMPLETE ; DONE? JNZ TEXIT ; GO TO TEXIT WHEN DONE * SEND CHAR TO MODEM MOV A,M ; GET CHAR ANI 7FH ; MASK OUT MSB JZ MODEM$DUMP$SKIP ; DON'T OUTPUT CPI 7FH ; DON'T OUTPUT JZ MODEM$DUMP$SKIP CALL SEND ; SEND CHAR CPI LF ; DELAY IF CZ LF$DELAY MODEM$DUMP$SKIP: CALL ABORT ; CHECK FOR ABORT INX H ; PT TO NEXT CHAR JMP MODEM$DUMP$NEXT * DETERMINE IF DUMP IS FINISHED; A=0 IF NO, A=0FFH IF YES DUMP$COMPLETE: PUSH H ; SAVE HL XCHG ; SAVE HL LHLD BUF$NEXT ; CHECK FOR DONE XCHG ; HL PTS TO NEXT BYTE, DE PTS TO END OF BUFFER MOV A,D ; START OF BUFFER? SUB H JC DC1 JNZ DC2 MOV A,E SUB L JC DC1 JNZ DC2 DC1: CALL PRINT$MESSAGE DB CR,LF,'** Buffer Dump Complete **',CR,LF,0 XRA A ; TURN OFF SKIP STA LF$SKIP CMA ; A=0FFH ORA A ; SET FLAGS POP H RET DC2: XRA A ; A=0 POP H RET * *: TERM II, Section 16 * **** DISPLAY/SET FILE NAME OF SPECIFIED FILE **** * TEXIT ENTRY POINT * FILE$SET: CALL PRINT$FN ; PRINT FILE NAME CALL PRINT$MESSAGE DB CR,LF,'Change File Name (Y/N/=N)? ',0 CALL GET$RESPONSE CPI 'Y' ; YES? JNZ TEXIT CALL LOAD$FCB ; LOAD FCB (FILE NAME) JMP TEXIT * *: TERM II, Section 17 * **** CONVERSATION MODE **** * * CONVERSATION MODE -- ALLOWS THE USER TO EMPLOY THE CURSOR * ADDRESSING ROUTINE 'GOTOXY' TO VIEW DYNAMICALLY * HIS CONVERSATION WITH THE EXTERNAL DEVICE * ALL COMMANDS AVAILABLE IN NORMAL TERMINAL MODE ARE ALSO AVAILABLE * IN CONVERSATION MODE. TWO LINES ARE DISPLAYED -- S: IS * USER'S INPUT WHICH IS BEING SENT, R: IS EXTERNAL DEVICE * BEING RECEIVED; WILL ERASE THE CORRESPONDING LINE * CONVERSATION: CALL LOS ; CHECK FOR LOSS OF SIGNAL CALL ST$CON ; CHECK CONSOLE STATUS JNZ CONVT ; GET CONSOLE CHAR CALL ST$MOD ; CHECK MODEM STATUS JZ CONVERSATION * PROCESS MODEM CHAR CALL IN$MOD ; GET MODEM CHAR LHLD NEXTR ; SET CURSOR FOR RECEIVED CHAR CALL BSTEST ; ? SHLD NEXTR ; RESET CURSOR CPI CR ; CLEAR LINE? JZ CONVT$CTRLY CPI LF ; IGNORE LF JZ CONVERSATION CPI BS ; ? CNZ OUT$CON ; PRINT JMP CONVERSATION * CLEAR SEND AND RECV LINES CONVT$CTRLZ: LHLD NEXTS ; CLEAR SEND CALL CLEAR SHLD NEXTS ; RESET CURSOR * CLEAR RECV LINE CONVT$CTRLY: LHLD NEXTR ; SET CURSOR CALL CLEAR ; CLEAR RECEIVE LINE SHLD NEXTR ; RESET CURSOR JMP CONVERSATION * PROCESS TERMINAL CHAR CONVT: CALL IN$CON ; GET CHAR CALL EXIT$CHECK ; CHECK FOR EXIT CHAR JZ TEXIT CPI LF ; SKIP JZ CONVERSATION CPI CTRLY ; CLEAR RECV LINE W/O XMITTING? JZ CONVT$CTRLY CPI CTRLZ ; CLEAR SEND AND RECV LINES? JZ CONVT$CTRLZ LHLD NEXTS ; SET CURSOR CPI CTRLX ; CLEAR SEND LINE W/O XMITTING? JZ CONVT$CTRLX CALL BSTEST ; ? SHLD NEXTS ; RESET CURSOR CALL OUT$MOD ; SEND TO MODEM CPI BS ; ? JZ CONVERSATION CALL OUT$CON ; PRINT ON CONSOLE CPI CR ; NEW LINE? JNZ CONVERSATION MVI A,LF ; SEND AFTER CALL OUT$MOD * CLEAR SEND LINE CONVT$CTRLX: CALL CLEAR ; CLEAR CURRENT LINE SHLD NEXTS ; RESET CURSOR JMP CONVERSATION * CHECK IF CHAR WAS AND BACK CURSOR IF SO BSTEST: CPI BS ; ? JNZ BSTST2 ; DO NOT DCX H ; BACK UP CURSOR PUSH PSW ; SAVE CHAR MOV A,L ; TOO FAR? CPI 3 JNC BSTST1 MVI L,3 ; SET TO BOL BSTST1: CALL GOTOHL ; POSITION CURSOR MVI A,' ' ; DELETE CHAR CALL OUT$CON MVI A,BS ; BACK UP AGAIN CALL OUT$CON DCX H ; BACK UP AGAIN POP PSW ; GET CHAR RET BSTST2: CPI ' ' ; DO NOT ADVANCE IF LESS THAN RC CALL GOTOHL ; POSITION CURSOR RET * *: TERM II, Section 18 * **** SUPPORT SECTION -- STATUS MESSAGE DISPLAYS **** * * * PRINT STATUS REPORT ON FACILITIES ENABLED IN SUPPORT PACKAGE * PRINT$STATUS: CALL PRINT$ID CALL PRINT$MESSAGE DB CR,LF,'++ TERM II Status Display ++',CR,LF,0 CALL EXITCHMS ; EXIT CHAR CALL BREAKMS ; BREAK CHAR CALL PRINT$MESSAGE DB CR,LF,'** Support Package Functions **' DB CR,LF,' CRT Clear Screen and Cursor Addressing: ',0 CALL STAT$CRT ; GET CRT STATUS CZ PS$OFF ; PRINT OFF IF ZERO CNZ PS$ON ; PRINT ON IF NOT ZERO CALL PRINT$MESSAGE DB CR,LF,' Printer: ',0 CALL STAT$TTY ; GET PRINTER STATUS CZ PS$OFF CNZ PS$ON CALL PRINT$MESSAGE DB CR,LF,' Modem 1: ',0 CALL STAT$M1 ; GET MODEM 1 STATUS CZ PS$OFF CNZ PS$ON CALL PRINT$MESSAGE DB ' Modem 2: ',0 CALL STAT$M2 ; GET MODEM 2 STATUS CZ PS$OFF CNZ PS$ON CALL PRINT$MESSAGE DB CR,LF,' Alt 1: ',0 CALL STAT$A1 ; GET ALTERNATE 1 STATUS CZ PS$OFF CNZ PS$ON CALL PRINT$MESSAGE DB ' Alt 2: ',0 CALL STAT$A2 ; GET ALTERNATE 2 STATUS CZ PS$OFF CNZ PS$ON CALL PRINT$MESSAGE DB ' Alt 3: ',0 CALL STAT$A3 ; GET ALTERNATE 3 STATUS CZ PS$OFF CNZ PS$ON CALL PRINT$MESSAGE DB CR,LF,' Telephone System Interface: ',0 CALL STAT$TI ; GET TELEPHONE INTERFACE STATUS CZ PS$OFF CNZ PS$ON CALL STAT$TI JZ PR$ST$1 ; SKIP CARRIER CHECK IF NO INTERFACE CALL PRINT$MESSAGE DB CR,LF,' Telephone System Carrier Status: ',0 CALL TI$CST ; CHECK CARRIER STATUS CZ PS$OFF CNZ PS$ON PR$ST$1: CALL PRINT$MESSAGE DB CR,LF,LF,'** TERM II File Specification **',0 LDA FCB+1 ; CHECK FOR FILE NAME CPI ' '+1 ; OR LESS IF NONE JC PR$ST$2 CALL PRINT$FN ; PRINT FILE NAME JMP PR$ST$3 PR$ST$2: CALL PRINT$MESSAGE DB CR,LF,' File Name Not Specified',CR,LF,0 PR$ST$3: CALL PRINT$MESSAGE DB CR,LF,'** TERM II Buffer Status **',CR,LF DB ' Overlay is ',0 LDA OVERLAY$LOADED ORA A ; 0=NO JNZ PR$ST$4 CALL PRINT$MESSAGE DB 'NOT',0 PR$ST$4: CALL PRINT$MESSAGE DB ' Loaded into the Memory Buffer',CR,LF DB ' Starting Page of Memory Buffer: ',0 LHLD BUFFER ; GET STARTING ADDRESS MOV A,H ; GET PAGE NUMBER CALL HEX$OUT CALL PRINT$MESSAGE DB ' Hexadecimal',CR,LF DB ' Ending Page of Memory Buffer: ',0 LHLD BUFEND MOV A,H DCR A CALL HEX$OUT CALL PRINT$MESSAGE DB ' Hexadecimal',CR,LF DB ' Space Left in Memory Buffer: ',0 LHLD BUF$NEXT ; GET CURRENT XCHG LHLD BUFEND ; GET END MOV A,H ; SUBTRACT CURRENT FROM END SUB D CALL DECIMAL$OUT ; PRINT IN DECIMAL CALL PRINT$MESSAGE DB ' Pages',CR,LF,0 CALL PRINT$MESSAGE DB CR,LF,'End of Page 1 -- Type ^C to Abort/Any Other Char to ' DB 'Continue - ',0 CALL GET$RESPONSE CPI CTRLC ; ABORT? RZ PR$FLAGS: CALL SCREEN$CLEAR CALL PRINT$ID CALL PRINT$MESSAGE DB '++ Flag Settings ++',CR,LF,0 CALL PR$INIT$CMND ; INITIAL EXIT COMMAND SETTING CALL PR$TERM$ENTRY ; TERMINAL MODE ENTRY STATUS CALL PRSPDMS ; PROCESSOR SPEED CALL AIMS ; AUTOMATIC INTERFACE INITIALIZATION CALL CRLF CALL ECHOMS ; ECHO CALL PRMS ; PRINTER CALL BUFMS ; BUFFER LOAD CALL MODMS ; MODEM CALL CRLF CALL EXITCHMS ; EXIT CHAR CALL BREAKMS ; BREAK CHAR CALL CRLF CALL XLATEMS ; TRANSLATION CALL MASKMS ; MASKS CALL PRINT$MESSAGE DB CR,LF,'**** End of TERM II Status Display ****',CR,LF DB '- Type Any Char to Continue - ',0 CALL GET$RESPONSE RET * CRT STATUS STAT$CRT: CALL SP$STAT ; GET SUPPORT PACKAGE STATUS ANI 1 ; 0=NOT SUPPORTED RET * TTY STATUS STAT$TTY: CALL SP$STAT ; GET SUPPORT PACKAGE STATUS ANI 10B ; 0=NOT SUPPORTED RET * MODEM 1 STATUS STAT$M1: CALL SP$STAT ; GET SUPPORT PACKAGE STATUS ANI 100B ; 0=NOT SUPPORTED RET * MODEM 2 STATUS STAT$M2: CALL SP$STAT ; GET SUPPORT PACKAGE STATUS ANI 1000B ; 0=NOT SUPPORTED RET * ALTERNATE 1 STATUS STAT$A1: CALL SP$STAT ANI 1$0000B RET * ALTERNATE 2 STATUS STAT$A2: CALL SP$STAT ANI 10$0000B RET * ALTERNATE 3 STATUS STAT$A3: CALL SP$STAT ANI 100$0000B RET * TELEPHONE INTERFACE STATUS STAT$TI: CALL SP$STAT ; GET SUPPORT PACKAGE STATUS ANI 1000$0000B ; 0=NOT SUPPORTED RET * STATUS ROUTINE (HL NOT CHANGED) SP$STAT: PUSH H ; SAVE HL CALL SP1$STAT POP H RET * SUPPORT ROUTINES TO PRINT 'ON' AND 'OFF' PS$OFF: PUSH PSW ; SAVE FLAGS CALL PRINT$MESSAGE DB 'OFF',0 POP PSW RET PS$ON: PUSH PSW ; SAVE FLAGS CALL PRINT$MESSAGE DB 'ON',0 POP PSW RET * * CHECK AUTOMATIC INTERFACE INITIALIZATION FLAG AND DISPLAY ITS STATUS * AIMS: CALL PRINT$MESSAGE DB 'Automatic Interface Initialization is ',0 LDA AUTO$INIT ; GET FLAG ORA A ; SET FLAGS CZ PS$OFF CNZ PS$ON JMP CRLF ; NEW LINE * * CHECK ECHO FLAG AND DISPLAY ECHO MESSAGE * ECHOMS: LDA ECHOFL ; GET ECHO FLAG ORA A ; ZERO MEANS NO JZ ECHONO CALL PRINT$MESSAGE DB '-- Echo ON --',CR,LF,0 RET ECHONO: CALL PRINT$MESSAGE DB '-- Echo OFF --',CR,LF,0 RET * * CHECK PRINTER FLAG AND DISPLAY MESSAGE * PRMS: LDA PRFL ; CHECK FLAG ORA A JZ PRNO CALL PRINT$MESSAGE DB '-- Printer ON --',CR,LF,0 RET PRNO: CALL PRINT$MESSAGE DB '-- Printer OFF --',CR,LF,0 RET * * CHECK BUFFER LOAD FLAG AND DISPLAY MESSAGE * BUFMS: LDA BUFLG ; CHECK FLAG ORA A JZ BUFNO CALL PRINT$MESSAGE DB '-- Buffer Load ON --',CR,LF,0 RET BUFNO: CALL PRINT$MESSAGE DB '-- Buffer Load OFF --',CR,LF,0 RET * * CHECK MODEM FLAG AND DISPLAY MESSAGE * MODMS: LDA M2$ON ; CHECK FLAG ORA A JZ MODNO CALL PRINT$MESSAGE DB '-- Modem 2 ON --',CR,LF,0 RET MODNO: CALL PRINT$MESSAGE DB '-- Modem 1 ON --',CR,LF,0 RET * * CHECK TRANSLATION TABLE FLAGS AND DISPLAY MESSAGE * XLATEMS: LDA XLATE$IN$FLAG ; INPUT TRANSLATION ORA A ; 0=NO JZ XLATEINO CALL PRINT$MESSAGE DB '-- Input Translation ON, ',0 JMP XLATEO XLATEINO: CALL PRINT$MESSAGE DB '-- Input Translation OFF, ',0 XLATEO: LDA XLATE$OUT$FLAG ; OUTPUT TRANSLATION ORA A ; 0=NO JZ XLATEONO CALL PRINT$MESSAGE DB 'Output Translation ON --',CR,LF,0 RET XLATEONO: CALL PRINT$MESSAGE DB 'Output Translation OFF --',CR,LF,0 RET * * DISPLAY PROCESSOR SPEED * PRSPDMS: CALL PRINT$MESSAGE DB ' Processor Speed is ',0 LDA PROCESSOR$SPEED ADI '0' ; CONVERT TO ASCII CALL PRINT$CONTROL$CHAR CALL PRINT$MESSAGE DB 'MHz',CR,LF,0 RET * * DISPLAY EXIT CHARACTER * EXITCHMS: CALL PRINT$MESSAGE DB 'Exit Character is ',0 CALL PRINT$EXIT$CHAR CALL CRLF RET * * DISPLAY BREAK CHARACTER * BREAKMS: CALL PRINT$MESSAGE DB 'Break Character is ',0 LDA BREAK$CHAR ; GET CHAR ORA A ; 0=NOT DEFINED JZ BREAKMS$NO CALL PRINT$CONTROL$CHAR CALL CRLF RET BREAKMS$NO: CALL PRINT$MESSAGE DB 'Not Defined',CR,LF,0 RET * * DISPLAY MASKING INFORMATION * MASKMS: CALL PRINT$MESSAGE DB 'Modem Character Input Masking is ',0 LDA MASK$IN$FLAG ; GET FLAG ORA A ; 0=OFF CZ PS$OFF CNZ PS$ON CALL CRLF CALL PRINT$MESSAGE DB 'TERM II Character Output Masking is ',0 LDA MASK$OUT$FLAG ; GET FLAG ORA A ; 0=OFF CZ PS$OFF CNZ PS$ON CALL CRLF LDA MASK$OUT$FLAG ; PRINT MSB MASK SETTING IF ON ORA A ; 0=OFF RZ CALL PRINT$MESSAGE DB ' Most-Significant-Bit (MSB) Output is ',0 LDA OUTPUT$MASK ; GET MASK (BIT 7 SIG) ANI 80H ; GET BIT 7 CZ PS$OFF CNZ PS$ON CALL CRLF RET * * PRINT TERM ENTRY POINT * PR$TERM$ENTRY: CALL PRINT$MESSAGE DB ' Terminal Mode Entry is ',0 LDA TERM$ENTRY ; GET FLAG ORA A ; 0=NO DIR JZ PRTE1 CALL PRINT$MESSAGE DB 'with',0 JMP PRTE2 PRTE1: CALL PRINT$MESSAGE DB 'without',0 PRTE2: CALL PRINT$MESSAGE DB ' Telephone Directory Display',CR,LF,0 RET * * PRINT INITIAL MASTER COMMAND LEVEL MESSAGE * PR$INIT$CMND: CALL PRINT$MESSAGE DB ' Initial Exit Mode is ',0 LDA INIT$CMND ; GET FLAG ORA A ; 0=FULL, 0FFH=ABBREVIATED JZ PRIC$MAST1 CALL PRINT$MESSAGE DB 'Abbreviated',CR,LF,0 JMP PRIC$CONT PRIC$MAST1: CALL PRINT$MESSAGE DB 'Master',CR,LF,0 PRIC$CONT: CALL PRINT$MESSAGE DB ' Current Exit Mode is ',0 LDA CURR$CMND ; GET FLAG ORA A ; 0=MASTER, 0FFH=ABBREVIATED JZ PRIC$MAST2 CALL PRINT$MESSAGE DB 'Abbreviated',CR,LF,0 RET PRIC$MAST2: CALL PRINT$MESSAGE DB 'Master',CR,LF,0 RET * *: TERM II, Section 19 * **** SEND/RECEIVE FILE MODE **** * * This section of the TERMINAL Program contains the routines * required to transmit and receive files between the target (user's) * computer and some host computer (accessed via the modem). * In order to provide a maximum amount of flexibility, TERM II * supports three intersystem file transfer protocols: Protocol 1, Protocol 2, * and Protocol 3. * Protocol 1 is a relatively-complicated file transfer protocol * which supports extensive error checking, detection, and automatic * retry. This protocol is intended to be used between two TERM II * systems. Since it makes extensive use of the ASCII control characters, * Protocol 1 does not readily lend itself to automatic transfer of files * between the target computer and an arbitrary host unless that host can * be user-programmed to use these ASCII control characters also. * Protocol 2 is a relatively-simple file transfer protocol which is * intended to be used between a TERM II target computer and an arbitary * host computer. It does not provide any error detection, and only under * extreme communication difficulty will it provide automatic retry for * error compensation. * Protocol 3 is the protocol employed by the MODEM2/XMODEM programs * of the CP/M User's Group. This protocol is compatable with many Computer * Bulletin Board Systems (CBBSs) across the United States and gives the * TERM II user the ability to readily transfer files to and from these systems. * This protocol also supports extensive error checking, detection, and * automatic retry. * * TEXIT ENTRY POINT * INITIAL COMMAND ENTRY POINT * SEND$FILE: CALL SCREEN$CLEAR CALL PRINT$ID CALL SCREEN$MESSAGE DB 4,15,'++ TERM II File Transfer Facility - SEND Function ++' DB CR,LF,LF,0 CALL INIT$FCB ; RESET FCB CALL OPEN$FILE ; OPEN THE FILE CALL PRINT$MESSAGE DB 'File Open',CR,LF,0 CALL SET$MONITOR ; SET MONITOR FLAG CALL SET$DELAY ; SET MODEM OUTPUT DELAY CALL SET$RING ; SET COMPLETION BELL XRA A ; TURN OFF SKIP AND DELAY STA LF$SKIP STA CR$DELAY CALL INIT$PRINT$DOT ; INIT DOT DISPLAY FOR NO-MONITORING CALL PRINT$MESSAGE DB CR,LF,'Transmission Protocol Selection' DB CR,LF,' 1 -- TERM II Protocol' DB CR,LF,' 2 -- Arbitrary Host Protocol' DB CR,LF,' 3 -- CP/M User''s Group (Christensen) Protocol' DB CR,LF,' Selection (1/2/3/^C/=1)? ',0 CALL GET$RESPONSE CPI CTRLC JZ TEXIT CPI '3' JZ SEND3$FILE$START CPI '2' JZ SEND2$FILE CALL CONTINUE CALL INIT$READ$DATA$BUFFER ; PREPARE TO READ FROM DISK * * FTP 1: SYNCHRONIZE WITH RECEIVER * CALL PRINT$MESSAGE DB CR,LF,'Sending SYN -- Type Exit Char to Abort',CR,LF,0 SEND$SYN$WAIT: CALL LOS ; CHECK FOR LOSS OF SIGNAL MVI A,SYN ; SEND SYN CALL SENDNM ; NO MONITOR MVI B,1 ; WAIT 1 SEC CALL RECVNM ; NO MONITOR JC SEND$SYN$WAIT CPI ACK ; MUST BE ACK JNZ SEND$SYN$WAIT CALL PRINT$MESSAGE DB '** FTP 1 Starting Transmission **',CR,LF,0 XRA A STA SECTNO ; ZERO SECTOR NUMBER * * FILE TRANSFER PROTOCOL 1 -- SEND * * This section of the TERMINAL Program contains the routines * required to transmit files via Protocol 1. Protocol 1 data transmission * format is described below: * * TRANSMISSION FORMAT: * SOH STX ETX ENQ * (for each Packet) * EOT EOT ... EOT (10 of them for end) * RESPONSE FORMAT: * ESC ACK or ESC CAN or NAK (NAK Assumed) * * In sending a file to an external computer, a number of situations may occur: * 1) TRANSMIT DATA AND RECEIVE AN ACKNOWLEDGE (ESC ACK) * 2) TRANSMIT DATA AND RECEIVE A NEGATIVE ACK (NAK) * 3) TRANSMIT DATA AND RECEIVE A CANCEL (ESC CAN) * 4) TRANSMIT DATA AND RECEIVE NEITHER ESC ACK NOR NAK * 5) TRANSMIT DATA AND RECEIVE NOTHING * SEND1$FILE: CALL PRINT$DOT ; PRINT DOT IF NOT MONITORING XRA A STA ERRCT ; ZERO ERROR COUNT LDA SECTNO ; INCREMENT SECTOR NUMBER INR A STA SECTNO * READ SECTOR AND ABORT IF DONE CALL READ$SECTOR ; LOAD UP TO 16 BLOCKS JZ EOF ; PROCESS EOF CONDITION * SEND OR REPEAT SECTOR REPTB: LXI SP,STACK CALL LOS ; CHECK FOR LOSS OF SIGNAL LHLD NEXT$DATA$BLOCK ; COPY DATA BLOCK TO BUFFER LXI D,BUFF MVI B,128 ; 128 BYTES CALL MOVE * SEND START OF HEADER MVI A,SOH CALL SENDNM * SEND DATA PACKET TYPE MVI A,0 ; DATA PACKET (FUTURE EXPANSION) CALL SENDNM * SEND SECTOR NUMBER AND ITS COMPLEMENT LDA SECTNO ; GET SECTOR NUMBER CALL SENDNM CMA ; COMPLEMENT IT CALL SENDNM * SEND START OF TRANSMISSION MVI A,STX CALL SENDNM * SEND SECTOR DATA XRA A ; A=0 STA CKSUM ; INIT CKSUM LXI H,BUFF SENDC: MOV A,M CALL SEND ; SEND CHAR CALL ADD$TO$CKSUM ; ADD CHAR TO CKSUM CPI LF ; DELAY ON CZ LF$DELAY INR L ; DONE WITH SECTOR? JNZ SENDC * SEND END OF TRANSMISSION MVI A,ETX CALL SENDNM * SEND CKSUM LDA CKSUM ; GET CHECKSUM CALL SENDNM * GET ACK ON SECTOR CALL RECV$ACK ; SEND ENQ AND GET ACK CPI ACK JZ SEND1$FILE RECV1$NAK$FLUSH: MVI B,1 ; FLUSH CHARS UNTIL NOTHING RECEIVED FOR 1 SEC CALL RECVNM JNC RECV1$NAK$FLUSH CALL PRINT$MESSAGE DB CR,LF,'** NAK Received **',CR,LF,0 LDA ERRCT INR A STA ERRCT CALL DISPLAY$ERRCT CPI ERROR$LIMIT JC REPTB ; CONTINUE IF NOT AT ERROR LIMIT CALL PRINT$MESSAGE DB CR,LF,'Error Limit Reached -- Continue (Y/N/=N)? ',0 CALL GET$RESPONSE CPI 'Y' ; YES? JNZ TEXIT XRA A ; ZERO ERROR COUNT STA ERRCT JMP REPTB * * SEND ENQ AND RECEIVE ACK OR NAK ONLY * RECV$ACK: MVI C,ERROR$LIMIT RECV1$ACK: PUSH B MVI A,ENQ ; SEND ENQ CALL SENDNM MVI B,5 ; 5 SEC TIMEOUT CALL RECVNM POP B JNC RECV2$ACK DCR C JNZ RECV1$ACK CALL ERXIT DB 'No Valid Response to ENQ -- Timeout',0 RECV2$ACK: CPI ESC ; IF ACK, MUST BE SEQUENCE OF ESC AND ACK JNZ RECV3$ACK PUSH B MVI B,1 ; 1 SEC TIMEOUT CALL RECVNM POP B JC RECV3$ACK CPI ACK ; ACK? RZ CPI CAN ; CANCEL? JNZ RECV3$ACK CALL PRINT$MESSAGE DB CR,LF,BEL,'** CAN Received -- Aborting **',CR,LF,0 CALL PAUSE JMP TEXIT * INTERPRET RESPONSE AS NAK RECV3$ACK: MVI A,NAK ; INTERPRET NON- AS RET * DELAY AFTER FOR SCROLLING LF$DELAY: PUSH H ! PUSH B MVI B,5 ; 5 MAJOR LOOPS LF$DLY$1: LDA CR$DELAY ; DELAY CONSTANT ORA A ; NO DELAY? JZ LF$DLY$4 MOV C,A ; ... IN C LF$DLY$2: LXI H,CPAUS ; ARBITRARY VALUE LF$DLY$3: DCX H ; MINOR LOOP MOV A,H ; DONE? ORA L JNZ LF$DLY$3 DCR C ; MINOR LOOP 2 JNZ LF$DLY$2 DCR B ; MAJOR LOOP JNZ LF$DLY$1 LF$DLY$4: POP B ! POP H RET * EOF -- END OF FILE PROCESSING EOF: CALL PRINT$MESSAGE DB BEL,CR,LF,'File Sent -- Sending EOT''s',CR,LF,0 MVI C,10 ; SEND 10 EOT'S SEOT: PUSH B ; SAVE BC MVI A,EOT CALL SENDNM POP B ; GET COUNT DCR C JNZ SEOT CALL PAUSE ; DELAY FOR MESSAGE * DONE - CLOSE UP SHOP XFER$CPLT: CALL TEST$BELL ; RING BELL IF DESIRED JMP OPTD * * FILE TRANSFER PROTOCOL 2 -- SEND * * This section of the TERMINAL Program contains the routines required * to transmit files via Protocol 2. This Protocol transmits data as follows: * * CR * * Before the transmission begins, synchronization with the receiver is * accomplished by sending out the sync character followed by a CR. Once * synchronized, each line is transmitted followed by a CR; at this time, the * sender waits for a response -- the sync character instructs it to proceed * and any other character causes a retry of the last line sent. * SEND2$FILE: CALL CONTINUE CALL INIT$READ$DATA$BUFFER ; PREPARE TO READ FROM DISK * DETERMINE IF USER WISHES OPTIONAL TIMEOUT ON CARAT MVI A,0FFH ; TURN ON TIMEOUT STA SEND2$HS$TO CALL PRINT$MESSAGE DB CR,LF,'Do you wish to timeout on Handshaking (Y/N/=N)? ',0 CALL GET$RESPONSE CPI 'Y' JZ SEND2$XL XRA A ; TURN OFF HANDSHAKING T/O STA SEND2$HS$TO * TURN ON TRANSLATION IF DESIRED SEND2$XL: CALL XLATE$ON * SYNCHRONIZE RECEIVER SEND2$SYNC: PUSH B ; SAVE COUNT MVI A,SYNC$CH ; SEND SYNC CHAR CALL SEND2 MVI A,CR CALL SEND2 * CHAR RECEIVED -- START PROCESSING SEND2$READY: CALL FLUSH$RESP ; FLUSH REST OF CHARS JNC SEND2$READY ; FLUSH SYNC CHAR ALSO CALL READ$BLOCK ; READ NEXT SECTOR; HL PTS TO 1ST BYTE * LINE TRANSMISSION LOOP SEND2$LOOP: MOV A,M ; SEND IF 1ST CHAR IS A ANI 7FH ; MASK OUT MSB CPI CR JNZ SEND2$LOOP1 MVI A,' ' ; SEND CALL SEND2 SEND2$LOOP1: CALL LOS ; CHECK FOR LOSS OF SIGNAL CALL SEND2$LINE ; SEND LINE PTED TO BY HL JNZ SEND2$DONE LDA MONIT$FLAG ; MONITORING? ORA A ; CONT IF SO JZ SEND2$LOOP CALL PRINT$DOT JMP SEND2$LOOP SEND2$DONE: CALL FLUSH$RESP ; FLUSH RESPONSES JNC SEND2$DONE ; FLUSH SYNC CHAR ALSO MVI A,SYNC$CH ; SEND ENDING SYNC CHAR CALL SEND2 MVI A,CR CALL SEND2 JMP XFER$CPLT ; TRANSFER COMPLETE * * ROUTINE TO FLUSH RESPONSES * THIS ROUTINE FLUSHES ALL CHARS EXCEPT SYNC; ON RET, CARRY MEANS NO CHAR * AND NO CARRY MEANS SYNC CHAR DETECTED * FLUSH$RESP: MVI B,1 ; 1 SEC DELAY CALL RECV RC ; CONTINUE UNTIL NO CHARS RECEIVED CPI SYNC$CH ; RETURN IF SYNC CHAR JNZ FLUSH$RESP RET * * ROUTINE TO SEND A LINE TO RECEIVER * SEND2$LINE: LXI D,QP ; USE PRINTER QUEUE AS BUFFER * BUILD LINE IN PRINTER QUEUE BUFFER BUILD$LINE: MOV A,M ; GET BYTE ANI 7FH ; MASK OUT MSB CPI CTRLZ ; EOF? JZ SEND2$EOF ; DONE CPI LF ; DON'T STORE JZ BLINE1 STAX D ; STORE BYTE INX D ; PT TO NEXT BLINE1: INX H PUSH PSW ; SAVE CHAR MOV A,H ; NEXT BLOCK? CPI 1 JNZ BLINE2 * LOAD NEXT BLOCK PUSH D ; SAVE QUEUE PTR CALL READ$BLOCK ; HL PTS TO 1ST BYTE POP D ; RESTORE * CHECK FOR BLINE2: POP PSW ; RESTORE CHAR CPI CR ; EOL? JNZ BUILD$LINE INX H ; SKIP OVER MOV A,H ; RELOAD BUFFER? CPI 1 JNZ BLINE$CR CALL READ$BLOCK ; READ NEXT BLOCK; HL PTS TO 1ST BYTE * SEND LINE IN QUEUE BUFFER BLINE$CR: PUSH H ; SAVE PTR TO NEXT BYTE LXI H,QP ; PT TO LINE BLINE$CR1: MOV A,M ; GET BYTE CALL SEND2 INX H ; PT TO NEXT CPI CR ; DONE? JNZ BLINE$CR1 LDA MONIT$FLAG ; MONITORING? ORA A ; 0 IF SO JNZ BLINE$CR1A MVI A,LF ; SEND TO CONSOLE IF MONITORING CALL OUT$CON * WAIT FOR RESPONSE COMMAND BLINE$CR1A: POP H ; RESTORE PTR LDA SEND2$HS$TO ; USE TIMEOUT ROUTINE? ORA A ; 0=NO JNZ BLINE$CR2 BLINE$SYNC: MVI B,30 ; VERY LARGE DELAY (30 SEC) CALL RECV JC BLINE$CR2 CPI SYNC$CH JNZ BLINE$SYNC BLINE$CR2: CALL FLUSH$RESP ; FLUSH REST OF CHARS JNC BLINE$CR2 ; FLUSH SYNC ALSO XRA A ; SET ZERO RET * EOF ENCOUNTERED -- DONE SEND2$EOF: MVI A,0FFH ; DONE ORA A ; SET FLAGS RET * * FILE TRANSFER PROTOCOL 3 - SEND * This routine implements the MODEM2/XMODEM communications * protocol designed by Ward Christensen et al. This protocol and the * MODEM2/XMODEM programs are in the public domain (CP/M User's Group). * * TRANSMISSION FORMAT: * SOH * (for Packet) * EOT (for end) * REPLY FORMAT: * ACK or CAN or NAK (NAK Assumed) * SEND3$FILE$START: CALL CONTINUE CALL INIT$READ$DATA$BUFFER ; PREP FOR DATA READ XRA A ; TURN OFF LF SKIP AND CR DELAY STA LF$SKIP STA CR$DELAY SEND3$FILE: MVI A,0 ; SET SECTOR NUMBER STA SECTNO MVI E,80 ; WAIT 80 SEC FOR INITIAL NAK CALL PRINT$MESSAGE DB CR,LF,'** Waiting for Initial NAK **',CR,LF,0 S3$WAITNAK: MVI B,1 ; 1 SEC DELAY CALL RECVNM ; WAIT FOR CHAR OR ABORT W/NO ECHO JC S3$WAITNAK1 CPI NAK ; NAK? JNZ S3$WAITNAK1 CALL PRINT$MESSAGE DB '** FTP 3 Starting Transmission **',CR,LF,0 JMP S3$SEND S3$WAITNAK1: DCR E ; COUNT DOWN JNZ S3$WAITNAK CALL PRINT$MESSAGE DB '** No Initial NAK in 80 Secs -- Aborting **',CR,LF,0 CALL PAUSE JMP TEXIT * SEND NEXT BLOCK S3$SEND: LXI SP,STACK CALL PRINT$DOT LDA SECTNO ; INCREMENT SECTOR NUMBER INR A STA SECTNO XRA A ; ZERO ERROR COUNT STA ERRCT CALL READ$SECTOR ; LOAD UP TO 16 BLOCKS JZ S3$EOF LHLD NEXT$DATA$BLOCK ; COPY DATA BLOCK TO BUFFER LXI D,BUFF MVI B,128 ; 128 BYTES CALL MOVE * TRANSMIT CURRENT PACKET S3$SEND$LOOP: * CHECK FOR LOSS OF CARRIER CALL LOS * SEND HEADER MVI A,SOH ; INIT CKSUM STA CKSUM MVI A,SOH ; SEND START-OF-HEADER CALL SENDNM LDA SECTNO ; SEND SECTOR NUMBER CALL SENDNM CALL ADD$TO$CKSUM LDA SECTNO CMA ; SEND COMPLEMENT OF SECTOR NUMBER CALL SENDNM CALL ADD$TO$CKSUM LXI H,BUFF ; SEND CURRENT BLOCK S3$SEND1: MOV A,M ; GET BYTE CALL SEND CALL ADD$TO$CKSUM ; ADD TO CHECKSUM INR L ; PT TO NEXT JNZ S3$SEND1 LDA CKSUM ; SEND THE CHECKSUM CALL SENDNM MVI B,10 ; WAIT 10 SECS MAX CALL RECVNM JC S3$SEND1$TO ; TIMEOUT ON ACK CPI ACK ; ACK? JZ S3$SEND ; SEND NEXT BLOCK ANI 7FH ; MASK MSB CPI CAN ; CANCEL? JNZ S3$SEND2 CALL PRINT$MESSAGE DB CR,LF,'** CAN Received -- Aborting **',CR,LF,0 CALL PAUSE JMP OPTD1 ; GOTO TERM S3$SEND1$TO: CALL PRINT$MESSAGE DB CR,LF,'** Timeout on ACK **',CR,LF,0 LDA ERRCT ; INCREMENT ERROR COUNT INR A STA ERRCT CPI ERROR$LIMIT JC S3$SEND$LOOP CALL PRINT$MESSAGE DB 'Error Limit Exceeded -- Aborting',CR,LF,0 CALL PAUSE JMP OPTD1 S3$SEND2: CALL PRINT$MESSAGE DB CR,LF,'** NAK Received **',CR,LF,0 LDA ERRCT ; INCR ERROR COUNT INR A STA ERRCT CPI ERROR$LIMIT ; LIMIT? JC S3$SEND$LOOP CALL PRINT$MESSAGE DB CR,LF,'Can''t send block -- aborting',CR,LF,0 CALL PAUSE JMP OPTD1 * END OF FILE -- DONE S3$EOF: CALL PRINT$MESSAGE DB CR,LF,'** EOF -- Transmitting EOT **',CR,LF,0 S3$EOF1: MVI A,EOT ; SEND END OF TRANSMISSION CALL SENDNM MVI B,1 ; WAIT FOR ACK WITH ABORT CALL RECVNM JC S3$EOF1 JMP TEXIT * *: TERM II, Section 20 * **** RECEIVE FILE MODE **** * * This is the entry point for the file transfer receive * function. It supports Protocols 1, 2, and 3 described above. * * TEXIT ENTRY POINT * INITIAL COMMAND ENTRY POINT * RECV$FILE: CALL SCREEN$CLEAR CALL PRINT$ID CALL SCREEN$MESSAGE DB 4,15,'++ TERM II File Transfer Facility - RECV Function ++' DB CR,LF,LF,0 * PREPARE DISK FILE FOR RECEPTION CALL INIT$FCB ; RESET FCB CALL ERASE$OLD$FILE CALL MAKE$NEW$FILE CALL SET$MONITOR ; SET MONITOR FLAG XRA A ; A=0 STA ERRCT ; INIT ERROR COUNT STA LF$SKIP ; SET SKIP FLAG OFF STA CR$DELAY ; TURN OFF DELAY STA DELAY ; SET MODEM OUTPUT DELAY FLAG CALL SET$RING ; SET COMPLETION BELL CALL INIT$PRINT$DOT ; INIT DOT DISPLAY FOR NO-MONITORING CALL PRINT$MESSAGE DB CR,LF,'Transmission Protocol Selection' DB CR,LF,' 1 -- TERM II Protocol' DB CR,LF,' 2 -- Arbitrary Host Protocol' DB CR,LF,' 3 -- CP/M User''s Group (Christensen) Protocol' DB CR,LF,' Selection (1/2/3/^C/=1)? ',0 CALL GET$RESPONSE CPI CTRLC JZ TEXIT CPI '3' JZ RECV3$FILE$START CPI '2' JZ RECV2$FILE CALL CONTINUE CALL INIT$WRITE$DATA$BUFFER ; PREPARE FOR WRITE TO DISK * * SYNCHRONIZE WITH TRANSMITTER * CALL PRINT$MESSAGE DB CR,LF,'Waiting for SYN from Transmitter -- Type Exit Char ' DB 'to Abort',CR,LF,0 RECV$SYN$WAIT: CALL LOS ; CHECK FOR LOSS OF SIGNAL MVI B,1 ; 1 SEC TIMEOUT CALL RECVNM JC RECV$SYN$WAIT CPI SYN ; GOT IT? JNZ RECV$SYN$WAIT CALL PRINT$MESSAGE DB 'Starting Reception',CR,LF,0 SYN$ACK: MVI A,ACK ; SEND ACK CALL SENDNM MVI A,0 ; SET SECTOR NUMBER STA SECTNO * * FILE TRANSFER PROTOCOL 1 -- RECEIVE * * In receiving a file from an external computer, a number of situations * may occur: * 1) ALL IS OK * 2) NO * 3) INVALID SECTOR NUMBER (NUMBER AND COMPLEMENT DON'T MATCH) * 4) SECTOR NUMBER LESS THAN EXPECTED * 5) SECTOR NUMBER GREATER THAN EXPECTED * 6) NO * 7) NO * 8) INVALID CKSUM * 9) NO * * WAIT FOR AND PICK UP HEADER RECV$HDR: LXI SP,STACK ; RESET STACK MVI B,6 ; 6 SEC TIMEOUT CALL RECVNM JC RECV$HDR$TIMEOUT ; TIMEOUT RECV$HDR1: CPI SYN ; SYN AGAIN? JZ SYN$ACK CPI EOT ; END OF TRANSMISSION? JZ RECV$DONE CPI SOH ; START OF HEADER? JZ RECV$SECTOR * INVALID HEADER RECEIVED ERROR INV$HDR: CALL PRINT$MESSAGE DB CR,LF,'** Invalid Header **',0 * SEND NAK AND GET BLOCK AGAIN SEND$NAK: MVI B,1 ; FLUSH UNTIL NO CHARS CALL RECVNM JNC SEND$NAK CALL PRINT$MESSAGE DB CR,LF,'** Sending NAK **',CR,LF,0 MVI A,NAK ; SEND NAK CALL SENDNM JMP RECV$HDR * SEND CANCEL AND KEEP ON SENDING UNTIL ABORT OR NO FURTHER INFO RECEIVED SEND$CAN: MOV B,A ; SAVE A IN B CALL PRINT$MESSAGE DB CR,LF,'** Sending CAN **',CR,LF,0 MOV A,B ; GET EXPECTED SECTOR NUMBER CALL DECIMAL$OUT ; DECIMAL VALUE MVI A,'/' CALL OUT$CON MOV A,C ; GET RECEIVED SECTOR NUMBER CALL DECIMAL$OUT CALL PRINT$MESSAGE DB ' Expected/Received Sector Number Error',CR,LF,0 SEND$CAN1: MVI B,1 ; 1 SEC TIMEOUT CALL RECVNM JNC SEND$CAN1 MVI A,ESC CALL SENDNM MVI A,CAN CALL SENDNM MVI B,3 ; 3 SEC TIMEOUT CALL RECVNM JNC SEND$CAN CALL PRINT$MESSAGE DB CR,LF,'** CAN Sent **',CR,LF,0 CALL PAUSE JMP TEXIT * RECEIVE A PACKET OF INFORMATION RECV$SECTOR: CALL LOS ; CHECK FOR LOSS OF SIGNAL * RECEIVE PACKET TYPE MVI B,1 ; 1 SEC TIMEOUT CALL RECVNM JC INV$HDR ORA A ; MUST BE TYPE 0 FOR NOW (FUTURE EXPANSION) JNZ PT$ERR * RECEIVE, VERIFY, AND COMPARE SECTOR NUMBER MVI B,1 ; 1 SEC TIMEOUT CALL RECVNM JC INV$HDR MOV C,A ; SAVE SECTOR NUMBER IN C MVI B,1 ; 1 SEC TIMEOUT CALL RECVNM JC INV$HDR ANA C ; OK? ZERO IF SO JNZ RECV$SN$ERR LDA SECTNO ; COMPARE AGAINST SECTOR NUMBER CMP C ; COMPARE ... IF C=OLD #, FLUSH JZ SEND$ACKF INR A ; ADD 1 CMP C ; COMPARE ... IF C>SECT NO, CANCEL JC SEND$CAN ; CANCEL IF INPUT SECT > EXPECTED SECT JNZ SEND$ACKF ; FLUSH SECTOR IF NOT EXPECTED SECT * RECEIVE STX MVI B,1 ; 1 SEC TIMEOUT CALL RECVNM JC INV$HDR CPI STX JNZ INV$HDR CALL PRINT$DOT ; PRINT DOT TO INDICATE GOOD NEW BLOCK * RECEIVE SECTOR DATA XRA A ; A=0 STA CKSUM ; INIT CKSUM LXI H,80H ; RECEIVE BUFFER RECV$CHAR: MVI B,1 ; 1 SEC TIMEOUT CALL RECV ; GET CHAR JC RECV$BODY$TIMEOUT CALL ADD$TO$CKSUM ; ADD CHAR TO CKSUM MOV M,A ; STORE CHAR INR L ; PT TO NEXT JNZ RECV$CHAR MVI B,2 ; 2 SEC TIMEOUT FOR ETX CALL RECVNM JC RECV$BODY$TIMEOUT CPI ETX ; ETX EXPECTED JZ RECV$ETX ; NAK OTHERWISE CALL PRINT$MESSAGE DB CR,LF,'** ETX Not Received **',0 JMP SEND$NAK * ETX RECEIVED RECV$ETX: MVI B,1 ; GET CHECKSUM CALL RECVNM JC RECV$CKSUM$TIMEOUT MOV B,A ; SAVE CHECKSUM IN B PUSH B ; SAVE CKSUM MVI B,1 ; GET ENQ CALL RECVNM POP B ; GET CKSUM JC RECV$ENQ$TIMEOUT CPI ENQ ; MUST BE ENQUIRY JNZ RECV$ENQ$TIMEOUT LDA CKSUM ; GET COMPUTED CKSUM MOV C,A ; STORE IN C MOV A,B ; COMPARE CKSUMS CMP C JNZ SEND$CKSUM$ERR * WRITE SECTOR TO DISK CALL WRITE$TO$FILE ; WRITE BUFF TO DISK JMP SEND$ACK ; GOOD BLOCK -- INCR SECTOR NUMBER AND ACK * INVALID PACKET TYPE ERROR PT$ERR: CALL PRINT$MESSAGE DB CR,LF,'** Invalid Packet Type **',0 JMP SEND$NAK * SEND ACKNOWLEDGE * FLUSH CHARS AND THEN SEND ACK SEND$ACKF: MVI B,1 ; FLUSH CALL RECVNM JNC SEND$ACKF JMP SEND$ACK1 * INCR SECTOR NUMBER AND THEN SEND ACK SEND$ACK: LDA SECTNO ; INCREMENT SECTOR NUMBER INR A STA SECTNO * SEND ACK SEND$ACK1: MVI A,ESC ; SEND CALL SENDNM MVI A,ACK ; ACKNOWLEDGE CALL SENDNM MVI B,6 ; 6 SEC TIMEOUT CALL RECVNM JC RECV$HDR$TIMEOUT CPI ENQ ; ANOTHER ENQUIRY? JZ SEND$ACK1 JMP RECV$HDR1 * DONE W/RECEPTION OF DATA RECV$DONE: CALL CLOSE$OUT$FILE ; WRITE BUFFER TO DISK AND CLOSE FILE * FLUSH EOT'S FROM LINE UNTIL NOTHING IS SENT FOR 1 SEC RECV$DONE$FLUSH: MVI B,1 ; 1 SEC DELAY CALL RECVNM JNC RECV$DONE$FLUSH JMP XFER$CPLT * * FILE TRANSFER PROTOCOL 2 -- RECEIVE * * This section implements the receiver portion of the Protocol 2 * file transfer system. It starts out waiting for the SYNC char, sends * a CR when received, and receives and stores lines on disk until another * SYNC char is received as the 1st char of a new line. , , and * are ignored. * RECV2$FILE: CALL CONTINUE CALL INIT$WRITE$DATA$BUFFER ; PREPARE TO WRITE TO DISK * TURN ON TRANSLATION CALL XLATE$ON * CONTINUE RECEIVING CHARS RECV2$CONT: LXI H,BUFF ; PT TO BUFFER POSITION SHLD BUF$NEXT * RECEIVE LINE FROM EXTERNAL SYSTEM RECV2$LINE: CALL LOS ; CHECK FOR LOSS OF SIGNAL MVI A,SYNC$CH ; SEND SYNC CHAR TO START LINE CALL SEND2 MVI A,CR CALL SEND2 LXI H,QP ; PT TO QUEUE BUFFER RECV2$LINE1: MVI B,20 ; GET 1ST CHAR CALL RECV JC RECV2$DONE ; TIMEOUT -- DONE ANI 7FH ; MASK OUT MSB CPI 7EH ; IGNORE , , AND ABOVE JNC RECV2$LINE1 CPI ' ' ; IGNORE ALL CHARS LESS THAN JC RECV2$LINE1 CPI SYNC$CH ; ABORT IF SYNC CHAR JNZ RECV2$CHAR1 * DONE WITH LOAD RECV2$DONE: LHLD BUF$NEXT ; PT TO NEXT CHAR POSITION MVI M,CTRLZ ; PLACE ^Z CALL CLOSE$FILE ; WRITE LAST BLOCK, WRITE BUFFER, AND CLOSE JMP XFER$CPLT * RECEIVE NEXT CHAR FROM EXTERNAL COMPUTER RECV2$CHAR: MVI B,3 ; 3 SEC DELAY CALL RECV ; GET CHAR JNC RECV2$CHAR1 CALL RING$BELL MVI A,CR ; FORCE * PROCESS NEXT CHAR RECV2$CHAR1: ANI 7FH ; MASK OUT MSB MOV M,A ; STORE CHAR INX H ; PT TO NEXT CPI CR ; DONE? JNZ RECV2$CHAR CALL FLUSH$RESP ; GET RID OF CHARS AFTER JNC RECV2$DONE ; FINISH IF ONE OF THEM WAS A SYNC CHAR LDA MONIT$FLAG ; PRINT PROMPT IF NOT MONITORING ORA A ; SET FLAGS JZ RECV2$CHAR3 * SEND PROMPT AT EOL FOR NON-MONITORING RECV2$CHAR2: CALL PRINT$DOT * STORE ENDING AND STORE LINE RECV2$CHAR3: MVI M,LF ; STORE INX H ; PT TO NEXT * STORE CHAR IN BUFFER AND WRITE TO DISK IF NECESSARY LHLD BUF$NEXT ; GET PTR LXI D,QP ; PT TO LINE RECV2$STORE: LDAX D ; GET CHAR MOV M,A ; PUT CHAR INX H ; PT TO NEXT INX D CPI LF ; DONE? JZ RECV2$SD MOV A,H ; BUFFER FULL? CPI 1 JNZ RECV2$STORE * WRITE BUFFER TO DISK PUSH D ; SAVE PTR TO NEXT CHAR CALL WRITE$TO$FILE ; WRITE BUFF TO DISK POP D ; RESTORE PTR TO NEXT CHAR LXI H,BUFF ; PT TO BEGINNING OF BUFFER JMP RECV2$STORE ; CONTINUE * END OF LINE RECV2$SD: MOV A,H ; BUFFER FULL? CPI 1 JNZ RECV2$SD1 * WRITE BUFFER TO DISK IF ALSO END OF BUFFER CALL WRITE$TO$FILE ; WRITE BUFF TO DISK LXI H,BUFF * SET PTR TO NEXT BYTE OF BUFFER AND CONTINUE RECV2$SD1: SHLD BUF$NEXT ; SET PTR TO NEXT BYTE IN BUFFER JMP RECV2$LINE ; GET NEXT LINE * * FILE TRANSFER PROTOCOL 3 - RECEIVE * This routine implements the MODEM2/XMODEM communications * protocol designed by Ward Christensen et al. This protocol and the * MODEM2/XMODEM programs are in the public domain (CP/M User's Group). * RECV3$FILE$START: CALL CONTINUE CALL INIT$WRITE$DATA$BUFFER ; PREPARE TO WRITE TO DISK MVI A,0 ; SET SECTOR NUMBER STA SECTNO CALL PRINT$MESSAGE DB CR,LF,'** FTP 3 Reception Initialized -- Waiting **',CR,LF,0 MVI A,NAK ; SEND INITIAL NAK CALL SENDNM RECV3$FILE: LXI SP,STACK XRA A ; ZERO ERROR COUNT STA ERRCT R3$FILE: CALL LOS ; CHECK FOR LOSS OF SIGNAL CALL PRINT$DOT MVI B,10 ; 10 SEC TIMEOUT CALL RECVNM JC R3$TOUT CPI SOH ; START OF HEADER? JZ R3$SOH ORA A ; FLUSH NULLS JZ R3$FILE CPI EOT ; END OF TRANSMISSION? JZ R3$EOT CALL PRINT$MESSAGE DB '** Invalid Header **',CR,LF,0 R3$SEND$NAK: CALL PRINT$MESSAGE DB CR,LF,'** Sending NAK **',CR,LF,0 MVI B,1 ; FLUSH CHARS CALL RECVNM JNC R3$SEND$NAK MVI A,NAK ; SEND NAK CALL SENDNM LDA ERRCT ; ABORT IF ERROR LIMIT REACHED INR A STA ERRCT CPI ERROR$LIMIT JC R3$FILE CALL PRINT$MESSAGE DB '** Error Limit Exceeded -- Aborting **',CR,LF,0 CALL PAUSE JMP TEXIT R3$TOUT: CALL PRINT$MESSAGE DB '** FTP 3 Timeout **',CR,LF,0 JMP R3$SEND$NAK * SOH RECEIVED -- START RECEPTION OF BLOCK HEADER R3$SOH: STA CKSUM ; INIT CHECKSUM WITH SOH MVI B,1 ; 1 SEC FOR BLOCK NUMBER CALL RECVNM JC R3$TOUT MOV C,A ; SAVE BLOCK NUMBER IN C CALL ADD$TO$CKSUM MVI B,1 ; GET COMPLEMENT OF BLOCK NUMBER CALL RECVNM JC R3$TOUT PUSH PSW CALL ADD$TO$CKSUM POP PSW CMA CMP C ; SAME? JZ R3$DATA CALL PRINT$MESSAGE DB '** Bad Block Number in Header **',CR,LF,0 JMP R3$TOUT * RECEIVE BLOCK R3$DATA: MOV A,C ; GET SECTOR NUMBER STA RSECTNO ; SAVE IT LXI H,BUFF ; LOAD INTO BUFFER XRA A ; SET CHECKSUM STA CKSUM R3$DATA$LOOP: MVI B,1 ; GET BYTE CALL RECV JC R3$TOUT MOV M,A ; PUT BYTE CALL ADD$TO$CKSUM INR L ; PT TO NEXT JNZ R3$DATA$LOOP * RECEIVE CHECKSUM LDA CKSUM ; GET CHECKSUM MOV C,A ; SAVE IN C MVI B,1 ; 1 SEC TIMEOUT CALL RECVNM JC R3$TOUT CMP C ; CKSUM OK? JZ R3$DATA$OK CALL PRINT$MESSAGE DB '** FTP 3 Checksum Error **',CR,LF,0 JMP R3$SEND$NAK * UPDATE SECTOR NUMBERS R3$DATA$OK: LDA RSECTNO ; GET RECEIVED SECTOR NUMBER MOV B,A LDA SECTNO ; GET EXPECTED SECTOR NUMBER CMP B ; IF EQUAL, PREVIOUS WAS RETRANSMITTED JZ R3$ACK ; PASS TO NEXT IF SO INR A ; CALC NEXT SECTOR NUMBER CMP B ; OK? JZ R3$DATA$OK1 CALL PRINT$MESSAGE DB CR,LF,'** FTP 3 Sync Error -- Aborting **',CR,LF,0 CALL PAUSE JMP TEXIT * OLD BLOCK -- SEND ACK TO XMIT NEXT R3$ACK: MVI A,ACK CALL SENDNM JMP RECV3$FILE * BLOCK WAS OK -- WRITE TO DISK R3$DATA$OK1: CALL WRITE$TO$FILE ; WRITE BUFF TO DISK * UPDATE SECTOR NUMBER LDA SECTNO ; INCREMENT INR A STA SECTNO * SEND ACK AND CONTINUE MVI A,ACK CALL SENDNM JMP RECV3$FILE * END OF TRANSMISSION R3$EOT: CALL PRINT$MESSAGE DB CR,LF,'** FTP 3 End of Transmission **',CR,LF,0 CALL CLOSE$OUT$FILE ; WRITE LAST BLOCK AND CLOSE FILE MVI A,ACK ; SEND ACK CALL SENDNM JMP OPTD1 ; RETURN TO TERMINAL MODE ************************************************************************ * SUPPORT ROUTINES ************************************************************************ * ADD CHAR IN A TO CHECKSUM ADD$TO$CKSUM: PUSH B ; SAVE BC PUSH PSW ; SAVE CHAR CPI LF ; LINE FEED? JNZ ATCKSUM1 LDA LF$SKIP ; SKIP ? ORA A ; 0=NO JNZ ATCKSUM2 ATCKSUM1: LDA CKSUM ; GET COMPUTED CKSUM MOV B,A ; STORE IN B POP PSW ; GET CHAR PUSH PSW ; SAVE CHAR ADD B ; ADD IN CKSUM STA CKSUM ; NEW COMPUTED CKSUM ATCKSUM2: POP PSW ; GET CHAR POP B ; GET BC RET * TURN OFF MONITOR OF I/O AND PRESERVE CURRENT MONITOR STATE MONITOR$SET$OFF: PUSH PSW LDA MONIT$FLAG ; GET CURRENT STATE STA TEMP$MONIT$FLAG ; SAVE IT MVI A,0FFH ; 0FFH=OFF STA MONIT$FLAG POP PSW RET * RESTORE MONITOR STATE FROM SETTING IT TO OFF MONITOR$RESET: PUSH PSW ; SAVE PSW LDA TEMP$MONIT$FLAG ; GET OLD STATE STA MONIT$FLAG POP PSW ; RESTORE PSW RET * INITIALIZE DISPLAY OF DOTS FOR NON-MONITORING FUNCTIONS INIT$PRINT$DOT: PUSH PSW ; AFFECT NO REGS MVI A,60 ; 60 DOTS/LINE STA DOT$COUNT POP PSW RET * PRINT DOTS IF NOT MONITORING SEND/RECEIVE PRINT$DOT: PUSH PSW ; AFFECT NO REGS LDA MONIT$FLAG ; MONITORING? ORA A ; 0=NO JZ PRINT$DOT$DONE MVI A,'.' ; PRINT DOT CALL OUT$CON LDA DOT$COUNT ; COUNT DOWN DCR A STA DOT$COUNT JNZ PRINT$DOT$DONE CALL INIT$PRINT$DOT ; REINITIALIZE COUNT CALL CRLF ; NEW LINE PRINT$DOT$DONE: POP PSW RET * ** RECEIVE ERROR CONDITIONS ** * * RECEIVED SECTOR NUMBER ERROR RECV$SN$ERR: CALL PRINT$MESSAGE DB CR,LF,'** Invalid Sector Number **',0 JMP SEND$NAK * BUFFER OVERFLOW ERROR RECV$OVFL: CALL PRINT$MESSAGE DB CR,LF,'** Receive Buffer Overflow **',0 JMP SEND$NAK * NO CHECKSUM ERROR RECV$CKSUM$TIMEOUT: CALL PRINT$MESSAGE DB CR,LF,'** Checksum not Received Timeout **',0 JMP RECV$HDR$TIMEOUT * ENQUIRY NOT RECEIVED ERROR RECV$ENQ$TIMEOUT: CALL PRINT$MESSAGE DB CR,LF,'** ENQuiry not Received Timeout **',0 JMP RECV$HDR$TIMEOUT * CHECKSUM ERROR SEND$CKSUM$ERR: CALL PRINT$MESSAGE DB CR,LF,'** ',0 MOV A,B ; GET RECEIVED CKSUM CALL HEX$OUT ; PRINT AS HEX MOV A,C ; GET COMPUTED CKSUM CALL HEX$OUT ; PRINT AS HEX CALL PRINT$MESSAGE DB 'Checksum (Received/Computed) Error **',0 JMP SEND$NAK * TIMEOUT DURING RECEPTION OF BODY RECV$BODY$TIMEOUT: CALL PRINT$MESSAGE DB CR,LF,'** Body Incomplete Timeout **',0 JMP SEND$NAK * TIMEOUT/SYNCHRONIZATION ERROR RECV$HDR$TIMEOUT: CALL PRINT$MESSAGE DB CR,LF,'** Timeout/Synchronization Error **',0 JMP SEND$NAK * FILE WRITE ERROR WRITE$ERROR: CALL ERXIT DB '** File Write Error **',0 * ** DISK ROUTINES ** * * * WRITE THE TERM PROGRAM OUT TO DISK * WRITE$TERM$TO$DISK: CALL PRINT$MESSAGE DB CR,LF,'** Writing TERM.COM to Disk **',CR,LF,0 CALL CONTINUE MVI E,0 ; SELECT DISK A: MVI C,SELDISK CALL BDOS LXI D,BUFF ; SET DMA ADDRESS MVI C,STDMA CALL BDOS LXI H,TERM$NAME ; INIT FCB FOR COPY LXI D,TERM$FILE MVI B,33 ; 33 BYTES CALL MOVE LXI D,TERM$FILE ; OPEN FILE PUSH D MVI C,ERASE ; ERASE FILE CALL BDOS POP D MVI C,MAKE ; MAKE FILE CALL BDOS CALL SP$END ; GET END PAGE DCR H ; DECREMENT BY 1 FOR 1ST PAGE MOV B,H ; PAGE COUNT IN B LXI H,PROGRAM$START ; COPY TERM TO DISK WTTD1: PUSH B ; SAVE COUNT CALL TERM$REWRITE ; WRITE 1/2 PAGE CALL TERM$REWRITE ; WRITE 1/2 PAGE POP B ; GET COUNT DCR B ; COUNT DOWN PAGES JNZ WTTD1 LXI D,TERM$FILE ; CLOSE FILE MVI C,CLOSE CALL BDOS RET * * WRITE BUFFER PTED TO BY HL TO DISK * TERM$REWRITE: LXI D,BUFF ; DESTINATION BLOCK TERM$RW1: MOV A,M ; GET BYTE STAX D ; PUT BYTE INX H ; PT TO NEXT INX D MOV A,E ; DONE? ORA A ; 0=YES JNZ TERM$RW1 PUSH H ; SAVE PTR TO NEXT LXI D,TERM$FILE MVI C,WRITE CALL BDOS POP H ; RESTORE PTR ORA A ; ERROR? JNZ WRITE$ERROR RET ** LOAD FILE NAME INTO FCB ** LOAD$FCB: CALL PRINT$MESSAGE DB CR,LF,'File Name (D:FILENAME.TYP)? ',0 CALL C1$FLUSH LXI D,FN$BUFFER ; FILE NAME BUFFER MVI C,CREAD ; READ FROM CONSOLE W/EDITING CALL BDOS CALL CRLF LXI H,FN$BUFFER+1 ; LOAD FCB NOW XRA A ; SET DEFAULT DRIVE STA FCB * CLEAR FCB LXI D,FCB+1 ; PT TO FN ENTRY MVI C,11 ; CLEAR 11 BYTES PUSH D MVI A,' ' ; STORE LFCB0: STAX D ; PUT CHAR INX D ; PT TO NEXT DCR C ; DECR COUNT JNZ LFCB0 CALL ZERO$FCB ; ZERO OUT FIELDS OF FCB * LOAD FCB WITH FILE NAME POP D ; GET PTR TO FN ENTRY IN FCB MOV A,M ; GET CHAR COUNT ORA A ; 0=CLEAR FCB RZ MOV C,M ; GET CHAR COUNT INX H ; CHECK FOR DRIVE SPEC INX H MOV A,M ; IS IT COLON? DCX H ; PT BACK TO CHAR COUNT DCX H CPI ':' ; COLON MEANS WE HAVE A DRIVE SPEC JNZ LFCB1 DCR C ; ADJUST CHAR COUNT DCR C RZ ; ABORT IF JUST DRIVE SPEC INX H ; GET DRIVE LETTER MOV A,M CALL CAPS ; CAPITALIZE INX H ; SKIP OVER COLON SUI 'A'-1 ; INVALID SPEC? JC BADDSPEC JZ BADDSPEC CPI 6 ; A-D JNC BADDSPEC STA FCB ; SET DRIVE SPEC LFCB1: INX H ; PT TO NEXT CHAR MOV A,M ; GET CHAR CPI '.' ; EXT? JZ LFCB2 CPI ' '+1 ; SKIP CHAR IF <= JC LFCB1S CALL CAPS ; CAPITALIZE STAX D ; STORE CHAR INX D ; PT TO NEXT LFCB1S: DCR C ; DECR COUNT RZ JMP LFCB1 * LOAD FCB WITH FILE TYPE LFCB2: INX H ; PT TO TYP DCR C ; DECR COUNT FOR '.' RZ LXI D,FCB+9 ; PT TO EXT IN FCB LFCB3: MOV A,M ; GET CHAR CPI ' '+1 ; SKIP CHAR IF <= JC LFCB3S CALL CAPS ; CAPITALIZE STAX D ; PUT IT INX D ; PT TO NEXT LFCB3S: INX H DCR C ; COUNT DOWN JNZ LFCB3 RET BADDSPEC: CALL PRINT$MESSAGE DB 'ERROR - Invalid Drive Specification',CR,LF,0 JMP LOAD$FCB * CLEAR FCB DATA INIT$FCB: LDA FCB+1 ; CHECK FOR PRESENCE OF FILE NAME CPI ' '+1 JC INIT$FCB$ERR FCB0: PUSH H ! PUSH D ! PUSH B CALL ZERO$FCB ; ZERO OUT FIELDS OF FCB CALL PRINT$FN ; PRINT FILE NAME CALL PRINT$MESSAGE DB ' Verify File Name (Y/N/=N)? ',0 CALL GET$RESPONSE CPI 'Y' JNZ INIT$FCB$OPT POP B ! POP D ! POP H RET INIT$FCB$OPT: CALL PRINT$MESSAGE DB ' Specify Different File (Y/N/=Y)? ',0 CALL GET$RESPONSE CPI 'N' ; ABORT TO TEXIT JZ TEXIT JMP INIT$FCB$ERR1 INIT$FCB$ERR: PUSH H ! PUSH D ! PUSH B CALL PRINT$MESSAGE DB CR,LF,'File Name Not Specified',0 INIT$FCB$ERR1: CALL LOAD$FCB ; GET FILE NAME POP B ! POP D ! POP H JMP FCB0 * ZERO OUT ALL BUT DISK NUMBER, FILE NAME, AND FILE TYPE FIELDS OF FCB ZERO$FCB: PUSH H PUSH B LXI H,FCB+12 ; START AT 12TH POSITION MVI B,24 ; 24 BYTES (INCL RANDOM I/O BYTES) ZERO$FCB$LOOP: MVI M,0 ; STORE ZERO INX H DCR B JNZ ZERO$FCB$LOOP POP B POP H RET * ZERO ALL ALL BUT DISK NUMBER, FILE NAME, AND FILE TYPE FIELDS OF SUBFIL ZERO$SUBFIL: PUSH H PUSH B LXI H,SUBFIL+12 ; START AT 12TH POSITION MVI B,24 ; 24 BYTES JMP ZERO$FCB$LOOP * PRINT FN AND EXT IN FCB ON CON: PRINT$FN: CALL PRINT$MESSAGE DB CR,LF,'File Name: ',0 CALL PR$FN$DISK ; DISK NAME LXI H,FCB+1 ; PT TO FN MVI C,8 ; 8 CHARS CALL PR$FN$LOOP MVI A,'.' ; SEPARATOR CALL OUT$CON MVI C,3 ; 3 CHARS IN EXT CALL PR$FN$LOOP CALL CRLF RET PR$FN$LOOP: MOV A,M ; GET CHAR INX H ; PT TO NEXT CALL OUT$CON DCR C JNZ PR$FN$LOOP RET PR$FN$DISK: LDA FCB ; GET SELECTED DISK ORA A ; DEFAULT? JNZ PR$FN$DISK1 PUSH H PUSH D PUSH B MVI C,RETDISK ; RETURN CURRENT DISK CALL BDOS POP B POP D POP H INR A ; A=A+1 FOR FOLLOWING PRINT PR$FN$DISK1: ADI 'A'-1 ; PRINT LETTER CALL OUT$CON MVI A,':' CALL OUT$CON RET * ERASE DESITINATION FILE IF IT EXISTS ERASE$OLD$FILE: CALL ZERO$FCB * SEE IF FILE EXISTS LXI D,FCB MVI C,SRCHF ;SEE IF IT EXISTS CALL BDOS INR A ;FOUND? RZ ;NO, RETURN * FILE EXISTS -- ASK USER IF HE REALLY WANTS TO DELETE IT CALL PRINT$MESSAGE DB 'File Exists -- Type Y to Erase (Y/N/=N)? ',0 CALL GET$RESPONSE CPI 'Y' JNZ TEXIT ; EXIT OPTIONS IF NOT ERASE * ERASE OLD FILE LXI D,FCB MVI C,ERASE CALL BDOS RET * CREATE THE DESTINATION FILE ON DISK MAKE$NEW$FILE: MVI A,0FFH ; INDICATE FILE OPEN STA FILE$OPEN$FLAG CALL ZERO$FCB LDA FCB ; DETERMINE DEFAULT DRIVE ORA A ; 0=DO IT JNZ MF1 MVI C,RETDISK ; DETERMINE DEFAULT DRIVE NUMBER CALL BDOS INR A ; ADJUST TO 1-16 STA FCB ; SET FCB TO IT MF1: MVI C,RSTDSK ; RESET DISK SYSTEM CALL BDOS LDA FCB ; LOG IN DISK DCR A ; ADJUST TO 0-15 MOV E,A MVI C,SELDISK ; LOG IN DISK CALL BDOS XRA A ; ZERO OUT USER NUMBER STA FCB LXI D,FCB MVI C,MAKE CALL BDOS INR A ;FF=BAD RNZ ;OPEN OK * DIRECTORY FULL - CAN'T MAKE FILE XRA A ; FILE NOT OPEN STA FILE$OPEN$FLAG CALL ERXIT DB 'Error - Can''t Create File -- ' DB 'Directory must be full',0 * FILE READ ROUTINE READ$SECTOR: LDA DATA$BLOCK$COUNT ; ANY BLOCKS LEFT? DCR A STA DATA$BLOCK$COUNT JNZ READ$SECTOR$NEXT LHLD DATA$BUFFER ; RESET PTR IN CASE OF CLOSED FILE LXI D,-80H ; PT TO BEFORE BUFFER DAD D SHLD NEXT$DATA$BLOCK LDA FILE$OPEN$FLAG ; READ NEXT GROUP ONLY IF FILE OPEN ORA A ; 0FFH=FILE OPEN CNZ READ$DATA$BUFFER READ$SECTOR$NEXT: LHLD NEXT$DATA$BLOCK ; PT TO NEXT DATA BLOCK LXI D,80H ; 128 BYTES LATER DAD D SHLD NEXT$DATA$BLOCK LDA DATA$BLOCK$COUNT ; GET BLOCK COUNT ORA A ; NON-ZERO MEANS VALID DATA (NOT EOF) RET * INITIALIZE DATA BUFFER FOR READING INIT$READ$DATA$BUFFER: XRA A ; ZERO DATA BLOCK COUNT STA DATA$BLOCK$COUNT CALL READ$DATA$BUFFER ; READ BUFFER 1ST TIME LDA DATA$BLOCK$COUNT ; INCREMENT BLOCK COUNT FOR 1ST TIME INR A STA DATA$BLOCK$COUNT RET * READ DATA BUFFER (2K) FROM DISK READ$DATA$BUFFER: LHLD DATA$BUFFER ; PT TO BUFFER MVI C,0 ; SET BLOCK COUNT MVI B,16 ; READ UP TO 16 BLOCKS RDB$LOOP: CALL READ$SECTOR$FROM$FILE JNZ RDB$EOF ; EOF REACHED ; GOOD READ INR C ; INCREMENT BLOCK COUNT DCR B ; COUNT DOWN JNZ RDB$LOOP RDB$SETUP: LHLD DATA$BUFFER LXI D,-80H ; 80H BYTES BEFORE DAD D SHLD NEXT$DATA$BLOCK ; SET PTR TO BEFORE BUFFER MOV A,C ; GET BLOCK COUNT STA DATA$BLOCK$COUNT ORA A ; SET FLAGS RET RDB$EOF: LDA FILE$OPEN$FLAG ; CLOSE FILE ONLY IF OPEN ORA A ; 0=NOT OPENED JZ RDB$SETUP PUSH H PUSH D PUSH B CALL COF$DONE ; CLOSE FILE, CLEAR FILE$OPEN$FLAG, PRINT MSG POP B POP D POP H JMP RDB$SETUP * PREPARE TO WRITE TO DATA BUFFER INIT$WRITE$DATA$BUFFER: LHLD DATA$BUFFER ; SET PTRS SHLD NEXT$DATA$BLOCK XRA A ; SET COUNT TO ZERO STA DATA$BLOCK$COUNT RET * READ BLOCK PTED TO BY FCB; FOR SEND2 ONLY READ$BLOCK: CALL READ$SECTOR ; READ NEXT BLOCK FROM DISK JZ SEND2$EOF ; ABORT LHLD NEXT$DATA$BLOCK ; COPY NEXT DATA BLOCK LXI D,BUFF PUSH D MVI B,128 ; 128 BYTES CALL MOVE POP H ; HL PTS TO BUFFER RET * OPEN THE DESTINATION FILE OPEN$FILE: CALL ZERO$FCB MVI A,0FFH ; TURN ON FILE OPEN FLAG STA FILE$OPEN$FLAG LDA FCB ; DETERMINE DEFAULT DRIVE ORA A ; 0=DO IT JNZ OF1 MVI C,RETDISK ; DETERMINE DEFAULT DRIVE NUMBER CALL BDOS INR A ; ADJUST TO 1-16 STA FCB ; SET FCB TO IT OF1: MVI C,RSTDSK ; RESET DISK SYSTEM CALL BDOS LDA FCB ; LOG IN DISK DCR A ; ADJUST TO 0-15 MOV E,A MVI C,SELDISK ; LOG IN DISK CALL BDOS XRA A ; ZERO OUT USER NUMBER STA FCB LXI D,FCB MVI C,OPEN CALL BDOS INR A ;OPEN OK? RNZ ;GOOD OPEN XRA A ; TURN OFF FILE OPEN FLAG STA FILE$OPEN$FLAG CALL ERXIT DB 'Can''t Open File',0 * CLOSE THE DESTINATION FILE CLOSE$FILE: LDA FILE$OPEN$FLAG ; FILE ALREADY OPEN? ORA A ; 0=NO JNZ CLOSE$FILE1 CALL PRINT$MESSAGE DB BEL,CR,LF,'File Not Opened -- Close Aborted',CR,LF,0 CALL PAUSE JMP TEXIT CLOSE$FILE1: CALL WRITE$TO$FILE ; WRITE LAST BLOCK * CLOSE FILE FOR OUTPUT CLOSE$OUT$FILE: LDA DATA$BLOCK$COUNT ; WRITE BUFFER TO DISK ORA A ; NOTHING TO WRITE? JZ COF$DONE MOV B,A ; COUNT IN B LHLD DATA$BUFFER ; PT TO BUFFER COF$LOOP: CALL WRITE$SECTOR$TO$FILE DCR B ; COUNT DOWN JNZ COF$LOOP COF$DONE: LXI D,FCB ; CLOSE FILE MVI C,CLOSE CALL BDOS XRA A ; TURN OFF FILE OPEN FLAG STA FILE$OPEN$FLAG RET * WRITE SECTOR PTED TO BY HL TO BUFF AND THEN WRITE IT TO FILE; * WHEN DONE, HL PTS TO NEXT SECTOR WRITE$HL$TO$FILE: PUSH D PUSH B PUSH H LXI D,BUFF MVI B,128 ; WRITE TO BUFF CALL MOVE CALL WRITE$TO$FILE ; WRITE TO BUFFER POP H ; PT TO NEXT LXI D,80H DAD D POP B POP D RET * WRITE CONTENTS OF BUFFER TO DISK WRITE$TO$FILE: LHLD NEXT$DATA$BLOCK ; WRITE BUFF INTO DATA BLOCK XCHG LXI H,BUFF MVI B,128 ; COPY 128 BYTES CALL MOVE XCHG SHLD NEXT$DATA$BLOCK LDA DATA$BLOCK$COUNT ; INCREMENT COUNT INR A STA DATA$BLOCK$COUNT CPI 16 ; 16 BLOCKS WRITTEN? JC CTRLZ$FILL ; FILL BLOCK WITH ^Z * WRITE DATA BUFFER TO DISK WRITE$DATA$BUFFER: LHLD DATA$BUFFER ; WRITE DATA BUFFER (16 BLOCKS) TO FILE SHLD NEXT$DATA$BLOCK XRA A ; SET DATA BLOCK COUNT TO ZERO STA DATA$BLOCK$COUNT MVI B,16 ; 16 BLOCKS WTF$LOOP: CALL WRITE$SECTOR$TO$FILE DCR B ; COUNT DOWN JNZ WTF$LOOP RET * FILL BLOCK WITH CTRL-Z'S CTRLZ$FILL: LXI D,BUFF ; PT TO BUFFER CTRLZ$FILL$LOOP: MVI A,CTRLZ ; GET CTRLZ STAX D ; STORE IT INR E ; PT TO NEXT JNZ CTRLZ$FILL$LOOP RET * PHYSICALLY WRITE SECTOR PTED TO BY HL TO FILE; ON EXIT, HL PTS TO NEXT WRITE$SECTOR$TO$FILE: PUSH D ! PUSH B ! PUSH H XCHG ; MAKE DE PT TO SECTOR MVI C,STDMA CALL BDOS LXI D,FCB ; WRITE TO DISK MVI C,WRITE CALL BDOS POP H ; GET PTR TO BLOCK LXI D,80H ; PT TO NEXT DAD D POP B ! POP D RET * PHYSICALLY READ SECTOR PTED TO BY HL FROM FILE; ON EXIT, HL PTS TO NEXT READ$SECTOR$FROM$FILE: PUSH D PUSH B PUSH H XCHG ; DE PTS TO DMA ADDRESS MVI C,STDMA ; SET DMA ADDRESS CALL BDOS LXI D,FCB ; READ NEXT SECTOR AND RETURN WITH ZERO RESET IF MVI C,READ ; END OF FILE REACHED CALL BDOS POP H LXI D,80H ; PT TO NEXT DAD D POP B ; GET COUNTS POP D ; GET DMA ADDRESS ORA A ; SET ZERO FLAG IF OK RET * READ SECTOR PTED TO BY HL FROM FILE; ON EXIT, HL PTS TO NEXT READ$HL$FROM$FILE: PUSH D ! PUSH B PUSH H CALL READ$SECTOR ; READ SECTOR (HL PTS TO SECTOR ON RET) JZ READ$HL$EOF POP D ; GET PTR TO DEST MVI B,128 ; 128 BYTES CALL MOVE XCHG ; HL PTS TO NEXT POP B ! POP D RET READ$HL$EOF: CALL PRINT$MESSAGE DB BEL,CR,LF,'ERROR -- EOF Encountered',0 CALL PAUSE JMP TEXIT * * QUESTIONS * ** REMOTE I/O FLAG SETTINGS BY USER ** * * SET (SCROLLING) DELAY TIMER SET$LF$DELAY: * SET TRANSMISSION XRA A ; TURN OFF FLAG STA LF$SKIP CALL PRINT$MESSAGE DB CR,LF,'Transmit ''s (Y/N/=N)? ',0 CALL GET$RESPONSE CPI 'Y' ; TRANSMIT ? JZ SLFD1 MVI A,0FFH ; TURN ON FLAG STA LF$SKIP * SET TIMER SLFD1: XRA A ; NO DELAY STA CR$DELAY CALL PRINT$MESSAGE DB CR,LF,' Delay (0 ... 9, =0)? ',0 CALL GET$RESPONSE SUI '0' ; CONVERT 0-9 ASCII TO BINARY CPI 10 ; OK IF LESS THAN 10 RNC INR A ; ADD 1 STA CR$DELAY RET * SET MODEM DELAY TIMER SET$DELAY: XRA A ; SET NO DELAY STA DELAY CALL PRINT$MESSAGE DB CR,LF,'Transmit Delay (SHORT/LONG/=SHORT)? ',0 CALL GET$RESPONSE CPI 'L' ; KEY ON LONG RNZ MVI A,20H ; SET LONG DELAY STA DELAY RET * SET FILE SEND/RECEIVE MONITOR FLAG SET$MONITOR: XRA A ; A=0 STA MONIT$FLAG ; TURN ON MONITOR BY DEFAULT STA TEMP$MONIT$FLAG CALL PRINT$MESSAGE DB CR,LF,'Monitor Information Transfer (Y/N/=Y)? ',0 CALL GET$RESPONSE CPI 'N' ; NO? RNZ STA MONIT$FLAG ; SET FLAG STA TEMP$MONIT$FLAG RET * SET COMPLETION BELL FLAG SET$RING: XRA A ; TURN OFF BELL STA COMPL$BELL CALL PRINT$MESSAGE DB CR,LF,'Ring Bell When Done (Y/N/=Y)? ',0 CALL GET$RESPONSE CPI 'N' ; NO? RZ STA COMPL$BELL ; SET BELL RET * *: TERM II, Section 21 * * VIEW Mode * VIEW$MODE$ENTRY: XRA A STA VECHOFL ; TURN OFF ECHO STA VNULL ; TURN OFF DISPLAY STA VDEL ; TURN OFF DISPLAY CALL SCREEN$CLEAR CALL SCREEN$MESSAGE DB 2,1,0 ; POSITION CURSOR CALL PRINT$ID CALL SCREEN$MESSAGE DB 5,9,'++ View Mode -- Complete I/O Display Mode ++',0 CALL SCREEN$MESSAGE DB 7,1,'Set View Mode Flags (Y/N/^C/=N)? ',0 CALL GET$RESPONSE CPI CTRLC JZ TEXIT ; GOTO TEXIT IF ABORT CPI 'Y' JNZ VIEW$MODE3 ; JUMP INTO VIEW MODE CALL PRINT$MESSAGE DB 'Enable Echo (Y/N/=N)? ',0 CALL GET$RESPONSE CPI 'Y' JNZ VIEW$MODE1 STA VECHOFL ; SET ECHO FLAG VIEW$MODE1: CALL PRINT$MESSAGE DB 'View Characters (Y/N/=N)? ',0 CALL GET$RESPONSE CPI 'Y' JNZ VIEW$MODE2 STA VNULL VIEW$MODE2: CALL PRINT$MESSAGE DB 'View Characters (Y/N/=N)? ',0 CALL GET$RESPONSE CPI 'Y' JNZ VIEW$MODE3 STA VDEL VIEW$MODE3: CALL SCREEN$CLEAR CALL SCREEN$MESSAGE DB 4,9,'** VIEW Mode **',CR,LF,0 CALL PRINT$MESSAGE DB CR,LF,' Status --' DB CR,LF,' View Echo: ',0 LDA VECHOFL ORA A ; 0=OFF CZ PS$OFF CNZ PS$ON CALL PRINT$MESSAGE DB CR,LF,' View Display: ',0 LDA VNULL ORA A ; 0=OFF CZ PS$OFF CNZ PS$ON CALL PRINT$MESSAGE DB CR,LF,' View Display: ',0 LDA VDEL ORA A ; 0=OFF CZ PS$OFF CNZ PS$ON MVI A,LF ; SET CURSOR AND HEADING CALL VIEW$OUT ** MAIN VIEW LOOP ** VIEW$MODE: CALL LOS ; CHECK FOR LOSS OF SIGNAL CALL ST$CON ; CHECK CONSOLE STATUS JNZ VIEWC ; GET CONSOLE CHAR IF READY CALL ST$MOD ; CHECK MODEM STATUS JZ VIEW$MODE ; CONTINUE VIEW MODE OR FALL THRU TO MODEM ** MODEM INPUT/CONSOLE OUTPUT SECTION ** CALL IN$MODX ; GET MODEM CHAR CALL VIEW$OUT ; PRINT JMP VIEW$MODE ** CONSOLE INPUT/MODEM OUTPUT SECTION ** VIEWC: CALL IN$CON1 ; GET CONSOLE CHAR AND ECHO IF SET MOV C,A ; SAVE CHAR IN C CALL EXIT$CHECK ; CHECK FOR EXIT CHAR JZ TEXITXX ; TEXIT WITH LEADING DELAY LDA VECHOFL ; ECHO? ORA A ; 0=NO MOV A,C ; RESTORE CHAR IN PREP FOR ECHO CNZ VIEW$OUT ; ECHO MOV A,C ; GET CHAR CALL OUT$MOD ; SEND TO MODEM JMP VIEW$MODE ** VIEW MODE OUTPUT ROUTINE ** VIEW$OUT: PUSH PSW ; SAVE CHAR FOR LATER PROCESSING ANI 80H ; CHECK FOR MSB SET JZ VO$NO$MSB MVI A,27H ; OUTPUT SINGLE QUOTE TO INDICATE MSB SET CALL OUT$CON VO$NO$MSB: POP PSW ; GET CHAR ANI 7FH ; MASK OUT MSB JZ VO$NULL ; CHECK FOR NULL OPTION CPI CR ; OUTPUT NORMALLY JZ OUT$CON CPI BS ; OUTPUT NORMALLY JZ OUT$CON CPI LF ; OUTPUT WITH HEADER JZ VO$MSG CPI 7FH ; CHECK FOR DELETE OPTION JZ VO$DEL JMP PRINT$CONTROL$CHAR * NULL OPTION CHECK VO$NULL: LDA VNULL ; 0=NO ORA A ; SET FLAGS RZ XRA A ; GET ZERO JMP PRINT$CONTROL$CHAR * DELETE OPTION CHECK VO$DEL: LDA VDEL ; 0=NO ORA A ; SET FLAGS RZ MVI A,7FH ; SET BYTE JMP PRINT$CONTROL$CHAR * SPECIAL PROCESSING BY VIEW$OUT VO$MSG: CALL OUT$CON ; ECHO CALL SCREEN$MESSAGE DB 1,40,'++ View Mode, Type ',0 ; ON LINE 2 FOR FOLLOWING CALL PRINT$EXIT$CHAR CALL PRINT$MESSAGE DB ' to Exit ++',0 CALL SCREEN$MESSAGE DB 24,1,0 ; POSITION CURSOR RET * *: TERM II, Section 22 * **** PRINT, SCREEN DISPLAY, AND INPUT/OUTPUT UTILITIES **** * * * CLEAR CRT SCREEN IF ROUTINE AVAILABLE * SCREEN$CLEAR: CALL STAT$CRT ; CHECK CRT STATUS RZ JMP CRT$CLR ; CLEAR IT * * PRINT AND POSITION MESSAGE ENDING IN 0 PTED TO BY RET ADR; FIRST 2 BYTES * ARE ROW AND COL OF WHERE MESSAGE IS TO BE PLACED * SCREEN$MESSAGE: XTHL ; HL PTS TO MESSAGE MOV D,M ; GET ROW VALUE INX H MOV E,M ; GET COL VALUE INX H ; PT TO MSG CALL STAT$CRT ; CURSOR ADDRESSING AVAILABLE? JZ SCREEN$MSG0 XCHG ; HL CONTAINS LOC, DE PTS TO MSG CALL GOTOXY ; POSITION ON SCREEN XCHG ; HL PTS TO MSG JMP SCREEN$MSG1 SCREEN$MSG0: CALL CRLF ; NEW LINE INSTEAD OF CURSOR POSITIONING SCREEN$MSG1: XTHL ; RESTORE STACK VALUE AND HL JMP PRINT$MESSAGE ; PROCESS TEXT OF MESSAGE * PRINT NAME OF PROGRAM AND VERSION NUMBER PRINT$ID: CALL PRINT$MESSAGE DB 'TERM II Communications Subsystem, Version ' DB VERS/10+'0','.',(VERS MOD 10)+'0' DB CR,LF,0 RET * PRINT THRU CP/M (STANDARD STRING PRINT) CPM$PRINT: PUSH B ! PUSH D ! PUSH H MVI C,PRINT ; PRINT FUNCTION CALL BDOS POP H ! POP D ! POP B RET * PRINT MESSAGE ENDING IN 0 PTED TO BY RET ADR PRINT$MESSAGE: XTHL ; GET ADDRESS OF STRING IN HL PUSH B MVI C,1 ; SET TAB COUNTER PR0 S3$EOF1: MVI A,EOT ; SEND END OF TRANSMISSION CALL SENDNM MVI B,1 ; WAIT FOR ACK WITH ABORT CALL RECVNM JC S3$EOF1 JZ PR$MESS$CR CPI LF ; JUST MOVE DOWN ONE LINE JZ PR$MESS$LF CPI TAB ; TAB? JZ PR$MESS$TAB INR C ; INCREMENT TAB COUNTER CALL OUT$CON ; PRINT CHAR JMP PR$MESS$LOOP PR$MESS$DONE: POP B XTHL ; RET ADR ON STACK RET PR$MESS$CR: MVI C,1 ; RESET TAB COUNT PR$MESS$LF: CALL OUT$CON ; OUTPUT CHAR JMP PR$MESS$LOOP PR$MESS$TAB: MVI A,' ' ; PRINT CALL OUT$CON INR C ; INCREMENT COUNT MOV A,C ; DONE? ANI 7 JNZ PR$MESS$TAB JMP PR$MESS$LOOP * PRINT EXIT CHAR AS SPECIAL CHAR PRINT$EXIT$CHAR: LDA EXIT$CHAR ; GET CHAR * PRINT CONTROL CHAR (FALL THRU TO FROM PRINT$EXIT$CHAR) PRINT$CONTROL$CHAR: ANI 7FH ; MASK MSB ORA A ; JZ PCC$NULL CPI CR ; JZ PCC$CR CPI LF ; JZ PCC$LF CPI BS ; JZ PCC$BS CPI TAB ; JZ PCC$TAB CPI ESC ; JZ PCC$ESC CPI DL-1 ; TILDE JZ PCC$TIL CPI DL ; JZ PCC$DEL CPI ' ' ; CHECK FOR < JC PCC CALL OUT$CON ; PRINT CHAR RET PCC: PUSH PSW ; SAVE CHAR MVI A,'^' ; PRINT ^ CALL OUT$CON POP PSW ; GET CHAR ADI '@' ; CONVERT TO CHAR CALL OUT$CON RET PCC$NULL: CALL PRINT$MESSAGE DB '',0 RET PCC$CR: CALL PRINT$MESSAGE DB '',0 RET PCC$LF: CALL PRINT$MESSAGE DB '',0 RET PCC$BS: CALL PRINT$MESSAGE DB '',0 RET PCC$TAB: CALL PRINT$MESSAGE DB '',0 RET PCC$ESC: CALL PRINT$MESSAGE DB '',0 RET PCC$TIL: CALL PRINT$MESSAGE DB '',0 RET PCC$DEL: CALL PRINT$MESSAGE DB '',0 RET * MOVE (HL) TO (DE) FOR B BYTES MOVE: MOV A,M ; GET BYTE STAX D ; PUT BYTE INX H ; PT TO NEXT INX D DCR B ; COUNT DOWN JNZ MOVE RET * CONTINUE MESSAGE CONTINUE: PUSH PSW ; SAVE PSW CONT: CALL PRINT$MESSAGE DB CR,LF,'Do you wish to Continue with this Procedure?' DB CR,LF,' (Y/N/?/=?)? ',0 CALL GET$RESPONSE CPI 'Y' ; YES? JZ CONT1 CPI 'N' ; NO? JZ TEXIT CALL PRINT$MESSAGE DB CR,LF,' The procedure about to be performed is critical,' DB CR,LF,'and you are given the option to abort to Command Mode' DB ' or',CR,LF,' continue with the procedure. Type y or Y to' DB ' continue',CR,LF,'or n or N to abort.',CR,LF,0 JMP CONT CONT1: POP PSW ; RESTORE PSW RET * CRLF ROUTINE CRLF: MVI A,CR CALL OUT$CON MVI A,LF * ** POLLING INPUT/OUTPUT ROUTINES ** * * SEND CHAR TO CON: OUT$CON: PUSH H ! PUSH D ! PUSH B PUSH PSW ; SAVE CHAR MOV C,A ; CHAR IN C LXI H,RET0 ; SET UP RET ADR PUSH H LHLD OUTADR ; ADR OF OUTPUT ROUTINE PCHL RET0: POP PSW ; RESTORE CHAR RET1: POP B ! POP D ! POP H ORA A ; SET FLAGS RET * INPUT CHAR FROM CON: IN$CON1: PUSH H ! PUSH D ! PUSH B LXI H,RET2 ; SET UP RET ADR PUSH H LHLD INADR ; ADR OF INPUT ROUTINE PCHL RET2: ANI 7FH ; MASK JMP RET1 IN$CON: CALL IN$CON1 ; GET CHAR CALL EXIT$CHECK ; DON'T TRANSLATE EXIT CHAR RZ CALL BREAK$CHECK ; DON'T TRANSLATE BREAK CHAR RZ CALL XLATE$OUT ; TRANSLATE CONSOLE OUTPUT RET * GET CON: INPUT STATUS ST$CON: PUSH H ! PUSH D ! PUSH B LXI H,RET1 ; SET UP RET ADR PUSH H LHLD STADR ; ADR OF STATUS ROUTINE PCHL * GET CHAR FROM CON: AND ECHO IF NECESSARY CON$CHAR: PUSH B ! PUSH D ! PUSH H CALL IN$CON ; GET CHAR FROM CONSOLE MOV C,A ; SAVE CHAR CALL EXIT$CHECK JZ CON$C1 CALL BREAK$CHECK JZ CON$C1 LDA ECHOFL ; ECHO ENABLED? ORA A ; ZERO MEANS NO JZ CON$C1 PUSH B ; SAVE CHAR MOV A,C ; GET CHAR CALL XLATE$IN ; PERFORM OUTPUT TRANSLATION CALL OUT$CON ; PRINT CHAR CALL BFLOAD ; LOAD BUFFER IF FLAG SET POP B ; RESTORE CHAR CON$C1: MOV A,C ; GET CHAR POP H ! POP D ! POP B RET * FLUSH CHARACTER FROM INPUT BUFFER (IF CHAR PRESENT) C1$FLUSH: CALL ST$CON ; CHAR PRESENT? RZ CALL IN$CON1 ; GET IT IF THERE RET * CHECK FOR EXIT CHAR AND SET ZERO FLAG IF SO EXIT$CHECK: PUSH PSW ! PUSH B ANI 7FH ; MASK OUT MSB MOV B,A LDA EXIT$CHAR ; GET EXIT CHAR CMP B ; COMPARE AGAINST MASKED CHAR IN A JZ EXIT$CHECK$MATCH POP B ! POP PSW ORA A ; SET FLAG TO NON-ZERO RNZ MVI A,1 ; CHAR WAS 0 (); SET FLAG TO NON-ZERO ORA A ; SET FLAG MVI A,0 ; SET CHAR RET EXIT$CHECK$MATCH: POP B ! POP PSW ; IT IS THE EXIT CHAR XRA A ; SET ZERO FLAG LDA EXIT$CHAR ; GET EXIT CHAR RET * CHECK FOR BREAK CHAR AND SET ZERO FLAG IF SO BREAK$CHECK: PUSH PSW ! PUSH B ANI 7FH ; MASK MSB MOV B,A LDA BREAK$CHAR ; GET BREAK CHAR ORA A ; NOT DEFINED JZ BREAK$CHECK$FAIL CMP B ; COMPARE JZ BREAK$CHECK$MATCH BREAK$CHECK$FAIL: POP B ! POP PSW ORA A ; SET FLAG TO NON-ZERO RET BREAK$CHECK$MATCH: POP B ! POP PSW XRA A ; SET ZERO FLAG LDA BREAK$CHAR RET * SEND CHAR TO MODEM OUT$MOD: PUSH PSW ; SAVE CHAR LDA M2$ON ; GET MODEM NUMBER (0=1,0FFH=2) ORA A ; SET FLAGS JZ OUT1$MOD POP PSW ; SEND TO MODEM 2 JMP OUT$MOD2 OUT1$MOD: POP PSW ; SEND TO MODEM 1 JMP OUT$MOD1 * GET CHAR FROM MODEM WITH MASKING AND TRANSLATION IN$MOD: CALL IN$MODX ; GET INPUT WITHOUT TRANSLATION CALL MASK$IN ; MASK INPUT MSB CALL XLATE$IN ; TRANSLATE INPUT RET * GET CHAR FROM MODEM IN$MODX: LDA M2$ON ; GET MODEM NUMBER (0=1,0FFH=2) ORA A ; SET FLAGS JZ IN$MODX1 CALL IN$MOD2 ; GET INPUT FROM MODEM 2 RET IN$MODX1: CALL IN$MOD1 ; GET INPUT FROM MODEM 1 RET * GET MODEM INPUT STATUS ST$MOD: LDA M2$ON ; GET MODEM NUMBER (0=1,0FFH=2) ORA A ; SET FLAGS JZ ST$MOD1 JMP ST$MOD2 * ABORT -- CHECK FOR EXIT$CHAR AND ABORT IF TYPED ABORT: CALL ST$CON ; CHAR TYPED? RZ ; ZERO=NO CHAR CALL IN$CON ; GET CHAR CALL EXIT$CHECK ; CHECK EXIT CHAR JZ TEXIT RET * DETERMINE IF INPUT IS TO BE MASKED AND DO SO IF SET MASK$IN: PUSH B ; SAVE BC MOV B,A ; BYTE IN B LDA MASK$IN$FLAG ; GET FLAG ORA A ; 0=NO JZ MASK$IN$NO MOV A,B ; GET BYTE ANI 7FH ; MASK MSB POP B ; RESTORE BC RET MASK$IN$NO: MOV A,B ; GET BYTE POP B ; RESTORE BC RET * DETERMINE IF OUTPUT IS TO BE MASKED AND DO SO IF SET MASK$OUT: PUSH B ; SAVE BC MOV B,A ; BYTE IN B LDA MASK$OUT$FLAG ; GET FLAG ORA A ; 0=NO JZ MASK$OUT$NO LDA OUTPUT$MASK ; GET MASK FOR MSB ANA B ; AND WITH CHAR POP B ; RESTORE BC RET MASK$OUT$NO: MOV A,B ; GET BYTE POP B ; RESTORE BC RET * DETERMINE IF INPUT IS TO BE TRANSLATED AND DO SO IF SET XLATE$IN: PUSH H ! PUSH B MOV C,A ; SAVE BYTE LDA XLATE$IN$ACTIVE ; GET FLAG ORA A ; 0=NO JZ NO$XLATE LXI H,XLATE$IN$TABLE PERFORM$XLATE: MVI B,0 ; SET HIGH-ORDER TO 0 FOR INDEX DAD B ; OFFSET INTO TABLE BY CHAR VALUE MOV A,M ; GET TRANSLATE BYTE POP B ! POP H RET NO$XLATE: MOV A,C ; GET BYTE POP B ! POP H RET * DETERMINE IF OUTPUT IS TO BE TRANSLATED AND DO SO IF SET XLATE$OUT: PUSH H ! PUSH B MOV C,A ; SAVE BYTE LDA XLATE$OUT$ACTIVE ; GET FLAG ORA A ; 0=NO JZ NO$XLATE LXI H,XLATE$OUT$TABLE JMP PERFORM$XLATE * ACTIVATE TRANSLATOR XLATE$ON: LDA XLATE$IN$FLAG ; GET USER'S SELECTION STA XLATE$IN$ACTIVE ; SET IT LDA XLATE$OUT$FLAG ; SAME STA XLATE$OUT$ACTIVE RET * DEACTIVATE TRANSLATOR XLATE$OFF: XRA A ; TURN OFF ACTIVE FLAGS STA XLATE$IN$ACTIVE STA XLATE$OUT$ACTIVE RET * CONVERT A TO HEX AND PRINT HEX$OUT: PUSH PSW ; SAVE RLC ; EXCHANGE NYBBLES RLC RLC RLC CALL NYBBLE$OUT ; PRINT HIGH NYBBLE POP PSW ; GET VALUE AND PRINT LOW NYBBLE CALL NYBBLE$OUT MVI A,' ' ; PRINT TRAILING JMP OUT$CON * PRINT LOW-ORDER NYBBLE OF A AS HEX DIGIT NYBBLE$OUT: ANI 0FH ; MASK FOR LOW NYBBLE CPI 10 ; CHAR/DIGIT BOUNDARY JC DEC$OUT ADI 'A'-10 ; CONVERT CHAR TO ASCII JMP OUT$CON ; PRINT IT DEC$OUT: ADI '0' ; CONVERT DIGIT TO ASCII JMP OUT$CON * CONVERT A TO ASCII DECIMAL CHARS AND OUTPUT DECIMAL$OUT: PUSH B PUSH PSW ; SAVE A XRA A ; TURN ON LEADING SPACE FLAG STA LDSP POP PSW ; GET A MVI B,100 ; PRINT 100'S CALL DECIMAL$DIGIT MVI B,10 ; PRINT 10'S CALL DECIMAL$DIGIT CALL DEC$OUT ; PRINT 1'S POP B RET DECIMAL$DIGIT: MVI C,0 ; SET DIGIT DDIG$LOOP: SUB B ; A=A-B JC DDIG$DONE INR C ; ADD 1 TO COUNT JMP DDIG$LOOP DDIG$DONE: ADD B ; A=A+B FOR ADJUSTMENT PUSH PSW ; SAVE A MOV A,C ; GET DIGIT ORA A ; ZERO? JNZ DDIG$NUM LDA LDSP ; LEADING ? ORA A ; 0=YES MOV A,C ; PREP FOR PRINT JNZ DDIG$NUM MVI A,' ' ; PRINT LEADING CALL OUT$CON POP PSW ; GET REST OF NUMBER RET DDIG$NUM: CALL DEC$OUT ; PRINT DIGIT MVI A,0FFH ; TURN OFF FLAG STA LDSP POP PSW RET * * GET RESPONSE FROM CONSOLE, CAPITALIZE, ECHO, SEND * GET$RESPONSE: CALL IN$CON1 ; GET CHAR CALL CAPS ; CAPITALIZE CALL OUT$CON ; ECHO PUSH PSW ; SAVE CHAR CALL CRLF POP PSW ; GET CHAR RET * ** MODEM RECV ROUTINE ** * RECVNM - RECEIVE WITHOUT MONITORING * RECV - RECEIVE WITH MONITORING IF FLAG IS SET * RECVNM: CALL MONITOR$SET$OFF ; TURN OFF MONITORING CALL RECV CALL MONITOR$RESET RET RECV: PUSH D ; SAVE PUSH B MSEC: LDA PROCESSOR$SPEED ; ADJUST FOR PROCESSOR SPEED MOV C,A MSEC1: LXI D,RECV$TIMEOUT$VALUE ; 1 SEC DCR COUNT MWTI: CALL ST$MOD ; GET MODEM RECEIVE STATUS JNZ MCHAR ; GOT CHAR DCR E ; COUNT DOWN JNZ MWTI ; FOR TIMEOUT DCR D JNZ MWTI DCR C ; ADJUST FOR PROCESSOR SPEED JNZ MSEC1 CALL ABORT ; ABORT? DCR B ; DCR # OF SECONDS JNZ MSEC * MODEM TIMED OUT RECEIVING POP B POP D ; RESTORE BC, DE STC ; CARRY SHOWS TIMEOUT RET * ** GOT MODEM CHAR * MCHAR: CALL IN$MODX ; GET CHAR FROM MODEM PUSH PSW ; SAVE CHAR * CHECK FOR MONITOR LDA MONIT$FLAG ORA A ; 0 MEANS YES JNZ MCHAR1 POP PSW ; GET CHAR PUSH PSW ; SAVE CHAR AGAIN CPI TILDA ; DON'T PRINT TILDA OR ABOVE JNC MCHAR1 CPI TAB ; DON'T PRINT TAB JZ MCHAR1 CALL OUT$CON ; PRINT CHAR ON CON: * CHECK FOR ABORT MCHAR1: CALL ABORT ; ABORT? POP PSW ; GET CHAR POP B POP D ; RESTORE BC,DE * TURN OFF CARRY TO SHOW NO TIMEOUT ORA A RET * ** MODEM SEND CHAR ROUTINE ** * SENDNM - SEND WITH NO MONITORING * SEND - SEND WITH MONITORING IF FLAG IS SET * SENDNM: PUSH PSW CALL MONITOR$SET$OFF ; TURN OFF MONITORING CALL SEND CALL MONITOR$RESET ; RESTORE MONITORING POP PSW RET SEND: PUSH PSW LDA MONIT$FLAG ; CHECK FOR MONITOR ORA A ; SET FLAGS JNZ SEND1 ; 0 MEANS YES POP PSW ; GET CHAR PUSH PSW ; SAVE CHAR AGAIN CALL OUT$CON ; PRINT CHAR ON CON: SEND1: CALL ABORT ; ABORT? * PAUSE BRIEFLY PUSH H PUSH B LDA DELAY ; GET DELAY VALUE ORA A ; NO DELAY IF ZERO JZ SEND10$SKIP MOV C,A ; ... IN C INR C ; AT LEAST 1 SEND10$DL: LXI H,CPAUS ; ARBITRARY DELAY SEND10: DCX H ; COUNT DOWN MOV A,H ORA L JNZ SEND10 DCR C ; DELAY VALUE JNZ SEND10$DL SEND10$SKIP: POP B POP H * CHECK FOR SKIPPING OF 'S LDA LF$SKIP ; SKIP 'S? ORA A ; 0=NO JZ SEND11 POP PSW ; GET CHAR CPI LF ; LINE FEED? RZ ; DON'T TRANSMIT PUSH PSW ; SAVE CHAR * SEND CHAR TO MODEM SEND11: POP PSW ; GET CHAR PUSH PSW ; SAVE CHAR CALL OUT$MOD POP PSW ; RESTORE CHAR RET * * SEND ROUTINE FOR SEND2 FUNCTIONS * SEND2: ANI 7FH ; MASK OUT MSB PUSH PSW ; SAVE CHAR LDA MONIT$FLAG ; CHECK FOR MONITOR ORA A ; SET FLAGS JNZ SEND21 POP PSW ; SEND CHAR TO CON: PUSH PSW CALL OUT$CON SEND21: CALL ABORT ; ABORT? * ALWAYS SKIP POP PSW ; GET CHAR CPI LF RZ * SEND CHAR AND TRAP ECHO PUSH PSW ; SAVE CHAR CALL OUT$MOD LDA ECHOFL ; CHECK FOR ECHO ORA A ; 0=NO LOCAL ECHO JNZ SEND24 * ENTRY POINT FOR RECEIVING AN ECHOED CHAR POP PSW ; GET CHAR PUSH PSW ; SAVE AGAIN PUSH B ; SAVE BC CALL CAPS ; CAPITALIZE MOV B,A ; CHAR IN B * ECHO LOOP SEND22: CALL ST$MOD ; GET MODEM RECEIVE STATUS JNZ SEND23 ; GET CHAR CALL ABORT ; ABORT? JMP SEND22 * GET CHAR AND CHECK FOR PROPER ECHO SEND23: CALL IN$MOD ; GET CHAR CALL CAPS ; CAPITALIZE CMP B ; SAME AS THAT SENT? JNZ SEND22 ; LOOK AGAIN POP B ; RESTORE BC POP PSW ; GET CHAR RET SEND24: POP PSW ; GET CHAR RET * *: TERM II, Section 23 * **** MISCELLANEOUS UTILITIES **** * * * PROTECTED CP/M ENTRY ROUTINE * BDOS: PUSH H ! PUSH D ! PUSH B CALL CPMBDOS ; ENTER CP/M POP B ! POP D ! POP H RET * * CAPITALIZATION ROUTINE * CAPS: CPI 61H ; SMALL A? RC CPI 7BH ; SMALL Z + 1? RNC SUI ' ' ; CAPITALIZE RET * * GOTOHL -- CONVERSATION MODE CURSOR POSITIONING * GOTOHL: PUSH PSW ; SAVE CHAR MOV A,L ; CHECK COL NUM CPI LINE$LEN-4 JZ GOTOR ; RESET LINE GOTOC: CALL GOTOXY ; POSITION CURSOR INX H ; PT TO NEXT CHAR POS POP PSW ; GET CHAR RET GOTOR: CALL CLEAR ; CLEAR CURRENT LINE POP PSW ; GET CHAR RET * * CLEAR -- CLEAR LINE IN ROW PTED TO BY H * CLEAR: MVI L,3 ; 3RD COL CALL GOTOXY ; POSITION CURSOR MVI B,LINE$LEN-4 MVI A,' ' ; CLEAR LINE CLEARL: CALL OUT$CON ; PRINT DCR B JNZ CLEARL CALL GOTOXY ; POSITION CURSOR RET * * DISPLAY ERROR COUNT IN A REG * DISPLAY$ERRCT: PUSH PSW ! PUSH B ! PUSH D MOV B,A ; COUNT IN B MVI A,ERROR$LIMIT SUB B ; TRIES REMAINING ADI '0' ; ASCII BIAS CALL OUT$CON CALL PRINT$MESSAGE DB ' Tries Remaining',CR,LF,0 POP D ! POP B ! POP PSW RET * *: TERM II, Section 24 * **** COMMAND TABLES **** * * * INITIAL COMMAND TABLE (OPTION ON COMMAND LINE) * ICTAB: DB 'C' ; CONVERSATION MODE DW TCONV DB 'E' ; ECHO DW ECHO$OPTION DB 'R' ; RECEIVE FILE DW RECV$FILE DB 'S' ; SEND FILE DW SEND$FILE DB 'T' ; TELEPHONE SUBSYSTEM DW TPHONE DB 'X' ; EXIT MODE DW INITIAL$TEXIT DB 0 * * COMMAND TABLE * CTAB: DB CTRLE ; SET EXIT CHAR; THIS MUST BE 1ST ENTRY IN CTAB DW TROUT DB CTRLB ; TRANSMIT BREAK DW TRANSMIT$BREAK DB CTRLC ; CTRL-C RETURNS TO CP/M DW EXIT DB 'B' ; TOGGLE BUFFER LOAD DW TBUF DB 'C' ; CONVERSATION MODE DW TCONV DB 'D' ; DUMP BUFFER DW DUMP$BUF DB 'E' ; TOGGLE ECHO DW TECHO DB 'F' ; FILE DISPLAY/SET DW FILE$SET DB 'G' ; GET TRANSLATION TABLES FROM DISK DW TEXIT$XLATE DB 'H' ; HANG UP PHONE AND EXIT DW HANG$EXIT DB 'I' ; INITIALIZE INTERFACES DW INITIALIZE DB 'L' ; LINK W/TELEPHONE SUBSYSTEM DW TPHONE DB 'M' ; SWITCH MODEMS DW MODEM$SWITCH DB 'P' ; TOGGLE PRINTER DW TPRNT DB 'Q' ; TOGGLE EXIT MODE DW TEXIT$SWITCH DB 'R' ; RECEIVE FILE DW RECV$FILE DB 'S' ; SEND FILE DW SEND$FILE DB 'T' ; TERMINAL MODE DW ENTER$TERM DB 'U' ; USER CP/M COMMAND DW USER$CMND DB 'V' ; VIEW MODE DW VIEW$MODE$ENTRY DB 'X' ; EXIT TO CP/M DW EXIT DB 'Y' ; STATUS DISPLAY DW STATUS1$DISPLAY DB 'Z' ; CLOSE FILE DW FCLOS DB '@' ; RUN BLS OVERLAY DW BLS$LOADER * ROUTINES RESIDENT IN EDITOR OVERLAY DB CTRLX ; TRANSLATION TABLE DW 1 ; XLATE$BUILD OFFSET 1 FROM OVERLAY DB CTRLY ; STATUS BUILDER DW 2 ; STATUS$BUILDER OFFSET 2 FROM OVERLAY DB 0 ; END OF COMMAND TABLE * * ABBREVIATED EXIT MODE COMMAND TABLE * CTAB$ABR: DB CTRLE ; SET EXIT CHAR; THIS MUST BE 1ST ENTRY DW TROUT DB 'B' ; TOGGLE BUFFER LOAD DW TBUF DB 'D' ; TOGGLE BUFFER DUMP DW DUMP$BUF DB 'E' ; TOGGLE ECHO DW TECHO DB 'H' ; HANG UP AND EXIT DW HANG$EXIT DB 'L' ; TELEPHONE INTERFACE DW TPHONE DB 'P' ; TOGGLE PRINTER DW TPRNT DB 'Q' ; TOGGLE EXIT MODE DW TEXIT$SWITCH DB 'T' ; TERMINAL MODE DW ENTER$TERM DB 'U' ; USER CP/M COMMAND DW USER$CMND DB 'X' ; SPECIAL EXIT MODE DW EXIT DB 'Y' ; DISPLAY STATUS DW STATUS1$DISPLAY DB 0 * * TELEPHONE SUBCOMMAND TABLE * PTAB: DB 'C'-'@' ; CTRL-C RETURNS TO CP/M DW EXIT DB 'E'-'@' ; EDITOR DW 3 ; PH$EDIT OFFSET 3 FROM OVERLAY DB 'A' ; ANSWER PHONE DW ANSWER$PHONE DB 'D' ; DIAL PHONE DW PH$DIAL DB 'E' ; ENABLE ECHO DW TPECHO DB 'H' ; HANG UP PHONE DW PH$HANG DB 'Q' ; ENTER EXIT MODE DW TEXIT DB 'T' ; ENTER TERMINAL MODE DW OPTD DB 'X' ; EXIT TO CP/M DW EXIT DB 'Y' ; STATUS DISPLAY DW STATUS2$DISPLAY DB 0 * *: TERM II, Section 25 * * DATA/ADDRESS BUFFER REGION * * DATA BUFFERS DATA$BLOCK$COUNT: DS 1 ; NUMBER OF DATA BLOCKS IN BUFFER NEXT$DATA$BLOCK: DS 2 ; PTR TO NEXT DATA BLOCK TO READ/WRITE DATA$BUFFER: DS 2 ; DATA BUFFER PTR FOR DISK I/O * INDIVIDUAL BUFFERS OVERLAY$LOADED: DS 1 ; OVERLAY LOADED FLAG (0=NO) CURRENT$DISK: DS 1 ; NUMBER OF CURRENT DISK DRIVE CURRENT$USER: DS 1 ; NUMBER OF CURRENT USER COMPL$BELL: DS 1 ; =0 IF NO BELL, <>0 IF BELL CURR$CMND: DS 1 ; CURRENT EXIT MODE (0=FULL, 0FFH=ABBREV) OPTC$NEXT: DS 1 ; CHAR AFTER OPTION IN COMMAND LINE ANS$ROUTINE: DS 1 ; ANSWER$PHONE ANSWER ROUTINE XFER (C=CONV, T=TERM, X= ; EXIT) LDSP: DS 1 ; LEADING SPACE FLAG (0=YES, 0FFH=NO LEADING SPACE) VECHOFL: DS 1 ; VIEW MODE ECHO FLAG (0=NO) VNULL: DS 1 ; VIEW MODE NULL FLAG (0=NO) VDEL: DS 1 ; VIEW MODE DELETE FLAG (0=NO) BELLCTR: DS 1 ; BUFFER FULL ALERT BELL COUNTER STADR: DS 2 ; ADDRESS OF STATUS ROUTINE INADR: DS 2 ; ADDRESS OF INPUT ROUTINE OUTADR: DS 2 ; ADDRESS OF OUTPUT ROUTINE NEXTS: DS 2 ; POSITION OF NEXT SEND CHAR NEXTR: DS 2 ; POSITION OF NEXT RECV CHAR CKSUM: DS 1 ; COMPUTED CHECKSUM STORAGE BUF$PTR: DS 2 ; PTR TO NEXT SECTOR BYTE FOR DISK DUMP SEND2$HS$TO: DS 1 ; 0=DON'T TIMEOUT (40 SEC DELAY) ON SEND2 HANDSHAKING FILE$OPEN$FLAG: DS 1 ; 0=FILE NOT OPEN, 0FFH=FILE OPEN LF$SKIP: DS 1 ; SKIP FLAG (FOR BUFFER DUMP) DOT$COUNT: DS 1 ; COUNT FOR DOT DISPLAY IF NOT MONITORING SEND/RECEIVE MONIT$FLAG: DS 1 ; SEND/RECEIVE MONITOR FLAG TEMP$MONIT$FLAG: DS 1 ; SEND/RECEIVE MONITOR FLAG BUFFER DS 2 ; SAFE SPACE FN$BUFFER: DB 40 ; UP TO 40 CHARS IN BUFFER DS 41 ; SPACE FOR BUFFER ERRCT: DS 1 ; ERROR COUNT RSECTNO: DS 1 ; RECEIVED SECTOR NUMBER FOR FTP 3 SECTNO: DS 1 ; SECTOR NUMBER FOR FTP 1&3 BUF$NEXT: DS 2 ; PTR TO NEXT BYTE TO LOAD INTO BUFFER QIP: DS 2 ; ADDRESS OF PRINTER INPUT CHAR PTR QOP: DS 2 ; ADDRESS OF PRINTER OUTPUT CHAR PTR CR$DELAY: DS 1 ; HIGH-ORDER DELAY LOOP VALUE FOR DELAY: DS 1 ; HIGH-ORDER DELAY LOOP VALUE BUFFER: DS 2 ; BUFFER PTR FOR END OF SUPPORT PACKAGE ; THIS IS ALSO BEGINNING OF OVERLAY BUFEND: DS 2 ; PTR TO END OF BUFFER TERMCMND: DB 12,'TERM * A1 /X',0 TERMBLS$FCB: DB 0,'TERMBLS OVR',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0 OVR$FILE: DB 0,'TERM OVR',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0 SUBFIL: DB 0,'$$$ SUB',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0 TERM$NAME: DB 0,'TERM COM',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0 TERM$FILE: DB 0,' ',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0 NUM$BUFFER: DB 42 ; INPUT LINE FOR PHONE NUMBER (MAX # OF CHARS) DS 43 ORG $/256*256+100H ; SET TO EVEN BOUNDARY QP: DS 256 ; PRINTER QUEUE * *: TERM II, Section 26 * * BDOS EQUATES (VERSION 2) * RDCON EQU 1 WRCON EQU 2 PRINT EQU 9 CREAD EQU 10 ; CONSOLE READ LINE CONST EQU 11 ; CONSOLE STAT RSTDSK EQU 13 ; RESET DISK SYSTEM SELDISK EQU 14 ; SELECT DISK OPEN EQU 15 ;0FFH=NOT FOUND CLOSE EQU 16 ; " " SRCHF EQU 17 ; " " SRCHN EQU 18 ; " " ERASE EQU 19 ;NO RET CODE READ EQU 20 ;0=OK, 1=EOF WRITE EQU 21 ;0=OK, 1=ERR, 2=?, 0FFH=NO DIR SPC MAKE EQU 22 ;0FFH=BAD REN EQU 23 ;0FFH=BAD RETDISK EQU 25 STDMA EQU 26 RETUSER EQU 32 CPMBDOS EQU CPM$BASE+5 CPM EQU CPM$BASE BOOT EQU CPM$BASE REIPL EQU CPM$BASE FCB EQU CPM$BASE+5CH ; SYSTEM FCB BUFF EQU CPM$BASE+80H ; SYSTEM BUFFER * *: TERM II, Section 27 * * SUPPORT PACKAGE ENTRY ADDRESSES * ORG $/256*256+256 ; START OF SUPPORT PACKAGE BASE EQU $ ; BEGIN OF SUPPORT PACKAGE XRA A ; NOTHING IMPLEMENTED NOP ; FILL FOR 3 BYTES OF JMP RET LXI H,BASE ; MARK END OF SUPPORT PACKAGE FOR SP$END MVI L,0 INR H RET SP1$STAT EQU BASE ; SUPPORT PACKAGE STATUS SP$END EQU BASE+3 ; END OF SUPPORT PACKAGE INI$CRT EQU BASE+15 ; CRT INIT CRT$CLR EQU BASE+24 ; SCREEN CLEAR ROUTINE GOTOXY EQU BASE+27 ; GOTO XY ROUTINE INI$PR EQU BASE+30 ; TTY INIT ST$PR EQU BASE+36 ; PRINTER OUTPUT STATUS OUT$PR EQU BASE+42 ; PRINTER OUTPUT INI$MOD1 EQU BASE+45 ; MODEM 1 INIT ST$MOD1 EQU BASE+48 ; MODEM 1 INPUT STATUS IN$MOD1 EQU BASE+54 ; MODEM 1 INPUT OUT$MOD1 EQU BASE+57 ; MODEM 1 OUTPUT BR$MOD1 EQU BASE+60 ; MODEM 1 BREAK INI$MOD2 EQU BASE+63 ; MODEM 2 INIT ST$MOD2 EQU BASE+66 ; MODEM 2 INPUT STATUS IN$MOD2 EQU BASE+72 ; MODEM 2 INPUT OUT$MOD2 EQU BASE+75 ; MODEM 2 OUTPUT BR$MOD2 EQU BASE+78 ; MODEM 2 BREAK INI$TI EQU BASE+126 ; TELEPHONE INTERFACE INITIALIZATION TI$INI EQU INI$TI TI$ANS EQU BASE+132 ; ANSWER MODE ON MODEM BOARD TI$HANG EQU BASE+138 ; HANG UP PHONE TI$RST EQU BASE+147 ; RING STATUS DETECT TI$CST EQU BASE+153 ; TELEPHONE INTERFACE CARRIER STATUS TI$DSTRT EQU BASE+156 ; START TELEPHONE DIALING TI$DSTOP EQU BASE+159 ; STOP TELEPHONE DIALING TI$DIGIT EQU BASE+162 ; DIAL DIGIT (IN A REG) TI$TIME EQU BASE+165 ; 0.1 SEC DELAY TIMER TI$PREPANS EQU BASE+168 ; PREPARE FOR ANSWERING TELEPHONE END