Conditionally replaceing one value with another
Big Table Lookups (example for sound playback)
Compressing large tables with linear interpolation by Peter Hemsley "
Ken Websters PIC16C74 serial port multiplexer. This includes a complete SRAM interface and memory test code.
This article discusses reading constant values from program memory (Flash).
See "File Select Register" fsr.htm and "Moving data blocks" memmove.htm for reading and writing values to RAM.
See "Electrically Eraseable Programmable Read Only Memory" for reading and writing values to EEPROM.
On SX chips, you can access program memory via the "IREAD" instruction. See SX tables /techref/scenix/lib/mem/tables_sx.htm .
Douglas Wood [dbwood at kc.rr.com] says:
On the 17Cxxx and 18Cxxx PICs, you can access program memory via the 'table read' instructions.
On the 16Fxxx PICs, you can access program memory via a set of SFR (special function registers).
On all other PICs, you'll have to use a 'computed GOTO/RETW-style' table.
Drew Vassallo says:
More specifically, typically you access data tables through a call and modification of a program counter. For Microchip's MPLAB or MPASM, the instruction for "data" that is used is "dt" followed by your data, which will automatically be assembled into a data table, one line for each byte of data.;;Call with byte to retrieve in W (in this case, W in range of 0-3) SimpleTable addwf PCL dt 0x01, 0x02, 0x03, 0x04
The dts are actually assembled as RETW #'s one after another. But watch out for one little "gottcha": Any instruction, other than a jump, that modifies the PCL register, also clears the 9th bit (bit 8). So addwf PCL only works if the target is in the first half of any 512 address page. See paging
Mike Keitz says:
...There are two situations where "paging" of the program memory is an issue. For CALL or GOTO, the pages are 800h instructions long [on the more advanced 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]
movlw high (TABLE_START) movwf pclath movf index,w addlw TABLE_START skpnc incf pclath,f movwf pcl TABLE_START retlw ...If the table is called such that W is the index:
addlw TABLE_START movwf temp rlf known_zero,w addlw high(TABLE_START) movwf pclath movf temp,w movwf pcl TABLE_START retlw ...
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:
ADDWF PCL 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.... movlw high string ; point to a string movwf look_hi ; in reality, a macro... movlw low string movwf look_lo 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, return if end of string ; otherwise... goto 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 movf look_hi,w ; set PCLATH movwf PCLATH movf look_lo,w ; and get PCL incf look_lo,f ; but post inc skpnz incf look_hi,f movwf PCL ; ok, now jump
Germain Morbe [GMorbe at CDSGMBH.COM] says:
...while still working on my cipher routine i need to inline read a short table without using ram. Here is what i found. Could be of interrest to some. Any shorter forms known?Enter with pointer in w Exit with Value1 to Value4 in w ... addwf pcl,f xorlw ( 0 xor VALUE1 xor VALUE2 xor 1 ) xorlw ( 1 xor VALUE2 xor VALUE3 xor 2 ) xorlw ( 2 xor VALUE3 xor VALUE4 xor 3 ) xorlw ( 3 xor VALUE4 ) ...
Rubenflc@yahoo asks:
Hello, I,m Rubén. I,m doing a project which I have to use a PIC16F873 and I,ve got a problem, I would like you tell me the bits of the PCLATH and PCL register. I,m doing the main program in page0 and where there is a table, but have to take a second table from page1 to compare and either of them has 128 positions.
Thanks
Dear Rubén, your email bounced.
Dear Ruben,
I think you will enjoy reading Table lookups http://www.piclist.com/techref/microchip/tables.htm . Once you figure it out, it's pretty simple. Please tell me what you find confusing. Can you write a simpler explanation ?
Have you seen page http://www.piclist.com/techref/piclist/begin.htm ?
Have you seen the example program on page http://www.dontronics.com/psbpix/lookdown.html ?
I use the fcall macro. Then I don't have to ever deal with PCLATH. Perhaps something like this:
; (warning: untested code) ;(macros go here) ; the fcall macro ; by Roger Froud of Amytech Ltd. fcall macro subroutine_name local here lcall subroutine_name ; set PCLATH correctly pagesel here ; set PCLATH correctly here: endm ;(initialization code goes here) ;... main_loop: ;... movlw 3 fcall string_table movwf temp1 ;... movlw 4 fcall offset_table movwf temp2 ;... goto main_loop ;... ;(subroutines go here) ;... ; doesn't matter if this is in page 0 or 1, as long as it's entirely within a 0x100 boundary string_table: ADDWF pcl RETLW 'D' RETLW 'E' RETLW 'F' RETLW 'G' IF ((HIGH ($)) != (HIGH (string_table+1))) ERROR "string_table CROSSES PAGE BOUNDARY!" ENDIF ;... ; doesn't matter if this is in page 0 or 1, as long as it's entirely within a 0x100 boundary offset_table: ADDWF pcl RETLW -3 RETLW -3 RETLW -2 RETLW -1 RETLW -0 RETLW -0 RETLW 1 IF ((HIGH ($)) != (HIGH (offset_table+1))) ERROR "offset_table CROSSES PAGE BOUNDARY!" ENDIF ;...
Does that help ?
Some people prefer the RETLW data method because it works in all Microchip PIC processors.
Alternatively, you could use EEDATA and related registers instead of the RETLW instruction to read a table of data in FLASH.
Some people prefer the EEDATA method because it allows them to pack more information into each FLASH location (14 bits rather than 8 bits), although it only works with the PIC16F87X processors
The data sheet for the PIC16F87XA from http://www.microchip.com/1000/pline/picmicro/ describes this alternative method in section "3.5 Reading FLASH Program Memory"
---- ; EXAMPLE 3-3: FLASH PROGRAM READ banksel MS_PROG_EE_ADDR MOVLW MS_PROG_EE_ADDR ; MOVWF EEADRH ; MS Byte of Program Address to read MOVLW LS_PROG_EE_ADDR ; MOVWF EEADR ; LS Byte of Program Address to read banksel EECON1 BSF EECON1, EEPGD ; Point to PROGRAM memory BSF EECON1, RD ; EE Read NOP ; Any instructions here are ignored as program ... NOP ; ... memory is read in second cycle after BSF EECON1,RD banksel EEDATA MOVF EEDATA, W ; W = LS Byte of Program EEDATA MOVWF DATAL ; MOVF EEDATH, W ; W = MS Byte of Program EEDATA MOVWF DATAH ; ----
More information than you need about the interaction between PCLATH and PC is at http://www.piclist.com/techref/microchip/pages.htm which lists the fcall macro. -- David Cary
See Also:
David A Cary of Motorguide Pinpoint Says:
Bob Ammerman of Ram Systems says:Here is a trick I use to avoid problems with PCLATH/PCLATU and interrupts on 18C chips:given:
rcall lookup_funcinstead of:
;align to page start if needed lookup_func: addwf PCL,F,A retlw 1 retlw 2I have used:
;align to page start if needed lookup_func: rcall lookup_index retlw 1 retlw 2 lookup_index: addwf TOSL,F,A return ; For large tables you can: lookup_index: addwf TOSL,F,A btfsc STATUS,C incf TOSH,F,A returnIn the program where I use this, I consider PCLATH/PCLATU to be resources usable _only_ in my interrupt handler. I use the above trick for code at task level. This seems to work quite well. It saves me the requirement of saving PCLATH in my interrupt handler (which is _very_ pressed for time).
I have _not_ seen the pattern of MOVFF somehow being interrupted in the middle. Are you saying that the instruction is basically skipped when this happens (ie: it doesn't execute before entering the interrupt and it doesn't execute on return from the interrupt). I'd love more particulars here if possible.
Also, in regard to the LFSR instruction. As far as I know, all silicon currently available is broken. Just say _no_ to the instruction. It is a shame, because this would be a _very_ useful instruction.
Archive:
Comments:
"For writing to PCL, the pages are only 100h instructions long. So [any] 256-value table definitely crosses one of those pages."
This is not true for the case where the table starts right at the beginning of a page, offset zero.
In fact I came across this a few days ago while coding a CRC-8 routine with a table lookup. The table was exactly 256 bytes and I could have it in a single page without problems just by doing this:
org 0x1ff lookup_value ; w preloaded in the range 0..255 addwf PCL table dt 0x01, 0x02, 0x03, 0x04 .... [256 bytes]
PCLATH would be preset to the corresponding value for 'table', not for
'lookup_value'.
I'm working with PIC16F876; I need to assign to a variable a value readable in a specific register; The program gives an offset number and then has to assign to variable AAA the value of register positioned in STARTADDRESS+GIVENOFFSET. Could someone help me?
See:
Comments:
Please keep in mind that the MPLAB assembler seems to have trouble with long strings in dt fields. If you have sting more then 200 bytes long, you may want to break in into 2 or more dt commands. I cannot find any documentation that explains this behaviour - I found this out the hard way, using MPLAB ver 5.62.
Questions:
Hi,
Your website is very interesting and I really should have been able to find the answer to my problem but I'm still confused. I've a program with 3 lookup tables and each time I try to use them my program jumps to the wrong location (because the tables are too long). I think I need to modify the PCLATH in some way, I can forward my program...any advice...HELP! :-)
DegDisTab addwf PCL,1
RETLW 00H ;0
RETLW 00H ;1
RETLW 00H ;2
RETLW 00H ;3
etc....
Regards
Craig
James Newton replies: Please read Big tables
Hi
I am a little confused by Microchip Appln Note AN556 on Page 3, where the Example 5 code segment implements a 13-bit computed GOTO.
After using this code segment, I see that my CALL to TABLE offsets by the wrong amount. I cant imagine that the code in the Application Note can be wrong, so what could I be doing wrong?
kind regards
Manoj
When we lookup a table's element,we can use the instructions: 'addwf pcl' and 'retlw'.But I am not well fimilar with the instruction 'tblrd',especially when I want to access the element with an offset.Anybody can help me to explain it in detail?? thanks
The "tblrd" instruction is not available on every PIC, as you can see on the PIC Microcontroller Instruction Set Comparison Matrix.
Some example programs that use the "tblrd" instruction:
Does that help ? -- David Cary
Interested:
Hello,
I have PIC18F4550
I worked with computed goto for driving 7-seg LEDs and it's OK.
When I read the MPASM about data directives, most of them are for PIC12/PIC16.
So, what're the directives for PIC18?
If I want to change computed goto method, how to develop sets of bytes, because now I want to drive dot matrix display. And each character is a set of 8-bytes.
For example, char 'A' is 0x3C,0x7E,0x66,0xC3,0xFF,0xC3,0xC3,0xC3
With the char generator.
1. Now, if I want to call multiple chars, then how to call multiple tables?
2. How to move from char to another one?
3. Which directive to apply?
Thank you,