; ZMOOX5.Z80 for ZMP 1.3                     Nov 24, '88
;
; 89/04/12 - Modified to ZMP v1.5		- George Conover
;	Updated to ver 1.4 by Ken Reid
;  	Adapted from I2OX-2.ASM by Ken Reid       
;	       Email: Houston NW Znode 713-937-8886
;       Slight modifications By W. H. Pearre 11/19/88              
; Modified to v1.3 standard Ron Murray Oct 12, 88
;
;-----------------------------------------------------------------------
;
;	System-dependent code overlay for ZMODEM
;
;   Insert your own code as  necessary	in  this  file.  Code  contained
; herein  has been written in Z80 code for use with M80 or SLR. Assemble
; as follows:
;
;	SLR ZMO-OX05/h
;	MLOAD ZMP.COM=ZMODEM.COM,ZMO-OX05.HEX
;  or
;	M80 =ZMOOX05.Z80
;	RELHEX ZMOOX05
;	MLOAD ZMP.COM=ZMPX.COM,ZMOOX05.HEX
;
;
; (Don't use L80 without changing the source  for  assembly  as  a  CSEG
; file.)
;
;-----------------------------------------------------------------------
;
; Notes on modifying this file:
;
;    C	requires  that functions do not change either index register (IX
; or IY). If your overlay requires either of these to be changed, ensure
; they are restored to the original values on return.
;
;    Since  collecting	parameters  from C functions can be tricky, only
; change the parts marked 'Insert your own code here'. Do NOT modify the
; jump table at the start. Do NOT modify the entry/exit sections of each
; function. Do NOT pass 'GO'. Do NOT collect $200.
;
;    Apart  from  defining  modem  functions,  this  file  also  defines
; terminal characteristics. Examples provided are for ADM-3A (with a few
; of my own additions). Modify to suit	your  own  terminal.  An  inline
; print routine is provided for printing strings in the usual way, usage
; is:
;
;		      CALL	PRINT
;		      DB	'required string',0
;
;-----------------------------------------------------------------------
;
;    Don't forget to set your clock speed at the clkspd variable.
;
;    If you find  your	overlay  exceeds  the  maximum	size  (currently
; 0400h),  you	will have to contact me for another version. If too many
; people need to do it, we haven't allowed enough room.
;
; Ron Murray Aug 15, 88
;
;-----------------------------------------------------------------------
;
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     'E'     ; Drive to find overlay files on ('A'-'P')
OVERUSER       EQU     15      ; User area to find files
;
USERDEF	EQU	0145H		; Origin of this overlay.  This address
;				;   may change with subsequent revisions
; NOT user-set variables
;
MSPEED	EQU	03CH		; Location of current baud rate. 3CH IS NORMAL
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			; DEBUG
;
ESC	EQU	1BH
CTRLQ	EQU	11H
CR	EQU	0DH
LF	EQU	0AH
BDOS	EQU	5
;
PORT	EQU	0CH	; Your base port (data or status)
MDCTL1	EQU	PORT+1	; Modem control port
MDDATP	EQU	PORT	; Modem data port
MDRCV	EQU	01H	; Modem receive ready
MDSND	EQU	04H	; Modem send ready bit
MDTXE	EQU	01H	; Modem send buffer empty, holding buffer empty
BRPORT	EQU	04H	; CTC timer port to set baudrate

; Jump table for the overlay: do NOT change this
;
CODEBGN	EQU	$
;
JUMPTAB: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)
	JP	SPARE		; Spare for later use
	JP	SPARE		; Spare for later use
	JP	SPARE		; Spare for later use
	JP	SPARE		; Spare for later use
	JP	SPARE		; Spare for later use
	JP	SPARE		; Spare for later use
;
; Screen print function
;
SCRNPR:	DS	0	
;
; <== Insert your own code here
;
;	CALL	PRINT
;	DB	'This function not supported.',CR,LF,0
;       
	RET
; <== End of your own code
;
SPARE:	RET
;
; User-defined entry routine
;
USERIN:	RET
;
; User-defined exit routine
;
USEROUT:RET
;
; Get a character from the modem: return in HL
; It is not necessary to test for status
;
MCHIN:	PUSH	BC
;
	IN	A,(MDDATP)	; Get the character in A
;
	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)		; In A
;
	PUSH	BC
	LD	C,A
;
MCHOUT1:IN	A,(MDCTL1)
	AND	MDSND
	JR	Z,MCHOUT1
	LD	A,C
	OUT	(MDDATP),A
	POP	BC
	RET			; Done
;
;Test for output ready: return TRUE (1) in HL if ok
;
MORDY:	DS	0
;
	IN	A,(MDCTL1)
	AND	MDTXE
	JR	Z,MORDYX
	LD	L,1
	LD	H,0
;
MORDYX:	DS	0
;
	LD	A,L		; Set/clear Z
	OR	A
	RET
