; MXO13PX8  MEX overlay for Epson PX-8			    Oct 18. 1985
;
; (delete the ";" in the above title line if assembling with MAC)
;
;
REV	EQU	12		; Overlay revision level
;
; MEX overlay for Epson PX-8 with MFU, Direct Connect modem, or external
; modem.  Support is provided for an external dialing overlay such as
; MXO-SM13.ASM. For details see MXO-PX8.DOC.
;
; Written 3/10/85 by David B. Kozinn
;
;	Copyright (C) 1985 by David B. Kozinn
;
; This program (in source or object code form) may be copied and distri-
; buted for non-commercial purposes.  Sale of this program is forbidden
; without written consent of the author.
;
; The author can be contacted on CompuServe [76703,565] via Easyplex, or
; (preferably) in CP-MIG (PCS-47) or EpsOnline (PCS-19).
;
;     PLEASE READ THE FOLLOWING IF YOU INTEND TO MODIFY THIS OVERLAY:
;     ---------------------------------------------------------------
; Much of the commentary has been removed in order to save space.  For
; complete documentation of how an overlay is structured, see the origi-
; nal overlay, MXO-PM.ASM, written by Ron Fowler.  Please be sure to
; read the comments in that overlay if you intend to change this one.
;
;	Version Date	Changes
;	------- ------- -------
;	  1.2	870502	Fixed another bug in the SET COMM command that
;			kept the uart from being properly set when using
;			the internal modem.  Also added pulse dial capa-
;			bility.  Removed all references to the letters
;			A-D in the tone dialing area to make room.
;					- Bob Kitchen
;
;	  1.1	870301	Fixed bugs in the SET COMM command so that the
;			correct values for "even" parity and "2" stop
;			bits were sent to the RS-232 device.  Also fixed
;			the problem with the screen not keeping up when
;			running at 1200 baud.
;					- Bob Kitchen
;
;	  1.0	850310	First general release version
;
;-----------------------------------------------------------------------
;
; Misc equates
;
NO	EQU	0
YES	EQU	0FFH
;
;
TPA	EQU	100H
CR	EQU	13
LF	EQU	10
TAB	EQU	9
BELL	EQU	7
ESC	EQU	01BH
CUROFF	EQU	032H
CURON	EQU	033H
XON	EQU	011H
XOFF	EQU	013H
;
; PX-8 port definitions
;
MCTLR2	EQU	002H		; Misc control signals (output)
CCR	EQU	00DH		; 8251 command port
DCRREG	EQU	084H		; Modem tone dialer control port (output)
OCR	EQU	085H		; Modem control register (output)
STR	EQU	086H		; Modem status register (input)
OMR	EQU	087H		; Modem port setup (output)
;
; PX-8 Special locations (see code for use)
;
WBLOC	EQU	0EC03H		; BIOS call Warm Boot location
WRSDAT	EQU	0F6A9H		; Working RS-232 data
CTLR1	EQU	0F0B0H		; CTLR1 register value
CTLR2	EQU	0F0B2H		; CTLR2 register value
RSMODE	EQU	0F6D0H		; 8251 Mode byte location
RSCMD	EQU	0F6D1H		; 8251 Command byte location
RSOPN	EQU	0F2C8H		; RS-232 open flag (00=Open)
SLVFLG	EQU	0F358H		; Slave cpu (6301) flag
INTBUF	EQU	0FB90H		; Internal rs232 buffer
INTBUFL	EQU	0160H		; Internal buffer length
;
; Bit definitions
;
MDMINIT	EQU	089H		; 8251 initialization string
BRKBIT	EQU	008H		; 8251 bit to turn break on
MDRCVB	EQU	001H		; Modem receive bits
MDRCVR	EQU	001H		; Modem recieve ready
MDSNDB	EQU	002H		; Modem send bits
MDSNDR	EQU	002H		; Modem recieve ready
USEINT	EQU	020H		; Set to 0, send this to CTLR2 to use int.
DCD	EQU	008H		; Use with RSIOX CTLIN
;
; Bit definitions for OCR
;
OHC	EQU	001H		; Off hook
HSC	EQU	002H		; Handset control
MON	EQU	004H		; Enable speaker
TXC	EQU	008H		; Transmit carrier
ANS	EQU	010H		; Answer mode
TEST	EQU	020H		; Test function
PWR	EQU	040H		; Modem Power
CCT	EQU	080H		; Connect to phone lne
;
; Bit definitions for STR
;
BDS	EQU	001H		; Bell Detect Signal. 0 if ringing
CTSMSK	EQU	004H		; Clear to send. (carrier det) =0 if clear
MII	EQU	080H		; Modem Installation Indicator 0=installed
;
; Special BIOS call locations
;
CONIN	EQU	WBLOC+006H	; Direct console input
CONOUT	EQU	WBLOC+009H	; Direct console output
RSOPEN	EQU	WBLOC+039H	; Open RS-232 port
RSCLOSE	EQU	WBLOC+03CH	; Close RS-232	port
RSINST	EQU	WBLOC+03FH	; Check for input
RSOUTST	EQU	WBLOC+042H	; Check for output
RSIN	EQU	WBLOC+045H	; Get a character
RSOUT	EQU	WBLOC+048H	; Send a character
RSIOX	EQU	WBLOC+051H	; Special RS-232 calls
SLAVE	EQU	WBLOC+072H	; Use slave CPU
;
; Equates for use with RSIOX
;
OPNIOX	EQU	010H		; Open using RSIOX
CLSIOX	EQU	020H		; Close using RSIOX
INSTS	EQU	030H		; Check for data in recieve buffer
CTLIN	EQU	070H		; Check carrier & DSR
;
; MEX locations
;
QUEUE	EQU	00D51H		; Queued I/O variable
MODE	EQU	00D54H		; Terminal mode byte (01 = in terminal mode)
SMART	EQU	00B00H		; Entry point for smart modem overlay
;
; Other equates
;
WTCTS	EQU	125		; How long to wait for carrier. 125=25 seconds
;
; MEX service processor stuff
;
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
;
NOSMART	EQU	NO		; Yes=no Smartmodem dialing overlay
;
PRINT	EQU	9		; Simulated BDOS function 9: print string
INBUF	EQU	10		; Input buffer, same structure as BDOS 10
;
	 IF	NOSMART
	ORG	SMART		; Put code in in case he calls Smartmodem
	CPI	255		;   without installing the overlay first
	RNZ			; Only do it once, at the end
	CALL	ILPRT
	DB	'** You have not installed a dialing overlay **'
	DB	BELL,CR,LF,0
	XRA	A		; Set zero flag
	MVI	A,3		; Tell him dialing has been aborted
	RET
	 ENDIF
