TITLE   'MODEM - PL/1 DC HAYES MODEM SUBROUTINES'
;       . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;       MODEM I/O AND CONTROL SUBROUTINES FOR DC HAYES MODEM
;       (ADAPTED FROM MICROMODEM 100 PROGRAM)
;       (ADAPTED AGAIN TO RUN WITH PL/1 ON AUGUST 30, 1980)
;       . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

;         ASSEMBLER STUFF
       NAME    'MODEM'
MODEM:  CSEG
FALSE:  EQU     0               ;TRUE/FALSE SUBSTITUTIONS
TRUE:   EQU     NOT FALSE

;         PORT ASSIGNMENTS

DATA    EQU     80H             ;DATA I/O PORT
STAT    EQU     DATA+1          ;STATUS PORT
MODE    EQU     DATA+2          ;MODE CONTROL PORT
CR1     EQU     DATA+1          ;CONTROL REG 1
CR2     EQU     DATA+2          ;CONTROL REG 2
CR3     EQU     DATA+3          ;CONTROL REG 3


;         BIT FUNCTIONS

;STATUS REGISTER
RRF     EQU     001H            ;RECEIVE REGISTER FULL
TRE     EQU     002H            ;TRANSMITTER HOLDING REGISTER EMPTY
PE      EQU     004H            ;PARITY ERROR
FE      EQU     008H            ;FRAMING ERROR
OE      EQU     010H            ;DATA OVERRUN ERROR
TMR     EQU     020H            ;TIMER STATUS
CD      EQU     040H            ;CARRIER PRESENT
RI      EQU     080H            ;NOT RING INDICATOR (LOW TRUE)

;CONTROL REGISTER 1 (CR1)
EPE     EQU     001H            ;EVEN PARITY ENABLE
LS1     EQU     002H            ;WORD LENGTH SELECT BIT 1
LS2     EQU     004H            ;WORD LENGTH SELECT BIT 2
SBS     EQU     008H            ;STOP BITS
PI      EQU     010H            ;PARITY INHIBIT
TMIE    EQU     020H            ;TIMER INTERRUPTS ENABLE

;CONTROL REGISTER 2 (CR2)
BRS     EQU     001H            ;BAUD RATE CONTROL
TXE     EQU     002H            ;TRANSMIT CARRIER ENABLE
MS      EQU     004H            ;MODE (0=ANSWER 1=ORIGINATE)
BRK     EQU     008H            ;SEND BREAK
ST      EQU     010H            ;SELF TEST
TIE     EQU     020H            ;TRANSMITTER INTERRUPT ENABLE
RIE     EQU     040H            ;RECEIVER INTERRUPT ENABLE
OH      EQU     080H            ;OFF-HOOK


;               * * *  RESET MODEM  * * *
MDMRST:
       PUBLIC  MDMRST
       XRA     A       ;ZERO CONTROL REGS.
       OUT     CR1
       OUT     CR2
       STA     MICR1   ;  AND MEMORY IMAGES OF REGISTERS.
       STA     MICR2
       RET

;               * * *  TRANSMIT CHAR  * * *
;       THIS ROUTINE SENDS A BYTE IN REGISTER C TO THE MODEM
;       TRANSMITTER.  ONLY REGISTER A IS DISTURBED.
MDMTXC:
       PUBLIC  MDMTXC
       CALL    PLICHR  ;GET THE CHAR TO OUTPUT.
MDMTXC$DIR:
       IN      STAT    ;GET MODEM STATUS.
       ANI     TXE     ;XMIT BUFFER EMPTY?
       JZ      MDMTXC$DIR ;...YES, LOOP.
       MOV     A,C     ;...NO, SEND THE BYTE.
       OUT     DATA
       RET

;               * * *  RECEIVE CHAR  * * *
;       THIS ROUTINE RECEIVES A BYTE FROM THE MODEM RECEIVER
;       IN REGISTER A.
MDMRXC:
       PUBLIC  MDMRXC
       IN      STAT    ;GET MODEM STATUS.
       ANI     RRF     ;RECV BUFFER EMPTY?
       JZ      MDMRXC  ;...YES, LOOP.
       IN      DATA    ;...NO, GET DATA.
       ANI     07FH    ;STRIP PARITY.
       RET

