指令
\(和\) (NASM)
$ 相当于本行行首一个隐藏的标号。是当前行的汇编地址。
例如:
jmp near $ infi: jmp near infi ;两者等价
$$ 代表当前汇编段的起始汇编地址,当程序未定义节或段,就默认自称一个汇编段,而且起始的汇编地址是0(程序起始处)。这样,用当前汇编地址减去程序开头的汇编地址(0),就是程序实体的大小。
adc
adc(Add With Carry)带进位加法指令。
要求:
目的操作数可以是8位或16位的通用寄存器和内存单元,源操作数可以是与目的数宽度一致的通用寄存器、内存单元和立即数(目的操作数和源操作数都为内存单元的除外)。
结果:
adc执行时,将目的操作数和源操作数相加,再加上CF位。也就是说,视CF位的状态,再加0或加1.
好处:
adc配合add可以进行16位以上的加法。
adc对OF、SF、ZF、AF、CF和PF的影响视计算结果而定。## add
add是加法指令,用于两个数相加
add 指令需要两个操作数,目的操作数可以是 8 位或者 16 位的通用寄存器,或者指向 8 位或者 16 位实际操作数的内存地址;源操作数可以是相同数据宽度的 8 位或者 16 位通用寄存器、指向 8 位或者 16 位实际操作数的内存地址,或者立即数,但不允许两个操作数同时为内存单元。相加后, 结果保存在目的操作数中。
add有以下几种形式
mov 寄存器, 数据 ;mov ax, 8 mov 寄存器, 寄存器 ;mov ax, bx mov 寄存器, 内存单元 ;mov ax, 0000:0000 mov 内存单元, 寄存器 ;mov 0, ax
and
逻辑“和”,两者都为真才是真,若0代表假,1代表真,则:
例:
0 and 0 = 0
0 and 1 = 0
1 and 0 = 0
1 and 1 = 1
and 指令的两个操作数都应当是字节或者 字。其中,目的操作数可以是通用寄存器和内存单元;源操作数可以是通用寄存器、内存单元或者 立即数,但不允许两个操作数同时为内存单元,而且它们在数据宽度上应当一致。
例:
and al, 0x55
and ch, cl
and ax, dx
and [label a], ah
and word [label_a], 0xf0f0
and dx, [label_a]
结果:两个操作数对应的各个比特位分别进行逻辑“与”,结果保存在目的 操作数中。
and 指令执行后,OF 和 CF 位被清零,SF、ZF、PF 位的状态依计算结果而定,AF 位的状态未定义。
cmp
cmp:比较指令。需要两个操作数,目的操作数可以是 8 位或者 16 位通用 寄存器,也可以是 8 位或者 16 位内存单元;源操作数可以是与目的操作数宽度一致的通用寄存器、 内存单元或者立即数,但两个操作数同时为内存单元的情况除外。
例:
cmp al, 0×08 cmp dx, bx cmp [label_a], cx
cmp 指令在功能上和 sub 指令相同,唯一不同之处在于,cmp 指令仅仅根据计算的结果设置相 应的标志位,而不保留计算结果,因此也就不会改变两个操作数的原有内容。cmp 指令将会影响到 CF、OF、SF、ZF、AF 和 PF 标志位
结果:
若ZF=1,则两个数相等
当无符号时,若:
CF=1,则说明有了进位或结尾,cmp是进行的减操作,因此可以看出是结尾,所以oprd1< oprd2
CF=0, 则说明无借位,此时如果ZF=0,说明oprd1-oprd2!=0,因此oprd1>oprd2
当有符号时:
若SF=0, OF=0,则说明此时的值为正数,没有溢出,故 oprd1< oprd2
db、dw和dd
db,b是byte的意思。是以字节为单位,定义一组数据,每个操作数占有1个字节(8bits)
dd,第二个d是double的意思。是以2个字,即4个字节(32bits),每个操作数占用4个字节。
dw,w是word的意思。是以一个字,即2个字节(16bits),每个操作数占用2个字节。
dword:32位## div
8086CPU提供了除法指令div,它可以做两种除法
① 16位二进制除以8位二进制
要求:被除数在AX中,除数可以由8位通用寄存器或内存单元提供。 结果:商在AL中,余数在AH中。
例:
div cl div byte [0×0023]
前一条指令中,寄存器 CL 用来提供 8 位的除数。假如 AX 中的内容是 0x0005,CL 中的内容 是 0x02,指令执行后,CL 中的内容不变,AL 中的商是 0x02,AH 中的余数是 0x01。 后一条指令中,除数位于数据段内偏移地址为 0x0023 的内存单元里。这条指令执行时,处理 器将数据段寄存器 DS 的内容左移 4 位,加上偏移地址 0x0023 以形成物理地址。然后,处理器再次 访问内存,从此处取得一个字节,作为除数同寄存器 AX 做一次除法
② 32位除以16位
要求:被除数的高16位在DX中,低16位在AX中。除数可以由16位通用寄存器或者内存单元提供。 结果:商在AX中,余数在DX中。
例:
mov dx, 0 mov ax, 0×8 mov bx, 0×3 div bx
equ
伪指令。意为“等于”,相当于C中的宏。
例:
app_lba_start equ 100
本语句的意思是:用标号app_lba_start来代表数值100.
例:
mov al, app_lba_start ;= mov al, 100
in和out
in和out用于端口的访问
in
指令是从端口读。例:
in al, dx in ax, dx
in指令的目的操作数必须是寄存器AL或AX。当访问8位的端口时,使用寄存器AL;访问16位的端口时,使用AX。in指令的源操作数应当是寄存器DX。 in指令不影响任何标志位。
out指令是向端口发送数据。
out指令的目的操作数可以是8位立即数或者寄存器DX,源操作数必须是AL或AX。
例:
out 0x37, al ;写0x37端口(8位) out 0xf5, ax ;写0xf5端口(16位) out dx, al ;这是一个8位端口,端口号位于寄存器DX中 out dx, ax ;这是一个16位端口。端口号位于寄存器DX中
out指令不影响任何标志位。#### inc
加一指令。
例:
inc bx ;=add bx, 1
jle
条件转移指令:小于或等于则转移## jmp
jmp是转移指令
例:
jmp 0×5000:0×f0c0 ;将CS:IP指向0×5000:0×f0c0,CPU从此处读取指令
相对近转移
infi: jmp near infi
在编译阶段,编译器是这么做的:用标号(目标位置)处的汇编地址减去当前指令的
汇编地址,再减去当前指令的长度(3),就得到了 jmp near infi 指令的实际操作数。
jne
条件转移指令
当ZF=0,转移到标号执行## jns和js
jns:SF=0则转移
js:SF=1则转移## jz(js)条件转移
jz(jump if zero) 即ZF(zero flag)为1跳转
jz的另一种写法就是je,je=jump if equal,jz和je的作用是完全一样的。## loop
循环,每次执行loop它会:
CX的内容减一
如果CX的内容不为零,转移到指定位置处执行,否则顺序执行后面的指令
例:
mov cx, 5 ;循环5次 flagt: mov ax, 1 loop flagt
loop的指令长度是2## MOV
mov有以下几种形式:
mov 寄存器, 数据 ;mov ax, 8 mov 寄存器, 寄存器 ;mov ax, bx mov 寄存器, 内存单元 ;mov ax, 0000:0000 mov 寄存器, 段寄存器 ;mov ax, ds mov 段寄存器, 寄存器 ;mov ds, ax mov 段寄存器, 内存单元 ;mov ds, 0 mov 内存单元, 寄存器 ;mov 0, ax mov 内存单元, 段寄存器 ;mov 0, ds
mov指令对以下寄存器有效
通用寄存器:ax, bx, cx, dx
段寄存器:cs, ds, es, fs, gs, ss都无效
变址寄存器:si, di
栈寄存器:sp, bp## movsb和movsw
这两个指令通常用于把数据从内存中的一个地方批量地传送(复制)到另一个地方
区别:
movsb的传送是以字节为单位的,而movsw的传送石以字为单位的。
要求:
原始数据串的段地址由DS指定,偏移地址由SI指定。即 **DS:SI** 传送目的地址由**ES:DI**指定 传送的字节数(movsb)或者字数(movsw)由CX指定。
此外,还要指定是正向传送还是反向传送。
正向传送:传送操作由内存区域的低地址段到高地址段
反向传送:传送操作由内存区域的高地址段到低地址段
每个要显示的字符实际上占两个字节:ASCII码和属性
neg
用0减去操作数。
例:
neg al ;0-al neg dx ;0-dx neg word [label_a] ;0-[label_a]
or
逻辑“或”,当两者有一个为真时即为真
or 指令的目的操作数可以是 8 位或者 16 位的通用寄存器,或者包含 8/16 位实际操作数的内存地址,源操作数可以是与目的操作数数据宽度相同的通用寄存器、内存单 元或者立即数。
例:
or al, al
or ax, dx
or [label_a], bx
or byte [label_a], 0x55
or 指令不允许目的操作数和源操作数都是内存单元的情况。当 or 指令执行时,两个操作数相 对应的比特之间分别进行各自的逻辑“或”运算,结果位于目的操作数中
or 指令对标志寄存器的影响是:OF 和 CF 位被清零,SF、ZF、PF 位的状态依计算结果而定, AF 位的状态未定义。
pop
,pop 指令的操作数可以是 16 位的寄存器或者内存单元。
例:
pop ax pop word [label_a]
pop 指令执行时,处理器将堆栈段寄存器 SS 的内容左移 4 位,再加上堆栈指针寄存器 SP 的内 容,形成 20 位的物理地址访问内存,取得所需的数据。然后,将 SP 的内容加操作数的字长,以指 向下一个堆栈位置。 pop 指令不影响任何标志位。
push
在 16 位的处理器上,push 指令的操作数可以是 16 位的寄存器或者 内存单元。
例:
push ax push word [label_a]
你可能觉得奇怪,push 指令只接受 16 位的操作数,为什么要对内存操作数使用关键字“word”。 事实上,8086 处理器只能压入一个字;但其后的处理器允许压入字、双字或者四字,因此,关键字 是必不可少的。 就 8086 处理器来说,因为压入堆栈的内容必须是字,所以,下面的指令都是非法的:
push al push byte [label_a]
处理器在执行 push 指令时,首先将堆栈指针寄存器 SP 的内容减去操作数的字长(以字节为单 位的长度,在 16 位处理器上是 2),然后,把要压入堆栈的数据存放到逻辑地址 SS:SP 所指向的内 存位置(和其他段的读写一样,把堆栈段寄存器 SS 的内容左移 4 位,加上堆栈指针寄存器 SP 提供 的偏移地址)
ret和retf
过程返回
指令
ret 和 retf 经常用做 call 和 call far 的配对指令。ret 是近返回指令,当它执行时,处理器只做一 件事,那就是从堆栈中弹出一个字到指令指针寄存器 IP 中。
retf 是远返回指令(return far),它的工作稍微复杂一点点。当它执行时,处理器分别从堆栈中 弹出两个字到指令指针寄存器 IP 和代码段寄存器 CS 中。
,尽管 call 指令通常需要 ret/retf 和它配对,遥相呼应,但 ret/retf 指令却并不依赖于 call 指令
ret/retf 指令对标志位也没有任何影响。
rol
循环左移(ROtate Left)
ror
循环右移(ROtate Right)。循环右移指令执行时,每右移一次,移出的比特既送 到标志寄存器的 CF 位,也送进左边空出的位。
shl
逻辑左移指令(SHift logical Left)
shr
逻辑右移指令(SHift logical Right)
例:
shr ax, 4 ;将ax中的内容右移4位
逻辑右移指令执行时,会将操作数连续地向右移动指定的次数,每移动一次, “挤”出来的比特被移到标志寄存器的 CF 位,左边空出来的位置用比特“0”填充。
shr 指令的目的操作数可以是 8 位或 16 位的通用寄存器或者内存单元,源操作数可以是数字 1、 8 位立即数或者寄存器 CL。
sub
sub是减法运算, 有以下几种形式
mov 寄存器, 数据 ;mov ax, 8 mov 寄存器, 寄存器 ;mov ax, bx mov 寄存器, 内存单元 ;mov ax, 0000:0000 mov 内存单元, 寄存器 ;mov 0, ax
times
times是一个伪指令,可用于重复它后面的指令若干次。
例:
times 20 mov ax, bx ;编译时重复生成mov ax, bx指令20次
times 203 db 0 ;编译时生成203个为0的字节
xor
在数学逻辑中xor是异或(eXclusive OR)的意思,或者叫互斥或、互斥的或运算。
例:若0代表假,1代表真
0 xor 0 = 0 0 xor 1 = 1 1 xor 0 = 1 1 xor 1 = 0
xor 指令的目的操作数可以是通用寄存器和内存单元,源操作数可以是通用寄存器、内存单元 和立即数(不允许两个操作数同时为内存单元)。而且,异或操作是在两个操作数相对应的比特之间单独进行的。
用法:
xor dx, dx ;清空dx