Making Effective Use of Memory
Understanding the memory map of the 68HC11 MCU based Handheld instrument controller.
The Mosaic Handheld instrument development platform contains plenty of memory for your C language application program. The standard Handheld provides 512K Flash for program storage and 128K of battery-backed RAM for variables and data. An enhanced memory option provides 1 MB Flash and 512K RAM. EEPROM is also available for long-term nonvolatile data storage. This page:
- Provides the memory map – that is, the addressable locations for each type of memory;
- Shows you how to address memory using the C programming language; and,
- Shows you how to efficiently use memory by organizing your variables into arrays and data structures.
The 68HC11 memory map
The Handheld's microcontroller, the Freescale 68HC11, is operated in a paged memory mode to greatly expand its addressable memory. In its standard configuration the Mosaic Handheld hosts 512K Flash and 128K RAM.
Expanded memory option
The total RAM on the standard configuration, or "128K", Handheld is actually 129K, comprising a 128K SRAM memory chip and 1K of onboard RAM on the 68HC11 processor. An expanded memory option is available that provides a 512K SRAM in place of the 128K SRAM. The default memory option’s 128K memory chip may be battery backed using a Li battery if the real time clock option is also chosen. If the expanded memory option is chosen, the memory can be battery backed by the NiMH battery pack.
Pages expand the processor’s addressable memory
The Handheld uses a paged memory system to expand the processor’s 64Kbyte address space to 2 Megabytes of addressable memory. The top half (32 Kbytes) of the address space (at addresses 0x8000 to 0xFFFF) addresses a common memory page that is always visible (i.e., accessible using standard 16-bit addresses) to any code running, no matter where it resides in the memory space. The bottom half (32 Kbytes) of the address space (at addresses 0x0000 to 0x7FFF) is duplicated many times and addressed through the processor’s 16-bit address bus augmented by a 6-bit page address. Together the address and page are held in a 32-bit data type, an xaddress.
A subroutine on any page can fetch or store to any address on the same page or in the common memory, or transfer control to another routine there. It "sees" a 64K address space comprising its own page at addresses from 0x0000 to 0x7FFF and the common memory at addresses 0x8000 to 0xFFFF. To address memory on another page, or to call a routine on another page, special memory access routines are used to change the page. The heap memory manager and array routines allow you to think of the paged memory as contiguous memory for data storage. The operating system automatically handles function calls and returns among the pages. There is very little speed penalty associated with changing pages.
Figure 1 below illustrates the memory map of the Handheld. Briefly, the upper 32K of the 68HC11's address space, the common memory, is always accessible without a page change. In the lower 32K of the processor’s address space, the operating system creates 64 pages of memory selected by an 6 bit on-chip port, with each page containing 32 Kbytes. The 32K of common memory at addresses 0x8000 to 0xFFFF (the upper half of the processor’s memory space) is always accessible without a page change. Up to 64 pages (32K per page) occupy the paged memory at addresses 0x0000 to 0x7FFF. The first 10 pages of the Handheld’s 20 pages of installed memory are shown in the figure. The expanded memory option provides a total of 48 pages of memory.
The following table illustrates the partitioning of the onboard memory between the operating system (Kernel) and your application functions. Most of the Flash memory is available as three blocks of contiguously addressable memory on pages 4-6, 10-17, and 20-2F. RAM for your application program is also available in three chunks, filling pages 1-3, 8-B, and 18-1F. There is also 20K available RAM on page 0F, and approximately 9K in the common memory. This 9K is particularly important because it is used to hold C variables and task space for each separate task your application program sets up.
Table 5-1 Partition of Flash and RAM among Kernel and Application Functions
Function | Size | Memory Page | Memory Address | Physical Location |
---|---|---|---|---|
Kernel Flash (64K) | ||||
Kernel | 32K | 00 | 0000 – 7FFF | Flash Socket 1 |
Kernel | 1K | 0E | Various | Flash Socket 1 |
Kernel | 19K | common | B400 – FFFF | Flash Socket 1 |
Kernel | 12K | 0F | 0000 – 2FFF | Flash Socket 1 |
GUI Toolkit | 2K | 7 | 0000 – 1F4A | Flash Socket 1 |
Kernel RAM (4K) | ||||
Kernel | 3.5K | common | 8000 – 8DFF | RAM |
Kernel | 0.5K | 0E | Various | RAM |
RTC | 48 bytes | B3D0 – B3FF | 68HC11 | |
GUI Toolkit | 75 bytes | 8E00 – 8E4B | RAM | |
GUI Toolkit Heap, Programmer specifies the RAM location for the GUI heap | variable | variable | variable | RAM |
Flash write-buffer, available for an application at run-time | 464 bytes | common | B200 – B3CF | 68HC11 |
Kernel EEPROM (192 bytes) | ||||
Kernel | 192 bytes | common | AE00 – AEBF | 68HC11 |
Application Flash (448K or 960K) | ||||
Application code | 96K | 04 – 06 (01– 03) | 0000 – 7FFF | Flash Socket 1 |
Kernel Extensions | 96K | 07,0C,0D | 0000 – 7FFF | Flash Socket 1 |
Application code and data | 256K | 10 – 17 (18 – 1F) | 0000 – 7FFF | Flash Socket 1 |
Application code and data available with the extended memory option | 512K | 20 – 2F | 0000 – 7FFF | Flash Socket 2 |
Application RAM (125K to 509K) | ||||
C Variables | 512 bytes | common | B000 – B1FF | 68HC11 |
C Variables, available at runtime but used during download as a Flash write buffer | 464 bytes | common | B200 – B3CF | 68HC11 |
C Variables and task area, some used by GUI Toolkit, optionally battery-backed | 8K | common | 8E00 – ADFF | RAM |
Arrays and heap memory, programmer specifies location of heap used by GUI Toolkit, optionally battery-backed | 20K | 0F | 3000 – 7FFF | RAM |
Arrays and heap memory, optionally battery-backed | 96K | 01 – 03 (04 – 06) | 0000 – 7FFF | RAM |
Arrays and heap memory, available with extended memory option | 128K | 08-0B | 0000 – 7FFF | RAM |
Arrays and heap memory, available with extended memory option | 256K | 18 – 1F (10 – 17) | 0000 – 7FFF | RAM |
Application EEPROM | ||||
EEPROM Variables | 320 bytes | Common | AEC0 – AFFF | 68HC11 |
Notes: | ||||
1. Pages not enclosed in parentheses indicate the standard, or run-time memory map; pages in parentheses indicate the addressing of the memory during program download, i.e., the download memory map. | ||||
2. Only the 128K RAM on the Handheld may be battery-backed. The Handheld that comes with the Starter Kit has 512K of non-battery backed RAM. | ||||
3. Application code is free to reside on pages 04-06 and 10-17. | ||||
4. Addresses from 8000 through FFFF comprise common memory that is visible to code on all pages. |
Table 5-1 above shows the distribution of Flash and RAM among kernel services and your application program, providing addresses and page numbers for each memory block. The Handheld’s onboard operating system uses some of the Flash, RAM and EEPROM, but most remains available for your application program.
The Flash, RAM and EEPROM are distributed among the common memory, which is limited to 32K, and the paged memory, which is limited only by available hardware. The following sections discuss the important addresses and pages in these two memory areas.
Kernel vs application memory space
Of the Handheld’s 512K of Flash memory, 448K is available for your application program and data storage. The remainder is used by the QED Forth Kernel for its multitasking operating system, debugger, interactive Forth compiler, assembler, and hundreds of pre-coded device driver functions. Of the 129K of RAM, 125K is available for application program use. In the "standard map", the pages are distributed among Kernel and Application Flash and RAM as: Kernel Flash (page 00 and parts of the common page, 0E and 0F); Kernel RAM (parts of the common page, 0E and 0F); Application Flash (pages 04 to 07, 0C to 0D, and 10 to 17); and Application RAM (pages 01 to 03 (and pages 08 to 0B and 18 to 1F with the expanded memory option).
Most of the Flash memory is available as two blocks of contiguously addressable memory on pages 04-06 and 10-17. RAM for your application program is also available in the paged memory in one contiguously addressable chunk, filling pages 01-03. There is also 20K available RAM on page 0F, and approximately 9K in the common memory. This 9K is particularly important because it is used to hold C variables and task space for each separate task your application program sets up.
Standard and the download memory maps
The Handheld has an alternate memory map called the "download map" that is used to facilitate the loading of code. To download code into the board the RAM and Flash addresses are swapped. Code is then downloaded into RAM that is addressed where Flash usually resides. Then the code is copied into the Flash, and the RAM and Flash addresses swapped back to the standard map.
In the download map, hex pages 4-7 and 10-17 are RAM, while hex pages 1-3 and 18-1F are Flash. In other words, the blocks of RAM at hex pages 1-3 and 18-1F in the standard map become Flash in the download map, and the blocks of Flash at hex pages 4-6 and 10-17 in the standard map become RAM in the download map.
The functions ALL.TO.FLASH and ENABLE.DOWNLOAD make it easy for Forth programmers to manage the download process for compiled applications up to 96 Kbytes long.
Common memory
The common memory is also partitioned between the operating system and application program. Table 5-2 below shows the common memory addresses used by the operating system, and in boldfaced type those addresses available to the application program.
Referring to Table 5-2 below, there are four unencumbered common memory areas your application can use:
- 0x8E00 – 0xADFF: These 8 Kbytes are available for your application. The Control-C compiler uses this area for static variables, arrays, task areas, etc.
- 0xAEC0 – 0xAFFF: The processor’s on-chip EEPROM (Electrically Erasable Programmable Read Only Memory) is located at 0xAE00 – 0xAFFF. Locations 0xAE00 – 0xAEBF are reserved by the operating system for use by the
SAVE
andRESTORE
utilities, and for interrupt vectors. EEPROM at 0xAEC0 – 0xAFFF is available to your programs. - 0xB000 – 0xB1FF: The 68HC11’s 1 Kbyte of on-chip RAM is located at 0xB000 – 0xB3FF.
- Locations 0xB000 – 0xB1FF are totally unencumbered and always available for your use (this area is named
ONCHIP_RAM
in the C linker command file; C programmers can locate data in this area using a#pragma
directive). - Locations 0xB200 – 0xB3CF are reserved for the flash programming routines.
- Locations 0xB3D0-0xB3DF are reserved for support of Forth interrupt service routines called from C-compiled programs; and,
- Locations 0xB3F0 – 0xB3FF are reserved for the real-time clock buffers.
- 0xB200 – 0xB3CF: These 464 bytes are also available for run-time variable storage, but are used by the kernel during download as a Flash buffer, or anytime that TO.FLASH is used, even at run time.
The remainder of the common memory area is used by either the processor or the kernel. The processor’s registers are located at 0x8000 – 0x805F, onboard hardware occupies addresses through 0x80FF, and the operating system reserves memory through location 0x8DFF for user areas, buffers, and stacks. For example, the default user area that runs the interactive Forth interpreter occupies 0x8400 – 0x84FF. Locations 0xB3D0-0xB3EF are reserved for support of Forth interrupt service routines called from C-compiled programs, and locations 0xB3F0 – 0xB3FF are reserved for the real-time clock buffers. Locations 0xB400 – 0xBFFF and 0xC100 – 0xFFFF contain kernel code. A notch at 0xC000 – 0xC0FF is not decoded by any onboard devices, but is used for addressing WildCards. The Handheld does not bring out the processor’s address/data bus for do-it-yourself memory mapped peripherals; instead, a direct interface to WildCard modules at addresses 0xC000-C0FF on pages 0-7 provides for versatile off-the-shelf I/O expansion.
Table 5-2 Partition of the Common Memory
Address | Size (bytes) | Type | Function |
---|---|---|---|
C100 – FFFF | 16128 | Flash | Kernel – code |
C000 – C0FF | 256 | I/O | Memory mapped I/O |
B400 – BFFF | 3072 | Flash | Kernel – code |
B3F0 – B3FF | 16 | RAM | Kernel – Real Time Clock Buffer |
B3E0 – B3EF | 16 | RAM | Kernel |
B3D0 – B3DF | 16 | RAM | Kernel – C/Forth ISR vectors |
B200 – B3CF | 464 | RAM | Application – C variables at runtime, Flash write buffer during program download |
B000 – B1FF | 512 | ONCHIP_RAM | Application – C variables |
AEC0 – AFFF | 320 | EEPROM | Application – nonvolatile storage |
AE00 – AEBF | 192 | EEPROM | Kernel |
8E00 – ADFF | 8192 | RAM | Application – C Variables and multitasking task areas, optionally battery-backed |
8500 – 8DFF | 2304 | RAM | Kernel – buffers and stacks |
8400 – 84FF | 256 | RAM | Kernel – Forth user area |
8060 – 83FF | 928 | RAM | Kernel – buffers and stacks |
8000 – 805F | 96 | RAM | Kernel – processor Control Registers |
Shaded and bold entries indicate memory available for application programs. |
Locations 0xB400 – 0xBFFF and 0xC100 – 0xFFFF contain kernel code. A notch at 0xC000 – 0xC0FF is not decoded by any onboard devices; this location is used to communicate with the Wildcards.
Paged memory
Occupying this memory space are 512K or 1MB Flash and 129K or 513K RAM. Of the Handheld’s maximum of 1M of Flash memory, all but 64K is available for your application program and data storage. The 64K of flash is used by the QED Forth Kernel for its multitasking operating system, debugger, interactive Forth compiler, assembler, and hundreds of pre-coded device driver functions.
Of the 513K of RAM, 509K is available for application program use. A smaller memory option of 128K RAM is available that allows the RAM to be battery backed.
Handheld memory map by page
It's sometimes nice while programming to have an overall view of the the QScreen's memory map organized page by page, as shown in the following table:
Page | RAM | Flash | Notes |
---|---|---|---|
Common | 11.5K | 19K | Kernel Flash and RAM, 8K of the RAM is available for your application |
00 | 32K | Kernel Flash | |
01-03 (04-06) | 96K | RAM and Flash available for your application | |
04-06 (01-03) | 96K | Ram and Flash page addresses are swapped in the download map (the top 6 bytes of page 04 are reserved for an autostart vector) | |
07 | 32K | A portion is used by the GUI Toolkit, the rest is available for KXs | |
08-0B | 128K | Additional RAM available with –MM option | |
0C | 32K | Flash available for KXs, write protectable by installing jumper J8, enabling "bullet-proof" kernel extensions (the top 6 bytes of page 0C are reserved for a boot vector) | |
0D | 32K | Flash available for KXs | |
0E | 0.5K | 1K | Kernel |
0F | 20K | 12K | Flash used by the kernel, but the RAM is available and particularly useful for a heap |
10-17 (18-1F) | 256K | Flash standard, swappable with RAM only available in –MM option | |
18-1F (10-17) | 256K | Available with –MM option, Page addresses are swapped with Flash in the download map | |
20-2F | 512K | Available with –MM option | |
Total: | 128 or 512K | 512 or 1024K | |
Notes: | Pages without parentheses are those of the standard map, and pages in parentheses are for the download map. | ||
The shaded, diagonally opposed boxes represent memory areas whose page addresses are swapped in the download map. | |||
Underlined memory is available with the –MM option. | |||
Beyond the RAM and Flash depicted here, there is an additional 1K of RAM and 512 bytes of EEPROM contained in the processor itself. |
It is possible to swap the page addresses of complimentary sections of RAM and Flash memory. For Forth programmers this makes code development particularly easy. You can develop and test your code by compiling directly into RAM using the download map, then copy the code to Flash and swap RAM and Flash page addresses by changing to the standard map. Your code is then in Flash at the same addresses at which it was compiled.
Addressing memory in C
Although 6-bits are sufficient to address the 64 possible pages, the page is padded out to a more standard 16-bit date type so that the full address, lower 16 bits plus 16-bit page, occupies 32 bits. We’ll refer to this full address as an xaddress (32-bit extended addresses). Three macros are available in the \MOSAIC\FABIUS\INCLUDE\MOSAIC\types.h
file to simplify the manipulation of xaddresses and their constituent 16-bit addresses and pages. These C macros are:
TO_XADDR XADDR_TO_ADDR XADDR_TO_PAGE
Multi-page C programs rely on a "page change" routine in the common kernel memory to call functions on other pages. Unlike the Forth compiler, the C compiler is not "page smart", and does not know at compile time whether a page change is needed. In fact, page changes are rarely needed, because most functions call other functions that are located on the same page or in common memory. Calls to functions on the same page or to common memory take only 11.5 or 13.75 microseconds, respectively, while function calls to other pages require just under 49 microseconds. Because page changes are rare, the average execution speed of multi-page C applications is not significantly impacted by the need for page changes.
Addressing Flash
Flash memory is nonvolatile, like PROM. Thus it retains its contents even when power is removed, and provides an excellent location for storing program code. Simple write cycles to the device do not modify the memory contents, so the program code is fairly safe even if the processor "gets lost". But flash memory is also re-programmable, and the flash programming functions are present right in the Handheld's onboard software library. These functions invoke a special memory access sequence to program the flash memory contents "on the fly". This allows you to modify your operating software (for example, to perform system upgrades). You can also store data in the flash device. You can program from 1 byte up to 65,535 bytes with a single function call using the pre-coded flash programming routine. Programming time is approximately 60 milliseconds per kilobyte.
Six special functions facilitate access to Flash memory. Their function names are:
DownloadMap() PageToFlash() PageToRam() StandardMap() ToFlash() WhichMap()
The FLASH programming functions use a buffer in the 68HC11's on-chip RAM starting at hex addresses B200-B3CF. The remaining on-chip RAM at B000 to B1FF is available to you. Also, because FLASH programming is generally not done at run-time, you can still use the Flash buffer for run-time variables.
Software development using Flash memory
Because code cannot be downloaded or compiled directly into flash memory, the flash memory map implements page swapping to provide a mechanism for getting the compiled code into the flash memory. There are two page-swap modes: one is called the Standard Map and the other is called the Download Map. As the names suggest, the Standard Map is used during run-time, and the Download Map is used during downloading C-compiler S-records from the PC to the Handheld. The two maps are very similar; the effect of changing from the Standard to the Download map is to swap the locations of pages between the Flash and the RAM.
Table 5-3 Addressing the Flash and RAM in Standard and Download Memory Maps
Flash Pages | RAM Pages | |||
---|---|---|---|---|
Standard Address Map | 04 – 06 | 10 – 17 | 01 – 03 | 18 – 1F |
Download Address Map | 01 – 03 | 18 – 1F | 04 – 06 | 10 – 17 |
In normal operation the Flash memory is addressed on pages 04-06 and 10-17, and the RAM is addressed on pages 01-03 and 18-1F. During download their addresses are swapped, so that the Flash is addressed at pages 01-03 and 18-1F and the RAM at pages 04-06 and 10-17.
To see how it works let’s consider a hypothetical download. Suppose you have compiled code intended to load into the Flash and run from it at addresses on page 4. Automated commands contained in the download file establish the download map, load the code into RAM, transfer the code to flash, and re-establish the standard map. In this case, the download file would:
- Swap the addresses of the RAM and Flash (by executing the command
DOWNLOAD.MAP
) so that the RAM is now addressed on page 04; - Download the code to its proper addresses on page 04;
- Copy the code (using the command
PAGE.TO.FLASH
) from page 04 into the Flash addressed on page 01; then, - Swap the RAM and Flash addresses back (by executing the command
STANDAD.MAP
) so that the Flash is now addressed on page 04, and the RAM on page 01 is available for run-time use by your program.
You can now execute your programs on page 04.
The Control-C download file does all this for you so you don’t need to worry about the details. But if you’re interested, just peruse the download file in the editor where you’ll see the commands it uses to manage memory during the download process.
You can now run the program by typing
MAIN↓
or any function name that was preceded with the _Q designator. (By the way, the _Q does not compromise performance in any way; it simply makes it possible for the PC-resident batch routines to send out the execution addresses of the designated functions to the Handheld to simplify debugging).
Locating nonvolatile data in EEPROM
The Handheld’s built-in EEPROM provides an ideal place to store calibration constants or other data that must be changed from time to time, but that must be retained even when power is removed. The EEPROM (Electrically Erasable Programmable Read-Only Memory) can be modified up to 10,000 times before it loses its ability to retain data. The ANALOGIO.C
file presents an example of how to locate a static "variable" in EEPROM.
You can read from EEPROM locations just as you would from any other memory location. But writing to them is done using special kernel functions. These functions take approximately 20 milliseconds per byte to reprogram the EEPROM cell.
The EEPROM variable should be declared as an un-initialized static variable; these are located by the linker in the "data" section, which normally points to system RAM where normal variables are stored. By following the syntax presented here, you can relocate the data section to point to EEPROM while defining the EEPROM variables, and then restore the data section to its standard RAM location. To define an EEPROM variable, use the following code:
#pragma option data=.eeprom // put the following variables in eeprom static uchar numsamples; static int nonvolatile_int; static float calibration_value; #pragma option data=.data // restore the data area to RAM
The #pragma
statements are pre-processor directives that are interpreted by the linker. In the code fragment above, we located three nonvolatile variables in EEPROM; note that we did NOT include initializers in the declaration statements. Initializers don’t make any sense for EEPROM variables, because special functions must be called to store values into EEPROM, so initialization can’t be accomplished by placing initialization data in the download file. These EEPROM variables must be initialized programmatically at run-time.
To store data into the EEPROM variables, use the following functions which are declared in the XMEM.H
file in the \FABIUS\INCLUDE\MOSAIC
directory:
void StoreEEChar(char value, char* addr) void StoreEEInt(int value, int* addr) void StoreEELong(long value, long* addr) void StoreEEFloat(float value, float* addr)
To learn how to interactively modify the contents of EEPROM variables, read the glossary entries for these functions in the Control-C Glossary.
These EEPROM storage functions are easy to use. For example, to store the value 123 into the character variable numsamples
, you would place the following statement in your program:
StoreEEChar( 123, &numsamples);
To avoid wearing out the EEPROM by executing unneeded write cycles, these functions check whether each EEPROM byte already holds its specified contents. If so, the write is not performed. Thus there is no penalty for redundant execution of commands that initialize particular locations in EEPROM.
While EEPROM variables must initialized programmatically at run-time the first time they are used, they don’t need to be re-initialized each time the processor starts up because the nonvolatile EEPROM retains the data. Even so, initializations can be performed every time the processor starts up, with no adverse effects on the life span of the EEPROM. For example, initialization code in an autostart routine could execute ATTACH functions to ensure that all needed interrupt vectors are properly initialized each time the processor restarts. If the EEPROM cells have been corrupted for some reason, the ATTACH command installs the correct contents, but if the specified interrupt vector information is already in the EEPROM, the memory cells are not needlessly rewritten.
All of the EEPROM storage routines globally disable interrupts while each EEPROM byte is being programmed, and it takes 20 milliseconds to program each byte. Thus you should avoid storing values in EEPROM while time-critical events are being serviced by interrupts.
For experts and the curious: Interrupts are disabled during stores to EEPROM because QED-Forth vectors all interrupts via the EEPROM, and the 68HC11 hardware does not allow any EEPROM cells to be read while a single EEPROM cell is being written to. Thus if an interrupt occurs while one of the EEPROM storage functions is writing to EEPROM, the interrupt will not be able to read the instruction code in the interrupt vector. Disabling interrupts prevents this error, but the interrupt service is delayed until the EEPROM write is finished.
Write-protecting EEPROM
It is possible to write-protect locations within the EEPROM to ensure the integrity of calibration constants or other vital information. This is done using the EEPROM block protect register named BPROT
(MC68HC11F1 Technical Data Manual mc68hc11f1.rev3.pdf, p.4-13. Four blocks of size 32, 64, 128, and 288 bytes may be individually protected by storing an appropriate configuration value to BPROT
. The contents of the BPROT
register may be changed using the C function InstallRegisterInits
; please consult its glossary entry for details.
To make a turnkeyed application maximally "bullet-proof" and fail-safe, consider using the BPROT
register to protect the first three blocks in the EEPROM totaling 224 bytes. This protects the onboard kernel’s configuration region (the first 32 bytes in EEPROM) plus the interrupt vectors (the next 160 bytes in EEPROM) plus an additional 32 bytes available the programmer. The remaining 288 bytes of EEPROM then remain available for modification by the application program.
Using C arrays and Forth (kernel) arrays
Storing data acquisition results in C arrays and Forth arrays
Programs written in Control-C use space in common memory to store variables. You may store simple variables or arrays of variables there using standard C syntax. However, common memory is a limited to approximately 9K. It can get used quickly in multitasking systems because each task requires a task area of about 1K. Consequently, the programmer may require access to additional RAM. Access is provided through the use of Kernel Arrays, also called Forth Arrays. Using Forth Arrays you may dynamically dimension arrays of virtually any size in the extended address space – and their memory allocation is automatically handled by the kernel’s heap memory manager.
The code presented in the sample program ANALOGIO.C
uses a C array and a FORTH_ARRAY
to store the results of multiple A/D conversions. This section uses that code as an example to discuss some interesting features of both C Arrays and FORTH_ARRAY
s.
Declaring a C array
The use of C arrays is discussed in detail in all standard C texts. In this program, the one-dimensional 16-element character array named results_8
is declared and allocated in RAM using the statement:
uchar results_8[DEFAULT_NUMSAMPLES];
where DEFAULT_NUMSAMPLES
is a constant equal to 16. The arrays are easy to use. For example, the following C statement assigns the last element in the array to a static variable named my_variable
:
my_variable = results_8[15];
To see another simple example that demonstrates how C arrays are accessed, look at the InitAnalog()
function in the ANALOGIO.C
file. The results_8
array is zeroed by executing the following statement:
for(i=0; i< DEFAULT_NUMSAMPLES; i++) results_8[i] = 0; // zero the array
Note that this array is dimensioned and allocated by the compiler and linker. In contrast, FORTH_ARRAYS
are dimensioned and allocated dynamically by the run-time program itself.
Converting a 16 bit address to a 32 Bit xaddress
The AD8ToCArray()
function that we just used provides an interesting example of type conversion. The definition of the function is:
_Q void AD8ToCArray( int channel) { EXTENDED_ADDR buffer; buffer.sixteen_bit.addr16 = results_8; buffer.sixteen_bit.page16 = 0; AD8Multiple(buffer.addr32,0,DEFAULT_NUMSAMPLES,channel); }
The purpose of AD8ToCArray()
is to properly call AD8Multiple()
which is defined in the ANALOG.H
file. AD8Multiple()
is optimized to use a FORTH_ARRAY
buffer, and so expects a 32 bit buffer xaddress instead of a simple 16 bit buffer address. To convert the simple 16 bit address returned by results_8
into a 32 bit extended address, we take advantage of the EXTENDED_ADDR
union defined in the TYPES.H
file in the \FABIUS\INCLUDE\MOSAIC
directory. The union is defined as:
typedef union{ xaddr addr32; struct{ uint page16; char* addr16; } sixteen_bit; } EXTENDED_ADDR;
To convert a 16 bit address into a 32 bit xaddress, we use the EXTENDED_ADDR
typedef to declare an instance of the union (named "buffer
" in this example), store the 16 bit address into the buffer.sixteen_bit.addr16
element, and store 0 (the default page) into the buffer.sixteen_bit.page16
element. Then we reference the corresponding 32 bit xaddress via the buffer.addr32
element of the union. While it is rare that you will have to convert from 16 bit to 32 bit address types, this example provides a template for how to do it.
A Review of Forth arrays
FORTH_ARRAY
s have two key advantages. First, they are allocated in paged memory, so they allow your program to access the large 1 Megabyte memory space of the Handheld. In contrast, C arrays must reside in the available common RAM which is limited to approximately 9 kilobytes on the Handheld. Second, they can be dynamically dimensioned, re-dimensioned and de-allocated (deleted) while your program is running; this boosts efficiency by maximizing the use of the available memory.
To define a new Forth Array, simply use the FORTH_ARRAY
typedef followed by a name of your choice. For example, in the ANALOGIO.C
file the following declaration appears:
FORTH_ARRAY results_12;
Before the FORTH_ARRAY
can be accessed at runtime, it must be dimensioned. This is typically accomplished by calling the DIM()
macro defined in the ARRAY.H
header file. For example, to dimension the results_12
array to have 10 rows and 1 column of integer data, we would execute:
DIM(int, 10, 1, results_12);
In the ANALOGIO.C
file, the pre-defined macro named DIM_AD12_BUFFER()
invokes the DIM()
routine for us (its definition is in the ANALOG.H
file in the \FABIUS\INCLUDE\MOSAIC
directory).
After the FORTH_ARRAY
is dimensioned, it can be accessed by a family of macros and functions that are defined in the ARRAY.H
header file and are described in the Control-C Glossary. These include functions that fetch from, store to, and calculate the address of individual elements, swap and copy entire arrays, fill an array with a specified character, and delete the array so that it no longer requires memory in the heap. The PrintForthArray()
and InitAnalog()
functions in ANALOGIO.C
provide examples of how to call a few of these functions.
Printing the contents of a Forth array
The PrintForthArray()
function presented in ANALOGIO.C
is a more general version of the PrintFPArray()
function in GETSTART.C
as discussed in an earlier chapter[pkc3]. The function is defined as follows:
_Q void PrintForthArray(int float_flag, FORTH_ARRAY* array_ptr) // works for FORTH_ARRAYS dimensioned using the standard DIM() macro. // float_flag is true if array holds float numbers, false otherwise. { int r, c; putchar(‘\n’); for (r = 0; r < NUMROWS(array_ptr); r++) // for each row { for (c = 0; c < NUMCOLUMNS(array_ptr); c++) // for each column if(float_flag) printf("%9.4g ",FARRAYFETCH(float,r,c,array_ptr)); else printf("%9ld ",ARRAYFETCH(long,r,c,array_ptr)); putchar(‘\n’); // newline after each row is printed PauseOnKey(); // implement xon/xoff output flow control } }
After calling putchar()
to output a newline character, we enter nested for()
statements that print the contents of each element. Because the compiler treats floating point numbers differently than numbers stored in other formats, we use FARRAYFETCH()
to access floating point arrays, and ARRAYFETCH()
to access char
, int
or long
arrays. The PauseOnKey()
function is called once per row to suspend the Handheld’s printed output if the terminal program has sent the XOFF
handshake character; the printout resumes when the terminal sends the XON
character. PauseOnKey()
also gives the user the ability to terminate the printout by typing a carriage return character from the terminal.
This function can be tailored to meet the detailed needs of your application. You can change the printf()
formatting, or insert extra carriage returns to confine the printout to one screen width.