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!" ENDIFP.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: