首页 / 资讯中心 / 文章详情

IBM-PC汇编习题答案深度验证:补码、寻址与标志位避坑

IBM-PC汇编习题答案深度验证:补码、寻址与标志位避坑 ★ FEATURED ARTICLE
简介《IBM-PC汇编语言程序设计》习题参考答案面向计算机专业学生、自学汇编的初学者及备考复习者。内容覆盖数制转换、8位补码运算、溢出与进位标志判断、位操作、ASCII码识别及字符串编码等核心知识点包含大量典型例题与详细计算过程答案按章节编排便于对照教材同步练习。压缩包仅为1个doc文档大小524KB轻量精简下载后用Word即可直接查阅。目前已有114人学习下载。通过系统分步解答和结果校核读者既能验证自己的计算结果也能强化补码加减、物理地址换算、字符编码识别等汇编入门的重点难点对理解低层数据表示和后续编写汇编程序很有帮助。1. 从“对答案”到“验答案”这份 IBM-PC 汇编习题答案该怎么用拿到《IBM-PC汇编语言程序设计》的习题答案第一反应基本都是“对答案”核一下自己算的数对不对。但这份由原作者整理的参考答案真正值钱的地方不在最终结果而在推理过程数制转换怎么做、补码减法为什么翻车、寻址方式的有效地址怎么组合、标志位在哪一步变。对正在啃实模式汇编、准备补考或者第一次接触 DEBUG 的人来说这相当于一份带解析的作业批改。我把手册拆完一遍后发现它并不是拿来就能照抄的。里面有几处手稿式省略比如没写操作数宽度、把多条指令连在一行、甚至有个别题目标号错位。所以后文所有结论我都强制用 DEBUG 和脚本重放了一遍——能直接用的答案不到一半剩下的是要二次加工的“半成品”。这篇笔记就把验证过程中值得记录的细节、参数和坑位按章节拆开。2. 数制转换与补码运算用手工验算和 30 行脚本把第 1 章答案跑通2.1 降幂法与除法结果一致但适用场景完全不同第 1.1 题要转 369、10000、4095、32767 这四个数。答案给的是两条路径降幂法和除法。降幂法的思路是每次找一个不大于剩余值的最大的 2 的幂比如 369 256 64 32 16 1写成二进制就是 1 0111 0001B除法是反复除以 2 取余再把余数倒序排列。两条路结果一致但手算时我一般用降幂法因为它每一步都能看出当前位的权重心算不容易错而写循环代码时用除法更顺手因为不需要维护“小于剩余值的最大幂”这个中间状态。x 369 bits n x while n: bits str(n 1) bits n 1 print(bits.zfill(16)) # 高位补0到16位方便按4位一组读 print(hex(x)) # 0x171对应答案的171H这段代码里n 1取最低位等价于n % 2但位运算语义更明确n 1等价于n // 2。zfill(16)是把结果补足到 16 位这样1 0111 0001能直接按0000 0001 0111 0001分组转十六进制避免高位缺失导致分组错位。注意 Python 的hex()输出小写且带0x和手册里的171H格式不同对照时只看数值部分即可。2.2 补码减法85-(-76) 的 OF1 不是偶然第 1.5 题里最值得复盘的是 (4)85-(-76)。按补码规则减法统一转成“被减数加负数的补码”。-76 的 8 位补码是 760100 1100B取反得 1011 0011B加 1 得 1011 0100B。于是 85-(-76) 变成 0101 0101B 0100 1100B 1010 0001B 0A1H。这个结果在 8 位宽度下符号位是 1表示负数 -95显然和真实值 161 不符所以 OF1。判定条件是两个同符号数相加结果符号位与操作数相反。这里 85 和 76 都是正数最高位都是 0加完变成 1就是溢出。a 85 0xFF # 0101 0101 b (-76) 0xFF # 1011 0100 s (a b) 0xFF of ((a 0x80) (b 0x80)) and ((s 0x80) ! (a 0x80)) print(hex(s), of) # 0xa1 True对应OF1 0xFF的作用是把操作数强制看成 8 位补码位模式避免 Python 整数自动扩展成 32 位。OF判断里先比两个操作数的符号位是否相同再比结果符号位是否被改变。这里用True表示 OF1和答案里的OF1对应。如果你把s直接按 16 位解释得到的是 00A1HOF0所以做题前一定要看清题目是否限定位宽。2.3 符号位、ASCII 与无符号读法同一个字节有三种身份第 1.6 和第 1.7 题把这种“多重身份”讲得很透。D8H 作为无符号数是 216作为 8 位补码带符号数是 -404FH 作为数值是 79作为 ASCII 码是字符 O。这说明一个问题汇编里不存在“这个字节是什么类型”的元信息类型完全由指令决定。用MOV AL, [BX]读出来就是字节用MOV AX, [BX]读出来就是字符号位要不要扩展看你在后面用的是CBW还是直接拼接。自查时我会按这个顺序看先确认题目限定的位宽和格式再确认指令默认的段寄存器最后才是数值本身。第 1 章大多数翻车都翻在前两步而不是算术。3. 寻址方式与有效地址计算把 3 个寄存器和 8 组内存值算给你看3.1 七种寻址方式一张表把 BX/SI/D 的组合算完第 3.1 题给了三样东西(BX)637DH(SI)2A9BH位移量 D7237H然后列出七种寻址方式。这题刷一遍基本就把实模式有效地址的规则全带出来了寻址方式有效地址计算结果立即寻址操作数在指令中无 EA直接寻址EA D7237H寄存器寻址操作数在 BX无 EABX 间接寻址EA (BX)637DHBX 寄存器相对寻址EA (BX) D0D5B4H基址变址寻址EA (BX) (SI)8E18H相对基址变址寻址EA (BX) (SI) D004FH最后一行是关键坑位637DH 2A9BH 7237H 1004FH但 8086 的有效地址寄存器只有 16 位最高进位直接丢弃所以答案只写 004FH。这不是算错而是实模式地址加法器的真实行为——EA 本身按 16 位截断物理地址的进位由段寄存器和地址加法器继续处理不影响 EA 寄存器的截断结果。很多教材把这个细节一笔带过做题时才会发现答案对不上。3.2 小端存储同一块内存被四条 MOV 读出四种结果第 3.4 题给的内存数值很典型(DS)2000H(BX)0100H(SI)0002H然后从 20100H 开始连续存 12H、34H、56H、78H从 21200H 开始存 2AH、4CH、B7H、65H。四条 MOV 指令取出的 AX 分别是 1200H、0100H、4C2AH、3412H后面三条内存寻址的差异全在宽度和端序。mem { 0x20100: 0x12, 0x20101: 0x34, 0x20102: 0x56, 0x20103: 0x78, 0x21200: 0x2A, 0x21201: 0x4C, } def word(addr): return mem[addr] | (mem[addr 1] 8) print(hex(word(0x20100))) # 0x3412 print(hex(word(0x20102))) # 0x7856mem[addr 1] 8就是把高地址字节放到高 8 位。x86 是 little-endian低字节在低地址所以 20100H 存 12H、20101H 存 34H 时读字结果是 3412H 而不是 1234H。这个顺序反了后面 3.20 题的双字加法、3.21 的双字求补全都会错。第 3.10 题顺带问了一个必考的区分MOV AX, TABLE是把 TABLE 单元的内容 1234H 送 AXLEA AX, TABLE是把 TABLE 的偏移地址 0032H 送 AX。前者访存后者只算地址。做题时如果答案和你预期差一个“取内容还是取地址”十有八九是这里混了。3.3 转移指令的偏移量3.5 题里的“03H”为什么不能省第 3.5 题的段内直接转移答案算的是(IP)5119H((IP)03H)7CDCH。这里面的 03H 是 JMP NEAR PTR 指令本身的机器码长度E9 后跟 16 位相对位移总共 3 字节。CPU 执行转移时IP 已经指向下一条指令所以相对位移的基准是当前 IP3而不是 JMP 所在地址。常见做法是先把指令长度算死再套公式目标IP 下一条指令的IP 位移量。对于段内间接寻址比如JMP WORD PTR [BX]目标 IP 直接取内存中的字不再加位移对于JMP D[BX]则要加上寄存器相对寻址算出的位移量。三种方式最容易漏的就是第一个“03H”我一般会在草稿上先把指令字节数标出来再动手算。4. 标志位、移位与多字节运算3.19 题逐指令拆 CF/SF/ZF/OF4.1 从 MOV AX,0 到 ROR一条条读 3.19 的答案第 3.19 题是整份手册里信息密度最高的一题十二三条指令每条都要你写出 AX、CF、SF、ZF、OF 五个量。把它完整走一遍基本就把标志位的更新规则复习完了。我把关键几条拆开看MOV AX, 0AX 变 0标志位不变。DEC AXAX 变 0FFFFHCF 不变SF1ZF0OF0。这里最反直觉的是 CF 不动。很多人以为减 1 一定借位但 x86 里 INC/DEC 专门设计成不影响 CF为的是多字节运算时中间增量不破坏外层的进位链。ADD AX, 7FFFH0FFFFH 7FFFH 17FFEH截断后 AX7FFEH。符号位是 0两个正数相加得到正数OF0但无符号结果超过 16 位CF1。ADD AX, 27FFEH 2 8000H符号位从 0 变 1OF1CF0。NOT AX8000H 取反得 7FFFH标志位不变。NOT 不影响任何标志这是和 NEG 最大的区别。SAL AX, 158D1H 左移一位变 B1A2H移动出去的位是 0CF0符号位从 0 变 1OF1。SAR AX, 1B1A2H 算术右移一位变 D8D1H最高位补原来的符号位 1符号不变OF0。ROR AX, 1循环右移一位后是 9397H。ROR 的 OF 定义为最高位和次高位的异或1 XOR 0 1所以 OF1。def add16(a, b): s (a b) 0xFFFF cf 1 if (a b) 0xFFFF else 0 sf 1 if (s 0x8000) else 0 zf 1 if s 0 else 0 of 1 if ((a 0x8000) (b 0x8000)) and ((s 0x8000) ! (a 0x8000)) else 0 return hex(s), cf, sf, zf, of print(add16(0xFFFF, 0x7FFF)) # (0x7ffe, 1, 0, 0, 0) print(add16(0x7FFE, 0x0002)) # (0x8000, 0, 1, 0, 1)这段脚本只模拟了 ADD 的标志计算cf用无符号进位判断of用同号相加判异号。SAL、SAR、ROR 这些移位标志没法靠一个通用函数覆盖建议直接用 DEBUG 的t单步看标志位变化。手算时比脚本更方便的是记住一条原则OF 只在“补码结果超出位宽可表示范围”时置 1CF 只在“无符号结果超出位宽”时置 1。两者在同一指令里可能完全不同比如 0FFFFH 7FFFH 就是 CF1、OF0。4.2 SAL 与 SAR53 和 -49 乘除 2 为什么用的是同一个 SAL第 3.25 题要求把 53 和 -49 分别乘以 2。答案里两个数都用SAL AL, 1得到 6AH 和 9EH。左移不分有无符号因为最高位移出去后由 CF 接收符号位被新位替换这是乘法场景下 SAL/SHL 语义一致的原因。除 2 就不一样了。53 用SAR AL, 1得到 1AH也就是 26向零截断-49 用SAR AL, 1得到 0E7H十进制 -25。如果把 -49 换成SHR AL, 1逻辑右移会把符号位补 0得到 67H十进制 103完全错误。所以选型规则很简单带符号数右移用 SAR无符号数右移用 SHR遇到“除以 2”先想操作数的类型再决定指令。这里还要注意 SAR 对负数的舍入方向。0CFH 算术右移一位是 0E7H等价于 -49 除以 2 后向负无穷取整得到 -25。C 语言里-49 / 2是向零截断结果是 -24和 SAR 的结果不一致。写汇编时如果需要向零截断得先判断符号再修正。4.3 多字节与多精度ADC 和 SBB 在双字运算里的位置第 3.20 题给了两组四字数据要求字相加、双字相加、双字相乘。答案解析里最典型的是双字加法MOV AX, DATAX ADD DATAY, AX MOV AX, DATAX2 ADC DATAY2, AX MOV DATAY4, 0 ADC DATAY4, 0第一组ADD产生进位后第二组必须用ADC把 CF 带进高 16 位最后再加ADC DATAY4, 0是为了承接最高位可能的进位。这里DATAY4就是专门为进位准备的扩展单元如果漏掉最后的ADC双字加法在数值超过 0FFFFFFFFH 时会静默丢失最高位。多字节乘法的结构也类似MUL的结果默认放在 DX:AX但双字乘双字时每两个 16 位块相乘会产生中间积必须把交叉项分别累加到对应的高位块里。答案给的 AA/BB/CC/DD 四个变量就是干这个的。做题时不要只背顺序要记住一个原则无论加减乘除多字节运算的本质是把 N 字节拆成若干 16 位块逐块运算并显式传递进位或借位。5. 避坑对照这份答案自查时最常见的 5 个问题5.1 寻址表达式缺宽度ADD [BX0B2H], CX编译不过现象第 3.2 题答案写成ADD [BX0B2H], CX拿给汇编器编译直接报 operand must have size加上BYTE PTR或WORD PTR才能通过。原因寄存器相对寻址只是算出了内存地址但双操作数指令里内存操作数的宽度必须由PTR显式指定。BX 寄存器本身不携带类型信息汇编器不知道你要按字节还是按字累加。解决字操作写ADD WORD PTR [BX0B2H], CX字节操作写ADD BYTE PTR [BX0B2H], AL。看到答案里省略了 PTR不要直接抄先判断操作数寄存器宽度再补全。这也是考题里最常见的“合法但不完整”写法。5.2 手稿式省略MOV BX, (6-1)*2不是合法指令现象第 3.3 题答案第二问写MOV BX, (6-1)*2然后直接用MOV DX, [BX(6-1)*2]。这不是 MASM 能识别的语法。原因原答案是在表达“第 6 个字的下标偏移是 10”中间省略了将数组起始地址装入 BX 的步骤甚至把立即数表达式当成地址的一部分来写。这是手稿转文字时常见的简化不是标准写法。解决按我的习惯重写为三条指令LEA BX, BLOCK MOV SI, (6-1)*2 MOV DX, [BX][SI]LEA取数组首地址SI存元素偏移最后基址变址并附带默认段 DS。如果直接把立即数塞给 BX 再[BX]取到的是数据段偏移 10 处的字而不是数组第 6 个元素。对照答案时看到这种缩略写法先补 LEA再算偏移。5.3 DEC 不影响 CF减法直觉在这条指令上失灵现象第 3.19 题执行DEC AX后答案标注“CF 不变”很多初学者写成 CF1后续ADC/SBB的推演全乱。原因x86 指令集中 INC/DEC 特意不影响 CF因为它们是“增量/减量”指令常用于循环计数和地址修正如果它们改动 CF会破坏外层多字节运算已经建立好的进位状态。SUB/SBB/CMP 才负责更新 CF。解决判断 CF 前先看指令类型。减法系列只看 SUB、SBB、CMP加法则看 ADD、ADC。遇到DECCF 维持上一状态不变其他 SF/ZF/OF 照常更新。这条规则在 3.13 题堆栈操作、3.18 题DEC AX里反复出现记牢能少踩很多坑。5.4 答案序号错位3.27 题的 RCR DL,1 被排到清单末尾现象第 3.27 题原题列了 (1) 到 (8) 八条指令但答案清单最后一条写的是“(4) RCR DL, 1”和题目序号无法对应容易让人以为做了八条后漏算一条。原因这是手稿排版/OCR 处常见的浮动注释错位不是技术错误。题号保留但没有严格按序排列答案数据本身仍按题目初始条件计算。解决遇到序号对不上不要顺着答案的排位走回到题面(DX)0B9H(CL)3(CF)1重新逐条验证。我一般把每条指令单独抄出来和答案值逐行对齐不依赖它标的编号。RCR DL,1 的初始值其实是(DL)0B9H, CF1右移一位后是 0DCH与答案一致。5.5 位宽陷阱8 位补码和 16 位补码结论完全不同现象第 1.5 题限定 8 位补码85-(-76) 结果写 0A1H、OF1。如果按 16 位算结果是 00A1H、OF0答案看起来就像“错了”。原因位宽决定符号位位置。8 位时 0A1H 最高位是 1解释为负数 -95明显溢出16 位时 00A1H 最高位是 0解释为正数 161正好是真实值。两个结果都对前提是位宽匹配。解决做题前先把位宽圈出来。题目说“8 位二进制补码”就全程按 8 位操作立即数超过 0FFH 要截断说“16 位双字”就在地址计算时保留 16 位 EA。遇到答案和预期差一个符号位先查位宽再查端序这两步能排除一半以上的错误。6. 把答案变成调试用例用 DEBUG 单步重放一遍 3.19 的指令序列最后说一个我到现在还在用的验证套路把习题答案里的每一条指令原封不动敲进 DEBUG用t单步跟踪逐条对比 AX 和标志位。这个方法不挑环境DOSBox 里打开 DEBUG 就能做。切入点是第 3.19 题的整段指令。先把初始状态清干净再输入指令序列-a MOV AX, 0 DEC AX ADD AX, 7FFF ADD AX, 2 NOT AX SUB AX, FFFF ADD AX, 8000 SUB AX, 1 AND AX, 58D1 SAL AX, 1 SAR AX, 1 NEG AX ROR AX, 1每条指令后用r看寄存器用t单步执行。DEBUG 的标志位显示是一组缩写NV 表示 OF0OV 表示 OF1UP 表示 DF0DI 表示 IF0PL 表示 SF0NG 表示 SF1NZ 表示 ZF0ZR 表示 ZF1NA 表示 CF0CY 表示 CF1。对照 3.19 的答案时直接把答案里的 CF/SF/ZF/OF 翻译成这些缩写逐行核对。我一般还会再写一段 Python 断言脚本把关键节点固化成自动化检查cases [ (ADD AX, 7FFF, 0xFFFF, 0x7FFF, 0x7FFE, 1, 0, 0, 0), (ADD AX, 2, 0x7FFE, 0x0002, 0x8000, 0, 1, 0, 1), (SUB AX, 1, 0x0000, 0x0001, 0xFFFF, 1, 1, 0, 0), ] for name, a, b, exp_s, exp_cf, exp_sf, exp_zf, exp_of in cases: s, cf, sf, zf, of add16(a, b) assert (s hex(exp_s) and cf exp_cf and sf exp_sf and zf exp_zf and of exp_of), nameadd16函数复用第 4 章的模拟逻辑断言失败会直接报出指令名定位到具体哪一步算错。这个脚本不是验证答案本身而是验证我自己有没有把标志位规则记混。从那以后我每次拿习题答案都强制走一遍 DEBUG 重放不再直接信纸面结论尤其是那些没有标注位宽和溢出状态的中间步骤。DEBUG 给你看的是硬件真实行为比翻答案页更快也比凭记忆推标志位更可靠。希望这套“答案当用例”的办法能帮你把习题册里的每一道题都变成可验证的调试记录。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站