【S3C2440】第14课、异常与中断之学习笔记

第14课、异常与中断
节4、und异常模式程序示例
1、30000020 <und_string>:
(省略)
3000003c: 216e6f69 cmncs lr, r9, ror #30

30000041 :
(省略)
答:因此,应在string之后,进行.align 4 操作;
ALIGN

2、关于未定义异常指令的跳转的正确格式
0) b do_und //可正常跳转,但是当Nand启动且该汇编文件过大时,会造成未定义异常处理程序中的函数在SRAM的4K之外

  1. ldr pc, do_und
  2. ldr pc, =do_und
  3. ldr pc, und_addr
    und_addr:
    .word do_und
    查看两组的反汇编,探究正确用法
1)方法1:
	_start:
		b reset
		ldr pc, do_und
	do_und:		
	und_code:
		bl print4
		.word 0xdeadc0de
	und_code:
		bl print4
		.word 0xdeadc0de
		bl print2

不可以正常打印,反复打印:
2_123
1_abc
4_678
反汇编如下:
30000000 <_start>:
30000000: ea000012 b 30000050
30000004: e51ff004 ldr pc, [pc, #-4] ; 30000008 <do_und>
30000008 <do_und>:
300000c8 <und_code>:
300000c8: eb00011f bl 3000054c
300000cc: deadc0de mcrle 0, 5, ip, cr13, cr14, {6}
300000d0: eb00010f bl 30000514 <pri2)方法2:
_start:
b reset
ldr pc, =do_und
do_und:
und_code:
bl print4
.word 0xdeadc0de
und_code:
bl print4
.word 0xdeadc0de
bl print2
可以正常打印,打印如下:
2_123
1_abc
4_678
Exception! cpsr = 0x600000db
Undefined instruction exception!
2_123

Wakakaka
0x00000000
0xabcdef12
0x00000012
0x0000000c
Aa1Bb2Cc3Dd4Ee5Ff6Gg7Hh8Ii9Jj:Kk;Ll<Mm=

反汇编如下:
30000000 <_start>:
30000000: ea000012 b 30000050
30000004: e59ff0d0 ldr pc, [pc, #208] ; 300000dc <.text+0xdc>
30000008 <do_und>:
300000d8 :
300000d8: eafffffe b 300000d8
300000dc: 30000008 andcc r0, r0, r8
300000c8 <und_code>:
300000c8: eb00011f bl 3000054c
300000cc: deadc0de mcrle 0, 5, ip, cr13, cr14, {6}
300000d0: eb00010f bl 30000514 <pri3)方法3:
_start:
b reset
ldr pc, und_addr
und_addr:
.word do_und
do_und:
und_code:
bl print4
.word 0xdeadc0de
und_code:
bl print4
.word 0xdeadc0de
bl print2
可以正常打印,打印结果如下:
2_123
1_abc
4_678
Exception! cpsr = 0x600000db
Undefined instruction exception!
2_123

Wakakaka
0x00000000
0xabcdef12
0x00000012
0x0000000c
Aa1Bb2Cc3Dd4Ee5Ff6Gg7Hh8Ii

