Bonky
Neither beliver nor reject anything, because any other person has rejected of believed it. Heaven has given you a mind for judging truth and error, Use it.
By Thomas Jefferson

微机原理 – 指令系统

什么是指令

一条指令通常组成为 操作码 [操作数],[操作数],其中靠近操作码的操作数是目标操作数,而后面那个是源操作数。当然,操作数的可以有,可以没有。操作数包括立即数,寄存器和内存。其中立即数只能作为源操作数。

寻址方式

寻址方式一共包括八种,其中可以分为三大类:立即寻址,寄存器寻址和存储器寻址:

寻址方式

指令系统

image-20200321143211242

下面主要是复习下前面学过的指令~

传送指令

数据传送指令

包括以下指令:

  • 通用数据传送指令 MOV
  • 堆栈操作指令 PUSH, POP
  • 交换指令 XCHG(exchange)
  • 查表指令 XLAT(translate)
  • 字位扩展指令 CBW(Change Byte to Word), CWD(Change Word to Double word)

通用数据传送指令 MOV

一般数据传送指令 MOV:将源操作数移动到目的操作数。需要满足下面几点:字长必须相同;不允许同时为存储器操作数(因为CPU对内存操作要么读,要么写,不能既读又写);不允许同时为段寄存器;不能把立即数移到段寄存器里; IP 和 CS 不作为目标操作数,FLAGS一般也不作为操作数在指令中出现。

堆栈操作指令 PUSH, POP

主要原则:先进后出,以字为单位。低位存储的是低8位,高位存储的是高8位。即1234H在堆栈段看是3412。

操作指令包括:PUSH OPRD(把OPRD压入堆栈区),POP OPRD(出栈的数送到OPRD中)。注意这里操作数不能是立即数,而且POP操作不能接CS。

交换指令 XCHG

交换两个存储单元的内容。

指令格式为:XCHG MEM/REG,MEM/REG(注意至少有一个要为寄存器,且寄存器都不能是段寄存器)

查表指令 XLAT

XLAT指令无操作数。用BX的内容代表表格首地址,AL内容为表内位移量,然后将BX+AL所指单元的内容送AL。

字位扩展指令 CBW, CWD

将符号数的符号位扩展到高位。

指令包括:CBW(将AL内容扩展到AX),CWD(将AX内容扩展到DX, AX)

地址传送指令

主要是取偏移地址指令LEA(Load Effective Addres), LDS(Load pointer using DS), LES(Load pointer using ES)。

取偏移地址指令 LEA, LDS, LES

将变量的16位偏移地址写入到目标寄存器。源操作数必须是一个存储器操作数。

指令格式:LEA REG,MEM。是把MEM的偏移地址送到寄存器REG中。值得注意的是,LEA BX, [AX]实际上和MOV BX, AX是一样的作用。

当然还有LDS和LES,均用于将一个32位的远地址指针写入到目标寄存器。格式是:LDS|LES REG, MEM,他们都是把偏移地址送到寄存器REG,不同的是一个是把段地址送到DS,一个是送到ES中。

标志传送指令

  • FLAGS和AH之间的操作LAHF(Load AH from Flags), SAHF(Store AH into Flags)
  • FLAGS和堆栈段的操作PUSHF(Push flags onto stack), POPF(Pop flags off stack)

FLAGS和AH之间的操作 LAHF, SAHF

作用:LAHF, SAHF都无操作数。LAHF将FLAGS的低8位装入AH,同理SAHF是把AH加载到FLAGS的低八位中。同理PUSHF和POPF~

输入输出指令

包括输入指令IN(从端口到累加器)和输出指令OUT

输入指令和输出指令 IN, OUT

指令格式:IN acc, PORTOUT PORT, acc(注意这里acc为AL,AX)

根据端口地址码的长度,指令具有两种不同的端口地址表现形式:直接寻址和间接寻址。端口地址为8位时,指令中直接给出8位端口地址;端口地址为16位时,指令中的端口地址必须由DX指定。

算术指令

加法指令

主要有三种:普通加法指令ADD,带进位位的加法指令ADC(Add with Carry),加1指令INC(Increase)。

普通加法指令 ADD

指令格式:ADD OPRD1, OPRD2。ADD指令的执行对全部6个状态标志位都产生影响(CF, SF, AF, ZF, PF和OF)

带进位位的加法指令ADC

