汇编语言程序:时钟显示实验

一、实验目的

  1. 熟悉系统功能调用INT 21H的有关功能。
  2. 编写时钟程序

二、实验任务

执行时钟程序时,屏幕上显示提示符“:” ,由键盘输入当前时、分和秒值,即 XX:XX:XX↙,随即显示时间,并不停地计时。
当有键按下时,立即停止计时,返回 DOS。

三、实验原理

首先利用系统调用 INT 21H 中 02H 功能,显示一个提示符“:”,要求用户从键盘输入时钟初值(即当前时间),其输入格式为 XX(时):XX(分):XX(秒) ↙(回车)。然后利用 0AH 功能调用接收从键盘输入的字符串,并将接收的字符串存入到缓冲区。
在利用 0AH 功能调用前要设置一个缓冲区,在调用时,用 DX 作为输入缓冲区的指针,由键盘输入的字符存入该缓冲区,直至遇到回车键为止。
程序中把输入的‘时’、‘分’、‘秒’初值分别从输入缓冲区中取出,各自放在一个寄存器中,然后调用一个延时 1 秒钟的子程序,每过 1 秒使秒值增1,然后检查是否已为 60 秒,若不是则转显示;若是,则使秒值为 0,分值增 1,再检查是否已为 60 分,若不是则转显示,若是,则使分值为 0,时值增 1,接着检查时值是否为 24 小时,若不是则转显示,若是,则使时值为 0,接着也是转显示。
只要有键按下,则程序停止运行,返回 DOS。

四、实验内容

最近在学习微机原理,同时就要学习有关汇编的相关知识,上面的这些内容是我从学校的实验任务书上直接复制粘贴下来的。这次我们需要做一个时钟实验的汇编程序,虽然实验任务书上有答案,但是很可惜答案是错的,所以我就耐着性子将所给的答案看了一遍,并改正了程序,最后使程序得以正常运行。
先放程序运行时的图片:

程序运行图

程序执行时,按下键盘可以发现计时终止。

放代码之前让我们先看一下这段程序的流程图

程序流程图

根据流程图可以编写以下程序

STACK SEGMENT
STACK ENDS

DATA SEGMENT
BUF DB 11					; BUF即为缓存区
	DB ?
	DB 10 DUP(?)

DATA ENDS

EXTRA SEGMENT
EXTRA ENDS

CODE SEGMENT
	ASSUME CS:CODE,DS:DATA,ES:EXTRA,SS:STACK
