汇编-端口
各种存储器都和CPU的地址线、数据线、控制线相连。CPU在操控它们的时候,把它们都当作内存来对待,把它们总地看做一个由若干存储单元组成的逻辑存储器,这个逻辑存储器我们称其为内存地址空间。
在PC机系统中, 和CPU通过总线相连的芯片除各种存储器外, 还有以下3种芯片。
(1)各种接口卡(比如,网卡、显卡)上的接口芯片,它们控制接口卡进行工作;
(2)主板上的接口芯片, CPU通过它们对部分外设进行访问;
(3)其他芯片,用来存储相关的系统信息,或进行相关的输入输出处理。
在这些芯片中, 都有一组可以由CPU读写的寄存器。这些寄存器, 它们在物理上可能处于不同的芯片中,但是它们在以下两点上相同。
(1)都和CPU的总线相连, 当然这种连接是通过它们所在的芯片进行的;
(2)CPU对它们进行读或写的时候都通过控制线向它们所在的芯片发出端口读写命令。
可见, 从CPU的角度, 将这些寄存器都当作端口, 对它们进行统一编址, 从而建立了一个统一的端口地址空间。每一个端口在地址空间中都有一个地址。
CPU可以直接读写以下3个地方的数据。
(1)CPU内部的寄存器;
(2)内存单元;
(3)端口。
端口的读写
在访问端口的时候, CPU通过端口地址来定位端口。因为端口所在的芯片和CPU通过总线相连,所以,端口地址和内存地址一样,通过地址总线来传送。在PC系统中,CPU最多可以定位64KB个不同的端口。则端口地址的范围为0~65535。
对端口的读写不能用mov, push、pop等内存读写指令。端口的读写指令只有两条:in和out, 分别用于从端口读取数据和往端口写入数据。
我们看一下CPU执行内存访问指令和端口访问指令时候, 总线上的信息:
(1)访问内存:
mov ax,ds:[8] ;假设执行前(ds)=0
执行时与总线相关的操作如下所示。
①CPU通过地址线将地址信息8发出;
②CPU通过控制线发出内存读命令, 选中存储器芯片, 并通知它, 将要从中读取数据;
③存储器将8号单元中的数据通过数据线送入CPU。
(2)访问端口:
in al,60h ;从60h号端口读入一个字节
执行时与总线相关的操作如下。
①CPU通过地址线将地址信息60h发出;
②CPU通过控制线发出端口读命令, 选中端口所在的芯片, 并通知它, 将要从中读取数据;
③端口所在的芯片将60h端口中的数据通过数据线送入CPU。
注意, 在in和out指令中, 只能使用ax或al来存放从端口中读入的数据或要发送到端口中的数据。访问8位端口时用al,访问16位端口时用ax。
对0~255以内的端口进行读写时:
in al,20h ;从20h端口读入一个字节
out 20h,al ;往20h端口写入一个字节
对256~65535的端口进行读写时,端口号放在dx中:
mov dx,3f8h ;将端口号3f8h送入dx
in al,dx ;从3f8h端口读入一个字节
out dx,al ;向3f8h端口写入一个字节
CMOS RAM芯片
下面的内容中, 我们通过对CMOS RAM的读写来体会一下对端口的访问。
PC机中, 有一个CMOS RAM芯片, 一般简称为CMOS。此芯片的特征如下。
(1)包含一个实时钟和一个有128个存储单元的RAM存储器。
(2)该芯片靠电池供电。所以, 关机后其内部的实时钟仍可正常工作, RAM中的信息不丢失。
(3)128个字节的RAM中, 内部实时钟占用0~0dh单元来保存时间信息, 其余大部分单元用于保存系统配置信息, 供系统启动时BIOS程序读取。BIOS也提供了相关的程
序,使我们可以在开机的时候配置CMOS RAM中的系统信息。
(4)该芯片内部有两个端口, 端口地址为70h和71h。CPU通过这两个端口来读写CMOS RAM
(5)70h为地址端口, 存放要访问的CMOS RAM单元地址; 71h为数据端口, 存放从选定的CMOS RAM单元中读取的数据, 或要写入到其中的数据。
可见, CPU对CMOS RAM的读写分两步进行, 比如, 读CMOS RAM的2号单元:
①将2送入端口70h;
②从端口71h读出2号单元的内容。
检测点
(1)编程, 读取CMOS RAM的2号单元的内容.
assume cs:code code segment start: mov al,2 ;赋值al out 70h,al ;将al送入端口70h in al,71h ;从端口71h处读出单元内容 mov ax,4c00h int 21h code ends end start
(2)编程, 向CMOS RAM的2号单元写入0.
assume cs:code code segment start: mov al,2 ;赋值al out 70h,al ;将al送入端口70h mov al,0 ;赋值al out 71h,al ;向端口71h写入数据al mov ax,4c00h int 21h code ends end start
shl和shr逻辑移位指令
shl逻辑左移指令
它的功能为:
(1)将一个寄存器或内存单元中的数据向左移位;
(2)将最后移出的一位写入CF中;
(3)最低位用0补充。
指令:mov al,01001000b
shl al,1 ;将al中的数据左移一位
执行后(al)=10010000b,CF=0。
如果移动位数大于1时,必须将移动位数放在cl中。
比如,指令:
mov al,01010001b
mov cl,3
shl al,cl
执行后(al)=10001000b,因为最后移出的一位是0,所以CF=0。
可以看出,将X逻辑左移一位,相当于执行 X=X*2。
shr逻辑右移指令
(1)将一个寄存器或内存单元中的数据向右移位;
(2)将最后移出的一位写入CF中;
(3)最高位用0补充。
如果移动位数大于1时,必须将移动位数放在cl中,
可以看出将X逻辑右移一位,相当于执行X=X/2(取整)。
CMOS RAM中存储的时间信息
在CMOS RAM中, 存放着当前的时间:年、月、日、时、分、秒。
这6个信息的长度都为1个字节,存放单元为:
这些数据以BCD码的方式存放。
BCD码是以4位二进制数表示十进制数码的编码方法, 如下所示。
可见, 一个字节可表示两个BCD码。则CMOS RAM存储时间信息的单元中, 存储了用两个BCD码表示的两位十进制数, 高4位的BCD码表示十位, 低4位的BCD码表示个位。
比如,00010100b表示14。
编程,在屏幕中间显示当前的月份。
分析,这个程序主要做以下两部分工作。
(1) 从CMOS RAM的8号单元读出当前月份的BCD码。
要读取CMOS RAM的信息, 首先要向地址端口70h写入要访问的单元的地址:
mov al,8
out 70h,al
然后从数据端口71h中取得指定单元中的数据:
in al,71h
(2)将用BCD码表示的月份以十进制的形式显示到屏幕上
我们可以看出, BCD码值=十进制数码值, 则BCD码值+30h=十进制数对应的ASCII码。
从CMOS RAM的8号单元读出的一个字节中, 包含了用两个BCD码表示的两位十进制数, 高4位的BCD码表示十位, 低4位的BCD码表示个位。比如, 00010100b表示14。
我们需要进行以下两步工作。
实验:访问CMOS RAM
编程,以“年/月/日 时:分:秒”的格式,显示当前的日期、时间
注意:CMOS RAM中存储着系统的配置信息, 除了保存时间信息的单元外, 不要向其他的单元中写入内容,否则将引起一些系统错误。
assume cs:code code segment start: mov al,9 ;年在9单元 out 70h,al ;将al送入端口70h in al,71h ; 读取年数据 mov ah,al ;ah保存年数据 mov cl,4 and al,00001111b ;保留低位数据 shr ah, cl ;保留高位数据 add ah,30h add al,30h mov bx,0b800h mov es,bx mov eS:[12*160+40],ah mov eS:[12*160+42],al mov ax,4c00h int 21h code ends end start
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
2020-09-03 C++opencv-像素操作
2020-09-03 C++虚析构函数
2019-09-03 qt5---QMainWindow
2019-09-03 python--队列