唯一和ADD的区别是,加法运算的时候包括了CF符号位OPRD1+OPRD2+CF -> OPRD1。所以在直接使用ADC一般先使用CLC先清零CF。

自增指令 INC

格式:INC OPRD 其中操作数不能是段寄存器和立即数。

减法指令

有这么几种:普通减法指令SUB,考虑借位的减法指令SBB(Sub with Borrow),减1指令DEC(Decrease),比较指令CMP(Compare),求补指令NEG(Negative)。

普通减法指令 SUB

格式:SUB OPRD1, OPRD2

考虑借位的减法指令 SBB

进行的操作是OPRD1-OPRD2-CF -> OPRD1

自减指令 DEC

格式:DEC OPRD 其中操作数不能是段寄存器和立即数。

求补指令 NEG

格式:NEG OPRD 其中操作数是8/16位寄存器或存储器操作数。其进行的操作是用零减去操作数,只要操作数不是0的话,那么CF会变为1。

当指定的操作数的值为80H(-128)或为8000H(-32768),则执行NEG指令后,结果不变,但OF置1,其它情况下OF均置0(因为正数最大127或者32767)

比较指令CMP

格式:CMP OPRD1, OPRD2。其实就相当于进行了一次减法,指令执行的结果不影响目标操作数,仅影响标志位。

比如两个无符号数的比较:若AX≥BX,CF=0;若AX<BX,CF=1;若AX=BX,ZF=1。

如果是带符号数的话,那么我们需要判断OF和SF的值。OF和SF状态相同,AX>BX;OF和SF状态不同,AX<BX。当然等于的时候只要ZF=1。

乘除法指令

乘法指令 MUL, IMUL

一个是无符号数乘法,而另外一个是符号数乘法。

格式:MUL|IMUL OPRD。注意操作数不能为立即数,而且乘法指令结果存储地址和操作数的位数相关。当操作数为一个字的话,存储结果会存到DX和AX;如果是一个字节的那么就只会存到AX中。

除法指令 DIV, IDIV

注意除法指令要求被除数是除数的双倍字长。

格式:DIV|IDIV OPRD,若操作数是字节数执行的是AX/OPRD,AH放余数,AL放商;如果是字的话执行的是DXAX/OPRD,DX放余数,AX放商(高位放余数,低位放商)

逻辑运算指令

基本上操作数的要求和MOV相同,其中NOT的操作数不能是立即数。

除NOT以外,其余指令的执行都会影响除AF外的5个状态标志;无论执行结果任何,都会使标志位OF=CF=0。NOT运算指令的执行不影响标志位。

与操作 AND

这里主要讲下AND的一些应用:

  • 使目标操作数的某些位不变,某些位清零:AND AL,0FH
  • 在操作数不变的 情况下使CF和OF清零:AND AX,AX

或操作 OR

  • 使目标操作数的某些位不变,某些位置1:OR AL,0FH
  • 在操作数不变的 情况下使CF和OF清零:OR AX,AX
  • 将数字转换为Ascii码:OR AL, 30H

非操作 NOT

操作数按位取反再送回原地址,指令的执行对标志位无影响。

异或操作 XOR

两操作数相“异或”,结果送目标地址。

测试指令 TEST

执行与运算,但运算的结果不送回目标地址,但会改变标志位。比如,对于DATA,当第3位是1的时候,DATA自增1。这里我们可以使用TEST DATA, 08H,如果第三位为0的时候,ZF=1,否则为0,然后这时候我们再加1即可。

移位操作指令

包括算术移位SAL, SAR(Arithmatic Shift Left/Right)和逻辑移位SHL, SHR(Shift Left/Right)。以及循环移位ROL, ROR(不带进位,Rotate Left/Right),RCL, RCR(带进位,Rotate Left/Right with Carry)。

对于非循环指令,左移可实现乘法运算,右移可实现除法运算(算术用于有符号数,逻辑用于无符号数)

对于所有的移位操作指令,指令的目标操作数为被移动对象,源操作数为移动次数。移动移动1位时由指令直接给出;移动两位及以上时,移位次数必须由CL指定。(即指令源操作数只能是1或CL)

算术左移和逻辑左移 SAL, SHL

其实作用是一样的。移出去的那一位移到CF上,然后右端补0。

image-20200512120122748

逻辑右移 SHR

image-20200512120224282

算术右移 SAR

