主文件
;***********************************************************
; 版权所有 (C)2018, WangMeow
;
; 文件名称: main.asm
; 内容摘要:该代码为主程序,用于调用其他功能子程序
; 当前版本: V1.0
; 作 者: WangMeow
; 完成日期: 20180701
;**********************************************************
extrn s1:far,clean:far ;主程序可调用的外部子程序有显示时间子程序和清屏子程序
assume cs:code,ss:stack,ds:data
stack segment stack
db 100h dup(0)
stack ends
data segment
menu db 10,13,10,13,' ----------WangMeow.Clock_System----------'
db 10,13
db 10,13,' --------------Menu--------------- '
db 10,13,' 1.Show time.'
db 10,13,' 2.Set alarm clock.'
db 10,13,' 0.exit.'
db 10,13,' --------------------------------- '
db 10,13,' Please choose one of 0~2:','$'
table dw pro1,pro2
tip1 db 10,13,' Input error!','$'
tip2 db 10,13,' Please set the hour:','$'
tip3 db 10,13,' Please set the minute:','$'
hour dw 64h
minute dw 64h
data ends
code segment
main proc
aton macro ;宏定义:将ASCII转换成数字
sub al,30h
endm
ntoad macro ;宏定义:将数字转换成各子程序的偏移地址
shl al,1
mov bl,al
mov bh,0
sub bx,2
endm
outscr macro x ;宏定义:输出字符串到屏幕
lea dx,x ;ds:dx = 输出字符串的首地址
mov ah,9 ;调用21h中断的9号功能,输出字符串
int 21h
endm
inputb macro
mov ah,0ch ;调用21h中断的c号功能,从键盘读入字符,al保存读入字符的ASCII码
mov al,01h
int 21h
endm
start:
run:
mov ax,data ;设置数据段
mov ds,ax
outscr menu ;宏调用
inputb ;宏调用
aton ;宏调用
cmp al,1 ;输入输入的不在[1,2]这个区间,那么去处理
jb deal
cmp al,2
ja deal
ntoad ;宏调用
call far ptr clean ;调用清屏子程序
mov si,00h ;这里si的作用相当于flag
call word ptr table[bx] ;使用代码的直接定址表调用子程序
jmp run ;主程序持续运行
deal:;用来处理非正常输入
cmp al,0 ;如果是输入是0,程序正常退出
je exit
outscr tip1 ;如果是非正常输入,输出错误提示
jmp run
exit:;程序正常退出
mov ax,4c00h
int 21h
main endp
;*****************************************************************
; 子程序:pro1
; 功能:负责远间调用S1子程序,负责显示时间和时间对比
; 入口参数:无
; 出口参数:无
;*****************************************************************
pro1 proc near ;PRO1子程序,显示时钟
call far ptr s1 ;调用S1子程序
ret
pro1 endp
;*****************************************************************
; 子程序:pro2
; 功能:负责设置闹钟,实现小时和分钟的输入
; 入口参数:无
; 出口参数:0:[0200]存放小时的低位,0:[0201]存放小时的高位
; 0:[0202]存放分钟的低位,0:[0203]存放分钟的高位
;调用注意事项:近调用,ret返回
;*****************************************************************
pro2 proc near ;调用PRO2子程序,用来输入要设置的时间
mov ax,0h ;将输入的时间保存到0:0200h这个物理地址里面
mov es,ax
mov bx,0200h
outscr tip2 ;宏调用
mov cx,2 ;循环两次输入字符
mov si,1
l1: inputb ;宏调用,输入小时
mov es:[bx+si],al ;将输入的小时的高位存到0201h里面,低位存到0200h里面
dec si
loop l1
add bx,2 ;偏移地址+2
outscr tip3 ;宏调用
mov cx,2 ;循环两次输入字符
mov si,1
l2: inputb ;宏调用,输入分钟
mov es:[bx+si],al ;将输入的分钟的高位保存到0203h里面,低位保存到0202h里面
dec si
loop l2
ret
pro2 endp
code ends
end start
其他子文件
;***********************************************************
; 版权所有 (C)2018, Wang Miao
;
; 文件名称: show.asm
; 内容摘要:该代码为子程序,用于动态时间显示以及时间对比
; 当前版本: V1.0
; 作 者: 汪淼
; 完成日期: 20180701
;**********************************************************
public s1 ;显示时间子程序
extrn clean:far,music:far ;显示时间子程序可调用的外部子程序有清屏子程序和音乐子程序
assume cs:code
code segment
s1 proc
show macro x,col ;宏定义,使用70h和71h端口获取系统时间
mov al,x
out 70h,al
in al,71h
mov ah,al ;13---18行代码是把系统时间的高位保存到ah,低位保存到al
mov cl,4
shr ah,cl
and al,00001111b
add ah,30h
add al,30h
mov bx,0b800h
mov es,bx
mov byte ptr es:[160*12+col*2],ah ;两个字节代表屏幕的一列,将代表时间的字符放入屏幕显示缓冲区
mov byte ptr es:[160*12+col*2+2],al
endm
sym macro
mov byte ptr es:[160*12+32*2],'/' ;美化时间显示
mov byte ptr es:[160*12+35*2],'/'
mov byte ptr es:[160*12+38*2],' '
mov byte ptr es:[160*12+41*2],':'
mov byte ptr es:[160*12+44*2],':'
endm
start:
call disptime
mov ah,01h ;检查是否有键盘输入
int 16h
jz start ;表示位ZF = 1时,无键盘输入,继续显示时间
mov ah,00h ;如果有键盘输入
int 16h
cmp al,27 ;ESC的ASCII是27(10进制)
jnz start ;如果按住的键盘不是ESC,继续显示时间
call far ptr clean ;如果是ESC,那么返回主程序
retf
;*****************************************************************
; 子程序:disptime
; 功能:负责动态显示时间
; 入口参数:日期在70h端口处的地址
; 出口参数:无
;*****************************************************************
disptime proc near
show 9,30 ;年
show 8,33 ;月
show 7,36 ;日
show 4,39 ;小时
call judge1 ;判断设置的小时与当前显示的是否一致
show 2,42 ;分钟
call judge2 ;判断设置的分钟与当前显示的是否一致
show 0,45 ;秒
sym ;符号
cmp si,01h ;如果flag = 1,说明闹钟与显示时间一直,响铃
je alarm
ret
alarm: call far ptr music
ret
disptime endp
;*****************************************************************
; 子程序:judge1
; 功能:负责判断设置的小时是否与显示的小时一直
; 入口参数:0:[0200]存放的小时的低位,0:[0201]存放的小时的高位
; 出口参数:无
;*****************************************************************
judge1 proc near ;判断小时的子程序
mov dx,0h
mov es,dx
mov bp,0200h
mov ch,es:[bp]
cmp ch,al ;al是小时的低位
jne nset
inc bp
mov ch,es:[bp]
cmp ch,ah ;ah是小时的高位
jne nset
jmp set
judge1 endp
;*****************************************************************
; 子程序:judge2
; 功能:负责判断设置的分钟是否与显示的分钟一直
; 入口参数:0:[0202]存放的分钟的低位,0:[0203]存放的分钟的高位
; 出口参数:无
;*****************************************************************
judge2 proc near ;判断分钟的子程序
mov dx,0h
mov es,dx
mov bp,0202h
mov ch,es:[bp]
cmp ch,al
jne nset
inc bp
mov ch,es:[bp]
cmp ch,ah
jne nset
jmp set
judge2 endp
return: ret ;这里标注一下,感谢贺老师的深刻教会,不应该在这里破坏函数的一体性
set: mov si,01h
jmp return ;这里标注一下,感谢贺老师的深刻教会,不应该在这里破坏函数的一体性
nset:
mov si,00h
jmp return ;这里标注一下,感谢贺老师的深刻教会,不应该在这里破坏函数的一体性
s1 endp
code ends
end start
;***********************************************************
; 版权所有 (C)2018, Wang Miao
;
; 文件名称: cls.asm
; 内容摘要:该代码为子程序,用于清屏
; 当前版本: V1.0
; 作 者: 汪淼
; 完成日期: 20180701
;**********************************************************
public clean ;清屏子程序
.8086
.MODEL small
.stack 10h
.code
;*****************************************************************
; 子程序:clean
; 功能:负责实现清屏功能
; 入口参数:无
; 出口参数:无
;*****************************************************************
clean proc
start:
mov ah,0fh ;读取显示器模式,dosbox中显示模式是80x25
int 10h
mov ah,0h ;设置显示器模式
int 10h
retf
clean endp
end start
;***********************************************************
; 版权所有 (C)2018, Wang Miao
;
; 文件名称: mus.asm
; 内容摘要:该代码为子程序,用于音乐响铃
; 当前版本: V1.0
; 作 者: 汪淼
; 完成日期: 20180701
;**********************************************************
public music ;音乐子程序
.8086
.MODEL small
.stack 100h
.data
mus_freq dw 524,524,784,784,880,880,784 ;闹钟的音乐,这里的音乐是一闪一闪亮晶晶
dw 698,698,659,659,587,587,524
dw 784,784,698,698,659,659,587
dw 784,784,698,698,659,659,587
dw 524,524,784,784,880,880,784
dw 698,698,659,659,587,587,524
dw -1
mus_time dw 3 dup(25,25),25 ;音乐节奏(音长)
dw 3 dup(25,25),25
dw 3 dup(25,25),25
dw 3 dup(25,25),25
dw 3 dup(25,25),25
dw 3 dup(25,25),25
dw 3 dup(25,25),25
dw 3 dup(25,25),25
dw 3 dup(25,25),25
dw 3 dup(25,25),25
dw 3 dup(25,25),25
dw 3 dup(25,25),25
.code
music proc
start:
mov ax, @data
mov ds, ax
lea si, mus_freq
lea di, mus_time
play:
mov dx, [si]
cmp dx, -1 ;如果是最后一个音符,那么结束演奏
je end_play
call sound ;否则,调用演奏子程序
add si, 2
add di, 2
jmp play
;*****************************************************************
; 子程序:sound
; 功能:负责演奏一个音符
; 入口参数:si - 要演奏的音符的频率的地址 di - 要演奏的音符的音长的地址
; 出口参数:无
;*****************************************************************
sound:
push ax
push dx
push cx
;8253 芯片(定时/计数器)的设置
mov al,0b6h ;8253初始化
out 43h,al ;43H是8253芯片控制口的端口地址
mov dx,12h
mov ax,34dch
div word ptr [si] ;计算分频值,赋给ax。[si]中存放声音的频率值。
out 42h, al ;先送低8位到计数器,42h是8253芯片定时器通道2的端口地址
mov al, ah
out 42h, al ;后送高8位计数器
;设置8255芯片, 控制扬声器的开/关
in al,61h ;读取8255 B端口原值 PB口的最低两位:PB0:控制8253定时器,PB1:控制喇叭发声
mov ah,al ;保存原值
or al,3 ;使低两位置1,以便打开开关
out 61h,al ;开扬声器, 发声
mov dx, [di] ;保持[di]时长
wait1:
mov cx, 28560 ;1s = 1428次循环,这里设置一首音乐20s
delay:
nop
loop delay
dec dx
jnz wait1
mov al, ah ;取回8255的PB口原值
out 61h, al ;回复8255PB口,停止发声
pop cx
pop dx
pop ax
ret
end_play:
retf
music endp
end start