万年历的8086汇编实现

 
设计题目:
用汇编语言编写一个打印月历的程序,要求用户在开始输入年、月。以后可以通过按下上下键来改变年份;左右键来改变月份。且每月的星期六、日用红色显示出来。
程序思路:
程序分为两大模块:主程序和具体打印部分。主程序负责处理用户的年、月输入;监测键盘动作,并把得到的信息处理成相应的入口参数送给打印模块。打印模块接受主程序的调用,对给定的年、月,打印出相应的月历。并且,处理界面上的要求。
程序中用到的算法(计算某一天是星期几):
     蔡勒公式: W = [C/4] - 2C + y + [y/4] + [13 * (M+1) / 5] + d - 1
C是世纪数减一,y是年份后两位,M是月份,d是日数。1月和2月要按上一年的13月和14月来算,这时C和y均按上一年取值。
计算出来的结果模取7。  
 
具体设计处理:
程序中多处使用了子程序,在主程序接受用户的年、月输入时,用GetYearMonth,此子程序把得到的合法输入放到数据段中的year和month变量中。所谓的合法变量是指输入的是数字字符(不是字母或者其他的字符)。否则,程序提示用户输入错误,接受用户的再次输入,直到用户输入的信息合法或者按下ESC键为止。
在每次得到合法的年、月后,就调用打印模块的display函数,程序以后就一直在这里循环,每次调用BIOS的16h号中断的0号功能,读取键盘的一个按键,并根据题目要求对上下左右键做出相应的动作。直到用户按下ESC为止。
在打印模块的display中,首先要把display设为public属性,以示此过程要被别的模块调用。display的入口参数是BX(year),DL,(month),进入display后首先做一下现场的保护,然后把DS的值切换到打印模块里来。接着把BX,DL里的值放到内存变量中,以腾出BX,DL来使用。下面开始设置程序运行的界面:每次都从DOS窗口的(0,0)处开始显示,所以把光标每次开始都设置在(0,0)处。在设置整个屏幕上的显示属性,使用10h号中断的6号功能。BH的性质如下:
bit0~2 : 字体颜色 (0:黑,1:蓝,2:绿,3:青,4:红,5:紫,6:综,7:白)
bit3 : 字体亮度 (0:字体正常,1:字体高亮度)
bit4~6 : 背景颜色 (0:黑,1:蓝,2:绿,3:青,4:红,5:紫,6:综,7:白)
bit7 : 字体闪烁 (0:不闪烁,1:字体闪烁)
接下来设置星期六、日两列的字体为红色。也是使用10h中断的6号功能。
然后打印出当前显示的是哪一年的哪一月,调用dispYM,即打印两个十进制数。
然后就可以打印出MON,TUR等字样,以标注哪天是星期几。
然后,计算出当前月的天数,调用GetMonthSize,把得到的结果放到数据段的MonthSize中。如果是1、2月,要根据 蔡勒公式进行变换。然后是计算当前月份的第一天是星期几的过程。计算出之后在屏幕上留下相应的空格(为了对齐!)。
剩下的就是根据Monthsize的大小进行循环的输出日期了,调用dispWeek,在此过程立,要注意一位数与两位数对格式的影响。另外,打印到星期天要输出换行。
具体程序代码
# 主模块:
; 月历打印
; 主程序设置好入口参数, BX= 年份, DL= 月份
; 调用子程序 display
;By wangrui
;2006-12-8
 
extrn display:far
 
Esccode   equ    01h
Up    equ 048h
Down equ    050h
Left equ 04bh
Right equ       04dh
 
dseg segment
      Year     dw 0
      Month    db 0
      temp     db 10 dup(0)
      count dw ?
      ErrMsg   db 0dh,0ah,"The input NOT decimal! $"
dseg ends
 
cseg segment
      assume cs:cseg,ds:dseg
start:
      mov ax,dseg
      mov ds,ax
 
      call GetYearMonth
 
Ws:
      mov bx,Year
      mov dl,Month
      call far ptr display
again:
      mov ah,0
      int 16h
      cmp ah,Esccode
      je Exit
      cmp ah,Up
      je NextY
      cmp ah,Down
      je PreY
      cmp ah,Left
      je PreM
      cmp ah,Right
      je NextM
      jmp again
 
NextY:
      inc Year
      jmp Ws
PreY:
      dec Year
      jmp Ws
NextM:
      inc Month
      cmp Month,12
      jbe skip0
      mov Month,1
      inc Year
skip0:
      jmp Ws
PreM:
      dec Month
      cmp Month,1
      jae skip1
      mov Month,12
      dec Year
skip1:
      jmp Ws
 
Exit:
      mov ah,4ch
      int 21h
 
;**************************************************
 
GetYearMonth      proc    near
      push ax
      push cx
      push si
      push di
 
inputagain:
      mov Year,0
      mov Month,0
 
      mov si,0
repeatY:
      mov ah,1
      int 21h
      cmp al,0dh
      je   EndY
      cmp al,20h
      je   EndY
      cmp al,1bh
      je   ExitDos0
      cmp al,30h
      jb   Err
      cmp al,39h
      ja   Err
      sub al,30h
      mov temp[si],al
      inc si
      jmp repeatY
 
Err:
      mov ah,9
      lea dx,ErrMsg
      int 21h
      mov ah,2
      mov dl,0dh
      int 21h
      mov dl,0ah
      int 21h
      jmp inputagain
 
EndY:
      mov bx,10
      mov di,si
      mov si,0
 
NextYBit:
      mov ah,0
      mov al,temp[si]
      mov count,di
      sub count,si
      dec count
      mov cx,count
      jcxz skipY
lopmul: mul bx
      loop lopmul
skipY: add Year,ax
      inc si
      cmp si,di
      jne NextYBit
     
;----------------------------------The year is put into [Year]
 
      push dx
      mov ah,2
      mov dl,0dh
      int 21h
      mov dl,0ah
      int 21h
      pop dx
      jmp skiplap
 
ExitDos0:
      jmp ExitDos
 
skiplap:
      mov si,0
repeatM:
      mov ah,1
      int 21h
      cmp al,0dh
      je   EndMon
      cmp al,20h
      je   EndMon
      cmp al,1bh
      je   ExitDos
      cmp al,30h
      jb   Err
      cmp  al,39h
      ja   Err
      sub al,30h
      mov temp[si],al
      inc si
      jmp repeatM
 
EndMon:  
      mov di,si
      mov si,0
      mov bl,10
NextMBit:
      mov al,temp[si]
      mov count,di
      sub count,si
      dec count
      mov cx,count
      jcxz skipM
lpmul: mul bl
      loop lpmul
skipM: add Month,al
      inc si
      cmp si,di
      jne NextMBit
 
;-------------------------The Month is put into [Month]
 
      push dx
      mov ah,2
      mov dl,0dh
      int 21h
      mov dl,0ah
      int 21h
      pop dx
 
      pop di
      pop si
      pop cx
      pop ax
      ret
ExitDos:
      mov ah,4ch
      int 21h
GetYearMonth endp
 
cseg ends
      end start
 
# 打印模块:
;***********************************
; 入口参数: bx = Year dl=Month
;***********************************
 
public display
data segment
      Year         dw ?
      Month       db ?
      leap     db ?
      weekstr db " MON TUR WEN THU FRI SAT SUN",'$'
      fillblank db "     $"
      fillblank4 db "    $"
      fillblank3 db "   $"
      c        db ?
      y        db ?
      firstday db ?
      temp  db ?
      MonthSize db ?
data ends
 
cseg segment
      assume cs:cseg,ds:data
display proc   far
      push ds
 
      mov ax,data
      mov ds,ax
 
      mov Year,bx
      mov Month,dl
 
      mov dx,0
      xor bh,bh
      mov ah,2
      int 10h
 
      mov ah,6
      mov al,0
      mov bh,01110000b
      mov ch,0
      mov cl,0
      mov dh,24
      mov dl,79
      int 10h
 
      mov ah,6
      mov al,0
      mov bh,01110100b
      mov ch,0
      mov cl,25
      mov dh,24
      mov dl,79
      int 10h
 
      call far ptr dispYM
     
      lea dx,weekstr
      mov ah,9
      int 21h
      mov ah,2
      mov dl,0dh
      int 21h
      mov dl,0ah
      int 21h
 
      call GetMonthSize      ; 把当前月份的天数放到 MonthSize
 
      cmp Month,2
      ja skip
      add Month,12
      dec Year
skip:
      mov bl,100
      mov ax,Year
      div bl
      mov c,al
      mov y,ah
      mov cl,2
      mov bl,c
      shr bl,cl    ;int(c/4)
      shl c,1       ;2*c
      sub bl,c    
      add bl,y
      shr y,cl     ;int(y/4)
      add bl,y    ;int(c/4)-2*c+y+int(y/4)--->bl
      inc Month
      xor ah,ah
      mov al,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
      cmp ah,0
      jG skipAdd
      add ah,7
