80x86 汇编语言 第二章 复习 x86 寻址方式

新人博主,大家的每一次阅读都会激励我继续创作,大家的点赞将会是我继续更新的巨大动力,对文中内容或实验过程中有任何疑问欢迎留言!

80x86 指令中的操作数可以存放在三种不同的部件中:第一种是 CPU 内的寄存器中,第二种是主存中,第三种是 I/O 设备的端口中。寻址方式不仅指明了操作数存放的部件,而且指明了操作数在该部件内的存放地址。

寄存器寻址

示例 1

INC BX;

其中, INC 为加1 指令的操作符,其操作数地址为寄存器 BX ,即操作数在 BX 中。

示例 2

ADD EAX, EDX

这是一条双操作数指令, 其中, ADD 为加法指令操作符, EAX 为目的操作数地址, EDX 为源操作数地址。

示例 3

DEC CH

其中, DEC 为减1 指令的操作符,寄存器CH 为操作数地址。

在寄存器寻址方式中, 所用寄存器的位数 决定了 指令操作数的类型 , 例如,采用 32 位寄存器表示操作数是双字类型; 采用 16 位寄存器表示操作数是字类型; 采用 8 位寄存器表示操作数是字节类型,使用时应根据需要正确选择。

寄存器间接寻址

在寄存器间接寻址方式中,操作数存放在主存储器中,而操作数的偏移地址 EA 在指令指明的寄存器 R 中,即寄存器 R 的内容为操作数的偏移地址 EA 。其寻址过程如下图所示。

image-20240204211626946

在 80x86 中, R 可以是 8 个 32 位通用寄存器( EAX 、 EBX 、 ECX 、EDX 、 EDI 、 ESI 、 EBP 、 ESP )中的任何一个;也可以是 4 个 16 位通用寄存器( BX 、 DI 、 SI 、 BP )中的一个,但不能是 8 位的通用寄存器。

这种寻址方式在未明确指定操作数所在的段时,如果 R 选用 BP 、 EBP 、 ESP ,则系统默认操作数在堆栈段中,操作数地址为“ SS : [R] ”;如果选用其他 32 位或 16 位寄存器,则系统均默认操作数在DS 所指示的段中,操作数地址为“ DS : [R] ”。明确指定操作数所在段的方法参见后文。

示例 1

MOV AX, [SI]

这是一条双操作数指令。其中 MOV 是传送指令的操作符; AX 是目的操作数地址,采用了寄存器寻址方式; [SI] 指明了源操作数采用寄存器间接寻址方式, [SI] 的内容为源操作数的偏移地址 EA 。由于源操作数用 SI 间接寻址并未明确指定所使用的段寄存器,故默认使用 DS 段寄存器。 操作数类型由目的寄存器决定 。示例过程如下图所示。

image-20240204212651570

  • 执行前:

    (AX) = 5 , (SI) = 20H , DS : (20H) = 0FFFFH

  • 执行:

    EA = (SI) = 0020H

    DS : (20H) = 0FFFFH → AX

  • 执行后:

    (AX) = 0FFFFH ,其它相关寄存器未发生变化。

注意: MOV 指令的两个操作数必须是同样的大小,两个操作数不能同时为内存操作数,指令指针寄存器 ( IP 、 EIP )不能作为目的操作数。

示例 2

MOV CX, [EBP]

该指令中的源操作数用 EBP 间接寻址,所以默认使用的段寄存器是 SS 。

例题

请分析在下列程序段执行后, EAX 、EBX 的内容为多少?

MOV EAX, -1
MOV [ESP], EAX
POP EBX

第一条指令执行后,(EAX) = -1 = 0FFFFFFFFH 。

第二条指令的目的操作数用 ESP 间接寻址,说明默认使用的段寄存器是 SS , ESP 指向栈顶,因此该指令将 EAX 的内容放入栈顶,但这里是 MOV 而不是 PUSH 所以栈顶指针不移动。

第三条指令将栈顶内容弹出送入 EBX 中。执行后,(EBX) = 0FFFFFFFFH 。

变址寻址

