;******************************************************************************
;Filename:	clock.asm
;Author:	Eric Torrence
;Date:		17/03/06  	
;Version:	1.00
;Description:  	
;  This firmware implements an octal counter output on GP0-2 based on a 
; clock with approximately half second period.
; 
;******************************************************************************
;Revision History:
;  none
;******************************************************************************

;************************** COMPILER INSTRUCTIONS *****************************

; The following lines specify the processor hardware, and must match
; the specific processor being used.
; The include file gives names to commonly used system registers
; which are used below rather than the specific hardware addresses.
; This makes the code more readable and less error-prone.

	list      p=12f675            ; list directive to define processor
	#include <p12f675.inc>        ; processor specific variable definitions

; Turn off annoying warning messages about BANK0/BANK1
	errorlevel  -302              ; suppress message 302 from list file

	__CONFIG   _CP_OFF & _CPD_OFF & _BODEN_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT  

; '__CONFIG' directive is used to embed the processor configuration word
; within the .asm file.  See data sheet for additional information on 
; configuration word settings.

;************************** VARIABLE DEFINITIONS ******************************

; cblock defines a specific range of memory locations and gives each byte
; a specific name which can be used elsewhere in the code.
	cblock	0x20
		COUNT				; Counter value
		DELAY_hi			; Hi word of delay counter
		DELAY_mid			; Mid word of delay counter
		DELAY_lo			; Lo word of delay counter
	endc

;*************************** DEFINE STATEMENTS ********************************
; 
; Timing is controled by a delay loop.  Each loop cycle takes 4 us when 
; the chip is driven from the internal 4 MHz clock.   We want to increment 
; the counter every half second, so we should count down from 125k or 
; 0x01e848.  This (just) takes three bytes, so we need to nest three 
; delay loops to do the job.
;
; Because of the way the loop works, we must add one to each value
; (only really matters for the hi loop).  There are also some extra
; operations for checking the mid and hi loop, so the mid loop isn't
; exactly 2^8 x 4 us, but it is determinsitic.  To adjust the timing,
; simply change the values defined below.
;
; With an external clock and careful counting of operations,
; this can be made extremely precise.
#define WAIT_hi		0x02	; Countdown time for wait loop
#define WAIT_mid	0xe9	; Countdown time for wait loop
#define WAIT_lo		0x49	; Countdown time for wait loop

; Set GP0-2 as output, rest as input (high-Z)
; Note:	 GP3 can only be an input
#define TRIS		B'111000'

;****************************** Start of Program ******************************
	org		0x000			; processor reset vector
	goto	Initialize		; Skip interrupt handler

;******************************************************************************
; Initialize
;	Initialize Special Function Registers     
;******************************************************************************
	org		0x005			; Start of Programm Memory Vector
Initialize:

; The following resets the factory calibration for the internal oscillator.
; This will give (slightly) more accurate timing values.
	banksel	OSCCAL
	call    0x3FF			; retrieve factory calibration value
	movwf   OSCCAL			; update register with factory calib

; Set I/O pins direction
	banksel	TRISIO
	movlw	TRIS			; Fill value into working register (W)
	movwf	TRISIO			; Move W to Tri-state register
	clrf	ANSEL			; Make all inputs digital
	
; Turn off weak pullups on outputs by setting hi bit of OPTION_REG
	movlw	B'10000000'		; Fill value into W
	movwf	OPTION_REG		; Move value from W to OPTION_REG
	clrf	INTCON			; disable all interrupts 

	banksel	GPIO			; Select Bank 0
	clrf	COUNT			; clear counter at start

CountLoop:
	incf	COUNT, w		; increment COUNT and store result in W
	andlw	0x07			; mask off three lowest bits in W
	movwf	GPIO			; copy result to output register
	movwf	COUNT			; copy result back to COUNT 
	call	WaitSub			; Call sub-routine to wait for a while
							; Execution returns here when done
	goto	CountLoop		; Go back to start of loop

;******************************************************************************
; WaitSub
;	Wait for defined time, nominally one-half second.
;
;******************************************************************************
WaitSub:

; Initialize the countdown counter
	movlw	WAIT_hi
	movwf	DELAY_hi
	movlw	WAIT_mid
	movwf	DELAY_mid
	movlw	WAIT_lo
	movwf	DELAY_lo

;   Most operations take 1 us to complete using the internal clock, the 
;   goto takes 2, so each counter cycle takes 4 us.  There are a few 
;   extra cycles for the mid and hi loop logic, but these make a small
;   error.  For precise timing, we should take these into account as well.
;
DelayLoop:
	nop						; No operation.  Needed to waste some time
	decfsz	DELAY_lo, f		; Decrement lo  byte, skip next line if zero
	goto	DelayLoop
	decfsz	DELAY_mid, f	; Decrement mid byte, skip next line if zero
	goto	DelayLoop
	decfsz	DELAY_hi, f		; Decrement hi  byte, skip next if zero
	goto	DelayLoop		; Check if wait counter has bit set
	return					; Return to call statement
    
	end			; directive 'end of program'