什么?高级语言不就是拿来简化阅读和编写的,怎么还会有人去学汇编...
哎哎,作者人菜瘾大,想看看代码最底层的逻辑。 话不多说,直接开始吧
如果想要更好的可读性,建议看一看cpp insights网站https://cppinsights.io/
开始¶
直接学指令真难懂吧! 还是先上一个例子
编译器¶
指针¶
用的最多的一般就是这个 qword ptr,代表quad word pointer,64位指针,该操作数后续接指针地址的值,另一个操作数则可以是寄存器的值、也可以是单独的值
还有:
- dword ptr
:32 位(4 字节)指针
- word ptr
:16 位(2 字节)指针
- byte ptr
:8 位(1 字节)指针
寄存器¶
在CPU中,程序员能够用指令读写的部件只有寄存器
寄存器用来存放临时数据,以8086CPU为例,所有的寄存器都是16位的。 1. %rax 作为函数返回值使用。 2. %rsp 栈指针寄存器,指向栈顶,Stack Point 3. %rbp 栈桢指针,指向栈基,大部分优化编译-O2其实已经去除了帧指针。Base Point 4. %rdi,%rsi,%rdx,%rcx,%r8,%r9 用作函数参数,依次对应第1参数,第2参数。。。 5. %rbx,%r12,%r13,%14,%15 用作数据存储,遵循被调用者使用规则,简单说就是随便用,调用子函数之前要备份它,以防他被修改 6. %r10,%r11 用作数据存储,遵循调用者使用规则,简单说就是使用之前要先保存原值 7. %rip: 相当于PC指针指向当前的指令地址,指向下一条要执行的指令
而带e前缀的寄存器,一般都是只用后8位
地址计算:物理地址,段地址和偏移地址¶
地址总线一般都设计为20位。 因此用16位段地址+16位偏移地址来计算出物理地址 * 物理地址=段地址X16 + 偏移地址 也就是说段地址在16进制上左移一位再加上偏移地址就是实际的物理地址。
1230:00c8
即 段:1230 + 偏移:00c8
物理地址 = 12300 + 00c8 = 123c8
8086CPU 内存分布¶
由于8086cpu位数只有16位,总线位数20位,需要段地址+偏移地址寻址。
段寄存器¶
8086由四个段寄存器CS、DS、SS、ES,总之后面加个S(Segament),你就懂这个是段寄存器了! 最核心的两个:CS和IP,CS:IP指向的内容将当作指令执行
DS和内存单元[Address]
¶
一般直接赋值地址的,赋值的都是段地址
mov bx.1000H
[···]
来表示一个单元,而里面的值就是偏移地址
mov al,[0]
[···]
就知道10000H数据,为什么呢?
这是因为8086CPU默认自动取ds中的数据为内存单元的段地址。因此,我们必须要给ds赋值
mov dx,bx
mov bx.1000H
mov dx,bx
mov al,[0]
现代CPU 内存分布¶
而现代CPU足足有64位,总线最大也有48位且一般都有32GB也是35位。后续都将以现代CPU内存分布进行总结
寄存器的变化¶
8086CPU --> 32位x86CPU
ax–>EAX
BP–>EBP(extended base pointer)
push,pop¶
push和pop是可以在寄存器和内存传输指令的!!! * push用来将后面的数据入栈,pop用来将后面的数据出栈
- push 寄存器
- push 段寄存器
- push 内存单元
mov、add、sub¶
他们的特点都是由两个操作数,
1. mov¶
比如mov,从第二个操作数将内存中的数据值向第一个操作数传送过去: * mov 寄存器 ,数据 AX= 18 * mov 寄存器,寄存器 AX = BX * mov 寄存器,内存单元 AX = 内存单元 * mov 内存单元,寄存器 内存单元 = AX * mov 段寄存器,寄存器
2. add¶
add 意味 前后两个操作数相加,并返回到第一个操作数上 * add ax,8 即 ax = ax + 8 * add ax,bx 即 ax = ax + bx
3.sub¶
sub 意味着第一个操作数减去第二个操作数,再返回第一个操作数
4.cmp¶
与
lea,Load Effective Address¶
我在《汇编原理》里并没有看到这个指令,《深入理解计算机系统也没有看到》
后来取往网上看了下,他和mov的区别就在于。
mov传的是内存值,而lea传的是有效地址.
比如下面的mov,传送过去得到的就是[0]
对应位置的值
而lea,获得的就是[0]
这个偏移位置地址,可以理解为c/c++中的&
mov al,[0]
lea al,[0]