变址寻址方式的操作数存放在主存储器中,其偏移地址 EA 是指令中指定寄存器的内容乘以比例因子后与给出的位移量之和。

使用格式包括

  • [R * F + V]
  • [R * F] + V
  • V[R * F]

寄存器 R 的内容乘以指定的比例因子 F ( F 可为 1 、 2 、 4 或 8 )后加上给定的位移量 V 作为操作数的偏移地址。其寻址过程如下图所示。

image-20240204223258583

其中,寄存器 R 作为变址寄存器使用。当 R 为 16 位寄存器或 ESP 时, F 只能为 1 并省略不写,其他用法规定与寄存器间接寻址相同。

示例 1

MOV AL, [EBX * 2] + 5

源操作数采用变址寻址方式, V 的值为 5 ,选用的变址寄存器为 EBX ,比例因子为 2 ,默认的段寄存器为 DS 。

示例 2

ADD -2[BP], AX

目的操作数采用变址寻址方式, V 的值为 -2 。由于采用 16 位寄存器 BP 作为变址寄存器,所以,比例因子只能为 1 并且必须省略,引用的段寄存器是 SS 。

在程序设计中,采用变址寻址可以方便地访问一维数组中的任一元素。

示例 3

某班 C 语言的考试成绩存放在以 CC 为首址的分数表中,该班学生各门功课的总分存放在以 TOTAL 为首址的总分表中,设学生的学号分别为 0 , 1 , 2 , 3 , … , 10H , 11H , … 现要取出学号为 10H 的同学的 C 语言分数累加到他的总分中。用变址方式实现该程序如下所示。

DATA SEGMENT
	CC DW 80, 90, 70, 65, ...
	TOTAL DW 5640, 5000, 4800, 6000, ...
DATA ENDS
; ...
	MOV SI, OFFSET CC			; SI 为 CC 表指针
	MOV DI, OFFECT TOTAl		; DI 为 TOTAL 表指针
	; ...
	MOV AX, 2*10H[SI]			; 取出学号为 10H 的同学的 C 语言分数
	ADD 2*10H[DI], AX			; 累加到总分中

基址变址寻址

基址加变址寻址方式的操作数存放在存储器中,其偏移地址 EA 是指令中指定的基址寄存器的内容、变址寄存器的内容乘以比例因子、位移量 V 三项相加之和。

使用格式包括

  • [BR + IR * F + V]
  • V[BR][IR * F]
  • V[BR + IR * F]

其功能为将变址寄存器 IR 的内容乘以比例因子 F ,与基址寄存器 BR 的内容和位移量 V 相加,作为操作数的偏移地址 EA 。

F 的规定同变址寻址方式。默认段寄存器由基址寄存器 BR 决定。

当使用 16 位寄存器时, BR 只能选用 BX 、 BP 之一; IR 只能选用 SI ,DI 之一; F 只能为 1 。当 BR = BX 时, 默认段寄存器为数据段寄存器 DS ; 当 BR = BP 时, 默认段寄存器为堆栈段寄存器 SS 。

当使用 32 位寄存器时, BR 可以选用任一 32 位通用寄存器; IR 可以选用除 ESP 之外的任一 32 位通用寄存器( BR 和 IR 可以相同)。在该寻址方式的表达式中, **未带比例因子的寄存器是 BR **; 当比例因子为 1 并省略时,则放在前面的寄存器是 BR 。当 BR 为 ESP 、 EBP 时,默认段寄存器用 SS ;其他用 DS 。

示例 1

MOV AX, 8[BX][SI]

其中, 目的操作数地址是 AX 。源操作数地址用基址加变址方式给出, V 的值为 8 , 基址寄存器选用 BX (决定了段寄存器为 DS ), 变址寄存器选用 SI 。

该语句源操作数的有效地址 EA = (基址寄存器) + (变址寄存器) + V = (BX) + (SI) + 8H 。

示例 2

MOV EAX, -6[EDI * 2][EBP]

