		title	'interrupt-Uhr -- time 1.600'

;********************************************************
;*							*
;*	TIME.ASM					*
;*							*
;*			CBIOS fuer jk82 CP/M PLUS	*
;*							*
;*			Interrupt-Uhr			*
;*							*
;*			Dieses Modul wird in das CBIOS	*
;*			eingebunden, wenn keine ausles-	*
;*			bare CMOS-Uhr im System vor-	*
;*			handen ist.			*
;*							*
;********************************************************
;
;********************************************************
;*							*
;*	Erstellt am:	18.05.84 durch: D. Janich	*
;*	fuer EPC 1.6	08.10.85 durch: L. Kleberhoff	*
;********************************************************
;
;********************************************************
;*							*
;*		  Copyright (C), 1984			*
;*	     Janich & Klass Computersystems		*
;*		    P.O. Box 101109			*
;*		   D 5600 Wuppertal 1			*
;*		      West Germany			*
;*							*
;********************************************************

; Dieses Modul unterstuetzt eine Interrupt-Uhr auf folgenden
; Platinen:
;
;		CPU 1: Kanal 1 und 3 des CTC werden benutzt
;		       Auf der Platine ist eine Verbindung von
;		       Pin 24 nach Pin 26 auf dem Stecker 1 zu jumpern.
;
;		CPU 2: Kanal 1 und 3 des CTC werden benutzt
;
;	      HKM-CPU: Kanal 3 des CTC wird benutzt
;
;	      EPC 1.6: Kanal 3 des CTC wird benutzt
;
;		EPC 2: keine Interrupt-Uhr moeglich
;		       Es wird eine Pseudo-Uhr implementiert, die bei
;		       jedem Aufruf von TIME die Uhr um eine Sekunde
;		       weiterzaehlt.
;

	maclib	z80
	maclib	sysdef
	maclib	ports

	public	?time			; Uhr initialisieren
	public	timeid			; Identifikation des Moduls

	extrn	sys$clk			; Systemtakt (in CHARIO)
	extrn	@date,@hour,@min,@sec	; (in SCB)
	extrn	setvec			; Interrupt-Vektor setzen (in BOOT)
	extrn	call0			; call Routine in Bank 0

;
;	**********************************************************************************
;
;	CTC-Initialisierung in Abhaengigkeit vom Systemtakt:
;
;	+------------------------------------------++------------------------------------+
;	I Benutzung von 2 CTC-Kanaelen		   II Benutzung von nur einem CTC-Kanal  I
;	+------------+-----------+--------+--------++-----------+-----+------------------+
;	I Systemtakt I Prescaler I 1. CTC I 2. CTC II Prescaler I CTC I Software-Counter I
;	+------------+-----------+--------+--------++-----------+-----+------------------+
;	I 4      MHz I    256	 I   125  I   125  II    256    I 250 I  Wechsel 62/63	 I
;	I 4,9152 MHz I    256    I   150  I   128  II    256    I 256 I		 75      I
;	I 6	 MHz I    256    I   252  I    93  II    256    I 252 I          93      I
;	I 8      MHz I		 I  	  I 	   II    256    I 252 I         124      I
;	+------------+-----------+--------+--------++-----------+-----+------------------+
;
;	Falls 2 CTC-Kanaele benutzt werden, erfolgt ein Interrupt zu jeder 
;	vollen Sekunde. Bei 6MHz liegt jedoch ein Fehler von ca. 5,5s pro Tag vor. 
;
;	Falls nur ein CTC-Kanal zur Verfuegung steht, wird der 2. Counter
;	per Software realisiert. Um die Interrupts zu minimieren, wird jedoch
;	bei 4 und 4,9152MHz mit anderen Zeitkonstanten gearbeitet. Bei 4MHz 
;	ergibt sich jedoch ein Teiler von 62,5. Deshalb wird abwechselnd 
;	62 und 63 als Endwert gewaehlt.
;
;	**********************************************************************************
;

