"); //-->
大 中 小
标签:IT/科技
这两天参加了一个编写操作系统的项目,因为要做很多底层的东西,而且这个操作系统是嵌入式的,所以开始学习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一句是将相当于数组的JumpTable的地址付给r4这个寄存器。
stop一段是用来是程序退出的,第一个语句“MOV r0,#0x18”将r0赋值为0x18,这个立即数对应于宏angel_SWIreason_ReportException。表示r1中存放的执行状态。语句“LDR r1,=0x20026”将r1的值设置成ADP_Stopped_ApplicationExit,该宏表示程序正常退出。然后使用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 give 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
SWI 0
在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
SWI 0
在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这条指令的地址是0x00008000,那么根据ARM体系的2级流水线 PC寄存器里指向是下两条指令 于是PC = 0x00008008 也就是伪指令DCD TASK_SW 声明的标号TASK_SW 的地址,注意DCD TASK_SW 这条指令本身不是ARM能执行的指令,也不会占有地址,这条指令靠汇编器汇编成可执行代码,它的意义就是声明 TASK_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条件码置位时,程序跳转到标号Label处执行
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到R12,LR)存入堆栈。
LDMFD R13!,{R0,R4-R12,PC} ;将堆栈内容恢复到寄存器(R0,R4到R12,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,则跳转时自动将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位立即数被忽略,用户请求的服务类型有寄存器R0的值决定,参数通过其他的通用寄存器传递.
mov r0,#12 ;调用12号软中断
mov r1,#34 ;设置子功能号位34
SWI 0
在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号为0x123456的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寄存器保存的是中断返回指令的地址,所以 [lr - 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 register */
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_TASK_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,上課時CODING完成的範例--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
http://www.akaedu.org/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中读取的数值;对于STR指令,Rd保存着将写入memory的数值
Rn:指针寄存器
FlexOffset:偏移量
例子:
ldr r0, [r1] ;r1作为指针,该指针指向的数存入r0
str r0, [r1, #4] ;r1+4作为指针,r0的值存入该地址
str r0, [r1, #4]! ;同上,并且r1 = r1 + 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指针,使程序返回到异常发生前所执行的地方,^标记用来使CPU退出异常模式,进入普通状态。*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。