The memory space of the 8051/8052 microcontroller is divided into code space, internal ram, external ram and the special function registers (sfr's).
The possible addresses of these different memory spaces is presented in the following table:
|internal ram||0||FF (7F for 8051)|
All of internal ram is indirectly addressable but only locations 0 to 7F are directly addressable. Directly accessing an address in the range 80 to FF accesses the sfr space. Parts of internal ram and parts of the sfr space are also bit addressable.
The Crossware 8051 ANSI C compiler allows access, directly from C, to all of these memory areas. The method of declaring a C variable so that it is located in or points to specific memory space is described elsewhere.
Two principle memory models are provided so that the user can program in C with minimum regard to the intricacies of the 8051 memory architecture. These are termed the small and large memory models. In addition, a tiny memory model is provided specifically to support 8051 variants that have a maximum of 2k bytes of code space and no external ram such as the Philips 80C750.
The small memory model locates all C variables by default in either internal ram or, if they are declared as const, in code space. Function parameters and local variables are located in internal ram.
The large memory model locates all C variables by default in either external ram or, if they are declared as const, in code space. Function parameters and local variables are located in external ram.
With both the small and the large memory models, the programmer can locate variables in any memory space by declaring them using a specific memory space qualifier.
As the table above indicates, the different memory spaces share address ranges. Therefore a pointer that points to, for instance, address 2F(hex) could be pointing to code space, internal ram or external ram. To overcome this ambiguity, the Crossware 8051 ANSI C compiler provides an enhanced pointer architecture. A memory specific pointer can point to only one particular memory space - code, internal ram or external ram. A pointer to internal ram consists of only one byte, whereas pointers to code space or external ram consist of two bytes. A generic pointer can point to any of these three memory areas. A generic pointer consists of three bytes. Two bytes hold the actual address which the generic pointer points to and the third byte holds a value which defines the memory space that it points to.
Generic pointers are essential for calls to library functions such as strcpy() since a pointer argument may point to code space, internal ram or external ram. However generic pointers are also relatively inefficient. The compiler has to generate code which will first interpret the third byte of the generic pointer and, depending on its value, jump to an instruction sequence that will access the appropriate memory space. The Crossware 8051 ANSI C compiler therefore goes to great lengths to avoid the use of generic pointers wherever possible. It does this as far as possible without the programmer needing to be aware of the different pointer types. The programmer writes ANSI C without giving any particular attention to declaration of pointers and the compiler analyses the code and makes an intelligent decision on the type each pointer should be. The pointers are smart pointers. Even a pointer to a pointer to a pointer to an int can configure itself to the appropriate non-generic pointer depending upon the way that it is used.
There is always a danger of course that if you define and use a pointer in one file and declare it as extern and use it in another file, the pointer will pick up different clues and configure itself as a different type of pointer in each file. No problem! The comprehensive type checking that the Crossware linker carries out across all of the modules of a program ensures that such an occurence will be brought to the attention of the programmer.
When generic pointers are essential, such as with the strcpy() library function, the programmer can still write ANSI compatible code. The compiler is smart enough to know which arguments to a library function call should be converted to generic pointers and which can remain as memory specific pointers.
Smart pointers allow the programmer, under the majority of circumstances, to ignore the details of the different pointer types yet still achieve the efficiency of memory specific pointers. The programmer can switch between small and large memory models without making any source code changes, and, unless 8051 specific features such as sfr accesses are being used, can also switch between 8051, 680X0 and other microprocessor architectures.