端口

⚠ 转载请注明出处:作者:ZobinHuang,更新日期:July 17 2021


知识共享许可协议

    本作品ZobinHuang 采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可,在进行使用或分享前请查看权限要求。若发现侵权行为,会采取法律手段维护作者正当合法权益,谢谢配合。


目录

有特定需要的内容直接跳转到相关章节查看即可。

    Section 1. 端口的概念:介绍了 8086 CPU 中端口的概念

    Section 2. 端口的读写过程:分析了端口的读写过程,并且给出了用于端口读写的指令

    Section 3. 实例:使用 8086 CPU 读取 CMOS RAM:通过读写主板上的 CMOS RAM 理解了使用 in 和 out 指令用于端口读写的过程

1. 端口的概念

    前面的文章中,我们讨论了 CPU 操作寄存器和内存的过程。除了这二者,CPU 还会面临着其它的操作对象,如外置接口卡芯片、主板接口芯片等等。这些芯片与内存 RAM 控制器一样,通过三类总线与 CPU 进行互联。同样地,在这些芯片上也都存在这一些可供 CPU 读写的寄存器。在 8086 CPU 的世界观中,把这些寄存器称为 端口

2. 端口的读写过程

    回顾 8086 CPU 读写内存的过程:

  1. CPU 通过地址线将读/写地址信息发出
  2. CPU 通过控制器向内存 RAM 控制器发出读写命令:选中 RAM 控制器,并且通知它要从中读/写数据
  3. CPU 和 RAM 控制器通过数据线进行数据传输

    对于端口的读写,有着类似的过程:

  1. CPU 通过地址线将读/写地址信息发出
  2. CPU 通过控制器向端口所在芯片发出读写命令:选中对应的端口芯片,并且通知它要从中读/写数据
  3. CPU 和端口芯片通过数据线进行数据传输

    对于 8086 CPU 来说,它所能访问到的端口是统一编址的。也就是说和内存一样,只要给定一个端口地址,就能唯一确定全局的某一个寄存器。8086 CPU 最多可以寻址 64KB 个不同的端口,也就是 0~65535 个端口。

    在汇编程序中,"in [register], [port_address]" 指令用于读取端口内容到指定寄存器,"out [port_address], [register]" 指令用于向指定端口写入指定寄存器中的数据。注意!在 8086 CPU 中,只能基于 AX 和 AL 寄存器来进行 in 和 out 操作,两者分别对应 16 位和 8 位操作。

3. 实例:使用 8086 CPU 读取 CMOS RAM

    除了我们在 BIOS 和 DOS 提供的中断例程 中所提到的,主板上有一个 ROM 芯片门用于存储系统上点之后的主板硬件自检和中断注册外 (i.e. BIOS),主板上还有一个名为 CMOS RAM 的芯片。这个芯片中有一个 实时钟 (RTC) 和一个 128-bytes 的 RAM 寄存器。这个芯片在主板掉电以后靠电池供电,因此可以一直工作。在这 128B 的 RAM 中,0~0DH 这 14 个字节用于保存时间信息,剩下的字节用于保存主板的配置信息,以供 BIOS 读取配置。

    为了访问这个芯片中存储的 128B RAM 数据,CPU 需要采用端口的方式来访问这个芯片。这个芯片内部有两个端口:70H 和 71H,其中 70H 为地址端口,CPU 向这个寄存器中写入要访问的 128B RAM 的地址;71H 为数据端口,CPU 可以从这个端口中读取选定的 128B RAM 单元的数据。

插曲:shl 和 shr 逻辑移位指令

    shlshr 指令可以用于对寄存器中存储的内容分别进行 左移位 和 右移位 的操作。以 shl 为例:"shl ax, 1" 指令将 AX 寄存器中保存的 16 位值进行逻辑左移,并且将被移出的最高位放入 CF (Carry Flag) 标志位中。如果要进行大于 1 位的逻辑移位,则需要将移动位数放入 CL 寄存器中,并使用 "shl al, cl" 的指令进行移位。

    在 CMOS RAM 中,存储时间信息的单元为:

存放单元 0 1 2 3 4 5 6 7 8 9
内容

    这些时间信息都是采用 BCD 码 的方式存储的。我们通过下面的程序来基于端口从 CMOS RAM 中读取月份信息,并将月份信息显示在屏幕上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
code segment
start: # 设置读取的 RAM 单元地址
mov al, 8
out 70h, al

# 将月份信息读取进来
in al, 71h

mov ah, al
# 在 AH 中保留十位信息
mov cl, 4
shr ah, cl

# 在 AL 中保留个位信息
and al, 00001111b

# 将 BCD 码转化为 ASCII 码
add ah, 30h
add al, 30h

# 显示在屏幕上
mov bx, 0B800h
mov es, bx
mov byte ptr es:[160*12+40*2], ah # 显示月份的十位数字
mov byte ptr es:[160*12+40*2+2], al # 显示月份的个位数字

mov ax, 4C00h
int 21h
code ends

end start

    运行的效果如下所示: