📄 000.asm
字号:
.model small
.stack
.data
HOUR db ?
MIN db ?
SEC db ?
MSEC db ?
NHOUR db 0 ;设置时间使用
NMIN db 0 ;设置时间使用
NSEC db 0 ;设置时间使用
RHOUR db 1 ;设置闹铃时间使用
RMIN db 1 ;设置闹铃时间使用
RSEC db 1 ;设置闹铃时间使用
FUNKEY db ? ;功能设置使用
keySw db 00h ;是否按键开关
RingSw db 00h ;闹铃开关
SWITCH db 0FFh ;刷新开关,真,则不断在定时器中断函数中刷新,否则不刷新
n db 0dh,0ah,'$'
count dw 1
sMsg db '**** welcome to my samall clock! ****',0dh,0ah,'$'
qMsg db '**** Input q to quit The clock ****',0dh,0ah
db ' **** Input s to set new Time ****',0dh,0ah
db ' **** Input r to set ringTime ****',0dh,0ah,'$'
setTMsg db 'Please Input new Time(HH:MM:SS)',0dh,0ah,'$'
setRMsg db 'Please Input Ring Time(HH:MM)',0dh,0ah,'$'
FamoussayingMsg db '---------------------------------------',0dh,0ah
db 'Reasonable rrangement time, ',0dh,0ah
db 'be equal to economy time.--Bacon',0dh,0ah
db 'Give up the person of time, ',0dh,0ah
db 'time also gives him up.--Shakespeare',0dh,0ah
db '---------------------------------------',0dh,0ah,'$'
.code
PUSHA MACRO
PUSH DS
PUSH AX
PUSH BX
PUSH CX
PUSH DX
ENDM
POPA MACRO
POP DX
POP CX
POP BX
POP AX
POP DS
ENDM
main proc far ;主程序
start:
mov ax,@data ;分配数据节
mov ds,ax
push ds ;存储ds
call GETTIME ;取得系统时间
call initUI ;初始化界面
mov al,1ch ;al<=矢量编号
mov ah,35h ;中断
int 21h ;回到 DOS
push es ;为复位的救援寄存器 (旧的基础住址)
push bx ;中断 1CH 的救援抵销
push ds
;固定的新中断矢量
mov dx,offset tUpdate ;dx<=程序流环的抵销
mov ax,seg tUpdate ;ax<=程序流环的节
mov ds,ax ;ds<=ax
mov al,1ch ;al<=vector#
mov ah,25h ;设定中断矢量
int 21h ;call DOS
pop ds ;出栈 ds
in al, 21h
and al, 11111100b
out 21h,al
sti
delay:
PUSHA
cmp ds:[keySw],0FFh ;检查是否有按键
jz exitdelay ;退出delay 如果有按键
ringP: ;闹铃判断 ;mov dl,ds:[MIN]
mov dl,ds:[RHOUR] ;判断闹铃时间(小时)
cmp dl,ds:[HOUR]
jne noRing
mov bl,ds:[RMIN] ;判断闹铃时间(分钟)
cmp bl,ds:[MIN]
jne noRing
mov dh,ds:[RingSw] ;检查闹铃开关是否打开.(只要调用SETRTIME就会打开闹铃开关)
cmp dh,00h
jz noRing
call GENSOUND ;调用 GENSOUND 来响铃
noRing:
POPA
jmp delay
exitdelay:
POPA
;回复旧的中断矢量
pop dx ;回复寄存器 (复位旧的抵销)
pop ds ;回复中断的旧的基础
mov al, 1ch ;al<=vector#
mov ah, 25h ;回复中断
int 21h ;返回 DOS
pop ds ; ds 出栈
;读敲入的按键
mov ah,07h
int 21h
cmp al,'s'
jne next1
;判断读入字符然后进行相应处理,此时中断函数已经取消,只是单线程
setT:
mov ds:[FUNKEY],al
call SETTIME ;调用子函数设置时间
mov ds:[keySw],00h
jmp start
next1:
cmp al,'r'
jnz next2
mov ds:[FUNKEY],al
call SETRING;调用字函数设置闹铃时间
mov ds:[keySw],00h
jmp start
next2: cmp al,'q'
je endmain
mov ds:[keySw],00h
jmp start
endmain:
mov ax,4c00h ;exit
int 21h
main endp
;定时中断程序()
;Introduction:
;在系统定时器(中断类型为8)的中断处理程序中,有一条中断指令INT 1CH,
;时钟中断每发生一次(约每秒发生18.2次,即55ms发生一次)都要嵌套调用
;一次中断类型1CH的处理程序。在ROM BIOS例程中,1CH的处理程序只有一条IRET,
;实际上它并没有作任何工作,只是为用户提供了一个中断类型号。如果用户有某种
;定时周期性的工作需要完成,就可以利用系统定时器的中断间隔,用自己设计的
;处理程序来代替原有的1CH的中断程序。本程序就是将原有1CH中断程序进行替换,
;实现了定时更新时间。采用DOS中断驻留中断程序。
tUpdate proc near
push ds ;工作寄存器进栈
push ax
push bx
push cx
push dx
mov ax, @data
mov ds, ax
sti
;判断是否现在是 cUpdate 的时间
dec count ;为定时器间隔计算(约18.2次为1秒)
jnz exit ;退出如果不 1 秒
mov count,18 ;控制流环间隔延迟(10s)
call CALTIME ;更新时间
;检测刷新开关是否打开
mov bl,ds:[SWITCH]
cmp bl,00
jz exit
;检测是否有按键按下,取适当的值进行退出控制
PUSHA
MOV AH, 0BH ;检测是否输入消息(按键消息)
INT 21H
INC AL
JNZ nokey
mov ds:[keySw],0FFh ;设置已经被按键
POPA
jmp exit
nokey:
POPA
call DISPLYH ;显示小时
call DISPLYM ;显示分钟
call DISPLYS ;显示秒
exit:
cli
mov al,20h ;设置 EOI
out 20h,al
pop dx ;出栈reg.
pop cx
pop bx
pop ax
pop ds
iret ;打断回返
tUpdate endp
;关闭定时刷新开关
closeSw proc near
PUSHA
mov bl,0h
mov ds:[SWITCH],bl ;关闭刷新开关
POPA
ret
closeSw endp
;打开定时刷新开关
openSw proc near
PUSHA
mov bl,0FFh
mov ds:[SWITCH],bl ;打开刷新开关
POPA
ret
openSw endp
;初始化显示界面,可以在此处添加额外的设置选项
initUI proc near
PUSHA
;设置显示方式(40×25 黑白文本,16级灰度)
mov ah,0h
mov al,00h
int 10h
;显示头标题
mov dh,00h
mov dl,02h
call MOVCUR
mov dx,offset sMsg ;dx<= sMsg的大小
mov ah,09h ;显示sMsg
int 21h ;返回 DOS
;显示结尾标题
mov dh,05h
mov dl,02h
call MOVCUR
mov dx,offset qMsg ;dx<= qMsg大小
mov ah,09h ;显示qMsg
int 21h ;返回 DOS
;显示名言信息
mov dh,09h
mov dl,00h
call MOVCUR
mov dx,offset FamoussayingMsg ; dx<= FamoussayingMsg的大小
mov ah,09h ;显示名言信息
int 21h ;返回DOS
POPA
ret
initUI endp
;移动光标(dh:row,dl:col) dh ;参数设置行dl ;参数设置列
MOVCUR proc near
PUSHA ;设置光标位置
mov ah,2h
mov bh,0
int 10h
POPA
ret
MOVCUR endp
;显示小时
DISPLYH PROC NEAR
PUSHA
mov dh,2h
mov dl,10d
call MOVCUR
mov dh,0
mov dl,ds:[HOUR]
call output ;调用output函数输出小时
mov dl,':'
mov ah,02h
int 21h
POPA
RET
DISPLYH ENDP
;显示分钟
DISPLYM PROC NEAR
PUSHA
;设置光标位置
mov dh,2h
mov dl,13d
call MOVCUR
mov dh,0
mov dl,ds:[MIN]
call output
mov dl,':'
mov ah,02h
int 21h
POPA
RET
DISPLYM ENDP
;显示秒
DISPLYS PROC NEAR
PUSHA
;设置光标位置
mov dh,2h
mov dl,16d
call MOVCUR
mov dh,0
mov dl,ds:[SEC]
call output
POPA
ret
DISPLYS endp
;调用DOS中断取得系统时间
GETTIME PROC NEAR
PUSHA
mov ah,2ch ;取得系统时间CH:CL=(H:M),DH:DL=(s:1/100s)
int 21h
mov ds:[HOUR],CH ;取得小时
mov ds:[MIN],CL ;取得分钟
; dec DH ;延迟1秒
mov ds:[SEC],DH ;取得秒
mov ds:[MSEC],DL
POPA
ret
GETTIME endp
;调整时间,累加秒,分,时
CALTIME proc near
PUSHA ;调整秒
inc ds:[SEC]
cmp ds:[SEC],60d
jb endc
mov ds:[SEC],0h
setM: ;调整分钟
inc ds:[MIN]
cmp ds:[MIN],60d
jb endc
mov ds:[MIN],0h
setH: ;调整小时
inc ds:[HOUR]
cmp ds:[HOUR],24d
jb endc
mov ds:[HOUR],0h
endc:
POPA
ret
CALTIME endp
;设置时间
SETTIME proc near
PUSHA
call closeSw ;关闭定时刷新
mov dh,11h
mov dl,00h
call MOVCUR ;移动光标
mov dx,00h
mov ah,09h ;输出提示信息,提示输出设置时间
mov dx,offset setTMsg
int 21h
call READNT ;读取时间(HH:MM:SS)
;设置时间
mov ch,ds:[NHOUR] ;HOUR
mov cl,ds:[NMIN] ;MIN
mov dh,ds:[NSEC] ;SECOND
mov dl,0h
mov ah,2Dh
int 21h
cmp al,00h
je SetSuccess
mov ah,02h ;设置时间中断调用失败处理
mov dl,'!'
int 21h
SetSuccess:
call openSw ;打开刷新开关
POPA
ret
SETTIME endp
;设置闹铃时间:
SETRING proc near
PUSHA
call closeSw ;关闭定时刷新
mov dh,11h
mov dl,00h
call MOVCUR ;移动光标
mov dx,00h
mov ah,09h ;输出提示信息,提示输出设置时间
mov dx,offset setRMsg
int 21h
call READRINGT ;读取闹铃时间(HH:MM)
mov ds:[RingSw],0FFH ;设置闹铃开关为 '开'
rSetSuccess:
call openSw ;打开刷新开关
POPA
ret
SETRING endp
;读取时间(从ASCII码到16进制数值的转换)(HH:MM:SS)
READNT proc near
PUSHA
mov ah,01h
int 21h
mov bx,0h ;bx存放得到的数值(16进制)
loop1: cmp al,'0' ;读取小时
jl endloop1
cmp al,'9'
jg endloop1
sub al,30h ;把字符翻译成数字
cbw
xchg ax,bx
mov cx,0ah ;把 10d 副值给 cx
mul cx
xchg ax,bx
add bx,ax
mov ah,01h ;阅读中断类型
int 21h ;呼叫操作系统中断
jmp loop1
;存取小时
endloop1:
mov ds:[NHOUR],bl;
mov bx,0h;
loop2:
mov ah,01h
int 21h
;bx存放得到的数值(16进制)
cmp al,'0' ;读取分钟
jl endloop2
cmp al,'9'
jg endloop2
sub al,30h ;把字符翻译成数字
cbw
xchg ax,bx
mov cx,0ah
mul cx
xchg ax,bx
add bx,ax
jmp loop2
;存取分钟
endloop2:
mov ds:[NMIN],bl
mov bx,0h
loop3:
mov ah,01h
int 21h
;bx存放得到的数值(16进制)
cmp al,'0' ;读取秒
jl rlast
cmp al,'9'
jg rlast
sub al,30h
cbw
xchg ax,bx
mov cx,0ah
mul cx
xchg ax,bx
add bx,ax
jmp loop3
rlast:
;存取秒
mov ds:[NSEC],bl
POPA
ret
READNT endp
;读取闹铃时间
READRINGT proc near
PUSHA
mov ah,01h
int 21h
mov bx,0h ;bx存放得到的数值(16进制)
rloop1:
cmp al,'0' ;读取小时
jl rendloop1
cmp al,'9'
jg rendloop1
sub al,30h
cbw
xchg ax,bx
mov cx,0ah
mul cx
xchg ax,bx
add bx,ax
mov ah,01h
int 21h
jmp rloop1
;存取小时
rendloop1:
mov ds:[RHOUR],bl;
mov bx,0h;
rloop2:
mov ah,01h
int 21h
;bx存放得到的数值(16进制)
cmp al,'0' ;读取分钟
jl rendloop2
cmp al,'9'
jg rendloop2
sub al,30h
cbw
xchg ax,bx
mov cx,0ah
mul cx
xchg ax,bx
add bx,ax
jmp rloop2
;存取分钟
rendloop2:
mov ds:[RMIN],bl
POPA
ret
READRINGT endp
;闹铃发声程序
;DI,保存发声频率,CX,BX指定发声程序的持续时间
GENSOUND proc near
PUSHA
push DI
;set the frequency
mov di,020h
mov bx,0FFh
mov al,0b6h ;写定时器模态普通型的。.
out 43h,al
mov dx,12h
mov ax,348ch
div di
out 42h,al ;写定时器 2 计数低位
mov al,ah
out 42h,al ;写定时器 2 计数高位
in al,61h ;取现在的出口设定
mov ah,al ;保存在ah中
or al,03h
out 61h,al
waitsound:
mov cx,500h
delaySound:
loop delaySound
dec bx
jnz waitsound
mov al,ah
out 61h,al
pop DI
POPA
ret
GENSOUND endp
;输出字符(dx中存放十进制数值)
output proc near
PUSHA
mov ax,dx ;把最后值给ah
mov dx,00h
mov cx,0h
trans:
mov bx,000ah
div bx
add dl,30h ;翻译成 character
mov dh,0h
push dx
inc cx
mov dx,00h
cmp ax,00h
jg trans
cmp cx,01h
jg print
addZero: ;增加前导0,,比如:02
mov dh,0
mov dl,'0'
push dx
inc cx
print:
cmp cx,0h
jle endout
pop dx
mov dh,0
dec cx
mov ah,02h
int 21h ;返回 DOS 输出
jmp print
endout:
POPA
ret
output endp
end start ;结束
.end
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -