直接修改修改bin文件改变点亮的LED
不管我们在计算机上做了多么复杂的动作,包括编写C语言代码、汇编代码、makefile文件等,最后都只不过是为了得到一个bin文件。因此,原则上当我们失去所有工具时,只要你足够强大,就能拿着一些官方文档直接制作bin文件,然后点亮一个LED灯。
一. 点亮LED1
在JZ2440中,LED原理图如下所示:
从上面两张图可知,JZ2440的LED为低电平启动,LED与GPIO引脚对应关系如下:
| 二极管 | 引脚 |
|---|---|
| nLED_1 | GPF4 |
| nLED_2 | GPF5 |
| nLED_4 | GPF6 |
接下来,打开S3C2440A芯片手册,找到PORT F CONTROL REGISTERS(GPFCON, GPFDAT) ,如下图所示:
从上图可知,GPFCON的地址为0X56000050,GFPDAT的地址为0X56000054,并且GPFCON[9:8]控制GPF4,将这两位设置成01就能够将GPF4设置成输出。
再来看GPFDAT寄存器表格:
从Description可知,写1就是高电平,写0就是低电平。
点点LED1的汇编代码led1.S如下所示
.text //代码段
.global _start
_start:
//配置GFPCON,GPFCON[9:8]写为01
ldr r1,=0x56000050
ldr r0,=0x100
str r0,[r1]
//配置GPFDAT,简单粗暴全部写0即可将LED1点亮
ldr r1,=0x56000054
ldr r0,=0
str r0,[r1]
//死循环 防止CPU乱执行
halt:
b halt
并编写Makefile文件
all:
arm-linux-gcc -c -o led1.o led1.S #编译
arm-linux-ld -Ttext 0 led1.o -o led1.elf #链接
arm-linux-objcopy -O binary -S led1.elf led1.bin #elf转换为bin文件
clean:
rm *.bin *.o *.elf
将bin文件烧写到JZ2440右边第一个LED就亮了。
从上面的代码可知,S3C2440点亮LED比起STM32要简单许多,前面没有时钟配置的操作。猜测是S3C2440的定位不是低功耗单片机,因此时钟默认都是打开的,然后直接配置控制寄存器和数据寄存器就可以。
二. 分析反汇编文件
在命令行中输入:
arm-linux-objdump -D led1.elf >led1.dis
就可得到led.bin的反汇编文件,注意反汇编文件是.dis。该文件内容如下:
led1.elf: file format elf32-littlearm
Disassembly of section .text:
00000000 <_start>:
0: e59f1014 ldr r1, [pc, #20] ; 1c <.text+0x1c>
4: e3a00c01 mov r0, #256 ; 0x100
8: e5810000 str r0, [r1]
c: e59f100c ldr r1, [pc, #12] ; 20 <.text+0x20>
10: e3a00000 mov r0, #0 ; 0x0
14: e5810000 str r0, [r1]
00000018 <halt>:
18: eafffffe b 18 <halt>
1c: 56000050 undefined
20: 56000054 undefined
其中第一列是指令的地址,第二列是机器指令码,第三列是反汇编出来的汇编码。
看第一条指令:
0: e59f1014 ldr r1, [pc, #20]
对应的是ldr r1,=0x56000050,0x56000050的地址是0x1c,那为什么是[pc,#20]呢?
首先,#20表示十进制数20,[pc,#20]表示将pc的值加上20。
然后,arm采用流水线的形式执行指令。当CPU在执行A地址的指令时,地址A+4的指令就会被译码,而地址A+8的指令就会被加载。因此,pc=当前执行指令的地址+8。
最后,[pc,#20]就是0+8+20=28=0X1c,就和0x56000050的地址对应上了
接下来看最关键的两条指令
4: e3a00c01 mov r0, #256
和
10: e3a00000 mov r0, #0 ; 0x0
4:是配置寄存,10:是写入数据寄存器,由于输出低电平直接写全0就可以了,修改代码只要修改4:这一句。
三. 修改led1.bin文件
先来看看led1.bin的内容,这里用Hex Editor Neo打开。
为了方便查看,点击View → Group By → Double words。
可以看到,led1.bin文件的16进制码和led1.dis文件的机器码是按顺序对应的。
因此,只要修改01列的数据就可以了。
现在,打开ARM Architecture Reference Manual.pdf(可直接google得到)查看mov指令的格式。
再来看看e3a00c01的二进制形式:[31:28]不用管[27:21]与mov指令的格式一致[20:16]不懂,也不管了[15:12]代表寄存器编号,这里是R0,因此全0就可以了[11:0]是shifter_operand,要重点说明一下。
[11:0]又分为[11:8](记为rotate)和[7:0](记为immed_8),mov r0, #256中的#256就是由shifter_operand转化而来。转化关系如下:立即数=immed_8 >> 2*rotate#256为0X100,可表示成0x1循环右移24(总共32位)位来表示,因此,[11:8]=1100b=0xc=12,而2*12=24
为了点亮nLED_2,要将GPFCON[11:10]设置成01b,汇编语句为
ldr r0,=0x400
0x400可以表示成0x1循环右移22位,因此:rotate=11=0xbimmed_8=0x01
则shifter_operand=0xb01
将led1.bin文件的01列改为e3a00b01,另存为led2.bin,然后将led2.bin烧写到JZ2440就可以看到中间的LED被点亮了。