;                       PLINK.ASM
;                (latest version OCT 18 1980)
;
;     PLINK - SUPPORT COMMUNICATIONS LINK WITH CYBER
;
;PLINK IS A CP/M TRANSIENT COMMAND WHICH ALLOWS THE USER TO
;ESTABLISH A COMMUNICATIONS LINK WITH A REMOTE COMPUTER
;
;       ORIGINAL BY L.E. HUGHES    EDCAM        JULY, 1977
;
;          This version by Keith Petersen, W8SDZ.
;          WITH HEATH EQUATES ADDED BY TOM JORGENSON
;
; TRS-80 MODEL 1 mods by Steve Vinokuroff, Vanc CBBS
; Optional Triger characters by Steve Vinokuroff
; TRS-80 mods by Dennis Breckenridge, Burnaby CBBS
; D.C.Hayes mods by Bruce Ratoff, Iselin NJ Remote CP/M
;
;This program currently supports the following modems
; or computers via conditional assembly.
;
;       1. PMMI   modem
;       2. ANY serial i/o board (TUART INCLUDED)
;       3. TRS-80 model 1
;       4. TRS-80 model 2
;       5. HEATH H8 WITH 8251 UART AT PORT 330Q
;       6. D.C.Hayes 80-103A or Micromodem 100
;
;
;-->NOTE: IF ASSEMBLED AS WRITTEN WILL WORK WITH D.C.Hayes 80-103A
;         OR MICROMODEM 100 AT PORT 90H
;
;PLINK CURRENTLY SUPPORTS TWO WAY TRANSFER OF TEXT FILES
;BETWEEN THE CP/M DISK AND THE REMOTE COMPUTER. THE FOLLOWING
;CONTROL CODES MAY BE INITIATED FROM THE CONSOLE KEYBOARD:
;
;       ****************************************************
;       *                     COMMANDS:                    *
;       *                                                  *
;       *  CONTROL E    EXIT PLINK TO CP/M WARM BOOT       *
;       *  CONTROL T    TRANSMIT ASCII FILE TO MODEM.      *
;       *               ASKS FOR DRIVE AND FILENAME.TYP    *
;       *  CONTROL C    ABORT FILE SEND TO MODEM           *
;       *  CONTROL Y    SAVE INCOMING ASCII IN RAM BUFFER  *
;       *               FOR LATER TRANSFER TO DISK         *
;       *  CONTROL Q    WRITE RAM BUFFER TO DISK - ASKS    *
;       *               FOR DRIVE AND FILENAME.TYP         *
;       *  DELETE       BACKSPACE WHEN IN COMMAND MODE     *
;       *               ASKING FOR FILENAME                *
;       *  CONTROL U    ABORT CURRENT LINE WHEN IN COMMAND *
;       *               MODE ASKING FOR FILENAME           *
;       *                                                  *
;       *  (NOTE: ALL OTHER CONTROL CODES ARE PASSED TO    *
;       *         MODEM OUTPUT)                            *
;       ****************************************************
;
;
;CONDITIONAL ASSEMBLY SWITCHES <<-- SET FOR YOUR SYSTEM
;
TUART   SET     0       ;CROMEMCO TUART I/O BOARD
PMMI    EQU     0       ;PUT A 1 HERE IF YOU HAVE A PMMI
DCH     EQU     1       ;PUT A 1 HERE IF YOU HAVE A D.C.HAYES
TRS1    EQU     0       ;PUT A 1 HERE IF YOU HAVE TRS80-MOD1
TRSPT   EQU     0       ;PUT 1 HERE IF YOU HAVE TRS80-MOD2
                       ;USING PICKLES & TROUT CP/M 2.X
H84     EQU     0       ;PUT 1 HERE IF YOU HAVE H8/H8-4
       IF      H84
TUART   SET     1
       ENDIF
;
INIT$REQUIRED   EQU     1 ;PUT 1 HERE IF INITIALIZATION NEEDED
;
       IF      TRS1 OR PMMI OR TUART OR DCH
PORT    EQU     1
       ENDIF
;
       IF      TRSPT
PORT    EQU     0       ;STILL NEED THE SWITCH
       ENDIF
;
;
;
;BDOS ENTRY POINT AND FUNCTION CODES
;
       IF      NOT     TRS1
BASE    SET     0       ;STANDARD CPM
       ENDIF
;
       IF      TRS1 OR H84
BASE    SET     4200H   ;TRS-80 MODEL 1 CP/M BASE ADDRESS
       ENDIF
