从下面的一个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 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