能力值:
(RANK:510 )
2 楼
SWI指令用于产生软件中断,以便用户程序能调用操作系统的系统例程。操作系统在SWI的异常处理程序中提供相应的系统服务,指令中24位的立即数指定用户程序调用系统例程的类型,相关参数通过通用寄存器传递,当指令中24位的立即数被忽略时,用户程序调用系统例程的类型由通用寄存器R0的内容决定,同时,参数通过其他通用寄存器传递。
SWI 0x02 ;该指令调用操作系统编号位02的系统例程。
能力值:
(RANK:510 )
3 楼
另外不知道你的构建系统是什么?比如ADS等。建议可以看看ADS提供的示例代码可以降低学习的难度。
能力值:
( LV2,RANK:10 )
4 楼
楼上的意思就是SWI后面的立即数是系统内自己定义好的,是固定的?
能力值:
(RANK:510 )
5 楼
我认为应该是这样的。我记得以前用ADS做过一些试验,反汇编后可以发现使用了SWI和一些特殊的编号。
我个人的理解是:SWI指令的使用和过去DOS下INT中断类似。
你可以再查查,欢迎把结果发布回来。呵呵。
能力值:
( LV2,RANK:10 )
6 楼
我看了Semihosting这份文档,里面说道SWI后跟的立即数是系统确定的,比如0x123456表示的软件终端,他会跳转到0x08这个地址,我还看了其他资料上说这个地址上一般放一条跳转指令,用来跳转到终端后的程序,我修改的程序如下:
MOV R1,#0x08
LDR R1,TestMode
SWI 0x123456
TestMode
..........
当结果依然不对,无法跳转到TestMode这里,并且始终停留在SWI这段,大大还请指教下,谢谢了
能力值:
(RANK:510 )
7 楼
MOV R1,#0x08
这条指令运行后,r1寄存器中的数据为0x08.
LDR R1,TestMode
这条指令可能有问题。如果你想把TestMode标号处的内存地址保存到r1中应该使用:
ldr r1, = TestMode
按照你的写法很可能是把TestMode标号后的一条指令编码保存到r1中了。你可以调试的时候注意一下r1中的数据。
能力值:
(RANK:510 )
8 楼
你所说“终端后的程序”应该是“中断后的程序”吧?
按照你提供的资料我估计应该这样写这几条指令:
mov r1,#0x08 ;r1中保存地址 ldr r0,=TestMode ;r0中保存标号TestMode地址。或者说让r0指向TestMode str r0,[r1] ;将r0中保存的TestMode地址写入0x08位置。 SWI 0x123456 TestMode
你先试试看,有问题大家再讨论。
能力值:
( LV2,RANK:10 )
9 楼
SWI 后面的立即数是自己定的,你可以通过指令中的立即数传递,如
swi 1;调用1号软中断
也可以通过寄存器传递,如
mov r0, #1;调用1号软中断
swi 0;忽略指令中的24位立即数
功能号在这里定义,每个功能号对应一个子程序,子程序的地址用DCD伪指令放于内存中。
SWI_function
DCD ChangeMode;0号功能
DCD function1; 1号功能
DCD function2; 2号功能
. .
. .
. .
当遇到SWI指令时,CPU就将处理器模式切换到管理模式,并产生中断,然后PC会跳到预先定义好的swi异常向量入口处,然后跳到swi中断服务程序,比如就是这里
SWI_Hander
STMFD SP!,{R0-R3,R12,LR} ;保存各寄存器
MRS R0,SPSR ;保存SPSR
STMFD SP!,{R0}
TST R0,#T_bit ;测试是Thumb还是ARM
LDRNEH R0,[LR,#-2] ;如果是 Thumb指令,就读取指16位令码
BICNE R0,R0,#0xFF00;取得Thumb指令中的8位立即数
LDREQ R0,[LR,#-4] ;如果是 ARM指令,就读取指32位令码
BICEQ R0,R0,#0xFF000000; 取得ARM指令中的24位立即数读到的24位立即数保存在R0中(就是对应的功能号)
LDR PC,[PC,R0,LSL #2] ;PC+R0*4->PC,这里实现跳转到对应的swi功能号去,
MOVS PC,LR
SWI_function
DCD ChangeMode
能力值:
( LV2,RANK:10 )
10 楼
回复"加百力":
按你的方法无法运行到SWI时无法切换到系统模式,而且感觉的PC也没跑到0x08那里
回复"guetcw"
你这段我在很多文章上都看到,但不明SWI如何和SWI_Hander联系起来,能否给个完完整整的简单代码,谢谢
谢谢大家的关心
能力值:
(RANK:510 )
11 楼
我只是顺着你的代码修改一下,一般0x08这样的地址应该处于系统保护的。恐怕一般不容易访问到。呵呵。
ADS开发系统中应该附带有代码的。在初学阶段多用可靠代码可以降低难度,也可以用Google搜搜。
能力值:
(RANK:510 )
12 楼
特别是你的程序本身不复杂,应该注意使用调试工具逐步跟踪,看看是哪个地方的错误?
调试技术是一项非常重要的基本功,自己写的代码调试起来还是比较容易的。
能力值:
( LV2,RANK:10 )
13 楼
我下的是盗版的ADS,安装教程上说这些帮助文件不能装,所以比较郁闷,我自己也做了好多试验,我在网上找了N久了,都是片段,我片段是懂的,但是我不知道SWI如何跳转到中断处理程序,这段详细的代码,网上都没有
能力值:
(RANK:510 )
14 楼
昨晚我练跆拳道的时候突然想到一个办法,也是我刚入门的时候经常使用的招数:
对于不熟悉的汇编语法可以写一个最简单的有等效功能的C语言程序,构建成功之后调试查看其汇编代码。
Xarm的语法也是用这种方法整理出来的。建议你试试。
能力值:
( LV2,RANK:10 )
15 楼
难道你不理解中断的含义啊,我上面说了的
当遇到SWI指令时,CPU就将处理器模式切换到管理模式,并产生中断,然后PC会跳到预先定义好的swi异常向量入口处,然后跳到swi中断服务程序
执行swi指令的时候CPU产生中断,然后CPU会自动的跳到中断向量地址上去,在ARM7中,共有8个中断向量,每个中断向量的地址都是固定的,比如lswi软件中断向量的地址是0x00000008,当发生swi中断的时候,CPU就肯定会跳到0x00000008这个地址上,除非你让ARM公司不这么做。而0x00000008这个地址上我们通常会放一条跳转指令,让CPU跳到我们的中断服务程序上来。在ARM的地址映射中0x00000000-0x0000001C这个地址范围对应的是ARM的8个中断异常入口,你可以去看看ARM中断的的一些资料。通常我们会在这几地址上构建一个中断向量表,当CPU发生中断后就可以跳到对应的中断服务上。
你这样做
mov r1,#0x08 ;r1中保存地址
ldr r0,=TestMode ;r0中保存标号TestMode地址。或者说让r0指向TestMode
str r0,[r1] ;将r0中保存的TestMode地址写入0x08位置。
SWI 0x123456
TestMode
是没有一点用的,你把TestMode 的地址装载到0x00000008这个地址上,产生swi中断后,PC指到这里,但它不是一条跳转指令,你放到0x00000008这个地址的数据(就是TestMode 的地址) 也许CPU根本就无法识别。
完整的测试代码一会贴上来。
能力值:
( LV2,RANK:10 )
16 楼
测试代码
startup.s 中的代码
;引入的外部标号在这声明
IMPORT __main ;C语言主程序入口(这个__main()是编译器提供的)
IMPORT SWI_Exception ;SWI软件中断散转函数
;给外部使用的标号在这声明
EXPORT Reset CODE32
AREA vectors,CODE,READONLY
ENTRY
;中断向量表
Reset
LDR PC, ResetAddr
LDR PC, UndefinedAddr
LDR PC, SWI_Addr
LDR PC, PrefetchAddr
LDR PC, DataAbortAddr
DCD 0
LDR PC, IRQ_Addr
LDR PC, FIQ_Addr
ResetAddr DCD ResetInit
UndefinedAddr DCD 0
SWI_Addr DCD SoftwareInterrupt
PrefetchAddr DCD 0
DataAbortAddr DCD 0
Nouse DCD 0
IRQ_Addr DCD 0
FIQ_Addr DCD 0 ;软件中断
SoftwareInterrupt
LDR SP, StackSvc ; 重新设置堆栈指针
STMFD SP!, {R0-R3, R12, LR}
MRS R3, SPSR
TST R3, #0x20 ; 中断前是否是Thumb状态
LDRNEH R0, [LR,#-2] ; 是: 读取Thumb状态SWI号
BICNE R0, R0, #0xff00
LDREQ R0, [LR,#-4] ; 否: 读取arm状态SWI号
BICEQ R0, R0, #0xFF000000
BL SWI_Exception ; r0 = SWI号,调用C语言SWI_Exception()函数
;参数通过r0传递
LDMFD SP!, {R0-R3, R12, PC}^
;/* 复位入口*/
ResetInit
;设置系统模式堆栈
MSR CPSR_c, #0xdf
LDR SP, StackSys
B __main ;跳转到c语言入口, __main()由编译器提供 StackSys DCD SysStackSpace + 31* 4
StackSvc DCD SvcStackSpace + 31* 4
;/* 分配堆栈空间 */
AREA MyStacks, DATA, NOINIT, ALIGN=2
SysStackSpace SPACE 32 * 4 ;系统模式堆栈
SvcStackSpace SPACE 32 * 4 ;管理模式堆栈空间
END
能力值:
( LV2,RANK:10 )
17 楼
main.c 中的代码
int SWI_Function1()
{
return 1;
}
int SWI_Function2()
{
return 2;
}
int Test()
{
return 0x12345;
}
void SWI_Exception(int SWI_Nu)
{//处于管理模式,可切换到其它模式。
switch(SWI_Nu)
{
case 0x1:
SWI_Function1();
break;
case 0x2:
SWI_Function2();
break;
case 0x12345:
Test();
break;
}
}
int main()
{
__asm
{
SWI 0x1;//调用1号功能
SWI 0x2;//调用2号功能
SWI 0x12345;//调用0x12345号功能
}
while(1);
return 0;
}
能力值:
( LV2,RANK:10 )
18 楼
我把整个项目文件上传给你,是在ADS下写的代码,你可以用ADS的DEBUG工具AXD进行仿真。
上传的附件:
能力值:
(RANK:510 )
19 楼
用C写程序再反汇编分析是个不错的办法。现在应该可以解决问题了。
能力值:
(RANK:510 )
20 楼
以后ADS的问题就可以多找找:guetcw了。
能力值:
( LV2,RANK:10 )
21 楼
SWI是ARM的软件中断,找到软件中断子程序
能力值:
( LV2,RANK:10 )
22 楼
回复"guetcw"
谢谢你的代码,我用你的程序运行到SWI 0x1时ADS会保存,"Process ARM7TDMI raised an exception. Cause: Undefined instruction"
能力值:
( LV2,RANK:10 )
23 楼
是你自己新建的工程吗,我在ADS里仿真是没有问题的,你按我建的工程设计,特别是ARM Linker /Layout 那里
能力值:
( LV2,RANK:10 )
24 楼
咦,按照你的工程设置就对了,但又多了些问题,大大能否给我个你的QQ号或者MSN我想当面请教,谢谢了,我的邮箱是taowenyin@tom.com或者Wenyin.tao@tpvaoc.com这两个都可以
能力值:
( LV2,RANK:10 )
25 楼
感谢"guetcw",感谢"加百力"今天晚上把你的代码理解了下,我终于知道中断是怎么操作的,所以来说下我的理解,有什么不对的地方还请朋友们指出来,谢谢
首先看一下工程设置在RO里面设置的是0x0000,可是为什么要设为0x0000呢,而不设为0x40000000,因为0x40000000是片内RAM的地址,他用于在线调试,而一般程序运行,都是从0x0000开始的,这里我有个猜测,应为LPC2210的FLash起始地址是0x80000000,而这里设置的0x0000是相对于0x80000000,即0x80000000=0x0000,0x80000001=0x0001.其次来说说LayOut里面的Object/Symbol和Section,前者设置的是工程的启动代码.o文件,即把startup.s改为startup.o,而后者表示启动代码的开始域名称。
说完工程设置来说说中断的操作,应为汇编是顺序执行的,所以把代码反汇编后在Disassembly最左边的一排指的是指令地址,由于ARM定位在0x00000008处指软中断的地址,所以在汇编程序开始的第三行必须写一个LDR PC, SWI_Addr这样的跳转指令对软件中断进行初始化,当产生中断后就可以直接跳到这里进行操作。这也就是一直困扰我的跳转指令是如何到0x00000008处的。
OVER