汇编笔记

文章目录


还有一篇写的不错的Blog可以作为参考:
https:;blog.csdn.net/Breeze_CAT/article/details/82465838

Chapter II

通用寄存器

前排提醒: 8086寄存器坑点(刷题整合)

  1. 8086中能够出现在[]中作为偏移地址的寄存器:
    只有: BX, BP, SI, DI

  2. 注意, BX & BP不允许出现在同一个[]中作为偏移地址

  3. 串处理指令中, 使用的默认寄存器:
    SRC string: DS:SI
    DST string: ES:DI

  4. CF & OF位的神奇判定法:
    CF是最高位产生了进位或借位时的标志

    而OF的设置判定:

    • 如果两个算子一个是有符号数, 一个是无符号数, 则运算结果必定不会溢出

    • 如果两个算子同符号, 运算后符号相反, 则表示溢出了
      即正数相加变成了负数, 就是溢出

  5. 段寄存器的赋值
    只有CS不可以直接赋值, 如: MOV CS, AX 和 POP CS
    而其他几个DS, ES, SS都可以直接MOV赋值

  6. SP栈顶指针的移动:
    进栈的方向是LSB
    即执行PUSH AX后, 栈顶指针SP-2, 而POP后, 栈顶指针SP+2


  • 所有的通用寄存器都可以自由使用
    但每个寄存器又有其单独的用途

  • AX,BX,CX,DX 可以拆分为高8位和低8位使用, 分别是AH&AL, BH&BL…
    而SP, BP, SI, DI四个只能以16位的方式使用

  • SP, BP, SI, DI四个寄存器更经常的用途是在储存器寻址时, 提供偏移地址

在这里插入图片描述

AX(accumulator)累加器

最常用的寄存器, 主要用在算数运算中, 或在乘除指令中指定用来存放操作数, 但在与外部设备传输信息时必须使用此寄存器

BX(base)基址变址

注意, BX & BP不允许出现在同一个[]中作为偏移地址

常常与BP基址指针寄存器同时使用, BP作为基址寄存器, BX作为偏移地址寄存器
通常与CX组合存放32位操作数, BX存高位, CX存低位
BX:CX

CX(count)计数器

可作为隐含的计数器使用, 通常与BX组合存放32位操作数, BX存高位, CX存低位

DX(data)数据

通常与AX共同使用用作双字长运算, DX用于存放高位字, AX用于存放低位字

DX:AX

SP(stack pointer)堆栈指针

SP表示栈顶指针,指向栈顶地址.与SS相配合使用, 其中SS为栈段

BP(base pointer)基址指针

注意, BX & BP不允许出现在同一个[]中作为偏移地址

通常将BP会与SS堆栈段寄存器联用用来确定堆栈段中某一储存单元的地址, 段地址默认储存在SS中, BP可作为堆栈区中的一个基地址,以便访问堆栈中的信息, 如

MOV AX,[BP+SI+6]
MOV AX,[BP+DI+6]

DI(destination index)目的变址 & SI(source index)源变址

SI&DI具有自动增量和自动减量的功能, 很方便用于变址

在串处理指令中, 这两个寄存器作为隐含的源变址和目的变址寄存器, 其中SI与DS数据段寄存器联用, DI与ES附加段寄存器联用, 分别达到在数据段和附加段中寻址的目的

专用寄存器

IP(instruction pointer)指令指针

IP用来存放代码段中的偏移地址. 通常是由程序运行时操作系统自动操控的, 无需用户手动操作, 并且有时手动操作将导致程序出错

FIAGS(program status word, PSW)标志寄存器/程序状态寄存器

FLAGS用来存放条件码标识, 控制标识和系统标识, 与单片机中的标志寄存器类似, 此寄存器是按位使用的, 各个位的功能如下:
在这里插入图片描述

一,状态标志:

CF(carry flag):进位位

作为无符号数溢出标志

如果运算结果的最高位产生一个进位或错位,则CF置1,否则CF清零。

CF是最高位产生了进位或借位时的标志
OF是次高位产生了进位或借位时的标志位

PF(parity flag):奇偶位

如果运算结果低8位中“1”的个数为偶数时,则PF置1, 否则PF清0。

AF(auxiliary carry flag):辅助进位位

反应运算结果低四位产生进位或错位的情况

ZF(zero flag):零值位

如果运算结果为零则ZF置1。否则清零。

SF(sign flag):符号位

如果运算结果为负,即一个数的最高位为1,则SF置1,否则SF清零。

OF(overflow flag):溢出位

作为有符号数溢出标志

若运算结果超出补码表示范围(8位-128 ~ +127,16位-32768 ~+32767)。若溢出,OF置1,否则OF清0。

OF最有用的判定是:

首先是溢出的定义:
溢出是针对有符号数而言的

  • 如果两个算子一个是有符号数, 一个是无符号数, 则运算结果必定不会溢出
  • 如果两个算子同符号, 运算后符号相反, 则表示溢出了
    即正数相加变成了负数, 就是溢出

特别说明:

在汇编中, CPU无法区分一个数是否是有符号数, 一般根据程序员的意愿来将一个储存单元内的二进制数解释为无符号数或有符号数
如乘法指令中, MUL用于无符号数, IMUL用于有符号数

二,控制标志

TF(trap flag):单步标志位

用于程序跟踪调试。当TF=1,CPU进入单步方式。

IF(interrupt flag):中断允许位

当IF=1时,CPU为开中断。 当IF=0时,CPU为关中断。

DF(direction flag):方向位

决定串操作指令执行时的指针寄存器的调整方向

2.4 存储器

2.4.1: 存储单元的地址和内容

数据在储存单元中的储存特点: 低位字节存入低地址,高位字节存入高地址, 即如果从储存单元中读取数据是从高位地址往低位地址读, 最后才能还原出数据

2.4.2: 实模式储存器寻址

关于实模式&保护模式(拓展知识)

实模式简单来说就是16位模式(其中80286有16位保护模式,但由于地址线的限制,实则都差不多),在16位模式下是使用20根地址线,可以寻址1MB内存,但又由于处理器内部的寄存器只有16位(以前的处理器),因此为了能访问整个1M内存,所以采用段地址+偏移地址的寻址方式(后头会相信说明)

保护模式是工作在32位模式下,具有32位的内部寄存器,以及32(or64)根地址线,最大访问空间为4GB内存,可以访问整个4G内存空间,但由于依然是在x86处理器的架构下,为了兼容,而依然采用段式寻址空间

保护模式可以看作是对实模式的扩充, 从16位处理器发展到32位处理器, 除了寻址范围的提升,32位处理器还发展出了很多的功能,与现代操作系统相辅相成,例如内存保护,分页系统,以及硬件支援的 虚拟内存,拥有这么多的功能,硬件自然要提供很多机制才能实现出来,这就叫做保护模式, 而实模式的保留则是为了兼容之前的16位程序, 所以现代的CPU在启动时,运行在实模式,但是启动过程中的某一个阶段切换成保护模式。

16位存储器寻址:

16位CPU中的寄存器只有16位, 即最多可以直接访问0~65535字节的存储空间, 而8086的总线有20根, 为了更好的利用剩余的4根总线, 开发者将CPU访问内存的方法设置为16位段地址+16位偏移地址, 为了使结果只有20位, 计算方法为将16位段地址向左以4个bit后与偏移地址按位相加形成物理地址

关于物理地址和逻辑地址:
物理地址是存储空间具体在地址总线上的位置
而逻辑地址是指令进行寻址时的EA(Effective Address)
二者还是有一定区别的!

段寄存器

上头说道了16位存储器寻址时的分段问题, 而后的处理器为了兼容同样也使用了段地址+偏移地址的方式访问储存器:

286以下有4个段寄存器: CS代码段 DS数据段 ES附加段 SS堆栈段
386开始多了两个段寄存器: FS GS (二者都没有专用名称)

在16位处理器中, 寄存器为16位, 所以偏移地址的表示最大为16位, 即64KB, 所以每个段最大可以占用64KB的储存空间, 但通常情况下, 各段在存储器中的大小分配是由操作系统完成的(即无需每个段都占用64KB的内存, 可以实现要多少分配多少, 所以最终可能每个段的大小不同, <64KB, 这种现象叫做段的重叠), 但是如果需要, 也可以手动分配空间

程序运行期间, 所有的信息都分类保存在各个段中, 如程序的代码保存在代码段CS, 数据保存在数据段DS等, 但是如果程序的某一段需要的空间>64KB, 则需要动态的修改该段的内容, 以保证所获得的信息的正确性
但是>64KB的情况现在基本不会用到, 做短期利益考虑, 不做拓展

段寄存器和存放偏移地址的寄存器之间的固定搭配(16位相应的删掉前头的E):这个表很重要
在这里插入图片描述

注意: 在8086寄存器组中,只有bx,bp,si,di这四个寄存器可以用在[……]中表示偏移地址

