Cheatsheet
编程 tips#
- add dl, '0'转字符格式
- add dl, 20h大写转小写
- sub dl, 20h小写转大写
- 32 位整数转 16 进制输出- 每次 rol -> push -> and 0Fh -> ... -> pop实现从高到低,每次取四位
- 逻辑判断 cmp al, 10,选择输出一个数字或者字母
 
- 每次 
- 二进制变量转十进制输出- 每次进行 64 / 32 -> 32除法运算防止溢出
- 使用栈进行反向输出,让个位最后输出
- 需要统计 push 的次数
 
- 每次进行 
- 删除字符串前面的字符- 使用 repe scasb来跳过等于al的字符
 
- 使用 
- strcmp- repe cmpsb- 如果最后一次相等,则全等,且过完了整个字符串
- 否则,不全等,停在了第一个不相等字符串的后面
 
 
- 内存清零- rep stosb来进行批量置零
 
- 过滤字符串字符- 首先设置好 cld,确认di, si地址正确,然后每一个字符都lodsb ... stosb来实现
 
- 首先设置好 
指令表#
- 数据传送指令- 通用数据传送指令 mov,push,pop,xchg- mov dest, src- 不能给 cs赋值
- 不影响标志位
- 两个操作数不能都是内存变量
- 等宽修饰!!
- 不能将常数赋值给段寄存器
- 不能引用 ip, fl寄存器
 
- 不能给 
- push op将- src压入堆栈并更新- sp- 不影响标志位
- 不支持 8 位操作数
 
- pop op弹出给- op,注意事项和- push一样
- xchg src1, src2交换两个操作数的值- 不影响标志位
- 不能有段寄存器
 
 
- 输入输出指令 in, out用于端口操作- in al, port从端口读取到- al- port< 0FFh 的时候才能用常数,否则要用 dx 寄存器,- in al, dx
 
- out port, al将- al写入到端口,和- in一样注意事项
 
- 地址传送指令 lea, lds, les- lea dest, srcload effective address 加载偏移地址
- lds dest, src将- src偏移地址放到- dest,段地址放在- ds
- les dest, src将- src偏移地址放到- dest,段地址放在- es
 
- 标志寄存器传送指令 lahf, sahf, pushf, popf- lahf将- fl低 8 位赋值给- ah(load AH with Flags)
- sahf将- ah赋值给- fl低 8 位 (Store AH in Flags)
- pushf(d)加 d 是- efl
- popf(d)加 d 是- efl
 
 
- 通用数据传送指令 
- 转换指令- 扩充指令- cbw- al->ax
- cwd- ax->dx:ax
- cdq- eax->edx:eax
- movsx dest, src将- src符号扩充放到- dest
- movzx dest, src将- src零扩充放到- dest
- xlat把- byte ptr ds:[bx+al]赋值给- al- 先构造 ds:bx->array
- 然后在 al中写入字节下标,使用xlat取这个字节
 
- 先构造 
 
 
- 扩充指令
- 算数运算指令- 加法指令- add dest, src
- inc op不影响 CF
- adc dest, src- dest = dest + src + CF
 
- 减法指令- sub dest, src
- sbb dest, src- dest = dest - src - CF- 计算借位减法也应该从低位算到高位
 
- dec op不影响 CF
- neg op
- cmp op1, op2- temp = op1 - op2- 影响标志位,但是并不保存结果
- 后面常伴随条件跳转指令
 
 
- 乘法指令- mul src无符号数乘法- src8 位,- ax = al * src
- src16 位,- dx:ax = ax * src
- src32 位,- edx:eax = eax * src
 
- imul有符号数乘法- imul src和- mul src用法相同
- imul src1, src2- src1 = src1 * src2
- imul src1, src2, imm- src1 = src1 * src2 * imm
 
 
- 除法指令- div op无符号数除法- 8-bit, ax / op = al...ah
- 16-bit, dx:ax / op = ax...dx
- 32-bit, edx:eax / op = eax...edx
- 溢出- 除数为 0,或者商过大导致商寄存器无法容纳
- CPU 会在除法前面插入 int 00h打印 overflow 并停止程序
- 可以调整 dx:[0]的int 00h中断矢量来改变行为
 
 
- 8-bit, 
- idiv op有符号数除法,和- div一样
 
- 浮点运算指令- fp 寄存器都是 80-bit,一共有 st(0), ..., st(7)八个
- fld op(float load) 将一个浮点数压入堆栈
- fild op(float int load) 将一个整数转换为浮点数压入堆栈
- fst dest(float store) 将- st(0)保存到一个浮点类型变量或小数寄存器
- fstp dest(float store and pop) 一样的操作,只是多了一个 pop 的步骤- fstp st一般用来当 pop 用
 
 
- fp 寄存器都是 80-bit,一共有 
 
- 加法指令
- 十进制调整指令- 压缩 BCD 码调整指令- daa(decimal adjust after addition) 将- al在加法之后重新调整为 BCD 码
- das(decimal adjust after substraction) 将- al在剑法之后重新调整为 BCD 码
 
- 
非压缩 BCD 码调整指令- aaa(ASCII adjust after addition) 非压缩 BCD 码加法调整
- aas(ASCII adjust after substraction) 减法调整
- aam(ASCII adjust after mul) 乘法调整
- aad(ASCII adjust after div)
 
 
- 压缩 BCD 码调整指令
- 逻辑运算和移位指令- 逻辑运算指令- and dest, src
- or dest, src
- xor dest, src
- not op
- test dest, src进行 and 运算但是不保留结果
 
- 移位指令 最后移出的一位永远放到 CF- shl dest, count逻辑左移- 没有 .386的话,只能是shl reg, 1
 
- 没有 
- shr dest, count逻辑右移
- sal dest, count算数左移,和 SHL 完全一样
- srl dest, count算数右移,左边补符号位
- rol dest, count循环左移,最后移出又移入的数还是放在 CF
- ror dest, count循环右移
- rcl dest, count带进位循环左移,右边补 CF,左边移到 CF,相当于 CF 作为多余的一位参与循环移位了
- rcr dest, count带进位循环右移
 
 
- 逻辑运算指令
- 字符串操作指令- 字符串复制指令 rep movsb/w/d(mov string in byte)- movsb先复制再移动 SI DI- byte ptr es:[di] = byte ptr ds:[si]分别是 data segment:source index 和 extra segment:destination index
- if (!DF) { di++; si++; } else { di--; si--; }w d 分别为 2 4
 
- rep- 先判断 CX 是不是 0,如果是就结束
- 进行一次操作,然后 CX--
 
- rep movsb将- ds:[si]指向的字符串复制到- es:[di],CX 指定复制字符数量,DF 制定复制方向
 
- 字符串比较指令 repe/ne cmpsb/w/d- cmpsb先比较再移动 SI DI- cmp byte ptr ds:[si], byte ptr es:[di],状态保存在 FL
- if (!DF) { di++; si++; } else { di--; si--; }
 
- repe/nerepeat if equal/not equal- 先判断 CX 是不是 0,如果是就结束
- 进行指令操作
- CX-- 不影响标志位
- 判断 ZF 是否为 0,再决定是终止还是继续
 
- strcmp: repe cmpsb- 初始化 CX 为字符串长度
- repe cmpsb
- 如果最后一次是 equal,那么遇到 \0退出,字符串全等
- 否则,找到不相等的字符,由于 cmpsb比较之后一定会移动 DI SI,所以dec si, dec di才能找到第一个不同的字符位置
 
 
- 搜索字符串指令 repe/ne scasb/w/d- scasb只和- es:[di]有关了,其实就是 cmpsb 里的源字符串变成了 al/ax/eax- cmp al, byte ptr es:[di]
- if (!DF) di++; else di--;
 
- strlen: repne scasb可以用来求字符串长度- al = 0
- 初始化 CX=0FFFFh,表示最大搜索长度
- 搜到 0 的时候,根据 repne 的流程,还会进行 CX--
- 使用 not cx得到搜索过的字符数量,还需要dec cx得到没有\0的字符数量
 
 
- 写入字符串指令 rep stosb/w/d- stosb将 al/ax/eax 写入- es:[di]- byte ptr es:[di] = al
- if (!DF) di++; else di--;
 
- 可以用于数组清零 cx=len; al=0; rep stosb- 同时使用 stosb/d- cx=len; push cx; shr cx, 2
- rep stosd
- pop cx; and cx, 3
- rep stosb
 
 
- 同时使用 
 
- 读取字符串指令 lodsb/w/d- 从 ds:[si]读取一个字符到 al/ax/eax,并按照 DF 指示移动 SI
- 简化了数组遍历过程,可以用于过滤字符
 
- 从 
 
- 字符串复制指令 
- 控制转移指令- 无条件跳转指令 jmp- jmp short dest- EBXX
- jmp near ptr dest- E9XXXX
- jmp far ptr dest- EAXXXXXXXX
 
- 条件跳转指令 jcc跳转距离全都是 1 字节,也就是 short jump- ja = jnbe
- jb = jc = jnae
- jae = jnc = jnb
- jbe = jna
- jg = jnle
- jl = jnge
- jge = jnl
- jle = jng
- jz = je
- jnz = jne
- jp = jpe有奇偶校验位则跳转,也就是运算结果低 8 位的 1 有偶数个
- jnp = jpojump is no parity
- jcxzjump if cx is zero
- jecxzjump if ecx is zero
 
 
- 无条件跳转指令 
- 循环指令- loop dest- CX--
- 如果 CX 不为 0,则跳转 dest
- loop 循环:循环体,cx--,判断;rep 系列循环:判断,循环体,cx--- 
这就会导致如果初始 CX=0,实际上会执行 FFFF 次
 
- 
 
- loopz dest == loope dest- CX--
- 如果 ZF==1 && CX!=0,才跳转到 dest
 
- loopnz dest
 
- 子程序调用与返回指令- call near ptr dest- E8 XXXX近调用- back_addr = ip + 3
- push back_addr
- id = back_addr + delta
 
- retn近返回- 流程- pop back_addr
- ip = back_addr
 
- retn idata16用于堆栈平衡,将调用函数时压入的参数弹出,这样主程序就不需要管理堆栈平衡了- pop back_addr
- sp += idata16- idata16是函数参数所占的字节数,不算返回地址的
- ip = back_addr
 
- 
注意- 标号定义、name proc...name endp定义和name proc near...name endp定义的函数中,ret 默认就是 retn
- name proc far...name endp定义的函数中,ret 默认是 retf,所以 retn 不能简写成 ret
 
- 标号定义、
 
- 流程
- call far ptr dest- 9A XXXX XXXX远调用- 流程- push cs
- push ip+5这样在堆栈里的 far ptr 也满足了小端规则
- ip = dest_l
- cs = dest_h
 
- dest 可以是远标号 name label far/ name proc far,也可以是 mem32- call dword ptr es:[di]
 
 
- 流程
- retf idata16远返回,并同时参数堆栈平衡
 
- 中断和中断返回指令- int idata8- CD XX- pushf
- push cs
- push ip+2
- ip = word ptr 0000:[idata8 * 4]
- cs = word ptr 0000:[idata8 * 4 + 2]
 
- int 3- CC软件端点中断- pushf
- push cs
- push ip+1
- ip = word ptr 0000:[000Ch]
- cs = word ptr 0000:[000Eh]
 
- into- CE溢出中断 interrupt on overflow- 先判断 OF 是否为 1,是才接着执行
- pushf
- push cs
- push ip+1
- ip = word ptr 0000:[0010h]
- cs = word ptr 0000:[0012h]
 
- iret中断返回- pop ip
- pop cs
- popf
 
 
| 短跳转 | 近跳转 | 远跳转 | |
|---|---|---|---|
| 指令格式 | jmp short dest | jmp near ptr dest | jmp far ptr dest | 
| 机器码 | EB | E9 | EA | 
| 示例 | EB06 | E93412 | EA78563412 | 
| 范围 | [80h, 7Fh] | [8000h, 7FFFh] | [0000:0000, FFFF:FFFF] | 
| 相对地址 | dest-($+2) | dest-($+3) | 绝对地址 | 
| 备注 | 相对偏移 | 相对偏移,小端规则 | 远指针,小端规则 | 
| 跳转指令 | 条件 | 解释 | 跳转指令 | 条件 | 解释 | |
|---|---|---|---|---|---|---|
| je | ZF==0 | jne | ZF!=0 | |||
| jae(above or eq) | CF==0 | 没有借位,说明第一个大 | jge | SF==OF | 正数且没有溢出/负数且有溢出(实际应该是正数) | |
| ja(above) | CF==0 && ZF==0 | 没有借位,而且不相等 | jg(greater) | SF==OF && ZF!=0 | 满足 jge且不相等 | |
| jbe | CF==1 | 有借位,说明第二个大 | jle | SF!=OF | 负数且没有溢出/正数且有溢出(实际应该是负数) | |
| jb | CF==1 && ZF!=0 | 有借位,而且不相等 | jl | SF!=OF && ZF!=0 | 满足 jle且不相等 | 
寄存器#
- ax, bx, cx, dx, sp, bp, si, di- bx, bp, si, di用来表示偏移地址,可以放在- []内
- ax, bx, cx, dx称为通用寄存器,常用于算数、逻辑运算
 
- cs, ds, es, ss用来表示段地址- cs:ip指向当前将要执行的指令,- ip是指令指针(instruction pointer),- cs是代码段寄存器
- ss:sp指向堆栈顶端,其中- sp是堆栈指针(stack pointer),- ss是堆栈段寄存器
- es附加段寄存器,和- ds一样,可以表示一个数据段的地址
 
- ipPC 指针
- fl标志寄存器
标志寄存器#
- ZF零标志
- SF符号标志
- OF溢出标志
- PF奇偶校验标志
- AF辅助进位标志和 BCD 码的调整有关
- DF方向标志- std; cld;
 
- IF中断标志,IF=0 禁止硬件中断- sti; cli
 
- TF陷阱标志,TF=1 进入单步模式,每一条指令后面都会跟着一条- int 01h- 只能使用 pushf popf来操作,例如pushf; pop ax; or ax, 100h; push ax; popf
- 调试器利用单步模式来获得控制权
 
- 只能使用 
内存空间#
| 地址范围 | 用途 | 大小 | 
|---|---|---|
| [0000:0000, 9000:0000] | 操作系统和用户程序 | 640K | 
| [A000:0000, A000:FFFF] | 映射显卡内存 图形模式 | 64K | 
| [B000:0000, B000:7FFF] | 映射显卡内存 | 32K | 
| [B800:0000, B800:7FFF] | 映射显卡内存 文本模式 | 32K | 
| [C000:0000, F000:FFFF] | 映射 ROM | 320K | 
常用中断#
- int 21h
| AH | 功能 | 调用参数 | 返回参数 | 
|---|---|---|---|
| 00 | 程序终止 | CS=程序段前缀 | |
| 01 | 键盘输入并回显 | AL=输入字符 | |
| 02 | 显示输出 | DL=输出字符 | |
| 09 | 显示字符串 | DS:DX=字符串地址 | |
| 4C | 带返回码结束 | AL=返回码 | 
- int 00h除法 overflow 错误中断
- int 10h- ah = 0, al = 13h显卡切换到 320x200 图形模式
- 显卡的文本模式直接这样就行 mov ax, 0B800h; mov es, ax; mov al, 'A'; mov ah, 71h; mov es:[di], ax;- 注意这里高位的内存地址更大,应该是颜色,低位才是字符
 
 
- int 16h- ah = 0从键盘读取一个键的编码,放到- ax