if cpu1 or cpu2

 ctc4$1$const	equ	125	; Konstante 1. CTC 4MHz
 ctc5$1$const	equ	150	; Konstante 1. CTC 4,9152MHz
 ctc6$1$const	equ	252	; konstante 1. CTC 6MHz
 ctc4$2$const	equ	125	; Konstante 2. CTC 4MHz
 ctc5$2$const	equ	128	; Konstante 2. CTC 4,9152MHz
 ctc6$2$const	equ	93	; Konstante 2. CTC 6MHz

else

 ctc4$const	equ	250	;        Konstante fuer CTC 4MHz
 ctc5$const	equ	0	; (=256) Konstante fuer CTC 4,9152MHz
 ctc6$const	equ	252	;	 Konstante fuer CTc 6MHz
 ctc8$const	equ	252	;	 Konstante fuer CTC 8MHz

endif

grenze41	equ	62	; 4MHz 1. Durchlauf
grenze42	equ	63	; 4MHz 2. Durchlauf
grenze5		equ	75	; 4,9152MHz
grenze6		equ	93	; 6MHz
grenze8		equ	124	; 8MHz

;===============================================================+
;								I
; Interrupt-Uhr-Identifikation					I
;								I
	dseg					;		I
;								I
timeid:						;		I
	db	'ID',cr,lf,'TIME     1.600  '	;		I
if epc2						;		I
	db	'pseudo-time for EPC II'	;		I
else						;		I
	db	'interrupt-time for '		;		I
 if cpu1					;		I
	db	'CPU I'				;		I
 endif						;		I
 if cpu2					;		I
	db	'CPU II'			;		I
 endif						;		I
 if hkm						;		I
	db	'HKM-CPU'			;		I
 endif						;		I
 if epc1					;		I
	db	'EPC 1.6'			;		I
 endif						;		I
endif						;		I
	db	0				;		I
;								I
;===============================================================+
;
;----------------------------------------------------------------
	cseg
;
?time:
	push h
	lxi h,time2 ! call call0	; time in Bank 0 aufrufen
	pop h ! ret

;
;----------------------------------------------------------------
	dseg
;
time2:	

if not epc2

	;********************************************************
	;*	Initialisierungen  fuer die Interrupt-Uhr	*
	;********************************************************

	lda	bereits$initialisiert$?
	ana	a
	rz			; return, wenn bereits initialisiert
	xra	a
	sta	bereits$initialisiert$?

  ;***** Interruptvektor in Tabelle eintragen *****

	push	d
	lxi	d,vektor$tabelle
	mvi	b,4		; Laenge der Tabelle in Worten
	call	setvec		; 4 Eintraege in Interrupt-Tabelle
	mov	a,h
	ora	l
	pop	d
	rz			; keine Uhr, wenn Tabelle voll

  ;***** CTC initialisieren *****

	mov	a,l		; Vektor fuer CTC
	out	ctc0		; Vektor in den CTC

  if cpu1 or cpu2

	mvi	a,timer$256	; Timer-Mode mit Prescaler 256 ohne Interrrupt
	out	ctc1		; Mode
	lda	sys$clk		; Systemtakt
	cpi	4		; 4 MHz ?
	push	psw
	jrnz	not$vier$mhz$1
	mvi	a,ctc4$1$const	; Konstante 1. CTC 4MHz
	jr	fuenf$mhz$1

   not$vier$mhz$1:

	cpi	5		; 5 MHz ?
	mvi	a,ctc5$1$const	; Konstante 1. CTC 4,9152MHz
	jrz	fuenf$mhz$1
	mvi	a,ctc6$1$const	; Konstante 1. CTC 6MHz

   fuenf$mhz$1:

	out	ctc1		; Zeitkonstante
	mvi	a,int$counter	; Counter-Mode mit Interrupt
	out	ctc3		; Mode
	pop	psw
	jrnz	not$vier$mhz$2
	mvi	a,ctc4$2$const	; Konstante 2. CTC 4MHz
	jr	fuenf$mhz$2

   not$vier$mhz$2:

	mvi	a,ctc5$2$const	; Konstante 2. CTC 4,9152MHz
	jrz	fuenf$mhz$2
	mvi	a,ctc6$2$const	; Konstante 2. CTC 6MHz

   fuenf$mhz$2:

	out	ctc3		; Zeitkonstante
	ret

  else

	mvi	a,int$timer$256 ; Timer-Mode mit Prescaler 256 und Interrrupt
	out	ctc3		; Mode
	lda	sys$clk		; Systemtakt
	cpi	4		; 4 MHz ?
	jrnz	not$vier$mhz
	mvi	a,ctc4$const	; Konstante CTC 4MHz
	jr	fuenf$mhz

   not$vier$mhz:

	cpi	5		; 5 MHz ?
	mvi	a,ctc5$const	; Konstante CTC 4,9152MHz
	jrz	fuenf$mhz
	mvi	a,ctc6$const	; Konstante CTC 6MHz oder 8 MHz

   fuenf$mhz:

	out	ctc3		; Zeitkonstante
	ret

  endif

  vektor$tabelle:		dw	int$clock
				dw	int$clock
				dw	int$clock
				dw	int$clock
  bereits$initialisiert$?:	db	1		; noch nicht initialisiert