;               * * *  GET RECV STATUS  * * *
;       THIS ROUTINE RETURNS A=TRUE IF A RECEIVED CHARACTER
;       IS WAITING OR A=FALSE IF NOT.
MDMRXS:
       PUBLIC  MDMRXS
       IN      STAT    ;GET MODEM STATUS.
       ANI     RRF     ;RECV BUFFER EMPTY?
       MVI     A,FALSE
       RZ              ;...YES, RETURN A=FALSE.
       MVI     A,TRUE  ;...NO, RETURN A=TRUE
       RET

;               * * *  CHECK CARRIER STATUS  * * *
;       THIS ROUTINE RETURNS TRUE IF CARRIER IS PRESENT.
MDMCDS:
       PUBLIC  MDMCDS
       IN      STAT    ;GET MODEM STATUS.
       ANI     CD      ;CARRIER DETECTED?
       MVI     A,FALSE
       RZ              ;...NO.
       MVI     A,TRUE  ;...YES.
       RET

;               * * *  CHECK RING STATUS  * * *
;       THIS ROUTINE CHECKS FOR PHONE RINGING RETURNING
;       TRUE IF SO.
MDMRIS:
       PUBLIC  MDMRIS
       IN      STAT    ;GET MODEM STATUS.
       ANI     RI      ;PHONE RINGING?
       MVI     A,TRUE
       RZ              ;...YES.
       MVI     A,FALSE
       RET

;               * * *  CHECK FOR MODEM ERROR  * * *
;       THIS ROUTINE RETURNS THE MODEM ERROR FLAGS IN
;       REGISTER A.  A=0 IF NO ERRORS.  OTHERWISE,
;       BIT 0 = PARITY ERROR  BIT 1 = FRAMING ERROR
;       BIT 2 = OVERRUN ERROR
MDMERR:
       PUBLIC  MDMERR
       IN      STAT    ;GET MODEM STATUS.
       RAR             ;SHIFT BITS RIGHT 2 PLACES.
       RAR
       ANI     7       ;KILL UNWANTED BITS.
       RET

;               * * *  CARRIER CONTROL  * * *
;       THIS ROUTINE TURNS THE CARRIER ON/OFF ACCORDING
;       TO REGISTER C.  C=FALSE FOR OFF; C=TRUE FOR ON.
MDMCRC:
       PUBLIC  MDMCRC
       CALL    PLICHR  ;GET THE COMMAND.
MDMCRC$DIR:
       MOV     A,C
       ORA     A
       MVI     A,TXE   ;CARRIER CONTROL BIT.
       JZ      MDMCC2  ;CLEAR IT IF A=FALSE.
       JMP     MDMSC2  ;SET IT IF A=TRUE.

;               * * *  BAUD RATE CONTROL  * * *
;       THIS ROUTINE SETS THE BAUD RATE GIVEN BY REGISTER
;       C.  C=FALSE FOR 110; C=TRUE FOR 300.
MDMBDC:
       PUBLIC  MDMBDC
       CALL    PLICHR  ;GET THE COMMAND.
MDMBDC$DIR:
       MOV     A,C
       ORA     A
       MVI     A,BRS   ;BAUD RATE SELECT BIT.
       JZ      MDMCC2  ;CLEAR IT IF A=FALSE.
       JMP     MDMSC2  ;SET IT IF A=TRUE.

;               * * *  HOOK CONTROL  * * *
;       THIS ROUTINE SETS THE HOOK ON OR OFF ACCORDING
;       TO REGISTER C.  C=FALSE FOR ON (IDLE); C=TRUE
;       FOR OFF (ACTIVE).
MDMSWH:
       PUBLIC  MDMSWH
       CALL    PLICHR  ;GET THE COMMAND.
MDMSWH$DIR:
       MOV     A,C
       ORA     A
       MVI     A,OH    ;HOOK CONTROL BIT.
       JZ      MDMCC2  ;CLEAR IT IF A=FALSE.
       JMP     MDMSC2  ;SET IT IF A=TRUE.

;               * * *  GET HOOK STATUS  * * *
;       THIS ROUTINE RETURNS THE HOOK STATUS IN REGISTER
;       A.  A=TRUE FOR OFF-HOOK; A=FALSE FOR ON-HOOK.
MDMSHS:
       PUBLIC  MDMSHS
       LDA     MICR2   ;GET CR2.
       ANI     OH      ;MASK FOR OFF-HOOK.
       MVI     A,TRUE
       RNZ             ;...OFF-HOOK.
       MVI     A,FALSE ;...ON-HOOK.
       RET

