On Wed, 24 Sep 1997 20:46:47 -0500 John Payson writes: >> I know that this is a simple question which has no >doubt been >> asked many times before, but I cannot understand or make any sense >of the >> PCLATH in relation to look-up tables. > >It's fairly simple to understand, if you bear these simple rules in >mind: >[note that when I refer to the top "byte" of the program counter or >PCLATH, >I'm talking about bits 8-12; the top 3 bits are unimplemented]. > >[1] On the 16Cxx chips, PCLATH is never modified except under program > control. On reset, its value is undefined and you should make no > assumptions about it. The PIC does reset the PCLATH register to 0 on a reset. But the important point is that it never automatically changes (except on reset). It is good practice though to not make any assumptions about reset other than maybe the program will start at 0. If time allows, the program will be more reliable setting PCLATH to the required value before each use. On the other hand, the PCL special function register always indicates the low 8 bits of the next instruction to be executed. If the program changes PCL, the PIC will (a) discard the prefetched next instruction, causing the PCL change to take 2 cycles (b) set the program counter to PCLATH:PCL and execute the instruction there. The assembler functions "high" and "low" are useful to find the address of an instruction in terms of the low 8 bits and the high bits. The conventional method of reading a table of 256 or fewer entries: ... movfw index ;Lookup table[index] call gettbl ;Get table value into W ... gettbl addwf PCL,f ;Index in W - vector to one of the entries table retlw 5 ;table[0] retlw 8 ;table[1] retlw 2 ;table[2] only works if PCLATH is set properly. In order to be sure the program works, set up PCLATH before going to the table: movlw high(table) ;Set up for table access movwf PCLATH movfw index call gettbl But there is an additional problem, if the table crosses a 256-byte boundary, then 2 different PCLATH values are needed to access it. I strongly recommend using "org" directives to place tables at known locations so this is certain not to happen. If the table is exactly 256 locations, the addwf PCL needs to be placed at xxFF so the add will never generate a carry (because the entire table is in the same 256 byte "page"). However since it is known that W will always be added to 0 then, a movwf PCL can be used with the same effect. If you insist on placing tables anywhere, you'll have to compute and load the proper PCLATH value at run-time. Here's an example of that: movlw high(table) ;First part of the table movwf PCLATH ;PCLATH there for now. movfw index ;The table index addlw low(table) ;Compute low 8 bits of table entry addr. skpnc ;Skip if did not cross 256 boundary incf PCLATH,f ;In the next 256 instr "page" call gettbl The 'gettbl' for this routine is: movwf PCL ;*Not* addwf since the low part of the table address has already been added to W (so carry can be checked to see if PCLATH needs to be incremented). This instruction can be anywhere (in the same 2K page as the table) and shared by several routines that lookup in different tables. Tables larger than 256 bytes can be handled rather simply. I recommend "org" the first entry in the table to xx00. Then use: movfw indexh ;High bits of table index addlw high(table) ;To the page of the table movwf PCLATH movfw indexl call gettbl ;Call a movwf PCL If the first entry in the (large) table is not at xx00, then you have to add indexl to low(table) and carry to the add indexh to high table. This will take a 3 more instructions. Again the table has to be entirely within a 2K page unless you have copies of the movwf PCL stored at the same place in every page or use a technique like: [setup indexh and indexl] movlw high(gettbll) ;Be sure call works (may not need) movwf PCLATH call gettbll ;Get table (starting at xx00) anywhere [result in W] gettbll movfw indexh addlw high(table) movwf PCLATH movfw indexl movwf PCL The reason for calling the change PCL instruction rather than just placing it inline should be obvious, since the table entries are return instructions a call must be used to put a valid return address on the stack. > >[2] On a reset, the PIC jumps to address 0 independent of PCLATH. > PCLATH. > >[3] On an interrupt, the PIC jumps to address 4 independent of PCLATH. On a part with more than 2K program space in use, you have to be wary of using goto or call instructions in an ISR since PCLATH may have been set to go to a different 2K page. Probably the fastest way around this is to place identical copies of parts of the ISR in the same place in all pages. Or save and restore PCLATH in RAM before any gotos in an ISR. If the ISR needs to use a table, and PCLATH is not constant throughout the entire program, then PCLATH will need to be saved of course before changing it in the ISR. > >[4] Upon executing the last word of any 256-byte page, the PIC will >increment > the high "byte" of the program counter, independent of PCLATH. > >[5] Any instruction that writes to address 2 (PCL) will cause the >value > written to be copied into bits 0-7 of the program counter and >PCLATH > to be copied into the high byte of the program counter. > >[6] The "call" and "goto" instructions will copy bits 0-10 of the >program > counter with the corresponding bits from the opcode, and will copy >the > remainder of the program counter from the appropriate bits of >PCLATH > (i.e. bit 11 will be taken from PCLATH.3 and bit 12 will be taken >from > PCLATH.4). Note that on parts with 2K or less of ROM the high >order > bits of the program counter (loaded from PCLATH) are irrelevant. > There is a very useful figure in the PIC data sheets "Loading of PC In Different Situations" that graphically summarizes these two points. In the PIC16F8X book it is Figure 4-8. On parts with less than 2K program space the PCLATH register is don't care for goto or call instructions, since these instructions can span the entire 2K range using the 10 address bits in the opcode. On parts with more than 2K, things get a lot more complicated. >Hopefully that will clarify things somewhat. I should warn you, >though: >the 17Cxx has very different rules for handling PCLATH, so code >migration >to that part is not so simple as it would seem. If you stick with >16Cxx, >though, you should do fine. And of course the 12-bit parts are also entirely different, having no PCLATH register at all. The only 12-bit parts of any consequence now are the 8-pin PIC12C50X. >