寄存器
CPU 本身只负责运算,不负责储存数据。数据一般都储存在内存之中, CPU 要用的时候就去内存读写数据。但是,CPU 的运算速度远高于内存的读写速度,为了避免被拖慢, CPU 都自带一级缓存和二级缓存。基本上,CPU 缓存可以看作是读写速度较快的内存。
CPU 缓存还是不够快,另外数据在缓存里面的地址是不固定的,CPU每次读写都要寻址也会拖慢速度。 因此,除了缓存之外,CPU 还自带了寄存器(register),用来储存最常用的数据。 也就是说,那些最频繁读写的数据(比如循环变量),都会放在寄存器里面,CPU 优先读写寄存器,再由寄存器跟内存交换数据。
寄存器不依靠地址区分数据,而依靠名称。每一个寄存器都有自己的名称, 我们告诉 CPU 去具体的哪一个寄存器拿数据,这样的速度是最快的。有人比喻寄存器是 CPU 的零级缓存。
种类
32 位 CPU 的寄存器大小就是4个字节
- EAX
- EBX
- ECX
- EDX
- EDI
- ESI
- EBP
- ESP 保存当前 Stack top 的地址
- EIP(指令指针)包含正在执行的当前指令的地址
EAX的最低有效2字节可被视为称为AX的16位寄存器。AX的最低有效字节可以用作单个8位寄存器AL,而AX的最高有效字节可以用作单个8位寄存器AH
操作
CPU 指令,%ebx是该指令要用到的运算子。一个 CPU 指令可以有零个到多个运算子。
操作码 [操作数, 操作数]
- 8位无符号数的范围为0-255
- 16位无符号数的范围为0-65 535
- 8位有符号数的范围为-128-+127
-
16位有符号数的范围为-32 768-+32 767
- 8位 byte 字节 、16位 word 字、32 dword 双字
_main:
push 3 ; push指令其实有一个前置操作。它会先取出 ESP 寄存器里面的地址 ESP -4 (-4) (- 高位向低位地址,4 类型是int,占用4个字节)
push 2 ; 运算子2 放入 Stack ESP-4 (-8)
call _add_a_and_b ; 调用函数 找_add_a_and_b标签
add %esp, 8 ; add_a_and_b函数终止执行,系统就回到刚才main函数中断的地方,继续往下执行 , ESP 寄存器里面的地址,手动加上8个字节,再写回 ESP 寄存器。这是因为 ESP 寄存器的是 Stack 的写入开始地址
ret ;
_add_a_and_b:
push %ebx ; 将 EBX 寄存器里面的值,写入_add_a_and_b这个帧。这是因为后面要用到这个寄存器,就先把里面的值取出来,用完后再写回去 ESP-4(-12)
mov %eax, [%esp+8] ; ESP 寄存器里面的地址加上8个字节就是push的2 取出的是2,再将2写入 EAX 寄存器。
mov %ebx, [%esp+12] ; ESP 寄存器里面的地址加上8个字节就是push的3 取出的是3,将其写入 EBX 寄存器
add %eax, %ebx ; EAX 寄存器的值(即2)加上 EBX 寄存器的值(即3),得到结果5,再将这个结果写入第一个运算子 EAX 寄存器
pop %ebx ; esp + 4 (-8) 取出 Stack 最近写入的值(即 EBX 寄存器的原始值),再将这个值写回 EBX 寄存器(因为加法已经做完了,EBX 寄存器用不到了)
ret ; 终止当前函数的执行,运行权交还给上层函数。也就是,当前函数的帧将被回收
大小端存储方式 通常是小端