;
BDOS    EQU     BASE+5
RESDSK  EQU     13      ;RESET DISK SYSTEM
OFFC    EQU     15      ;OPEN FILE
CFFC    EQU     16      ;CLOSE FILE
DFFC    EQU     19      ;DELETE FILE
RRFC    EQU     20      ;READ RECORD
WRFC    EQU     21      ;WRITE RECORD
MFFC    EQU     22      ;MAKE FILE
;
;       TRS80 PICKLES AND TROUT SIO CALLS
;       OFFSET BY -3 THAT IS ADD 3 TO ALL CALLS
;
SETSIO  EQU     30H     ;SET UP Z80 SIO
SIOTST  EQU     33H     ;READ SIO STATUS
SIOINP  EQU     36H     ;INPUT A CHAR
SIOOUT  EQU     39H     ;OUTPUT A CHAR
;
;DEFAULT FCB AND FIELD DEFINITIONS
;
FCB     EQU     BASE+5CH
FN      EQU     1       ;FILE NAME FIELD (REL)
FT      EQU     9       ;FILE TYPE FIELD (REL)
EX      EQU     12      ;FILE EXTENT FIELD (REL)
NR      EQU     32      ;NEXT RECORD FIELD (REL)
DBUF    EQU     BASE+80H ;DEFAULT DISK BUFFER ADDRESS
;
;ASCII CONTROL CHARACTERS
;
CR      EQU     0DH     ;CARRIAGE RETURN
LF      EQU     0AH     ;LINE FEED
DEL     EQU     7FH     ;DELETE (RUBOUT)
BELL    EQU     07H     ;BELL SIGNAL
TAB     EQU     09H     ;HORIZONTAL TAB
XON     EQU     11H     ;X-ON CHARACTER
NULL    EQU     00H     ;NULL CHAR
;
;THE FOLLOWING "TRIGER" EQUATE IS SET TO "LF" (LINEFEED)
;BY DEFAULT. AN OPTIONAL TRIGER CHAR MAY BE PASSED VIA FCB1
;
; IE:  PLINK B          WILL SET TRIGER TO "BELL"
;
;THE FOLLOWING OPTIONS ARE ALLOWED
;
;       1. B = BELL  07H
;       2. X = XON   11H
;       3. U = UPLOAD NO TRIGER CHECK AT ALL
;ANY OTHER ASCII CHARACTER MAY BE PASSED THROUGH FCB1
;
;
TRIGER  EQU     LF      ;DEFAULT VALUE
;
;
;WARNING CHARACTER FOR LOW MEMORY
;
WRNSIG  EQU     BELL    ;IF YOU HAVE ONE, PUT 'BELL' HERE
                       ;...ELSE PUT '*' HERE.
;
;MODEM I/O PORT ADDRESSES
;
       IF      PMMI
MODD    EQU     0C1H    ;MODEM DATA PORT
MODS    EQU     0C0H    ;MODEM STATUS PORT
MODINIT EQU     29H     ;INITIALIZE BYTE ORIGINATE,
                       ;7 DATA, EVEN PARITY, 1 STOP
       ENDIF
;
       IF      DCH
MODD    EQU     90H     ;MODEM DATA PORT
MODS    EQU     91H     ;MODEM STATUS PORT
MODINIT EQU     05H     ;7 DATA, EVEN PARITY, 1 STOP
       ENDIF
;
       IF      TRS1
MODD    EQU     0EBH    ;TRS80 MOD 1 RS232 DATA PORT
MODS    EQU     0EAH    ; AND THE RS232 STATUS PORT
       ENDIF
;
       IF      TUART
MODD    EQU     0D8H    ;<<--MODIFY FOR YOURS
MODS    EQU     0DDH    ;<<--MODIFY FOR YOURS
       ENDIF
;
;MODEM STATUS PORT BIT DEFINITIONS
;
       IF      PMMI
MTBE    EQU     01H     ;MODEM TRANS. BUFFER READY FLAG
MRDA    EQU     02H     ;MODEM RECEIVE DATA AVAIL. FLAG
MXOR    EQU     03H     ;MASK TO MAKE MTBE AND MRDA "LOW TRUE"
       ENDIF
;
       IF      DCH
MTBE    EQU     02H     ;MODEM TRANS. BUFFER READY FLAG
MRDA    EQU     01H     ;MODEM RECEIVE DATA AVAIL. FLAG
MXOR    EQU     03H
       ENDIF
;
       IF      TRS1
MTBE    EQU     40H     ;TRS80 MOD1 RS232 BUFFER READY
MRDA    EQU     80H     ;MODEM RECEIVE DATA AVAIL.
MXOR    EQU     0C0H
       ENDIF
;
       IF      TUART   ;<<--OR ANY OTHER SERIAL I/O
MTBE    EQU     20H     ;<<--MODIFY FOR YOURS
MRDA    EQU     1H      ;<<--MODIFY FOR YOURS
MXOR    EQU     21H     ;<<--MODIFY FOR YOURS
       ENDIF
;
;       **MAIN PROGRAM**
;
       ORG     BASE+100H
;
LINK:   LXI     SP,STACK+64     ;CREATE LOCAL STACK
       LHLD    BASE+1  ;POINT TO CP/M JMP TABLE
       LXI     D,3     ;GET READY TO ADD 3
       DAD     D       ;POINT TO CON STATUS JMP
       SHLD    CITCAL+1 ;MODIFY CALL ADRS
       DAD     D       ;POINT TO CON IN JMP
       SHLD    RCCAL+1 ;MODIFY CALL ADRS
       DAD     D       ;POINT TO CON OUT JMP
       SHLD    WCCAL+1 ;MODIFY CALL ADRS
       LDA     FCB+1   ;SEE IF OPTIONAL TRIGER CHAR
       CPI     20H     ;BLANK.. ?
       JZ      SKP     ;..BLANK SO USE DEFAULT "LF"
       CPI     'B'     ;BELL WANTED
       JZ      TRGBEL
       CPI     'X'     ;XON WANTED
       JZ      TRGXON
       CPI     'U'     ;UPLOADING NO CHECKING FOR TRIGER
       JZ      TRGUPL
;
SETTRG  STA     OVERLY+1 ;STORE THE CHARACTER AS IS THEN
       JMP     SKP
;
TRGBEL  MVI     A,BELL
       JMP     SETTRG
;
TRGXON  MVI     A,XON
       JMP     SETTRG
;
TRGUPL  XRA     A               ;ZERO OUT JUMP
       STA     OVERL1+1        ;CHANGE CHECK FOR C/R TO NULL
       STA     OVERL2+1        ;AND SEND LINEFEEDS AS WELL
       JMP     SKP
;
SKP     EQU     $
       IF      H84
       MVI A,80H;      SET DLAB BIT IN 8250 UART
       OUT 0DBH;       8250 AT PORT D8H (330Q)
       NOP ! NOP ! NOP
       NOP ! NOP
       MVI A,01H;      MSB OF BAUD RATE DIVISOR
       OUT 0D9H;       ...TO UART
       NOP ! NOP ! NOP
       NOP ! NOP
       MVI A,80H;      LSB OF BAUD RATE DIVISOR
       OUT 0D8H;       ...TO UART
       NOP ! NOP ! NOP
       NOP ! NOP
       MVI A,03H;      8 BITS, 1 STOP BIT, NO PARITY, DLAB RESET
       OUT 0DBH;       ...TO UART
       NOP ! NOP ! NOP
       NOP ! NOP
       MVI A,0;        RESET CONTROL REGISTER
       OUT 0DCH;       ...TO UART
       JMP     CONT
       ENDIF
       IF      INIT$REQUIRED AND NOT H84 OR PMMI AND NOT H84
       MVI     A,MODINIT
       OUT     MODS    ;INITIALIZE MODEM PORT
       ENDIF
;
       IF      TUART
       MVI     A,80H   ;DSR ON BIT 7 PARL PORT B
       OUT     54H
       ENDIF
;
       IF      TRSPT           ;MUST SET UP SERIAL CHANNEL
RESET:  LXI     H,INITR         ;STORE RETURN ADDRESS
       PUSH    H
       LHLD    1
       LXI     D,SETSIO        ;SIO SETUP ROUTINE
       DAD     D
       PUSH    H               ;STORE ON STACK
       MVI     C,00H           ;NO PARITY CHAN-A
       MVI     D,0E6H          ;8 bits ,1 STOP
       MVI     E,3             ;300 BAUD
       MVI     L,00H           ;DISABLE EXT/ACK SIO FUNCTIONS
       MVI     H,'S'-40H       ;CONTROL S (X-ON)
       RET                     ;TROUGH SETUP PROG
INITR   NOP                     ;DO IT TO IT
       ENDIF
;
       IF      TRS1            ;INIT FOR TRS80 MOD1 RS232
       OUT     0E8H            ;RESET RS232
       IN      0E9H            ;READ THE SWITCHES
       ANI     0F8H
       ORI     5
       OUT     0EAH            ;SET DSR AND CTS
       MVI     A,55H           ;300 BAUD
       OUT     0E9H
       ENDIF
;
;
       IF      PORT
       IN      MODD         ;CLEAR MODEM UART READ BUFFERS
       IN      MODD
       ENDIF
;
CONT    XRA     A       ;CLEAR CHAR BUFFERS
       STA     INCH
       STA     OUTCH
       STA     FLAG    ;CLEAR TEXT SAVE FLAG
       LXI     H,TBUF  ;SET PTR TO TBUF
       SHLD    PTR
       LXI     H,0     ;SIZE = 0
       SHLD    SIZE
       LXI     H,LINKMS  ;PRINT SIGN-ON MESSAGE
       CALL    WCS
;
;        MAIN LOOP
;
LINK3:  CALL    CITEST  ;JUMP IF NO DATA FROM CONSOLE
       JZ      LINK4
       CALL    RCC     ;ELSE READ CONSOLE DATA
       CPI     20H
       CC      PCC     ;CALL PCC IF CONTROL CHAR
       JC      LINK4   ;JUMP IF PCC HANDLED CHAR
       ORI     80H     ;ELSE SET VALID DATA BIT
       STA     INCH    ;AND STORE IN INPUT CHAR BUFFER
LINK4:  LDA     OUTCH   ;JUMP IF NO DATA FOR CONSOLE
       ORA     A
       JP      LINK5
       ANI     7FH     ;ELSE DISCARD VALID DATA BIT
       CALL    WCC     ;SEND CHAR TO CONSOLE
       XRA     A       ;THEN CLEAR OUTPUT CHAR BUFFER
       STA     OUTCH
LINK5:  CALL    MITEST  ;JUMP IF NO DATA FROM MODEM
       JZ      LINK6
       CALL    RMC2    ;ELSE READ MODEM DATA
       CALL    SAVE    ;SAVE CHAR IN TEXT BUFFER IF FLAG ON
       ORI     80H     ;SET DATA VALID BIT
       STA     OUTCH   ;STORE IN OUTPUT CHAR BUFFER
LINK6:  CALL    MOTEST  ;JUMP IF MODEM XMIT BUFFER BUSY
       JZ      LINK7
       LDA     INCH    ;JUMP IF NO DATA FOR MODEM
       ORA     A
       JP      LINK7
       ANI     7FH     ;DISCARD VALID DATA BIT
;
       IF      PORT
       OUT     MODD    ;OUTPUT CHAR TO MODEM
       ENDIF
;
       IF      TRSPT
       PUSH    B       ;STORE REGISTERS
       PUSH    H
       PUSH    D
       CALL    WMC     ;SEND CHAR
       POP     D
       POP     H
       POP     B
       ENDIF
;
       XRA     A       ;...THEN CLEAR INPUT CHAR BUFFER
       STA     INCH
LINK7:  JMP     LINK3   ;END OF MAIN LOOP
;
LINKMS: DB      CR,LF,'PLINK as of 25-SEP-80'
       DB      CR,LF,LF,'READY',CR,LF,LF,0
;
;        PCC - PROCESS CONTROL CHARACTER
;
PCC:    CPI     'E'-40H ;JUMP OUT IF CTRL E
       JNZ     PCC1
       PUSH    H
       LXI     H,AYS   ;PRINT 'ARE YOU SURE'
       CALL    WCS
       POP     H
       CALL    RCC     ;GET ANSWER
       CALL    WCC     ;ECHO IT
       ANI     5FH     ;MAKE UPPER CASE
       CPI     'Y'     ;YES?
       JZ      PCCEX   ;EXIT
       CALL    WCCR    ;CRLF
       STC             ;TELL LINK TO IGNORE THIS CHARACTER
;
       IF      TRSPT
       POP     PSW     ;GOBBLE UP CALL ADDRESS
       JMP     RESET   ;RE-INITIALIZE SIO
       ENDIF
;
       IF      PORT
       RET
       ENDIF
;
PCC1:   CPI     'T'-40H ;JUMP IF NOT CONTROL-T
       JNZ     PCC2
       CALL    STF     ;TRANSMIT TEXT FILE TO MODEM
       STC             ;TELL LINK TO IGNORE THIS CHARACTER
       RET
;
PCC2:   CPI     'Y'-40H ;JUMP IF NOT CONTROL-Y
       JNZ     PCC3
       MVI     A,1     ;TURN ON TEXT SAVE FLAG
       STA     FLAG
       LXI     H,PCCMR ;PRINT 'SAVING INCOMING TEXT IN MEMORY'
       CALL    WCS
       STC             ;TELL LINK TO IGNORE THIS CHARACTER
       RET
;
PCC3:   CPI     'Q'-40H ;JUMP IF NOT CONTROL-Q
       JNZ     PCC4
       XRA     A       ;TURN OFF TEXT SAVE FLAG
       STA     FLAG
       CALL    WTB     ;WRITE TEXT BUFFER TO DISK
       STC
       RET
;
PCC4:   STC             ;LET LINK HANDLE ALL OTHER CONT. CODES
       CMC
       RET
;
PCCEX:  LXI     H,DISMS ;PRINT 'MODEM NOT DISCONNECTED'
       CALL    WCS
       JMP     BASE    ;EXIT TO WARM BOOT
;
AYS:    DB      CR,LF,'EXIT TO CP/M - ARE YOU SURE (Y OR N)?',0
       IF      PMMI OR DCH
DISMS:  DB      CR,LF,'++DON''T FORGET - THE MODEM '
       DB      'IS NOT DISCONNECTED++',CR,LF
       DB      'USE "MODEM D" TO DISCONNECT',0
       ENDIF
;
       IF      NOT PMMI AND NOT DCH
DISMS:  DB      CR,LF,'+++ EXIT TO CP/M +++',CR,LF,0
       ENDIF
;
PCCMR:  DB      CR,LF,'SAVING INCOMING TEXT IN MEMORY',CR,LF,0
;
;        STF - SEND TEXT FILE (TO MODEM)
;
STF:    CALL    GFN     ;GET NAME OF DISK FILE TO SEND
       JC      STF6    ;JUMP IF FILE NAME ERROR
       CALL    OPEN    ;TRY TO OPEN SPECIFIED FILE
       CPI     255     ;JUMP IF FILE NOT FOUND
       JZ      STF7
STF1:   CALL    READ    ;READ NEXT RECORD INTO DBUF
       CPI     1       ;JUMP IF END-OF-FILE
       JZ      STF5
       LXI     H,DBUF  ;POINT TO DISK BUFFER
       MVI     C,128
STF2:   MOV     A,M     ;FETCH NEXT CHAR FROM DBUF
       INX     H
       CPI     'Z'-40H ;JUMP IF END-OF-FILE CHARACTER
       JZ      STF5
OVERL2  CPI     LF      ;IGNORE LINE FEEDS
       JZ      STF4
       CALL    WMC     ;WRITE CHARACTER TO MODEM
       CALL    WCC     ;WRITE CHARACTER TO CONSOLE
OVERL1  CPI     CR      ;JUMP IF NOT CARRIAGE RETURN
       JNZ     STF4
STF3:   CALL    CITEST  ;CHECK CONSOLE DATA READY
       JZ      STF3A   ;NO DATA THERE
       CALL    RCC     ;GET CONSOLE CHARACTER
       CPI     'C'-40H ;CONTROL C ABORTS IT
       JZ      STF8
STF3A:  CALL    MITEST  ;WAIT FOR NEXT MODEM KHARACTER
       JZ      STF3
       CALL    RMC2    ;CHECK MODEM FOR TRIGGER CHAR.
OVERLY  CPI     TRIGER
       JNZ     STF3
       CALL    WCCR    ;SEND CRLF TO CONSOLE
STF4:   DCR     C       ;LOOP THRU REST OF DBUF
       JNZ     STF2
       JMP     STF1    ;GO GET NEXT RECORD FROM DISK
;
STF5:   LXI     H,STFSM ;PRINT 'FILE SEND COMPLETE'
       CALL    WCS
       RET
;
STF6:   LXI     H,STFS1 ;PRINT 'FILE NAME ERROR'
       CALL    WCS
       RET
;
STF7:   LXI     H,STFS2 ;PRINT 'FILE NOT FOUND'
       CALL    WCS
       RET
;
STF8:   LXI     H,STFSA ;PRINT 'FILE SEND ABORTED'
       CALL    WCS
       RET
;
STFSM:  DB      'FILE SEND COMPLETE',CR,LF,0
STFS1:  DB      'FILE NAME ERROR',CR,LF,0
STFS2:  DB      'FILE NOT FOUND',CR,LF,0
STFSA:  DB      CR,LF,'FILE SEND ABORTED',CR,LF,0
;
;        SAVE - SAVE CHAR IN TEXT BUFFER IF FLAG ON
;
;        ENTRY CONDITIONS
;               A - CHARACTER TO SAVE
;
SAVE:   PUSH    PSW
       LDA     FLAG
       ORA     A
       JNZ     SAVE1
       POP     PSW
       RET
;
SAVE1:  POP     PSW
       CPI     DEL     ;RUBOUT (DEL) ?
       RZ              ;YES, IGNORE IT
       CPI     20H     ;TEST FOR CONTROL CHARACTERS
       JNC     SAVE2   ;JUMP IF NOT CONTROL CHAR.
       CPI     CR      ;ALLOW CR TO BE SAVED
       JZ      SAVE2
       CPI     LF      ;ALLOW LF TO BE SAVED
       JZ      SAVE2
       CPI     TAB     ;ALLOW TAB TO BE SAVED
       JZ      SAVE2
       RET             ;IGNORE ALL OTHER CONTROL CHARS.
;
SAVE2:  PUSH    H
       LHLD    SIZE    ;SIZE = SIZE + 1
       INX     H
       SHLD    SIZE
       LHLD    PTR
       MOV     M,A
       INX     H
       SHLD    PTR
       PUSH    PSW
       LDA     BASE+7  ;GET SYSTEM SIZE
       SUI     1       ;SO WE DONT CRASH CP/M
       CMP     H       ;ARE WE OUT OF ROOM?
       JZ      SAVEAB  ;YES, ABORT
       SUI     4       ;LEAVE SOME ROOM (1K)
       CMP     H
       MVI     A,WRNSIG  ;SIGNAL CONSOLE RUNNING OUT OF SPACE
       CC      WCC
       POP     PSW
       POP     H
       RET
;
;       SAVEAB - RAN OUT OF ROOM, ISSUE MESSAGE AND FLOW
;                THROUGH TO DISK SAVE ROUTINE
;
SAVEND: DB      BELL,CR,LF,'ABORTING - NO ROOM LEFT',0
;
SAVEAB: LXI     SP,STACK+64  ;REINITIALIZE STACK
       LXI     H,SAVEND  ;PRINT 'ABORTING - NO ROOM LEFT'
       CALL    WCS
       LXI     H,LINK  ;SET UP RETURN ADDRESS
       PUSH    H       ;LEAVE IT ON THE STACK
;
;        WTB - WRITE TEXT BUFFER TO DISK
;
WTB:    LHLD    SIZE    ;JUMP IF TEXT BUFFER EMPTY
       MOV     A,L
       ORA     H
       JZ      WTB5
       MVI     C,RESDSK ;RESET IN CASE READ-ONLY
       CALL    BDOS
       CALL    GFN     ;GET FILE NAME
       JC      WTB6    ;JUMP IF FILE NAME ERROR
       CALL    DELT    ;DELETE OLD FILE, IF ANY
       CALL    MAKE    ;MAKE NEW FILE
       LHLD    SIZE    ;DE = TBUF SIZE
       XCHG
       LXI     H,DBUF  ;TOP OF STACK POINTS TO DBUF
       PUSH    H
       LXI     H,TBUF  ;HL POINTS TO TBUF
WTB1:   MVI     C,128   ;DISK BUFFER SIZE
WTB2:   MOV     A,M     ;FETCH NEXT BYTE OF TBUF
       INX     H
       XTHL
       MOV     M,A     ;STORE IN DBUF
       INX     H
       XTHL
       DCX     D       ;SIZE = SIZE - 1
       MOV     A,D     ;EXIT LOOP IF SIZE = 0
       ORA     E
       JZ      WTB3
       DCR     C       ;LOOP UNTIL DBUF FULL
       JNZ     WTB2
       CALL    WRITE   ;WRITE FULL DBUF TO DISK
       XTHL            ;TOP OF STACK POINTS TO DBUF
       LXI     H,DBUF
       XTHL
       JMP     WTB1    ;LOOP UNTIL END OF TBUF
;
WTB3:   POP     H       ;HL POINTS TO CURRENT PLACE IN DBUF
WTB4:   MVI     M,'Z'-40H ;STORE EOF CODE
       INX     H
       DCR     C       ;LOOP THRU REST OF DBUF
       JNZ     WTB4
       CALL    WRITE   ;WRITE LAST SECTOR TO DISK
       CALL    CLOSE   ;CLEAN UP ACT AND GO HOME
       LXI     H,TBUF  ;CLEAR TEXT BUFFER
       SHLD    PTR
       LXI     H,0
       SHLD    SIZE
       LXI     H,WTBSM ;PRINT 'BUFFER SAVED ON DISK'
       CALL    WCS
       RET
;
WTB5:   LXI     H,WTBS1 ;PRINT 'TEXT BUFFER EMPTY'
       CALL    WCS
       RET
;
WTB6:   LXI     H,WTBS2 ;PRINT 'FILE NAME ERROR'
       CALL    WCS
       RET
;
WTBSM:  DB      CR,LF,'BUFFER SAVED ON DISK',CR,LF
       DB      'MEMORY SAVE CANCELLED',CR,LF,0
WTBS1:  DB      'TEXT BUFFER EMPTY',CR,LF,0
WTBS2:  DB      'FILE NAME ERROR',CR,LF,0
;
;        WCS - WRITE CONSOLE STRING
;
;
;        ENTRY CONDITIONS
;               HL - POINTS TO STRING (TERM BY ZERO BYTE)
;
WCS:    MOV     A,M
       INX     H
       ORA     A
       RZ
       CALL    WCC
       JMP     WCS
;
;        WCCR - WRITE CONSOLE CARRIAGE RETURN (AND LINE FEED)
;
WCCR:   MVI     A,CR
       CALL    WCC
       MVI     A,LF
;
;        WCC - WRITE CONSOLE CHARACTER
;
;        ENTRY CONDITIONS:
;               A - CHARACTER TO WRITE
;
WCC:    PUSH    PSW
       PUSH    B
       PUSH    D
       PUSH    H
       MOV     C,A     ;GET CHARACTER FOR CBIOS
WCCAL:  CALL    $-$     ;MODIFIED BY INIT.
       POP     H
       POP     D
       POP     B
       POP     PSW
       RET
;
;        RCS - READ CONSOLE STRING (WITH ECHO)
;
;        EXIT CONDITIONS
;               B - NUMBER OF CHARACTERS READ (<255)
;               HL - POINTS TO LAST CHAR STORED (CR)
;
RCS:    LXI     H,IBUF
       MVI     B,0
RCS1:   CALL    RCC     ;READ NEXT CHAR FROM CONSOLE
       CPI     DEL     ;JUMP IF NOT DEL
       JNZ     RCS2
       INR     B       ;IGNORE DEL IF IBUF ALREADY EMPTY
       DCR     B
       JZ      RCS1
       DCX     H       ;ELSE DISCARD LAST CHAR
       MOV     A,M     ;ECHO DISCARDED CHAR TO CONSOLE
       CALL    WCC
       DCR     B       ;DECREMENT COUNT
       JMP     RCS1    ;       AND LOOP
;
RCS2:   CPI     'U'-40H ;JUMP IF NOT CONTROL U
       JNZ     RCS3
       CALL    WCCR    ;ELSE ABORT CURRENT LINE
       JMP     RCS     ;       AND START OVER
;
RCS3:   CALL    WCC     ;ECHO CHAR TO CONSOLE
       MOV     M,A     ;STORE CHAR IN IBUF
       INR     B       ;INCREMENT COUNT
       CPI     CR      ;JUMP IF CARRIAGE RETURN
       JZ      RCS4
       INX     H       ;ELSE ADVANCE POINTER
       JMP     RCS1    ;       AND LOOP
;
RCS4:   MVI     A,LF    ;ISSUE LINE FEED AND RETURN
       CALL    WCC
       RET
