# 指令 ## $和$(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代表真,则: 例: ```php 0 and 0 = 0 0 and 1 = 0 1 and 0 = 0 1 and 1 = 1 ``` and 指令的两个操作数都应当是字节或者 字。其中,目的操作数可以是通用寄存器和内存单元;源操作数可以是通用寄存器、内存单元或者 立即数,但不允许两个操作数同时为内存单元,而且它们在数据宽度上应当一致。 例: ```php 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 ```