编程软件:Masm for Windows 集成实验环境 2015
宏和子程序部分已经被提取出来,原先程序已经详细注释了,只是稍作美化了一下,为了方便理解,建议读者先理解一下结构体,如何用地址的方法,准确定位到数据,虽然使用的是结构体存储数据,但是存取改数据,我个人还是使用的地址定位的方法,所以结构体后面的地址,是相对地址,只要理解地址如何取用数据,就会方便理解代码了;另外,本人编写过程中,传递的参数使用的极少,所以子程序调用的时候,可能有些寄存器内的数据已经在上一段程序中被定义好了,如果要理解程序,阅读起来可能有点乱,所以寄存器里面的数据如果能记住,读代码就会方便很多,主要需要记得是BX,DX,CX,SI,DI(BP只在查询学号并修改成绩中有用到) ,BX我主要是用来存结构体的首位地址,还有结构体中数据每一行的首地址,ADD BX,TYPE students 就是换行的方式,DX主要保存一些数据,CX则是计数器,SI有时候也作为计数器,SI和DI也会当做相对定位的指针,准确定位到每一个字节区域,用来配合BX的找出结构体中的数据,结构体数据存储方式如下
BX(结构体首地址) | DI(0) | DI(1) | DI(2) | DI(3) | DI(4) | DI(5) | DI(6) | DI(7) | DI(8) | DI(9) | DI(10) | DI(11) | DI(12) | DI(13) | ... | ... | ... |
BX+TYPE students | DI(0) | ... | ... | ... | ... | ... | |||||||||||
..... | |||||||||||||||||
..... | |||||||||||||||||
..... |
上面每一个小格就是一个字节单元,SNO:DI(0~8)为学号存储单元可存9个字节数据,NAME1:DI(9~12)为名字存储单元可存4个字节数据,GRADE:DI(13)为成绩单元存入0~100的成绩,所以只需要一个字节单元。理解上述部分,数据存储方式,程序就通俗易懂了,汇编主要还是理解代码在机器内的运作方式,只要懂得机器内如何运作代码,汇编就容易上手了。。。改变代码内的N值,可控制输入学生信息的个数,以下N=5。按空格结束,所有结束方式均为按空格。。。
运行结果如下图:
数据输入:
数据排序输出:
按学号查找并修改成绩:
退出程序:
1.宏定义部分
SHUCHU MACRO X
MOV AH,2
MOV DL,X
INT 21H
ENDM
S_SHUCHU MACRO Y
LEA DX,Y
MOV AH,9
INT 21H
ENDM
SHURU MACRO
MOV AH,1
INT 21H
ENDM
CHECK MACRO X
.IF X>'9' || X<'0' && X!=' '
S_SHUCHU HINT3 ;提示输入错误
SHURU ;让提示显示,避免被刷掉,按任意键返回主菜单
JMP RESTORE ;跳回主菜单
.ENDIF
ENDM
GR_SHUCHU MACRO X
MOV AL,X ;接收成绩
MOV AH,0 ;AX高位清0
MOV CL,10 ;除数,等会用来取余数和商
MOV DH,0 ;压栈前高位置0
.WHILE AL != 0 ;如果商不为0,则继续除10
DIV CL
MOV DL,AH ;余数置于DL
ADD DL,30H ;转换为ASCII码
PUSH DX ;压栈
INC DH
MOV AH,0 ;高位即余数清0
.ENDW
;循环完成取数操作,即从右到左,从个位数开始取数,一直到最高位停止取数
.IF DH == 0 ;判断输入成绩是否为0,若为0直接输出
SHUCHU 30H
.ELSE ;否则弹出栈中相应内容输出
MOV CL,DH
.WHILE CL
POP DX
SHUCHU DL
DEC CL
.ENDW
.ENDIF ;成绩输出结束
ENDM
2.子程序定义部分
GR_SHURU PROC
MOV CX,0
MOV DL,10
.WHILE CX<=100
SHURU ;键盘接收一个字符
CHECK AL ;检测输入的是否为数字
.IF AL == ' '
MOV [BX+DI],CL ;如果输入空格,则成绩接收结束,并存入对应储存单元,即结构体GRADE单元
RET
.ELSE
SUB AL,30H ;进行ASCII码转换为数字
CBW ;AL扩展为AX,高位清0
XCHG CX,AX ;交换数值
MUL DL ;AL×10放入AX中
ADD CX,AX ;将数字相加,得到新的数值,放入cl中
.ENDIF
.ENDW
S_SHUCHU WARING ;成绩输入不合格输出警告
RET
GR_SHURU ENDP
SHOW PROC NEAR
MOV AX,3 ;清屏
INT 10H
S_SHUCHU LIST
RET
SHOW ENDP
;菜单选项子程序
CHOICE PROC NEAR
SHURU ;调用宏输入一个字符
.IF AL=='1' ;调用输入功能
CALL INPUT
.ELSEIF AL =='2' ;调用输出功能,且排序
CALL RANK ;调用排序,对数据按成绩进行排序,由高到低
CALL OUTPUT
.ELSEIF AL=='3' ;调用查找功能
CALL SEARCH
.ELSEIF AL=='0' ;退出程序
JMP EXIT
.ELSE
S_SHUCHU WARING ;输出警告提示
.ENDIF
SHURU ;接收一个字符,用于暂停当前界面,按任意键刷新菜单
RET
CHOICE ENDP
INPUT PROC NEAR
S_SHUCHU HINT1
LEA BX,STU_ARRAY ;结构体初始化指针,行定位
MOV SI,0 ;统计输入的行数
.WHILE SI
MOV CX,9 ;循环,学号共定义9个字节空间
MOV DI,0 ;相对指针,用于定位对应的结构体内的元素,列定位
LPI:
SHURU
CHECK AL ;检测输入字符是否为数字
.IF AL == ' '
JMP XHI ;若输入空格,则跳到下一项输入
.ELSE
MOV [BX+DI],AL ;将学号逐个输入到对应的位置,最大9位
INC DI
.ENDIF
LOOP LPI
XHI:
SHUCHU ' '
MOV CX,4 ;姓名输入,最大可输入4个字符
MOV DI,9 ;结构体定义的NAME段位置定位
LPI1:
SHURU
.IF AL == ' '
JMP XMI ;输入空格表示输入下一项
.ELSE
MOV [BX+DI],AL ;对应位置存入相应字符,最多输入4位
INC DI
.ENDIF
LOOP LPI1
XMI:
SHUCHU ' '
MOV DI,13 ;定位到GRADE区域,成绩输入
CALL GR_SHURU ;调用子程序,成绩输入
ADD BX,TYPE students ;跳到下一行,即结构体数字的下一行,加上相应的结构体大小
INC SI ;行数统计自增一次
SHUCHU 13
SHUCHU 10
.ENDW
RET
INPUT ENDP
OUTPUT PROC NEAR ;该部分可参照输入子程序,理解地址所对应数据,方便理解代码
S_SHUCHU HINT2
LEA BX,STU_ARRAY
MOV SI,0
.WHILE SI
MOV DI,0
LPO:
MOV DL,[BX+DI]
SHUCHU DL
INC DI
LOOP LPO
SHUCHU ' '
MOV CX,4
LPO1:
MOV DL,[BX+DI]
SHUCHU DL
INC DI
LOOP LPO1
SHUCHU ' '
MOV DL,[BX+DI]
GR_SHUCHU DL
ADD BX,TYPE students
INC SI
SHUCHU ' '
MOV DX,SI ;获取当前的行数
GR_SHUCHU DL ;将行数转换为对应的ASCII码,输出对应的行数
SHUCHU 13
SHUCHU 10
.ENDW
RET
OUTPUT ENDP
RANK PROC NEAR
MOV CX,N-1 ;总的数据个数减一,即循环次数
.WHILE CX ;冒泡排序法
PUSH CX ;外循环次数保护,压栈
LEA BX,STU_ARRAY ;回到结构体的第一行
.WHILE CX
MOV DI,13
MOV DL,[BX+DI] ;取到的第一个值,与它的后一位值比较,对应到结构体为GRADE部分
MOV DH,[BX+DI+14] ;取到的第二个值,与其前一位比较,同上
.IF DL
MOV DI,0
.WHILE DI<14 ;结构体实际长度为14
XCHG AL,[BX+DI]
XCHG [BX+DI+14],AL ;.while内为前后数据交换部分
XCHG [BX+DI],AL
INC DI
.ENDW
.ENDIF
ADD BX,TYPE students ;跳到第二个位置与后一段数据比较,冒泡排序法
DEC CX ;内循环自减
.ENDW
POP CX
DEC CX ;外循环自减
.ENDW
RET
RANK ENDP
SEARCH PROC NEAR
S_SHUCHU FIND
LEA BX,STU_ARRAY ;取存储数据的首地址,即结构体首地址
LEA BP,SOURCE ;存放查找的学号的首地址
MOV CX,9 ;循环,学号共定义了9个字节空间
MOV DI,0 ;相对指针,用于定位需要存入元素,列定位
LPF:
SHURU
CHECK AL ;检测输入字符是否为数字
.IF AL == ' '
JMP FNXT ;若输入空格,则跳到下一项输入
.ELSE
MOV [BP+DI],AL ;将学号逐个输入到对应的位置,最大9位
INC DI
.ENDIF
LOOP LPF
FNXT:
MOV CX,N ;查找对比的个数,即总共的行数(学生数)
LPF1:
PUSH CX ;保护外循环次数,压栈
MOV CX,9 ;学号一共有9个存储区域
MOV DI,0
LPF2:
MOV AL,[BX+DI] ;结构体,已经存入的数据
MOV AH,[BP+DI] ;查找输入的学号
CMP AL,AH ;按位对比学号
JNZ FNXT1 ;如果有一个没有相等则跳过该学生,该学生不是要查找的学生
INC DI
.IF DI == 9
POP CX ;弹出多余的CX,避免无法返回,即RET执行和栈有关,需先弹出栈中多余的数据,避免程序运行出错
JMP FNXT2 ;如果查找到对应学号则跳出,执行修改成绩程序段
.ENDIF
LOOP LPF2 ;循环9次对比完一位的学号
FNXT1:
ADD BX,TYPE students ;指向下一个学生,即下一行数据区域
POP CX ;外循环被保护的CX,出栈
LOOP LPF1 ;一共对比N次,即和N个学生的学号比较
S_SHUCHU NFIND
RET
FNXT2:
S_SHUCHU HINT4
MOV SI,13 ;指针指向GRADE对应段
MOV DL,[BX+SI] ;get到原始成绩
GR_SHUCHU DL ;输出旧的成绩
S_SHUCHU HINT5 ;提示是否修改成绩
SHURU ;接收字符,判断是否修改成绩
.IF AL=='y'
S_SHUCHU RESULT
MOV DI,13
CALL GR_SHURU ;调用输入成绩子程序
.ENDIF
RET
SEARCH ENDP
源代码如下:
students STRUCT ;偏移量
SNO DB 9 DUP(?) ;0-8
NAME1 DB 4 DUP(?) ;9-12
GRADE DB ? ;13
students ENDS ;结构体共14个字节
DATAS SEGMENT
N = 5 ;数组大小定义
STU_ARRAY students N DUP(<>) ;结构体数组
JIANGE DD 10 DUP (?) ;数据溢出显示遮挡菜单
LIST DB 13,10
DB '*~*~*~*~*~*~*~LIST*~*~*~*~*~*~*~*~*~*~*',13,10
DB '@ 1.INPUT @',13,10
DB '@ 2.OUTPUT(RANK) @',13,10
DB '@ 3.FIND(SNO) @',13,10
DB '@ 0.QUIT @',13,10
DB '*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*',13,10
DB 'PLEASE INPUT YOUR CHOICE:$'
WARING DB 'INPUT ERROR!!!$'
HINT1 DB 13,10,'SNO NAME GRADE',13,10,'$'
HINT2 DB 13,10,'SNO NAME GRADE RANK',13,10,'$'
HINT3 DB 13,10,'INPUT ERROR! PLEASE AFRESH.',13,10,'$'
HINT4 DB ' OLD GRADE:$'
HINT5 DB 13,10,'ARE THE SCORES MODIFIED?(y/n)',13,10,'$'
FIND DB 13,10,'PLEASE ENTER THE (SNO) YOU WANT TO FIND,END BY SPACE:',13,10,'$'
NFIND DB 13,10,'NOT FOUND!!',13,10,'$'
RESULT DB 13,10,'PLEASE ENTER NEW GRADE:',13,10,'$'
SOURCE DB 9 DUP(?)
DATAS ENDS
;字符输出宏定义
SHUCHU MACRO X
MOV AH,2
MOV DL,X
INT 21H
ENDM
;字符串输出宏定义
S_SHUCHU MACRO Y
LEA DX,Y
MOV AH,9
INT 21H
ENDM
;单字符输入宏定义
SHURU MACRO
MOV AH,1
INT 21H
ENDM
;字符检测
CHECK MACRO X
.IF X>'9' || X<'0' && X!=' '
S_SHUCHU HINT3 ;提示输入错误
SHURU ;让提示显示,避免被刷掉,按任意键返回主菜单
JMP RESTORE ;跳回主菜单
.ENDIF
ENDM
;成绩输出宏定义
GR_SHUCHU MACRO X
MOV AL,X ;接收成绩
MOV AH,0 ;AX高位清0
MOV CL,10 ;除数,等会用来取余数和商
MOV DH,0 ;压栈前高位置0
.WHILE AL != 0 ;如果商不为0,则继续除10
DIV CL
MOV DL,AH ;余数置于DL
ADD DL,30H ;转换为ASCII码
PUSH DX ;压栈
INC DH
MOV AH,0 ;高位即余数清0
.ENDW
;循环完成取数操作,即从右到左,从个位数开始取数,一直到最高位停止取数
.IF DH == 0 ;判断输入成绩是否为0,若为0直接输出
SHUCHU 30H
.ELSE ;否则弹出栈中相应内容输出
MOV CL,DH
.WHILE CL
POP DX
SHUCHU DL
DEC CL
.ENDW
.ENDIF ;成绩输出结束
ENDM
;主程序代码段 MAIN
CODES SEGMENT
ASSUME CS:CODES,DS:DATAS
START:
MOV AX,DATAS
MOV DS,AX
MOV AX,TYPE STUDENTS
RESTORE: ;输入错误后,跳回的点
.WHILE 1
CALL SHOW ;调用菜单
CALL CHOICE ;接收选项并调用对应子程序
.ENDW ;死循环
EXIT:
MOV AH,4CH
INT 21H
;主程序代码段结尾 MAIN
;成绩输入子程序
GR_SHURU PROC
MOV CX,0
MOV DL,10
.WHILE CX<=100
SHURU ;键盘接收一个字符
CHECK AL ;检测输入的是否为数字
.IF AL == ' '
MOV [BX+DI],CL ;如果输入空格,则成绩接收结束,并存入对应储存单元,即结构体GRADE单元
RET
.ELSE
SUB AL,30H ;进行ASCII码转换为数字
CBW ;AL扩展为AX,高位清0
XCHG CX,AX ;交换数值
MUL DL ;AL×10放入AX中
ADD CX,AX ;将数字相加,得到新的数值,放入cl中
.ENDIF
.ENDW
S_SHUCHU WARING ;成绩输入不合格输出警告
RET
GR_SHURU ENDP
;菜单显示子程序
SHOW PROC NEAR
MOV AX,3 ;清屏
INT 10H
S_SHUCHU LIST
RET
SHOW ENDP
;菜单选项子程序
CHOICE PROC NEAR
SHURU ;调用宏输入一个字符
.IF AL=='1' ;调用输入功能
CALL INPUT
.ELSEIF AL =='2' ;调用输出功能,且排序
CALL RANK ;调用排序,对数据按成绩进行排序,由高到低
CALL OUTPUT
.ELSEIF AL=='3' ;调用查找功能
CALL SEARCH
.ELSEIF AL=='0' ;退出程序
JMP EXIT
.ELSE
S_SHUCHU WARING ;输出警告提示
.ENDIF
SHURU ;接收一个字符,用于暂停当前界面,按任意键刷新菜单
RET
CHOICE ENDP
;输入子程序
INPUT PROC NEAR
S_SHUCHU HINT1
LEA BX,STU_ARRAY ;结构体初始化指针,行定位
MOV SI,0 ;统计输入的行数
.WHILE SI