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 this 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			;adrs 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