	title	'Tarbell Boot loader module for CP/M 3.0'

; Copyright (c) 1983 Tarbell Electronics

; version 1.2 5-26-83 Edwin E. Smith
; added real time clock support 4-13-83
; added banked equate for non-banked systems 5-26-83
; note @nbnks byte must appear in move module

	true	equ	-1
	false	equ	0

	banked	equ	true

	public	?init,?ldccp,?rlccp,?time
	extrn	?pmsg,?conin
	extrn	@civec,@covec,@aivec,@aovec,@lovec
	extrn 	@cbnk,?bnksl,?bank,@date,@hour,@min,@sec
	extrn	@nbnks

	maclib z80

bdos	equ	5
memenb	equ	0ah			;memory manager enable port
imask	equ	09h			;interupt mask port
intvect	equ	1			;rtc interrupt vector
;
; timer equates
;
tch0	equ	04h		;timer chan 0 address
tch1	equ	05h		;timer chan 1 address
tch2	equ	06h		;timer chan 2 address
tcmnd	equ	07h		;timer command port
cntr0	equ	00000000b	;counter 0
cntr1	equ	01000000b	;counter 1
cntr2	equ	10000000b	;counter 2
rlword	equ	00110000b	;read/load lsb 1st, msb 2nd.
rlhbyte	equ	00100000b	;read/load msb only.
rllbyte	equ	00010000b	;read/load lsb only.
cntrlt	equ	00000000b	;counter latching operation.
binary	equ	00000000b	;select binary operation.
bcd	equ	00000001b	;select bcd operation.
mode0	equ	00000000b	;interrupt on terminal count.
mode1	equ	00000010b	;programmable one-shot.
mode2	equ	00000100b	;rate generator.
mode3	equ	00000110b	;square wave rate generator.
mode4	equ	00001000b	;software triggered strobe.
mode5	equ	00001010b	;hardware triggered strobe.
intervl	equ	40000		;this = 20 ms (50 hz period)
tintrvl	equ	50		;this = 1 second

	dseg				; init done from banked memory

?init:
	mvi	a,0ffh			;mask all interupts
	out	imask
	lxi	h,08000h
	shld	@civec
	shld	@covec			; assign console to CRT:
	lxi	h,04000h
	shld	@lovec 			; assign printer to LPT:
	lxi	h,02000h
	shld	@aivec
	shld	@aovec			; assign AUX to CRT1:
	xra	a
	call	?bnksl			;select bank 0
	out	memenb			;turn on memory manager
;
;if we don't want instant crash we must put a jump at loc. 0008h
;in all banks.
;
	call	pg0init			;setup interupt vector(s)
;
;set timer to stop running
;
	mvi	a,cntr2+rlword+mode2+binary	;timer values
	out	tcmnd		;send it
	mvi	a,1		;stop timer
	out	tch2
	dcr	a
	out	tch2
	in	imask		;clear interrupt latch
;
;set up timer and interrupt controller
;this routine is called once
;
timint:
	mvi	a,cntr2+rlword+mode2+binary
	lxi	b,intervl	;timer interval
	out	tcmnd		;send init byte
	mov	a,c		;get low byte of count
	out	tch2		;send it
	mov	a,b		;get high byte of count
	out	tch2		;send it
	mvi	a,0fdh		;mask all but vi1
	out	imask		;clear intrp mask bits
;
;now we can announce ourselves to the world.
;
	lxi	h,signon$msg
	call	?pmsg			; print signon message
	ret	


	cseg	; boot loading most be done from resident memory
	
; This version of the boot loader loads the CCP from a file
; called CCP.COM on the system drive (A:).

; First time, load the A:CCP.COM file into TPA
?ldccp:
; Reload CCP from disk file also
?rlccp:	xra	a
	sta	ccp$fcb+15		; zero extent
	lxi	h,0
	shld	fcb$nr			; start at beginning of file
	lxi	d,ccp$fcb
	call	open			; open file containing CCP
	inr	a
	jz	no$CCP			; error if no file...
	lxi	d,0100h
	call	setdma			; start of TPA
	lxi	d,128
	call	setmulti		; allow up to 16k bytes
	lxi	d,ccp$fcb
	call	read			; load the thing
	ret

no$CCP:			; here if we couldn't find the file
	lxi	h,ccp$msg
	call	?pmsg			; report this...
	call	?conin			; get a response
	jmp	?ldccp			; and try again

;****************************************************************
;real time clock interrupt handler and jump vector setup	*
;routines must be in common memory.				*
;****************************************************************

    ; No external clock.
?time:
	ret

;here is where the time gets updated

ticker:	push	psw		;save the world
	push	h		;...well part of it anyway
	lxi	h,tick		;point to tick counter
	dcr	m		;decrease by 1
	jrnz	tickdone	;= 1 second yet
	mvi	m,tintrvl	;1 sec elapsed, reset counter
;
;one second has passed so tell the world
;
	lda	@sec		;get current second
	adi	1		;do a decimal add
	daa			;make decimal again
	sta	@sec		;update second counter
	cpi	60h		;new minute yet ?
	jrnz	tickdone	;nope exit
;
;one minute has passed so update
;
	xra	a
	sta	@sec		;clear seconds
	lda	@min		;get current minute
	adi	1		;do a decimal add
	daa			;make decimal again
	sta	@min		;update minute counter
	cpi	60h		;new hour yet ?
	jrnz	tickdone	;nope exit
;
;one hour has passed so update
;
	xra	a
	sta	@min		;clear minutes
	lda	@hour		;get current hour
	adi	1		;do a decimal add
	daa			;make decimal again
	sta	@hour		;update hour counter
	cpi	24h		;new day yet ?
	jrnz	tickdone	;nope exit
;
;it's tomorrow so lets tell the system
;
	xra	a
	sta	@hour		;clear hours
	lhld	@date		;get the current date
	inx	h
	shld	@date		;update day counter
;
;done with updates so lets service the interrupt
;
tickdone:
	in	imask		;clear the interupt latch
;
;now restore the registers that we used
;
	pop	h
	pop	psw
	ei			;ready for the next one
	ret
;
;tick counter 
;

tick:	db	50		;50 ticks = 1 second


;****************************************************************
;initialize the rtc jump vectors in each bank			*
;****************************************************************

pg0init:
	if	banked
	lda	@nbnks		;get the number of memory banks
	else
	mvi	a,1		;init bank 0 only
	endif
pg0$init$loop
	dcr	a		;a=bank number
	push	psw		;save it
	call	?bank		;select the bank
	mvi	a,0c3h		;jump instruction
	sta	intvect * 8	;location 0008h for vector 1
	lxi	h,ticker	;get address of rtc service routine
	shld	(intvect * 8)+1	;complete jump instruction
	pop	psw
	jnz	pg0$init$loop	;loop till all banks are setup
;
;we were called from bank 0 so we will return to bank 0
;
	ret

;****************************************************************
; end of real time clock stuff					*
;****************************************************************


	; CP/M BDOS Function Interfaces

open:
	mvi	c,15
	jmp	bdos		; open file control block

setdma:
	mvi	c,26
	jmp	bdos		; set data transfer address

setmulti:
	mvi	c,44
	jmp	bdos		; set record count

read:
	mvi	c,20
	jmp	bdos		; read records


signon$msg
	db	13,10,13,10,'Tarbell Electronics'
	db	13,10,'CP/M Plus Version 3.0 3-15-83',13,10,0

ccp$msg
	db	13,10,'BIOS Err on A: No CCP.COM file',0


ccp$fcb
	db	1,'CCP     ','COM',0,0,0,0
	ds	16
fcb$nr
	db	0,0,0

	end
