;; tmr0_16bit.asm
        ;;
        ;; The purpose of this program is to test how well gpsim can simulate
        ;; TMR0 for a 16bit-core pic (like the 18fxxx family)
        ;; Here are the tests performed:
	;;
	;; -- TMR0L and TMR0H can be read and written
	;; -- Writing to TMR0L 

	list    p=18f452                ; list directive to define processor
	include <p18f452.inc>           ; processor specific variable definitions
        include <coff.inc>              ; Grab some useful macros
        radix   dec                     ; Numbers are assumed to be decimal

	include "delay.inc"	; Defines the "DELAY" macro


;----------------------------------------------------------------------

; Printf Command
.command macro x
  .direct "C", x
  endm

;----------------------------------------------------------------------
GPR_DATA                UDATA
temp            RES     1
temp1           RES     1
temp2           RES     1
failures        RES     1

TMR0_RollOver	RES	1
tmr0Lo		RES	1
tmr0Hi		RES	1
psa		RES	1
b16bitMode	RES	1
countLo		RES	1
countHi		RES	1

  GLOBAL tmr0Lo, tmr0Hi
  GLOBAL psa,b16bitMode
  GLOBAL done
  GLOBAL countLo, countHi


;----------------------------------------------------------------------
;   ******************* MAIN CODE START LOCATION  ******************
;----------------------------------------------------------------------
STARTUP    CODE	0

	bra	Start

;------------------------------------------------------------------------
;
;  Interrupt Vector
;
;------------------------------------------------------------------------

INT_VECTOR   CODE    0x008               ; interrupt vector location


check_TMR0_interrupt:

	BTFSC	INTCON,T0IF		;If the roll over flag is not set
	 BTFSS	INTCON,T0IE		;or the interrupt is not enabled
	  RETFIE 1			; Then leave

    ;; TMR0 has rolled over. Clear the pending interrupt and notify
    ;; the foreground code by incrementing the "roll over" counter
    ;; Notice that the increment operation is atomic - this means
    ;; we won't have to worry about the foreground code and interrupt
    ;; code clashing over accesses to this variable.
	
        BCF     INTCON,T0IF,0           ; Clear the pending interrupt
        INCF    TMR0_RollOver,F         ; Set a flag to indicate rollover

ExitInterrupt:
	RETFIE	1


MAIN	CODE

Start:	
  ; At reset, all bits of t0Con should be set.
  .assert "t0con == 0xff"
	nop

  ;------------------------------------------------------------
  ; The high byte of TMR0 is exposed to the firmware as a shadow register. When
  ; firmware writes to this register, the value is latched. When the firmware
  ; writes the to the low byte, then the latched high byte will get copied
  ; along with the low byte to TMR0. This allows all 16-bits to be written in a
  ; single cycle. Similarly, when TMR0L is read, the high byte of the timer
  ; is copied to the shadowed register. 

TMR0H_ShadowRegisterTest:
  .command  "echo *** Testing TMR0H"

	MOVLW	42
	MOVWF	TMR0H
  .assert "tmr0h == 42"

	MOVWF	TMR0L
  .assert "tmr0l == 42"

	MOVF	TMR0L,W
  .assert "tmr0l == 42"
	MOVF	TMR0H,W
  .assert "tmr0h == 42"

    ; Clear the shadowed TMR0H register and verify that it cleared.
	CLRF	TMR0H
  .assert "tmr0h == 0"

    ; Now, when we read the low byte of Timer 0, the high byte gets
    ; refreshed. So let's check that TMR0H changes due to a TMR0L read:
	MOVF	TMR0L,W
  .assert "tmr0l == 42"
	MOVF	TMR0H,W
  .assert "tmr0h == 42"
	NOP

TMR0_8BitModeTest:

 .command  "echo *** Testing 8-bit mode"

  ; Stop the timer and clear its value
	CLRF	T0CON
	CLRF	TMR0L
	CLRF	TMR0H
  .assert "(tmr0l == 0) && (tmr0h == 0)"

  ; The timer is off, so any value written to it should be read back
  ; unchanged.
	MOVLW	42
	MOVWF	TMR0L
  .assert "tmr0l == 42"

	MOVWF	TMR0H
  .assert "tmr0h == 42"
	MOVWF	tmr0Hi
  ;------------------------------------------------------------
  ; 8-bit mode tests
  ;
  ; First, TMR0 is tested in 8-bit mode and with interrupts disabled.
  ; All 8 combinations of PSA are tested.

	CLRF	psa	; Shadow's the PSA bits of T0CON

