汇编语言实验 11:访问 CMOS RAM

430 阅读6分钟

1. 预备知识

  1. CPU 可以读写三个地方的数据:CPU 内部的寄存器,内存单元和端口。

  2. CPU 通过端口地址定位端口,使用 in 和 out 指令读写端口,且只能使用 AX(16 位)或 AL(8 位)来存放从端口读入或发送到端口的数据。

  3. PC 机中,有一个 CMOS RAM 芯片,或 CMOS 芯片,该芯片具有以下特性:

    • 包含一个实时钟和一个包含 128 个存储单元的 RAM(Random Access Memory) 存储器

    • 该芯片靠电池供电,关机后实时钟和 RAM 仍正常工作

    • 128 个字节的 RAM 中,实时钟占用 0~0dh 单元保存时间信息,其他大部分用于保存系统配置信息

    • CPU 通过芯片内部的 70h 端口和 71h 端口来读写 CMOS RAM

    • 70h 为地址端口,存放要访问 CMOS 单元的地址;71h 为数据端口,存放从指定单元读取的数据

  4. shl 是逻辑左移指令,将最后移出的一位写入 CF 中,最低位用 0 补充。

;左移一位
mov al,01001000b
shl al,1
;左移三位
mov al,01001000b
mov cl,3
shl al,cl
  1. shr 是逻辑右移指令,将最后移出的一位写入 CF 中,最高位用 0 补充,用法同 shl。

  2. 在 CMOS 中,存放着当前系统时间,年、月、日、时、分、秒,这六个信息的长度都是 1 个字节,分别对应于第 9、8、7、4、2、0 个存储单元。

  3. 上述时间信息以 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. 总结

  1. CPU 通过芯片内部的 70h 端口和 71h 端口来读写 CMOS RAM

  2. shl 和 shr 是汇编语言中的逻辑移位指令,可等效于 2 次幂的乘法或除法

  3. CMOS 内部存放着系统时间,通过指定端口号可获取不同时间位的信息

  4. 参考:汇编语言/王爽著.——北京:清华大学出版社,2003