Wednesday, June 12, 2013

RPN Scientific Calculator: Software Update

Work continues on my calculator. Each byte of a BCD number now holds only one digit instead of two. This eliminates the functions that were needed to align and pad numbers when every byte held two numbers. It also makes calculations a little simpler and reduces the amount of space the functions take up in the Flash.

Another improvement is that arrays can be stored in the external SRAM and accessed similarly to how internal memory is accessed. To read or write anything to the external memory a function has to be used. That means a simple equation like this:
X+=Y*Z-Q;
would become this if those variables were stored externally:
 RAM_Write(X,RAM_Read(X)+(RAM_Read(Y)*RAM_Read(Z)-RAM_Read(Q));
This is difficult to decipher and inconvenient to use when writing code. My solution was to make a small program that preprocesses the source code and replaces any variables that should be stored externally with their corresponding read and write commands. These variables are marked off with #pragma directives. #pragma is a note to the compiler to do something special that may be specific to that compiler. If the compiler doesn't recognize the directive, it is ignored. Here is an example of what the preprocessor would do.

This is unprocessed code:
void puts(unsigned char *msg)
{
     #pragma MM_VAR msg
     for (int i=0;msg[i];i++) putchar(msg[i]);
}

int
main()
   
     #pragma MM_OFFSET 200
     #pragma MM_DECLARE

     unsigned char text1[30];
     unsigned char text2[30];
     #pragma MM_END

     text1[0]='A';
     text1[1]=0;
     puts(text1);
}
 This is preprocessed code:
void puts(unsigned char *msg)
{
     #pragma MM_VAR msg
     for (int i=0;
RAM_Read(msg+i);i++) putchar(RAM_Read(msg+i));

}

int
main()
   
     #pragma MM_OFFSET 200
     #pragma MM_DECLARE

     unsigned char *text1=(unsigned char*)200;
     unsigned char *text2=(unsigned char*)230;

     #pragma MM_END

     RAM_Write(text1+0,'A');
     RAM_Write(text1+1,0);

     puts(text1);

}
Arrays are converted to pointers that store offsets to the external memory. This way none of the function prototypes have to be changed. The code should work the same way whether the variables are stored in internal or external RAM. External memory can be turned off if needed to make tracking down bugs easier. One disadvantage to this is that function arguments must be either internally or externally stored but a function can't accept both types for one argument. For things that need both I will need a wrapper function. The preprocessor doesn't support all of the C standard since I only spent about a day on it. Adding support for all of the possible ways there are to access a variable would take a lot more work and I wouldn't be likely to use that functionality anyway. Things like variable names that span multiple lines or variable assignments inside of other statements aren't supported but I didn't end up using any of those features in my code.

The new code for adding and subtracting BCD numbers fits in about 2K, including LCD driving code. Using the preprocessor to store all the values externally adds another 500 bytes. The code for multiplication is also much smaller than the last version.