汇编语言简要记录
-
基础:
-
使用汇编语言编写的源代码,然后通过相应的汇编程序将它们转换成可执行的机器代码。这一过程被称为汇编过程。
-
普遍地说,每一种特定的汇编语言和其特定的机器语言指令集是一一对应的。
-
DB, DW, DD, DQ, DT 依次为 1, 2, 4, 8, 10
-
x86/amd64汇编指令的两大风格分别是Intel汇编与AT&T汇编,分别被Microsoft Windows/Visual C++与GNU/Gas采用(Gas也可使用Intel汇编风格)
-
项目 Intel风格(dosbox使用的是这种) AT&T风格 操作数顺序 目标操作数在前 源操作数在前 寄存器 原样 加%前缀 立即数 原样 加 $前缀 16进制立即数 用后缀B与H分别表示二进制与十六进制 对于16进制字母开头的要加前缀0 加前缀0x 访问内存长度的表示 前缀BYTE PTR, WORD PTR, DWORD PTR和QWORD PTR表示字节,字,双字和四字 后缀b,w,l,q表示字节,字,双字和四字 引用全局或静态变量var的值 [var] var 引用全局或静态变量var的地址 var $var 引用局部变量 需要基于栈指针(rsp) 绝对寻址 [imm] imm 间接寻址 [reg] (%reg) 基址相对寻址 [reg +imm] imm(%reg) 变址寻址 [base+index] (base,index) 变址寻址 imm[base+index] imm(base,index) 比例变址寻址 imm[base + index * scale ] imm(base, index, scale) scale只能是1,2,4,8其中的一个数字(1省略不写就是普通变址寻址) 代码注释 单行注释用;+注释内容。例如:mov rax, rdx ;这里是注释 注意 这里imm为立即数,base和index为寄存器,scale为伸缩量
区分 地址 和 数
-
举例
DS: [1000h]; 这是一个地址, 位置是 1000h 3000h; 这是一个数, 大小是 3000h
- 助记符--->机器指令
- 变量--->操作数存放地址
- 指令前的标号--->该指令的存放地址
-
-
为什么要分段(内存, 虚拟内存, 分段部件, 分页部件)
-
历史
- 1978年 推出 16 位 cpu8086, 内外数据线为 16 位, 地址总线为 20 位, 主存寻址 1MB
- 1982年 推出 cpu80286, 内外数据线为 16 位, 地址总线为 24 位, 主存寻址 16MB
- 1985年 推出32位 cpu80386, 内外数据线为 32 位, 地址总线为 32 位, 主存寻址 4GB(1MB*2^12)
-
物理原因
-
总线 20 位 ---> 寻址 1MB
-
总线 32 位 ---> 寻址 4G
-
段寄存器为 16 位 ---> 段的大小为 64K
-
最低端 80X86 16 位虚拟机中, 内部结构是 16 位, 主线是 20 位, 为了解决这一问题:
- 将 1MB(20 位)的主存按 64KB(16 位)分段
- 设置四个段寄存器 CS, DS, SS, ES 保存段首址(20 位的高 16 位), 将这个 16 位(左移四位再变成 20 位)加上数据的偏移地址就得到了物理地址
- 其中 CS--->IP, SS--->SP, 一般情况下不需要定义附加数据段, 如果必须定义, 最简单的方法是让附加数据段与数据段重合.
-
32 位暂时没看
-
-
-
寄存器
- 数据寄存器组(可以用作 16, 8 位, 但是此时不能用作指示器, 变址寄存器?)
- EAX 累加器
- EBX 基址寄存器
- ECX 计数寄存器
- EDX 数据寄存器
- 指示器变址寄存器组(存放偏移地址, 用作指示器或者变址寄存器, 可用作 16, 不能用作
- ESI 源操作数指示器
- EDI 目的操作数指示器
- EBP 基址寄存器
- ESP 专用堆栈指示器, 一般不做数据寄存器
- 段寄存器
- CS 代码段寄存器
- SS
- DS
- ES, FS, GS
标志寄存器
- zf: 零标志位, 相关指令执行后结果为 0 则 zf=1, 否则 0
mov ax,1
sub ax,1 - pf: 奇偶标志位, 结果中 1 为偶数 pf=1, 否则 0
- sf: 符号标志位, 结果为负, sf=1, 否则为 0
- cf: 进位标志位, 在进行无符号数运算的时候,CF记录了运算结果的最高有效位向更高有效位向更高位的进位值/借位值,产生进位或向更高位借位都会使CF=1
- of: 溢出标志位, 超出机器所能表示的范围 of=1, 否则 0
- 数据寄存器组(可以用作 16, 8 位, 但是此时不能用作指示器, 变址寄存器?)
-
子程序
- NEAR FAR
- NEAR 可省略
- FAR
- 区别主要在于 NEAR 只是把 ip 入栈, 把 ea 赋值给 ip, FAR 在 NEAR 的基础上还要把 CS 入栈(最先入 cs, 再入 ip)
- RET 根据 NEAR 还是 FAR 出栈
- 传递参数
- 寄存器法
- 约定单元法
- 堆栈法
模块化处理
F2T10.ASM NAME F2T10 PUBLIC F2T10; 这里的 public 指明了 F2T10 是可以被其他模块调用的, 该语句可以放在任何地方 DATA SEGMENT USE16 PARA PUBLIC 'DATA' ;段名 segment 使用类型 定位方式 组合方式 '类别' ;定位方式中para为默认, 定义段在什么样的起始边界开始 ;类别的作用是生成 exe 的时候进行分组(分配空间) ;组合方式连在一起, public 同类别段会放在一起 ... DATA ENDS CODE SEGMENT USE16 PARA PUBLIC 'CODE' ASSUME CS:CODE, DS:DATA F2T10 PROC ... F2T10 ENDP CODE ENDS END ----------------------------------------- MAIN.ASM ;以上是一个子模块, 下面的是主模块 NAME MAIN EXTRN F2T10:NEAR; 这里指明 F2T10 是外部模块的, 例如子程序, 如果两个代码块的类别名相同, 相当于在一个段里, 用 EXTRN F2R10:NEAR, 否则用 far IF1 ;IF XXX ... ELSE ... ENDIF 如果条件成立就执行块中的语句, 其中if 有几个固定搭配 INCLUDE MACRO.LIB ENDIF .386 DATA SEGMENT USE16 PARA PUBLIC 'DATA' ... DATA ENDS CODE SEGMENT USE16 PARA PUBLIC 'CODE' ASSUME CS:CODE, DS:DATA START:... CODE ENDS START END masm main.asm masm F2T10.asm 然后执行:LINK MAIN+F2T10;生成 main.exe
- NEAR FAR
-
指令合集
MOV OPD, OPS; MOVE DEST SRC XLAT; TRANSLATE ([BX+AL])->AL LEA OPD, OPS; LOAD EFFECTIVE ADDRESS 立即寻址 等价于 MOV OPD, OFFSET OPS, 其中 OPD 必须是 16/32 位寄存器 ;标志位? NEG OPD; NEGTIVE 将 OPD 的每一位取反最后加一, 包括符号位, 得到结论: 负数"取补码"得到绝对值 IMUL OPD, OPS;INTEGER MULTIPLICATION (OPD)*(OPS)->OPD IMUL OPD, OPS, N; (OPS)*N->OPD IMUL OPS; (AL)*(OPS)->AX (AX)*(OPS)->DX, AX (EAX)*(OPS)->EDX, EAX MUL DIV OPS; (AX)/(OPS)->AL 商, AH 余数 (DX, AX)/(OPS)->AX 商, AH 余数 (EDX, EAX)/(OPS)->EAX商, EDX余数 IDIV NOT OPD; 取反 AND OPD, OPS; 逻辑与 TEST OPD, OPS 常见用法: TEST ECX, ECX 判断 ecx 是否为空, 如果为空则 zf=1 总结: test 是逻辑与运算, cmp 是 sub 运算, cmp a1, a2 执行操作 a1-a2 XOR OPD, OPS; 异或 SAL OPD, N 或者 SHL OPD, N; ARITHMATIC SHIFT LEFT, SHIFT LEFT 左移, 逻辑算数都一样 SHR OPD, N; 逻辑右移, 加 0 SAR OPD, N; 算数右移, 加符号位 ROL OPD, N; 循环左移 ROR OPD, N RCL OPD, N; ROTATE LEFT WITH CARRY 带进位循环左移 RCR OPD, N ABOVE, BELOW 无符号, GREATER LESS 有符号 JA; JUMP WHEN ABOVE JNA; JUMP WHEN NOT ABOVE JAE; JUMP WHEN ABOVE OR EQUAL JNAE; JUMP WHEN NOT ABOVE OR EQUAL 不大于, 且不等于 JB; JUMP WHEN BELOW JNB; JUMP WHEN NOT BELOW JBE; JUMP WHEN BELOW OR EQUAL JNBE; JG; JUMP WHEN GREATER JNG JGE; JUMP WHEN GREATER OR EQUAL JNGE; JUMP WHEN NOT GREATER OR EQUAL JL; JUMP WHEN LESS JNL; JUMP WHEN NOT LESS JLE JNLE; JUMP WHEN NOT LESS OR EQUAL JE; JUMP WHEN EQUAL JZ; JUMP WHEN HAS ZERO FLAG JZ=JE JNE JNZ JO; JUMP WHEN HAS OVERFLOW FLAG JC; JUMP WHEN HAS CARRY FLAG ;pf: 奇偶标志位, 结果中 1 为偶数 pf=1, 否则 0 JP; JUMP WHEN HAS PARITY(奇偶) FLAG JPO; JUMP WHEN PARITY FLAG IS ODD, JP = JPE JPE; JUMP WHEN PARITY FLAG IS EVEN, JNP = JPO JS; JUMP WHEN HAS SIGN FLAG INT; INTERRUPT DW; DEFINE WORD PROC; PROCEDURE ENDS; END SEGMENT PTR; POINTER MOVSX; EXTENDED MOVE WITH SIGN DATA MOVZX; EXTENDED MOVE WITH ZERO DATA *********************** PUSH OPS; SP=SP-1 POP OPD; SP=SP+1 DIV;https://blog.csdn.net/loovejava/article/details/7044242 IMUL OPD, OPS; 有符号数乘法 IMUL OPD, OPS, N; OPS*N->OPD IMUL OPS; AL*OPS->AX 字节 AX*OPS->DX,AX 字 EAX*OPS->EDX,EAX 双字 MUL;无符号乘法,用法同上 ADD AX,10;把 ax 加上 10 再存到 ax 里 SUB LOOP 标号 逻辑移位, 总是用 0 补充 SHL EDX, 7;EDX 左移 7 位 shr ;右移 算数移位, 用符号位补充 sar;右移 CBW ;将 AL 中的符号扩展到 AH 里 ;意义 无符号比较 有符号比较 > JA, JNBE JG, JNLE >= JAE, JNB JNC, JGE, JNL < JB, JNAE JC, JL, JN, JNGE < JBE, JNA JLE, JNG CMP BYTE PTR[SI],"#" ;ptr前面的类型有byte(字节)、word(字)、dword(双字)、qword(四字)、tbyte(十字节)、far(远类型)和near(近类型) LEA lea指令 load effective address, 加载有效地址,可以将有效地址传送到指定的的寄存器。指令形式是从存储器读数据到寄存器, 效果是将存储器的有效地址写入到目的操作数, 简单说, 就是C语言中的”&”. mov指令 在CPU内或CPU和存储器之间传送字或字节,它传送的信息可以从寄存器到寄存器,立即数到寄存器,立即数到存储单元,从存储单元到寄存器,从寄存器到存储单元,从寄存器或存储单元到除CS外的段寄存器(注意立即数不能直接送段寄存器),从段寄存器到寄存器或存储单元。 但是注意 (1) MOV指令中的源操作数绝对不能是立即数和代码段CS寄存器; (2) MOV指令中绝对不允许在两个存储单元之间直接传送数据; (3) MOV指令中绝对不允许在两个段寄存器之间直接传送数据; (4) MOV指令不会影响标志位 使用[]区别 第二操作数加不加中括号[]的区别就是: lea对变量没有影响是取地址,对寄存器来说加[]时取值,第二操作数不加[]非法 mov对变量来说没有影响是取值,对寄存器来说是加[]时取地址,第二操作数不加[]是取值