; MXH-CPM3.ASM  --  MEXP hardware overlay for CP/M Plus.
;
; This MEXP overlay includes SET and ABAUD routines (for
; setting and ; automatically changing baud rates) which
; use  generic  CP/M+  BDOS  calls. The SET command also
; allows one to switch between any two CP/M+ i/o devices
; (called modem and xmodem here).  As  such, the overlay
; can be configured for any CP/M+ system  in  which  the
; character i/o device table has been implemented simply
; by  including  the hardware dependent modem and xmodem
; port specifications  in  the tables MODTBL and XMODTBL
; at the end of the overlay.
;
; MXH-CPM3.ASM  was  derived  from  the  general purpose
; overlay  file  M7GP-1.ASM  with  baudrate  code   from
; M7C3-1.ASM by R. Saeks - 5/7/85.
;
VERSION:	EQU	00H		;Version number
;
BELL:		EQU	07H		;bell
CR:		EQU	0DH		;carriage return
ESC:		EQU	1BH		;escape
LF:		EQU	0AH		;linefeed
YES:		EQU	0FFH
NO:		EQU	0
BDOS:		EQU	5
DUMMY:		EQU	0		;dummy byte to be filled in
					;by SETPORT with correct data
;
MODFIRST:	EQU	YES		;yes if modem port at logon
XMODFIRST:	EQU	NO		;yes if xmodem port at logon
;
; The following JUMP instruction is necessary for MEXPLUS
; loadable overlays.
;
		ORG	100H
		DB	0C3H		; JMP instruction for MEX 1.2
		DS	2	;(for  "JMP   START" instruction)
;
; The following DB statements define the initial MEX parameters.
;
PMMIMODEM:	DB	NO	;yes=PMMI S-100 Modem			103H
SMARTMODEM:	DB	YES	;yes=HAYES Smartmodem, no=non-PMMI	104H
TOUCHPULSE:	DB	'T'	;T=touch, P=pulse (Smartmodem-only)	105H
CLOCK:		DB	40	;clock speed in MHz x10, 25.5 MHz max.	106H
				;20=2 MHh, 37=3.68 MHz, 40=4 MHz, etc.
MSPEED:		DB	5	;0=110 1=300 2=450 3=600 4=710 5=1200	107H
				;6=2400 7=4800 8=9600 9=19200 default
BYTDLY:		DB	5	;0=0 delay  1=10ms  5=50 ms - 9=90 ms	108H
				;default time to send character in ter-
				;minal mode file transfer for slow BBS.
CRDLY:		DB	5	;0=0 delay 1=100 ms 5=500 ms - 9=900 ms 109H
				;default time for extra wait after CRLF
				;in terminal mode file transfer
NOOFCOL:	DB	5	;number of DIR columns shown		10AH
SETUPTST:	DB	YES	;yes=user-added Setup routine		10BH
SCRNTEST:	DB	YES	;Cursor control routine 		10CH
ACKNAK:		DB	YES	;yes=resend a record after any non-ACK	10DH
				;no=resend a record after a valid-NAK
BAKUPBYTE:	DB	YES	;yes=change any file same name to .BAK	10EH
CRCDFLT:	DB	YES	;yes=default to CRC checking		10FH
TOGGLECRC:	DB	YES	;yes=allow toggling of CRC to Checksum	110H
CONVBKSP:	DB	NO	;yes=convert backspace to rub		111H
TOGGLEBK:	DB	YES	;yes=allow toggling of bksp to rub	112H
ADDLF:		DB	NO	;no=no LF after CR to send file in	113H
				;terminal mode (added by remote echo)
TOGGLELF:	DB	YES	;yes=allow toggling of LF after CR	114H
TRANLOGON:	DB	NO	;yes=allow transmission of logon	115H
				;write logon sequence at location LOGON
SAVCCP:		DB	NO	;yes=do not overwrite CCP		116H
LOCONEXTCHR:	DB	NO	;yes=local command if EXTCHR precedes	117H
				;no=external command if EXTCHR precedes
TOGGLELOC:	DB	YES	;yes=allow toggling of LOCONEXTCHR	118H
LSTTST:		DB	YES	;yes=printer available on printer port	119H
XOFFTST:	DB	NO	;yes=checks for XOFF from remote while	11AH
				;sending a file in terminal mode
XONWAIT:	DB	NO	;yes=wait for XON after CR while	11BH
				;sending a file in terminal mode