段寄存器详解:
  1. CS & IP
    段寄存器CS指向存放程序的内存段,IP是用来存放下条待执行的指令在该段的偏移量,把它们合在一起可在该内存段内取到下次要执行的指令。

    CS不可以直接赋值, 如: MOV CS, AX
    这个段寄存器的值要由系统设置
    其他的DS、ES、SS都可以用MOV赋值,但不能用立即数

  2. SS & SP & BP
    段寄存器SS指向用于堆栈的内存段,SP是用来指向该堆栈的栈顶,把它们合在一起可访问栈顶单元
    指针寄存器BP,其缺省的段寄存器也是SS,用BP可访问整个堆栈,不仅仅是只访问栈顶

    因为SP固定指向栈顶, 所以需要访问堆栈中的其他元素时, 需要使用BP

  3. DS & ES
    段寄存器DS指向数据段,ES指向附加段,在存取操作数时,二者之一和一个偏移量合并就可得到存储单元的物理地址。
    通常,缺省的数据段寄存器是DS,只有一个例外,即在进行串操作时,其目的地址的段寄存器规定为ES。

80X86的数据储存方式:

80X86的CPU绝大多数是用小端模式进行存储
而ARM绝大多数都是大端存储

小端模式:

将数据的高位放在低字节, 低位放在高字节
取数据时, 向低字节方向进行解析

大端模式:
和小端模式相反

将数据的高位放在高字节, 低位放在低字节
去数据时, 向高字节方向进行解析

所以有如下代码:|

DATA	SEGMENT
	TABLE   DW   10H,20H,30H,40H,50H,60H,70H,80H
	ENTRY      DW   5
DATA	ENDS
;*******************
CODE	SEGMENT
	ASSUME 	CS: CODE, DS: DATA
START:
	MOV 	AX, DATA
	MOV 	DS, AX
;*******************
	MOV  	BX, OFFSET  TABLE
	MOV 	CX, 10
RE:
	MOV  	AX, [BX]
	ADD  	BX, 1
	LOOP 	RE
;*******************
	MOV 	AX, 4C00H
	INT 	21H
CODE	ENDS
;*******************
	END	START

每次MOV AX, [BX]后, AX的值为:

0010, 2000, 0020, 3000, 0030, 4000 …



Chapter III 指令系统和寻址方式

指令的一般格式(这一整行为一条指令)

操作码 操作数, 操作数, ... , 操作数

通常操作数字段为一个或两个, 较少的为3个, 通常称为一地址指令, 二地址指令, 三地址指令

80x86の寻址

寻址方式用来确定操作数地址, 从而找到操作数
每个指令通常都有其对应的寻址方式, 而掌握每一种寻址方式是使用指令的前提, 否则后头看指令时会一脸懵逼
但是后头的指令基本上没有说特别使用哪一种, 通常是除了立即数以外都能用, 所以就TM记着怎么用就好, 不用记相应的名字

除了立即寻址方式和寄存器寻址方式以外,其他各种寻址方式的操作数都在除代码段以外的储存区中, 即需要通过段基地址和偏移地址相加而取的

在80x86中, 通常将操作数的偏移地址称为有效地址, 有效地址可以用以下四种成分组成:

  1. 位移量(displacement)
    位移量是存放在指令中的一个8位, 16位或32位的数(类似立即数的存储方式)
    但他不是一个立即数,而是一个地址
  2. 基址(base)
    基址是存放在基址寄存器(BX)中的内容
    其指有效地址的基址部分。通常要来指向数据段中的数组和字符串的首地址
  3. 变址(index, 即下标)
    变址是存放在变址寄存器(SI&DI)中的内容
    它通常用来访问数组中的某个元素和字符串中的某个字符。
  4. 比例因子(scale factor, 386机型后有)
    比例因子的值通常就是1,2,4,8, 且是乘上去的
    比例因子用于在寻址过程中将变址寄存器的内容乘以比例因子来取得变址值, 所以在访问元素长度为2,4,8字节的数组特别有用

有效地址的计算公式:
EA(effective address) = 基址 + (变址 * 比例因子) + 位移量
其中除比例因子以外, 其他3个都可以是负数, 以保证指针移动的灵活性
至于为何要整上4种, 而不是仅仅基址+位移量两种, 是因为引入变址&比例因子使得有效地址(偏移量)的计算更加灵活

而在32位寄存器中, 既可以采用32位寻址,也可以用16位寻址

关于偏移地址:
EA的计算公式中, 除了基址, 后头的三个组成偏移地址

而在指令的表示中, []中括号内的通常是偏移地址

关于四种成分具体的区别:
其中, 一个EA有效地址中的位移量是直接给出的数字, 比例因子为特定的数字, 而基址和变址分别储存在相应的寄存器中, 具体如以下表格:
在这里插入图片描述
上表说明了各种访问类型相对应的段的默认选择(即程序默认使用的寄存器), 而作为底层语言, 程序员可以强制指定段寄存器使用, 称为段跨越前缀, 如:

;首先解释一下段前缀:
MOV AX,DS:[BX]  ;其中DS:就是段地址, 类比于IP与端口的关系, 如: 192.168.1.1:8080

mov ax,[0000]
;程序默认使用的是ds:0000

;而使用了跨越段前缀就使用你指定的段寄存器
mov ax,es:[0000]
;程序就会使用es:0000

但是有三种情况不允许使用段跨越前缀:

  1. 串处理指令的目的串必须使用ES段
  2. PUSH指令的目的和POP指令的源必须使用SS段
  3. 指令必须存放在CS段中

在这里插入图片描述

MOV指令

MOV为传送指令:

MOV d,s  ;d为目的操作数(destination), s为源操作数(source)
立即寻址方式

在程序的编码中, 操作数直接存放在指令中, 紧跟在操作码之后, 作为指令的一部分存放在代码段里(相当于预处理赋值)

立即寻址方式常用于给寄存器赋初值, 并且只能用于原操作数字段, 不能用于目的操作数字段

MOV AL, 5			;执行后(AL)=05H
MOV AX, 3064H		;执行后(AX)+3064H
MOC EAX, 12345678H 	;执行后(EAX)=12345678H
寄存器寻址方式

此时操作数存储在寄存器中, 指令指定寄存器号. 此种方法由于操作数储已经储存在寄存器中, 不需要访问储存器, 所以运算速度较高(相当于寄存器变量)

 MOV AX,BX		;执行前: (AX)=3064H, (BX)=1234H, 执行后: (AX)=1234H, BX保持不变
 MOV ECX, EDX	;大致同上
直接寻址方式

操作数的有效地址就是位移量, 即在有效地址中, 仅有位移量一个变量, 其他三个都空, 如:
在这里插入图片描述
在这里插入图片描述
与C++类似, 地址同样可以用符号代替(类比于变量), 称为符号地址:

MOV AX, VALUE  	此时的VALUE即为存放在操作数单元的符号地址
寄存器的间接寻址方式

操作数的有效地址只有基址或变址, 即有效地址就在基址寄存器或变址寄存器中
这个和寄存器寻址方式有点区别, 前者是直接用寄存器, 而这个使用的是寄存器里的数

MOV AX, [BX]	;当(DS)=2000H, (BX)=1000H时, 物理地址为20000H+1000=21000H

在这里插入图片描述

寄存器相对寻址方式(直接变址寻址方式)

有效地址=基址寄存器或变址寄存器+位移量

如:

;count为16位位移量
MOV AX, COUNT[SI]	;这种不用+
MOV AX, [COUNT+SI]
;当(DS)= 3000H, (SI)= 2000H, COUNT= 3000H
则物理地址= 30000+2000+3000 =35000H

直接变址寻址方式也可以使用段跨越前缀:

MOV DL, ES:STRING[SI]
基址变址寻址方式(based indexed addressing)

有效地址=基址寄存器+变址寄存器

所以有效地址由两种成分组成。它所允许使用的寄存器及其对应的默认段见表3.1和表3.2.

MOV AX, [BX][DI]
MOV AX, [BX+DI]
;当(DS)=2100H, (BX)=0158H, (DI)=10A5H
则EA= 0158+10A5= 11FDH,物理地址= 21000+11PD=221FDH
;32位状态
EDX MOV, [EBEX] [ESIT]

这种寻址方式通常用于数组或表格处理,首地址可存放在基址寄存器中,而用变址寄存器来访问数组中的各个元素。由于两个寄存器都可以修改,所以它比直接变址方式更加灵活。

此种寻址方式使用段跨越前缀时的格式为:

MOV AX, ES: [BX] [SI]

注意: 在汇编中, 4,5,6种寻址方式很常用

相对基址变址寻址方式(relative based indexed addressing)

有效地址=基址寄存器+变址寄存器+位移量

所以有效地址由三种成分组成,如

MOV AX, MASK[BX][SI]
MOV AX, MASK[BX+SI]
MOV AX, [MASK+BX+ SI]
;如(DS)= 3000H, (BX)= 2000H. (SI)= 1000H,MASK= 0250H,
则物理地址=16d × (DS)+(BX)+(SD)+MASK
=30000 +2000-1000-020 = 33250H

;32位寻址方式
MOV EAX。ARRAY[EBX][ECX]

这种寻址方式通常用于对二维数组的寻址,例如,存储器中存放着由多个记录的文件,则位移量可指向文件之首,基址寄存器指向某个记录,变址寄存器则指向该记录中的一个元素。这种寻址方式也为堆栈处理提供了方便,一般(BP)可指向栈顶,从栈顶到数组的首址可用位移量表示,变址寄存器可用来访问数组中的某个元素。