就是符号位不进行改变,然后其它位右移(当然原来的符号位在高位的第一位和第二位)

截屏2020-05-12 下午12.03.56

循环移位指令 ROL, ROR, RCL, RCR

主要的应用如下:

  • 用于对某些位状态的测试;
  • 高位部分和低位部分的交换;
  • 与非循环移位指令一起组成32位或更长字长数的移位。

串操作指令

针对数据块或字符串的操作,可实现存储器到存储器的数据传送。指令的执行需要确定:串所在的区域,串的首地址(原串、目标串起始地址),串长度(大小),串的操作方向。

源串偏移地址由SI指定,一般存放在数据段,允许段重设。目标串偏移地址由DI指定,必须在附加段串长度值由CX指定操作方向由DF标志位决定,DF=0,增地址方向,DF=1,减地址方向。

Quesiton 什么是段重设?就是把段设置为别的段,偏移地址不改变。比如MOV操作数默认为数据段(DS),可以通过以下重设到附加段,MOV AX,ES: [1200H]

重复前缀

  • 无条件重复:
    • REP:当CX=0时,重复结束。
  • 条件重复:
    • REPZ/REPE:CX=0或ZF=0,则重复执行结束。
    • REPNZ/REPNE:CX=0或ZF=1,重复结束。

串操作指令

image-20200514092212303

串传送指令 MOVS

将源数据串传送到目标地址,常与无条件重复前缀连用,格式包括三种:

  • MOVS OPRD1, OPRD2,这种很少用,只用于段重设的情况。
  • MOVSB,按字节传送。
  • MOVSW,按字传送。

下面是一个简单的程序:

串比较指令 CMPS

用于实现两个数据串的比较,目标串减去源串,结果不写回目标地址,常与条件重复前缀连用。同样地,有三种格式:

  • CMPS OPRD1, OPRD2
  • CMPSB,按字节比较。
  • CMPSW,按字比较。

串扫描指令 SCAS

常用于在指定存储区域中寻找和累加器相同(AX或者AL)某个关键字,其实和CMPS差不多,也是两个相减。

  • SCAS OPRD,操作数是目的操作数。
  • SCASB
  • SCASW

串装入指令 LODS

其实和MOVS相同,只不过目的操作数不是内存,而是累加器。所以,LODS通常不喝重复前缀连用(会覆盖累加器)。用于将内存某个区域的数据串依次装入累加器,以便显示或输出到接口。

  • LODS OPRD,这里操作数是源操作数。
  • LODSB,默认DS:SI
  • LODSW

串存储指令 STOS

常用于将内存某个区域置同样的值,把累加器的内容送入内存DS:SI中。有下面三种形式。

  • STOS OPRD
  • STOSB
  • STOSW

串操作指令应用注意事项

  • 需要定义附加段,目标操作数必须在附加段。
  • 需要设置数据的操作方向,通过确定DF的状态。
  • 源串和目标串指针分别为SI和DI,串长度值必须由CX给出。
  • 只有串操作可以加前缀,注意重复前缀的使用方法。
  • 传送类指令前加无条件重复前缀,串比较类指令前加条件重复前缀,但前缀不影响ZF状态。

控制指令

程序控制类指令以“隐含”的方式修改CS和IP ,以实现控制程序走向的目的( Intel指令集不允许由指令直接修改CS和IP),默认是每次执行的时候IP加1。通过修改IP或CS和IP,实现程序的三种基本控制结构:顺序,分支,循环。

转移指令

通过修改指令的偏移地址或段地址及偏移地址实现程序的转移。

无条件转移指令 JMP

可以实现在当前代码段内或段间转移。

  • 无条件段内转移:目标地址是16位偏移地址。
    • 段内直接转移:转移的目标地址由指令直接给出。格式是JMP Label
    • 段内间接转移:转移的目标地址存放在某个16位寄存器或存储器的某两个单元中,格式是 MOV BX,1200H; JMP BX
  • 段间直接转移:目标地址为32位,包括段地址和偏移地址。由于是32位,所以只能由4个内存单元组成。
    • 段间直接转移:JMP FAR Label
    • 段间间接寻址:JMP DWORD PTR[BX]

条件转移指令

转移范围特别的小,只能转移-128~+127。

image-20200514101305319

循环控制指令

以当前IP为中心的-128~+127范围内循环,循环次数由CX寄存器指定。

无条件循环指令 LOOP

格式:LOOP LABEL

