;  PROGRAM: ZMO-TV05.Z80
;  AUTHOR:  Michael Evenson
;  VERSION: 1.5
;  DATE:  10 Nov, 1988
;-----------------------------------------------------------------------
;	This overlay is set up for Televideo TS80x(H) using modem port.
;	 Uses a Z80SIO AND A Z80CTC.
;		flags must be set for Televideo TS806, TS816, or TS802(H)
;		currently does not support TS803 (STI) or TS804 (MPM system)
;-----------------------------------------------------------------------
; 89/04/12 - Modified to ZMP v1.5		- George Conover
; 89/03/15 - Removed the beginning remarks to make the file
;            smaller. If you need the remarks there are in the
;            file ZMP-BLNK.Z80 			- George Conover
; 89/03/14 - Modified to ZMP v1.4		- George Conover
; 88/11/10 - First version of this file		- Michael Evenson
;
;	Written by -
;	  Ron Murray, c/o Z-Node 62, 061-9-450-0200, Perth, Western Australia.
;-----------------------------------------------------------------------
NO	EQU	0
YES	EQU	NOT NO

; User-set variables: ***********

CLKSPD	EQU	4		; Processor clock speed in MHz
DEBUG	EQU	NO

;Set the following two equates to the drive and user area which will contain
;   ZMP's .OVR files, .CFG file, .FON file and .HLP file. Set both to zero
;   (null) to locate them on the drive from which ZMP was invoked.

OVERDRIVE	EQU	'A'	; Drive to find overlay files on ('A'-'P')
OVERUSER	EQU	0	; User area to find files

;------------------------------------------------------------------------------


; NOT user-set variables
USERDEF	EQU	00145H		; origin of this overlay
				; This address should remain constant
				; with subsequent revisions.
MSPEED	EQU	003CH		; current baud rate id
OVSIZE	EQU	0400H		; max size of this overlay

	.Z80			; use z80 code
	ASEG			; absolute

	 IF	DEBUG
	ORG	100H		; so you can debug it with cebug, zsid, etc
	 ELSE
	ORG	USERDEF
	 ENDIF

;	be sure to set the correct system and port identifier here
;		(only one of each please)

TS802	EQU	YES
TS806	EQU	NO
TS816	EQU	NO

MODEMP	EQU	YES
PRINTP	EQU	NO

;

ESC	EQU	1BH
CTRLQ	EQU	11H
CR	EQU	0DH
LF	EQU	0AH
BDOS	EQU	5


; Televideo specific equates  ******************************************

	 IF	TS802
	  IF	MODEMP
MDATA	EQU	020H
MSTAT	EQU	022H
CTC	EQU	08H
	  ELSE
MDATA	EQU	021H
MSTAT	EQU	023H
CTC	EQU	09H
	 ENDIF

	 ELSE

	  IF	TS806
	   IF	MODEMP
MDATA	EQU	050H
MSTAT	EQU	052H
CTC	EQU	80H
	   ELSE
MDATA	EQU	051H
MSTAT	EQU	053H
CTC	EQU	81H
	   ENDIF
	  ELSE
	   IF	TS816
	    IF	MODEMP
MDATA	EQU	059H		; channel B
MSTAT	EQU	05BH
CTC	EQU	C1H		; CTC 1
	    ELSE
MDATA	EQU	058H		; channel A
MSTAT	EQU	05AH
CTC	EQU	C0H		; CTC 0
	    ENDIF
	   ENDIF
	  ENDIF
	 ENDIF

RDA	EQU	0
TBE	EQU	2
MODE	EQU	47H
ONHOOK	EQU	7FH
ONLINE	EQU	80H
ERROR	EQU	70H
BREAK	EQU	0F8H
RESET	EQU	30H
;			******************************************

; Main code starts here

CODEBGN	EQU	$

;Jump table for the overlay: do NOT change this
JUMP_TAB:
	JP	SCRNPR		; screen print
	JP	MRD		; modem read with timeout
	JP	MCHIN		; get a character from modem
	JP	MCHOUT		; send a character to the modem
	JP	MORDY		; test for tx buffer empty
	JP	MIRDY		; test for character received
	JP	SNDBRK		; send break
	JP	CURSADD		; cursor addressing
	JP	CLS		; clear screen
	JP	INVON		; inverse video on
	JP	INVOFF		; inverse video off
	JP	HIDE		; hide cursor
	JP	SHOW		; show cursor
	JP	SAVECU		; save cursor position
	JP	RESCU		; restore cursor position
	JP	MINT		; service modem interrupt
	JP	INVEC		; initialise interrupt vectors
	JP	DINVEC		; de-initialise interrupt vectors
	JP	MDMERR		; test uart flags for error
	JP	DTRON		; turn DTR on
	JP	DTROFF		; turn DTR OFF
	JP	INIT		; initialise uart
	JP	WAIT		; wait seconds
	JP	MSWAIT		; wait milliseconds
	JP	USERIN		; user-defined entry routine
	JP	USEROUT		; user-defined exit routine
	JP	GETVARS		; get system variables
  	JP	SETPORT		; Set port (0 or 1)
