
简介这是一份河北科技大学《微型计算机原理及应用技术》课程设计报告选题为“密码校验程序”面向正在完成微机原理课设、需要参考汇编实现与报告结构的在校生。报告完整覆盖课程设计流程先明确密码校验规则包括长度限制、数字字母混合、禁用空格特殊字符等再给出进度安排与小组分工核心内容详述MASM编译环境下的算法实现绘制流程图并分模块给出密码接收、长度与字符类型校验、错误提示等汇编代码。文档点明LEA、MOV、CMP、条件分支、CALL等关键指令的使用场景帮助理解底层编程思路还介绍了单步执行、断点、寄存器查看等调试方法附录附有带注释的源程序代码便于对照。压缩包内共1个文件为doc格式文档大小约249KB内容集中直接打开即可查阅。目前已有652人学习下载适合需要掌握课设框架、对照调试或撰写报告的读者。1. 微机原理课设的密码校验一份能直接跑通的可视化汇编Demo如果看到“密码校验”四个字你的第一反应可能是哈希、加盐、登录鉴权——但这份课设讲的完全是另一码事。它没有加密没有哈希甚至连密码都没有存储保护它做的事情就是在DOS实模式下用汇编语言显示一个方框菜单输入两段字符串逐字节比对它们是否完全一致错满三次就返回DOS。就这么个“原始”程序恰恰把INT 10H/INT 21H中断调用、0AH缓冲输入格式、CMPSB串比较指令和BP计数器这几个汇编课里最容易卡壳的点全串在了一起。对正在写微机原理课设或想从看懂指令跨到写出程序的人这份资料的价值在于界面代码、校验算法、错误分支、退出逻辑全部齐全照着重写就能跑通。2. MASM环境与中断调用先把编译链跑通再背功能号表汇编课设跟Java课设最大的区别是它连运行环境都需要你先理解一遍。这份密码校验程序是16位实模式汇编面向DOS中断不是Windows下那种带消息循环的Win32程序。所以第一步不是打开某个现代IDE而是先把MASM的编译链路跑通。2.1 MASM 5.0/6.11的编译链asm到exe的三步转化课程设计报告原文用的是MASM 5.0它对应的编译流程是将asm源文件汇编成obj目标文件再用link把obj连接成exe可执行文件。实际操作时三个命令就能完成MASM PASS.ASM; LINK PASS.OBJ; PASS.EXE命令行结尾的分号表示“使用默认回答不再逐个提问”否则MASM会停下来问你是否要生成列表文件、交叉引用文件等交互过程很烦。LINK后面那个分号同理它会把“是否生成映像文件”“是否调整栈大小”等提示全部跳过。第一次编译时我建议你保留交互把Map文件生成出来看一眼段分布虽然小程序的段信息没什么可看的但能帮你建立“CS、DS、SS、ES各自占哪块内存”的直觉。参数层面MASM常用的开关有两个/Z显示错误行号和出错代码所在行适合初期排错/Zi生成调试信息配合DEBUG工具单步跟踪。链接时加/CO参数可以生成COFF格式调试符号但DOS实模式程序用不到保持默认即可。我一般会准备一个DOSBox环境把MASM 5.0或6.11的目录挂载成一个盘符在纯DOS模拟环境里执行上述三条命令这比在老式物理机上折腾要稳得多。提示MASM的交互式提问在脚本化使用时用分号跳过但第一次学习时不要加分号看一遍它会生成哪些文件对理解汇编产物有帮助。2.2 INT 10H与INT 21H功能号这份资源里能直接抄的参数表密码校验程序全身都靠软中断驱动核心功能全部集中在下面这张表里。这张表是这份资源里最值得背的东西因为整个程序的每一条功能分支都能映射到其中一行的调用。中断功能号功能入口参数出口参数在本程序中的位置INT 10H00H设置显示模式AL03H80×25彩色文本无每次循环开头的“清屏”INT 10H0EH显示单个字符AL字符ASCII码无输入密码后补回车换行INT 21H09H显示字符串DS:DX缓冲区首址字符串以$结尾无显示菜单、提示、错误信息INT 21H0AH缓冲输入字符串DS:DX缓冲区首址缓冲区第二字节写回实际长度输入密码、输入校验码INT 21H01H键盘输入单字符无AL字符ASCII码回显退出前“按任意键”INT 21H4CH终止程序返回DOSAL返回码无EXIT退出点INT 16H00H键盘读入无回显无ALASCII码AH扫描码主菜单读选项每个调用的使用场景在后面的章节会展开这里先讲一个最容易混淆的点09H和0AH都要求DS:DX指向缓冲区但09H读的是以$结尾的字符串0AH写的是输入缓冲区结构。新人容易把0AH输入缓冲区里自动填入的回车符0DH当成普通字符导致后续串比较时长度永远多1——这个问题后面有专门一条避坑记录。2.3 段寄存器初始化不写这几行代码程序一跑就崩源码开头这几行是所有8086实模式程序的地基START: MOV AX, CODE MOV DS, AX CLD把代码段地址送进AX再转给DS让数据访问指向当前代码段。因为这个小程序把菜单字符串、提示语、缓冲区全部定义在CODE段里ASSUME伪指令只是告诉编译器“段关联是这样”真正把段地址写进寄存器必须靠MOV指令完成。漏掉MOV DS,AX这一行后面的LEA DX,MENU配合INT 21H的09H功能去读字符串会从DS默认指向的错误段取数据表现就是菜单乱码或者直接死机。对新手来说段寄存器就是一个黑匣子不初始化就访问内存后果完全随机。CLD的作用是清方向标志保证REPZ CMPSB串比较时SI和DI自动递增即从低地址往高地址逐字节比。漏掉CLDES:DI和DS:SI会向反方向移动长度越长越容易在比较后半段读到自己缓冲区外的垃圾字节。2.4 界面字符集201、186、205这些数字决定了菜单长什么样主菜单不是用绘图API画的是靠ASCII扩展字符集拼出来的。代码里那些201、187、200、188、205、186分别对应左上角、右上角、左下角、右下角、横线、竖线MENU DB 5 DUP(0AH) DB 31 DUP( ), MAIN MENU, 0DH, 0AH DB 25 DUP( ), 201, 21 DUP(205), 187, 0DH, 0AH DB 25 DUP( ), 186, 1.INPUT THE PASSWORD, 186, 0DH, 0AH DB 25 DUP( ), 186, 2. CHECK THE PASSWORD , 186, 0DH, 0AH DB 25 DUP( ), 186, 3.RETURN TO DOS ... , 186, 0DH, 0AH DB 25 DUP( ), 200, 21 DUP(205), 188, 0DH, 0AH DB 28 DUP( ), CHOICE(1,2,3):$5 DUP(0AH)先输出5个换行把菜单整体下移31 DUP( )和25 DUP( )负责水平方向缩进0DH 0AH是回车加换行保证每行从第0列重新开始。最后那个$不是普通字符是09H功能的字符串结束标记。如果你的菜单预览时最底下一行“CHOICE”没出现多半是前面某一行字符串少了$09H功能会一直往后读到第一个$为止——这种错误在汇编字符串里非常隐蔽。把这里的ASCII码换成其他符号方框样式就整体变了这是后期想让界面不那么“教科书”的免费改装点。3. 菜单框架与键盘分发CMP/JNZ链路背后的主循环设计这一章讲的是程序怎么“活”起来开机进菜单按键进分支调用完再回来重新画菜单。看起来是几行跳转指令但这里面藏着汇编程序循环设计的常见套路。3.1 用显示模式切换代替清屏MOV AX,3与INT 10H的组合整个程序的主循环以这两行开头AGAIN: MOV AX, 3 INT 10H把AL设为03H调用INT 10H的00H功能设置80×25彩色文本模式。切换到新模式时硬件会自动清空显存所以这比专门调用上卷清屏更省事。代价是屏幕会闪一下——每轮循环都重新切换模式视觉上有个瞬时抖动但课设演示完全能接受。如果你想让菜单更正式一点可以改成INT 10H的06H功能上卷清屏MOV AH, 6 MOV AL, 0 MOV BH, 7 MOV CX, 0 MOV DX, 184FH INT 10HAL0表示清空整个窗口BH7是黑底白字属性CX和DX定义窗口矩形。不过这份资源里的主角是AGAIN: MOV AX,3这条路径它的优点是一行代码搞定缺点是每次返回菜单都会把屏幕重新初始化一次所有非文本显示内容如果有的话都会被清掉。这个取舍适合纯文本程序不需要刻意改。清屏之后紧跟的是菜单输出LEA DX, MENU MOV AH, 9 INT 21HLEA把MENU的偏移地址放进DX09H功能从DS:DX开始逐字符输出直到遇到$停止。这里要注意LEA拿到的是偏移地址不是段地址实际寻址用DS段寄存器配合当前偏移完成。数据在CODE段里DS已经指向CODE所以能正常工作。3.2 主菜单的分发跳转INT 16H读取与CMP/JNZ链菜单显示完程序进入键盘等待状态。这份资源用的是INT 16H的00H功能而不是INT 21H的01H功能INPUT: MOV AH, 0 INT 16H CMP AL, 1 JNZ CP2 CALL LP1 JMP AGAIN CP2: CMP AL, 2 JNZ CP3 CALL LP2 JC EXIT JMP AGAIN CP3: CMP AL, 3 JNZ INPUT EXIT: ...INT 16H的00H功能从键盘缓冲区读一个字符AL放ASCII码。它不回显到屏幕适合菜单这种“按了就行”的场景。INT 21H的01H会回显而且会等待回车如果你把01H用在菜单上按完1屏幕上多一个字符不说缓冲区里还可能残留回车符。分发逻辑用的是CMP/JNZ链CMP AL,1把AL和字符1的ASCII码31H比较相等时ZF1JNZ不跳转进入CALL LP1不相等时ZF0JNZ跳到CP2继续判断2同样方式走完3。三个选项都不是JNZ INPUT回到等待状态不会做任何响应——这是最简单的非法输入处理不提示、不计数、不退出。如果你想让非法输入也闪烁提示可以在INPUT前补一个分支但课的评分点一般不在这个位置。3.3 子程序返回值的传递用CF进位标志报告三次失败主循环里有一行很见功力的设计CALL LP2 JC EXITLP2是密码校验子程序。它如果三次都输错会在返回前置进位标志CF1主循环的JC EXIT就能在子程序返回后立刻判断“校验失败该退出了”。汇编程序里子程序的“返回值”经常不在内存里而是放在寄存器或标志位上成功时清零CF、失败时置位CF调用方用JC/JNC读取结果比在内存里约定一个变量更高效。这种风格在C语言里完全没有对应物却是汇编课设里老师最喜欢问的“你这里为什么用JC”的考点。把三次失败与退出联动起来的完整链路是LP2里每次失败DEC BPBP减到0后执行STC置CF1再JMP OK跳过“PASSWORD CORRECT”的输出直接等待按键RET返回主循环。主循环拿到CF1JC EXIT进入退出流程。3.4 每次调用后都要重画菜单牺牲效率换可靠性你会在主循环里看到LP1返回后JMP AGAINLP2成功返回后也JMP AGAIN三路分支全部回到AGAIN重新执行MOV AX,3/显示菜单/等待按键。这个设计表面看有点浪费时间每次输完密码都要把整个界面刷新一遍但它保证了用户无论从哪个子程序回来看到的都是一个干净的新菜单不需要在子程序里做任何界面恢复。对于课设报告里“程序流程图”那一节的绘制这种结构也最清晰一个主循环节点三个分支子节点数据流简洁。如果你想把效率提上来可以把AGAIN中的模式切换去掉只保留菜单显示和等待按键但那样如果某个子程序改了屏幕内容返回时旧的菜单残影还在调试成本更高。4. 密码校验的核心算法缓冲输入、长度预比较与三次错误计数这一章直接进入资源里含金量最大的部分密码的输入、比对和错误计数。先贴完整代码再拆开讲。以下是原程序LP2过程的精简版保留了所有关键动作LP2: MOV AX, 3 INT 10H PUSH DS POP ES MOV BP, 3 SSSS: LEA DX, PASS MOV AH, 9 INT 21H LEA DX, MSG2 MOV AH, 0AH INT 21H LEA SI, BUF1 LEA DI, BUF2 MOV CH, 0 MOV CL, MSG11 CMP CL, MSG21 JNZ WRONG REPZ CMPSB JZ RIGHT WRONG: LEA DX, NO MOV AH, 9 INT 21H DEC BP JNZ SSSS STC JMP OK RIGHT: LEA DX, YES MOV AH, 9 INT 21H OK: MOV AH, 1 INT 21H RET注意这段代码把缓冲区BUF1/BUF2起始地址赋给了SI和DI比较从BUF1/BUF2开始跟输入缓冲区MSG1/MSG2分离。原因后面详细讲。4.1 INT 21H/0AH的缓冲输入格式MSG1 DB 255,0与BUF1的对应关系0AH功能是DOS下的缓冲键盘输入。它要求DS:DX指向一个固定格式的缓冲区第一个字节表示缓冲区最大容量第二个字节由DOS在输入完成后写入实际读入的字符数不含结尾回车从第三个字节开始才是用户输入的字符内容。MSG1 DB 255, 0 BUF1 DB 255 DUP(0)MSG1的第一个字节255是给DOS看的表示“这个缓冲区最多装255个字符”第二个字节初始为0输入完成后DOS会把实际输入长度写到MSG11这个位置。BUF1是真正存放字符内容的空间。之所以要拆成MSG1和BUF1两个名字是因为LEA DX,MSG1作为0AH的参数而后续串比较需要从BUF1的起始地址开始——如果直接用LEA DX,MSG1赋值给SI会把前两个长度字节也带进比较密码永远对不上。这里有个参数边界DOS的0AH功能最大缓冲区上限255如果你把第一个字节改成300DOS会截断到255并鸣叫如果小于你实际输入的长度多余的字符直接丢弃并响铃。课设里密码不可能超过255所以源码这个设计完全没有发挥边界问题。输入按回车结束回车符0DH会放在缓冲区末尾但实际长度计数不含它。这是最容易让人误判的地方BUF1里第CL个字节之后是0DH而CL取的是MSG11的真实字符数。注意0AH功能把回车符0DH留在缓冲区末尾但长度计数不含它。任何把回车符算进长度的判断都会导致比较结果错位。4.2 长度预比较CMP CL,MSG21为什么能提前拦截错误进入比较前代码做了一件事MOV CH, 0 MOV CL, MSG11 CMP CL, MSG21 JNZ WRONG先把第一次输入的密码长度读到CL和第二次输入的校验码长度比较。长度不同直接进入错误分支不执行后续串比较。这是一个很小的优化却让程序逻辑清晰了很多长度不同两个字符串必然不同再逐字节比就是浪费时间。至于为什么用CL不是整个CX——因为长度最大255高8位CH不用先清零保证CX整体数值正确。长度比较放在串比较之前还有个额外好处REPZ CMPSB指令在CX0时会一次都不比较直接结束而长度为0的空密码在这种流程下也能正常进入RIGHT分支。空密码课设里基本用不到但逻辑上它是自洽的。长度相等时进入串比较REPZ CMPSB JZ RIGHTREPZ CMPSB的含义是重复执行“比较DS:SI和ES:DI指向的两个字节SI/DI自动递增CX减1”直到CX0或者发现两个字节不一致才停止。每次比较的结果记录在ZF标志位上全部相同时ZF1JZ跳到RIGHT出现任何一个不同字节ZF0落到WRONG分支。这一切成立的前提是ES指向正确。LP2开头PUSH DS/POP ES把代码段地址复制给ES因为CMPSB约定DS:SI比ES:DI而本程序没有单独的数据段只能用这种办法让ES和DS指向同一个段。如果你忘了这一步ES会指向残留的未知段CMPSB读到的是完全随机的内容程序表现就是“密码怎么输都对/错”。4.3 三次错误计数机制BP递减与STC构造返回状态三次错误计数完全靠BP寄存器完成MOV BP, 3 SSSS: ... WRONG: DEC BP JNZ SSSS STC JMP OKBP初始化为3每次进入错误分支DEC BP然后JNZ SSSS判断BP是否到0。没到0就回到SSSS重新提示输入密码到0就说明三次都错了执行STC把进位标志置1再跳到OK等待按键。OK是所有分支的公共出口MOV AH,1/INT 21H等待用户按任意键然后RET返回主循环。有同学问为什么不把三次失败直接写成一个循环次数检查的JMP而是用DEC/JNZ组合——因为8086指令集里没有“等于某个数就跳转”的单一指令DECJNZ是最紧凑的写法。BP在这个程序里就是普通数据寄存器不像在保护模式下有栈帧的固定职责这点可以放心用。STC置位后不会自动清除主循环JC EXIT读取后程序就结束不存在下次误判CF的问题如果你在同一个子程序里还要调用其他可能改变CF的指令注意顺序。4.4 密码一致时的出口JZ RIGHT与错误分支的公共收尾RIGHT分支做的事情就三样显示“PASSWORD CORRECT”等待按键返回主菜单。它和WRONG分支共用OK代码段这个共用设计省掉了重复的等待按键代码。从代码组织角度看把公共操作提到所有分支的最后用不带条件的JMP进入比每个分支末尾各写一份MOV AH,1/INT 21H更便于维护——以后你想在按键前加一个清屏只需要改一处。这里还有一条血泪经验不要图省事把长度比较省掉直接靠REPZ CMPSB处理不同长度的字符串。两个长度不同的字符串短的那个末尾会被当成字符参与比较结果可能是意外的相等或者不相等而且这种错误往往只在某些特定输入组合下出现极难排查。每次你看到“密码明明不同却显示CORRECT”先回头检查长度预比较是否被删了。5. 常见问题与避坑调试这个课设最容易翻车的五个点汇编程序调试跟高级语言不一样没有堆栈回溯也没有异常抛出程序挂了就是黑屏、乱码、死机三选一。下面这五条是把这份课设跑通时高频出现的坑全部按现象、原因、解决的顺序写方便你拿着症状直接对照。我调这个课设时几乎全踩过一遍每一条都是真实发生过的场面。5.1 现象编译通过了运行后屏幕全是乱码或直接卡死现象程序在主菜单字符串还没显示出来就花屏或者显示一串不可读的符号之后死机。原因多半是DS没有初始化。源码START位置如果没有MOV AX,CODE/MOV DS,AXLEA DX,MENU配合09H功能读取字符串时DS还是上电时的残留值取出来的数据根本不是菜单内容。另一种可能是CLD方向标志没清09H功能不受影响但后续串比较方向就反了。解决确认START前三行是MOV AX,CODE/MOV DS,AX/CLD。在DOSBox里跑的话还可以先在DEBUG下用R命令查看DS值是否等于CS确定段寄存器状态。把这一段代码背下来写任何DOS汇编程序都用得上。5.2 现象密码明明一样却提示PASSWORD ERROR现象设置密码和校验密码输入完全一致的字符串程序仍然进入WRONG分支三次之后直接退出。原因最常见是SI和DI指向了带长度字节的缓冲区头即LEA SI,MSG1而不是LEA SI,BUF1。这样CMPSB比较的第一个字节是缓冲区容量255第二个字节是输入长度第三个才开始是字符内容对不上是必然的。另一个常见原因是读长度时把回车符算了进去比如用INC指令把MSG11的值加1后再比较实际长度就永远偏大。解决把SI和DI指向BUF1和BUF2长度字节读MSG11和MSG21。如果长度总比实际输入多1检查输入字符串时是不是手动在字符串尾部补了0DH。用DEBUG的D命令查看BUF1内容前两个字节应该是“00 03”容量、长度之类的结构第三个字节开始才是输入的字符。5.3 现象输入密码后光标不换行提示信息挤在一行现象输完密码屏幕光标停在行尾下一行提示直接叠在刚才的输入内容后面界面看起来像没做完排版。原因0AH功能输入以回车键结束但DOS不会帮你输出换行。回车符被存进缓冲区屏幕光标没有移动到下一行。源码里补了MOV AX,0E0DH/INT 10H和MOV AL,0AH/INT 10H分别输出回车的ASCII码0DH和换行的ASCII码0AH。少了这两行就会出现这个现象。解决在每次0AH输入后补上0DH和0AH的输出。这段在LP1中已经写好了复制粘贴时不要删掉。如果你用的是MASM 6.11也可以直接用INT 21H的02H功能输出DL里的字符但效果一样用哪种看代码风格顺眼。5.4 现象按1/2/3没反应程序像卡死现象菜单正常显示但键盘怎么按都不进分支或者按几次后突然跳进错误分支。原因最常见是误用了INT 21H的01H读菜单选项。01H会回显并等待回车确认而程序里CMP AL,1马上判断如果用户只按了1没按回车AL里是1但回车键会被后续读入的字符覆盖造成跳转混乱。INT 16H的00H没有回显按键立即返回更适合菜单。解决菜单读键统一使用MOV AH,0/INT 16H。如果一定要用01H需要自己处理回车后的残留换行但完全没有必要绕这个弯。这个坑在报告里很容易被忽略因为单步调试时看不出问题连按三个键才会暴露。5.5 现象三次错误后没有退出而是重新回到菜单现象错误提示第三次后程序没有返回DOS主菜单又显示出来可以继续操作。原因LP2里三次失败后STC置CF1但主循环如果没有JC EXIT这一行会继续执行JMP AGAIN。另一个可能LP2最后的RET被提前执行或者RET前误执行了清除标志位的指令——某些MOV/LEA操作不影响CF但INT 21H调用可能改变标志寄存器在STC和RET之间不要夹其他INT调用。解决确认主循环CP2分支中LP2调用后的顺序是JC EXIT再JMP AGAIN。调试时可以在STC之后临时加一条INT 21H的06H显示字符观察CF是否被破坏。这个“用标志位传状态”的模式后面写更复杂的汇编程序也会反复用到值得一次调通。6. 进阶与验证把三次改成五次用DEBUG盯住INT 21H的返回值这一章是收尾也是把这份课设真正变成你自己的东西的临门一脚。三个小改动做完你对整个程序的控制力会上一个台阶。6.1 改错误次数一个寄存器的改动把LP2里的MOV BP,3改成MOV BP,5三次就变五次。因为BP就是循环计数器错误分支执行一次就DEC一次。配套的把NO字符串改得更友好一点NO DB 0DH, 0AH, 9, PASSWORD ERROR (5 TRIES MAX)$这样用户看到的不再是无脑报错而是知道自己还剩几次机会。扩展成“剩余次数”动态显示也不难但需要在WRONG分支里把BP值转成十进制字符再输出涉及除法和查表课设里可以不做。这也是回答答辩追问“怎么改成动态次数提示”最现成的素材思路就是BP里有剩余次数找到查表入口把数字映射到ASCII码再输出。6.2 加提示音字符串里塞一个07H就能响铃把错误提示字符串改成NO DB 0DH, 0AH, 07H, 9, PASSWORD ERROR$07H是BEL控制字符终端遇到它会让扬声器或蜂鸣器响一声。DOS终端如此DOSBox同样能模拟。每输错一次响一声界面反馈立刻变立体。这个改动只在字符串输出里加一个字节是整套代码中性价比最高的增强。6.3 验证缓冲输入DEBUG.EXE单步跟踪INT 21H返回打开DOSBox进入项目目录输入DEBUG PASS.EXE用T单步执行走到INT 21H指令前先用P跳过调用再用U命令反汇编找到LEA DX,MSG2所在行记下它的偏移地址——这个偏移在-COM和-EXE格式里会不同以反汇编结果为准。然后输入D DS:偏移地址重点看第二个字节。输入“abc”回车后MSG21应该是03H表示实际输入3个字符。如果这里读到04H说明多了一个回车符或换行符被计入比较必然出错。这个验证方法能一举确认0AH缓冲区的长度写入是否符合预期。汇编程序的所谓调试很多时候就是盯住内存里一个字节对不对。有一次我在调一段DOS启动脚本时图省事把REPZ CMPSB换成了自写LOOP循环结果忘了CMP会改变ZF标志整整一个下午都在排查“密码明明正确却报错”的怪现象。从那以后我每改完一份汇编课设代码都强制自己在DEBUG里单步走一遍长度比较那段。改次数、加响铃、看缓冲区这三件事做完这份课设对你来说就没有秘密了。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。