题二代码:
下面的版本和1楼写的不一样,为了代码的重用,主要几个功能都封装成子程式,
利用参数传递就能使用。
也因为要封装,所以用了invoke和local区域变量,须用masm6.x以后版本编译
编译方法: ml xxxx.asm
几个有点用的子程序,可以在其他程式使用
1.
invoke GetmonthDay, month
输入月份得出该月份最大天数,month可以是暂存器,变量或立即数,可混用。
比如:
invoke GetmonthDay,ax ;ax=输入月份,传回该月最大天数
invoke GetmonthDay,12 ;输入月份=12(立即数),传回12月最大天数=31
2.
GetTotalDay year,month,day
输入年月日,得出该日期和1000/1/1相差的总天数由dx:ax传回
year,month和day可以是暂存器,变量或立即数,可混用
比如:
invoke GetTotalDay,ax,bx.cx ;ax=年,bx=月,cx=日
invoke GetmonthDay,2016,12,29 ;传回该日期1000/1/1相差天数,由dx:ax传回
大家或者有疑问,这子程序只有这条题目才适用吧,比如只想计算某年某月某日的总天数,岂不得物无所用?
换一个想法,把该年该月该日当参数求得A值,该年1月1日作参数求得B值,A-B+1,就是该年该月该日的总天数了。
虽然是绕了一个圈子,但高阶语言/WINDOW API等,呼叫一个函数难道绕的圈子少了,这是封装程序无可避免的代价。
3.
invoke GetDate,address
address缓冲地址,传回ax=年,bx= 月,cx=日,dx= 0 (非闰年), =1(闰年)
用法:
先在资料区定义一个缓冲
InputBuffer db 11,0,11 dup (0)
...
...
lea mov si,InputBuffer
invoke GetDate,si
若使用者若输入31/11/2016
则ax=2016,bx=11,cx=31,dx=1(闰年)
若使用者输入29/2/2015或31/11/2018等错误日期,则传回cf=1
4.
LeapYear子程序
使用方法
mov ax,年份
call LeapYear ;传回AX=1(闰年),AX=0(非闰年)
程序代码:
;程式功能:输入两个日期,计算两个日期相差天数
;因用了invoke和local区域变量,须用masm6.x以后版本编译
;编译方法: ml xxxx.asm
;
.MODEL small,stdcall
.data
InputBuffer db 11,0,11 dup (0)
FirstStr db 10,13,'Input First Date(dd/mm/yyyy):','$'
SecondStr db 10,13,'Input Second Date(dd/mm/yyyy):','$'
DayDiffStr db 10,13,'Difference:'
DayDiffNum db 10 dup (20h)
DaysStr db ' day(s)$'
DaysStr_len equ $ - offset DaysStr
UserTotaldate dd 0,0
InputCount dw 0
.stack 100h
.code
;---------------------------------------------------------------------
; ------ 主程式在后段,请由start: 开始阅读------------------
;---------------------------------------------------------------------
;输入月份,传回ax=该月天数,若输入不符(1-12之外),cf=1
GetmonthDay proc near GiveMonth:word ;(1-12)
push bx
mov bx,GiveMonth
dec bx
cmp bx,11
ja GetMdxErr
mov ax,28
cmp bx,1 ;是否2月
jz GetMDok ;是
cmp bx,6 ;少于等于7月
jbe GetMD10
inc bx ;大于7月加1
GetMD10:mov ax,30
test bx,1
jnz GetMDok ;奇数不加ax=30
inc ax ;偶数加1,ax=31
GetMDok:clc ;ax=月份天数
jmp short GetMDxErrx
GetMDxErr:
stc
GetMDxErrx:
pop bx
ret
GetmonthDay endp
;-------------------------------------------------------------------
GetTotalDay proc near xyear:word,xmonth:word,xday:word
local TotalHi,TotalLo,LLyear:word ;区域变量(32bit总天数),闰月调整(0或1)
xor ax,ax
mov word ptr TotalLo,ax ;初始化
mov word ptr TotalHi,ax ;初始化
mov word ptr LLyear,ax ;初始化
mov ax,xyear ;取年份
call LeapYear ;输入AX=年份,输出AX=1(闰年),AX=0(非闰年)
or ax,ax
jz GetTDA
mov word ptr LLyear,1
GetTDA:mov di,xyear ;取年份
GetTD0:cmp di,1000 ;和基准1000比较
ja GetTD3 ;大于
jz GetTD4 ;等于,则不处理输入年份和1000的相差
; di = less then 1000
mov si,+1 ;预设加
mov ax,di ;取得输入年份
GetTD1:call LeapYear ;是否闰年
mov bx,365 ;一年多少天
or ax,ax ;是否闰年?
jz GetTD2 ;不是
inc bx ;是闰年,365+1
GetTD2:
add word ptr TotalLo,bx ;加总日期
adc word ptr TotalHi,0 ;带进位加
add di,si ;输入年份 加或减1 , 用以接近1000
jmp short GetTD0
GetTD3: ;大于1000到此
lea ax,[di-1] ;本年不计,取前一年值,若di=2017,lea ax,[di-1]后, ax=2016
mov si,-1; ;预设减,, 用以接近1000
jmp short GetTD1 ;回去计算闰年
GetTD4: ;得该年和1000/1/1相差年日数
;取得第一输入年月日,且已计算 和1000/1/1相差年日数
mov dx,1 ;以下计算该年总天数
xor ax,ax
xor si,si
mov dx,1 ;由1月起
cmp xmonth,dx ;输入月份是否1月
jz GetTD7 ;是
GetTD5:cmp dx,xmonth
jae GetTD7
invoke GetmonthDay, dx ;以月份(dx)查找该月天数
cmp dx,2 ;是否2月
jnz GetTD6 ;不是
add ax,LLyear ;若闰月调整 28->29
GetTD6:add si,ax ;累加
inc dx ;下一月
jmp short GetTD5
GetTD7:mov ax,si ;取加总
add ax,xday ;加上输入天数
;得该年总天数
add word ptr TotalLo,ax ;加总日期
adc word ptr TotalHi,0 ;带进位加
mov dx,TotalHi
mov ax,TotalLo ;总天数由dx:ax传回
ret
GetTotalDay endp
;--------------------------------------------------------------------------
;转值子程序,把输入的10进制文字转成16进制
;输入:ds:si数字字符串起点,以0dh结束
;输出:ax=转换后的16进制值,cf=1表示有非数字字符
GetValue:
push bx
push di
xor bx,bx
mov di,10
GetV10:lodsb ;指向起点
mov ah,0 ;清除
;cmp al,0 ;0dh ;回车?
cmp al,0 ;-----zero
jz Getvx
cmp al,'0' ;以下比较0-9,否则cf=1离开
jb Getvy
cmp al,'9'
ja Getvy
sub al,'0' ;ascii -> 值
xchg bx,ax ;交换
mul di
add bx,ax ;累加
jmp short GetV10
Getvx:
mov ax,bx ;ascii转值后由ax转回
clc ;成功cf=0
jmp short Getvz
Getvy:
stc ;错误 cf=1
Getvz:
pop di
pop bx
ret
;------------------------------------
;以下程序送出dx:ax的32bit结果到di,也可改成印出
;output DX:AX,dword (0-FFFFFFFFh / 0-4294967295)
print_dec: xor cx,cx
xchg bp,dx
mov si,10 ;div by 10
mov bx,30h
print_dec1: or bp,bp
jz print_dec3
xchg bp,ax
xor dx,dx
div si
xchg bp,ax
div si
or dl,bl
push dx
inc cx
jmp short print_dec1
print_dec3: xor dx,dx
div si
or dl,bl
push dx
inc cx
or ax,ax
jnz print_dec3
print_dec4: pop ax
stosb
loop print_dec4
ret
;--------------------------------------------------------------
GetDate proc near BufferAdd:word
local Uyear,Umonth,Uday,LimitDay,splitCount,LYear:word
local UserInputAddress[4]:word
mov word ptr splitCount,0 ; [/]数目初始化
mov word ptr LYear,0 ;闰年初始化
mov dx,BufferAdd
mov di,dx
mov ax,0C0Ah
int 21h
add di,2 ;指向字串符位置
xor cx,cx
mov cl,[di-1] ;取实际输入数
mov bx,cx
or cx,cx
jnz okinput
mov ax,0 ;没有输入
jmp short GetdataXX
GetdataX:
mov ax,-1 ;错误输入
GetdataXX:
stc
ret
okinput:
mov byte ptr [di+bx],0 ;0dH ->0
mov al,'/' ;分隔符
lea bx,UserInputAddress
retry:
repnz scasb
jnz NotFound
mov ss:[bx],di
mov byte ptr [di-1],0 ;清除分隔符
inc splitCount ;加分隔符数目
add bx,2 ;下一地址
or cx,cx ;完了?
jnz retry ;没
NotFound:
cmp splitCount,2 ;只容许2个[/]分隔符
jnz GetdataX ;不符
mov si, UserInputAddress[2] ;取年
call GetValue ;取值
cmp ax,4000 ;以下比较限制1000-4000
ja GetdataX
cmp ax,1000
jb GetdataX
mov Uyear,ax ;存
mov si,UserInputAddress[0] ;取月
call GetValue ;传回AX(月)
mov Umonth,ax ;存
invoke GetmonthDay,ax ;取该月天数限制
jc GetdataX ;不符(1-12)
mov LimitDay,ax ;存天数限制
mov si,BufferAdd
add si,2 ;指向日期
call GetValue ;取值
mov Uday,ax ;存输入日期
mov ax,Uyear ;取输入年份
Call LeapYear ;计算是否闰年子程序,传回1表示闰年,0表示非闰年
or ax,ax ;是闰年不?
jz GetTD ;不是闰年
mov word ptr LYear,1 ;闰年
cmp Umonth,2 ;二月?
jnz GetTD ; 不是二月
inc LimitDay ;闰年,2月份的限制+1
GetTD:
mov ax,Uday ;使用者输入日期
cmp ax,LimitDay ;比较输入日期和该月份天数限制
ja GetdataX ;输入日期大于月份限制, 再输入
clc ;所有日期输入正常
mov ax,Uyear ;传回年
mov bx,Umonth ;传回月
mov cx,Uday ;传回日
mov dx,LYear ;闰年与否...0=不是,1=是
ret
GetDate endp
;-------------------------------------------------------------------
LeapYear: ;输入AX=年份,输出AX=1(闰年),AX=0(非闰年)
push bx
push dx
push si
mov si,ax
mov bx,400 ;年份除以400余数等于零,为闰年
mov dx,0
div bx
or dx,dx ;是否0
jz last ;余数为零,直接输出闰年
mov dx,0
mov bx,4 ;除以400余数不等于0,进行除以4余数是否等0的判断
mov ax,si
div bx
or dx,dx ;是否0
jz ok ;除以四余数等于0,跳往除以100余数是否等0的判断
over: ;除以四余数不等于0,则不为闰年,结束
mov ax,0 ;不是闰年
jmp nil
ok:
mov bx,100 ;除以100余数为零,则不为闰年,余数不为零,则为闰年
mov dx,0
mov ax,si
div bx
or dx,dx ;是否0
jz over
last:
mov ax,1 ;闰年
nil:
pop si
pop dx
pop bx
ret
;----------------------------------------------------------------------------
;--------------------------主程式-----------------------------------------
;----------------------------------------------------------------------------
start:
MOV AX,@DATA
MOV DS,AX
MOV ES,AX
cld ;正向
;-------------输入/测试开始------------------------------------------------
mov inputCount,0 ;初始化
TryInput:
cmp inputCount,0 ;根据inputCount决定显示第1/第2字符串
lea dx,FirstStr
jz Whoinput
lea dx,SecondStr
Whoinput:
mov ah,9
int 21h
lea si,InputBuffer ;输入缓冲,要求输入格式(dd/mm/yyyy)
invoke GetDate,si ;读取输入子程序,传回ax(年),bx(月),cx(日),dx(闰年)
jnc Nextin ;正常
or ax,ax ;没有输入
jz quit ;离开
jmp short TryInput ;输入错误
Nextin:
invoke GetTotalDay,ax,bx,cx ;输入年,月,日....传回dx:ax=总天数 (由输入日期至1000/1/1)
mov bx,inputCount
shl bx,1 ; x 2
shl bx,1 ; x 4
mov word ptr UserTotaldate[bx],ax ;根据inputCount存入总天数
mov word ptr UserTotaldate[bx+2],dx
inc inputCount ;next input ?
cmp inputCount,2
jb TryInput ;second输入
;-------------输入/测试完成------------------------------------------------
mov ax,word ptr UserTotaldate
mov dx,word ptr UserTotaldate+2 ;取第1输入值入:dx:ax
mov bx,word ptr UserTotaldate+4
mov cx,word ptr UserTotaldate+6 ;取第2输入值入:cx:bx
cmp dx,cx ;比较高位
ja noChg ;大于
jb xxchg ;少于
;-- 32高位相等
cmp ax,bx ;比较低位
jae noChg ;大于等于不交换
xxchg:
xchg ax,bx ;少于则交换
xchg dx,cx
noChg:
sub ax,bx
sbb dx,cx
mov di,offset DayDiffNum
Call print_dec ; 输出结果至字符串
mov si,offset DaysStr
mov cx,DaysStr_len
rep movsb
mov dx,offset DayDiffStr ;印结果
mov ah,9
int 21h
mov ah,7 ;暂停
int 21h
quit:
mov ax,4c00h ;离开
int 21h
;--------------------------------------------------------------------------
end start
[此贴子已经被作者于2016-7-3 11:25编辑过]