第十四章 端口
第十四章 端口
在这一章,我们可以学到CPU不仅跟各种存储器相连接,还与一下三种芯片会有交互:
- 各种接口卡,比如,网卡、显卡
- 主板上的接口芯片,比如一些I/O接口
- 其他芯片,用来存储相关的系统信息,比如后面要讲到的CMOS RAM
当然,这些芯片都拥有各自的读写寄存器,这些寄存器虽然都不在同一个物理芯片里,但是它们均有两个共同点:
- 都和CPU的总线相连(通过它们所在的芯片处理)
- CPU对它们进行读或写的时候都能通过控制线向它们所在的芯片发出端口读写命令。
那CPU是如何知道这些芯片的存在呢?
笔者个人猜测是主板制订的某些基础规范,哪些设备该接哪些接口,先保证系统的正常启动;然后有些通用的多样化的设备,可以让商家自行定制驱动程序,交由操作系统来读写。
那我们该如何使用这些芯片呢?
CPU会将已知的这些芯片上的寄存器进行统一编址,建立一个统一的端口地址空间(这里的端口就是代指可访问的芯片上的寄存器),每个端口(芯片)在端口地址空间内都有一个地址。
在学完这章节后,我们能知道CPU可以直接从以下三个地方读取数据:
- CPU内部的寄存器
- 内存单元(内存)
- 端口
端口的读写
CPU最多可以定位 *64KB(0~65535) 个不同的端口,端口地址和内存地址一样可以通过地址总线进行传输。
端口的读写命令仅两条:
- in al||ax, 端口地址
- out 端口地址, al||ax
但是和内存访问指令的流程相差无几:
内存访问流程
mov ax, ds:[0AH]
- CPU通过 地址总线将地址数据 0AH 发出(注意,这个数据在硬件里是一个持续性的电压,不是一次性的,请注意,不然会想不通 CPU怎么知道把地址数据发到哪里 的问题,有兴趣的可以阅读我的计算机组成原理笔记)
- CPU通过 控制总线 发出内存读命令(也是一个电压),选中指定得存储器芯片,并通知他将要读取数据
- 存器将0AH号单元中得数据通过数据线送入CPU
端口访问流程
in al, 60h
- CPU通过 地址总线 锁定端口地址为60h的端口
- CPU通过 控制总线 发出端口读命令,选中端口所在的芯片,并通知他将要读取数据
- 端口所在的芯片将60H号端口中得数据通过数据线送入CPU
注意端口的读写指令里,只能使用ax或al来存放从端口中读入的 或 要发送到端口的数据。访问8位端口时用al,访问16位端口时用ax。
另外对0~255以内的端口进行读写时:
in al, 20h
out 20h, al
对256~65535的端口进行读写时,端口号放在dx中:
mov dx, 3f8h
in al, dx
out dx, al
shl和shr指令
shl(shift logic left)逻辑左移指令
- 将一个寄存器或内存单元中的数据向左移位
- 将最后移出的一位写入CF中(当移动N位时,CF里只保存最后一个移出的数据)
- 最低位用0补充
举个例子:
; 移动1位的例子
; 原数据
mov al, 01001000b
; 左移1位
; 原始数据 0100 1000
; 左移1位 01001 000
; 低位0补齐 1001 0000
; 由于移动了1位,最高位就是这个被移出的数据,故CF=0
shl al, 1
;结果
(al) = 10010000b
; 移动2位的例子
; 原数据
mov cl, 2
mov al, 01001000b
; 原始数据 0010 1000
; 左移2位 010010 00
; 低位0补齐 0010 0000
; 由于移动了2位,最后一位就是这个被移出的数据,故CF=1
shl al, 2
;结果
(al) = 00100000b
注意,移位在数学逻辑上相当于乘2,移动一次,N = N * 2,移动三次就相当于 N = N * 2 ^ 3 = ((N * 2) * 2) * 2
shr(shift logic right)逻辑右移指令
- 将一个寄存器或内存单元中的数据向右移位
- 将最后移出的一位写入CF中(当移动N位时,CF里只保存最后一个移出的数据)
- 最高位用0补充
这里就不多介绍了,和逻辑左移一样。另外计算机里除开逻辑左移、逻辑右移还有一个叫做算术右移(SAR指令)(算术左移和逻辑左移一样就不讲了)。该指令相较于逻辑右移,仅在第三步 最高位补充上有所不同:
逻辑右移 | 算术右移 | |
---|---|---|
步骤一 | 将一个寄存器或内存单元中的数据向右移位 | 同逻辑右移 |
步骤二 | 将最后移出的一位写入CF中(当移动N位时,CF里只保存最后一个移出的数据) | 同逻辑右移 |
步骤三 | 最高位用0补充 | 原最高位如果是1,那么此时用1补充;否则用0补充 |
14.4 CMOS RAM 中存储的时间信息
在CMOS RAM中,存放着当前的时间:年、月、日、时、分、秒,这6个信息
内存单元 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
信息 | 秒 | 分 | 时 | 日 | 月 | 年 |
以上的数据都是采用 BCD码 的方式存放。
BCD码简单来说就是用 四个二进制位 表示 一个10进制(0~9)。但是四个二进制位每一位上代表的含义可以不同,比如我们传统的认知——第一位表示1,第二位表示2,第三位表示4,第四位表示8,这种表示方法叫做 8421BCD码,此时十进制8就用1000来表示;如果第一位表示1,第二位表示2,第三位表示4,第四位表示2,那么这种表示方法就叫 2421BCD码,此时十进制 7 就用 * 1110*表示。
举个例子:
- 从CMOS RAM的8号单元中读取当前月份的BCD码
assume cs:codesg
codesg segment
start:
mov al, 8
out 70h, al
in al, 71h
mov ax, 4c00H
int 21H
codesg ends
end start
- 从CMOS RAM的8号单元中读取当前月份 输出在屏幕上
; 调用21H的02号功能
; 大全上说02号功能没有返回参数??其实是有的,就是打印出来的字符的ascii码
assume cs:codesg
codesg segment
start:
mov al, 8
out 70h, al
in al, 71h
mov ah, al
mov cl, 4
shr ah, cl
and al, 00001111b
add al, 30H
add ah, 30H
; 保存一下,因为调用02H功能好像会在AL输出输入的参数
mov bl, al
mov dl, ah
mov ah, 02H
int 21H
mov al, bl
mov dl, al
mov al, 0
mov ah, 02H
int 21H
mov ax, 4c00H
int 21H
codesg ends
end start