;
;        RCC - READ CONSOLE CHARACTER
;
;        EXIT CONDITIONS
;               A - CHARACTER READ
;
RCC:    PUSH    B
       PUSH    D
       PUSH    H
RCCAL:  CALL    $-$     ;MODIFIED BY INI\.
       POP     H
       POP     D
       POP     B
       RET
;
;        WMC - WRITE MODEM CHARACTER
;
;        ENTRY CONDITIONS
;               A - CHARACTER TO WRITE
;
;
       IF      PORT
WMC:    PUSH    PSW
WMCL:   IN      MODS
       XRI     MXOR
       ANI     MTBE
       JNZ     WMCL
       POP     PSW
       ANI     7FH     ;STRIP PARITY BIT
       OUT     MODD
       RET
       ENDIF
;
       IF      TRSPT
WMC:    PUSH    H
       PUSH    D
       PUSH    PSW
WMCL:   CALL    MOTEST  ;TEST STATUS
       JZ      WMCL    ;LOOP TILL TX EMPTY
       POP     PSW     ;RESTORE CHAR
       ANI     7FH     ;STRIP PARITY
       PUSH    B       ;STORE B
       MOV     C,A     ;PUT CHAR INTO C
       MVI     B
,00H    ;CHANNEL A
       LXI     H,WMCRE ;STORE RETURN ADDRESS
       PUSH    H
       LHLD    1       ;GET BASE ADDRESS
       LXI     D,SIOOUT
       DAD     D
       PCHL            ;JUMP TO IT
WMCRE:  POP     B       ;RESTORE IT
       POP     D
       POP     H
       RET
       ENDIF
;
;        RMC - READ MODEM CHARACTER
;
;        EXIT CONDITIONS:
;               A - CHARACTER READ
;
;
       IF      PORT
RMC:    IN      MODS
       XRI     MXOR
       ANI     MRDA
       JNZ     RMC
RMC2:   IN      MODD
       ANI     7FH
       RET
       ENDIF
;
       IF      TRSPT
RMC:    CALL    MITEST  ;CHAR AVAILABLE
       JZ      RMC     ;LOOP IF NOT READY
RMC2:   PUSH    B       ;STORE B
       PUSH    D
       PUSH    H
       MVI     B,00H   ;CHANNEL A
       LXI     H,RMCRE ;RETURN ADDRESS
       PUSH    H
       LHLD    1
       LXI     D,SIOINP
       DAD     D
       PCHL
RMCRE:  POP     H
       POP     D
       POP     B
       ANI     7FH     ;STRIP PARITY
       RET
       ENDIF
;
;
;        GFN - GET FILE NAME
;
GFN:    LXI     H,GFNSD ;PRINT 'WHICH DRIVE?'
       CALL    WCS
       CALL    RCC     ;GET ANSWER FROM CONSOLE
       CALL    WCC     ;ECHO IT TO CONSOLE
       ANI     5FH     ;MAKE UPPER CASE
       SUI     'A'-1
       JC      GFN     ;REQUIRE ALPHABETIC
       JZ      GFN
       CPI     17      ;ALLOW 16 DRIVES (AS IN CP/M 2.X)
       JNC     GFN
       STA     FCB
GFNB:   LXI     H,GFNS1 ;PRINT 'FILENAME? '
       CALL    WCS
       CALL    RCS     ;READ RESPONSE INTO IBUF
       LXI     H,FCB+FN  ;BLANK FILL FN AND FT FIELDS
       MVI     C,11
GFN1:   MVI     M,' '
       INX     H
       DCR     C
       JNZ     GFN1
       LXI     H,IBUF  ;POINT TO INPUT BUFFER
       LXI     D,FCB+FN  ;SCAN OFF FN FIELD
       MVI     C,9
GFN2:   MOV     A,M     ;FETCH NEXT CHAR FROM IBUF
       INX     H
       CPI     61H     ;IF LC, CONVERT TO UC
       JC      GFN2A
       SUI     20H
GFN2A:  CPI     CR      ;JUMP IF END OF LINE
       JZ      GFN5
       CPI     '.'     ;JUMP IF END OF NAME
       JZ      GFN3
       STAX    D       ;ELSE STORE CHAR IN FN FIELD
       INX     D
       DCR     C       ;LOOP IF 8 OR LESS CHARS SO FAR
       JNZ     GFN2
       JMP     GFN6    ;ELSE TAKE ERROR EXIT
;
GFN3:   LXI     D,FCB+FT  ;SCAN OFF FT FIELD
       MVI     C,4
GFN4:   MOV     A,M     ;FETCH NEXT CHAR FROM IBUF
       INX     H
       CPI     61H     ;IF LC, CONVERT TO UC
       JC      GFN4A
       SUI     20H
GFN4A:  CPI     CR      ;JUMP IF END OF LINE
       JZ      GFN5
       STAX    D       ;ELSE STORE CHAR IN FT FIELD
       INX     D
       DCR     C       ;LOOP IF 3 OR LESS CHARS SO FAR
       JNZ     GFN4
       JMP     GFN6    ;ELSE TAKE ERROR EXIT