;               * * *  MODE CONTROL  * * *
;       THIS ROUTINE SETS THE MODE (ANSWER OR ORIGINATE)
;       ACCORDING TO REGISTER C.  C=FALSE FOR ANSWER;
;       C=TRUE FOR ORIGINATE.
MDMMOD:
       PUBLIC  MDMMOD
       CALL    PLICHR  ;GET THE COMMAND.
MDMMOD$DIR:
       MOV     A,C
       ORA     A
       MVI     A,MS    ;MODE SELECT BIT.
       JZ      MDMCC2  ;CLEAR IT IF A=FALSE.
       JMP     MDMSC2  ;SET IT IF A=TRUE.

;               * * *  SELF-TEST CONTROL  * * *
;       THIS ROUTINE SETS THE SELF-TEST MODE
;       ACCORDING TO REGISTER C.  C=FALSE FOR NORMAL;
;       C=TRUE FOR SELF-TEST.
MDMSLT:
       PUBLIC  MDMSLT
       CALL    PLICHR  ;GET THE COMMAND.
MDMSLT$DIR:
       MOV     A,C
       ORA     A
       MVI     A,ST    ;SELF TEST BIT.
       JZ      MDMCC2  ;CLEAR IT IF A=FALSE.
       JMP     MDMSC2  ;SET IT IF A=TRUE.

;               * * *  SEND BREAK  * * *
;       THIS ROUTINE SENDS A BREAK CONSISTING OF A
;       SPACE (LOW TONE) FOR A NUMBER OF 50 MS IN-
;       TERVALS SPECIFIED BY REGISTER C.
MDMBRK:
       PUBLIC  MDMBRK
       CALL    PLICHR  ;GET # OF INTERVALS.
MDMBRK$DIR:
       PUSH    B
       MVI     C,2     ;WAIT 100 MS.
       CALL    MDMDLY$DIR
       POP     B
       PUSH    H       ;SET THE BREAK BIT.
       LXI     H,MICR2
       MVI     A,BRK
       ORA     M
       OUT     CR2
       CALL    MDMDLY$DIR ;DELAY (C)*50 MS.
       MVI     A,BRK   ;RESET BREAK BIT.
       CMA
       ANA     M
       OUT     CR2
       POP     H
       RET

;               * * *  SET PARITY  * * *
;       THIS ROUTINE SETS THE PARITY ACCORDING TO REG C.
;       C=0 FOR NO PARITY, C=1 FOR ODD PARITY AND C=2
;       FOR EVEN PARITY.
MDMPAR:
       PUBLIC  MDMPAR
       CALL    PLICHR  ;GET THE COMMAND.
MDMPAR$DIR:
       MOV     A,C     ;TEST FOR NO PARITY.
       ORA     A
       JZ      MDMPAF  ;TURN OFF PARITY.
       MVI     A,PI    ;CLEAR PARITY INHIBIT BIT.
       CALL    MDMCC1
       MOV     A,C     ;SET LSB FOR EVEN/ODD PARITY.
       RAR
       ANI     1
       MVI     A,EPE   ;CLEAR EPE IF ODD.
       JZ      MDMCC1
       JMP     MDMSC1  ;SET EPE IF EVEN.
MDMPAF:
       MVI     A,PI    ;INHIBIT PARITY.
       JMP     MDMSC1

;               * * *  SELECT WORD LENGTH  * * *
;       THIS ROUTINE SETS THE WORD LENGTH ACCORDING TO REG C.
;       C = NUMBER OF BITS (5,6,7,8)
MDMWLN:
       PUBLIC  MDMWLN
       CALL    PLICHR  ;GET THE COMMAND.
MDMWLN$DIR:
       MOV     A,C     ;REMOVE BIAS.
       SUI     5
       RC
       CPI     3+1
       CMC
       RC
       ADD     A       ;SHIFT LEFT 1 PLACE.
       MOV     C,A     ;SAVE RESULT.
       LDA     MICR1   ;GET MEMORY IMAGE.
       ANI     NOT LS1+LS2 ;CLEAR WORD LENGTH BITS.
       ORA     C       ;PUT IN NEW ONES.
       STA     MICR1   ;UPDATE MEMORY IMAGE.
       OUT     CR1     ;WRITE IT TO MODEM.
       RET

