任务二
一、任务内容
要求:
- 在屏幕特定位置显示当前系统日期和时间;
- 显示的日期和时间要求即时更新,即设计为跳动的时钟;
- 可自定义电子钟的显示区域、显示位置、背景和前景色或以图形化方式显示;
- 当按下某个键,如“Q”时,程序返回DOS,数字钟消失。
提高:
再设计一款倒计时,倒计时时间由用户设定(如30分钟),时间到有提示(如时间背景、前景色变化或文本提示等)。
二、设计思路
三、实现代码(该部分代码来自于一个博主文章)
//文件名2.asm
;===========
;数据段data
;===========
data segment
count dw 1
info db 'Current time is:',0dh,0ah,'$' ;提示语
datestr db '00-00-0000',0dh,0ah,'$' ;日期显示信息:month-day-year
timestr db '00:00:00',0dh,0ah,'$' ;时间显示信息:hour:minute:second
data ends
;===========
;代码段code
;===========
code segment
assume cs:code,ds:data
main proc far
start:
mov ax,data
mov ds,ax
;调色、位置(时钟日期显示的位置以及背景色、字体颜色)
mov ah,0
mov al,3
int 10h
mov ah,6
mov al,0 ;al=0 全屏幕为空白
mov bh,00000100b ;背景色0000黑色,0100红色(黑底红字)
mov ch,0 ;左上角行号
mov cl,0 ;左上角列号
mov dh,50h ;右下角行号
mov dl,50h ;右下角列号
int 10h
;保存原中断向量
mov al,1ch
mov ah,35h
int 21h ;获取1ch中断向量到es:bx
push es
push bx
push ds ;保存
;设置新的中断向量
mov dx,offset intpro
mov ax,seg intpro
mov ds,ax
mov al,1ch
mov ah,25h
int 21h ;设置中断向量ds:dx
pop ds
in al,21h ;读中断屏蔽寄存器
and al,11111110b ;开定时器中断
out 21h,al ;写中断屏蔽寄存器
sti ;开中断
;等待中断
s:
mov ah,1
int 21h
cmp al,'q' ;'q'退出
jz quit
jmp s
;恢复1ch原中断向量
quit:
pop dx
pop ds
mov al,1ch
mov ah,25h
int 21h
;调色、位置(跳出时钟效果后的背景色以及字体颜色)
mov ah,00h
mov al,03h
int 10h
mov ah,06h
mov al,00h ;al=0 全屏幕为空白
mov bh,00000111b ;背景色0000黑色,
0111白色(黑底白字)
mov ch,0h ;左上角行号
mov cl,0h ;左上角列号
mov dh,50h ;右下角行号
mov dl,50h ;右下角列号
int 10h
mov ax,4c00h
int 21h
main endp
;------------------------------------------------------
;中断处理intpro
;------------------------------------------------------
intpro proc near
;保护现场
push ds
push ax
push cx
push dx
mov ax,data
mov ds,ax
sti
dec count
jnz exit
;显示提示("current time is:"的显示位置)
mov ah,02h
mov bh,0h ;页号
mov dh,0ah ;行号
mov dl,20h ;列号
int 10h
mov dx,offset info
mov ah,09h
int 21h
call dispdate
call disptime
mov count,18
exit:
cli
pop dx
pop cx
pop ax
pop ds
iret
intpro endp
;----------------------------------------------------------
;显示日期子程序dispdate
;----------------------------------------------------------
dispdate proc near
push ax ;al星期
push cx ;cx年
push dx; ;dh月 dl日
push si
;获取系统日期:入口:ah<-2ah 出口:cx<-year dh<-month dl<-day
mov ah,2ah ;2ah取系统日期
int 21h
mov ax,cx ;年份转4为ASCII码
mov bx,10
lea si,datestr+9 ;指向字符串年份最后一位
mov cx,4 ;年份4位,循环4次
push dx ;保存月日
y:
xor dx,dx ;dx清零,保存余数即个位
div bx ;商存ax,余数存dx
add dl,30h
mov [si],dl
dec si
loop y
pop dx ;还原dx,处理日
mov al,dl
xor ah,ah
div bl ;求日的个位,存ah,十位存al
add ax,3030h ;分别转ASCII码
sub si,2
mov [si],ax ;存日
mov al,dh ;处理月
xor ah,ah
div bl ;求月的个位,存ah,十位存al
add ax,3030h ;分别转ASCII码
sub si,3
mov [si],ax ;存日
;显示提示(月-日-年显示位置)
mov ah,02h
mov bh,0h ;页号
mov dh,0bh ;行号
mov dl,22h ;列号
int 10h
;显示日期字符串
mov dx,offset datestr
mov ah,09h
int 21h
pop si
pop dx
pop cx
pop ax
ret
dispdate endp
;----------------------------------------------------------
;显示时间子程序disptime
;----------------------------------------------------------
disptime proc near
push ax
push cx
push dx
push si
;获取系统时间:入口:ah<-2ch,出口:ch<-hour,cl<-minute,dh<-second
mov ah,2ch
int 21h
mov bl,10
lea si,timestr
mov al,ch ;处理小时
xor ah,ah
div bl
add ax,3030h
mov [si],ax
add si,3
mov al,cl ;处理分
xor ah,ah
div bl
add ax,3030h
mov [si],ax
add si,3
mov al,dh ;处理秒
xor ah,ah
div bl
add ax,3030h
mov [si],ax
;显示提示(时:分:秒显示的位置)
mov ah,02h
mov bh,0h ;页号
mov dh,0ch ;行号
mov dl,23h ;列号
int 10h
;显示时间字符串
mov dx,offset timestr
mov ah,09h
int 21h
pop si
pop dx
pop cx
pop ax
ret
disptime endp
code ends
end start
四、调试结果及分析
分析:任务二不管在代码量上还是在思路上都比任务一难很多。还好老师给出了一部分的代码,不然真的不知道怎么下手。根据老师代码只需要将不断输出的时间改为在同一位置输出跳动。比如秒需要在同一位置跳动,就需要不断清空数据,然后输出数据,不断循环从而达到时钟实时跳动的效果。同时设置时钟显示的位置以及颜色。当输入’q’后,就恢复1ch原中断向量,重新设置跳出时钟效果后的背景色以及字体颜色(00000111黑底白字)。
操作指令:
运行结果:
加分题:
倒计时:设计一款倒计时,倒计时时间由用户设定(如30分钟),时间到有提示(如时间背景、前景色变化或文本提示等)。
设计思路:
代码部分(该部分代码来源于一个博主):
;================
;数据段data
;================
data segment
sinput db 'Please enter hours, minutes, and seconds : $'
soutput db 'Any key to again and ESC to return.Time is : $'
buf db 20h
db 0
db 20h dup(0)
timehour dw 0 ;时
timeminute dw 0 ;分
timesecond dw 0 ;秒
num dw ?
data ends
;===============
;堆栈段stack
;===============
stack segment para stack
dw 30h dup(0)
stack ends
;===============
;代码段CODE
;===============
code segment
assume cs:code,ds:data,ss:stack
start:
mov ax,data
mov ds,ax
mov al,11111101b ;关定时器中断
out 21h,al ;中断屏蔽寄存器数据传回去
push ds ;保存数据段地址
mov ax,seg int08h ;seg取得标号的段地址
mov ds,ax ;获得08h号中断的段地址,放在中断向量中
mov dx,offset int08h ;获得08h号中断的偏移地址,放在中断向量中
mov al,08h ;设置中断号
mov ah,25h ;设置中断向量
int 21h ;调用系统dos中断
pop ds ;恢复数据段地址
mov al,00110110b ;设置通道0的方式3
out 43h,al ;输出控制字,43h是8253定时器芯片的控制寄存器地址
mov ax,11932 ;定时器的时钟频率为1.1931817MHz,计数初值=1193182/100,10ms中断一次
out 40h,al ;40h为计数器地址
mov al,ah ;先输出低位,再输出高位
out 40h,al ;40h为计数器地址
timing:
;换行
mov dl,0dh ;cr
mov ah,2 ;显示一个字符
int 21h ;调用系统dos中断
mov dl,0ah ;lf
mov ah,2 ;显示一个字符
int 21h ;调用系统dos中断
mov ah,09h ;显示字符串
lea dx,sinput ;取段内偏移地址
int 21h ;调用系统dos中断
mov ah,0ah ;键盘输入到缓冲区
lea dx,buf ;取段内偏移地址
int 21h ;调用系统dos中断
call inputtime ;调用子程序处理字符串
mov ah,09h ;显示字符串
lea dx,soutput ;取段内偏移地址
int 21h ;调用系统dos中断
mov dx,100 ;初始化为0
mov al,11111100b ;开键盘和定时器中断
out 21h,al ;中断屏蔽寄存器数据传回去
re:
mov ah,08h ;无回显键盘输入到al
int 21h ;调用系统dos中断
cmp al,27 ;按键是否ESC
je do_esc ;是退出
cmp al,252 ;按键是否是指定字符
je re ;是循环
mov al,11111101b ;关定时器中断
out 21h,al ;中断屏蔽寄存器数据传回去
jmp timing ;不是就重新对时
do_esc:
mov al,11111101b ;关定时器中断
out 21h,al ;中断屏蔽寄存器数据传回去
;退出代码
mov ah,4ch
int 21h
;----------------------
;输入数字inputtime
;----------------------
inputtime proc
;初始化
mov dx,0
mov bx,10
mov si,2
mov num,0
mov ax,0
mov cx,0
lop:
mov al,buf[si] ;寄存器相对寻址,从缓冲区取一个字符
cmp al,0dh ;是否是cr
je final ;等于就跳转,JNE相反
sub al,30h ;减30h,从ASCII码转数字
cmp num,0 ;与0比较,相当于判断初始化
je do_deal ;等于就跳转,JNE相反
push ax ;当前数字压入栈中
mov ax,num ;当前数送入运算寄存器中
mul bx ;隐含寻址,在ax中,相当于num乘以10
mov num,ax ;运算结果存进num中
pop ax ;之前的数据弹出
do_deal:
add num,ax ;加上之前的数据
mov ax,0 ;清零
inc si ;自加1
mov al,buf[si] ;寄存器相对寻址,从缓冲区取一个字符
cmp al,' ' ;是否是空格
je do_space ;等于就跳转,JNE相反
jmp lop ;跳转,处理下一个
do_space:
inc si ;偏移量再自加1,跳过空格
mov dx,num ;把num存起来
mov num,0 ;原先数清零
inc cx ;标志自加1
cmp cx,1 ;与1比较,判断是不是时
je hour ;等于就跳转,JNE相反
cmp cx,2 ;与2比较,判断是不是分
je minute ;等于就跳转,JNE相反
jmp lop ;跳转,处理下一个
hour:
mov timehour,dx ;把时传送到变量
jmp lop ;跳转,处理下一个
minute:
mov timeminute,dx ;把分钟传送到变量
jmp lop ;跳转,处理下一个
final:
mov dx,num ;把num存起来
mov timesecond,dx ;把秒传送到变量
;换行
mov dl,0dh ;cr
mov ah,2 ;显示一个字符
int 21h ;调用系统dos中断
mov dl,0ah ;lf
mov ah,2 ;显示一个字符
int 21h ;调用系统dos中断
ret ;子程序退出重置堆栈
inputtime endp
;-----------------------------
;显示数字num shownum
;-----------------------------
shownum proc
;第一位
mov bl,100 ;%先除100
mov ax,num ;送入运算寄存器
div bl ;除法,ax隐含寻址
add al,30h ;商转换为ASCII码
push ax ;将余数先压入栈中
cmp al,30h ;看一看首位是不是0
je bittwo ;若是,则不显示直接跳转到下一个
mov dl,al ;传送字符
mov ah,2 ;显示一个字符
int 21h ;调用系统dos中断
bittwo:
pop ax ;将余数取出
;第二位
mov al,ah ;送入运算寄存器
mov ah,0 ;高位清零
mov bl,10 ;%再除以10
div bl ;除法,ax隐含寻址
add al,30h ;商转换为ASCII码
push ax ;将余数先压入栈中
mov dl,al ;传送字符
mov ah,2 ;显示一个字符
int 21h ;调用系统dos中断
pop ax ;将余数取出
;第三位
add ah,30h ;余数转换为ASCII码
mov dl,ah ;传送字符
mov ah,2 ;显示一个字符
int 21h ;调用系统dos中断
ret ;子程序退出重置堆栈
shownum endp
;-------------------------
;08h计数器中断 int08h
;------------------------
int08h proc near
;保护现场
push ax
push bx
push cx
cmp dx,100 ;计数达到100,执行一次,相当于1s一次
jge count ;大于等于100就跳转
jmp endint ;没达到就结束中断响应
count:
mov dx,0 ;计数值清零
push dx ;保存计数值
mov ah,03h ;获取光标位置信息
int 10h ;BIOS中断
push dx ;将获得的信息压入栈中
mov ax,timehour ;传送指令
mov num,ax ;传送指令
call shownum ;调用子程序显示数字
mov dl,':'
mov ah,2 ;显示一个字符
int 21h ;调用系统dos中断
mov ax,timeminute ;传送指令
mov num,ax ;传送指令
call shownum ;调用子程序显示数字
mov dl,':'
mov ah,2 ;显示一个字符
int 21h ;调用系统dos中断
mov ax,timesecond ;传送指令
mov num,ax ;传送指令
dec ax ;秒数自减1
mov timesecond,ax ;传送指令
call shownum ;调用子程序显示数字
pop dx ;将之前的光标信息从栈中取出
mov bh,0 ;显示到第一页
mov ah,02h ;设置光标位置
int 10h ;BIOS中断
pop dx
;进位处理
cmp timesecond,0 ;小于等于0,退一位
jle secondcarry ;秒数
jmp endint ;中断返回
secondcarry:
mov timesecond,59 ;秒数得到借位
dec timeminute ;分钟自减1
cmp timeminute,0 ;小于等于0,退一位
jle minutecarry ;分钟退位
jmp endint ;中断返回
minutecarry:
mov timeminute,59 ;分钟得到借位
dec timehour ;小时自减1
cmp timehour,0 ;小于等于0,清零退出
jle hourcarry ;清零退出
jmp endint ;中断返回
hourcarry:
mov timehour,0 ;清零
jmp endint ;中断返回
endint:
;中断返回
mov al,20h
out 20h,al ;发送中断结束指令
;恢复现场
pop ax
pop bx
pop cx
inc dx ;计数加一
iret ;中断返回
int08h endp
code ends
end start
运行结果:
分析:在网上找了很多关于计时器的代码,一个个试,终于找到了一个可以运行成功的计时器,然后通过对代码的理解(有些代码还是不能理解)。之后我直接在计时器代码上修改,只改变了计算时间部分的代码实现了递减的效果。