TITLE   'MEXPLUS overlay for SwitchCom Canadiana 212A modem V 1.3'

; (DELETE ABOVE TITLE LINE IF ASSEMBLING WITH ASM)

; SwitchCom Canadiana 212A overlay for MEXPLUS: revision 1.3 86/12/01

; This module adapts MEX or MEXPLUS for the SwitchCom Canadiana 212A
; modem.  Made by:  SwitchCom Manufacturing Inc
;                   100-10 Amber St., Markham, Ontario, Canada
;                   L3R 3A2                     Tel:  416-475-0296

; This overlay will work with any port overlay that terminates
; prior to 0B00H and will work with either MEX or MEXPLUS.

; 86/12/26  V1.3 - moved modem initialization code to front of
;                  dial code and added flag to make sure that
;                  modem is only initialized once
;                                                      - Ian Cottrell

; 86/06/01  V1.2 - added 'space bar return busy' code (thanks
;                  to Al Jewer).  This code allows the user to
;                  push the space bar if he knows that the
;                  called number is busy.  If he is dialing
;                  a series of numbers, MEX will advance to the
;                  next one.
;                                                       - Ian Cottrell

; 86/01/18  V1.1 - filter all but numerals from dial routine,
;                  tightened code, made compatible with MEXPLUS
;                                                       - Ian Cottrell

; 85/03/06  V1.0 - Written - based on MXO-SM11.ASM
;                                                       - Ian Cottrell
;                                                         Sysop
;                                                         Info Centre BBS
;                                                         Ottawa, Ont
;                                                         Canada
;                                                         613-952-2289

FALSE   EQU     0
TRUE    EQU     NOT FALSE


DISC    EQU     TRUE            ; <<== change to FALSE if you disc. with DTR
                               ;      always true for MEX 1.2 or greater

; System constants

NDISCV  EQU     015FH           ; New smart modem disconnect here
DIALV   EQU     0162H           ; Location of dial vector in port overlay
DISCV   EQU     0165H           ; Location of disconnect vector in port overlay
DIALOC  EQU     0B00H           ; Dialing code goes here
MEX     EQU     0D00H           ; "CALL MEX"
SMTABL: EQU     0D55H           ; Smart modem init, SSET, de-init

; The following are function codes for the MEX Service Processor

INMDM   EQU     255             ; Return char from mdm in A, CY=no char in 100ms
TIMER   EQU     254             ; Delay 100ms * reg B
TMDINP  EQU     253             ; B=# secs to wait for char, CY=no char
CHEKCC  EQU     252             ; Check for ^C from kbd, Z=present
SNDRDY  EQU     251             ; Test for modem send ready
RCVRDY  EQU     250             ; Test for modem receive ready
SNDCHR  EQU     249             ; Send a character to the modem (after SNDRDY)
RCVCHR  EQU     248             ; Recv a character from the modem (after RCVRDY)
ILP     EQU     240             ; Inline print
KSTAT   EQU     11              ; Keyboard status
KBDIN   EQU     01              ; Keyboard input


CR      EQU     13
LF      EQU     10
CTRLQ   EQU     'Q'-40H


       ORG     100H

       DB      0C3H            ; We're an 8080/Z-80 overlay

       ORG     DIALV           ; Overlay the dialing vector

       JMP     DIAL

        IF     DISC            ; If providing disconnect code,
       ORG     NDISCV          ;   overlay the vector

       JMP     DISCON
        ENDIF                  ; DISC

; This is the DIAL routine called by MEX to dial a digit. The digit
; to be dialed is passed in the A register.  Note that two special
; codes must be intercepted as non-digits: 254 (start dial sequence)
; and 255 (end-dial sequence).  Mex will always call DIAL with 254
; in the accumulator prior to dialing a number.  Mex will also call
; dial with 255 in A as an indication that dialing is complete. Thus,
; the overlay may use these values to "block" the number, holding it
; in a buffer until it is completely assembled (that's the scheme
; employed here ).

; After the 254-start-dial sequence, MEX will call the overlay with
; digits, one-at-a-time.  MEX will make no assumptions about the digits,
; and will send each to the DIAL routine un-inspected (some modems,
; like the Smartmodem, allow special non-numeric characters in the
; phone number, and MEX may make no assumptions about these.
; The Canadiana does not allow non-numberic characters, so they are
; filtered out by the overlay).

