; ------------------------------------------------
; Sleep timer software
; Prog by H.Momose
;
; MCU   : PIC16F84
; CLOCK : 8MHz

		.include	16f84.h
		.osc		hs
		.wdt		off
		.pwrt		on
		.protect	off


tset	equ		5.0		; Time set button input
pcout	equ		5.1		; SSR control output
led_1m	equ		5.2		; LED 1min digit common output
led_10m	equ		5.3		; LED 10min digit common output
led_dp	equ		6.1		; LED decimal point output


		org 0ch			; General ram
		
scale_ms	ds	1		; millisec scaler
scale_500ms	ds	1		; 1/2sec scaler		 These are changed by Interrupt

mcnt_stat	ds	1		; master counter status
mcnt_sec	ds	1		; master counter sec
mcnt_min	ds	1		; master counter min

tmp1		ds	1
tmp2		ds	1

wait_cn		ds	1
wait_cn2	ds	1

tset_latch	ds	1
w_saved		ds	1
stat_saved	ds	1
		
		
		org	0			; Reset vector
		goto	start
		
		org	4			; Interrupt vector
		goto	inth

; -----------------------------------------------
; Startup / initializer
;
;
start
											; Port Direction (1=>input / 0=>output)
		mov		!ra,	#00000001b			; Port A (NA NA NA NC LED_10M LED_1M PCOUT TSET)
		mov		!rb,	#00000000b			; Port B (LED: g b f a c e dp d)
		clr		ra
		clr		rb
		
											; Setup interrupt / timeref
		mov		scale_ms,		#250
		mov		scale_500ms,	#1
		setb 	rp0							; focus bank 1
		mov		option,	#00000011b			; (OPTION: /rbpu intedg t0cs t0se psa ps2:ps0)
		clrb 	rp0							; focus bank 0
		mov 	intcon,  #10100000b			; (INTCON: GIE EEIE T0IE INTE RBIE T0IF INTF RBIF)
		
											; Setup time counter
		mov		mcnt_min,	#0				; min <= 0
		mov		mcnt_sec,	#0				; sec <= 0
		
											; Initialize other memories
		mov		mcnt_stat,	#0
		
; -----------------------------------------------
; Master counter
;
;
mcnt_start
		
		csne	mcnt_stat,	scale_500ms		; (if mcnt_stat != scale_500ms) skip next
		goto	mcnt_end					; in case (cnt_stat==scale_500ms)

mcnt_cnt_stat_ne_scale_500ms				; in case cnt_stat != scale_500ms
		mov		mcnt_stat, 	scale_500ms		; mcnt_stat <= scale_500ms
		cse		mcnt_stat,	#1				; if( mcnt_stat == 1 )skip next
		goto	mcnt_end

mcnt_cnt_stat_eq_1							; in case mcnt_stat == 1
		
		csa		mcnt_sec,	#0				; if(mcnt_sec > 0) skip next
		goto	mcnt_sec_eq_0

mcnt_sec_gt_0								; in case mcnt_sec > 0
		dec		mcnt_sec					; mcnt_sec --
		setb	pcout						; Turn on SSR
		goto	mcnt_end
		
mcnt_sec_eq_0								; in case mcnt_sec == 0
		csa		mcnt_min, #0				; if(mcnt_min > 0) skip
		goto	mcnt_min_eq_0

mcnt_min_gt_0								; in case mcnt_min > 0
		dec		mcnt_min					; mcnt_min --
		mov		mcnt_sec, #59				; mcnt_sec <= 59
		setb	pcout						; Turn on SSR
		goto	mcnt_end
		
mcnt_min_eq_0								; in case (mcnt_min == 0 && mcnt_sec == 0)
		clrb	pcout						; Turn off SSR
		
mcnt_end


; -----------------------------------------------
; LED display
; Change wait value for LED brightness.
LED_DUTY_ON		equ		2
LED_DUTY_OFF	equ		4