TOGXOFF:	DB	YES	;yes=allow toggling of XOFF checking	11CH
IGNORCTL:	DB	YES	;yes=CTL-chars above ^M not displayed	11DH
EXTRA1:		DB	0	;for future expansion			11EH
EXTRA2:		DB	0	;for future expansion			11FH
BRKCHR:		DB	'@'-40H	;^@ = Send 300 ms. break tone		120H
NOCONNCT:	DB	'N'-40H	;^N = Disconnect from the phone line	121H
LOGCHR:		DB	'L'-40H	;^L = Send logon			122H
LSTCHR:		DB	'P'-40H	;^P = Toggle printer			123H
UNSAVE:		DB	'R'-40H	;^R = Close input text buffer		124H
TRANCHR:	DB	'T'-40H ;^T = Transmit file to remote		125H
SAVECHR:	DB	'Y'-40H	;^Y = Open input text buffer		126H
EXTCHR:		DB	'^'-40H	;^^ = Send next character		127H
;
;
		DS	2		;				128H
;
IN$MODCTL1:	IN	08DH	! RET	;in modem control port		12AH
		DS	7
OUT$MODDATP:	OUT	08CH	! RET	;out modem data port		134H
		DS	7
IN$MODDATP:	IN	08CH	! RET	;in modem data port		13EH
		DS	7
ANI$MODRCVB:	ANI	02H	! RET	;bit to test for receive ready	148H

CPI$MODRCVR:	CPI	02H	! RET	;value of rcv. bit when ready	14BH
ANI$MODSNDB:	ANI	04H	! RET	;bit to test for send ready	14EH
CPI$MODSNDR:	CPI	04H	! RET	;value of send bit when ready	151H
;
;  Following are two new entry points for MEXPLUS
;  DCDTST returns data-carrier detect in A: 0 if no carrier
;  present, 0FFH if carrier is present, and 0FEH if overlay
;  doesn't know (i.e., doesn't support carrier detect)
;  RNGDET works similarly for ring-detect.
;
DCDTST:	JMP	DCDVEC		;data carrier detect			154H
RNGDET:	JMP	RNGVEC		;ring-detect				157H
;
; End of MEXPLUS added vectors
;
OUT$MODCTL1:	OUT	08DH	! RET	;out modem control port #2	15AH
OUT$MODCTL2:	OUT	08DH	! RET	;out modem control port #1	15DH
;
LOGONPTR:	DW	LOGON		;for user message.		160H
		DS	6		;				162H
JMP$GOODBYE:	JMP	GOODBYE		;				168H
JMP$INIT:	JMP	INIT		;go to user written routine	16BH
JMP$ABAUD:	JMP	ABAUD		;auto baud rate change		16EH
		RET  !	NOP  !	NOP	;(by-passes PMMI routine)	171H
		RET  !	NOP  !	NOP	;(by-passes PMMI routine)	174H
JMP$SETUPR:	JMP	SETUPR		;				177H
JMP$SPCLMENU:	JMP	SPCLMENU	;				17AH
JMP$SYSVER:	JMP	SYSVER		;				17DH
JMP$BREAK:	JMP	SENDBRK		;				180H
;
;
; Do not change the following six lines.
;
JMP$ILPRT:	DS	3		;				183H
JMP$INBUF	DS	3		;				186H
JMP$INLNCOMP:	DS	3		;				189H
JMP$INMODEM	DS	3		;				18CH
JMP$NXTSCRN:	DS	3		;				18FH
JMP$TIMER	DS	3		;				192H
;
;
; Routine to clear to end of screen.  If using CLREOS and CLRSCRN, set
; SCRNTEST to YES at 010AH (above).
;
CLREOS:		CALL	JMP$ILPRT	;VT-52 change for 		195H
		DB	ESC,'k',0,0,0	;your terminal			198H
		RET			;				19DH
;
CLRSCRN:	CALL	JMP$ILPRT	;VT-52 change for		19EH
		DB	ESC,'v',0,0,0	;your terminal			1A1H
		RET			;				1A6H
	
;
SYSVER:		CALL	JMP$ILPRT	;				1A7H
		DB	'Version for CP/M Plus'
		DB	CR,LF,'Hardware Overlay MXH-CPM3'
                DB      VERSION / 10 + ' ',VERSION mod 10 + ' '
		DB	CR,LF,0
		RET
;.....
;
;
;-----------------------------------------------------------------------
;
; NOTE:  You can change the SYSVER message to be longer or shorter
; so long as the maximum length for the overlay is not exceeded.
;
;-----------------------------------------------------------------------
;
; You can put in a message at this location which can be called up with
; CTL-O if TRANLOGON has been set TRUE.  You can use several lines if
; desired.  End with a 0.
;
LOGON:		DB	'How are you today?',CR,LF,0
;.....
;
;
; Add your own routine here to send a break tone to reset some time-share
; computers, if desired.
;
SENDBRK:	RET
;.....
;
;
; Add your own routine here to put DTR low and/or send a break tone.
;
GOODBYE:	RET
;.....
;
;  return data carrier detect status
;      0    = no carrier
;      255  = carrier present
;      254  = we don't know (DCD not supported)
;
DCDVEC:		MVI	A,0FEH		;return 255 if carrier detect
		RET
