移位指令
当 C N T > 1 CNT > 1 CNT>1 时,CNT 必须是 CL 寄存器
逻辑左移
SHL OPR , CNT
将寄存器或内存单元中的数据向左移 CNT 位,最后移除的一位写入 CF,最低位用 0 补充
循环左移
ROL OPR , CNT
将寄存器中的值的最高位存入 CF 寄存器,其他位左移一位,将 CF 寄存器的值补给最低位,以此循环 CNT 次
逻辑右移
SHR OPR , CNT
将寄存器或内存单元中的数据向右移 CNT 位,最后移
除的一位写入 CF,最高位用 0 补充
循环右移
ROR OPR , CNT
将寄存器中的值的最低位存入 CF 寄存器,其他位右移一位,将 CF 寄存器的值补给最高位,以此循环 CNT 次
算数左移
SAL OPR , CNT
将寄存器中的值的最高位存入 CF 寄存器,其他位左移一位,最低位补 0,以此循环 CNT 次
算数右移
SAR OPR , CNT
将寄存器中的值的最低位存入 CF 寄存器,其他位右移一位,最高位保持不变,以此循环 CNT 次
带进位循环左移
RCL OPR CNT
将寄存器中的值的最高位存入 CF 寄存器,其他位左移一位,将 此次操作前的CF 寄存器的值补给最低位,以此循环 CNT 次
带进位循环右移
RCR OPR CNT
将寄存器中的值的最低位存入 CF 寄存器,其他位右移一位,将 此次操作前的CF 寄存器的值补给最高位,以此循环 CNT 次
操作显存数据
显示的原理
屏幕上的内容就是显存中的数据
在 8086 CPU 中显存地址是
[
A
0000
,
B
F
F
F
F
]
[A0000,BFFFF]
[A0000,BFFFF] 的 128K RAM,其中
[
B
8000
H
,
B
F
F
F
F
H
]
[B8000H,BFFFFH]
[B8000H,BFFFFH] 共 32K 的空间,是
80
×
25
80 \times 25
80×25 彩色字符模式第 0 页的显示缓冲区
显示缓冲区的结构
描述内存单元的标号
标号
- 代码段中的标号可以用来标记指令、段的起始地址
- 代码段中的数据也可以用标号
- 在段中使用的标号没有 “ : ” 它们同时描述内存地址和单元长度的标号,同时规定了在此标号对应的内存地址后的内存单元的数据类型,这类标号称作数据标号
数据标号
- 数据标号标记了存储数据的单元的地址和长度
- 数据标号不同于仅仅表示地址的地址标号
数据段中的数据标号
数据最好专门存放到数据段中,实现分段管理,数据段中可以直接用数据标号
地址标号只能在代码段中使用,数据段中不可以使用地址标号,只能使用数据标号
扩展用法
将标号当作数据来定义
例
data segment
a DB 1,2,3,4,5,6,7,8
b DW 0
c DW a,b
data ENDS
可以通过 c 间接的访问 a 和 b 指向的内存单元,相当于
data segment
a DB 1,2,3,4,5,6,7,8
b DW 0
c DW OFFSET a,OFFSET b
data ENDS
data segment
a DB 1,2,3,4,5,6,7,8
b DW 0
c DD a,b
data ENDS
相当于
data segment
a DB 1,2,3,4,5,6,7,8
b DW 0
c DW OFFSET a,SEG a,OFFSET b,SEG b
data ENDS
SEG 操作符的作用是取段地址
直接定址表
直接定址表法
用查表得方法,通过依据数据,直接算出所要找得元素的位置
直接定址表分类
数据的直接定址表
用查表得方法解决问题
利用表,在两个数据集合之间建立一种映射关系,用查表得方法根据给出得数据得到其在另一集合中得对应数据。
映射关系
- 数值 [ 0 , 9 ] + 30 H = 字符 [ ′ 0 ′ , ′ 9 ′ ] [0,9] + 30H = 字符 ['0','9'] [0,9]+30H=字符[′0′,′9′]
- 数值 [ 10 , 15 ] + 37 H = 字符 [ ′ A ′ , ′ F ′ ] [10,15] + 37H = 字符 ['A','F'] [10,15]+37H=字符[′A′,′F′]
建立表依次存储字符 [ ′ 0 ′ , ′ F ′ ] ['0','F'] [′0′,′F′],通过数值 [ 0 , 15 ] [0,15] [0,15] 直接查找到对应字符
TABLE DB '0123456789ABCDEF'
####优点
- 算法清晰和简洁
- 加快运算速度
- 使程序易于扩充
代码的直接定址表
- 将若干个功能写成若干个对应的子程序
- 将这些功能子程序的入口地址存储在一个表中,它们在表中的位置和功能号对应
- 对应关系为 功能号 × 2 = 对应的功能子程序在地址表中的偏移 功能号 \times 2 = 对应的功能子程序在地址表中的偏移 功能号×2=对应的功能子程序在地址表中的偏移
中断及其处理
中断
定义
CPU 不再接着(刚执行完的指令)向下执行,而是转去处理中断信息
内中断
定义
由 CPU 内部发生的事件而引起的中断
CPU 内部产生的中断信息及中断号
除法错误(0)
单步执行(1)
执行 INTO 指令(4)
执行 INT n 指令(立即数 N)
外中断
定义
由外部设备发生的事件而引起的中断
中断处理程序
- CPU 接到中断信息后会执行处理程序
- 中断信息和其处理程序的入口地址入口之间有某种联系,CPU 根据中断信息可以找到要执行的处理程序
- 标志寄存器和断点是中断隐指令入栈,IRET 指令出栈
- 其他子程序是中断处理程序内部出入栈
中断处理程序的常规的步骤
- 保存用到的寄存器
- 处理中断
- 恢复用到的寄存器
- 用 IRET 指令返回
中断向量表
由中断类型码,查表得到中断处理程序的入口地址,从而定位中断程序
8086 CPU 的中断向量表,每个中断程序入口地址占用 4 字节
(
I
P
)
=
(
中断类型码
×
4
)
,
(
C
S
)
=
(
中断类型码
×
4
+
2
)
(IP) = (中断类型码 \times 4) , (CS) = (中断类型码 \times 4 + 2)
(IP)=(中断类型码×4),(CS)=(中断类型码×4+2)
中断过程
- 中断过程由CPU硬件自动完成
- 用中断类型码找到中断向量,并用它设置 CS 和 IP
8086 CPU 的中断过程
- 从中断信息中取得中断类型码
- 标志寄存器的值入栈(中断过程中要改变标志器的值,要先行保护)
- 设置标志寄存器的第 8 位 TF 和 第 9 位 IF 的值为 0
- CS 的内容入栈
- IP 的内容入栈
- 从中断向量表读取中断处理程序的入口地址,设置 IP 和 CS
代码流程
取得中断类型码 N
PUSHF
TF = 0
IF = 0
PUSH CS
PUSH IP
(IP) = (N * 4)
(CS) = (N * 4 + 2)
编写中断处理程序
- CPU 随时都可能检测到中断信息,所以中断处理程序必须常驻内存(一直存储在内存某段空间之中)。
- 中断处理程序的入口地址(即中断向量),必须存储在对硬的中断向量表表项中( [ 0000 : 0000 , 0000 : 03 F F ] [0000 : 0000,0000 : 03FF] [0000:0000,0000:03FF])
- 如果中断程序需要用到固定的数据,将数据写到中断程序的代码区,保证与代码一起加载
自己编写的中断处理程序应该放在哪?
- 应该存放在内存的确定位置,要重新找个地方,不破坏原有的系统
- 在操作系统之上使用计算机,所有的硬件资源都在操作系统的管理之下,应该向操作系统申请获得存放程序的内存,但是要引入好多技术细节
- 最简便的方案是绕过操作系统,直接在找到一块别的程序不会用到的内存区,将程序传送到其中即可
- 内存 [ 0000 : 0000 , 0000 : 03 F F ] [0000 : 0000,0000 : 03FF] [0000:0000,0000:03FF] 大小为 1KB 的空间是系统存放中断向量表, DOS 系统和其他应用程序都不会随便使用这段空间。
- 8086 支持 256 个中断,但实际上系统中要处理的中断事件远没有达到 256 个
- 利用中断向量表中的空闲单元来存放我们的程序,选用合适的空间来存放程序
安装中断程序
- 设置 DS : IP 指向程序源地址
- 设置 ES : DI 指向目标地址
- 设置 CX 为传输长
(在程序结尾写一个标号, 程序长度 = 程序结尾的标号地址 − 程序标号对应的地址 程序长度 = 程序结尾的标号地址 - 程序标号对应的地址 程序长度=程序结尾的标号地址−程序标号对应的地址) - 设置传输方向为正
- REP MOVSB
设置中断向量表
将中断程序入口地址,写到中断向量表的对应表项中(逻辑同查表过程)
单步中断
Debug 的 T 命令
- Debug 提供了单步中断的中断处理程序,功能为显示所有寄存器中的内容后等待输入命令
- Debug 利用了 CPU提供的单步中断功能
- 使用 T 命令时,Debug 将 TF 标志设置为 1,使 CPU 工作在单步中断方式下
单步中断过程
当 CPU 执行完一条指令之后,如果检测到标志寄存器 TF 位为 1,则产生单步中断(中断类型码为 1,引发中孤单程序,执行中断处理程序)
TF 陷阱标志(Trap Flag)
- 用于调试时的单步方式操作。
- 当 TF = 1 时,每条指令执行完后产生陷阱,由系统控制计算机
- 当 TF = 0 时,CPU 正常工作,不产生陷阱
IF 中断标志(Interrupt Flag)
- 当 IF = 1 时,允许 CPU 相应可屏蔽中断请求
- 当 IF = 0 时,关闭中断
8086 CPU 提供的设置 IF 的指令
- STI 设置 IF = 1
- CLI 设置 IF = 0
为什么中断过程要把 TF 位设置为 0
- 中断处理程序也由若干条代码组成
- 如果在执行中断处理程序之前, TF = 1,则 CPU 在执行完中断程序的第一条指令后,又要产生单步中断,转去执行单步中断的中断处理程序的第一条指令,形成死循环
中断不响应的情况
- 一般情况下 CPU 在执行完当前指令后,如果检测到中断信息,就响应中断,引发中断过程
- 在有些情况下, CPU 在执行完当前指令后,即便发生中断,也不会响应
- 例如 在执行完向 SS 寄存器传送数据的指令后,即便是发生中断, CPU 也不会响应,是因为 SS : SP 是联合指向栈顶的,对它们的设置必须连续完成,以此保证对栈的正确操作。
由 INT 指令引发的中断
格式
INT 中断类型码
功能
引发中断过程
中断过程
- 取中断类型码
- 标志寄存器入栈,IF = 0, TF = 0
- CS 和 IP 入栈
- ( I P ) = ( N × 4 ) , ( C S ) = ( 中断类型码 × 4 + 2 ) (IP) = (N \times 4) , (CS) = (中断类型码 \times 4 + 2) (IP)=(N×4),(CS)=(中断类型码×4+2)
编写供应用程序调用的中断程序
技术手段
- 编程时,可以用 INT 指令调用子程序
- 此子程序即中断处理程序,简称为中断例程
- 可以自定义中断例程,实现特定功能
BIOS 和 DOS 中断处理
BIOS
含义
是在系统板的 ROM 中存放着一套程序
容量
8 KB
地址
从 FE000H 开始
主要内容
- 硬件系统的检测和初始化程序
- 外部中断和内部中断的中断例程
- 用于对硬件设备进行 I/O 操作的中断例程
- 其他和硬件系统相关的中断例程
BIOS 功能调用表
AH | 功能 | 调用参数 | 返回参数 |
00 | 设置显示方式 | AL=00:40x25黑白文本方式 | |
01 | 置光标类型 | CH0-3=光标起始行;CL0-3=光标结束行 | |
02 | 置光标位置 | BH=显示页号;DH:DL=行:列 | |
03 | 读光标位置 | BH=显示页号 | CH=光标起始行 |
04 | 读光笔位置 | AH=0:光笔未触发 | |
05 | 置显示页 | AL=页号 | |
06 | 窗口上卷 | AL=上卷行数;AL=0:整个窗口空白 | |
07 | 窗口下卷 | 同06功能 | |
08 | 读光标位置的字符和属性 | BH=显示页 | AH=属性 |
09 | 在光标位置显示字符和属性 | BH=显示页;BL=字符属性 | |
0A | 在光标位置显示字符和属性 | BH=显示页 | |
0B | 置彩色调色板 | BH=彩色调色板ID | |
0C | 写像素 | DX=行(0-199)CX=列(0-639) | |
0D | 读像素 | DX=行(0-199)CX=列(0-639) | AL=像素值 |
0E | 显示字符 | AL=字符;BL=前景颜色 | |
0F | 取当前显示方式 | AH=字符列数 | |
13 | 显示字符串 | ES:BP=串首地址;CX=串长度 | 光标返回起始位置光标跟随移动光标返回起始位置光标跟随移动 |
AH | 功能 | 调用参数 | 返回参数 |
00 | 终止进程 | CS=程序段前缀段地址 | |
01 | 带回显的键盘输入 | AL=输入字符 | |
02 | 显示一个字符 | DL=待输出字符的ASCII码 | |
03 | 异步通讯输入 | AL=输入的数据 | |
04 | 异步通讯输出 | DL=待输出的数据 | |
05 | 打印机输出 | DL=待输出的字符 | |
06 | 直接控制台I/O | DL=0FFH:输入 | AL=输入的字符 |
07 | 无回显的键盘输入 | AL=输入的字符 | |
08 | 无回显的键盘输入(检测Ctrl-C) | AL=输入的字符 | |
09 | 字符串输出 | DS:DX=字符串首 | |
0A | 键盘输入至缓冲区 | DS:DX=缓冲区首 | DS:[DX+1]=输入的字符数 |
0B | 检测键盘状态 | AL=00:有输入 | |
0C | 清除缓冲区并请求指定的输入功能 | AL=输入功能的功能号 | |
0D | 磁盘复位 | 清除文件缓冲区 | |
0E | 指定当前缺省磁盘驱动器 | DL=驱动器号0:A;1:B | |
0F | 打开文件 | DS:DX=FCB首地址 | AL=00:文件打开 |
10 | 关闭文件 | DS:DX=FCB首地址 | AL=00:文件关闭 |
11 | 查找第一个目录项 | DS:DX=FCB首地址 | AL=00:找到 |
12 | 查找下一个目录项 | DS:DX=FCB首地址 | AL=00:找到 |
13 | 删除文件 | DS:DX=FCB首地址 | AL=00:成功删除 |
14 | 顺序读 | DS:DX=FCB首地址 | AL=00:读成功 |
15 | 顺序写 | DS:DX=FCB首地址 | AL=00:写成功 |
16 | 建立文件 | DS:DX=FCB首地址 | AL=00:文件成功建立 |
17 | 文件改名 | DS:DX=特殊的FCB首地址 | AL=00:改名成功 |
19 | 取当前缺省驱动器名 | AL=缺省驱动器号 | |
1A | 置DTA地址 | DS:DX=DTA首地址 | |
1B | 取缺省驱动器的FAT信息 | AL=每簇扇区数 | |
1C | 取任意驱动器的FAT信息 | DL=驱动器号 | 同1BH功能 |
21 | 随机读 | DS:DX=FCB首地址 | AL=00:读成功 |
22 | 随机写 | DS:DX=FCB首地址 | AL=00:写成功 |
23 | 测定文件大小 | DS:DX=FCB首地址 | AL=00:成功,文件长度写入FCB |
24 | 设置随机记录号 | DS:DX=FCB首地址 | |
25 | 设置中断向量 | DS:DX=中断向量 | |
26 | 建立程序段前缀 | DS:DX=新的程序段前缀 | |
27 | 随机块读 | DS:DX=FCB首地址 | AL=00:读成功 |
28 | 随机块写 | DS:DX=FCB首地址 | AL=00:写成功 |
29 | 分析文件名 | ES:DI=FCB首地址 | AL=00:标准文件 |
2A | 取日期 | CX=年 | |
2B | 设置日期 | CX:DH:DL=年:月:日 | AL=00:成功 |
2C | 取时间 | CH:CL=时:分 | |
2D | 设置时间 | CH:CL=时:分 | AL=00:成功 |
2E | 置磁盘自动读写标志 | AL=00:关闭标志 | |
2F | 取磁盘缓冲区首地址 | ES:BX=缓冲区首地址 | |
30 | 取DOS版本号 | AH=发行号;AL=版号 | |
31 | 结束进程并驻留 | AL=返回码 | |
33 | Ctrl-Break检测 | AL=00:取状态 | DL=00:关闭Ctrl-Break检测 |
35 | 取中断向量 | AL=中断号 | ES:BX=中断向量 |
36 | 取空闲磁盘空间 | DL=驱动器号 | 成功:AX=每簇扇区数 |
38 | 置/取国家信息 | DS:DX=信息区首地址 | BX=国家码;AX=错误码 |
39 | 建立子目录 | DS:DX=ASCIIZ串首地址 | AX=错误码 |
3A | 删除子目录 | DS:DX=ASCIIZ串首地址 | AX=错误码 |
3B | 改变当前目录 | DS:DX=ASCIIZ串首地址 | AX=错误码 |
3C | 建立文件 | DS:DX=ASCIIZ串首地址 | 成功:AX=文件句柄 |
3D | 打开文件 | DS:DX=ASCIIZ串首地址 | 成功:AX=文件句柄 |
3E | 关闭文件 | BX=文件句柄 | 失败:AX=错误码 |
3F | 读文件或设备 | BX=文件句柄 | 成功:AX=实际读入的字节数 |
40 | 写文件或设备 | BX=文件句柄 | 成功:AX=实际写出的字节数 |
41 | 删除文件 | DS:DX=ASCIIZ串首地址 | 成功:AX=00 |
42 | 移动文件指针 | BX=文件句柄 | 成功:DX:AX=新指针的位置 |
43 | 置/取文件属性 | DS:DX=ASCIIZ串首地址 | 成功:CX=文件属性 |
44 | 设备文件I/O控制 | BX=文件句柄 | DX=设备信息 |
45 | 复制文件句柄 | BX=文件句柄1 | 成功:AX=文件句柄2 |
46 | 强制复制文件句柄 | BX=文件句柄1 | 失败:AX=错误码 |
47 | 取当前目录路径名 | DL=驱动器号 | 成功:DS:SI=ASCIIZ串 |
48 | 分配内存空间 | BX=申请的内存数量(节) | 成功:AX=分到的内存首址 |
49 | 释放内存空间 | ES=内存起始段地址 | 失败:AX=错误码 |
4A | 调整已分配的内存块 | ES=原内存起始段地址 | 失败:AX=错误码 |
4B | 装入/执行进程 | DS:DX=ASCIIZ串首地址 | 失败:AX=错误码 |
4C | 带返回码结束 | AL=返回码 | |
4D | 取返回代码 | AX=返回代码 | |
4E | 查找第一个匹配文件 | DS:DX=ASCIIZ串首地址 | AX=错误代码 |
4F | 查找下一个匹配文件 | DS:DX=ASCIIZ串首地址 | AX=错误代码 |
54 | 取盘自动读写标志 | AL=当前标志值 | |
56 | 文件改名 | DS:DX=ASCIIZ串(旧) | AX=错误代码 |
57 | 置/取文件日期和时间 | BX=文件句柄 | 成功:DX:CX=日期和时间 |
58 | 取/置分配策略码 | AL=0:取码 | 成功:AX=策略码 |
59 | 取扩充错误码 | AX=扩充错误码 | |
5A | 建立临时文件 | DS:DX=ASCIIZ串首地址 | 成功:AX=文件句柄 |
5B | 建立新文件 | DS:DX=ASCIIZ串首地址 | 成功:AX=文件句柄 |
5C | 控制文件存取 | AL=00:封锁 AL=01:开启 | |
62 | 取PSP地址 | BX=PSP地址 |
调用中断流程
- BIOS 和 DOS 在所提供的中断例程中包含了许多子程序
- 和硬件设备相关的 DOS 中断例程中,一般都调用 BIOS 的中断例程
BIOS 和 DOS 中断例程的安装过程
- CPU 加电后,初始化 (CS) = 0FFFFH,(IP) = 0,自动从 FFFF : 0 单元开始执行程序。FFFF : 0 处有一条跳转指令, CPU 执行该指令后,转去执行 BIOS 中的硬件系统检测和初始化程序
- 初始化化程序将建立 BIOS 所支持的中断向量,即将BIOS提供的中断例程的入口地址登记在中断向量表
- 硬件系统检测和初始化完成后,调用 INT 19H 进行操作系统的引导,从此计算机交由操作系统控制
- DOS 启动后,除完成其他工作外,还将它提供的中断例程装入内存,并建立相应的中断向量。