;
;
	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.
;
PMODEM:	DB	NO		; Yes=PMMI modem \ / These 2 locations are not
SMODEM:	DB	NO		; Yes=Smartmodem / \ referenced by MEX
TPULSE:	DB	'T'		; T=tone, P=pulse (tone or pulse dial flag)
CLOCK:	DB	28		; 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	1		; 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	1		; 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	YES		; Yes=if home cursor and clear screen
				; Routine at CLRSCRN
	DB	0		; Was once ACKNAK, now spare
BAKFLG:	DB	YES		; 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	NO		; 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	YES		; 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
;
; The next two are for PMMI, we don't use them, but they have to hang
; around anyway.
;
	DB	250
	DB	0
;
; Low-level modem I/O routines
;
INCTL1:	JMP	MODSTAT		; In modem control port
	DB	0,0,0,0,0,0,0	; Spares
;
OTDATA:	JMP	MODOUT		; Out modem data port
	DB	0,0,0,0,0,0,0	; Spares
;
INPORT:	JMP	MODIN		; In modem data port
	DB	0,0,0,0,0,0,0	; Spares
;
; Bit-test routines
;
MASKR:	ANI	MDRCVB ! RET	; Bit to test for receive ready
TESTR:	CPI	MDRCVR ! RET	; Value of receive bit when ready
MASKS:	ANI	MDSNDB ! RET	; Bit to test for send ready
TESTS:	CPI	MDSNDR ! RET	; Value of send bit when ready
;
;
; 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 table
;
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:	DS	3		; Called before exit to CP/M
INMODV:	JMP	NITMOD		; Initialization. Called at cold-start
NEWBDV:	JMP	PBAUD		; Set baud rate
NOPARV:	DS	3		; Set modem for no-parity
PARITV:	DS	3		; 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
;
; Entry points here are for compatibility with MDM7
;
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).
;
CLREOS:	LXI	D,EOSMSG
	MVI	C,PRINT
	CALL	MEX
	RET
;
CLS:	LXI	D,CLSMSG	; Null unless patched
	MVI	C,PRINT
	CALL	MEX
	RET
;
	XRA	A		; Don't allow queued I/O (it interferes
				; With the stuff we're doing)
	STA	QUEUE		; Set to Queued I/O flag location
;
;-----------------------------------------------------------------------
;
;		*** END OF FIXED FORMAT AREA ***
;
;-----------------------------------------------------------------------
;
; Modem initialization.  First make sure that the dialing overlay ad-
; dress is correct (some overlays ORG the dial vector and stick a JMP
; B00 there.)  We have to make sure that we get control first.	Also,
; store away the current value in the DISCV address, because it might be
; the address of the dialing overlay disconnect vector.  We'll use this
; to disconnect along with the regular disconnect code if an external
; modem is being used.	Then, set up the default communications parame-
; ters.  Then, if the port is already open, just return, else set up to
; use the built-in modem.
;
NITMOD:	LXI	H,PDIAL		; Get address of our dialing routine
	SHLD	DIALV+1		; Store it as the address to jump to
	LHLD	DISCV+1		; Get address of disconnect vector
	SHLD	SMDISCV+1	; Save it in case it's needed
	LXI	B,PDISC		; Get our disconnect routine address
	XRA	A		; Clear carry flag
	DB	0EDH,042H	; SBC HL,BC If zero, then the same
	JZ	NIT0		; Don't do anything special
	DCR	A		; Get 0FFH to store
	STA	GOTDISC		; Indicate that we've got external disc
	LXI	H,PDISC		; Get the address of our disconnect routine
	SHLD	DISCV+1		; Store it in the proper place
;
NIT0:	LXI	H,WRSDAT	; Get working rs232 data
	LXI	D,BAUDRATE
	LXI	B,4
	DB	0EDH,0B0H	; LDIR
	LDA	BAUDRATE	; Find out what baud we're set to
	LXI	H,BAUDTBL
	MVI	B,0
