iBinary 阅读(103) 评论(0)

         汇编指令详解

8080指令详解

1.8086系统下,Inter指令系统共有117条指令(看似很多,分一下类)

1.数据传送类指令(专门传送数据的)

2.算术运算类指令(加减乘除的运算的)

3.位操作类指令(或  异货 与 ….)

4.串操作类指令 (内存拷贝,内存连续地址拷贝的操作)

5.控制转移类指令(跳转,比如C语言的Goto)

6.处理机控制类指令(计算机的待机 ,重启 等等,让CPU待机睡眠的指令)

                      学习指令的注意事项

1.指令的功能,也就是这个指令可以实现什么操作.通常的话,指令就是指令功能英文单字的缩写方式,比如 mov传送指令,其实全程叫做 move

2.指令支持的寻址方式,也就是说这个指令中的操作数决定了采用何种寻址方式,寻址方式决定了指令的语法

3.指令对标志位的影响,这个指令执行之后,是否会对各个标志位又影响.

4.其他方面,比如使用指令的时候,是否效率高,CPU执行周期是否会长

a.这里提供一个汇编金手指 的软件,可以在今天的百度云盘连接中获取,可以很快的查询各种指令

b.也可以利用Inter手册查询指令的语法,比如昨天讲的怎么看inter手册

 

 

 

 

 

补充知识,理解reg mod mem imm accum segreg英文什么意思

英文名称

含义

Reg

寄存器的意思

Mod

寻址方式是哪一种

Mem

内存

Imm

立即数

Accum

累加寄存器(ax)

Segreg

段寄存器(ds ss es cs)

M8

代表内存8位,一个字节

M16

代表内存16位,两个字节

看一下,下面讲指令的时候会用到

例如mov指令

mov reg/mem,imm 表示指令是可以把一个立即数给 寄存器,或者内存

汇编语法则为  mov  ax,100  ,或者 mov  byte ptr[2000],10h,把10给2000取内容

为什么有byte ptr(或者 word ptr)主要为了给2000的时候,不知道读取多少给,指定方式给

 

                数据传送指令类

 

一丶通用数据传送指令

mov  指令 xchg指令 xlat指令

 

1.MOV传送指令

指令

指令支持

指令功能

MOV

1. Mov reg/mem,imm

2. Mov reg/mem/seg,reg

3. Mov reg/seg,mem

4. Mov reg/mem,seg

 

1.立即数传送给寄存器或者主存(内存)

2.寄存器传送给 段寄存器,或者内存,或者 寄存器

3.内存中的内容送给 寄存器,或者段寄存器

4.段寄存器送给寄存器或者给主存取内容给值

例如 mov byte ptr[2000],12 是把2000指向的内存地址的值

 

mov 指令传送功能图

 

 

请注意,立即数不能直接给段寄存器,都是通过中转的

mov 注意事项

1.两个操作数的类型不一致

      例如源操作数是字节,目的操作数是字,或者是相反

例如  mov al,050AH al是八位寄存器,只能接受八位,而这里是16位了

      对于存储器,和立即数同时都作为操作数的情况下,必须显示的指明是什么类型

例如 mov [2000h],12h  错误,应该写成 mov byte ptr(或者word ptr)[2000h],12h

如果只是12h,则用byte ptr,当然你也可以写成word ptr,但是修改内存的值修改后则是修改的两个字节的长度

2.两个操作数不能是存储器

      例如 mov [2000h],[2200h] 因为内存只是存储的,传送的结果要保存在寄存器当

当中,显然内存没有内置CPU,所以通过寄存器中转,所以不能直接这样    

3.小心段操作寄存器

      1.立即数不能直接给段寄存器

                 例如 mov ds,100  (比如经过寄存器的中转)

      2.不能直接改变cs段寄存器的值

           mov cs,[si] 这条指令是可以编译通过,但是运行的时候,因为你把代码段的值改了,然后CS:IP确定的吓一跳指令就会出错,比如你的这条指令下面还有个mov ax,0 当你上面改了,那么mov ax,0 永远不会执行,而你熟悉的改了cs的值可以,如果不熟悉那么代码段就被破坏,程序就会执行崩溃.

      3.段寄存器和段寄存器不能直接数据传送

           mov ds,es

2.交换指令

      xchg reg/mem/accum ,  reg/mem/accum

其实就是寄存器的值交换

例如ax = 0

       bx = 1

xchg ax,bx 那么此时ax的值就是1,bx就是0

有人说mov 指令也可以,确实是可以,但是指令周期不一样,这个比mov指令快一个

指令周期,而且还不浪费寄存器(否则需要三个寄存器完成交换)

寄存器和存储器交换

xchg ax,[2000h] 字交换,ax正好可以放下16位

      等价于 xchg [2000h],ax 这里会有人说为什么不用 word ptr说明一下

      因为后面跟着的是16位寄存器,它默认就是 word ptr了

xchg al,[2000h] 字节交换

      等同于 xchg [2000h],al 同上为什么不用byte ptr

