;V5.7   (Revised 10/17/80)
;
;
;REMOTE CONSOLE PROGRAM FOR CP/M
;BASED ON AN ORIGINAL PROGRAM WRITTEN
;BY DAVE JAFFE, JANUARY 1979
;
;Rewritten for PMMI modem
;by Ward Christensen, February 1979
;
;Lobotomized for D C Hayes 80-103
;by Bruce Ratoff, May 1980
;
;
;I'd also like to give credit to Bill Precht
;       for the "label + offset" idea allowing
;       this program to relocate itself
;       without using DDT to initially set it up
;
;
;10/17/80 Add "ani 7f" to console output so lower-to-upper case
;         conversion doesn't get fooled.  (BRR)
;
;10/09/80 Correct 2.X incompatibility in FCB setup for welcome file.
;         Also fixed bug that caused loss of welcome file when /A used.
;         (BRR)
;
;09/12/80 Reset user 0/drive A in move-up logic so BYE /A works
;         from other drives/users.
;         by Bruce Ratoff
;
;06/11/80 Ignore modem characters received with framing or overrun
;         errors.  This should help eliminate some of the noise
;         sensitivities.
;         by Bruce Ratoff
;
;06/08/80 Added (optional) lower-to-upper case conversion on output
;         by asking extra question at signon.
;         by Bruce Ratoff
;
;06/04/80 Added keyboard input timeout to eliminate the "asleep
;         at the switch" phenomenon and telco noises that sound
;         like a carrier.
;         by Bruce Ratoff
;
;05/26/80 Put in "warm boot command" logic to force user into
;         a chosen CCP command and disable warm boot on entry.
;         The JMP at location 0 is replaced by a CALL, which
;         is used to tell BYE to disconnect instead of doing
;         warm boot.  Program may re-enable warm boot by storing
;         JMP opcode back into location 0.  Also, whenever
;         warm boot is disabled, control-c is converted to null.
;         by Bruce Ratoff
;
;05/20/80 Put test at CARCK to force valid user/drive, as
;         suggested by Keith Petersen.
;         by Bruce Ratoff
;
;05/12/80 Changed modem I/O equates, ports and flags for the
;         D C Hayes 80-103A.
;         By Bruce Ratoff
;
;01/24/80 Added routines to preserve registers when calling
;         the user's CBIOS.  Added conditional assembly for
;         callback feature.  Increased stack space to 60.
;         By Keith Petersen.
;
;09/24/79 Added routines to allow automatic multiple baud
;         rate selection, exit to CP/M from local console,
;         echo nr. of nulls selected. By Keith Petersen,
;         with thanks to Bob Mathias for suggestions.
;
;05/06/79 Added routine to allow "callback" operation so modem
;         does not answer normal voice calls.  By Robbin Hough
;         and Keith Petersen, W8SDZ.
;
;------------------------------------------------
;CHANGE THE FOLLOWING EQUATE TO AN AREA IN YOUR
;HI MEMORY WHERE THIS PROGRAM MAY PATCH ITSELF IN.
;APPROX MEMORY REQUIREMENTS: 900 BYTES.
;
DEST    EQU     0F800H  ;RUNNING LOCATION OF CODE
;
;CHANGE THE FOLLOWING TO YOUR LOCAL CONSOLE KEYBOARD
;DATA PORT NUMBER.
;
CONDATA EQU     05H     ;LOCAL CONSOLE INPUT DATA PORT
;
;CHANGE THE FOLLOWING IF YOUR HAYES IS NOT AT 090H
;(THE OTHER PORT EQUATES ARE BASED ON THIS VALUE)
;
DPORT   EQU     090H    ;UART DATA PORT
;
;YOU WILL LIKELY ALSO WANT TO CHANGE THE PASSWORD,
;LOCATED BELOW AT LABEL "PASSWD", AND THE MESSAGES
;PRINTED AT LABEL "WELCOME" AND JUST ABOVE LABEL
;"HANGUP"
;
;------------------------------------------------
;
;THIS PROGRAM RUNS UP IN HIGH RAM.  IT GETS THERE
;BY BEING MOVED THERE WHEN 'BYE' IS TYPED.
;
;THE PROGRAM IN HI RAM DOES THE FOLLOWING:
;
;       1.      HANGS UP THE PHONE
;       2.      AWAITS RING DETECT, ALLOWS EXIT
;               TO CP/M IF LOCAL KBD TYPES CTL-C
;       3.      OUTPUTS CARRIER
;       4.      AWAITS INCOMING CARRIER
;               GOING TO STEP 1 IF NONE
;               FOUND IN 15 SECONDS
;       5.      ASKS NUMBER OF NULLS (0-9)
;       6.      TYPES THE FILE "WELCOME" FROM
;               DISK, ALLOWING CTL-C TO SKIP IT
;       7.      ASKS FOR A PASSWORD, ALLOWING
;               5 TRIES TO GET IT RIGHT.
;       8.      WHEN PASSWORD ENTERED, DROPS
;               INTO CP/M.
;       9.      CALLER CAN LEAVE BY HANGING UP,
;               (ANY TIME CARRIER IS LOST, IT
;               WAITS 15 SECONDS, THEN GOES
;               BACK TO STEP 1), OR THE CALLER
;               MAY TYPE THE PROGRAM NAME (BYE)
;
;       SYSTEM EQUATES:
FALSE   EQU     0
TRUE    EQU     NOT FALSE
BDOS    EQU     5
CR      EQU     0DH
LF      EQU     0AH
;
MAX$DRIVE       EQU     2       ;HIGHEST SUPPORTED DRIVE (0=A, 1=B, etc)
MAX$USER        EQU     3       ;HIGHEST SUPPORTED USER
PRINTER EQU     FALSE   ;WANT TO RETAIN LIST DEVICE?
DUAL$IO EQU     TRUE    ;WANT CONSOLE & MODEM?
CALLBAK EQU     TRUE    ;WANT CALLBACK FEATURE?
PSWDREQ EQU     FALSE   ;PASSWORD REQUIRED?
FASTCLK EQU     TRUE    ;TRUE IF 4MHZ SYSTEM CLOCK
CPM2    EQU     TRUE    ;TRUE FOR CP/M VERSION 2.x
WBCMND  EQU     TRUE    ;TRUE TO USE WARM BOOT COMMAND
WBUSER  EQU     3       ;USER # FOR WARM BOOT COMMAND (MUST BE 0 IN 1.4 CP/M)
WBDRV   EQU     0       ;DRIVE # FOR WARM BOOT COMMAND (0=A, 1=B, ETC.)
;                        (COMMAND STRING GOES AT LABEL 'WBCSTR' NEAR
;                               END OF PROGRAM)
TIMEOUT EQU     TRUE    ;TRUE IF TIMING OUT ON CONSOLE INPUT
TOVALUE EQU     20*5*60 ;TIMEOUT TIME IN 20THS OF A SECOND
;
;       HAYES MODEM PORT ASSIGNMENTS:
;
;HAYES MODEM PORT EQUATES (DPORT PREVIOUSLY DONE)
;
TPORT   EQU     DPORT+1 ;CONTROL/STATUS PORT
RPORT   EQU     DPORT+1 ;RATE GEN/MODEM STATUS
CPORT   EQU     DPORT+2 ;MODEM CONTROL
;
;MODEM CONTROL COMMAND WORDS
;
P3CLEAR EQU     00H     ;IDLE MODE
;
;
;
;SWITCH HOOK AND MODEM COMMANDS,
;       OUTPUT TO TPORT (PORT 0)
;
P0BYE   EQU     0       ;ON HOOK, OR DIALING BREAK
P0ORIG  EQU     84H     ;OFF HOOK, ORIG.
P0ANSW  EQU     82H     ;ANSWER PHONE
P08BIT  EQU     06H     ;8 DATA BITS
P0NOPY  EQU     10H     ;NO PARITY
P0EPS   EQU     01H     ;EVEN PARITY SELECT
P0TSB   EQU     08H     ;2 STOP BITS
P0EI    EQU     20H     ;ENABLE INTERRUPTS
P0NORM  EQU     P08BIT+P0NOPY ;I USE 8 BITS, NO PARITY
P0110   EQU     P08BIT+P0NOPY+P0TSB ;SAME W/2 STOP BITS
;
;MODEM STATUS, INPUT ON TPORT (PORT 1)
;
P2RDET  EQU     80H     ;RING DETECT
P2CTS   EQU     40H     ;CTS (CARRIER DETECT)
;
;HAYES MODEM STATUS MASKS
;
P0TBMT  EQU     2       ;XMIT BUFF EMPTY
P0DAV   EQU     1       ;DATA AVAILABLE
P0RPE   EQU     4       ;REC'D PARITY ERR
P0ORUN  EQU     10H     ;OVERRUN
P0FERR  EQU     08H     ;FRAMING ERROR
;
;BAUD RATE DIVISORS
;
B110    EQU     0       ;110 BAUD
B300    EQU     1       ;300 BAUD
;
       ORG     100H
