; =============================================================================
;
;                              Litos8 system timer
;
; =============================================================================

; -----------------------------------------------------------------------------
; Exported user functions (4):
;	TimerSetMode - initialize timer counter
;	TimerSetVal - set counter new initial value
;	TimerGetVal - get counter current value
;	TimerGetOut - get counter ouput
; -----------------------------------------------------------------------------

; Ports 40h-43h, clock 1193182 Hz, timer Intel 8254
;
; 40h - counter 0, system timer (default set to TIMER_MODE_GEN, 65536)
; 41h - counter 1, RAM refresh counter (don't use it!)
; 42h - counter 2, speaker
; 43h - control (whole action must be done after write, or system may hang)
;	B0: 0=counter is a 16-bit binary counter (0..0FFFFh)
;	    1=counter is a 16-bit decimal counter 4x4 bit decades (0..9999h)
;	B1..B3: counter mode (see below)
;	B4..B5: latch mode: 0=counter Latch, 1=LOW, 2=HIGH, 3=LOW then HIGH
;	B6..B7: counter index 0..2
;    or B6..B7 = 3, read status:
;		B1..B3: mask of counters to access
;		B4..B5: 1=read counter value
;			2=read counter status (from 40h..42h):
;				B7: out pin state
;				B6: 1=counter value is 0
;				B0..B5: like defined in the Control Word Reg.

		CODE_SECTION

; -----------------------------------------------------------------------------
;                          Initialize timer counter
; -----------------------------------------------------------------------------
; INPUT:	AL = timer mode (TIMER_MODE_CNT, ...)
;		CX = counter initial value (1 to 65535, 0 stands for 65536)
;		DL = counter index (0 to 2)
; NOTES:	On mode TIMER_MODE_GEN value of 1 is illegal.
; 		It temporaly disables interrupts.
;		It takes aprox. 7 us.
; -----------------------------------------------------------------------------

; ------------- push registers

TimerSetMode:	pushf			; push flags
		push	ax		; push AX
		cli			; disable interrupts

; ------------- prepare control word -> AL

		shl	al,1		; AL = timer mode << 1, BIN mode
		or	al,B5+B4	; set 2-byte mode
		mov	ah,dl		; AH <- counter index
		ror	ah,1
		ror	ah,1		; AH <- shift to bits 6-7
		or	al,ah		; AH <- add counter index
		
; ------------- set counter mode

		out	TIMER_CTRL,al	; set counter mode
		SHORT_DELAY		; short delay

; ------------- prepare counter port -> DX

TimerSetMode4:	push	dx		; push DX
		mov	dh,0		; DH <- 0
		add	dl,TIMER_BASE	; DX <- counter port

; ------------- set initial value LOW

		mov	al,cl		; AL <- initial value LOW
		out	dx,al		; set initial value LOW
		SHORT_DELAY		; short delay
		
; ------------- set initial value HIGH

		mov	al,ch		; AL <- initial value HIGH
		out	dx,al		; set initial value HIGH

; ------------- pop registers

		pop	dx		; pop DX
		pop	ax		; pop AX
		popf			; pop flags (maybe enable interrupts)
		ret

; -----------------------------------------------------------------------------
;                       Set counter new initial value
; -----------------------------------------------------------------------------
; INPUT:	CX = counter new init. value (1 to 65535, 0 stands for 65536)
;		DL = counter index (0 to 2)
; NOTES:	On mode TIMER_MODE_GEN value of 1 is illegal.
; 		It temporaly disables interrupts.
;		It takes aprox. 4 us.
; -----------------------------------------------------------------------------

; ------------- push registers

TimerSetVal:	pushf			; push flags
		push	ax		; push AX
		cli			; disable interrupts
		jmp	short TimerSetMode4

; -----------------------------------------------------------------------------
;                          Get counter current value
; -----------------------------------------------------------------------------
; INPUT:	DL = counter index (0 to 2)
; OUTPUT:	AX = counter current value
; NOTES:	It temporaly disables interrupts.
;		It takes aprox. 7 us.
; -----------------------------------------------------------------------------

; ------------- push registers

TimerGetVal:	pushf			; push flags
		push	dx		; push DX
		cli			; disable interrupts

; ------------- set counter LATCH command

		mov	al,dl		; AL <- counter index
		ror	al,1
		ror	al,1		; AL <- shift to bits 6-7
		out	TIMER_CTRL,al	; set counter latch command
		SHORT_DELAY		; short delay

; ------------- prepare counter port -> DX

		mov	dh,0		; DH <- 0
		add	dl,TIMER_BASE	; DX <- counter port

; ------------- get current value LOW

		in	al,dx		; AL <- get current value LOW
		SHORT_DELAY		; short delay
		mov	ah,al		; AH <- current value LOW
		
; ------------- get current value HIGH

		in	al,dx		; AL <- get current value HIGH
		xchg	al,ah		; AL <- value LOW, AH <- value HIGH

; ------------- pop registers

		pop	dx		; pop DX
		popf			; pop flags (maybe enable interrupts)
		ret

; -----------------------------------------------------------------------------
;                           Get counter output
; -----------------------------------------------------------------------------
; INPUT:	DL = counter index (0 to 2)
; OUTPUT:	AL = counter current output state (0 or 1)
; NOTES:	It temporaly disables interrupts.
;		It takes aprox. 4 us.
; -----------------------------------------------------------------------------

; ------------- push registers

TimerGetOut:	pushf			; push flags
		push	dx		; push DX
		cli			; disable interrupts

; ------------- set counter LATCH STATUS command

		mov	al,B1		; AL <- prepare bit of COUNTER 0
		xchg	cx,dx		; CL <- counter index
		shl	al,cl		; AL <- counter bit
		xchg	cx,dx		; CX, DX <- return old value
		or	al,B7+B6+B5	; prepare LATCH STATUS command
		out	TIMER_CTRL,al	; set LATCH STATUS command
		SHORT_DELAY		; short delay

; ------------- Get current status

		add	dl,TIMER_BASE	; EDX <- counter port
		in	al,dx		; AL <- get current status
		rol	al,1		; AL <- OUT pin value shift B7 -> B0
		and	al,B0		; mask value

; ------------- Pop registers

		pop	dx		; pop DX
		popf			; pop flags (maybe enable interrupts)
		ret

; -----------------------------------------------------------------------------
;                            Constant data
; -----------------------------------------------------------------------------

		CONST_SECTION

; -----------------------------------------------------------------------------
;                            Uninitialised data
; -----------------------------------------------------------------------------

		DATA_SECTION
