# 问题描述
利用汇编语言实现一个可以在显示器上显示时、分、秒的电子时钟,并能提供设置闹钟时间、选择闹钟铃声功能。
基本要求:
- 设计一个基本的具有显示时、分、秒的电子时钟。
- 设置闹钟时间,到点响铃
- 选择闹钟铃声,本实验提供了两种铃声供选择
- 程序运行良好、界面清晰。
# 数据结构
本程序分为五大模块:界面显示模块、时间实时显示模块、闹钟响铃模块、闹钟设置模块、闹铃选择模块。
数据结构:
字符串:显示界面、获取用户输入的闹钟时间和铃声选择。
变量:
```c++
INT_BUFF DB 40 ;
输入的时间字段
DB ?
DB 40 DUP(?)
OUT_BUFF DB '00:00:00','$'
IPTIMEH DB ? ;
输入的时
IPTIMEF DB ? ;
输入的分
IPTIMEM DB ? ;
输入胡秒
OPTIMEH DB ? ;
当前的时
OPTIMEF DB ? ;
当前的分
OPTIMEM DB ? ;
当前胡秒
MUSIC_CHOOSE DB 1;
选择的闹铃类型
```
宏定义:带有两个参数,音符频率和持续时间
```c++
PLAY MACRO A,B ;
播放音乐
PUSH SI
PUSH BP
LEA SI,A
LEA BP,DS:
B
CALL MUS_PLAYER
POP BP
POP SI
ENDM
```
寄存器:寄存器子程序传参
流程图:
![](https://www.writebug.com/myres/static/uploads/2022/1/28/32852f01f686619b36984575532ffec3.writebug)
# 算法描述
主函数
```c++
DISPLAY:
;
时间显示部分
MOV AX,DATAS
MOV DS,AX
;
获取当前时间
CALL GETTIME
;
置光标位置 DH DL分别为行号和列号
MOV AH,2 ;
显示mess1
MOV BH,0
MOV DH,0
MOV DL,0
INT 10H
LEA DX,MESS1
MOV AH,9
INT 21H
;
置光标位置
MOV AH,2 ;
显示mess2
MOV BH,0
MOV DH,9
MOV DL,12
INT 10H
LEA DX,MESS2
MOV AH,9
INT 21H
CALL DELAY2
MOV AH,01H ;
调用键盘I/O中断功能号1,获取键盘输入值到AL
INT 16H
CMP AL,'E';
是E键,转到闹钟设置子程序
JNE NEXT3
CALL EDITTIME
NEXT3:
CMP AL,'C';
是C键,转到音乐选择子程序
JNE NEXT4
CALL EDITMUSIC
NEXT4:
CMP AL,1BH ;
是Esc键,退出程序
JZ QUIT
MOV AX,0
JMP DISPLAY ;
循环
QUIT:
MOV AH,4CH
INT 21H
RET
```
时间实时显示模块
获取当前时间子程序 GETTIME
```c++
MOV AH,2CH ;
时间调用功能号2CH,时CH 分CL 秒DH 微妙DL
INT 21H
ADD DL,1 ;
修正1秒
CMP DL,60
JNE NEXT1
ADD CL,1
MOV DL,0
NEXT1:
MOV OPTIMEH,CH;
保存当前时间
MOV OPTIMEF,CL
MOV OPTIMEM,DH
LEA SI,OUT_BUFF;
设置时
MOV AL,CH
CBW
CALL DEC_DIV ;
二进制-》十进制
INC SI;
设置分
MOV AL,CL
CBW
CALL DEC_DIV ;
二进制-》十进制
INC SI;
设置秒
MOV AL,DH
CBW
CALL DEC_DIV ;
二进制-》十进制
;
置光标位置
MOV AH,2
MOV BH,0
MOV DH,9 ;
DH DL分别为行号和列号
MOV DL,25
INT 10H
LEA DX,OUT_BUFF ;
显示时间
MOV AH,09H
INT 21H
;
检查是否到预定时间
CALL CHECK
```
二进制转化成十进制子程序 DEC_DIV
```c++
MOV CX,10
MOV DX,0
DIV CX
PUSH DX
MOV DX,0
DIV CX
PUSH DX
POP AX
ADD AL,30H
MOV [SI],AL ;
设置值
INC SI
POP AX
ADD AL,30H
MOV [SI],AL ;
设置值
INC SI
```
检查是否到预定时间子程序 CHECK
```c++
MOV AL,OPTIMEH ;
时
CMP IPTIMEH,AL
JNE EXIT
MOV AL,OPTIMEF ;
分
CMP IPTIMEF,AL
JNE EXIT
MOV AL,OPTIMEM ;
秒
CMP IPTIMEM,AL
JNE EXIT
;
打开扬声器,播放音乐
CALL MUSIC
```
选择播放的闹铃类型子程序 MUSIC
```c++
MOV AL,MUSIC_CHOOSE
A_:
CMP AL,1
JNZ B_
PLAY MUS_FREQ_1,MUS_TIME_1
JMP EXIT1
B_:
CMP AL,2
JNZ EXIT1
PLAY MUS_FREQ_2,MUS_TIME_2
EXIT1:
```
闹钟响铃模块
音乐数据:
```c++
;
太湖船
MUS_FREQ_1 DW 330, 392, 330, 294, 330, 392, 330, 294, 330 ;
音符频率
DW 330, 392, 330, 294, 262, 294, 330, 392, 294
DW 262, 262, 220, 196, 196, 220, 262, 294, 330, 262
DW -1
MUS_TIME_1 DW 3 DUP(50), 25, 25, 50, 25, 25, 100 ;
音符持续时间
DW 2 DUP(50, 50, 25, 25), 100
DW 3 DUP(50, 25, 25), 100
;
两只老虎
MUS_FREQ_2 DW 262,294,330,262,262,294,330,262
DW 330,349,392,330,349,392,392,440
DW 392,349,330,262,392,440,392,349
DW 330,262,294,196,262,294,196,262,-1
MUS_TIME_2 DW 25,25,25,25,25,25,25,25,25,25
DW 50,25,25,50,12,12,12,12,25,25
DW 12,12,12,12,25,25,25,25,50,25,25,50
```
获取音符子程序 MUS_PLAYER
```c++
MOV AX,0
FREQ:
MOV DI, [SI] ;
表中音符频率
CMP DI, 0FFFFH
JE END_MUS ;
-1为结束标志
MOV BX,DS:
[BP] ;
取出音符持续时间
CALL SOUNDF
ADD SI,2
ADD BP,2
JMP FREQ
END_MUS:
RET
```
发出声音子程序 SOUNDF
```c++
MOV AL,0B6H ;
10110110B 对定时器2进行初始化
OUT 43H,AL ;
8253/54 43h 端口
MOV DX,12H ;
ax得到送往定时器2的计数值
MOV AX,348CH ;
(12, 348ch)/di = 1193100hz/freq
DIV DI ;
余数在dx中 ax为商
OUT 42H,AL ;
输出计数值到42h端口
MOV AL,AH
OUT 42H,AL
IN AL,61H ;
读入61h端口设置到al
MOV AH,AL ;
移入高八位
OR AL,3 ;
输出到61h 0,1两位置1 发声
OUT 61H,AL ;
out al->61h
;
延时 硬件
WAIT1:
MOV CX,3314 ;
装入 15.08微妙的倍数
CALL WAITF
DELAY1:
DEC BX
JNZ WAIT1
MOV AL,AH
OUT 61H,AL
RET
```
延时子程序 WAITF
```c++
PUSH AX
WAITF1:
IN AL,61H ;
61h PB4
AND AL,10H
CMP AL,AH ;
是否改变
JE WAITF1
MOV AH,AL
LOOP WAITF1 ;
until cx=0
POP AX
RET
```
闹钟设置模块
修改闹钟时间子程序 EDITTIME
```c++
LEA DX,TN;
输出输入提示
MOV AH,09H
INT 21H
LEA DX,INT_BUFF;
获取输入时间
MOV AH,0AH
INT 21H
;
置光标位置
MOV AH,2
MOV BH,0
MOV DH,13 ;
DH DL分别为行号和列号
MOV DL,14
INT 10H
LEA DX,SUCCESS;
输出选择成功提示
MOV AH,9
INT 21H
MOV DX,0
MOV AX,0
LEA SI,INT_BUFF;
获取输入的时 十进制-》二进制
ADD SI,3
MOV DH,[SI]
SUB DH,30H
INC SI
MOV DL,[SI]
SUB DL,30H
MOV CL,10
MOV AL,DH
MUL CL
ADD AL,DL
MOV IPTIMEH,AL
ADD SI,2
MOV DH,[SI] ;
获取输入的分 十进制-》二进制
SUB DH,30H
INC SI
MOV DL,[SI]
SUB DL,30H
MOV CL,10
MOV AL,DH
MUL CL
ADD AL,DL
MOV IPTIMEF,AL
ADD SI,2
MOV DH,[SI] ;
获取输入的秒 十进制-》二进制
SUB DH,30H
INC SI
MOV DL,[SI]
SUB DL,30H
MOV CL,10
MOV AL,DH
MUL CL
ADD AL,DL
MOV IPTIMEM,AL
```
闹铃选择模块
选择音乐子程序 EDITMUSIC
```c++
;
置光标位置
MOV AH,2
MOV BH,0
MOV DH,11 ;
DH DL分别为行号和列号
MOV DL,0
INT 10H
LEA DX,MUSICMESS;
输出选择音乐提示
MOV AH,09H
INT 21H
LEA DX,INT_BUFF
MOV AH,0AH;
获取音乐输入
INT 21H
LEA SI,INT_BUFF
ADD SI,3
MOV DL,[SI]
SUB DL,30H
MOV MUSIC_CHOOSE,DL
;
置光标位置
MOV AH,2
MOV BH,0
MOV DH,13 ;
DH DL分别为行号和列号
MOV DL,14
INT 10H
LEA DX,SUCCESS;
输出选择成功提示
MOV AH,9
INT 21H
```
# 效果与测试情况
## 运行界面
只能输入大写 C、大写 E 和 Esc 键,否则键盘监听事件失效
![](https://www.writebug.com/myres/static/uploads/2022/1/28/073395622e81388da2d0c132cc901962.writebug)
## 测试结果
输入大 C,回车,会出现”PLEASE CHOOSE THE TYPE OF MUSIC:1(Boat in Taihu Lake) 2(Two Tigers)”提示,若输入 1,回车,选择第一首哥作为闹铃:若输入 2,回车,选择第二首歌作为闹铃,选择后会提示“CHOOSE SUCCESSFUL!”。
![](https://www.writebug.com/myres/static/uploads/2022/1/28/6b40249ee4f76eeffe27a92dc58f795d.writebug)
![](https://www.writebug.com/myres/static/uploads/2022/1/28/680b9f656013a438adc83a3e32974e93.writebug)
输入大 E,回车,会出现”PLEASE INPUT THE NEW TIME(HH:MM:SS)”提示,按格式输入时间:xx:xx:xx,选择后会提示“CHOOSE SUCCESSFUL!”,到设定的时间会响铃。
![](https://www.writebug.com/myres/static/uploads/2022/1/28/b85d5047746f60156dd39244f25c15b9.writebug)
输入 Esc 键,结束程序
![](https://www.writebug.com/myres/static/uploads/2022/1/28/1db6baf13a13f6c18be37363ef46e68f.writebug)
# 分析与讨论
本次实验设计中遇到了很多问题,在每一个模块都有遇到了一定的难度,每一个子程序之间的联系非常复杂。
首先,如何获取当前系统的时间,查阅了资料才知道 INT 21H 的 2CH 功能号可以获取系统的时间,但是返回的值是二进制,要转化成十进制输出。由于系统使