L1_tmr0_8BitModeTest:   ; Beginning of the loop

    ; Start off with T0CON and TMR0L in a known state.

  .command  "psa"
	CLRF	T0CON
	CLRF	TMR0L
	CLRF	tmr0Lo

    ; Assume that the PSA is not assigned to TMR0:

	MOVLW	(1<<TMR0ON) | (1<<T08BIT) | (1<<PSA)

    ; if psa is non-zero then the prescaler *is* assigned to tmr0
	MOVF	psa,F
	BZ	L_psaInitComplete

    ; psa is one greater than the actually Prescale bits
	DECF	psa,W
	ANDLW	7
	IORLW	(1<<TMR0ON) | (1<<T08BIT)
		
L_psaInitComplete:
	MOVWF	T0CON
    ;
    ; tmr0 is running now. It should increment once every PSA cycles.
    ;
    ; First read the whole 16-bit counter note

	MOVF	TMR0L,W
	MOVF	TMR0H,W
  .assert "W == tmr0Hi"
	NOP

    ; Now we'll check TMR0L after various delays. The assertion works
    ; by reading tmr0l and comparing it to the number of cycles that
    ; have gone by since the timer started. The shift right by 'psa' allows
    ; the same assertion to be used as we loop through all combinations
    ; of psa.

    ; Single cycle checks.

  .assert "tmr0l == ((tmr0Lo+4)>>psa)"
	NOP
  .assert "tmr0l == ((tmr0Lo+5)>>psa)"
	NOP
  .assert "tmr0l == ((tmr0Lo+6)>>psa)"
	NOP
  .assert "tmr0l == ((tmr0Lo+7)>>psa)"
	NOP
  .assert "tmr0l == ((tmr0Lo+8)>>psa)"
	NOP
  .assert "tmr0l == ((tmr0Lo+9)>>psa)"
	NOP
  .assert "tmr0l == ((tmr0Lo+10)>>psa)"

  ; 8 23-cycle delays.

	MOVLW	23 - 10
	RCALL	DelayCycles
  .assert "tmr0l == ((tmr0Lo+33)>>psa )"

	MOVLW	23 - 10
	RCALL	DelayCycles
  .assert "tmr0l == ((tmr0Lo+56)>>psa )"

	MOVLW	23 - 10
	RCALL	DelayCycles
  .assert "tmr0l == ((tmr0Lo+79)>>psa )"

	MOVLW	23 - 10
	RCALL	DelayCycles
  .assert "tmr0l == ((tmr0Lo+102)>>psa )"

	MOVLW	23 - 10
	RCALL	DelayCycles
  .assert "tmr0l == ((tmr0Lo+125)>>psa )"

	MOVLW	23 - 10
	RCALL	DelayCycles
  .assert "tmr0l == ((tmr0Lo+148)>>psa )"

	MOVLW	23 - 10
	RCALL	DelayCycles
  .assert "tmr0l == ((tmr0Lo+171)>>psa )"

	MOVLW	23 - 10
	RCALL	DelayCycles
  .assert "tmr0l == ((tmr0Lo+194)>>psa )"

  ; 255 and 256 cycle boundary condition

	MOVLW	61 - 10
	RCALL	DelayCycles
  .assert "tmr0l == ((tmr0Lo+255)>>psa )"
	NOP

  .assert "tmr0l == (((tmr0Lo+256)>>psa)&0xff)"

  ; now for some bigger delays.

	DELAY  251
  .assert "tmr0l == (((tmr0Lo+256+251)>>psa)&0xff)"

	DELAY  251
  .assert "tmr0l == (((tmr0Lo+256+2*251)>>psa)&0xff)"

	DELAY  251
  .assert "tmr0l == (((tmr0Lo+256+3*251)>>psa)&0xff)"

	DELAY  251
  .assert "tmr0l == (((tmr0Lo+256+4*251)>>psa)&0xff)"

	DELAY  251
  .assert "tmr0l == (((tmr0Lo+256+5*251)>>psa)&0xff)"

	DELAY  251
  .assert "tmr0l == (((tmr0Lo+256+6*251)>>psa)&0xff)"

	DELAY  251
  .assert "tmr0l == (((tmr0Lo+256+7*251)>>psa)&0xff)"

	DELAY  251
  .assert "tmr0l == (((tmr0Lo+256+8*251)>>psa)&0xff)"

	DELAY  8191
  .assert "tmr0l == (((tmr0Lo+256+8*251+8191)>>psa)&0xff)"

	INCF	psa,F
	btfss	psa,3
	 bra	L1_tmr0_8BitModeTest

    ; Through looping through all combinations of PSA.
    ; Now make sure that the high byte of TMR0 hasn't changed.
	MOVF	TMR0H,W
  .assert "W == tmr0Hi"
	NOP


