ICS 与 8086 复习 06:8086 寻址与数据传送
2000H 可能是一个数,也可能是偏移地址,还可能是段寄存器的内容。读汇编程序时,先弄清它在当前指令中的身份,才能正确计算结果。8086 的寻址题看似形式很多,实际可以沿着同一条路径求解:确定操作数在哪里,算出有效地址,再按操作宽度读取数据。
本文采用 8086 实模式与 Intel/MASM 语法,双操作数指令的目的在前、源在后。复习时优先掌握寻址合法性、默认段、大小端和栈的变化;它们也会反复出现在后续的程序分析题中。
先判断是数值还是内存内容
比较下面三条指令:
mov ax, 1234h
mov ax, [1234h]
mov ax, bx
第一条把立即数 1234H 送入 AX。第二条访问默认数据段内偏移为 1234H 的一个字,把其中内容送入 AX。第三条直接复制 BX 的值。
因此,看到方括号时,应先想到“按这个地址访问内存”。但 MASM 的符号变量有类型,若 value DW 1234H,mov ax,value 同样表示读取内存;它与 mov ax,OFFSET value 的含义不同,后者取得的是变量的偏移地址。
寻址方式要针对具体操作数判断。mov ax,1234h 的源是立即寻址,目的仍是寄存器寻址,不能把整条指令简单标成只有一种寻址方式。
有效地址和物理地址分两步计算
有效地址 EA 是段内偏移。8086 实模式下,在本篇未跨越地址空间上界的例子中,物理地址为:
PA = 段寄存器值 × 10H + EA
注意 10H 是十进制的 16。段值左移四位后再加偏移,不能直接把两个十六进制数相加。
8086 数据寻址可以整理成下表。
| 形式 | 示例 | EA 的来源 |
|---|---|---|
| 直接寻址 | [2000H] |
指令中的偏移量 |
| 寄存器间接 | [BX] |
一个地址寄存器 |
| 寄存器相对 | [SI+10H] |
地址寄存器加位移 |
| 基址变址 | [BX+DI] |
一个基址加一个变址 |
| 相对基址变址 | [BP+SI+10H] |
基址、变址与位移相加 |
这里有两个必须记牢的限制。第一,8086 的一般内存有效地址只能使用 BX、BP、SI、DI,不能写 [AX]、[CX]、[DX] 或 [SP]。第二,双寄存器组合只能从 BX/BP 中选一个、从 SI/DI 中选一个,所以 [BX+BP]、[SI+DI] 都不合法。
默认段同样有规律:一般数据访问使用 DS;有效地址包含 BP 时使用 SS。显式的段超越前缀可以改用指定的段,例如 mov ax,DS:[BP]。
这是一般数据访问的规则。取指使用 CS:IP,压栈弹栈使用 SS:SP,串指令又有专门的隐含地址约定,不能只凭出现了 DI 就一律认定使用 DS。
一道寻址题的完整解法
已知:
SS=3000H,DS=2000H
BP=0100H,SI=0020H
物理地址30130H处的字节是34H
物理地址30131H处的字节是12H
执行:
mov ax, [bp+si+10h]
第一步计算偏移:EA=0100H+0020H+0010H=0130H。
第二步选择段。因为地址表达式包含 BP,默认使用 SS,因此 PA=3000H×10H+0130H=30130H。
第三步确定宽度。目的操作数 AX 是 16 位寄存器,所以读取从该地址开始的两个字节。
第四步按小端顺序组合:低地址的 34H 是低字节,后一地址的 12H 是高字节,最终 AX=1234H。BP、SI 以及被读取的内存都没有因此改变。
这四步可以直接用作答题模板。尤其不要把 EA=0130H 当作 AX 的结果,也不要因为 DS 已知就跳过默认段的判断。
MOV 的合法性和 LEA 的区别
普通 MOV 不影响条件标志,源数据也不会因为“传送”而被清除。判断是否合法时,依次检查以下几项:
- 两边宽度是否匹配,例如
mov al,ax不合法。 - 是否同时访问两个内存操作数,普通 MOV 不支持这种形式。
- 是否试图把立即数直接送入段寄存器,或在两个段寄存器间直接传送。
- 目的是否是立即数、CS 或 IP;这些都不能作为普通 MOV 的目的。
装载数据段可以写为:
mov ax, 2000h
mov ds, ax
mov ds,2000h 不能替代这两条指令。
对于只有内存和立即数、无法推断宽度的指令,要用 PTR 明确类型:
mov byte ptr [bx], 1
mov word ptr [bx], 1
前者改一个字节,后者改两个字节。PTR 规定本次访问的宽度,不会重新定义整片内存的永久类型。
LEA 则只计算有效地址,不读取该地址中的数据:
lea ax, [bx+si+10h]
若 BX 为 0100H、SI 为 0020H,结果就是 AX=0130H,不会再取 DS:0130H 处的一个字。LDS、LES 又不同:它们从内存读取一个由偏移和段值组成的远指针,同时装载通用寄存器与 DS 或 ES。
栈题抓住操作顺序
8086 的栈以字为单位操作。执行普通 PUSH 时,先把 SP 减 2,再向 SS:SP 指向的位置写入一个字;POP 先读取栈顶字,再把 SP 加 2。
设 AX=1234H、BX=ABCDH、SP=0100H,执行:
push ax
push bx
pop cx
pop dx
| 执行后 | SP | 关键结果 |
|---|---|---|
push ax |
00FEH |
栈顶字为 1234H |
push bx |
00FCH |
新栈顶字为 ABCDH |
pop cx |
00FEH |
CX=ABCDH |
pop dx |
0100H |
DX=1234H |
第二次压栈后,偏移 00FCH、00FDH 两字节分别为 CDH、ABH。这同时体现了栈向低地址增长和字数据的小端存储。
POP 不会把原内存擦成零,只会读出数据并移动栈顶。保存 AX、BX、CX 后,恢复顺序通常应为 CX、BX、AX。普通 PUSH、POP 不改条件标志;专门恢复标志的 POPF 另当别论。
8086 不支持 push al 或 push 1234h。后代 x86 增加的指令形式,不能直接搬进限定为 8086 的改错题。
常见误区
把 mov ax,[bx] 误读成复制 BX,会把地址和内容混淆;把 LEA 误读成 MOV,则会多做一次内存读取。计算物理地址时漏乘 16、含 BP 仍使用 DS、按高地址先拼字,也都会让后续结果整体出错。
另一个跨课程陷阱是套用 386 的比例变址。[EAX+ESI*4] 是后续 IA-32 内容;8086 的地址寄存器和可用组合应按本篇限制单独记忆。
自测与答案
- 已知 DS=
1000H、BX=0020H,物理地址10020H、10021H分别存78H、56H。mov ax,[bx]后 AX 是多少?lea ax,[bx]呢? [BP+DI+8]、[BX+BP]、[SP]在 8086 中哪些合法?合法者默认使用哪个段?- 初始 SP=
0200H,依次执行push ax、push bx、pop cx后,SP 是多少?CX 来自哪个寄存器原值?
答案:1. 前者为 5678H,后者为 0020H。2. 只有 [BP+DI+8] 合法,默认使用 SS;另外两种无对应的 8086 一般寻址形式。3. SP=01FEH,CX 得到 BX 压栈时的值。
对应课件
《汇编语言 第3章.pptx》:第 7—33 页寻址方式,第 47—61 页 MOV 与栈,第 66—68 页 LEA、LDS、LES。页码按文件中的幻灯片顺序计。
讨论这篇文章
关于《ICS 与 8086 复习 06:8086 寻址与数据传送》的补充、不同意见或延伸话题,都可以留在这里。
滚动到这里时自动载入留言。