3.xlat 换码指令(查表指令)

将bx指定的缓冲区中,al执行的位移处的一个字节数据取出赋值给al

后面没有操作数,默认操作数就是 dx和al

直接 一个 xlat即可.默认操作的就是dx 和al

但是转变成mov 相当于 改为 mov al,ds:[dx+al]

什么意思,就是dx数据里面有一块完整的内存(比如是ASCII码 a b c d)

al 就给定一个下标,然后调用xlat指令,就可以根据al的下标获取出来 abcd其中的一个

ASCII码重新放到al中

相当于数组寻址

C语言代码应该写成

char d[5] = {‘a’,’b’,’c’…};

char ch = 0;

ch =  *(d(数组首地址) * ch(下标))然后取出内容来重新给ch,看一下汇编怎么写吧

当然上面只是伪代码

 

 

第一步,使用e命令给400偏移处写入 abcdefg的字符

第二步 使用 d命令查看

 

 

第三步开始汇编,先给bx赋值偏移量为400,则 DS:[DX]可以取得61(也就是a)的编码

但是怎么取出来,要根据al给定的下标,比如al给了3,那么就是 DS:[DX+AL] 也就是取出63的编码,赋值给al

 

我这里给的是0,所以下标取内容是61

 

3.堆栈指令

首先看一下栈

 

 

SS代表栈的段地址,SP代表是栈顶 BP代表是栈底

堆栈的操作指令有两种,第一种是PUSH压栈,第二种是POP出栈

首先mov指令模拟

第一步,要压栈之前,sp需要-2,留出一个字的空间,这样才可以把字压入栈中

mov sp,bp 栈底和栈顶一样

sup sp,2   栈顶-2留出两个空间让数据压栈

mov bp sp,让栈底和栈顶一样的位置

mov word ptr [bp],1 栈底位置处压入1

出栈

mov bp,sp  首先栈顶栈底一样

mov ax,[bp] 栈底的数据给ax

add sp,2      栈顶指针移动

mov bp,sp   栈底位置和栈顶位置一样

但是事实x86提供了指令

push 和pop

push ax  把 ax压栈

pop ax  出栈的值放到ax中

push的指令

      push r16/m16/seg  (可以是16位寄存器,2个字节的内存,或者段寄存器)

pop 一样

 

标志寄存器传送指令

      1.有两对4条指令(分别对应8位寄存器,和16位寄存器)

低八位传送  LAHF 和SAHF

16位传送 PUSHF和POPF

什么意思

LAHF 代表 的意思   第一个字母 L代表load(加载的意思) AH八位寄存器AH,F是标志位

表示把 Flag标志寄存器里面的低八位标志,传送到 AH中

伪代码

      mov ah,flag(当然这是不行的,flag是16位,比如使用关键字)

SAHF   S可以理解为设置,或者保存的意思 

意思就是 AH的高八位当做标志位给Flag寄存器的标志位赋值

表示我们通过AH的值,设置flag标志寄存器

如果我们要把标志位都清零怎么办

xor ah,ah  (为什么用这个,因为这个CPU寻址的时候指令执行周期短,如果

写成 mov ah,0那么带有立即数所以比较慢,玩汇编就是要这样玩的)

SAHF 清零标志位

PUSHF 和POPF都是一样的,只不过有入栈出栈的操作,所以指令周期更长

清零

pushf   获取标志位到ax中,但是需要pop获取

pop ax

xor ax,ax  获取到了设置为0

push ax   重新压栈

popf  即可  popf设置标志寄存器,一般来说不用设置标志寄存器的高八位

下面还有一段代码

pushf          ;保存全部标志到堆栈

pop ax        ;从堆栈中取出全部标志

or ax,0100h ;设置D8=TF=1,这个地方就是对某一位设置

            ;ax其他位不变

push ax ;将ax压入堆栈

popf     ;FLAGS←AX

;将堆栈内容取到标志寄存器   

上面这个代码写调试器相当于单步代码

2.地址传送指令

LEA 有效地址传送指令  例如 LEA AX,[bp] 这样

      执行 LEA r16,mem  (具体查询 inter手册)

      上面这样写只是求出内存地址,假设bp的值是2000,则给ax是2000,而不是2000里面的内容,这样写比add快,但是真正用法不是这样的,真正用法就是求内存地址而已(注意不是求出内存单元的值,如果写成 add ax,[bp],如果是这样,那么ax的值就不是2000了,就是2000地址里面的内容了)

LDS LES指针传送指令  

LDS 改变段寄存器的,试想一下,程序一大,数据段就应该有很多,但是你不能只有一个

两个段使用的时候可以来回切换

使用 lds ax,ds:[0]

使用后会修改 ax的值和ds段寄存器的值

内存的前两个字节给 r16

内存地址+2的连个字节给ds

LES是一样的

例子

mov word ptr [3060h],0100h

mov word ptr [3062h],1450h

les di,[3060h]     ;es=1450h,di=0100h

lds si,[3060h]     ;ds=1450h,si=0100h

