PIC Microcontoller Memory Method

Table lookups

Mike Keitz says:

...There are two situations where "paging" of the program memory is an issue. For call or GOTO PIC processors]. Most short programs don't need to be concerned with them. [HOWEVER] For writing to PCL, the pages are only 100h instructions long. So [any] 256-value table definitely crosses one of those pages. And the program will crash when the index into the table gets large enough if your table-access code doesn't set up PCLATH properly.

[Here is some code that sets up PCLATH correctly]

	mov	W, #high   (TABLE_START)
;*** WARNING: PCLATH register bits are in STATUS PAx bits. Or use PAGE/IREAD if possible
;   movwf  pclath
	mov	pclath, W
	mov	W, index
;*** WARNING: ADDLW was expanded in three instructions! Check if previous instruction is a skip instruction. 
;   addlw  TABLE_START
	mov	Hack, W
	mov	W, #TABLE_START
	add	W, Hack
	snb	C
;*** WARNING: PCLATH register bits are in STATUS PAx bits. Or use PAGE/IREAD if possible
;    incf  pclath,f
	inc	pclath
	mov	PC, W
TABLE_START
	retw	#...

If the table is called such that W is the index:

;*** WARNING: ADDLW was expanded in three instructions! Check if previous instruction is a skip instruction. 
;   addlw  TABLE_START
	mov	Hack, W
	mov	W, #TABLE_START
	add	W, Hack
	mov	temp, W
	mov	W, <<known_zero
;*** WARNING: ADDLW was expanded in three instructions! Check if previous instruction is a skip instruction. 
;   addlw  high(TABLE_START)
	mov	Hack, W
	mov	W, #high(TABLE_START)
	add	W, Hack
;*** WARNING: PCLATH register bits are in STATUS PAx bits. Or use PAGE/IREAD if possible
;   movwf  pclath
	mov	pclath, W
	mov	W, temp
	mov	PC, W
TABLE_START
	retw	#...

Andrew Warren of Fast Forward Engineering - San Diego, California http://www.geocities.com/SiliconValley/2499 says:

...write table-lookup code [with] automatic page-crossing checks built in.

I generally do it like this:

	add	PC, W

    TABLE1:

        DT      "Test"

        IF ((HIGH ($)) != (HIGH (TABLE1)))
            ERROR "TABLE1 CROSSES PAGE BOUNDARY!"
        ENDIF

P.S. By the way, another common way that a newly-inserted CALL can screw up previously-working code is if the called routine changes the PCLATH register... Or if your previosuly-working code expected the W register or STATUS flags to remain unchanged across the portion of the program where you inserted your CALL.

Rich Leggitt says:

...something like this should work for arbitrary table of any length located anywhere in the program space.
                ...
	mov	W, #high string	; point to a string
	mov	look_hi, W	; in reality, a macro...
	mov	W, #low string
	mov	look_lo, W
	call	process	; go process it
                ...

; subroutine to process string at look_hi/look_lo
process	call	lookup	; get a byte (this is the magic)

                ; here, do something with byte in W
; also,	ret
                ; otherwise...

	jmp	process	; do it again

; Jump to address in look_hi/look_lo, which presumably is an RETLW.
; Note pointer post increment.
; Equivalent to: W=*look_ptr++
lookup	mov	W, look_hi	; set PCLATH
;*** WARNING: PCLATH register bits are in STATUS PAx bits. Or use PAGE/IREAD if possible
;                movwf PCLATH
	mov	PCLATH, W
	mov	W, look_lo	; and get PCL
	inc	look_lo	; but post inc
	snb	Z
	inc	look_hi
	mov	PC, W	; ok, now jump


See Also: