第十四章 端口
CPU可以直接读写数据的地方:
- CPU内部的寄存器
- 内存单元
- 端口
14.1 端口的读写
对8位端口进行读写,用al保存数据,对16位端口进行读写,用ax保存数据
对0~255以内端口进行读写时
in al,20h out 20h,al
对256~65535端口进行读写时,需要用dx保存端口号
mov dx,3f8h in al,dx out dx,al
14.2 CMOS RAM
70h地址端口:存放要访问的CMOS RAM单元的地址
71h数据端口:存放从选定的CMOS EAM单元中读取数据
(1)
assume cs:code code segment start: mov al,2 out 70h,al in al,71h mov ax,4c00h int 21h code ends end start
(2)
assume cs:code code segment start: mov al,2 out 70h,al mov al,0 out 71h,al mov ax,4c00h int 21h code ends end start
14.3 shl和shr指令
shl逻辑左移指令
shr逻辑右移指令
功能:
- 将一个寄存器或内存单元中的数据左/右移位
- 将最后移出的一位写入CF中
- 最低位/最高位用0补存
如果要移动一位以上,需要用cl寄存器保存次数
检测点 14.2
assume cs:code code segment start: mov ax,66 mov bx,ax shl ax,1 mov cl,3 shl bx,cl add ax,bx mov ax,4c00h int 21h code ends end start
14.4 CMOS RAM中储存的时间信息
存放的当前时间包括:年,月,日,时,分,秒,长度都为1字节,存放单元在
秒:0 分:2 时:4
日:7 月:8 年:9
实验14 访问CMOS RAM
assume cs:code data segment db 9,8,7,4,2,0 data ends code segment start: mov ax,data mov ds,ax mov bx,0b800h mov es,bx mov di,160*12+33*2 mov si,0 mov cx,6 s: call show cmp byte ptr ds:[si],7 jna s1;不大于7就跳转 ;年,月 mov byte ptr es:[di],'/' jmp stop s1: jb s2;小于7就跳转 ;日 mov byte ptr es:[di],' ' jmp stop s2: cmp byte ptr ds:[si],0 je stop;等于0就跳转 ;时,分 mov byte ptr es:[di],':' stop: inc si add di,2 loop s mov ax,4c00h int 21h show: push cx mov al,ds:[si] out 70h,al in al,71h ;获取数据 mov ah,al mov cl,4 shr ah,cl and al,00001111b ;十进制转ASCII码 add ah,30h add al,30h ;写入显示缓冲区 mov es:[di],ah mov byte ptr es:[di+2],al add di,4 pop cx ret code ends end start
第15章 外中断
15.1 接口芯片和端口
外设的输入不直接送入内存和CPU,而是先送入端口中;CPU向外设的输出也不是直接输入外设,而是先送入端口中,再由相关的芯片送到外设,CPU还可以向外设输出控制信息,而这些控制命令也是先送到相关芯片的端口中,然后再由相关的芯片根据命令对外设实施控制。
可见,CPU通过端口和外部设备进行联系。
15.2 外中断信息
外中断源分为:
- 可屏蔽中断:CPU可以不响应的外中断。sti,设置IF=1;cli,设置IF=0
- 不可屏蔽中断:CPU必须响应的外中断。中断类型码固定为2
15.3 PC机键盘的处理过程
Key | Down | Up | Key | Down | Up | Key | Down | Up | Key | Down | Up |
---|---|---|---|---|---|---|---|---|---|---|---|
Esc | 1 | 81 | [ { | 1A | 9A | , < | 33 | B3 | center | 4C | CC |
1 ! | 2 | 82 | ] } | 1B | 9B | . > | 34 | B4 | right | 4D | CD |
2 @ | 3 | 83 | Enter | 1C | 9C | / ? | 35 | B5 | + | 4E | CE |
3 # | 4 | 84 | Ctrl | 1D | 9D | R shift | 36 | B6 | end | 4F | CF |
4 $ | 5 | 85 | A | 1E | 9E | * PrtSc | 37 | B7 | down | 50 | D0 |
5 % | 6 | 86 | S | 1F | 9F | alt | 38 | B8 | pgdn | 51 | D1 |
6 ^ | 7 | 87 | D | 20 | A0 | space | 39 | B9 | ins | 52 | D2 |
7 & | 8 | 88 | F | 21 | A1 | CAPS | 3A | BA | del | 53 | D3 |
8 * | 9 | 89 | G | 22 | A2 | F1 | 3B | BB | / | E0 35 | B5 |
9 ( | 0A | 8A | H | 23 | A3 | F2 | 3C | BC | enter | E0 1C | 9C |
0 ) | 0B | 8B | J | 24 | A4 | F3 | 3D | BD | F11 | 57 | D7 |
- _ | 0C | 8C | K | 25 | A5 | F4 | 3E | BE | F12 | 58 | D8 |
= + | 0D | 8D | L | 26 | A6 | F5 | 3F | BF | ins | E0 52 | D2 |
Bksp | 0E | 8E | ; : | 27 | A7 | F6 | 40 | C0 | del | E0 53 | D3 |
Tab | 0F | 8F | ' " | 28 | A8 | F7 | 41 | C1 | home | E0 47 | C7 |
Q | 10 | 90 | ` ~ | 29 | A9 | F8 | 42 | C2 | end | E0 4F | CF |
W | 11 | 91 | L shift | 2A | AA | F9 | 43 | C3 | pgup | E0 49 | C9 |
E | 12 | 92 | \ | | 2B | AB | F10 | 44 | C4 | pgdn | E0 51 | D1 |
R | 13 | 93 | Z | 2C | AC | NUM | 45 | C5 | left | E0 4B | CB |
T | 14 | 94 | X | 2D | AD | SCRL | 46 | C6 | right | E0 4D | CD |
Y | 15 | 95 | C | 2E | AE | home | 47 | C7 | up | E0 48 | C8 |
U | 16 | 96 | V | 2F | AF | up | 48 | C8 | down | E0 50 | D0 |
I | 17 | 97 | B | 30 | B0 | pgup | 49 | C9 | R alt | E0 38 | B8 |
O | 18 | 98 | N | 31 | B1 | - | 4A | CA | R ctrl | E0 1D | 9D |
P | 19 | 99 | M | 32 | B2 | left | 4B | CB | Pause | E1 1D 45 E1 9D C5 | - |
1.键盘输入
一般按下一个键时产生的扫描码称为通码,松开一个键产生的扫描码称为断码,扫描码长度为一个字节,通码第7位为0,断码第7位为1,即:断码=通码+80h
2.引发9号中断
键盘的输入到达60h端口时,相关的芯片就会向CPU发出中断类型码为9的可屏蔽中断信息。CPU检测到该信息后,如果IF=1,则响应中断,否则就会屏蔽中断。
3.执行int9中断例程
BIOS键盘缓冲区 它是系统启动后,BIOS用于存放int 9中断历程所接收的键盘输入的内存区。该内存区可以存储15个键盘输入,因为int 9中断例程除了接收扫描码外,还要产生对应的ASCII码,所以,在缓冲区中,一个键盘输入用一个字单元存放,高位扫描码,低位字符码。
inc和dec等算术运算指令不影响标志寄存器的中断和调试位
15.4 编写int9中断例程
模拟以下几步:
- 标志寄存器入栈;
- IF=0,TF=0;
- CS,IP入栈
- (IP)=(DS)*16+0;(CS)=(DS)*16+2
3,4可以简化为call dword ptr ds:[0]
注意:
- 该程序要在返回之前,将中断向量表中的int 9中断例程的入口地址恢复为原来的地址,否则程序返回后,别的程序将无法使用键盘。
- 取中断码是为了确定程序入口地址,在这里中断码已经确定,所以不能用int指令调用。
assume cs:code stack segment db 128 dup (0) stack ends data segment dw 0,0 data ends code segment start: ;栈段地址 mov ax,stack mov ss,ax mov sp,128 ;数据段地址 mov ax,data mov ds,ax ;原9号中断向量表中的入口地址保存到ds:[0],ds:[2] mov ax,0 mov es,ax push es:[9*4] pop ds:[0] push es:[9*4+2] pop ds:[2] ;设置新int9中断例程入口地址 mov word ptr es:[9*4],offset int9 mov es:[9*4+2],cs ;设置显示缓冲区 mov ax,0b800h mov es,ax mov di,160*12+40*2 mov ah,'a' s: ;显示字符 mov es:[di],ah call delay inc ah cmp ah,'z' jna s ;恢复原来int9中断例程入口地址 mov ax,0 mov es,ax push ds:[0] pop es:[9*4] push ds:[2] pop es:[9*4+2] mov ax,4c00h int 21h ;延时 delay: push dx push ax ;10 0000h写入高地址和低地址 mov dx,10h mov ax,0 s1: ;只能使用sub,不能dec sub ax,1 sbb dx,0 cmp dx,0 jne s1 cmp ax,0 jne s1 pop ax pop dx ret ;新中断例程 int9: push ax push bx push es ;从60h端口读出键盘的输入 in al,60h pushf pushf ;取出标志寄存器 pop bx ;IF=0,TF=0 and bh,11111100b ;保存标志寄存器 push bx popf call dword ptr ds:[0];对int指令进行模拟,调用原来的int9中断例程,处理其他硬件细节 cmp al,1;判断是否按下ESC jne int9ret mov ax,0b800h mov es,ax inc byte ptr es:[di+1];属性值+1 int9ret: pop es pop bx pop ax iret code ends end start