这种寻址方式及以下几种寻址方式使用段跨越前缀的方式与之前类似

比例变址寻址方式

有效地址=变址寄存器*比例因子+位移量

如:

MOV  EAX,COUNT[ESI*4]	
基址比例变址寻址方式

有效地址=变址寄存器*比例因子+基址寄存器

如:

MOV ECX, [EAX][EDX*8]
相对基址比例变址寻址方式

有效地址=变址寄存器*比例因子+基址寄存器,+位移量

3.1.2在讲转移指令时在拓展,现在先放着

80x86指令系统

x86系统の主要指令可以分为以下6种:

  • 数据传送指令
  • 串处理指令
  • 算数指令
  • 控制转移指令
  • 逻辑指令
  • 处理机控制指令

数据传送指令

数据传送指令用于把数据, 地址或立即数传送到寄存器或储存单元中, 即相当于实现赋值功能

通用数据传送指令
MOV (move)						;传送

MOVSX(move with sign-extend)	;带符号扩展传送, 386后可用

MOVZX(move with zero-extend) 	;带零扩展传送, 386后可用

PUSH(push onto the stack)		;进栈

POP(pop from the stack)			;出栈

PUSHA/PUSHAD(push all registers);所有寄存器进栈 

POPA/POPAD( pop all registers)	;所有寄存器出栈

XCHG(exchange)					;交换
  1. MOV传送指令
    格式为: MOV DST, SRC
    执行操作: (DST) <–(SRC)
    其中DST为目的操作数, SRC 为源操作数

    MOV使用要点:

    • MOV指令不允许在储存器和储存器之间传送
      (即两个操作数不允许都是储存器)

      其用于在CPU内部或CPU与储存器之间传送字,字节,双字节

    • MOV对于立即数的使用:
      只有寄存器(不包括段寄存器)和地址可以使用立即数
      其他所有的都不可以使用立即数
      并且注意这个地址是符号地址或是

      段寄存器的存在相当于储存器

      如:

      MOV 	ES, 200H 	;报错!
      ;段寄存器的存在相当于储存器
      
    • MOV的两个操作是的长度必须是相同的, 否则报错
      当操作数长度不同时, 使用MOVSX & MOVZX

    最好的方式就是任何MOV指令传送都通过一个通用寄存器(如AX)做中转, 这样最不容易出错

    MOV补充:

    MOV     [BX],10H 	;MOV实际上不支持这种操作, 但是在编译器中是可以通过的
    
    ;必须在前头说明地址的数据类型:
    MOV		WORD PTR[BX], 1001H	;正确
    ;因为立即数无类型, 所以需要显式说明
    

    实际上相当于DST为储存器寻址方式, SRC为立即数寻址方式

  2. MOVSX & MOVZX 带符号/带零拓展的传送指令

;reg为寄存器, mem为储存器
MOVSX	REG1, REG2	
MOVSX	REG, MEM

​ 目的操作数必须是寄存器

​ 操作: 将源操作数符号扩展传送到目的寄存器中

格式与MOV相同, 但是要注意的是, 这两个指令的源操作数的长度一定要小于目的操作数的长度(相当于拓展)
  1. PUSH & POP 进栈&出栈
    格式:

    PUSH SRC
    POP DST
    

    PUSH支持所有的寻址方式
    ​(寄存器reg, 储存器mem, 立即数data, 段寄存器segreg)

    POP支持处立即数data以外的任何寻址方式

    二者的执行效果:
    相当于对堆栈指针SP移位, 16位操作±2字节, 32位操作±4字节, 而后将SRC的数据进栈弹出SRC或将栈顶元素存入DST

注意: 进栈的方向是LSB, 即执行PUSH AX后, 栈顶指针SP-2, 而POP后, 栈顶指针SP+2

  1. PUSHA/PUSHAD & POPA/POPAD 所有寄存器进栈/出栈
    其中AD版本是32位的操作, A版本是16位的操作

    此指令执行后将所有的通用寄存器进栈/出栈, 进栈顺序为AX CX DX BX SP BP SI DI (注意都是指令执行前的值), 出栈顺序相反, 执行后SP仍然指向栈顶

  2. XCHG 交换指令

    此指令可以在寄存器之间或寄存器与储存器之间交换信息
    (高级语言中需要使用3条语句)

    格式:

    XCHG OPR1 OPR2
    

    使用要点:

    1. 两个操作数必须有一个在寄存器中

    2. 不允许使用段寄存器

累加器专用传送指令

此Part只限于EAX, AX, AL传送信息

  1. IN输入指令 & OUT输出指令

    IN & OUT用于所有的I/O端口与CPU之间的通信, 但是这个端口是啥并不知道

    ;长格式
    IN AC(累加器), PORT(单, 双, 四字节) 
    OUT PORT, AC
    ;短格式
    IN AC, DX
    OUT DX, AC
    

    长格式和短格式的区别:
    PORT只能是单字节数据(即00H~FFH), 直接指向外部端口(但只能是前256个), 当需要访问更多的端口时, 只能使用短格式, 必须将端口号先保存到DX中(此时支持16位和32位), 在由DX输出

  2. XLAT换码指令
    通常用于编码的快速转换
    首先在使用XLAT指令前, 通常已经在Data Segment数据段建立的一个转换表(如数码管的段显示表, 其实就TM是一个数组), 将此表的首地址加载给BX, 并在AX中装入偏移量, 用XLAT后, CPU会将DS+BX+AX指向的值(注意是指向的值)装入AX中

    XLAT OPR	;OPR为操作数, 通常是符号化的首地址, 这种写法提高程序的可读性
    XLAT   		;直接就这样, 简化版
    ;执行效果:
    ;16位: 将BX+AL的值赋给AL, 此时必须零扩展到16位
    ;32位: 将EBX+AL的值赋给AL, 此时必须零扩展到32位
    

    如:

    ;执行前: (BX)=0040H, (AL)=0FH, (DS)=F000H,
    XLAT
    ;执行后, AX的值为F000H+0040H+0FH地址处储存的值
    
地址传送指令

注意这里和之前的MOV指令不同的是, MOV传送的是源操作数中的值, 而地址传送指令传送的是源操作数的地址

这里只介绍LEA & LDS, 而后的几种与LDS相似, 只是指向的寄存器不同而已

  1. LEA(load effective address)有效地址送寄存器指令
    格式:

    LEA REG, SRC
    ;将SRC的有效地址存入REG寄存器中
    

    REG可以是16位或32位寄存器, 但是不可以使用段寄存器
    SRC可以是除立即寻址方式和寄存器寻址方式以外的任何一种储存器寻址方式(即前头的6种)
    如:

    ;执行前: (BX)=0400H, (SI)=003CH
    LEA BX, [BX+SI+0F62H]
    ;执行后: (BX)=0400+003C+0F62=139EH, 这里得到的是有效地址, 而不是地址指向的值
    

注意: 当地址长度与REG寄存器长度不同时(如16位转32位), 会默认对有效地址进行截断或零扩展

  1. LDS(load DS with pointer)指针送寄存器和段寄存器指令
    格式:

    LDS REG, SRC
    

    SRC只能使用储存器寻址方式
    当REG为16位寄存器时, 将SRC指向的储存单元中存放的偏移地址装入REG中, 并将SRC+2中的16位数装入指定的段寄存器中(LDS是DS段寄存器, 而其他几个指令对应其他的段寄存器)
    当REG为32位寄存器时, REG被装入的是SRC指向储存单元中的32位偏移地址, 而后将指定段寄存器装入SRC+4中储存的16位数

标志寄存器传送指令

这4个指令使用频率不是很高, 主要功能是将FLAGS标志寄存器中的信息进行各种传送

	LAHF			;(load AH with flags)				将FLAGS传送至AH
	SAHF			;(store AH into flags)				将AH传送至FlAGS
	PUSHF/PUSHFD	;(push the flags or extra-flags)	将FLAGS进栈
	POPF/POPFD		;(pop the flags or extra-flags)	将栈顶的1Byte传送至FLAGS
类型转换指令

此类型指令均不影响标志位

	CBW			;convert byte to word			字节转换为字, 将AL符号扩展到AX
	;如果AL的最高有效位为0, 则AH=0, 否则AH=0FFH
	CWD/CWDE	;convert word th double word	字转换为字节, 将AX符号扩展到DX, 
	;形成DX:AX双字, 如果AL的最高有效位为0, 则DX=0, 否则DX=0FFFFH
	CDQ			;convert double to quad		双字转换为4字, 将EAX符号扩展到EDX, 
	;形成EDX:EAX中的4字
	BSWAP		;byte swap						字节交换
	;使指令指定的32位寄存器的字节次序变反, 即1,4互换, 2,3互换

算数指令

此类指令与FLAGS标志位关联紧密

在这里插入图片描述

加法指令
ADD	 DST, SRC	(add)				;加法
ADC	 DST, SRC	(add with carry)	;带进位加法
INC	 OPR		(increment)			;加1, 相当于++
XADD DST, SRC	(exchange and add)	;交换并相加, 486以后可用

ADD:
普通加法指令, 对CF进位标志有影响

