我们以S3C2440的GPIO汇编代码为例:
.text
.global _start
_start:
//把0x100写到地址0x56000050上,配置gpf4为输出引脚,
ldr r1, =0x56000050
ldr r0,=0x100
str r0,[r1]
//把0x00写到地址0x56000054上,设置gpf4输出低电平,
ldr r1, =0x56000054
ldr r0,=0x00
str r0,[r1]
//死循环
halt:
b halt
在上面的代码中,我们使用了伪指令ldr把值存放到r0,r1寄存器里面,所谓伪指令就是假的指令,它并不是真正的ARM指令,我们通过对elf文件进行反汇编看一下伪指令ldr对应哪些真正的汇编指令,我们修改下我们之前用的makefile文件对elf文件进行反汇编,修改后的makefile内容如下:
all:
arm-linux-gcc -c -o led_on.o led_on.S
arm-linux-ld -Ttext 0 led_on.o -o led_on.elf
arm-linux-objcopy -O binary -S led_on.elf led_on.bin
arm-linux-objdump -D led_on.elf > led_on.dis
clean:
rm *.bin *.o *.elf
反汇编之后得到的dis文件如下:
led_on.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 18: eafffffe b 18 1c: 56000050 undefined 20: 56000054 undefined ldr r1, =0x56000050被转换成了ldr r1, [pc, #20],从内存中读取值,把1c地址对应的值56000050存放到r1里面。 ldr r0,=0x100 直接被转换成了mov指令。 ldr r1, =0x56000054被转换成了ldr r1, [pc, #12] ; 20 <.text+0x20>,把20地址对应的值56000054存放到r1里面。 上面最左边的0 4 8这一列代表地址,第二列代表机器码,第三列代表汇编码。 我们之前说s3c2440里面有cpu,cpu里面一共更有r0-r15一共16个寄存器,这里我们使用pc值表示寄存器。我们看如下表格 左边是R0-R15,右边a1,a2....是它的别名。 pc是指program counter程序计数器,当你把一个地址写到PC寄存器的时候,程序就会跳到那个地址去,pc的值是当前指令的地址加上8,为什么要加上8,是因为ARM系统里面CPU是以流水线的方式去执行的,流水线只是当前执行地址A的指令,那么已经在对地址A+4的指令进行译码,已经在读取地址A+8的指令,这个A+8就是PC的值。 lr是link register,用来保存返回地址,我们使用函数调用的时候,执行完这个函数要跳回原来的地方,这个原来的地方就保存在这个lr寄存器里面。 sp是stack pointer,栈指针。 我们再看前面反汇编生成的代码,第一行, 0: e59f1014 ldr r1, [pc, #20] ; 1c <.text+0x1c> r1等于PC+20,当前指令是0,PC 是当前指令的地址加上8也就是8,那么r1=8+20=28=0x1c,然后去0x1c这个地址去读取内存的值0x56000050写入到r1里面。 另外我们看到反汇编生成的文件的第二列是机器码,也就是bin文件里面的内容,但是我们用notpad++打开led_on.bin之后发现并不是e5 9f 10 14这种,这是因为notpad++这个软件的插件有问题,我们用ultraedit软件打开bin文件之后就好了。 下面我们修改程序,点亮LED2, 方法1:修改之前的汇编代码点亮LED2 我们打开芯片手册,搜索GPFCON,发现只要把0x400写到GPFCON寄存器就可以把gpf5设置为输出引脚了, .text .global _start _start: //把0x400写到地址0x56000050上,配置gpf5为输出引脚, ldr r1, =0x56000050 ldr r0,=0x400 str r0,[r1] //把0x00写到地址0x56000054上,设置gpf5输出低电平, ldr r1, =0x56000054 ldr r0,=0x00 str r0,[r1] //死循环 halt: b halt 然后我们重新编译即可。 方法二:直接修改之前控制LED时的led_on.bin,点亮LED2。 led_on.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 18: eafffffe b 18 1c: 56000050 undefined 20: 56000054 undefined 我们只要把上面mov r0, #256 ; 0x100 对应的机器码e3a00c01修改为mov r0, #1024 ; 0x400对应的机器码就可以了,那么我们要知道机器码e3a00c01里面哪一部分对应的是0x100,然后修改成0x400就可以了,那么我们看一下MOV指令对应的机器码的格式, mov r0, #256 ; 0x100的时候对应的机器码是e3a00c01=1110,0011,1010,0000,0000,1100,0000,0001。 前面bit24-21是1101用来表示MOV指令,bit15-12是0000表示r0,后面的bit11-0就用来表示0x100. 那么我们只需要修改低12位就可以表示mov r0, #1024。 那么低12位怎么表示一个立即数呢,这12位拆分成高四位和低八位。,表示方式如下: 我们看一下0x100对应的机器码的低12位是1100,0000,0001,也就是0000,0001右移2*1100,也就是0000,0001右移24位. 0000,0001循环右移1位成为1000,0000,0000,0000,0000,0000,0000,0000 然后再循环右移23位成为0000,0000,0000,0000,0000,0001,0000,0000=0x100. 那么0x400=0000,0000,0000,0000,0000,0100,0000,0000,那么它可以由0000,0001右移22位得到。那么可以得到0x400对应的rotate=22/2=11=1011, 那么可以得到0x400对应的机器码的12位是1011,0000,0001, 那么由mov r0, #256 ; 0x100的时候对应的机器码是e3a00c01=1110,0011,1010,0000,0000,1100,0000,0001。可以知道 mov r0, #1024 ; 0x400对应的机器码是1110,0011,1010,0000,0000,1011,0000,0001=e3a00b01. 通过上面的对比可以发现,显然是直接修改汇编代码比较简单,修改机器码的方法太麻烦了。
上一篇:S3C2440裸机------时钟
下一篇:S3C2440框架与启动过程
推荐阅读最新更新时间:2024-11-12 07:24
设计资源 培训 开发板 精华推荐
- LT8714IFE 300kHz、5V 输入的典型应用电路产生 -2.5V 至 2.5V 输出,提供 -5A 至 5A 的输出电流同步 PWM 控制器
- 使用 ON Semiconductor 的 LV4924VH 的参考设计
- STEVAL-MKI126V1,基于MP45DT02的智能语音演示板
- LT3091MPR 基准缓冲器的典型应用
- 具有数字输入和处理功能的高保真 175W D 类音频放大器参考设计
- 具有大接地回路电压的 LTC2862AHDD-1 RS485 链路的典型应用
- SIP79L09AC 三端小电流负电压稳压器典型应用
- LT6657BHMS8-5 扩展电源范围基准的典型应用电路
- SG1842交直流开关转换器典型应用电路
- 使用 Analog Devices 的 LTC2862AHS8-2 的参考设计