访问CMOS中的系统时间

项目情景

编程,以“年/月/日 时:分:秒”的格式,显示当前的日期、时间。

注意:CMOS RAM中存储着系统的配置信息,除了保存时间信息的单元外,不要向其他的单元中写入内容,否则将引起一些系统错误。

分析

在Linux系统上对应的命令date的效果如图所示:
date
而我们需要实现的就是和上图差不多的效果。

PC机中存在一个被称为CMOS RAM(下简称CMOS)的部件,包含一个实时钟与一个128字节的RAM,我们所需要的系统时间就存储在其中。CMOS有两个端口,端口地址为70h71h。CPU通过这两个端口读写CMOS。
70h是地址端口,71h是数据端口。CPU需要先将需要进行读写操作的CMOS单元地址送入70h,再对71h端口进行读写。
在CMOS中存放着的时间信息,长度都为1字节,存放的单元依次为:
秒:0 分:2 时:4 日:7 月:8 年:9
可以将其分为两组,一组年月日步长为1,一组时分秒步长为2

时间信息以BCD码形式存放,即将各位数字分别以二进制形式保存。1字节可保存两位,高4位保存十位,低4位保存个位,显示时需要将二者分离。

程序实现

指定各段的段寄存器:

1
assume cs:code,ds:text,ss:stack

定义数据段text,用于存放需要打印的格式化时间字符串:
1
2
3
4
text segment
db "yy/mm/dd hh:mm:ss","$"
; 格式化:年/月/日 时:分:秒
text ends

定义栈段stack,用于暂存寄存器:
1
2
3
stack segment
db 16 dup (0)
stack ends

定义代码段code和程序起始点start:
1
2
3
4
5
code segment
start:
......
code ends
end start

初始化段寄存器,ds指向text段:
1
2
mov ax,text
mov ds,ax

清屏:
1
2
3
4
5
6
7
8
mov ax,0b800h
mov es,ax
mov si,0
mov cx,2000
s2:
mov word ptr es:[si],0720h
add si,2
loop s2

接下来定义子程序read_cmos,读取CMOS的指定单元(al存放),转化为字符串,并写入text段指定地址(bx给出):
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
read_cmos:
; 名称:read_cmos
; 功能:读取CMOS RAM指定单元
; 参数:(al)->地址(CMOS)
; 返回:ds:bx(in)
push cx
push ax

; 读取CMOS
out 70h,al
in al,71h

; 分离个位与十位
mov ah,al
mov cl,4
shr ah,cl
and al,00001111b

; BCD -> Ascii
add ah,30h
add al,30h

; 写入
mov ds:[bx],ah
mov ds:[bx+1],al

pop ax
pop cx
ret

调用read_cmos子程序,分两组年月日时分秒写入:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
; 年月日
mov al,9
mov cx,3
mov bx,0
s0:
call read_cmos
add bx,3
dec al ; 年:9,月:8,日:7
loop s0

; 时分秒
mov al,4
mov cx,3
mov bx,9
s1:
call read_cmos
add bx,3
sub al,2 ; 时:4,分:2,秒:0
loop s1

利用int 10hint 21h提供的中断例程打印字符串:
1
2
3
4
5
6
7
8
9
mov ah,2   ; 设置光标
mov bh,0 ; 页
mov dh,12 ; 行
mov dl,20h ; 列
int 10h

mov ah,9 ; 打印
mov dx,0 ; 文本地址
int 21h

将光标设置在最后一行,显示提示符:
1
2
3
4
5
mov ah,2   ; 设置光标
mov bh,0 ; 页
mov dh,24 ; 行
mov dl,0 ; 列
int 10h

最后返回:
1
2
mov ax,4c00h
int 21h

完整的程序如下:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
assume cs:code,ds:text,ss:stack

text segment
db "yy/mm/dd hh:mm:ss","$"
; Format
text ends

stack segment
db 16 dup (0)
stack ends

code segment
start:
mov ax,text
mov ds,ax

; Clear
mov ax,0b800h
mov es,ax
mov si,0
mov cx,2000
s2:
mov word ptr es:[si],0720h
add si,2
loop s2

mov al,9
mov cx,3
mov bx,0
s0:
call read_cmos
add bx,3
dec al ; y:9,m:8,d:7
loop s0

mov al,4
mov cx,3
mov bx,9
s1:
call read_cmos
add bx,3
sub al,2 ; h:4,m:2,s:0
loop s1

mov ah,2 ; Set Cursor
mov bh,0 ; Page
mov dh,12 ; Line
mov dl,20h ; Column
int 10h

mov ah,9 ; Print
mov dx,0 ; text
int 21h

mov ah,2 ; Set Cursor
mov bh,0 ; Page
mov dh,24 ; Line
mov dl,0 ; Column
int 10h

; Terminate
mov ax,4c00h
int 21h

read_cmos:
; Name:read_cmos
; Function:Read designated byte of CMOS RAM
; Parameter:(al)->address(CMOS)
; Return:ds:bx(in)
push cx
push ax

; Read CMOS
out 70h,al
in al,71h

; Isolate
mov ah,al
mov cl,4
shr ah,cl
and al,00001111b

; BCD -> Ascii
add ah,30h
add al,30h

mov ds:[bx],ah
mov ds:[bx+1],al

pop ax
pop cx
ret

code ends
end start

体量不大,逻辑也还是很清晰的。

运行结果

编译、链接,生成time.exe:
编译
运行结果如图所示:
运行结果
可以看到,完美地实现了我们预想的功能,已经达到了系统工具级水准(
项目完成

~~~~~~~~~~~~~~ 本文已结束 感谢您的阅读 ~~~~~~~~~~~~~~