;
;MOVE THE MODEM INTERFACE PROGRAM UP TO HI RAM
;AND JUMP TO IT.
;
MOVEUP  lxi     sp,80h  ;set a temporary stack
       LXI     B,PEND-START+1  ;NUMBER OF BYTES TO MOVE
       LXI     H,DEST+PEND-START+1 ;END OF MOVED CODE
       LXI     D,SOURCE+PEND-START     ;END OF SOURCE CODE
MVLP    LDAX    D       ;GET BYTE
       DCX     H       ;BUMP POINTERS
       MOV     M,A     ;NEW HOME
       DCX     D
       DCX     B       ;BUMP BYTE COUNT
       MOV     A,B     ;CHECK IF ZERO
       ORA     C
       JNZ     MVLP    ;IF NOT, DO SOME MORE
       push    h       ;save jump address
       sub     a       ;force user 0, disk A
       sta     4       ;at next sign-in
       IF      CPM2
       mvi     e,0     ;need to set user 0 in bdos
       mvi     c,32    ;so that open succeeds on welcome file
       call    bdos
       ENDIF
       mvi     e,0
       mvi     c,14    ;also need drive A default
       call    bdos
       mvi     a,0C3H  ;reset boot trap/control-c trap
       sta     0
       ret             ;JUMP TO "START" (was PUSHed above)
