C-like array into Program memory in MPLAB?

Discussion in 'Programmer's Corner' started by korchoi, Jun 14, 2015.

  1. korchoi

    Thread Starter Member

    Jun 5, 2015
    Let us suppose that a PIC microcontroller (speciffically the 16f628a) will handle a large block of data. Each Byte of this block has been previously defined by another application.
    Since the PIC microcontroller is Harvard(Program and data are separate), any constant in program memory must be stored as instructions like load-immediate.
    The only way to store constants on the PIC is through data EEPROM and Instruction Memory. Unfortunately, The EEPROM will not fit the block.
    To make it worse, this specific part does not support self-programming.That means the program cannot modify or even directly read the value stored at the instruction memory.
    Is there any way to reduce this:

    #define block[1] something
    #define block[2] something else
    #define block[200] last byte

    load_immediate block[1] //loads first char of array block
    store area //stores on RAM address area
    load_immediate block[2] //loads secind char of array block
    store area+1 //stores into Area + 1
    ... //and so on...
    load_immediate block[200] //up to the last char.
    store area+199
    Into this:
    __define_array block //generates defines for the array block.
    allocate(block, area) //generates the instructions that allocate the entire array into ram starting from address area.

    Is there any macro like this in mplab? Or will i have the hand cramps to store an ascii table without having it vanish everytime i turn the microcontroller off?
    This non-accessible ROM thing is really snipping with my mind.
  2. JohnInTX


    Jun 26, 2012
    Use the 'dt' directive to put constants into ROM when you assemble the code. The tables will be blasted into ROM when you program the chip. Use 'de' to program the EEPROM with default data when programming the chip. These and other directives are described in MPASM help under 'Directives'

    For ROM tables in the midrange PICs, the only way to fetch them is to CALL them ('dt data' generates retlw data) and use the data returned in W. To index the table, you need those nausea-inducing things I referenced here.

    Code (Text):
    1.  ORG 100h
    2. TableW:
    3. bcf PCLATH,2 ; select ROM page 100h lookups by setting up PCLATH bits
    4. bcf PCLATH,1
    5. bsf PCLATH,0
    6. addwf PCL,F ; W is index into table (array of data)
    7. ; Store data as retlw 'one byte'
    8. dt "12345",0  ; stores ASCII 12345. a null terminated string
    9. dt 00h,32h, 0f0h ; stores 00h, 32h, F0h
    Call TableW with 0<= W < Size of table (check it!). Upon return, W will be loaded with the byte of data in the table. Bump the index for the next byte. Since you actually are CALLing the table data, you MUST ensure that W is valid (does not exceed the table size).

    This method is OK for small <256 byte tables ROM page 0. Bigger ones or those in other ROM pages need a different lookup scheme as described in the referenced post. Note that the 3 LSbits of PCLATH have to be set up to match the address of the ROM table - that's why its ORG'd at a specific place. A much more general way is to use a full 16 bit address as described in the earlier post.

    This is the ONLY way to get table data with a midrange PIC. Sorry.
    Last edited: Jun 14, 2015
  3. John P

    AAC Fanatic!

    Oct 14, 2008
    Not entirely true. I use the PIC16F690 a lot, and it has the ability to read from flash, though it can't write it. But a lot of midrange PICs can do that too. Note that if you do store data in flash, you can read 14 bits per operation. I won't do a search, but maybe there's a processor pin-compatible with the 628 that can read its own flash.

    That method of using RETLW to store data is unbelievably hideous. Since PCLATH is being reset, do you need to store the value it had when the call was made, and restore it afterward? I'm never sure how to treat PCLATH--fortunately I've always been able to avoid dealing with it.
  4. JohnInTX


    Jun 26, 2012
    You're right.. and I must be careful about using absolutes like ONLY way. Thanks, John_P. I guess I knew about the ability to read midrange ROM but never use it. I have macros to do the lookup via PCL that works on any baseline/midrange so I just stick with that. Plus it looks like you should disable interrupts when doing the required sequence to read flash? Don't like that.

    For your question on the 13 bit midrange, calls and gotos encode 11 bits (a 2K ROM bank) of the target address in the instruction itself and PCLATH bits 4,3 form the upper 2 bits of the total 8K addressable. In a one-bank part (2K of ROM) PCLATH bits 4,3 are just left at 00. In a multi bank part, those 2 upper bits have to be set up before a call/goto to form the complete address (and effectively select one of 4 possible 2K ROM banks). All calls/gotos will wind up in the bank selected by PCLATH. Note that even though the full 13 bit return address is pushed onto the stack and used to return to the caller in the original bank, PCLATH is NOT restored after the return - if you want to stay in the original bank you have to manually restore bits 4,3 after the code returns. If you don't, the next call or goto will zing off into the new bank. This 'feature' has its uses but is mostly a code-eating pain - it makes your call take up to 5 instructions. Arranging code to minimize crossing banks helps here and macros will save your life. MPASM provides 'pagesel routine' which generates code to set PCLATH to get to 'routine'. I don't particularly like it, preferring my own macros.

    Table lookups that modify PCL force a jump too BUT since those instructions do not encode any of the upper address bits, the entire upper address will be taken from PCLATH <4-0>, bits <2,0> are now in play. This effectively breaks up the ROM into four 2K banks <4,3> of eight 256byte pages each <2,0>. PCLATH 4,3 is set up when you arrive at the lookup (you have to call the lookup routine) but you DO have to ensure that bits 2-0 are set to match the 1 of 8 256 byte pages you are accessing. Also, since the result of ADDWF PCL,F does NOT propagate a carry into PCLATH, table lookups using it are a strictly page-oriented affair - tables that span 256 byte boundaries or are larger than a page require more work, hence my advice to calculate a 16 bit address from the get-go or in some cases with small tables, just locate them on a page boundary using ORG. Note that since calls/gotos do NOT use bits <2,0>, they can be ignored until its time to do a lookup. Finally, since lookups modify the PC, it is vital that the index (W in this case) be bounds-checked to avoid jumping into the weeds. With the flash-read, you get bad data with a bad index, with ADDWF, you lose control.

    Any time you have interrupts in a >2K parts, you must save, set, and restore PCLATH in the interrupt routine. Consider the case where you have set PCLATH to goto and execute code in ROM bank 01. You get an interrupt that takes you to 0004h in bank 0. Then, as the TS originally did at the interrupt vector, you goto the interrupt service routine in ROM bank 0. Well, no, you didn't. Since PCLATH is 01xxx, you just went back to bank 01. Kaboom! So yes, as part of the context save and restore, you have to manage PCLATH - save the current value, set it to find your service routines and restore it before retfie. I know, right?

    John_P, I know you probably knew most of this stuff but thought I'd expand on your question for the TS and hopefully fill in any blanks for you. Cheers!
  5. korchoi

    Thread Starter Member

    Jun 5, 2015
    wow, really interesting. This RETLW instruction caught my eyes when i looked at it in the datasheet, i had no idea it was a dodgy way to store data.
    Maybe they just were expecting that everyone would just use the small ammounts in the data eeprom.
    Right now i am working on a sinewave generator and a table with 40 points of the sine should be enough. Might as well use the data eeprom.
    For some reason, this load/store immediate thing feels like a half-assed method to me. Indirect load and store to ROM feels much more natural.
  6. takao21203

    AAC Fanatic!

    Apr 28, 2012
    First you constrain yourself to antiquated MCU then you complain yet RETLW is perfectly fine just a means how doing things.

    Ever heard of C language?

    Or a more decent + more recent PIC.

    I've done scrolling message with 16F57 (admittedly in C and using up all the FLASH and using external serial RAM).
    Sure it needs a large table with character bitmaps you wouldnt really want to do this in assembler.
  7. korchoi

    Thread Starter Member

    Jun 5, 2015
    I simply can't source better PIC's from here where i live.
    Plus, The simplicity of the architecture would nullify the pain of baremetal programming.C in such a limited chip would probably cause problems.
    In assembly i can just set up some offsets in certain RAM areas and
    But you are right. I need more memory. 5x256=1.5KB should do(i plan on using latin-extended ascii).Take character from string, fetch corresponding 5-byte stream from ROM to RAM framebuffer, repeat untill string fills the framebuffer area, then display;delete first char, then fetch next. It's all set in my head.
    I fear however, that doing this and pushing it to a tightly timed medium will be too much for the little thing to manage.
    Maybe that old Atmega 128 KB eeprom from a karaoke machine will come in handy for what i am about to do.That is, if the static from the supermarket bag has not already killed it...
    I just need some glue logic to access this 40-pin digital dinosaur. Embedded and general purpose don't marry without some good old pressure.
    Now that will be theme for a completely different thread. I shall not derail.
    so skip the ramblings to here: i have the 16f877 borrowed from my school. i also have an old at49s parallel eeprom of 128KB.
    after some good old 74xx logic and port manipulation i can do this. I still don't have access to a C compiler, though(and i don't think i need it).
  8. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    Microchip has several C compilers freely available for download so you should be able to set up access to one.
  9. korchoi

    Thread Starter Member

    Jun 5, 2015
    c18 is old and only seems to be compatible with the 18F series
    mikroC is paid. I'm not pirating devkits to build cool stuff.
    XC8 simply doesn't install.
    I am on ubuntu 14.2, by the way. A lot seems to have changed this decade. They changed the GUI and stuff. Now the old things don't work anymore.
    I am a very specific case: my samsung laptop has a sneaky bios.
    I can't dual boot windows 8 and ubuntu, because everytime I boot W8, the BIOS deletes all other EFI entries. Windows in, linux gone.

    That's why i am not using a C compiler. I hope i have made my bizarre situation clear.
  10. GopherT

    AAC Fanatic!

    Nov 23, 2012
    I just left that same situation and I am so happy to be in the modern era. I don't know why I insisted that my 2003 era desktop on winXP was good enough. I hope you can leap a decade of technology soon.
  11. John P

    AAC Fanatic!

    Oct 14, 2008
    MikroC is free if you can live with a limited output file size.