第十五章 外中断
第十六章 直接定址表
第十五章 外中断
15.1 接口芯片的端口
外设的输入不直接送入内存和CPU,而是送入相关的接口芯片的端口中;CPU向外设的输出也不是直接送入外设,而是先送入端口中,再由相关的芯片送到外设。CPU还可以向外设输出控制命令,而这些控制命令也是先送到相关芯片的端口中,然后再由相关的芯片根据命令对外设实施控制。
CPU 通过端口和外设进行联系
15.2 外中断信息
外设通过中断信息来通知CPU端口信息发生变化,需要处理
外设引发的中断信息称为外中断信息
引发外中断的信息源称为 外中断源,分为以下两类
- 可屏蔽中断
这是CPU可以不响应的外中断
如果标志寄存器 IF = 1 ,则CPU响应中断;如果 IF = 0,则CPU不响应
设置IF位的指令:
sti
设置 IF = 1
cli
设置 IF = 0
- 不可屏蔽中断
CPU 必须响应的外中断
在 8086CPU 中,不可屏蔽中断的中断类型码固定为 2
15.3 PC 机键盘的处理过程
- 键盘输入
键盘上的每一个键相当于一个开关,键盘中有一个芯片对键盘上的每一个键的开关状态进行扫描。
按下一个键时,开关接通,该芯片就产生一个扫描码,扫描码说明了按下的键在键盘上的位置。扫描码被送入主板上的相关接口芯片的寄存器中,该寄存器的端口地址为 60h
松开按下的键时,也产生一个扫描码,扫描码说明了松开的键在键盘上的位置。松开按键时产生的扫描码也被送入 60h 端口中
一般将按下一个键时产生的扫描码称为通码,松开一个键产生的扫描码称为断码。扫描码长度为一个字节,通码的第7位为0,断码的第7位为1,即:
断码 = 通码 + 80h
![]()
续表
![]()
- 引发9号中断
键盘的输入到达 60h 端口时,相关的芯片就会向 CPU 发出中断类型码为 9 的可屏蔽中断信息。CPU检测到该中断信息后,如果 IF=1,则响应中断,引发中断过程,转去执行 int 9 中断例程
- 执行 int 9 中断例程
BIOS提供了 int 9 中断例程,用来进行基本的键盘输入处理,主要的工作如下
(1)读出 60h 端口中的扫描码;
(2)如果是字符键的扫描码,将该扫描码和它所对应的字符码(即ASCIⅡ码)送入内存中的 BIOS 键盘缓冲区;如果是控制键(比如Ctrl)和切换键(比如CapsLock)的扫描码,则将其转变为状态字节(用二进制位记录控制键和切换键状态的字节)写入内存中存储状态字节的单元;
(3)对键盘系统进行相关的控制,比如说,向相关芯片发出应答信息
0040:17单元存储键盘状态字节,该字节记录了控制键和切换键的状态。键盘状态字节各位记录的信息如下
0:右shift状态,置1表示按下右shift键;1:左shift状态,置1表示按下左shift键;
2:Ctrl状态,置1表示按下Ctrl键;
3:Alt状态,置1表示按下Alt键;
4:ScrollLock状态,置1表示Scroll指示灯亮;
5:NumLock状态,置1表示小键盘输入的是数字;
6:CapsLock状态,置1表示输入大写字母;
7:Insert状态,置1表示处于删除态。
15.4 编写 int 9 中断例程
编程:在屏幕中间依次显示“a”~“z”,并可以让人看清。在显示的过程中,按下 Esc 键后,改变显示的颜色。
1 | assume cs:code |
上面的程序完成了 在屏幕中间依次显示 “a”~“z” ,每个字母显示一段时间(通过 delay 子程序完成),让人可以看清
那么如何做到每按一次 Esc ,就改变字体颜色呢?
方法就是编写自己的 int 9 中断处理程序并实现以下功能:
(1)从 60h 端口读出键盘的输入;
(2)调用 BIOS 的 int 9 中断例程,处理其他硬件细节;
(3)判断是否为 Esc 的扫描码,如果是,改变显示的颜色后返回;如果不是则直接返回
完整的代码如下
1 | assume cs:code |
第十六章 直接定址表
讨论如何有效合理地组织数据
16.1 描述了单元长度的标号
1 | assume cs:code |
注意上面代码中的标号 a 和 b 的后面都没有跟 ‘:’ ,它们是特殊的标号,同时描述内存地址和单元长度
标号 a,描述了地址 code:0,和从这个地址开始,以后的内存单元都是字节单元;而标号 b 描述了地址 code:8,和从这个地址开始,以后的内存单元都是字单元。
这种标号又叫做数据标号
检测点 16.1
下面的程序将 code 段中 a 处的 8 个数据累加,结果存储到 b 处的双字中,补全程序
1 | assume cs:code |
16.2 在其他段中使用数据标号
一般来说,我们不在代码段中定义数据,而是将数据定义到其他段中。在其他段中,我们也可以使用数据标号来描述存储数据的单元的地址和长度。
注意,在后面加有“:”的地址标号,只能在代码段中使用,不能在其他段中使用
1 | assume cs:code,ds:data |
注意:如果想在代码段中直接使用数据标号访问数据,则需要用 assume
把标号所在的段和一个段寄存器联系起来,比如上面代码中的 ds:data
可以将标号当作数据来定义
比如
1 | data segment |
就相当于
1 | data segment |
又比如
1 | data segment |
相当于
1 | data segment |
seg
指令取得某一标号的段地址
检测点 16.2
下面的程序将 data 段中 a 处的 8 个数据累加,结果存储到 b 处的字中,补全程序
1 | assume cs:code,es:data |
16.3 直接定址表
用查表的方法编写程序的技巧
编写子程序,以十六进制的形式在屏幕中间显示给定的字节型数据
分析:显然,我们希望能够在数值 0~15 和字符 “0”~“F” 之间找到一种映射关系。这样用 0~15 间的任何数值,都可以通过这种映射关系直接得到“0”~“F”中对应的字符。
具体的做法是,建立一张表,表中依次存储字符 “0”~“F”,我们可以通过数值 0~15 直接查找到对应的字符。
程序如下
1 | ;用al传送要显示的数据 |
像这种可以通过依据数据,直接计算出所要找的元素的位置的表,我们称其为直接定址表。
16.4 程序入口地址的直接定址表
可以在直接定址表中存放子程序的地址从而方便地实现子程序的调用
实现一个子程序 setscreen ,提供以下功能
(1)清屏:将显存中当前屏幕中的字符设为空格符;
(2)设置前景色:设置显存中当前屏幕中处于奇地址的属性字节的第0、1、2位;
(3)设置背景色:设置显存中当前屏幕中处于奇地址的属性字节的第4、5、6位;
(4)向上滚动一行:依次将第n+1行的内容复制到第n行处;最后一行为空。我们将这4个功能分别写为4个子程序,请读者根据编程思想,自行读懂下面的程序。
入口参数说明如下。
(1)用ah寄存器传递功能号:0表示清屏,1表示设置前景色,2表示设置背景色,3表示向上滚动一行;
(2)对于1、2号功能,用al传送颜色值,(al)={0,1,2,3,4,5,6,7}。
1 | sub1: push bx |
我们可以将这些功能子程序的入口地址存储在一个表中,它们在表中的位置和功能号相对应。对应关系为:功能号*2=对应的功能子程序在地址表中的偏移。程序如下:
1 | setscreen: jmp short set |
第十六章结束啦(实验十五十六没有做,第十七章不看)
汇编语言完结撒花