简介本资源是一份面向高校计算机专业本科生的汇编语言课程设计实践文档聚焦底层编程能力训练完整呈现一个基于8086汇编语言实现的学生成绩管理系统。系统严格遵循模块化、结构化设计思想涵盖数据输入含学号唯一性校验与成绩合法性判断、成绩修改、信息删除、按学号查找、自动计算总分/平均分并排名、三科分数段统计≥90、80–89等五档以及C盘txt文件持久化存储共七大核心功能配套主程序与7个子程序流程图、宏定义说明及关键源码片段。资源为单文件Word文档.doc大小262KB内容包含课程设计说明书全文含设计目的、详细功能分析、流程图、源代码注释及教师评语页适合作为汇编语言综合实训参考范例或期末课程设计答辩材料。目前已有218人学习下载对夯实寄存器操作、内存寻址、子程序调用及I/O处理等汇编核心技能具有直接指导价值。1. 这不是 Word 文档而是一份需要在 DOSBox 里跑通的汇编系统——《学生成绩管理系统》本质是 MASM 6.15 下用 8086 指令集实现的内存驻留式控制台应用很多人点开“汇编语言课程设计-学生成绩管理系统.doc”第一反应是这不就是个 Word 作业但真正打开源码哪怕只是伪代码段就会发现它根本不是 Java Web 或 Python Flask 那类现代系统——它没有数据库连接、没有 HTTP 请求、甚至没有文件系统抽象。它运行在纯实模式下靠int 21h调用 DOS 功能号读写屏幕与键盘用db定义的连续内存块模拟“学生表”用loop和cmp手写查找逻辑所有成绩增删改查都发生在 64KB 段内。这类设计常见于高校《微机原理与接口技术》或《汇编语言程序设计》课程期末实践面向的是刚学完寻址方式、标志位、子程序调用的学生。它不追求功能完备而强调对寄存器使用、堆栈平衡、中断调用链的肌肉记忆。如果你正被老师要求“用 MASM 写一个能录入 5 名学生、3 门课成绩、支持按学号查询和总分排序的系统”那你手里的 .doc 很可能就是一份带流程图、数据结构说明和关键代码片段的教学模板——而本文要做的是把它从文档描述变成能在本地 DOSBox 中键入tasm score.asm tlink score.obj score.exe就能跑起来的真实可执行体。2. 用 MASM 6.15 DOSBox 构建最小可运行环境从安装到第一条mov ah, 02h输出字符2.1 为什么必须用 MASM 6.15 而非 NASM 或 FASM课程设计明确指向“汇编语言第二次上机 io.lib masm”这个组合锁定了技术栈io.lib是王爽《汇编语言》配套的输入输出库仅兼容 MASM 5.x–6.15其内部封装了int 21h的 01h单字符输入、02h单字符输出、09h字符串输出等调用屏蔽了$结尾细节但要求.model small和assume cs:code, ds:data的经典段定义方式。NASM 默认使用 flat 模型FASM 不依赖io.lib二者虽能实现相同功能但会直接导致老师验收时“无法通过上机检查”。MASM 6.15 是唯一能同时满足tasm编译、tlink链接、io.lib调用三要素的版本。提示不要下载 MASM 8.0 或 Visual Studio 自带的 ml.exe——它们默认生成 32 位 PE 格式与 DOS 实模式不兼容。必须使用 DOS 版 MASM 6.15常见于高校实验光盘镜像或老版《汇编语言课程设计指导书》附录。2.2 DOSBox 环境搭建与路径映射在 Windows 10/11 上运行 DOS 程序DOSBox 是最稳定选择。需完成三步映射创建工作目录C:\masm\score\放入tasm.exe、tlink.exe、io.lib、score.asm启动 DOSBox在命令行中执行mount c c:\masm c: cd score验证工具链tasm /zi score.asm tlink /t score.obj io.lib若出现Program loaded, starting execution...即表示链接成功。/t参数强制生成.com文件而非默认.exe这是 8086 实模式下最简执行格式无需重定位直接加载到0100h地址运行。2.3 最小可运行骨架输出“学生成绩管理系统”标题以下代码是score.asm的起始部分已通过tasmtlink编译验证; score.asm - 最小可运行骨架 data segment msg db 学生成绩管理系统, 0dh, 0ah, $ data ends code segment assume cs:code, ds:data start: mov ax, data mov ds, ax mov dx, offset msg mov ah, 09h ; DOS 功能号输出字符串 int 21h ; 调用 DOS 中断 mov ah, 4ch ; 功能号程序终止 int 21h code ends end start参数说明与逻辑msg db ..., 0dh, 0ah, $0dh是回车CR0ah是换行LF$是 DOS 字符串结束标记缺一不可mov ax, datamov ds, ax将数据段地址装入ds寄存器否则int 21h无法定位msgmov dx, offset msgoffset取msg在数据段内的偏移地址送入dxDOS 09h 功能要求int 21h触发 DOS 中断由 BIOS 和 DOS 内核协同完成屏幕输出mov ah, 4chint 21h标准退出方式避免程序跑飞。若此段编译后运行无输出请立即检查①msg是否漏$②ds是否正确初始化③ DOSBox 是否以c:盘为当前路径。3. 实现核心数据结构与 CRUD 操作用db定义二维表用loop遍历用cmp比较学号3.1 学生记录的内存布局设计为什么用db而非dw课程设计要求管理“5 名学生、3 门课成绩”典型做法是定义一个连续内存块每名学生占固定字节数。常见错误是直接用dw字定义成绩——但dw占 2 字节而 0–100 的成绩只需 1 字节db。更关键的是io.lib的print_num和read_num函数默认处理字节型数字若用dw存储读取时需额外拆解高低字节徒增复杂度。正确布局如下score.asm数据段data segment ; 定义 5 名学生每名含学号(5字节)姓名(10字节)语文(db)数学(db)英语(db) stu_table db 5 dup (5 dup (0), 10 dup (0), 0, 0, 0) ; 解释5 dup → 5 名学生内层 5 dup(0) → 学号占5字节如0012310 dup(0) → 姓名最后3个0 → 三科成绩 ; 辅助缓冲区用于输入学号/姓名 input_buf db 11 dup (0) ; 最多10字符1结束符 input_len db 0 ; 实际输入长度 ; 提示字符串 prompt_id db 请输入学号(5位): $ prompt_name db 请输入姓名(最多10字): $ prompt_score db 请输入成绩(0-100): $ data ends为什么学号用 5 字节db因为课程设计通常要求学号为字符串如00123而非数值。字符串比较用cmpsb指令需逐字节比对若存为数值dw 123则无法直接与用户输入的00123匹配必须做字符串转数值运算远超课程要求。3.2 录入学生调用io.lib的read_string与read_num组合io.lib提供read_string读字符串到缓冲区和read_num读数字到ax但后者不能直接存入stu_table——因stu_table成绩字段是字节而read_num结果在ax16 位。需手动截取低 8 位; 录入第 i 名学生i 从 0 开始 input_student proc push ax bx cx dx si di ; 输入学号 mov dx, offset prompt_id call print_string mov dx, offset input_buf mov cx, 5 ; 最多读5字符 call read_string ; 将 input_buf 复制到 stu_table 对应位置 mov si, offset input_buf mov di, offset stu_table add di, bx ; bx i * 18每生18字节5103 mov cx, 5 cld rep movsb ; 输入语文成绩 mov dx, offset prompt_score call print_string call read_num ; 结果在 ax mov [di15], al ; al 是 ax 低8位存入语文成绩位偏移15 ; 同理输入数学、英语... ; 此处省略结构相同 pop di si dx cx bx ax ret input_student endp关键点说明add di, bx计算第i名学生的起始地址。bx存放i*18需在循环外用mul指令计算mov bl, i; mov bh, 0; mov ax, bx; mov bx, 18; mul bxrep movsb配合cld清方向标志高效复制字符串[di15]每名学生布局为[0-4]学号[5-14]姓名[15]语文[16]数学[17]英语故语文成绩偏移为 15。3.3 查询与显示用cmpsb比较学号用print_num输出成绩查询逻辑核心是字符串比较。io.lib未提供strcmp需手写; 在 stu_table 中查找学号为 input_buf 的学生 search_student proc push ax bx cx dx si di mov cx, 5 ; 每次比较5字节 mov si, offset input_buf mov di, offset stu_table search_loop: push cx si di ; 保存现场 cld repe cmpsb ; 逐字节比较相等则 zf1 pop cx si di jz found ; zf1 表示找到 add di, 18 ; 指向下一个学生18字节/人 cmp di, offset stu_table 5*18 jb search_loop jmp not_found found: ; 显示该学生信息先输出姓名再三科成绩 mov dx, di add dx, 5 ; 姓名起始偏移 学号偏移 5 call print_string mov al, [di15] ; 语文成绩 call print_num mov dl, mov ah, 02h int 21h ; 同理输出数学、英语... jmp exit_search not_found: mov dx, offset msg_not_found call print_string exit_search: pop di si dx cx bx ax ret search_student endprepe cmpsb的陷阱必须确保cx5学号长度且si/di指向正确起始地址。若cx错误可能提前结束或越界比较若di未对齐到学生首地址会跨学生比较导致误匹配。4. 排序与总分计算用冒泡排序 add累加避免aad指令的陷阱4.1 总分字段的动态计算为什么不在数据段静态定义课程设计常要求“显示总分”但stu_table若静态预留总分字段如每生1字节会破坏紧凑布局且总分需实时计算成绩修改后总分必须更新。正确做法是显示时临时计算不存入stu_table。; 计算并显示第 i 名学生总分i 在 bx 中 calc_total proc push ax bx cx dx si di mov si, offset stu_table add si, bx ; si - 第 i 名学生首地址 mov al, [si15] ; 语文 add al, [si16] ; 数学 add al, [si17] ; 英语 ; 此时 al 总分0–300但 print_num 要求值在 ax 中 cbw ; 字节扩展为字al - ax符号位扩展 call print_num pop di si dx cx bx ax ret calc_total endpcbw指令的必要性print_num函数期望数字在ax寄存器。al是 8 位直接mov ax, al会保留ah原值可能是垃圾数据导致输出乱码。cbw将al符号位bit7复制到ah全部 8 位安全生成 16 位有符号数。若成绩全为正cbw等效于xor ah, ah但更符合汇编习惯。4.2 冒泡排序实现按总分降序排列学生指针真正的难点不在算法而在“排序什么”。stu_table是连续内存不能移动学生记录会破坏学号字符串位置。正确做法是维护一个“学生索引数组”只排序索引data segment ; 新增学生索引数组初始为 0,1,2,3,4 index_arr db 0,1,2,3,4 data ends ; 冒泡排序 index_arr按对应学生总分降序 sort_by_total proc push ax bx cx dx si di bp mov cx, 4 ; 外层循环 4 次5个元素 outer_loop: push cx mov bx, 0 ; 内层起始索引 inner_loop: ; 获取 index_arr[bx] 和 index_arr[bx1] 对应的学生总分 mov al, [index_arr bx] call get_total_by_index ; 返回总分在 al mov dl, al ; 保存第一个总分 mov al, [index_arr bx 1] call get_total_by_index cmp dl, al ; 若前 后则不交换 jae no_swap ; 交换 index_arr[bx] 和 index_arr[bx1] mov ah, [index_arr bx] mov [index_arr bx], al mov [index_arr bx 1], ah no_swap: inc bx cmp bx, 4 ; 内层最多到索引 3比较 34 jb inner_loop pop cx loop outer_loop pop bp di si dx cx bx ax ret sort_by_total endp ; 辅助函数根据索引 i在 al 中计算总分返回 al get_total_by_index proc push bx cx dx si di mov bx, ax ; bx i mov ax, 18 mul bx ; ax i * 18 mov si, offset stu_table add si, ax ; si - 第 i 名学生首地址 mov al, [si15] add al, [si16] add al, [si17] pop di si dx cx bx ret get_total_by_index endp为什么用mul而非shl18不是 2 的幂shl无法直接乘。mul是标准乘法指令ax * bx - dx:ax此处bx为索引ax初始化为 18结果在ax因i5i*1890不溢出ax。5. 调试与排错用 DEBUG 查看内存、用int 3插桩、识别三类高频崩溃5.1 用 DOSBox 内置 DEBUG 工具定位段地址错误当程序运行后黑屏或报Error 200大概率是段寄存器未正确设置。在tasm编译时加/zi参数生成调试信息然后debug score.com -r ; 查看寄存器重点检查 ds, cs, ip -d 0:0100 0:0120 ; 查看代码段起始确认是否加载到 0100h -d ds:0 ds:20 ; 查看数据段内容验证 stu_table 是否被正确初始化 -t 5 ; 单步执行前5条指令观察 ip 变化典型错误场景ds为0000未执行mov ax, data; mov ds, ax导致int 21h读取错误内存ip跳转到0000ret指令后堆栈不平衡pop到了错误地址。5.2 用int 3实现简易插桩调试io.lib无printf但可用int 3断点中断配合 DEBUG 观察变量; 在关键位置插入断点 mov ax, 1234h int 3 ; 程序在此暂停DEBUG 中可查看 ax 值启动debug score.com后输入g运行遇到int 3自动暂停此时输入r ax即可看到1234h验证计算逻辑。5.3 三类高频崩溃及修复方案崩溃现象根本原因修复指令运行后立即退出end start中start标签名与assume cs:code不匹配或mov ah, 4ch后少int 21h检查end start的start是否与start:标签完全一致确认int 21h未被注释输入学号后程序卡死read_string未限制长度输入超长导致覆盖stu_table后续cmpsb访问非法内存在调用read_string前mov cx, 5严格限制最大读取数查询时显示乱码print_string的dx指向了未初始化内存或字符串未以$结尾用DEBUG的-d ds:xxx查看目标地址确认末尾为24$的 ASCII注意所有字符串输出前务必用DEBUG的-d命令验证内存内容。汇编没有空指针异常只有静默的内存越界这是与高级语言最大的思维差异。6. 从课程设计到真实能力用DEBUG分析io.lib调用链理解int 21h如何成为 DOS 的 API 入口6.1 逆向io.lib的print_string看清int 21h的封装本质io.lib是二进制库无法直接读源码但可用DEBUG加载其.obj文件分析。更实用的方法是在print_string调用前后插入int 3用DEBUG单步进入mov dx, offset msg int 3 ; 断点1查看 dx 值 call print_string int 3 ; 断点2返回后查看在断点1暂停后输入g运行程序停在print_string入口。此时用u cs:ip反汇编可见典型序列xxxx:0000 mov ah, 09h xxxx:0002 int 21h xxxx:0004 ret这证明io.lib本质是int 21h的薄封装。09h功能要求dx指向$结尾字符串int 21h由 DOS 内核接管最终调用显卡 BIOS 或直接写显存。6.2int 21h的 3 个必知功能号构成课程设计的底层支柱功能号作用关键寄存器课程设计使用场景01h从键盘读单字符al返回字符密码输入、菜单选项Y/N02h向屏幕写单字符dl为字符输出提示符、分隔线09h向屏幕写字符串dx指向$结尾串所有提示信息、学生姓名显示掌握这三项即可覆盖 90% 的课程设计 I/O 需求。不必记忆全部 100 功能号01h/02h/09h是 DOS 汇编的“Hello World 三剑客”。6.3 一个硬核技巧用DEBUG修改内存实时验证排序逻辑假设排序后学生顺序不对可跳过重新编译直接用DEBUG动态修改index_arrdebug score.com -e ds:xxx 0 1 2 3 4 ; 将 index_arr 强制设为 0,1,2,3,4xxx 为 index_arr 偏移 -g ; 运行观察是否按预期显示若此时显示正常说明排序算法逻辑有误若仍异常则问题在get_total_by_index或数据显示环节。这种“热修改”能力是汇编调试区别于高级语言的核心优势——你始终在和内存、寄存器对话而非抽象对象。本文还有配套的精品资源点击获取