简介:《IBM-PC汇编语言程序设计》习题参考答案,面向使用沈美明、温冬婵教材学习8086/80x86汇编的学生与自学者,用于核对课后练习、梳理数据表示与运算规则。压缩包仅含1个doc文档,约524KB,内容按章节组织,覆盖数制转换、补码加减与CF/OF标志判断、位运算、ASCII字符编码、字符串处理,以及实模式存储分段、物理地址计算和寄存器状态等典型题目。已有115人学习,适合在复习或备考时对照答案定位思路,也可作为教师布置与讲评习题的参考。通过逐题答案与运算过程,读者可检查手工换算是否准确,理解补码运算和标志位变化,并借助存储器示例巩固段地址与偏移地址的换算方法。
1. 一份“答案”里藏着计算机的思维方式
很多人拿到《IBM-PC 汇编语言程序设计》的习题答案,第一反应是对数字:题做完了翻一翻,十六进制对上了就合上书。可真翻几页会发现,那些0F7H、1010 1011B、CF=0、OF=1本身就在讲一件事——一台只会“通”和“断”两种状态的机器,怎么把十进制数、负数、字母和内存地址统统变成电信号。打个比方,这就像看菜谱的成品图:图是结果,可要真想学会做菜,得看懂图里为什么先放盐后放糖。这份 IBM-PC 汇编语言程序设计答案的价值不在数字本身,而在它把每一步推导摊开给你看:为什么 369 是171H,为什么 -85 写成0ABH,为什么 4FH 念作字母 O。搞懂这些,等于拿到一张“CPU 视角”的入场券。
2. 为什么汇编的答案看起来像天书:答案背后的三层地基
翻开任何一页答案,扑面而来的是进制、补码、ASCII 这三类东西。它们不是三个孤立的知识点,而是三层依次叠起来的地基:最底下是数制,负责把人类习惯的十进制翻译成开关状态;中间是补码,负责让减法不用真的造一台减法电路;最上面是 ASCII,负责让字母和符号也能进入这台只认数字的机器。三层地基任何一层没夯实,后面的寻址方式、标志位、串操作都会看得云里雾里。所以读答案的正确顺序不是从第一题开始硬啃,而是先把这三层各自搞明白,再回头看具体题目,会发现每一行推导都有出处。
2.1 第一层地基:数制转换,除 2 取余背后的关与开
答案第一章开头就是十进制转二进制。以 369 为例,两种手算路子都值得走一遍。降幂法是找“不超过它的最大 2 的幂”:369 里能装下 256,剩 113;能装下 64,剩 49;能装下 32,剩 17;能装下 16,剩 1;能装下 1,剩 0。于是 256、64、32、16、1 这几个位置填 1,其余填 0,得到1 0111 0001B。另一种是除法:反复除以 2,把每次的余数从下往上倒着读。两种方法结果一样,前者直观,后者适合写程序。
十六进制那一步更省事。因为 16 等于 2 的 4 次方,所以每 4 位二进制正好对应 1 位十六进制。把1 0111 0001从右往左四位一组补齐,变成0001 0111 0001,也就是171H。同理 10000 是10 0111 0001 0000B,分组后0010 0111 0001 0000,写成2710H;4095 是十二个 1,分组后1111 1111 1111,就是FFFH;32767 是十五个 1,写成7FFFH。这条“四位一组”的规矩记住,二进制和十六进制之间的互转基本不会翻车。
# 用整除取余把十进制拆成任意进制,和手算的“除 2 取余”完全等价 def to_base(n, base): digits = "0123456789ABCDEF" if n == 0: return "0" out = [] while n: out.append(digits[n % base]) # 取余,得到当前最低位 n //= base # 商继续往下除 return "".join(reversed(out)) # 余数是倒着生成的,要反过来读 print(to_base(369, 2)) # 101110001 print(to_base(369, 16)) # 171 print(to_base(32767, 16)) # 7FFF这段代码里n % base取的是当前最低位,n //= base是把已经处理过的那一位“砍掉”,循环结束时out里存的是从低位到高位的顺序,所以最后要reversed。把base换成 16,就是答案里那些H结尾数字的生成过程;换成 2,就是那串 1 和 0。理解了这个循环,再看答案里的十六进制加减法就不会觉得是玄学——3A+B7之所以等于F1H,是因为按十六进制满 16 进 1 算完,再换回十进制 241 校核,两边对得上就说明没算错。
2.2 第二层地基:补码,让减法变成加法的那只黑匣子
计算机里没有“负号”这个符号,只有高低电平。要让一台只会加法的机器完成减法,就得给负数换一套写法,这套写法叫补码。规则很干脆:正数的补码就是它自己;负数的补码是把它对应的正数按位取反再加 1。以 85 为例,八位二进制是0101 0101B,取反得1010 1010B,加 1 得1010 1011B,也就是0ABH。所以答案里 -85 写成1010 1011B,不是笔误,而是它在这套规则下的“身份证”。
这套规则最妙的地方在于,加法和减法可以共用一套电路。(-85)+76在答案里被算成1010 1011B + 0100 1100B = 1111 0111B,也就是0F7H。按无符号数看,这是 171+76=247;按有符号数看,0F7H表示 -9,正好等于 -85+76。同一串比特,两种读法都成立,这就是补码省下来的那套硬件。八位补码能表示的范围是 -128 到 127,一旦结果跑出这个区间,符号位就会被“挤歪”,答案里那个OF=1就是在报告这件事。
还有一个常被忽略的点:同一串二进制,被当成有符号数和无符号数,读出来的十进制完全不同。D8H按无符号读是 216,按补码读是 -40;FFH按无符号读是 255,按补码读是 -1。答案在 1.6 题里专门问这个,就是在提醒:位的含义不写在位里,写在你看它的方式里。
提示:判断一个十六进制数是不是负数,看最高位那一位。八位里最高位是 1,按补码解释就是负数;要算它的真值,用 256 减去这个无符号数即可,比如
D8H就是 256-216=-40。
2.3 第三层地基:ASCII,4FH 为什么念作字母 O
数字能进机器了,字母怎么办?答案是给每个字符编一个号,这套编号就是 ASCII。答案里 1.7 题问4FH表示什么字符,答“O”,因为 4FH 换算成十进制是 79,而大写字母 O 的编号正是 79。同理2BH是 43,对应加号+;73H是 115,对应小写s;59H是 89,对应大写Y。这三组数字最好背下来当锚点:大写 A 是 41H,小写 a 是 61H,数字 0 是 30H,空格是 20H。
最容易搞混的是数字字符和数值本身。字符'0'的 ASCII 码是30H,也就是十进制 48,不是 0。答案里那道把3692转成 ASCII 的题,结果是33H 36H 39H 32H,正是四个数字字符各自的编号。字符串末尾还出现了0AH和0DH,这两个是控制字符:0DH是回车,0AH是换行。文本文件里看到的一行结束,底层就是这两个字节在起作用。
# 逐个字符看它的 ASCII 码,和答案里的十六进制序列一一对应 s = "For example" print(" ".join(f"{ord(ch):02X}H" for ch in s)) # 46H 6FH 72H 20H 65H 78H 61H 6DH 70H 6CH 65Hord(ch)取字符的编号,02X表示用两位大写十六进制显示,不够两位前面补 0。跑一遍就能看到空格为什么是20H——它在字符串里也占一个位置,不是“什么都没有”。理解了这一层,答案里那些串操作题目(把一段字符串搬到另一块内存、在字符串里找空格、找连字符)就不再神秘:它们无非是在一堆编号上做比较和搬运。
3. 寄存器与寻址方式:答案里出现频率最高的两个名词
翻过前两章,答案的画风会突变:题目里开始出现(BX)=637DH、(DS)=2000H、EA=...、PA=...这类写法。初学者常在这里卡住,因为寄存器名字太多,寻址方式名字太长。其实这两件事说的是一回事:寄存器是 CPU 手边那几个能立刻拿到东西的抽屉,寻址方式是“告诉 CPU 去哪个抽屉、或者按什么路线去内存里取东西”的说法。把抽屉认全、把路线认熟,后面所有指令题都只是套用。
3.1 寄存器:CPU 桌面上的几个抽屉
答案 2.7 题把这些抽屉的分工列得很清楚。加减法这类运算,操作数放在数据寄存器里,也就是 AX、BX、CX、DX 这四个十六位的;循环计数默认用 CX,因为循环指令隐含地拿它当计数器;乘法和除法绕不开 AX 和 DX,十六位乘十六位会得到三十二位结果,高十六位放 DX、低十六位放 AX;段地址放在四个段寄存器 CS、DS、SS、ES 里;下一条要执行的指令地址由 CS 和 IP 一起给出;要从堆栈取数据的地址由 SS 和 SP 一起给出。
更细一层,AX、BX、CX、DX 每个还能拆成两个八位的半边,比如 AX 拆成 AH 和 AL,高八位叫 AH,低八位叫 AL。答案里有道题专门考这个:把BL的内容送到DL,属于八位传送,不碰高八位。这种拆法在老架构里是为了兼容更早的八位芯片,写程序时要注意别把不想改的半边顺手改掉。
| 寄存器 | 主要用途 | 答案里的典型问法 |
|---|---|---|
| AX、BX、CX、DX | 存放运算数据,AX 常用于乘除和累加 | 加法、减法用哪些寄存器 |
| SP、BP、SI、DI | 指示堆栈和内存地址 | 哪些寄存器能指示存储器地址 |
| CS、DS、SS、ES | 保存各逻辑段的起始地址 | 段地址存在哪里 |
| IP | 存放下一条要执行指令的偏移 | 指令地址由谁给出 |
| FLAGS | 存放运算结果的状态标志 | OF、SF、ZF、CF 的值 |
答案 2.8 题问“哪些寄存器可以用来指示存储器地址”,标准答案是 BX、BP、SI、DI,加上堆栈操作时的 SP,以及对应的段寄存器,三十二位及以后的机型还有 E 系列。注意这里问的是“能用来指示地址”,不是“专门存地址”,因为 BX 平时也拿来装数据,只在加了方括号的场合才当地址用。这种“一个寄存器身兼数职”的设计,是理解后面寻址方式的关键。
3.2 寻址方式:你要的东西到底放在哪个抽屉里
寻址方式听起来吓人,拆开看就一句话:操作数在哪。答案 3.1 题给了一组数:(BX)=637DH,(SI)=2A9BH,位移量 D=7237H,然后让你分别算七种寻址方式下的有效地址。有效地址(EA)的意思是“偏移地址是多少”,它不等于物理地址,物理地址还要加上段地址换算。
立即寻址最简单,操作数直接写在指令里,没有有效地址这回事。直接寻址把偏移地址写死,EA 就是那个位移量 7237H。寄存器寻址的操作数就在寄存器里,也不需要算 EA,值就是 637DH。寄存器间接寻址是先让 BX 装着一个地址,再去那个地址取内容,EA 等于 BX 的值 637DH。寄存器相对寻址是“BX 加上一个位移量”,EA 等于 637DH+7237H=D5B4H。基址变址寻址是两个寄存器相加,EA 等于 637DH+2A9BH=8E18H。相对基址变址把三者加起来,EA 等于 637DH+2A9BH+7237H=1004FH。
最后这个结果超过了一个段的边界,十六位偏移装不下,最高位的那次进位被丢掉,EA 实际变成 004FH。答案特意把这个回绕写出来,是在提醒:偏移地址是十六位的,超过 FFFFH 就绕回去了。这个细节在实际程序里是踩坑高发区,段边界附近访问数据很容易翻车。
; 同一块内存,五种写法,五种寻址方式 MOV AX, 1200H ; 立即寻址:操作数就在指令里 MOV AX, [1200H] ; 直接寻址:偏移地址写死在指令中 MOV BX, 1200H MOV AX, [BX] ; 寄存器间接:地址存在 BX 里,再去取 MOV AX, 1100H[BX] ; 寄存器相对:BX 加上位移量 1100H MOV AX, [BX][SI] ; 基址变址:BX 加 SI MOV AX, 1100H[BX][SI] ; 相对基址变址:三者相加这里最关键的一条规矩:方括号表示“去这个地址取内容”,没有方括号就是数值本身。MOV AX, 1200H是把数值 1200H 装进 AX;MOV AX, [1200H]是去偏移 1200H 那个内存单元把内容取出来装进 AX。这两条指令长得像,语义完全不同,答案 3.4 题就是专门练这个区分:给一串内存单元的内容,让你分别算出七种寻址方式下 AX 到底拿到什么。
3.3 物理地址:段地址乘以 16 再加偏移,为什么是这么个算法
实模式下内存地址是二十位的,能寻址 1MB;可寄存器只有十六位,最大表示到 FFFFH,也就是 64KB。两个十六位拼一个二十位,办法就是把段地址左移四位(相当于乘以 16,十六进制里就是末尾补一个 0),再加上偏移地址。答案 2.4 题给的三个组合最能说明问题:3017:000A算出来是30170H+000AH=3017AH;3015:002A算出来是30150H+002AH=3017AH;3010:007A算出来是30100H+007AH=3017AH。三个写法完全不同,指向的却是同一个物理单元。
这就是“段可以重叠”的含义:段的划分是逻辑上的,不是物理上切好的格子。也正因为偏移地址是十六位,每一段最多只能覆盖 10000H 个字节,也就是 64KB,答案 2.6 题问的正是这个数。至于程序的第一条指令在哪,答案 2.5 题给的做法是拿 CS 和 IP 套同一个公式:(CS)=0A7F0H、(IP)=2B40H,物理地址就是0A7F00H+2B40H=0AAA40H。
# 段地址左移 4 位再加偏移,就是这个公式的全部 def physical(seg, off): return (seg << 4) + off for s, o in [(0x3017, 0x000A), (0x3015, 0x002A), (0x3010, 0x007A)]: print(f"{s:04X}:{o:04X} -> {physical(s, o):05X}H") # 3017:000A -> 3017AH # 3015:002A -> 3017AH # 3010:007A -> 3017AHseg << 4就是左移四位,等价于乘 16,也等价于在十六进制数末尾添一个 0。参数seg是段寄存器里的值,off是偏移,两个都是十六位,加起来最多二十位,正好覆盖 1MB。写程序时如果算出来的物理地址看着不对,先检查这两点:段地址有没有漏掉那个“乘 16”,偏移有没有超出 64KB 回绕。
4. 标志位与指令:为什么答案总要写 CF=0、OF=1
答案里几乎每道算术题后面都跟着一串CF=0;OF=0,看着像多余的尾巴,其实是整份资料里信息密度最高的部分。指令执行完,CPU 不只是给出一个结果,还会顺手在标志寄存器里留下一份“体检单”,记录这次运算有没有进位、有没有溢出、结果是不是零、是正还是负。后面的条件跳转全靠这份体检单做判断,所以答案必须把它写全。读懂这份体检单,等于读懂了程序的分支逻辑从哪来。
4.1 标志位:运算完 CPU 留下的那张体检单
状态标志一共六个:CF 是进位标志,记录最高位有没有往上进或借;OF 是溢出标志,记录有符号数的结果有没有跑出表示范围;SF 是符号标志,记录结果的最高位是 0 还是 1;ZF 是零标志,结果为零时置 1;AF 是辅助进位,记录低四位有没有向高四位进位,主要给十进制调整指令用;PF 是奇偶标志,记录结果低八位里 1 的个数是奇数还是偶数。另外还有三个控制标志:DF 决定串操作时地址是增还是减,IF 控制可屏蔽中断,TF 用于单步调试。
答案 2.6 题给了一行调试器输出的寄存器快照,后面跟着NV UP DI PL NZ NA PO NC,这串缩写就是标志位的速记。NV是 No oVerflow,OF=0;UP是方向向上,DF=0;DI是关中断,IF=0;PL是 Plus,SF=0 表示结果为正;NZ是 Not Zero,ZF=0;NA是 No Auxiliary carry,AF=0;PO是 Parity Odd,PF=0 表示低八位里 1 的个数为奇数;NC是 No Carry,CF=0。把这八个缩写背下来,看调试器界面就不会一头雾水。
| 标志 | 全称含义 | 为 1 时说明什么 | 调试器缩写 |
|---|---|---|---|
| CF | 进位/借位 | 最高位发生了进位或借位 | CY / NC |
| OF | 溢出 | 有符号结果超出表示范围 | OV / NV |
| SF | 符号 | 结果最高位为 1,即负数 | NG / PL |
| ZF | 零 | 结果恰好为 0 | ZR / NZ |
| AF | 辅助进位 | 低四位向高四位有进位 | AC / NA |
| PF | 奇偶 | 结果低八位中 1 的个数为偶数 | PE / PO |
| DF | 方向 | 串操作地址递减 | DN / UP |
注意 AF 这个标志在逻辑运算之后是“无定义”的,答案里常写“AF 无定义”,意思是不用去管它,不同芯片可能给出不同值。这种“无定义”不是答案偷懒,而是硬件规格本身就这么写的,写程序时不能依赖它。
4.2 指令家族:答案里出现最多的五类操作
整份答案翻下来,指令虽然多,但按功能归类就那么五族。传送族负责搬东西:MOV 在寄存器和内存之间搬,PUSH 和 POP 走堆栈,XCHG 交换两边,LEA 取有效地址。算术族负责算:ADD、SUB 是基本加减,ADC 和 SBB 带上进位一起算,专门用于多字节运算;INC 和 DEC 是自增自减;NEG 取补;MUL 和 IMUL 是无符号和有符号乘法;DIV 和 IDIV 是对应的除法;CMP 做减法但不保存结果,只改标志位。
逻辑族做位级操作:AND 用来清零某些位,OR 用来置位,XOR 用来翻转,NOT 全部取反,TEST 做与运算但不保存结果,只改标志位,常配合条件跳转做位检测。移位族里 SHL 和 SAL 左移,SHR 逻辑右移,SAR 算术右移,ROL、ROR、RCL、RCR 是循环移位。串操作族 MOVS、STOS、LODS、SCAS、CMPS 配合 REP 前缀批量处理内存,方向由 DF 决定。
答案 3.23 题是个好例子:(BX)=0E3H,变量 VALUE 里是 79H,三条逻辑指令分别执行。XOR BX, VALUE让1110 0011和0111 1001逐位异或,得1001 1010,即 9AH;AND BX, VALUE得0110 0001,即 61H;OR BX, VALUE得1111 1011,即 0FBH。三条指令执行完,CF 和 OF 都被清 0,SF、ZF、PF 按结果重新设置,AF 无定义。
; 以 0E3H 和 79H 为例,看三种逻辑运算怎么改寄存器 MOV BX, 0E3H MOV VALUE, 79H XOR BX, VALUE ; 0E3H ^ 79H = 09AH,CF=0,OF=0 AND BX, VALUE ; 0E3H & 79H = 061H,CF=0,OF=0 OR BX, VALUE ; 0E3H | 79H = 0FBH,CF=0,OF=0逻辑运算有个统一规矩:CF 和 OF 一律清 0,AF 无定义,SF、ZF、PF 按结果设置。记住这条,答案里那些一串串的标志位就不用逐条背了。算术运算则要看具体情况,加法和减法会同时影响 CF 和 OF,而 INC 和 DEC 只改 OF 不改 CF,这个细节在答案里出现过好几次,属于典型的易错点。
4.3 移位、串操作与“双字求补”那道题
移位指令看着简单,用起来讲究不少。左移一位等于乘 2,53左移得6AH,也就是 106;-49左移得9EH,按补码读是 -98,正好是两倍。右移分两种:逻辑右移 SHR 高位补 0,算术右移 SAR 高位补原来的符号位。同一个0B9H,SHR 一位得5CH,SAR 一位得DCH。按有符号数看,0B9H是 -71,除以 2 应该得 -35.5,向下取整是 -36,而DCH正是 -36;5CH是 92,那是把它当无符号数 185 除以 2 的结果。选错指令,正负号就没了。
答案 3.21 题要求对 DX:AX 里的双字长数求补,标准写法是三行:NEG DX、NEG AX、SBB DX, 0。前两行分别对高低十六位取补,第三行把低半部分产生的借位补到高半部分去。少了最后那一行,低十六位是 0 的时候结果就会错一位。另一种写法是先NOT DX、NOT AX,再ADD AX, 1、ADC DX, 0,本质一样,都是“取反加一”在双字上的展开。这类题在答案里出现频率很高,因为它是多精度运算的入门功,考试爱考,实际写大数运算时也真用得上。
串操作那块,答案 3.29 题把正向复制和反向复制放在一起问。正向复制先把方向标志清 0,让 SI 和 DI 自动递增,再用REP MOVSB一次搬完;反向复制要先置方向标志,把两个指针都指到末尾,再递减着搬。同一段数据,两个方向的写法差好几行,但核心就一句:DF 决定指针往哪走。至于在字符串里找空格、找连字符,用的是REPNE SCASB,找到就停,然后把 DI 减一得到那个字符的地址。
5. 常见误区与易混概念:这五个坑初学者几乎都会踩
答案看得懂,题却做不对,多数时候不是不会,而是把两个长得像的概念混成一个。汇编这门语言特别容易出这种事:同一个符号在不同语境下意思不同,同一条指令换个操作数就换了语义,同一串比特换个解释方式就是另一个数。下面五组是答案里反复出现、也最容易被糊弄过去的区分点。
5.1 易混点一:CF 是给无符号数看的,OF 是给有符号数看的
易混在哪:两个都叫“溢出”,都在加减法之后变化,很多题同时给出CF=1;OF=0,看着像自相矛盾。
为什么会混:CF 记录的是最高位有没有往上进或借,这是无符号数的边界,八位无符号最大 255,再加就往 256 绕回去;OF 记录的是有符号结果有没有跑出 -128 到 127,这是符号位的边界。同一串二进制,两个视角各自算各自的账,所以完全可能一个报警一个不报。
怎么区分:答案 1.5 题里85-(-76)的结果是0A1H。按无符号算,85 加 76 得 161,没超过 255,所以 CF=0;按有符号算,85 加 76 得 161,早就超出 127,所以 OF=1。反过来85+(-76)得09H,无符号算是 85 加 180 得 265,超过 255,CF=1;有符号算是 9,在范围内,OF=0。判断时先问自己:这道题要的是无符号结果还是有符号结果,答案自然分开。
5.2 易混点二:MOV AX, TABLE 与 LEA AX, TABLE
易混在哪:两条指令的源操作数都写着同一个符号名 TABLE,看起来在做同一件事。
为什么会混:汇编里符号名既代表“这个变量所在的位置”,也代表“这个位置里存的东西”,具体指哪个要看指令。MOV 取的是内容,LEA 取的是地址,两者只差一个词的功夫。
怎么区分:答案 3.10 题给的条件是 TABLE 位于数据段的 0032 单元,里面存的内容是 1234H。MOV AX, TABLE执行完 AX 是 1234H;LEA AX, TABLE执行完 AX 是 0032H。有个好记的判断法:凡是带方括号或者直接写变量名的 MOV,都是去取内容;LEA 相当于MOV AX, OFFSET TABLE,拿的是偏移地址。想让 AX 装地址,用 LEA;想让它装数据,用 MOV。
5.3 易混点三:立即数、直接寻址、寄存器间接寻址
易混在哪:三种写法的指令都很短,MOV AX, 1200H、MOV AX, [1200H]、MOV AX, [BX],一眼扫过去区别只是有没有方括号。
为什么会混:1200H 这个数,既可以当数值用,也可以当地址用,写出来长得一模一样,全看有没有方括号和方括号里是什么。
怎么区分:没有方括号就是立即数,操作数直接嵌在指令里;方括号里是数字就是直接寻址,去那个偏移处取内容;方括号里是寄存器就是寄存器间接寻址,寄存器里存的是地址。答案 3.4 题把这几种写法摆在一起,给了一组内存单元的内容让你分别算 AX,就是为了逼你分清“数”和“地址”的区别。写程序时养个习惯:只要是想访问内存,方括号不能省,否则指令语义立刻反转。
5.4 易混点四:SAR 与 SHR,一个保符号一个不保
易混在哪:都是右移,名字只差一个字母,移位后数值也常常看着差不多。
为什么会混:对正数来说两者结果完全一样,只有在处理负数时才分道扬镳,而初学时又很少拿负数练手,于是就把它们当成同一个东西。
怎么区分:SHR 是逻辑右移,空出来的高位一律补 0;SAR 是算术右移,空出来的高位补原来的符号位,所以负数右移之后还是负数。答案里0B9H这个例子最能说明问题:SHR 一位得5CH,符号位被冲成 0,负数变成了正数;SAR 一位得DCH,最高位仍然是 1,数值从 -71 变成 -36。只要操作数可能为负,右移就用 SAR;只有确认是无符号数,才用 SHR。
5.5 易混点五:堆栈是往低地址长的,进栈是 SP 减不是加
易混在哪:直觉上“往里放东西”应该是地址往上加,可堆栈偏偏反着来。
为什么会混:堆栈区在内存里的画法是向上排的,图上看是从下往上堆,但地址数值是从高往低走,图和数值方向相反,看着就别扭。
怎么区分:记住两个动作的顺序。PUSH 是先减后存:SP 先减 2,再把十六位数据存进去,高字节放高地址、低字节放低地址;POP 是先取后加:先把数据取出来,SP 再加 2。答案 3.13 题给的初始状态是(SS)=0FFA0H、(SP)=00B0H,栈顶物理地址是FFA00H+00B0H=FFAB0H;压入一个字后 SP 变成 00AEH,指向FFAAEH;再压一个变成 00ACH,指向FFAACH;弹出一个又回到 00AEH。把这几个数在纸上画一遍,方向感就正过来了。
6. 什么时候该翻这份答案,什么时候别翻
这份资料最合适的用法,是当“对照表”而不是“教科书”。它给的是结果和推导,没有讲解,所以你手里得先有一份自己的推导。比较稳妥的节奏是这样:先把题做一遍,把中间结果写在纸上,包括二进制形式、十六进制形式、每一步之后的关键寄存器值和标志位;卡住超过半小时再看答案,看的时候只对中间步骤,不对最终数字;发现某一步对不上,回头找是哪条指令的语义记错了,而不是把答案抄一遍了事。按这个流程走,一道题的价值能顶三道。
反过来说,有几种场景它帮不上忙。想靠它从零学汇编,会很难受,因为答案里每一步都默认你已经知道指令在干什么;想学三十二位、六十四位或者保护模式下的写法,也不合适,这份资料的内容集中在实模式、十六位寄存器和那几个经典段寄存器上;想学工程里怎么写可维护的汇编代码,同样不合适,习题追求的是把概念练到,不是把代码写漂亮。用错场景,再好的资料也会让人觉得“看了跟没看一样”。
| 场景 | 建议 | 原因 |
|---|---|---|
| 课后做题、期末复习 | 适合 | 题目和答案一一对应,覆盖面全 |
| 核对寻址方式算出的有效地址 | 适合 | 每道题都给了完整推导过程 |
| 确认某条指令执行后标志位怎么变 | 适合 | 几乎每题都标注了 CF、OF、SF、ZF |
| 从零开始学汇编语法 | 不适合 | 没有讲解,只有结果 |
| 学习现代保护模式编程 | 不适合 | 内容集中在实模式和十六位环境 |
| 直接抄答案交作业 | 不适合 | 抄完还是不会,下次照样卡 |
真正值得从这份资料里带走的,是一种检查习惯。答案里那些看起来啰嗦的中间步骤——把十进制先写成二进制、再分组写成十六进制、每一步之后标出 CF 和 OF——恰恰是最有价值的部分。它们构成一条可以复现的推理链,你照着走一遍,就能自己发现哪里想岔了。
从那以后我每次对着答案检查推导,都强制先把自己的中间结果写在纸上,尤其是标志位那一栏,一个都不许空着;哪一栏空着,哪一栏就一定是没想明白的地方。这份资料已经整理成可直接查阅的文档,需要的话在下载入口自取就行,建议先做题再对照,效果比顺着读一遍强得多。希望帮到你。
本文还有配套的精品资源,点击获取