ARM汇编

来源:互联网 发布:恶意软件清除工具 编辑:程序博客网 时间:2024/05/17 01:54

这两天参加了一个编写操作系统的项目,因为要做很多底层的东西,而且这个操作系统是嵌入式的,所以开始学习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退出异常模式,进入普通状态。
                                                                                       

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 = r1 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,并跳至sub1
        ~ ~ ~
sub1
        ~ ~ ~
        mov        pc,        lr                        ; 从sub1中返回
【使用本地label(local label)】
        本地label可以在一个程序段内多次使用,用数字作为label的名称,也可以在数字后面跟一些字母。引用本地label的语法是:        %{F|B}{A|T}n{routname},其中F代表向前搜索本地label,B代表向后搜索,A/T不常使用。
例子
100        ; 定义本地label,名称为“100”
~ ~ ~
100                                                                                ; 第二次定义本地label,名称为“100”
        ~ ~ ~
        b                %f100                                                ; 向前跳到最近的“100”处
~ ~ ~
        b                %b100                                                ; 向后跳至最近的“100”处
100                                                                                ; 第三次定义本地label 100

8、        条件执行
条件:状态寄存器中某一或某几个比特的值代表条件,对应不同的条件后缀cond,如:
后缀 (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 prog1
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 指令实验,Thumb 指令实验和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 是不分组寄存器。这意味着在所有处理器模式下,它们都存取一样的32 位寄存器。它们是真正的通用寄存器,没有架构所隐含的特殊用途。

 

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
 0
 
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
 系统
 PC, R14~R0,CPSR
 

其它位程序状态寄存器的其它位保留,用作以后的扩充。

 

4.1.3. 实验操作步骤
1. 执行ADS1.2开发环境,打开实验系统例程目录下ARMcode_test 子目录下的ARMcode.mcp 工程文件。

2. 透过操作菜单栏或使用快捷命令编译链接项目。

3. 选择Debug 菜单Remote Connect 进行连接软件仿真器,执行Download命令下载程序,并打开寄存器窗口。

4. 单步执行程序并观察和记录寄存器R0-R15 的值变化。

5. 结合实验内容和相关数据,观察程序执行,透过实验加深理解ARM指令的使用。

 

 

 

4.1.4  试验程序代码
;本程序将数据区从数据区SRC复制到目标数据区DST。复制时,以8个字节为单位进行。对于

;最后所剩不足的8个字节的数据,以字为单位进行复制,这时程序跳转到copywords处执行。

;在进行以8个字为单位的数据复制时,保存了所有的8个工作寄存器。

 

;设置本段程序的名称(Block)及属性

 AREA Block,CODE,READONLY

 

                    

;设置将要复制的字数(定义变量num,并赋值为20)

num EQU 20                

 

;程序入口标志

       ENTRY

start

 

;r0寄存器指向源数据区(SRC 标识的地址放入R0)

       LDR       r0,=src   

 

;r1寄存器指向目标数据区(DST 标识的地址放入R1)      

       LDR       r1,=dst   

      

;r2指定将要复制的字数(装载num 的值到R2)  

       MOV      r2, #num

             

;设置数据栈指针(R13),用于保存工作寄存器数值(设定SP堆栈开始地址为0x400)

       MOV      sp, #0x400

 

             

;进行以8个字节为单位的数据复制

blockcopy

 

;需要进行的以8个字为单位的复制次数( R2 右移3 位后的值放入R3)

       MOVS r3,r2, LSR #3    

;对于剩下的不足8个字的数据,跳转到copywords,以字为单位复制(判断是否为0,为0 跳移)

      

       BEQ copywords                  

;保存工作寄存器(把R4 到R11 的值保存到SP 标识的堆栈中)

       STMFD sp!, {r4-r11}         

 

octcopy

;从数据区读取8个字节的数据,放到8个寄存器中,并更新目标数据区指针r0(把R0 中的地址标识的内容顺序装载到R4 到R11 中)

 

       LDMIA r0!, {r4-r11}          

;将这8个字数据写入到目标数据区,并更新目标数据区指针r1(把R4 到R11 的值顺序保存到以R1 起始地址的内存中)

       STMIA r1!, {r4-r11}           

 

;将块的复制次数减1 (R3 -1 计数)

       SUBS r3, r3, #1                  

 

;循环,直到完成以8个字为单位的块复制

       BNE octcopy                      

 

;需要注意的是,LDMIA 或者STMIA 指令执行后,R0,R1 的值产生变化,每一次寄存器操作,R0 或者R1 的值会自动增加一个字节的量,这里操作了8 个寄存器,R0 或者R1 的值也相应增加了8 个字节

 

 

;恢复工作寄存器值(把刚才保存的SP 堆栈中的值恢复到R4 到R11 中)

       LDMFD sp!, {r4-r11}  

       

copywords

;剩下不足8个字的数据的字数(逻辑与,把R2 前7 位扔掉)

       ANDS r2, r2, #7          

 

;数据复制完成(判断是否为0,为0 跳移)

       BEQ stop                           

 

wordcopy

;从源数据区读取18个字的数据,放到r3寄存器中,并更新目标数据区指针r0(把R0 表示地址的内容的后4 位全部拷贝到R3)

       LDR r3, [r0], #4   

 

;将这r3中数据写入到目标数据区中,并更新目标数据区指针r1 (把R3 的内容,放入以R1 为起始地址的4 位内存中)      

       STR r3, [r1], #4           

      

;将字数减1;(R2 -1 放回R2)

       SUBS r2, r2, #1           

 

;循环,直到完成以字为单位的数据复制(判断是否为0,不为0 跳移,同样的,这里R0,R1 操作后,R0,R1 会自动加上便宜量)   

       BNE wordcopy                  

                                         

stop

;从应用程序中退出

       MOV      r0,   #0x18

       LDR       r1,   =0x20026

       SWI 0x123456

;定义数据区bloackdata

 

       AREA     Bloackdata,     DATA,    READWRITE

;定义源数据区src及目标数据区dst

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

4.1.5 实验练习题
1. 撰写程序循环对R4~R11 进行8 次累加,R4~R11 起始值为1~8,每次加操作后把R4~R11 的内容放入SP 堆栈中,SP 初始设定为0x800。最后把R4~R11 用LDMFD 指令清空值为0。

2. 对于每一种ARM 的寻址方法,简短的写出相对的应用程序片段。

4.2 ARM 处理器工作模式实验
4.2.1. 实验说明
         实验目的:透过实验掌握ARM 处理器工作模式的切换。

         实验设备:软件需要ADS1.2开发环境,Windows 98/2000/NT/XP。

         实验内容:透过ARM 组译指令,在各种处理器模式下切换并观察各种模式下缓存器的区别;掌握ARM 不同模式的进入与退出。

4.2.2. 实验原理
ARM 处理器模式

ARM 架构支持下表2-2 所列的7 种处理器模式。

工作模式
 描述
 
用户模式(User,usr)
 正常程序执行的模式
 
快速中断模式(FIQ,fig)
 用于高速数据传输和数据处理
 
外部中断模式(IRQ,irq)
 用于通常的中断处理
 
特权模式(管理模式)(Supervisor,sve)
 共操作系统使用的一种模式
 
数据访问中止模式(Abort,abt)
 用于虚拟存储及存储保护
 
未定义指令中断模式(Undefind,und)
 用于支持通过软件方针的协处理器
 
系统模式(System,sys)
 用于运行特权的操作系统任务
 

 

 

大多数应用程序在用户模式下执行。当处理器工作在用户模式时,正在执行的程序不能存取某些被保护的系统资源,也不能改变模式,除非例外(exception)发生。这允许适当撰写操作系统来控制系统资源的使用。

除用户模式外的其它模式称未特权模式。它们可以自由的存取系统资源和改变模式。其中的5 种称为异常模式,即

n         FIQ(Fash Interrupt request);

n         IRQ(Interrupt ReQuest);

n         管理(Supervisor);

n         中止(Abort) ;

n         未定义(Underfined) 。

当特定的异常出现时,进入相应的模式。每种模式都有某些附加的寄存器,以避免异常出现时用户模式的状态不可靠。

剩下的模式是系统模式。仅ARM 架构V4 以及以上的版本有该模式。不能由于任何异常而进入该模式。它与用户模式有完全相同的寄存器。然而它是特权模式,不受用户模式的限制。它供需要存取系统资源的操作系统工作使用,单希望避免使用与例外模式有关的附加寄存器。避免使用附加寄存器保证了当任何异常出现时,都不会使工作的状态不可靠。

4.2.3. 实验操作步骤
1.开发环境,打开实验系统例程目录下ARMMode_test/ARMMode.MCP 项目,并编译链接项目。

3. 单步执行程序并观察和记录CPSP 和SPSR 缓存器值的变化;并观察在相应模式下执行程序后对应缓存器值的变化。

4. 结合实验内容和相关数据,观察程序执行,透过实验加深理解和掌握

4.2.4  试验程序代码
;设置本段程序的名称(Block)及属性

 AREA Block,CODE,READONLY

 

;程序入口标志

       ENTRY

start

 

       B     Reset_Handler

      

Undefined_Handler

       B Undefined_Handler

       B SWI_Handler

      

Prefetch_Handler

       B Prefetch_Handler

      

Abort_Handler

       B Abort_Handler

       NOP ;空操作

      

IRQ_Handler

       B IRQ_Handler

      

FIQ_Handler

       B FIQ_Handler

      

SWI_Handler

       mov pc, lr

;前面部分是处理程序,主要处理各种模式的入端口跳移

 

Reset_Handler

 

;into System mode

       MRS R0,CPSR      ;复制CPSR 到R0

       BIC R0,R0,#0x1F ;清除R0 的后5 位

       ORR R0,R0,#0x1F       ;设定R0 的最后5 位为11111

       MSR       CPSR_c,R0    ;把R0 装载到CPSR,切换到系统模式

       MOV R0, #1         ;对系统模式下的R0 赋值,下面的R1~R15 一样

       MOV R1, #2

       MOV R2, #3

       MOV R3, #4

       MOV R4, #5

       MOV R5, #6

       MOV R6, #7

       MOV R7, #8

       MOV R8, #9

       MOV R9, #10

       MOV R10, #11

       MOV R11, #12

       MOV R12, #13

       MOV R13, #14

       MOV R14, #15

;into FIQ mode

       MRS R0,CPSR

       BIC R0,R0,#0x1F

       ORR R0,R0,#0x11 ;设定R0 的最后5 位为10001

       MSR CPSR_c,R0 ;把R0 装载到CPSR,切换到Fiq 模式

       MOV R8, #16 ;给Fiq 模式的特有缓存器R8 赋值, 下面的R9~R14 一样

       MOV R9, #17

       MOV R10, #18     

       MOV R11, #19

       MOV R12, #20

       MOV R13, #21

       MOV R14, #22

;into SVC mode

       MRS R0,CPSR

       BIC R0,R0,#0x1F

       ORR R0,R0,#0x13 ;设定R0 的最后5 位为10011

       MSR CPSR_c,R0   ;把R0 装载到CPSR,切换到Svc 模式

       MOV R13, #23      ;给SVC 模式的特有缓存器R13 赋值, 下面的R14 一样

       MOV R14, #24

;into Abort mode

       MRS R0,CPSR

       BIC R0,R0,#0x1F

       ORR R0,R0,#0x17 ;设定R0 的最后5 位为10111

       MSR CPSR_c,R0 ;把R0 装载到CPSR,切换到Abort 模式

       MOV R13, #25 ;给Abort 模式的特有缓存器R13 赋值, 下面的R14 一样

       MOV R14, #26

;into IRQ mode

       MRS R0,CPSR

       BIC R0,R0,#0x1F

       ORR R0,R0,#0x12 ;设定R0 的最后5 位为10010

       MSR CPSR_c,R0 ;把R0 装载到CPSR,切换到IRQ 模式

       MOV R13, #27 ;给IRQ 模式的特有缓存器R13 赋值, 下面的R14一样

       MOV R14, #28

;into UNDEF mode

       MRS R0,CPSR

       BIC R0,R0,#0x1F

       ORR R0,R0,#0x1b        ;设定R0 的最后5 位为11011

       MSR CPSR_c,R0                 ;把R0 装载到CPSR,切换到UNDEF 模式

       MOV R13, #29             ;给UNDEF 模式的特有缓存器R13 赋值, 下面的R14 一样

       MOV R14, #30

       B Reset_Handler ;跳移到最开始地方循环

       END

原创粉丝点击