项目情景
编程,以“年/月/日 时:分:秒”的格式,显示当前的日期、时间。
注意:CMOS RAM中存储着系统的配置信息,除了保存时间信息的单元外,不要向其他的单元中写入内容,否则将引起一些系统错误。
分析
在Linux系统上对应的命令date
的效果如图所示:
而我们需要实现的就是和上图差不多的效果。
PC机中存在一个被称为CMOS RAM(下简称CMOS)的部件,包含一个实时钟与一个128字节的RAM,我们所需要的系统时间就存储在其中。CMOS有两个端口,端口地址为70h
和71h
。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
4text segment
db "yy/mm/dd hh:mm:ss","$"
; 格式化:年/月/日 时:分:秒
text ends
定义栈段stack
,用于暂存寄存器:1
2
3stack segment
db 16 dup (0)
stack ends
定义代码段code
和程序起始点start
:1
2
3
4
5code segment
start:
......
code ends
end start
初始化段寄存器,ds
指向text
段:1
2mov ax,text
mov ds,ax
清屏:1
2
3
4
5
6
7
8mov 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
29read_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 10h
和int 21h
提供的中断例程打印字符串:1
2
3
4
5
6
7
8
9mov 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
5mov ah,2 ; 设置光标
mov bh,0 ; 页
mov dh,24 ; 行
mov dl,0 ; 列
int 10h
最后返回:1
2mov 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
95assume 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
:
运行结果如图所示:
可以看到,完美地实现了我们预想的功能,已经达到了系统工具级水准(
项目完成