汇编期末冲刺:2022 B 卷逐题拆解与三个课程项目
⚠︎ This post has no English translation yet — showing the original Chinese text.
这篇讲考试本身:一道题一道题拆,错在哪、怎么改、给几分,最后用三个项目把所有零散的东西串起来。
系列里的另外三篇:为什么学汇编 讲入门和路线,环境与 14 课实战 讲怎么跑代码,王爽完整教程 按教材章节查知识点。
源码在仓库
汇编/目录里,每道编程题都有能直接编译运行的标准答案。
目录
1. 这份卷子的结构
2022-2023 学年 B 卷是能找到的最新一份,题型和现在最接近:
| 大题 | 分值 | 考什么 |
|---|---|---|
| 一、分析简答 | 40 | 找指令错误、伪操作错误、移位、数据定义、标志位、ORG |
| 二、程序分析 | 30 | 填空、读运行结果、宏展开 |
| 三、程序设计 | 30 | 写完整程序 |
一个很实在的观察:前 70 分几乎全是”能不能一眼看出问题”,真正的完整编程只占 30 分。所以复习的重心不该是背程序,而是把常见的错误形式认熟。
下面所有标 [实测] 的地方,都是把题里的错代码真的敲进 MASM 跑出来的报错原文,不是书上抄的。
2. 分析简答题:六类错误
2.1 内存 + 内存
ADD VAR1, VAR2 ; ✗ error A2052: Improper operand type8086 的加法电路一头必须接寄存器,硬件上就没有”内存 + 内存”这条指令。改法是借一个寄存器中转:
mov ax, VAR1add ax, VAR2mov VAR1, ax ; 注意:ADD 不会自动写回内存得分点:很多同学只写到 add ax, VAR2 就停了,忘了结果还得搬回去。
2.2 类型不匹配
SUB AL, VAR1 ; ✗ warning A4031: Operand types must matchAL 是 8 位,VAR1 是 16 位的字变量,宽度对不上。有意思的是 MASM 只给警告不给错误,但考卷要的就是这个错误写法。改法:sub al, byte ptr VAR1,或者把 AL 换成 AX。
2.3 跳转目标搞混
三个错误指向同一件事——跳转的目标必须是地址,不能是数据:
JMP LAB[SI] ; ✗ error A2044: Must be associated with dataJNZ VAR1 ; ✗ error A2045: Must be associated with codeJMP NEAR LAB ; ✗ error A2028: Operator expectedJMP LAB[SI]把代码标号和变址寻址搅在一起。LAB是代码段地址,[SI]默认按数据段寻址,汇编器不知道你要哪段。勉强能编过的写法是jmp word ptr cs:LAB[SI],但考试写标准答案。JNZ VAR1更直接:VAR1里装的是数据,不是地址。条件转移的目标只能是标号。JMP NEAR LAB是语法问题,NEAR不能裸用,得写JMP NEAR PTR LAB,或者直接JMP LAB。
2.4 伪操作错误
DATA_SEG SEG ; ✗ error A2105: Expected: instruction or directiveSEG 是取段地址运算符(mov ax, seg data 里的那个),不是伪指令。定义段用 SEGMENT。
MYDATA SEGMENT/DATA ; ✗ error A2105: Expected: align, combine, or 'class'SEGMENT 后面只能跟对齐、组合、类别三个字段,/DATA 什么都不是。
END 的位置是最常犯错的一个:
MAIN_PROC PROC FAR ... END MAIN_PROC ; ✗ 错在这MAIN_PROC ENDPEND 是整个源程序的最后一条语句,它一出现,后面的东西全被无视,于是过程没闭合、段也没闭合。报错原文是:
Open segments: CODEOpen procedures: MYP正确顺序:
MAIN_PROC ENDPEND MAIN_PROC ; END 后面跟"程序入口标号"2.5 移位当乘法,要补一句
MOV CL, 3SHL AX, CL ; ×8SHL BL, CL ; ×8SHL DH, CL ; ×8这三条能完成任务,但必须说清条件才能拿满分:移位是无检测的乘法,乘积超出位数范围时高位直接丢失。比如 BL = 40H,×8 = 200H 装不进 8 位,结果就是错的。
另外 8086 上移位数大于 1 时必须放 CL,不能写 SHL AX, 3。
2.6 MOV 和 OFFSET:搬内容还是搬地址
MOV AX, TABLE ; AX = 0FF00H ← TABLE 里的内容MOV AX, OFFSET TABLE ; AX = 0100H ← TABLE 自己的偏移一句话:一个搬内容,一个搬地址。LEA AX, TABLE 和后者等价。
配套的还有 WORD PTR 的用法:
CODELIST DB 1,7,8,3,2MOV SI, WORD PTR CODELIST ; SI = 0701H(小端序)CODELIST 本身是字节型,不加 WORD PTR 汇编器不让你一次取两个字节。
3. 标志位与 ORG:两道需要动手算的题
3.1 标志位连线题
初始 BX = 0FFFFH,逐条执行后填 SF / ZF / CF:
| 指令 | SF | ZF | CF | 原因 |
|---|---|---|---|---|
XOR BX, BX | 0 | 1 | 0 | 结果为 0;XOR 恒清 CF |
ADD BX, 8001H | 1 | 0 | 0 | 结果 8001H,最高位为 1;未进位 |
SAR BX, 1 | 1 | 0 | 1 | 8001H → C000H,移出的 bit0 进 CF |
背一句口诀就够应付全部移位题:移出去的位进 CF。SHL、SHR、SAR、ROL 都遵守这条。
3.2 ORG 与内存分配
DATA SEGMENT ORG $+20HDA1 DB 10H DUP(2,3)DA2 DW 10H DUP(2,3) ORG $+20HBUF DD DA1,DA2DATA ENDS把 $ 当成”当前偏移”,一步步挪:
ORG $+20H:$从 0 → 20HDA1 DB 10H DUP(2,3):每个重复单元是 2 字节(2 和 3),重复 10H 次 → 32 字节,$→ 40HDA2 DW 10H DUP(2,3):每个重复单元是 2 个 DW = 4 字节,重复 10H 次 → 64 字节,$→ 80HORG $+20H:$→ A0H
答案:DA1 占 32 字节,DA2 占 64 字节,BUF 的偏移是 A0H。
题目本身不长,但最容易错的是第 3 步——DW 每个元素 2 字节,一个重复单元里有两个 DW,所以是 4 字节不是 2 字节。这种题光看说明书没用,得手算几遍。
4. 程序分析题:填空的套路
4.1 求数组平均值
FLD DW 10,-20,30,-60,-71,80,79,56 MOV CX, 8 XOR AX, AX (1) MOV SI, OFFSET FLD ← 空①R1: (2) ADD AX, [SI] ← 空② ADD SI, 2 LOOP R1 MOV CL, 8 IDIV CL填空题的判据全在上下文里:
- 循环体用
[SI]且ADD SI, 2→SI是数组指针,但没人初始化它 → 空①必须是MOV SI, OFFSET FLD。 - 循环体只有指针自增没有累加 → 空②是
ADD AX, [SI]。
还有一个容易被忽略的细节:数据里有负数,所以用的是 IDIV(带符号除法)而不是 DIV。题目特意这么选。
4.2 两数交换
要求大者进 NUM,小者进 NUM+1,两者都是8 位带符号数:
MOV AL, NUMCMP AL, NUM+1(1) JGE DONE(2) XCHG AL, NUM+1 MOV NUM, ALDONE:两个得分点:
- 题目强调”带符号” → 必须用
JGE/JL这组,用JAE/JB直接错。这是整道题的题眼。 - 相等时谁在谁的位置都对,所以用
JGE(大于等于)而不是JG。
4.3 读循环结果
MOV AX,0 / MOV BX,1 / MOV CX,5LP: ADD AX,BX ADD BX,2 LOOP LP手动展开:9 次累加项是 1、3、5、7、9,和是 25。但还有两个不用算就能拿分的地方:
BX最后又加了 2,变成 11(不是 9)。CX循环完是 0。
判卷时这两处经常被漏掉,写了才不丢分。
5. 宏展开:一个宏两种命运
MSG MACRO P1, P2, P3 IN&P1 P2 P3ENDMK=1MSG %K, DB, 'MY NAME'MSG C, AX展开结果:
IN1 DB 'MY NAME' ; P1=1,& 拼成数据名INC AX ; P1=C,& 拼成指令助记符这道题的妙处在于同一个宏,P1 传不同的值,& 拼接出来一个是数据定义、一个是指令。
% 的作用也要说清:%K 把 K=1 先算出数值再传参。不写 %,传过去的是字面 K,拼出来是 INK,名字对不上直接报错。
怎么验证:看列表文件。MASM 会把你”粘贴”出来的每一行都列出来:
masm L14MAC, L14MAC.OBJ, L14MAC.LST;打开 .lst 搜 inc ax,能看见展开的痕迹。这个技巧比背结论管用,宏题不再靠想象。
两个小提醒:
- 仓库
汇编/LAB/L14LIS.TXT是一份已经生成好的样本,直接打开就能看到展开过程。 - 你自己生成的列表文件会把源码注释的中文一起写进去,按 GBK 编码,在 VS Code 里显示成乱码。别管乱码——汇编出来的指令、地址、机器码全是纯 ASCII,直接搜
inc ax就能找到。想看中文就用记事本以 ANSI 编码打开。
6. 三道程序设计题
三道题都在仓库 汇编/EXAM/ 里有可直接编译的答案。
6.1 统计 AX 中 “1” 的个数
思路是 ROL 把最高位转进 CF,看 CF 计数,转 16 次。
坑:CX 通常用来存计数,但这里结果要计数、循环也要计数,两者都抢 CX。解决办法是结果放 CX,循环次数改用 BX。
6.2 求带符号数 X 的绝对值
CMP AL, 0JNS done ; 非负就跳过NEG ALdone:考点藏在边界上:X = 80H(-128)时 NEG 之后还是 80H,因为 +128 装不进 8 位有符号数。老师爱追问这个。
6.3 1~50 中偶数累加
小步长直接走偶数:
mov ax, 0mov bx, 2sum_loop: add ax, bx add bx, 2 cmp bx, 50 jbe sum_loop结果 650。关键在于 650 > 255,所以存结果的变量必须用 DW 不能用 DB——这种细节是拉开分差的地方。
7. 三个课程项目
单个知识点会了,下一步是把它们串起来。三个项目的难度是递增的。
7.1 P1:整数计算器
CALC: 12+34 = 46CALC: 7*6 = 42CALC: 100/7 = 14 rem 2CALC: 3-10 = -7输入一整行,程序自己解析两个数和运算符。用到的新招式:
- 0Ah 缓冲输入:缓冲区格式是
db 最大长度, 实际长度, 内容...,实际长度在[buf+1]。这个格式记不住就会一直读到垃圾。 - 16 位乘除:
mul bp的积在DX:AX,div bp的商在AX、余数在DX。 - 负数显示:先判断符号,打
-,再neg取绝对值。
有个细节值得注意:3-10 和 5-12 输出都是 -7,这正好验证了负数处理逻辑是对的。另外 1000*1000 和 5/0 这种边界输入,程序里都做了检查。
7.2 P2:成绩管理
choice> 1score (1-100): 88score (1-100): 73score (1-100): 95score (1-100): 101 invalid! only 1-100score (1-100): 0 invalid! only 1-100score (1-100): OK, total = 3
choice> 4 --- stats --- max = 95 min = 73 avg = 85.3这是课程设计级别的综合程序,把菜单、数组、排序、统计全放进一个程序里。
数组下标和地址的换算值得单独看一眼:scores + i*2。8086 不支持 [bx+i*2] 这种乘过之后再寻址的写法,得先算好偏移再加。
平均分 85.3 是 256/3 = 85.33 保留一位,余数单独算小数位。
7.3 P3:电子钟
19:31:07 Press ESC to quit这个项目把前面几课全用上了:
- L10 的显存直写,把黄色提示固定在最后一行
- L11 的
int 21h2Ch 取时间 + 补零打印 - L12 的非阻塞按键检测(这里是 DOS 的 0Bh/08h 版本)
最值得学的是它的主循环结构:
每一轮心跳: a. 查键盘,ESC 就退出 b. 取时间 c. 秒变了才重画“秒变了才重画” 用了一个叫”脏检查”的模式:把上次显示的秒存起来,只有变化了才动笔。不做这个,屏幕每秒闪几十次。这个套路以后写任何菜单或游戏都会用到。
另外注意光标定位:用 int 10h 2 号功能把光标移到固定位置再打印,而不是每次都刷整屏。
8. 复习清单
按投入产出比排序:
| 题型 | 考什么 | 在哪练 |
|---|---|---|
| 指令找错 | 内存-内存不行、类型要匹配、跳转目标必须是标号 | L05CMP + 本文第 2 节 |
| 伪操作 | SEGMENT/ENDP/END 的顺序,SEG 是运算符 | L13DIR L01HI |
| 数据定义 | ORG / DUP / $ / 偏移计算 | L13DIR |
| 标志位 | SHL/SAR 后的 CF/SF/ZF | L08NUM |
| 程序填空 | 指针初始化 + 累加 + 有符号/无符号跳转的选择 | L04BX L05CMP |
| 读循环结果 | 循环结束后 CX 归零、BX 多走一步 | L03LOOP L06SORT |
| 宏 | MACRO / & 拼接 / % 展开 | L14MAC |
| 程序设计 | 位统计、绝对值、条件累加 | EXAM/Z22B1~3 |
| 上机题 | U/T/P/R/D/G | EXAM/DEBUG练习.md |
两个复习建议:
第一,边做题边跑。 觉得”这条指令应该报错”,就真敲进 MASM 看一眼。报错原文记在脑子里,考场上比推理快得多。本文所有 [实测] 标记的地方都是这么来的。
第二,每道编程题先自己写,再看答案。 汇编/EXAM/ 里的答案程序都能直接编译运行,写完之后对比输出。看得懂和写得出,中间隔着一条河。