反汇编如下:
30000000 <_start>:
30000000: ea000012 b 30000050
30000004: e51ff004 ldr pc, [pc, #-4] ; 30000008 <und_addr>
30000008 <und_addr>:
30000008: 3000000c andcc r0, r0, ip
3000000c <do_und>:
300000c8 <und_code>:
300000c8: eb00011f bl 3000054c
300000cc: deadc0de mcrle 0, 5, ip, cr13, cr14, {6}
300000d0: eb00010f bl 30000514

3、
现象1:反复打印
123
abc
123
abc
现象2:打印一次
2_123
1_abc
4_678
3_345
Exception! cpsr = 0x600000db
Undefined instruction exception!

现象分析和解决方案:
现象1分析:程序反复执行reset, sdram, 未定义指令(并未进入未定义处理程序);
bug指令:ldr pc, do_und
现象2分析:程序执行reset, sdram, 未定义指令并跳转到未定义指令(进入未定义处理程序);
bug指令:ldmia sp!, {r0-r12, pc}后的‘^’未加;

4、关于汇编字符串的引用的方法

ldr r1, =und_string
	bl print_exception
und_string:
	.string "Undefined instruction exception!"

ldr r1, =.string或者 ldr r1, =string
bl print_exception
.string "Undefined instruction exception!"果:Error!
start.o(.text+0xd0): In function halt': : undefined reference to.string’

5、关于异常与中断跳转指令 b do_und;的隐患:b do_und为相对跳转,异常处理程序do_und内的函数
调用指令 bl print_exception也是相对跳转,如果Nand启动,函数print_exception在4K之外,该指令
必定出错!因此,为保险,应该在重定位之后就将程序进入SDRAM执行!

修改代码:

_start:
		ldr pc, =do_und
		ldr pc, =do_swi
	reset:
		ldr pc, =sdram

但是,问题:ldr Rd, =xxx; 对目标寄存器Rd的赋值操作,是先把xxx(地址)的值保存到内存中,
然后再读出该内存空间中的值(所代表的地址)赋值给目标寄存器Rd。xxx所保存的内存由编译器
来决定,通常放在该汇编文件的结尾(如)的内存空间中。当start.S很大且Nand启动时,
有可能去SRAM的4K空间之外取值(地址)do_und/do_swi/…。为防止汇编大小大于4K,Nand启动时,
4K之外的内存无法被读取到,现修改do_und/do_swi/…的内存空间到SRAM的前面。
修改代码:

_start:
		b reset          /* vector 0 : reset */
		ldr pc, und_addr /* vector 4 : und */
		ldr pc, swi_addr /* vector 8 : swi */
	und_addr:
		.word do_und
	swi_addr:
		.word do_swi
	do_und:	...
	do_swi:	...

但是,问题:ldr Rd, =sdram/sp/0x4C000014/…
异常与中断的跳转和处理程序经过设置已经在SRAM/Norflash的空间的前面存储和运行,但是在reset:内,
重定位之前的一些存储伪指令:ldr Rd, =sdram/0x4C000014/sp, 的存储空间也应汇编文件的结尾(如)
的内存空间中,这种情况怎么处理?
其中,实例代码如下:
(0.)代码do_und: ldr sp, =0x34000000的反汇编如下:
30000014 <do_und>:
30000014: e3a0d30d mov sp, #872415232 ; 0x34000000
所以,伪指令ldr sp, =0x34000000类型不论;
(1.)ldr pc, =do_und/do_swi/…/und_string/swi_string由于异常处理跳转到SDRAM执行,不论;
(2.)ldr pc, =main是在ldr pc, =sdram之后跳转到SDRAM中执行的,也不论;
(3.)ldr pc, =sdram伪指令的反汇编代码如下:
30000090 :
30000108: e59ff038 ldr pc, [pc, #56] ; 30000148 <.text+0x148>
3000010c :

30000124 :

30000148: 3000010c andcc r0, r0, ip, lsl #2
(4.)reset:
/* 关闭看门狗 /
ldr r0, =0x53000000
ldr r1, =0
ldr r0, =0x4C000014(CLKDIVN)的反汇编:
30000090 :
30000090: e3a00453 mov r0, #1392508928 ; 0x53000000
30000094: e3a01000 mov r1, #0 ; 0x0
3000009c: e3a00313 mov r0, #1275068416 ; 0x4c000000
300000a8: e59f0084 ldr r0, [pc, #132] ; 30000134 <.text+0x134>
30000124 :
30000134: 4c000014 stcmi 0, cr0, [r0], {20}
--------------------------------------------------------------------
reset:
ldr sp, =0x40000000+4096 /
先假设是nor启动 /的反汇编如下:
30000090 :
300000e0: e59fd058 ldr sp, [pc, #88] ; 30000140 <.text+0x140>
30000124 :
30000140: 40001000 andmi r1, r0, r0
-------------------------------------------------------------------
reset:(结尾处)
/
设置 sp_usr */
ldr sp, =0x33f00000
ldr pc, =sdram的反汇编如下:
30000104: e59fd038 ldr sp, [pc, #56] ; 30000144 <.text+0x144>
30000108: e59ff038 ldr pc, [pc, #56] ; 30000148 <.text+0x148>
30000124 :
30000144: 33f00000 mvnccs r0, #0 ; 0x0
30000148: 3000010c andcc r0, r0, ip, lsl #2
对情景(3.)(4.)的分析,正如前面把【ldr pc, =do_und;】改为【ldr pc, und_addr; und_addr:.word do_und; 】的担忧一样,
ldr Rd, =xxx; 对目标寄存器Rd的赋值操作,是先把xxx(地址)的值保存到内存中,然后再读出该内存空间中的值(所代表的地址)
赋值给目标寄存器Rd。xxx所保存的内存由编译器来决定,通常放在该汇编文件的结尾(如)的内存空间中。
当start.S很大且Nand启动时,有可能去SRAM的4K空间之外取值(地址)sdram/0x4C000014/sp,需不需要修改标号sdram的内存到SRAM
的前面?还有,对于特殊功能寄存器和sp栈的设置的读存操作如何处理?
对于这些伪指令的使用,可不可以使用mov/movs来代替其功能?

任务:
1、验证上面的猜想
2、向程序1
3、次而程序2

节7、按键外部中断程序的疑惑:
1、调用其他文件的函数,不需要提前声明吗?
例如:

include "s3c2440_soc.h"
 #include "uart.h"
  #include "init.h"

int main(void)
{
	led_init();
	interrupt_init();  /* 初始化中断控制器 */
	key_eint_init();   /* 初始化按键, 设为中断源 */
	...
}

中main()函数开头的三个函数都没有声明直接调用,而且编译完全没有问题;
答:(查帖:https://bbs.csdn.net/topics/390994678?page=1 :没有声明的函数为什么可以调用?)
a.在c语言中全局的函数和变量是对每个文件可见的,但在别的文件中变量需要用extern声明,才可用;而函数不需要
b.函数不需extern声明,即可在其他的文件可用,而变量必须用extern声明才可用。
c.只要声明全局变量就默认 前面加extern(程序员可以不加,但编译器默认加上)
d.早期的编译器允许这种做法,再现在的C标准要求函数使用前必须先声明,但看在那些业已存在的
大量程序的面子上,有很多编译器直到现在还容许这种做法。

2、2440手册中,GPFDAT [7:0] 位 描述 初始状态
当端口配置为输入端口时,相应位为引脚状态。
当端口配置为输出端口时,引脚状态将与相应位相同。
当端口配置为功能引脚,将读取到未定义值。
问题:GPF作为外部中断引脚时,是不是说GPFDAT为随机值不可用?
但为什么视频中的代码把GPFDAT作为 if() 的条件判断成功编译且烧写成功?
能烧写成功是不是说 “当端口配置为功能引脚,和配置为输出端口时一样,引脚状态将与相应位相同”?
第14课,第7节的视频和代码

3、按键外部中断其中两个按键不管用的原因查找
只有一个键的中断有用,但也证明
初始化外部中断源时,其中两个键的GPGCON写成GPFCON!
程序调试方法:验证并调换出出错的文件,然后与正确文件逐行对比查找原因

第8节:
普通变量的定义格式: int i;
函数指针变量的定义格式: int (*p)(int, int)
区别: 一个变量名在表达式后面,一个变量名在表达式中间!

函数指针的本质还是指针,是变量!
方式1:
int max(int, int);
int min(int, int);
int (*p)(int, int);
方式2:
int fun(int, int, int(*p)(int, int))
int max(int, int);
int min(int, int);
总结:方式1还是建立一个和原函数平等的函数,然后通过这个通用的函数调用同类的函数;
方式2已经把原函数的参数和其函数指针作为形参全部归到一个上一级的新的函数内,这样调用新的函数,更加直观直接

typedef的用法:
1)
2)命名一个新的类型名代表数组类型
typedef int Arr[10];

4)命名一个新的类型名代表指向函数的指针类型:
typedef int(*Pointer)();
Pointer pf;
作用:采用如同定义变量的方法那样先生成一个类型名,然后用它去定义变量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值