;
NIT1:	CMP	M		; Do we match this table entry?
	JZ	NIT2		; Yes
	INX	H
	INR	B
	JMP	NIT1
;
NIT2:	MOV	A,B		; Store the table entry locally
	STA	MDMSPD
	CALL	SMSPEED		; Set modem speed for time for xfer
	LDA	RSOPN		; See if the port is already open
	ORA	A
	RZ			; Don't do anything if so
	CALL	RSOPEN		; Turn the port on
	CALL	PWRON		; Try to turn the modem on
	IN	STR		; Read status register
	ANI	MII		; Is modem installed?
	JNZ	OPENIT		; No, open the RS-232 port normally
	CALL	STPORT		; Set up to use the direct connect
	LDA	OCRVAL		; Turn off modem power to conserve
	ANI	(NOT PWR) AND 0FFH
	STA	OCRVAL
	OUT	OCR
	MVI	A,0FFH		; Indicate we're using the internal modem
	STA	MDMTYPE
	RET
;
OPENIT:	CALL	RSCLOSE		; Close the port to clean things up
	CALL	RSOPEN		; Open it up for the regular port
	RET
;
; Send-break routine
;
PBREAK:	LDA	RSCMD		; Get the current command for the 8251
	ORI	BRKBIT		; Turn break on
	OUT	CCR		; Send it to the 8251
	MVI	B,3		; Wait 300 ms
	MVI	C,TIMER
	CALL	MEX
	LDA	RSCMD		; Restore the 8251 to normal
	OUT	CCR
	RET
;
; Disconnect the modem
;
PDISC:	LDA	MDMTYPE		; See if we're using an external modem
	ORA	A		; This will be zero if so
	JNZ	PDISC0		; If not, don't worry about it
	LDA	GOTDISC		; See if we had an external routine
	ORA	A		; This will be non-zero if so
	JNZ	SMDISCV		; Call it if it's there
;
PDISC0:	CALL	RSCLOSE
	LDA	OCRVAL		; Get current modem control values
	ANI	0FFH-(CCT+TXC+OHC) ; On-hook, carrier off,disconnect
	OUT	OCR		; Send it
	ANI	(NOT PWR) AND 0FFH ; Now turn power off too
	OUT	OCR
	STA	OCRVAL		; Save this as current mod. ctl. value
	CALL	RESPORT		; Reset to use internal
	CALL	SPKROFF		; Turn the speaker off
	CALL	RSOPEN
	RET
;
; External dialing routine disconnect code is called through here.
;
SMDISCV:JMP	PDISC		; <-----This may be modified
;
;-----------------------------------------------------------------------
;
;			DIALING ROUTINES
;
; The DIGITS table is a translation for digit to register value for all
; of the supported tone digits.  The modem also understands that a com-
; ma (,) means to wait for n seconds before continuing (for compatabil-
; ity with various intelligent modems).  Any other values (such as -,
; (, or ) ) are ignored.  For pulse dialing, only the digits 0-9 are
; used, all else, except a comma, is ignored.
;
DIGITS:	DB	1DH,10H,11H,12H	; 0, 1, 2, 3
	DB	14H,15H,16H,18H	; 4, 5, 6, 7
	DB	19H,1AH		; 8, 9
;
SPLAT:	DB	1CH		; *
;
CRUNCH:	DB	1EH		; #
;
PDIAL:	MOV	B,A		; Save the digit
	LDA	MDMTYPE		; See if we're using an external modem
	ORA	A
	MOV	A,B		; Restore the digit
	JZ	SMART		; If so, jump to the Hayes routines
	CPI	254		; Start-dial?
	JZ	STDIAL
	CPI	255		; End-dial
	JZ	ENDIAL
	CPI	','		; Smartmodem pause command
	JNZ	CKDIG		; If not pause, continue
	LDA	COMDLY		; Delay n seconds
	MOV	B,A
	ADD	A		; X 2
	ADD	A		; X 4
	ADD	A		; X 8
	ADD	B		; X 9
	ADD	B		; X 10
	MOV	B,A
	MVI	C,TIMER
	CALL	MEX
	RET
;
CKDIG:	CPI	'9'+1		; Digits are 0-9
	RNC			; Too big...return
	SUI	'0'
	JC	CKSPEC		; Too small....check if it's * or #
	PUSH	A
	LDA	TPULSE		; Check to see if tone or pulse
	CPI	'P'
	JZ	PUDIAL		; Go to pulse dial routines
	POP	A
	LXI	H,DIGITS	; Get start of digits table
	JMP	DIALIT
;
CKSPEC:	LXI	H,SPLAT		; Check for specials
	CPI	('*'-'0') AND 0FFH ; Is it *?
	JZ	DIALIT1		; Yup, go dial
	INX	H		; Point to CRUNCH
	CPI	('#'-'0') AND 0FFH ; Is it #?
	RNZ			; No, forget it
	JMP	DIALIT1		; Yup, go dial
;
; First get the value to send to the tone control register.  At DIALIT,
; HL contains the proper table, and A contains the offset into that table.
;
DIALIT:	MVI	B,0
	MOV	C,A		; Get offset into BC
	DAD	B		; Get real byte location into HL