; After receiving the end-dial sequence (255) the overlay must take
; whatever end-of-dial actions are necessary *including* waiting for
; carrier at the distant end.  The overlay should monitor the keyboard
; during this wait (using the MEX keystat service call), and return
; an exit code to MEX in the A register, as follows:

;       0 - Carrier detected, connection established
;       1 - Far end busy (only for modems that can detect this condition)
;       2 - No answer (or timed out waiting for modem response)
;       3 - Keyboard abort (^C only: all others should be ignored)
;       4 - Error reported by modem

; <No other codes should be returned after an end-dial sequence>

; The overlay should not loop forever in the carrier-wait routine, but
; instead use either the overlay timer vector, or the INMDMV (timed 100
; ms character wait) service call routine.

; The DIAL routine is free to use any of the registers, but must return
; the above code after an end-dial sequence

       ORG     DIALOC

; First, check to see if modem has been initialized.  If not,
; initialize Canadiana by setting for numeric responses and
; turning off the echo.

DIAL:   PUSH    PSW             ; Save number
       LDA     INITFL          ; Get initialization flag
       ORA     A               ; Test
       JNZ     DIAL1           ; If done, skip init code
       LXI     H,ECHO          ; Else,point to message to set echo
       CALL    SCSEND          ; Send it to modem
ELOOP:  MVI     C,INMDM         ; Wait for response from modem
       CALL    MEX
       JNC     ELOOP           ; Get it all
       LXI     H,VERBOSE       ; Point to message to set verbose
       CALL    SCSEND          ; Send it to modem
VLOOP:  MVI     C,INMDM         ; Wait for response
       CALL    MEX
       JNC     VLOOP
       MVI     A,0FFH          ; Set initialization flag
       STA     INITFL

DIAL1:  POP     PSW             ; Restore number
       LHLD    DIALPT          ; Fetch pointer
       CPI     254             ; Start dial?
       JZ      STDIAL          ; Jump if so
       CPI     255             ; End dial?
       JZ      ENDIAL          ; Jump if so

; Not start or end sequence, must be a digit to be sent to the modem

       CPI     '0'             ; Only allow numbers
       RC                      ; If not a number, just ignore it
       CPI     '9'+1
       RNC
       MOV     M,A             ; Must be a number, so put it in buffer
       INX     H               ; Advance pointer
       SHLD    DIALPT          ; Stuff pointer
       RET                     ; All done

; Here on a start-dial sequence

STDIAL: LXI     H,DIALBF        ; Set up buffer pointer
       SHLD    DIALPT
       RET

; Here on an end-dial sequence

ENDIAL: MVI     M,CR            ; Stuff end-of-line into buffer
       INX     H               ;   followed by terminator
       MVI     M,0
       LXI     H,SCDIAL        ; Point to dialing string
       CALL    SCSEND          ; Send it
WAITSC: MVI     C,INMDM
       CALL    MEX             ; Catch any output from the modem
       JNC     WAITSC          ; Loop until no more characters

; The following loop waits for a result from the modem (up to
; 60 seconds; you may change this value in the following line).
; Note that the Canadiana has an internal 30 second timeout while
; waiting for a carrier on the other end.

RESULT: MVI     C,60            ; <<== Maximum time to wait for result (in secs)
SCWLP:  PUSH    B
       MVI     B,1             ; Check for a char, up to 1 sec wait
       MVI     C,TMDINP        ; Do timed input
       CALL    MEX
       POP     B
       JNC     SCTEST          ; If modem had a character, jump
       PUSH    B               ; Else, test for character from console
       MVI     C,KSTAT         ; Check for keypress
       CALL    MEX
       ORA     A
       POP     B
       JZ      SCNEXT          ; Jump if no keypress
       PUSH    B               ; Else, get character from keyboard
       MVI     C,KBDIN
       CALL    MEX
       POP     B
       CPI     'C'-40H         ; CTL-C?
       JNZ     SPTST           ; If not, go test for space
       MVI     A,3             ; Else, get abort code