START:
	MOV AX,DATA
	MOV DS,AX
	MOV DL, ':'				; 显示:
	MOV AH,2
	INT 21H
	MOV DX,OFFSET BUF		; 以DX为缓存区指针
	MOV AH, 0AH				; 接受用户输入的时间
	INT 21H
	MOV BX,OFFSET BUF+2		; 将用户输入的时间的首地址赋给BX
	MOV AL,[BX]				; 
	AND AL,0FH				; AL的高四位置0,0-9的ASCI为30H-39H,高四位置0后,AL中的值即为一般所使用的“数字”,如31H会变成01H,即变成非压缩BCD码
	MOV [BX],AL				;
	INC BX					
	MOV AL,[BX]
	AND AL,0FH
	MOV [BX],AL
	INC BX					; 两次INC BX 是为了跳过 :
	INC BX
	MOV AL,[BX]
	AND AL,0FH
	MOV [BX],AL
	INC BX
	MOV AL,[BX]
	AND AL,0FH
	MOV [BX],AL
	INC BX
	INC BX
	MOV AL,[BX]
	AND AL,0FH
	MOV [BX],AL
	INC BX
	MOV AL,[BX]
	AND AL,0FH
	MOV [BX],AL
	MOV BX,OFFSET BUF+2
	CALL TOBCD				; 跳转至TOBCD
	MOV CH,AL				; 将AL中的值存入CH中,此处AL中的值为小时
	ADD  BX,3 					
	CALL TOBCD
	MOV  DH,AL				; 将AL中的值存入DH中,此处AL中的值为分钟
	ADD BX,3
	CALL TOBCD				
	MOV DL,AL				; 将AL中的值存入DL中,此处AL中的值为秒
	AGAIN: CALL DELAY		; 进入延时程序
	MOV AL, DL				; 将秒移入AL中
	ADD AL,1				; 秒+1
	DAA						; 调整
	CMP AL,60H
	JNE SECOND				; 秒不满60跳转
	MOV DL,0    			; 当秒达到60时,将秒数置零
	MOV AL,DH					
	ADD AL,1				; 分钟+1
	DAA						; 调整
	CMP AL, 60H
	JNE MINUTE				; 分钟不满60跳转
	MOV DH, 0				; 分钟满60,将分钟置零
	MOV AL, CH
	ADD AL, 1
	DAA
	CMP AL, 24H
	JNE HOUR				; 小时不满24跳转
	MOV CH, 0				; 将小时置零
	JMP DISPLAY				; 如果不需要JMP SECOND/MINUTE/HOUR 则跳转DISPLAY
	SECOND: MOV DL,AL 		; 将AL中的值存入DL(秒)中,此处AL中的值为+1后的秒
	JMP DISPLAY
	MINUTE: MOV DH, AL		; DH(分)
	JMP DISPLAY
	HOUR: MOV CH, AL		; CH(小时)
	JMP DISPLAY 
	DISPLAY: MOV BX,OFFSET BUF	; 显示程序,用于将目前所存储的时/分/秒显示
	MOV  AL,0DH				; 添加回车
	MOV [BX],AL
	INC  BX 
	MOV  AL,0AH				;添加换行
	MOV [BX],AL
	INC  BX
	MOV  AL,CH				; 将CH中的小时值移入AL中
	CALL TRAN
	INC  BX
	MOV AL, ':'
	MOV [BX],AL
	INC BX
	MOV AL,DH				; 将DH中的分钟值移入AL中
	CALL TRAN
	INC BX
	MOV AL,':'
	MOV  [BX], AL
	INC  BX	
	MOV AL,DL				; 将DL中的秒值移入AL中
	CALL TRAN
	INC  BX
	MOV  AL,'$'				
	MOV [BX], AL			; 以'$'结尾
	PUSH BX					; 入栈保护
	PUSH CX
	PUSH DX
	MOV  DX, OFFSET BUF
	MOV  AH,9
	INT  21H
	MOV  AH,06			; 判断是否有键落下 
	MOV  DL,  0FFH
	INT  21H
	POP DX
	POP CX
	POP BX
	JNZ BREAK				; 若有键落下 跳转终止程序
	JMP AGAIN			; 无键落下,继续程序
	BREAK: MOV AH,4CH		; 终止程序,返回DOS
	INT 21H
	TOBCD PROC			; TOBCD BEGIN
	MOV AL,[BX]			; 该段程序是为了将DS段中的连续两个非压缩BCD码转化为一个压缩BCD码
	SHL AL,1			; SHL AL, 4 可以将[BX]中的值转到高四位
	SHL AL,1
	SHL AL,1
	SHL AL,1
	OR AL,[BX+1]		; 将[BX+1]中的值转到低四位,
	RET					; 这段程序的目的即将用户输入的秒/分钟/小时变成压缩BCD码存入AL中
	TOBCD ENDP			; TOBCD END
	TRAN  PROC			; 将压缩BCD码转换成ASCⅡ码并存入存储器,即将用压缩BCD码存储的秒/分/时转成ASCⅡ码的形式
	MOV CL,AL
	SHR AL,1
	SHR AL,1
	SHR AL,1
	SHR AL,1
	OR  AL,30H
	MOV [BX],AL
	INC BX
	MOV AL,CL
	AND AL,0FH
	OR AL,30H
	MOV [BX],AL
	RET   
	TRAN ENDP			; TRAN程序段结束
	DELAY PROC			; 延时程序段Begin
	PUSH CX
	PUSH AX
	PUSH BX
	MOV BX, 2FH			; 请注意不同电脑的主频的不同,所以给BX的值也会不相同,请根据实际情况进行修改
	CIR: MOV CX,0FFFFH
	GOON: LOOP GOON
	dec BX
	JNZ CIR
	POP BX
	POP AX
	POP CX
	RET
	DELAY ENDP 			;延时程序段END
CODE ENDS
	END START

上述程序虽然较长,但是和流程图结合来看的话,如果有一定汇编语言基础的话相信时比较容易理解的。

五、改进

1. 取消换行输出,直接在原位置输出时间

这个其实很简单,先看效果图:

在这里插入图片描述

需要改动的就是原本的DISPLAY程序段的内容

DISPLAY: MOV BX,OFFSET BUF	; 显示程序,用于将目前所存储的时/分/秒显示
	MOV  AL,0DH				; 添加回车
	MOV [BX],AL
	INC  BX 
	MOV [BX],AL
	INC  BX
	MOV  AL,CH				; 将CH中的小时值移入AL中
	CALL TRAN
	INC  BX
	MOV AL, ':'
	MOV [BX],AL
	INC BX
	MOV AL,DH				; 将DH中的分钟值移入AL中
	CALL TRAN
	INC BX
	MOV AL,':'
	MOV  [BX], AL
	INC  BX	
	MOV AL,DL				; 将DL中的秒值移入AL中
	CALL TRAN
	INC  BX
	MOV  AL,'$'				
	MOV [BX], AL			; 以'$'结尾