;
DIALIT1:LDA	TPULSE		; Check for tone or pulse
	CPI	'P'
	RZ			; Return if pulse
	MOV	A,M		; Get value to dial into A
	OUT	DCRREG		; Start sending the tone
	MVI	B,1		; Send it for 100ms (which is kinda long
	MVI	C,TIMER		; But it's easier than coding my own timing
	CALL	MEX		; Routine.)
	XRA	A
	OUT	DCRREG		; Turn tone off
	MVI	B,1		; Wait for 100ms for the inter-digit delay
	MVI	C,TIMER
	CALL	MEX
	RET
;
; Pulse dialing routines (one pulse is 60ms onhook followed by 40ms off-
; hook).
;
PUDIAL:	POP	A
	CPI	0
	JNZ	PUDIAL1		; Jump if not equal to zero
	MVI	A,0AH		; 0 is 10 for pulse dialing
;
PUDIAL1:CALL	ST60		; Onhook for 60ms
	CALL	ST40		; Offhook for 40ms
	DCR	A
	CPI	0
	JNZ	PUDIAL1		; Repeat per value of each digit
	MVI	B,7		; 700ms between digits
	MVI	C,TIMER
	CALL	MEX
	RET
;
STDEL:	DCX	B		; Time delay subroutine for
	MOV	A,B		; Producing the correct pulse
	ORA	C		; Lengths
	JNZ	STDEL
	RET
;
ST60:	PUSH	A
	LDA	OCRVAL		; Connect to phone, offhook, speaker on
	ANI	(NOT OHC) AND 0FFH ; Onhook
	OUT	OCR
	LXI	B,6000		; 60ms onhook
	CALL	STDEL
	POP	A
	RET
;
ST40:	PUSH	A
	LDA	OCRVAL		; Connect to phone, offhook, speaker on
	OUT	OCR
	LXI	B,4000		; 40ms offhook
	CALL	STDEL
	POP	A
	RET
;
; Start-dial sequence:	Go thru normal init sequence, assuming that the
; guy is not trying to dial while connected.  (This should disconnect
; him anyway).
;
STDIAL:	CALL	PDISC		; Disconnect
	LDA	OCRVAL		; Turn on monitor soon
	ORI	MON
	STA	OCRVAL
	CALL	RESPORT		; I don't know why, but I gotta do this
	CALL	PWRON		; Init modem and ports
	CALL	SPKRON		; Turn power to speaker on
	LDA	OCRVAL		; Get current value for OCR
	ANI	(NOT ANS) AND 0FFH ; Set originate mode
	STA	OCRVAL
	OUT	OCR
OFFHK:	LDA	OCRVAL		; Have to reload 'cause we're called directly
	ORI	CCT		; Connect to phone
	OUT	OCR
	ORI	OHC		; Go off hook
	OUT	OCR
	STA	OCRVAL
	MVI	B,20		; Wait 2 seconds for dial tone
	MVI	C,TIMER
	CALL	MEX
	RET			; Dialing init done
;
; End-dial sequence: Watch to see if CTS goes on within 50 seconds.   If
; so, turn on carrier, turn speaker off, and connect to the line.
;
ENDIAL:	LDA	OCRVAL		; Turn on carrier
	ORI	TXC
	OUT	OCR
	STA	OCRVAL
	CALL	STPORT
	MVI	E,WTCTS		; # of ms * 5 to wait
;
LP1:	MVI	B,2		; 200 ms.
	MVI	C,TIMER
	CALL	MEX
	IN	STR		; Check modem status register
	ANI	CTSMSK		; See if we've got carrier yet
	JZ	GOTCAR		; If it's zero, then we've got carrier
	MVI	C,CHEKCC	; Not yet, see if he hit ctl-c
	CALL	MEX
	MVI	A,3		; Get ready to return code
	JZ	NOCAR		; Yup, return
	DCR	E		; Nope, count down
	JNZ	LP1		; Keep going if more time
	MVI	A,2		; Set code in A to 2 (no answer)
;
NOCAR:	PUSH	PSW		; Save the return code
	CALL	PDISC		; Hang up
	POP	PSW		; Get the return code back
	RET
;
; GOTCAR - Come here to go on-line, we've got a carrier from remote
;
GOTCAR:	CALL	SPKROFF		; Turn the speaker off
	XRA	A		; Report that we got carrier
	RET
;
;		  <end of PX-8 dialing routines>
;-----------------------------------------------------------------------
;
PBAUD:	RET			; Use SET COMM for this
;
; Sign-on message
;
SYSVER:	LXI	D,SOMESG
	MVI	C,PRINT
	CALL	MEX
	RET
;
SOMESG:	DB	'Epson PX-8 overlay V'
	DB	REV/10+'0'
	DB	'.'
	DB	REV MOD	10+'0'
	DB	CR,LF,'$'