;
; Spare jumps for compatibility with future versions
;
	JP	SPARE		; spare for later use
	JP	SPARE		; spares for later use
	JP	SPARE		; spares for later use
	JP	SPARE		; spares for later use
	JP	SPARE		; spares for later use
	JP	SPARE		; spares for later use

SPARE:	RET

;-------------------------------------------------------------------------


;Screen print function
SCRNPR:
				; <== Insert your own code here
	CALL	PRINT
	DB	'This function not supported.',CR,LF,0
				; <== End of your own code
	RET


;Get a character from the modem: return in HL
MCHIN:
	PUSH	BC
				; <== Insert your own code here

MCHIN2:
	IN	A,(MSTAT)	; check for char waiting
	BIT	RDA,A
	JR	Z,MCHIN2

	IN	A,(MDATA)	; read the char


				; <== End of your own code


	LD	L,A		; put in HL
	LD	H,0
	OR	A		; set/clear Z
	POP	BC
	RET

;Send a character to the modem
MCHOUT:
	LD	HL,2		; get the character
	ADD	HL,SP
	LD	A,(HL)
				; <== Insert your own code here

	PUSH	BC
	LD	B,A		; save the char

MCHOUT2:
	IN	A,(MSTAT)	; check for uart ready
	BIT	TBE,A
	JR	Z,MCHOUT2

	LD	A,B		; char in a
	OUT	(MDATA),A	; send it
	POP	BC

				; <== End of your own code
	RET			; done

;Test for output ready: return TRUE (1) in HL if ok
MORDY:
				; <== Insert your own code here

	LD	HL,0
	IN	A,(MSTAT)
	BIT	TBE,A		; transmit buffer empty
	JR	Z,MORDY1
	INC	HL
MORDY1:

				; <== End of your own code

	LD	A,L		; set/clear Z
	OR	A
	RET

;Test for character at modem: return TRUE (1) in HL if so
MIRDY:
				; <== Insert your own code here

	LD	HL,0
	IN	A,(MSTAT)
	BIT	RDA,A		; received data available
	JR	Z,MIRDY1
	INC	HL
MIRDY1:

				; <== End of your own code
	LD	A,L		; set/clear Z
	OR	A
	RET

;Send a break to the modem: leave empty if your system can't do it
SNDBRK:
				; <== Insert your own code here
	LD	A,5
	OUT	(MSTAT),A
	LD	A,BREAK		; F8
	OUT	(MSTAT),A

	LD	HL,300		; wait 300 mS
	CALL	WAITHLMS

	LD	A,5
	OUT	(MSTAT),A
	LD	A,ONLINE	; 68
	OUT	(MSTAT),A

	LD	A,3
	OUT	(MSTAT),A
	LD	A,0E1H
	OUT	(MSTAT),A	; E1

				; <== End of your own code
	RET
;
;Test UART flags for error: return TRUE (1) in HL if error.
MDMERR:
				; <== Insert your own code here
	LD	HL,0
	IN	A,(MSTAT)
	AND	ERROR
	JR	Z,MDMER2
	INC	HL
MDMER2:
				; <== End of your own code
	LD	A,L		; set/clear Z
	OR	A
	RET



;Turn DTR ON
DTRON:
				; <== Insert your own code here
	LD	A,5
	OUT	(MSTAT),A

	LD	A,(COMBYT)	; get the one we used last time
	OR	ONLINE		; 80h - set bit 7 high
	LD	(COMBYT),A	; save it for next time
	OUT	(MSTAT),A	; go

				; <== End of your own code
	RET



;Turn DTR OFF
DTROFF:
				; <== Insert your own code here

	LD	A,5
	OUT	(MSTAT),A

	LD	A,(COMBYT)	; get the one we used last time
	AND	ONHOOK		; 7Fh - clear bit 7
	LD	(COMBYT),A	; save it for next time
	OUT	(MSTAT),A	; go

				; <== End of your own code
	RET



;Initialise the SIO +++
; The SIO is set up in four steps:
; 	1)  Reset
;       2)  Reg 4 - clock, stop bits, parity
;	3)  Reg 5 - dtr, Tx bits, Brk, TxEn, rts
;	4)  Reg 3 - Rx bits, RxEn

