2.10.6. Using register-based MAP and FIELD directives

Register-based MAP and FIELD directives define register-based symbols. There are two main uses for register-based symbols:

Defining register-based symbols

Register-based symbols can be very useful, but you must be careful when using them. As a general rule, use them only in the following ways:

  • As the location for a load or store instruction to load from or store to. If Location is a register-based symbol based on the register Rb and with numeric offset, the assembler automatically translates, for example, LDR Rn,Location into LDR Rn,[Rb,#offset].

    In an ADR or ADRL instruction, ADR Rn,Location is converted by the assembler into ADD Rn,Rb,#offset .

  • Adding an ordinary numeric expression to a register-based symbol to get another register-based symbol.

  • Subtracting an ordinary numeric expression from a register-based symbol to get another register-based symbol.

  • Subtracting a register-based symbol from another register-based symbol to get an ordinary numeric expression. Do not do this unless the two register-based symbols are based on the same register. Otherwise, you have a combination of two registers and a numeric value. This results in an assembler error.

  • As the operand of a :BASE: or :INDEX: operator. These operators are mainly of use in macros.

Other uses usually result in assembler error messages. For example, if you write LDR Rn,=Location, where Location is register-based, you are asking the assembler to load Rn from a memory location that always has the current value of the register Rb plus offset in it. It cannot do this, because there is no such memory location.

Similarly, if you write ADD Rd,Rn,#expression, and expression is register-based, you are asking for a single ADD instruction that adds both the base register of the expression and its offset to Rn. Again, the assembler cannot do this. You must use two ADD instructions to perform these two additions.

Setting up a C-type structure

There are two stages to using structures in C:

  1. Declaring the fields that the structure contains.

  2. Generating the structure in memory and using it.

For example, the following typedef statement defines a point structure that contains three float fields named x, y and z, but it does not allocate any memory. The second statement allocates three structures of type Point in memory, named origin, oldloc, and newloc:

typedef struct Point
{
    float x,y,z;
} Point;
Point origin,oldloc,newloc;

The following assembly language code is equivalent to the typedef statement above:

PointBase   RN      r11
            MAP     0,PointBase
Point_x     FIELD   4
Point_y     FIELD   4
Point_z     FIELD   4

The following assembly language code allocates space in memory. This is equivalent to the last line of C code:

origin  SPACE   12
oldloc  SPACE   12
newloc  SPACE   12

You must load the base address of the data structure into the base register before you can use the labels defined in the map. For example:

        LDR     PointBase,=origin
        MOV     r0,#0
        STR     r0,Point_x
        MOV     r0,#2
        STR     r0,Point_y
        MOV     r0,#3
        STR     r0,Point_z

is equivalent to the C code:

origin.x = 0;
origin.y = 2;
origin.z = 3;

Making faster access possible

To gain faster access to a section of memory:

  1. Describe the memory section as a structure.

  2. Use a register to address the structure.

For example, consider the definitions in Example 2.22.

Example 2.22. 

StartOfData     EQU     0x1000
EndOfData       EQU     0x2000
                MAP     StartOfData
Integer         FIELD   4
String          FIELD   MaxStrLen
Array           FIELD   ArrayLen*8
BitMask         FIELD   4
EndOfUsedData   FIELD   0
                ASSERT  EndOfUsedData <= EndOfData

If you want the equivalent of the C code:

Integer = 1;
String = "";
BitMask = 0xA000000A;

With the definitions from Example 2.22, the assembly language code can be as shown in Example 2.23.

Example 2.23. 

        MOV     r0,#1
        LDR     r1,=Integer
        STR     r0,[r1]
        MOV     r0,#0
        LDR     r1,=String
        STRB    r0,[r1]
        MOV     r0,#0xA000000A
        LDR     r1,=BitMask
        STRB    r0,[r1]

Example 2.23 uses LDR pseudo-instructions. Refer to Loading with LDR Rd, =const for an explanation of these.

Example 2.23 contains separate LDR pseudo-instructions to load the address of each of the data items. Each LDR pseudo-instruction is converted to a separate instruction by the assembler. However, it is possible to access the entire data section with a single LDR pseudo-instruction. Example 2.24 shows how to do this. Both speed and code size are improved.

Example 2.24. 

                AREA    data, DATA
StartOfData     EQU     0x1000
EndOfData       EQU     0x2000
DataAreaBase    RN      r11
                MAP     0,DataAreaBase
StartOfUsedData FIELD   0
Integer         FIELD   4
String          FIELD   MaxStrLen
Array           FIELD   ArrayLen*8
BitMask         FIELD   4
EndOfUsedData   FIELD   0
UsedDataLen     EQU     EndOfUsedData - StartOfUsedData
                ASSERT  UsedDataLen <= (EndOfData - StartOfData)
                AREA    code, CODE
                LDR     DataAreaBase,=StartOfData
                MOV     r0,#1
                STR     r0,Integer
                MOV     r0,#0
                STRB    r0,String
                MOV     r0,#0xA000000A
                STRB    r0,BitMask

Note

In this example, the MAP directive is:

MAP 0, DataAreaBase

not:

MAP StartOfData,DataAreaBase

The MAP and FIELD directives give the position of the data relative to the DataAreaBase register, not the absolute position. The LDR DataAreaBase,=StartOfData statement provides the absolute position of the entire data section.

If you use the same technique for a section of memory containing memory-mapped I/O (or whose absolute addresses must not change for other reasons), you must take care to keep the code maintainable.

One method is to add comments to the code warning maintainers to take care when modifying the definitions. A better method is to use definitions of the absolute addresses to control the register-based definitions.

Using MAP offset,reg followed by label FIELD 0 makes label into a register-based symbol with register part reg and numeric part offset. Example 2.25 shows this.

Example 2.25. 

StartOfIOArea   EQU     0x1000000
SendFlag_Abs    EQU     0x1000000
SendData_Abs    EQU     0x1000004
RcvFlag_Abs     EQU     0x1000008
RcvData_Abs     EQU     0x100000C
IOAreaBase      RN      r11
                MAP     (SendFlag_Abs-StartOfIOArea),IOAreaBase
SendFlag        FIELD   0
                MAP     (SendData_Abs-StartOfIOArea),IOAreaBase
SendData        FIELD   0
                MAP     (RcvFlag_Abs-StartOfIOArea),IOAreaBase
RcvFlag         FIELD   0
                MAP     (RcvData_Abs-StartOfIOArea),IOAreaBase
RcvData         FIELD   0

Load the base address with LDR IOAreaBase,=StartOfIOArea. This allows the individual locations to be accessed with statements like LDR R0,RcvFlag and STR R4,SendData.

Copyright © 2000, 2001 ARM Limited. All rights reserved.ARM DUI 0068B
Non-Confidential