1. CPU
- 从存储器中取一条指令
- 分析指令的操作码
- 从存储器中读取操作数
- 执行指令
- 写入结果集
- 回到 1.
运算器进行信息处理,寄存器进行信息存储,控制器控制各种器件工作,总线连接各种器件。
16 位和 32 位操作系统的区别
2. 内存
2.1. 地址
8086 汇编中可供使用的内存空间有 1MB。故物理地址的取值范围为 00000h 到 0FFFFFh。由于没有 20 位的寄存器来直接表示物理地址,我们一般采用逻辑地址来间接访问物理地址。逻辑地址由 16 位段地址:16 位偏移地址构成,计算方式为:
故一个段可以表示的内存有 64KB,且其是首地址的物理地址的 16 进制表示的个位必然是 0。
选用不同的偏移地址,对应的段可能会有重合。一个端的结束地址即起始地址加上 10000h。
2.1.1. 偏移地址和段地址
data segment
a db "ABC"
s db "Hello$World", 0Dh, 0Ah, 0
data ends这里数组 a 和 s 都在 data 段中,data 段的首字节为 a[0],数组 s 的偏移地址为 offset s = 3,因为 s[0] 和 a[0] 的距离是 3 字节。
-
使用
offset 变量名或标号名来引用变量或标号的偏移地址。 -
使用
seg 变量名或标号名或段名来引用段地址。比如使用mov ax, data语句的效果和mov ax, seg s或mov ax, seg a的效果一样。
2.1.2. 直接寻址和间接寻址
直接寻址的两种方式:
段寄存器:[常数];如:cs:[1000h],ds:[2000h]。段寄存器:var[常数]等价于段寄存器:[var+常数],这里 var 是程序里定义的变量名或函数名;如ds:s[-2]等价于ds:[s-2],编译器在编译期会自动把 var 的偏移地址给加进来,如 offset s=8,则编译后变为ds:[7]。
直接寻址时的缺省段址为 DS,即在不指定段寄存器时,默认为 DS。
间接寻址的两种方式:
段寄存器:[寄存器(+常数)],仅 BX、BP、SI、DI 四个寄存器可选。段寄存器:[寄存器+寄存器(+常数)],其中一个必须从 BX、BP 中选,另一个必须从 SI、DI 中选。
间接寻址时如果寄存器中有 BP,则缺省段址为 SS,否则也为 DS。
笔记
在 32 位系统中,新增了用 在 32 位系统中...
寄存器+寄存器*n+常数(这里 n 可以为 2、4、8)的寻址方法,可以方便数组的访问。与 16 位系统不同,32 位系统中对中括号中的寄存器没有要求,EBP、EBX、EDI、EAX、ECX、EDX、ESP 都可以放在中括号中。
2.1.3. 小端规则
当 CPU 把大于 8 位的数据写入内存时,会遵循小端规则:会先写入低 8 位再写入高 8 位。
2.1.4. 宽度修饰
汇编语言中可以通过这 3 个宽度修饰词限定变量的宽度:
byte ptr:8 位(1 字节)word ptr:16 位(2 字节)dword ptr:32 位(4 字节)
在符合这两种情况时,不需要加宽度修饰:
- 指令中的变量有变量名:可以根据变量的声明确认宽度
- 指令中的另一个操作数有明确宽度(比如寄存器):说明当前变量的宽度需与另一操作数的确定宽度保持一致
对于 mov ds:[bx], 1 这种指令,其中一个变量没有变量名修饰,也没法根据另一个操作数来确定宽度,这时候就需要用宽度修饰符来修饰。
2.1.5. 地址传送指令
详见 IV 8086指令系统。
2.2. 内存空间划分
2.3. 显卡
2.3.1. 文本模式的显卡地址映射
文本模式下显卡共 80×25 个位置,以左上角为原点。显卡将被映射到 B800 这个段,每个屏幕上的字符占用两个内存单元,前一个(如 B800:0000)决定字符,后一个(如 B800:0001)决定前景色和背景色。屏幕上某个字符的偏移地址可以这样计算:
2.3.2. 图形模式的显卡地址映射
图形模式下共支持 320×200 个像素点,以左上角为原点。显卡将被映射到 A000 这个段。一个内存地址表示一个像素点,故屏幕上某像素的偏移地址可以这样计算:
3. 寄存器
3.1. 通用寄存器
通用寄存器包括:
- AX:Accumulator 累加器(和
mul、div有关) - BX:Base 基地址寄存器(和取地址有关,如
ds:[bx]) - CX:Count 计数器(和
loop指令有关) - DX:Data 数据寄存器(和
mul、div有关,也与输入输出的暂存有关)
3.2. 段地址寄存器
段地址寄存器包括:
- CS:代码段寄存器,CS:IP 指向当前将要执行的指令。
- 只能通过
jmp far ptr、jmp dword ptr、call far ptr、retf、int、iret等指令间接改变,不能通过mov指令直接修改。
- 只能通过
- DS:数据段寄存器。
- 在开始运行时,DS 内的数据并不是数据段的段地址,而是 PSP 段址,因此需要手动先
mov ax, data再mov dx, ax来将 DS 设为数据段的段址。
- 在开始运行时,DS 内的数据并不是数据段的段地址,而是 PSP 段址,因此需要手动先
- ES:附加段寄存器。
- SS:堆栈段寄存器,SS:SP 指向堆栈顶端。
DS、ES、SS 寄存器可以用 mov 指令赋值,但源操作数不能是常数,只能是寄存器或变量。可以对段寄存器赋值的寄存器仅限 AX、BX、CX、DX、SP、BP、SI、DI,变量必须是 word ptr 宽度的。
3.3. 偏移地址寄存器
偏移地址寄存器包括:IP、SP、BP、SI、DI,其中 IP 必须搭配 CS 使用,SP 必须搭配 SS 使用。
特别的,BX 也可以作为偏移地址寄存器使用,故实际能放在 [] 被用于间接寻址的寄存器有:BX、BP、SI、DI。这四个寄存器除了用于间接寻址外,也可以正常参与运算。
3.4. 标志寄存器
FL 是标志寄存器,共 16 个位,其中 6 个是状态标志:CF、ZF、SF、OF、PF、AF,3 个是控制标志:DF、IF、TF,还有 7 个是保留位。
标志寄存器的值不能被直接修改,但是可以通过 pushf 和 popf 被存入/取出到堆栈中,因此也可以搭配 push,pop 指令和别的寄存器对其进行简介修改。
CF、ZF、SF、OF、PF 寄存器都有与之相关的跳转指令,如:jc 表示 jump if carry,即 CF=1 时跳转;jnc 表示 jump if not carry,即 CF=0 时跳转。他们被称作 JCC 指令,在 IV 8086指令系统 中会进一步介绍。
CF、DF、IF 寄存器有与之相关的设置或清空指令,如:clc 表示 clear carry,即将 CF 置 0;stc 表示 set carry,即将 CF 置 1。
3.4.1. 进位标志 CF(Carry Flag)
CF 会被加减、乘法、移位运算影响,发生下列事件时 CF 会被置 1,否则置 0:
- 两数相加(最高位)发生进位
- 两数相减(最高位)发生借位
- 两数相乘的宽度超过被乘数的宽度
- 移位时最后被移除的一位为 1
3.4.2. 零标志 ZF(Zero Flag)
ZF 会被算术运算、逻辑运算、移位运算影响,运算结果不等于 0 时 ZF 置 1,否则 ZF 置 0。
3.4.3. 符号标志 SF(Sign Flag)
CF 会被算术运算、逻辑运算、移位运算影响,SF 将被置为运算结果的最高位。
3.4.4. 溢出标志 OF(Overflow Flag)
OF 会被 add、sub、mul、imul 指令影响,发生下列事件时 OF 会被置 1,否则置 0:
- 两个正数相加变负数
- 两个负数相加变正数
- 两数相乘的乘积宽度超过被乘数宽度
- 移位前后最高位发生改变
3.4.5. 奇偶校验标志 PF(Parity Flag)
ZF 会被算术运算、逻辑运算、移位运算影响,运算结果的低八位有偶数个 1 时将 PF 置 1,否则将 PF 置 0。
3.4.6. 辅助进位标志 AF(Auxiliary Flag)
AX 会被 add 和 sub 指令影响,当第 3 位向第 4 位进位/借位时 AF 置 1 否则将 AF 置 0。
3.4.7. 方向标志 DF(Direction Flag)
DF 可以控制 rep movsb 的运行方向(类似于 memcpy 函数)。当 DF=0 时,字符串操作按正方向(低地址到高地址)执行;当 DF=1 时,字符串操作按反方向(高地址到低地址)执行。详见 IV 8086指令系统 中的字符串操作部分。
3.4.8. 中断标志 IF(Interrupt Flag)
IF 用于禁止或允许硬件中断。当 IF=0 时禁止硬件中断;当 IF=1 时允许硬件中断。
这样,被 cli 到 sti 包裹起来的代码在执行时就不会被硬件中断,可在需要修改中断向量等场景下。
3.4.9. 陷阱标志(Trap Flag)
TF 用于让 CPU 进入单步模式。当 TF=1 时,每执行一条指令后,CPU 会自动插入一条 int 01h 中断。
4. 端口
端口地址与内存地址独立,仅有 16 位偏移地址,其取值范围是 。CPU 不能直接对 I/O 设备进行控制,需要通过向端口发送控制信号或读取来自端口的信号,指令如下:
in 寄存器 端口地址:从端口读入信号并存储到寄存器中。out 端口地址 寄存器:将寄存器的值发送到对应端口。