INIT:

	LD	HL,2		; get parameters
	ADD	HL,SP
	EX	DE,HL
	CALL	GETPARM		; in HL
	LD	(BRATE),HL	; baud rate
	CALL	GETPARM
	LD	(PARITY),HL	; parity
	CALL	GETPARM
	LD	(DATA),HL	; data bits
	CALL	GETPARM
	LD	(STOP),HL	; stop bits


				; <== Insert your own code here
				; using values below

	PUSH	BC

	LD	A,0
	OUT	(MSTAT),A	; point to reg 0
	LD	A,18H		; reset
	OUT	(MSTAT),A	;				*** step 1

	LD	A,4		; point to wrt reg 4
	OUT	(MSTAT),A
	LD	E,44H		; assume  x16, 1 stop, No parity
	LD	A,(STOP)	; set stop bits
	CP	2		; set 2 if required
	JR	NZ,SETPAR
	SET	3,E

SETPAR:
	LD	A,(PARITY)	; set parity bits
	CP	'O'
	JR	NZ,SETPA2
	SET	0,E		; ODD
	JR	SETPA3

SETPA2:	CP	'E'
	JR	NZ,SETPA3
	SET	0,E
	SET	1,E		; EVEN

SETPA3:	LD	A,E
	OUT	(MSTAT),A	;				*** step 2



	LD	A,5		; point to wrt reg 5 - dtr, Tx bits, etc
	OUT	(MSTAT),A
	LD	E,0EAH		; assume dtr, TX 8 bits, TxEn, rts
	LD	A,(DATA)
	CP	7
	JR	NZ,SETBI2
	RES	6,E		; 7 bits

SETBI2:	LD	A,E
	OUT	(MSTAT),A	;				*** step 3
	
	LD	A,3		; point to wrt reg 3
	OUT	(MSTAT),A
	LD	E,0C1H		; assume 8 bits
	LD	A,(DATA)
	CP	7
	JR	NZ,SETBI3
	RES	7,E		; 7 bits

SETBI3:	LD	A,E
	OUT	(MSTAT),A	;				*** step 4


SETBRATE:
	LD	DE,(BRATE)	; get baud rate value (0-10)
	LD	HL,BRVAL
	ADD	HL,DE
	LD	A,(HL)		; get value from table
	OR	A		; 0 = not valid
	JR	Z,SETBRX	; so quit
	LD	A,MODE		; 47h
	OUT	(CTC),A		; select the correct timer and mode of the CTC
	LD	A,(HL)		; then get value back
	OUT	(CTC),A		; do it
	LD	A,(BRATE)
	LD	(MSPEED),A	; tell zmp it's ok
SETBRX:
	POP	BC
	RET



BRATE:	DW	7		; baud rate:
PARITY:	DW	'N'		; parity
DATA:	DW	8		; data bits
STOP:	DW	1		; stop bits
COMBYT:	DB	0EAH		; save it here

;Values for Z80CTC control reg for each baud rate: 0 if invalid
BRVAL:
	DB	 0	;   110		0
	DB	80H	;   300		1
	DB	 0	;   450		2
	DB	40H	;   600		3
	DB	 0	;   710		4
	DB	20H	;  1200		5
	DB	10H	;  2400		6
	DB	08H	;  4800		7
	DB	04H	;  9600		8
	DB	02H	; 19200		9
	DB	0	; 38400	       10
	DB	0	; 57600	       11
	DB	0	; 76800	       12

;
; Set the port. ZMP supplies either 0 or 1 as a parameter.
;
setport:
	ld	hl,2		; get port number
	add	hl,sp
	ex	de,hl
	call	getparm		; in HL (values are 0 and 1)

				; <== Insert your own code here

				; <== End of your own code
	ret
;****************************************************************************
;Video terminal sequences: these are for Televideo TS950: Modify as you wish
;Cursor addressing: 
CURSADD:
	LD	HL,2		; get parameters
	ADD	HL,SP
	EX	DE,HL
	CALL	GETPARM		; in HL
	LD	(ROW),HL	; row
	CALL	GETPARM
	LD	(COL),HL	; column
				; <== Insert your own code here
				; using values in row and col
	CALL	PRINT
	DB	ESC,'=',0	; ADM-3A leadin
	LD	A,(ROW)		; row first
	ADD	A,' '		; add offset
	CALL	COUT
	LD	A,(COL)		; sane for column
	ADD	A,' '
	CALL	COUT
				; <== end of your own code
	RET

ROW:	DS	2		; row
COL:	DS	2		; column


;Clear screen:
CLS:
	CALL	PRINT
	DB	ESC,'*',0
	RET

;Inverse video on:
INVON:
	CALL	PRINT
	DB	ESC,'G4',0
	RET