;
; Input from the status port.  Since the PX-8 has 2 status ports, and
; we can't tell why we're being call (for input or output), we have to
; kludge here.	We'll call both in & out status, and fake a 1 byte sta-
; tus word.  See the MOD??? bytes for the values. Also, we check here
; to see if carrier has been lost (only if we were using the modem.)  If
; so, then we'll disconnect from the phone line, reset the port and tell
; the user.
;
;	 NOTE:	There is really no need for output status, so
;		these routines always return "ready to output".
;		However, the basic code structure to support
;		both input and output status has been left in
;		should it ever be necessary.
;
; Automatic X-ON/X-OFF flow control is also done from this point.
;
;	 NOTE:	For the automatic X-ON/X-OFF flow control to
;		work properly, ASIZE: must be set to 255h at
;		memory location 0D22h.	If not, use MEXPAT.ASM
;		to reset the buffers.
;
MODSTAT:PUSH	B		; Push all registers (except A)
	PUSH	D
	PUSH	H
	LDA	CTLR2		; See if we're using an MFU
	ANI	USEINT
	JNZ	MDSTAT1		; If not, forget about it
	IN	STR		; Else check modem status register
	ANI	CTSMSK
	JZ	MDSTAT1		; Everything Ok if it's there
	CALL	PDISC		; Otherwise hang up
	CALL	ILPRT		; And say something about it
	DB	CR,LF,'** Carrier Lost **',BELL,CR,LF,0
;
MDSTAT1:LDA	MODE		; Get terminal mode
	CPI	1		; 01 means in terminal mode
	JNZ	MDSTAT4		; If not, then don't worry about it
;
MDSTAT2:MVI	B,INSTS		; Check for characters in input buffer
	LXI	H,WIPEOUT	; Here's where to put the returned info
	CALL	RSIOX
	JNZ	MDSTAT2		; Wait until we get good status
	XRA	A		; Clear carry
	LXI	H,200		; 3/4 full buffer
	DB	0EDH,042H	; SBC HL,BC
	JNC	MDSTAT3		; Continue if < 200 chars in buffer
	LDA	PENDXOFF	; Is there an XOFF outstanding already?
	ORA	A		; It'll be 0 if not
	JNZ	MDSTAT4		; If so, then just go ahead
	MVI	C,XOFF		; Send out an XOFF
	CALL	RSOUT
	XRA	A
	DCR	A
	STA	PENDXOFF	; Indicate a pending XOFF
	JMP	MDSTAT4		; Continue
;
MDSTAT3:MVI	B,INSTS		; Check input again, this time we're
	LXI	H,WIPEOUT	; Seeing if it's time to send an XON
	CALL	RSIOX
	JNZ	MDSTAT3
	XRA	A
	LXI	H,50
	DB	0EDH,042H	; SBC HL,BC
	JC	MDSTAT4		; Don't send XON until < 50 chars in buffer
	LDA	PENDXOFF	; Ok, we've got < 50, did we send an XOFF?
	ORA	A
	JZ	MDSTAT4		; No, so don't do anything
	MVI	C,XON
	CALL	RSOUT		; Send the XON
	XRA	A
	STA	PENDXOFF
;
MDSTAT4:CALL	RSINST		; Now get status
	ANI	MDRCVB		; Mask off what we don't care about
	ORI	MDSNDB		; Always say that output is ready
;
POPEM:	POP	H
	POP	D
	POP	B
	RET
;
; Get a character
;
MODIN:	PUSH	B
	PUSH	D
	PUSH	H
	MVI	B,INSTS
	LXI	H,STATBLK
	CALL	RSIOX		; Make sure there is really data so we
	ORA	A		; Don't ever hang waiting
	JZ	MODIN1
	CALL	RSIN
;
MODIN1:	POP	H
	POP	D
	POP	B
	RET
;
; Send a character
;
MODOUT:	PUSH	B
	PUSH	D
	PUSH	H
	MOV	C,A
	CALL	RSOUT
	POP	H
	POP	D
	POP	B
	RET
;
; Type character in A on console
;
TYPE:	PUSH	H		; Save 'em
	PUSH	D
	PUSH	B
	MOV	C,A		; Align output character
	CALL	CONOUT
	POP	B
	POP	D
	POP	H
	RET
;
; SETPORT - set up to use internal modem
;
STPORT:	LDA	CTLR2		; Get current port info
	ANI	(NOT USEINT) AND 0FFH ; Use internal modem
;
SPORT:	STA	CTLR2
	OUT	MCTLR2
	RET
;
; RESPORT - set up to use external modem
;
RESPORT:LDA	CTLR2		; Get current port info
	ORI	USEINT		; Use external modem
	JMP	SPORT		; Store & send to modem
;
; PWRON - Turn power on to the modem (& some other initialization stuff)
;
PWRON:	MVI	A,MDMINIT	; Initialize the 7508
	OUT	OMR
	LDA	OCRVAL		; Get current parameters
	ORI	PWR		; Turn the power on
	OUT	OCR
	STA	OCRVAL		; Save the parameters
	MVI	B,1		; Wait a while to let things settle
	MVI	C,TIMER
	CALL	MEX
	RET
;
; SPKRON - Turn the speaker on
;
SPKRON:	MVI	A,0FFH
	STA	SLVFLG
	LXI	D,SLVPRAM1
	CALL	SLAVE
	RET
;
; SPKROFF - Turn speaker off
;
SPKROFF:LDA	OCRVAL		; Get current params
	ANI	(NOT MON) AND 0FFH ; We're not monitoring anymore
	OUT	OCR
	STA	OCRVAL
	MVI	A,0FFH
	STA	SLVFLG
	LXI	D,SLVPRAM2
	CALL	SLAVE
	RET