指令对标志位的影响:
CF=1 最高有效位向高位有进位
CF=0 最高有效位向高位无进位
OF=1 两个同符号数相加(正数+正数 或 负数+负数),结果符号与其相反。
OF=0 两个不同符号数相加,或同符号数相加,结果符号与其相同。

ADC:
进位加法指令, 通常与ADD联用, 用于计算双精度数. 其中先将低位用ADD, 再将高位用ADC
计算时将CF也加入计算, 如果相加产生进位, 会再使用一个寄存器存放高位字, DX:AX, BX:CX
如:

;初始: DX=0002H, AX=0F365H,  BX=0005H, CX=0E024H
ADD	AX, CX 
;执行后: AX=0D389H, SF=1, ZF=0, CF=1, OF=0
ADC	DX, BX
;执行后: DX=0008H, SF=0, ZF=0, CF=0, OF=0

指令对标志位的影响:
CF=1 最高有效位向高位有进位
CF=0 最低有效位相高位无进位
OF=1 两个同符号数相加,结果符号与其相反,
OF=0 两个同符号数相加,或同符号相加,结果符号与其相同

INC:
++指令

指令对标志位的影响:
对CF无影响
OF=1 两个同符号数相加,结果符号与其相反,
OF=0 两个同符号数相加,或同符号相加,结果符号与其相同。

XADD:

XADD DST, SRC

交换并相加, 使用一个temp暂存中间结果(执行过程相当于高级语言的3条语句)
具体执行操作:
TEMP<–(SRC)+(DST)
(SRC)<–(DST)
(DST)<–TEMP

减法指令
SUB DST, SRC		(subtract)						;减法
SBB	DST, SRC		(subtract with borrow)			;带借位减法
DEC	OPR				(decrement)						;自减一
NEG	OPR				(negate)						;求补
CMP	OPR1, OPR2		(compare)						;比较
CMPXCHG	DST, SRC	(compare and exchange)			;比较并交换, 486后可用
CMPXCHG8B DST		(compare and exchange 8 byte)	;比较并交换8字节, 奔腾后可用

CPU中的减法都是将SRC取反+1后加上DST, 与加法就差一个求补的过程

SUB:
普通减法指令, 与ADD相似, 当DST<SRC时会产生借位, 时CF=1

指令对标志位的影响:
CF=1 二进制减法运算中最高有效位向高位有借位(被减数小于减数,不够减的情况)
CF=0 二进制减法运算中最高有效为向高位无借位(被减数〉=减数,够减的情况)
OF=1 两数符号相反(正数-负数,或负数-正数),而结果符号与减数相同。
OF=0 同符号数相减时,或不同符号数相减,其结果符号与减数不同。

SBB:
带借位的减法指令, 与ADC类似, 与SUB联用, 用于计算双精度数, 低位用SUB, 高位用SBB

指令对标志位的影响:
CF=1 二进制减法运算中最高有效位向高位有借位(被减数小于减数,不够减的情况)
CF=0 二进制减法运算中最高有效为向高位无借位(被减数〉=减数,够减的情况)
OF=1 两数符号相反(正数-负数,或负数-正数),而结果符号与减数相同。
OF=0 同符号数相减时,或不同符号数相减,其结果符号与减数不同。

DEC:
自减一指令, 即–

指令对标志位的影响:
对CF无影响
OF=1 两数符号相反(正数-负数,或负数-正数),而结果符号与减数相同。
OF=0 同符号数相减时,或不同符号数相减,其结果符号与减数不同。

NEG:
求补指令, 将OPR取反后+1

指令对标志位的影响:
CF=1  不为0的操作数求补时
CF=0  为0的操作数求补时
OF=1 操作数为-128(字节运算)或操作数为-32768(字运算)
OF=0 当求补运算的操作数不为-128(字节)或-32768(字)时

CMP:
比较指令, 与SUB指令类似, 将OPR1-OPR2, 但是不保存结果, 只是根据结果操作CF位

指令对标志位的影响:
CF=1 二进制减法运算中最高有效位向高位有借位(被减数小于减数,不够减的情况)
CF=0 二进制减法运算中最高有效为向高位无借位(被减数〉=减数,够减的情况)
OF=1 两数符号相反(正数-负数,或负数-正数),而结果符号与减数相同。
OF=0 同符号数相减时,或不同符号数相减,其结果符号与减数不同。

CMPXCHG & CMPXCHG8B
比较并交换指令, SRC只能用8位, 16位, 32为寄存器, DST可用寄存器或任意寻址的方式
<这个比较作用比较迷惑, 先放着>

乘法指令

注意乘法和除法都不可以使用立即数

MUL SRC 	(unsigned multiple)	;无符号乘法
IMUL SRC	(signed multiple)	;有符号乘法
;SRC可以使用处立即数外的任何一种寻址方式

MUL:
无符号乘法, 将SRC与ac相乘, 使用AL, AX或EAX, 并将结果扩容一倍分别保存在AX, DX:AX, EDX:EAX中, 具体使用的ac有SRC的长度决定

IMUL:
有符号乘法, 具体操作与MUL相同

其中使用MUL会将SRC解释为无符号数, 而IMUL会将SRC解释为有符号数, 所以混用这两个会出大问题

乘法指令对于符号位的影响:
乘法指令对于处CF&OF外的flag无定义(即使用乘法指令后其他位不确定)

  • 对于MUL: 当乘积的高位部分全为0(即乘积没有超出原大小), CF=OF=0, 否则CF=OF=1
  • 对于IMUL: 如果乘积的高位是低位的符号拓展, 则CF=OF=0, 否则CF=OF=1

注意: CF和OF一起变

IMUL的双, 三操作数:

IMUL允许使用双, 三操作数, 因为这种指令不会像无操作数的版本对结果进行拓展, 所以必须确保结果不会溢出, 而单操作数保证不会溢出, 具体使用:

IMUL REG, SRC		;执行效果: REG=REG*SRC , 其中REG不扩展
IMUL REG, SRC, IMM	;执行效果: REG=SRC*IMM , 其中REG不扩展
除法指令

注意乘法和除法都不可以使用立即数

DIV SRC		(unsigned divide)
IDIV SRC	(signed divide)
;SRC可以采用除立即数以外的任何一种寻址方式

注意: 除法指令的SRC不可以使用立即数

除法指令与乘法指令相似, 同样也是将ac除以SRC, 但是会窄化ac(乘法指令是扩展ac), 其中SRC最少为16位, 所以经常需要对SRC进行扩展
对于AC的大小选择根据SRC而定, AC的大小为SRC的两倍, 即当SRC为8bit时, 使用AX, 并将结果保存在AL中

除法指令需要注意的是余数的保存:

  • 当SRC为8bit时, 商保存在AL, 余数保存在AH
  • 当SRC为16bit时, 商保存在AX, 余数保存在DX
  • 当SRC为32bit时, 商保存在EAX, 余数保存在EDX

除法指令对所有的FLAGS都无定义(即使用后所有位都不确定)

逻辑指令

这部分的功能与高级语言相同

逻辑运算指令
AND		DST, SRC	;逻辑与
OR		DST, SRC	;逻辑或
NOT		OPR			;逻辑非
XOR		DST, SRC	;异或
TEST	OPR1, OPR2	;测试

这几个指令(除TEST)都是将结果保存在第一个操作数中
并且, 指令的操作数必须有一个在寄存器中, 而其他的可以使用任意一种寻址方式

AND:

AND <寄存器/存储单元>,<立即数/寄存器/存储单元>

指令对标志位的影响:
指令执行后 CF 和 OF 置零,AF无定义
PF=1 结果操作数中1的个数为偶数时置1
PF=0 结果操作数中1的个数为奇数时置0

OR:

OR <寄存器/存储单元>,<立即数/寄存器/存储单元>

指令对标志位的影响:
令执行后 CF 和 OF 置零,AF无定义。
PF=1 结果操作数中1的个数为偶数时置1
PF=0 结果操作数中1的个数为奇数时置0

NOT:

NOT <寄存器/储存单元>

对OPR取非, 并将结果存在OPR中
不允许使用立即数(就一个操作数TM怎么用立即数)

对FLAGS标志寄存器无影响

XOR:

XOR <寄存器/存储单元>,<立即数/寄存器/存储单元>

指令对标志位的影响:
令执行后CF 和 OF 置零,AF无定义。
PF=1 结果操作数中1的个数为偶数时置1
PF=0 结果操作数中1的个数为奇数时置0

TEST:
对两个操作数进行AND操作, 但是不保留结果, 仅仅是操作FLAGS的标志位, 而后可通过检验FLAGS标志位查看结果

指令对标志位的影响:
令执行后 CF 和 OF 置零,AF无定义。
PF=1 结果操作数中1的个数为偶数时置1
PF=0 结果操作数中1的个数为奇数时置0

位测试并修改指令

没啥用, 先放着

位扫描指令

没啥用, 先放着