其中,目的操作数地址为 EAX 。源操作数地址用基址加变址方式给出, V 的值为 -6 ,在该寻址方式的表达式中, 未带比例因子的寄存器是 BR ,基址寄存器选用 EBP (决定了段寄存器为 SS ), 变址寄存器选用 EDI 。

在程序设计中, 采用基址加变址寻址方式可以方便地访问二维数组, 如矩阵等。对矩阵访问时, 可以将 V 作为矩阵首址, 行首址置入 BR 中, IR * F 表示列编号为 (IR) 的元素相对于行首址的字节距离。

立即寻址

立即寻址方式所提供的操作数是紧跟在指令操作码后面的一个采用 8 位、 16 位或 32 位二进制补码表示的有符号数, 构成了指令的一部分, 位于代码段中。也就是说, 操作数的存放地址就是指令操作码的下一单元, 计算 PA 时使用的段寄存器是 CS , 而 EA 的值来自指令指示器 IP/EIP 中的内容。

示例 1

MOV WORD PTR [SI], 12H

其中, 源操作数采用立即寻址方式; 目的操作数使用的是寄存器间接寻址方式, 其地址由 DS : [SI] 决定。 WORD PTR 用来说明操作数类型为字。因此, 汇编程序将把源操作数 12H 翻译成 0012H 。该语句将 0012H 送到 DS : [SI] 指向的字存储单元中。

示例 2

ADD EAX, -12345678H

其中, 目的操作数的地址是 EAX 。由于 EAX 是 32 位寄存器, 也就决定了操作数的类型为 双字 , 因此, 汇编程序将源操作数 -12345678 翻译成 32 位补码表示 0EDCBA988H 。该语句将 EAX 的内容与 0EDCBA988H 相加, 结果保存到 EAX 中。

立即寻址方式主要用来给寄存器或存储器赋初值, 也可以与寄存器操作数或存储器操作数进行算术逻辑运算。立即寻址方式不仅能简化数据的存取, 使指令的书写直观、清晰,而且由于它是随同指令码一起被预取到 CPU 内部的, 不需要再单独进行存储器操作, 因此执行速度快。

直接寻址

在直接寻址方式中, 操作数存放在主存储器中。操作数的偏移地址 EA( 16 位或 32 位)紧跟在指令操作码后面, 构成了指令的一部分, 通过指令指示器 IP/EIP 获取。

使用格式包括:

  • 段寄存器名 : [n]
  • 变量
  • 变量 + 常量

变量 或 变量 + 常量 的地址表达式在汇编过程中将转换成“ 段寄存器名 : [n] ”的形式。其中, 数值 n 是汇编程序计算地址表达式后得到的结果, 变量所在的段决定了所使用的段

示例 1

MOV DS:[20H], CL

此例中的目的操作数采用了直接寻址方式。源操作数存放在 CL 中, 它决定了操作数的类型是字节。该语句将 CL 内容送到 DS : [20H] 指向的 1 字节大小的存储单元中。

示例 2

假设 BUF 为定义在附加数据段 ES 中的字节类型变量, 相对于段首址的偏移地址为 10H (汇编程序在汇编时会自动计算该值)。

INC BUF

该语句经汇编后翻译成 INC BYTE PTR ES:[0010H] , 功能为将 BUF 的内容(也即地址为 ES : [0010H] 字节单元的内容)加 1 后仍送回 BUF 中。

示例 3

SUB WORD PTR DS:[1000H], 55AAH

目的操作数采用了直接寻址方式, 源操作数采用立即寻址方式。其中 WORD PTR 指定了操作数类型为字。该语句将地址为 DS : [1000H] 字单元的内容减去 55AAH 后, 结果存放回 DS : [1000H] 字单元中。

直接寻址方式适用于处理单个变量(访问静态分配的变量)的场合,例如。

MOV AX, A1
MOV A2, BX
SUB CX, A3
MOV DX, A4 + 6

其中, A1 , A2 , A3 , A4 均为字变量, 对应存储器中相应的字存储单元。

寻址方式有关问题补充

显式/隐式操作数

带操作数的指令中有一些指令显式地指定操作数, 另一些指令隐含地指定操作数, 还有些则采用二者的组合。

