Hola gente, aca dejo algunos consejos de que hacer cuando nos quedamos cortos con la RAM. Los consejos van orientados a micros de 8 bits de FreeScale, pero basicamente se pueden aplicar a otros micros.
Zero Page y resto de la memoriaEn los micros HC(S)08 la memoria RAM arranca desde la posición 0x80 hasta la posición final que depende de cada familia, los 128 bytes más bajos ( 0x80 - 0xFF ) se denominan ZERO PAGE y en esta zona el micro puede acceder en forma mas eficiente que al resto de la memoria, por esta razon el compilador separa el acceso a la misma.
Si arrancamos un projecto normal con el compilador todas las alocaciones de variables globales son colocadas a partir de la direccion 0x100 y de esta manera tendremos 128 bytes que nunca serán usados. Si queremos acceder a estas posiciones podemos usar estas dos técnicas, existen más pero no me parecen prolijas.
Mediante pragmas
#pragma DATA_SEG _DATA_ZEROPAGE
int Index1;
#pragma DATA_SEG DEFAULT
int Index2;
En este caso Index1 se alocara entre las direcciones 0x80 y 0xFF e Index2 entre las direcciones 0x100 y el final. Lo bueno de este método es que el compilador al saber a que región pertenece cada variable, aprovecha los modos de direccionamiento mas eficientes para cada caso, la contra que tiene este método es que el código queda menos limpio al colocar estos pragmas.
Modificando el archivo de configuración del linkerEn este caso tocamos el archivo de configuración del linker para nuestro proyecto ( extension PRM ) de la sgte manera:
NAMES END
SEGMENTS
RAM = READ_WRITE 0x0080 TO 0x086F;
ROM = READ_ONLY 0x1860 TO 0xFFAF;
ROM1 = READ_ONLY 0x0870 TO 0x17FF;
ROM2 = READ_ONLY 0xFFC0 TO 0xFFCB;
END
PLACEMENT
DEFAULT_RAM INTO RAM;
_PRESTART, /* startup code */
STARTUP, /* startup data structures */
ROM_VAR, /* constant variables */
STRINGS, /* string literals */
VIRTUAL_TABLE_SEGMENT, /* C++ virtual table segment */
DEFAULT_ROM,
COPY /* copy down information: how to initialize variables */
INTO ROM; /* ,ROM1,ROM2: To use "ROM1,ROM2" as well, pass the option -OnB=b to the compiler */
END
Al realizar esto el linker empieza alocar desde la dirección 0x80, los pro de este método es que no tengo que estar ensuciando el código con pragmas, la contra ahora es que el compilador no va usar instrucciones mas eficientes al acceder a las variables alocadas entre 0x80 y 0xFF.
Uso de unionesLas uniones son similares a las estructuras, pero el tamaño final de la misma es igual al máximo elemento de los componentes que la componen, si se quiere saber mas sobre uniones se debe consultar bibliografía.
typedef union
{
byte TCPTxBuffer[256];
byte ModemAnswer[128];
dword RetriesCnt;
}tOverlayBuffer;
En este caso el tamaño de la union tOverlayBuffer sera de 256 bytes porque TCPTxBuffer mide eso y es el elemento mas grande, de esta manera estoy colocando 3 variables en un mismo bloque de memoria de 256 bytes, sino usara la union deberia destinar 256+128+4 = 388 bytes ! con el ahorro de 132 bytes. Pero no todo lo que reluce en la vida es oro, la cuestión es que como ahora las variables estan compartidas el uso de las mismas
no puede ser simultaneo. Para el correcto uso de esta técnica, uno deberá analizar el arbol de ejecución de nuestras funciones y determinar que variables son accedidas/modificadas en diferentes ramas y asi colocarlas dentro de la union.
Uso de máscaras en vez de flags booleanosbool IsButton1Pressed;
bool IsButton2Pressed;
bool GPSInited;
En este caso tenemos tres flags que indican un estado ( on-off ) y como el bool es C es normalmente un typdef de un byte para los tres flags ocupamos 3 bytes, podemos reemplazar estos 3 bytes por un solo byte utilizando estas macros:
#define IS_BUTTON1_PRESSED 0x01
#define IS_BUTTON2_PRESSED 0x02
#define GPS_INITED 0x04
#define CLEARFLAG( Flag, FlagMask ) \
(( Flag ) &= ( ~FlagMask ))
#define SETFLAG( Flag, FlagMask ) \
(( Flag ) |= ( FlagMask ))
#define ISFLAGSET( Flag, FlagMask ) \
((( Flag ) & ( FlagMask )) != 0 )
/* Aloco mascara global */
byte GlobalMask;
...
/*Seteo Flasg */
SETFLAG( GlobalMask, IS_BUTTON1_PRESSED );
/* Borro Flag */
CLEARFLAG( GlobalMask, IS_BUTTON2_PRESSED );
/* Pregunto */
if ( ISFLAG( GlobalMask, GPS_INITED ))
...
else
...
Las máscaras deben ser definidas como constantes con un solo bit prendido, por eso en un byte podemos solamente usar 8 máscaras ( 0x01-0x02-0x04-0x10-0x20-0x40-0x80) de esta manera podemos ahorrarmos 7 bytes por cada 8 flags globales. Tambien podemos apilar mascaras tanto para el Set, Clear o verificacion usando el operador (|) or .
Existen otras técnicas, como por ejemplo el uso de tipos bit, pero no son estandares del ANSI C, asi que no las uso y no todos los compiladores la soportan.
Espero les sirva.
Saludos !