目录
[8086 CPU 内存寻址机制](#8086 CPU 内存寻址机制)
[1. 为什么引入分段寻址?](#1. 为什么引入分段寻址?)
[2. 物理地址计算公式](#2. 物理地址计算公式)
[3. 主要段寄存器](#3. 主要段寄存器)
[基址寻址 / 直接寻址](#基址寻址 / 直接寻址)
[基址 + 变址寻址](#基址 + 变址寻址)
[相对基址 + 变址寻址](#相对基址 + 变址寻址)

汇编选择基础知识点
先理解什么是"栈帧"?
这个在前面已经提到了很多次
函数调用时,会在栈中为该函数分配一块内存区域来存储:
-
函数的局部变量
-
函数的参数
-
返回地址、保存的
ebp等
这块区域就叫做栈帧(Stack Frame),由 ebp (基址指针)+ esp (栈顶指针)管理。
堆栈结构
↑
| ← esp(栈顶)
局部变量3 [ebp-0Ch]
局部变量2 [ebp-08h]
局部变量1 [ebp-04h]
旧的ebp [ebp] ← ebp(基址)
返回地址 [ebp+4]
参数1 [ebp+8]
参数2 [ebp+0Ch]
|
↓
什么是汇编寻址方式?
-
汇编寻址方式 (Addressing Modes)是指 CPU 执行指令时,如何找到操作数(数据)所在位置的方法。它定义了指令中操作数的来源和访问方式,是汇编语言中最核心的概念之一。
-
在机器指令中,除了操作码(做什么),还必须明确操作数在哪里。寻址方式就是解决"数据在哪里"的问题,不同的寻址方式决定了指令的灵活性、执行速度和适用场景。
汇编寻址的目的
-
提高程序的灵活性:能够灵活访问寄存器、立即数、内存变量、数组、结构体等。
-
优化执行效率:不同场景选择最合适的寻址方式,能显著减少指令周期和内存访问次数。
-
实现高级语言特性:指针、数组、结构体、栈帧等功能在底层都是通过不同寻址方式实现的。
-
支撑复杂数据结构处理:如数组遍历、链表操作、函数调用等。
段寻址
8086 CPU 内存寻址机制
1. 为什么引入分段寻址?
8086 是 16 位 CPU,但地址总线为 20 位,可寻址 1MB 内存(2²⁰ = 1,048,576 字节)。
由于寄存器只有 16 位,无法直接表示 20 位地址,因此采用 "段 + 偏移" 的分段内存管理模式。
2. 物理地址计算公式
物理地址 = (段基址 << 4) + 偏移地址
-
<< 4等价于段基址 × 16(左移 4 位,低 4 位补 0) -
段基址:16 位,存放在段寄存器中
-
偏移地址:16 位,最大范围 64KB(一个段的大小)
3. 主要段寄存器
|--------|---------------|------------|
| 段寄存器 | 英文全称 | 主要用途 |
| CS | Code Segment | 代码段(存放指令) |
| DS | Data Segment | 数据段(存放数据) |
| SS | Stack Segment | 栈段(存放栈数据) |
| ES | Extra Segment | 附加段(辅助数据段) |
FS 和 GS 在 80386 及以后新增,常用于线程局部存储等。
段地址计算实际案例
; 示例:初始化数据段
mov ax, @data ; @data 是汇编器提供的当前数据段基址
mov ds, ax ; 将段基址加载到 DS 寄存器
; 访问数据
mov al, [szHello] ; 从 DS:szHello 偏移处取一个字节
详细计算示例:
假设段基址 DS = 1230H,偏移地址 BX = 00C8H
-
段起始物理地址 = 1230H × 16 = 12300H
-
物理地址 = 12300H + 00C8H = 123C8H
汇编代码演示:
mov ax, 1230H
mov ds, ax
mov bx, 00C8H
mov al, [bx] ; 实际访问物理地址 123C8H
段重叠现象
同一物理地址可以有多种逻辑地址表示,这是分段机制的重要特点。
重叠示例:
-
段基址 1230H + 偏移 00C8H = 物理地址 123C8H
-
段基址 122CH + 偏移 0108H = 物理地址 123C8H(同一位置)
当然段不能跨段寻址这里涉及到段保护的问题 这个后面再说
寻址方式
立即数寻址
立即数寻址是指操作数直接嵌入在指令中,不需要从内存或寄存器中读取。
-
这种方式简单高效,常用于初始化常量值。
-
mov eax, 111h展示了这一方式: -
直接将十六进制值 111h 移动到 EAX 寄存器中。
-
原理在于,CPU 在执行时直接从指令码中提取立即数,无需额外寻址周期,提高执行速度。
-
这是最快的寻址方式,适用于常量初始化、标志设置等场景。
mov eax, 111h ; EAX = 0x111
mov ebx, 12345678h ; 32位立即数
add ecx, 100 ; 立即数参与运算
push 0 ; 压入立即数到栈
mov dword ptr [var], 5 ; 直接给内存变量赋值
寄存器寻址
-
寄存器寻址使用 CPU 内部寄存器作为操作数来源或目标。
-
这种方式速度最快,因为寄存器位于 CPU 内部,无需访问内存。
-
在源码中,
mov ebx, 111h先将立即数加载到 EBX,然后mov ebx, eax将 EAX 的值复制到 EBX。 -
原理是寄存器间的直接数据传输,通过内部总线完成,适用于临时计算和数据传递。
mov eax, 111h
mov ebx, eax ; EBX ← EAX
add ecx, ebx ; ECX ← ECX + EBX
xchg eax, edx ; 交换寄存器内容
inc esi ; ESI 自增
sub eax, eax ; EAX 清零(推荐写法)
基址寻址 / 直接寻址
-
基址寻址使用一个基址寄存器(如 EAX)来存储内存地址,然后访问该地址的内容。
-
源码中
lea eax, dwIndex使用 LEA 指令将变量 dwIndex 的地址加载到 EAX 中。 -
原理是通过计算有效地址(Effective Address),将基址直接作为偏移,实现对内存变量的间接访问,常用于数组或结构体的基地址计算。
.data
dwIndex DWORD 0A5A5A5A5h.code
lea eax, dwIndex ; EAX = 变量地址
mov eax, dwIndex ; 直接读取变量内容
mov dwIndex, 12345678h ; 直接写入变量
寄存器间接寻址
-
寄存器间接寻址通过寄存器中的值作为内存地址来访问数据。
-
源码中
lea ebx, dwIndex先加载地址到 EBX,然后mov esi, [ebx]从该地址读取值到 ESI。 -
原理是寄存器内容被解释为指针,CPU 通过内存管理单元(MMU)访问相应内存位置,适用于指针操作。
lea ebx, dwValue ; EBX = 内存地址
mov esi, [ebx] ; ESI ← [EBX] 指向的内容
mov dword ptr [ebx], 88888888h ; 通过指针写入
mov al, byte ptr [ebx] ; 读取单字节
寄存器相对寻址
-
寄存器相对寻址在基址寄存器基础上添加偏移量。
-
源码中涉及栈帧操作:
mov ebp, esp设置基指针, -
mov esi, [ebp + 4]访问栈上偏移4字节的位置,然后恢复栈。 -
原理是使用 EBP 作为栈帧基址,加上偏移访问局部变量或参数,常用于函数调用中的栈管理。
push ebp
mov ebp, espmov eax, [ebp + 8] ; 第1个参数
mov ebx, [ebp - 4] ; 局部变量
mov ecx, [ebp + 12] ; 第2个参数mov esp, ebp
pop ebp
ret
基址 + 变址寻址
-
这种寻址结合基址和变址寄存器,实现动态偏移。
-
源码中
mov eax, [ebx + esi]将 EBX(基址)+ ESI(变址)处的内存值加载到 EAX。 -
原理是通过地址计算单元(AGU)相加两个寄存器值,形成有效地址,适用于数组遍历(如数组[base + index])。
lea ebx, array ; EBX = 数组基地址
mov esi, 2 ; 索引
mov eax, [ebx + esi*4] ; array[2]
相对基址 + 变址寻址
-
类似于上一种,但添加常量偏移。
-
源码中
mov eax, [eax + ecx + 4]使用 EAX(基址)+ ECX(变址)+ 4(偏移)访问内存。 -
原理是扩展寻址灵活性,AGU 计算总偏移,常见于结构体成员访问。
lea eax, structArray
mov ecx, 3mov edx, [eax + ecx*8 + 4] ; structArray[3].val (假设结构体大小8字节)
比例因子寻址
-
比例因子寻址允许变址寄存器乘以一个比例(如 2、4、8),用于数组元素大小调整。
-
源码中
mov eax, [eax + ecx * 4]表示 EAX + (ECX * 4) 处的内存访问。 -
原理是模拟数组索引(如 int array[ecx],每个元素4字节),AGU 处理乘法和加法,提高数据结构访问效率。
mov ecx, 5
mov eax, [intArray + ecx4] ; intArray[5] (4字节元素)
mov al, [byteArray + ecx] ; byteArray[5] (比例因子1可省略)
mov edx, [structArray + ecx16 + 8] ; 结构体数组成员 -
x86 最强大的寻址模式,由硬件直接支持 1/2/4/8 倍乘法,是数组和结构体数组访问的最优选择。
总结
|----|----------|---------------------------------|---------------|
| 序号 | 寻址方式 | 典型指令形式 | 主要应用场景 |
| 1 | 立即数寻址 | mov eax, 123h | 常量初始化 |
| 2 | 寄存器寻址 | mov eax, ebx | 临时计算、高速操作 |
| 3 | 直接/基址寻址 | mov eax, var / lea eax, var | 全局变量访问 |
| 4 | 寄存器间接寻址 | mov eax, [ebx] | 指针、动态内存 |
| 5 | 寄存器相对寻址 | mov eax, [ebp+8] | 函数参数与局部变量 |
| 6 | 基址+变址寻址 | mov eax, [ebx+esi] | 简单数组遍历 |
| 7 | 基址+变址+偏移 | mov eax, [ebx+esi+4] | 结构体成员访问 |
| 8 | 比例因子寻址 | mov eax, [ebx+esi*4] | 数组与结构体数组(最高效) |