移位指令
SHL OPR, CNT	(shift logical left)			;逻辑左移
SHR OPR, CNT	(shift logical right)			;逻辑右移
SAL OPR, CNT	(Shift arithmetic left)		;算术左移
SAR OPR, CNT	(shift arithmetic right)		;算术右移
ROL OPR, CNT	(rotate left)					;循环左移
ROR OPR, CNT	(rotate right)				;循环右移
RCL OPR, CNT	(rotate left through carry		;带进位循环左移
RCR OPR, CNT	(rotate right through carry	;带进位循环右移
SHLD DST, REG, CNT	(shift left double)		;双精度左移
SHRD DST, REG, CNT	(shift right double)		;双精度右移

其中:

  • CNT为count, 表示移位次数, 可以是8bit立即数(0~255), 或者是存在CL中的数(只能是这两个的一种)

    但是, 在8086中, 立即数只能是1, 而其他移位量需要先丢到CL寄存器中, 才能用于移位

  • OPR可使用处立即数以外的任何寻址方式(立即数无法作为有效位置保存结果)

具体操作:
在这里插入图片描述
在这里插入图片描述
其中:

  • SAR算数右移为执行逻辑右移后, 在最高位填入原先最高位的值
    (相当于不改变最高位)
  • ROL循环左移&ROR右移仅仅将被移出位存入CF并补充到移位序列的末尾(即最高位复制到进位标志位和最低位)
    RCL & RCR而带进位循环左移&右移为将CF位也加入移位的序列(CF相当于中间缓冲)

对FLAGS的操作:

  • 当CNT为1时, 对OF位有影响(最高位改变时OF置1, 否则置0), 否则OF位无定义
  • 循环移位指令不影响其他标志位
  • 移位指令影响SF & ZF & PF 位(根据移位后寄存器内的结果设置), 而AF位无定义

串处理指令

串指令用于处理存储在存储器里的数据串, 具体可理解为字符串, 字符数组这一类的东西, 类似于C++中的字符串处理函数等等

前排提醒:

串处理指令使用的默认寄存器:
SRC string: DS:SI
DST string: ES:DI
AC: AL 或 AX

所以使用串处理指令之前需要保证DS & ES都储存了对应字符串的段地址

所以串处理指令的操作数仅仅向编译器提供了类型, 以便编译器选用合适的指令和每次操作后指针的偏移量

方向标志在串处理指令中的作用:
主要是方向标志位DF的作用:

  • DF=1:每次操作后使SI和DI减小
  • DF=0:每次操作后使SI和DI增大

变更方向标志的两条指令:

CLD:该指令使DF=0 clear DF
STD:该指令使DF=1 set DF

通常需要在串指令之前做的操作:

  1. 把存放在数据段中的源串首地址(如反向传送则应是末地址)放入源变址寄存器中;
  2. 把将要存放数据串的附加段中的目的串首地址(或反向传送时的末地址)放入目的变址寄存器中;
  3. 把数据串长度放入计数寄存器;
  4. 建立方向标志
  5. 而后再开始串指令操作
MOVS	串传送
CMPS	串比较
SCAL	串扫描
LODS	从串取
STOS	存入串
INS		串输入
OUTS	串输出
;配合使用的指令前缀:
REP		string_primitive		重复
REPE/REPZ		相等/为零则重复
REPNE/REPNZ		不相等/不为零则重复

串处理指令基本都不改变FLAGS标志寄存器

REP:

  • REP是串处理的核心, 建立并简化了循环, 如果没有REP等循环操作, 串处理指令仅仅执行一次

  • 串处理操作中的循环操作, 与手写循环相比更为简单, 重复执行相应的串操作直到计数寄存器为0为止

  • 可以使用的指令为:
    上头除CMPS和SCAS, 因为这两个需要利用其判比条件
    (这俩是REPE/REPZ, REPNE/REPNZ专用)

  • 使用CX(16bit)或ECX(32bit)作为计数器
    具体的选用根据地址长度(16bit/32bit)而定(自动判断)

具体执行的操作:

Yes
No
if countReg==0
退出
countReg--
执行后面的串处理指令

MOVS:串传送指令
用于将已存在的串传送到指定位置, 类似于MOV, 不过可以联用REP, 更加方便

MOVS	DST, SRC
MOVSB				;byte
MOVSW				;word
MOVSD				;double

后三种格式明确指明了操作的字节长度, 但第一种必须在操作数中向编译器指明
不同的字节长度对应了SRC与DST在每次操作后的偏移量: 字节±1, 字±2, 以此类推
其中, 加减操作的决定有FLAGS的DF方向标志位决定(具体操作看上头)

MOVS的寻址方式是编译器默认的(使用两个隐含的寻址方式, 具体看上头), 两个操作数只是提供给编译器做类型检查用

不改变FLAGS标志寄存器

STOS:存入串指令
将一个值连续的存储到一段存储器中, 即相当于在一个数组中存入相同的值
常用STOS指令在初始化某一缓冲区

;根据DST自动判定类型以选用合适的长度
STOS	DST	
;指定字节长度
STOSB
STOSW
STOSD

该指令把AL、AX或EAX的内容存入由目的变址寄存器指向的附加段的某单元中,并根据DF的值及数据类型修改目的变址寄存器的内容(自动偏移)

不改变FLAGS标志寄存器

LODS从串取指令
从指定数据串中连续读取内容, 通常不与REP联用, 即很少循环这个指令

LODS	SRC
LODSB
LODSW
LODSD

该指令把由SRC指向的内容送到AL、AX或EAX中,并根据方向标志和数据类型修改源变址寄存器的内容(自动偏移)
指令允许使用段跨越前缀来指定非数据

不改变FLAGS标志寄存器

与IO相关的串处理指令:

;串输入指令
INS		DST, DX
INSB
INSW
INSD
;串输出指令
OUTS	DX, SRC
OUTSB
OUTSW
OUTSD

其中端口号储存在DX中, 同样也是根据方向标志和数据类型修改源变址寄存器的内容(自动偏移)

REPE/REPZ & REPNE/REPNZ:
当相等/为零时重复串操作
当不相等/不为零时重复串操作

配合CMPS和SCAS指令使用, 不同的指令对应不同的跳出条件

具体执行的操作:

Yes
No
CX==0或ZF==1
退出
CX--
执行后头的串指令

即: 当比较结束时, 只有两种情况:

  • CX=0, ZF=1
  • CX!=0, ZF=0 表明字符串存在不相同的字符

CMPS:串比较指令

CMPS	SRC, DST
CMPSB
CMPSW
CMPSD

指令把由SRC指向的数据段中的一个字节、字或双字与由DST所指向的附加段中的一个字节、字或双字相减,但不保存结果,只根据结果设置FLAGS:
指令对条件码的影响:
CF=1 二进制减法运算中最高有效位向高位有借位(被减数小于减数,不够减的情况)
CF=0 二进制减法运算中最高有效为向高位无借位(被减数〉=减数,够减的情况)
OF=1 两数符号相反(正数-负数,或负数-正数),而结果符号与减数相同。
OF=0 同符号数相减时,或不同符号数相减,其结果符号与减数不同

SCAS串扫描指令
用的不多, 先放着


与转移地址有关的寻址方式

注意, 本部分的OPR地址中, 以立即数作为位移量不可以单独出现, 需要和基址或变址一同出现

如:

MOV 	BX, [1000H]	;这个是上头的寻址方法, 可以使用

;但是这3个就都会报错, 不可以直接使用光秃秃的立即数作为位移量
JMP 	SHORT	[1000H]
JMP 	NEAR PTR[1000H]
JMP 	WORD PTR[1000H]
;除非使用符号地址, 或是加上基址或变址
JMP 	SHORT		FF
JMP 	NEAR PTR	FF[10H]
JMP 	WORD PTR	FF
JMP 	WORD PTR	[BX+100H]

本部分主要是使用JMP等转移指令指令修改IP寄存器, 指向指定语句的地址, 使程序跳转到指定的语句执行

前排提醒: 几种寻址方式提供的都只有一个OPR, 真正区别的是前头的操作符

段内直接寻址

这玩意用的多 …程序简单, 无需使用其他的寻址方式

特征: 跳转的指针只有一个位移量, EA=IP+位移量
操作符NEAR PTR, SHORT
在这里插入图片描述
执行后: IP=IP+位移量

;格式:
JMP	位移量
;例如:
JMP	NEAR PTR PTOGIA 
JMP	SHORT QUEST
;其中PROGIA和QUEST都是转向的符号地址
段内间接寻址

特征: EA=REG或MEM (某一寄存器或储存单元的内容)
操作符WORD PTR

JMP BX
JMP WORD PTR[BP+TABLE]
段间直接寻址

特征: 提供了一个转向段地址, 和一个偏移地址
操作符FAR PTR
执行后: CS= 偏移地址的段地址(转向段地址), IP=偏移地址
在这里插入图片描述

JMP FAR PTR NEXTROUTINT
段间间接寻址

特征: 用储存器中的两个相继字(组成Double Word), 来取代IP和CS的内容
操作符DWORD PTR
其中: IP=前两个字节, CS=后两个字节
其中, 储存单元的寻址可以使用处立即数和直接寄存器之外的所有方式
在这里插入图片描述

JMP DWORD PTR[INTERS+BX]
JMP DWORD PTR[EDI]

控制转移指令

各种转移指令, 结合指令寻址方式, 在各个语句之间进行跳转

无条件跳转指令

相当于C++中的goto, 100%跳转

通用格式:

JMP	(跳转指令)跳转地址OPR

其中OPR通常直接使用符号地址(即相当于标识符)
而转移方式的名称都与对应的指令寻址方式相关, 直接可以想到

段内直接短转移
采用段内直接寻址方式

JMP	SHORT OPR

执行的操作:(IP)<–(IP)+8位位移量
386及其后继机型则为:(EIP)<–(EIP)+8位位移量
如操作数长度为16位,则还需(EIP)<–(EIP)AND 0000FFFFH

段内直接近转移
采用段内直接寻址方式

这里与短转移的不同主要是: 操作数的地址为16位以上

JMP NEAR PTR OPR

执行的操作:(IP)<–(IP)+16位位移量
386及其后继机型为:(EIP)<–(EIP)+32位位移量
如操作数长度为16位,则(EIP)<–(EIP)AND 0000FFFFH

段内间接近转移
采用段内间接寻址方式

之前使用的都是相当于立即数的符号地址, 而这里使用的是正经的寻址方式
其将OPR的地址传给IP

JMP FAR PTR OPR

执行的操作:(IP)<–(EA)
386及其后继机型则为:(EIP)<–(EA)
如操作数长度为16位,则(EIP)<–(EIP)AND 0000FFFFH

段间直接远转移
采用段间直接寻址方式

这里使用直接寻址的方式, OPR通常直接使用符号地址

JMP DWORD PTR OPR

(IP)<–OPR的段内偏移地址
(CS)<–OPR所在段的段地址

386后序机型:(其实就是多个拓展区)
(EIP)<–OPR的段内偏移地址
(CS)<–OPR所在段的段地址
如操作数长度为16位,则(EIP)<–(EIP)AND 0000FFFFH

段间间接远转移
这里与上头的类似, 使用处立即数和寄存器之外的任何寻址方式

JMP DWORD PTR OPR

执行的操作:
(IP)<–(EA)
(CS)<–(EA+2)
386及其后继机型则为:
(EIP)<–(EA)
(CS)<–(EA+4)
如果操作数长度为16位,则(EIP)<–(EIP)AND O00FFFFH


条件转移指令

注意与if相反, 条件转移指令满足条件跳转

下头东西多, 所有冗余的记其中一个即可(达到应用的地步), 其他的保证看得懂就好

1、根据单个条件标志的设置情况转移
这组包括10种指令。它们一般适用于测试某一次运算的结果并根据其不同特征产生程序分支作不同处理的情况

快速浏览大表:
在这里插入图片描述

具体判定条件:

  1. JZ(或JE)(Gump if zcro,or cqual)结果为零(或相等)则转移。
    格式:JZ(或JE)OPR
    测试条件:ZF=1

  2. JNZ(或 JNE)(Gump if not zero.or not equal)结果不为零(或不相等)则转移。
    格式:JNZ(或JNE)OPR
    测试条件:ZF=0

  3. JS(jump if sign)结果为负则转移。
    格式:JS OPR
    测试条件:SF=1

  4. JNS(jump if not sign)结果为正则转移。
    格式:JNS OPR
    测试条件:SF=0

  5. JO(jump if overflow)溢出则转移。
    格式:JO OPR
    测试条件:OF=1

  6. JNO(Gump if not overflow)不溢出则转移。
    格式:JNO OPR
    测试条件:OF=0

  7. JP(或JPE)(jump if parity,or parity even)奇偶位为1则转移。
    格式:JP(或JPE)OPR
    测试条件:PF=1

  8. JNP(或JPO)(jump if not parity,or parity odd)奇偶位为0则转移。
    格式:JNP(或JPO)OPR
    测试条件:PF=0

  9. JB(或JNAE,或JC)(Gump if below,or not above or equal,or carry)低于,或者不高于或等于,或进位为1则转移。
    格式:JB(或JNAE,或JC)OPR
    测试条件:CF=1

  10. JNB(或JAE,或INC)Xiump if not blow,or above or equal,or not cary)不低于,或者高于或等于,或进位为零则转移。
    格式:JNB(或JAE,或JNC)OPR
    测试条件: CF=0

最后两种指令在这一组指令中可以只看作JC和JNC,它们只用CF的值来判别是否
转移。

2. 比较两个无符号数,并根据比较的结果转移

本部分的几个核心字母(与无符号数有区分):

  • B: below
  • A: above
  • E: equal

快速浏览大表:
在这里插入图片描述
具体判定:

  1. JB(或JNAE,或JC)低于,或者不高于或等于,或进位位为1则转移。

  2. JNB(或JAE,或JNC)不低于,或者高于或等于,或进位位为0则转移。,的
    以上两种指令与(1)组指令中的9和10两种完全相同。

  3. JBE(或JNA)(jump if below or equal,or not above)低于或等于,或不高于则
    转移。
    格式:JBE(或JNA)OPRIO三并加离的。
    测试条件:CFVZF=1

  4. JNBE(或JA)(jump if not below or equal,or above)不低于或等于,或高于则
    格式:JNBE(或JA)OPR格式:JNBE(或JA)OPR
    测试条件:CFVZF=0

3. 比较两个带符号数,并根据比较结果转移

本部分的几个核心字母(与无符号数有区分):

  • L: less
  • G: greater
  • E: equal

在这里插入图片描述

  1. JL(或JNGE)(jump if less,or not greater or equal) 小于,或者不大于或等于则转移
    格式:JL(或JNGE)OPR格式:JL(或JNGE)OPR
    测试条件:SFVOF=1

  2. JNL(或JGE)(jump if not less,or greater or equal)不小于,或者大于或等于则转移
    格式:JNL(或JGE)OPR格式:JNL(或JGE)OPR
    测试条件:SFVOF=0

  3. JLE(或JNG)(jump if less or equal,or not greater)小于或等于,或者不大于则转移
    格式:JLE(或JNG)OPR格式:JLE(或JNG)OPR
    测试条件:(SFVOF)VZF-=1crcster)不小于或等于,或者大于则

  4. JNLE(或 JG)(Gump if not less or cqual,or greater) 不小于等于, 或者大于则转移。
    格式:JNLE(或JG)OPR格式:JNLE(或JG)OPR
    测试条件:(SFVOF)VZF=0

