简介:这是《IBM-PC汇编语言程序设计》(沈美明、温冬婵编著)一书的习题参考答案文档,专为正在学习IBM-PC汇编语言的高校学生、自学者及备考者整理,可帮助解决课后习题答案不完整、解题思路不清、无从下手等问题。文档按教材章节顺序编排,从第一章开始逐题给出规范解答,内容覆盖十进制、二进制、十六进制之间的数制转换,8位补码表示的带符号数加减法运算及CF、OF标志位变化,AND、OR、NOT、XOR等位运算,ASCII码与字符串的编码转换等核心知识点;每个题目均附详细转换过程、运算结果与必要说明,便于读者对照教材检验思路、查漏补缺,在反复练习中巩固对数据表示和底层运算规则的理解。资源为1个doc文档,压缩包大小524KB,体积精简、内容集中,适合直接查阅或打印使用。目前已有113人浏览学习,非常适合作为课程同步辅导、期末复习及考研基础阶段的参考资料。
1. 这个“答案”到底是什么:先搞清楚你手里拿到的是不是能跑的代码
很多人第一次接触《IBM-PC汇编语言程序设计》这本紫皮书,是在学“什么是汇编语言”的时候。老师布置了一堆课后题,于是你上网搜“IBM-PC汇编语言程序设计答案”,下载了一个压缩包,里面是几十个 .asm 文件和几份 .doc 文档。你以为这就是全部,结果复制一个 .asm 进编辑器,一编译全是报错。这个标题指的东西很明确:沈美明、温冬婵那本教材的课后习题解答。但网上流传的“答案”大致有三种——纯文本思路解析、带注释的源码片段、以及从老软盘/打印稿复刻出来的残缺代码。真正值钱的不是那段抄来的代码,而是你怎么把它整理成能运行、能验证、能学到东西的程序。这篇文章就解决这个问题:答案怎么用、环境怎么搭、代码怎么编译、坑在哪里。
2. 搭一个能跑 x86 汇编的环境:DOSBox + MASM/TASM 的选择与安装
2.1 为什么选 DOSBox + MASM 而不是直接在 Windows 11 上跑
这本教材讲的是 8086 实模式汇编,跑在 DOS 环境下。现代的 64 位 Windows 系统根本不能直接运行 16 位可执行程序,双击 .exe 只会得到“不是有效的 Win32 应用程序”的提示。这是第一个翻车点:你把答案编译出来了,却没法运行。常见做法是用 DOSBox 模拟一个 DOS 环境,再用 MASM 或 TASM 汇编、连接。我一般选 DOSBox + MASM 6.0,原因很简单:MASM 6.0 的错误提示比 5.0 人性化很多,而且对简化段定义支持更好;TASM 也可以,但某些老答案里的伪指令在 TASM 下行为有差异。DOSBox 是跨平台的,Windows、macOS、Linux 都有包,装好后就是一个虚拟的 C 盘。
2.2 用 DOSBox 挂载目录并运行第一个 .asm:最小命令集
DOSBox 启动后默认在 Z 盘,你需要把宿主机的一个目录挂载成 C 盘。比如我在 D:\asm 下放所有习题代码,那么启动后执行:
mount c d:\asm c: dir解释一下:mount c d:\asm是把宿主机 D 盘的 asm 目录映射成 DOS 的 C 盘;c:切换盘符;dir是查看目录,确认你看到文件。挂载后,写一个最简测试程序 hello.asm:
code segment assume cs:code start: mov ah, 4ch int 21h code ends end start这段代码不做任何输出,只是调用 INT 21H 的 4CH 功能正常退出。把它存成 hello.asm,然后在 DOSBox 里执行:
masm hello.asm,,,; link hello.obj,,,; hello.exeMASM 和 LINK 后面那一串逗号是核心参数:第一个逗号后是目标文件名,留空则默认取源文件名;第二个逗号后是列表文件,留空默认生成 .lst;第三个逗号后是交叉引用文件,留空默认生成 .crf;最后的;表示对后续提问全部用默认值。这一步能过,说明环境是通的。我见过很多新手卡在masm hello.asm;,少写了后面三个逗号,导致 MASM 交互式询问时卡住,其实分号也可以直接结束,但规范的逗号写法更稳妥。
2.3 MASM 和 TASM 在汇编、连接上的参数差异
如果你拿到答案是 TASM 版本的,或者你想换工具链,需要知道两个汇编器的命令差异。下面的表格是我常用的参数对照:
| 操作 | MASM 6.0 | TASM 4.1 |
|---|---|---|
| 汇编 | masm 文件.asm,,,; | tasm /zi 文件.asm |
| 连接 | link 文件.obj,,,; | tlink /v 文件.obj |
| 生成列表 | 默认生成 .lst | tasm /l 文件.asm |
| 调试信息 | masm /zi 文件.asm,,,; | tasm /zi 文件.asm |
/zi是告诉汇编器生成调试信息,这样用 DEBUG 或者 Turbo Debugger 时能看到变量名和源码行号。MASM 里省略则无调试信息,但 LINK 出的 exe 照样能跑。TASM 的tlink /v对应包含调试信息。还有一点:如果你用的是 MASM 5.0 而不是 6.0,遇到assume cs:code, ds:data这样的完整段定义没问题,但遇到.model small简化段定义就会报错。所以下载工具时优先选 MASM 6.0,省掉一堆兼容性麻烦。
3. 把答案文本变成可执行程序:清洗、编译、连接三条流水线
3.1 从答案文本到 .asm 文件的整理流程
网上流传的答案,很多是从老式打印机扫描或复刻出来的,文件里带着行号、分页符、全角标点,甚至有些行被断成了两截。如果你直接复制到代码编辑器,第一行就能让你怀疑人生。我一般会用 Python 写一个清洗脚本,把所有 .txt 转成可编译的 .asm。脚本逻辑很简单:
import re import os def clean_asm(src_path, dst_path): with open(src_path, 'r', encoding='gbk', errors='ignore') as f: content = f.read() lines = content.splitlines() out_lines = [] for line in lines: # 去掉行号:行首的纯数字加空格,例如 "1234 MOV AX, 5" line = re.sub(r'^\s*\d{4}\s*', '', line) # 去掉分页符和特殊控制字符 line = line.replace('\f', '').replace('\r', '') # 全角逗号、冒号、分号换成半角 line = line.replace(',', ', ').replace(':', ':').replace(';', ';') out_lines.append(line) with open(dst_path, 'w', encoding='gbk') as f: f.write('\n'.join(out_lines)) # 用法示例 clean_asm('answer_ch3.txt', 'ch3_1.asm')这段脚本解决三个最常见问题:行号、全角符号、GBK 编码。参数说明:encoding='gbk'是因为老答案文件大多是 GBK 编码,用 UTF-8 读会直接乱码或报错;errors='ignore'表示遇到无法解码的字节直接跳过,保留主体代码。注意脚本只做文本清洗,不会帮你修正语法错误。清洗完的文件必须用编辑器打开看一眼,确认代码段、数据段、堆栈段的结构是完整的。
3.2 编译、连接、调试的三条命令与常见报错对应
拿到清洗后的 .asm,在 DOSBox 里按顺序执行三条命令:
masm ch3_1.asm,,,; link ch3_1.obj,,,; debug ch3_1.exemasm是汇编,把 .asm 转成 .obj;link是连接,把 .obj 转成 .exe;debug是 DOS 自带的调试器,用来单步追踪。如果汇编阶段报错,最常见的是error A2006: undefined symbol,意思是代码里引用了没有定义的符号。很多答案代码片段只给了程序主体,漏了变量定义段,你需要根据上下文补上data segment里的变量声明。另一类常见报错是error A2004: phase error,这通常发生在标号定义冲突或段内偏移越界时,常见原因是答案代码里用了org伪指令重定位,但后面没有对应调整。链接阶段报错一般是LNK1123这类,原因是 .obj 文件格式不兼容或者你用 32 位链接器去连 16 位目标文件。解决方法是确认 MASM 的版本是 16 位工具链,而不是随 Visual Studio 安装的 32 位 link.exe。
3.3 答案里常见的中文注释乱码问题
老答案的注释几乎都是中文,而且编码是 GBK。现代编辑器默认 UTF-8,打开就是一堆乱码。乱码不影响编译,因为注释在汇编时直接被忽略,但会影响你阅读代码逻辑。解决方法是转码。在宿主机上用命令:
iconv -f GBK -t UTF-8 ch3_1.asm > ch3_1_utf8.asm-f GBK指定源编码,-t UTF-8指定目标编码。转换后把 ch3_1_utf8.asm 复制回 DOSBox 挂载目录,源文件名变了,编译时要记得改命令。如果源文件里混有半角字符,iconv 可能报Illegal byte sequence,这时用-c参数忽略非法字符:iconv -c -f GBK -t UTF-8。还有更省事的办法:用 VS Code 打开文件后,点击右下角编码栏,选择“通过编码重新打开”,再选 GBK(gb18030),显示就正常了。这个操作不改变文件本身,但能让你看清注释,自己心里有数。
4. 调试答案时必踩的 5 个坑:现象、原因、解决一条条来
4.1 现象:编译通过但程序一运行就死机
这是最常见的翻车点。你辛辛苦苦把答案整理好,masm和link都通过了,运行 exe 后 DOSBox 直接黑屏或卡死。原因往往不是程序逻辑错,而是段寄存器没有初始化。很多课后答案为了篇幅只写核心代码,故意省略了mov ax, @data和mov ds, ax。在实模式下,数据段寄存器 DS 默认指向 PSP 之后,不设置就访问不了你的数据变量,程序一跑就飞。解决办法是在start:标号后补上两行:
data segment msg db 'Hello$' data ends code segment assume cs:code, ds:data start: mov ax, data mov ds, ax mov ah, 09h lea dx, msg int 21h mov ah, 4ch int 21h code ends end start注意assume ds:data只是告诉汇编器“我打算把 DS 指向 data 段”,它不会自动生成指令。真正把段地址装入 DS 的必须是你写的mov ax, data和mov ds, ax。这个坑教材里不会特意强调,但几乎每个答案都有。
4.2 现象:打印出来全是乱码
程序能跑,但屏幕上输出的不是预期字符串,而是一堆符号。原因有两种可能:一是字符串没有以$结尾。DOS 的 09H 功能调用会一直打印直到遇到$字符,如果你的db定义漏了'$',它会继续读后面的内存字节直到碰巧遇到一个$,中间全被打印出来。二是字符串定义在数据段,但 DS 指向了代码段,导致 DX 指向的地址实际上是代码字节。解决办法是检查字符串定义,确认每个输出串都以$收尾;再检查 DS 初始化代码,确保mov ax, @data写的是数据段名而不是代码段名。如果答案里用的是data segment而不是.data,那么@data这个符号可能不存在,改成实际段名mov ax, data。
4.3 现象:DOSBox 里找不到文件
明明在上一个目录里刚创建的 ch3_1.asm,切到 DOSBox 就File not found。这通常是因为挂载路径不对。DOSBox 的mount c d:\asm是宿主机路径,如果你把文件放在了D:\asm\code里,但在 DOSBox 里直接c:\是根目录,当然找不到。解决办法是先dir看根目录下有什么,或者挂载时挂到具体子目录:
mount c d:\asm\code c: dir还有一个小陷阱:DOSBox 对扩展名敏感,Windows 系统默认隐藏已知文件扩展名,你建的ch3_1.asm.txt在 DOSBox 里显示为ch3_1.asm.txt,不是 .asm。在宿主机资源管理器里把“查看文件扩展名”打开,确认文件后缀确实是 .asm 而不是 .txt。
4.4 现象:答案用的是 0AH 功能调用,DOSBox 里不回显
有些习题是键盘输入类的,答案会调用INT 21H的0AH功能读取缓冲输入。这个功能需要你预先定义一个输入缓冲区,缓冲区第一个字节是最大长度,第二个字节是实际读入字符数,后面才是数据。答案代码里经常只写buffer db 20 dup(?),没有设置首字节最大长度,导致调用失败或者不回显。正确写法是:
buffer db 20, 0, 20 dup('$')这里有三个值:第一个20是缓冲区最大长度,第二个0是实际长度占位,第三个20 dup('$')是存放输入字符的空间。调用时把 DX 指向缓冲区首地址:
lea dx, buffer mov ah, 0ah int 21h如果头两个字节没按这个结构写,DOS 会读到一个随机长度,表现就是回车后没有任何反应。这个坑在实验题里出现频率很高,建议自己写输入功能时直接套用这个结构。
4.5 现象:段定义方式太旧,MASM 6.0 报错
有些答案是按照 MASM 5.0 的旧语法写的,比如用AUTO段、STACK段组合或者奇怪的SEGMENT READONLY参数。MASM 6.0 虽然兼容大部分老代码,但遇到AT表达式或者某些连接指令时会报error A2105: expected。这种情况不要硬改,我一般会直接用 MASM 6.0 的兼容模式编译:
masm /Zm ch3_1.asm,,,;/Zm是启用 MASM 5.0 兼容模式,让汇编器接受老版本的伪指令。如果还报错,那就看一下具体是哪一行,通常是把.sall这种不常用的控制伪指令删掉就能过。还有一点:老答案可能默认是assume cs:code风格,没有定义堆栈段,连接器会警告LNK4038,但一般不影响运行。如果你追求干净,可以补一个堆栈段:
stack segment para stack 'stack' db 100h dup(0) stack ends注意段属性必须带para stack,否则连接器不认它做堆栈段。加了之后警告消失。
5. 用 DEBUG 验证答案:反汇编、单步、读内存的三板斧
5.1 DEBUG 的 U、D、T、G 命令怎么用
答案能不能跑通,编译链接只是第一步。你要真正确认这段代码干的事和书上说的一样,就得用 DEBUG 去“看”运行过程。DEBUG 是 DOS 自带的黑匣子工具,功能不多但够用。最核心的四条命令:
-u cs:0 -d 200 -t -g 0100u是反汇编,u cs:0从代码段偏移 0 开始显示机器码对应的汇编指令;d是查看内存,d 200显示 DS:0200 处的十六进制字节和 ASCII 字符;t是单步执行,每执行一条指令就停住,显示各寄存器状态;g是运行到指定地址,g 0100是运行到 CS:0100 处停下。拿一个答案程序来说,你先r看寄存器,然后t一步一步走,观察 AX、BX、DX 的变化,再d看数据段内存有没有被写入正确值。这个过程比直接运行 exe 更能暴露逻辑错误。我遇到过很多答案代码,表面上跑出正确结果,但内存里多了不该有的字节,全是指针越界,单步才能发现。
5.2 把答案的算法改成你自己的:一个字符串排序的例子
只看答案永远学不会写汇编。一个有效的做法是把答案里的小算法拆了重新拼。比如书里有一道字符串排序题,答案用的是冒泡排序。你可以不看答案,自己先写一遍,再对照看差异。下面是我常写的字符串排序子程序框架:
sort proc mov cx, n ; 外循环次数 outer: push cx mov si, offset str mov cx, n - 1 inner: mov al, [si] cmp al, [si + 1] jbe skip xchg al, [si + 1] mov [si], al skip: inc si loop inner pop cx loop outer ret sort endp说明:mov cx, n是外循环次数,outer标签每轮减少一个待排序元素;内循环用cmp al, [si + 1]比较相邻字节,jbe表示低于或等于则跳过交换;xchg al, [si + 1]是交换内存字节和 AL 寄存器。loop指令每次自动递减 CX 并判断是否为 0,不为 0 则跳转。这个框架的关键参数是n,必须是字符串长度减 1,否则数组越界。你把答案里对应的片段替换成这个,再运行,就能看到两种写法的差异——答案可能用了cmp后加jg跳转,而这里用jbe跳转,都是一样的思路,但你亲手写了一遍就不容易忘。
5.3 验证答案正确性的三个层次
验证一个答案是否“对”,不能只看它能不能跑。我习惯分三层验证。第一层:编译连接通过,exe 能启动不崩溃。这是底线,但很多网上的答案是做不到的,能做到就赢了大多数人。第二层:输入书上的测试用例,输出结果和教材给出的参考答案一致。比如键盘输入“abc”输出“cba”,这层通过说明逻辑正确。第三层:用 DEBUG 单步跟踪关键路径,确认每个寄存器的值变化符合预期。比如看LOOP的计数器有没有减到 0,看INT 21H调用前的 AH 是不是正确功能号,看数据段内存里有没有意外覆盖。第三层是最耗时间的,但也是收获最大的。很多题目的陷阱就在于某个寄存器没有保护,答案能跑但结果不定,只有通过单步看寄存器才能发现。
6. 让答案变成你的程序设计实践:从抄写到修改的学习技巧
答案本身只是半成品,怎么用它决定了你是“抄完就忘”还是“练成手艺”。我用的方法是一个三遍法,适合任何课后习题。第一遍:拿一道题,先不看答案,自己从流程图开始写 .asm,哪怕写不出来也要硬着头皮写几行。第二遍:打开答案,对照自己的代码,找出卡住的地方,把答案的关键段落抄一遍,但不要整段复制,而是手动敲进编辑器。第三遍:把答案关上,凭记忆重新实现这道题,然后和答案做差异对比,同时用 DEBUG 各跑一遍看结果。三遍下来,一个程序的段结构、寄存器用途、常见中断调用基本就刻在脑子里了。
具体执行时有一个小技巧:给每道题建立一个独立目录,比如ch3_ex1、ch3_ex2,目录里放solve.asm(你自己的实现)、answer.asm(原答案)、note.txt(为什么答案这里用LEA而不用MOV)。我一般是先做LEA和MOV OFFSET的辨析,这两个指令在访问变量地址时看起来可以互换,但LEA还能做算术运算,比如lea si, [si + 2];而mov si, offset msg在代码段限定下更直观。这个笔记会变得越来越厚,考前翻一遍比重新看教材效率高得多。还有一点,DOSBox 里的 DEBUG 是英文提示,看不懂没关系,用debug /?把帮助打印出来慢慢查,比在网上搜索快。
我早期做汇编作业时,为了赶进度直接照抄答案,结果练习一过就忘,考试时一上机就翻车。后来养成的习惯是:拿到答案先不看注释,自己推一遍算法思路,再对照代码,把差异点记在 note.txt 里。这个动作让我真正理解了什么是指针、什么是偏移地址,也让我在调试其他高级语言程序时,多了一层“底层直觉”。这个标题里的回答,说到底不是给你一份能抄的代码,而是给你一条验证自己汇编能力的路径。希望我的这套处理流程能让你少走一点弯路,真正把答案变成你自己的程序设计实践。
本文还有配套的精品资源,点击获取