ARM汇编学习笔记

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

这两天参加了一个编写操作系统的项目,因为要做很多底层的东西,而且这个操作系统是嵌入式的,所以开始学习ARM汇编,发现ARM汇编和一般PC平台上的汇编有很多不同,但主要还是关键字和伪码上的,其编程思想还是相同的。

现将一些学习感悟部分列出来,希望能给有问题的人一点帮助。

1、ARM汇编的格式:
在ARM汇编里,有些字符是用来标记行号的,这些字符要求顶格写;有些伪码是需要成对出现的,例如ENTRY和END,就需要对齐出现,也就是说他们要么都顶格,要么都空相等的空,否则编译器将报错。

常量定义需要顶格书写,不然,编译器同样会报错。

2、字符串变量的值是一系列的字符,并且使用双引号作为分界符,如果要在字符串中使用双引号,则必须连续使用两个双引号。

3、在使用LDR时,当格式是LDR r0,=0x022248,则第二个参数表示地址,即0x022248,同样的,当src变量代表一个数组时,需要将r0寄存器指向src 则需要这样赋值:LDR r0,=src 当格式是LDR r0,[r2],则第二个参数表示寄存器,我的理解是[]符号表示取内容,r2本身表示一个寄存器地址,取内容候将其存取r0这个寄存器中。

4、在语句:
CMP r0,#num
BHS stop
书上意思是:如果r0寄存器中的值比num大的话,程序就跳转到stop标记的行。

但是,实际测试的时候,我发现如果r0和num相等也能跳转到stop 标记的行,也就是说只要r0小于num才不会跳转。

下面就两个具体的例子谈谈ARM汇编(这是我昨天好不容易看懂的,呵呵)。

