1. 预备知识
-
CPU 可以读写三个地方的数据:CPU 内部的寄存器,内存单元和端口。
-
CPU 通过端口地址定位端口,使用 in 和 out 指令读写端口,且只能使用 AX(16 位)或 AL(8 位)来存放从端口读入或发送到端口的数据。
-
PC 机中,有一个 CMOS RAM 芯片,或 CMOS 芯片,该芯片具有以下特性:
-
包含一个实时钟和一个包含 128 个存储单元的 RAM(Random Access Memory) 存储器
-
该芯片靠电池供电,关机后实时钟和 RAM 仍正常工作
-
128 个字节的 RAM 中,实时钟占用 0~0dh 单元保存时间信息,其他大部分用于保存系统配置信息
-
CPU 通过芯片内部的 70h 端口和 71h 端口来读写 CMOS RAM
-
70h 为地址端口,存放要访问 CMOS 单元的地址;71h 为数据端口,存放从指定单元读取的数据
-
-
shl 是逻辑左移指令,将最后移出的一位写入 CF 中,最低位用 0 补充。
;左移一位
mov al,01001000b
shl al,1
;左移三位
mov al,01001000b
mov cl,3
shl al,cl
-
shr 是逻辑右移指令,将最后移出的一位写入 CF 中,最高位用 0 补充,用法同 shl。
-
在 CMOS 中,存放着当前系统时间,年、月、日、时、分、秒,这六个信息的长度都是 1 个字节,分别对应于第 9、8、7、4、2、0 个存储单元。
-
上述时间信息以 BCD 码的形式存放,每个 BCD 码以 4 位二进制表示十进制,上述时间信息的每位需使用 2 个 BCD 码存储,且高 4 位表示十位、低 4 位表示个位。如从 CMOS 中读取当前月:
mov al,8
out 70h,al ;向端口70h写入要访问的单元地址
in al,71h ;从数据端口71h中取得指定单元的数据
mov ah,al
mov cl,4
shr ah,cl ;十位
and al,00001111b ;个位
2. 实验任务 1:读取 CMOS 单元
读取 CMOS RAM 的 2 号单元的内容。
2.1 实验分析
根据预备知识,首先基于 out 指令将 2 送入端口 70h,表示要读取 2 号单元的内容;然后基于 in 指令从 71h 端口读取数据。整体代码为:
assume cs:code
code segment
start:
mov al,2h
out 70h,al
in al,71h
mov ax,4c00h
int 21h
code ends
end start
2.2 实验结果
由结果可知,从 2 号单元读取的内容为 19H。
3. 实验任务 2:shl 和 shr 的应用
用加法和移位指令计算 (ax)=(ax)*10。
3.1 实验分析
(ax)=(ax)*10=(ax)*2+(ax)*8,而乘以 2 和乘以 8 可分别通过左移 1 位和 3 位实现。整体代码:
assume cs:code
code segment
start:
mov ax,5h ;(AX)=5
mov bx,ax
shl ax,1 ;计算左移1位的结果
mov cl,3
shl bx,cl ;计算左移3位的结果
add ax,bx ;(AX)=(AX)+(BX)
mov ax,4c00h
int 21h
code ends
end start
3.2 实验结果
设 (AX)=5,乘以 10 的结果为 32h。
图分别标出了 AX 左移 1 位的结果(红色),左移 3 位的结果(蓝色)和最终结果(绿色)。
4. 实验任务 3:显示系统时间
以年/月/日 时:分:秒的格式,显示当前系统时间。
4.1 实验分析
如预备知识第七条读取当前月的代码,通过给 70h 端口传入不同值可以获取不同的时间位信息,逐个显示即可。为简化程序,本文使用子程序调用的方式。使用数据段存放待访问的端口地址和字符串间的间隔符:
data segment
db '9/8/7 4:2:0'
data ends
基于 cmp 指令判断当前读取的字符是否为数字,如果为数字,则从指定端口读取数据并显示;否则直接显示当前字符。
mov cl,ds:[si] ;读取字符
cmp cl,48
jb show_ch ;直接显示当前字符
cmp cl,57
ja show_ch ;直接显示当前字符
call get_ch ;从指定端口读取数据并显示
直接显示当前字符:
show_ch:
mov es:[bx+di],cl
mov byte ptr es:[bx+di+1],2h
从指定端口读取数据并显示:
sub cl,48 ;字符转换为整数
mov al,cl ;传入指定端口号
out 70h,al
in al,71h ;取对应端口的内容,高4位为十位、低4位为个位
mov ah,al
mov cl,4
shr ah,cl ;高4位
and al,00001111b ;低4位
add ah,30h ;转换为十进制的字符形式
mov es:[bx+di],ah
mov byte ptr es:[bx+di+1],2h ;显示十位
add di,2
add al,30h ;转换为十进制的字符形式
mov es:[bx+di],al
mov byte ptr es:[bx+di+1],2h ;显示个位
add di,2 ;偏移2个字节写字符
inc si ;偏移1个字节取字符
ret ;子程序返回
整体代码为:
assume cs:code
data segment
db '9/8/7 4:2:0'
data ends
code segment
start:
mov ax,data
mov ds,ax ;段寄存器DS指向数据段
mov ax,0b800h
mov es,ax ;段寄存器ES指向显示缓冲区
mov bx,160*12+20*2 ;BX存放显示偏移
mov si,0
mov di,0 ;分别用于索引源段和目的段
mov cx,11 ;循环次数
s:
push cx ;保护(CX)
mov cl,ds:[si] ;读取字符
cmp cl,48
jb show_ch ;直接显示当前字符
cmp cl,57
ja show_ch ;直接显示当前字符
call get_ch ;从指定端口读取数据并显示
pop cx ;恢复(CX)
loop s
mov ax,4c00h
int 21h
show_ch:
mov es:[bx+di],cl
mov byte ptr es:[bx+di+1],2h
inc si ;偏移1个字节取字符
add di,2 ;偏移2个字节写字符
pop cx ;恢复(CX)
sub cx,1
jmp far ptr s ;段间转移至s处,调用show_ch后跳过get_ch及后续代码
get_ch:
sub cl,48 ;字符转换为整数
mov al,cl ;传入指定端口号
out 70h,al
in al,71h ;取对应端口的内容,高4位为十位、低4位为个位
mov ah,al
mov cl,4
shr ah,cl ;高4位
and al,00001111b ;低4位
add ah,30h ;转换为十进制的字符形式
mov es:[bx+di],ah
mov byte ptr es:[bx+di+1],2h ;显示十位
add di,2
add al,30h ;转换为十进制的字符形式
mov es:[bx+di],al
mov byte ptr es:[bx+di+1],2h ;显示个位
add di,2 ;偏移2个字节写字符
inc si ;偏移1个字节取字符
ret ;子程序返回
code ends
end start
4.2 实验结果
5. 总结
-
CPU 通过芯片内部的 70h 端口和 71h 端口来读写 CMOS RAM
-
shl 和 shr 是汇编语言中的逻辑移位指令,可等效于 2 次幂的乘法或除法
-
CMOS 内部存放着系统时间,通过指定端口号可获取不同时间位的信息
-
参考:汇编语言/王爽著.——北京:清华大学出版社,2003