;AMSTRAD OVERLAY FOR ZMP13

;	System-dependent code overlay for ZMODEM
;
false	equ	0
true	equ	not false

; User-set variables: ***********

clkspd	equ	4		; Processor clock speed in MHz

debug	equ	false

userdef	equ	0145h		; origin of this overlay
				; This address may change with subsequent
				;  revisions.

PORT		EQU	0FADCH		;amstrads serial output port
MDCTL1		EQU	PORT+1		;modem control port
MDDATP		EQU	PORT		;modem data port
MDMRCV		EQU	01H		;your bit to test for receive
MDMSND		EQU	04H		;your bit to test for send
MDTXE		EQU	04H		;your value when send ready
CTCTX		EQU	0FBDCH		;8253 counter 0 (ch a tx.)
CTCRX		EQU	CTCTX+1		;8253 counter 1 (ch a rx.)
CTCMODE		EQU	CTCTX+3		;8253 write mode word

; NOT user-set variables

mspeed	equ	05ch		; location of current baud rate. 
ovsize	equ	0400h		; max size of this overlay

	org	userdef


esc	equ	1bh
ctrlq	equ	11h
cr	equ	0dh
lf	equ	0ah
bdos	equ	5


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

; Spare jumps for compatibility with future versions
	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

;
; Main code starts here
;
;Screen print function
scrnpr:
				; <== Insert your own code here
	call	print
	db	'This function not supported.',cr,lf,0
				; <== End of your own code
spare:
	ret

; User-defined entry routine: leave empty if not needed
userin:
	ret

; User-defined exit routine: leave empty if not needed
userout:
	ret


;Get a character from the modem: return in HL
mchin:
	push	bc
	LD	BC,MDDATP
	IN	A,(C)
	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)
	PUSH	BC
	LD	BC,MDDATP
	OUT	(C),A
	POP	BC
	ret			; done

;Test for output ready: return TRUE (1) in HL if ok
mordy:
	call	mstat		;modem status
	and	MDMSND
	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
	call	mstat
	and	MDMRCV

				; <== End of your own code
	ld	a,l		; set/clear Z
	or	a
	ret

;get status of uart

mstat:	PUSH	BC
	LD	A,10H		;reset interupts
	LD	BC,MDCTL1
	OUT	(C),A
	IN	A,(C)
	POP	BC
	RET

;
sndbrk:
	call	dtroff
	LD	A,5
	LD	BC,MDCTL1
	OUT	(C),A
	LD	A,0F8H		;send a break tone

	ld	hl,300		; wait 300 mS
	call	waithlms
	call	dtron
				; <== End of your own code
	ret
;
;Test UART flags for error: return TRUE (1) in HL if error.
mdmerr:
				; <== Insert your own code here

				; <== End of your own code
	ld	a,l		; set/clear Z
	or	a
	ret



;Turn DTR ON
dtron:	push	bc
	ld	bc,MDCTL1
	push	af		;save all registers used
	ld	a,5		;say register 5
	out	(c),a	;select write register 5
	ld	a,(wrreg5)	;a copy of write reg 5
	set	7,a		;set DTR bit
	ld	(wrreg5),a	;update copy
	out	(C),a	;to modem control port
	pop	af		;restore registers
	pop	bc
	ret

;Turn DTR OFF
dtroff:	push	bc
	ld	bc,MDCTL1
	push	af		;save all registers used
	ld	a,5
	out	(c),a	;select write register 5
	ld	a,(wrreg5)	;old contents of wr reg5
	res	7,a		;reset DTR bit
	ld	(wrreg5),a	;update copy
	out	(c),a
	pop	af		;restore registers
	pop	bc
	ret
;
wrreg5:	db	0EAH		;saved copy of write only reg (set at initmod)
;

;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 (BINARY 7 or 8)
	call	getparm
	ld	(stop),hl	; stop bits (BINARY 1 or 2)


				; <== Insert your own code here
	ld	a,(brate)
				; using values below
	ld	(mspeed),a	; don't forget to load mspeed with the
				; current brate value if the new rate is
				; valid. See table of values below.
				; <== End of your own code