条件循环指令 LOOPZ, LOOPNZ

先使CX-1,再根据CX中的值及ZF值来决定是否继续循环。

  • LOOPZ ,继续循环的条件:CX≠0,且ZF=1
  • LOOPNZ,继续循环的条件:CX≠0,且ZF=0

过程调用

用于调用一个子过程,子过程执行结束后要返回原调用处,必须保护返回地址。一般步骤如下:

  1. 保护断点:将调用指令的下一条指令的地址(断点)压入堆栈
  2. 获取子过程的入口地址:子过程第1条指令的偏移地址
  3. 执行子过程:功能实现,参数的保存及恢复
  4. 恢复断点,返回原程序:将断点偏移地址由堆栈弹出

包括段内调用和段间调用:

  • 段内调用:被调用程序与调用程序在同一代码段,调用前只需保护断点的偏移地址。格式是:CALL [NEAR] PROC
  • 段间调用:格式是:CALL FAR PROC

子程序的最后一条指令必须是RET

中断指令

某种异常或随机事件使处理器暂时停止正在运行的程序,转去执行一段特殊处理程序,并在处理结束后返回原程序被中断处继续执行的过程。

中断与过程调用都是从一个正在执行的过程转向另一个过程(处理程序), 并在执行 完后返回原程序继续执行,但还是有区别的:

  • 中断是随机事件或异常事件引起,调用是事先已在程序中安排好;
  • 调用指令在指令中直接给出子程序入口地址,中断指令只给出中断向量码,入口地址则在向量码指向的内存单元中。
  • 调用可以是近过程调用或远过程调用,中断处理程序均为远过程(所以既保护偏移地址也保护段地址)
  • 响应中断请求不仅要保护断点地址,还要保护 FLAGS内容

中断指令的格式是:INT n,其中n是中断类型码,范围是0 〜 255。因为偏移地址和段地址一共32位,所以需要四个字节单元,存放中断服务子程序入口地址的单元的偏移地址为n×4。中断返回的话使用的是IRET

中断指令的执行过程如下:

  1. 将FLAGS压入堆栈;
  2. 将INT指令的下一条指令的CS、IP压栈;
  3. 由n×4得到存放中断向量的地址;
  4. 中断向量(中断服务程序入口地址)送CS和IP寄存器;
  5. 转入中断服务程序。

处理器控制指令

这类指令用来对CPU进行控制,如修改标志寄存器,使CPU暂停,使CPU与外部设备同步等。这些指令都没有操作数。

image-20200514103042324

汇编程序编写

操作数

  • 取值运算符
    • OFFSET:取得其后变量或标号的偏移地址
    • SEG:取得其后变量或标号的段地址
  • 属性运算符 PTR:用于指定其后存储器操作数的类型,例如MOV BYTR PTR[BX],12H

伪指令

数据定义指令

用于定义数据区中变量的类型及其所占内存空间大小。包括DB,DW,DD,DQ(4 Words=8 Bytes),DT(10 Bytes)。一个例子是:DATA1 DB 11H,22H,33H,44H

重复操作符 DUP

当同样的操作数重复多次时,可以使用重复操作符。格式如下:

如:M1 DB 10 DUP(0)

随机值 ?

例:MEM1 DB 34H, ’A’, ?DW 20 DUP (?)

调整偏移量伪指令 ORG

默认情况下,程序或变量在逻辑段中的起始偏移地址为0。利用ORG,我们可以规定程序或变量在逻辑段中的起始地址,例如:

符号定义伪指令 EQU

类似于高级语言中的宏定义。用符号名取代后边的表达式,不可重新定义。例如:CONSTANT EQU 100。EQU定义的符号,不占用内存空间,编译的时候替换。

段定义伪指令

说明逻辑段的起始和结束。

设定段寄存器伪指令 ASSUME

说明所定义逻辑段的性质,格式如下:ASSUME 段寄存器名:段名[,段寄存器名:段名,…]

结束伪指令

表示源程序结束,格式:END [标号]

过程定义伪指令

用于定义一个过程体:

宏命令伪指令

当源程序中需要多次使用同一个程序段时,可以将该程序段定义为一个宏:

下面是宏调用的使用:

image-20200514105552243

BIOS、DOS功能调用

20150518003828416

20150518003833661

Share

You may also like...

发表评论

电子邮件地址不会被公开。 必填项已用*标注