;               * * *  SET NUMBER OF STOP BITS  * * *
;       THIS ROUTINE SETS THE NUMBER OF STOP BITS ACCORDING
;       TO REGISTER C.  C = STOP BITS (1 OR 2)
MDMNST:
       PUBLIC  MDMNST
       CALL    PLICHR  ;GET THE COMMAND.
MDMNST$DIR:
       MOV     A,C     ;REMOVE BIAS.
       SUI     1
       RC
       CPI     1+1
       CMC
       RC
       ORA     A
       MVI     A,SBS   ;STOP BIT SELECT BIT.
       JZ      MDMCC1  ;CLEAR IT.
       JMP     MDMSC1

;               * * *  DELAY ROUTINE  * * *
;       THIS ROUTINE WAITS 50MS TIMES THE VALUE OF THE
;       REGISTER C.
MDMDLY:
       PUBLIC  MDMDLY
       CALL    PLICHR  ;GET THE TIME AMOUNT.
MDMDLY$DIR:
       MOV     A,C
       ORA     A       ;IF ZERO, DON'T WAIT.
       RZ
       PUSH    B
MDMDL1:
       OUT     CR3     ;START TIMER.
MDMDL2:
       IN      STAT    ;WAIT TILL TIMER BIT
       ANI     TMR     ;TIMES OUT.
       JZ      MDMDL2
       DCR     C       ;LOOP (C) TIMES.
       JNZ     MDMDL1
       POP     B
       RET

;               * * *  START 50MS TIMER  * * *
MDMSTM:
       PUBLIC  MDMSTM
       OUT     CR3
       RET

;               * * *  CHECK TIMER STATUS  * * *
MDMCTM:
       IN      STAT    ;GET MODEM STATUS.
       ANI     TMR     ;TIME OUT?
       MVI     A,TRUE
       RNZ             ;...YES.
       MVI     A,FALSE
       RET

;               * * *  GO OFF HOOK  * * *
;       THIS ROUTINE GOES OFF HOOK AND WAITS FOR A DIAL TONE.
;       MICROMODEM HAS NO DIAL TONE DETECTOR.  WE JUST GO OFF
;       HOOK, WAIT 2 SEC, ASSUME A DIAL TONE IS PRESENT AND
;       RETURN.
MDMDLT:
       PUBLIC  MDMDLT
       PUSH    B
       MVI     C,TRUE  ;GO OFF-HOOK.
       CALL    MDMSWH$DIR
       MVI     C,40    ;WAIT 2 SECONDS.
       CALL    MDMDLY$DIR
       POP     B
       RET

;               * * *  DIAL PULSE GENERATION  * * *
;       THIS ROUTINE GENERATES A DIALING PULSE FOR THE DIGIT
;       IN REGISTER C.  IF C=0 THEN 10 PULSES WILL BE
;       PRODUCED.  THE DIGIT IN THE C REGISTER MAY BE
;       BINARY OR ASCII.  AFTER THE DIGIT IS OUT-PULSED,
;       THIS ROUTINE WILL PAUSE FOR 600 MS FOR INTER-DIGIT
;       SPACING.
MDMPLS:
       PUBLIC  MDMPLS
       CALL    PLICHR  ;GET THE DIGIT.
MDMPLS$DIR:
       PUSH    B
       MOV     A,C     ;GET DIGIT.
       MVI     B,10    ;PRE-LOAD PULSE COUNTER
       ANI     00FH    ;KILL POSSIBLE ASCII BIAS.
       CPI     0       ;SEE IF WE'RE DOING 10 PULSES.
       JZ      MDMPL1
       MOV     B,A     ;PULSE COUNT TO B
MDMPL1:
       MVI     C,FALSE ;GO ON-HOOK.
       CALL    MDMSWH$DIR
       MVI     C,1     ;WAIT 50 MS.
       CALL    MDMDLY$DIR
       MVI     C,TRUE  ;GO OFF-HOOK AGAIN.
       CALL    MDMSWH$DIR
       MVI     C,1     ;DELAY 50 MS.
       CALL    MDMDLY$DIR
       DCR     B       ;LOOP FOR ALL PULSES.
       JNZ     MDMPL1
       MVI     C,11    ;WAIT 550 MS.
       CALL    MDMDLY$DIR
       POP     B
       RET