示例 1

CBW

该指令为符号扩展指令,将 AL 寄存器中的符号位扩展到 AH 中。隐含操作数在 AL 和 AH 中, 它是隐含的寄存器寻址方式。

示例 2

PUSH BUFA

该指令将字变量 BUFA (显式源操作数, 直接寻址)压入栈顶(隐含的目的操作数, 是使用 SP/ESP 的寄存器间接寻址方式)。

事实上, 指令都有隐含的操作数。例如, 虽然 IP/EIP 不能作为显式操作数使用,但所有的指令都会修改它;又如,所有算术运算指令都要更新标志寄存器。但我们在讨论寻址方式时, 主要是指显式操作数的寻址方式。

各寻址方式之间的关系

根据操作数存放的位置和特点, 将上述 6 种寻址方式归纳为 3 大类: 寄存器方式、存储器方式和立即方式。其中存储器方式包括寄存器间接寻址方式、变址寻址方式、基址加变址寻址方式和直接寻址方式 4 种。

双操作数指令中的源操作数和目的操作数的寻址方式必须是以下 5 种组合之一:

  1. 寄存器对寄存器;
  2. 寄存器对存储器;
  3. 存储器对寄存器;
  4. 立即方式对寄存器, 但立即方式只能用于源操作数;
  5. 立即方式对存储器, 但立即方式只能用于源操作数。

注意, 一条指令的源操作数和目的操作数不能同时用存储器方式表示。

不含变量的存储器方式所表示的操作数类型是不明确的。立即数是没有类型的。如果指令语句中没有一个操作数的类型是明确的, 则必须用属性定义算符 PTR (字节类型 BYTE PTR 、字类型 WORD PTR 、双字类型 DWORD PTR )指明其中一个操作数的类型。例如:

; 将 A 的 ASCII 码值 41H 转化为字类型 0041H 后送到 DS:[EDX] 指向的字存储单元中
MOV WORD PTR [EDX], 'A'	
DEC BYTE PTR [SI];			; 将 DS:[SI] 指向的字节存储单元的内容减 1

段的显示说明

如果指令中不希望使用默认的段寄存器, 则可以通过段的显式说明来达到目的, 也就是在对应的寻址方式表达式前加上段超越前缀(或称跨段前缀)。

下面几个例子的源操作数说明了在不同的情况下段的选择原则, 也说明了段超越前缀在段选择中的最高优先级。

MOV AX, [SI]

间址寄存器 SI 决定了使用默认的段寄存器 DS 。

MOV AX, [BP+SI]

基址寄存器 BP 决定了使用段寄存器 SS , 寄存器 SI 不再影响段寄存器的选择。

; SUM 是 DS 段内的变量
MOV AX, SUM[BP+SI]

变量 SUM 决定了使用段寄存器 DS , 寄存器 BP 和 SI 不再影响段寄存器的选择。

; SUM 是 DS 段内的变量
MOV AX, CS:SUM[BP+SI]

段超越前缀 CS: 决定了使用段寄存器 CS , 变量 SUM 、寄存器 BP 和 SI 均不再影响段寄存器的选择。

寻址方式综合示例

编写汇编源程序代码段的过程就是各种寻址方式灵活应用的过程。下面以一段程序为例,我们可以用不同的寻址方式来改写这段程序。

.386

DATA SEGMENT USE16				; 数据段
BUF1 DB 20, 39, 50, -20, 0, -12	; BUF1 为字节变量数组
BUF2 DB 6 DUP(0)				; BUF2 为长度为 6 的字节变量数组,初始化为 0
DATA ENDS

STACK SEGMNENT USE16 STACK		; 堆栈段
DB 200 DUP(0)
STACK ENDS

CODE SEGMENT USE16				; 代码段
	ASSUME CS:CODE, SS:STACK, DS:DATA
START:
	MOV AX, DATA				; 将 DATA 段的段首址(立即数)送到 DS 中
	MOV DS, AX					; 立即数不能直接送到段寄存器中
	MOV SI, OFFSET BUF1			; SI 为 BUF1 的取数指针(BUF1 的 EA → SI)
	MOV DI, OFFSET BUF2			; DI 为 BUF2 的取数指针(BUF2 的 EA → DI)
	MOV CX, 6					; CX 为循环计数器,准备循环 6 次
