LSL 逻辑左移:
LSR 逻辑右移:
ROR 循环右移 ROR 循环右移
ASR 算术右移
RRX 带扩展的循环右移
LDMIA R0,{R1-R5} ;R1=[R0]
;R2=[R0+4]
;R3=[R0+8]
;R4=[R0+12]
;R5=[R0+16]
指令中IA表示在执行完一次Load操作后,R0自增4。
该指令将以R0为起始地址的5个字数据分别装入R1,R2,R3,R4,R5中。
STMFD R13!,{R0,R1,R2,R3,R4}
;将R0-R4中的数据压入堆栈,R13为堆栈指针
LDMFD R13!,{R0,R1,R2,R3,R4}
;将数据出栈,恢复R0-R4原先的值
第二个操作数:#immed_8r:常数表达式; Rm:寄存器方式; Rm, shift:寄存器移位方式。
MOV R0, R1, LSL #5 ; R0=R1左移5位
MVN R0,#0 ; R0= -1 ;MVN 数据取反传送指令
ADD R0,R1,R2,LSL #5 ; R0=R1+R2左移5位
第一个64位操作数存放在寄存器R3 R2中;
第二个64位操作数存放在寄存器R5 R4中;
64位结果存放在R1 R0中。
ADDS R0,R2,R4 ;低32位相加,S表示结果影响条件标志位的值
ADC R1,R3,R5 ;高32位相加
SUB R0,R1,R2,LSL #5 ; R0=R1-R2左移5位
第一个64位操作数存放在寄存器R3 R2中;
第二个64位操作数存放在寄存器R5 R4中;
64位结果存放在R1 R0中。
64位的减法(第一个操作数减去第二个操作数)可由以下语句实现:
SUBS R0,R2,R4 ; 低32位相减,S表示结果影响条件标志位的值
SBC R1,R3,R5 ; 高32位相减
RSB R0, R1, R2, LSL #5 ; R0=R2左移5位-R1 sub 指令操作数换了
AND R0, R0, #5
ORR R0, R0, #5 ;ORR 逻辑或指令
MULS R0, R1, R2 ; R0=R1×R2,结果影响寄存器CPSR的值 MUL 32位乘法指令
MLA R0, R1, R2, R3 ; R0=R1×R2+R3 MLA 32位乘加指令
SMULL{
Rm、Rs的值为32位的有符号数。
SMULL R0, R1, R2, R3 ;R0=R2×R3的低32位 ;R1=R2×R3的高32位
SMLAL R0, R1, R2, R3 ;R0=R2×R3的低32位+R0 ;R1=R2×R3的高32位+R1
UMULL 64位无符号数乘法指令
CMP R0, #5 ;计算R0-5,根据结果设置条件标志位
ADDGT R0, R0, #5 ;如果R0>5,则执行ADDGT指令
CMN R0, #5 ;把R0与-5进行比较CMN 反值比较指令
☞第三章ppt 48页
伪指令:在ARM汇编语言程序里,有一些特殊指令助记符,这些助记符与指令系统的助记符不同,没有相对应的操作码,通常称这些特殊指令助记符为伪指令。
符号定义伪指令
定义局部变量:LCLA、LCLL、LCLS ;LCLA num1
定义全局变量:GBLA(数字变量)、GBLL(逻辑)、GBLS(字符串)
变量赋值: SETA、SETL、SETS
用法: str3 SETS “Hello!”;将该变量赋值为“Hello!”
寄存器列表定义名称:RLIST {寄存器列表};在LDM/STM指令中,列表
中的寄存器按照寄存器的编号由低到高的次序访问,与列表中的寄存器排列
次序无关。 pblock RLIST {R0-R3,R7,R5,R9}
;将寄存器列表名称定义为pblock,可在ARM指令
;LDM/STM中通过该名称访问寄存器列表
数据定义伪指令 分配存储单元,并初始化
DCB 字节分配
Array1 DCB 1,2,3,4,5 ;数组
str1 DCB “Your are welcome!”;构造字符串并分配空间
DCW/DCWU 半字(2字节)分配
Arrayw1 DCW 0xa,-0xb,0xc,-0xd
;构造固定数组,分配半字存储单元
DCD/DCDU 字(4字节)分配
Arrayd1 DCD 1334,234,345435
;构造数组,并分配字存储单元
DCQ/DCQU 8个字节分配 字对齐
Arrayd1 DCQ 234234,98765541
;构造数组,分配字单元存储空间。
;注意:DCQ不能给字符串分配空间
DCFS/DCFSU 单精度浮点数分配
DCFD/DCFDU 双精度浮点数分配
SPACE 分配一块连续的存储单元
freespace SPACE 1000 ;分配1000字节的存储空间
FIELD 定义一个结构化的内存表的数据域 不分配存储单元
MAP 定义一个结构化的内存表首地址
MAP 0x130,R2 ;内存表首地址=0x130+R2
count FIELD 4;定义count的长度为4字节,位置为0xF1000+0
x FIELD 4;定义x的长度为4字节,位置为0xF1004
y FIELD 4;定义y的长度为4字节,位置为0xF1008
汇编控制伪指令
ALIGN [表达式[,偏移量]]
说明:通过填充字节,使当前的位置满足一定的对齐方式。
表达式:表达式的值为2的n次幂, 0≤n≤31,如1、2、4、8、16等,用于指定对齐方式。
没有表达式:默认,字对齐。
偏移量:数字表达式,自动对齐到“表达式值+偏移量”。
例如:
B START
ADD R0, R1, R2
DATA1 DCB “abcde”
ALIGN 4
START LDR R5, [R6]
AREA 段名 属性,……
功能:定义段。
说明:如果段名以数字开头,那么该段名需用“|”字符括起来,如|7wolf|,用C的编译器产生的代码一般也用“|”括起来。
属性:表示该代码段/数据段的相关属性,多个属性用“,”分隔。
常见属性如下:
① DATA:定义数据段。
② CODE:定义代码段。
③ READONLY:表示本段为只读。
④ READWRITE:表示本段可读写。
⑤ ALIGN=表达式,表达式的值为2的n次幂。
⑥ COMMON属性:定义一个通用段,这个段不包含用户代码和数据。
注意:一个程序至少包含一个段,可以有多个数据段,多个代码段。
例: AREA test,CODE,READONLY
格式: CODE16/CODE32
功能:CODE16指示编译器后面的代码为16位的Thumb指令。
CODE32指示编译器后面的代码为32位的ARM指令。
说明:用于汇编源代码中同时包含Thumb和ARM指令的情况。
CODE16/CODE32不能对处理器进行状态的切换
格式: ENTRY
功能:指定汇编程序的入口。
在一个完整的汇编程序中至少要有一个ENTRY。
程序中也可以有多个,此时,程序的真正入口点可在链接时指定。
但在一个源文件里最多只能有一个ENTRY,也可以没有
格式: 名称 EQU 表达式 [,类型]
功能:等值 类似宏定义
num1 EQU 1234 ;定义num1为1234
格式: GET 文件名
说明:将一个源文件包含到当前的源文件中,并将被包含的源文件在当前位置展开进行汇编处理。
GET E:\code\prog1.s ;通知编译器在当前源文件
;包含源文件E:\code\ prog1.s
GET prog2.s ;通知编译器当前源文件包含
;可搜索目录下的prog2.s
格式: INCBIN 文件名 (用于读取数据)
说明:将一个数据文件或者目标文件包含到当前的源文件中,编译时被包含的文件不作任何变动地存放在当前文件中,编译器从后面开始继续处理。
例如:
AREA constdata,DATA,READONLY
INCBIN data1.dat ;源文件包含文件data1.dat
INCBIN E:\DATA\data2.bin
;源文件包含文件E:\DATA\data2.bin
格式: IMPORT 标号 [, WEAK]
功能:告诉编译器,这个标号是在外部定义的,即在其他的源文件中定义的。
[, WEAK]:如果所有的源文件都没有找到这个标号的定义,编译器也不会提示错误信息。
IMPORT _ printf
;通知编译器当前文件要引用函数_ printf
格式: EXPORT 标号 [, WEAK]
功能:声明一个全局标号,其他文件可以引用。
可以用GLOBAL代替EXPORT。
[, WEAK]:可选项,声明其他文件有同名的标号,则该同名标号优先于该标号被引用。
EXPORT main ;声明一个可全局引用的函数main
及其他一些常用伪指令等。
ADR伪指令小范围地址读取
格式:
ADR{} ,< expr>
功能:基于PC相对偏移的地址值(expr 地址表达式)→Rd。
当地址值是非字对齐时,取值范围在-255~255字节之间。
当地址值是字对齐时,取值范围在-1 020~1 020字节之间。
LOOP MOV R1,#0xF0
ADR R2,LOOP ;将LOOP的地址放入R2,因为PC值为当前指令
地址值加8字节,所以本 ADR指令被编译器换成“SUB R2,PC,#0xc”
ADRL伪指令 中等范围地址读取
ADRL {} ,< expr>
功能:类似于ADR, 但比ADR读取更大范围的地址。
当地址值是非字对齐时,取值范围在-64KB~64 KB之间;
地址值是字对齐时,取值范围在-256KB~256 KB之间。
在编译源程序时,ADRL被编译器替换成两条合适的指令。
ADRL R0,DATA_BUF
ADRL R1,DATA_BUF+80
DATA_BUF
SPACE 100 ;定义100字节缓冲区
LDR伪指令 大范围地址
LDR{} ,< =expr/label-expr >
说明:32位的立即数或一个地址值→Rd。
在汇编编译源程序时,LDR伪指令被编译器替换成一条合适的指令。
若加载的常数未超出MOV或MVN的范围,则使用MOV或MVN指令代替该LDR伪指令;
LDR R0,=0x12345678 ;加载32位立即数0x12345678
LDR R0,=DATA_BUF+60 ;加载DATA_BUF地址+60
LTORG
功能:声明一个数据缓冲池(文字池)的开始。
通常放在无条件跳转指令之后,或者子程序返回指令之后,以免处理器错误地将数据缓冲池中地数据作为指令来执行。
Func1
……
MOV PC, LR
LTORG
DATA
SPACE 256 ;从DATA标号开始预留256字节的内存单元。
END
NOP
伪操作:伪指令完成的操作即为伪操作。 作用:伪指令在源程序中的作用是为完成汇编程序作各种准备工作的,由汇编程序在源程序的汇编期间进行处理,仅在汇编过程中起作用。
汇编语言程序的结构
AREA Init,CODE,READONLY ;定义一个代码段
ENTRY ;定义一个程序入口
LOOP1 MOV R0, #211 ;给参数R0赋值211
MOV R1, # 106 ;给参数R1赋值106
MOV R2, # 64 ;给参数R2赋值64
MOV R3, # 5 ;给参数R3赋值5
BL SUB1 ;调用子程序SUB1,同时将子程序的返回地址
;存放在链接寄存器R14(LR)中。
MOV R0, # 0x18 ;传送到软件中断的参数
LDR R1, = 0x20026 ;传送到软件中断的参数
SWI 0x123456 ;通过软件中断指令返回
SUB1
SUB R0, R0, R1 ;子程序代码
SUB R0, R0, R2
SUB R0, R0, R3
MOV PC, LR ;从子程序返回
END
MOV R0, # 0x18 ;传送到软件中断的参数
LDR R1, = 0x20026 ;传送到软件中断的参数
SWI 0x123456 ;通过软件中断指令返回 不明白含义
C语言代码:
if ( a = = 0 || b = = 1)
c = d + e ;
对应的ARM代码段:
CMP R0, #0 ;判断R0是否等于0
CMPNE R1, #1 ;如果R0不等于0,判断R1是否等于1
ADDEQ R2, R3, R4 ;R0=0或R1=1时,R2=R3+R4
例:将串1中的字符数据拷贝到串2,按字节拷贝。 字符串定义指令 DCB,以0x00为结束符。 字节拷贝指令 LDRB、STRB。 拷贝方法 (1)初始化: R1←源串,R0←目的串
(2)拷贝一个字节: LDRB R2,[R1] STRB R2,[R0]
(3)R1、R0指向下一个要拷贝的字节数据: ADD R1,R1,#1 ADD R0,R0,#1
(4)判断字符串是否结束: 判断 CMP R2,#0x00 转移 BNE
AREA StrCopy,CODE,READONLY
ENTRY ;程序入口
start
LDR r1, =srcstr ;初始串的指针
LDR r0, =dststr ;结果串的指针
BL strcopy ;调用子程序执行复制
stop
MOV r0,#0x18 ;执行中止
LDR r1,=0x20026
SWI 0x123456
strcopy
LDRB r2,[r1],#1 ;加载并且更新源串指针
STRB r2,[r0],#1 ;存储且更新目的串指针
CMP r2,#0 ;为0,字符串结束
BNE strcopy
MOV pc,lr
AREA Strings,DATA,READWRITE
srcstr DCB "First string - source", 0
dststr DCB "Second string - destination", 0
END
AREA Block,CODE,READONLY ;段定义
num EQU 20 ;被拷贝的数据字数
ENTRY ;程序入口
start
LDR r0,=src ;r0 = 源串指针
LDR r1,=dst ;r1 = 目的串指针
MOV r2,#num ;r2 = 拷贝字数
MOV sp,#0x400 ;设置堆栈指针 (r13)
blockcopy
MOVS r3,r2,LSR #3 ;字数/8
BEQ copywords ;少于8个字,转
STMFD sp!,{r4-r11} ;入栈保护
octcopy
LDMIA r0!,{r4-r11} ;从源串加载8个字
STMIA r1!,{r4-r11} ;放入目的串
SUBS r3,r3,#1 ;8倍字数减1
BNE octcopy ;未完,继续
LDMFD sp!,{r4-r11} ;出栈恢复
copywords
ANDS r2,r2,#7 ;“字数/8”的余数
BEQ stop ;为0,转
wordcopy
LDR r3,[r0],#4 ;从源串加载一个字且指针自增
STR r3,[r1],#4 ;存储到目的串
SUBS r2,r2,#1 ;字数减1
BNE wordcopy ;未完,继续
stop
MOV r0,#0x18 ;执行中止
LDR r1,=0x20026
SWI 0x123456
AREA BlockData,DATA,READWRITE
src DCD 1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4
dst DCD 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
END
#include
void my_strcpy (const char *src, char *dest) //声明一个函数
{
char ch; //声明一个字符型变量。
__asm //调用关键词__asm。
{
LOOP //循环入口。
LDRB ch,[src],#1 // ARM指令,ch←[src], src ←src +1。
STRB ch,[dest],#1 // ARM指令, [dest]←ch,[dest]←[dest]+1。
CMP ch, #0 //比较ch是否为零,判断字符串是否结束。 BNE LOOP // NE为不相等条件, ch是否为零,否则循环。
}
}
int main ( ) ;C语言主程序
{
char *a = "forget it and move on!"; //声明字符型指针变量
char b[64]; //字符型数组
// r0 目的串指针,r1 源串指针对应a,b
my_strcpy (a, b);//调用函数,进行复制
printf ("original: %s", a); //屏幕输出,a的数值
printf ("copyed: %s", b); //屏幕输出,b的数值
return 0;
}