dsp_start
										; Display 10m digit
		mov		tmp1, mcnt_min			; tmp1 = mcnt_min
		cse		mcnt_sec, #0			; if(mcnt_sec == 0) skip next
		inc		tmp1					; tmp1 ++
		mov		w, tmp1					; w = tmp1
		call	div10					; w = w / 10
		call	getfont					; w = ledfont(w)
		mov		rb, w					; rb = w
		
		setb	led_10m					; Turn on 10min digit
		mov		wait_cn, #LED_DUTY_ON	; wait LED on duty
		call	wait_ms
		clrb	led_10m					; Turn off 10min digit
		
		
		mov		wait_cn, #LED_DUTY_OFF	; wait LED blanking
		call	wait_ms
		
										; Display 1m digit
		mov		tmp1, mcnt_min			; tmp1 = mcnt_min
		cse		mcnt_sec, #0			; if(mcnt_sec ==0) skip next
		inc		tmp1					; tmp1 ++
		mov		w, tmp1					; w = tmp1
		call	divamari				; w = w % 10
		call	getfont					; w = ledfont(w)
		snb		pcout					; if(pcout == 0) skip next
		call	getdp					; w |= dp
		mov		rb, w					; rb = w
		
		setb	led_1m					; Turn on 1min digit
		mov		wait_cn, #LED_DUTY_ON	; wait LED on duty
		call	wait_ms
		clrb	led_1m					; Turn off 1min digit
		
		mov		wait_cn, #LED_DUTY_OFF	; wait LED blanking
		call	wait_ms
		
dsp_end

; -----------------------------------------------
; Time set push button
; Note LED takes '+1' min
; ex left 00min:30sec => display 1 min
;
tset_start
		sb		tset					; if(tset==1) skip next
		goto	tset_eq_0
tset_eq_1
		mov		tset_latch, #1
		goto	tset_end

tset_eq_0
		cse		tset_latch, #1			; if(tset_latch == 1) skip next
		goto	tset_end

tset_latch_eq_1
		mov		tset_latch, #0
		csb		mcnt_min, #89			; if(mcnt_min < 89) skip next
		goto	tset_mcnt_gt_89
		add		mcnt_min, #10			; min += 10
		goto	tset_end
		
tset_mcnt_gt_89
		mov		mcnt_min, #98
		
tset_end
		goto	mcnt_start


; -----------------------------------------------
; LED font table
; No decimal point included (off)
; w <= digit
; call getfont
; w => fontdata
;
getfont	jmp	pc+w
		retw	10000010b					; 0 (g b f a c e dp d)
		retw	10110111b					; 1
		retw	00101010b					; 2
		retw	00100110b					; 3
		retw	00010111b					; 4
		retw	01000110b					; 5
		retw	01000010b					; 6
		retw	10000111b					; 7
		retw	00000010b					; 8
		retw	00000110b					; 9
		
; -----------------------------------------------
; LED dp blinker
;
;
getdp
		mov		tmp1, w
		cse		scale_500ms, #0				; if(scale_500ms == 1) skip next
		goto	getdp_0
		and		tmp1, #11111101b
		mov		w, tmp1
		ret
getdp_0
		or		tmp1, #00000010b
		mov		w, tmp1
		ret

; -----------------------------------------------
; Wait ms order
; wait_cn = REQUIRED WAIT TIME
; call wait_ms
wait_ms
wait_ms0
		mov	wait_cn2,#0
wait_ms1
		nop
		nop
		nop
		nop
		nop
		nop
		djnz	wait_cn2, wait_ms1
		djnz	wait_cn, wait_ms0
		ret


;--------------------------------
; Divide by 10
; OUT(w) <= IN(w) / 10
div10
		mov		tmp1,		W
		mov		tmp2,		#0
div10_0
		csae	tmp1,		#10					; if(tmp >= 10) skip next
		goto	div10_end
		inc		tmp2
		sub		tmp1, 		#10
		goto	div10_0

div10_end
		mov		W,			tmp2
		ret
		
		
;--------------------------------
; amari
; OUT(w) <= IN(w) % 10
divamari
		mov		tmp1, 		W
		mov		tmp2, 		#0
divamari_0
		csae	tmp1,		#10			;if (tmp1 >= 10) skip next
		goto	divamari_end
		sub		tmp1,		#10
		goto	divamari_0
divamari_end
		mov		W, 			tmp1
		ret


; -----------------------------------------------
; Interrupt handler
; Invoked by hardware timer TMR0
; 
inth
		mov		w_saved, w				; PUSH working reg
		mov		stat_saved, status		; PUSH status reg
		
		cse		scale_ms, #0			; if(scale_ms == 0) skip next
		goto	inth_dec_ms				; case scale_ms > 0
										; case scale_ms == 0
		mov		scale_ms, #250
		
		cse		scale_500ms, #0			; if(scale_500ms ==0) skip next
		goto	inth_dec_500ms			; case scale_500ms > 0
		
										; case scale_ms == 0 AND scale_500ms = 0
		mov		scale_500ms, #1
		goto	inth_0
		
inth_dec_ms
		dec		scale_ms
		goto	inth_0

inth_dec_500ms
		dec		scale_500ms

inth_0
		mov		tmr0, #8				; Check for counter offset value
		clrb	t0if					; Clear interrupt flag
		mov		status, stat_saved		; POP status reg
		mov		w, w_saved				; POP working reg
		retfie