;
;Test for character at modem: return TRUE (1) in HL if so
;
MIRDY:	DS	0
;
	IN	A,(MDCTL1)
	AND	MDRCV
	JR	Z,MIRDY1
	LD	L,1
	JR	MIRDYX
;
MIRDY1:	LD	L,0
;
MIRDYX:	LD	H,00H
;
	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:	DS	0
;
	LD	A,5
	OUT	(MDCTL1),A	; Send to the status port
	LD	A,0F8H		; DTR normal, send break tone
	OUT	(MDCTL1),A
;
	LD	HL,300
	CALL	WAITHLMS	; Wait 300 milliseconds
;
	LD	A,5		; Restore normal, 8 bits, DTR on, etc.
	OUT	(MDCTL1),A
	LD	A,0E8H
	OUT	(MDCTL1),A
	RET
;
; Test UART flags for error: return TRUE (1) in HL if error
;
MDMERR:	DS	0
;
	LD	A,010H		; Register 0, reset interrupts
	OUT	(MDCTL1),A
	IN	A,(MDCTL1)
;
	LD	A,L		; Set/clear Z
	OR	A
	RET
;
; Turn DTR (and optionally RTS) ON
;
DTRON:	DS	0
;
	LD	A,05H
	OUT	(MDCTL1),A
	LD	A,0E8H
	OUT	(MDCTL1),A
;
	RET
;
; Turn DTR ( and RTS?) OFF
;
DTROFF:	DS	0
;
	LD	A,05H
	OUT	(MDCTL1),A
	LD	A,078H
	OUT	(MDCTL1),A
;
	RET
;
;Initialise the UART
;
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
;
	PUSH	BC
	LD	A,0		; Select register
	OUT	(MDCTL1),A
	LD	A,018H		; Throw out of mode
	OUT	(MDCTL1),A
	LD	A,04H		; Select register
	OUT	(MDCTL1),A
	LD	A,044H		; Set ASCII parameters (44=1, 4C=2 stop)
	OUT	(MDCTL1),A
	LD	A,03H		; Select register
	OUT	(MDCTL1),A
	LD	A,0C1H		; Enable receive
	OUT	(MDCTL1),A
	LD	A,05H		; Select register
	OUT	(MDCTL1),A
	LD	A,0EAH		; Enable send, 'DTR', RTS'
	OUT	(MDCTL1),A
;
        LD	A,(BRATE)	; minor correction - DIW
	CP	1		; 300 bps
	JP	Z,OK300
	CP	5		; 1200 bps
	JP	Z,OK1200
	CP	6		; 2400 bps
	JP	Z,OK2400
	CP	8		; 9600 bps
	JP	Z,OK9600
	JR	SETBRX		; Not valid
;
;
OK300:	LD	A,1		; MSPEED 300 baud value
	LD	HL,BD300
	JP	LOADBD
;
OK1200:	LD	A,5
	LD	HL,BD1200
	JP	LOADBD
;
OK2400:	
	LD	A,6
	LD	HL,BD2400
	JP	LOADBD
;
OK9600:	LD	A,8
	LD	HL,BD9600
;
LOADBD:	LD	(MSPEED),A	; Change time-to-send to match baudrate
	LD	A,L		; Get "LSP" baud rate byte
	OUT	(BRPORT),A
	LD	A,H		; Get "MSP" baud rate byte
	OUT	(BRPORT),A
	RET

SETBRX:	POP	BC
	XOR	A
	LD	(MSPEED),A
	RET
;
BD300	EQU	384		; 300 baud
BD1200	EQU	96		; 1200 baud
BD2400	EQU	48		; 2400 baud
BD9600	EQU	12		; 9600 baud
;
;
BRATE:	DS	2		; Baud rate:
				; 0 = 110 baud	1 = 300 baud	2 = 450 baud
				; 3 = 600 baud	4 = 710 baud	5 = 1200 baud
				; 6 = 2400 baud 7 = 4800 baud	8 = 9600 baud
				; 9 = 19200 baud
PARITY:	DW	'N'		; Parity (will be 'N', 'E' or 'O')
DATA:	DW	'8'		; Data bits (will be 7 or 8)
STOP:	DS	'1'		; Stop bits (will be 1 or 2)
;
;-----------------------------------------------------------------------
;
; 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 ADM-3A: 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 column
;
	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	01AH,0
	RET
;
; Inverse video on
;
INVON:	CALL	PRINT
	DB	ESC,'j',0
	RET
;
; Inverse video off
;
INVOFF:	CALL	PRINT
	DB	ESC,'k',0
	RET
;
; Turn off cursor
;
HIDE:	CALL	PRINT
	DB	ESC,'z',0
	RET
;
; Turn on cursor
;
SHOW:	CALL	PRINT
	DB	ESC,'v',0
	RET
;
; Save cursor position
;
SAVECU:	RET
;
; Restore cursor position
;
RESCU:	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 1 ms
	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 registers
	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
;
; 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 low
	INC	HL
	LD	D,(HL)		; Then hihi
	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 is too large
	 ENDIF
;
	END
