type
status
date
slug
summary
tags
category
icon
password
指令 | 作用 | 代码示例(格式) |
LSL | 逻辑左移,最低有效位用0填充 | SUB R3,R2,R1,LSL #2 ;R3←R2-(R1左移2位) |
LSR | 逻辑右移,最高有效位用0填充 | SUB R3,R2,R1,LSR R0 ;R3←R2-(R1右移R0位) |
ASL | 算术左移,空出的有效位用0填充 | ADD R3,R2,R1,ASL #2 ;R3←R2+(R1左移2位) |
ASR | 算术右移,移如果源操作数是正数,空出的最高有效位用0填充,如果是负数用1填充 | SUB R3,R2,R1,ASR R3 ;R3←R2-(R1算术右移R3位) |
ROR | 循环右移,移出的字的最低有效位依次填入空出的最高有效位 | SUB R3,R2,R1,ROR #2 ;R3←R2-(R1循环右移2位) |
RRX | 带进位位的循环右移。将寄存器的内容循环右移1位,空位用原来C标志位填充。 | SUB R3,R2,R1,RRX;R3←R2-(R1带进位位循环右移1位) |
B | 跳转到指定地址执行,地址范围限制在当前PC寄存器所指向的指令地址的±32MB范围 | BNE forward;Z位为0时跳转至forward |
BL | 带返回链接跳转到指定地址执行,地址范围限制在当前PC寄存器所指向的指令地址的±32MB范围 | BLNE forward;Z位为0时跳转至forward,并将PC保存在LR中 |
BX | 跳转到指令中所指定的目标地址,并实现状态切换。(T<-Rm[0]) | BX R0 ;跳转,切换状态 |
ADD | ADD指令把第1源操作数寄存器Rn和第2源操作数operand2相加后,将结果存放到目的寄存器 Rd。 | ADD R0,R2,R3,LSL#1 ;R0 ←(R2) +(R3<<1) |
ADC | ADC指令将operand2的数据与Rn的值相加,再加上CPSR中的C条件标志位,结果保存到Rd寄存器 | ADCS R1,R5,R9 ;加第二个字,带进位 |
SUB | SUB指令用寄存器Rn减去operand2,结果保存到Rd中。 | SUB R0,R2,R3,LSL#1 ;R0←(R2)-(R3<<1) |
SBC | SBC指令用寄存器Rn减去operand2,再减去CPSR中的C条件标志位的反码,结果保存到Rd中。 | SBC R5,R1,R3 ;R5=R1-R3-/C |
RSB | RSB指令用operand2减去寄存器Rn,结果保存到Rd中。 | RSB R3,R1,#0xFF00 ;R3=0xFF00-R1 |
RSC | RSC 指令用寄存器operand2减去Rn,再减去CPSR中的C条件标志位的反码,结果保存到Rd中。 | RSC R3,R1,#0 |
MUL | MUL指令将Rm和Rs中的值相乘,结果的低32位保存到Rd中.Rd ≠ Rm | MUL R1,R2,R3 ;R1=R2×R3 |
MLA | 指令将Rm和Rs中的值相乘,再将乘积加上第3个操作数,结果的低32位保存到Rd中。注: Rd ≠ Rm | MLA R0,R1,R2,R3 ;R0←(R1)X(R2)+(R3) |
UMULL | UMULL指令将Rm和Rs中的值作无符号数相乘,结果的低32位保存到RdLo中,高32位保存到RdHi中 | UMULL R0,R1,R5,R8 ;(R1,R0)←R5×R8 |
UMLAL | UMLAL指令将Rm和Rs中的值作无符号数相乘,64位乘积与RdHi、RdLo相加,结果的低32位保存到RdLo中,而高32位保存到RdHi中。 | UMLAL R0,R1,R5,R8 ;(R1,R0)←R5×R8+(R1,R0) |
SMULL | SMULL指令将Rm和Rs中的值作有符号数相乘,结果的低32位保存到RdLo中,而高32位保存到RdHi中。 | SMULL R2,R3,R7,R6 ;(R3,R2)←R7×R6 |
SMLAL | SMLAL指令将Rm和Rs中的值作有符号数相乘,64位乘积与RdHi、RdLo相加,结果的低32位保存到RdLo中,高32位保存到RdHi中。 | SMLAL R2,R3,R7,R6;(R3,R2)←R7×R6+(R3,R2) |
AND | AND指令将operand2的值与寄存器Rn的值按位逻辑“与”操作,结果保存到Rd中。 | ANDS R0,R0,#0x01 ;R0=R0&0x01,取出最低位数据 |
ORR | ORR指令将operand2的值与寄存器Rn的值按位逻辑“或”操作,结果保存到Rd中 | ORR R0,R0,#0x0F ;将R0的低4位置1 |
EOR | EOR指令将operand2的值与寄存器Rn的值按位逻辑“异或”操作,结果保存到Rd中。 | EOR R1,R1,#0x0F ;将R1的低4位取反 |
BIC | BIC指令将寄存器Rn的值与operand2的值的反码按位逻辑“与”操作,结果保存到Rd中。 | BIC R1,R1,#0x0F ;将R1的低4位清0,其它位不变 |
MOV | MOV指令将operand2传送到目标寄存器Rd中。 | MOVS R1,R0,LSL#3 ;将寄存器R0 * 8传送到寄存器R1,并影响标志位 |
MVN | MVN指令将operand2按位取反后传送到目标寄存器Rd中。 | MVN R1,#0xFF ;R1←0xFFFFFF00 |
CMP | CMP指令将寄存器Rn的值减去operand2的值,但不存储运算结果,只根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行 | CMP R1,R0 ;(R1)-(R0);根据结果设置CPSR的标志位 |
CMN | CMN指令将寄存器Rn的值减去operand2的负数(即加上operand2的值),但不存储运算结果,只根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。 | CMN R0,#1 ;R0+1,判断R0是否为-1 |
TST | TST指令将寄存器Rn的值与operand2的值按位逻辑“与”操作,但不存储运算结果,只根据操作的结果更新CPSR中的相应条件标志位。该指令一般用来检测是否设置了特定的位。 | TST Rl,#0x0F ;判断R1的低4位是否为0 |
TEQ | TEQ指令将寄存器Rn的值与operand2的值按位逻辑“异或”操作,但不存储运算结果,只根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行 | TEQ R0,R1 ;比较R0与R1是否相等(不影响V位和C位) |
LDR | 指令从内存中取数据放入寄存器(可以增加S、H、B后缀) | LDR R1,[R0,#0x10];将R0+0x10地址处的数据读出,保存到R1中(R0的值不变) |
STR | 指令将寄存器中数据保存到存储器中(可以增加S、H、B后缀) | STR R1,[R0,-R2];将R1的数据保存到R0-R2地址处 |
LDM | 加载多个寄存器 | LDMFD SP!,{R0 - R7,PC}^ ;恢复现场,包括CPSR,异常处理返回,SP值更新。(带了^) |
STM | 存储多个寄存器 | STMFD SP!,{R0 - R7,LR} ;现场保存,将R0~R7、LR入栈,SP值更新 。 |
SWP | 将一个内存单元[Rn]的内容读取到一个寄存器Rd中,同时将另一个寄存器Rm的内容写入到该内存单元中 | SWP R1,R1,[R0];将R1的内容与R0指向的存储单元的内容进行交换。 |
MRS | 把状态寄存器psr(CPSR或SPSR)的内容传送到目标寄存器中 | MRS R2,SPSR ; R2 <-SPSR |
MSR | 在ARM处理器中,只有MSR指令可以直接设置状态寄存器CPSR或SPSR | MSR CPSR_c,R0 ;传送R0的内容到CPSR,但仅仅修改CPSR中的控制位域 |
SWI | 软件中断指令SWI产生软件异常中断,用来实现用户模式到特权模式的切换 | SWI 10 ;中断类型号为10(注:没有#号) |
BKPT | 断点指令用于软件调试;它使处理器停止执行正常指令而进入相应的调试程序 | ㅤ |
CLZ | 前导0计数指令CLZ 对Rm中的前导0的个数进行计数,结果放到Rd中 | MOV R2, #0X17C00
CLZ R3, R2 ;R3=15 |
伪指令 | 作用 | 指令示例(格式) |
GBLA | 定义一个 全局数字变量,其默认初值为 0 | GBLA Test1 ;定义一个全局数字变量,变量名为 Test1 |
GBLL | 定义一个 全局逻辑变量 ,其默认初值为 FALSE | GBLL Test2 ;定义一个全局逻辑变量,变量名为 Test2 |
GBLS | 定义一个 全局字符串变量,其默认初值为 空 | GBLS Test3 ;定义一个全局字符串变量,变量名为 Test3 |
LCLA | 定义一个局部数字变量,其默认初值为 0 | LCLA Test4 ;定义一个局部数字变量,变量名为 Test4 |
LCLL | 定义一个局部逻辑变量,其默认初值为 FALSE(假) | LCLL Test5 ;定义一个局部逻辑变量,变量名为 Test5 |
LCLS | 定义一个局部字符串变量,其默认初值为 空 | LCLS Test6 ;定义一个局部字符串变量,变量名为 Test6 |
SETA | 用于给一个已经定义的全局或局部数字变量赋值 | Test1 SETA 0xAA ;将Test1变量赋值为0xAA。 |
SETL | 用于给一个已经定义的全局或局部逻辑变量赋值 | Test2 SETL {TRUE} ;将Test2 变量赋值为真; |
SETS | 用于给一个已经定义的全局或局部字符串变量赋值 | Test3 SETS “Testing” ;将Test3变量赋值为“Testing” |
RLIST | 对一个列表定义一个统一的名称 | LoReg RLIST {R0-R7} ;定义寄存器列表{R0-R7}的名称为LoReg |
LTORG | 用于声明一个数据缓冲池(文字池)的开始。 | LTORG;定义数据缓冲池的开始位置 |
MAP | 定义一个结构化的内存表的首地址。MAP 可以用“^” 代替 | MAP 0x100,R9 ;内存表的首地址为R9+0X100 |
FIELD | FIELD用于定义一个结构化内存表中的数据域,“#”与FIELD同义。 | A FIELD 16 ;定义A的长度为16字节,位置为 0X100 |
SPACE | 用于分配一片连续的存储区域并初始化为 0。 | DataSpace SPACE 100 ;分配连续的100字节的存储单元并初始化为 0。 |
DCB | DCB用于分配一段字节内存单元,并用伪指令中的expr初始化 | String DCB “send,data is error!”,0;构造字符串 |
DCD | 用于分配一段字内存单元,并用伪指令中的expr初始化,字对齐,可定义数据表格或其它常数。“&”与DCD同义 | Src DCD 1,2,3,4,5,6,7,8 |
DCDU | 用于分配一段字内存单元,并用伪指令中的expr初始化。DCDU伪指令分配的内存不需要字对齐,可定义数据表格或其它常数 | Src DCDU 1,2,3,4,5,6,7,8 |
DCFD | DCFD伪指令用于为双精度的浮点数分配一片连续的字存储单元并用伪指令中指定的表达式初始化。每个双精度的浮点数占据两个字单元。 DCFD分配的字存储单元是字对齐的。 | FdataTest DCFD 2E30,-3E-20;定义双精度浮点数,字对齐 |
DCFDU | DCFDU具有DCFD同样的功能,而用DCFDU分配的字存储单元并不严格字对齐 | FdataTest DCFDU 2E30,-3E-20;定义双精度浮点数,字不对齐 |
DCFS | DCFS和DCFSU伪指令用于为单精度的浮点数分配一片连续的字存储单元并用伪指令中指定的表达式初始化。每个单精度的浮点数占据一个字单元。字对齐 | FdataTest DCFS 1E3,-4E-9 ;定义单精度浮点数,字对齐 |
DCFSU | DCFS和DCFSU伪指令用于为单精度的浮点数分配一片连续的字存储单元并用伪指令中指定的表达式初始化。每个单精度的浮点数占据一个字单元。不严格字对齐 | FdataTest DCFSU 1E3,-4E-9 ;定义单精度浮点数,字不对齐 |
DCQ | DCQ用于分配一段双字的内存单元,并用64位的整数数据literal初始化,DCQ伪指令分配的内存需要字对齐。 | Data DCQ –225,2101 |
DCQU | DCQU具有DCQ同样的功能,但分配的内存不需要字对齐 | Data DCQU number+4 ;number必须是已经定义过的数字表达式 |
DCW | DCW用于分配一段半字的内存单元,并用指定的数据expr初始化,DCW伪指令分配的内存需要半字对齐。 | Data DCW –592,123,6543 |
DCWU | DCWU具有DCW同样的功能,但分配的内存不需要半字对齐 | Data DCWU –592,123,6543 |
ALIGN | ALIGN是边界对齐伪指令,它可以通过添加填充字节的方式,使当前位置满足一定的对齐方式 | ALIGN 4,3 ; 4字节对齐+3偏移量. |
AREA | 定义一个代码段或数据段。 | AREA Init, CODE,READONLY;该伪指令定义了一个代码段,段名为 Init ,属性为 只读。 |
CODE16 | CODE16告诉汇编编译器后面的指令序列为16位的Thumb指令 | CODE16 |
CODE32 | CODE32告诉汇编编译器后面的指令序列为32位的ARM指令 | CODE32 |
ENTRY | 指定程序的入口点。 | ENTRY |
END | END 伪指令用于通知编译器汇编工作到此结束,不再往下汇编了 | END |
EXPORT/GLOBAL | 声明一个源文件中的符号,使此符号可以被其他源文件引用 | EXPORT DoAdd;申明一个全局引用的标号DoAdd |
IMPORT | 声明一个符号是在其他源文件中定义的 | IMPORT main |
EXTERN | 与 IMPORT 相同 | EXTERNDoAdd;申明一个全局引用的标号DoAdd |
EQU | 用于为程序中的常量、标号等定义一个等效的字符名字,其作用类似于 C语言 中的 #define。 | abcd EQU 0x1c, CODE32;定义abcd符号的值为绝对地址0x1c,而且此处为ARM指令 |
GET/INCLUDE | 用于将一个源文件包含到当前的源文件中,并将被包含的源文件在当前位置进行汇编 | GET c:\a2.s |
INCBIN | 用于将一个目标文件或数据文件包含到当前的源文件中,被包含的文件不做任何变动地存放在当前文件中,编译器从其后开始继续处理 | INCBIN a1.dat |
RN | 用于给一个寄存器定义一个别名,以提高程序的可读性。 | Temp RN R0 ;将R0定义一个别名Temp |
ROUT | 用于给一个局部变量定义作用范围 | Routine ROUT ;定义局部标号的有效范围,名称为routine |
宏指令 | 作用 | 指令示例(格式) |
ADR | 用于将一个 近地址值 传递到一个寄存器中 | ADR R4,start |
ADRL | 类似于ADR,但可以把更远的地址赋给目标寄存器 | ADRL R4,start + 60000 |
LDR | 大范围的地址读取 | LDR R0, =0x12345678 ;加载32位立即数0x12345678 |
NOP | 空操作 | NOP |
宏
- 格式
MACRO $标号 宏名 $参数1, $参数2,….. 程序段(宏定义体) MEND
- 示例
MACRO ;宏定义指令 $MDATA MAXNUM $NUM1,$NUM2 ;主标号,宏名,参数 语句段 $MDATA.WAY1 ;宏内标号,必须写为“主标号.宏内标号” 语句段 $MDATA.WAY2 ;宏内标号 语句段 MEND ;宏结束指令 ;程序中调用该宏: Lab1 MAXNUM 0x01, 0x02
程序示例
- 使用后变址寻址方式完成表复制程序:
COPY ADR R1,NEXT1 ;R1指向NEXT1 ADR R2,NEXT2 ;R2指向NEXT2 LOOP LDR R0,[R1],#4 ;取一个数 STR R0,[R2],#4 ;复制一个数 …… NEXT1 ;源数据 …… NEXT2 ;目标数据
- 现已知寄存器 R0 中存放了数据 a ,寄存器 R1 中存放了数据 b ,编写一个程序段,求取 a 和 b 的最大公约数并将其存入寄存器 R0
gcb CMP R0,R1 ;比较a和b的大小 SUBGT R0,R0,R1 ;如果a>b,则a=a-b SUBLT R1,R1,R0 ;如果a<b,则b=b-a BNE gcb ;如果a!=b,则返回gcb MOV PC,LR ;如果a=b,则返回主程序
- BX指令示例(实现状态切换)
CODE32 ;32位编码 ARM1 LDR R0,=Thumb1 ;把Thumb地址赋给R0,末位自动置1 MOV LR,PC ;设置返回地址 BX R0 ;跳转,切换状态 ADD R1,R2,#2 … CODE16 ;16位编码 Thumb1 ADD R1,R3,#1 ;Thumb程序 … BX LR ;跳转到返回地址,状态切换
- BLX指令示例(实现状态切换+返回地址保存)
CODE32 ;32位编码 ARM1 LDR R0,=Thumb1+1 ;把Thumb地址赋给R0,末位置1 ;MOV LR,PC ;设置返回地址,此条指令注销 BLX R0 ;跳转,切换状态, ;LR ← 下一条指令地址 ADD R1,R2,#2 … CODE16 ;16位编码 Thumb1 ADD R1,R3,#1 ;Thumb程序 … BX LR ;跳转到返回地址,状态切换
- 有两个128位数,第一个数由高到低存放在寄存器 R7~R4 中,第二个数由高到低存放在寄存器 R11~R8 中,请编写程序把两个数相加,运算结果由高到低存放到寄存器 R3~R0中
ADDS R0,R4,R8 ;加低位字,不带进位 ADCS R1,R5,R9 ;加第二个字,带进位 ADCS R2,R6,R10 ;加第三个字,带进位 ADCS R3,R7,R11 ;加第四个字,带进位
以上的S后缀都是必须的。
这段程序是有缺陷的,因为最后一次加法运算仍然可能出现溢出或者进位
- 使用SBC实现64位减法
SUBS R4,R0,R2 SBC R5,R1,R3 ;(R5,R4)=(R1,R0)-(R3,R2)
存放在R0和R1中的64位数(R0是低位) 减去存放于R2和R3中的64位数(R2是低位) ,结果存放到R4和R5两个寄存器中(R4是低位) 。
- 求64位数值的负数
RSBS R2,R0,#0 ;求一个32位数的负数 RSC R3,R1,#0 ;使用RSC指令实现
- 将寄存器R2中的高8位数据传送到寄存器R3的低8位,原R3高24位置换为低24位
MOV R0,R2,LSR #24 ;将R2右移24位,即将其高8位移至低8位送R0。此时R2原来的内容没有变 ORR R3,R0,R3,LSL #8 ;将R3左移8位后将R0低8位送至R3。
- 利用LDR指令实现多分支程序跳转
…… MOV R0,N ;到这里R2中存储的应该是要调用的函数的序号 ADR R5,JPTAB LDR PC,[R5,R0,LSL #2] …… JPTAB DCD FUN_SUB0 DCD FUN_SUB1 DCD FUN_SUB2 ……
- 设置CPSR的进位位
MRS R0, CPSR ;R0<- CPSR ORR R0,R0,#0x20000000 ;置1进位位C MSR CPSR_f, R0 ;CPSR_f<-R0[31:24]
- 系统启动时的堆栈初始化
INITSTACK ;堆栈初始化子程序 MOV R0,LR ;保存返回地址 MRS R1,CPSR ;保存CPSR原值 MSR CPSR_c,#0xD7 ;切换到终止模式 …… ;设置堆栈大小等 LDR SP,StackSvc ;设置堆栈指针 MSR CPSR_c,#0xD2 ;切换到中断模式 …… ;设置堆栈大小等 LDR SP,StackIrq ;设置堆栈指针 …… ;对其它模式设置 MSR CPSR_c,R1 ;恢复CPU原模式 MOV PC,R0 ;子程序返回
- 进入SWI中断程序后,提取中断类型码(中断类型码使用立即数给出)
T_bit EQU 0x20 ;用于测试Thumb标志位(bit5) SWI_handler STMFD SP!,{R0-R3,R12,LR} MRS R0,SPSR ;中断前的CPSR值到R0 TST R0,#T_bit ;测试T位标志 LDRNEH R0,[LR,# -2] ;读取16位的SWI指令码 BICNE R0,R0,#0xFF00 ;获取SWI中的中断号 LDREQ R0,[LR,# -4] ;读取32的SWI指令码 BICEQ R0,R0,#0xFF000000 ;获取SWI中的中断号 …… ;转去处理相应的软中断 LDMFD SP!,{R0-R3,R12,PC} ;中断返回,包括恢复原CPSR值
- 一些实现乘法的方法
MOV R0,R0,LSL #n ;R0=R0<<n;R0= R0*2^n ADD R0,R0,R0,LSL #n ;R0=R0+R0*2n= R0*(2^n+1) RSB R0,R0,R0,LSL #n ;R0=R0*2n-R0= R0*(2^n-1)
- 转换数据存储方式
- 将寄存器R0中的数据存储方式转换成另一种存储方式。指令执行前R0中数据存储方式为:R0=A,B,C,D;指令执行后R0中数据存储方式为:R0=D,C,B,A
- 将内存中从0x400800开始的100个字数据相加,其结果存于R3、R2中(R3中为高32位)。主要考察的是循环结构
LDR R0,=0x400800 MOV R1,#100 ;初始化循环次数 MOV R2,#0 MOV R3,#0 loop ;循环体 LDR R4,[R0],#4 ADDS R2,R2,R4 ADC R3,R3,#0 ;或者ADCS SUBS R1,R1,#1 ;循环计数器减1,设置条件标志 BNE loop ;循环计数器不为0,跳到loop继续执行 …… ;循环计数器为0,程序继续执行
- 在链表中搜索与某一数据相等的元素。
- 链表的每个元素包括两个字,第1个字中包含一个字节数据;第2个字中包含指向下一个链表元素的指针,当这个指针为0时表示链表结束。
代码执行前R0指向链表的头元素,R1中存放将要搜索的数据;代码执行后R0指向第1个匹配的元素,或者当没有匹配元素时,R0为0。
- 数据定义伪指令示例
GBLA StrLen GBLS Str1 GBLS Str2 GBLS Str3 Str1 SETS "AAA" Str2 SETS "BBB" Str3 SETS Str1 :CC: Str2 ;Str3=“AAABBB”(:CC:拼接运算符) StrLen SETA :LEN: Str3 ;StrLen=6,:LEN:是求字符串长度的运算符 AREA blockcopy,CODE,READONLY …… Src DCD 1,2,3,4,5,6,7,8 StrMem DCB Str3,0 ;从StrMem开始的连续7个单元(一个字符串代表多个expr) ;分别存放的是AAABBB的ASCII码和0. …… END
- 控制程序流向伪指令程序示例
GBLA NU1 NU1 SETA 1 AREA blockcopy,CODE,READONLY ENTRY WHILE NU1 < 3 LDR R0,=Src LDR R1,=0x40000000 MOV R2,#NU1 LDR SP,=0x40000840 NU1 SETA NU1+1 WEND ......... END
- 伪指令操作示例1——ALIGN、LTORG。。。
AREA example, CODE, READONLY ENTRY MOV R0,#1 B start DATA1 DCB "strin" ;造成地址不对齐 ALIGN 4 ;使后续指令4字节对齐 start BL func1 BL func2 B start func1 LDR r0, =start LDR r1, =Darea +12 LDR r2, =Darea + 6000 MOV pc,lr ;返回 LTORG ; 子程序结束,声明文字池 func2 LDR r3, =Darea +60 LDR r4, =Darea +6004 MOV pc, lr Darea SPACE 8000 END
- 伪指令操作示例2——实现分支跳转以及方法顺序执行
AREA example,CODE,READONLY ;伪指令操作示例2 ENTRY CODE32 START CMP R0,#8 ;R0的初值为0 ADDLT PC,PC,R0,LSL#2 B method_d B method_0 B method_1 B method_2 B method_3 B method_4 B method_5 B method_6 B method_7 method_0 MOV R0,#1 ;这里添加实现method_0的代码 B end0 method_1 MOV R0,#2 ;这里添加实现method_1的代码 B end0 …… method_7 MOV R0,#8 ;这里添加实现method_7的代码 B end0 method_d MOV R0,#0 ;因流水线的原因不可能到达 end0 B START END
- ARM汇编程序设计模板
DataMem EQU 0x40000000 GBLA Str1 Str1 SETS "AAAA" EXPORT AREA example,CODE,READONLY IMPORT ;定义代码段,一个ARM汇编程序至少有一个代码段 ENTRY ;定义程序入口点 start …… LTORG ;定义数据缓冲池 StrMem DCB 0 ;分配内存单元 ALIGN 4 Temp SPACE 100 ;分配100字节连续的字节单元 END ;汇编语言程序结束
- 编写一个分支程序段,如果寄存器 R5 中的数据等于 10,就把 R5 中的数据存入寄存器 R1;否则把 R5 中的数据分别存入寄存器 R0 和 R1。流程图为:
- 编写一个程序段,当寄存器R1中的数据大于R2中数据时,将R2中的数据加10存入寄存器 R1;否则将R2中数据加 5 存入寄存器 R1。流程图为:
- 编写一个程序段,判断寄存器R1中数据是否为10、15、12、22。如果是,则将R0中的数据加1;否则将R0设置为 0XF。流程图如下:
- 下面是一个汇编代码的函数,它引用了一个在其他文件中定义的全局变量globvar,将其加 2 后写回 globvar
AREA globals, CODE, READONLY EXPORT asmsubrouttine IMPORT globvar ;引入全局变量 asmsubrouttine LDR R1,=globvar LDR R0,[R1] ADD R0,R0,#2 STR R0,[R1] MOV PC,LR END
- C程序可调用汇编函数实例
- 本汇编程序把R1指向的数据块复制到R0指向的存储块
在 C 语言文件中,调用 strcopy 函数的方法如下:
- 汇编程序调用C函数实例
- 有C程序
现需要在汇编程序中实现功能:int f(int i) {return –g(i, 2*i, 3*i, 4*i,5*i)}
汇编代码如下:
- 内联汇编——使能IRQ
void enable_IRQ(void) { int tmp; _ _asm //声名内联汇编代码 { MRS tmp, CPSR BIC tmp, tmp, #0x80 MSR CPSR_c, tmp } }
- 内联汇编——获取模式
void Read_mode(void) { int tmp, RR; RR = 0xFFFFFFE0; __asm //声名内联汇编代码 { MRS tmp, CPSR BIC tmp, tmp, RR } }
- 嵌入式汇编——加法函数
_ _asm int add(int i, int j) { ADD R0, R0, R1 MOV PC, LR }
需要注意的是返回的时候需要手动返回
- 中断服务程序的编写——方法一
- 汇编程序部分
C语言部分:
- 中断服务程序宏定义方法二,除reset中断外的其余6种中断都可以使用该宏定义的程序段进入。中断向量表的定义与方法一相同。唯一的区别应该就是中断服务程序如何编写,以及IRQ中断如何绑定
- 在方法1中,直接将二级分发程序写在IRQHandler下,而在方法二中是在ResetHandler中直接将IRQHandler标号的值替换为二级分发程序的值
使用这种方法同样需要将自己编写的中断服务程序在C语言中在二级中断向量表中注册
后续的在C程序中的处理方式和方法一 一样
一些疑问
- 关于通用寄存器
- 通用寄存器:0-15
- 一般通用寄存器:0-14
- 完全通用寄存器:0-13
一些例题
- 思考:在调试阶段如果进入了中止异常模式,该如何找出问题所在呢?
- 可以通过中止模式下的R14找到进入异常前的程序位置。如果意外地发生了未定义异常,可以采取类似的处理方法。
- 假设R0=0x1,R3=0x3,指令执行之前CPSR部分标志位为 nZcv,分别执行如下指令CPSR的值有何变化?
SUB R1,R0,R3 ;R0的值减去R3的值,结果存入R1 SUBS R1,R0,R3 ;R0的值减去R3的值,结果存入R1影响标志位。
分析:执行第1条指令对于标志寄存器的值没有任何影响,因此CPSR的值不变。执行第2条指令后CPSR=Nzcv
- 分别执行下面两条指令有何区别?
LDR R3,[R0,#4] LDR R3,[R0,#4]!
分析:在上述指令中,第1条指令没有后缀!,指令的结果是把R0加4作为地址指针,把这个指针所指向的地址单元所存储的数据读入R3,R0的值不变。第2条指令除了实现以上操作外,还把R0+4的结果送到R0中。
- 下面三条指令有何区别?
ADD R4,R3,#1 ADDEQ R4,R3,#1 ADDS R4,R3,#1
分析:第一条指令没有特殊点,第二条指令当Z=1时执行,第三条指令执行结束后将更新CPSR
- 下列命令中,汇编器把立即数转换为移位操作:
MOV R0,#0x104 ; uses 0x41 ror 30 ADD R1,R2,#0xFF0000 ; uses 0xFF ror 16
- 带有立即数的MOV 指令的二进制编码为:
MOV R0,#0xF200 ;E3A00CF2. 0xF200 =0xF2 ROR(2*C) MOV R1,#0x110000 ;E3A01811. 0x110000 =0x11 ROR(2*8) MOV R4,#0x12800 ;E3A04B4A. 0x12800 =0x4A ROR(2*B)
- CMP指令与SUBS指令的区别?
- CMP指令不保存计算结果,而SUBS指令会保存运算结果
- 已知(R0)=0X00000000和(R1)=0X00009000,并已知在存储器中首地址为 0X00009000 的区域中存放了数据0X01010101,在首地址为 0X00009004 的区域存放了数据 0X02020202。试写出执行了指令 LDR R0,[R1,#4] 后 R0 和 R1 中的数据。
- 解答:(R0)= 0X02020202 (R1)= 0X00009000
- 条件如上题,试写出执行了指令 LDR R0,[R1,#4]!后的 R0 和 R1 中的数据。
- 解答:(R0)=0X02020202 (R1)=0X00009004
- 条件如上题,试写出执行了指令 LDR R0,[R1],#4 后 R0 和 R1 中的数据
- 解答:(R0)=0X01010101 (R1)=0X00009004
- 解释下列指令
LDR R1,[R0,#0X10] ;将R0+16地址处的数据读出,保存到R1中(R0的值不变) LDR R1,[R0,R2,LSL #02] ;将R0+R2*4地址的数据读出,保存到R1中(R0、R2的值不变) LDR Rd,label ;label为程序标号, label必须是当前指令-4~4KB范围内(LDR指令的12位数)
- 数据在存储器中的存储情况如下图所示。现已知作为基地址寄存器 R0 的内容为 0X00000018 ,试分析如下指令执行后的结果。
解:(R1)=0X00000004、(R2)=0X00000005、(R3)=0X00000006、(R0)=0X00000024
解:(R1)=0X00000005、(R2)=0X00000006、(R3)=0X00000007、(R0)=0X00000024
解:(R3)=0X00000004、(R2)=0X00000003、(R1)=0X00000002、(R0)=0X0000000C
解:(R3)=0X00000003、(R2)=0X00000002、(R1)=0X00000001、(R0)=0X0000000C
- 假设R0和R1存放一个64位数据,R0中存放数据的低32位;R2和R3中存放另一个64位数据,R2中存放数据的低32位。对这两个64位数进行加、减和比较运算。(R1,R0)、(R3,R2)
- 两个64位数据的加法运算,结果保存到R0和R1中
ADDS R0,R0,R2 ;低32位相加,设置CPSR的C标志位。 ADC R1,R1,R3 ;高32位的带位相加
- 两个64位数据的减法运算,结果保存到R0和R1中。
SUBS R0,R0,R2 ;低32位相减,设置CPSR的C标志位。 SBC R1,R1,R3 ;高32位的带位相减
- 两个64位数据的比较操作,并设置CPSR中的条件标志位。
CMP R1,R3 ;比较高32位 CMPEQ R0,R2 ;如果高32位相等,比较低32位
- 已知某定义的宏,使用语句HandlerFIQ HANDLER HandleFIQ调用该宏,在程序预编译后,该宏调用语句将展开为以下程序段:
HandlerFIQ sub sp,sp,#4 stmfd sp!,{r0} ldr r0,=HandleFIQ ldr r0,[r0] str r0,[sp,#4] ldmfd sp!,{r0,pc}
请写出该宏的定义程序段
该题答案见一些重点程序部分的宏定义示例
- 写出下面代码的指令执行结果(R3-R6)
AREA blockcopy,CODE,READONLY ENTRY LDR R1,=ftt LDR R2,=ftt2 LDR R3,[R1] LDR R4,[R2] LDR R5,[R1, #4] LDR R6,[R2, #4] Src DCD 1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4 StrMem DCB “ABCS”,0 MAP Src ftt FIELD 8 ftt2 FIELD 8 END
程序段执行后,R3=1 ,R4=3 ,R5=2 ,R6=4
- 写出下面代码的指令执行结果(R1、R4)
AREA blockcopy,CODE,READONLY ENTRY ARM LDR R0,=ss1 LDR R1,=ss2 LDR R4,[R0] ALIGN 4 ss1 DCB 1 ALIGN 4,3 ss2 DCB 3 END
该段程序执行完成后,假如R0的值为0x10001000,则R1=0x10001003 ,R4=0x03000001
- 写出下面代码的指令执行结果(Str3、Str4)
GBLL VAR GBLS Str1 GBLS Str2 GBLS Str3 GBLS Str4 Str1 SETS "AAAA" Str2 SETS "BCDEFG" llll SETL {False} Str3 SETS Str1:CC:(Str2:LEFT:3):CC::STR:VAR Str4 SETS Str1:CC:Str2:LEFT:3:CC::STR:VAR
Str3对应的字符串为:AAAABCDF
Str4对应的字符串为:AAAF
- 中断信号流向例题——以INTTIMER0, INTTIMER2和INT_UART0为例。首先我们得做几个假设:
- 假设1:这三个中断的屏蔽被取消。
- 假设2:PRIORITY寄存器中ARBMODE2,ARBMODE5皆为0,既不进行优先级的自动旋转排序,任何时候ARBITER2,ARBITER5控制的中断组优先级次序分别为0-1-2-3-4-5和1-2-3-4。(即ARBSEL2和ARBSEL5、ARB_SEL6皆为00)
- 假设3:这三个中断皆为IRQ类型。
- 假设4:这三个中断同时被触发。
- 处理过程如下:
- SRCPND:INTTIMER0、NTTIMER2和INT_UART0三个中断被同时触发,此时三个中断信号流向SRCPND寄存器,使该寄存器中的第10位,12位,28位被置为1。
- 信号进一步流经INTMODE寄存器,这三个中断皆为IRQ类型(所以快速中断不会被INTMASK屏蔽)
- 中断信号继续向前流经INTMASK寄存器,这三个中断都没有被屏蔽,于是中断信号继续向前流向PRIORITY寄存器。
- INTPND和INTOFFSET:经过优先级判断,INTTIMER0中断信号使INTPND寄存器的第10位置1(INTTIMER0优先级最高),此时INTOFFSET寄存器的值为10。
- CPU转向相应的中断服务例程进行处理。
- 中断服务器程序中,我们的程序将INTPND和SRCPND的第10清0,然后进行中断服务程序的处理,直到INT_TIMER0中断处理完毕。
- 此时SRCPND的第12位,28位仍为1(这两个中断请求未被处理),故他们会继续被CPU以刚才描述的方式进行处理。
- 中断处理过程
- 在一个采用S3C2440作为处理器的嵌入式系统中,假设中断INTTIMER0、INTTIMER3、INT_SPI1、EINT0这四个中断同时产生,这四个中断产生之前,CPSR寄存器的低8位的值为0x1F。相关寄存器的值如下:
- 当INTTIMER0、INTTIMER3、INT_SPI1、EINT0这四个中断同时产生后,SRCPND寄存器中的值(使用32位二进制来表示)为:0010 0000 0000 0000 0010 0100 0000 0001
- INTPND寄存器中的值(使用32位二进制来表示)为:0000 0000 0000 0000 0000 0100 0000 0000
- 此时INTOFFSET寄存器的值为10 (使用10进制数表示)。
- CPU将进行INT_TIMER0中断的中断服务例程进行处理(从INTTIMER0、INTTIMER3、INT_SPI1、EINT0中选填一个)。
- 当第一个中断服务程序结束后,SRCPND寄存器中的值(使用32位二进制来表示)为:0010 0000 0000 0000 0010 0000 0000 0001
- INTPND寄存器中的值(使用32位二进制来表示)为:0000 0000 0000 0000 0010 0000 0000 0000
- 如果程序要屏蔽INT_TIMER0中断,请写出相关程序代码(注:只需写出相关指令代码,不需要写出完整的汇编程序结构)。
LDR R1,=0x4A000008 LDR R2,[R1] ORR R2, R2, #0x400 STR R2,[R1]
试回答以下问题:
- 作者:Noah
- 链接:https://imnoah.top/article/ARMReview/QuickSe
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。