1、原题再现:
输入一个学生的身份证号码,程序检验身份证号码是否合法。并显示省份、年龄、性别等信息。
下面是身份证号码的规则:
身份证号码共18位。前6位是地址码,7–14位是出生年月日,后面4位是生成的代码,第17位用来做性别判定: 偶数 – 女 ; 奇数— 男 。
第十八位数字的计算方法为:
1)、 将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为:7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2
2)、将这17位数字和系数相乘的结果相加。
3)、用加出来和除以11,看余数是多少?
4)、余数只可能有0 1 2 3 4 5 6 7 8 9 10这11个数字。其分别对应的最后一位身份证的号码为1 0 X 9 8 7 6 5 4 3 2。
5)、通过上面得知如果余数是2,就会在身份证的第18位数字上出现罗马数字的Ⅹ。如果余数是10,身份证的最后一位号码就是2。
2、设计思路
功能主要分为两个部分,一是判断是否合法,二是输出身份信息,包括省份,出生日期,性别,年龄。
判断合法:包括判断:①首两位省份信息是否合法,即判断是否有省份的编码是输入的首两位。
②判断出生日期是否合法。首先判断月份数是不是合理,合理的话就判断接下来日期是否合理:先判断是不是二月,不在二月的话就只要判断日期数是否在最大与最小值之间即可;是二月就需要判断是否是闰年了:先判断日期是否>29或者<0,是的话就不合法,不是的话再判断是不是29,是的话,就判断前面年份数是否是闰年,是的话就合法,否则不合法。③判断第十八位是否合法。直接将十七位乘上系数再累加,最后除以11,根据余数判断尾数是否合法。④判断长度是否合法。直接将输入字符串的id[1],即实际输入长度(不包括回车)与18比较即可。
输出身份信息:输出省份信息、出身日期、年龄、性别。
最终界面:
3、具体实现
运行环境:DOSBox,怎么在DOSBox上运行汇编可参考:DOSBox里运行汇编程序
部分代码:(完整版在最后)
stack segment stack
db 200 dup(0)
stack ends
data segment
id db 20,?,20 dup('$')
info1 db 'Please input your id:$' ;提示输入id
info2 db 'Your province is:$' ;输出省份信息
info3 db 'The number length is incorrect!$' ;输入身份证号码位数不对
info4 db 'The id number is of correct length!!$' ;长度正确
info5 db 'Id card number valid!!$' ;进行多重检查后最后提示检查合格
info6 db 'your gender is:$' ;输出性别
info7 db 'woman!$'
info8 db 'man!$'
info9 db 'The id number ends correctly!$' ;身份证号尾数正确
info10 db 'The last digit of the id number is incorrect!$' ;尾数不正确
info11 db 'Province information exists!$' ;省份信息存在
info12 db 'The province information does not exist!$' ;省份信息不存在
info13 db 'The current year is a leap year! Reasonable year of birth!$' ;闰年
info14 db 'The current year is not a leap year! The year of birth is not reasonable!$' ;不是闰年
info15 db 'Reasonable date of birth!$' ;出生日期合理
info16 db 'The date of birth is not reasonable!$' ;出生日期不合理
info17 db 'Congratulations, the id number entered is legal and valid!$' ;最终判断合法且有效
info18 db 'Your identity information is:$' ;身份证信息为
info19 db 'Your date of birth is:$' ;输出出生日期
info20 db 'Your age is:$' ;输出年龄
province db 11h,'BJ',12h,'TJ',13h,'HE',14h,'SX',15h,'NM'
db 21h,'LN',22h,'JN',23h,'HL',31h,'SH'
db 32h,'JS',33h,'ZJ',34h,'AH',35h,'FJ',36h,'JX'
db 37h,'SD',41h,'HA',42h,'HB',43h,'HN'
db 44h,'GD',45h,'GX',46h,'HI',50h,'CQ',51h,'SC'
db 52h,'GZ',53h,'YN',54h,'XZ',61h,'SN'
db 62h,'GS',63h,'QH',64h,'NX',65h,'XJ',71h,'TW'
db 81h,'HK',82h,'MO',91h,'FOREIGN';省份编号
coef db 7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2 ;计算第18位数字时的系数
final db 1,0,'X',9,8,7,6,5,4,3,2 ;第18位可能的取值
leapyear db 31,29,31,30,31,30,31,31,30, 31,30,31 ;闰年每月的天数
notleapyear db 31,28,31,30,31,30,31,31,30,31,30,31 ;平年每月的天数
divisors dw 10000,1000,100,10,1 ;将ax中的16进制年龄转成十进制然后输出
result db 0,0,0,0,0,'$' ;存放年龄
data ends
code segment
assume cs:code,ds:data,ss:stack
checkbirth MACRO ;判断出生日期是否合法
local no
local judge
local yes
local ff
local leap
clc
push ax
push bx
push cx
push dx
push si
push di
xor ax,ax
xor bx,bx
xor cx,cx
xor dx,dx
mov al,id[12] ;获取四位出生有效日期,不含年份
mov bl,id[13]
mov cl,id[14]
mov dl,id[15]
sub al,30h ;01h
sub bl,30h ;02h
sub cl,30h ;00h
sub dl,30h ;08h
mov di,10
mov si,dx
mul di ;dx:ax=ax*di
add ax,bx
mov bx,ax ;bx存储月份数12=c
mov ax,cx
mul di
add ax,si ;ax里面存储着天数8=8
mov si,12
cmp bx,si
ja no ;月份数大于12,不合理,直接退出
mov si,0
cmp bx,si
jbe no ;月份数<=0,不合理,直接退出
jmp judge ;月份数都合理,转向判断日期的部分
no:display info16 ;出身日期不合理,请重新输入
clc
stc
jmp exit
yes:display info15 ;出生日期合理
clc
jmp exit
judge:xor cx,cx ;先根据月份数找到可能的两种日期,即平年和闰年,分别存在cx,dx里面
xor dx,dx
mov si,bx
mov cl,leapyear[si-1] ;两种可能的天数
mov dl,notleapyear[si-1]
cmp cl,dl
jz ff ;两个日期数相同,就不需要判断是否为闰年了,只需要判断大小月是否满足
mov si,29 ;该月份为2月
cmp ax,si
ja no ;大于29显然不合理
mov si,0
cmp ax,si
jbe no ;<=0显然也不合理,否则就要判断是否是闰年了
mov si,29
cmp ax,si
jz leap ;日期为29则判断是不是闰年
jmp yes ;否则合理
leap:checkleapyear
jc yes ;返回CF=1说明是闰年,出生日期有效
jnc no
ff: cmp ax,cx ;两个可能的天数一致,只需要判断大小月,即只需要判断天数是否在有有效区间内
ja no ;大于可能的最大日期,直接判断不合理
mov si,0
cmp ax,si
jbe no ;<=0也不合理
jmp yes ;否则一定会理的
exit:pop di
pop si
pop dx
pop cx
pop bx
pop ax
ENDM
checkleapyear MACRO ;判断是不是闰年
ENDM
checkend MACRO ;检查第十八位是否合法
ENDM
checkgender MACRO
local woman
local man
local exit
push bx
xor bx,bx
mov bl,id[18] ;第17位,表示性别
display info6
test bl,01h ;与1进行与运算判断奇偶
jz woman
lea dx,info8
mov ah,09h
int 21h
jmp exit
woman:lea dx,info7
mov ah,09h
int 21h
exit:pop bx
ENDM
checklength MACRO ;检查长度是否合格
local corr
local wrong
local exit
clc
push ax
mov al,[id+1] ;id字符串的长度存在地址首位的下一位
cmp al,18
jnz wrong
corr:display info4
jmp exit
wrong:display info3
clc
stc ;cf=1表示长度不正确,继续输入
exit: pop ax
ENDM
checkprovince MACRO ;检查前两位省份信息是否正确
ENDM
display MACRO x ;输出字符串并换行
push ax
push dx
newline
mov ah,09h
lea dx,x
int 21h
pop dx
pop ax
ENDM
dispchar MACRO x ;输出单个字符并换行
push dx
push ax
newline
mov ah,02h
mov dl,x
int 21h
pop ax
pop dx
ENDM
dispchar2 MACRO x ;输出单个字符不换行,用于输出省份信息
push dx
push ax
mov ah,02h
mov dl,x
int 21h
pop ax
pop dx
ENDM
assign MACRO r1,c1,r2,c2 ;这里犯了一个致命错误,就是把cx,dx压入堆栈,导致数据初始化不成功
mov ch,r1
mov cl,c1
mov dh,r2
mov dl,c2
ENDM
cls MACRO r1,c1,r2,c2,x ;清屏并将光标置于左上角
push ax
push bx
push dx
mov ah,06h
mov al,0
mov bh,x
assign r1,c1,r2,c2 ;设置入口值
int 10h
mov ah,02h ;置光标位置
mov bh,0
mov dh,0
mov dl,0
int 10h
pop dx
pop bx
pop ax
ENDM
getprovince MACRO ;根据id获取省份信息
ENDM
getbirth MACRO ;获取出生日期
ENDM
getage MACRO ;获取年龄
ENDM
newline MACRO ;回车换行
push ax
push dx
mov ah,02h
mov dl,0ah
int 21h
mov ah,02h
mov dl,0dh
int 21h
pop dx
pop ax
ENDM
main: mov ax,data ;主程序
mov ds,ax
cls 0,0,99,99,0bh
clc ;先将cf清零,下面备用
agn:newline
display info1
mov ah,0ah ;不合理则循环输入
lea dx,id
int 21h
checkprovince ;若不合理就让cf=1
jc agn
checklength ;同样长度不为18就继续要求输入
jc agn
checkbirth ;出生日期不合理
jc agn
checkend
jc agn
newline
display info17 ;提示有效
newline
display info18 ;开始显示信息
mov cx,30
newline
line1:mov dl,2dh ;显示分割线------
mov ah,02h
int 21h
loop line1
getprovince ;显示身份信息
getbirth ;显示出身日期
getage ;显示年龄信息
checkgender ;显示性别信息
newline
mov cx,30
line2:mov dl,2dh ;显示分割线------
mov ah,02h
int 21h
loop line2
mov ah,4ch
int 21h
code ends
end main