bit 比特 1位(1b)
byte 字节 8位(2B)
word 字 16位
dword 双字 32位
qword 四位 64位
计算机寻址
大多数以字节为寻址单位进行寻址
机器码:就是01
但是01看起来太痛苦了,所以一般使用16进制来呈现
汇编语言就是这些机器码的助记符
寄存器:
计算机的指令由cpu来执行
CPU和内存是分开的
寄存器存在于CPU中,是CPU的直接操作对象
通用寄存器
RAX 低32位 EAX ;低16位 AX ;8-15位AH,低八位 AL
RBX
RCX
RDX
RDI 低32位 EDI
RSI
R8 低32位R8d ; 低16位 ;R8W ;低8位 ;R8B
R9
R10
R11
R12
R13
R14
R15
通用寄存器用于参数传递以及算数运算等通用场合
RSP 栈顶指针 低32位 ;ESP
RBP 栈底指针
两者用于维护程序运行时的函数栈
EFLAGS 标志寄存器 记录状态标志,包括AF,PF,SF,ZF,OF,CF等标识符
RIP 指令计数器 保存下一条将会执行的指令的地址
不能直接修改,正常情况下会每一次运行一条指令自增一条指令的长度,当发生跳转是才会以其他方式改变其值
寻址方式
1.立即寻址 1234h 1234h这个数字本身
2.直接寻址 [1234h] 内存地址1234h
3.寄存器寻址 RAX 访问RAX寄存器
4.寄存器间接寻址 [RAX] 访问RAX寄存器存储的值的这一内存地址
5.变址寻址 [RAX+1234h] 访问RAX寄存器存储的值+1234h这一内存地址
汇编指令
两种格式 intel和AT&T
两者差别只要在源和目的操作数顺序上
数据传送指令mov mov rax rbx rax=rbx
取地址指令lea lea rax[rbx] rax=&*rbx
算数运算指令add add rax rbx rax=rax+rbx
sub sub rax rbx rax=rax-rbx
逻辑运算指令and and rax rbx rax=rax&rbx
xor xor rax rbx rax=rax | rbx
函数调用指令call call 1234h| 执行内存1234h处的函数
函数返回指令ret ret 函数返回
比较cmp cmp rax rbx 比较rax和rbx,结果保存在EFLAG寄存器
无条件跳转jmp jmp 1234h eip=1234h
栈操作指令push push rax 将rax存储的值压栈
pop pop rax 将栈顶的值赋给rax,rsp+=8
call 和 jmp指令的区别
call是函数调用,需要一些函数地址的保存压栈参数传递的操作
jmp指令类似于函数中的ifelse语句,只涉及跳转,不能作为函数的调用来使用
pop时为何是+8而不是-8,因为栈是逆向生长的
计算机在执行汇编代码时,只会顺序执行
通过call,jmp,ret这种指令来完成跳转
举个栗子:
int i=0;
while(i<100)
i++;
i=0
翻译成汇编:
mov rax 0;//int a=0;
label1:
inc rax;//a++
cmp rax 100;
jpg label2;//if a>=100,break
jmp label1;//loop
label2:
mov rax 0;
jge是通过eflag寄存器的标志位来判断
eflag标志位是通过之前的cmp来设置
int a,b;//存放于rax和rbx中
两种swap方式:
第一种:int c=a;a=b;b=c;
第二种: a=a+b;b=a-b;a=a-b;
第一种方式
mov rcx rax
mov rax rbx
mov rbx rcx
第二种方式
add rax rbx
mov rcx rax
sub rcx rbx
mov rbx rcx
sub rax rbx
对比
从高级指令层次看
申请了变量c,第二种直接在原本变量上操作
汇编语言层次上看
第一种方式指令数少,而且第二种方法涉及算数运算指令
从结果上看,可以直接:xchg rax rbx