下面是该除法宏被引用时的结果。
ASSERT r5 <> r4 ;如果寄存器全不相等,显示提示信息
ASSERT r5 <> r2
ASSERT r4 <> r2
ASSERT r0 <> r5 ;如果$Div不为空,产生三个提示信息
ASSERT r0 <> r4
ASSERT r0 <> r2
ratio
MOV r2, r4 ;将除数放在r2中
CMP r2, r5, LSR #1 ;r2乘以2,直到2*r2>r5
90 MOVLS r2, r2, LSL #1
CMP r2, r5, LSR #1
BLS %b90 ;b表示向后搜索
MOV r0, #0 ;初始化r0
91 CMP r5, r2 ;判断r5是否大于r2
SUBCS r5, r5, r2 ;如果大于,r5减去r2,结果放入r5
ADC r0, r0, r0 ;r0乘以2
MOV r2, r2, LSR #1 ;r2除以2
CMP r2, r4 ;循环,直到r2
BHS %b91
10.5.4 使用MAP和FIELD命令描述数据结构
可以使用MAP和FIELD伪操作来描述数据结构。这两个伪操作一般是一起使用的。
使用MAP和FIELD伪操作定义数据结构有以下好处。
· 容易维护。
· 可以方便地实现相同数据结构的重复定义。
· 可以更高效地存取数据。
MAP伪操作指定数据结构的基址。
FIELD伪操作指定一个数据项所需的存储器数量,并为该数据项指定一个标号。对结构中的每个数据项重复该命令。
注意
使用MAP和FIELD伪操作当定义一个数据结构时,不分配存储器空间。使用定义常数的命令(如 DCD)来分配存储器空间。
1.相对映射
MAP/FIELD伪操作和使用寄存器相对寻址的LOAD/STORE指令配合使用,访问事先定义好的结构体是MAP/FIELD伪操作的基本用法。
下面是一个使用寄存器相对寻址的LOAD指令访问结构体的例子。
MAP 0
consta FIELD 4 ;consta占用4字节,偏移量为0
constb FIELD 4 ;constb占用4字节,偏移量为4
x FIELD 8 ;x占用8字节,偏移量为8
y FIELD 8 ;y占用8字节,偏移量为16
string FIELD 256 ;string占用256,偏移量为24
上面定义的数据结构,可以使用下列指令来访问:
MOV r9,#4096
LDR r4,[r9,#constb]
标号是相对于数据结构的开始位置的。用于存放映射的起始地址的寄存器(此例中为 r9)称为基址寄存器。
数据结构的位置是由运行时装载到基址寄存器的值确定的。MAP/FIELD伪操作不实际分配内存地址。
同一映射可以用于描述数据结构的多个实例。它们可以位于存储器中的任何位置。
备注
r9是“ARM-Thumb 程序调用标准”中的静态基址寄存器 (***)。详细信息请参阅本书附录。
2.基于寄存器的映射
一般情况下,每次访问一个数据结构时可以使用相同的寄存器作为基址寄存器。可以在使用MAP定义映射的基址时指定该寄存器的名称。
下面的例子显示了一个基于寄存器的映射。
MAP 0,r9
consta FIELD 4 ;consta变量占用4个字节,地址偏移量为0(相对于r9中的地址)
constb FIELD 4 ;constb变量占用4个字节,地址偏移量为4
x FIELD 8 ;x占用8个字节,地址偏移量为8
y FIELD 8 ;y占用8个字节,地址偏移量为16
string FIELD 256 ;字符串占用256字节,起始地址偏移量为24
利用示例中的映射,可以访问数据结构(不管它在内存中什么位置):
ADR r9,datastart
LDR r4,constb ; => LDR r4,[r9,#4] ;该伪操作被编译为LDR r4,[r9,#4]
constb包含从数据结构开始位置算起的数据项的偏移量,也包含基址寄存器。在此例中,基址寄存器是在MAP命令中定义的r9。
3.相对于程序的映射
可以使用程序计数器 (r15) 作为一个映射的基址寄存器。当使用r15做为基址寄存器时,被使用LOAD/STORE指令寻址的数据结构偏移量不能超出4KB范围。这是因为使用PC寄存器为基址的LOAD/STORE指令,最大寻址能力为4KB。
下面的例子显示了使用r15作为基址寄存器的程序段。其中包含一个为数据结构分配存储器空间的SPACE伪操作。
datastruc SPACE 280 ;保留280个字节来定义数据结构
MAP datastruc
consta FIELD 4
constb FIELD 4
x FIELD 8
y FIELD 8
string FIELD 256
使用下面的指令读取constb域所包含的内容。
LDR r2,constb ; => LDR r2,[pc,offset] 该伪操作被编译为LDR r2,[pc,offset]
在此例中,不需要在装载数据之前装载基址寄存器,此时使用程序计数器PC作为默认寄存器。
注意
由于处理器里面的流水线,r15寄存器值和 LDR 指令的实际地址并不相同。但是,汇编器将修正此问题。
4.定义结构体的结束
可以使用带有0操作数的FIELD伪操作来标记结构内的一个位置。标记位置后位置计数器并未增加(即偏移量offset不增加)。
下面的例子使用事先定义好的常量MaxStrLen和ArrayLen定义了字符串和数组的大小。为了防止MaxStrLen和ArrayLen值过大,使结构体超出所能使用的内存范围,使用了“FIELD 0”伪操作来标识结构体的结束。
StartOfData EQU 0x1000
EndOfData EQU 0x2000
MAP StartOfData
Integer FIELD 4
Integer2 FIELD 4
String FIELD MaxStrLen
Array FIELD ArrayLen*8
BitMask FIELD 4
EndOfUsedData FIELD 0
ASSERT EndOfUsedData <= EndOfData
该例中使用了:
· 一个EQU命令定义可使用的最大内存空间;
· 一个带有0操作数的FIELD伪操作来标记数据结构的结束位置;
· 一个ASSERT伪操作确认数据结构的结束位置并未超出可用内存。
5.强制内存对齐
如果在定义数据结构时,使用了一些字符变量(或其他非4字节的变量),就可能造成内存访问的对齐问题。
下面例子中显示了一个数据结构的定义。该定义方式使整数访问不能保证在字边界对齐。
StartOfData EQU 0x1000
EndOfData EQU 0x2000
MAP StartOfData
Char FIELD 1
Char2 FIELD 1
Char3 FIELD 1
Integer FIELD 4 ; 非字边界对齐
Integer2 FIELD 4
String FIELD MaxStrLen
Array FIELD ArrayLen*8
BitMask FIELD 4
EndOfUsedData FIELD 0
ASSERT EndOfUsedData <= EndOfData
这里,不能使用ALIGN伪操作来修正该边界对齐问题,因为ALIGN伪操作只对齐存储器内的当前位置。而MAP和FIELD伪操作不为其定义的结构分配任何存储空间。
解决的方法是,在Char3 FIELD 1后插入一个虚拟的FIELD 1。但是,如果改变了字符变量的数目,就会造成维护困难。每次必须重新计算正确的填充数。
下面的例子说明了一个更好的调整填充的方法。该示例使用一个带有0操作数的FIELD伪操作来标记字符数据的结束位置。第二个FIELD命令根据标号的值插入正确的填充数。使用“:AND:”运算符来计算正确的值。
StartOfData EQU 0x1000
EndOfData EQU 0x2000
MAP StartOfData
Char FIELD 1
Char2 FIELD 1
Char3 FIELD 1
EndOfChars FIELD 0
Padding FIELD (-EndOfChars):AND:3
Integer FIELD 4
Integer2 FIELD 4
String FIELD MaxStrLen
Array FIELD ArrayLen*8
BitMask FIELD 4
EndOfUsedData FIELD 0
ASSERT EndOfUsedData <= EndOfData
“(-EndOfChars):AND:3”表达式计算正确的填充数:
· 如果EndOfChars是0 mod 4,则填充数是0;
· 如果EndOfChars是1 mod 4,则填充数是3;
· 如果EndOfChars是2 mod 4,则填充数是2;
· 如果EndOfChars是3 mod 4,则填充数是1。
无论何时添加或删除字符变量,它会自动调整填充数。
下面是该除法宏被引用时的结果。
ASSERT r5 <> r4 ;如果寄存器全不相等,显示提示信息
ASSERT r5 <> r2
ASSERT r4 <> r2
ASSERT r0 <> r5 ;如果$Div不为空,产生三个提示信息
ASSERT r0 <> r4
ASSERT r0 <> r2
ratio
MOV r2, r4 ;将除数放在r2中
CMP r2, r5, LSR #1 ;r2乘以2,直到2*r2>r5
90 MOVLS r2, r2, LSL #1
CMP r2, r5, LSR #1
BLS %b90 ;b表示向后搜索
MOV r0, #0 ;初始化r0
91 CMP r5, r2 ;判断r5是否大于r2
SUBCS r5, r5, r2 ;如果大于,r5减去r2,结果放入r5
ADC r0, r0, r0 ;r0乘以2
MOV r2, r2, LSR #1 ;r2除以2
CMP r2, r4 ;循环,直到r2
BHS %b91
10.5.4 使用MAP和FIELD命令描述数据结构
可以使用MAP和FIELD伪操作来描述数据结构。这两个伪操作一般是一起使用的。
使用MAP和FIELD伪操作定义数据结构有以下好处。
· 容易维护。
· 可以方便地实现相同数据结构的重复定义。
· 可以更高效地存取数据。
MAP伪操作指定数据结构的基址。
FIELD伪操作指定一个数据项所需的存储器数量,并为该数据项指定一个标号。对结构中的每个数据项重复该命令。
注意
使用MAP和FIELD伪操作当定义一个数据结构时,不分配存储器空间。使用定义常数的命令(如 DCD)来分配存储器空间。
1.相对映射
MAP/FIELD伪操作和使用寄存器相对寻址的LOAD/STORE指令配合使用,访问事先定义好的结构体是MAP/FIELD伪操作的基本用法。
下面是一个使用寄存器相对寻址的LOAD指令访问结构体的例子。
MAP 0
consta FIELD 4 ;consta占用4字节,偏移量为0
constb FIELD 4 ;constb占用4字节,偏移量为4
x FIELD 8 ;x占用8字节,偏移量为8
y FIELD 8 ;y占用8字节,偏移量为16
string FIELD 256 ;string占用256,偏移量为24
上面定义的数据结构,可以使用下列指令来访问:
MOV r9,#4096
LDR r4,[r9,#constb]
标号是相对于数据结构的开始位置的。用于存放映射的起始地址的寄存器(此例中为 r9)称为基址寄存器。
数据结构的位置是由运行时装载到基址寄存器的值确定的。MAP/FIELD伪操作不实际分配内存地址。
同一映射可以用于描述数据结构的多个实例。它们可以位于存储器中的任何位置。
备注
r9是“ARM-Thumb 程序调用标准”中的静态基址寄存器 (***)。详细信息请参阅本书附录。
2.基于寄存器的映射
一般情况下,每次访问一个数据结构时可以使用相同的寄存器作为基址寄存器。可以在使用MAP定义映射的基址时指定该寄存器的名称。
下面的例子显示了一个基于寄存器的映射。
MAP 0,r9
consta FIELD 4 ;consta变量占用4个字节,地址偏移量为0(相对于r9中的地址)
constb FIELD 4 ;constb变量占用4个字节,地址偏移量为4
x FIELD 8 ;x占用8个字节,地址偏移量为8
y FIELD 8 ;y占用8个字节,地址偏移量为16
string FIELD 256 ;字符串占用256字节,起始地址偏移量为24
利用示例中的映射,可以访问数据结构(不管它在内存中什么位置):
ADR r9,datastart
LDR r4,constb ; => LDR r4,[r9,#4] ;该伪操作被编译为LDR r4,[r9,#4]
constb包含从数据结构开始位置算起的数据项的偏移量,也包含基址寄存器。在此例中,基址寄存器是在MAP命令中定义的r9。
3.相对于程序的映射
可以使用程序计数器 (r15) 作为一个映射的基址寄存器。当使用r15做为基址寄存器时,被使用LOAD/STORE指令寻址的数据结构偏移量不能超出4KB范围。这是因为使用PC寄存器为基址的LOAD/STORE指令,最大寻址能力为4KB。
下面的例子显示了使用r15作为基址寄存器的程序段。其中包含一个为数据结构分配存储器空间的SPACE伪操作。
datastruc SPACE 280 ;保留280个字节来定义数据结构
MAP datastruc
consta FIELD 4
constb FIELD 4
x FIELD 8
y FIELD 8
string FIELD 256
使用下面的指令读取constb域所包含的内容。
LDR r2,constb ; => LDR r2,[pc,offset] 该伪操作被编译为LDR r2,[pc,offset]
在此例中,不需要在装载数据之前装载基址寄存器,此时使用程序计数器PC作为默认寄存器。
注意
由于处理器里面的流水线,r15寄存器值和 LDR 指令的实际地址并不相同。但是,汇编器将修正此问题。
4.定义结构体的结束
可以使用带有0操作数的FIELD伪操作来标记结构内的一个位置。标记位置后位置计数器并未增加(即偏移量offset不增加)。
下面的例子使用事先定义好的常量MaxStrLen和ArrayLen定义了字符串和数组的大小。为了防止MaxStrLen和ArrayLen值过大,使结构体超出所能使用的内存范围,使用了“FIELD 0”伪操作来标识结构体的结束。
StartOfData EQU 0x1000
EndOfData EQU 0x2000
MAP StartOfData
Integer FIELD 4
Integer2 FIELD 4
String FIELD MaxStrLen
Array FIELD ArrayLen*8
BitMask FIELD 4
EndOfUsedData FIELD 0
ASSERT EndOfUsedData <= EndOfData
该例中使用了:
· 一个EQU命令定义可使用的最大内存空间;
· 一个带有0操作数的FIELD伪操作来标记数据结构的结束位置;
· 一个ASSERT伪操作确认数据结构的结束位置并未超出可用内存。
5.强制内存对齐
如果在定义数据结构时,使用了一些字符变量(或其他非4字节的变量),就可能造成内存访问的对齐问题。
下面例子中显示了一个数据结构的定义。该定义方式使整数访问不能保证在字边界对齐。
StartOfData EQU 0x1000
EndOfData EQU 0x2000
MAP StartOfData
Char FIELD 1
Char2 FIELD 1
Char3 FIELD 1
Integer FIELD 4 ; 非字边界对齐
Integer2 FIELD 4
String FIELD MaxStrLen
Array FIELD ArrayLen*8
BitMask FIELD 4
EndOfUsedData FIELD 0
ASSERT EndOfUsedData <= EndOfData
这里,不能使用ALIGN伪操作来修正该边界对齐问题,因为ALIGN伪操作只对齐存储器内的当前位置。而MAP和FIELD伪操作不为其定义的结构分配任何存储空间。
解决的方法是,在Char3 FIELD 1后插入一个虚拟的FIELD 1。但是,如果改变了字符变量的数目,就会造成维护困难。每次必须重新计算正确的填充数。
下面的例子说明了一个更好的调整填充的方法。该示例使用一个带有0操作数的FIELD伪操作来标记字符数据的结束位置。第二个FIELD命令根据标号的值插入正确的填充数。使用“:AND:”运算符来计算正确的值。
StartOfData EQU 0x1000
EndOfData EQU 0x2000
MAP StartOfData
Char FIELD 1
Char2 FIELD 1
Char3 FIELD 1
EndOfChars FIELD 0
Padding FIELD (-EndOfChars):AND:3
Integer FIELD 4
Integer2 FIELD 4
String FIELD MaxStrLen
Array FIELD ArrayLen*8
BitMask FIELD 4
EndOfUsedData FIELD 0
ASSERT EndOfUsedData <= EndOfData
“(-EndOfChars):AND:3”表达式计算正确的填充数:
· 如果EndOfChars是0 mod 4,则填充数是0;
· 如果EndOfChars是1 mod 4,则填充数是3;
· 如果EndOfChars是2 mod 4,则填充数是2;
· 如果EndOfChars是3 mod 4,则填充数是1。
无论何时添加或删除字符变量,它会自动调整填充数。
举报