显示万年历的程序(汇编语言实现,附源代码)

运行环境:Masm for Windows 集成实验环境 2015

一、课题内容和要求

课题内容:

用汇编语言编写一个有简单界面显示的日历,要求输入年月日后,将该月的完整日历显示出来,包括星期几,且每月的星期六,日用不同颜色来表示。并且要求显示输入的日期。

基本要求:

(1)设计一个简单的接受键盘输入年月日信息的界面。

(2)输入年月日后,将该月的完整日历显示出来,包括星期几,且每月的星期六、星期日有标识。

(3)实物演示时要求讲出程序原理和设计思想。

(4)程序运行良好、界面清晰。

提高要求:

(1)界面色彩鲜明、友好,能够突出显示主题;

(2)用不同的色彩明显表示出输入的日期;

(3)错误输入要有提示及处理,结果显示清晰、合理。

二、课题需求分析

本课题目标系统的功能框架图如下所示。

 具体需求如下:

(1)界面显示:首先设计一个可以接受键盘输入的界面,界面的显示可以采用BIOS屏幕功能,其次界面要色彩鲜明,所以采用彩色文本的方式。程序运行的背景颜色运用BIOS的06H功能就能将其颜色设置成棕底。其入口参AL=上卷行数,AL=0:整个窗口空白,BH=卷入行属性,CH:CL=左上角行号:列号,DH:DL=右下角行号:列号。

(2)输入日期:要求从键盘输入日期,那么要做的就是完成通过键盘输入得到要显示的年月日,并能够将年月日储存起来同时将输入的日期显示出来这个功能。在这里可以调用DOS的 0AH 的功能,0AH功能是等待键盘输入,并将键入的一串字符送入数据缓冲区,同时在屏幕上显示字符串。该功能要求键入的字符串必须以“回车”做为结束标志,既按下回车键后,本次功能调用结束,光标返回当前行始格“回车”符ODH保存在缓冲区当中在接受字符的过程中,输入字符显示在屏幕上,响应Ctrl_C,按下退格键可删除屏幕以及缓冲区的当前字符。对缓冲区的格式有如下要求:缓冲区要设置在程序数据段,调用前,缓冲区的首址偏移量应赋给DX寄存器。缓冲区首单元应预置“允许接收的字符个数”(包括回车键在内)。用户键入回车之后,由0AH功能把实际键入的字符个数(不包括回车键)写入BUF0+1单元。键入的字符串从BUF0+2单元开始依次存放。因此缓冲区的容量要不小于键入的键入的串的长度(包括回车键)+2。在程序中这么定义的:

BUF0   DB 9

            DB ?

            DB 9 DUP(?)

(3)输入年月日后,将该月的完整日历显示出来:首先在屏幕上先显示出星期一到星期日,在设计时将“SUN  MON  TUE  WED  THU  FRI  SAT”在屏幕上显示出来,然后在下面依次显示完这个月的所有天数。在显示每个月的第一天的时候,首先要能够确定第一天是星期几,这样才能够正确的向下显示。并且显示这个月的最后一天不能够超出这个月的最大天数,在这里也就要考虑到各个月不同天数的情况(尤其是二月,闰年的二月有29 天)。在确定输入的那个月的第一天是星期几可以通过蔡勒公式求得计算:W = [C/4] - 2C + y + [y/4] + [13 * (M+1) / 5] + d – 1(C 是世纪数减 1,y 是年份后两位,M 是月份,d是日数,计算出来的结果模取7)。求得这个月的第一天是星期几后,会发现如果是星期一的话,最后算得的W的值为1。依次类推,星期日的 W 值为 0,那么可以根据 W 值来确定要显示的行和列,最后限定一下要显示的这个月的最后一天,那么日历就可以正确的显示出来了。在用蔡勒公式计算的时候要注意1月和2月按上一年的13月和14月来算,这时 C 和 y 均按上一年取值。日历的显示也要注意光标的定位。设置星期六、日两列的字体为红色青底,打印出当前显示的是哪一年的哪一个月,打印出 MON,TUR等字样,以标注哪天是星期几。根据月的大小进行循环的输出日期,在此过程中,要注意一位数与两位数对格式的影响,最后显示出来的界面要清洁。

(4)用不同的色彩明显表示出输入的日期和星期六、日两列的字体有标识:想设置星期六、日两列的字体为红字青底。 这个只需在显示日历的时候,判断要显示的是否是第一列和最后一列,如果是就运用BIOS的06H功能就能将其颜色设置成红色青底。其入口参AL=上卷行数,AL=0:整个窗口空白,BH=卷入行属性,CH:CL=左上角行号:列号,DH:DL=右下角行号:列号。对于输入日期的颜色和错误提示的颜色也都是通过这种方法实现的。

(5)错误输入要有提示及处理:比如要输入20200619,只能输入像这种格式的8个字符,通过控制BUF0缓冲区的大小就完成了。但是由于输入的是年月日,也要判断其是否符合现实:公历每年有 12 个月,其中 1、3、5、7、8、10、12 月为大月,每月 31 天。4、6、9、11 月为小月,每月30天。公历有闰年平年之分,若某年是闰年,则该年2月有29天,全年有366天,若某年是平年,则该年2月有28天,全年有365天。也就要对输入的年月日进行处理,月数在1到12之间,日数在1到相应年份月数的最大天数之间,如果输入不正确,要显示出错误输入信息,并且提示重新输入年月日。

三、课题设计

1软件设计

(1)主要变量及数据结构

A1 DB "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~$"

A2 DB "*** * *  *   Calendar   *  * * ****$"

A3 DB "designer:B17040605 sxf $"

A4 DB "Please input the date:$"

A5 DB "Please input the date like :20200601$"

A6 DW 0

A7 DB 0

A8 DB "Calendar:$"

A9 DB "-----------------------------------$"

B DW ?        ;两个日期的显示距离

C1 DB 0       ;世纪数

G DB 0        ;G为取出每个月的天数

N DB ?        ;N为每月第一天是星期几

P DW ?        ;P为数值权重

Y DB ?        ; 蔡勒公式里面的y

BUF0 DB 9

DB ?

DB 9 DUP(?)

BUF1 DB 31,?,31,30,31,30,31,31,30,31,30,31

BUF2 DB ' 1$ 2$ 3$ 4$ 5$ 6$ 7$ 8$ 9$10$11$12$13$14$

15$16$17$18$19$20$21$22$23$24$25$26$27$28$29$30$31$'

WEEK DB "SUN  MON  TUE  WED  THU  FRI  SAT$"

YEAR DW ?

MONTH DW ?

DAY DW ?

LINE DB 1

WRONG DB "!!! Wrong!Please enter the correct format like :20200601 !!!$"

2 主要算法流程

  (1)判断闰年算法:年份值能被400整除;或者能被4整除但不能被100整除的是闰年,其余的年份都为平年。

(2)求输入的日期对应月的第一天是星期几算法:在确定输入的那个月的第一天是星期几可以通过蔡勒公式求得计算:W = [C/4] - 2C + y + [y/4] + [13 * (M+1) / 5] + d – 1(C 是世纪数减 1,y 是年份后两位,M 是月份,d是日数,计算出来的结果模取7)。在用蔡勒公式计算的时候要注意 1 月和 2 月按上一年的 13 月和 14 月来算,这时 C 和 y 均按上一年取值。还要判断如果输入 的年后两位是00的时候,计数时将年用99进行替代。

四、课题实现

1程序源代码

DATAS SEGMENT
A1 DB "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~$"
A2 DB "*** * *  *   Calendar   *  * * ****$"
A3 DB "designer:B17040605 sxf $"
A4 DB "Please input the date:$"
A5 DB "Please input the date like :20200601$"
A6 DW 0
A7 DB 0
A8 DB "Calendar:$"
A9 DB "-----------------------------------$"
B DW ?        ;两个日期的显示距离
C1 DB 0       ;世纪数
G DB 0        ;G为取出每个月的天数
N DB ?        ;N为每月第一天是星期几
P DW ?        ;P为数值权重
Y DB ?        ; 蔡勒公式里面的y
BUF0 DB 9
DB ?
DB 9 DUP(?)
BUF1 DB 31,?,31,30,31,30,31,31,30,31,30,31
BUF2 DB ' 1$ 2$ 3$ 4$ 5$ 6$ 7$ 8$ 9$10$11$12$13$14$15$
16$17$18$19$20$21$22$23$24$25$26$27$28$29$30$31$'
WEEK DB "SUN  MON  TUE  WED  THU  FRI  SAT$"
YEAR DW ?
MONTH DW ?
DAY DW ?
LINE DB 1
WRONG DB "!!! Wrong!Please enter the correct format like :20200601 !!!$"
DATAS ENDS
STACKS SEGMENT
STACKS ENDS
;------------------以下是代码部分-------------------------------
CODES SEGMENT
ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
;输入界面样式
BEG:
MOV AX,DATAS
MOV DS,AX
MOV AH,0           ;设置显示方式
MOV AL,3           ;80*25彩色文本方式
INT 10H
MOV AH,6
MOV AL,0
MOV CH,0
MOV CL,0 
MOV DH,80
MOV DL,80
MOV BH,01110000B   ;前四位定义背景色为棕色
INT 10H
;显示“~~~~~~~~~~~~~~~~~~~~~~~~~~~~~”
MOV DH,LINE
MOV DL,1
CALL GUANGBIAO
LEA DX,A1
CALL OUTP
ADD LINE,2
;显示“*** * * *  Calendar   * * * ****”
MOV DH,LINE
MOV DL,1
CALL GUANGBIAO
LEA DX,A2
CALL OUTP
ADD LINE,2
;显示“designer:B17040605 sxf ”
MOV DH,LINE
MOV DL,8
CALL GUANGBIAO
LEA DX,A3
CALL OUTP
ADD LINE,2
;显示“Please input the date like :20200601”
MOV DH,LINE
MOV DL,1
CALL GUANGBIAO
LEA DX,A5
CALL OUTP
ADD LINE,2   
;输入日期
INPUT:
MOV AH,6 
MOV AL,0
MOV CH,LINE
MOV CL,23          ;当前行,23列到30列(输入8位字符)
MOV DH,LINE
MOV DL,30
MOV BH,00100001B   ;底色显示为绿色
INT 10H
;显示“Please input the date:”
MOV DH,LINE
MOV DL,1
CALL GUANGBIAO
LEA DX,A4
CALL OUTP
;把输入的日期保存在缓冲区BUF0中
MOV DH,LINE
MOV DL,23
CALL GUANGBIAO
LEA DX,BUF0 
MOV AH,10
INT 21H
ADD LINE,1
;ASCII码转真值
LEA DI,BUF0+2            ;DI指向日期输入串的首地址
MOV CX,4
MOV AX,0
P1: MOV BL,[DI]          ;取字符送到BL
SUB BL,30H               ;字符减去30H,转化为真值
MOV BH,0 
MOV P,10
MUL P
ADD AX,BX
INC DI                   ;DI指向下一个字符
DEC CX                   ;长度减一
JNZ P1
MOV YEAR,AX              ;获得年份值
CALL PDRN                ;调用判断平年还是闰年的程序
JC L1                    ;有进位,借位,跳转到L1,输出闰年;否则顺序执行,是平年
LEA DI,BUF1
MOV [DI+1],BYTE PTR 28   ;平年把28送入BUF1中
JMP L2
L1:
LEA DI,BUF1
MOV [DI+1],BYTE PTR 29   ;闰年把29送入BUF1中
L2:
LEA DI,BUF0+6
MOV CL,2
MOV AX,0
P2: MOV BL,[DI]          ;取字符送到BL
SUB BL,30H               ;字符减去30H,转化为真值
MOV BH,0
MOV P,10
MUL P
ADD AX,BX
INC DI
DEC CX                   ;长度减一
JNZ P2
MOV MONTH,AX             ;获得月份值
LEA DI,BUF0+8
MOV CL,2
MOV AX,0
P3: MOV BL,[DI]          ;取字符送到BL
SUB BL,30H               ;字符减去30H,转化为真值
MOV BH,0
MOV P,10
MUL P
ADD AX,BX
INC DI
DEC CX                  ;长度减一
JNZ P3
MOV DAY,AX              ;获得日值
CMP MONTH,1             ;月份<1?报错
JC ERROR
CMP MONTH,13            ;月份>12,报错
JNC ERROR
CMP DAY,1
JC ERROR
CALL PDXQJ              ;显示日历,调用PDXQJ子程序返回N的值,N为每月第一天是星期几
MOV DI,OFFSET BUF1
DEC MONTH               ;PDXQJ中MONTH加了1
DEC MONTH               ;偏移值
ADD DI,MONTH            ;取出当月的天数
MOV CL,[DI]
MOV G,CL                ;取出每个月的天数为G
CMP BYTE PTR DAY,CL     ;输入的天如果超过这个月的最大天数就报错
JA ERROR
;显示“~~~~~~~~~~~~~~~~~~~~~~~~~~”
MOV DH,LINE
MOV DL,1
CALL GUANGBIAO
LEA DX,A1
CALL OUTP
ADD LINE,2
;显示“Calendar:XXXX.XX.XX”
MOV DH,LINE
MOV DL,7
CALL GUANGBIAO
LEA DX,A8
CALL OUTP
MOV DH,LINE
MOV CL,8
MOV DL,16
CALL GUANGBIAO
LEA DI,BUF0+2
MOV AX,0
;显示“---------------------------”
ADD LINE,1
MOV DH,LINE
MOV DL,1
CALL GUANGBIAO
LEA DX,A9
CALL OUTP
ADD LINE,2
;显示“SUN  MON  TUE  WED  THU  FRI  SAT”
MOV DH,LINE
MOV DL,0
CALL GUANGBIAO
LEA DX,WEEK
CALL OUTP 
INC LINE
MOV SI,OFFSET BUF2
MOV AL,5
MUL N               ;N为每月第一天是星期几
MOV B,AX            ;5*N
CMP B,35            ;B<35,执行POS  ,如果每个月一号是星期天,显示一号的行号减1
JC POS
SUB LINE,1
;周日设置红色显示0~3
POS:
MOV DH,LINE         ;每个月的一号的光标位置
MOV DL,BYTE PTR B   ;B=5*N
CALL GUANGBIAO
MOV A7,31
CMP DL,A7
JBE POS1            ;DL<=31(星期六),跳转到POS1,否则行加1,列从0开始
ADD LINE,1          ;两行日期间隔
MOV AX,0
MOV B,AX
MOV AH,6 
MOV AL,0
MOV CH,LINE
MOV CL,0
MOV DH,LINE
MOV DL,3
MOV BH,00110100B
INT 10H
JMP POS
;周六的日历显示红色30~33
POS1: 
CMP DL,30           ;不是星期六也不是星期天,不用红色
JC NORED 
MOV AH,6
MOV AL,0
MOV CH,LINE
MOV CL,30
MOV DH,LINE
MOV DL,33
MOV BH,00110100B
INT 10H
;打印日历
NORED: 
MOV DX,SI
CALL OUTP          ;在当前光标位置显示数字
ADD SI,3
ADD B,5            ;每两个日期的显示距离5
DEC G              ;G为取出每个月的天数
JNZ POS
MOV AH,7           ;无回显键盘输入
INT 21H
MOV BL,1
MOV LINE,BL
JMP BEG
;显示错误提示信息
ERROR:
MOV AH,6 
MOV AL,0
MOV CH,LINE
MOV CL,0           ;当前行,23列到30列(输入8位字符)
MOV DH,LINE
MOV DL,60
MOV BH,01000001B   ;底色显示为红色
INT 10H
MOV DL,1
CALL GUANGBIAO
LEA DX,WRONG
CALL OUTP
INC LINE
JMP INPUT
EXIT: MOV AH,4CH
INT 21H
;光标的函数GUANGBIAO
GUANGBIAO PROC
PUSH AX
PUSH BX
MOV AH,2          ;2号功能调用,预置光标的位置
MOV BH,0
INT 10H           ;定光标
POP BX
POP AX
RET
GUANGBIAO ENDP
;显示输出的函数OUTP
OUTP PROC
PUSH AX
MOV AH,9          ;9号功能调用,字符串输出
INT 21H
POP AX
RET
OUTP ENDP
;判断是平年还是闰年的程序PDRN
PDRN PROC 
PUSH AX
PUSH BX
PUSH CX
PUSH DX
MOV AX,YEAR
MOV CX,AX
MOV DX,0           ;DX=0
MOV BX,4
DIV BX             ;AX/4
CMP DX,0           ;DX中为余数,与0比较
JNZ B1
MOV AX,CX
MOV BX,100
DIV BX             ;AX/100
CMP DX,0
JNZ B2
MOV AX,CX
MOV BX,400
DIV BX             ;AX/400
CMP DX,0
JZ B2
B1: CLC            ;CF=0
JMP B3
B2: STC            ;CF=1
B3: POP DX
POP CX
POP BX
POP AX
RET
PDRN ENDP
;利用蔡勒公式获得这个月的一号是星期几
PDXQJ PROC         ;保护现场
PUSH AX
PUSH BX
PUSH CX
PUSH DX
MOV BL,100
MOV AX,YEAR        ;年份给AX
DIV BL
MOV C1,AL
MOV Y,AH
MOV CL,2
MOV BL,C1
SHR BL,CL          ;int(c/4)
SHL C1,1            ;2*c
SUB BL,C1
ADD BL,Y
SHR Y,CL            ;int(y/4)
ADD BL,Y           ;int(c/4)-2*c+y+int(y/4)
INC MONTH
CMP MONTH,3        ;是一月?转移
JC J1 
CMP MONTH,4        ;是二月?转移
JC J1
XOR AH,AH
MOV AX,MONTH
MOV DX,13
MUL DX
MOV CX,5
DIV CX
XCHG AX,BX
CBW
XCHG AX,BX
ADD BX,AX         ;int(c/4)-2*c+y+int(y/4)+int(13*(m+1)/5)
MOV AX,BX
MOV CL,7
IDIV CL            ;计算出来的结果模取7
CMP AH,0
JG SKIPADD
ADD AH,7
SKIPADD:
MOV BL,AH
MOV N,BL
JMP J3              ;还原现场
;J1处理:所以要判断如果输入的年后两位是00的时候计数时将年用99进行替代。
J1: MOV BL,100
MOV AX,YEAR
DIV BL
MOV C1,AL
MOV Y,AH
CMP Y,1             ;如果年份后两位是00,则前一年是99
JNC J2
ADD Y,100
;J2:1月和2月按上一年的13月和14月来算这时C和y均按上一年取值。
J2: SUB Y,1
MOV CL,2
MOV BL,C1
SHR BL,CL          ;int(c/4)
SHL C1,1            ;2*c
SUB BL,C1
ADD BL,Y
SHR Y,CL
ADD BL,Y
ADD MONTH,12
XOR AH,AH
MOV AX,MONTH
MOV DX,13
MUL DX
MOV CX,5
DIV CX
XCHG AX,BX
CBW
XCHG AX,BX
ADD BX,AX           ;int(c/4)-2*c+y+int(y/4)+int(13*(m+1)/5)
MOV AX,BX
MOV CL,7             ;计算出来的结果模取7
IDIV CL
CMP AH,0             ;AH > 0 跳转
JG ADD1
ADD AH,7             ;星期天N为7
ADD1:
MOV BL,AH
MOV N,BL
SUB MONTH,12        ;还原月份值
J3: POP DX
POP CX
POP BX
POP AX
RET
PDXQJ ENDP
CODES ENDS
END START