;......
;
;  return ring-detect status:
;
;     0   =  not ringing
;     255 =  ring detected
;     254 =  we don't know
;
RNGVEC:		MVI	A,0FEH		;return "we don't know"
		RET
;
;
;
; You can use this area for any special initialization or setup you may
; wish to include.  Each must stop with a RET.	You can check the other
; available overlays for ideas how to write your own routines if that
; may be of some help.
;
INIT:	  	MVI	C,12		;routine to check if CP/M+
		CALL	BDOS		;Get version #
		CPI	30H
		JNC	INITPORT	;jump if CP/M+
		CALL	JMP$ILPRT	;return error message if not CP/M+
		DB	'Requires CP/M Version 3',CR,LF,0
		RST	0

INITPORT:	MVI	A,3EH
		OUT	9DH
		MVI	A,27H
		OUT	8DH
		RET
;
; Routine to automatically set baud rate from data stored
; in phone directory
;
ABAUD:		PUSH	H		;don't alter anybody
		PUSH	D
		PUSH	B
		CPI	1		;check if 300 baud
		JZ	OK300
		CPI	5		;check if 1200 baud
		JZ	OK1200
		CPI	6		;check if 2400 baud
		JZ	OK2400
		CPI	7		;check if 4800 baud
		JZ	OK4800
		CPI	8		;check if 9600 baud
		JZ	OK9600
		STC			;return error for STBAUD caller
		JMP	SETEXIT		;restore BC, DE and HL regs
;
; Routine to set baud rate from command line input
;
SETUPR:		PUSH	H		;don't alter anybody
		PUSH	D
		PUSH	B
SETUPR1:  	CALL	JMP$ILPRT	;display SET message
		DB	CR,LF,'             Input Baud Rate or Port'
		DB	CR,LF,'<300, 1200, 2400, 4800, 9600,'
		DB	' (M)odem, or (X)modem>: ',0
		LXI	D,SETBUF
		CALL	JMP$INBUF	;input command
		LXI	D,SETBUF+2
		CALL	JMP$INLNCOMP
		DB	'300',0		;check if 300 baud
		JNC	OK300
		CALL	JMP$INLNCOMP
		DB	'1200',0	;check if 1200 baud
		JNC	OK1200
		CALL	JMP$INLNCOMP
		DB	'2400',0	;check if 2400 baud
		JNC	OK2400
		CALL	JMP$INLNCOMP
		DB	'4800',0	;check if 4800 baud
		JNC	OK4800
		CALL	JMP$INLNCOMP
		DB	'9600',0	;check if 9600 baud
		JNC	OK9600
		CALL	JMP$INLNCOMP
		DB	'M',0		;check if Modem port
		JNC	SETMOD
		CALL	JMP$INLNCOMP
		DB	'm',0
		JNC	SETMOD
		CALL	JMP$INLNCOMP
		DB	'X',0		;check if Xmodem port
		JNC	SETXMOD
		CALL	JMP$INLNCOMP
		DB	'x',0
		JNC	SETXMOD
		CALL	JMP$ILPRT
		DB	'              ++ Incorrect entry ++',CR,LF,BELL,0
		JMP	SETUPR1		;repeate if incorrect entry


SETMOD:		LXI	H,MODTBL	;load modem port equate table
		CALL	SETPORT		;set MODTBL equates in DUMMY bytes
		JMP	SETEXIT		;restore BC, DE, and HL regs

SETXMOD:	LXI	H,XMODTBL	;load xmodem port equate table
		CALL	SETPORT		;set XMODTBL equates in DUMMY bytes
		JMP	SETEXIT		;restore BC, DE, and HL regs
;
;subroutine to insert port addresses and masks into DUMMY
;bytes in mex I/O subroutines.  Routine enters with the
;address of the port data table in reg hl.
;

