32位汇编:实验4传送类指令的使用
将上述汇编程序并保存为 test4.asm,使用 ML 和 Link 进行汇编和链接,生成可执行文件
传送指令方式分析
数据段布局
首先,我们需要了解数据段的布局。
变量名 | 地址 | 初始值 | 大小 |
---|---|---|---|
var1 | 00403000h | 1010h | 2字节 |
var2 | 00403002h | 未定义 | 2字节 |
var3 | 00403004h | 1234h | 2字节 |
var4 | 00403006h | 6789h | 2字节 |
var5 | 00403008h | 10h,20h,30h | 3字节 |
buffer | 0040300Bh | 10个0 | 10字节 |
arrayB | 00403015h | 10h,20h,30h,40h,50h | 5字节 |
arrayW | 0040301Ah | 1FFh,200h,300h | 6字节 |
arrayD | 00403020h | 10000h,20000h | 8字节 |
接下来,我们逐条分析代码执行后的寄存器和内存变化:
MOV ax, var1
- AX = 1010h
- AX = 1010h
MOV var2, ax
- AX = 1010h, var2 = 1010h
MOV CL, 80h
- CL = 80h
- CL = 80h
MOVSX DX, CL
- DX = FF80h(符号扩展)
- DX = FF80h(符号扩展)
MOVZX DX, AL
- DX = 0010h(零扩展)
- DX = 0010h(零扩展)
mov ax, var3
- AX = 1234h
- AX = 1234h
xchg ax, var4
- AX = 6789h, var4 = 1234h
交换AX和var4的值
mov var3, ax
- AX = 6789h, var3 = 6789h
- AX = 6789h, var3 = 6789h
MOV al, [var5+2]
- AL = 30h
- AL = 30h
MOV al, var5+3
- AL = 00h
- AL = 00h
lea ebx, [buffer+5]
- EBX = 00403010Fh
将buffer+5的有效地址加载到EBX
mov al, arrayB
- AL = 10h
- AL = 10h
mov al, [arrayB+1]
- AL = 20h
- AL = 20h
mov al, [arrayB+2]
- AL = 30h
- AL = 30h
mov ax, arrayW
- AX = 01FFh
- AX = 01FFh
mov ax, [arrayW+2]
- AX = 0200h
- AX = 0200h
mov eax, arrayD
- EAX = 00010000h
- EAX = 00010000h
mov eax, [arrayD+4]
- EAX = 00020000h
- EAX = 00020000h
mov eax, [arrayD+TYPE arrayD]
- EAX = 00020000h(TYPE arrayD = 4)
将arrayD+TYPE(arrayD)地址处的双字传送到EAX
lea ebx, [arrayD+4]
- EBX = 00403024h
- EBX = 00403024h
mov eax, [ebx]
- EAX = 00020000h
- EAX = 00020000h
知识疑解
符号扩展(Sign Extension)和零扩展(Zero Extension)是汇编语言中处理数据类型转换的两种重要技术,主要用于将较小数据类型的值转换为较大数据类型。
基本概念
符号扩展(Sign Extension)
- 原理:用源操作数的符号位(最高位)填充目标操作数的所有高位
- 应用场景:有符号数的扩展
- 指令:
MOVSX
(Move with Sign-Extension)
零扩展(Zero Extension)
- 原理:用0填充目标操作数的所有高位
- 应用场景:无符号数的扩展
- 指令:
MOVZX
(Move with Zero-Extension)
1. 符号扩展过程分析
MOV CL,80h; CL = 80h = 1000 0000b
MOVSX DX,CL; 将CL符号扩展到DX
执行过程:
- CL的二进制值:
1000 0000
- 最高位(符号位)是1,表示负数
- 符号扩展:用1填充DX的高8位
- 结果:DX =
1111 1111 1000 0000
= FF80h
数值含义:
- 作为有符号数:80h = -128(8位有符号数)
- 扩展后:FF80h = -128(16位有符号数)
- 数值保持不变:-128
2. 零扩展过程分析
; 假设执行前AL = 10h (来自AX=1010h的低字节)
MOVZX DX,AL; 将AL零扩展到DX
执行过程:
- AL的二进制值:
0001 0000
= 10h - 零扩展:用0填充DX的高8位
- 结果:DX =
0000 0000 0001 0000
= 0010h
数值含义:
- 作为无符号数:10h = 16(十进制)
- 扩展后:0010h = 16(十进制)
- 数值保持不变:16
数值对比表
操作 | 源值 | 扩展方式 | 结果 | 数值解释(有符号) | 数值解释(无符号) |
---|---|---|---|---|---|
MOVSX DX,CL | 80h | 符号扩展 | FF80h | -128 | 65408 |
MOVZX DX,AL | 10h | 零扩展 | 0010h | 16 | 16 |
- 符号扩展:保持有符号数的数值不变(-128扩展后仍是-128)
- 零扩展:保持无符号数的数值不变(16扩展后仍是16)
扩展规则总结
源类型 | 目标类型 | 正确指令 | 错误指令 | 原因 |
---|---|---|---|---|
有符号字节 | 有符号字 | MOVSX | MOVZX | 保持符号 |
无符号字节 | 无符号字 | MOVZX | MOVSX | 保持数值 |
有符号字 | 有符号双字 | MOVSX | MOVZX | 保持符号 |
无符号字 | 无符号双字 | MOVZX | MOVSX | 保持数值 |
MOV al, var5+3
和 MOV al, [var5+2]
的区别详解
特性 | MOV al, [var5+2] | MOV al, var5+3 |
---|---|---|
语法 | 带有方括号 [ ] | 没有方括号 |
操作类型 | 内存访问(间接) | 内存访问(直接) |
MASM解释 | 明确的间接寻址 | MASM隐式解释为内存访问 |
操作数 | 内存地址 var5+2 处的内容 | 内存地址 var5+3 处的内容 |
推荐程度 | 推荐(明确清晰) | 不推荐(容易混淆) |
MOV al, var5+3
- MASM的隐式内存访问
MOV al, var5+3; 将var5+3地址处的字节内容传送到AL
执行过程:
- MASM 根据上下文(目标操作数是8位寄存器AL)推断这是内存访问
- 计算地址:
var5地址 + 3
- 假设
var5
地址为00403008h
,则var5+3 = 0040300Bh
- 访问内存地址
0040300Bh
,读取该处的字节值 var5+3
指向buffer
数组的起始位置,初始值为00h
- 结果:AL = 00h
特点:
- 没有方括号,但MASM根据上下文推断为内存访问
- 只在MASM汇编器中这样解释
- 在其他汇编器(如NASM)中可能有不同含义
mov eax, [arrayD+TYPE arrayD]
mov eax, [arrayD+TYPE arrayD]
TYPE arrayD
: 汇编时常量表达式,在编译时计算
执行过程
- 编译时计算:
TYPE arrayD
返回数组元素的大小arrayD
是DWORD
数组,所以TYPE arrayD = 4
arrayD+TYPE arrayD
=arrayD+4
- 运行时执行:
- 计算有效地址:
arrayD的地址 + 4
- 访问该地址处的4字节数据(双字)
- 将数据加载到
eax
寄存器
- 计算有效地址:
内存布局示例
假设 arrayD
起始地址为 00403020h
:
地址 内容 说明
00403020h 00010000h arrayD[0] = 10000h
00403024h 00020000h arrayD[1] = 20000h ← 这里就是arrayD+4
结果: eax = 00020000h
(arrayD的第二个元素)
特点
- 编译时计算: 偏移量在编译阶段确定
- 直接寻址: 地址计算不依赖运行时寄存器值
- 可维护性高: 使用
TYPE
操作符,代码更健壮