只需要删除原本DISPALY程序段中的MOV AL,0AH ;添加换行 即可。

2. 提供错误提示

这个稍微有点复杂,目前只提供两种错误的提示,即输入不是数字或是”:”的错误,还有输入时间值过大的错误如输入了24:60:60, 还是先看效果图:
在这里插入图片描述
从效果图可以看出当输入的数字过大时会出现TIME ERROR的错误,当输入其他字符时,会出现Character ERROR 的错误,当然直接按下回车键,也会被判定为Character ERROR

为了实现效果图所展现的功能,首先需要在DATA SEGMENT中添加以下语句

ERRORINFO1 DB 'Character ERROR', 0DH, 0AH, '$'
ERRORINFO2 DB 'TIME ERROR', 0DH, 0AH,'$'

在代码段中语句应该修改为

CODE SEGMENT
	ASSUME CS:CODE,DS:DATA,ES:EXTRA,SS:STACK
START:
	MOV AX,DATA
	MOV DS,AX
	MOV DL, ':'
	MOV AH,2
	INT 21H
	MOV DX,OFFSET BUF
	MOV AH, 0AH
	INT 21H
	JMP ERRCH				; 当用户输入时间后,进行检验,判断是否有错误的字符
	ERRCH: LEA BX, BUF+2	
	PUSH CX
	MOV CL, 8
	MOV AH,0
	CIRCLE:MOV AL, [BX]		;循环判断每一个字符
	CALL JUDGECH			; 存在错误AH置1
	INC BX
	LOOP CIRCLE
	CMP AH, 1
	JE CHERR				; AH=1 即出错,需打印错误信息,并重新输入
	MOV BX,OFFSET BUF+2
	MOV AL,[BX]
	AND AL,0FH
	MOV [BX],AL
	INC BX
	MOV AL,[BX]
	AND AL,0FH
	MOV [BX],AL
	INC BX
	INC BX
	MOV AL,[BX]
	AND AL,0FH
	MOV [BX],AL
	INC BX
	MOV AL,[BX]
	AND AL,0FH
	MOV [BX],AL
	INC BX
	INC BX
	MOV AL,[BX]
	AND AL,0FH
	MOV [BX],AL
	INC BX
	MOV AL,[BX]
	AND AL,0FH
	MOV [BX],AL
	MOV BX,OFFSET BUF+2
	CALL TOBCD				; 将数字转化为BCD码
	MOV CH,AL				; 将AL中的值存入CH中,此处AL中的值为小时
	ADD  BX,3 					
	CALL TOBCD
	MOV  DH,AL				; 将AL中的值存入DH中,此处AL中的值为分钟
	ADD BX,3
	CALL TOBCD				
	MOV DL,AL				; 将AL中的值存入DL中,此处AL中的值为秒
	MOV AH, 0
	CALL JUDGEOVER			; 进入判断时间值是否错误的程序
	CMP AH,1				; 同样当出错时,AH置1
	JE TIMEERR
	AGAIN: CALL DELAY		; 进入延时程序
	MOV AL, DL				; 将秒移入AL中
	ADD AL,1				; 秒+1
	DAA						; 调整
	CMP AL,60H
	JNE SECOND
	MOV DL,0    			; 当秒达到60时,将秒数置零
	MOV AL,DH					
	ADD AL,1				; 分钟+1
	DAA						; 调整
	CMP AL, 60H
	JNE MINUTE
	MOV DH, 0				; 将分钟置零
	MOV AL, CH
	ADD AL, 1
	DAA
	CMP AL, 24H
	JNE HOUR				
	MOV CH, 0				; 将小时置零
	JMP DISPLAY				; 如果不需要JMP SECOND/MINUTE/HOUR 则跳转DISPLAY
	CHERR: lea dx, ERRORINFO1
	mov ah,9
	int 21h
	jmp start
	TIMEERR: LEA DX, ERRORINFO2
	MOV AH,9
	INT 21H
	JMP START
	SECOND: MOV DL,AL 		; 将AL中的值存入DL中,此处AL中的值为+1后的秒
	JMP DISPLAY
	MINUTE: MOV DH, AL
	JMP DISPLAY
	HOUR: MOV CH, AL
	JMP DISPLAY 
	DISPLAY: MOV BX,OFFSET BUF
	MOV  AL,0DH				; 添加回车
	MOV [BX],AL
	INC BX
	MOV AL, ' '
	MOV [BX], AL
	INC  BX
	MOV  AL,CH
	CALL TRAN
	INC  BX
	MOV AL, ':'
	MOV [BX],AL
	INC BX
	MOV AL,DH
	CALL TRAN
	INC BX
	MOV AL,':'
	MOV  [BX], AL
	INC  BX	
	MOV AL,DL
	CALL TRAN
	INC  BX
	MOV  AL,'$'
	MOV [BX], AL
	PUSH BX
	PUSH CX
	PUSH DX
	MOV  DX, OFFSET BUF
	MOV  AH,9
	INT  21H
	MOV  AH,06
	MOV  DL,  0FFH
	INT  21H
	POP DX
	POP CX
	POP BX
	JNZ BREAK				; 跳转终止程序
	JMP AGAIN			;
	BREAK: MOV AH,4CH		; 返回DOS
	INT 21H
	TOBCD PROC			; TOBCD BEGIN
	MOV AL,[BX]			; 该段程序是为了将DS段中的连续两个非压缩BCD码转化为一个压缩BCD码
	SHL AL,1			; SHL AL, 4 可以将[BX]中的值转到高四位
	SHL AL,1
	SHL AL,1
	SHL AL,1
	OR AL,[BX+1]		; 将[BX+1]中的值转到低四位
	RET					
	TOBCD ENDP			; TOBCD END
	TRAN  PROC			; 将压缩BCD码转换成ASCⅡ码并存入存储器,即将用压缩BCD码存储的秒/分/时转成ASCⅡ码的形式
	MOV CL,AL
	SHR AL,1
	SHR AL,1
	SHR AL,1
	SHR AL,1
	OR  AL,30H
	MOV [BX],AL
	INC BX
	MOV AL,CL
	AND AL,0FH
	OR AL,30H
	MOV [BX],AL
	RET   
	TRAN ENDP
	DELAY PROC			; 延时程序段Begin
	PUSH CX
	PUSH AX
	PUSH BX
	MOV BX, 2FH
	CIR: MOV CX,0FFFFH
	GOON: LOOP GOON
	dec BX
	JNZ CIR
	POP BX
	POP AX
	POP CX
	RET
	DELAY ENDP 				;延时程序段END
	JUDGECH PROC			; 判断是否出现字符错误
	CMP AL, 30H
	JB lower				; 小于30H跳转
	CMP AL, 39H
	JA higher				; 大于39H跳转
	RET
	lower: CMP AL, ':'
	JE EQUAL				; 等于':'跳转
	MOV AH, 1
	RET
	higher:CMP AL, ':'
	JE EQUAL
	MOV AH,1
	RET
	EQUAL: RET
	JUDGECH ENDP
	JUDGEOVER PROC			; 判断时间值是否出错
	CMP CH, 24H
	JAE AE
	CMP DH, 60H
	JAE AE
	CMP DL, 60H
	JAE AE
	RET
	AE:MOV AH,1				; 任何一个时间值出错,都将跳转至AE处
	RET
	JUDGEOVER ENDP
