SWI 指令

EMBEST ARM

SWI 指令

 

SWI : 软件中断

(Software Interrupt)

  SWI{条件}  <24 位编号>
指令格式

这是一个简单的设施,但可能是最常用的。多数操作系统设施是用 SWI 提供的。没有 SWI 的 RISC OS 是不可想象的。

Nava Whiteford 解释了 SWI 是如何工作的(最初在 Frobnicate issue 12½)...

 


我将试图在本文中解释 SWI 是如何工作的。

SWI 是什么?

SWI 表示 Software Interrupt。在 RISC OS  中使用 SWI 来访问操作系统例程或第三方生产的模块。许多应用使用模块来给其他应用提供低层外部访问。

SWI 的例子有:

  • 文件器 SWI,它辅助读写磁盘、设置属性等。
  • 打印机驱动器 SWI,用来辅助使用打印并行端口。
  • FreeNet/Acorn TCP/IP 协议栈 SWI,用 TCP/IP 协议在 Internet 上发送和接收数据。

在以这种方式使用的时候,SWI 允许操作系统拥有一个模块结构,这意味着用来建立完整的操作系统的所需的代码可以被分割成许多小的部分(模块)和一个模块处理程序(handler)。

当 SWI 处理程序得到对特定的例程编号的一个请求的时候,它找到这个例程的位置并执行它,并传递(有关的)任何数据。

它是如何工作的?

首先查看一下如何使用它。一个 SWI 指令(汇编语言)看起来如下:
   SWI &02
   SWI "OS_Write0"
这些指令实际上是相同的,将被汇编成相同的指令。唯一的不同是第二个指令使用一个字符串来表示 SWI 编号 &02。在使用采用了字符串编号的程序的时候,在执行之前首先查找这个字符串。

在这里我们不想处理字符串,因为它不能给出它要进行什么的一个真实表示。它们通常用于增进一个程序的清晰程度,但不是实际执行的指令。

让我们再次看一下第一个指令:

   SWI &02
这是什么意思? 字面的意思是进入 SWI 处理程序并传递值 &02。在 RISC OS 中这意味着执行编号是 &02 的例程。

它是如何这么作的? 它如何传递 SWI 编号和进入 SWI 处理程序?

如果你查看内存的开始 32 字节(位于 0-&1C)并反汇编它们(查开实际的 ARM 指令)你将见到如下:

 

地址       内容               反汇编
00000000 : 0..å : E5000030 : STR     R0,[R0,#-48]
00000004 : .óŸå : E59FF31C : LDR     PC,&00000328
00000008 : .óŸå : E59FF31C : LDR     PC,&0000032C
0000000C : .óŸå : E59FF31C : LDR     PC,&00000330
00000010 : .óŸå : E59FF31C : LDR     PC,&00000334
00000014 : .óŸå : E59FF31C : LDR     PC,&00000338
00000018 : .óŸå : E59FF31C : LDR     PC,&0000033C
0000001C :  2?ã : E3A0A632 : MOV     R10,#&3200000
让我们仔细看一下。

除了第一个和最后一个指令之外(它们是特殊情况)你见到的都是把一个新值装载到 PC (程序计数器)的指令,它们告诉计算机到哪里去执行下一个指令。还展示了这个值是从内存中的一个地址接受来的。(你可以在 !Zap 主菜单上使用“Read Memory”选项去自己查看一下。)

这看起来好象与 SWI 没多少关系,下面做进一步的说明。

一个 SWI 所做的一切就是把模式改变成超级用户并设置 PC 来执行在地址 &08 处的下一个指令! 把处理器转换到超级用户模式会切换掉两个寄存器 r13 和 r14 并用 r13_svc 和 r14_svc 替换它们。

在进入超级用户模式的时候,还把 r14_svc 设置为在这个 SWI 指令之后的地址。

这个实际上就象一个连接到地址 &08 的分支指令(BL &08),但带有用于一些数据(SWI 编号)的空间。

象我说过的那样,地址 &08 包含跳转到另一个地址的一个指令,就是实际的 SWI 程序的地址!

此时你可能会想“稍等一会! 还有 SWI 编号呢?”。实际上处理器忽略这个值本身。SWI 处理程序使用传递来的 r14_svc 的值来获取它。

下面是完成它的步骤(在存储寄存器 r0-r12 之后):

  1. 它从 r14 中减去 4 来获得 SWI 指令的地址。
  2. 把这个指令装载到一个寄存器。
  3. 清除这个指令的高端 8 位,去掉了 OpCode 而只剩下的 SWI 编号。
  4. 使用这个值来找到要被执行的代码的例程的地址(使用查找表等)。
  5. 恢复寄存器 r0-r12。
  6. 使处理器离开超级用户模式。
  7. 跳转到这个例程的地址。
容易吧! ;)

下面是一个例子,来自 ARM610 datasheet:

0x08 B Supervisor

EntryTable
 DCD ZeroRtn
 DCD ReadCRtn
 DCD WriteIRtn

 ...

Zero   EQU 0
ReadC  EQU 256
WriteI EQU 512
 
; SWI 包含需要的例程在位 8-23 中和数据(如果有的话)在位 0-7 中。
; 假定 R13_svc 指向了一个合适的栈

STMFD R13, {r0-r2 , R14}
 ; 保存工作寄存器和返回地址。
LDR R0,[R14,#-4]
 ; 得到 SWI 指令。
BIC R0,R0, #0xFF000000
 ; 清除高端的 8 位。
MOV R1, R0, LSR #8
 ; 得到例程偏移量。
ADR R2, EntryTable
 ; 得到入口表(EntryTable)的开始地址。
LDR R15,[R2,R1,LSL #2]
 ; 分支到正确的例程

WriteIRtn
 ; 写 R0 中的位 0 - 7 中的字符。

.............
 LDMFD R13, {r0-r2 , R15}^
 ; 恢复工作空间,并返回、恢复处理器模式和标志。
这就是 SWI 指令的基本处理步骤。

来源:

The ARM610 datasheet by Advanced Risc Machines
The ARM RISC Chip - A programmers guide by van Someren Atack published by Addison Wesley

 


回到目录页