操作数类型
立即操作数(immediate),寄存器操作数(register)和内存操作数(memory).
直接内存操作数
指令使用内存操作数实际使用的是操作数的地址.
假设内存操作数val1位于偏移10400h处,那么把var1送al寄存器的汇编指令如下:
mov AL, var1
MASM将指令汇编为 A0 00010400,机器指令第一个自己A0是操作码,剩下部分是var1的32位地址值.
编写程序时使用纯数字地址表示内存操作数是可以的,使用var1这样的符号名更方便.
表示直接内存操作数的其他记法:mov AL, [var1] ;方括号暗示了要进行寻址操作
MOV
2个操作数尺寸必须一致
2个操作数不能同时为内存操作数
目的操作数不能是CS, EIP, IP
立即数不能直接送至段寄存器
保护模式下,程序不应直接修改段寄存器.
整数的零/符号扩展
要么手动区分(eax, ax, ah, al).要么使用如下指令
MOVZX dsc, src ;将源操作数的内容复制到目的操作数,并将该值零扩展至16位或32位.适用于无符号整数.
MOVSX dsc, src ;将源操作数的内容复制到目的操作数,并将该值符号扩展至16位或32位.适用于有符号整数.
LAHF和SAHF
LAHF将EFLAGS寄存器的低字节复制到AH寄存器.
SAHF将AH寄存器的值复制到EFLAGS寄存器的低字节.
XCHG
交换2个操作数的内容.有下面3个格式.
XCHG reg, reg
XCHG reg, mem
XCHG mem, reg
XCHG不接受立即操作数.其他规则与mov相同.
ADD, SUB
操作数格式与mov相同. 影响标志:零标志,符号标志,溢出标志,辅助进位标志,奇偶标志.
补码表示法.
各个标志位状态改变的规则.
NEG 取反指令
和数据相关的操作符和伪指令
OFFSET
返回数据标号的偏移地址.
保护模式下,偏移是32位的,实地址模式下,偏移是16位的.
ALGN伪指令
ALGN 边界值 ; 将变量的位置按字节,字,双字或段边界对齐.
边界值可以是1,2,4,或16.
bval BYTE ? ; 00404000
ALGN 2
wval WORD ? ; 00404002
bval2 BYTE ? ; 00404004
ALGN 4
dval DWORD ? ; 00404008
PTR操作符
重载操作数声明的默认尺寸.类似于c中的强制转换符.
myDouble DWORD 12345678h
mov ax, WORD PTR myDouble ; ax=5678h
wordList WORD 5678h, 1234h
mov eax, DWORD PTR wordList ; eax=12345678h
TYPE操作符
按字节返回变量的大小.
LENGTHOF
计算数组中元素的数目.如果声明了一个跨多行的数组,LENGTHOF只把第一行的数据作为数组的元素.
然而也可以再第一行的最后加一个逗号,以连接下一行的初始值.
byte1 BYTE 10, 20, 30 ; 3
array1 WORD 30 DUP(?), 0, 0 ; 32
array2 5 DUP(3 DUP(?) ) ; 15
array3 BYTE 10, 20 ,30 ; 3
BYTE 40, 50, 60
array3 BYTE 10, 20 ,30, ; 6
BYTE 40, 50, 60
SIZEOF操作符
返回LENGTHOF和TYPE的乘积.
LABEL伪指令
插入一个标号并赋予其尺寸属性而无须分配任何世界的存储空间.
常用法师位数据段内气候定义的变量提供一个别名以及一个不同的尺寸属性.(省略多处写PTR的繁琐)
.data
val16 LABEL WORD
val32 DWORD 12345678h
mov ax val16 ;ax = 5678h
mov dx [val16+2] ; dx = 1234h
有时需要用两个较小的整数构造一个较大的整数.
.data
LongValue LABEL DWORD
val1 WORD 5678h
val2 WORD 1234h
.code
mov eax, LongValue ;EAX = 12345678h
间接寻址
处理数组时完全使用直接寻址是不切实际的.
保护模式,间接操作数可以是任何用方括号括起来的任意的32位通用寄存器.
实地址模式使用寄存器做间接操作数的话,只用使用SI, DI, BX, BP做寄存器.避免使用BP.
.data
val1 BYTE 10h
.code
mov esi OFFSET val1
mov al, [esi] ; al = 10h
使用间接操作数的寄存器应进行初始化.否则保护模式下可能产生通用保护故障.实地址模式下引起未知问题.
PTR:与间接操作数的联合使用.有时候在一条指令的上下文中,操作数的大小通常并不明确.
inc [esi] ;错误.编译器并不知道ESI指向一个字节,一个字或其他尺寸的操作数
inc BYTE PTR [esi] ;正确
数组:
.data
array1 BYTE 10h, 20h, 30h
.code
move esi, OFFSET array1
mov al, [esi]
inc esi
mov al, [esi]
inc esi
mov al, [esi]
变址操作数
变址操作数把常量和寄存器相加以得到一个有效地址.任何32位寄存器都可以作为变址寄存器.2种格式:
constant[reg]
[constant + reg]
如:array[esi], [array + esi], array[esi + 4]
实地址模式只能使用SI,DI,BX,BP寄存器.避免使用BP.
指针
16位模式 32位模式
NEAR指针: 相对数据段开始的16位偏移地址 相对于数据段开始的32位偏移地址
FAR指针: 32位的段-偏移地址 48位的段选择子-偏移地址
默认为NEAR指针.
arrayb BYTE 10h, 20h, 30h, 40h
arrayw WORD 1000h, 2000h, 3000h
ptrB DWORD arrayb
ptrW DWORD arrayw
还可试用TYPEDEF操作符:
TYPEDEF允许创建用户自定义的类型.
TYPEDEF的声明通常至于程序开始处,数据段之前.如:
PMYTYPE TYPEDEF PTR BYTE
.data
arrayB BYTE 10h, 20h, 30h, 40h
ptr1 PMYTYPE ? ;未初始化
ptr2 PMYTYPE arrayB ;指向数组
JMP指令
无条件转义指令
通常情况下JMP只能跳转到当前过程内的标号处
JMP 目的地址
top:
....
JMP top
LOOP指令
LOOP 目的地址
ECX被自动用作计数器,每次循环减一.如果ECX等于0,则不跳转,否则一直跳转到目的地址.
move ecx, 5
L1:
inc ax
loop L1
常犯错误为将ECX初始化为0.减一得到FFFFFFFF.
在寄存器不够用或者多重嵌套中,必须自己想办法保存恢复ECX的值.
一个复制字符串的例子:
INCLUDE Irvine32.inc
.data
source BYTE "This is the source string", 0
target BYTE SIZEOF source DUP(0), 0
.code
main PROC
mov esi, 0
mov ecx, SIZEOF source
L1:
mov al, source[esi]
mov target[esi], al
inc esi
loop L1
exit
main ENDP
END main