CODE ENDS
	END START

可以看出与原本的程序相比,增加了一些关于错误判断的程序。

3. 利用DOS系统功能实现延时一秒

主要利用DOS系统中的读取时间功能(INT 21H)实现延时

中断类型号(AH)功能  调用参数返回参数
2CH读取时间(CX:DX)=时间
CH=小时(0-23), CL = 分(0-59)
DH=秒(0-59), CL = 百分秒(0-99)

延时程序修改如下:

DELAY PROC			; 延时程序段Begin
	PUSH AX
	PUSH BX
	PUSH CX
	PUSH DX
	MOV AH, 2CH
	int 21h
	ADD DH, 1H
	CMP DH, 3CH			; 与60比较,如果不等于60,可以进行下一步
	JNE NOTEQU 		
	mov dh, 00h			; 如果相等需要将DH置0	
	NOTEQU: MOV BL, DH	; 无论是否相等都会执行的程序可以写在跳转的语句中。
	COMPARETIME: MOV AH, 2CH
	INT 21H
	CMP BL, DH
	JNE COMPARETIME
	pop dx
	pop cx
	pop bx
	pop ax
	RET
	DELAY ENDP 			;延时程序段END 

六、结语

用汇编语言编写程序时,存在很多比较麻烦的地方,如在判断时,需要分成多步进行操作等。但是在汇编中直接对数据进行处理的感觉还是不同于高级语言,你能够较清楚地了解数据在哪里,如何进行使用。

类似文章