SETPORT:
		MOV	A,M		;get modem control port
		STA	IN$MODCTL1+1
		STA	OUT$MODCTL1+1
		STA	OUT$MODCTL2+1
		INX	H		;get modem data port
		MOV	A,M
		STA	OUT$MODDATP+1
		STA	IN$MODDATP+1
		INX	H		;get carrier detact bit
		MOV	A,M
		STA	DCDVEC+5
		INX	H		;get receive ready bit
		MOV	A,M
		STA	ANI$MODRCVB+1
		INX	H		;get receive ready mask
		MOV	A,M
		STA	CPI$MODRCVR+1
		INX	H		;get trans ready bit
		MOV	A,M
		STA	ANI$MODSNDB+1
		INX	H		;get trans ready mask
		MOV	A,M
		STA	CPI$MODSNDR+1
		INX	H		;get CPM3 device number
		MOV	A,M
		STA	DEVICE
;
;routine to compute the address of baudrate parameter
;in the CPM3 char i/o device table for the specified device.
;

ADCHRTBL:	MVI	A,20		;BIOS call to return character I/O
		STA	FUNC		;device table address
		MVI	C,50		;cp/m+ direct BIOS call
		LXI	D,FUNC		;direct BIOS call data structure
		CALL	BDOS
		PUSH	H		;Save the table address
		LXI	H,7		;Offset to baudrate
		LDA	DEVICE
		ORA	A
		JZ	ISZERO		;jump if cp/m+ i/o device # 0
		LXI	D,8

DEVLP:		DAD	D		;add (8xDEV #) to baudrate offset
		DCR	A
		JNZ	DEVLP

ISZERO:		POP	D		;Get Table address
		DAD	D		;Form baudrate address
		SHLD	BAUDAD		;save baudrate address for DEV #
		RET
;
;subroutines to load mspeed and cp/m+ baudrate codes 
;
						;baudrate  mspeed  cpm3
OK300:		MVI	A,1	;300 baud	;           code   code
		MVI	B,6			;-----------------------
		JMP	LOADBD			;300          1     6
						;600          3     7
OK1200:		MVI	A,5	;1200 baud	;1200         5     8
		MVI	B,8			;2400         6    10
		JMP	LOADBD			;4800         7    12
						;9600         8    14
OK2400		MVI	A,6	;2400 baud	;19200        9    15
		MVI	B,10
		JMP	LOADBD

OK4800		MVI	A,7	;4800 baud
		MVI	B,12
		JMP	LOADBD

OK9600		MVI	A,8	;9600 baud
		MVI	B,14	;fall through to LOADBD
;
;routine to store mspeed baudrate code (reg a) in MSPEED and
;cp/m+ baudrate code (reg b) at (BAUDAD) in i/o device table
;and to reset the i/o port to the new baudrate.
;

LOADBD:		STA	MSPEED		;store mspeed
		LHLD	BAUDAD		;set CPM3 char table
		MOV	M,B
		LDA	DEVICE		;reset device
		MOV	L,A		;store cp/m+ device number
		MVI	H,0		;in "reg c" of direct BIOS
		SHLD	BCREG		;data structure
		MVI	A,21		;device init BIOS call
		STA	FUNC
		MVI	C,50		;cp/m+ direct BIOS call
		LXI	D,FUNC		;direct BIOS call data structure
		CALL	BDOS
SETEXIT:	POP	B		;restore BC, DE, and HL regs
		POP	D
		POP	H
		RET
;
DEVICE:		DB	DUMMY		;storage for cp/m+ device number
SETBUF:		DB	10,0
		DS	2

FUNC:		DS	1		;cp/m+ direct BIOS call
AREG:		DS	1		;data structure
BCREG:		DS	2
DEREG:		DS	2
HLREG:		DS	2
BAUDAD:		DS	2		;storage for baudrate address
					;in char i/o device table
;
;Equates for Modem port (set for CompuPro Interfacer 4)
;
MODBASE:	EQU	08CH
;
MODTBL:
	DB	MODBASE+1		;modem control/status port
	DB	MODBASE			;modem data port
	DB	040H			;mask for carrier detect bit
	DB	02H			;mask to test for receive
	DB	02H			;value when receive ready
	DB	04H			;mask to test for send
	DB	04H			;value when ready to send
	DB	8			;cp/m+ device number
;
;Equates for Xmodem port (set for Zilog DART)
;
XMODBASE:		EQU	08CH
;
	XMODTBL:
	DB	XMODBASE+1		;modem control/status port
	DB	XMODBASE		;modem data port
	DB	040H			;mask carrier detect bit
	DB	02H			;mask to test for receive
	DB	02H			;value when receive ready
	DB	04H			;mask to test for send
	DB	04H			;value when ready to send
	DB	9			;cp/m+ device number
;
; If using the Hayes Smartmodem this is unavailable without a special
; change.
;
SPCLMENU:  RET
;
;
;.....
;
	  END
;
tructure
BCREG:		DS	2
DEREG:		DS	2
HLREG:		DS	2
BAUDAD:		DS	2		;storage for baudrate addr