skipAdd:
      mov bl,ah
      mov firstday,bl
 
;-------------------------------- 计算出当前月份的第一天是星期几
 
      mov cl,bl
      mov ch,0
      dec cl
      jcxz skipF
FillB:   mov ah,9
      lea dx,fillblank
      int 21h
      loop FillB
skipF:
      mov cl,1
disLop: call dispWeek           ; 日期已经放入 cl
      inc cl
      inc bl
      cmp bl,8
      jb skipModle
     
      mov bl,1
      mov ah,2
      mov dl,0dh
      int 21h
      mov dl,0ah
      int 21h
 
skipModle:
      cmp cl,MonthSize
      jbe dislop
     
      mov ah,2
      mov dl,0dh
      int 21h
      mov dl,0ah
      int 21h
 
      pop ds
      ret
display endp
 
;******************************************************************
 
GetMonthSize proc    near
      push ax
      push dx
 
      mov leap,0
      mov ax,Year
      mov ch,00000011b
      and ch,al
      mov bl,100
      div bl
      mov cl,ah
      cmp ch,0
      jne skipNLeap
      cmp cl,0
      jne skipLeap
 
      mov ax,Year
      mov bx,400
      mov dx,0
      div bx
      cmp dl,0
      je skipLeap
skipNLeap:
      jmp gmsize
skipLeap:
      inc leap
gmsize:
      mov al,Month
      cmp al,2
      je return2
      cmp al,8
      jb skipInc
      inc al
skipInc:
      test al,1
      jz   returnEven
      mov MonthSize,31
      jmp return
returnEven:
      mov MonthSize,30
      jmp return
 
return2:
      mov MonthSize,28
      cmp leap,0
      je return
      inc MonthSize
return:     
      pop dx
      pop bx
      ret
GetMonthSize endp
 
;*****************************************************************
 
dispWeek proc    near
      push cx
      push bx
      push ax
      push dx
 
      cmp cl,10
      jae DoubleDig
 
      lea dx,fillblank4
      mov ah,9
      int 21h
      mov dl,cl
      add dl,30h
      mov ah,2
      int 21h
      jmp back
     
DoubleDig:
      mov ch,10
      mov ah,0
      mov al,cl
      div ch
 
      mov cx,ax
      mov ah,9
      lea dx,fillblank3
      int 21h
      mov dl,cl
      add dl,30h
      mov ah,2
      int 21h
      mov dl,ch
      add dl,30h
      int 21h
back:
      pop dx
      pop ax
      pop bx
      pop cx
      ret
dispWeek endp
;*********************************************************
 
dispYM      proc    far
      push bx
      push dx
 
      mov ax,year
      mov dh,10
      xor si,si
next:   div dh
      mov cl,ah
      mov ch,0
      inc si
      push cx
      mov ah,0
      cmp ax,0
      jne next
 
      mov cx,si
repeat:      pop dx
      mov ah,2
      add dl,30h
      int 21h
      loop repeat
 
      mov dl,0dh
      mov ah,2
      int 21h
      mov dl,0ah
      mov ah,2
      int 21h
 
      mov al,Month
      mov ah,0
      mov bl,10
      div bl
      mov cx,ax
      mov dl,cl
      mov ah,2
      add dl,30h
      int 21h
      mov dl,ch
      add dl,30h
      int 21h
 
      mov dl,0dh
      mov ah,2
      int 21h
      mov dl,0ah
      mov ah,2
      int 21h
 
      pop dx
      pop bx
      ret
dispYM      endp
;****************************************************
cseg ends
      end
 
心得体会:
     这次程序设计我选的题目思路比较简单,容易编写,这是因为我考虑到在实现结构化的程序和高级算法时,汇编语言难以显示出它的优势,所以,我主要练习了语言本身(偷懒了! J )。但在此过程中也不是一帆风顺的,特别开始的时候我忘记了在不同模块之间数据段的切换,造成的错误使我莫名其妙了好一阵子,也花去了我很长的调试时间。这也显示了汇编语言的复杂和繁琐!但是,通过这次的程序设计,我还是学到了很多的东西,比如说DOS监测键盘的方法,设置显示界面的属性等,还有不怕麻烦的精神( L )。
                                                                                    2007-1-1
 

你可能感兴趣的:(万年历的8086汇编实现)