4. 测试CX或ECX的值为0则转移指令
这一块就这两个指令

  1. JCXZ(jump if CX register is zero)CX寄存器的内容为零则转移。
    格式: JCXZ OPR
    测试条件:(CX)=0

  2. JECXZ (jump if ECX register is zero) ECX寄存器的内容为零则转移
    格式:JECXZ OPR
    测试条件: (ECX)=0

条件设置指令

暂时没啥用, 先放着

循环指令:

此为更为简单的实现循环的指令:

LOOP(loop)循环
LOOPZ/LOOPE(loop while zero,or equal)当为零或相等时循环
LOOPNZ/LOOPNE(loop while nonzero,or not equal)当不为零或不相等时循环

格式;

LOOP OPR
LOOPZ(或LOOPE)OPR
LOOPNZ(或LOOPNE)OPR
  1. LOOP循环指令。
    格式:LOOP OPR
    测试条件:(Count Reg)=0

  2. LOOPZ / LOOPE 当为零或相等时循环指令。
    格式:LOOPZ(或LOOPE)OPR
    测试条件:ZF=1且(Count Reg)=0

  3. LOOPNZ / LOOPNE 当不为零或不相等时循环指令。
    格式:LOOPNZ(或LOOPNE)OPR
    测试条件:ZF=0且(Count Reg)=0

这三条指令的执行步骤是:

Yes
No
执行完成, 回到Loop
CX--
满足跳出条件?
JMP OPR
退出循环
其他指令

上述说明中的Count Reg:

  • LOOP: 在实地址模式下使用CX寄存器, 保护模式下使用ECX寄存器
  • LOOPW:在任何模式下都会使用CX寄存器作为计数器
  • LOOPD:在任何模式下都会使用ECX寄存器作为计数器

子程序:

子程序相当于函数, 集成分割一定的功能
(子程序的定义在下头的PROC伪操作中)
主要使用两个指令:

CALL	DST
RET		
RET		EXP

CALL & REP 指令的功能与JMP无条件跳转相似, 但其会操作堆栈来修改IP寄存器以实现不同的地址转移与返回, 可以理解为:
call指令,相当于

CALL 	ADDRESS
;相当于:
push	IP 		;具体应该说是call下面一行的ip
jmp 	ADDRESS
;*********************
ret		;指令,相当于
pop IP

ret num 	;带参数的ret, n为立即数
;相当于:
POP 	IP
ADD 	SP, 2
ADD 	SP, num	;最后参数的作用!

CALL指令具体执行的操作对程序员不可见
几种不同的CALL具体过程如下:

<1> ret
用栈中的数据改动IP的地址,从而实现近转移
( ip ) = ( (ss)*16+ sp )
( sp ) =( sp ) + 2
相当于pop ip

<2>retf
用栈中的数据来改动CS以及IP的值,实现段间转移
( ip ) = ( (ss)*16+ sp )
( sp ) =( sp ) + 2
( cs ) = ( (ss)*16+ sp )
( sp ) =( sp ) + 2
相当于
Pop ip
Pop cs

<3> call xxx(行号)
先把当前IP压栈,然后跳转,相当于实现近转移
( sp ) = ( sp ) – 2
( (ss)*16+ sp ) = ( ip )
( ip ) = ( ip ) + 16位位移
相当于:
Push ip
Jmp near ptr xxx(行号)

