• X86汇编指令


    8086CPU地址总线宽度为20, 也就是说一个内存物理地址是5位,内存地址空间为1Mb;数据总线为16位;寄存器为16位。

     

    16位结构的CPU包括以下特性:

    1,运算符最多处理16位数据。

    2,寄存器最大宽度为16位。

    3,寄存器与运算器之间的通路是16位。

     

    这里就出现了一个问题,如果由16位推出20位的物理地址呢?

    所以就出现了段的概念:

    一个物理地址由段地址和偏移地址构成,即物理地址=段地址*16 + 偏移地址

    也可以说段地址左移4位,然后我们可以推出偏移地址最大为2的16次方,即16Kb.

    这里我们要强调一个概念:

    CPU是死的,一点都不智能,对于它本身而言,并不知道什么段的概念,段只是用来让我们编程者使用的,我们可以自己定义一个段的开始(定义CS或者DS),然后利用偏移地址灵活执行我们写的指令,这样我们就可以避免指令在内存空间的互相掩盖。

     

    8086CPU不支持将一个数据直接写到段寄存器中去。

     

    8086的栈操作是以字为单位的,这里我们还是要强调一个概念:

    CPU是死的,它才不知道栈的概念呢,栈是我们编程者自己在数据段里开辟,然后我们用pop,push汇编指令操作,一切都是我们编程者操作出来的。

    CPU是如何知道栈的地址呢?:

    CPU才不会知道呢,是我们编程者自己首先定义好了一个栈段,然后我们将这个栈段地址赋值到SS栈段寄存器中去,同时初始化SP寄存器。这样以后我们pop或者push一个字时,sp加2或者减2。还是那句话,都是我们编程者实现栈的功能,让SS和SP永远指向栈顶。

     

    只有bx,si,di,bp这四个寄存器可以进行内存单元寻址的,即[bx]这样语法操作的,其中bx,bp这两个寄存器不能同时出现,如[bx,bi],还有si,di这两个寄存器也是不能同时出现的。

    bp寄存器隐形的段地址是ss。如我们这样操作[ba],那么是通过SS段地址寻址内存单元的。

     

    寄存器寻址几种方式:

    1,直接寻址:[idata]    idata 为立即数

    2,寄存器间接寻址: [bx] , 默认段地址是DS

    3,寄存器相对寻址:[bx+idata] ,默认段地址是DS

    4,基址变址寻址:[bx+si], 默认段地址DS

    5,相对基址变址寻址:[bx+si+idata], SA是DS

     

    指令要处理的数据长度问题:

    对于这个问题,我们要根据编程者自己的选择的编译器而定,像我们在windows下用的都是Masm风格的汇编编程,在linux上则是使用AT&T风格的。

    这里是就masm风格而言,当我们操作一个寄存器时,我们根据寄存器的长度来确定数据的长度,比如:

    mov ax, [0]; 这里我们就是要操作的数据长度是16位的。

    但是当没有寄存器的时候,我们就要通过标记符来确定了。格式如下:

    操作符 X ptr data, data; 这里X ptr依据(X可以使word或者byte)来指定操作数据长度。

    像push和pop就不需要指定了,他们两默认的是一个字。

     

    div除法指令:被除数一般默认在AX或者BX中,或者就是32位的存储在AX,BX中,然后除完之后,余数在DX中,商在AX中

     

    伪指令:

    db:定义一个字节

    dw:定义一个字

    dd:double defined word 定义两个字节

    dup:与db,dw,dd配合使用,表示复制的意思,比如: db 3 dup(0);

     

    转移指令:

    修改CS,IP或者仅仅修改IP的指令统称为转移指令,比如:

    jmp data,只修改ip

    jmp SA:EA 修改cs和IP

     

    offset操作符:

    获取一个标号的偏移地址,比如:

    start:

        mov ....

    s:

        mov ax, offset s;   此时相当于(mov ax, 1)

     

    jmp指令:

    jmp idata; jmp sa:ea; jmp short 标号(在段内短跳转);jmp far ptr 标号(段间跳转);jmp reg; jmp word ptr 内存单元地址(这里是将那个内存单元里的值给IP,段内转移); jmp dword ptr 内存单元地址(段间跳转,以内存单元地址所存的两个字的高地址为段地址,低地址为偏移地址);

    jcxz指令:

    当cx=0 跳转

     

    loop指令:

    相当于执行cx自减一,并前cx不等于0就跳转,跳转位移是8位,也就是说偏移地址在-128~127之间。

     

    ret 指令:

    有两条指令:ret,retf指令

    ret指令相当于pop IP

    retf    相当于连续执行    pop IP; pop CS 两条指令

     

    call指令:

    格式如下:

    call 标号;    将当前IP压栈,然后跳到标号处执行(改变IP值)

    call far ptr 标号;    段间转移

    call reg(16位);    跳到IP=寄存器中的内容

    call word ptr 内存单元地址;    跳到内存单元给定的值

    call dword ptr 内存单元地址;  相当于执行:push CS; push IP; jmp dword ptr 内存单元地址;

     

    call 和 ret一般配合使用,相当于调用了一个子函数。

     

    mul乘法指令:

    和div一样,用到AX,DX寄存器

     

    标志寄存器:

    里面存储的信息可以称为PSW(程序状态字),包括这些状态:

    第六位,ZF:标明执行结果是否为0;

    第二位, PF:奇偶标志位:执行结果中如果1的个数为偶数的话,则置1

    第七位,SF:符号标志位:结果为负数则为1

    第零位,CF:进位借位标志符:当最高有效位需要像更高位进位或者借位则置1(对于无符号位数)

    第十一位,OF:溢出标志位(对于有符号位数)

    第十位:DF:方向标志位:在串处理中,控制每次si,di的增减。DF为0,则每次si,di递减

     

     

    串传送指令:

    movsb;

    相当于执行如下指令:mov es:[di], byte ptr ds:[si]; 如果DF=0 ,则后续为:inc si; inc di; 

    如果DF=1,则后续为: dec si; dec di;

    movsw;

    每次传送的是一个字

     

    一般我们传送一个串的话选择这样的命令:

    rep movsb; 

    相当于:

    s: movsb;

    loop s;  依据CX来判断

     

     

    pushf; popf;  将标志寄存器压栈或者弹出

     

     

     

     

     

     

     

    条件转移指令:

    je: equal,如果等于了,则ZF=0,依ZF作为判断                     jne:   no equal

    jb: below,如果第一个数小于第二个数,则CF=1                     jnb; no below

    ja: above                                                                              jna:

  • 相关阅读:
    爱她就用python给她画个小心心 ♥(ˆ◡ˆԅ)
    用python画小猪佩奇(非原创)
    (解释文)My SQL中主键为0和主键自排约束的关系
    (细节)My SQL中主键为0和主键自排约束的关系
    My SQL常用操作汇总
    博客搬运同步至腾讯云+社区声明
    mysql在ubuntu中的操作笔记(详)
    vim编辑器操作汇总
    linux常用命令汇总
    在python中单线程,多线程,多进程对CPU的利用率实测以及GIL原理分析
  • 原文地址:https://www.cnblogs.com/Karma-wjc/p/4157416.html
Copyright © 2020-2023  润新知