;
;----------------------------------------------------------------
	cseg
;

	;********************************************************
	;*	Einsprungpunkt fuer die Interrupt-Uhr		*
	;********************************************************

  int$clock:	sspd	stack$lokal	; ld (stack$lokal),sp
		lxi	sp,stack$lokal	; ld sp,stacklokal
		push	psw

  if not (cpu1 or cpu2)

		lda	zaehler		; Software-Zaehler incrementieren
		inr	a
		sta	zaehler

		cpi	grenze41	; 4MHz 1. Durchlauf
		jrc	return		; < grenze41 (4MHz)
		lda	sys$clk		; Systemtakt
		cpi	4		; 4MHz ?	
		jrz	takt$vier
		cpi	5		; 4,9152MHz ?
		jrz	takt$fuenf
		cpi	6		; 6 MHz
		jrz	takt$sechs

   takt$acht: ;***** 8MHz *****
		lda	zaehler
		cpi	grenze8
		jrnz	return
		jr	sekunden

   takt$sechs:;***** 6MHz *****
		lda	zaehler
		cpi	grenze6		; <> grenze6 (6MHz)
		jrnz	return
		jr	sekunden

   takt$vier: ;***** 4MHz *****

		lda	grenzflag	; 0: 1. Durchlauf ; 1: 2. Durchlauf
		ana	a
		mvi	a,1		; Flag fuer 2.Durchlauf
		jrz	durchlauf$1
		lda	zaehler
		cpi	grenze41	; 4MHz 2.Durchlauf
		jrz	return
		xra	a		; Flag fuer 1. Durchlauf

   durchlauf$1:

		sta	grenzflag
		jr	sekunden

   takt$fuenf: ;***** 4,9152MHz *****

		lda	zaehler
		cpi	grenze5
		jrnz	return

   sekunden:	xra a ! sta zaehler	; zaehler = 0

  endif		
endif
		lda @sec		; sekunden incrementieren
		adi 1 ! daa ! sta @sec

		cpi 60h	! jrnz return

minuten:

		xra a ! sta @sec	; sekunden = 0
		lda @min		; minuten incrementieren
		adi 1 ! daa ! sta @min

		cpi 60h	! jrnz return

stunden:

		xra a ! sta @min	; minuten = 0
		lda @hour		; Stunden incrementieren
		adi 1 ! daa ! sta @hour

		cpi 24h ! jrnz return

tage:

		xra a ! sta @hour	; Stunden = 0
		push h
		lhld @date		; Tage incrementieren
		inx h ! shld @date
		pop  h

return:

if epc2
		ret
else
		pop	psw
		lspd	stack$lokal	; ld sp,(stack$lokal)
		ei
		reti
endif

if not (cpu1 or cpu2 or epc2)

 zaehler:	db	0		; Software-Zaehler
 grenzflag:	db	0		; 0: 1. Durchlauf ; 1: 2. Durchlauf (6MHz)

endif
		ds	4		; 2 Worte Stacktiefe
stack$lokal:	ds	2

		end