五、测试数据及其结果分析

(1)错误输入时如图所示:

说明:第一次输入的月份大于12月份,不符合实际;第二次输入的日期数大于6月最大天数30,不符合实际;第三次输入的月份小于1月份,不符合实际;第四次输入的日期数小于1,不符合实际;

(2)输入润年二月份日期时,显示正确,如图所示:

说明:输入的日期为20080219,通过判断发现2008年是闰年,所以2月份应该有29天,所以如图输出正确。

(3)输入平年二月份日期时,显示正确,如图所示:

说明:输入的日期为20090208,通过判断发现2009年是平年,所以2月份应该有28天,所以如图输出正确。

(4)输入普通日期时,显示正确,如图所示:

说明:输入的日期为20090208,所以6月份应该有30天,通过查看手机日历发现与图输出的日历信息一致。

六、课题完成过程中遇到的问题及解决方法

问题1:刚开始设计算每个月的第一天是星期几,运用蔡勒公式没有考虑年份后两位是00年的特殊情况,计算时将00减1,这样程序也会出现错误,所以

解决方法:增加了判断如果输入的年后两位是00的语句,计数时要将上一年的年数用99进行替代。

问题2:设计算每个月的第一天是星期几,运用蔡勒公式没有考虑到1月和2月的特殊情况,导致程序运行时其他的月准确但是1月和2月出现错误。

解决方法:增加了判断一二月份的语句,如果输入的是1月和2月就按上一年的13月和14月进行计算。

问题3:当每个月的第一天是星期天的时候,出现多打一行的格式错误。

解决方法:增加了CMP B,35   JC POS  SUB LINE,1三条语句,如果B<35,执行POS,既如果每个月一号是星期天,显示一号的行号减1。

问题4:设计算每个月的第一天是星期几,运用蔡勒公式计算出来结果模7,但之后把表示每月第一天是星期天的N设为7(这时候算出来星期天的N设为0,导致矛盾)

解决方法:增加了CMP AH,0    JG ADD1   ADD AH,7三条语句,表示如果结果大于0跳转。当结果为0,把AH加7,这样星期天对应的N为7。

  • 4
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值