Updated January 2018
{qualifier} MAP<level> <varname>{,<type> {,<size>}} {,@<origin>}
{qualifier} MAP<level> <varname>, <type> ,<size>, <initialvalue>
The MAP statement defines variables in terms of size and data type as well as position in memory relative to other variables. The latter capability is particularly useful for defining compound structures such as file records so as to match precisely with a physical byte layout on an external medium. All variables are initialized to zero or null, but the second form of the MAP statement above allows you to specify a different initial value.
See MAP Statement Extensions for information on the qualifier option.
MAP1 (level 1, i.e. top-level) variables are positioned on even byte boundaries, subject to override by the ALIGN# PRAGMAs. Lower-level MAP statements (MAP2 thru MAP9) are aligned on byte boundaries (no inter-variable spacing), and may be addressed individually or as a group via the higher level variable(s).
See History, below, for a discussion on unfinalized MAP statements.
MAP1 CALORIES'CONSUMED,B,3
MAP1 CALORIES'BURNED,B,3
The above example defines two top-level 3-byte binary variables which are logically independent but in fact will be adjacent in memory (subject to alignment fill characters).
MAP1 TIME'REC
MAP2 ID,S,4
MAP2 WORKDAYS(7)
MAP3 DTX
MAP4 MON,B,1
MAP4 DAY,B,1
MAP4 YR,B,1
MAP3 INOUTS(2)
MAP4 IN,B,3
MAP4 OUT,B,3
MAP2 RATE,F,6
MAP1 TIME'REC$,S,115,@TIME'REC
The second example above illustrates a more complex MAP structure which includes multi-level arrays and an overlay. The actual layout in memory and the syntax for accessing the individual variables is as follows:
Memory Offsets |
Variable |
Aliases |
|
Memory Offsets |
Variable |
Aliases |
0 |
ID |
TIME'REC, TIME'REC$ |
|
12 |
|
|
1 |
|
|
|
13 |
|
|
2 |
|
|
|
14 |
IN(1,2) |
INOUTS(1,2) |
3 |
|
|
|
15 |
|
|
4 |
|
|
|
16 |
|
|
5 |
MON(1) |
WORKDAYS(1), DTX(1) |
|
17 |
OUT(1,2) |
|
6 |
DAY(1) |
|
|
18 |
|
|
7 |
YR(1) |
|
|
19 |
|
|
8 |
IN(1,1) |
INOUTS(1,1) |
|
20 |
MON(2) |
WORKDAYS(2), DTX(2) |
9 |
|
|
|
21..34 |
... |
|
10 |
|
|
|
35 |
MON(3) |
WORKDAYS(3), DTX(3) |
11 |
OUT(1,1) |
|
|
36..109 |
... |
|
Note the way the array addressing syntax percolates down through the levels. For example, the variable OUT appears in the map as a scalar, but it is a child of the array INOUTS(2), which is itself a child of the array WORKDAYS(7), which makes OUT an array with two subscripts, as if it were mapped as OUT(7,2), except that the individual elements, are not contiguous with each other. (Only the elements corresponding to the top level first subscript are contiguous.)
The example also illustrates overlaying an entire map structure, TIME'REC, with another variable, TIME'REC$, can be a different type and size. The variable TIME'REC, like all top-level compound variables, is itself an unformatted (data type X) variable whose length is determined by the sum of all its lower-level dependent variables (115 bytes in this case). So the entire structure can be accessed via the single unformatted variable TIME'REC (useful for manipulating the structure as a whole, such as performing record-level file operations). By means of the @ overlay mechanism, we can define additional variables that occupy the same space and thus provide alternate views (or aliases) of the same data. In this case, the variable TIME' REC$,S,115 is overlaid on top of TIME'REC. (TIME'REC and TIME'REC$ are different variables, although in this case they happen to occupy the same space.) Overlaying a string on an unformatted/structured variable can be useful for some advanced techniques. You might also use the same technique in cases where a file contains multiple record layouts, or where you want to manipulate the data at a higher or lower level. For example, we could also overlay TIME'REC with an array of 1 byte elements to simplify operating on it at the byte level.
Note that when overlaying, the variables do not have to be the same overall size. The next position in memory (i.e. the location for the next MAP1 variable) will be determined by the larger of the variables overlaid on each other.
See Also
2015 August, A-Shell 6.1.1416, compiler edit 748: MAP2+ statements are now flagged as illegal if not preceded by a finalized MAP1 statement. Unfortunately, A-BASIC does not make it clear when a MAP1 statement is finalized, other than by the next MAP1 statement. But it is never quite clear if there will be another MAP statement since they are technically legal anywhere in a program. In retrospect, the language could have used an "ENDMAP" statement, although you could effectively create your own just by defining a dummy MAP1 statement at the end of your last multi-level MAP structure.
The ambiguity didn't really have much significance in earlier versions of A-ShellBASIC, except perhaps in certain kinds of nested overlays, where one variable was overlaying another whose position was still in flux because it was part of a higher level array whose element members hadn't been finalized. But it became more of a problem with the introduction of the language features such as DEFSTRUCT, SIZEOF(VAR), .OFFSIZ(VAR), etc.
As of now, the rule is that any reference to a previous variable, except in a MAP statement initializer—and when the referenced variable has been finalized, causes the current MAP1 level to be finalized. If you attempt to follow that with a MAP2 or higher, it will be triggered as an illegal MAP level.
This may cause compile errors in programs that previously compiled, but it will prevent the kind of problem fixed in compiler edit 747 from escalating into a runtime problem. If you're not sure whether you've used the technique described, recompilation with this version or higher is highly recommended.