;
SOURCE  EQU     $       ;BOUNDARY MEMORY MARKER
;
OFFSET  EQU     DEST-SOURCE ;RELOC AMOUNT
;-----------------------------------------------;
;       THE FOLLOWING CODE GETS MOVED           ;
;        TO HI RAM LOCATED AT "DEST",           ;
;           WHERE IT IS EXECUTED.               ;
;-----------------------------------------------;
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
;XX   C A U T I O N :  IF MODIFYING ANYTHING    XX
;XX     IN THIS PROGRAM FROM HERE ON:           XX
;XX     A-L-L  LABELS MUST BE OF THE FORM:      XX
;XX     label   EQU     $+OFFSET                XX
;XX     IN ORDER THAT THE RELOCATION TO HI RAM  XX
;XX     WORK SUCCESSFULLY.  FORGETTING TO       XX
;XX     SPECIFY '$+OFFSET' WILL CAUSE THE PRO-  XX
;XX     GRAM TO JMP INTO WHATEVER IS CURRENTLY  XX
;XX     IN LOW MEMORY, WITH UNPREDICTABLE       XX
;XX     RESULTS.  BE CAREFUL....                XX
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
;
;       IF CARRIER LOST, HANG UP, AWAIT RING.
;       OTHERWISE, SAY GOODBYE, AND HANG UP
;
START   EQU     $+OFFSET
       LXI     SP,STACK
       XRA     A       ;GET 0
       STA     LOSTFLG ;SHOW NO CARR. LOST
;CHECK FOR /A OPTION ON COMMAND - REQUEST TO
;GO IMMEDIATELY INTO ANSWER MODE
       LXI     H,FCB+1 ;TO OPTION
       MOV     A,M
       CPI     '/'     ;OPTION?
       JNZ     NOSLASH
;GOT AN OPTION - VALIDATE IT
       INX     H       ;TO OPTION BYTE
       MOV     A,M     ;GET IT
       CPI     'A'     ;ANSWER?
       JZ      ANSWER
;NO OPTION, OR INVALID ONE
NOSLASH EQU     $+OFFSET
       CALL    CARCK   ;SIGNED OFF W/THIS PROG?
       JC      HANGUP  ;NOBODY THERE
       CALL    ILPRT   ;PRINT THIS MSG:
       DB      CR,LF,'GOOD BYE, CALL AGAIN'
       DB      CR,LF,CR,LF,0
       CALL    UNPATCH ;UNDO BIOS PATCHES
;
;
;       NOBODY THERE, OR WE ARE DONE, SO HANG UP
;
HANGUP  EQU     $+OFFSET
       LXI     SP,STACK ;SET UP LOCAL STACK
       sub     a       ;force user 0, disk A
       sta     4       ;at next sign-in
       IF      CPM2
       mvi     e,0     ;need to set user 0 in bdos
       mvi     c,32    ;so that open succeeds on welcome file
       call    bdos
       ENDIF
       mvi     e,0
       mvi     c,14    ;also need drive A default
       call    bdos
       mvi     a,0C3H  ;reset boot trap/control-c trap
       sta     0
;
;CLEAR DTR CAUSING PHONE TO HANG UP
       MVI     A,P3CLEAR ;CLEAR..
       OUT     CPORT   ;..DTR
;
;       AWAIT RINGING
;
RINGWT  EQU     $+OFFSET
;CHECK LOCAL KEYBOARD FOR CTL-C EXIT REQUEST.
;NOTE: MUST DO DIRECT INPUT BECAUSE CBIOS PATCHES
;ARE NOT DONE UNTIL CALL COMES IN.
       IN      CONDATA ;CHECK LOCAL KBD
       ANI     7FH     ;STRIP PARITY BIT
       CPI     'C'-40H ;CONTROL C?
       JZ      0       ;YES, --EXIT-- TO CP/M
;
RINGW2  EQU     $+OFFSET
       IN      RPORT   ;GET THE STATUS
       ANI     P2RDET  ;RINGING?
       JNZ     RINGWT  ;NO, WAIT
;
;THE PHONE IS RINGING, NOW WAIT UNTIL RING IS FINISHED
ENDRING EQU     $+OFFSET
       CALL    DELAY   ;.1 SEC DELAY FOR DEBOUNCE
       IN      RPORT   ;GET STATUS
       ANI     P2RDET  ;STILL RINGING?
       JZ      ENDRING ;WAIT UNTIL RING FINISHED
;
       IF      CALLBAK ;NEXT ROUTINES IMPLEMENT CALLBACK
;
;      THIS ROUTINE MINIMIZES THE COMPUTER'S INTERFERENCE
;      WITH NORMAL HOUSEHOLD PHONE USE BY HAVING COMPUTER
;      FOLK DIAL, LET THE PHONE RING ONCE, HANG UP AND
;      THEN DIAL AGAIN.  WHEN THE PHONE RINGS ONLY ONCE IT
;      ALERTS THE COMPUTER WHICH THEN WAITS FOR AND ANSWERS
;      ANY RING WHICH OCCURS WITHIN THE NEXT 40 SECONDS.
;
       MVI     L,45    ;DELAY 4.5 SECONDS FOR NEXT RING
WAITNX  EQU     $+OFFSET
       CALL    DELAY   ;WAIT .1 SECONDS
       DCR     L       ;MORE TO GO?
       JNZ     WAITNX  ;YES?...LOOP
       IN      RPORT   ;GET THE STATUS
       ANI     P2RDET  ;RINGING AGAIN?
       JNZ     EXPECT  ;NO?...ITS FOR ME!
;CALL NOT FOR COMPUTER - WAIT UNTIL RINGING DONE, THEN RESET
WAITNR  EQU     $+OFFSET
       MVI     L,100   ;WAIT FOR 10 SECS NO RINGING
WAITNRL EQU     $+OFFSET
       CALL    DELAY   ;DELAY .1 SECONDS
       IN      RPORT   ;GET THE STATUS
       ANI     P2RDET  ;STILL RINGING?
       JZ      WAITNR  ;YES, WAIT 10 MORE SECONDS
       DCR     L       ;NO RING, MAYBE WE'RE DONE
       JNZ     WAITNRL ;NO, LOOP SOME MORE
       JMP     HANGUP
;
EXPECT  EQU     $+OFFSET
       LXI     H,400   ;40 SECONDS TO REDIAL
RELOOK  EQU     $+OFFSET
       IN      RPORT
       ANI     P2RDET  ;RINGING AGAIN?
       JZ      ANSWER
       CALL    DELAY
       DCX     H
       MOV     A,H
       ORA     L
       JNZ     RELOOK
       JMP     HANGUP
;
       ENDIF           ;END OF CALLBACK ROUTINES
;
;SETUP MODEM
ANSWER  EQU     $+OFFSET
       MVI     A,P0ANSW ;TURN ON
       OUT     CPORT   ;..DTR
       MVI     E,20
ANSWR1  EQU     $+OFFSET
       CALL    DELAY   ;GIVE TIME TO TURN ON
       DCR     E
       JNZ     ANSWR1
       MVI     A,P0110
       OUT     TPORT   ;ANSWER PHONE
       CALL    DELAY   ;GIVE TIME FOR ANSWER
       IN      CONDATA ;CLEAR LOCAL KBD PORT
       IN      DPORT   ;CLEAR MODEM PORT
       IN      DPORT   ;MAKE SURE ITS CLEAR
;OUTPUT VALUE ALLOWING MODEM TO HANG UP ON
;LOSS OF CARRIER
       CALL    CARCK   ;LOOK FOR CARRIER
       JC      HANGUP  ;AWAIT ANOTHER CALLER
;NOW TEST INPUT FOR BAUD RATE
       CALL    PATCH    ;PATCH JMP TABLE
       CALL    TSTBAUD  ;SEE IF BAUD = 110
       JZ      WELCOME  ;YES, EXIT
       MVI     A,P0NORM ;SET FOR 1 STOP BIT, ETC.
       OUT     TPORT
       MVI     A,B300+P0ANSW ;SET DIVISOR
       OUT     CPORT    ;.. TO 300 RATE
       CALL    TSTBAUD  ;SEE IF BAUD = 300
       JZ      WELCOME  ;YES, EXIT
       CALL    UNPATCH  ;RESTORE ORIG BIOS JMP TBL
       JMP     ANSWER   ;TEST MORE - INVALID BAUD RATE
;
;WELCOME TO THE SYSTEM
;
WELCOME EQU     $+OFFSET
;
GETNULL EQU     $+OFFSET
       CALL    ILPRT   ;PRINT THIS MSG:
       DB      CR,LF
       DB      'HOW MANY NULLS DO YOU NEED? ',0
       CALL    MINPUT  ;GET VALUE
       MOV     C,A
       CALL    MOUTPUT ;ECHO CHAR
       MOV     A,C
       CPI     '0'
       JC      GETNULL ;BAD, RETRY
       CPI     '9'+1
       JNC     GETNULL ;BAD
       SUI     '0'     ;MAKE BINARY
       STA     NULLS   ;SAVE COUNT
GETULC  EQU     $+OFFSET
       CALL    ILPRT
       DB      CR,LF,'CAN YOUR TERMINAL DISPLAY LOWER CASE? ',0
       MVI     A,20H
       STA     ULCSW   ;FORCE CASE CONVERSION FOR NOW
       CALL    MINPUT  ;GET Y OR N
       MOV     C,A
       CALL    MOUTPUT ;ECHO
       CPI     'N'
       JZ      DONEOPT ;WE'RE ALREADY SET UP FOR NO LOWER CASE
       CPI     'Y'
       JNZ     GETULC  ;WASN'T Y OR N...RE-ASK
       SUB     A
       STA     ULCSW   ;SET FLAG FOR NO CONVERSION
DONEOPT EQU     $+OFFSET
       CALL    ILPRT
       DB      CR,LF,0
;PRINT THE WELCOME FILE
       LXI     H,WELFILN ;SOURCE
       LXI     D,FCB   ;DESTINATION
       MVI     B,16    ;LENGTH
       CALL    MOVE    ;MOVE THE NAME
;SET DMA ADDR TO 80H
       LXI     D,80H
       MVI     C,STDMA
       CALL    BDOS
;OPEN THE WELCOME FILE
       LXI     D,FCB
       MVI     C,OPEN
       CALL    BDOS
;DID IT EXIST?
       INR     A       ;A=> 0 MEANS "NO"
       JZ      PASSINT ;NO WELCOME FILE
;GOT A FILE, TYPE IT
       XRA     A       ;GET 0
       STA     FCBRNO  ;ZERO RECORD #
       LXI     H,100H  ;GET INITIAL BUFF POINTER
;TYPE THE WELCOME FILE
WELTYLP EQU     $+OFFSET
       CALL    RDBYTE  ;GET A BYTE
       CPI     1AH     ;EOF?
       JZ      PASSINT ;YES, DONE
       MOV     C,A     ;SETUP FOR TYPE
       CALL    MOUTPUT ;TYPE THE CHAR
       CALL    MSTAT   ;CHECK FOR..
       ORA     A       ;CHAR TYPED?
       JZ      WELTYLP ;..NO, LOOP
       CALL    MINPUT  ;..YES, GET CHAR
       CPI     'C'-40H ;CTL-C?
       JNZ     WELTYLP ;..NO, LOOP UNTIL EOF