;
GFN5:   XRA     A
       STA     FCB+EX  ;SET EXTENT NUMBER TO ZERO
       STA     FCB+NR  ;SET RECORD NUMBER TO ZERO
       STC             ;CLEAR ERROR FLAG AND RETURN
       CMC
       RET
;
GFN6:   STC             ;SET ERROR FLAG AND RETURN
       RET
;
GFNSD:  DB      CR,LF,'WHICH DRIVE? ',0
GFNS1:  DB      CR,LF,'FILENAME? ',0
;
;        OPEN - OPEN DISK FILE
;
OPEN:   PUSH    H
       PUSH    D
       PUSH    B
       LXI     D,FCB
       MVI     C,OFFC
       CALL    BDOS
       POP     B
       POP     D
       POP     H
       RET
;
;        READ - READ RECORD FROM DISK FILE
;
READ:   PUSH    H
       PUSH    D
       PUSH    B
       LXI     D,FCB
       MVI     C,RRFC
       CALL    BDOS
       POP     B
       POP     D
       POP     H
       RET
;
;        CLOSE - CLOSE DISK FILE
;
CLOSE:  PUSH    H
       PUSH    D
       PUSH    B
       LXI     D,FCB
       MVI     C,CFFC
       CALL    BDOS
       POP     B
       POP     D
       POP     H
       RET
;
;        DELT - DELETE DISK FILE
;
DELT:   PUSH    H
       PUSH    D
       PUSH    B
       LXI     D,FCB
       MVI     C,DFFC
       CALL    BDOS
       POP     B
       POP     D
       POP     H
       RET
;
;        WRITE - WRITE RECORD TO DISK
;
WRITE:  PUSH    H
       PUSH    D
       PUSH    B
       LXI     D,FCB
       MVI     C,WRFC
       CALL    BDOS
       POP     B
       POP     D
       POP     H
       RET
;
;        MAKE - MAKE NEW DISK FILE
;
MAKE:   PUSH    H
       PUSH    D
       PUSH    B
       LXI     D,FCB
       MVI     C,MFFC
       CALL    BDOS
       POP     B
       POP     D
       POP     H
       RET
;
;       CITEST - CHECK CONSOLE INPUT STATUS
;
CITEST: PUSH    B
       PUSH    D
       PUSH    H
CITCAL: CALL    $-$     ;MODIFIED BY INIT.
       ORA     A       ;SET ZERO FLAG
       POP     H
       POP     D
       POP     B
       RET             ;ZERO FLAG CARRIES ANSWER
;
;       MITEST - CHECK MODEM INPUT STATUS
;
       IF      PORT
MITEST: IN      MODS    ;GET MODEM UART STATUS
       XRI     MXOR    ;INVERT HIGH-TRUE BITS
       ANI     MRDA    ;ANY DATA AVAILABLE?
       MVI     A,0
       JNZ     MITST1
       CMA
MITST1: ORA     A
       RET             ;ZERO FLAG CARRIES ANSWER
       ENDIF
;
;
       IF      TRSPT
;
MITEST: PUSH    B
       PUSH    H
       PUSH    D
       MVI     B,00    ;CHANNEL A
       LXI     H,MITSTR
       PUSH    H
       LHLD    1
       LXI     D,SIOTST
       DAD     D
       PCHL
MITSTR: POP     D
       POP     H
       ANI     01      ;TX EMPTY
       POP     B
       RET             ;ZERO FLAG HOLDS THE ANSWER
       ENDIF
;
;       MOTEST - CHECK MODEM OUTPUT STATUS
;
;
       IF      PORT
MOTEST: IN      MODS    ;GET MODEM UART STATUS
       XRI     MXOR    ;INVERT HIGH-TRUE BITS
       ANI     MTBE    ;UART READY FOR CHARACTER?
       MVI     A,0
       JNZ     MOTST1  ;ZERO FLAG CARRIES ANSWER
       CMA
MOTST1: ORA     A       ;SET ZERO FLAG IF READY
       RET
       ENDIF
;
       IF      TRSPT
MOTEST: PUSH    B
       PUSH    H
       PUSH    D
       MVI     B,00    ;CHANNEL A
       LXI     H,MOTSTR
       PUSH    H
       LHLD    1
       LXI     D,SIOTST
       DAD     D
       PCHL
MOTSTR: ANI     02      ;BUFFER EMPTY
       POP     D
       POP     H
       POP     B
       RET
       ENDIF
;
;        DATA AREA
;
INCH:   DS      1       ;INPUT CHAR BUFFER (TO CYBER)
OUTCH:  DS      1       ;OUTPUT CHAR BUFFER (FROM CIBER)
STACK:  DS      80      ;LOCAL STACK
IBUF:   DS      256     ;INPUT BUFFER
;
;        TEXT BUFFER
;
FLAG:   DS      1       ;TEXT SAVE FLAG
PTR:    DS      2       ;TEXT BUFFER POINTER
SIZE:   DS      2       ;TEXT BUFFER SIZE
TBUF:   EQU     $       ;START OF TEXT BUFFER
;
       END     LINK
;