;               * * *  DIAL A NUMBER  * * *
;       THIS ROUTINE GENERATES ALL THE DIALING PULSES
;       FOR A NUMBER GIVEN AS A VARYING CHARACTER STRING.
;       THEREFORE, IT DIALS THE NUMBER.
MDMDLN:
       PUBLIC  MDMDLN
       CALL    MDMDLT  ;GET DIAL TONE.
       CALL    PLICHR  ;GET THE STRING LENGTH IN C.
       ORA     A       ;LENGTH=ZERO?
       RZ              ;...YES, RETURN.
       INX     H       ;BUMP TO BEGINNING OF STRING.
MDMDLN$LP:
       MOV     A,M     ;GET THE NEXT NUMBER.
       CPI     '*'     ;DELAY?
       JNZ     MDMDLN$NUM ;...YES.
       PUSH    B
       MVI     C,40    ;WAIT 2 SEC.
       CALL    MDMDLY$DIR
       POP     B
       JMP     MDMDLN$NXT ; GET NEXT CHAR.
MDMDLN$NUM:
       CPI     '9'+1   ;ASCII 0-9?
       JNC     MDMDLN$NXT ;...NO, SKIP IT.
       CPI     '0'
       JC      MDMDLN$NXT ;...NO, SKIP IT.
       PUSH    B       ;SAVE CURRENT COUNT.
       MOV     C,A     ;PULSE OUT THE NUMBER.
       CALL    MDMPLS$DIR
       POP     B
MDMDLN$NXT:
       INX     H       ;BUMP PTR.
       DCR     C       ;DECREMENT COUNT.
       RZ              ;RETURN IF NO MORE DIGITS.
       JMP     MDMDLN$LP ;GO PULSE ANOTHER DIGIT.

;               * * *  SET CR1  * * *
;       THIS ROUTINE SETS BITS IN CONTROL REGISTER
;       1.  REGISTER A HAS 1'S WHERE BITS ARE TO
;       BE SET.
MDMSC1:
       PUSH    H
       LXI     H,MICR1 ;OR IN BITS TO SET.
       ORA     M
       MOV     M,A     ;SAVE RESULT.
       OUT     CR1     ;SET THE MODEM.
       POP     H
       RET

;               * * *  CLEAR CR1 * * *
;       THIS ROUTINE CLEARS THE BITS IN CONTROL REGISTER
;       1.  REGISTER A HAS 1'S FOR THE BITS TO BE CLEARED.
MDMCC1:
       PUSH    H
       LXI     H,MICR1 ;MEMORY IMAGE OF CR1.
       CMA
       ANA     M       ;TURN OFF THE BIT.
       MOV     M,A     ;UPDATE MEMORY.
       OUT     CR1     ;UPDATE MODEM.
       POP     H
       RET

;               * * *  SET CR2  * * *
;       THIS ROUTINE SETS BITS IN CONTROL REGISTER
;       2.  REGISTER A HAS 1'S WHERE BITS ARE TO
;       BE SET.
MDMSC2:
       PUSH    H
       LXI     H,MICR2 ;OR IN BITS TO SET.
       ORA     M
       MOV     M,A     ;SAVE RESULT.
       OUT     CR2     ;SET THE MODEM.
       POP     H
       RET

;               * * *  CLEAR CR2 * * *
;       THIS ROUTINE CLEARS THE BITS IN CONTROL REGISTER
;       2.  REGISTER A HAS 1'S FOR THE BITS TO BE CLEARED.
MDMCC2:
       PUSH    H
       LXI     H,MICR2 ;MEMORY IMAGE OF CR2.
       CMA
       ANA     M       ;TURN OFF THE BIT.
       MOV     M,A     ;UPDATE MEMORY.
       OUT     CR2     ;UPDATE MODEM.
       POP     H
       RET

;               * * *  GET PL/I PARAMETER * * *
;       THIS ROUTINE GETS A PL/I PARAMETER ACCORDING TO
;       ITS STANDARD CALLING CONVENTIONS.
PLICHR:
       PUSH    D       ;SAVE DE.
       MOV     E,M     ;GET PARM PTR FROM 0(HL).
       INX     H
       MOV     D,M
       LDAX    D       ;GET THE ONE BYTE PARM.
       MOV     C,A     ;PUT IT IN (C).
       XCHG            ;PUT PARM PTR IN HL.
       POP     D
       RET

;               * * *  DATA AREA FOR MODEM I/O  * * *
       DSEG
MICR1:  DB      0       ;MEMORY IMAGE OF CONTROL REGISTER 1
MICR2:  DB      0       ;MEMORY IMAGE OF CONTROL REGISTER 2
       END