VisUAL模拟的ARM板子如图,它没有模拟外设,仅仅模拟了CPU、ROM、RAM。
红色区域不能读不能写,只能运行其中的程序。
RAM可读可写。
根据栈指针指向,可分为满(Full)/空(Empty)
根据压栈时SP的增长方向,可分为增/减:
组合就有四种方式:
常用的是满减
入栈时用STMDB,也可以用STMFD
出栈是用STMIA,也可以用LDMFD
压栈就是先减后存,出栈就是先读后增
MOV只能在寄存器之间移动数据,或者把立即数移动到寄存器中。
位操作
AND R1,R2,#(1<<4)
BIC R1,R2,#(1<<4)
ORR R1,R2,R3
比较
CMP R0,R1;比较R0-R1的结果
CMP R0,#0x12
ADR PC,LR
10进制里,每一位的权重,从右往左数:个十百千万,也就是:10^0, 10^1, 102,103……
16进制里,每一位的权重,从右往左数,分别是:16^0, 16^1, 16^2, 16^3, 16^4, ……
8进制里,每一位的权重,从右往左数,分别是:8^0, 8^1, 8^2, 8^3, 8^4, ……
2进制里,每一位的权重,从右往左数,分别是:2^0, 2^1, 2^2, 2^3, 2^4, ……
10进制,逢10进1,16 = 110^1 +610^0 = 16
16进制:逢16进1, 0x10 = 116^1 + 016^0 = 16
8进制: 逢8进1, 020 = 28^1 + 08^0 = 16
2进制: 逢2进1, 0b10000 = 12^4 + 02^3 + 02^2 + 02^1 +0*2^0 = 16
它们表示的值是一样的。
在代码里,
10进制这样写:123456789,每位最大值为9
16进制这样写:0x12ABCDEF, 每位最大值为15,
A表示10,B表示11,C表示12,D表示13,E表示14,F表示15
8进制这样写:01234,每位最大值为7
2进制这样写:0b0110,每位最大值为1 (注意:C语言没有二进制数值的表示方法)
硬件上用多个晶体管表示数据,晶体管只有2个状态,0和1。——二进制
后续为了简便,使用八进制和十六进制表示。
在C语言中表示:
假设int a = 0x12345678;
16进制数中每位数值占据4 bit;在内存中,是以8个bit作为1byte。
因此0x12345678中每两位作为1byte, 其中0x78是低byte,0x12是高byte。
在内存中的存储方式有两种:
0x12345678的低位(0x78)存在低地址,即方式1,叫做小字节序(little endian)。
0x12345678的低位(0x78)存在低地址,即方式2,叫做大字节序(big endian)。
一般ARM芯片都是小字节序,有些处理器,可以设置某个寄存器,让整个系统使用大字节序或小字节序。
嵌入式开发中,只涉及逻辑移位,不关心符号位,都是补0。
算术移位,需要分有符号型值和无符号型值:
在KEIL的User选项中,如下图添加这两项:
fromelf --bin --output=led.bin Objects\led_c.axf
fromelf --text -a -c --output=led.dis Objects\led_c.axf
然后重新编译,就会得到二进制文件led.bin,反汇编文件led.dis。
使用GCC工具链编译程序时,在Makefile中有这一句:
$(OBJDUMP) -D -m arm led.elf > led.dis # OBJDUMP = arm-linux-gnueabihf-objdump
它就是把可执行程序led.elf,反汇编,得到led.dis。