;Amstrad initialization -- sets sio for 8 bits, 1 stop, no parity etc.
;
INITMOD:
	LD	BC,MDCTL1
	LD	A,00H		;select reg.
	OUT	(C),A
	LD	A,18H		;throw out of mode
	OUT	(C),A
	LD	A,04H		;select reg.
	OUT	(C),A
	LD	A,44H		;set ascii parameters
	OUT	(C),A
	LD	A,03H		;select reg.
	OUT	(C),A
	LD	A,0C1H		;enable receive
	OUT	(C),A
	LD	A,05H		;select reg.
	OUT	(C),A
	LD	A,0EAH		;enable send, dtr, rts
	OUT	(C),A

;Initialise 8253 for reqd baud rate

	LD	A,(MSPEED)	;get the selected value
	CP	1		;300 bps? NEWBD arrives here
	JP	Z,OK300
	CP	5		;1200 bps? NOW 75/1200 bps
	JP	Z,OK1200
	CP	6		;2400 bps?
	JP	Z,OK2400
	CP	8		;9600 bps?
	JP	Z,OK9600
	jp	init		;no valid rate
;
ITMOD1: LD	A,036H		;select counter 0 (a tx)
	LD	BC,CTCMODE
	OUT	(C),A		;send to 8253 timer
;first do tx chan.
;
ITMOD2: LD	A,068H		;default to 1200
	LD	BC,CTCTX
	OUT	(C),A		;send to 8253 timer
;
ITMOD3: LD	A,00H		;1200 baud, msb portion
	OUT	(C),A		;to timer
;now do rx chan.
;
ITMOD4: LD	A,076H		;select counter 1 (a rx)
	LD	BC,CTCMODE
	OUT	(C),A
;
ITMOD5: LD	A,068H		;load 1200, msb
	LD	BC,CTCRX
	OUT	(C),A
;
ITMOD6: LD	A,00H
	OUT	(C),A
	RET
;.....
; Sets the modem speed via the set command.
;
OK300:	LD	A,1
	LD	HL,(BD300)
	LD	DE,(BD300)
	JP	LOADBD
;
OK1200:	LD	A,5
	LD	HL,(BD1200)
	LD	DE,(BD1200)
	JP	LOADBD
;
OK2400:	LD	A,6
	LD	HL,(BD2400)
	LD	DE,(BD2400)
	JP	LOADBD
;
OK9600:	LD	A,8
	LD	DE,(BD9600)
	LD	HL,(BD9600)
;
LOADBD:	LD	(MSPEED),A	;change time-to-send to match baudrate
	LD	A,L		;get baudrate byte
	LD	(ITMOD2+1),A	;send to 8253 ch a tx for new baudrate
	LD	A,E
	LD	(ITMOD5+1),A	;now rx
	LD	A,H		;2nd 1/2 of baudrate
	LD	(ITMOD3+1),A	;do tx first
	LD	A,D
	LD	(ITMOD6+1),A	;then rx
	JP	ITMOD1	;reinitialize to new baudrate, then done
;
; Table of baudrate parameters
;
BD300:	DEFW	01A0H
BD1200:	DEFW	0068H
BD2400:	DEFW	0034H
BD9600:	DEFW	000DH
;
BAUDBUF:DEFB	10,0
	DEFS	10

	ret
;--------------------------------------------------------------------------

stop:	dw	1		; stop bits
parity:	dw	'N'		; parity
data:	dw	8		; data bits
brate:	dw	6		; baud rate:

;--------------------------------------------------------------------------
;Values of brate for each baud rate
;
; baud rate    brate
;
;   110		 0
;   300		 1
;   450		 2
;   600		 3
;   710		 4
;  1200		 5
;  2400		 6
;  4800		 7
;  9600		 8
; 19200		 9
; 38400		10
; 57600         11
; 76800         12
;

;****************************************************************************
;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 col
	call	print
	db	esc,'Y',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	1ah,0
	ret

;Inverse video on:
invon:
	call	print
	db	esc,'p',0
	ret

;Inverse video off:
invoff:
	call	print
	db	esc,'q',0
	ret

;Turn off cursor:
hide:
	call	print
	db	esc,'f',0
	ret

;Turn on cursor:
show:
	call	print
	db	esc,'e',0
	ret

;Save cursor position:
savecu:
	call	print
	db	esc,'j',0
	ret

;Restore cursor position:
rescu:
	call print
	db	esc,'k',0
	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

	 if	($ - codebgn) gt ovsize
toobig:	jp	errval		; Overlay too large!
	 endif

	end

 in hl, address still in de
	ret

	 if	($ - codebgn) gt ovs