SCOFF:  PUSH    PSW             ; Save it
       MVI     B,CR            ; Shut down the modem
       MVI     C,SNDCHR
       CALL    MEX
       POP     PSW             ; Restore return code
       RET

SPTST:  CPI     ' '             ; Space?
       JNZ     SCNEXT          ; If not, round the loop again
       MVI     A,1             ; Else, get busy code
       JP      SCOFF           ; Now go shut down modem

SCNEXT: DCR     C               ; Decrement counter
       JNZ     SCWLP           ; Continue if not done

; One minute with no modem response (or no connection)

SCTIMO: MVI     A,2             ; Timed out, so return timeout code
       RET

; Modem gave us a result, check it

SCTEST: ANI     7FH             ; Ignore any parity
       CALL    SCANAL          ; Test the result
       JC      RESULT          ; Go try again if unknown response
       MOV     A,B             ; A=result
       PUSH    PSW             ; Save it
SCTLP:  MVI     C,INMDM         ; Eat any additional chars from Candaiana
       CALL    MEX
       JNC     SCTLP           ; Until 100ms of quiet time
       POP     PSW             ; Return the code
       RET

SCANAL: MVI     B,0             ; Prep connect code
       CPI     '2'             ; 2=connect
       RZ
       INR     B               ; Prep busy cond; B=1
       INR     B               ; Prep no connect msg; B=2
       CPI     '3'             ; 3=no connect (from Canadiana)
       RZ

; Unknown response, return carry to caller.  But first,
; flush the unknown response line from the modem.

WTLF:   CPI     1               ; 'Dialing'?
       STC
       RZ                      ; End if so
       MVI     C,INMDM         ; No - get next char
       CALL    MEX
       JNC     WTLF            ; Unless busy, loop
       RET

; Following routine disconnects the modem using Canadiana
; codes.  All registers are available for this function.
; Nothing is returned to caller.

       IF      DISC

DISCON: MVI     B,20
       MVI     C,TIMER         ; Wait 2 seconds
       CALL    MEX
       LXI     H,SCATN         ; Send 3 Ctrl-Q's
       CALL    SCSEND
       PUSH    B
       MVI     B,5
DISCLP: MVI     C,TMDINP        ; Wait for response from modem
       CALL    MEX
       JNC     DISCLP          ; Wait til all done
       POP     B
       LXI     H,ECHRES        ; Reset echo
       CALL    SCSEND
ECHLP:  MVI     C,INMDM         ; Wait for next response
       CALL    MEX
       JNC     ECHLP
       LXI     H,VERRES        ; Reset verbose
       CALL    SCSEND
       XRA     A               ; Clear the init flag in case
       STA     INITFL          ;  we want to dial again
       RET

SCATN:  DB      CTRLQ,CTRLQ,CTRLQ,CR,LF,0       ; Disconnect
ECHRES: DB      'E=1',CR,LF,0                   ; Turn echo back on
VERRES: DB      'V=2',CR,LF,0                   ; Turn verbose back on
       ENDIF

;Canadiana Utility routing:  send string to modem

SCSEND: MVI     C,SNDRDY        ; Wait for modem ready
       CALL    MEX
       JNZ     SCSEND
       MOV     A,M             ; Fetch next character
       INX     H
       ORA     A               ; End?
       RZ                      ; Done if so
       MOV     B,A             ; No, position for sending
       MVI     C,SNDCHR        ; Send the character
       CALL    MEX
       JMP     SCSEND

; Data area

SCDIAL: DB      'D'
DIALBF: DS      52              ; 2 * 24 char max, + CR + NULL + slop
DIALPT: DS      2               ; Dial position pointer
VERBOSE:DB      'V=1',CR,LF,0   ; Set for numeric responses from modem
ECHO:   DB      'E=0',CR,LF,0   ; Turn echo off
INITFL: DB      0               ; Storage for init flag
                               ; must be 0 to start

NOTIMP: MVI     C,ILP
       CALL    MEX
       DB      CR,LF,'Not Implemented',CR,LF,0
JUSTRT: RET



       ORG     SMTABL          ; Table of smart modem vectors here

       DS      2
       DW      NOTIMP          ; SSET command (not implemented)
       DW      JUSTRT          ; Smart modem exit (do nothing)

       END