;Inverse video off:
INVOFF:
	CALL	PRINT
	DB	ESC,'G0',0
	RET

;Turn off cursor:
HIDE:
	CALL	PRINT
	DB	ESC,'.1',0
	RET

;Turn on cursor:
SHOW:
	CALL	PRINT
	DB	ESC,'.2',0
	RET

;Save cursor position:
SAVECU:
	RET

;Restore cursor position:
RESCU:
	RET

;User-defined entry routine: leave empty if not used
USERIN:
	RET

;User-defined exit routine: leave empty if not used
USEROUT:
	RET


;****************************************************************************

;Service modem interrupt:
MINT:
	RET			; my system doesn't need this

;Initialise interrupt vectors:
INVEC:
	RET			; ditto

;De-initialise interrupt vectors:
DINVEC:
	RET			; ditto

;****************** End of user-defined code ********************************
;		Do not change anything below here.

;Modem character test for 100 ms
MRD:
	PUSH	BC		; save bc
	LD	BC,100		; set limit
MRD1:
	CALL	MIRDY		; char at modem?
	JR	NZ,MRD2		; yes, exit
	LD	HL,1		; else wait 1ms
	CALL	WAITHLMS
	DEC	BC		; loop till done
	LD	A,B
	OR	C
	JR	NZ,MRD1
	LD	HL,0		; none there, result=0
	XOR	A
MRD2:
	POP	BC
	RET

; Inline print routine: destroys A and HL

PRINT:
	EX	(SP),HL		; get address of string
PLOOP:
	LD	A,(HL)		; get next
	INC	HL		; bump pointer
	OR	A		; done if zero
	JR	Z,PDONE
	CALL	COUT		; else print
	JR	PLOOP		; and loop
PDONE:
	EX	(SP),HL		; restore return address
	RET			; and quit

;
;Output a character in A to the console
;
COUT:
	PUSH	BC		; save regs
	PUSH	DE
	PUSH	HL
	LD	E,A		; character to E
	LD	C,2
	CALL	BDOS		; print it
	POP	HL
	POP	DE
	POP	BC
	RET

;Wait(seconds)
WAIT:
	LD	HL,2
	ADD	HL,SP
	EX	DE,HL		; get delay size
	CALL	GETPARM
				; fall thru to..
;Wait seconds in HL
WAITHLS:
	PUSH	BC		; save bc
	PUSH	DE		; de
	PUSH	IX		; and ix
	LD	IX,0		; then point ix to 0
				; so we don't upset memory-mapped i/o

;Calculate values for loop constants. Need to have two loops to avoid
;   16-bit overflow with clock speeds above 9 MHz.

OUTERVAL	EQU	(CLKSPD / 10) + 1
INNERVAL	EQU	(6667 / OUTERVAL) * CLKSPD

WAIT10:
	LD	B,OUTERVAL

WAIT11:
	LD	DE,INNERVAL

WAIT12:
	BIT	0,(IX)		; time-wasters
	BIT	0,(IX)
	BIT	0,(IX)		; 20 T-states each
	BIT	0,(IX)
	BIT	0,(IX)
	BIT	0,(IX)
	DEC	DE
	LD	A,E
	LD	A,D
	OR	E
	JR	NZ,WAIT12	; 150 T-states per inner loop
	DJNZ	WAIT11		; decrement outer loop
	DEC	HL		; ok, decrement count in hl
	LD	A,H
	OR	L
	JR	NZ,WAIT10
	POP	IX		; done -- restore ix
	POP	DE		; de
	POP	BC		; and bc
	RET

;Wait milliseconds
MSWAIT:
	LD	HL,2
	ADD	HL,SP
	EX	DE,HL		; get delay size
	CALL	GETPARM
				; fall thru to..
;Wait milliseconds in HL
WAITHLMS:
	PUSH	DE
W1MS0:
	LD	DE,39 * CLKSPD
W1MS1:
	DEC	DE
	LD	A,D
	OR	E
	JR	NZ,W1MS1
	DEC	HL
	LD	A,H
	OR	L
	JR	NZ,W1MS0
	POP	DE
	RET

;Get next parameter from (de) into hl
GETPARM:
	EX	DE,HL		; get address into hl
	LD	E,(HL)		; get lo
	INC	HL
	LD	D,(HL)		; then hi
	INC	HL		; bump for next
	EX	DE,HL		; result in hl, address still in de
	RET
;
;Get address of user-defined variables
;
GETVARS:
	LD	HL,USERVARS
	RET
USERVARS:
	DW	OVERDRIVE	; .OVR etc. drive/user
	DW	OVERUSER

	 IF	($ - CODEBGN) GT OVSIZE
TOOBIG:	JP	ERRVAL		; Overlay too large!
	 ENDIF

	END