<4>call far ptr
把CS。IP压栈处理,然后实现跳转,相当于段间转移。远转移
( sp ) = ( sp ) – 2
( (ss)*16+ sp ) = ( cs )
( sp ) = ( sp ) – 2
( (ss)*16+ sp ) = ( ip )
(cs) = 当前行号段地址
(ip) = 当前行号偏移地址
相当于:
Push cs
Push ip
Jmp far ptr xxx

<5> call reg(16bit)
跳转到16位寄存器上中存储的地址
( sp ) = (sp) – 2
( (SS)*16 + (sp) ) = (IP)
(IP) = ( 16bit Reg )
相当于:
Push IP
Jmp 16bit Reg

<6> call word ptr 内存单元地址
相当于
Push IP
Jmp word ptr 内存单元地址
如:call word ptr ds:[0]

<7> call dword ptr 内存单元地址
相当于
Push cs
Push ip
Jmp dword ptr 内存单元地址
比如:jmp dword ptr DS:[0];

CALL指令结合之前的几种转移地址的寻址方式使用

外部设备操作:

外部设备与主机(CPU和存储器, 键盘, 显示器等)的通信是通过外设接口进行的,每个接口由一组寄存器组成:
– 数据寄存器:存放要在外设和主机间传送的数据。
– 状态寄存器:保存外部设备或接口的状态信息。
– 命令寄存器: CPU给外设或接口的控制命令通过此寄存器送给外部设备。

实际上外部设备的操作就是操作这些个寄存器

DOS功能调用:

DOS功能调用是DOS操作系统为用户提供的许多功能子程序,可以实现输入输出(相当于标准输入输出)

基本的调用方式是:

  • 设置调用所需的参数(其实就是寄存器);
  • 功能号送AH寄存器;
  • 用INT 21H来调用(INT为interrupt, CPU中断)
键盘输入:

功能号01
功能:从键盘输入一个字符
入口参数:无
出口参数:键入字符的ASCII码存AL寄存器
实例:

MOV AH , 01
INT 21H
显示输出:

功能号02
功能:将一个字符送显示器显示
入口参数:显示字符的ASCII码存DL寄存器
出口参数:无
实例:

MOV DL , ’A’
MOV AH , 02
INT 21H
字符串输入:

功能号0A
功能:从键盘输入一个字符串
入口参数:存放输入字符串的数据区地址存DX寄存器
出口参数:字符串的长度和每个字符的ASCII码存入数据区的相应位置
例如:

;数据段定义字符串缓冲区如下(都是伪操作):
maxlen db 16			;最大字符数(包括\n)
actlen db ?
string db 16 dup(?)
;输入字符串的指令如下:
lea dx, maxlen		;注意送DX的是maxlen, 不是string (而读取时是string)
mov ah, 0ah
int 21h

注意:
最后存入的字符串中还有其他东西:

  • 第1个字节: 最大字符数,由程序员给出(如上, 通常和字符串内存申请定义在一起)
  • 第2个字节: 实际输入字符数,自动填入
  • 第3个字节开始: 字符串按字节存入缓冲区
  • 最后结束字符串的回车0DH也要占用1个字节
    在这里插入图片描述

所以在这之后如果需要读取字符串, 则需要跳过前两个字节

字符串输出:

功能号09
功能:将一个字符串送显示器显示
入口参数:显示字符串的首地址存入DX寄存器
出口参数:无
例如:

;数据段定义字符串如下:
string db ‘hello,world!$’	;最后的$相当于C原生字符串最后的\0, 起到终止符的作用
;输出字符串的指令如下:
lea dx, string			;注意这里就直接是string了
mov ah, 09h
int 21h

Chapter IV .asm程序格式:

各种伪操作:

汇编中の几种指令:

  • 指令(之前Ch.3中的都是这玩意):
    在程序运行期间由计算机执行
  • 伪操作(相当于预处理指令):
    又称伪指令,在汇编程序对源程序汇编期间由汇编程序处理的操作
  • 宏指令(就是C++中的宏):
    在汇编程序对源程序汇编期间由汇编程序展开宏操作

指令集的选择:
默认为只适用8086指令集, 如需其他的, 在汇编程序开头加一个就好:
在这里插入图片描述

段定义伪操作:

段在汇编中的作用相当于C++中的函数, 即各个段互相组合拼凑成了.asm程序

格式:

segment_name SEGMENT
. . .
segment_name ENDS
  • 段定义中的内容:
    对于数据段、附加段和堆栈段来说,一般是存储单元的定义、分配伪操作;
    对于代码段则是指令及伪操

ASSUME伪操作:

明确段和段寄存器的关系

ASSUME的具体作用:

要用assume把段跟段寄存器对应起来的原因是原来的DOS找到的空闲内存的地址不是固定的,无法找到一个地址在任何时候都是空闲的。于是DOS需要可以重定位的程序,而当时的定位方式就是设置段寄存器的值使该程序能在可分配(空闲)的内存中可用。那就需要知道某个段被重定位时候需要修改哪个段寄存器的值才能正确执行。assume提供这种段和重定位代码时需要对应修改的寄存器的关系给编译器,编译器再这个信息写到二进制文件中去。比如DOS下的exe程序记录在文件头中。

ASSUME伪操作格式:

ASSUME segreg: seg_name, …
  • 段寄存器名(segreg)必须是CS、 DS、 ES或SS
  • 段名(seg_name)必须是由SEGMENT定义的段中的段名

例如:

data segment
. . .
data ends

code segment
assume cs: code , ds: data
start:

mov ax , data
mov ds , ax
. . .
mov ax, 4c00h 	;这两条指令是DOS的返回指令, 使用完DOS后通常(基本)需要返回程序
int 21h

code ends
end start

MODEL伪操作:

为存储模型の定义与简化段定义伪操作, 想偷懒时用这个方法,
格式:

.MODEL memory_model [ , model options]
  • 说明:
    用来表示存储模型,即说明程序在内存中如何安放各个段。

有7种存储模型:

  • Tiny: 所有数据和代码都放在一个段内
  • Small: 数据和代码各自放在一个64KB段内,最常用的一种模型
  • Medium: 代码使用多个段,数据合并成一个64KB的段组
  • Compact: 代码放在一个64KB的代码段内,数据可放在多个段
  • Large: 代码和数据都可用多个段
  • Huge: 与Large模型相同,差别是允许数据段的大小超过64KB
  • Flat: OS/2或其它保护模式的操作系统下允许使用32位偏移量

使用了MODEL后的各个段名:

由于使用MODEL后每个段都是匿名的, 所以需要特殊的使用方法
在这里插入图片描述
例如:

.model small
.stack 100h
.data		;使用时, 直接加个点就好(segment需要使用start和end)
array db 0, 1, 2, 3, 4, 5, 6, 7, 8
.code
start:
mov ax, @data	;@data为提取data段首地址
mov ds, ax
…
mov ax, 4c00h	;这两条指令是DOS的返回指令, 使用完DOS后通常(基本)需要返回程序
int 21h
end start

Mnemonics伪操作

用于数据定义与储存器分配(就比如上头的申请数组的伪操作)
其实就是创建数组和变量用的

  • 格式:
[Variable]Mnemonic Operand1, … ,Operandn[;Comments]

说明:

  1. Variable:非必须, 作为符号地址使用,其值为操作数第1个字节的偏移地址, 类型为Mnemonics, 即每个元素的长度(相当于不同类型的指针), 所以对其进行算数运算也和指针一样
  2. Comments:非必须, 分号开始, 为注释部分, 可有可无,说明该伪操作的功能
  3. Mnemonics: 为助记符, 常用的有以下几种类型
    在这里插入图片描述
  4. Operand:为存入申请的内存的初始值
    操作数可以是常数、表达式、字符串,也可以为?,单纯申请存储空间,不存入数据
    操作数可以使用复制操作符(dup)来复制某个或某些操作数

例如:

data_byte db 10,3*20,10h,?
data word dw 100,100h,-5,?
message db 'HELLO'

;使用dup(即duplicate缩写)重复申请一定数量的内存(根据Mnemonics决定)
;dup具体的语法就是下头的:
array1 	db 2 dup(0,1,2,2)
array2 	db 100 dup(?)
;注意dup操作可以嵌套
array3	db 100 dup(0,2 dup(1,2),0,3)

注意: variable符号地址的使用:
只能将符号地址传递到对应类型的寄存器中:

oper1 db 1 , 2
oper2 dw 1234h , 5678h

;如这两个都是错误的
mov ax , oper1+1 	;为8bit
mov al , oper2 		;为16bit

如果需要强制使用这种方法, 需要使用ptr类型限定符

PTR强制转换:

汇编里面 ptr 用来临时指定类型
可以理解为,ptr是临时的类型转换,相当于C语言中的强制类型转换

格式: type PTR variable
其中, type可以是BYTE 、 WORD 、 DWORD 、FWORD 、 QWORD、 TBYTE