第一个是使用跳转表解决分支转移问题的例程,源代码如下(保存的时候请将文件后缀名改为s):
AREA JumpTest,CODE,READONLY
CODE32
num EQU 4
ENTRY
start
MOV r0, #4
MOV r1, #3
MOV r2, #2
MOV r3, #0
CMP r0, #num
BHS stop
ADR r4, JumpTable
CMP r0, #2
MOVEQ r3, #0
LDREQ pc, [r4,r3,LSL #2]
CMP r0, #3
MOVEQ r3, #1
LDREQ pc, [r4,r3,LSL #2]
CMP r0, #4
MOVEQ r3, #2
LDREQ pc, [r4,r3,LSL #2]
CMP r0, #1
MOVEQ r3, #3
LDREQ pc, [r4,r3,LSL #2]
DEFAULT
MOVEQ r0, #0
SWITCHEND
stop
MOV r0, #0x18
LDR r1, =0x20026
SWI 0x123456
JumpTable
DCD CASE1
DCD CASE2
DCD CASE3
DCD CASE4
DCD DEFAULT
CASE1
ADD r0, r1, r2
B SWITCHEND
CASE2
SUB r0, r1, r2
B SWITCHEND
CASE3
ORR r0, r1, r2
B SWITCHEND
CASE4
AND r0, r1, r2
B SWITCHEND
END
程序其实很简单,可见我有多愚笨!还是简要介绍一下这段代码吧。

首先用AREA伪代码加上CODE,表明下面引出的将是一个代码段(于此相对的还有数据段DATA),ENTRY 和END成对出现,说明他们之间的代码是程序的主体。

start段给寄存器初始化。

ADR r4, JumpTable一句是将相当于数组的Ju mpTable的地址付给r4这个寄存器。

stop一段是用来是程序退出的,第一个语句―MOV r0,#0x18‖将r0赋值为0 x18,这个立即数对应于宏angel_SWIreason_ReportException。

表示r1中存放的执行状态。

语句―LDR r1,=0x20026‖将r1的值设置成ADP_Stopped_App licationExit,该宏表示程序正常退出。

然后使用SWI,语句―SWI 0x123456‖结束程序,将CPU的控制权交回调试器手中。

在JumpTable表中,DCD类型的数组包含四个字,所以,当实现CASE跳转的时候,需要将给出的索引乘上4,才是真正前进的地址数。

再看一个用汇编实现冒泡排序的例程:
AREA Sort,CODE,READONLY
ENTRY
start
MOV r4,#0
LDR r6,=src
ADD r6,r6,#len
outer
LDR r1,=src
inner
LDR r2,[r1]
LDR r3,[r1,#4]
CMP r2,r3
STRGT r3,[r1]
STRGT r2,[r1,#4]
ADD r1,r1,#4
CMP r1,r6
BLT inner
ADD r4,r4,#4
CMP r4,#len
SUBLE r6,r6,#4
BLE outer
stop
MOV r0,#0x18
LDR r1,=0x20026
SWI 0x123456
AREA Array,DATA,READWRITE
src DCD 2,4,10,8,14,1,20
len EQU 7*4
END
用汇编实现循环需要跳转指令,但是因为ARM系统只有一个CPSR寄存器,所以要实现双重循环还是有些难度。

上面这个代码还是有相当大的借鉴意义。

程序不难读懂,和C语言的冒泡排序基本思路是完全一样的。

Load CodeWarrior from the Start Menu.
Create a new project (File | New), select ARM Executable Image and gi ve it the name "hello".
Create a new assembler source file (File | New Text File) and paste the following code in it.
; Hello world in ARM assembler
AREA text, CODE
; This section is called "text", and contains code
ENTRY
; Print "Hello world"
; Get the offset to the string in r4.
adr r4, hello ;; "address in register"
loop ; "loop" is a label and designates an address
; Call putchar to display each character
; to illustrate how a loop works
ldrb r0, [r4], #1 ; Get next byte and post-index r4
cmp r0, #0 ; Stop when we hit a null
beq outputstring ;; "branch if equal" = cond. goto
bl putchar
b loop ;; "branch" = goto
outputstring
; Alternatively, use putstring to write out the
; whole string in one go
adr r0, hello
bl putstring ;; "branch+link" = subroutine call finish
; Standard exit code: SWI 0x123456, calling routine 0x18
; with argument 0x20026
mov r0, #0x18
mov r1, #0x20000 ; build the "difficult" number...
add r1, r1, #0x26 ; ...in two steps
SWI 0x123456 ;; "software interrupt" = sys call hello
DCB "Hello World\n",0
END
从下面的一个ARM 汇编小程序要弄懂的以下三个问题:
1).在ARM状态转到THUNB状态和BX的应用
2).汇编的架构
3).SWI指令的使用
AREA ADDREG,CODE,READONLY
ENTRY
MAIN
ADR r0,ThunbProg + 1 ;(为什么要加1呢?因为BX指令跳转到指定的地址执行程序时, 若(BX{cond} Rm)Rm的位[0]为1,则跳转时自动将CPSR中的标志T置位即把目标代码解释为Thunb代码)
BX r0
CODE16
ThunbProg
mov r2,#2
mov r3,#3
add r2,r2,r3
ADR r0,ARMProg
BX ro
CODE32
ARMProg
mov r4,#4
mov r5,#5
add r4,r4,r5
stop mov r0,#0x18
LDR r1,=0x20026
SWI 0x123456
END
SWI--软中断指令:
SWI指令用于产生软中断,从拥护模式变换到管理模式,CPSR保存到管理模式的SPSR中.
SWI{cond} immed_24 ;immed_24为软中断号(服务类型)
使用SWI指令时,通常使用以下两种方法进行传递参数,SWI 异常中断处理程序就可以提供相关的服务,这两种方法均是用户软件协定.SWI异常中断处理程序要通过读取引起软中断的SWI指令,以取得24位立即数.
(1)指令中的24位立即数指定了用户请求的服务类型,参数通过通用寄存器传递.
mov r0,#34 ;设置子功能号位34
SWI 12 ;调用12号软中断
(2)指令中的24位立即数被忽略,用户请求的服务类型有寄存器RO的值决定,参数通过其他的通用寄存器传递.
mov r0,#12 ;调用12号软中断
mov r1,#34 ;设置子功能号位34
SWI0
在SWI异常中断处理程序中,取出SWI立即数的步骤为:首先确定引起软中断的SWI指令是ARM指令还是Thunb指令,这可通过对SPSR访问得到;然后取得该SWI指令的地址,这可通过访问LR寄存器得到;接着读出指令,分解出立即数.如如下程序:
T_bit EQU 0X20
SWI_Handler
STMFD SP!,{R0-R3,R12,LR} ;现场保护
MRS R0,SPSR ;读取SPSR
STMFD SP!,{R0} :保存SPSR
TST R0,#T_bit
LDRNEH R0,[LR,#-2] ;若是Thunb指令,读取指令码(16位)
BICNE R0,#0XFF00 :取得Thunb指令的8位立即数
LDREQ R0,[LR,#-4] ;若是ARM指令,读取指令码(32位)
BICEQ R0,#0XFF000000 ;取得ARM指令的24位立即数
....
LDMFD SP!,{R0-R3,R12,PC}^ ;SWI异常中断返回
ARM汇编的SWI指令软中断
从下面的一个ARM 汇编小程序要弄懂的以下三个问题:
1).在ARM状态转到THUNB状态和BX的应用
2).汇编的架构
3).SWI指令的使用
AREA ADDREG,CODE,READONLY
ENTRY
MAIN
ADR r0,ThunbProg + 1 ;(为什么要加1呢?因为BX指令跳转到指定的地址执行程序时, 若(BX{cond} Rm)Rm的位[0]为1,则跳转时自动将CPSR中的标志T置位即把目标代码解释为Thunb代码)
BX r0
CODE16
ThunbProg
mov r2,#2
mov r3,#3
add r2,r2,r3
ADR r0,ARMProg
BX ro
CODE32
ARMProg
mov r4,#4
mov r5,#5
add r4,r4,r5
stop mov r0,#0x18
LDR r1,=0x20026
SWI 0x123456
END
SWI--软中断指令:
SWI指令用于产生软中断,从拥护模式变换到管理模式,CPSR保存到管理模式的SPSR中.
SWI{cond} immed_24 ;immed_24为软中断号(服务类型)
使用SWI指令时,通常使用以下两种方法进行传递参数,SWI 异常中断处理程序就可以提供相关的服务,这两种方法均是用户软件协定.SWI异常中断处理程序要通过读取引起软中断的SWI指令,以取得24位立即数.
(1)指令中的24位立即数指定了用户请求的服务类型,参数通过通用寄存器传递.
mov r0,#34 ;设置子功能号位34
SWI 12 ;调用12号软中断
(2)指令中的24位立即数被忽略,用户请求的服务类型有寄存器RO的值决定,参数通过其他的通用寄存器传递.
mov r0,#12 ;调用12号软中断
mov r1,#34 ;设置子功能号位34
SWI0
在SWI异常中断处理程序中,取出SWI立即数的步骤为:首先确定引起软中断的SWI指令是ARM指令还是Thunb指令,这可通过对SPSR访问得到;然后取得该SWI指令的地址,这可通过访问LR寄存器得到;接着读出指令,分解出立即数.如如下程序:
T_bit EQU 0X20
SWI_Handler
STMFD SP!,{R0-R3,R12,LR} ;现场保护
MRS R0,SPSR ;读取SPSR
STMFD SP!,{R0} :保存SPSR
TST R0,#T_bit
LDRNEH R0,[LR,#-2] ;若是Thunb指令,读取指令码(16)
BICNE R0,#0XFF00 :取得Thunb指令的8位立即数LDREQ R0,[LR,#-4] ;若是ARM指令,读取指令码(32位)
BICEQ R0,#0XFF000000 ;取得ARM指令的24位立即数
....
LDMFD SP!,{R0-R3,R12,PC}^ ;SWI异常中断返回
Thu Oct 12 2006
软件中断SWI的实现
在需要软件中断处调用
__SWI 0xNum ;Num为SWI中断处理模块的编号,见表SwiFunction
;软件中断
SoftwareInterrupt
CMP R0, #12 ;R0中的SWI编号是否大于最大值
/* 下面这句语句把(LDRLO地址+ 8 + R0*4) 的地址装载到PC寄存器,举例如果上面的Num="1",也就是R0 = 1, 假设LDRLO这条指令的地址是0x000 08000,那么根据ARM体系的2级流水线PC寄存器里指向是下两条指令于是PC =0x00008008 也就是伪指令DCD TASK_SW 声明的标号TASK_S W 的地址,注意DCD TASK_SW 这条指令本身不是ARM能执行的指令,也不会占有地址,这条指令靠汇编器汇编成可执行代码,它的意义就是声明TA SK_SW的地址,, [PC, R0, LSL #2] 这个寻址方式就是PC + R0的值左移2位的值( 0x01<<2 => 0x04 ),这样PC的值就是0x0000800C, 即ENTER_ CRITICAL的地址于是ARM执行该标号下的任务*/
LDRLO PC, [PC, R0, LSL #2]
MOVS PC, LR
SwiFunction
DCD TASK_SW ;0
DCD ENTER_CRITICAL ;1
DCD EXIT_CRITICAL ;2
DCD ISRBegin ;3
DCD ChangeToSYSMode ;4
DCD ChangeToUSRMode ;5
DCD __OSStartHighRdy ;6
DCD TaskIsARM ;7
DCD TaskIsTHUMB ;8
DCD OSISRNeedSwap ;9
DCD GetOSFunctionAddr ;10
DCD GetUsrFunctionAddr ;11
TASK_SW
MRS R3, SPSR ;保存任务的CPSR
MOV R2, LR ;保存任务的PC
MSR CPSR_c, #(NoInt | SYS32Mode) ;切换到系统模式
STMFD SP!, {R2} ;保存PC到堆栈
STMFD SP!, {R0-R12, LR} ;保存R0-R12,LR到堆栈
;因为R0~R3没有保存有用数据,所以可以这样做
B OSIntCtxSw_0 ;真正进行任务切换
ENTER_CRITICAL
;OsEnterSum++
LDR R1, =OsEnterSum
LDRB R2, [R1]
ADD R2, R2, #1
STRB R2, [R1]
;关中断
MRS R0, SPSR
ORR R0, R0, #NoInt
MSR SPSR_c, R0
MOVS PC, LR
批量数据加载/存储指令实验2007-08-22 12:08:06
大中小
标签:arm指令ldm/stm
这个程序用批量传输指令传输数据,一次可传8个字:
AREA Block, CODE, READONLY ; name this block of code num EQU 20 ; Set number of words to be copied ENTRY ; mark the first instruction to call
start
LDR r0, =src ; r0 = pointer to source block
LDR r1, =dst ; r1 = pointer to destination block
MOV r2, #num ; r2 = number of words to copy
MOV sp, #0x400 ; set up stack pointer (r13)
blockcopy
MOVS r3,r2, LSR #3 ; number of eight word multiples
BEQ copywords ; less than eight words to move ?
STMFD sp!, {r4-r11} ; save some working registers
octcopy
LDMIA r0!, {r4-r11} ; load 8 words from the source
STMIA r1!, {r4-r11} ; and put them at the destination
SUBS r3, r3, #1 ; decrement the counter
BNE octcopy ; ... copy more
LDMFD sp!, {r4-r11} ; dont need these now - restore originals copywords
ANDS r2, r2, #7 ; number of odd words to copy
BEQ stop ; No words left to copy ?
wordcopy
LDR r3, [r0], #4 ; a word from the source
STR r3, [r1], #4 ; store a word to the destination
SUBS r2, r2, #1 ; decrement the counter
BNE wordcopy ; ... copy more
stop
MOV r0, #0x18 ; angel_SWIreason_ReportException
LDR r1, =0x20026 ; ADP_Stopped_ApplicationExit
SWI 0x123456 ; ARM semihosting SWI
下面的这个程序实现同样的功能,每次只能传一个字:
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
AREA Word, CODE, READONLY ; name this block of code num EQU 20 ; Set number of words to be copied ENTRY ; mark the first instruction to call
start
LDR r0, =src ; r0 = pointer to source block
LDR r1, =dst ; r1 = pointer to destination block
MOV r2, #num ; r2 = number of words to copy
wordcopy
LDR r3, [r0], #4 ; a word from the source
STR r3, [r1], #4 ; store a word to the destination
SUBS r2, r2, #1 ; decrement the counter
BNE wordcopy ; ... copy more
stop
MOV r0, #0x18 ; angel_SWIreason_ReportException
LDR r1, =0x20026 ; ADP_Stopped_ApplicationExit
SWI 0x123456 ; ARM semihosting SWI
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
当处理器工作在ARM状态时,几乎所有的指令均根据CPSR中条件码的状态和指令的条件域有条件的执行。

当指令的执行条件满足时,指令被执行,否则指令被忽略。

每一条ARM指令包含4位的条件码,位于指令的最高4位[31:28]。

条件码共有16种,每种条件码可用两个字符表示,这两个字符可以添加在指令助记符的后面和指令同时使用。

例如,跳转指令B可以加上后缀EQ变为BEQ表示―相等则跳转‖,即当CPSR中的Z标志置位时发生跳转。

1、B指令
B指令的格式为:
B{条件} 目标地址
B指令是最简单的跳转指令。

一旦遇到一个B 指令,ARM 处理器将立即跳转到给定的目标地址,从那里继续执行。

注意存储在跳转指令中的实际值是相对当前PC值的一个偏移量,而不是一个绝对地址,它的值由汇编器来计算(参考寻址方式中的相对寻址)。

它是24 位有符号数,左移两位后有符号扩展为32 位,表示的有效偏移为26 位(前后32MB的地址空间)。

以下指令:
B Label ;程序无条件跳转到标号Label处执行
CMP R1,#0 ;当CPSR寄存器中的Z条件码置位时,程序跳转到标号Lab el处执行
BEQ Label
3.3.6 批量数据加载/存储指令
ARM微处理器所支持批量数据加载/存储指令可以一次在一片连续的存储器单
元和多个寄存器之间传送数据,批量加载指令用于将一片连续的存储器中的数据传送到多个寄存器,批量数据存储指令则完成相反的操作。

常用的加载存储指令如下:
—LDM 批量数据加载指令
—STM 批量数据存储指令
LDM(或STM)指令
LDM(或STM)指令的格式为:
LDM(或STM){条件}{类型} 基址寄存器{!},寄存器列表{∧}
LDM(或STM)指令用于从由基址寄存器所指示的一片连续存储器到寄存器列表所指示的多个寄存器之间传送数据,该指令的常见用途是将多个寄存器的内容入栈或出栈。

其中,{类型}为以下几种情况:
IA 每次传送后地址加1;
IB 每次传送前地址加1;
DA 每次传送后地址减1;
DB 每次传送前地址减1;
FD 满递减堆栈;
ED 空递减堆栈;
FA 满递增堆栈;
EA 空递增堆栈;
{!}为可选后缀,若选用该后缀,则当数据传送完毕之后,将最后的地址写入基址寄存器,否则基址寄存器的内容不改变。

基址寄存器不允许为R15,寄存器列表可以为R0~R15的任意组合。

{∧}为可选后缀,当指令为LDM且寄存器列表中包含R15,选用该后缀时表示:除了正常的数据传送之外,还将SPSR复制到CPSR。

同时,该后缀还表示传
入或传出的是用户模式下的寄存器,而不是当前模式下的寄存器。

指令示例:
STMFD R13!,{R0,R4-R12,LR} ;将寄存器列表中的寄存器(R0,R4到R 12,LR)存入堆栈。

LDMFD R13!,{R0,R4-R12,PC} ;将堆栈内容恢复到寄存器(R0,R4到R 12,LR)。

ARM汇编的SWI指令软中断[转贴2007-05-25 11:21:49]
从下面的一个ARM 汇编小程序要弄懂的以下三个问题:
1).在ARM状态转到THUNB状态和BX的应用
2).汇编的架构
3).SWI指令的使用
AREA ADDREG,CODE,READONLY
ENTRY
MAIN
ADR r0,ThunbProg 1 ;(为什么要加1呢?因为BX指令跳转到指定的地址执行程序时, 若(BX{cond} Rm)Rm的位[0]为1,则跳转时自动将CP SR中的标志T置位即把目标代码解释为Thunb代码)
BX r0
CODE16
ThunbProg
mov r2,#2
mov r3,#3
add r2,r2,r3
ADR r0,ARMProg
BX ro
CODE32
ARMProg
mov r4,#4
mov r5,#5
add r4,r4,r5
stop mov r0,#0x18
LDR r1,=0x20026
SWI 0x123456
END
SWI--软中断指令:
SWI指令用于产生软中断,从拥护模式变换到管理模式,CPSR保存到管理模式的SPSR中.
SWI{cond} immed_24 ;immed_24为软中断号(服务类型)
使用SWI指令时,通常使用以下两种方法进行传递参数,SWI 异常中断处理程序就可以提供相关的服务,这两种方法均是用户软件协定.SWI异常中断处理程序要通过读取引起软中断的SWI指令,以取得24位立即数.
(1)指令中的24位立即数指定了用户请求的服务类型,参数通过通用寄存器传递.
mov r0,#34 ;设置子功能号位34
SWI 12 ;调用12号软中断
(2)指令中的24位立即数被忽略,用户请求的服务类型有寄存器R0的值决定,参数通过其他的通用寄存器传递.
mov r0,#12 ;调用12号软中断
mov r1,#34 ;设置子功能号位34
SWI0
在SWI异常中断处理程序中,取出SWI立即数的步骤为:首先确定引起软中断的SWI指令是ARM指令还是Thunb指令,这可通过对SPSR访问得到;然后取得该SWI指令的地址,这可通过访问LR寄存器得到;接着读出指令,分解出立即数.如如下程序:
T_bit EQU 0X20
SWI_Handler
STMFD SP!,{R0-R3,R12,LR} ;现场保护
MRS R0,SPSR ;读取SPSR
STMFD SP!,{R0} :保存SPSR
TST R0,#T_bit
LDRNEH R0,[LR,#-2] ;若是Thunb指令,读取指令码(16位)
BICNE R0,#0XFF00 :取得Thunb指令的8位立即数
LDREQ R0,[LR,#-4] ;若是ARM指令,读取指令码(32位)
BICEQ R0,#0XFF000000 ;取得ARM指令的24位立即数
....
LDMFD SP!,{R0-R3,R12,PC}^ ;SWI异常中断返回
基于s3c2410软中断服务的uC/OS-II任务切换
1.关于软中断指令
软件中断指令(SWI)可以产生一个软件中断异常,这为应用程序调用系统例程提供了一种机制。

语法:
SWI {<cond>} SWI_number
SWI执行后的寄存器变化:
lr_svc = SWI指令后面的指令地址
spsr_svc = cpsr
pc = vectors + 0x08
cpsr模式= SVC
cpsr I = 1(屏蔽IRQ中断)
处理器执行SWI指令时,设置程序计数器pc为向量表的0x08偏移处,同事强制切换处理器模式到SVC模式,以便操作系统例程可以在特权模式下被调用。

每个SWI指令有一个关联的SWI号(number),用于表示一个特定的功能调用或特性。

【例子】一个ARM工具箱中用于调试SWI的例子,是一个SWI号为0x1234 56的SWI调用。

通常SWI指令是在用户模式下执行的。

SWI执行前:
cpsr = nzcVqift_USER
pc = 0x00008000
lr = 0x003fffff ;lr = 4
r0 = 0x12
执行指令:
0x00008000 SWI 0x123456
SWI执行后:
cpsr = nzcVqIft_SVC
spsr = nzcVqift_USER
pc = 0x00000008
lr = 0x00008004
r0 = 0x12
SWI用于调用操作系统的例程,通常需要传递一些参数,这可以通过寄存器来完成。

在上面的例子中,r0
用于传递参数0x12,返回值也通过寄存器来传递。

处理软件中断调用的代码段称为中断处理程序(SWI Handler)。

中断处理程序通过执行指令的地址获取软件中断号,指令地址是从lr计算出来的。

SWI号由下式决定:
SWI_number = <SWI instruction> AND NOT<0xff000000>
其中SWI instruction就是实际处理器执行的32位SWI指令
SWI指令编码为:
31 - 28 27 - 24 23 - 0
cond 1 1 1 1 immed24
指令的二进制代码的bit23-bit0是24bit的立即数,即SWI指令的中断号,通过屏蔽高8bit即可获得中断号。

lr寄存器保存的是中断返回指令的地址,所以[l r - 4] 就是执行SWI的执行代码。

通过load指令拷贝整个SWI指令到寄存器,使用BIC屏蔽指令的高8位,获取SWI中断号。

;read the SWI instruction
LDR r10, [lr, #-4]
BIC r10, r10, #0xff000000
2. 周立功移植uC/OS-II到s3c2410的软中断服务级的任务切换
uC/OS-II的任务调度函数
uC/OS-II的任务级的调度是由函数OS_Sched( )完成的。

void OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status regi ster */
OS_CPU_SR cpu_sr;
#endif
INT8U y;
OS_ENTER_CRITICAL();
if ((OSIntNesting == 0) && (OSLockNesting == 0)) { /* Sched. only if all ISRs done & not locked */
y = OSUnMapTbl[OSRdyGrp]; /* Get pointer to HPT ready to run */ OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
OSCtxSwCtr++; /* Increment context switch counter */
OS_TASK_SW(); /* Perform a context switch */
}
}
OS_EXIT_CRITICAL();
}
详细解释可以参考《嵌入式实时操作系统uC/OS-II》,os_sched函数在确定所有就绪任务的最高优先级高于当前任务优先级时进行任务切换,通过OS_TA SK_SW( )宏来调用。

OS_TASK_SW( )宏实际上定义的是SWI软中断指令。

见OS_CPU.H文件的代码:
__swi(0x00) void OS_TASK_SW(void); /* 任务级任务切换函数*/
__swi(0x01) void _OSStartHighRdy(void); /* 运行优先级最高的任务*/
__swi(0x02) void OS_ENTER_CRITICAL(void); /* 关中断*/
__swi(0x03) void OS_EXIT_CRITICAL(void); /* 开中断*/
__swi(0x40) void *GetOSFunctionAddr(int Index); /* 获取系统服务函数入口*/
__swi(0x41) void *GetUsrFunctionAddr(int Index);/* 获取自定义服务函数入口*/
__swi(0x42) void OSISRBegin(void); /* 中断开始处理*/
__swi(0x43) int OSISRNeedSwap(void); /* 判断中断是否需要切换*/
__swi(0x80) void ChangeToSYSMode(void); /* 任务切换到系统模式*/
__swi(0x81) void ChangeToUSRMode(void); /* 任务切换到用户模式*/
__swi(0x82) void TaskIsARM(INT8U prio); /* 任务代码是ARM代码*/
__swi(0x83) void TaskIsTHUMB(INT8U prio); /* 任务代码是THUMB */
__swi(0x00) void OS_TASK_SW(void); 是与ADS相关的代码,通过反汇编可以看到,调用OS_TASK_SW实际上被替换成swi 0x00 软中断指令。

执行此执行,pc会跳转到向量表的0x08偏移处。

中断向量表:(见Startup.s文件)
CODE32
AREA vectors,CODE,READONLY
; 异常向量表
Reset
LDR PC, ResetAddr
LDR PC, UndefinedAddr
LDR PC, SWI_Addr
LDR PC, PrefetchAddr
LDR PC, DataAbortAddr
DCD IRQ_Addr
LDR PC, IRQ_Addr
LDR PC, FIQ_Addr
ResetAddr DCD ResetInit
UndefinedAddr DCD Undefined
SWI_Addr DCD SoftwareInterrupt
PrefetchAddr DCD PrefetchAbort
DataAbortAddr DCD DataAbort
Nouse DCD 0
IRQ_Addr DCD IRQ_Handler
FIQ_Addr DCD FIQ_Handler
执行SWI 0x00指令后,pc会跳转到SoftwareInterrupt代码处开始执行:
见Os_cpu_a.s文件的SoftwareInterrupt函数:
SoftwareInterrupt
LDR SP, StackSvc ; 重新设置堆栈指针
STMFD {R0-R3, R12, LR}
MOV R1, SP ; R1指向参数存储位置
MRS R3, SPSR
TST R3, #T_bit ; 中断前是否是Thumb状态
LDRNEH R0, [LR,#-2] ; 是: 取得Thumb状态SWI指令
BICNE R0, R0, #0xff00
LDREQ R0, [LR,#-4] ; 否: 取得arm状态SWI指令
BICEQ R0, R0, #0xFF000000 ; 如上面所述,此处通过屏蔽SWI指令的高8位来获取SWI号,r0 = SWI号,R1指向参数存储位置
CMP R0, #1
LDRLO PC, =OSIntCtxSw ;为0时跳转到OSIntCtxSwdi地址处
LDREQ PC, =__OSStartHighRdy ; 为1时,跳转到__OSStartHighRdy 地址处。

SWI 0x01为第一次任务切换
BL SWI_Exception ;进入中断号散转函数
LDMFD {R0-R3, R12, PC}^
StackSvc DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)
以上就是任务切换软中断级服务的实现。

利用arm 組語的PRE-INDEX 與POST-INDEX ADDRESSING,上課時COD ING完成的範例--1+2+3+...+10之和分類:IT技術分享2008/01/30 17:17於今日97/01/30 上課時實作的程式範例,先貼上程式碼。

因是上課當場coding完成的,so沒有加上註解^^
範例一:使用POST-INDEX ADDRESSING實作的code
=======================================
AREA ASM6,CODE,READONLY
ENTRY
START
LDR R0,=ARR1
MOV R1,#0
LOOP
LDR R2,[R0],#4
ADD R1,R1,R2
CMP R2,#0
BNE LOOP
STOP
LDR R0,=0X18
LDR R1,=0X20026
SWI 0X123456
AREA ARR,DATA,READWRITE
ARR1 DCD 1,2,3,4,5,6,7,8,9,10,0
END
範例二:使用PRE-INDEX ADDRESSING實作的code
=======================================
AREA ASM8,CODE,READONLY
ENTRY
START
LDR R0,=ARR1
MOV R1,#0
MOV R3,#0
LOOP
LDR R2,[R0,R1,LSL #2]
ADD R1,R1,#1
ADD R3,R3,R2
CMP R2,#0
BNE LOOP
STOP
LDR R0,=0X18
LDR R1,=0X20026
SWI 0X123456
AREA ARR,DATA,READWRITE
ARR1 DCD 1,2,3,4,5,6,7,8,9,10,0
END
/bbs/redirect.php?tid=231&goto=lastpost
常用ARM指令
1、内存访问指令
基本指令:
LDR:memory -> register (memory包括映射到内存空间的非通用寄存器) STR:register -> memory
语法:
op{cond }{B}{T} Rd , [Rn ]
op{cond }{B} Rd , [Rn , FlexOffset ]{!}
op{cond }{B} Rd , label
op{cond }{B}{T} Rd , [Rn ], FlexOffset
op:基本指令,如LDR、STR
cond:条件执行后缀
B:字节操作后缀
T:用户指令后缀
Rd:源寄存器,对于LDR指令,Rd将保存从memory中读取的数值;对于S TR指令,Rd保存着将写入memory的数值
Rn:指针寄存器
FlexOffset:偏移量
例子:
ldr r0, [r1] ;r1作为指针,该指针指向的数存入r0
str r0, [r1, #4] ;r1+4作为指针,r0的值存入该地址
str r0, [r1, #4]! ;同上,并且r1 = r 1 + 4
ldr r1, =0x08100000 ;立即数0x08100000存到r1
ldr r1, [r2], #4 ;r2+4作为指针,指向的值存入r1,并且r2=r2+4
【label的使用】
addr1 ;定义一个名为―addr1‖的label,addr1 = 当前地址
dcd 0 ;在当前地址出定义一个32bit的变量
~~~
ldr r1, label1 ;r1 = addr1,r1即可以作为var1的指针
ldr r0, [r1]
add r0, r0, #1
str r0, [r1] ;变量var1的值加1 【FlexOffset的使用】
FlexOffset可以是立即数,也可以是寄存器,还可以是简单的表达式
2、多字节存取指令(常用于堆栈操作)
基本指令:
LDM:memory ――> 多个寄存器
STM:多个寄存器――> memory
语法:
op{cond }mode Rn{!}, reglist {^}
mode:指针更新模式,对应于不同类型的栈。

最常用的是―FD‖模式,相当于初始栈指针在高位,压栈后指针值减小。

Rn:指针寄存器
!:最后的指针值将写入Rn中
reglist:要操作的寄存器列表,如{r0-r8, r10}
^ :完成存取操作后从异常模式下返回
例子:
;异常处理程序:
sub lr, lr, #4 ; lr –4是异常处理完后应该返回的地方
;保存r0~r12和lr寄存器的值到堆栈并更新堆栈指针。

stmfd sp!, {r0-r12, lr}
;异常处理
ldmfd sp!, {r0-r12, pc}^ ;从堆栈中恢复r0~r12,返回地址赋给pc指针,使程序返回到异常发生前所执行的地方,^标记用来使C PU退出异常模式,进入普通状态。

3、算术运算指令
基本指令:
ADD:加
SUB:减
语法:
op{cond }{S} Rd, Rn, Operand2
S:是否设置状态寄存器(CPSR),如:N(有符号运算结果得负数)、Z(结果得0)、C(运算的进位或移位)、V(有符号数的溢出)等等。

Rd:保存结果的寄存器
Rn:运算的第一个操作数
Operand2:运算的第二个操作数,这个操作数的值有一些限定:如可以是8位立即数(例:0xa8)或一个8为立即数的移位(例:0xa800,而0xa801就不符合)。

也可以是寄存器,或寄存器的移位(如―r2,lsl #4‖)。

例子:
add r0, r1, r2 ; r0 = r1 + r2
adds r0, r1, #0x80 ; r0
= r1 + 0x80,并设置状态寄存器
subs r0, r1, #2000 ; r0 = r1 –2000,并设置状态寄存器
4、逻辑运算指令
基本指令:
AND:与
ORR:或
EOR:异或
BIC:位清0
语法:
op{cond }{S} Rd, Rn, Operand2
语法类似算术运算指令
例子:
ands r0,r1,#0xff00 ; r0 = r1 and 0xff00,并设置状态寄存器
orr r0, r1, r2 ; r0 = r1 and r2
bics r0, r1, #0xff00 ; r0 = r 1 and ! (0xff00)
ands r0, r1, #0xffff00ff ; 错误5、MOV指令
语法:
MOV{cond}{S} Rd, Operand2
例子:
mov r0, #8 ; r0 = 8 mov r0, r1 ; r0 = r1 不同于LDR、STR指令,该指令可以寄存器间赋值
6、比较指令
基本指令:
CMP:比较两个操作数,并设置状态寄存器
语法:
CMP{cond } Rn, Operand2
例子:
cmp r0, r1 ; 计算r0 –r1,并设置状态寄存器,由状态寄存器可以知r0是否大于、小于或等于r1 cmp r0, #0 ;
7、跳转指令
基本指令:
B:跳转
BL:跳转并将下一指令的地址存入lr寄存器
语法:
op{cond} label
label:要跳向的地址
例子:
loop1
~ ~ ~
b loop1 ; 跳到地址loop1处
bl sub1 ; 将下一指令地址写入lr,并跳至s ub1
~ ~ ~
sub1
~ ~ ~
mov pc, lr ; 从sub1中返回
【使用本地label(local label)】
本地label可以在一个程序段内多次使用,用数字作为label的名称,也可以在数字后面跟一些字母。

引用本地label的语法是:%{F|B}{A|T}n{routn ame},其中F代表向前搜索本地label,B代表向后搜索,A/T不常使用。

例子
100 ; 定义本地label,名称为―100‖
~ ~ ~
100 ; 第二次定义本地labe l,名称为―100‖
~ ~ ~
b %f100 ; 向前跳到最近的―100‖处
~ ~ ~
b %b100 ; 向后跳至最近的―100‖处
100 ; 第三次定义本地label 100
8、条件执行
条件:状态寄存器中某一或某几个比特的值代表条件,对应不同的条件后缀con d,如:
后缀(cond) 状态寄存器中的标记意义
EQ Z =
1 相等
NE Z =
0 不相等
GE N和V相同> =
LT N和V不
同<
GT Z = 0, 且N和V相同> LE Z = 1, 或N和V不同<=
例子:
cmp r0, r1 ;比较r0和r1
blgt sub1 ;如果r0>r1,跳转到sub1,否则不操作
;――――――――――――――――――――
;一段循环代码
ldr r2, =8 ;r2 = 8
loop
;这里可以进行一些循环内的操作
subs r2, r2, #1 ;r2 = r2 –1,并设置状态位
bne loop ;如果r2不等于0,则继续循环
;――――――――――――――――――――
mov r0, #1 ; r0 = 1
cmp r2, #8 ; 比较r2和8
movlt r0, #2 ; 如果r2<8,r0 = 2
ARM汇编程序结构
;――――――――――――――――――――
AREA EX2, CODE, READONLY
;AREA指令定义一个程序段,名称为EX2,属性为:CODE、READONLY INCLUDE Common.inc ;包含汇编头文件
IMPORT sub1 ;引用外部符号
EXPORT prog1 ;向外输出符号
ENTRY ;ENTRY指令定义程序的开始
start ;此处定义了一个label start
MOV r0, #10
MOV r1, #3
ADD r0, r0, r1 ;r0 =r0 +r1
prog1 ;此处定义了一个label pro g1
MOV r0, #0x18
LDR r1, =0x20026
SWI 0x123456
END ;END指令表示程序段的结束;――――――――――――――――――――
宏的使用
定义宏:
MACRO ;宏的起始
{label} macroname para1,para2……
;代码
MEND ;宏结束
引用宏:
marconame para1,para2……
例子
;定义一个宏,完成两个寄存器内容交换
MACRO
swap $w1, $w2, $w3
mov $w3, $w1
mov $w1, $w2
mov $w2, $w3
MEND
;使用这个宏
ldr r0, =1
ldr r1, =2
swap r0, r1, r2 ;此处调用了宏swap,运行完后r0、r1的值交换了
一般可以把宏写在宏文件(.mac文件)中,在程序里用INCLUDE指令包含宏文件最超值的ARM7/ARM9开发板系列
AVR单片机开发板与仿真器
本章节主要介绍ARM 处理器的基本程序设计方法,包含ARM 指令实验,Thu mb 指令实验和ARM 处理器工作模式实验。

4.1 ARM 指令实验
4.1.1 实验说明
实验目的:透过实验掌握ARM 组译指令的使用方法。

实验设备:硬件使用PC 主机,软件使用Embest IDE 2003 整合开发环境,Windows 98/2000/NT/XP。

实验内容:使用简单ARM 组译指令,操作寄存器和内存区作互相的数据交换。

4.1.2 实验原理
ARM 处理器共有37个寄存器:31个通用寄存器,包括程序计数器(PC)。

这些寄存器都是32 位的。

6个状态寄存器。

这些寄存器也是32 位的,但是只是使用了其中的12 位。

ARM 通用寄存器
通用寄存器(R0~R15)可分为3 类:
不分组寄存器R0~R7;
分组寄存器R8~R14;
程序计数器R15。

1)不分组寄存器R0~R7
R0~R7 是不分组寄存器。

这意味着在所有处理器模式下,它们都存取一样的3 2 位寄存器。

它们是真正的通用寄存器,没有架构所隐含的特殊用途。

2)分组寄存器R8~R14
R8~R14 是分组寄存器。

它们存取的物理寄存器取决于当前的处理器模式。

若要存取特定的物理寄存器而不依赖当前的处理器模式,则要使用规定的各字。

寄存器R8~R12 各有两组物理寄存器:一组为FIQ 模式,另一组为除了FIQ以外的所有模式。

寄存器R8~R12 没有任何指定的特殊用途。

只是使用R8~R14来简单地处理中断。

寄存器R13,R14 各有6 个分组的物理寄存器。

1 个用于用户模式和系统模式,其它5 个分别用于5 种异常模式。

寄存器R13 通常用做堆迭指标,称为SP。

每种异常模式都有自己的R13。

寄存器R14 用作子程序链接寄存器,也称为LR。

3) 程序计数器R15
寄存器R15 用做程序计数器(PC)。

程序状态寄存器在所有处理器模式下都可以存取当前的程序状态寄存器CPSR。

CPSR 包含条件码标志位,中断禁止位,当前处理器模式以及其它状态和控制信息。

每种异常模式都有一个程序状态保存寄存器SPSR。

当例外出现时,SPSR 用于保留CPSR的状态。

CPSR 和SPSR 的格式如下:
31
30
29
28
27
26 8
7
6
5
4
3
2
1
N
Z
C
V
Q
DNM(RAZ)
I
F
T
M
M
M
M
M
条件码标志:N,Z,C,V 大多数指令可以测试这些条件码标志以决定程序指令如何执行
控制位:最低8 位I,F,T 和M 位用做控制位。

当异常出现时改变控制位。

当处理器在特权模式下也可以由软件改变。

中断禁止位:I 置1 则禁止IRQ 中断。

F 置1 则禁止FIQ 中断。

T 位:T=0 指示ARM 执行。

T=1 指示Thumb 执行。

在这些架构系统中,可自由地使用能在ARM 和Thumb 状态之间切换的指令。

模式位:M0, M1, M2, M3 和M4 (M[4:0]) 是模式位.这些位
决定处理器的工作模式.如表2-1 所示。

表4-1 ARM 工作模式M[4:0]
M[4:0]
模式
可存取的寄存器
0b10000
用户模式
PC, R14~R0,CPSR
0b10001
FIQ模式
PC, R14_fiq~R8_fiq,R7~R0,CPSR,SPSR_fiq
0b10010
IRQ模式
PC, R14_irq~R8_fiq,R12~R0,CPSR,SPSR_irq
0b10011
管理模式
PC, R14_svc~R8_svc,R12~R0,CPSR,SPSR_svc
0b10111
中止
PC, R14_abt~R8_abt,R12~R0,CPSR,SPSR_abt
0b11011
未定义
PC, R14_und~R8_und,R12~R0,CPSR,SPSR_und
0b11111
系统。

相关文档
最新文档