;
;GET THE PASSWORD
;
PASSINT EQU     $+OFFSET
       IF      PSWDREQ
       MVI     D,5     ;5 TRIES AT PASSWORD
PASSINP EQU     $+OFFSET
       CALL    ILPRT
       DB      CR,LF,'ENTER PASSWORD: ',0
       LXI     H,PASSWD ;POINT TO PASSWORD
       MVI     E,0     ;NO MISSED LETTERS
       IN      DPORT   ;CLEAR OUT GARBAGE
PWMLP   EQU     $+OFFSET
       CALL    MINPUT  ;GET A CHAR
       CPI     'U'-40H ;CTL-U?
       JZ      PASSINP ;YES, RE-GET IT
       CPI     60H     ;LOWER CASE?
       JC      NOTLC   ;NO,
       ANI     5FH     ;MAKE UPPER CASE ALPHA
NOTLC   EQU     $+OFFSET
       CMP     M       ;MATCH PASSWORD?
       JZ      PWMAT   ;..YES
       MVI     E,1     ;..NO, SHOW MISS
       CPI     CR      ;C/R?
       JNZ     PWMLP   ;..NO, WAIT FOR C/R
;PASSWORD DIDN'T MATCH
PWNMAT  EQU     $+OFFSET
       CALL    ILPRT
       DB      '++INCORRECT++',CR,LF,0
       DCR     D       ;MORE TRIES?
       JNZ     PASSINP ;YES
       JMP     BADPASS ;NO, GO HANG UP
;CHARACTER MATCHED IN PASSWORD
PWMAT   EQU     $+OFFSET
       INX     H       ;TO NEXT CHAR
       CPI     CR      ;END?
       JNZ     PWMLP   ;..NO, LOOP
;END OF PASSWORD.  ANY MISSED CHARS?
       MOV     A,E     ;GET FLAG
       ORA     A
       JNZ     PWNMAT  ;NOT RIGHT
       ENDIF
;PASSWORD CORRECT
       CALL    ILPRT
       DB      CR,LF,'BOOTING SYSTEM...',0
       IF      WBCMND
       MVI     A,0FFH  ;SET WARM BOOT FLAG (TESTED AT MOUTPUT)
       STA     WBFLAG
       ENDIF
       JMP     VWARMBT ;GO LOAD CP/M
;
;TSTBAUD ATTEMPTS TO READ A LF OR CR, RETURNS WITH
;ZERO FLAG IF THE CHARACTER READ IS ONE OF THESE TWO.
;
TSTBAUD EQU     $+OFFSET
       CALL    MINPUT  ;GET CHARACTER FROM MODEM
       CPI     CR      ;IF A CARRIAGE RETURN...
       RZ              ;.. RETURN
       CPI     LF      ;IF A LINEFEED...
       RET             ;RET ZERO FLAG, ELSE NOT ZERO
;
;       LOSS OF CONNECTION TEST
;
;THIS ROUTINE AUTOMATICALLY HANGS UP THE
;PHONE AFTER LOSS OF CARRIER
;
;THIS ROUTINE CHECKS IF CARRIER IS STILL
;PRESENT, AND IF SO, RETURNS WITH CARRY CLEAR.
;IF NOT, IT WAITS FOR CARRIER FOR 15 SECS.
;IF THIS TIME RUNS OUT, IT RETURNS WITH CARRY SET.
;
;
CARCK   EQU     $+OFFSET
       PUSH    D
       MVI     E,150
CARCK1  EQU     $+OFFSET
       IN      RPORT   ;LOOK AT STATUS
       ANI     P2CTS   ;GET CARRIER DETECT BIT
       JNZ     CARCK2  ;IF CARRIER ON, continue with checks
;LOOP UNTIL EITHER TIME EXHAUSTED, OR
;CARRIER RETURNS
       CALL    DELAY   ;KILL .1 SEC
       DCR     E       ;COUNT DOWN TIME
       JNZ     CARCK1
       STC
       pop     d
       ret
CARCK2  EQU     $+OFFSET
       lda     4               ;check disc/user #
       ani     0fh             ;isolate drive
       cpi     max$drive+1     ;valid drive?
       jc      carck3          ;yes, skip this junk
       lda     4               ;restore whole login byte
       ani     0f0h            ;retain user #
       sta     4               ;force drive to A
       jmp     0               ;force warm boot (punishment?)
carck3  equ     $+offset
       if      cpm2            ;skip this junk if cp/m 1.x
       lda     4               ;re-fetch login byte
       ani     0f0h            ;isolate user #
       cpi     max$user*16+1   ;valid user ?
       jc      carck4          ;yes, don't change
       lda     4               ;refresh login byte again
       ani     0fh             ;keep drive, zero user
       sta     4
       jmp     0               ;force warm boot
       endif                   ;end of cp/m 2.x dependant stuff
CARCK4  EQU     $+OFFSET
       ora     a               ;clear carry
       POP     D
       RET
;
;       .1 SEC DELAY ROUTINE
;
DELAY   EQU     $+OFFSET
       PUSH    B
       IF      FASTCLK
       LXI     B,16667 ;.1 SEC COUNT FOR 4MHZ SYS CLOCK
       ENDIF
       IF      NOT FASTCLK
       LXI     B,8334  ;.1 SEC COUNT FOR 2MHZ SYS CLOCK
       ENDIF