;
; Print in-line message ... blows away C register
;
ILPRT:	MVI	C,ILP		; Get function code
	JMP	MEX		; Go do it
;
; PLACECUR - Place cursor at row/col specified by BC.
;
PLACECUR:
	PUSH	PSW
	MOV	A,B
	ADI	01FH		; Add in offset
	STA	ROW
	MOV	A,C
	ADI	01FH		; Add in offset
	STA	COL
	CALL	ILPRT
	DB	ESC,'='
;
ROW:	DS	1
;
COL:	DS	1
	DB	0
	POP	PSW
	RET
;
; Keyin - get a character into A
;
KEYIN:	PUSH	B
	PUSH	D
	PUSH	H
	CALL	CONIN		; Direct console input
	POP	H
	POP	D
	POP	B
	RET
;
; UCASE - Convert character in A to uppercase
;
UCASE:	CPI	'a'
	RC			; Return if not lower case
	CPI	'z'+1
	RNC			; Return if > lower case z
	ANI	05FH		; Else change to upper case
	RET
;
; SMSPEED - Set time to transfer speed
;
SMSPEED:LDA	MDMSPD		; Get locally stored speed value
	MOV	C,A		; Put in lower half of BC
	MVI	B,0		; Zap upper half of BC
	LXI	H,XSPDTBL	; Get address of xfer speed table
	DAD	B		; Get address of time for xfer byte
	MOV	A,M		; Get the value
	STA	MSPEED		; Store it
	RET
;
; Data area
;
;
; XSPDTBL - transfer speed table. The number in parenthesis is the value
;	    given to MEX.  This differs from the actual baud rate in
;	    some cases due to the baud rates available on the PX-8.
;
XSPDTBL:  DB	0		; Unused
	  DB	0		; 110 Baud	 (110)
	  DB	0		; 150 Baud	 (110)
	  DB	1		; 300 Baud	 (300)
	  DB	3		; 800 Baud	 (600)
	  DB	5		; 1200 Baud	 (1200)
	  DB	6		; 2400 Baud	 (2400)
	  DB	7		; 4800 Baud	 (4800)
	  DB	8		; 9600 Baud	 (9600)
	  DB	9		; 19200 Baud	 (19200)
;
EOSMSG:	  DB	ESC,'Y','$'	; Clear to end-of-screen
CLSMSG:	  DB	ESC,'*','$'	; Clear whole screen
OCRVAL:	  DB	0		; OCR register value
MDMTYPE:  DB	0		; Modem type, 0FFH=external, 0=internal
GOTDISC:  DB	0		; Got external disconnect routine, FF=Yes
MDMSPD:	  DS	1		; Current port speed
COMDLY:	  DB	2		; #secs to wait for , in dial string
PENDXOFF: DB	0		; 0FFH if XOFF pending, 0 otherwise
STATBLK:  DS	9		; Returned info from RSIOX INSTS call
;
;--------------------Start of slave CPU data----------------------------
;
; The following group of data items are for using the slave CPU.
;
;	DO NOT ADD OR REMOVE ANYTHING IN THIS AREA!!!
;
SLVPRAM1: DW	ONSNDCMD
	  DW	ONSNDLEN
	  DW	ONRETVAL
	  DW	ONRETLEN
;
ONSNDCMD: DB	072H,080H
ONSNDLEN: DB	2
ONRETVAL: DB	1
ONRETLEN: DB	1
SLVPRAM2: DW	OFFSDCMD
	  DW	OFFSDLEN
	  DW	OFFRTVAL
	  DW	OFFRTLEN
OFFSDCMD: DB	072H,0
OFFSDLEN: DB	2
OFFRTVAL: DB	1
OFFRTLEN: DB	1
;
;--------------------End of Slave CPU data------------------------------
;
; 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,'$'
;
CMDTBL:	DB	'?'+80H		; "set ?"
	DW	STHELP
	DB	'OFFHOO','K'+80H ; "set offhook"
	DW	SETOFF
	DB	'COM','M'+80H	; "set comm"
	DW	SETCOMM
	DB	'DELA','Y'+80H	; "set delay"
	DW	SETDLY
	DB	'PULS','E'+80H	; "set pulse"
	DW	SETPUL
	DB	0		; <<=== table terminator
;
; SET <no-args>: print current statistics
;
SETSHO:	CALL	CARRSH		; 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
	MVI	C,CHEKCC	; Check for console abort
	CALL	MEX
	POP	H		; It's done
	JNZ	SETSLP		; Continue if no abort
	RET
;
GOHL:	PCHL
;
; table of SHOW subroutines
;
SHOTBL:	DW	SHSEP
	DW	SHHOOK
	DW	SHDLY
	DW	SHPUL
	DW	SHSEP
	DW	0		; <<== table terminator
;
; LF seperator
;
SHSEP:	CALL	ILPRT
	DB	CR,LF,0
	RET
;
; SET ?  processor
;
STHELP:	LXI	D,HLPMSG
	MVI	C,PRINT
	CALL	MEX
	RET
