移位指令
当 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 启动后,除完成其他工作外,还将它提供的中断例程装入内存,并建立相应的中断向量。