DELAY1  EQU     $+OFFSET
       DCX     B       ;COUNT DOWN DELAY TIME
       MOV     A,B
       ORA     C
       JNZ     DELAY1  ;CONTINUE COUNTING TILL DONE
       POP     B
       RET
;
;
KDELAY  EQU     $+OFFSET
       PUSH    B
       IF      FASTCLK
       LXI     B,8334  ;.05 SEC COUNT FOR 4MHZ
       ENDIF
       IF      NOT FASTCLK
       LXI     B,4167  ;.05 SEC COUNT FOR 2MHZ
       ENDIF
       JMP     DELAY1
;
;
;PATCH IN THE NEW JMP TABLE (SAVING THE OLD)
;
PATCH   EQU     $+OFFSET
       CALL    TBLADDR ;CALC HL= CP/M JMP TABLE
       LXI     D,VCOLDBT ;POINT TO SAVE LOCATION
       CALL    MOVE    ;MOVE IT
;NOW MOVE NEW JMP TABLE TO CP/M
       CALL    TBLADDR ;CALC HL=CP/M'S JMP TABLE
       XCHG            ;MOVE TO DE
       LXI     H,NEWJTBL ;POINT TO NEW
       CALL    MOVE    ;MOVE IT
       RET
;
UNPATCH EQU     $+OFFSET
       CALL    TBLADDR ;HL=CP/M'S JMP TABLE
       XCHG            ;MOVE TO DE
       LXI     H,VCOLDBT ;GET
SAVED TABLE
       CALL    MOVE    ;MOVE ORIG BACK
       RET
;
;CALCULATE HL=CP/M'S JUMP TABLE, B=LENGTH
;
TBLADDR EQU     $+OFFSET
       LHLD    1       ;GET BIOS POINTER
       DCX     H       ;..SKIP
       DCX     H       ;..TO
       DCX     H       ;..COLD BOOT
;
       IF      NOT PRINTER
       MVI     B,18    ;BYTES TO MOVE
       ENDIF
;
       IF      PRINTER ;RETAIN LIST DEVICE?
       MVI     B,15    ;DON'T MOVE LISTER JUMP
       ENDIF
;
       RET
;
;MOVE (HL) TO (DE), LENGTH IN (B)
;
MOVE    EQU     $+OFFSET
       MOV     A,M     ;GET A BYTE
       STAX    D       ;PUT AT NEW HOME
       INX     D       ;BUMP POINTERS
       INX     H
       DCR     B       ;DEC BYTE COUNT
       JNZ     MOVE    ;IF MORE, DO IT
       RET             ;IF NOT,RETURN
;
;COMMON ROUTINE TO CHECK FOR CARRIER LOST,
;CALLED FROM CONSOLE STATUS, AND CONSOLE OUT
;
CHECK   EQU     $+OFFSET
       CALL    CARCK   ;SEE IF CARRIER STILL ON
       RNC             ;ALL OK
;CARRIER IS LOST.  TYPE MESSAGE SO LOCAL CONSOLE
;       SHOWS THE REASON
BADPASS EQU     $+OFFSET ;COME HERE ON BAD PASSWORD
       MVI     A,1     ;SHOW CARRIER LOST SO
       STA     LOSTFLG ;..WE WON'T CK AGAIN
       LXI     SP,STACK ;ENSURE VALID STACK
       CALL    ILPRT
       DB      CR,LF
       DB      '++CARRIER LOST++'
       DB      CR,LF,'   ',0
       CALL    UNPATCH ;RESTORE ORIG BIOS JMP TBL
       XRA     A       ;CLEAR OUT CARRIER..
       STA     LOSTFLG ;..LOST FLAG
       JMP     HANGUP
;
;READBYTE ROUTINE - USED TO READ THE
;       WELCOME FILE
;
RDBYTE  EQU     $+OFFSET
       MOV     A,H     ;TIME TO READ?
       ORA     A       ;..IF AT 100H
       JZ      NORD    ;NO READ REQ'D
;HAVE TO READ A SECTOR
       LXI     D,FCB
       MVI     C,READ
       CALL    BDOS
       ORA     A       ;OK?
       MVI     A,1AH   ;FAKE UP EOF
       RNZ             ;RET EOF IF BAD
       LXI     H,80H
NORD    EQU     $+OFFSET
       MOV     A,M     ;GET CHAR
       INX     H       ;TO NEXT
       RET
;
;KEYBOARD/MODEM STATUS TEST ROUTINE
;
MSTAT   EQU     $+OFFSET
;
       IF      DUAL$IO ;WANT LOCAL CONSOLE?
       CALL    CONSTAT ;GET LOCAL STATUS
       ORA     A
       RNZ             ;RET IF LOCAL CHAR
       ENDIF
;
       IN      TPORT   ;GET STATUS
       ANI     P0DAV   ;DATA AVAILABLE?
       RZ              ;RETURN IF NOT READY
       IN      TPORT   ;GET STATUS AGAIN
       ANI     18H     ;CHECK FRAMING AND OVERRUN BITS
       JZ      MSTAT1  ;NO ERRORS...LEGIT CHARACTER
       IN      DPORT   ;SWALLOW CHARACTER (CLEARS P0DAV)
       SUB     A       ;RETURN FALSE (0)
       RET