;
; The help message
;
HLPMSG:	DB	0CH,'SET command, PX-8 version:',CR,LF
	DB	CR,LF,'SET COMM     set/display comm parameters'
	DB	CR,LF,'SET DELAY n  set delay for comma in dial string'
	DB	CR,LF,'SET PULSE    set pulse dialing'
	DB	CR,LF,'SET OFFHOOK  go offhook'
	DB	CR,LF,LF,'$'
;
; Show carrier status
;
CARRSH:	CALL	CARRCK		; Check for it
	LXI	D,NOMESG	; Tell about carrier
	MVI	C,PRINT
	CZ	MEX		; Print the "NO" if no carrier
	LXI	D,CARMSG	; Print "carrier present"
	MVI	C,PRINT
	CALL	MEX
	RET
;
NOMESG:	DB	'no $'
CARMSG:	DB	'carrier present',CR,LF,'$'
;
; Check the PX-8 for carrier-present (Z=no)
;
CARRCK:	MVI	B,CTLIN		; Get status byte
	CALL	RSIOX
	ANI	DCD		; Check for carrier detect
	RET
;
; Set OFFHOOK processor
;
SETOFF:	CALL	PDISC		; Disconnect if anything was there
	CALL	PWRON		; Fire up the modem
	CALL	OFFHK
;
SHHOOK:	CALL	ILPRT
	DB	'modem is ',0
	LDA	OCRVAL
	ANI	OHC
	JZ	SHONHK
	CALL	ILPRT
	DB	'off-hook',CR,LF,0
	RET
;
SHONHK:	CALL	ILPRT
	DB	'on-hook',CR,LF,0
	RET
;
; Set delay for comma
;
SETDLY:	MVI	C,EVALA		; Get the number
	CALL	MEX
	MOV	A,H		; Validate
	ORA	A
	JNZ	SETERR
	MOV	A,L
	STA	COMDLY		; Store new rate
;
; Show dialing delay
;
SHDLY:	CALL	ILPRT
	DB	'delay time for dialing: ',0
	LDA	COMDLY
	MOV	L,A
	MVI	H,0
	MVI	C,DECOUT
	CALL	MEX
	CALL	ILPRT
	DB	' sec',CR,LF,0
	RET
;
; Set for tone or pulse dialing
;
SETPUL:	MVI	C,SBLANK	; Any arguments?
	CALL	MEX
	JC	PULSON		; If not, pulse on
	MVI	A,'T'		; Tone dial flag
	STA	TPULSE
	JMP	SHPUL
PULSON:	MVI	A,'P'		; Pulse dial flag
	STA	TPULSE
;
; Show tone or pulse status
;
SHPUL:	CALL	ILPRT
	DB	'dialing mode: ',0
	LDA	TPULSE
	CPI	'T'
	JZ	SHPUL1
	CALL	ILPRT
	DB	'pulse',CR,LF,0
	RET
;
SHPUL1:	CALL	ILPRT
	DB	'tone',CR,LF,0
	RET
;
; 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
;
SETCOMM:CALL	CLS		; Clear the screen
	LXI	D,MENU
	MVI	C,PRINT
	CALL	MEX		; Display the setup menu
;
; Display current values on setup screen
;
	CALL	DBAUD
	CALL	DDBITS
	CALL	DPARITY
	CALL	DSTP
	CALL	DMODEM
;
; Get user's response and set paramiters
;
SETUP0:	CALL	KEYIN
	CALL	UCASE
	CPI	ESC
	JZ	SETUP
	CPI	'9'+1		; Greater than baud selection
	JNC	SDBITS		; Check for data bits
	CPI	'1'		; Less than 1?
	JC	SETUP0		; Get another response
	ANI	0FH
	MOV	E,A
	MVI	A,10
	SUB	E
	STA	MDMSPD
	MOV	C,A
	MVI	B,0
	LXI	H,BAUDTBL
	DAD	B
	MOV	A,M
	STA	BAUDRATE
	CALL	DBAUD
	JMP	SETUP0
;
SDBITS:	CPI	'B'+1		; Greater than data bits
	JNC	SPARITY		; Check for parity
	CPI	'A'		; Less than 'a'
	JC	SETUP0		; Get another response
	SUI	3FH		; Make 2 or 3
	STA	DBITS
	CALL	DDBITS
	JMP	SETUP0
;
SPARITY:CPI	'E'+1		; Greater than parity
	JNC	SSTOP		; Check for stop bits
	CPI	'E'		; Check for "even" parity
	JNZ	SPAR0
	SUI	'B'		; Even parity = 3
	JMP	SPAR1
;
SPAR0:	SUI	'C'		; Make 0 or 1
;
SPAR1:	STA	PARBITS
	CALL	DPARITY
	JMP	SETUP0
;
SSTOP:	CPI	'G'+1		; Greater than stop bits
	JNC	SMOD		; Check for modem
	SUI	'F'-1
	CPI	1
	JZ	SSTOP1
	ADI	1		; Two stop bits = 3
;
SSTOP1:	STA	STP
	CALL	DSTP
	JMP	SETUP0
;
SMOD:	CPI	'I'+1		; Greater than baud
	JNC	SETUP0		; Bad selection get another response
	SUI	'H'
	JZ	SMOD1
	CALL	PWRON		; Try to turn the modem on
	IN	STR		; Read status register
	ANI	MII		; Is modem installed?
	JNZ	SMOD2
	MVI	A,0FFH