;------------------------------------------------------------
; Interrupt tests

	NOP
	NOP
	NOP


	CLRF	T0CON		;Stop TMR0
	CLRF	INTCON		;Clear any pending interrupts
	BSF	INTCON,T0IE	;Enable TMR0 overflow interrupts
	BSF	INTCON,GIE	;Enable global interrupts

	CLRF	psa		;Loop counter and prescaler assignment.

L1_InterruptTest:

    ; Stop and clear TMR0:
	CLRF	T0CON
	CLRF	TMR0H
	CLRF	TMR0L
	CLRF	TMR0_RollOver   ;Interrupt flag

    ;Software counter to count cycles

	SETF	countLo		;Start off with counter==-1
	SETF	countHi

    ; Assume that the PSA is not assigned to TMR0:

	MOVLW	(1<<TMR0ON) | (1<<T08BIT) | (1<<PSA)

    ; if psa is non-zero then the prescaler *is* assigned to tmr0
	MOVF	psa,F
	BZ	L_psaInitComplete2

    ; psa is one greater than the actually Prescale bits
	DECF	psa,W
	ANDLW	7
	IORLW	(1<<TMR0ON) | (1<<T08BIT)
L_psaInitComplete2:
	MOVWF	T0CON

    ; Now enter into a loop and wait for the interrupt.
    ; Each loop iteration takes exactly 8 cycles.

TMR0_WaitForInt:
	NOP
	NOP
	clrwdt
	INFSNZ	countLo,F
	 INCF	countHi,F
        BTFSS   TMR0_RollOver,0
	 bra	TMR0_WaitForInt

  .assert " (((countHi<<8)+countLo)>>(5+psa)) == 1"
	INCF	psa,F
	btfss	psa,3
	 bra	L1_InterruptTest

	nop

;------------------------------------------------------------
; 16bit-mode tests

	CLRF	psa

L1_tmr0_16BitModeTest:

	CLRF	T0CON
	CLRF	TMR0H
	CLRF	TMR0L

    ; Assume that the PSA is not assigned to TMR0:

	MOVLW	(1<<TMR0ON) | (1<<PSA)

    ; if psa is non-zero then the prescaler *is* assigned to tmr0
	MOVF	psa,F
	BZ	L_psa16bitInitComplete

    ; psa is one greater than the actually Prescale bits
	DECF	psa,W
	ANDLW	7
	IORLW	(1<<TMR0ON)
		
L_psa16bitInitComplete:
  .command  "psa"
	MOVWF	T0CON


	MOVF	TMR0L,W
  .assert "( ((tmr0h<<8)+W) == (1>>psa))"

	MOVF	TMR0L,W
  .assert "( ((tmr0h<<8)+W) == (2>>psa))"

	MOVF	TMR0L,W
  .assert "( ((tmr0h<<8)+W) == (3>>psa))"

	MOVF	TMR0L,W
  .assert "( ((tmr0h<<8)+W) == (4>>psa))"

	MOVF	TMR0L,W
  .assert "( ((tmr0h<<8)+W) == (5>>psa))"


	MOVF	TMR0L,W
	MOVWF	tmr0Lo

  .command  "tmr0Lo"
  .command  "tmr0h"

	DELAY	1024-2
	MOVF	TMR0L,W
	MOVWF	tmr0Lo

  .command  "tmr0Lo"
  .command  "tmr0h"

	DELAY	8192-2
	MOVF	TMR0L,W
	MOVWF	tmr0Lo

  .command  "tmr0Lo"
  .command  "tmr0h"

	INCF	psa,F
	btfss	psa,3
	 bra	L1_tmr0_16BitModeTest

	nop
done:
  .assert  "\"*** PASSED 16bit-core TMR0 test\""
        bra     $

failed:
        movlw   1
        movwf   failures
  .assert  "\"*** FAILED 16bit-core TMR0 test\""
        bra     done

 end


syntax highlighted by Code2HTML, v. 0.9.1