MSTAT1  EQU     $+OFFSET
       MVI     A,0FFH  ;SHOW READY
       ORA     A
       RET
;
;MODEM INPUT FUNCTION, CHECKS LOCAL CONSOLE FIRST
;
MINPUT  EQU     $+OFFSET
       IF      TIMEOUT
       PUSH    H               ;INITIALIZE TIMEOUT COUNTER
       LXI     H,TOVALUE
       SHLD    TOCNT
       POP     H
       ENDIF
MINPUT1 EQU     $+OFFSET
       LDA     LOSTFLG ;KNOWN LOSS..
       ORA     A       ;..OF CARRIER?
       CZ      CHECK   ;CARRIER STILL ON?
;
       CALL    MSTAT   ;ANYTHING?
       ORA     A
;
       IF      NOT TIMEOUT
       JZ      MINPUT  ;LOOP TILL CHAR RCD
       ENDIF
;
       IF      TIMEOUT
       JNZ     MINPUT2
       CALL    KDELAY          ;KILL .05 SEC
       PUSH    H
       LHLD    TOCNT           ;KNOCK DOWN TIMEOUT COUNTER
       DCX     H
       SHLD    TOCNT
       MOV     A,H
       ORA     L
       POP     H
       JNZ     MINPUT1         ;STILL MORE TIME...KEEP TRYING
       CALL    ILPRT
       DB      '+++INPUT TIMED OUT',7,7,0
       JMP     NOSLASH
       ENDIF
;
;GOT CHAR - SEE WHICH PORT
;
MINPUT2 EQU     $+OFFSET
       IF      DUAL$IO ;BOTH LOCAL AND REMOTE?
       CALL    CONSTAT ;CHECK LOCAL CONSOLE
       ORA     A       ;CHAR?
       JNZ     CONIN   ;..YES, READ IT, RET.
       ENDIF
;
;LOCAL CONSOLE WASN'T READY, SO READ MODEM
       IN      DPORT   ;GET DATA BYTE
       ANI     7FH     ;DELETE PARITY
       JZ      MINPUT  ;IGNORE NULLS
       IF      WBCMND
       CPI     3       ;CONTROL-C?
       RNZ             ;NO, PASS IT THRU
       LDA     0       ;SEE IF WARM BOOT DISABLED
       CPI     0C3H    ;JMP MEANS WARM BOOT OK
       MVI     A,3     ;SO RETURN CONTROL-C AS TYPED
       RZ
       SUB     A       ;ELSE CONVERT IT TO A NULL
       ENDIF
       RET
;
;MODEM OUTPUT ROUTINE.  OUTPUTS TO MODEM,
;THEN TO LOCAL CONSOLE
;
MOUTPUT EQU     $+OFFSET
;IF WE ALREADY KNOW CARRIER IS LOST,
;DON'T CHECK FOR IT AGAIN
       LDA     LOSTFLG ;KNOWN LOSS OF CARRIER?
       ORA     A
       CZ      CHECK   ;CARRIER STILL ON?
       IF      WBCMND
       LDA     WBFLAG  ;IS THIS FIRST WARM BOOT SINCE SIGNON?
       ORA     A       ;IF NOT, SKIP THIS STUFF
       JZ      MOUTP1
       SUB     A       ;TURN OFF FLAG FOR NEXT TIME
       STA     WBFLAG
       MVI     A,0CDH  ;DISABLE WARM BOOT
       STA     0
       LDA     7       ;GET BDOS ADDRESS
       SUI     8       ;CCP IS 2K DOWN FROM BDOS
       MOV     H,A
       MVI     L,0     ;HL NOW CONTAINS CCP ENTRY ADDRESS
       PUSH    H       ;SAVE FOR LATER
       MVI     L,7     ;POINT TO BYTE COUNT IN CCP COMMAND BUFFER
       XCHG            ;MAKE IT DEST POINTER (DE)
       LXI     H,WBCSTR        ;POINT TO COMMAND STRING TO DROP IN
       MOV     B,M     ;GET BYTE COUNT
       INR     B       ;UP BY 2 TO INCLUDE COUNT AND TRAILING NULL
       INR     B
       CALL    MOVE    ;DROP IT IN TO CCP
       MVI     A,WBUSER*16+WBDRV       ;SET LOCATION 4 (USER/DRIVE)
       STA     4
       MOV     C,A     ;ALSO PASS IT TO CCP
       POP     H       ;GET BACK CCP ENTRY ADDRESS
       PCHL            ;GO THERE
MOUTP1  EQU     $+OFFSET
       ENDIF
       IN      TPORT   ;READ MODEM STATUS
       ANI     P0TBMT  ;XMIT BUFF EMPTY?
       JZ      MOUTPUT ;LOOP IF NOT READY
       MOV     A,C     ;GET CHAR
       ANI     7FH     ;STRIP HIGH BIT
       CPI     60H     ;CHECK FOR LOWER CASE
       JC      MOUTP2  ;SKIP IF NOT LC
       CPI     7FH     ;CHECK FOR RUBOUT
       JZ      MOUTP2
       PUSH    H
       LXI     H,ULCSW ;SUBTRACT EITHER 20H OR 0
       SUB     M
       POP     H
       MOV     C,A     ;FORCE ON LOCAL AS WELL AS REMOTE
MOUTP2  EQU     $+OFFSET
       OUT     DPORT   ;OUTPUT TO MODEM
