汇编代码如下,点亮 jz2440 LED1 灯
/*
* 点亮LED2: gpf5
*/
.text
.global _start
_start:
/* 配置GPF5为输出引脚
* 把0x400写到地址0x56000050
*/
ldr r1, =0x56000050
ldr r0, =0x100 /* mov r0, #0x400 */
str r0, [r1]
/* 设置GPF5输出高电平
* 把0写到地址0x56000054
*/
ldr r1, =0x56000054
ldr r0, =0 /* mov r0, #0 */
str r0, [r1]
/* 死循环 while(true) 相当于 */
halt:
b halt
反汇编以后的代码: 上面代码存在伪指令 伪指令转化为在真正 汇编指令
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
最终生成的: led.on.dis
led_on.elf: file format elf32-littlearm
Disassembly of section .text:
伪指令转化为在真正 汇编指令
地址:可以理解为序号,地址在sram定义真正的
地址 机器码 汇编码
00000000 <_start>:
0: e59f1014 ldr r1, [pc, #20] ; 1c <halt+0x4>
4: e3a00c01 mov r0, #256 ; 0x100
8: e5810000 str r0, [r1]
c: e59f100c ldr r1, [pc, #12] ; 20 <halt+0x8>
10: e3a00000 mov r0, #0 ; 0x0
14: e5810000 str r0, [r1]
00000018 <halt>:
18: eafffffe b 18 <halt>
1c: 56000050 .word 0x56000050
20: 56000054 .word 0x56000054
Disassembly of section .ARM.attributes:
00000000 <.ARM.attributes>:
0: 00001741 andeq r1, r0, r1, asr #14
4: 61656100 cmnvs r5, r0, lsl #2
8: 01006962 tsteq r0, r2, ror #18
c: 0000000d andeq r0, r0, sp
10: 00543405 subseq r3, r4, r5, lsl #8
14: 01080206 tsteq r8, r6, lsl #4
地址是nand上的地址 ,cpu 从0地址上读取依次读取机器码,会把所有的机器码loading到内存里面去, 程序运行内存上的机器码
上面代码解读 :
arm 有32个寄存器, r0-r16,
pc : 程序指针 当前地址 +8 , 为什么, pc = 当期地址+8 , 在执行0时候,已经在解析4,读取8在 [这个叫做 汇编的流水线]
pc : 程序寄存器, 当前程序执行在哪里, 所以只有一个,一个国家只有一个皇帝 , pc 指向那条指令, cpu执行哪条 , 程序跑飞了, pc寄存器内容不对了
sp : 栈指针
lr : 包含返回地址, 函数执行完毕要回到原来地方,原来地方就是保持在lr里面
arm 内部的寄存器 + 别名
解析第一条指令 : 0: e59f1014 ldr r1, [pc, #20] ; 1c <halt+0x4> (这就是伪指令的实际汇编)
当前指令地址是0 , pc = 0 + 8 + 20 = 0x1c 去0x1c 读取该地址的值56000050 写入 r1
1c: 56000050 .word 0x56000050
0:表示当前地址 8可以理解为流水 执行当前执行,实际读取往后读取到8地址, 20就是#20
【这里可以这么理解: pc首先指向0,执行该指令, 然后执行0x1c读取寄存器 pc指了2个方法】
第二条: mov r0, #5376 ; 0x1500 把 0x1500 写入 r0 【pc继续往下走,执行第二条】
第三条: e5810000 str r0, [r1] 把 r0 0x1500 写入到 r1 对应内存
第四条: c: e59f100c ldr r1, [pc, #12] ; 20 <halt+0x8>
0xc 【当前指令的地址】+ 8 + 20 = 0x20 去0x20地址读取它的值0x056000054存放到 r1中 r1=0x56000054(cpu把寄存器单做内存来使用)
.....
编译器 把 伪指令转化为在真正 汇编指令
把汇编指令转化为机器码, led.bin内部就是机器码
机器码就是可以烧写到裸机上
汇编码、C都是给程序员一样看的,机器码给机器跑的
在上面基础上要实现功能:
练习: 在 原来编译的 led_on.S 修改 机器码, 点亮 LED2
那么需要去修改机器码, 如何修改机器码 , 就要知道 上面的第二条 mov 指令
这内部是如何转化的 汇编码和机器码
4: e3a00c15 mov 《============》 r0, #5376 ; 0x1500
看芯片手册: ARM Architecture Reference Manual 找到mov 指令
上图解读:
bit[23-20] 1010 表示mov指令
bit[15-12] 0000 表示r0
bit[11-0] 表示0x100
修改机器码, mov r0,#0x400 那么就可以点亮led2了, 如果是汇编,那么如何修改机器码直接
bit[11-0] 12位如何表示 0x100 研究?
分析 :
整个12 位: 高4位 (rotate) + 低8位 (immed_8) = 12
立即数
低8位运算:
高4位运算:
例子:
0x100 二进制 0001 00000000 立即数 1100 0000 0001 如何运算来的
如何运算得到立即数的:
一共32位,1要通过右移编程 ... 0001 ,1 右移24位
ratate = 24/2 = 12 1100 所以立即数就是: 1100 0000 0001
同理要点亮led2:
0x400 而进制 0100 00000000
低8位: 1左移动22位 ---- 0000 0001
高8位: 22/2= 1 ---- 1011
立即数: 1011 0000 0001
用UE打开led.bin,修改地址 e3a00c01 为 e3a00cb1烧进去 ,那么就可以点亮led2了
0x100是不是 1循环右移 24位 ? 这里以32位为准,
00000....000000000000000(23个0)1 右移动24位就是 00-00(23位)100000000(8个0) 23+1+8=32
那么我们把 :
12位: 高4位 (rotate,移位数) + 低8位 (immed_8 立即数,循环右移动2*rotate位)
rotate = 12 immed_8 = 1
-比如: 0x100 立即数: 1100 00000001
如果我们要点亮第二个LED灯: 此时 ldr r0, =0x400 /* mov r0, #0x400 */
0x400 1右移动 22位 0000...00(21个0)10000....0(10个0)
rotate = 22/2=11 1011
immed_8 = 1 0000 0001
修改: bit[11-0] 其他的不变, 在LED的基础上
修改led.bin
刷进去点亮的是led2
资源地址:修改机器码点亮led2-资料2021-11-21.zip-Unix文档类资源-CSDN下载