Title   'MEX overlay for the PMMI version 2.2'
;
; Ignore the Title error message if using ASM.
;
REV     equ     22              ; overlay revision number
LVL     equ     'c'             ; overlay level ( as in Revision 2.2a )
;
; MEX PMMI OVERLAY VERSION 1.0: written 04/27/84            by Ron Fowler
;
;  "   "      "       "    2.0: generally rewritten 9/18/84 by Fred M. Spinner
;  "   "      "       '    2.1: rewritten 09/27/84 by Bill Norris
;  "   "      "       "    2.2:     "     10/30/84 by Bill Norris
;
;
; Version 2.2 notes:            Added 'SET EXTRA' command.  This is a toggle
;                                 that allows you to see on the console when
;                                 certain overlay functions are invoked.
;                               Corrected a minor bug in the clear screen and
;                                 clear to end of screen functions.
;                               Added 15 to SET PPS command.
;                               Added SET ONLINE ( SET OFFHOOK synonym )
;                               OFF-HOOK and ON-HOOK have been changed from
;                                 scattered in-line calls to subroutines.
;                               Renamed SET FIDGET to SET ICD (intercall delay)
;                               Renamed SET IDG    to SET IDD (interdigit delay)
;                               The default PMMI base port was changed to 80h
;                                 in version 2.1 due to conflicts with several
;                                 disk controller boards.  If your PMMI is not
;                                 addressed at this port, just use the SET BASE
;                                 command (and CLONE) if you don't want to edit
;                                 and re-assemble this file.
;                               Speeded up keyboard abort (^X and ^C). ^S also
;                                 accepted here (during dialing of a number).
;                               Phone number '#' is replaced at dial time from
;                                 the keyboard.
;
;
; Version 2.1 notes:            Unifies scattered PMMI I/O; allows modification
;                                 of the PMMI base port with SET BASE command.
;                               No PMMI I/O is done in-line.  It is all done
;                                 using subroutine calls.  This should make all
;                                 modifications simpler (including future
;                                 changes to support MEX 2.x).
;                               Base port address is printed in signon message.
;                               SET FIDGET added (delay between calls).
;                               BASE/FIDGET changes are preserved through CLONEs.
;                               Uses BDOS 6 to force BUSY instead of using hard
;                               coded keyboard I/O for the CTRLX function. (See
;                               description in Version 2.0 notes below).
;
;
; Version 2.0 9/18/84:  Numerous bugs repaired.  The offhook command
; now actually takes the modem off hook.  The DTR bytes were swapped
; in 1.1 so that baud rates over 300 would barf.  This has been repaired
; The SET MODE command is no longer available.  Use SET ORIG, ORG, or
; ORIGINATE and SET ANS or ANSWER to switch modes.  The actual routine
; to switch modes has been re-written from scratch, also.  Overlay version
; message more verbose (better looking, also) now.  If your know your
; keyboard (terminal)'s Status and Data Port and also know the value
; (bit) to check for for keyboard input, you can also implement the CTRLX
; feature which is handy to have if you know a number is busy and you
; want to call the next, etc. without further delays.  The keyboard info
; goes under KSTAT, KPORT, KBIT, and KVAL which mean Keyboard/Terminal status
; port, Keyboard data port, AND bit to status input, and value to check for
; after status is ANDed, respectively.   Set CTRLX to YES if you have
; this information and want to use this feature, or set it to NO to use
; the standard MEX keyboard trap.
;
;                               Fred M. Spinner
;
;
; (V1.1) 05/17/84 (Jim Byram) : Small bugs repaired.  Answer-mode
; bit (ANBIT) and originate-mode bit (ORBIT) were reversed; fixed
; SET MODE help message.
;
; This is a MEX overlay file for the PMMI modem.  You can use it as
; a model for designing your own modem overlay (or you can use any
; existing MDM7 overlay, if available).
;
; If you use this as a template for writing your own overlay, and
; distribute it to others, please pare down these comments as much
; as possible (to keep the overlays small). I'll maintain this file
; with as many notes and references as possible, but this will hope-
; fully be the only "big" overlay.
;
; There are advantages to recoding your overlay to conform to the
; techniques presented here: MEX 2.0 will likely have a much simpler
; overlay structure; if you stick to the label names and coding
; suggestions used here, you'll easily be able to follow the overlay
; upgrade instructions when MEX 2.0 hits the streets. Also, you can
; make use of the MEX service processor to write a very versatile
; SET command (as done here).
;
; Note that all overlays may freely use memory up to 0CFFH.  If your
; overlay must work with the MEX Smartmodem overlay (MXO-SMxx.ASM),
; the physical modem overlay should terminate by 0AFFH.
;
; For purposes of example, this is a "full-featured" MEX overlay.  In
; practice, your overlay may be much simpler (all that is really re-
; quired is the modem I/O code; fancy stuff like the SET command, and
; even the disconnect routine, may be left open.  You will need DIAL
; code, though, unless your modem doesn't support autodialing).
;
;------------------------------------------------------------
;
; Misc equates
;
NO      equ     0
YES     equ     0FFh
TPA     equ     100h
CR      equ     13
LF      equ     10
BS      equ     8
TAB     equ     9
ESC     equ     27
CTRLX   equ     YES             ;YES, if use ^X for fake "BUSY"
CONTX   equ     'X'-40h


;
; Equates used only by PMMI routines grouped together here.
;
;
; PMMI port definitions
;
PORT    equ     080h            ;PMMI base port (data or status)
                               ; PORT now also used by NITMOD.

MODCT1  equ     0               ;modem control port (added to base port).
MODDAT  equ     MODCT1+1        ;modem data port      "
BAUDRP  equ     MODCT1+2        ;modem baud rate port "
MODCT2  equ     MODCT1+3        ;modem status port    "
;
; PMMI bit definitions
;
MDRCVB  equ     02h             ;modem receive bit (DAV)
MDRCVR  equ     02h             ;modem receive ready
MDSNDB  equ     01h             ;modem send bit
MDSNDR  equ     01h             ;modem send ready bit
;
;
CTSMSK  equ     4               ;mask for CTS bit
BRKMSK  equ     0FBh            ;mask to set break
PARMSK  equ     0CFh            ;mask to remove parity bits
OPARIT  equ     00h             ;odd-parity bits
EPARIT  equ     20h             ;even-parity bits
NPARIT  equ     10h             ;no-parity bits
MODEMK  equ     0FCh            ;mode mask
ANMODE  equ     1Eh             ;answer mode
ANBIT   equ     2               ;answer-mode bit
ORIGMD  equ     1Dh             ;originate mode
ORBIT   equ     1               ;originate-mode bit
WTCTS   equ     150             ;number of seconds (x5) to wait for the
                               ;computer to answer after PMMI auto-dial
                               ;100=20 sec, 150=30 sec, 255=51 sec.
                               ;any number 0-255 acceptable
;
;
; Modem control command words
;
BRKMASK equ     0               ;tele line on hook (break while dialing)
CLEAR   equ     3Fh             ;idle mode
DTMSK   equ     1               ;dial tone mask
MAKEM   equ     1               ;tele line make (off hook)
RBLMT   equ     35              ;7 seconds to wait til no-ring-heard msg
RBWAIT  equ     50              ;5 second delay before redialing PMMI
SMRWT   equ     15              ;1.5 sec delay before redialing HAYES
TMPUL   equ     80h             ;timer pulses mask bit
TRATE   equ     250             ;value for 0.1 second
;
;
; MEX service processor stuff ... MEX supports an overlay service
; processor, located at 0D00H (and maintained at this address from
; version to version).  If your overlay needs to call BDOS for any
; reason, it should call MEX instead; function calls below about
; 240 are simply passed on to the BDOS (console and list I/O calls
; are specially handled to allow modem port queueing, which is why
; you should call MEX instead of BDOS).  MEX uses function calls
; above about 244 for special overlay services (described below).
;
; Some sophisticated overlays may need to do file I/O; if so, use
; the PARSFN MEX call with a pointer to the FCB in DE to parse out
; the name.  This FCB should support a spare byte immediately pre-
; ceeding the actual FCB (to contain user # information).  If you've
; used MEX-10 for input instead of BDOS-10 (or you're parsing part
; of a SET command line that's already been input), then MEX will
; take care of DU specs, and set up the FCB accordingly.  There-
; after all file I/O calls done through the MEX service processor
; will handle drive and user with no further effort necessary on
; the part of the programmer.
;
MEX     equ     0D00h           ;address of the service processor
INMDM   equ     255             ;get char from port to A, CY=no more in 100 ms
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 char from modem (after rcvrdy)
LOOKUP  equ     247             ;table search: see CMDTBL comments for info
PARSFN  equ     246             ;parse filename from input stream
BDPARS  equ     245             ;parse baud-rate from input stream
SBLANK  equ     244             ;scan input stream to next non-blank
EVALA   equ     243             ;evaluate numeric from input stream
LKAHED  equ     242             ;get nxt char w/o removing from input
GNC     equ     241             ;get char from input, cy=1 if none
ILP     equ     240             ;inline print
DECOUT  equ     239             ;decimal output
PRBAUD  equ     238             ;print baud rate
;
;
CONOUT  equ     2               ;simulated BDOS function 2: console char out
PRINT   equ     9               ;simulated BDOS function 9: print string
INBUF   equ     10              ;input buffer, same structure as BDOS 10
;
       org     TPA             ;we begin
;
;
       ds      3               ;MEX has a JMP START here
;
; The following variables are located at the beginning of the program
; to facilitate modification without the need of re-assembly. They will
; be moved in MEX 2.0.
;
PMODEM: db      YES             ;yes=PMMI modem \ / These 2 locations are not
SMODEM: db      NO              ;yes=Smartmodem / \ referenced by MEX
TPULSE: db      'T'             ;T=touch, P=pulse (not referenced by MEX)
CLOCK:  db      55              ;clock speed x .1, up to 25.5 mhz.
MSPEED: db      1               ;sets display time for sending a file
                               ;0=110  1=300  2=450  3=600  4=710
                               ;5=1200 6=2400 7=4800 8=9600 9=19200
BYTDLY: db      5               ;default time to send character in
                               ;terminal mode file transfer (0-9)
                               ;0=0 delay, 1=10 ms, 5=50 ms, 9=90 ms
CRDLY:  db      5               ;end-of-line delay after CRLF in terminal
                               ;mode file transfer for slow BBS systems
                               ;0=0 delay, 1=100 ms, 5=500 ms, 9=900 ms
COLUMS: db      5               ;number of directory columns
SETFL:  db      YES             ;yes=user-defined SET command
SCRTST: db      NO              ;yes=if home cursor and clear screen
                               ;routine at CLRSCRN
       db      0               ;was once ACKNAK, now spare
BAKFLG: db      NO              ;yes=make .BAK file
CRCDFL: db      YES             ;yes=default to CRC checking
                               ;no=default to Checksum checking
TOGCRC: db      YES             ;yes=allow toggling of Checksum to CRC
CVTBS:  db      NO              ;yes=convert backspace to rub
TOGLBK: db      YES             ;yes=allow toggling of bksp to rub
ADDLF:  db      NO              ;no=no LF after CR to send file in
                               ;terminal mode (added by remote echo)
TOGLF:  db      YES             ;yes=allow toggling of LF after CR
TRNLOG: db      YES             ;yes=allow transmission of logon
                               ;write logon sequence at location LOGON
SAVCCP: db      YES             ;yes=do not overwrite CCP
LOCNXT: db      NO              ;yes=local cmd if EXTCHR precedes
                               ;no=not local cmd if EXTCHR precedes
TOGLOC: db      YES             ;yes=allow toggling of LOCNXTCHR
LSTTST: db      YES             ;yes=allow toggling of printer on/off
                               ;in terminal mode. Set to no if using
                               ;the printer port for the modem
XOFTST: db      NO              ;yes=allow testing of XOFF from remote
                               ;while sending a file in terminal mode
XONWT:  db      NO              ;yes=wait for XON after sending CR while
                               ;transmitting a file in terminal mode
TOGXOF: db      YES             ;yes=allow toggling of XOFF testing
IGNCTL: db      NO              ;yes=do not send control characters
                               ;above CTL-M to CRT in terminal mode
                               ;no=send any incoming CTL-char to CRT
EXTRA1: db      0               ;for future expansion
EXTRA2: db      0               ;for future expansion
BRKCHR: db      '@'-40h         ;^@ = Send a 300 ms. break tone
NOCONN: db      'N'-40h         ;^N = Disconnect from phone line
LOGCHR: db      'L'-40h         ;^L = Send logon
LSTCHR: db      'P'-40h         ;^P = Toggle printer
UNSVCH: db      'R'-40h         ;^R = Close input text buffer
TRNCHR: db      'T'-40h         ;^T = Transmit file to remote
SAVCHR: db      'Y'-40h         ;^Y = Open input text buffer
EXTCHR: db      '^'-40h         ;^^ = Send next character
PRATE:  db      167             ;125=20pps dialing, 250=10pps
       db      0               ;not used
;
; Low-level modem I/O routines: this will be replaced with
; a jump table in MEX 2.0 (you can insert jumps here to longer
; routines if you'd like ... I'd recommend NOT putting part of
; a routine in this area, then jumping to the rest of the routine
; in the non-fixed area; that will complicate the 2.0 conversion)

INCTL1: jmp     iCTL1           ;in modem control port
       db      0,0,0,0,0,0,0

OTDATA: jmp     oDATA
       db      0,0,0,0,0,0,0

INPORT: jmp     iDATA
       db      0,0,0,0,0,0,0


; Bit-test routines.  These will be merged with the above
; routines in MEX 2.0 to provide a more reasonable format
;
MASKR:  ani MDRCVB              ;bit to test for receive ready
       ret
TESTR:  cpi MDRCVR              ;value of receive bit when ready
       ret
MASKS:  ani MDSNDB              ;bit to test for send ready
       ret
TESTS:  cpi MDSNDR              ;value of send bit when ready
       ret

;
; Unused area: was once used for special PMMI functions,
; Now used only to retain compatibility with MDM overlays.
; You may use this area for any miscellaneous storage you'd
; like but the length of the area *must* be 12 bytes.
;
       ds      12
;
; Special modem function jump table: if your overlay cannot handle
; some of these, change the jump to "DS 3", so the code present in
; MEX will be retained.  Thus, if your modem can't dial, change the
; JMP PDIAL at DIALV to DS 3, and MEX will print a "not-implemented"
; diagnostic for any commands that require dialing.
;
; DIALV  dials the digit in A. See the comments at PDIAL for specs.
;
; DISCV  disconnects the modem
;
; GOODBV is called just before MEX exits to CP/M.  If your overlay
;        requires some exit cleanup, do it here.
;
; INMODV is called when MEX starts up; use INMODV to initialize the modem.
;
; NEWBDV is used for phone-number baud rates and is called with a baud-rate
;        code in the A register, value as follows:
;
;        A=0:   110 baud       A=1:   300 baud      A=2:   450 baud
;        A=3:   600 baud       A=4:   710 baud      A=5:  1200 baud
;        A=6:  2400 baud       A=7:  4800 baud      A=8: 19200 baud
;
;        If your overlay supports the passed baud rate, it should store the
;        value passed in A at MSPEED (107H), and set the requested rate. If
;        the value passed is not supported, you should simply return (with-
;        out modifying MSPEED) -or- optionally request a baud-rate from the
;        user interactively.
;
; NOPARV is called at the end of each file transfer; your overlay may simply
;        return here, or you may want to restore parity if you set no-parity
;        in the following vector (this is the case with the PMMI overlay).
;
; PARITV is called at the start of each file transfer; your overlay may simply
;        return here, or you may want to enable parity detection (this is the
;        case with the PMMI overlay).
;
; SETUPV is the user-defined command ... to use this routine to build your own
;        MEX command, set the variable SETFL (117H) non-zero, and add your SET
;        code.  You can use the routine presented in the PMMI overlay as a
;        guide for parsing, table lookup, etc.
;
; SPMENU is provided only for MDM compatibility, and is not used by MEX 1.0 for
;        any purpose (it will be gone in MEX 2).
;
; VERSNV is called immediately after MEX prints its sign-on message at cold
;        startup -- use this to identify your overlay in the sign-on message
;        (include overlay version number in the line).
; BREAKV is provided for sending a BREAK (<ESC>-B in terminal mode).  If your
;        modem doesn't support BREAK, or you don't care to code a BREAK rou-
;        tine, you may simply execute a RET instruction.
;
LOGON:  ds      2               ;needed for MDM compat, not ref'd by MEX
DIALV:  jmp     PDIAL           ;dial digit in A (see info at PDIAL)
DISCV:  jmp     PDISC           ;disconnect the modem
GOODBV: jmp     DUMMY           ;called before exit to CP/M
INMODV: jmp     NITMOD          ;initialization. Called at cold-start
NEWBDV: jmp     PBAUD           ;set baud rate
NOPARV: jmp     NOPAR           ;set modem for no-parity
PARITV: jmp     PARITY          ;set modem parity
SETUPV: jmp     SETCMD          ;SET cmd: jump to a RET if you don't write SET
SPMENV: ds      3               ;not used with MEX
VERSNV: jmp     SYSVER          ;Overlay's voice in the sign-on message
BREAKV: jmp     PBREAK          ;send a break
;
; The following jump vector provides the overlay with access to special
; routines in the main program (retained and supported in the main pro-
; gram for MDM overlay compatibility). These should not be modified by
; the overlay.
;
; Note that for MEX 2.0 compatibility, you should not try to use these
; routines, since th
is table will go away with MEX 2.0 (use the MEX
; service call processor instead).
;
ILPRTV: ds      3               ;replace with MEX function 9
INBUFV: ds      3               ;replace with MEX function 10
ILCMPV: ds      3               ;replace with table lookup funct. 247
INMDMV: ds      3               ;replace with MEX function 255
NXSCRV: ds      3               ;not supported by MEX (returns w/no action)
TIMERV: ds      3               ;replace with MEX function 254
;
;
; Clear/screen and clear/end-of-screen. Each routine must use the
; full 9 bytes alloted (may be padded with nulls).
;
; These routines (and other screen routines that MEX 2.0 will sup-
; port) will be accessed through a jump table in 2.0, and will be
; located in an area that won't tie the screen functions to the
; modem overlay (as the MDM format does).
;

CLREOS: lxi     h,SCRTST
       lxi     d,EOSMSG
       jmp     PRMAYBE


CLS:    lxi     h,SCRTST
       lxi     d,CLSMSG
       jmp     PRMAYBE

;
;------------------------------------------------------------
;
;       *** END OF FIXED FORMAT AREA ***
;
;------------------------------------------------------------
;

; Data area
ERRFLG: db      0               ;connection error code
UCTLB:  db      ORIGMD          ;uart-control byte image
BAUDSV: db      52              ;current baud rate (dflt 300)
MODCTB: db      07FH            ;modem control byte
INTERD: db      2               ;inter-digit delay in 100's of ms
PRXTRA: db      YES             ; Diagnostic prints upon function usage
WTNUM:  db      WTCTS           ;
OFFHK:  db      0               ;
                               ;
BPTAB:  db      '0123456789ABCDEF', 80h
NITBYT: db      PORT            ; Should be the PMMI base port.  Original port
                               ; is C0 hex.  Recommended alternate is 80 hex.
DIALFLG: db     0               ;
ABOBYT: db      0               ;
TEMP:   dw      0               ;


PRMAYBE: mov    a,m
       ora     a
       rz
PRMBOK: mvi     c,PRINT
       jmp     MEX

iCTL1:  mvi     a,MODCT1
       jmp     BPIN
iCTL2:  mvi     a,MODCT2
       jmp     BPIN
iDATA:  mvi     a,MODDAT        ;in modem data port
       jmp     BPIN
iBDRP:  mvi     a,BAUDRP
       jmp     BPIN

oCTL1:  push    psw
       mvi     a,MODCT1
       jmp     BPOUT
oCTL2:  push    psw
       mvi     a,MODCT2
       jmp     BPOUT
oDATA:  push    psw             ;out modem data port
       mvi     a,MODDAT
       jmp     BPOUT
oBDRP:  push    psw
       mvi     a,BAUDRP
       jmp     BPOUT

BPIN:   push    b
       mov     b,a
       lda     NITBYT
       add     b
       sta     BPINX+1
BPINX:  in      $-$
       pop     b
       ret

BPOUT:  push    b
       mov     b,a
       lda     NITBYT
       add     b
       sta     BPOUTX+1
       pop     b
       pop     psw
BPOUTX: out     $-$
       ret


; Modem initialization.  This overlay doesn't do any initialization.
; (if we did, we'd disconnect a call already in progress).

NITMOD: lda     NITBYT          ; Convert hex byte to ascii nibble.
       rar
       rar
       rar
       rar
       ani     0Fh
       mvi     d,0
       mov     e,a
       lxi     h,BPTAB
       dad     d
       mov     a,m
       sta     BPMSG           ; Store base port in sign-on message.
       mvi     a,LVL
       sta     LEVEL
       ret


; PMMI send-break routine
PBREAK: lda     MODCTB          ;get the last modem control byte
       ani     BRKMSK          ;set the transmit break bit low
       call    oCTL2           ;send it to the modem
       mvi     b,2
       call    TIMERV          ;send a space tone for 200 ms.
       lda     MODCTB          ;get the last modem control byte
       call    oCTL2           ;restore to normal
       lxi     h,PRXTRA
       lxi     d,BRKMSG
       jmp     PRMAYBE

BRKMSG: db      '.break. $'


;  Setup PMMI for odd/even parity.
PARITY: lda     UCTLB           ;send what's in the image byte
       jmp     oCTL1


; set no-parity
NOPAR:  lda     UCTLB           ;get uart/modem control byte
       ani     PARMSK          ;reset parity bits
       ori     NPARIT          ;add no-parity bits
       jmp     oCTL1


; disconnect the modem
PDISC:  call    HUKONN          ; Hang up
       call    oCTL2           ; clear DAV, ESD, etc
       push    b               ;
       lxi     h,PRXTRA        ;
       lxi     d,DSC1MSG       ;
       call    PRMAYBE         ;
       mvi     b,20            ;wait for PMMI to disconnect (2 sec) %%* 1 to 2
       mvi     c,TIMER         ;0.1 second per timer interval
       call    MEX             ;
       lxi     h,PRXTRA        ;
       lxi     d,DSC2MSG       ;
       call    PRMAYBE         ;
       pop     b               ;
       ret                     ;

DSC1MSG: db     '.di$'
DSC2MSG: db     'sc. $'

; exit routine
DUMMY:  ret                     ;we don't need one
;
;
;------------------------------------------------------------
;
;               <PMMI DIALING ROUTINES BEGIN>
;
; 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 (we don't do this with
; the PMMI, however; we just dial the digits as they come in).
;
; After the 254-start-dial sequence, MEX will call the overlay with
; digits, one-at-a-time.  MEX will make no assumptions about the dig-
; its, 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). This
; dialing routine validates digits, and ignores any except 0-9 and
; comma (uses comma to simulate Smartmodem delay).
;
; 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
;
;
PDIAL:  cpi     254             ;start-dial?
       jz      STDIAL          ;
       cpi     255             ;end-dial
       jz      ENDIAL          ;
                               ;
       push    psw             ;
       mvi     a,7             ;
       sta     DIALFLG         ;
       pop     psw             ;
                               ;
PDIAL1: cpi     ','             ;smartmodem pause command
       jnz     PDIAL2          ;if not pause, continue
       mvi     b,10            ;delay 1 second
       jmp     TIMOUT          ;


PDIAL2: cpi     '#'             ;
       jnz     CKDIG           ;
       mvi     a,BS            ;
       call    PUT1C           ;
PDIAL3: call    GET1C           ;
       jz      PDIAL3          ;
       call    PUT1C           ;
       jmp     PDIAL1          ;


CKDIG:  cpi     '9'+1           ; digits are 0-9
       rnc                     ; too big...
       sui     '0'             ;
       rc                      ; too small....
       jnz     DIALIT          ; just right...
       mvi     a,10            ; convert zero to 10 pulses

; Send a digit to the modem.
DIALIT: mov     c,a             ; save the digit
       lda     ERRFLG          ; before we try to dial...
       ora     a               ; ...check dialtone error flag
       rnz                     ; ...if no DT, exit now
       lda     PRATE           ; value for dial speed
       call    oBDRP           ;
       call    WAITLO          ; wait for timer lo
       call    WAITHI          ; wait for timer hi

       call    GET1C           ;
DIAL1:  cpi     3               ; ^C ?
       jz      DIALX           ;
       cpi     24              ; ^X ?
       jz      DIALX           ;
       cpi     19              ; ^S ?
       jnz     DIGLP           ;

DIALP:  call    GET1C           ; Wait for a character
       jz      DIALP           ;
       cpi     19              ;
       jmp     DIGLP           ;
                               ;
DIALX:  sta     ABOBYT          ;

DIGLP:  lda     ABOBYT          ;
       ora     a               ;
       jnz     DIGLP2          ;
                               ;
       call    HUKOFF          ; Go off-hook
       call    WAITLO          ;
       call    HUKONN          ;
       call    WAITHI          ;
DIGLP2: dcr     c               ;
       jnz     DIGLP           ; send rest of digit
       lda     ABOBYT          ;
       ora     a               ;
       rnz                     ;
                               ;
       call    HUKOFF          ;
       lda     INTERD          ; get inter-digit delay
       mov     b,a             ;
       jmp     TIMOUT          ;


; Wait for  negative edge of timer pulse
WAITLO: call    iBDRP
       ani     TMPUL
       jnz     WAITLO
       ret


; Wait for positive edge of timer pulse
WAITHI: call    iBDRP
       ani     TMPUL
       jz      WAITHI
       ret


; Start-dial sequence: disconnect, wait for dial-tone
STDIAL: xra     a               ; reset error flag
       sta     ERRFLG          ;
       call    PDISC           ; on-hook
       call    HUKOFF          ;
       call    PROFFH          ; (possibly) print .off hook. message

; Wait routine will return with carry set if unable to get dialtone.
       mvi     d,DTMSK         ;dial tone mask
       mvi     e,50            ;waits up to 10 sec. for dial tone
       call    WAIT            ;wait for dial tone
       rnc                     ;if dial tone within 10 seconds

       sta     ERRFLG          ;(action on error deferred until
       call    PDISC           ;no tone, hang up
       ret                     ;   dialing is completed)


; End-dial sequence
ENDIAL: call    ENDIT           ;close out dialing
       push    psw             ;
       xra     a               ;
       sta     DIALFLG         ;
       sta     ABOBYT          ;
       pop     psw             ;
       ora     a               ;successfully connected?
       rz                      ;exit now if so
       push    psw             ;nope, save the error code
       call    PDISC           ;shut down the modem
       pop     psw             ;
       ret                     ;


ENDIT:  lda     ERRFLG          ;no-dialtone error from STDIAL?
       ora     a
       rnz                     ;if so, return the error here
       call    OFF             ;go off-hook
       lda     UCTLB           ;get uart/modem control byte
       call    oCTL1           ;send it
       mvi     d,4             ;clear-to-send mask
                               ;
       lda     WTNUM           ;
       mov     e,a             ;
       call    WAIT            ;
       rnc                     ;return A=0 if good
                               ;
       cpi     'C'-40h         ;keyboard abort?
       rz                      ;if so return it
                               ;
       if      CTRLX           ;
       cpi     1               ;Fake busy?
       rz                      ;Return if so
       endif                   ;CTRLX
                               ;
       mvi     a,2             ;nope, convert error to "no answer"
       ret                     ;

;       <end of PMMI dialing routines>
;------------------------------------------------------------


;       Go Off-Hook
OFF:    lda     BAUDSV          ;set current baud rate
       call    oBDRP           ;
       lda     MODCTB          ;Load current DTR
       call    oCTL2           ;
       call    HUKOFF          ;
       lda     UCTLB           ;
       call    oCTL1           ;
       mvi     b,2             ;wait 200 ms    %%* changed from 1 to 2.
       call    TIMOUT          ;
       lda     DIALFLG         ;
       ora     a               ;
       jz      PRONLN          ;
       xra     a               ;
       sta     DIALFLG         ;
PRWAIT: lxi     h,PRXTRA        ;
       lxi     d,WAITMSG       ;
       jmp     PRMAYBE         ;

PROFFH: lxi     h,PRXTRA        ;
       lxi     d,OFFHMSG       ;
       jmp     PRMAYBE         ;

PRONLN: lxi     h,PRXTRA        ;
       lxi     d,ONLNMSG       ;
       jmp     PRMAYBE         ;

OFFHMSG: db     '.off-h. $'     ; Force PMMI to on-line status.
ONLNMSG: db     '.on-line. $'   ; Force PMMI to on-line status.
WAITMSG: db     '.wait. $'      ; Force PMMI to   "      " and wait
                               ;  for either a carrier or timeout.


HUKOFF: mvi     a,255           ; Go Off-Hook
       sta     OFFHK           ;
       mvi     a,MAKEM         ;
       jmp     oCTL1           ;


HUKONN: xra     a               ; Go On-Hook
       sta     OFFHK           ;
       mvi     a,BRKMASK       ;
       jmp     oCTL1           ;




; Time-out routine.  Must be called with mask in D reg. for input at
; relative port 2 and number of seconds (times 10) in E reg.
WAIT:   mvi     b,2             ; 200 ms
       call    TIMOUT          ; wait for timer to go high then low
       call    iBDRP           ; pmmiaddr+2 (modem status port)
       ana     d               ; (cts or dialtone mask)
       rz                      ; active low, so return on 0

       if      not CTRLX       ;
       mvi     c,CHEKCC        ;not yet, check for console-abort
       call    MEX             ;abort?
       mvi     a,3             ;set error code 3 if abort active
       stc                     ;
       rz                      ;return if aborted
       endif                   ;not CTRLX

       if      CTRLX           ;
       lda     ABOBYT          ;
       ora     a               ;
       jz      WAIT0           ;
       push    psw             ;
       xra     a               ;
       sta     ABOBYT          ;
       pop     psw             ;
       jmp     WAIT1           ;

WAIT0:  call    GET1C           ;
       jz      WAITOR          ;
                               ;
WAIT1:  cpi     CONTX           ;'^X?'
       jnz     WAIT2           ;no, check for ^C
       call    GET1C           ;Clear out garbage
       mvi     a,1             ;yes, return fake error code
       stc                     ;
       ret                     ;
WAIT2:  cpi     3               ;Duplicate MEX ^C trap
       jnz     WAITOR          ;Not ^C, continue
       call    GET1C           ;Clear out garbage
       mvi     a,3             ;"ABORT" error
       stc                     ;Yes, pass error
       ret                     ;code and return
       endif                   ;CTRLX


WAITOR: dcr     e               ;
       jnz     WAIT            ; nope, downcount
       inr     a               ; set error=4 (modem error); cy already set
       ret                     ;


GET1C:  push    h
       push    d
       push    b
       mvi     c,6
       mvi     e,0FFh
       call    5
       pop     b
       pop     d
       pop     h
       ani     7Fh
       ret


PUT1C:  push    h
       push    d
       push    b
       mvi     c,6
       mov     e,a
       call    5
       pop     b
       pop     d
       pop     h
       ret


; Set baud-rate code in A (if supported by your modem overlay).  PMMI
; supports only five rates, which are validated here. NOTE: this routine
; (ie, the one vectored through NEWBDV) should update MSPEED with the
; passed code, but ONLY if that rate is supported by the hardware.
PBAUD:  push    h               ;don't alter anybody
       push    d
       push    b
       mov     e,a             ;code to DE
       mvi     d,0
       lxi     h,BAUDTB        ;offset into table
       dad     d
       mov     a,m             ;fetch code
       ora     a               ;0? (means unsupported code)
       stc                     ;return error for STBAUD caller
       jz      PBEXIT          ;exit if so
       call    oBDRP           ;good rate, set it
       sta     BAUDSV          ;save it
       mov     a,e             ;get speed code back
       sta     MSPEED          ;make it current
       call    GETDTR          ;get correct DTR based on baud rate
       sta     MODCTB          ;save the code
       call    CARRCK          ;is a connection in progress?
       jnz     PBEXIT          ;skip this if not
       lda     MODCTB          ;yep, set up DTR
       call    oCTL2

PBEXIT: lxi     h,PRXTRA
       lxi     d,BDSTMSG
       call    PRMAYBE
       pop     b               ;all done
       pop     d
       pop     h
       ret

BDSTMSG: db     '.bd-rt. $'

; table of baud rate divisors for supported rates
BAUDTB: db      142,052,035,026,022     ;110,300,450,610,710
       db      0,0,0,0,0               ;1200,2400,4800,9600,19200


; Sign-on message
SYSVER: lxi     d,LINMSG
       mvi     c,PRINT
       call    MEX
       lxi     d,SOMESG
       mvi     c,PRINT
       call    MEX
       lxi     d,bpmess
       mvi     c,PRINT
       call    MEX
CARRSH: lxi     d,NOMESG                ;tell about carrier
       call    CARRCK                  ;check for it
       mvi     c,PRINT
       cz      CMSG
       cnz     MEX
       lxi     d,LINMSG
       mvi     c,PRINT
       call    MEX
       lxi     d,GRBMSG
       mvi     c,PRINT
       call    MEX
       ret
CMSG:   push    psw
       mvi     c,PRINT
       lxi     d,CARMSG
       call    MEX
       pop     psw
       ret


SOMESG: db      '* PMMI overlay version - '
       db      REV/10+'0'
       db      '.'
       db      REV MOD 10+'0'
LEVEL:  db      '   *',CR,LF,'$'
BPMESS: db      '* Base port = '
BPMSG:  db      'x0 hex.          *'
NLMSG:  db      cr,lf,'$'

NOMESG: db      '* No carrier present.          *$'
CARMSG: db      '* Carrier IS present.          *$'
LINMSG: db      CR,LF,'********************************',CR,LF,'$'
GRBMSG: db      CR,LF,'$'
;
;
; get DTR port value based on baud rate
;
GETDTR: lda     BAUDSV
       cpi     52              ;>300?
       mvi     a,05Fh          ;set speed configuration (ARRRRRRGGGGHHHH.)
       rc                      ;done if so (Swapped in version 2.0)
       mvi     a,07Fh          ;reset speed config bit (ARRRRRRGGGGGHHHH.)
       ret


; check the PMMI for carrier-present (NZ=no)
CARRCK: call    iBDRP           ;get status byte
       ani     CTSMSK
       rnz
       push    psw
       mvi     a,255
       sta     OFFHK
       pop     psw
       ret


; Newline on console
CRLF:   mvi     a,CR
       call    TYPE
       mvi     a,LF            ;fall into TYPE


; type char in A on console
TYPE:   push    h               ;save 'em
       push    d
       push    b
       mov     e,a             ;align output character
       mvi     c,CONOUT        ;print via MEX
       call    MEX
       pop     b
       pop     d
       pop     h
       ret


; strings to clear-to-end-of-screen, and clear-screen
; Note: these are dummy strings, not intended to be displayed...
EOSMSG: db      ' -clr eos- $'  ;clear to end-of-screen
CLSMSG: db      ' -clr all- $'  ;clear whole screen


;
;------------------------------------------------------------
;
; The remainder of this overlay implements a very versatile
; SET command -- if you prefer not to write a SET for your
; modem, you may delete the code from here to the END statement.
;
;
; Control is passed here after MEX parses a SET command.
;
SETCMD: mvi     c,SBLANK        ;any arguments?
       call    MEX
       jc      SETSHO          ;if not, go print out values
       lxi     d,CMDTBL        ;parse command
       call    TSRCH           ;from table
       push    h               ;any address on stack
       rnc                     ;if we have one, execute it
       pop     h               ;nope, fix stack
SETERR: lxi     d,SETEMS        ;print error
       mvi     c,PRINT
       call    MEX
       ret
;
SETEMS: db      CR,LF,'SET command error',CR,LF,'$'
;
; SET command table ... note that tables are constructed of command-
; name (terminated by high bit=1) followed by word-data-value returned
; in HL by MEX service processor LOOKUP.  Table must be terminated by
; a binary zero.
;
; Note that LOOKUP attempts to find the next item in the input stream
; in the table passed to it in HL ... if found, the table data item is
; returned in HL; if not found, LOOKUP returns carry set.
;
CMDTBL: db      '?'+80h                 ; "set ?"
       dw      STHELP                  ;
       db      'BAU','D'+80h           ; "set baud"
       dw      STBAUD                  ;
       db      'ID','D'+80h            ; "set id"
       dw      SETIDD                  ;
       db      'ANSWE','R'+80h         ; "set answer"
       dw      STANSW                  ;
       db      'AN','S'+80h            ; "set ans" (same as above)
       dw      STANSW                  ;
       db      'ORIGINAT','E'+80h      ; "set originate"
       dw      STORIG                  ;
       db      'ORI','G'+80h           ; "set orig" (same as above)
       dw      STORIG                  ;
       db      'OR','G'+80h            ; "set org" (same as above)
       dw      STORIG                  ;
       db      'OFFHOO','K'+80h        ; "set offhook"
       dw      OFF                     ;
       db      'ONLIN','E'+80h         ; "set online" (same as offhook)
       dw      OFF                     ;
       db      'ONHOO','K'+80h         ; "set onhook"
       dw      PDISC                   ;
       db      'OFFLIN','E'+80h        ; "set offline" (same as onhook)
       dw      PDISC                   ;
       db      'PP','S'+80h            ; "set pps"
       dw      SETPPS                  ;
       db      'BAS','E'+80h           ; "set PMMI base port."
       dw      SETBP                   ;
       db      'IC','D'+80h            ; "set delay between calls"
       dw      SETICD                  ;
       db      'EXTR','A'+80h          ; "set extra print mode"
       dw      SETXTRA                 ;
       db      'XYZZ','Y'+80h          ;
       dw      XYZZY                   ;
                                       ;
       db      0                       ; <<=== table terminator
;
; SET <no-args>: print current statistics
;
SETSHO: call    SYSVER          ;show carrier present/not present
       lxi     h,SHOTBL        ;get table of SHOW subroutines
SETSLP: mov     e,m             ;get table address
       inx     h
       mov     d,m
       inx     h
       mov     a,d             ;end of table?
       ora     e
       rz                      ;exit if so
       push    h               ;save table pointer
       xchg                    ;adr
s to HL
       call    GOHL            ;do it
       call    CRLF            ;print newline
       mvi     c,CHEKCC        ;check for console abort
       call    MEX
       pop     h               ;it's done
       jnz     SETSLP          ;continue if no abort
       call    CRLF
       ret

GOHL:   pchl


; table of SHOW subroutines
SHOTBL: dw      BDSHOW
       dw      MDSHOW
       dw      SHOICD
       dw      SHOIDD
       dw      SHOPPS
       dw      SHOXTRA
       dw      CRLF
       dw      0               ;<<== table terminator

;
; SET ?  processor
;
STHELP: lxi     d,HLPMSG
       mvi     c,PRINT
       call    MEX
       ret
;
; The help message
;
HLPMSG: db      cr,lf,'SET command, for the PMMI S-100 modem (r.i.p.)'
       db      cr,lf
       db      cr,lf,'SET ANSWER <or> SET ANS     ... put PMMI in answer mode'
       db      cr,lf,'SET BASE <hex #>            ... set new PMMI base port'
       db      cr,lf,'SET BAUD <value>            ... set baud rate'
       db      cr,lf,'    BAUD values allowed are:    110, 300, 450, 600, and 710'
       db      cr,lf,'SET EXTRA <value>           ... OFF if <value> == 0, else ON'
       db      cr,lf,'    EXTRA function diagnostics displayed: BAUD RATE, BREAK,'
       db      cr,lf,'          DISCONNECT, OFF-HOOK, and WAIT (for answer tone)'
       db      cr,lf,'SET ICD <value>'
       db            '             ... intercall delay; 150 == 30 seconds'
       db      cr,lf,'SET IDD <value>             ... interdigit delay in 100''s msec'
       db      cr,lf,'SET OFFHOOK <or> SET ONLINE ... force PMMI online'
       db      cr,lf,'SET ONHOOK <or> SET OFFHOOK ... disconnect without message'
       db      cr,lf,'SET ORIGINATE <or> SET ORIG ... put PMMI in originate mode'
       db      cr,lf,'SET PPS <value>             ... may be 10, 15 or 20 pulses/sec.'
       db      cr,lf
       db      cr,lf, '$'


; SET BAUD processor
STBAUD: mvi     c,BDPARS        ;function code
       call    MEX             ;let MEX look up code
       jc      SETERR          ;invalid code
       call    PBAUD           ;no, try to set it
       jc      SETERR          ;not-supported code
BDSHOW: call    ILPRT           ;display baud
       db      'Baud rate: ', tab, '   ', 0
       lda     MSPEED
       mvi     c,PRBAUD        ;use MEX routine
       call    MEX
       ret


; SET MODE processor
MDSHOW: call    ILPRT           ;show mode
       db      'Mode:', tab, tab, '   ', 0
       lda     UCTLB           ;get UART B image
       ani     ORBIT           ;orig?
       jz      MDORIG
       call    ILPRT
       db      'Originate', 0
       ret

MDORIG: call    ILPRT
       db      'Answer', 0
       ret

STORIG: mvi     l,ORBIT
       jmp     CHGAO

STANSW: mvi     l,ANBIT
CHGAO:  lda     UCTLB
       ani     MODEMK
       ora     l
       sta     UCTLB
       call    OHKBYT
       jnz     MDSHOW
       call    oCTL1
       call    OFF
       jmp     MDSHOW

OHKBYT: lda     OFFHK
       cma
       ora     a
       ret

;
; SET PPS command processor
;
SETPPS: lxi     d,PPSTBL        ;get value
       call    TSRCH
       jc      SETERR          ;not found in table? error out
       mov     a,l             ;yep, set it
       sta     PRATE
SHOPPS: call    ILPRT
       db      'PPS rate: ', tab, '   ', 0
       lda     PRATE           ;display PPS
       cpi     250
       jnz     SHO2
       call    ILPRT
       db      '10', 0
       ret
SHO2:   cpi     125
       jnz     SHO3
       call    ILPRT
       db      '20',0
       ret
SHO3:   call    ILPRT
       db      '15',0
       ret


PPSTBL: db      '1','0'+80H     ;"set pps 10"
       dw      250
       db      '1','5'+80h     ;"set pps 15"
       dw      167
       db      '2','0'+80H     ;"set pps 20"
       dw      125
       db      0               ;<<=== table terminator
;
; SET IDIG command processor
;
SETIDD: mvi     c,EVALA
       call    MEX             ;get numeric
       mov     a,h             ;validate
       ora     a
       jnz     SETERR
       mov     a,l
       sta     INTERD          ;set new rate
SHOIDD: call    ILPRT
       db      'Inter-digit delay: ', 0
       lda     INTERD          ;get value
       mov     l,a             ;move delay to HL
       mvi     h,0
       mvi     c,DECOUT        ;print it
       call    MEX
       call    ILPRT
       db      '00 ms',0
       ret


; Set BASE PORT command processor.
SETBP:  mvi     c,SBLANK
       call    MEX
       mvi     c,GNC           ;get char from input, cy=1 if none
       call    MEX
       call    UPPER
       mov     l,a             ; Character to test is in L.
       shld    TEMP

       lxi     b,BPTAB-1
       lxi     d,10h
       lxi     h,-10h

SETLP:  inx     b               ; Advance to next allowable character.
       dad     d               ; Add 10 hex to base port address.
       ldax    b               ;
       ora     a               ;
       jm      SETNG           ; Jump if character typed not in allowable set.
       push    h               ;
       lhld    TEMP            ;
       cmp     l               ; Valid port requested?
       shld    TEMP            ;
       pop     h               ; Restore new base port.
       jnz     SETLP           ; No match, try again.
       sta     BPMSG           ; Patch sign-on message with Port # (ascii).
       mov     a,l             ;
       sta     NITBYT          ; Save base port (hex) for I/O and for CLONING.
       ret


BPNOGO: db      7, '  ****  Invalid port  ****', cr, lf, '$'
SETNG:  lxi     d,BPNOGO
       mvi     c,PRINT
       jmp     MEX


SETICD: mvi     c,EVALA
       call    MEX             ;get numeric
       mov     a,h             ;validate
       ora     a
       jnz     SETERR
       mov     a,l
       sta     WTNUM           ;set new rate
SHOICD: call    ILPRT
       db      'Inter-call delay:  ', 0
       lda     WTNUM           ;get value
       mov     l,a             ;move delay to HL
       mvi     h,0
       mvi     c,DECOUT        ;print it
       call    MEX
       call    ILPRT
       db      ' ticks. (150 ticks=30 seconds)', 0
       ret

SETXTRA: mvi    c,EVALA
       call    MEX
       mov     a,h
       ora     a
       jnz     SETERR
       mov     a,l
       sta     PRXTRA
SHOXTRA: call   ILPRT
       db      'Extra print mode:  ',0
       lda     PRXTRA
       ora     a
       lxi     d,ONNMSG
       jnz     SHOXT2
       lxi     d,OFFMSG
SHOXT2: jmp     PRMBOK

ONNMSG: db      'ON$'
OFFMSG: db      'OFF$'


; Compare next input-stream item in table @DE; CY=1
; if not found, else HL=matched data item
TSRCH:  mvi     c,LOOKUP        ;get function code
       jmp     MEX             ;pass to MEX processor


XYZZY:  call ILPRT
       db      'Nothing happens...', 0
       ret


; Print in-line message
ILPRT:  mvi     c,ILP           ;get function code
       jmp     MEX             ;go do it


TIMOUT: mvi     c,TIMER         ;
       jmp     MEX             ;


UPPER:  cpi     'a'
       rc
       cpi     'z'+1
       rnc
       sui     'a'-'A'
       ret


;------------------------------------------------------------
;
; End of PMMI MEX modem overlay
;
;------------------------------------------------------------


       end