其作用相当于C++中的*解引用
具体是将从给定的地址指向的储存空间中的数据解释为相应类型, 所以:

  • 当取出数据时使用PTR, 就指明按何种类型取出

    MOV 	AX, WORD PTR[BX]
    ;将[BX]指向的储存空间解释为WORD PTR, 从而从其中取出16bit的数据存入AX中
    
  • 当存入数据时使用PTR, 就指明了按何种类型存入

    MOV 	WORD PTR[BX], 100H	
    ;将[BX]指向的储存空间解释为WORD, 从而存入100H
    

详细解答:

mov cl,byte ptr [eax] 
;[eax]是将eax中的值解释为一个地址
;而后byte ptr是将这个地址指向的储存空间中储存的值解释为byte
;而后MOV这个byte类型的值到cl中
mov cx, word ptr [eax] 
;word ptr是将这个地址指向的储存空间中储存的值解释为word
mov ecx, dword ptr [eax] 
;DWORD ptr是将这个地址指向的储存空间中储存的值解释为Double Word

例如:

oper1 db 1 , 2
oper2 dw 1234h , 5678h
. . .
mov ax , word ptr oper1+1 	
;(ax)=3402h 取的是oper1的第二个元素, 并将其解释为word
mov al , byte ptr oper2 	
;(al)=34h	取得是oper2的第一个元素, 并将其解释为byte

label伪操作

用于定义变量variable的类型属性,
前头的ptr限定符只是一次性的临时解释, label相当于是永久改变了标签的属性

格式: name label type

例如:

word_array dw 50 dup(?)
word_array label byte	;;此后word_array相当于db 100dup(?)

$伪操作:

$表示的是地址计数器的值
在汇编程序MASM对源程序汇编的过程中,使用地址计数器( location counter )保存当前正在汇编的指令的偏移地址(相当于自动控制地址计数器)

具体使用:

array dw 1 , 2 , $+4 ,3 ,4 , $+4	

效果如下:
0078H的值为0078(当前地址计数器的值)+4

在这里插入图片描述

ORG伪操作:

用来设置当前地址计数器的值
如不希望连续分配内存时可以使用这个玩意:

格式: ORG constant expression

例如:

VECTORS SEGMENT
ORG 10		;调整地址计数器的值为10D, 即0AH
VECT1 DW 47A5H ;偏移地址为0AH
ORG 20		;调整地址计数器的值为20D, 即14H
VECT2 DW 0C596H ;偏移地址为14H
. . .
VECTORS ENDS

EVEN伪操作

作用是使下一个变量或指令开始于偶数字节地址
此为对齐操作

例如:

DATA_SEG SEGMENT
EVEN		;啥也不用加, 就一个EVEN
WORD_ARR DW 100 DUP(?)
. .
DATA_SEG END

ALIGN伪操作:

作用是使下一个变量或指令开始于2n
也是对齐操作
格式: ALIGN boundary 其中, boundary必须是2的幂
如:

.DATA
ALIGN 4
ARRAYDD 100 DUP(?)

基数控制伪操作:

汇编程序默认的数为十进制,(其实就是编写指令时用的所有数字都是10进制的)
当使用其它基数表示的常数时,需要使用标记:

  1. 二进制: B
  2. 十进制: D
  3. 十六进制: H,如果第一个字符是字母时,应在其前面加上数字0
  4. 八进制: O
  5. 字符串可以看成串常数,可以用单引号或双引号括起来,如‘ABCD’

过程定义伪操作PROC:

用于定义子程序(就是函数)

标准格式:

label PROC [attributes] [USES reglist], parameter_list
	...
label ENDP
  • label: 作为符号地址保存子程序的段地址
    与其他标识符的定义有相同的要求
  • PROC: 伪操作标志
  • ENDP伪操作结束标志
  • attributes: 子程序属性, 具体可以是以下任意内容
[distance] [langtype] [visibility] [prologuearg]

在这里插入图片描述

但是通常使用的只有distance:

  1. NEAR:
    提供段内调用
    当子程序与调用点定义在同一个段中时使用
  2. FAR:
    提供段间调用
    当子程序与调用点定义在不同的段中时使用
    但是当定义在同一个段中时也可以使用
    所以这是一个通用的选择

注意这两个控制的都是RET指令的返回方式


汇编语言指令格式

这部分除了name比较容易迷惑以外, 其他都好, 就直接抄PPT了

汇编语言源程序中的每个语句可以由4项组成,格式如下:

[ name ] operation operand [ ; comment]
;name: 		标号变量, 常用与跳转与循环
;operation: 	指令, 伪操作, 宏指令
;operand:		寄存器, 标号, 变量, 常数, 表达式
;comment: 		注释

例如:

string 	db ‘hello’
start: 	mov al,string+2
		jz match
match: 	lea si,string

name 名字项

源程序中可使用下列字符表示名字:
字母: a ~ z
数字: 0 ~ 9
特殊符号: ? . @ _ $

  • 说明:
  1. 除数字外,其它所有字符都可以放在源语句的第一个位置
  2. 名字中如果用到 . ,则必须是第一个字符
  3. 汇编程序能识别31个字符
  • 名字项可以作为标号或变量
  • 名字项表示本语句的符号地址
  • 可有可无,当需要用符号地址来访问该语句时才出现
name作为标号时:
  • 标号的定义:
    标号在代码段中定义,后面跟着冒号:
  • 标号的使用:
    在转移指令中出现, 或作为过程名(子程序名)定义,在CALL指令的操作数字段出现.二者均表示转向地址
  • 标号的段地址:
    定义标号所在的段的起始地址,此值必须在段寄存器CS中
  • 标号代表的偏移量:
    从段的起始地址到定义标号的位置之间的字节数
  • 标号的类型:
    NEAR 为段内引用, FAR 为段外引用
name作为变量时:
  • 变量在数据段(DATA SEGMENT)或附加段(EXTRA SEGMENT)中定义
    经常在操作数字段出现
  • 变量的段地址:
    定义变量的段的起始地址,此值必须在段寄存器CS中
  • 变量代表的偏移量:
    从段的起始地址到定义变量的位置之间的字节数
  • 变量的类型:
    定义该变量所保留的字节数

operation操作项

  • 指令:汇编程序MASM将其翻译为机器指令
  • 伪操作:汇编程序MASM将根据所要求的功能进行
    处理
  • 宏指令:宏展开

operand操作数项:

  • 操作数项由一个或多个表达式组成,多个操作数之间用逗号分开
  • 对于指令,一般给出操作数地址
    对于伪操作或宏指令,给出所要求的参数
  • 可以是常数、寄存器、标号、变量或表达式
operand作为表达式时:
  • 表达式是常数、寄存器、标号、变量与一些操作符相结合的序列
  • 分为: 数字表达式和地址表达式
  • 常用的操作符:
    (1) 算术操作符
    (2) 逻辑与移位操作符
    (3) 关系操作符
    (4) 数值回送操作符
    (5) 属性操作符
operand含有算数操作符时:
  • 包括: + - * / mod
  • 可用于数字表达式或地址表达式中
  • 当用于地址表达式时,运算结果要与明确的物理意义
    经常使用的是:地址 + 数字量 或 地址 – 数字量
例程:

要求把首地址为BLOCK的字数组的第6个字中存储的数据传送到DX寄存器

MOV DX,BLOCK+6*2 	;直接寻址
;或:
LEA BX,BLOCK+6*2
MOV DX,[BX] 		;寄存器间接寻址
;或:
MOV BX,6*2
MOV DX,BLOCK[BX] 	;寄存器相对寻址
;或:
LEA BX,BLOCK
MOV SI,6*2
MOV DX,[BX][SI] 	;基址变址寻址

数值回送操作符:

至今没有用到过, 仅仅记录PPT的内容

作用是把一些特征或存储器地址的一部分作为数值回送

TYPE

格式: TYPE expression
功能:

  1. 如果表达式是变量,则汇编程序回送该变量的以字节数表示的类型:
    DB为1, DW为2, DD为4, DF为6,DQ为8,DT为10
  2. 如果表达式是标号,则汇编程序回送代表标号类型的数值: NEAR为 -1, FAR为-2
  3. 如果表达式是常数,则回送0

例如:

.DATA
ARRAY DW 1,2,3

ADD SI , TYPE ARRAY
;等效于:
ADD SI, 2

LENGTH

对于变量中使用DUP的情况,将回送分配给该变量的单元数,其它情况回送1
格式: LENGTH variable

例如:

FEES DW 100 DUP(?)

MOV CX , LENGTH FEES
;等效于:
MOV CX , 100
ARRAY DW 1,2,3

MOV CX , LENGTH ARRAY
;等效于:
MOV CX , 1

SIZE

用于回送分配给该变量的字节数,此值是LENGTH值和TYPE值的乘积
格式: SIZE variable

例如:

MOV CX , SIZE FEES
;等效为:
MOV CX , 200

MOV CX , SIZE ARRAY
;等效为:
MOV CX , 2

OFFSET:

用于汇编程序回送变量或标号的偏移地址值
格式: OFFSET variable或label
例如:

MOV BX , OFFSET oper1
;该指令与下面指令是等价的:
LEA BX , oper1

SEG:

与OFFSET类似, 但是回送变量或标号的段地址值
格式: SEG variable 或 label
例如:

MOV BX , SEG oper1



本部分记录上机细节问题

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值