第 8章 输入输出方法
8.1 输入输出的基本概念
8.2 无条件方式输入输出
8.3 查询方式输入输出
8.4 中断方式输入输出本章要点
8.1 输入输出的基本概念从第 2章的图 2.1可以看到,计算机的硬件系统由 CPU、内存、外设三大部分构成,它们相互之间通过一组信息传递的公共通道 ──总线联系在一起的。 CPU和内存构成了计算机的主机部分,是计算机中的高速设备。而大多数外部设备都是慢速设备,用来把从主机以外采集到的数据送入主机内部,或者把主机内的数据传递到外部,外设中的外部存储器还可以存储大量的数据。那么,CPU作为计算机的核心,它又是如何控制外设的呢?
8.1.1 外设接口接口,用于把 CPU来的控制命令转换成外设的控制信号,把外设的工作情况转换成 CPU可以读取并处理的状态信号 。
计算机系统中对外设接口也进行了编号,这个号码称作,外设端口号,。 每一个端口号对应外设接口中的一个存放字节型数据的元件,称为一个,外设端口,。
一个外设接口中往往需要多个外设端口,占据多个外设端口号 。 CPU控制外设就是通过从这些外设端口中读取数据以及向它们发送数据实现的 。
大多数外设接口从功能上可以分为控制部件,状态部件和数据部件三大组成部分 。
控制部件又称为命令部件,专用于接收来自主机的操作命令,并转换成对外设的控制信号;
状态部件负责向主机转达外设的当前工作情况;
数据部件是内外数据交换的缓冲器,临时存放需要传递的数据 。
8.1.2 控制外设的指令由于 8088采取了独立编址方式,就需要有特殊的指令控制对外设端口的操作 。 8088系统设计有两条专用指令,IN和
OUT。
【 指令格式 】 IN d1,d2
【 功能 】 从 d2指明的外设端口中读取 1字节或 2字节数据,
送到操作数 d1指出的地方 。
【 说明 】
( 1) 8088系统限制 d1只能是 AL或 AX。 当外设端口号不超过 255时,d2操作数可直接写端口号码,是外设的直接寻址方式;端口号超过 255时,必须先把端口号放在 DX中,以 DX
作为 d2操作数,这是外设的间接寻址方式 。
( 2) 当 d1是 AL时,该指令从指定的端口中读 1字节数据;
当 d1是 AX时,CPU将从 d2对应的端口读 1字节数据到 AL,从下一个端口号读 1字节数据到 AH,即总共读取 16位数据送到
AX 中 。
【 指令格式 】 OUT d1,d2
【 功能 】 把操作数 d2指明的 1字节或 2字节数据送到 d1对应的外设端口中 。
【 说明 】
( 1) d2只能是 AL或 AX。当外设端口号不超过 255时,d1
操作数可直接写端口号码;端口号超过 255时,必须先把端口号放在 DX中,以 DX作为 d1操作数。
( 2) 当 d2是 AL时,该指令把 AL中的 8位数据送往 d1指定的外设端口;当 d2是 AX时,该指令把 AL中的 8位数据送到 d1对应的端口,把 AH中的 8位数据送往下一端口,即把
16位数据送到 d1对应的端口及下一端口中 。
【 例 8.1】 说明下面各指令或程序段的功能 。
( 1) MOV DX,61H
IN AL,DX
( 2) MOV BX,61H
MOV AL,[BX]
( 3) OUT 21H,AL
( 4) MOV SI,21H
MOV [SI],AL
【 解 】
( 1) 先把立即数 61H放到 DX中,然后以 DX中的 61H作为外设端口号,从相应的外设端口读取 1字节数据送到 AL
中 。
( 2) 先把立即数 61H放到 BX中,然后以 BX中的 61H作为偏移地址,以缺省段寄存器 DS 中的值为段地址,从相应的内存中取出 1字节数据送到 AL中 。
( 3) 把 AL中的 1字节数据送往 21H号外设端口 。
( 4)先把立即数 21H放到 SI中,然后以 SI中的 21H作为偏移地址,以缺省段寄存器 DS 中的值为段地址,把 AL中的 1字节数据送到相应的内存中。
8.1.4 输入输出方式主机与外设之间数据交换的方法有 4种:无条件方式、
查询方式、中断方式,DMA方式。
1,无条件方式又称为直接方式,是指 CPU可在任何时刻直接以外设操作指令与外部设备进行数据传递 。 显然,这种方式对外设有很高的要求,它必须能像内存一样时刻准备着与 CPU
进行数据传递,并且能够跟上 CPU的速度,保证传送的信息的正确性 。
2,查询方式使用查询方式工作的外设必须至少有两个部件,其中之一是状态部件 。 CPU每一次与外设进行数据交换之前,
先从状态部件读取信息,判断外设是否处于,就绪,
( Ready) 状态 。 如果来自外设的状态信息反映出外设,没有准备好,或正,忙,( Busy),说明还不能进行数据传递;反之,当 CPU检测到外设已准备好 ( Ready) 后,可以与外设进行一次数据交换 。
3,中断方式这是指每当外设准备好,能够进行数据传递时,就向
CPU发出一个特殊的请求信号,称为中断请求信号 。 CPU
收到中断请求后,暂停当前的工作,转而执行一段预先设计好的中断服务程序,完成对外设的数据交换 。 执行完中断服务程序后,CPU仍回到被暂停的程序继续执行 。
4,直接存储器存取 DMA( Direct Memory Access) 方式这是一种不通过 CPU,在内存与外设之间直接进行高速数据交换的方法。
进行 DMA方式的数据传递必须有一个前提条件,就是
CPU能够让出总线的控制权,交由 DMA方式数据传递的专用控制器控制,当数据传递结束后,CPU再收回总线控制权 。
DMA控制器专门用于控制内存与外设之间的直接数据传递,但是它没有数据处理能力 。 DMA控制器包括:控制寄存器,状态寄存器,地址寄存器和字节计数器 。
完成 DMA传送的步骤:
( 1) DMA控制器向 CPU发出 HOLD信号,请求使用总线 。
( 2) CPU发出响应信号 HOLD给 DMA,并将总线让出 。
( 3) 传输数据的存储器地址 ( 在地址寄存器中 ) 通过地址总线发出 。
( 4) 传输的数据字节通过数据总线进行传递 。
( 5) 地址寄存器增 1,指向下一个字节 。
( 6) 字节计数器减 1。
( 7) 如字节计数器不为零,转向 ( 3) 。
( 8) 否则 DMA控制器撤消总线请求信号 HOLD,传递结束 。
系统总线端口
61H,P7 P6 P5P4P3P2 P1 P0 1.193MHz
定时器 A
B
与门 放大电路扬声器图 8.1 扬声器与系统总线的连接
8.2 无条件方式输入输出计算机系统中的扬声器是一种简单的输出设备,可以随时从 CPU接收控制命令。图 8.1是扬声器的连接原理图。
【 例 8.2】 利用无条件数据传送方式,让计算机的扬声器在 1秒钟内保持发出 500 Hz 的声音 。
【 解 】 完整的程序如下:
delnum = 14000
sta SEGMENT STACK
DW 1024 DUP( 0)
sta ENDS
code SEGMENT
ASSUME CS:code,SS:sta
delay PROC NEAR ;时间延迟子程序,以 CX中的值作为
PUSH AX ;循环次数,做若干次的乘法产生时
PUSH DX ;间延迟的效果
del,IMUL AX ;用乘法指令延迟,执行时间较长
LOOP del
POP DX
POP AX
RET
delay ENDP
main,MOV CX,500
sou,PUSH CX
IN AL,61H ;读出原 61H端口的数据
AND AL,11111100B ;清最低两位
OUT 61H,AL ;送低电位到 P1
MOV CX,delnum ;取控制延迟时间的循环次数值
CALL delay
IN AL,61H
OR AL,00000010B ;置 P1位为 1
OUT 61H,AL
MOV CX,delnum
CALL delay
POP CX
LOOP sou
MOV AX,4C00H
INT 21H
code ENDS
END main
从这个例子可以看出,对扬声器的控制是不需要任何条件的,程序中在向 61H号端口送数据时根本不考虑扬声器是否准备好、是否可接收数据等问题。由端口 61H到放大电路构成了扬声器的接口电路,该接口以系统总线上送来的信号驱动薄膜的振动,按无条件方式进行数据传送。
8.3 查询方式输入输出大多数外设不可能像扬声器那样工作,这些外设处理数据的速度和提供数据的速度往往比主机内部速度慢得多,因而 CPU与这类设备进行数据传递前必须先判断它们是否,就绪,。 CPU以查询方式从外设读取一批数据,以及向外设送出一批数据的流程如图 8.2所示 。
Y
读状态端口
Ready?
处理完?
N
Y
N
向数据端口写一个数据或向命令端口发一个操作命令
( b)查询方式输出操作流程( a)查询方式输入操作流程读状态端口
Ready?
从数据端口读一个数据处理完?
N
Y
N
Y
打印机是一种可以按查询方式工作的输出外设,它与主机连接的接口部件是打印机适配器,也就是平常所说的,标准并行接口,或,并口,。 打印机接口中的三大部件齐备,
它们各自占据一个外设端口号码,分别是:
数据部件 ── 378H;
状态部件 ── 379H;
控制部件 ── 37AH。
状态端口各位的含义如下:
Busy ACK PE SELECT ERROR
D7 D6 D5 D4 D3 D2 D1 D0
( 1) D7位:是打印机的,忙,信号 。 这一位为 0表示打印机当前正处于,忙,状态,为 1表示打印机不忙 。
( 2) D6位:是打印机送回的认可信号 。 当打印机从接口中正确接收 1字节数据后,就送回一个低电位的认可信号,
这个信号只维持很短的时间,然后又恢复高电位 。
( 3) D5位:缺纸信号 。 这一位为 1表示打印机无纸,因而无法打印,为 0时表示正常工作 。
( 4) D4位:联机信号 。 这一位为 1表示正处于联机工作状态,为 0表示没有联机,这时不能工作 。
( 5) D3位:出错信号 。 为 0表示打印机内部出现错误,
不能工作,为 1则正常 。
控制端口各位的含义如下:
INT SELECT INIT Auto Feed STB
D7 D6 D5 D4 D3 D2 D1 D0
( 1) D4位:允许中断信号 。 将该位置 1,允许打印机以中断方式工作,为 0则不允许 。
( 2) D3位:联机命令 。 置 1将设置打印机的联机工作方式,控制打印机时总是把这一位置 1,否则打印机不能正常工作 。 打印机正常打印时这一位需要保持 1。
( 3) D2位:初始化信号 。 正常工作时总是把这一位置 1,
需要把打印机重新初始化时,则在这一位先清 0再置 1,并且要维持清 0的时间 0.05秒以上 。 初始化又称作打印机复位,
复位时可观察到的现象是打印头回到最左边 。
( 4) D1位:自动走纸 。 置 1要求打印机在打印完一行后
( 回车时 ) 自动走纸,清 0时则需要向打印机输出换行符
( 0AH) 控制走纸 。 这一位通常被置为 0。
( 5) D0位:选通信号 。 CPU通过在这一位上先置 1再清 0,
通知打印机从数据部件中取走一字节数据并打印 。
把图 8.2中查询输出的流程稍做修改,可以作为控制打印机以查询方式工作的程序流程,如图 8.3所示。
计数器清 0
读状态端口
Ready?
送数据到数据端口送先高后低的 STB信号打印完?
计数器加 1,取下一数据
N
Y
Y
N
结束图 8.3 查询方式控制打印机
【 例 8.3】 控制打印机以查询方式工作,打印 26个英文字母,共打印 30行 。
【 解 】
sta SEGMENT STACK
DB 1024 DUP( 0)
sta ENDS
data SEGMENT
print DB 'abcdefghijklmnopqrstuvwxyz',0DH,0AH
data ENDS
code SEGMENT
ASSUME CS:code,SS:sta,DS:data
init PROC NEAR ;打印机初始化程序
MOV DX,37AH
MOV AL,08H
OUT DX,AL
MOV CX,1000
init1,LOOP init1 ;延迟,维持初始化信号一段时间
MOV AL,0CH
OUT DX,AL ;结束初始化,保持联机
RET
init ENDP
start,MOV AX,data
MOV DS,AX
MOV CX,30 ;30行
again,LEA BX,print ;待打印字符串首地址存放于 BX中
next,MOV DX,379H
wait,IN AL,DX
TEST AL,80H
JZ wait ;打印机忙,转去再读状态端口
DEC DX ;数据端口号
MOV AL,[BX]
OUT DX,AL ;送数据到数据端口暂存
MOV DX,37AH
MOV AL,0DH
OUT DX,AL ;置 STB信号为 1
DEC AL
OUT DX,AL ;置 STB信号为 0
CMP [BX],0AH
JZ newline
INC BX
JMP next
newline,LOOP again
MOV AX,4C00H
INT 21H
code ENDS
END start
8.4.1 中断的基本概念
1,中断
CPU暂停正在执行的程序,转去完成另一件工作,完成后再回到原来的程序继续执行的现象称为中断 。
2,中断源能够导致 CPU中断的事件称为中断源 。
8.4 中断方式输入输出
3,中断源分类内中断,来自 CPU内部的中断请求 。
( 1) 中断指令
( 2) 错误的中断
① 除法错中断
② 溢出中断若产生溢出 ( OF=1),INTO中断发生溢出的算术操作 。
如,ADD AX,BX
INTO
( 3) 为 DEBUG设置的中断
① 单步中断:每条指今执行后,CPU产生中断 。
产生的条件,TF( 陷井标 ) =1。
② 断点中断,CPU执行到断点处 ( INT 3 ) 产生中断 。
外中断,来自 CPU芯片外的中断请求 。
外中断源分为两类:非屏蔽外中断和可屏蔽外中断 。
可屏蔽外中断的控制:中断屏蔽寄存器 ( 端口号 21H) (P293)
标志寄存器的 IF位( 1:允许中断)
4,中断号在计算机系统中,各种中断源都被统一地编排了一个互不相同的号码,用以唯一地标识一个中断源,这个号码称为中断号 。
5,中断源识别由于中断源有不同的类型,向 CPU申请中断的方式也各不相同。当 CPU知道有中断请求后,还必须判断出究竟是几号中断请求。 CPU确定中断号的过程称为中断源识别。如果中断请求来自 CPU内部,CPU内有相应机制可以取到内中断请求的中断号;如果是不可屏蔽外中断请求,系统只安排了唯一的中断号( 2号);当中断请求来自 CPU的 INTR外引脚时,情况就比较复杂了。
6,中断优先级
8088把所有中断源划分为 4个等级,以 0级为最高,依次降低等级 。
0级 ── 除单步中断以外的内中断;
1级 ── 非屏蔽外中断;
2级 ── 可屏蔽外中断;
3级 ── 单步中断 。
7,中断服务程序
CPU响应某个中断时去执行的程序称为,中断服务程序,
或,中断处理程序,。
8,中断向量把中断服务程序的入口地址的各个字节按照一定的规则排列起来,构成的一个有特定含义的数据组合称为,中断向量,或,中断矢量,(中断服务程序的段地址和偏移地址 )。
9,中断向量表把所有这些中断向量集中起来,按照中断号由 0到 255的顺序,从内存物理地址为 0处开始依次存放,构成一张,中断向量表,。 (中断向量地址?)
00000 0号中断服务程序偏移地址低字节
00001 0号中断服务程序偏移地址高字节
00002 0号中断服务程序段地址低字节
00003 0号中断服务程序段地址高字节
00004 1号中断服务程序偏移地址低字节
00005 1号中断服务程序偏移地址高字节
00006 1号中断服务程序段地址低字节
00007 1号中断服务程序段地址高字节
00008 2号中断服务程序偏移地址低字
003FF 0FFH号中断服务程序段地址高字节
…
11,中断嵌套在 CPU执行一个低级别的中断服务程序时,如果系统中又产生了一个高级别的中断请求,这时系统会暂停低级中断服务,优先处理高级别中断,处理完后再继续低级中断服务 。
这种高级别中断打断低级别中断服务的现象称为中断嵌套 。
中断嵌套的典型例子是,在一个中断服务程序中,需要在屏幕上显示一些信息,屏幕显示需要用到后面将要说明的
10H号中断调用,因此在中断服务程序中就会写有 INT 10H
指令 。 这是一条内中断调用指令,是最高级别的中断,在执行到该指令时,系统会转去先进行 10H号中断服务,在屏幕上进行显示,10H号中断返回后,再继续原先低级别的中断服务 。
8.4.2 中断处理过程不论是软中断还是硬中断,当 CPU响应中断时,自动完成下列操作:
( 1) 取中断号 n( 中断源识别 ) 。
( 2) 把标志寄存器 ( PSW) 的内容入栈 。
( 3) 当前 CS的值入栈 。
( 4) 当前 IP的值入栈 。
( 5) 把标志寄存器中的 IF和 TF标志位清 0。
( 6) 从物理地址 4× n处取出 4个字节的 n号中断的中断向量,设取出的数据依次是 b0,b1,b2,b3,把 ( b1,b0) 拼成一个字型数据送到 IP中,
( b3,b2) 拼成一个字型数据送到 CS中 。
( 7) 按 CS:IP的新值继续执行 。
8.4.3 与中断有关的指令
【 中断指令 】 INT n
【 功能 】 产生一次 n号中断请求 。
【 中断返回指令 】 IRET
【 功能 】 从栈中弹出 3个字,第 1个弹出的送到 IP,第 2个到 CS,
第 3个到 PSW。
【 指令格式 】 CLI
【 功能 】 把标志寄存器的 IF标志位清 0,使 CPU不响应可屏蔽外中断 。
【 指令格式 】 STI
【 功能 】 把标志寄存器的 IF标志位置 1,允许 CPU响应可屏蔽外中断 。
8.4.4 中断与子程序的比较
( 1) 都需要相应程序段的支持 。
( 2) 软件中断与子程序都由特定指令调用 。
( 3) 发生调用时,系统自动记载返回地址 。
( 4) 软件中断和子程序都可以带有入口参数和出口参数 。
( 5) 可以用子程序调用代替软件中断的调用指令 。
INT指令与 FAR类型子程序调用的 CALL指令之间的主要差别在于是否把标志寄存器入栈,因此,只要能够先把标志寄存器入栈保存,再用 CALL指令同样可以进行软件中断服务子程序的调用 。 比如,下面的方法完全可以代替 INT 21H指令:
MOV BX,0
MOV DS,BX ;中断向量表的段地址
MOV BX,84H ;21H号中断向量在中断向量表中的偏移地址
PUSHF
CALL FAR PTR [BX] ; 以 子 程 序 调 用 的
CALL指令去调用 21H号中断服务子程序软件中断,硬件中断与子程序三者之间也存在着本质的差别,主要体现在:
( 1) 调用方式不同 。 软件中断由 INT指令调用,子程序用 CALL指令调用,而硬件中断是由硬件提出申请,不需要任何指令 。
( 2) 系统保护的值不同 。 中断调用时,系统会把标志寄存器,CS和 IP入栈保存,而子程序调用时,系统只入栈保存
IP或者 CS和 IP。
( 3) 返回方式不同 。 中断返回指令是 IRET,而子程序返回指令是 RET,并且子程序还有 NEAR和 FAR两种类型 。
( 4) 共享方式不同 。 硬件中断的服务程序不能被其它程序共享,而是直接由系统掌握,软件中断的服务程序可以被任何程序以 INT指令的形式调用,并且调用者不必关心中断服务程序到底在内存的哪一个地方,也不需要把中断服务程序与调用它的程序拼装到一起 。 子程序的共享方式比较丰富,但有一点,就是被调用的子程序必须与调者拼装在一起,形成最终的执行文件 。
( 5) 在内存中存在的时间不同 。 中断服务程序通常是长期保留在内存中,而子程序是随可执行文件一起进入内存,当可执行文件执行完后,子程序所占用的内存也随之释放 。
8.4.6 中断服务程序主程序为中断所做的准备工作和硬件(包括 CPU和外设接口)
自动完成的动作。
( 1) 设置中断和向量 。
( 2) 设置设备的中断屏蔽位 。
( 3) 设置 CPU的中断允许位 IF( 开中断 ) 。
( 4) 外设接口送中断请求给 CPU。
( 5) 当前指令执行完成后,CPU送响应信号给外设接口 。
( 6) CPU接收中断类型号 。
( 7) 当前的 PSW,CS和 IP保存入栈 。
( 8) 清除 IF,TF。
( 9) 中断向量送 IP和 CS。
编写中断处理子程序的步骤,。
( 1) 保存寄存器内容 。
( 2) 如允许中断嵌套,则开中断 ( STI) 。
( 3) 处理中断 。
( 4) 关中断 。
( 5) 送中断结束命令 ( EOI) 给中断命令寄存器 。
( 6) 恢复寄存器内容 。
( 7) 返回被中断的程序 ( IRET) 。
编写中断服务子程序需要掌握的技术:
让一段程序常驻内存;
修改中断向量使其指向新的中断服务程序 。
8.4.6.1 常驻内存技术
【 驻留方法 】 MOV AH,31H
INT 21H
入口参数,DX=需要驻留的程序的节长度 。
【 说明 】
( 1) 驻留前要告诉 DOS,驻留程序的长度是多少 。 方法是把驻留长度放在 DX 中,长度单位是,节,而不是字节,
1节等于 16个字节 。 如果需要驻留的程序长度是 n 字节,则
DX的值可通过下面的计算式算得:
DX=( n ÷ 10H)+ 1+ 10H
其中( n ÷ 10H)+ 1是计算出驻留程序需要多少“节”,
加 1是为了预防驻留程序以字节计算的长度不是 16的整数倍。
再加 16节是因为每个程序在调入内存时,操作系统都为它安排了一个称为“程序段前缀( PSP)”的专用内存区,并且放在程序的前面,这个程序段前缀的长度是 256字节,刚好
16节,它必须与需要驻留的程序一起驻留在内存。
数据段,200B÷ 16+ 1= 13节代码段,500B÷ 16+ 1= 32节
PSP,16节
──────────────
总计,61节因此,调用 DOS的 31H号子功能进行程序驻留前,必须把
DX置为 61,即 42H。
8.4.6.2 修改中断向量的技术
【 设置中断向量 】
入口参数,AH= 25H
AL=中断号
DS:DX=新的中断服务程序的入口地址调用,INT 21H
【 取中断向量 】
入口参数,AH=35H
AL=中断号出口参数,ES:BX=中断向量调用,INT 21H
8.4.6.3 中断编程实例
8088系统中有一个定时器,它每隔约 0.05秒向 CPU
发出一次 8号中断请求,这是一个硬中断,不受程序的控制。 8号中断服务程序中有一条调用 1CH号中断的指令,而 1CH号中断是一个可供用户使用的中断,它只由
8号中断调用,与硬中断有类似的效果。下面就利用
1CH号中断,编写一个响铃程序。
【 例 8.4】 编一段中断处理程序,在主程序运行的过程中,每隔 10秒钟响铃一次,同时在屏上显示
,The bell is ring!”。
编程如下:
DATA SEGMENT
COUNT DW 1
MESS DB ‘The bell is ring!’,0DH,0AH,’$’
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,ES:DATA
MAIN PROC FAR
START,PUSH DS
SUB AX,AX
PUSH AX
MOV AX,DATA
MOV DS,AX
MOV AL,1CH;取中断向量
MOV AH,35H
INT 21H
PUSH ES ;存原中断向量段址
PUSH BX ;存原中断向量偏移量
PUSH DS
MOV DX,OFFSET RING ;新偏移量送 DX
MOV AX,SEG RING
MOV DS,AX;新段址送 DS
MOV AL,1CH ;写入新的中断向量
MOV AH,25H
INT 21H
POP DS
IN AL,21H ;读取中断屏蔽字
AND AL,11111110B;允许定时器中断
OUT 21H,AL
STI ;开中断
MOV DI,2000
DELAY,MOV SI,3000;延时
DELAY1,DEC SI
JNZ DELAY1
DEC DI
JNZ DELAY
POP DX ;恢复原中断向量
POP DS
MOV AL,1CH
MOV AH,25H
INT 21H
RET
MAIN ENDP
RING,PROC NEAR
PUSH DS ;各工作寄存器内容入栈
PUSH AX
PUSH CX
PUSH DX
MOV AX,DATA
MOV DS,AX
STI
DEC COUNT ;计秒值
JNZ EXIT
MOV DX,OFFSET MESS
MOV AH,09H
INT 21H
MOV DX,100
IN AL,61H
AND AL,0FCH
SOUND,XOR AL,02
OUT 61H,AL;扬声器发声
MOV CX,140H
WAIT1,LOOP WAIT1;延时等待
DEC DX
JNE SOUND
MOV COUNT,182
EXIT,CLI;关中断
POP DX
POP CX
POP AX
POP DS
IRET ;中断返回
RING ENDP
CODE ENDS
END START
【 例 8.5】 编写一个时钟程序,要求把时钟信号显示在屏幕的右上角,并在程序结束后常驻内存。
【 分析 】 由于系统每隔 0.05秒就产生一次 1CH号中断,
因此在 1CH号中断服务程序中应该有一个 0到 20的计数器,
初值为 20。 每次中断调用该服务程序时,就把计数器的值减
1,当计数器的值没减到 0时,说明还没有中断 20次,即不足
1秒钟,因而不需要更新钟的读数 。 当计数值到 0后,就需要让时钟向前走 1秒,把秒数加 1,若满 60秒则向分钟数进 1,
分钟数满 60后再向小时数进 1,小时数满 24就清 0,并且要把已走了 1秒的钟的当前读数显示在屏幕上 。 另外,为了计数下一秒,还要把 0到 20的计数器重新置初值 20。 处理完这些工作后,中断结束并返回 。
主程序需要完成以下工作:把 1CH号中断向量改为新的中断服务程序的入口地址,让新的中断服务程序常驻内存。
【 解 】
code SEGMENT
ASSUME CS:code,DS:code
clock PROC FAR
DEC CS:[x] ;计数器减 1
JZ c2 ;满 1秒转
JMP cend ;不足 1秒,直接转中断结束
c2,PUSH DS
PUSH ES
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI ;保护现场
PUSH CS
POP DS
MOV [x],20 ;对 0到 20的计数器重新赋初值
INC [s] ;秒数加 1
CMP [s],60
JB c1 ;不满 60秒转
MOV [s],0 ;计满 60秒后,秒数清 0
INC [m] ;分钟数加 1
CMP [m],60
JB c1 ;不满 60分钟转
MOV [m],0 ;计满 60分钟后,分钟数清 0
INC [h] ;小时数加 1
CMP [h],24
JB c1 ;不满 24小时转
MOV [h],0 ;计满 24小时后,小时数清 0
c1,MOV AL,[s]
MOV AH,0
MOV DL,10
DIV DL
OR AX,3030H
MOV [t+6],AX ;把秒钟数拆分成两位 ASCII送入变量 t+6处
MOV AL,[m]
MOV AH,0
DIV DL
OR AX,3030H
MOV [t+3],AX ;把分钟数拆分成两位 ASCII送入变量 t+3处
MOV AL,[h]
MOV AH,0
DIV DL
OR AX,3030H
MOV [t],AX ;把小时数拆分成两位 ASCII送入变量 t处
MOV CX,8
CLD
MOV AX,0B800H
MOV ES,AX ;取显示缓冲区段地址
MOV AH,1FH ;时钟读数的字符属性,兰底,高亮度白字
LEA SI,[t]
MOV DI,71*2
c3,LODSB
STOSW
LOOP c3 ;以直写屏方式显示
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
POP ES
POP DS
cend,IRET
t DB '00:00:00' ;存放时钟读数的变量
h DB 0 ;计小时数的变量
m DB 0 ;计分钟数的变量
s DB 0 ;计秒钟数的变量
x DB 20 ;0到 20次的计数变量
clock ENDP
main,MOV AX,code
MOV DS,AX
MOV AH,2CH
INT 21H ;取当前系统时间
MOV [h],CH ;置小时数初值
MOV [m],CL ;置分钟数初值
MOV [s],DH ;置秒钟数初值
LEA DX,clock
MOV AX,251CH
INT 21H ;置 1CH号中断向量,使指向 CLOCK
子程序
MOV AX,3100H
LEA DX,main
MOV CL,4
SHR DX,CL
ADD DX,11H ;计算驻留程序的节长度
INT 21H ;结束并驻留
RET
code ENDS
END main
本 章 要 点输入输出设备是计算机对外发挥作用的途径 。 不同的外设有各自的特点,需要不同的控制方法,但总的来说,外设都需要通过一个接口部件与计算机的主机相连 。 接口负责主机内外的信号转达,接口通常包括命令部件,数据部件和状态部件三大组成部分,每个部件占用一个或多个端口地址 。
输入输出的主要方式有 4种 。 直接方式适用于那些能与主机速度相匹配的外设,查询和中断方式则用于速度较慢,控制较复杂的外设,而 DMA方式提供了一种不通过 CPU直接在主机与内存之间进行高速数据传递的方法 。 用户可以编写程序对以直接方式,查询方式,中断方式工作的外设进行操作控制 。
中断是 CPU的一种重要工作方式,由中断源发出中断请求信号开始,经过中断源识别,现场保护,执行中断服务程序,
现场恢复的过程,使 CPU在完成中断服务后能接着被中断的程序继续执行 。
8.1 输入输出的基本概念
8.2 无条件方式输入输出
8.3 查询方式输入输出
8.4 中断方式输入输出本章要点
8.1 输入输出的基本概念从第 2章的图 2.1可以看到,计算机的硬件系统由 CPU、内存、外设三大部分构成,它们相互之间通过一组信息传递的公共通道 ──总线联系在一起的。 CPU和内存构成了计算机的主机部分,是计算机中的高速设备。而大多数外部设备都是慢速设备,用来把从主机以外采集到的数据送入主机内部,或者把主机内的数据传递到外部,外设中的外部存储器还可以存储大量的数据。那么,CPU作为计算机的核心,它又是如何控制外设的呢?
8.1.1 外设接口接口,用于把 CPU来的控制命令转换成外设的控制信号,把外设的工作情况转换成 CPU可以读取并处理的状态信号 。
计算机系统中对外设接口也进行了编号,这个号码称作,外设端口号,。 每一个端口号对应外设接口中的一个存放字节型数据的元件,称为一个,外设端口,。
一个外设接口中往往需要多个外设端口,占据多个外设端口号 。 CPU控制外设就是通过从这些外设端口中读取数据以及向它们发送数据实现的 。
大多数外设接口从功能上可以分为控制部件,状态部件和数据部件三大组成部分 。
控制部件又称为命令部件,专用于接收来自主机的操作命令,并转换成对外设的控制信号;
状态部件负责向主机转达外设的当前工作情况;
数据部件是内外数据交换的缓冲器,临时存放需要传递的数据 。
8.1.2 控制外设的指令由于 8088采取了独立编址方式,就需要有特殊的指令控制对外设端口的操作 。 8088系统设计有两条专用指令,IN和
OUT。
【 指令格式 】 IN d1,d2
【 功能 】 从 d2指明的外设端口中读取 1字节或 2字节数据,
送到操作数 d1指出的地方 。
【 说明 】
( 1) 8088系统限制 d1只能是 AL或 AX。 当外设端口号不超过 255时,d2操作数可直接写端口号码,是外设的直接寻址方式;端口号超过 255时,必须先把端口号放在 DX中,以 DX
作为 d2操作数,这是外设的间接寻址方式 。
( 2) 当 d1是 AL时,该指令从指定的端口中读 1字节数据;
当 d1是 AX时,CPU将从 d2对应的端口读 1字节数据到 AL,从下一个端口号读 1字节数据到 AH,即总共读取 16位数据送到
AX 中 。
【 指令格式 】 OUT d1,d2
【 功能 】 把操作数 d2指明的 1字节或 2字节数据送到 d1对应的外设端口中 。
【 说明 】
( 1) d2只能是 AL或 AX。当外设端口号不超过 255时,d1
操作数可直接写端口号码;端口号超过 255时,必须先把端口号放在 DX中,以 DX作为 d1操作数。
( 2) 当 d2是 AL时,该指令把 AL中的 8位数据送往 d1指定的外设端口;当 d2是 AX时,该指令把 AL中的 8位数据送到 d1对应的端口,把 AH中的 8位数据送往下一端口,即把
16位数据送到 d1对应的端口及下一端口中 。
【 例 8.1】 说明下面各指令或程序段的功能 。
( 1) MOV DX,61H
IN AL,DX
( 2) MOV BX,61H
MOV AL,[BX]
( 3) OUT 21H,AL
( 4) MOV SI,21H
MOV [SI],AL
【 解 】
( 1) 先把立即数 61H放到 DX中,然后以 DX中的 61H作为外设端口号,从相应的外设端口读取 1字节数据送到 AL
中 。
( 2) 先把立即数 61H放到 BX中,然后以 BX中的 61H作为偏移地址,以缺省段寄存器 DS 中的值为段地址,从相应的内存中取出 1字节数据送到 AL中 。
( 3) 把 AL中的 1字节数据送往 21H号外设端口 。
( 4)先把立即数 21H放到 SI中,然后以 SI中的 21H作为偏移地址,以缺省段寄存器 DS 中的值为段地址,把 AL中的 1字节数据送到相应的内存中。
8.1.4 输入输出方式主机与外设之间数据交换的方法有 4种:无条件方式、
查询方式、中断方式,DMA方式。
1,无条件方式又称为直接方式,是指 CPU可在任何时刻直接以外设操作指令与外部设备进行数据传递 。 显然,这种方式对外设有很高的要求,它必须能像内存一样时刻准备着与 CPU
进行数据传递,并且能够跟上 CPU的速度,保证传送的信息的正确性 。
2,查询方式使用查询方式工作的外设必须至少有两个部件,其中之一是状态部件 。 CPU每一次与外设进行数据交换之前,
先从状态部件读取信息,判断外设是否处于,就绪,
( Ready) 状态 。 如果来自外设的状态信息反映出外设,没有准备好,或正,忙,( Busy),说明还不能进行数据传递;反之,当 CPU检测到外设已准备好 ( Ready) 后,可以与外设进行一次数据交换 。
3,中断方式这是指每当外设准备好,能够进行数据传递时,就向
CPU发出一个特殊的请求信号,称为中断请求信号 。 CPU
收到中断请求后,暂停当前的工作,转而执行一段预先设计好的中断服务程序,完成对外设的数据交换 。 执行完中断服务程序后,CPU仍回到被暂停的程序继续执行 。
4,直接存储器存取 DMA( Direct Memory Access) 方式这是一种不通过 CPU,在内存与外设之间直接进行高速数据交换的方法。
进行 DMA方式的数据传递必须有一个前提条件,就是
CPU能够让出总线的控制权,交由 DMA方式数据传递的专用控制器控制,当数据传递结束后,CPU再收回总线控制权 。
DMA控制器专门用于控制内存与外设之间的直接数据传递,但是它没有数据处理能力 。 DMA控制器包括:控制寄存器,状态寄存器,地址寄存器和字节计数器 。
完成 DMA传送的步骤:
( 1) DMA控制器向 CPU发出 HOLD信号,请求使用总线 。
( 2) CPU发出响应信号 HOLD给 DMA,并将总线让出 。
( 3) 传输数据的存储器地址 ( 在地址寄存器中 ) 通过地址总线发出 。
( 4) 传输的数据字节通过数据总线进行传递 。
( 5) 地址寄存器增 1,指向下一个字节 。
( 6) 字节计数器减 1。
( 7) 如字节计数器不为零,转向 ( 3) 。
( 8) 否则 DMA控制器撤消总线请求信号 HOLD,传递结束 。
系统总线端口
61H,P7 P6 P5P4P3P2 P1 P0 1.193MHz
定时器 A
B
与门 放大电路扬声器图 8.1 扬声器与系统总线的连接
8.2 无条件方式输入输出计算机系统中的扬声器是一种简单的输出设备,可以随时从 CPU接收控制命令。图 8.1是扬声器的连接原理图。
【 例 8.2】 利用无条件数据传送方式,让计算机的扬声器在 1秒钟内保持发出 500 Hz 的声音 。
【 解 】 完整的程序如下:
delnum = 14000
sta SEGMENT STACK
DW 1024 DUP( 0)
sta ENDS
code SEGMENT
ASSUME CS:code,SS:sta
delay PROC NEAR ;时间延迟子程序,以 CX中的值作为
PUSH AX ;循环次数,做若干次的乘法产生时
PUSH DX ;间延迟的效果
del,IMUL AX ;用乘法指令延迟,执行时间较长
LOOP del
POP DX
POP AX
RET
delay ENDP
main,MOV CX,500
sou,PUSH CX
IN AL,61H ;读出原 61H端口的数据
AND AL,11111100B ;清最低两位
OUT 61H,AL ;送低电位到 P1
MOV CX,delnum ;取控制延迟时间的循环次数值
CALL delay
IN AL,61H
OR AL,00000010B ;置 P1位为 1
OUT 61H,AL
MOV CX,delnum
CALL delay
POP CX
LOOP sou
MOV AX,4C00H
INT 21H
code ENDS
END main
从这个例子可以看出,对扬声器的控制是不需要任何条件的,程序中在向 61H号端口送数据时根本不考虑扬声器是否准备好、是否可接收数据等问题。由端口 61H到放大电路构成了扬声器的接口电路,该接口以系统总线上送来的信号驱动薄膜的振动,按无条件方式进行数据传送。
8.3 查询方式输入输出大多数外设不可能像扬声器那样工作,这些外设处理数据的速度和提供数据的速度往往比主机内部速度慢得多,因而 CPU与这类设备进行数据传递前必须先判断它们是否,就绪,。 CPU以查询方式从外设读取一批数据,以及向外设送出一批数据的流程如图 8.2所示 。
Y
读状态端口
Ready?
处理完?
N
Y
N
向数据端口写一个数据或向命令端口发一个操作命令
( b)查询方式输出操作流程( a)查询方式输入操作流程读状态端口
Ready?
从数据端口读一个数据处理完?
N
Y
N
Y
打印机是一种可以按查询方式工作的输出外设,它与主机连接的接口部件是打印机适配器,也就是平常所说的,标准并行接口,或,并口,。 打印机接口中的三大部件齐备,
它们各自占据一个外设端口号码,分别是:
数据部件 ── 378H;
状态部件 ── 379H;
控制部件 ── 37AH。
状态端口各位的含义如下:
Busy ACK PE SELECT ERROR
D7 D6 D5 D4 D3 D2 D1 D0
( 1) D7位:是打印机的,忙,信号 。 这一位为 0表示打印机当前正处于,忙,状态,为 1表示打印机不忙 。
( 2) D6位:是打印机送回的认可信号 。 当打印机从接口中正确接收 1字节数据后,就送回一个低电位的认可信号,
这个信号只维持很短的时间,然后又恢复高电位 。
( 3) D5位:缺纸信号 。 这一位为 1表示打印机无纸,因而无法打印,为 0时表示正常工作 。
( 4) D4位:联机信号 。 这一位为 1表示正处于联机工作状态,为 0表示没有联机,这时不能工作 。
( 5) D3位:出错信号 。 为 0表示打印机内部出现错误,
不能工作,为 1则正常 。
控制端口各位的含义如下:
INT SELECT INIT Auto Feed STB
D7 D6 D5 D4 D3 D2 D1 D0
( 1) D4位:允许中断信号 。 将该位置 1,允许打印机以中断方式工作,为 0则不允许 。
( 2) D3位:联机命令 。 置 1将设置打印机的联机工作方式,控制打印机时总是把这一位置 1,否则打印机不能正常工作 。 打印机正常打印时这一位需要保持 1。
( 3) D2位:初始化信号 。 正常工作时总是把这一位置 1,
需要把打印机重新初始化时,则在这一位先清 0再置 1,并且要维持清 0的时间 0.05秒以上 。 初始化又称作打印机复位,
复位时可观察到的现象是打印头回到最左边 。
( 4) D1位:自动走纸 。 置 1要求打印机在打印完一行后
( 回车时 ) 自动走纸,清 0时则需要向打印机输出换行符
( 0AH) 控制走纸 。 这一位通常被置为 0。
( 5) D0位:选通信号 。 CPU通过在这一位上先置 1再清 0,
通知打印机从数据部件中取走一字节数据并打印 。
把图 8.2中查询输出的流程稍做修改,可以作为控制打印机以查询方式工作的程序流程,如图 8.3所示。
计数器清 0
读状态端口
Ready?
送数据到数据端口送先高后低的 STB信号打印完?
计数器加 1,取下一数据
N
Y
Y
N
结束图 8.3 查询方式控制打印机
【 例 8.3】 控制打印机以查询方式工作,打印 26个英文字母,共打印 30行 。
【 解 】
sta SEGMENT STACK
DB 1024 DUP( 0)
sta ENDS
data SEGMENT
print DB 'abcdefghijklmnopqrstuvwxyz',0DH,0AH
data ENDS
code SEGMENT
ASSUME CS:code,SS:sta,DS:data
init PROC NEAR ;打印机初始化程序
MOV DX,37AH
MOV AL,08H
OUT DX,AL
MOV CX,1000
init1,LOOP init1 ;延迟,维持初始化信号一段时间
MOV AL,0CH
OUT DX,AL ;结束初始化,保持联机
RET
init ENDP
start,MOV AX,data
MOV DS,AX
MOV CX,30 ;30行
again,LEA BX,print ;待打印字符串首地址存放于 BX中
next,MOV DX,379H
wait,IN AL,DX
TEST AL,80H
JZ wait ;打印机忙,转去再读状态端口
DEC DX ;数据端口号
MOV AL,[BX]
OUT DX,AL ;送数据到数据端口暂存
MOV DX,37AH
MOV AL,0DH
OUT DX,AL ;置 STB信号为 1
DEC AL
OUT DX,AL ;置 STB信号为 0
CMP [BX],0AH
JZ newline
INC BX
JMP next
newline,LOOP again
MOV AX,4C00H
INT 21H
code ENDS
END start
8.4.1 中断的基本概念
1,中断
CPU暂停正在执行的程序,转去完成另一件工作,完成后再回到原来的程序继续执行的现象称为中断 。
2,中断源能够导致 CPU中断的事件称为中断源 。
8.4 中断方式输入输出
3,中断源分类内中断,来自 CPU内部的中断请求 。
( 1) 中断指令
( 2) 错误的中断
① 除法错中断
② 溢出中断若产生溢出 ( OF=1),INTO中断发生溢出的算术操作 。
如,ADD AX,BX
INTO
( 3) 为 DEBUG设置的中断
① 单步中断:每条指今执行后,CPU产生中断 。
产生的条件,TF( 陷井标 ) =1。
② 断点中断,CPU执行到断点处 ( INT 3 ) 产生中断 。
外中断,来自 CPU芯片外的中断请求 。
外中断源分为两类:非屏蔽外中断和可屏蔽外中断 。
可屏蔽外中断的控制:中断屏蔽寄存器 ( 端口号 21H) (P293)
标志寄存器的 IF位( 1:允许中断)
4,中断号在计算机系统中,各种中断源都被统一地编排了一个互不相同的号码,用以唯一地标识一个中断源,这个号码称为中断号 。
5,中断源识别由于中断源有不同的类型,向 CPU申请中断的方式也各不相同。当 CPU知道有中断请求后,还必须判断出究竟是几号中断请求。 CPU确定中断号的过程称为中断源识别。如果中断请求来自 CPU内部,CPU内有相应机制可以取到内中断请求的中断号;如果是不可屏蔽外中断请求,系统只安排了唯一的中断号( 2号);当中断请求来自 CPU的 INTR外引脚时,情况就比较复杂了。
6,中断优先级
8088把所有中断源划分为 4个等级,以 0级为最高,依次降低等级 。
0级 ── 除单步中断以外的内中断;
1级 ── 非屏蔽外中断;
2级 ── 可屏蔽外中断;
3级 ── 单步中断 。
7,中断服务程序
CPU响应某个中断时去执行的程序称为,中断服务程序,
或,中断处理程序,。
8,中断向量把中断服务程序的入口地址的各个字节按照一定的规则排列起来,构成的一个有特定含义的数据组合称为,中断向量,或,中断矢量,(中断服务程序的段地址和偏移地址 )。
9,中断向量表把所有这些中断向量集中起来,按照中断号由 0到 255的顺序,从内存物理地址为 0处开始依次存放,构成一张,中断向量表,。 (中断向量地址?)
00000 0号中断服务程序偏移地址低字节
00001 0号中断服务程序偏移地址高字节
00002 0号中断服务程序段地址低字节
00003 0号中断服务程序段地址高字节
00004 1号中断服务程序偏移地址低字节
00005 1号中断服务程序偏移地址高字节
00006 1号中断服务程序段地址低字节
00007 1号中断服务程序段地址高字节
00008 2号中断服务程序偏移地址低字
003FF 0FFH号中断服务程序段地址高字节
…
11,中断嵌套在 CPU执行一个低级别的中断服务程序时,如果系统中又产生了一个高级别的中断请求,这时系统会暂停低级中断服务,优先处理高级别中断,处理完后再继续低级中断服务 。
这种高级别中断打断低级别中断服务的现象称为中断嵌套 。
中断嵌套的典型例子是,在一个中断服务程序中,需要在屏幕上显示一些信息,屏幕显示需要用到后面将要说明的
10H号中断调用,因此在中断服务程序中就会写有 INT 10H
指令 。 这是一条内中断调用指令,是最高级别的中断,在执行到该指令时,系统会转去先进行 10H号中断服务,在屏幕上进行显示,10H号中断返回后,再继续原先低级别的中断服务 。
8.4.2 中断处理过程不论是软中断还是硬中断,当 CPU响应中断时,自动完成下列操作:
( 1) 取中断号 n( 中断源识别 ) 。
( 2) 把标志寄存器 ( PSW) 的内容入栈 。
( 3) 当前 CS的值入栈 。
( 4) 当前 IP的值入栈 。
( 5) 把标志寄存器中的 IF和 TF标志位清 0。
( 6) 从物理地址 4× n处取出 4个字节的 n号中断的中断向量,设取出的数据依次是 b0,b1,b2,b3,把 ( b1,b0) 拼成一个字型数据送到 IP中,
( b3,b2) 拼成一个字型数据送到 CS中 。
( 7) 按 CS:IP的新值继续执行 。
8.4.3 与中断有关的指令
【 中断指令 】 INT n
【 功能 】 产生一次 n号中断请求 。
【 中断返回指令 】 IRET
【 功能 】 从栈中弹出 3个字,第 1个弹出的送到 IP,第 2个到 CS,
第 3个到 PSW。
【 指令格式 】 CLI
【 功能 】 把标志寄存器的 IF标志位清 0,使 CPU不响应可屏蔽外中断 。
【 指令格式 】 STI
【 功能 】 把标志寄存器的 IF标志位置 1,允许 CPU响应可屏蔽外中断 。
8.4.4 中断与子程序的比较
( 1) 都需要相应程序段的支持 。
( 2) 软件中断与子程序都由特定指令调用 。
( 3) 发生调用时,系统自动记载返回地址 。
( 4) 软件中断和子程序都可以带有入口参数和出口参数 。
( 5) 可以用子程序调用代替软件中断的调用指令 。
INT指令与 FAR类型子程序调用的 CALL指令之间的主要差别在于是否把标志寄存器入栈,因此,只要能够先把标志寄存器入栈保存,再用 CALL指令同样可以进行软件中断服务子程序的调用 。 比如,下面的方法完全可以代替 INT 21H指令:
MOV BX,0
MOV DS,BX ;中断向量表的段地址
MOV BX,84H ;21H号中断向量在中断向量表中的偏移地址
PUSHF
CALL FAR PTR [BX] ; 以 子 程 序 调 用 的
CALL指令去调用 21H号中断服务子程序软件中断,硬件中断与子程序三者之间也存在着本质的差别,主要体现在:
( 1) 调用方式不同 。 软件中断由 INT指令调用,子程序用 CALL指令调用,而硬件中断是由硬件提出申请,不需要任何指令 。
( 2) 系统保护的值不同 。 中断调用时,系统会把标志寄存器,CS和 IP入栈保存,而子程序调用时,系统只入栈保存
IP或者 CS和 IP。
( 3) 返回方式不同 。 中断返回指令是 IRET,而子程序返回指令是 RET,并且子程序还有 NEAR和 FAR两种类型 。
( 4) 共享方式不同 。 硬件中断的服务程序不能被其它程序共享,而是直接由系统掌握,软件中断的服务程序可以被任何程序以 INT指令的形式调用,并且调用者不必关心中断服务程序到底在内存的哪一个地方,也不需要把中断服务程序与调用它的程序拼装到一起 。 子程序的共享方式比较丰富,但有一点,就是被调用的子程序必须与调者拼装在一起,形成最终的执行文件 。
( 5) 在内存中存在的时间不同 。 中断服务程序通常是长期保留在内存中,而子程序是随可执行文件一起进入内存,当可执行文件执行完后,子程序所占用的内存也随之释放 。
8.4.6 中断服务程序主程序为中断所做的准备工作和硬件(包括 CPU和外设接口)
自动完成的动作。
( 1) 设置中断和向量 。
( 2) 设置设备的中断屏蔽位 。
( 3) 设置 CPU的中断允许位 IF( 开中断 ) 。
( 4) 外设接口送中断请求给 CPU。
( 5) 当前指令执行完成后,CPU送响应信号给外设接口 。
( 6) CPU接收中断类型号 。
( 7) 当前的 PSW,CS和 IP保存入栈 。
( 8) 清除 IF,TF。
( 9) 中断向量送 IP和 CS。
编写中断处理子程序的步骤,。
( 1) 保存寄存器内容 。
( 2) 如允许中断嵌套,则开中断 ( STI) 。
( 3) 处理中断 。
( 4) 关中断 。
( 5) 送中断结束命令 ( EOI) 给中断命令寄存器 。
( 6) 恢复寄存器内容 。
( 7) 返回被中断的程序 ( IRET) 。
编写中断服务子程序需要掌握的技术:
让一段程序常驻内存;
修改中断向量使其指向新的中断服务程序 。
8.4.6.1 常驻内存技术
【 驻留方法 】 MOV AH,31H
INT 21H
入口参数,DX=需要驻留的程序的节长度 。
【 说明 】
( 1) 驻留前要告诉 DOS,驻留程序的长度是多少 。 方法是把驻留长度放在 DX 中,长度单位是,节,而不是字节,
1节等于 16个字节 。 如果需要驻留的程序长度是 n 字节,则
DX的值可通过下面的计算式算得:
DX=( n ÷ 10H)+ 1+ 10H
其中( n ÷ 10H)+ 1是计算出驻留程序需要多少“节”,
加 1是为了预防驻留程序以字节计算的长度不是 16的整数倍。
再加 16节是因为每个程序在调入内存时,操作系统都为它安排了一个称为“程序段前缀( PSP)”的专用内存区,并且放在程序的前面,这个程序段前缀的长度是 256字节,刚好
16节,它必须与需要驻留的程序一起驻留在内存。
数据段,200B÷ 16+ 1= 13节代码段,500B÷ 16+ 1= 32节
PSP,16节
──────────────
总计,61节因此,调用 DOS的 31H号子功能进行程序驻留前,必须把
DX置为 61,即 42H。
8.4.6.2 修改中断向量的技术
【 设置中断向量 】
入口参数,AH= 25H
AL=中断号
DS:DX=新的中断服务程序的入口地址调用,INT 21H
【 取中断向量 】
入口参数,AH=35H
AL=中断号出口参数,ES:BX=中断向量调用,INT 21H
8.4.6.3 中断编程实例
8088系统中有一个定时器,它每隔约 0.05秒向 CPU
发出一次 8号中断请求,这是一个硬中断,不受程序的控制。 8号中断服务程序中有一条调用 1CH号中断的指令,而 1CH号中断是一个可供用户使用的中断,它只由
8号中断调用,与硬中断有类似的效果。下面就利用
1CH号中断,编写一个响铃程序。
【 例 8.4】 编一段中断处理程序,在主程序运行的过程中,每隔 10秒钟响铃一次,同时在屏上显示
,The bell is ring!”。
编程如下:
DATA SEGMENT
COUNT DW 1
MESS DB ‘The bell is ring!’,0DH,0AH,’$’
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,ES:DATA
MAIN PROC FAR
START,PUSH DS
SUB AX,AX
PUSH AX
MOV AX,DATA
MOV DS,AX
MOV AL,1CH;取中断向量
MOV AH,35H
INT 21H
PUSH ES ;存原中断向量段址
PUSH BX ;存原中断向量偏移量
PUSH DS
MOV DX,OFFSET RING ;新偏移量送 DX
MOV AX,SEG RING
MOV DS,AX;新段址送 DS
MOV AL,1CH ;写入新的中断向量
MOV AH,25H
INT 21H
POP DS
IN AL,21H ;读取中断屏蔽字
AND AL,11111110B;允许定时器中断
OUT 21H,AL
STI ;开中断
MOV DI,2000
DELAY,MOV SI,3000;延时
DELAY1,DEC SI
JNZ DELAY1
DEC DI
JNZ DELAY
POP DX ;恢复原中断向量
POP DS
MOV AL,1CH
MOV AH,25H
INT 21H
RET
MAIN ENDP
RING,PROC NEAR
PUSH DS ;各工作寄存器内容入栈
PUSH AX
PUSH CX
PUSH DX
MOV AX,DATA
MOV DS,AX
STI
DEC COUNT ;计秒值
JNZ EXIT
MOV DX,OFFSET MESS
MOV AH,09H
INT 21H
MOV DX,100
IN AL,61H
AND AL,0FCH
SOUND,XOR AL,02
OUT 61H,AL;扬声器发声
MOV CX,140H
WAIT1,LOOP WAIT1;延时等待
DEC DX
JNE SOUND
MOV COUNT,182
EXIT,CLI;关中断
POP DX
POP CX
POP AX
POP DS
IRET ;中断返回
RING ENDP
CODE ENDS
END START
【 例 8.5】 编写一个时钟程序,要求把时钟信号显示在屏幕的右上角,并在程序结束后常驻内存。
【 分析 】 由于系统每隔 0.05秒就产生一次 1CH号中断,
因此在 1CH号中断服务程序中应该有一个 0到 20的计数器,
初值为 20。 每次中断调用该服务程序时,就把计数器的值减
1,当计数器的值没减到 0时,说明还没有中断 20次,即不足
1秒钟,因而不需要更新钟的读数 。 当计数值到 0后,就需要让时钟向前走 1秒,把秒数加 1,若满 60秒则向分钟数进 1,
分钟数满 60后再向小时数进 1,小时数满 24就清 0,并且要把已走了 1秒的钟的当前读数显示在屏幕上 。 另外,为了计数下一秒,还要把 0到 20的计数器重新置初值 20。 处理完这些工作后,中断结束并返回 。
主程序需要完成以下工作:把 1CH号中断向量改为新的中断服务程序的入口地址,让新的中断服务程序常驻内存。
【 解 】
code SEGMENT
ASSUME CS:code,DS:code
clock PROC FAR
DEC CS:[x] ;计数器减 1
JZ c2 ;满 1秒转
JMP cend ;不足 1秒,直接转中断结束
c2,PUSH DS
PUSH ES
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI ;保护现场
PUSH CS
POP DS
MOV [x],20 ;对 0到 20的计数器重新赋初值
INC [s] ;秒数加 1
CMP [s],60
JB c1 ;不满 60秒转
MOV [s],0 ;计满 60秒后,秒数清 0
INC [m] ;分钟数加 1
CMP [m],60
JB c1 ;不满 60分钟转
MOV [m],0 ;计满 60分钟后,分钟数清 0
INC [h] ;小时数加 1
CMP [h],24
JB c1 ;不满 24小时转
MOV [h],0 ;计满 24小时后,小时数清 0
c1,MOV AL,[s]
MOV AH,0
MOV DL,10
DIV DL
OR AX,3030H
MOV [t+6],AX ;把秒钟数拆分成两位 ASCII送入变量 t+6处
MOV AL,[m]
MOV AH,0
DIV DL
OR AX,3030H
MOV [t+3],AX ;把分钟数拆分成两位 ASCII送入变量 t+3处
MOV AL,[h]
MOV AH,0
DIV DL
OR AX,3030H
MOV [t],AX ;把小时数拆分成两位 ASCII送入变量 t处
MOV CX,8
CLD
MOV AX,0B800H
MOV ES,AX ;取显示缓冲区段地址
MOV AH,1FH ;时钟读数的字符属性,兰底,高亮度白字
LEA SI,[t]
MOV DI,71*2
c3,LODSB
STOSW
LOOP c3 ;以直写屏方式显示
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
POP ES
POP DS
cend,IRET
t DB '00:00:00' ;存放时钟读数的变量
h DB 0 ;计小时数的变量
m DB 0 ;计分钟数的变量
s DB 0 ;计秒钟数的变量
x DB 20 ;0到 20次的计数变量
clock ENDP
main,MOV AX,code
MOV DS,AX
MOV AH,2CH
INT 21H ;取当前系统时间
MOV [h],CH ;置小时数初值
MOV [m],CL ;置分钟数初值
MOV [s],DH ;置秒钟数初值
LEA DX,clock
MOV AX,251CH
INT 21H ;置 1CH号中断向量,使指向 CLOCK
子程序
MOV AX,3100H
LEA DX,main
MOV CL,4
SHR DX,CL
ADD DX,11H ;计算驻留程序的节长度
INT 21H ;结束并驻留
RET
code ENDS
END main
本 章 要 点输入输出设备是计算机对外发挥作用的途径 。 不同的外设有各自的特点,需要不同的控制方法,但总的来说,外设都需要通过一个接口部件与计算机的主机相连 。 接口负责主机内外的信号转达,接口通常包括命令部件,数据部件和状态部件三大组成部分,每个部件占用一个或多个端口地址 。
输入输出的主要方式有 4种 。 直接方式适用于那些能与主机速度相匹配的外设,查询和中断方式则用于速度较慢,控制较复杂的外设,而 DMA方式提供了一种不通过 CPU直接在主机与内存之间进行高速数据传递的方法 。 用户可以编写程序对以直接方式,查询方式,中断方式工作的外设进行操作控制 。
中断是 CPU的一种重要工作方式,由中断源发出中断请求信号开始,经过中断源识别,现场保护,执行中断服务程序,
现场恢复的过程,使 CPU在完成中断服务后能接着被中断的程序继续执行 。