汇编
关于汇编语言的总结, 原则是只记录要点
实例
整数加减
.386 |
子程序
注意问题
- 清除堆栈参数
- 保存寄存器现场, 可以用
uses eax...
- 局部变量
;c 调用规范 |
;stdcall 规范 |
;局部变量 |
汇编要点总结
操作模式
保护模式
所有指令特性可用, 分配给程序的独立内存区域叫段
32 位保护模式下, 可寻址 4GB 地址空间
虚拟 8086 模式
保护模式的一个子模式
实地址模式
程序直接访问系统内存和硬件设备
寻址 1MB 地址空间
系统管理模式
电源管理, 系统安全, 通常由计算机制造商实现
寄存器
- 数据寄存器组(可以用作 16, 8 位, 但是此时不能用作指示器, 变址寄存器?)
- EAX 累加器, 乘除指令默认使用, 乘法乘数和积, 除法被除数和商
- EBX 基址寄存器
- ECX 计数寄存器, 默认循环计数
- EDX 数据寄存器, 乘法高位, 除法余数
- 指示器变址寄存器组(存放偏移地址, 用作指示器或者变址寄存器, 可用作 16, 不能用作 8)
- ESI 源操作数指示器
- EDI 目的操作数指示器
- EBP 基址寄存器
- ESP 专用堆栈指示器, 一般不做数据寄存器
- 段寄存器
- CS 代码段寄存器
- SS
- DS
- ES, FS, GS
- 指令指针
- EIP 保护下一条将要执行的指令的地址
- 标志寄存器 EFLAGS
- 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
数据类型
byte 8, word 16, dword 32, qword 64
栈
32 位入栈操作栈顶指针-4
指令
mov
;mov 不能从内存到内存 |
算数指令
;自增自减 inc dec |
循环
;loop ecx 每次循环-1 |
跳转
;cmp jcond |
子程序与堆栈
;push 先减少 esp, 再将操作数复制到堆栈 |
字符串
;重复指令 |
其他
;等号伪指令, 将符号和一个整数表达式连接起来 |
dosbox
mount c ~/Desktop/netclass/asm/asm/dosbox |
基础:
使用汇编语言编写的源代码,然后通过相应的汇编程序将它们转换成可执行的机器代码。这一过程被称为汇编过程。
普遍地说,每一种特定的汇编语言和其特定的机器语言指令集是一一对应的。
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 位暂时没看😬
内存中的数据存放
- 高八位在在高地址, 低八位在低地址
寄存器寻址
- 寄存器寻址 MOV AX, BX
- 寄存器间接寻址 MOV AX, [SI] ; AX=地址为 SI 的值 的值
- 变址寻址 MOV AL, [R*F] + V; 其中 F 应该是为了应对字节, 字, 双字的
- 基址加变址寻址 MOV AX, [BR+IR*F+V]; 默认段寄存器由 BR 决定, 为了表示矩阵
- 立即寻址 MOV AX, 036H
- 直接寻址 MOV DS:[20H], CL
子程序
- NEAR FAR
- NEAR 可省略
- FAR
- 区别主要在于 NEAR 只是把 ip 入栈, 把 ea 赋值给 ip, FAR 在 NEAR 的基础上还要把 CS 入栈(最先入 cs, 再入 ip)
- RET 根据 NEAR 还是 FAR 出栈
- 传递参数
- 寄存器法
- 约定单元法
- 堆栈法
模块化处理
F2T10.ASM |
字符串操作
- 默认:
- 源串指示器 SI
- 目的串指示器 DI
- 重复次数 CX
- SCAS 搜索值 AX
- LODS 目的地址 AX
- STOS 源地址
- MOVS
- CMPS
- REPE CMPS 未比较完且相等时继续
- REPNE CMPS 未比较完且不相等时继续
- SCAS 在 DI 中搜索 AL
- LODS
- STOS
输入输出
I/O 空间
- IN
- IN AL/AX/EAX, 立即数
- IN AL/AX/EAX, DX
- OUT 同上
中断
win32
- 32 位
- 输入输出退出等需要调用 api
- 动态链接
- 库
- user32.lib
- kernel32.lib
指令合集
MOV OPD, OPS; MOVE DEST SRC |
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.