For those interested here's follow up information to my Graphics LCD Library
. In my implementation I included the ability to save the entire display (1024 bytes) to flash program memory. Three C callable assembly routines were written. They can be downloaded from the links below: ReadPGM.asm WritePGM.asm ErasePGM.asm
In your C program you'll need to reserve some space in flash program memory. There are many options here. I decided to simply declare three global variables as follows:
#declare MemBuf 1024
rom unsigned char GLCD_Mem[MemBuf+64]; /* Save area in flash program memory */
ram unsigned char Flash; /* Write input buffer */
rom unsigned char *p_mem; /* Adjusted pointer to GLCD_Mem */
The linker will reserve 1024+64 byte in flash program memory at some address. The write/erase functions in the processor (PIC18F4550) require this to be aligned on a 64 byte boundary. Adding in an extra 64 bytes guarantees you'll find a 64 byte boundary near the base address and 1024 bytes there after.
You'll need to initialize the pointer "p_mem" to the correct byte at the boundary. The C pointer arithmetic to handle this is below:
unsigned int mem_64;
mem_64 = (unsigned int)GLCD_Mem; /* Init p_mem to 64 byte boundary */
mem_64 = (mem_64/64 + (mem_64%64 == 0 ? 0 : 1))*64 - mem_64;
p_mem = &GLCD_Mem[mem_64];
p_mem can thus be passed to the erase/write functions and used directly. I standardized on 64 byte blocks to make things easy. In reality the write can work with 32 bytes. The read routine works on single bytes. A working buffer of 64 bytes is required and that needs to be placed in ram memory. From here the short writes can be done to the 32 registers followed by a long write of all 32 registers. Doing this twice writes 64 bytes which is automatically handled in the assembly routine.
You can write one 64 byte ram buffer as follows:
Adding 64 bytes to p_mem points to the next block as follows:
unsigned int i;
i = 0;
load the flash buffer here
i += 64;
} do while Flash has data
Reading is a simple matter of calling ReadPGM with an address.
where: 0 >= i <= 1023
byte = ReadPGM(p_mem+i);
Section 6, pg 81 of DS39632E covers how the chip needs to be programmed. It also provides example code which serves the basis for what's presented here. I found a mistake in it with the TBLWR instruction, however. It took me several hours to find this.
Chapter 3, pg 39 of DS51288J provides useful information about C calling conventions, runtime memory model and the stack. Worth a read if you are planning on mixed languages. One point to keep in mind is using the "AUTO" directive on C function arguments. This forces the compiler to push function parameters on the stack. Static allocation will cause global symbols to be generated which won't get resolved at link time in the assembly functions shown. Another detail I spent hours on trying to make sense of.
That's how I did it. My routines work for the small memory model where addresses are 16 bits. You'll need to change anything that deals with pointers to make this work for a large memory model. Lots of ways to get this job done so use what works best in your application. Hope this helps someone else out there.