输入输出的基本概念
各种输入输出设备(如:打印机、键盘、鼠标等)都要通过一个硬件接口或控制器和 CPU 相连。例如,打印机通过打印接口与系统相连;显示器通过显示控制器和系统相连。从程序设计的角度看,接口由一组寄存器组成,是完成输入输出的桥梁。程序利用 I/O 指令,存取接口上的寄存器,获得外部设备的状态信息,控制外部设备的动作,从而实现输入输出。
I/O端口地址和I/O指令
I/O端口地址
为了存取接口上的寄存器,系统给这些寄存器分配专门的存取地址,这样的地址称为I/O端口地址。
在某些微型机上,I/O端口地址和存储单元地址统一编址。这相当于把 I/O 接口(设备)视为一个或几个存储单元,利用存取内存单元的指令就可存取接口上的寄存器。但这会减少原本就有限的一部分存储空间,同时由于访问内存的指令一般超过2字节,从而延长了外部设备与处理器进行数据交换的时间。
在以 Intel 的 8086 系列的处理器为 CPU 的系统中,I/O端口地址和存储单元的地址是各自独立的,分别占两个不同的地址空间。8086/8088 提供的I/O端口地址空间达 64K 个 8 位端口(或32K个16位端口)。但实际上PC机一般只使用 0 到 3FFH 之间的 I/O端口地址。
I/O 指令
由于 8086/8088 的 I/O端口地址和内存单元地址是独立的,所以要用专门的I/O指令来存取端口上的寄存器,也就是说要用专门的I/O指令进行输入输出。
I/O指令属于数据传送指令组
(1)输入指令
输入指令一般格式如下:
IN 累加器,端口地址
- 输入指令从一个输入端口读取一个字节或一个字,传送至AL或AX中。
- 端口地址可采用直接方式表示,也可采用间接方式表示。
- 当采用直接方式表示端口地址时,端口地址仅为8位,即0~255;当采用间接方式表示端口地址时,端口地址存放在DX寄存器中,端口地址可为16位。
- 输入指令有如下四种方式:
直接端口寻址
IN AL,PORT ;PORT 是一个8位的立即数
IN AX,PORT
间接端口寻址
IN AL,DX ;PORT 是一个字时,相当于同时从n和n+1分别读取一个字节。
IN AX,DX
当端口地址超过255时,只能采用dx间接端口寻址
(2)输出指令
OUT 端口地址,累加器
输出指令将 AL 中的一个字节,或在AX中的一个字,输出到指定端口。与 IN 指令一样,端口地址可采用直接方式和间接方式。
直接方式
OUT PORT,AL
OUT PORT,AX
间接方式
OUT DX,AL ;PORT 是一个字时,相当于同时从n和n+1分别输出一个字节。
OUT DX,AX
数据传送方式
CPU与外设之间交换的信息
CPU与外设之间交换的信息包括数据、控制和状态信息。尽管这三种信息具有不同性质,但他们都通过 IN 和 OUT 指令在数据总线上进行传送,所以通常采用分配不同端口的方法将它们加以区别。
数据是CPU和外设真正要交换的信息。数据通常为8位或16位。可分为各种不同类型。不同的外设要传送的数据类型也是不同的。
控制信息输出到I/O接口,告诉接口和设备要做什么工作。
从接口输入的状态信息,表示 I/O 设备当前状态。在输入数据前,通常要先取得表示设备是否已准备好的状态信息;在输出数据前,往往要先取得表示设备是否在忙的状态信息。
数据传送方式
系统中数据传送的方式主要有:
(1)无条件传送方式
在不需要查询外设装填,即已知外设已经准备好或不忙时,可以直接使用 IN 或 OUT 指令实现数据传送。这种方式软件实现简单,只要在指令中指明端口地址,就可选通过指定外设进行输入输出。
无条件传送方式是方便的,但要求外设工作速度能与CPU同步,否则就可能出错。例如,在外设还没有准备好的情况下,就用 IN 指令得到数据就可能是不正确的数据。
(2)查询方式
查询传送方式适用于 CPU 与外设不同步的情况。输入之前,查询外设数据是否已准备好,弱数据已准备好,则输入;否则继续查询,直到数据准备好。输出之前,查询外设是否 “忙”,若不”忙”,则输出;否则继续查询,直到不 “忙”。也就是说,要等待到外设准备好时才能输入或输出数据,而通常外设速度要远远慢于 CPU 速度,于是查询时间就将花费大量时间。
(3)中断方式
为了提供CPU的效率,可采用中断方式。当外设准备好时,外设向CPU 发出中断请求,CPU转入中断处理程序,完成输入输出工作。
(4)直接存储器传送(DMA)方式
由于告诉 I/O 设别(如磁盘机等)准备数据的时间短,要爱u传送速度快等特点,所以一般采用直接存储器传送方式,即告诉设备与内存储器直接交换数据。这种方式传送数据是成组进行的。其过程是:先把数据在告诉外设中存放的起始位置、数据在内存储器中存放的起始地址、传送数据长度等参数输出到连接高速外设的接口(控制器),然后启动高速外设,设备住呢比开始直接传送数据。当高速外设直接传送准备好后,向处理机发送一个直接传送的请求信号,处理机以最短时间批准进行直接传送,并让出总线控制权,高速外设在其控制器控制下交换数据。数据交换完毕后,由高速外设发出 “完成中断请求”,并交回总线控制权。处理机响应上述中断,由对应的中断处理程序对高速外设进行控制或对已经传送的数据进行处理,中断返回后,原程继续运行。
存取 RT/CMOS RAM
关于 RT/CMOS RAM
- RT/CMOS RAM 是一种低耗电存储器,其主要作用是用来存放系统配置信息,以及系统日期。
- RT/CMOS RAM 作为一个 I/O 接口芯片,系统分配的 I/O 端口地址为 70H 至 7FH,通过 IN 和 OUT 指令对其进行存取。它共提供64个字节RAM单元,前14个字节用于实时钟,剩下的50个字节用于系统配置。
存取 RT/CMOS RAM
在存取 RT/CMOS RAM 芯片内部的 64 个字节内容时,往往要分两步进行。即先把要存取单元的地址传入端口70H,然后再存取端口 71H。单元地址指的是上图中的位移。
(1)读操作代码片段
1 | MOV AL,n ;n 是要访问的单元地址 |
案例:读取 CMOS 并显示当前是几月份。
1 | ;程序名:RCMOS.asm |
(2)写操作代码片段
1 | MOV AL,n ;n 是要访问的单元地址 |
案例:修改并重新读取 CMOS 报警秒。
1 | ;程序名:WCMOS.asm |
CMOS RAM累加和检查和查询方式传送数据,就不写了。这些都是和硬件相关的知识,暂时用不到。
中断
中断是一种使 CPU 挂起正在执行的程序而转去处理特殊事件的操作。特殊事件是来自外设的输入输出请求,例如键盘引起的键盘中断,由串行口引起的串行口中断等;也可能是计算机的一些异常事件或其他内部原因,例如:除数为0
中断的传送方式
中断传送方式的具体过程是:当 CPU 需要输入或输出数据时,先作一些必要的准备工作(有时包括启动外部设备),然后继续执行程序;当外设完成一个数据的输入或输出后,则向 CPU 发出中断请求,CPU 就挂起正在执行的程序,转去执行输入或输出擦欧总,在完成输入或输出操作后,返回原程序继续执行。
中断传送方式是CPU和外部设备进行输入输出的有效方式,一直被大多数计算机所采用,它可以避免因反复查询外部设备的状态而浪费时间,从而提高CPU的效率。不过,每中断依次,只传送依次数据,数据的传送效率并不高,所以,中断传送方式一般用于低速外设。另外,与查询方式相比,中断方式实现比较复杂,对硬件的条件也较多。
中断向量表
IBM PC 系列及其兼容机共能支持256种类型的中断,系统给每一种中断都安排一个中断类型号(简称中断号),中断类型号依次为 0~FFH。例如,属于外部中断的定时器中断类型号为 08 和键盘中断类型号 09,属于内部中断的除法出错中断类型号为 0 等等。
每种类型的中断都由响应的中断处理程序来处理,为了使系统在响应中断后,CPU 能快速地转入对应地中断处理程序,系统用一张表来保存这些中断处理程序的入口地址,该表就称为中断向量表。中断向量表的每一项保存一个中断处理程序的入口地址,它相当于一个指向中断处理程序的指针,所以称为中断向量。中断向量也依次编号为 0~FFH,n号中断向量就是保存处理中断类型为 n 的中断处理程序的入口地址。所以,一般不再取分中断类型号和中断向量号。
中断向量表被存放在内存最低端的 1K 字节空间种。其中每个中断向量占用四个字节,前两个字节保存中断处理程序入口地址的偏移,后两个字节保存中断处理程序的入口地址的段值,所以含有256个中断向量的中断向量表需要占用1K字内存空间。
中断向量所在的单元地址=中断向量号4,如 21h号中断向量在中断向量表的 214=84=54h=0000:0054h 处。
在 IBM PC系列及其兼容机上,除保留给用户使用的 60H68H 和 F1HFFH 中断向量号外,可以认为其他中断向量号已被分配。
中断向量不一定非要指向中断处理程序,也可以作为指向一组数据的指针。如,1DH号中断向量就指向显示器参数,1EH号中断向量指向软盘基数。当然,如果中断向量m没有指向中断处理程序,那么就不应该发生类型为m的中断。
设置和获取中断向量
在系统程序或应用程序由于某种需要而提供新的中断处理程序时,就要设置对应的中断向量,使其指向新的中断处理程序。
下面的程序片段直接设置 n 号中断向量,假设对应中断处理程序的入口标号是
1 | INTHAND: |
在上面的程序片段中使用了关中断指令 CLI,关中断就是关闭中断指令,此时中断指令是无法使用的。这样做目的是保证用于设置中断向量的两条传送指令能够连续执行,避免中间执行其他中断指令导致中断程序出错。如果能确定当前是关中断状态,当然就不再需要使用关中断指令,也不需要随后地开中断指令。另外,如果能够肯定在设置 n 号中断向量过程中不发生类型为n地中断,那么可不考虑是否设为关中断状态。
实际上,通常是利用 DOS 提供地25h号系统功能调用设置中断向量,这样可以避免考虑许多细节。
25号系统功能调用时设置中断向量,其入口参数如下:
1 | AL=中断向量(类型)号 |
下面的程序片段设置 n 号中断向量,假设对应中断处理程序入口标号是
1 | INTHAND: |
有时需要取得中断向量。例如:在应用程序要用自己的中断处理程序代替系统原有的某个中断处理程序时,先要保存原中断向量,待应用程序结束时再恢复原中断向量。
下面的程序片段直接从中断向量中取得 n 号中断向量,并保存到双字变量 OLDVECTOR 中:
1 | .... |
与利用 DOS 功能调用设置中断向量一样,实际上一般都利用ODS提供的35H号系统功能调用取得中断向量。35H号系统调用的功能是获取中断向量,其入口参数如下:
入口参数:
AL=中断向量(类型)号
出口参数:
1 | ES=中断处理程序入口地址的段值 |
下面是程序片段取得n号中断向量,并将其保存到双字变量 OLDVECTOR 中:
1 | .... |
中断响应过程
中断响应过程
通常 CPU 在执行完每一条指令后均要检测是否有中断请求,在有中断请求且满足一定条件时就响应中断,这个过程如下图所示:
- 取得中断类型号
- 把标志寄存器的内容压入堆栈中
- 禁止为外部中断和单步中断(使IF和TF标志位为0)
- 把下一条要执行的指令的地址压入堆栈(CS和IP)
- 根据中断类型号从中断向量表中取中断处理程序入口地址
- 转入中断处理程序
在CPU响应中断转入中断程序时,中断处理程序在最后从堆栈中弹出返回地址和原标志寄存器的值结束中断,返回被中断的程序。
中断返回指令
中断处理程序利用中断返回指令从堆栈中弹出返回地址和原标志值。中断返回指令格式如下:
1 | IRET |
该指令的功能从中断返回。具体操作如下所示:
1 | IP <= [SP] |
在执行中断返回指令 IRET 时的堆栈变化如下
外部中断
由于发生在 CPU 外部的某个事件引起的中断称为外部中断。外部中断以完全随机的方式中断正在运行的程序。
外部中断有两条外部中断请求线:INTR(可屏蔽中断请求),NMI(非屏蔽中断请求)
可屏蔽外部中断
键盘和硬盘等外设的中断请求都通过中断控制器 8259A 传给可屏蔽中断请求INTR。中断控制器 8259A 共能接收 8 个独立的中断请求信号 IRQ0IRQ7。在AT机上,有两种中断控制器 8259A,一主一从。从 8259A 连接到主 8259A 的 IRQ2 上,这样 AT 系统就可以接收15个独立的中断请求信号。IRQ7时,送出的对应中断类型号分别是 08H~0FH 如下图所示
中断控制器 8259A 在控制外设中断方面起着重要的作用。如果接收到一个中断请求信号,并且满足一定的条件,那么它就把中断请求信号传到 CPU 的可屏蔽中断请求线 INTR,使CPU感知到有外部中断请求;同时也把相应的中断请求类型号送给 CPU,使 CPU 在响应中断时可根据中断类型号取得中断向量,转相应的中断处理程序。
中断控制器 8259A 是可编程的,也就是说可由程序设置它如何控制中断。在及其系统加电初始化期间,已对 8259A 进行过初始化。在初始化时规定了在传出中断请求 IRQ0
例如,设传出中断请求 IRQ1(键盘中断),那么送出的中断类型号为9,所以键盘中断类型号为9,键盘中断处理程序入口地址存放在 9 号中断向量中。
从普通汇编语言设计角度看,中断控制器 8259A 包含了两个寄存器:中断屏蔽寄存器和中断命令寄存器,它们决定了传出一个中断请求信号的条件。中断屏蔽寄存器的 I/O 端口地址是 21H,它的8位对应控制8个外部设备,通过设置这个寄存器的某个位为 0 或 1 来允许或禁止相应外部设备中断。当第 i 位为 0 时,表示允许传出来自 IRQi 的中断请求信号,当第 i 位为 1 时,表示禁止传出来自 IRQi 的中断请求信号。中断屏蔽寄存器的内容称为中断屏蔽字。在 PC 系列及其兼容机上,中断屏蔽寄存器各位与对应的外设的关系如下图所示:
例如:为了使中断控制器 8259A 只传出来自键盘的中断请求信号,可设置中断屏蔽字 11111101B,程序片段如下
1 | MOV AL,11111101B |
例如:下面的程序片段使中断屏蔽寄存器的位 4 为 0,从而允许传出来自串行通信口 1 的中断请求信号:
1 | IN AL,21H |
尽管中断控制器把外设的中断请求信号由 INTR 传给 CPU,但 CPU 是否响应还取决于中断允许标志位 IF。如果 IF 为 0,则 CPU 仍不响应由 INTR 传入的中断请求。所以,由 INTR 传入的外部中断请求称为可屏蔽外部中断请求,由此引起的中断称为可屏蔽中断。由于外设的中断请求均由 INTR 传给 CPU,CPU 响应外设中断请求称为开中断(IF=1),反之称为关中断(IF=0)。CPU 在响应中断时会自动关中断,从而避免在中断过程中再响应其他外设中断。程序员在程序中可使用关中断指令 CLI和开中断指令 STI。
非屏蔽外部中断
当收到从 NMI 传来的中断请求信号时,不论是否处于开中断状态 CPU 总会响应。所以,由 NMI 传入的外部中断请求称为非屏蔽外部中断请求,由此引发的中断称为非屏蔽中断。不可屏蔽中断请求由电源掉电、存储器出错或者总线奇偶校验出错等紧急故障产生,要求 CPU 及时处理。
内部中断
由发生在 CPU 内部的某个事件引起的中断称为内部中断。由于内部中断是 CPU 在执行某些指令时产生,所以也称为软件中断。其特征是:不需要外部硬件的支持;不受标志 IF 的控制。
中断指令 INT 引起的中断
中断指令一般格式如下:
INT n
其中,n是一个 0~0FFH的立即数。CPU 在执行中断指令后,便产生一个类型号为 n 的中断,从而转入对应的中断程序。
例如,为了调用DOS系统功能,就在程序中安排如下的中断指令:
INT 21H
当CPU 执行该指令后,就产生一个类型为 21H 的中断,从而转入对应的中断处理程序,也即转入 DOS 系统功能服务程序。值得指出的是,程序员根据需要在程序中安排中断指令,所以它不会真正随机产生,而完全受程序控制。
CPU 遇到特殊情况引起的中断
- 除法错误中断
在执行除法指令时,如果 CPU 发现除数为 0 或者商超过规定的范围,那么就产生一个除法错误中断,中断类型号为0。
例如,在执行下面的程序片段时,会产生一个0号类型的中断:1
2
3MOV AX,1234
MOV CL,3
DIV CL ;商超过 255(AL容纳不下)
为了避免产生0号类型的中断,可改写上述程序片段如下:
1 | MOV AX,1234 |
- 溢出中断
8086/8088提供一条专门检测运算溢出的指令,该指令的格式如下:在溢出标志位 OF 置 1 时,如果执行该指令,则产生溢出中断。溢出中断的类型号规定为 4。如果溢出标志 OF 为 0,则执行该指令后并不断产生溢出中断。1
INTO
用于程序调试的中断
单步中断
如单步中断 TF 为 1,则在每条指令执行后产生一个单步中断,中断类型号规定为1。产生但不中断后,CPU就执行单步中断处理程序。由于CPU在响应中断时,已把 TF 置为0,所以,不会以单步方式执行单步中断程序。通常,由调试工具把 TF 置 1,在执行完一条被调试程序的指令后,就转入单步中断处理程序,一般情况下,但不中断处理程序报告各寄存器的当前内容,程序员可据此调试程序。断点中断
8086/8088 提供一条特殊的中断指令 “INT 3”,调试工具可用它替换断点处的代码,当 CPU 执行这条中断指令后,就产生类型号为3的中断。这种中断称为断点中断。通常情况下,断点中断处理程序恢复被替换的代码,并报告各寄存器的当前内容,程序员可据此调试程序。所以说中断指令 “INT 3”特殊是因为它只有一个字节长,其他的中断指令长 2 字节。
中断优先级和中断嵌套
中断优先级
系统中有多个中断源,当多个中断源同时向 CPU 请求中断时,CPU 按系统设计时规定的优先级响应中断请求。在 IBM PC 系列及其兼容机系统中,规定的优先级如下:中断嵌套
CPU 在执行中断处理程序时,又发生中断,这种情况称为中断嵌套。
在中断处理过程中,发生内部中断,引起中断嵌套是经常的事。例如:CPU在执行中断处理程序时,遇到软中断指令,就会引起中断嵌套。在中断处理过程中,发生非屏蔽中断,也会引起中断嵌套。
由于 CPU 在响应中断的过程中,已自动关中断,所以 CPU 也就不再自动响应可屏蔽中断。如果需要在中断处理过程的某些时候响应可屏蔽中断,那么可在中断处理程序中安排开中断指令,CPU在执行中断指令后,就处于开中断状态,也就可以响应可屏蔽中断了,直到再关中断。所以,如果再中断处理程序中使用了开中断指令,也就可能会发生可屏蔽中断引起的中断嵌套。
8086/8088没有限制中断嵌套的深度(层次),但客观生受到堆栈容量限制。
中断处理程序的设计
CPU 在响应中断后,自动根据中断类型,取中断向量,并转入中断处理程序,所以,具体的处理工作由中断处理程序完成。不同的中断处理,由不同的中断处理程序完成。对应外设中断的外设中断处理程序和对应指令中断的软中断处理程序有些区别,下面对它们的设计分别作些介绍。
外设中断处理程序
在开中断的情况下,外设中断的发生时随机的,在设计外设中断处理程序时必须充分注意到这一点。外设中断处理程序的主要步骤如下:
(1)必须保护现场。这里的现场可理解为中断发生时CPU各内部寄存器的内容。CPU 在响应中断时,已把各标志和返回地址压入堆栈,所以要保护的现场主要是指通用寄存器的内容和除代码段寄存器外的其他三个段寄存器的内容。因为中断的发生是随机的,所以凡是中断处理程序中要重新赋值的各个寄存器的原有内容必须预先保护。保护的一般方法是把它们压入堆栈。
(2)尽快完成中断处理。外设中断处理必须尽快完成,所以外设中断处理必须追求速度上的高效率。因为在进行外设中断处理时,往往不再响应其他外设的中断请求,因此必须快,以免影响其他外设的中断请求。
(3)恢复现场。在中断处理完成后,依次恢复被保护寄存器的原有内容。
(4)通知中断控制器中断已结束。如果应用需要,也可提早通知中断控制器中断结束,这样做必须考虑到外设中断的嵌套。
(5)利用 IRET 指令实现中断返回。
此外,应及时开中断。除非必要,中断处理程序应尽早开中断,以便CPU响应具有更高优先级的中断请求。软中断处理程序
由中断指令引起的软件中断尽管是不可屏蔽的,但它不会随机发生,只有在CPU执行了中断指令后,才会发生。所以,中断指令类似于子程序调用指令相应的软中断处理程序在很大程度上类似于子程序,但并不等同于子程序。软中断的主要步骤如下:
(1)考虑切换堆栈,由于软中断处理程序往往在开中断状态下执行,并且可能较复杂(要占用大量的堆栈空间),所以因该考虑切换堆栈。切换堆栈对实现中断嵌套等均较为有利。
(2)及时开中断。开中断后,CPU就可响应可屏蔽的外设中断请求,或者说使外设中断请求可及时得到处理。但要注意,如如果软中断程序要被外设中断处理程序 “调用”,则是否要开中断或者核实开中断应另外考虑。
(3)应保护现场。应该保护中断处理程序要重新赋值的寄存器原有内容,这样使用软中断指令时,可不必考虑有关寄存器内容的保护问题。
(4)完成中断处理。但不必过分追求速度上的高效率,除非它是被外设中断处理程序 “调用” 的。
(5)恢复现场。依次恢复被保护寄存器的原内容。
(6)堆栈切换。如果在开始时切换了堆栈,那么也要再重新切换回原堆栈。
(7)一般利用 IRET 指令实现中断返回。
基本输入输出系统 BIOS
介绍 BIOS 基本概念的基础上,介绍键盘输入、显示和打印输出。
基本输入输出系统 BIOS 概念
固化在 ROM 中的基本输入输出系统 BIOS 包含了主要I/O设备的处理程序和许多常用例行程序,它们一般以中断处理程序的形式存在。例如:负责显示输出的 显示I/O程序作为 10H 号中断处理程序存在,负责打印输出的打印I/O程序作为17H号中断处理程序存在,而负责键盘输入的键盘 I/O 程序作为 16H号中断处理程序存在。再如,获取内存容量的例行程序作为 12H号中断处理程序存在。BIOS是直接建立再硬件基础上。
磁盘操作系统DOS建立在BIOS的基础上,通过BIOS操纵控制硬件。例如,DOS调用BIOS显示I/O程序完成显示输出,调用打印I/O程序完成打印输出,调用键盘 I/O程序完成键盘输入。尽管 DOS和BIOS都提供某些相同的功能,但它们之间的层次关系是明显的。
应用程序DOS、BIOS和外设接口之间的关系如下图所示:
通常应用程序应该调用 DOS 提供的系统功能完成输入输出或其他操作。这样做不仅实现容易,而且对硬件的依赖性最少。但有时 DOS不提供某种服务,例如,取打印机状态信息,那么就不能调用DOS实现了。
应用程序可以通过BIOS进行输入输出或完成其他功能。在下列三种场合可考虑BIOS:一是需要利用BIOS提供而DOS不提供的功能场合;二是不能利用 DOS 功能调用的场合;三是出于某种原因需要绕过DOS的场合。由于 BIOS 提供的设备处理程序和常用例行程序都以中断处理程序的形式存在,所以应用程序调用BIOS较为方便。但 BIOS 毕竟比 DOS 更靠近硬件。
应用程序也可以直接操纵外设接口来控制外设,从而获得速度上最高的效率,但这样的应用程序不仅复杂而且与硬件关系十分密切,此外,还需要程序员对硬件性能比较了解熟悉。所以,应用程序一般不能直接与硬件发生关系。
值得指出的是,有时应用程序需要扩充或替换 ROM BIOS 中的某些处理程序或例行程序,那么这些新的 BIOS程序原则上不能调用 DOS 提供的功能。
键盘输入
键盘中断处理程序
当用户按键盘时,键盘接口会得到一个代表被按键的键盘扫描码,同时产生一个中断请求。如果键盘中断是允许的,并且CPU处于开中断状态,那么 CPU 通常会响应中断请求。由于键盘中断的中断类型号为 9,所以 CPU 响应键盘中断,就是转入 9 号中断处理程序。我们把 9 号中断处理程序称为键盘中断处理程序。它属于外设中断处理程序这一类。
键盘中断处理程序首先从键盘接口取得代表被按键的扫描码,然后根据扫描码判定用户所按的键并作相应的处理,最后通知中断控制器中断结束并实现中断返回。键盘上面的键可简单地分成五种类型:字符键(字母、数字和符号等),功能键(如F1等),控制键(Ctrl、Alt和左右Shift),双态字(如Num Lock和Caps Lock等),特殊请求键(如Print screen等)。键盘中断处理程序对五种键地基本处理如下:
如果用户按的是双态键,那么就设置有关标志,在 AT 以上档次的系统上还要改变 LED 指示器状态。如果用户按的是控制键,那么就设置有关标志。如果用户按的是功能键,那么就根据键盘扫描码和是否按下某些控制键(如Alt)确定系统扫描码,把系统扫描码和一个全0字节一起存入键盘缓冲区。如果用户是字符键,那么就根据键盘扫描码和是否按下某些控制键(如Ctrl)确定系统扫描码,并且得出对应的ASCII码,把系统扫描码和ASCII码一起存入键盘缓冲区。如果用户按的是特殊请求键,那么就产生一个相应的动作,例如用户按 Print screen 键,那么就调用 5H 号中断处理程序打印屏幕。
键盘缓冲区
键盘缓冲区是一个先进先出的环形队列,结构和占用的内存区域如下:
1 | BUFF_HEAD DW ? ;0040:001AH |
BUFF_HEAD 和 BUFF_TAIL 是缓冲区的头指针和尾指针,当这两个指针相等时,表示缓冲区为空。由于缓冲区本身16个字,而存放一个键的扫描码和对应的 ASCII 码需要占用一个字,所以键盘缓冲区可实际存放 15 个键的扫描码和ASCII码。键盘中断处理程序把所按字符键或功能键的扫描码和对应的ASCII码依次存入键盘缓冲区。如缓冲区已满,则不再存入,而是发出 “嘟” 的一声。
键盘中断处理程序根据控制键和双态键建立的标志在内存单元 0040:0017H 字单元中。
案例:验证键盘缓冲区是否在 0040:001EH~003DH 中
1 | ;程序名:keyboard.asm |
我这边输入的是键盘字母键的第一行 q~p,键盘缓冲区中也存入了相应的字符.
键盘I/O程序
尽管系统程序和应用程序可从键盘缓冲区中取得用户所按键的代码,但除非特殊情况,一般不宜直接存取键盘缓冲区,而应调用BIOS提供的I/O程序。
键盘 I/O 程序以 16H 号中断处理程序的形式存在,它属于软中断程序这一类。它的主要功能是进行键盘输入。一般情况下,系统程序和应用程序的键盘输入最后都是调用它完成的。简单的键盘 I/O 程序从键盘缓冲区中取得所有键的ASCII码和扫描码返回给调用者。键盘中断程序、键盘缓冲区和键盘I/O程序之间的关系如下图所示:
键盘 I/O 程序的功能和调用方法
键盘 I/O 程序提供的每一个功能都有一个编号。在调用键盘 I/O 程序时,把功能编号置入 AH 寄存器,然后发出中断指令 “INT 16H”。调用返回后,从有关寄存器中取得出口参数。除保存出口参数的寄存器外,其他寄存器内容保持不变。
我们把控制键和双态键统称为变换键,调用键盘 I/O 程序的 2 号功能可获得各变换键的状态。变换键状态字节各位的定义如下图所示:
其中高4位记录双态键的变换情况,每按一下双态键,则对应的位值取反;低4位反应控制键是否正被按下,按着某个控制键时,对应的位为1。
16H 号基本功能
如果键盘缓冲区中有字符,那么中断处理就会很快结束,读到的字符是调用发出之前用户按下的字符。如果键盘缓冲区空,那么就等待用户按键后调用才会返回,读到的字符是调用发出之后按下的字符。如果程序员处于某种原因,要从键盘取得在调用发出之后用户按下的字符,那么就要先清除键盘缓冲区。下面的程序片段先清除键盘缓冲区,然后再从键盘读取一个字符:
1 | AGAIN: |
案例:查验键盘缓冲区指针变化(相关子程序查看上面的案例)。
1 | ;程序名:keyboard_point.asm |
从运行结果来看,我们键盘输入一个字符后,触发9号中断,将数据存储到键盘缓冲区,尾指针+2。随后用 int 16每取一个字母,键盘缓冲区头指针就会+2。通过这个案例应该更容易理解上面清除键盘缓冲区的代码片段。
案例:通过修改缓冲区头指针,使键盘缓冲区不为空,并打印出键盘缓冲区的数据(相关子程序查看上面的案例)。
1 | assume cs:code,ds:data |
int 16H 的 1 号功能只做判断,不会改变头指针地址。通过该程序,加深键盘缓冲区的了解。
例1:写一个程序完成如下功能:读键盘,并把所按键盘显示出来,在检测到按下 SHIFT 键后,就结束运行。
1 | ;程序名:T5-1.asm |
键盘 I/O 程序的 2 号功能取得变换键状态字节,其中最低的两位有一个为1 表示按下 shift 键。在调用 0 号功能读键盘之前,要先调用 1 号功能判断键盘缓冲区是否为空,不然读错了会导致不能及时检测到用户按下的 shift 键。
显示输出
显示器通过显示适配卡与系统相连,显示适配卡是显示输出的接口。母千常用的显示适配卡是 VGA 和 TVGA 等。它们都支持两种显示方法:文本显示方式和图形显示方式,每一类显示方式还含有多种显示模式。
文本显示方式
所谓文本显示方式是指以字符为单位显示的方式。字符通常是指字母、数字、普通符号(如运算符号)和一些特殊符号(如棱形块和矩形块)。
通常 0~3 号显示模式为文本显示方式,它们之间的区别是每屏可显示的字符和可使用的颜色数目不同。这里以最常用的 3 号显示模式作为代表介绍。
在 3 号文本显示模式下,显示器的屏幕被划分成 80 列 25 行,所以每一屏最多可显示2000(80x25)个字符。我们用行号和位号组成的坐标来定位屏幕上的每个可显示位置,左上角的坐标规定为(0,0),向右增加列号,向下增加行号。
显示属性
屏幕上显示的字符取决于字符代码和字符属性。这里的属性是指显示属性,它规定字符显示时的特性。在单色显示时,属性定义了前景色和背景色。下图给出了彩色显示时字节各位的定义。
在属性字节中,RGB分别表示红、绿、蓝,I表示亮度,BL表示闪烁。位03组合 16 中前景色,位46组合8种背景色,亮度和闪烁只能用于前景色。下图给出了彩色文本模式下 IRGB 组合成的通常颜色。
显示存储区
显示适配卡带有显示存储器,用于存放屏幕上显示文本的代码及属性或图形信息。显示存储器作为系统存储器的一部分,可用访问普通内存的方法访问显示存储器。通常,为显示存储器安排的存储地址空间的段值是 B800H 或 B000H,对应的内存区域称为显示存储区。
3号文本模式下,屏幕上每一个显示位置依次对应显示存储区的两个字节单元,这种对应关系如下图所示:
低字节是 ASCII 码,高字节是属性
为了屏幕上某个位置显示字符,只需要把显示字符的代码及其属性填到显示存储区种的对应存储单元即可。
案例:写一个程序在屏幕的左上角以黑底白字显示字符 “A”:
1 | ;程序名:screen.asm |
为了了解屏幕上某个显示位置所显示的字符是什么,或显示的颜色是什么,那么只要从显示存储区种的对应存储单元中取出字符的代码和属性即可。
案例:写一个程序片段在屏幕右下角显示黑底蓝字的字母’A’,再复制该字符到屏幕左上角。
1 | ;程序名:screen1.asm |
例2:采用直接写屏法在屏幕上用多种属性显示字符串 “HELLO”
1 | ;程序名:T5-2.asm |
这个例子中,坐标是用编译器来算的,而书中的例子用 mul 指令来算的(这会降低程序运行的速度)。
显示 I/O 程序的功能和调用方法
利用直接写屏的方法,程序可以快速显示,但为了实现直接写屏,必须了解显示存储器用存储空间的具体细节和显示存储区与屏幕显示位置的对应关系,并且最终程序也与显示适配卡相关。所以,除非追求显示速度,一般不采用直接写屏的方法,而实调用BIOS提供的显示I/O程序。
BIOS 提供的显示 I/O 程序作为 10H 号中断处理程序。显示 I/O 程序的主要功能如下图所示:
页号:为了支持屏幕上显示 2000 多个字符,需要显示存储器容量约为4KB。如果显示存储器的内容为 32KB ,那么显示存储器可存放 8 屏显示内容。为此,把显示存储器再分成若干段,称为显示页。
例3:写一个程序完成如下功能:再屏幕中间不为开出一个窗口,随后接收用户按键,并把按键字符显示字窗口的最底行,当窗口运行满时,窗口内容向上滚动一行;用户按 CTRL+C键时,结束运行。
1 | ;程序名:T5-3.asm |
例4:调用显示 I/O 程序实现 T5-2.asm 显示字符串的代码。
1 | ;程序名:T5-4.asm |
打印输出
打印 I/O 程序的功能和调用方法
BIOS提供的打印 IO 程序为 17H 号中断处理程序。
例5:写一个在0号打印机上打印屏幕内容的程序。
1 | ;程序名:T5-5.asm |
没有打印机,直接抄代码,加深点映像。后续,右打印机再进行测试。
软中断程序举例
时钟显示程序
在系统加电初始化期间,把系统定时器初始化位每隔约 55 毫秒发出一次中断请求。CPU 在响应定时中断请求后转入 8H 号中断处理程序。BIOS 提供的 8H 号中断程序中有一条中断指令 “INT 1CH”,所以每秒要调用到约 18.2 次 1CH 号中断处理程序。而 BIOS 的 1CH 号中断处理程序实际上并没有做任何工作,只有一条中断返回指令。这样安排的目的是为应用程序留下一个软接口,应用程序质押奥提供新的 1CH 号中断处理程序,就可能实现某些周期性的工作。
例6:利用该软接口,实现时钟显示
1 | ;程序名:T5-6.asm |
打印软中断程序,因为没有打印机,暂时先忽略。
- 本文标题:8086汇编-输入输出与中断
- 本文作者:9unk
- 创建时间:2022-09-27 17:15:00
- 本文链接:https://9unkk.github.io/2022/09/27/8086-hui-bian-shu-ru-shu-chu-yu-zhong-duan/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!