mem指定主存的连续4个字节作为逻辑地址(32位的地址指针),送入DS:r16或ES:r16

 

三丶输入输出指令

IN

OUT

这个指令很强大,可以直接通过端口操作硬件

比如键盘的端口是 64

我们可以利用IN从键盘中读取数据

OUT往键盘的缓冲区写入数据

当然怎么写还有看键盘的规范,寄存器呀,参数呀等等.

这个功能和驱动过保护有关,以前都说驱动过保护,就是就是玩着两个指令,

比如HOOK(钩子,不懂可以百度)你去HOOK应用层的软件,但是HOOK不了我,你HOOK的时候需要调用API,我连API都不用调用,直接在底层就操作数据了

从键盘缓冲区取一个指令

IN ax,64(端口号)

输出(伪代码,没有看硬盘厂家的标准)

OUT ax,64

IN AL,i8

;字节输入:AL←I/O端口(i8直接寻址)

IN AL,DX

;字节输入:AL←I/O端口(DX间接寻址)

IN AX,i8

;字输入:AX←I/O端口(i8直接寻址)

IN AX,DX

;字输入:AX←I/O端口(DX间接寻址)

将数据传送给外设(伪代码)


 

OUT i8,AL

;字节输出:I/O端口←AL(i8直接寻址)

OUT DX,AL

;字节输出:I/O端口←AL(DX间接寻址)

OUT i8,AX

;字输出:I/O端口←AX(i8直接寻址)

OUT DX,AX

;字输出:I/O端口←AX(DX间接寻址)

 

算术运算类指令

a.加法指令

      ADD reg,imm/reg/mem

                     ;reg←reg+imm/reg/mem

ADD mem,imm/reg

                     ;mem←mem+imm/reg

看指令就会操作,可以 立即数给寄存器,寄存器给鸡存器,地址的内容给寄存器

 

 

2.带进位的加法

      ADC reg,imm/reg/mem

       ;reg←reg+imm/reg/mem+CF

ADC mem,imm/reg

       ;mem←mem+imm/reg+CF

带进位的加法就是两数相加的时候,进位也算

比如 1234 + 4321

低八位县桑拿  34 + 21 是否进位,如果进位,假设为1

那么高八位计算的结果需要加上进位

12+43 + 1 +上面算的结果

具体自己调试一下Debug看一下

例子,双字加法,也就是32位加法,使用8086的16位寄存器完成

mov ax,4652h   ;ax=4652h

add ax,0f0f0h    ;ax=3742h,CF=1  //这里的0f0f0h 前边加0表示是16进制,比如C语言需要加上 0x

mov dx,0234h   ;dx=0234h

adc dx,0f0f0h    ;dx=f325h,CF=0

;DX.AX=0234 4652H

      +F0F0 F0F0H

      =F325 3742H

3.增量INC

      INC reg/mem

;reg/mem←reg/mem+1

很简单,寄存器,或者内存自增1

C语言中相当于i++ ++ i这种

INC bx, bx自增1

INC byte ptr[bx] 地址自增

b减法指令

      SUB reg,imm/reg/mem

              ;reg←reg-imm/reg/mem

SUB mem,imm/reg

              ;mem←mem-imm/reg

同加法一样,只不过相反

2.带借位的减法指令

       SBB reg,imm/reg/mem

              ;reg←(reg-(imm/reg/mem)-CF)

SBB mem,imm/reg

              ;mem←mem-imm/reg-CF

SBB指令将目的操作数减去源操作数,再减去借位CF(进位),结果送到目的操作数。

SBB指令按照定义相应设置状态标志

SBB指令主要与SUB配合,实现多精度减法运算

同加法一样,只不过是借位

双字减法例子

mov ax,4652h   ;ax=4652h

sub ax,0f0f0h    ;ax=5562h,CF=1

mov dx,0234h   ;dx=0234h

sbb dx,0f0f0h    ;dx=1143h,CF=1

;DX.AX=0234 4652H

      -F0F0 F0F0H

      =1143 5562H

3.减量指令DEC

      自减

DEC reg/mem

;reg/mem←reg/mem-1

INC指令和DEC指令都是单操作数指令

 主要用于对计数器和地址指针的调整

 

4.求补指令 NEG

这个指令比较重要,求一个数的补码

NEG reg/mem

;reg/mem←0-reg/mem

请注意,CPU并不会执行补码,

而是写成  SUB 0,1

用0减去操作数1,然后结果返回操作数

求补运算也可以表达成,将操作数按位取反后加1

NEG指令对标志的影响与用零作减法的SUB指令一样

跳转指令

JMP 预习

 inter手册放到连接中,需要的自己查询,只需要把上面的 (imm r16 ...这些弄明白了,那么看汇编的语法就懂了,而且有指令周期)

链接:http://pan.baidu.com/s/1b5lo9g 密码:4n5y

如有错误,希望批评改正,如果不懂,请私信留言.谢谢支持,官方论坛 www.w1x8.com希望大家可以去论坛活跃气氛