LOOPA:
	MOV AL, [SI]				; 从 BUF1 为首址的存储区中取一数(寄存器间接寻址)
	MOV [DI], AL				; 送入 BUF2 区
	INC SI						; 指针加 1 ,指向 BUF1 存储区的下一单元
	INC DI						; 指针加 1 ,指向 BUF2 存储区的下一单元
	DEC CX						; 循环计数值减 1
	JNZ LOOPA					; CX != 0 则循环
	MOV AH 4CH					; 循环 6 次后退出程序
	INT 21H
CODE ENDS
	END START

该程序的功能为: 将以 BUF1 为首址的字节存储区中的内容传送到以 BUF2 为首址的字节存储区中去。下面用变址寻址方式改写该程序:

.386

DATA SEGMENT USE16				; 数据段
BUF1 DB 20, 39, 50, -20, 0, -12	; BUF1 为字节变量数组
BUF2 DB 6 DUP(0)				; BUF2 为长度为 6 的字节变量数组,初始化为 0
DATA ENDS

STACK SEGMNENT USE16 STACK		; 堆栈段
DB 200 DUP(0)
STACK ENDS

CODE SEGMENT USE16				; 代码段
	ASSUME CS:CODE, SS:STACK, DS:DATA
START:
	MOV AX, DATA				; 将 DATA 段的段首址(立即数)送到 DS 中
	MOV DS, AX					; 立即数不能直接送到段寄存器中
	MOV SI, 0					; SI 为变址寄存器,起始值为 0
	MOV CX, 6					; CX 为循环计数器,准备循环 6 次
LOOPA:
	MOV AL, BUF1[SI]			; 从 BUF1 取数时,首址 BUF1 不变,SI作变址寄存器
	MOV BUF2[DI], AL			; 向 BUF2 送数时,首址 BUF2 不变,SI作变址寄存器
	INC SI						; 指针加 1 ,指向相对于首址的下一字节单元
	DEC CX						; 循环计数值减 1
	JNZ LOOPA					; CX != 0 则循环
	MOV AH 4CH					; 循环 6 次后退出程序
	INT 21H
CODE ENDS
	END START

下面用基址加变址方式改写该程序:

.386

DATA SEGMENT USE16				; 数据段
BUF1 DB 20, 39, 50, -20, 0, -12	; BUF1 为字节变量数组
BUF2 DB 6 DUP(0)				; BUF2 为长度为 6 的字节变量数组,初始化为 0
DATA ENDS

STACK SEGMNENT USE16 STACK		; 堆栈段
DB 200 DUP(0)
STACK ENDS

CODE SEGMENT USE16				; 代码段
	ASSUME CS:CODE, SS:STACK, DS:DATA
START:
	MOV AX, DATA				; 将 DATA 段的段首址(立即数)送到 DS 中
	MOV DS, AX					; 立即数不能直接送到段寄存器中
	LEA BX, BUF1				; BUF1 的 EA 送到 BX 寄存器中,作为基址寄存器
	LEA BP, BUF2				; BUF2 的 EA 送到 BP 寄存器中,作为基址寄存器
	MOV SI, 0					; SI 为变址寄存器,起始值为 0
	MOV CX, 6					; CX 为循环计数器,准备循环 6 次
LOOPA:
	MOV AL, [BX][SI]			; 以基址变址方式从 BUF1 中取一个数
	MOV DS:[BP][SI], AL			; 以基址变址方式向 BUF2 送取一个数
	INC SI						; 指针加 1 ,指向相对于首址的下一字节单元
	DEC CX						; 循环计数值减 1
	JNZ LOOPA					; CX != 0 则循环
	MOV AH 4CH					; 循环 6 次后退出程序
	INT 21H
CODE ENDS
	END START

在访问存储器单元时, 几种存储器寻址方式可以相互转换。

  • 29
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值