;
SMOD1:	STA	MDMTYPE
	CALL	DMODEM
	JMP	SETUP0
;
SMOD2:	MVI	A,BELL
	CALL	TYPE
;
SMOD3:	LXI	B,0201H
	CALL	PLACECUR
	CALL	ILPRT
	DB	TAB,'** NO MODEM **',0
	XRA	A
	STA	MDMTYPE
	CALL	DMODEM
	JMP	SETUP0
;
; Display baud rate
;
DBAUD:	LXI	B,30DH
	CALL	PLACECUR
	LXI	H,BAUDSPD
	MVI	D,0
	LDA	MDMSPD		; Get baud rate code
	CPI	0FFH
	RZ			; Unknown baud rate
	MOV	E,A		; X1
	ADD	A		; X2
	ADD	A		; X4
	ADD	E		; X5
	ADD	E
	MOV	E,A
	DAD	D		; Point to correct rate
	XCHG
	MVI	C,PRINT
	CALL	MEX
	CALL	ILPRT
	DB	' bps ',CR,LF,0
	RET
;
; Display data bits
;
DDBITS:	LXI	B,50DH
	CALL	PLACECUR
	LDA	DBITS
	CPI	2
	MVI	A,'7'
	JZ	DDBITS1
	MVI	A,'8'
;
DDBITS1:CALL	TYPE
	RET
;
; Display parity
;
DPARITY:LXI	B,60DH
	CALL	PLACECUR
	LDA	PARBITS
	ORA	A
	JNZ	DPAR1
	CALL	ILPRT
	DB	'none',0
	RET
DPAR1:	CPI	1
	JNZ	DPAR2
	CALL	ILPRT
	DB	'odd ',0
	RET
DPAR2:	CALL	ILPRT
	DB	'even',0
	RET
;
; Display stop bits
;
DSTP:	LXI	B,70DH
	CALL	PLACECUR
	LDA	STP
	CPI	1
	MVI	A,'1'
	JZ	DSTP1
	MVI	A,'2'
;
DSTP1:	CALL	TYPE
	RET
;
; Display modem type
;
DMODEM:	LXI	B,80DH
	CALL	PLACECUR
	LDA	MDMTYPE
	ORA	A
	JNZ	DMODEM1
	CALL	ILPRT
	DB	'external',0
	RET
;
DMODEM1:CALL	ILPRT
	DB	'internal',0
	RET
;
SETUP:	CALL	STPORT		; Set up for internal modem just in case
	CALL	SMSPEED		; Set time to xfer speed
	LDA	MDMTYPE		; See what kind of modem we're using
	INR	A		; If FF, then it's internal
	JZ	SETUP1		; Yep, it's internal
	CALL	RESPORT		; Using external, do setup
;
SETUP1:	LXI	H,PARAMS	; Copy all settings to RSIOX string
	LXI	D,WIPEOUT
	LXI	B,9
	DB	0EDH,0B0H	; LDIR
	LXI	H,BAUDRATE	; Copy uart settings to working area
	LXI	D,WRSDAT
	LXI	B,4
	DB	0EDH,0B0H	; LDIR
	MVI	B,CLSIOX
	CALL	RSIOX		; Close port first
	MVI	B,OPNIOX
	LXI	H,WIPEOUT
	CALL	RSIOX		; Open with new parameters
	CALL	CLS
	CALL	ILPRT
	DB	ESC,CURON
	DB	CR,LF,LF,LF
	DB	TAB,TAB,'Communication initialization completed',CR,LF
	DB	LF,LF,0
	RET
;
BAUDSPD:DB	'50   $110  $150  $300  $600  $'
	DB	'1200 $2400 $4800 $9600 $19200$'
;
BAUDTBL:DB	0FFH		; 50   baud not supported
	DB	2		; 110  baud
	DB	4		; 150  baud
	DB	6		; 300  baud
	DB	8		; 600  baud
	DB	0AH		; 1200 baud
	DB	0CH		; 2400 baud
	DB	0DH		; 4800 baud
	DB	0EH		; 9600 baud
	DB	0FH		; 19.2k baud
;
MENU:	DB	ESC,CUROFF
	DB	TAB,'Select alphanumeric or ESC to return',CR,LF,LF
	DB	'bit rate  :           1=19200    2=9600     3=4800'
	DB	'     4=2400'
	DB	'     5=1200',CR,LF
	DB	'                      6=600      7=300      8=150'
	DB	'      9=110',CR,LF
	DB	'data bits :           A=7        B=8',CR,LF
	DB	'parity    :           C=none     D=odd      E=even'
	DB	CR,LF
	DB	'stop bits :           F=1        G=2',CR,LF
	DB	'modem type:           H=external I=internal$'
;
PARAMS:	  DW	INTBUF
	  DW	INTBUFL
BAUDRATE: DS	1
DBITS:	  DS	1
PARBITS:  DS	1
STP:	  DS	1
SPECIAL:  DB	0FFH		; Not XON/XOFF, not SI/SO, DTR on, RTS on
;
WIPEOUT:  DS	9		; This area gets overwritten by BIOS call
;
;-----------------------------------------------------------------------
;
;		   End of PX-8 MEX modem overlay
;
;-----------------------------------------------------------------------
;
	END
