实验(6)位置无关跳转bl的深入理解
实验目的:连接地址非零,使用位置无关跳转bl指令,实现函数的顺利跳入,连接地址非零(连接地址为0x00000008),不搬移代码,但能进入主函数和中断。
代码清单:
(1)head.S
@**************************************************************************
@ File:head.S
@功能:初始化,设置中断模式、系统模式的栈,设置好中断处理函数
@**************************************************************************
.extern main
.text
.global _start
_start:
@**************************************************************************
@中断向量,本程序中,除Reset和HandleIRQ外,其他异常都没有使用
@**************************************************************************
@0x00地址处的指令为"b Reset",在系统复位后,这条指令将跳去执行标号"Reset"开始的代码
b Reset
@0x04:未定义指令终止模式的向量地址
HandleUndef:
b HandleUndef
@0x08:管理模式的向量地址,通过SWI指令进入此模式
HandleSWI:
b HandleSWI
@0x0c:指令预取终止导致的异常的向量地址
HandlePrefetchAbort:
b HandlePrefetchAbort
@0x10:数据访问终止导致的异常的向量地址
HandleDataAbort:
b HandleDataAbort
@0x14:保留
HandleNotUsed:
b HandleNotUsed
@0x18:中断模式的向量地址
b HandleIRQ
@0x1c:快中断模式的向量地址
HandleFIQ:
b HandleFIQ
HandleIRQ: @HandleIRQ开始的代码用于处理中断
sub lr,lr,#4 @计算中断处理完毕后的返回地址
stmdb sp!,{r0-r12,lr} @保存使用到的寄存器
bl EINT_Handle @调用中断服务函数,在interrupt.c中
ldmia sp!,{r0-r12,pc}^ @中断返回,^表示将spsr的值复制到cpsr
Reset:
ldr sp,=4096
@设置栈指针,以下都是C函数,调用前需要设好栈。栈是用 @来保存C函数的变量和返回地址
bl disable_watch_dog @关闭WATCHDOG,否则CPU会不断重启
msr cpsr_c, #0xd2@进入中断模式
ldr sp, =3072@设置中断模式栈指针
msr cpsr_c, #0xdf @进入系统模式
ldr sp, =4096@设置系统模式栈指针,
bl init_led@初始化LED的GPIO管脚
bl init_irq@调用中断初始化函数,在init.c中
msr cpsr_c, #0x5f@设置I-bit=0,开IRQ中断
bl main@调用main函数,千万不要用ldr pc,=main,这样会导致找不到main的情况
(2)int.c
#include "s3c24xx.h"
#define GPB5_out (1<<(5*2))
#define GPB6_out (1<<(6*2))
#define GPB7_out (1<<(7*2))
#define GPB8_out (1<<(8*2))
#define GPB0_timer0 (2<<(0*2))
#define GPB5_msk(3<<(5*2))
#define GPB6_msk(3<<(6*2))
#define GPB7_msk(3<<(7*2))
#define GPB8_msk(3<<(8*2))
#define GPF0_eint (0x2<<(0*2))
#define GPF1_eint (0x2<<(1*2))
#define GPF2_eint (0x2<<(2*2))
#define GPF4_eint (0x2<<(4*2))
#define GPF0_msk (3<<(0*2))
#define GPF1_msk (3<<(1*2))
#define GPF2_msk (3<<(2*2))
#define GPF4_msk (3<<(4*2))
#define S3C2410_MPLL_200MHZ ((0x5c<<12)|(0x04<<4)|(0x00))
#define S3C2440_MPLL_200MHZ ((0x5c<<12)|(0x01<<4)|(0x02))
#define PCLK (50000000)
void disable_watch_dog(void)
{
WTCON = 0;
}
void init_led(void)
{
GPBCON&=~(GPB5_msk|GPB6_msk|GPB7_msk|GPB8_msk);
GPBCON|=GPB5_out|GPB6_out|GPB7_out|GPB8_out;
GPBDAT|=0x0f<<5;
}
void init_irq( )
{
GPFCON&=~(GPF0_msk|GPF1_msk|GPF2_msk|GPF4_msk);
GPFCON|=GPF0_eint|GPF1_eint|GPF2_eint|GPF4_eint;
EINTMASK&=~(1<<4);
PRIORITY&=(~(1<<6))&(~(1<<1))&(~(1<<0));
INTMSK &=(~(1<<0))&(~(1<<1))&(~(1<<2))&(~(1<<4));
}
void memsetup(void)
{
volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;
p[0] = 0x22011110;//BWSCON
}
(3)interrupt.c
#include "s3c24xx.h"
int EINT_Handle()
{
unsigned long oft = INTOFFSET;
GPBDAT=~(0x0f<<5);
if( oft == 4 )
EINTPEND = (1<<4); // EINT8_23ºÏÓÃIRQ5
SRCPND = 1<<oft;
INTPND = 1<<oft;
return 0;
}
(4) s3c24xx.h
/* WOTCH DOG register */
#define WTCON (*(volatile unsigned long *)0x53000000)
/* SDRAM regisers */
#define MEM_CTL_BASE 0x48000000
#define SDRAM_BASE 0x30000000
/* NAND Flash registers */
#define NFCONF (*(volatile unsigned int *)0x4e000000)
#define NFCMD (*(volatile unsigned char *)0x4e000004)
#define NFADDR (*(volatile unsigned char *)0x4e000008)
#define NFDATA (*(volatile unsigned char *)0x4e00000c)
#define NFSTAT (*(volatile unsigned char *)0x4e000010)
/*GPIO registers*/
#define GPBCON (*(volatile unsigned long *)0x56000010)
#define GPBDAT (*(volatile unsigned long *)0x56000014)
#define GPFCON (*(volatile unsigned long *)0x56000050)
#define GPFDAT (*(volatile unsigned long *)0x56000054)
#define GPFUP (*(volatile unsigned long *)0x56000058)
#define GPGCON (*(volatile unsigned long *)0x56000060)
#define GPGDAT (*(volatile unsigned long *)0x56000064)
#define GPGUP (*(volatile unsigned long *)0x56000068)
#define GPHCON (*(volatile unsigned long *)0x56000070)
#define GPHDAT (*(volatile unsigned long *)0x56000074)
#define GPHUP (*(volatile unsigned long *)0x56000078)
/*UART registers*/
#define ULCON0 (*(volatile unsigned long *)0x50000000)
#define UCON0 (*(volatile unsigned long *)0x50000004)
#define UFCON0 (*(volatile unsigned long *)0x50000008)
#define UMCON0 (*(volatile unsigned long *)0x5000000c)
#define UTRSTAT0 (*(volatile unsigned long *)0x50000010)
#define UTXH0 (*(volatile unsigned char *)0x50000020)
#define URXH0 (*(volatile unsigned char *)0x50000024)
#define UBRDIV0 (*(volatile unsigned long *)0x50000028)
/*interrupt registes*/
#define SRCPND (*(volatile unsigned long *)0x4A000000)
#define INTMOD (*(volatile unsigned long *)0x4A000004)
#define INTMSK (*(volatile unsigned long *)0x4A000008)
#define PRIORITY (*(volatile unsigned long *)0x4A00000c)
#define INTPND (*(volatile unsigned long *)0x4A000010)
#define INTOFFSET (*(volatile unsigned long *)0x4A000014)
#define SUBSRCPND (*(volatile unsigned long *)0x4A000018)
#define INTSUBMSK (*(volatile unsigned long *)0x4A00001c)
/*external interrupt registers*/
#define EINTMASK(*(volatile unsigned long *)0x560000a4)
#define EINTPEND(*(volatile unsigned long *)0x560000a8)
/*clockregisters*/
#define LOCKTIME (*(volatile unsigned long*)0x4c000000)
#define MPLLCON (*(volatile unsigned long*)0x4c000004)
#define UPLLCON (*(volatile unsigned long*)0x4c000008)
#define CLKCON (*(volatile unsigned long*)0x4c00000c)
#define CLKSLOW (*(volatile unsigned long*)0x4c000010)
#define CLKDIVN (*(volatile unsigned long*)0x4c000014)
/*PWM&Timerregisters*/
#define TCFG0 (*(volatile unsigned long*)0x51000000)
#define TCFG1 (*(volatile unsigned long*)0x51000004)
#define TCON(*(volatile unsigned long*)0x51000008)
#define TCNTB0(*(volatile unsigned long*)0x5100000c)
#define TCMPB0 (*(volatile unsigned long*)0x51000010)
#define TCNTO0 (*(volatile unsigned long*)0x51000014)
#define GSTATUS1(*(volatile unsigned long*)0x560000B0)
#define U16 unsigned short int
(5)makefile:
ARCH = arm
-mcpu=s3c2440
objs := head.o init.o interrupt.o main.o
int.bin: $(objs)
arm-linux-ld -Ttext 0x00000008 -o int_elf $^
arm-linux-objcopy -O binary -S int_elf int.bin
arm-linux-objdump -D -m arm int_elf > int.dis
%.o:%.c
arm-linux-gcc -nostdlib -g -c -o $@ $<
%.o:%.S
arm-linux-gcc -nostdlib -g -c -o $@ $<
clean:
rm -f int.bin int_elf int.dis *.o
Q&A:
实验思考:
(1)为什么没有把代码搬移到对应的运行地址上,却能进入主函数呢?原因在于跳入主函数的时候采取与位置无关的跳转指令bl。
(2)为什么能顺利进入中断响应中断函数,因为代码被自动搬移到SRAM的时候,加载地址就是从0开始的,一旦中断发生,pc直接跳入到0x00000018上,用位置无关跳转指令b HandleIRQ,即可进入中断处理函数中。
(3)如果把b HandleIRQ改成ldr pc,= HandleIRQ,还能否进入中断?
(4)本实验连接地址和加载地址不一致,但是缺能顺利进入主函数,也能顺利进入中断,关键原因是利用与位置无关的跳转,那么什么是与位置无关呢?
(5)请对比实验4和实验5,体会三个实验的区别和联系
(6)查阅网络资源,将所有与位置无关的跳转指令列出来。
e