4. CMOS RAM读写与系统时间获取
任务目标
本案例通过x86 I/O端口访问CMOS RAM,实现:读取系统当前时间(时:分:秒)与日期(年-月-日),并修改CMOS中某个字节(如秒寄存器)以观察写入效果。代码运行于实模式环境(如QEMU + FreeDOS或裸机引导),直接操作端口0x70/0x71。
CMOS端口访问原理
CMOS RAM通过两个端口访问:0x70写入要访问的寄存器地址,0x71读写该寄存器数据。时间寄存器以BCD码存储,需转换为十进制显示。注意:访问CMOS前应关闭NMI中断(将0x70最高位置1),读取时若正在更新(寄存器0x0A的bit 7为1)需等待。
关键完整代码
; cmos_time.asm - 读取并修改CMOS时间
; 编译: nasm -f bin cmos_time.asm -o cmos_time.com
; 运行: 在DOS/FreeDOS中执行 cmos_time.com,或放入引导扇区
org 0x100
cpu 8086
; 等待CMOS更新完成
wait_update:
mov al, 0x0A
out 0x70, al
in al, 0x71
test al, 0x80
jnz wait_update
ret
; 读取指定CMOS寄存器,返回BCD值在AL
read_cmos:
out 0x70, al
in al, 0x71
ret
; BCD转ASCII并打印
print_bcd:
push ax
mov al, ah
shr al, 4
add al, '0'
mov ah, 0x0E
int 0x10
pop ax
push ax
mov al, ah
and al, 0x0F
add al, '0'
mov ah, 0x0E
int 0x10
pop ax
ret
start:
; 关闭NMI并等待更新完成
mov al, 0x80
out 0x70, al
call wait_update
; 读取秒 (寄存器0x00)
mov al, 0x00
call read_cmos
mov ah, al
call print_bcd
; 打印冒号
mov al, ':'
mov ah, 0x0E
int 0x10
; 读取分 (寄存器0x02)
mov al, 0x02
call read_cmos
mov ah, al
call print_bcd
; 打印冒号
mov al, ':'
mov ah, 0x0E
int 0x10
; 读取时 (寄存器0x04)
mov al, 0x04
call read_cmos
mov ah, al
call print_bcd
; 换行
mov al, 0x0D
mov ah, 0x0E
int 0x10
mov al, 0x0A
mov ah, 0x0E
int 0x10
; 修改秒寄存器:写入BCD值0x30(即30秒)
call wait_update
mov al, 0x80 | 0x00 ; 关闭NMI,选择秒寄存器
out 0x70, al
mov al, 0x30
out 0x71, al
; 等待1秒后重新读取验证
mov cx, 0xFFFF
delay:
loop delay
call wait_update
mov al, 0x00
call read_cmos
mov ah, al
call print_bcd
; 程序结束(返回DOS)
mov ax, 0x4C00
int 0x21
编译与运行
- 安装NASM:
sudo apt install nasm(Linux)或下载Windows版。 - 编译:
nasm -f bin cmos_time.asm -o cmos_time.com - 运行方式(任选其一):
- 使用QEMU + FreeDOS:
qemu-system-i386 -fda fdos.img,将cmos_time.com放入镜像后执行。 - 裸机引导:将生成的
.com文件作为引导扇区(需额外处理,本案例不展开)。
- 使用QEMU + FreeDOS:
验证方法与判定标准
- 预期输出:程序首先打印当前时间(格式
HH:MM:SS),随后将秒强制设为30,延时后再次打印秒值,应显示30(或30±1,若恰好跨越秒边界)。 - 通过条件:两次打印的秒值中第二次为
30,且时间格式正确、无乱码。 - 观察修改效果:若在修改后立即用其他工具(如DOS的
DEBUG)读取CMOS寄存器0x00,其值应为0x30(BCD),证明写入成功。
注意事项
- 修改CMOS秒寄存器会导致系统时间跳变,在真实硬件上操作需谨慎;本案例建议在虚拟机中执行。
- 若在Windows/Linux现代操作系统下直接运行,会因端口权限受限而失败;必须使用实模式环境。
- BCD转换仅适用于二进制时间格式(寄存器
0x0B的bit 2为0时);若系统设为二进制格式,需改用二进制转换逻辑。