原创 arm的IDE工具ADS中的例子-SWI的具体分析

2007-10-30 22:04 2880 0 分类: MCU/ 嵌入式
 
这里面一般有四个文件:main.c ahandle.s chandle.s swi.h。main.c是程序的入口。Ahanle.s是软中断的处理程序,但它只是处理程序的一部分,是软件中断处理程序的入口,主要的处理部分在chandle.s中,Ahanle.s文件调chanle.s文件,chanle.s文件中有处理程序的具体实现细节。而swi.h文件是中断处理程序在c语言中的声明。

 

首先让我们来看看swi.h文件,具体内容如下:

               

__swi(0int multiply_two(intint);
__swi(
1int add_two(intint);
__swi(
2int add_multiply_two(intintintint);
__swi(
3) __value_in_regs struct four_results
    many_operations(
intintintint);
struct four_results
...{
    
int a;
    
int b;
    
int c;
    
int d;
}
;

 

最上面的__swi(0)…,__swi(1)…, __swi(2)…, __swi(3)…,是指你要调用多少号的中断,括号里面的数字被放到指令中,所以你想判断要调用几号软件中断服务程序,你必须用像:

LDREQ   r0, [lr, #-4]

BICEQ   r0, r0, #0xFF000000    ; ...extract comment field  

这样的指令来读把号码读出来。可能有人要问,为什么是lr要减4呢?因为在执行中断指令 swi * 时,编译器自动的把lr指向中断指令的下一条指令的地址。而我们要得到的中断号却在swi * 这条指令中,所以我们要把这条指令用 lr-4 的方式把它取出来。然后用把前8位的指令码去掉,得出中断号。__swi(0)等后面的 int multiply_two(int, int) 等,这函数的名是自己定的,是为了在c语言中能够调用汇编编写的中断函数。最后看这个结构体,这个结构体没有什么特别的意思,主是为了测试程序用的。 

 

接下来看看main.c文件,具体内容如下: 

 

#include <stdio.h>
#include 
"swi.h"
unsigned 
*swi_vec = (unsigned *)0x08;
extern void SWI_Handler(void);
 
unsigned Install_Handler( unsigned routine, unsigned 
*vector )
...{
    unsigned vec, old_vec;
 
    vec 
= (routine - (unsigned)vector - 8>> 2;
    
if (vec & 0xff000000)
    
...{
        printf(
"Handler greater than 32MBytes from vector");
    }

    vec 
= 0xea000000 | vec;     /**//* OR in 'branch always' code */
 
    old_vec 
= *vector;
    
*vector = vec;
    
return (old_vec);
}

 
 
int main( void )
...{
    
int result1, result2;
    
struct four_results res_3;
 
   Install_Handler( (unsigned) SWI_Handler, swi_vec );
 
    printf(
"result1 = multiply_two( 2, 4 ) = %d ", result1 = multiply_two( 24 ));
    printf(
"result2 = multiply_two( 3, 6 ) = %d ", result2 = multiply_two( 36 ));
    printf(
"add_two( result1, result2 ) = %d ", add_two( result1, result2 ));
    printf(
"add_multiply_two( 2, 4, 3, 6 ) = %d ", add_multiply_two( 2436 ));
    res_3 
= many_operations( 12431 );
 
    printf(
"res_3.a = %d ", res_3.a );
    printf(
"res_3.b = %d ", res_3.b );
    printf(
"res_3.c = %d ", res_3.c );
    printf(
"res_3.d = %d ", res_3.d );
    
return 0;
}


 

首先看到是的两个文件包含,第一个就不说了,第二个是为了能够使用定义的结构体和能够调用中断程序。接下来两句:

unsigned *swi_vec = (unsigned *)0x08;

extern void SWI_Handler(void);

第一句是定义一个指针,这个指针指向0x08地址的单元,是软中断的中断向量号。第二句是声明一个汇编的外部函数。

接下是Install_Handler这个方法,这个方法主要作用是把能跳转到用汇编编写的软件中断处理程序的指令存到arm规定的中断向量表中,下面我们具体分析一下这个函数。首先看看这句:

vec = (routine - (unsigned)vector - 8) >> 2;

这句让我搞了半天也没明白,后来是问的老师。在arm体系结构参考手册中关于BL:

1. Sign-extending the 24-bit signed (two's complement) immediate to 32 bits.

2. Shifting the result left two bits.

3. Adding this to the contents of the PC, which contains the address of the branch instruction plus?.

它是把BL中24有符号数扩展成32位,然后左移两位,再这个数加8加到pc中。所以,我们得先把这个数减8再右移两位,好让arm的自动操作能算出正确的地址。但是我没明白arm为什么先左移再加8。

   if (vec & 0xff000000)

    {

        printf("Handler greater than 32MBytes from vector");

    }

接下来看看bl要跳转的地址,手册里规定bl跳转不能超过+-32M的地址空间。

    vec = 0xea000000 | vec;     /* OR in 'branch always' code */

这句中,0xea000000是bl的二进制编码。用0xea000000和要跳转的相对地址相或操作,就是一条要跳转到中断处理程序相对地址的指令(bl addr)。

然后用 *vector = vec; 把这条指令保存到中断向量表中,也就是把指令保存到0x08这个地址所指向的空间。

    old_vec = *vector;

    return (old_vec);

这两句是为了保存0x08这个中断向量中的内容。 

下面说说ahandle.s这个文件中的内容:

 




    AREA SWI_Area, CODE, READONLY
 
    EXPORT SWI_Handler
    IMPORT C_SWI_Handler
 
T_bit EQU 
0x20
 
SWI_Handler
 
        STMFD   sp
!...{r0-r3, r12, lr} ; Store registers
    MOV     r1, sp                 ; Set pointer to parameters
    MRS     r0, spsr               ; Get spsr
        STMFD   sp
!...{r0}              ; Store spsr onto stack
    TST     r0, #T_bit             ; Occurred 
in Thumb state?
    LDRNEH r0, [lr,#
-2]           ; Yes: Load halfword and...
    BICNE   r0, r0, #
0xFF00        ; ...extract comment field
    LDREQ   r0, [lr,#
-4]           ; No: Load word and...
    BICEQ   r0, r0, #
0xFF000000    ; ...extract comment field
 
        ; r0 now contains SWI number
        ; r1 now contains pointer to stacked registers
 
    BL      C_SWI_Handler          ; Call main part of handler
        LDMFD   sp
!...{r0}              ; Get spsr from stack
    MSR     spsr_cf, r0            ; Restore spsr
        LDMFD   sp
!...{r0-r3, r12, pc}^ ; Restore registers and return
 
    END

    这段程序是中断处理程序的一部分,由它调用中断处理程序的主要处理部分,主要处理部分是用c语言写的chandle.c。下面分析一下这段程序。从下面几条语句开始:

    STMFD   sp!, {r0-r3, r12, lr} ; Store registers

    MOV     r1, sp                 ; Set pointer to parameters

    MRS     r0, spsr               ; Get spsr

    STMFD   sp!, {r0}              ; Store spsr onto stack

第一条语句是把c语言中函数调用时的参数保存到堆栈中,在c语言的中断处理程序中读出这些参数。保存lr是因为一会要调用别的函数,所以防止lr丢失。为什么要保存r12这个我有点不太明白,因为这段程序运行根本就没用到它。第二条是把堆栈的地址保存到r1中,因为一会要把它传到c语言的中断处理函数中。

MRS     r0, spsr               ; Get spsr

STMFD   sp!, {r0}

这两句是把spsr保存到堆栈中,但我看这段程序运行也没改动这它,真不知道是为什么?

 

TST     r0, #T_bit             ; Occurred in Thumb state?

    LDRNEH r0, [lr,#-2]           ; Yes: Load halfword and...

    BICNE   r0, r0, #0xFF00        ; ...extract comment field

    LDREQ   r0, [lr,#-4]           ; No: Load word and...

    BICEQ   r0, r0, #0xFF000000    ; ...extract comment field

这一小段的代码主要作用是判断现在arm是thumb状态还是arm状态,根据不同的状态取半字还是字,然后得到用户要调用几号软中断,把这个号放到r0中。这里为什么会是lr-4呢,因为当main.c中执行中执行h文件中声明的方法时,就是调用了swi * 指令,当执行 swi * 这条指令时,编译器同时把这条指令的下一条指令的地址放到lr中,我们要得到swi * 这条指令,并得到 * (就是用户要调多少号软中断的号)是多少时,就得用lr-4了。

 

    BL      C_SWI_Handler          ; Call main part of handler

    LDMFD   sp!, {r0}              ; Get spsr from stack

    MSR     spsr_cf, r0            ; Restore spsr

    LDMFD   sp!, {r0-r3, r12, pc}^ ; Restore registers and return

一切准备工作都做完后,就调用c语言编写的具体的处理函数。函数返回后,恢复保存的寄存器。 

 

下面讲一讲最后的一个文件chandle.h文件。

 

它的作用主要就是根据ahandle.c程序中SWI_Handler方法传过来的参数,做相应的处理。具体内容如下:



void C_SWI_Handler( int swi_num, int *regs )
...{
    
switch( swi_num )
    
...{
    
case    0:
        regs[
0= regs[0* regs[1];
      
break;
    
case    1:
        regs[
0= regs[0+ regs[1];
      
break;
    
case    2:
        regs[
0= (regs[0* regs[1]) + (regs[2* regs[3]);
      
break;
    
case    3:
    
...{
        
int w, x, y, z;
 
        w 
= regs[0];
        x 
= regs[1];
        y 
= regs[2];
        z 
= regs[3];
 
        regs[
0= w + x + y + z;
        regs[
1= w - x - y - z;
        regs[
2= w * x * y * z;
        regs[
3=(w + x) * (y - z);
    }

    
break;
 
    }

}


 

对于这段程序,只介绍一下参数的传递,第一个参数是软中断的号,也就是swi * 指令中的 * 。第二个参数传过来的是一个指针,是指向堆栈的指针。因为在 ahandle.s中,把main.c中调用函数时的实参都放到了堆栈中。

 

所有的文件都说明完了,最后说一下程序的流程。

程序以main.c的main函数开时,首先调用main.c中的Install_Handler函数,把跳转到中断向量处理程序(中断处理程序就是ahandle.s中的SWI_Handler函数)的指令放到中断向量表中。当有中软中断出现时,arm就会调用跳转到中断向量处理程序(SWI_Handler),中断向量处理程序就会调用具体的处理函数chandle.c中的C_SWI_Handler函数。
广告

文章评论 0条评论)

登录后参与讨论
相关推荐阅读
embedtek 2007-10-31 22:31
UCOS-II移植ARM的读书笔记(12.11--12.25)
http://blog.csdn.net/zhhg_1220/ UCOS-II移植ARM的读书笔记(12.11) 真是很郁闷,昨天晚上边看移植代码边记下来的笔记不知道怎么回事在保存的时候竟然不见了。。...
embedtek 2007-10-31 20:43
ARM汇编的SWI指令软中断
从下面的一个ARM 汇编小程序要弄懂的以下三个问题:1).在ARM状态转到THUNB状态和BX的应用2).汇编的架构3).SWI指令的使用    AREA ADDREG,CODE,READONLY  ...
embedtek 2007-10-31 20:30
uc/OS II移植中软件中断的理解与应用
uc/OS II移植中软件中断的理解与应用1.   软件中断SWISWI(software interrupt)软件中断,由用户定义的中断指令.可以用于用户模式下的程序调用特权操作指令.在实时操作系统...
embedtek 2007-10-31 20:29
Luminary Micro半导体-LM3S101微控制器(超低价格的ARM芯片)
概述  Luminary Micro StellarisTM系列的微控制器是首款基于ARM® CortexTM-M3的控制器,它将高性能的32位计算引入到对价格敏感的嵌入式微控制器应用中。这些堪称先锋...
embedtek 2007-10-31 19:49
ARM9微控制器LPC3180的软硬件平台设计
来源:网络 作者:不详 发布时间:2007-03-02   摘要 介绍以Philips LPC3180微控制器为核心的嵌入式软硬件平台设计;对系统设计的硬件部分和软件部分进行详细的分析,并针对LPC3...
embedtek 2007-10-31 19:47
AVR单片机的RTOS-AVRX应用
来源:网络 作者:不详 发布时间:2007-03-02 摘  要:详细介绍AVR系列单片机的专用实时嵌入式操作系统AVRX的特点,并以ATmega16单片机为平台,结合AVR单片机应用实例分析AVRX...
广告
我要评论
0
0
广告
关闭 热点推荐上一条 /2 下一条