Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

(ARM).Using the ARM assembler

.pdf
Скачиваний:
40
Добавлен:
23.08.2013
Размер:
86.57 Кб
Скачать

Describing Data Structures Using ^ and # Directives

An array is also included within this structure to illustrate the use of ADR on a registerbased symbol. If you want to load misc.data[misc.i] into R1, for instance, use the following code:

ADR

MiscBase,misc

 

LDR

r0,Misc_I

;Get misc.i into r0

ADR

r1,Misc_data

;Address misc.data

LDR

r1,[r1,r0,LSL #2]

;Load array element

At this point, it is worth mentioning the other main use of :INDEX: outside macros. Suppose that you want to operate on two structures of the same type at the same time¾for example, you want the equivalent of the pseudo-C code:

newloc.x = oldloc.x + (value in R0); newloc.y = oldloc.y + (value in R1); newloc.z = oldloc.z + (value in R2);

You alternately want PointBase to point to the oldloc structure and the newloc one. Continually changing PointBase in this way is inefficient: you just want to set up the two pointers in two different registers. This can be done using code similar to:

PointBase2 RN R9

ADR

PointBase,oldloc

ADR

PointBase2,newloc

LDR

r3,Point_x

ADD

r3,r3,r0

STR

r3,[PointBase2,#:INDEX:Point_x]

LDR

r3,Point_y

ADD

r3,r3,r1

STR

r3,[PointBase2,#:INDEX:Point_y]

LDR

r3,Point_z

ADD

r3,r3,r1

STR

r3,[PointBase2,#:INDEX:Point_z]

Note Smaller and faster code exists for this example, for example using LDM, STM and a couple more registers can reduce its length by four instructions. However, this does not work if, for example, you are trying to access non-contiguous fields in the structure, or there are not enough registers available.

Application Note 50

ARM DAI 0050A

9

Open Access

Describing Data Structures Using ^ and # Directives

Making faster access possible

Here, you describe your memory area as a structure, then use a register to address that structure while you are processing it.

For example, consider the definitions from above:

StartOfMyData

EQU

0x1000

EndOfMyData

EQU

0x2000

 

^

StartOfMyData

Integer

#

4

Integer2

#

4

String

#

MaxStrLen

Array

#

ArrayLen*8

BitMask

#

4

EndOfUsedData

#

0

ASSERT EndOfUsedData <= EndOfMyData

Suppose that you want the equivalent of the C code:

Integer = 0;

Integer2 = 1;

String = "";

BitMask = 0xA000000A;

With the definitions as above, the assembler code is similar to:

MOV

r0,#0

LDR

r1,=Integer

STR

r0,[r1]

MOV

r0,#1

LDR

r1,=Integer2

STR

r0,[r1]

MOV

r0,#0

LDR

r1,=String

STRB

r0,[r1]

MOV

r0,#0xA000000A

LDR

r1,=BitMask

STRB

r0,[r1]

This contains separate LDRs of literals to address each of the data items; this is unnecessary, since you only need to load a single address to be able to access the entire area.

 

Application Note 50

10

ARM DAI 0050A

Open Access

Describing Data Structures Using ^ and # Directives

The way to do this is effectively to create a my data area layout structure, with definitions as follows:

StartOfMyData

EQU

0x1000

EndOfMyData

EQU

0x2000

DataAreaBase

RN

r11

 

^

0,DataAreaBase

StartOfUsedData

#

0

Integer

#

4

Integer2

#

4

String

#

MaxStrLen

Array

#

ArrayLen*8

BitMask

#

4

EndOfUsedData

#

0

UsedDataLen

EQU

EndOfUsedData - StartOfUsedData

ASSERT UsedDataLen <= (EndOfMyData - StartOfMyData)

The assembler code for the above is then:

LDR DataAreaBase,=StartOfMyData

MOV r0,#0

STR r0,Integer

MOV r0,#1

STR r0,Integer2

MOV r0,#0

STRB r0,String

MOV r0,#0xA000000A

STRB r0,BitMask

This is significantly shorter and faster than the original.

Note Note that the ^ directive is ^ 0,DataAreaBase, not ^ StartOfMyData,DataAreaBase. The reason for this is that you are using the ^ and # directives to describe the layout of the data within the data area, relative to the DataAreaBase register, and not its absolute position. The LDR DataAreaBase,=StartOfMyData statement then provides the absolute position of the entire data area.

Finally, if you want to use the same technique for an area 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. Unlike the corresponding situation for non- register-based ^ and # directives, you cannot easily revert to using EQU directives. One method is to add comments to the code warning maintainers to take care when modifying the definitions; a better one is to use definitions of the absolute addresses to control the register-based definitions. This is rather tricky and is one of the rare uses of a registerbased ^ directive with a non-zero expression: the basic idea is that using ^ offset,reg followed by label # 0 makes label into a register-based symbol with register part reg and numeric part offset.

Application Note 50

ARM DAI 0050A

11

Open Access

Describing Data Structures Using ^ and # Directives

For example, the way to do this with the I/O location example above is to write:

StartOfIOArea EQU 0x1000000

SendFlag_Abs EQU 0x1000000

SendData_Abs EQU 0x1000004

RcvFlag_Abs EQU 0x1000008

RcvData_Abs EQU 0x100000C

IOAreaBase

RN

r11

 

^

(SendFlag_Abs-StartOfIOArea), IOAreaBase

SendFlag

#

0

 

^

(SendData_Abs-StartOfIOArea),IOAreaBase

SendData

#

0

 

^

(RcvFlag_Abs-StartOfIOArea),IOAreaBase

RcvFlag

#

0

 

^

(RcvData_Abs-StartOfIOArea),IOAreaBase

RcvData

#

0

After loading up 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.

2.4 Warnings in the use of ^ and # directives

The use of ^ and # directives is encouraged as they allow the use of more maintainable data structures. This assumes that the order in which the elements are physically placed in memory is not important to the programmer or program. Unfortunately this can cause problems if multiple elements in a structure are loaded or stored in one instruction. This can occur when several single byte elements are loaded into one register, or when a Store or Load multiple registers instruction (STM and LDM) is used to store or load multiple words from or to multiple registers. The problem arises because both these operations require the different data elements to be contiguous in memory, and also to be in a specific order. These operations will work to start with, but if in future maintenance the order of these elements is changed (or a new element is added), this breaks the program in such a way that it will not be picked up at assembly time. This would therefore counteract the maintainability of these data structures. There are various methods of avoiding these problems.

A sample structure follows:

MiscBase

RN

r10

 

^

0,MiscBase

MiscStart

#

0

Misc_a

#

1

Misc_b

#

1

Misc_c

#

1

Misc_d

#

1

MiscEndOfChars

#

0

MiscPadding

#

(-:INDEX:MiscEndOfChars) :AND: 3

Misc_I

#

4

Misc_J

#

4

Misc_K

#

4

Misc_data

#

4*20

MiscEnd

#

0

MiscLen

EQU

MiscEnd-MiscStart

 

 

 

 

Application Note 50

12

ARM DAI 0050A

Open Access

Describing Data Structures Using ^ and # Directives

There is, of course, no problem in using LDM/STM instructions for accessing single data elements which are larger than a word (that is, arrays). An example of this is the 20-word element Misc_data in the example below, which could be accessed as follows:

ArrayBase

RN

R9

ADR

ArrayBase, MiscBase

LDMIA

ArrayBase, {R0-R5}

The above example loads the first six elements in the array Misc_data. Since the array is a single element, it covers contiguous memory locations. It is unlikely that in the future anyone will split it into two separate arrays.

However, for the case of loading Misc_I, Misc_J, and Misc_K into registers R0, R1, and R2 the following would work, but could cause problems in the future:

ArrayBase

RN

R9

ADR

ArrayBase, Misc_I

LDMIA

ArrayBase, {R0-R2}

The problem lies in the fact that in future, maintenance of the code the order of Misc_I, Misc_J, and Misc_K could be changed, or a new element Misc_New could be added in the middle. Either of these small changes would break the code. If these elements need to be accessed separately elsewhere, so you do not want to amalgamate them into a single array element, you must amend the code. The first remedy is to comment the structure to prevent changes affecting this area:

Misc_I

#

4

;

==}

Do not split/reorder

Misc_J

#

4

;

}

these 3

elements, STM

Misc_K

#

4

;

==}

and LDM

instructions used.

If the code is strongly commented, no deliberate changes should be made which would affect the workings of the program. Unfortunately, mistakes can still occur. A second method of catching these problems would be to add ASSERT directives just before the STM/LDM instructions to check that the labels are in the correct order and are consecutive:

ArrayBase RN

R9

; Check that the structure elements are correctly ordered for LDM

ASSERT

(((Misc_J-Misc_I) = 4) :LAND: ((Misc_K-Misc_J) = 4))

ADR

ArrayBase, Misc_I

LDMIA

ArrayBase, {R0-R2}

This ASSERT directive stops assembly at this point if the structure is not in the correct order to be loaded with an LDM. Remember that the element with the lowest address always is loaded or stored to or from the lowest numbered register.

Application Note 50

ARM DAI 0050A

13

Open Access

Using Macros

3 Using Macros

Using macros in your assembler source does not ease porting to another assembler for the ARM, but it does provide some improvement to code readability.

Macros give you a means of placing a single instruction in your source which is expanded at assembly time to several assembler instructions and directives, just as if you had written those instructions and directives within the source at that point.

As an example, consider a TestAndBranch instruction. This would normally take two ARM instructions. You must tell the ARM Assembler, by means of a macro definition, that whenever it meets the TestAndBranch instruction, it is to insert the code you have given it in the macro definition. This is a convenience; you could just as easily write the relevant instructions out each time, but instead you let the Assembler do it for you.

The Assembler determines the destination of the branch from a macro parameter. This is a piece of information specified each time the macro is invoked; the macro definition specifies how it is used. In the TestAndBranch example, you could also make the register to be tested a parameter, and even the condition to be tested for. Your macro definition might be:

 

MACRO

 

$label

TestAndBranch $dest, $reg, $cc

$label

CMP

$reg, #0

 

B$cc

$dest

 

MEND

 

The line after the MACRO directive is called the macro prototype statement. The lines containing the CMP and B instructions are the ones which will be substituted in the source. The macro ends when the Assembler encounters the MEND directive. The $label allows the line on which the macro is invoked to have a label which is substituted for each occurrence of $label in the macro.

This is an example of how this macro may be invoked:

test

TestAndBranch

NonZero, r0, NE

 

;

 

 

;

 

NonZero

 

 

After substitution this becomes:

test

CMP

r0, #0

 

BNE

NonZero

 

;

 

 

;

 

NonZero

 

 

 

Application Note 50

14

ARM DAI 0050A

Open Access

Using Macros

As a more complex example, the following macro performs an unsigned integer division. The macro expands into shift and subtract operations to calculate the quotient and remainder. The macro takes four parameters:

$Bot is the register which holds the divisor.

$Top is the register which holds the dividend. It also holds the remainder of the

division, that is, $Top = $Top MOD $Bot.

$Div is the register where the quotient of the division will be placed, that is, $Div = $Top DIV $Bot, if required.

$Temp is a temporary register used during the calculation.

The macro checks that there is no conflict of register names (that is, that no two parameters use the same register), and is optimized in the case where the quotient is not required as an output, by checking whether the $Div parameter is NULL.

Note that local labels are used to avoid multiply defined labels if DivMod is used more than once in the assembler source. The letter b is used when referencing the labels to indicate that the Assembler should search backwards for the label. Use f to search forwards.

 

 

MACRO

 

$Lab

DivMod $Div,$Top,$Bot,$Temp

 

ASSERT $Top <> $Bot

;Produce an error if the

 

ASSERT $Top <> $Temp

;registers supplied are

 

ASSERT $Bot <> $Temp

;not all different.

 

[

“$Div” <> “”

 

 

ASSERT $Div <> $Top

 

 

ASSERT $Div <> $Bot

 

 

ASSERT $Div <> $Temp

 

 

]

 

 

$Lab

 

 

 

MOV

$Temp, $Bot

;Put divisor in $Temp

 

CMP

$Temp, $Top, LSR #1

;double it until

90

MOVLS

$Temp, $Temp, LSL #1

;2 * $Temp > $Top.

 

CMP

$Temp, $Top, LSR #1

 

 

BLS

%b90

 

 

[

“$Div” <> “”

 

 

MOV

$Div, #0

;initialise quotient

 

]

 

 

91

CMP

$Top, $Temp

;can we subtract $Temp?

 

SUBCS

$Top, $Top,$Temp

;if we can, do so.

 

[

“$Div” <> “”

 

 

ADC

$Div, $Div, $Div

;Double $Div

 

]

 

 

 

MOV

$Temp, $Temp, LSR #1

;Halve $Temp,

 

CMP

$Temp, $Bot

;and loop until gone

 

BHS

%b91

;past original divisor

 

MEND

 

 

Application Note 50

ARM DAI 0050A

15

Open Access

Using Macros

If this macro were to be invoked as follows:

DivMod

r0, r5, r4, r2

the expanded assembler code would be:

ASSERT r5 <> r4

ASSERT r5 <> r2

ASSERT r4 <> r2

ASSERT r0 <> r5

ASSERT r0 <> r4

ASSERT r0 <> r2

 

MOV

r2, r4

 

CMP

r2, r5, LSR #1

90

MOVLS

r2, r2, LSL #1

 

CMP

r2, r5, LSR #1

 

BLS

%b90

 

MOV

r0, #0

91

CMP

r5, r2

 

SUBCS

r5, r5, r2

 

ADC

r0, r0, r0

 

MOV

r2, r2, LSR #1

 

CMP

r2, r4

 

BHS

%b91

;Produce an error if the ;registers supplied are ;not all different.

;Put divisor in $Temp ;double it until

;2 * r2 > r5.

;initialise quotient ;can we subtract r2? ;if we can, do so.

;Double r0 ;Halve r2,

;and loop until gone ;past original divisor

 

Application Note 50

16

ARM DAI 0050A

Open Access

Using the Barrel Shifter

4 Using the Barrel Shifter

The ARM’s arithmetic logic unit has a 32-bit barrel shifter capable of various shift and rotate operations. Data involved in the data processing group of instructions may pass through the barrel shifter, either as the result of an explicit instruction from the programmer, or as a result of the Assembler’s internal computations.

The barrel shifter has a carry in, which takes its input from the C flag of the CPSR, and a carry out, which may be latched back into the C bit of the CPSR for logical data operations.

4.1 Assembler mnemonics

There are five assembler mnemonics for shift types:

LSR Logical Shift Right

LSL Logical Shift Left

ASR Arithmetic Shift Right

ROR Rotate Right

RRX Rotate Right with Extend.

Logical shift right

A logical shift right (LSR) takes the contents of the register operand and moves each bit by the specified amount to a less significant position, filling the most significant bits with zeros. The least significant bits of the register operand which do not map into the result are discarded¾except that the most significant discarded bit becomes the barrel shifter’s carry out.

For example, if r0 contains:

11110000111100001111000011110000

r0 LSR #5 gives:

00000111100001111000011110000111

Logical shift left

A logical shift left (LSL) takes the contents of the register operand and moves each bit by the specified amount to a more significant position, filling the least significant bits with zeros. The most significant bits of the register operand which do not map into the result are discarded¾except that the least significant discarded bit becomes the barrel shifter’s carry out.

For example, if r0 contains:

11110000111100001111000011110000

r0 LSL #5 gives:

00011110000111100001111000000000

Application Note 50

ARM DAI 0050A

17

Open Access

Using the Barrel Shifter

Arithmetic shift right

An arithmetic shift right (ASR) is similar to a logical shift right, except that the most significant bits are filled with bit 31 of the register operand instead of zeros. This preserves the sign in 2’s complement notation.

For example, if r0 contains:

11110000111100001111000011110000

r0 ASR #5 gives:

11111111100001111000011110000111

Rotate Right

A rotate right (ROR) performs a logical shift right operation, but re-introduces at the most significant end of the register operand any bits which are discarded from the least significant end.

For example, if r0 contains:

11110000111100001111000011110000

then r0 ROR #5 will yield:

10000111100001111000011110000111

4.2 Register operands

Register operands can be of the following types:

Unshifted register

Syntax: register-name

Example: r0

Example use:

MOV r1, r0

; moves contents of r0 unshifted into r1

Register shifted by a constant amount

The register operand is shifted by a constant amount, in the range 031, 131 or 132 (depending on shift type).

Syntax: register-name, shift-type #amount

Example: r0, LSR #1

Example use:

MOV r1, r0, LSR #1 ; move contents of r0 divided by 2 into r1

 

Application Note 50

18

ARM DAI 0050A

Open Access