;
       IF      DUAL$IO ;TO LOCAL ALSO?
       CALL    CONOUT  ;SEND TO REGULAR BIOS
       ENDIF
;
;CHECK FOR NULLS
;
       CPI     LF      ;TIME FOR NULLS?
       RNZ             ;NO, RETURN
;SEND NULLS IF REQUIRED
       LDA     NULLS   ;GET COUNT
       ORA     A       ;ANY?
       RZ              ;..NO
       PUSH    B
       MOV     B,A     ;SAVE COUNT
       MVI     C,0     ;0 IS A NULL
NULLP   EQU     $+OFFSET
       CALL    MOUTPUT ;TYPE A NULL
       DCR     B       ;MORE?
       JNZ     NULLP   ;..YES, LOOP
       POP     B
       RET
;
;BOOT TRAP - BECOMES DISCONNECT IF JMP AT 0 HAS BEEN ALTERED
;
MBOOT   EQU     $+OFFSET
       LDA     0       ;LOOK AT OPCODE
       CPI     0C3H    ;IS IT STILL JMP?
       JZ      VWARMBT ;YES, ALLOW IT
       JMP     NOSLASH ;NO, DISCONNECT
;
;       INLINE PRINT ROUTINE
;       CALL ILPRT
;       DB      'MSG',0
;
ILPRT   EQU     $+OFFSET
       XTHL            ;SAVE HL, GET MSG
       PUSH    B       ;SAVE
ILPLP   EQU     $+OFFSET
       MOV     C,M     ;GET CHAR
       CALL    MOUTPUT ;OUTPUT IT
       INX     H       ;POINT TO NEXT
       MOV     A,M     ;TEST
       ORA     A       ;..FOR END
       JNZ     ILPLP
       POP     B       ;RESTORE
       XTHL            ;RESTORE HL, RET ADDR
       RET             ;RET PAST MSG
;
;ACCESS PASSWORD (ENDS IN C/R)
;
PASSWD  EQU     $+OFFSET
       DB      '******' ;THE PASSWORD ITSELF
       DB      CR      ;END OF PASSWORD
;ALLOW ROOM FOR BIGGER PASSWORD TO BE
;       PATCHED IN
       DB      0,0,0,0,0,0,0,0,0,0,0,0,0
;
;WARM BOOT COMMAND STRING
;
       IF      WBCMND
WBCSTR  EQU     $+OFFSET
       DB      WBCEND-WBCSTR-2
       DB      'RIBBS'
       DB      0
WBCEND  EQU     $+OFFSET
       ENDIF
;
;THIS AREA IS USED FOR VECTORING CALLS TO THE
;USER'S CBIOS, BUT SAVING THE REGISTERS FIRST
;IN CASE THEY ARE DESTROYED.
;
CONSTAT EQU     $+OFFSET
       PUSH    B
       PUSH    D
       PUSH    H
       CALL    VCONSTAT
       POP     H
       POP     D
       POP     B
       RET
;
CONIN   EQU     $+OFFSET
       PUSH    B
       PUSH    D
       PUSH    H
       CALL    VCONIN
       POP     H
       POP     D
       POP     B
       RET
;
CONOUT  EQU     $+OFFSET
       PUSH    B
       PUSH    D
       PUSH    H
       CALL    VCONOUT
       POP     H
       POP     D
       POP     B
       RET
;
;       THIS IS THE JMP TABLE WHICH IS COPIED
;       ON TOP OF THE ONE POINTED TO BY
;       LOCATION 1 IN CP/M
;
NEWJTBL EQU     $+OFFSET
       JMP     MBOOT   ;BOOT TRAP
       JMP     MBOOT   ;BOOT TRAP
       JMP     MSTAT   ;MODEM STATUS TEST
       JMP     MINPUT  ;MODEM INPUT ROUTINE
       JMP     MOUTPUT ;MODEM OUTPUT ROUTINE
       RET             ;DUMMY LIST DEVICE
       NOP
       NOP
;
WELFILN EQU     $+OFFSET
       DB      0,'WELCOME    ',0,0,0,0
;WELCOME FILE NAME ^^^^^^^^^^^
;
NULLS   EQU     $+OFFSET
       DB      5
WBFLAG  EQU     $+OFFSET
       DB      0
TOCNT   EQU     $+OFFSET
       DW      0
ULCSW   EQU     $+OFFSET
       DB      0
;
PEND    EQU     $+OFFSET ;END OF RELOCATED CODE
;
;KEEP TRACK OF LOST CARRIER WHEN TYPING
;"++CARRIER LOST++" SO WE DON'T LOOP
;
LOSTFLG EQU     $+OFFSET
       DS      1
;
;SAVE THE CP/M JUMP TABLE HERE
;
VCOLDBT EQU     $+OFFSET
       DS      3
VWARMBT EQU     $+OFFSET
       DS      3
VCONSTAT EQU    $+OFFSET
        DS     3
VCONIN   EQU    $+OFFSET
        DS     3
VCONOUT  EQU    $+OFFSET
        DS     3
VLISTOUT EQU    $+OFFSET
        DS     3
;
       DS      60
STACK   EQU     $+OFFSET        ;LOCAL STACK
;
WRCON   EQU     2
OPEN    EQU     15
READ    EQU     20
STDMA   EQU     26
FCB     EQU     5CH
FCBRNO  EQU     FCB+32
;
       END