新闻  |   论坛  |   博客  |   在线研讨会
ARM汇编
mayer | 2009-09-05 19:53:48    阅读:6130   发布文章

 

大 中 小
标签: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退出异常模式,进入普通状态。*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
推荐文章
最近访客