转载必须注明出处,违者必究。http://www.cnblogs.com/dennisOne
CPU除了有运算能力外,还要有I/O能力。那么如何及时处理外设的输入?需要解决两个问题:外设的输入随时都可能发生,CPU如何得知?CPU从何处得到外设的输入?
本章以键盘输入为例,讨论这两个核心问题。
☞第一个核心问题:CPU从何处得到外设的输入?
- PC系统的接口卡和主板上,装有各种接口芯片,这些外设接口芯片的内部有若干(外)寄存器,CPU把这些寄存器当做端口。
- 外设的输入送入相关的接口芯片的端口中;CPU向外设的输出先送入端口中,再由相关芯片送到外设。
- CPU可以向外设输出控制命令,这些控制命令也是先送到相关芯片的端口中,然后再由相关的芯片根据命令对外设实施控制。
- 端口是连接CPU和外部设备的桥梁。
☞第二个核心问题:外设的输入随时都可能发生,CPU如何得知?
简言之,通过CPU提供的中断机制。我们知道CPU的内部需要处理的事情发生的时候,将产生中断信息,引发中断过程;当CPU外部有需要事情发生的时候,比如外设的输入到达,相关芯片将向CPU发出相应的中断信息,CPU在执行完当前指令后,可以检测发送过来的中断信息,引发中断过程,处理外设的输入。[CPU内部有中断逻辑]
☞外中断源分类
分类 | 特点 | 中断过程 | 备注 |
可屏蔽中断 | CPU根据FR的IF决定是否响应。若IF=1,则CPU在执行完当前指令后响应中断,否则反之。 | (1).CPU通过数据总线把中断类型码送入CPU; (2). 标志寄存器入栈,IF=0,TF=0; (3). CS、IP入栈; (4). (IP)=(n*4),(CS)=(n*4+2); --->转去执行中断处理程序。 | (1).几乎所有外中断都是可屏蔽中断; (2).需要处理可屏蔽中断,可以使用sti指令。 sti ; 设置IF=1 cli ; 设置IF=0
|
不可屏蔽中断 | CPU必须响应的外中断,当CPU检测不可屏蔽中断信息后,执行外当前指令后,立即响应,引发中断过程。 | (1). 不可屏蔽中断的中断类型码固定是2; (2). 标志寄存器入栈,IF=0,TF=0; (3). CS、IP入栈; (4). (IP)=(2*4),(CS)=(2*4+2); --->转去执行中断处理程序。 | 本章不讨论 |
☞PC机键盘的处理过程
- 键盘输入
- 键盘中有一个芯片对键盘上的每个键的开关状态进行扫描,按下[通码]或者松开[断码]一个键,都会产生一个扫描码(1个字节),用来记录键的位置。
- 扫描码被送入主板上的相关芯片的寄存器中,该寄存器的端口地址为60h。
- 断码=通码+80h
-
引发9号中断
键盘的输入到达60h端口,相关的芯片就会向CPU发出终端类型码为9的可屏蔽中断信息,CPU检测到该中断信息后,如果IF=1,则响应中断,引发中断过程,转去执行int 9中断例程。
-
执行int 9中断例程
BIOS提供了int 9中断例程,用来进行基本的键盘输入处理。
(1). 读出60h端口中的扫描码;
(2). 如果是字符键的扫描码,将该扫描码和它对应的字符码(ASCII)送入内存中的BIOS键盘缓冲区;
如果是控制键(eg Ctrl)或者切换键(eg CapsLock)的扫描码,则将其转变为状态字节,写入内存中存储状态字节的单元;
(3). 对键盘系统进行相关的控制。
BIOS键盘缓冲区是系统启动后,BIOS用于存放int 9中断例程所接受的键盘输入的内存区。该区可以存储15个键盘输入,一个键盘输入用2个字节存放,高位字节是扫描码,低位字节是字符码。
状态字节存放在0040:17单元。该字节记录了控制键和切换键的状态。
☞编写int 9 的中断程序
bios提供的int 9中断例程只是做了简单的处理,不能满足实际需求。
编程:实现按下ctrl,换色。
1 assume cs:code 2 3 stack segment 4 db 128 dup (0) 5 stack ends 6 7 data segment 8 dw 0, 0 9 data ends 10 11 code segment 12 start: 13 mov ax, stack 14 mov ss, ax 15 mov sp, 128 16 17 mov ax, data 18 mov ds, ax 19 20 mov ax, 0 21 mov es, ax 22 23 push es:[9*4] 24 pop ds:[0] 25 push es:[9*4+2] 26 pop ds:[2] ; 将int 9中断例程的入口地址保存在ds:0 ds:2单元中 27 28 mov word ptr es:[9*4], offset int9 29 mov es:[9*4+2], cs ; 在中断向量表中设置新的int 9中断例程的入口地址 30 31 mov ax, 0b800h 32 mov es, ax 33 mov ah, 'a' 34 35 s: mov es:[160*12+40*2], ah 36 call delay 37 inc ah 38 cmp ah, 'z' 39 jna s 40 41 mov ax, 0 42 mov es, ax 43 44 push ds:[0] ; 恢复原来的中断例程 45 pop es:[9*4] 46 push ds:[2] 47 pop es:[9*4+2] 48 49 mov ax, 4c00h 50 int 21h 51 52 delay: 53 push ax 54 push dx 55 mov dx, 1000h ; 循环10000000h次 56 mov ax, 0 57 s1: sub ax, 1 58 sbb dx, 0 59 60 cmp ax, 0 61 jne s1 62 cmp dx, 0 63 jne s1 64 65 pop dx 66 pop ax 67 ret 68 69 ; --------------- 以下为新的int 9 中断例程 ---------------------- 70 int9: push ax 71 push bx 72 push es 73 74 in al, 60h 75 76 ; 模拟int 9中断 77 pushf ; 1:标志寄存器入栈 78 79 pushf ; 2:设置IF=0,TF=0 80 pop bx 81 and bh, 11111100b 82 push bx 83 popf 84 85 call dword ptr ds:[0] ; 3:调用原来的int 9中断例程 86 ; 以上相当于在我们新定义的int 9中断中调用原来的int 9中断 87 88 cmp al, 1 ; 新增加功能(按下esc键换色) 89 jne int9ret 90 91 mov ax, 0b800h 92 mov es, ax 93 inc byte ptr es:[160*12+40*2+1] 94 95 int9ret: 96 pop es 97 pop bx 98 pop es 99 iret 100 101 code ends 102 end start