04 寄存器
1 总结#
8086 一共有 14 个寄存器,分别为:
- 通用寄存器:
AX, BX, CX, DX
- 段地址寄存器:
CS, DS, ES, SS
- 偏移地址寄存器:
IP, SP, BP, SI, DI
- 标志寄存器:
FL
80386 中除了段地址寄存器仍然是 16 位,其余均扩展到 32 位
2 通用寄存器#
用于算数、逻辑、移位运算
名称 | AX |
BX |
CX |
DX |
---|---|---|---|---|
助记 | accumulator | base | count | data |
3 段地址寄存器#
名称 | CS |
DS |
ES |
SS |
---|---|---|---|---|
助记 | code segment | data segment | extra segment | stack segment |
是否能用 mov 赋值 |
不能 只能用 jmp, call, ret, int 等间接改变 |
可以 但是 SRC 必须是寄存器或变量而不是 imm |
可以 但是 SRC 必须是寄存器或变量而不是 imm |
可以 但是 SRC 必须是寄存器或变量而不是 imm |
可用的源寄存器 | 无 | AX, BX, CX, DX, SP, BP, SI, DI |
AX, BX, CX, DX, SP, BP, SI, DI |
AX, BX, CX, DX, SP, BP, SI, DI |
间接赋值 | |
---|---|
3.1 寄存器内容初始化#
ASMF 05 段和堆栈#3 寄存器初始化赋值和
psp
段 有更多关于psp
段的内容
CS:IP |
SS:SP |
DS |
ES |
---|---|---|---|
代码段段地址:首条指令偏移地址 |
堆栈段段地址:堆栈段长度 |
psp 段段地址 |
psp 段段地址 |
这也就是为什么需要先对 DS
进行赋值,才能引用 data segment 中的变量
4 偏移地址寄存器#
名称 | IP |
SP |
BP |
SI |
DI |
(BX ) |
---|---|---|---|---|---|---|
助记 | instruction pointer | stack pointer | base | |||
用于 [] 间接寻址? |
不能 | 不能 | 能 隐含 SS 段 |
能 隐含 DS 段 |
能 隐含 DS 段 |
能 隐含 DS 段 |
参与算数、逻辑、移位运算? | 不能 | 不能 | 能 | 能 | 能 | 能 |
5 FL 标志寄存器#
- FL 是 16 位的,但是其中只有每一位的布尔值有效,整体没有意义
15 | 14 | 13 | 12 |
---|---|---|---|
x | x | x | x |
0 | 0 | 0 | 0 |
11 | 10 | 9 | 8 |
OF | DF | IF | TF |
溢出标志 | 复制方向标志 | 中断标志 | 陷阱标志 |
7 | 6 | 5 | 4 |
SF | ZF | x | AF |
符号标志 | 零标志 | 0 | 辅助进位标志 |
3 | 2 | 1 | 0 |
x | PF | x | CF |
0 | 奇偶校验标志 | 1 | 进位标志 |
5.1 Carry Flag 进位标志#
- CF Carry Flag 进位标志
- 加法操作的进位、左移出来的 1,都会存到 CF
jc
如果有进位则跳转
- 移位操作最后移出去的一位,也会保存到 CF 中
5.1.1 与 CF 相关的指令#
jc |
jnc |
adc |
clc |
stc |
---|---|---|---|---|
有进位跳转 | 无进位跳转 | 带进位加法 | CF = 0 |
CF = 1 |
adc ax, bx ; ax = ax + bx + CF
16 位转二进制输出 | |
---|---|
16 位转二进制输出 (advanced) | |
---|---|
Attention
mov
不会改变任何标志,push
pop
也不会改变任何标志
5.2 Zero Flag 零标志#
jz/je
在ZF=1
的时候跳转,本质上是相同的jnz/jne
是相反的指令- 使用
jz
还是je
,需要在对应的语境下选择
5.3 Sign Flag 符号标志#
- 每次都保存运算结果的最高位
js
符号跳转,SF==1
则跳转jns
SF==0
则跳转
5.4 Overflow Flag 溢出标志#
有符号数加法溢出 CF 相当于无符号数加法溢出标志
- 正负相加永不溢出
jo
溢出跳转,jno
不溢出跳转
5.5 Parity Flag 奇偶校验位#
PF=1
表示结果的低八位中有偶数个 1jp/jpe
如果 parity even 跳转jnp/jpo
如果 pairty odd 跳转
Note
标准 ASCII 码只有 7 位,多出的第八位就是奇偶校验位;而扩展 ASCII 码没有
5.6 Auxiliary Flag 辅助进位标志#
第三位向第四位产生进位或借位
AF 和 BCD 码有关,用 16 进制表示十进制数
daa
指令 (decimal adjust for addition) 加法的十进制调整if AF == 1 or (AL & 0Fh) > 9: AL += 6
5.7 Direction Flag#
5.7.1 字符串复制的方向#
1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 |
---|---|---|---|---|---|---|
A | B | C | D | E |
1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 |
---|---|---|---|---|---|---|
A | B | A | B | C | D | E |
要将字符串复制到以 1002 位首地址的位置,此时 源首地址<目标首地址,复制应该按反方向,地址从大到小。否则会导致还没遍历到的原始数据被覆写:
1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 |
---|---|---|---|---|---|---|
A | B | A | B | A | B | A |
- 源首地址<目标首地址:反方向
- 源首地址>目标首地址:正方向
5.7.2 Example: 正方向复制#
1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 |
---|---|---|---|---|---|---|
A | B | C | D | E |
细节
- 首先将 A 复制到 1000
- 然后
cx--
,si++
,di++
- 继续进行,直到
cx == 0
Note
si
source index 源偏移地址di
destination index 目标偏移地址
5.7.3 Example: 反方向复制#
1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 |
---|---|---|---|---|---|---|
A | B | C | D | E |
细节
- 这里的
si, di
都是从末尾开始的,只要反方向复制,一开始的传入值都应该是末地址 - 每次都是
si--, di--, cx--
,其他同理
5.8 Interrupt Flag 中断标志#
IF=1
允许硬件中断,cli
置零IF=0
禁止硬件中断,sti
置一
Note
mov ah, 1; int 21h
是函数调用,软件中断,代码在显式地用int n
的形式调用函数集的函数
什么是硬件中断
example: add 1 to 100 | |
---|---|
- 键盘中断:假如用户在执行上面程序时敲键盘,此时 CPU 必须暂停并处理本次键盘输入:将键盘输入编码保存到系统中的键盘缓冲区队列,
int 9h
会返回原来的指令 - 时钟中断:约每 55 ms 会在下一条指令前插入一个时钟中断
int 8h
,将操作系统内部的一个计数器 +1
- 软件中断是显式的 explicit
- 硬件中断是隐式的 implicit
5.8.1 example: 修改函数指针时的保护操作#
这样能保证 int 9h
不会在地址改了一半的时候被硬件调用,从而产生错误
5.9 Trap Flag 陷阱标志#
TF=1
时,CPU 进入单步模式 (single-step mode),每执行一条指令,就会插入一个int 1h
中断int 1h
是未定义的,调试器会自定义一个int 1h
的中断函数- 调试器 jmp 到被调试程序,被调试程序取得控制权
- 被调试程序进行一步,调用
int 1h
返回调试器 - 调试器可以观察被调试程序的寄存器状态和当前正在执行的命令等
set TF | |
---|---|
clear TF | |
---|---|
如果我要做一个调试器?
- 编写断点
int 1h
程序 - 翻译指令,断行并显示汇编代码
- 等待用户输入
5.9.1 int 1h
函数的定义#
那么 int 1h
的函数首地址(函数指针)为 1234h:5678h
Tip
int n
函数的指针,一定保存在 0:n*4
处,这是因为每个函数指针都需要占用 4 个字节
int 21h
一定存放在 0:84h
处
5.9.2 example: antidbg#
在执行的时候替换了
int 1h
的函数指针,所以更改了单步模式下调用的程序
5.10 pushf, popf
#
专门执行
FL
的堆栈操作
设置 TF=1,进入单步运行模式 | |
---|---|