精华内容
下载资源
问答
  • AX,BX,CX,DX 寄存器

    千次阅读 2014-05-23 13:51:32
    AX(AL): (1)在乘法指令中存放乘数和乘积 ...(5)在输入/输出指令中作数据寄存器 AH:在LAHF指令中作目的寄存器 AL: (1)用在组合型BCD码的加减法调整指令中 (2)在XLAT指令中,作目的寄存器 BX: (1)在XLAT指
    AX(AL):
    (1)在乘法指令中存放乘数和乘积
    (2)在除法指令中存放被除数和商
    (3)用在非组合型BCD码运算的调整中
    (4)用在某些串操作指令(LODS,STOS,SCAS)中
    (5)在输入/输出指令中作数据寄存器
    AH:在LAHF指令中作目的寄存器
    AL:
    (1)用在组合型BCD码的加减法调整指令中
    (2)在XLAT指令中,作目的寄存器

    BX:
    (1)在XLAT指令中,作基址寄存器
    (2)还可以作为存放地址的寄存器构成寄存器间接寻址或者基址寻址

    CX:在循环指令和重复前缀中,作循环次数计数器
    CL:在移位指令中,做移位次数计数器(移位指令执行后,CL中内容不变)

    DX:
    (1)在字乘法和除法指令中,作辅助累加器(即存放乘积或被除数的高16位)
    (2)在输入输出指令中存放16位的端口地址
     
    展开全文
  • 8086汇编寄存器指令汇总

    千次阅读 多人点赞 2018-04-30 23:28:34
    1 寄存器的分类 通用寄存器: ...指令寄存器: ip 标志寄存器: FR 段寄存器: cs,ds,ss,es 2 ax,bx,cx,dx 这四个寄存器存放一般数据,你可以在里面存放任何数据,但是其用途会有不同。 AX


    1   寄存器的分类

    通用寄存器:

    ax,bx,cx,dx,(ah,al,bh,bl,ch,cl,dh,dl);

    sp,bp,si,di

    指令寄存器:

    ip

    标志寄存器:

    FR

    段寄存器:

    cs,ds,ss,es

    2   ax,bx,cx,dx

    这四个寄存器存放一般数据,你可以在里面存放任何数据,但是其用途会有不同。

    AX (Accumulator):累加寄存器,也称之为累加器;

    BX (Base):基地址寄存器,[bx]表示对bx中存放内容作为地址的内存单元进行访问

    CX (Count):计数器寄存器,例如jcxz,loop指令都是根据CX寄存器的值进行判断然后决定是否进行跳转

    DX (Data):数据寄存器,在进行32位的乘除法操作时,用它存放被除数的高16位或余数。它也用于存放I/O端口地址

    3     ah,al,bh,bl,ch,cl,dh,dl

    每个AX,BX,CX,DX都可以分为高8位,低8位,这两个8位可以单独的做位一个寄存器来使用。例如:

    MOV AH,00h

    MOV AL,FFh

    4   sp,bp,di,si

    SI (Source Index):源变址寄存器,在串处理指令中,SI作为隐含的源变址寄存器与DS联用,以确定数据段中的存储单元地址

    DI (Destination Index):目的变址寄存器,在串处理指令中,DI和附加段寄存器ES联用,以达到在附加段中寻址的目的

    SP (Stack Pointer):堆栈指针寄存器,它指定栈顶的段偏移地址,和SS决定的栈段地址,一起决定了栈顶元素的地址

    BP (Base Pointer):基指针寄存器,主要用于给出堆栈中数据区基址的偏移

    5 CS,DS,SS,ES

    CS (Code Segment):代码段寄存器,存放当前执行的程序的段地址

    DS (DataSegment):数据段寄存器,存放当前执行的程序所用操作数的段地址

    SS (StackSegment):堆栈段寄存器,存放当前执行的程序所用堆栈的段地址

    ES (ExtraSegment):附加段寄存器,存放当前执行程序中一个辅助数据段的段地址

             8086CPU的CPU内部总线是16根,但是地址总线是20根,为了达到20根线的寻址能力,就用了两个16位的地址去合成一个20位的总线。例如一个20位的地址ABCDE,可以表示为ABCD,000E,那么ABCDE =ABCD*16 + E;

    或者 ABC0 + 00DE,那么ABCDE = ABC0* 16 + DE;

    或者 AB00 + 0CDE,那么ABCDE = AB00*16 + CDE;

    或者A000 + BCDE,那么ABCDE = A000*16 + BCDE;

    从上面可以看出对于8086CPU的一个段的最大长度就是64kb了。那么所谓的段寄存器的作用就可想而知,他们就是为了提供20位地址中的段地址。

    6   IP,FLAG

    IP (Instruction Pointer):指令指针寄存器,和CS一起决定了指定的地址CS:IP

    FLAG:标志寄存器;


    <1>进位标志 CF,记录运算时最高有效位产生的进位值。 
    <2>符号标志 SF,记录运算结果的符号。结果为负时置1,否则置0。 
    <3>零标志  ZF,运算结果为0时ZF位置1,否则置0。 
    <4> 溢出标志 OF,在运算过程中,如操作数超出了机器可表示数的范围称为溢出。溢出时OF位置1,否则置0。 
    <5>辅助进位标志 AF,记录运算时第3位(半个字节)产生的进位值。 
    <6>奇偶标志 PF,用来为机器中传送信息时可能产生的代码出错情况提供检验条件。当结果操作数中1的个数为偶数时置1,否则置0。

    <7>方向标志 DF,在串处理指令中控制处理信息的方向。当DF=1时,串处理从高地址向低地址方向处理。当DF=0时,串处理就从低地址向高地址方向处理。 
    <8>陷阱标志 TF,用于调试时的单步方式操作。当TF=1时,每条指令执行完后产生陷阱,由系统控制计算机;当TF=0时,CPU正常工作,不产生陷阱。 
    <9>中断标志 IF,用于控制可屏蔽硬件中断。当IF=1时,允许8086微处理器响应中断请求,否则关闭中断。 

    //*****************************************

    看《汇编语言(第三版)》--王爽,暂时看到第11章,所以可能不太全面,接下来发现有缺漏、错误的,会继续更新修正。

    然后下面写的是部分书上的内容加上我个人的理解,正因如此,由于我知识的局限,可能会出现比较多的错误。

    前提简介:

    >寄存器是CPU中程序员可以用指令读写的部件。程序员通过改变各种寄存器中的内容来实现对CPU的控制。------《汇编语言(第三版)》第2章


    简单来说,我认为寄存器粗略看有点像控制台,用户输入命令(修改寄存器中的数据),然后操作系统执行用户的命令(CPU执行命令)。除此之外,我认为寄存器还有存储功能,能有效提高CPU运算速度(不需要再从内存读取数据)。


    既然说到寄存器控制CPU,就说说是如何控制CPU的,在之前,先简单说说一些和这个没有多大关系,但作为前提的内容(当课外阅读吧):

    前提:首先所有的程序简单点说都是由代码组成,而且不管最初是什么语言的代码,到最后都转化成二进制数字,然后其中固定的二进制数字串对应着该CPU中相应的动作(就是遇到这串二进制数字,CPU就会执行相应的操作),我们就称这样一个固定的二进制数字串为一条指令吧。然后这些指令都存储在内存中(暂时当它都存储在内存中,实际上是部分在内存,部分在磁盘),然后运行的时候,就是CPU一条一条地从内存提取指令、执行指令


    小结:看完上面,我们再来整理一下上面内容,其实与CPU相关(换句话说CPU的动作)只有最后一句,从内存提取指令,然后执行指令,好啦,问题来啦,内存那么大,CPU怎么知道从哪里提取指令?怎么提取指令?然后怎么执行指令?


    然后后面两个问题的答案和控制CPU已经没什么大关系了,因为这两个我们都不能控制(推荐看《深入了解计算机系统》),所以说到最后,我们控制CPU实际上就是控制CPU从哪里读取指令(数据)


    说到这里又来说说前提(课外阅读...),其实这个也是上面第二个问题(怎么提取指令)的答案,让我们先来看看图片(所有图片均来自《汇编语言(第三版)-王爽》)


    从内存读取数据基础版:

    1.把需要读写的内存地址传递给内存(送上地址总线)

    2.内存把相应地址的数据传递回给内存


    但由于当时条件限制,当时8086CPU是16位结构的CPU,就是说,在8086CPU内部,能够一次性处理、传输、暂时存储的信息的最大长度是16位。然后内存单元的地址在送上地址总线之前,必须在CPU中处理、传输、暂时存放,而对于16位CPU能一次性处理、传输、暂时存储16位的地址


    而实际上8086CPU外的地址总线有20位,而从内部只能传送出16位地址,这使20位地址总线不能得到充分利用因此采用由2个16位地址合成的方法形成一个20位的物理地址。至于如何合成,又来看看图片:

    物理地址=段地址x16+偏移地址

    p.s.图片中的数字是16进制,4个二进制数字等于一个16进制数字


    从内存读取数据完整版:

    1.CPU中的相关部件提供两个16位的地址:段地址,偏移地址

    2.段地址和偏移地址通过内部总线送入一个为地址加法器的部件

    3.地址加法器将两个16位地址合成一个20位的地址

    4.地址加法器通过内部总线将20位物理地址送入输入输出控制电路

    5.输入输出控制电路将20位物理地址送上地址总线

    6.地址总线将20位物理地址送到存储器

    7.存储器将相应的地址上的数据传出


    第三个前提

    有一个硬件部分叫时钟,然后在每一个时钟周期,CPU都从内存读取进一条指令


    我们现在来总结一下前面的3个前提:在每一个时钟周期,CPU根据当前的段地址和偏移地址读取一条指令进来,然后执行。(前面一大段最后总结剩下一句。。。)


    好啦,问题又来啦,CPU怎么知道当前的段地址和偏移地址是什么?说到现在,终于和我们的主题套上关系啦,对的,就是通过我们的主角---寄存器,来得知,段地址和偏移地址都有相应的寄存器进行存储,特别是要执行的指令的段地址和偏移地址更是有固定的寄存器(cs,ip)进行存储,然后CPU发出地址,就是直接把cs,ip中的数据发出去。

    寄存器:

    在8086CPU中一共有14个寄存器:

    ax,bx,cx,dx,si,di,sp,bp,ip,cs,ss,ds,es,flag

    每个寄存器都是16位,在下面对各个寄存器的逐一介绍中,根据功能或者其特点可能会将几个寄存器划分在一起介绍(所以一个寄存器可能会出现在多个地方,最后会给出总结表)

    通用寄存器(ax,bx,cx,dx)

    将它们4个划分一起是由于它们的一个共同特性:

    都可以分为独立使用的2个8位寄存器来使用

    ax:ah,al

    bx:bh,bl

    cx:ch,cl

    dx,dh,dl


    h结束的为高位,l的为低位,而且这两个8位寄存器之间相互独立,例如:ah本来为0000,0000,当你向al(其余一样)传入一个数据,但这个数据已经超8位,就假设为1,0000,0000,最后结果为ah:0000,0000,al:0000,0000。传进去的数据的最高位(超出的一位),将会被舍弃,而不是往ah进位。


    好吧,实际上除了这个,它们也就没什么其它共同特点了(读写功能,那个所有寄存器都可以干啦)。。。

    地址相关寄存器:段地址+偏移地址(cs,ss,ds,es,ip,sp,bp,bx,si,di)

    在上面的前提简介的最后一部分中,提到过段地址和偏移地址存储在寄存器中,在这部分中将会详细介绍:

    先来大体了解一下里面哪些用来存储段地址,哪些存储偏移地址:

    段地址寄存器(段寄存器):cs,ss,ds,es

    可用来存储偏移地址的寄存器:ip,sp,bp,si,di

    可以看出它们往往是成对出现的,而且可以说都是固定组合,我们就一对对来看吧:

    cs:ip:指示指令地址

    cs和ip是8086CPU中两个最关键的寄存器,它们指示了CPU当前要读取指令的地址


    在8086CPU机中,任意时刻,设cs中的内容为M,ip中的内容为N,8086CPU将从内存Mx16+N单元开始,读取一条指令执行,若该条指令不是跳转指令,则执行完后,L为刚执行完的指令的长度(所占内存单元的个数),N=N+L,然后继续读取下一条指令执行。


    换句话说,8086机中,任意时刻,CPU将cs:ip指向的内容当做指令执行,就是说假如你原来存储在某个内存单元的是数据,但是如果你的cs:ip指向了该内存单元,CPU依然会把内存单元中的内容当做指令来执行


    而且,cs,ip也是唯二的两个寄存器不能被mov指令修改,只能被转移指令修改。

    ss:sp:指示栈顶地址

    8086CPU提供了相关的指令来以栈的方式访问内存空间,也就是说,基于8086CPU编程时,可将一段内存当做栈来使用,然后这时,ss:sp指示的就是栈顶的地址


    栈中的一个单元大小是两个内存单元,就是16位,就是每次入栈,不管是8位还是16位,最后在栈中都占据16位大小也就是2个内存单元。


    在8086CPU中,任意时刻,设ss中的内容为M,sp中的内容为N,每次入栈后,N=N-2(栈底在高地址,栈顶在低地址),每次出栈后,N=N+2。当使用push,pop指令时,CPU就会按照当前ss:sp指示的位置,进行入栈,出栈操作。

    ds(es):idata(bx,bp,si,di):指示代码段地址

    这里和前面的两对都不同,它们不用一一对应,而且除了用寄存器中的数据,还可以直接用立即数来作为偏移地址。它们用于对内存单元中的内容以数据形式读取。

    使用格式为:ds(es):[idata(bx,bp,si,di)]

    p.s.接下来用sa代表段地址,表示ds或者es均可

    上面代表的是:段地址为ds中的内容,偏移地址为idata 的内存单元中的数据。

    而且还有几点必须注意:

    1.只有这4个寄存器可以作为偏移地址放进[]中,其余的寄存器如ax,cx...放进

    去就是错误的指令

    2.在[]中,这4个寄存器可以单个出现,或只能以4种组合出现:

    sa:[bx]

    sa:[bp]

    sa:[si]

    sa:[di]

    sa:[bx+si]

    sa:[bx+di]

    sa:[bp+si]

    sa:[bp+di]

    3.两个内存单元之间不可以直接使用mov进行数据传输 最后来总结一下代码段

    的合法寻址方式,也可以说是偏移地址中寄存器的组合方式:

    -----------------------------------------------------

    寻址方式名称

    sa:[idata]直接寻址

    sa:[bx]寄存器间接寻址

    sa:[bp]寄存器间接寻址

    sa:[si]寄存器间接寻址

    sa:[di]寄存器间接寻址

    sa:[bx+idata]寄存器相对寻址

    sa:[bp+idata] 寄存器相对寻址

    sa:[si+idata] 寄存器相对寻址

    sa:[di+idata] 寄存器相对寻址

    sa:[bx+si]基址变址寻址

    sa:[bx+di] 基址变址寻址

    sa:[bp+si] 基址变址寻址

    sa:[bp+di] 基址变址寻址

    sa:[bx+si+idata]相对基址变址寻址

    sa:[bx+di+idata] 相对基址变址寻址

    sa:[bp+si+idata] 相对基址变址寻址

    sa:[bp+di+idata] 相对基址变址寻址

    ---------------------------------------------------

    循环(loop)相关寄存器(cx)

    cx表示的是循环的次数,设cx中的内容为N,每执行一次loop指令后,N=N-1,当N>0时,执行跳转(继续循环),否则跳出循环,执行下一条指令(内存上向邻的下一条)。

    除法(div)相关寄存器(ax,dx)

    除法指令:

    div 寄存器

    div 内存单元

    div后面跟着的是除数,如果除数为8位,则被除数为16位,如果除数为16位,则被除数为32位;如果被除数为16位,则默认存放在ax,结果的商存放在al,余数存放ah;如果被除数为32位,则高16位存放在dx,低16位存放在ax;除得的商存放在ax,余数存放在dx

    乘法(mul)相关寄存器(ax,dx)

    乘法指令:
    mul 寄存器
    mul 内存单元
    两个相乘的数必须位数一致,如果都为8位,一个默认放在al中,结果存放在ax中;如果都为16位,一个默认存放在ax中,结果的高位存放在dx中,低位存放在ax中。

    转移指令(jcxz)相关寄存器(cx)

    格式:jcxz label
    如果cx中的内容为0,则转移到label处执行,否则执行下一条指令

    复制指令(movsb,movsw)相关寄存器(ds,si,es,di)

    格式:movsb
    功能:把源地址的单元内容复制给目的地址的单元,在每次操作完后,根据df的值控制si、di的增减
    源地址:ds:[si]
    目的地址:es:[di]
    df = 1:si、di递增
    df = 0: si、di递减

    标志寄存器(flag)

    首先,flag和其它寄存器不一样,其它寄存器是整个寄存器具有一个含义(寄存器的16个位作为一个整体)。而flag是按位起作用的,也就是说,它的每一位都有专门的含义,记录特定的信息。
    其中8086CPU的flag寄存器结构下:
    15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
    OF DF IF TF SF ZF AF PF CF

    上面空着的位表示没有使用,其中被使用的标志位的含义为:
    CF:进位值\借位值
    adc、abb指令与其密切相关
    PF:1:为偶数;0:为奇数
    AF:
    ZF:1:为0;0:不为0
    SF:1:为负;1:非负
    TF:
    IF:
    DF:1:每次操作后si、di递减;0:每次操作后si、di递增
    OF:1:溢出;0:没有溢出

    使用标志寄存器的相关指令

    cmp
    分类指令转 移 条 件说 明
    (Ⅰ)
    JZ/JEZF=1为零/相等, 则转移
    JNZ/JNEZF=0不为零/不相等, 则转移
    JSSF=1为负, 则转移
    JNSSF=0为正, 则转移
    JOOF=1溢出, 则转移
    JNOOF=0不溢出, 则转移
    JPPF=1奇偶位为1, 则转移
    JNPPF=0奇偶位为0, 则转移
    JCCF=1进位位为1, 则转移
    JNCCF=0进位位为0, 则转移
    (Ⅱ)JB/JNAE/JCCF=1低于/不高于等于, 则转移
    JNB/JAE/JNCCF=0不低于/高于等于, 则转移
    JBE/JNA(CF * ZF)=1低于等于/不高于, 则转移
    JNBE/JA(CF * ZF)=0不低于等于/高于, 则转移
    (Ⅲ)JL/JNGE(SF * OF)=1小于/不大于等于, 则转移
    JNL/JGE(SF * OF)=0不小于/大于等于, 则转移
    JLE/JNG((SF * OF) * ZF)=1小于等于/不大于, 则转移
    JNLE/JG((SF * OF) * ZF)=0不小于等于/大于, 则转移

    寄存器功能总结:

    ---------------------------------------------------------------------

    ax:

    特性:

     可被拆分为2个8位寄存器

    功能:

    (1)除法:1.被除数为16位,ax存放16位被除数,ax存放商。2.被除数为 32位,ax存放被除数的低16位,ax存放商

    (2)乘法:1.相乘的数为8位,al存放一个相乘的数,且结果存放在ax中。2.相乘的数为16位,ax存放一个相乘的数

    ,且结果的低位存放在ax中

    -----

    bx:

    特性:

    可被拆分为2个8位寄存器

    功能:

    用作对内存单元的寻址中的偏移地址

    -----

    cx:

    特性:

    可被拆分为2个8位寄存器

    功能:

    1.用来存放loop、rep的循环次数

    2.用jcxz的条件判断

    -----

    dx:

    特性:

    可被拆分为2个8位寄存器

    功能:

    (1)除法:1.被除数为16位,dx存放余数。2.被除数为32位,dx存放被除数的高16位,dx存放余数

    (2)乘法:相乘的数为16位,相乘结果的高位存放在dx中

    -----

    cs:特性:

    不能被mov修改,只有转移指令能修改

    功能:

    存放指令段地址

    -----

    es:

    特性:

    ...

    功能:

    可用作ds的替补|有多个数据段的时候,用es顶上

    -----

    ip:

    特性:

    不能被mov修改,只有转移指令能修改

    功能:

    存放指令偏移地址

    -----

    bp:

    特性:

    ...

    功能:

    存放内存单元的偏移地址

    -----

    sp:

    特性:

    ...

    功能:

    存放栈顶的偏移地址

    ----

    si:

    特性:

    ...

    功能:

    存放内存单元的偏移地址

    -----

    di:

    特性:

    ...

    功能:

    存放内存单元的偏移地址

    ---

    psw:

    ...

    -------------------------------------------------------------------------

    引子

    打算写几篇稍近底层或者说是基础的博文,浅要介绍或者说是回顾一些基础知识,

    自然,还是得从最基础的开始,那就从汇编语言开刀吧,

    从汇编语言开刀的话,我们必须还先要了解一些其他东西,

    像  CPU ,内存这些知识点还是理解深刻一点的比较好,

    所以这一篇博文就绕着 80x86  CPU 中寄存器的基础部分下手,至于其他的一些将会在后续的博文中介绍 。

    同时在这里说明一下,本篇博文介绍的算是比较详细的了,而且介绍的知识点也是比较多的,所以造成博文长度过长,

    如果有兴趣想了解这一块的话,还请自行斟酌好阅读比例,建议分 3 次以上阅览 。

            

          

    读者定位

    本博文主要将介绍的是  8086 CPU 中的寄存器,既然是 8086 CPU 寄存器简介的话,

    自然,面向的是初级一些的读者,其中不会涉及太多难点,同时,所有的介绍,我也会尽可能的从基础开始,

    然后循序渐进的介绍,同时也会尽量的将知识点介绍详细,

    介绍的过程中也会涉及到一些汇编程序代码,当然,采用的是最简单的方式介绍而已,

    本篇博文也就是回顾一些基础知识,读者主要定位于想对 8086 CPU 有所了解,

    希望对整个程序设计的底层有所了解的朋友,而且读者最好是拥有一定的计算机基础和汇编语言基础 。

               

             

    开头

    首先浅要介绍一下  Intel  CPU 的发展史吧:

    Intel CPU 系列,最初是 4 位微处理器 4004,然后到到 8 位微处理器的 8008 ,

    再到 8 微微处理器 8080,以及稍后的 16 位微处理器 8086,

    由 8086 开始,Intel 进入现在所谓的  x86  时代 。

    Intel  8086 为 16 位  CPU ,而因为在 8086 之前的 CPU 都是 8 位 CPU,这样也就造成了很多的外设也只支持 8 位,

    因此  Intel  紧接着就退出了 8 位的 8088 CPU,因此  Intel 8088 也就可以看做是 8086 的 8 位版本;

    如果是但从汇编语言的角度上来说,8086 和 8088 是没有区别的,即 8086 上跑的程序可以不加修改的移植到 8088 ,

    8088 上跑的程序也可以不加修改的移植到 8086 上,

    当然,还是有些特殊的地方是不同的,而这些基本上在这里可以忽略掉,

    在 8088  CPU 之后,Intel  又推出了  80186 ,80286 ,这两款 CPU 均是 16 位  CPU ,

    而对于 80186 来说,其与 8086 的区别可以简单的看做是 80186 多了几条指令而已,

    而 80286 则不同,80286 的地址总线数目有了变化,

    在 8086 , 8088 , 80186 上,CPU 的地址总线都是 20 根,即可最大寻址 220 即达到 1MB 的寻址能力,

    而对于 80286 CPU 来说,其地址总线数目达到了 24 根,从而最大寻址能力为 224 即 16MB,

    由于支持更多的物理内存寻址,因此 80286 便开始成为了多任务,多用户系统的核心。

    而后来,Intel  又推出了 80386 ,80386 为 32 位微处理器,Intel 80x86 家族的 32 位微处理器始于 80386;

    同时 80386 也完全兼容先前的 8086/8088,80186,80286,并且 80386 全面支持 32 位数据类型和 32 位操作,

    并且 80386 的数据总线根数和地址总线根数均达到了 32 根,从而可以最大物理寻址为 232  即 4GB 。

    而之后的 80486 也是 32 位微处理器,而后又出来了 Pentium 和 Pentium Pro 等等第五代微处理器,

    这些处理器虽然也是 32 位微处理器,但是他们的数据总线和地址总线都有所扩展,

    比如 Pentium 的数据总线达到 64 位,而 Pentium Pro 的地址总线位数达到了 36 位 。

                 

    好,关于 Intel CPU 的介绍就到这里了,下面就要开始回归中心,看 CPU 中的寄存器了,

    首先,从学习的角度来说,从 8086/8088  CPU 下手是不错的选择,而我这里选择的也是 8086 CPU 而已,

    说实在的,像 80386 CPU 我也还没有研究过,像奔腾这些,呵呵,扯更远了,

    说到底也就只能拿 8086 出来晒晒而已,当然,从 8086 开始也是学习的最佳路径 。

           

    说了这么久,到底寄存器是什么呢?其实很简单,寄存器就是个存储信息的单元或者说是器件又或者说是容器而已,

    就比如内存也是一个存储介质或者说是存储单元而已,其实寄存器从理解上来说和内存差不多,

    只不过寄存器(这里讨论的寄存器都是 CPU 中的寄存器,不包括外设上的寄存器)位于  CPU  内部,而内存位于 CPU 外部,

    而且,寄存器比内存可是珍贵得多啊,就拿内存和硬盘来比,肯定是内存在使用上珍贵得多,是 PC 中的稀有资源,

    而寄存器是 CPU 中的稀有资源,内存和寄存器相比就像硬盘和内存相比一样 。

    而对于一个汇编程序员来说,CPU 中主要可以使用的也就是寄存器而已,汇编程序员可以使用指令来读写 CPU 中的寄存器,

    从而可以实现对于 CPU 的控制,当然,不同的 CPU ,寄存器的个数和结构都是不一样的,

    比如 8086 CPU 中,寄存器的个数也就 14 个而已,

    并且 8086 CPU 中所有的寄存器的结构为 16 位,即一个寄存器中可以存放下 2B 即 2 个字节,

    而到了 80386 CPU 中,寄存器的个数也比 8086 增多了,比如在 80386 中添加了系统地址寄存器等寄存器,

    同时寄存器的结构也变了,比如在 80386 中绝大多数的寄存器为 32 位,而有些寄存器则是 16 位 。

    8086  CPU 中寄存器总共为 14 个,且均为 16 位 。

    即 AX,BX,CX,DX,SP,BP,SI,DI,IP,FLAG,CS,DS,SS,ES 共 14 个。

    而这 14 个寄存器按照一定方式又分为了通用寄存器,控制寄存器和段寄存器。

    通用寄存器:

    AX,BX,CX,DX 称作为数据寄存器:

    AX (Accumulator):累加寄存器,也称之为累加器;

    BX (Base):基地址寄存器;

    CX (Count):计数器寄存器;

    DX (Data):数据寄存器;

    SP 和 BP 又称作为指针寄存器:

    SP (Stack Pointer):堆栈指针寄存器;

    BP (Base Pointer):基指针寄存器;

    SI 和 DI 又称作为变址寄存器:

    SI (Source Index):源变址寄存器;

    DI (Destination Index):目的变址寄存器;

    控制寄存器:

    IP (Instruction Pointer):指令指针寄存器;

    FLAG:标志寄存器;

    段寄存器:

    CS (Code Segment):代码段寄存器;

    DS (Data Segment):数据段寄存器;

    SS (Stack Segment):堆栈段寄存器;

    ES (Extra Segment):附加段寄存器;

               

               

    通用寄存器

    从上面可以知道,在 8086 CPU 中,通用寄存器有 8 个,分别是 AX,BX,CX,DX,SP,BP,SI,DI ,

    至于为什么给它们取名做通用寄存器,那是因为,这些个寄存器每一个都有自己专门的用途,

    比如 CX 作为计数寄存器,则是在使用 LOOP 指令循环时用来指定循环次数的寄存器,

    如果它们每一个都只有一个专用的作用,那就它们只能称之为专用寄存器了,

    正是因为这些个寄存器还可以用来传送数据和暂存数据,所以才称它们为通用寄存器 。

    下面就按顺序来一一介绍这几个通用寄存器了:

    数据寄存器(AX,BX,CX,DX):

    数据寄存器有 AX,BX,CX,DX 四个组成,

    由于在 8086 之前的 CPU 为 8 位 CPU,所以为了兼容以前的 8 位程序,

    在 8086 CPU 中,每一个数据寄存器都可以当做两个单独的寄存器来使用,

    由此,每一个 16 位寄存器就可以当做 2 个独立的 8 位寄存器来使用了 。

    AX 寄存器可以分为两个独立的 8 位的 AH 和 AL 寄存器;

    BX 寄存器可以分为两个独立的 8 位的 BH 和 BL 寄存器;

    CX 寄存器可以分为两个独立的 8 位的 CH 和 CL 寄存器;

    DX 寄存器可以分为两个独立的 8 位的 DH 和 DL 寄存器;

    除了上面 4 个数据寄存器以外,其他寄存器均不可以分为两个独立的 8 位寄存器 ;

    注意在上面标志中的“独立”二字,这两个字表明 AH 和 AL 作为 8 位寄存器使用时,

    可以看做它们是互不相关的,也就是看做两个完全没有联系的寄存器 X 和 Y 即可,

    比如指令   MOV   AH , 12H ,CPU 在执行时根本就不会知道 AL 中是什么鬼东西,因为它只认识  AH 。

    下面给出一幅 16 位数据寄存器的结构图:

    表示 16 位 寄存器 AX 可以表示成两个 8 位寄存器,

    其中 AH 表示高位的 8 位寄存器,AL 表示低位的 8 位寄存器 。

    image

    AX 寄存器:

    如上所说,AX 的另外一个名字叫做累加寄存器或者简称为累加器,其可以分为 2 个独立的 8 位寄存器 AH 和 AL;

    在写汇编程序时,AX 寄存器可以说是使用率最高的寄存器(不过,总共才那么 14 个寄存器,哪一个不经常使用咯?),

    既然 AX 是数据寄存器的话,那么理所当然,其可以用来存放普通的数据,由于其是 16 位寄存器,

    自然也就可以存放 16 位数据,但是因为其又可以分为 2 个独立的 8 位寄存器 AH 和 AL ,

    所以,在 AH 和 AL 中又可以独立的存放 2 个 8 位的数据,

    可以有以下代码(即将 AX 当做普通的寄存器使用,即可以用来暂存数据):

    MOV AX,1234H	       ;向寄存器 AX 传入数据 1234H
    MOV AH,56H	       ;向寄存器 AX 的高 8 位寄存器 AH 中传入数据 56H
    MOV AL,78H		;向寄存器 AX 的低 8 位寄存器 AL 中传入数据 78H
    3 条语句的执行过程如下:

    而既然 AX 又被称作为累加器,自然其还有一点点特殊的地方的:

    AX 寄存器还具有的特殊用途是在使用 DIV 和 MUL 指令时使用,

    DIV 在 8086 CPU 中是除法指令,而在使用除法的时候有两种情况,即除数可以是 8 位或者是 16 位的,

    而且除数可以存放在寄存器中或者是内存单元中,而至于被除数的话,自然,应该由 AX 来代替了,

    当除数是 8 位时,被除数一定会是 16 位的,并且默认是放在 AX 寄存器中,

    而当除数是 16 位时,被除数一定是 32 位的,因为 AX 是 16 位寄存器,自然,放不下 32 位的被除数,

    所以,在这里还需要使用另一个 16 位寄存器 DX ,

    其中 DX 存放 32 位的被除数的高 16 位,而 AX 则存放 32 位的被除数的低 16 位,

    同时,AX 的作用还不仅仅是用来保存被除数的,当除法指令执行完成以后,

    如果除数是 8 位的,则在 AL 中会保存此次除法操作的商,而在 AH 中则会保存此次除法操作的余数,

    当然,如果除数是 16 位的话,则 AX 中会保存本次除法操作的商,而 DX 则保存本次除法操作的余数。

    上面介绍的是 AX 寄存器在除法操作中的应用,下面还需要介绍一下 AX 在乘法操作中的应用,

    当使用 MUL 做乘法运算时,两个相乘的数要么都是 8 位,要么都是 16 位,

    如果两个相乘的数都是 8 位的话,则一个默认是放在 AL 中,

    而另一个 8 位的乘数则位于其他的寄存器或者说是内存字节单元中,

    而如果两个相乘的数都是 16 位的话,则一个默认存放在 AX 中,

    另一个 16 位的则是位于 16 的寄存器中或者是某个内存字单元中。

    同时,当 MUL 指令执行完毕后,如果是 8 位的乘法运算,则默认乘法运算的结果是保存在 AX 中,

    而如果是 16 位的乘法运算的话,则默认乘法运算的结果有 32 位,

    其中,高位默认保存在 DX 中,而低位则默认保存在 AX 中。

    AX 寄存器在 DIV  指令中的使用:

    MOV DX,0H		;设置 32 位被除数的高 16 位为 0H
    MOV AX,8H		;设置 32 位被除数的低 16 位为 8H
    MOV BX,2H		;设置 16 位除数为 2H
    DIV BX		       ;执行计算
    4 条语句的执行过程如下:

    image

    AX 寄存器在 MUL  指令中的使用:

    MOV AX,800H		;设置 16 位乘数为 800H
    MOV BX,100H		;设置 16 位乘数为 100H
    MOV DX,0H		;清空用来保存乘法结果的高 16 位    
    MUL BX		       ;执行计算
    4 条语句的执行过程如下:

    image

           
    BX 寄存器:

    首先可以明确的是,BX 作为数据寄存器,表明其是可以暂存一般的数据的,

    即在某种程度上,它和 AX 可以暂存一般性数据的功能是一样的,

    其同样为了适应以前的 8 位 CPU ,而可以将 BX 当做两个独立的 8 位寄存器使用,即有 BH 和 BL,

    除了暂存一般性数据的功能外,BX 作为通用寄存器的一种,BX 主要还是用于其专属功能 – 寻址(寻址物理内存地址)上,

    BX 寄存器中存放的数据一般是用来作为偏移地址使用的,何为偏移地址呢?

    既然是偏移地址的话,当然得有一个基地址了,而这个基地址其实就是段地址,这里就涉及到了段寄存器,

    当然,在介绍 BX 寄存器的时候,我不会去介绍段寄存器,上面提到 BX 的主要功能是用在寻址上,

    那么,其是如何寻址的呢?

    对于寻址这个话题,我会在我的下一篇博文中作出详细的介绍,

    而这里,我只点一下,在 8086 CPU 中,CPU 是根据 <段地址:偏移地址> 来进行寻址操作的,

    而 BX 中存放的数据表示的是偏移地址的话,自然,便可以通过 <段地址:[BX]> 的方式来完成寻址操作了。

    为了介绍 BX 在寻址当中的作用,下面我给出一副示意图:

    image

    上面的示意图表示:可以令 BX = 2,然后通过 DS : [BX] 来访问到内存中段地址为 DS,且偏移量为 2 的内存单元了。

    上面介绍的这种寻址方式是 BX 在寻址中最最简单的应用了,而对于稍微复杂的寻址方式,

    还可以依赖于 SI,DI,BP 等寄存器来一起完成,当然,这会是下一篇博文将要介绍的内容了。

    BX 寄存器在寻址中的使用:

    MOV BX,5H
    MOV AH,11H
    MOV AH,[BX]		;设置 AX 的值为偏移地址为 BX 中的值时所代表的内存单元
    3 条语句的执行过程如下:

    image

    从上图可以看出,在偏移地址为 5 时的内存单元中的数据位 BBH,

    image

    而从这幅图上面就可以看出,确实通过 [BX] 找到了偏移地址为 5 处的内存单元,并且将内存单元移入了 AH 中。

                  

    CX 寄存器:

    CX 寄存器作为数据寄存器的一种呢,其同样具有和 AX,BX 一样的特点,即可以暂存一般性的数据,

    同时还可以将其当做两个独立的 8 位寄存器使用,即有 CH 和 CL 两个 8 位寄存器,

    当然,CX 也是有其专门的用途的,CX 中的 C 被翻译为 Counting 也就是计数器的功能,

    当在汇编指令中使用循环 LOOP 指令时,可以通过 CX 来指定需要循环的次数,

    而 CPU 在每一次执行 LOOP 指令的时候,都会做两件事:

    一件就是令 CX = CX – 1,即令 CX 计数器自动减去 1;

    还有一件就是判断 CX 中的值,如果 CX 中的值为 0 则会跳出循环,而继续执行循环下面的指令,

    当然如果 CX 中的值不为 0 ,则会继续执行循环中所指定的指令 。

    CX 寄存器在循环中的使用(输出 5 个白底蓝字的 A):

    MOV AX,0B800H
    MOV DS,AX		;使用 80x25 彩色字符模式,内存地址 0xB8000 - 0xBFFFFF
    MOV BX,0		;从 0xB8000 开始
    MOV CX,5H		;循环 5 次
    MOV DX,41H		;A 的16 进制为 41H
    MOV AX,01110001B	;显示白底蓝字
    s:  MOV [BX],DX	;显示 ASCII 字符
        ADD BX,1
        MOV [BX],AX	;设置字符显示属性
        ADD BX,1
    LOOP s
    语句的执行过程如下:

    image

                  

    DX 寄存器:

    DX 寄存器作为数据寄存器的一种,同样具有和 AX,BX,CX 一样的特点,即可以暂存一般性的数据,

    同时还可以将其当做两个独立的 8 位寄存器使用,极有 DH 和 DL,

    同时,DX 作为一个通用寄存器的话,自然其还有其他的用途,而关于 DX 在其他方面的用途,

    其实在前面介绍 AX 寄存器时便已经有所介绍了,

    即当在使用 DIV 指令进行除法运算时,如果除数为 16 位时,被除数将会是 32 位,而被除数的高 16 位就是存放在 DX 中,

    而且执行完 DIV 指令后,本次除法运算所产生的余数将会保存在 DX 中,

    同时,在执行 MUL 指令时,如果两个相乘的数都是 16 位的话,

    那么相乘后产生的结果显然需要 32 位来保存,而这 32 位的结果的高 16 位就是存放在 DX 寄存器中 。

    DX 寄存器在 DIV  指令中的使用(即 2293812 / 256 = 8960  余数为 52):

    MOV DX,0023H	;32 位被除数的高 16 位
    MOV AX,0034H	;32 位被除数的低 16 位
    MOV BX,100H	;16 的除数
    DIV BX  
    语句的执行过程如下:

    image
    可以看到在语句结束以后,AX = 2300H  即十进制的 8960,而 DX = 34H即十进制的 52 和我们的结果是一致的。

    DX 寄存器在 MUL  指令中的使用则各位可以参考在 AX 中 MUL 运算的使用,这里就不贴出来了。

              

    指针寄存器(BP,SP):

    BP 寄存器:

    8086  CPU 中的指针寄存器包括两个,即 SP 和 BP ,在这里呢,我先只对 BP 寄存器做介绍,

    因为 SP 寄存器实质上必须和 SS 段寄存器一起使用,所以,我将会把 SP 寄存器留到后面和 SS 段寄存器一起作介绍。

    BP (Base Pointer)也就是基指针寄存器,它和其他的几个用来进行寻址操作所使用的寄存器(还有 BX,SI,DI)没有太大的区别,

    关于 SI 和 DI 寄存器的下面请见下文。

    首先,BP 寄存器作为通用寄存器的一种,说明其是可以暂存数据的,而后,BP 又不是数据寄存器,

    也就意味着其不能分割成 2 个独立的 8 位寄存器使用,

    而后当以 […] 的方式访问内存单元而且在 […] 中使用了寄存器 BP 的话,

    那么如果在指令中没有明确或者说是显示的给出段地址时,

    段地址则使用默认的 SS 寄存器中的值(BX,SI,DI 会默认使用 DS 段寄存器),

    比如 DS:[BP] 则在这里明确给出了段地址位于 DS 中,

    所以,这里代表的内存单元即是段地址为 DS ,偏移量为 BP 寄存器中的值的内存单元,

    而如果单单是使用 [BP] 的话,则代表的内存单元是段地址为 SS,偏移量为 BP 寄存器中的值的内存单元。

    并且 BP 寄存器主要适用于给出堆栈中数据区的偏移,从而可以方便的实现直接存取堆栈中的数据,

    至于堆栈的话,会在后面的博文中介绍。

    在 8086 CPU 中,只有 4 个寄存器可以以  […]  的方式使用,这四个寄存器分别是 BX,SI,DI,BP。

    下面的  Demo  是 BX 寄存器在寻址中的使用:

    MOV BP,0
    MOV AX,[BP]         ;将 SS:[BP] 代表的内存单元移入 AX 中
    MOV AX,CS:[BP]      ;将 CS:[BP] 代表的内存单元移入 AX 中
    语句的执行过程如下:

    image

                

    变址寄存器(SI,DI):

    首先,变址寄存器和上面介绍的指针寄存器(也就是 BP 和 SP),它们的功能其实都是用于存放某个存储单元地址的偏移,

    或者是用于某组存储单元开始地址的偏移,即作为存储器指针使用,当然,由于变址寄存器和指针寄存器都是属于通用寄存器,

    所以它们也可以保存算术结果或者说是具有暂存数据的功能,但是因为它们不是数据寄存器,所以无法分割成 2 个独立的 8 位寄存器使用,

    关于变址寄存器和指针寄存器的详细使用,笔者将会在下一篇博文中作出最详细的介绍,

    SI (Source Index) 是源变址寄存器,DI (Destination Index) 即是目的变址寄存器,

    8086 CPU 中的 SI 寄存器和 DI 寄存器其实和 BX 寄存器的功能是差不多的,

    只不过 SI 寄存器和 DI 寄存器均不是数据寄存器,所以它们不能够拆分为 2 个独立的 8 位寄存器,

    而这也就是 SI 寄存器和 DI 寄存器与BX 寄存器所不同的地方,

    既然,SI,DI 两个寄存器的功能和 BX 差不多,自然,SI 和 DI 中也是可以暂存一般性数据的,

    同时,通过使用 SI 和 DI 寄存器也是可以用来完成寻址操作的。

    比如下面的代码就是可行的:

    MOV SI,0		;初始化偏移地址为 0
    MOV AX,[SI]		;将段地址为 DS 偏移地址为 SI 的内存单元中的值移入 AX 中
    MOV AX,DS:[SI]		;将段地址为 DS 偏移地址为 SI 的内存单元中的值移入 AX 中
    MOV AX,SS:[SI]		;将段地址为 SS 偏移地址为 SI 的内存单元中的值移入 AX 中
    
    MOV DI,0    		;初始化偏移地址为 0
    MOV AX,[DI]		;将段地址为 DS 偏移地址为 DI 的内存单元中的值移入 AX 中
    MOV AX,DS:[DI]		;将段地址为 DS 偏移地址为 DI 的内存单元中的值移入 AX 中
    MOV AX,SS:[DI]		;将段地址为 SS 偏移地址为 DI 的内存单元中的值移入 AX 中
            

           

    其他寄存器(CS,IP,SS,SP,DS,ES)

    由于段寄存器总是和其他一些像指针寄存器,变址寄存器,控制寄存器一起使用,

    所以在这里,我并不会单独介绍段寄存器,而是将段寄存器和一些其他的常用寄存器搭配介绍 。

    由于下面的介绍中会涉及到很多关于段和栈的概念,而段和栈的介绍又都必须关系到物理内存,

    所以在介绍段寄存器以及其他一些呈协作关系的寄存器之前,还是先来介绍一下这几个基本的概念比较好。

    8086 CPU 访问内存(物理地址):

    当 CPU 需要访问一个内存单元时,需要给出内存单元的地址,

    而每一个内存单元在物理内存空间中都有一个唯一的地址,

    即可以通过这个地址定位到内存单元,而这个地址即为物理地址。

    CPU 通过地址总线将一个内存单元的物理地址送入存储器,

    而后 CPU 便可以通过这个物理地址来访问这个物理地址所指向的内存单元了。

    那么这个物理地址在 CPU 中是如何形成的呢?

    首先,我们知道 8086 CPU 的地址总线是 20 根,

    即每次都可以传输 20 位的地址,从而寻址能力有 220 也就是 1MB 的大小,

    但是 8086 CPU 的寄存器只有 16 位,也就是在 8086 CPU 的内部,

    一次性处理,传输,暂存的地址都只能是 16 位,

    即 8086 CPU 不能完整的保存下一个物理地址(物理地址为 20 位),

    如果单单以最简单的方式(即直接用 16 位寄存器来保存物理地址)的话,那么,寻址能力只有 216 ,也就是 64KB,

    如果真以如此简单的方式的话,那么地址总线还需要 20 根干嘛呢?而且,难不成我们以后的内存就是 64KB 了吗?

    当然不是的,8086 CPU 在这里采取了一定的措施从而使其寻址能力达到 1MB 。

    8086 CPU 在内部通过两个 16 位的地址进行合成从而形成一个 20 位的物理地址,由此,8086 CPU 的寻址能力便可以达到 1MB 。

    那么 8086 CPU 又是如何将两个 16 位的地址合成为一个20 位的物理地址的呢?

    当 CPU 在访问内存时,其会使用一个 16 位的基地址,然后再使用一个 16 位的偏移地址,

    通过将基地址和偏移地址传入 8086  CPU 的地址加法器中进行合成即可以构造出 20 位的物理地址。

    至于合成的方式如下:

    基地址其实是通过一个 16 位的段地址来形成的,将一个段地址左移 4 位即形成了基地址,

    而至于偏移地址的话,自然不必多说,为 16 位,通过将基地址和偏移地址相加便形成了 20 位的物理地址 。

    下面给出一幅示意图来表示物理地址的合成:

    image

    段:

    至于段的话,其实在物理内存中是没有段这一概念的,事实上,段的概念来自于  CPU ,

    因为 CPU 拥有段寄存器,既然在 CPU 中拥有了段寄存器,自然,在 CPU 中就肯定有段的概念了,

    其实段也就是在编程时,我们将若干个地址连续的内存单元看做是一个段,

    然后通过将一个段地址左移 4 位形成基地址,再通过这个基地址来定位这个段的起始地址,

    然后,再通过偏移地址便可以精确定位到段中的内存单元了,由于段的起始地址是一个段地址左移 4 位,

    所以很明显,段的起始地址肯定是 16 的倍数,而且由于一个段内部,只能通过偏移地址来定位,

    而偏移地址为 16 位,所以一个段的长度也就是 216 也就是 64KB 的大小。

    在编程时,可以讲一段内存定义成为一个段,而这里,我们又可以引出数据段,代码段,栈段这三种类型的段 。

    何为数据段呢?其实就是我们自个儿定义一段内存(当然段起始地址肯定是 16 的倍数,并且段长度 <= 64KB),

    然后我们在这个段里头存放我们所需要使用的数据,这就是数据段;

    何为代码段呢?其实也很简单,也是我们自己在编程的时候定义一段内存,然后这段内存用来存放我们的代码(也就是指令),

    既然是存放的代码,自然就称之为代码段;

    何为栈段呢?至于栈段的话,有接触过数据结构的朋友应该是很清楚栈的,而这里我们也就是在内存中分配出一个段,

    然后将这个段当做栈来使用,对于栈的介绍,详见下文;

    这里呢,顺便还点出几个关于段寄存器的内容,当然下文还会详细介绍的,

    首先,对于任何一个段来说,均有段地址,而这些段地址是存放在段寄存器中(段寄存器的作用也在于此),

    但是对于不同的段,它们默认的段地址存放在不同的段寄存器中,像

    数据段来说,它的段地址存放在  DS (Data  Segment)寄存器中,

    代码段的段地址存放在  CS (Code  Segment)寄存器中,

    栈段的段地址存放在  SS (Stack  Segment)寄存器中 。

    下面给出一幅在段中寻址的示意图:

    image 

    上面的示意图中,通过将段地址左移四位,然后与偏移地址相加便可以得到 20 位的物理地址了 。

    栈:

    8086  CPU 中提供了对栈的支持,并且其还提供了相应的指令来以栈的方式访问内存空间 。

    什么是栈?

    通过上面在段中的介绍,栈其实就是一个段,再说白一点,也就是一块内存,当然,这块内存是一块连续的内存 。

    既然栈是一个段的话,那么当然就可以以使用段的方式来使用栈,当然,除了像段一样的使用栈以外,

    栈还提供了其特殊的访问方式(如果和段一模一样的话,那还需要栈干吗呢?),

    众所周知,栈是先进后出类型的数据结构,在 8086  CPU 中也是如此,

    可以通过 ”PUSH“  指令将数据压入栈中,然后再通过 ”POP“  指令将栈顶的元素取出来 。

    下面给出一幅示意图来描述栈:

    image

    即通过 PUSH  10 来将元素 10 放入栈中,因为,先前栈中没有任何数据,所以,10 就会作为栈顶元素存在,

    然后再在栈中压入元素 20 ,此时,栈顶中的元素就是 20 了,然后再使用  POP 指令将栈顶元素取出,

    此时取出的栈顶元素是 20 ,取出 20 后,栈中便只剩下 10 了,自然 10 就成为了栈顶,

    最后再通过 POP 指令将栈顶 10 取出,此时,栈便变成了空栈了 。

    好了,在介绍段寄存器之前的基础知识介绍就到这里了,下面开始正式介绍段寄存器以及与它们协作使用的寄存器。

                      

    CS 寄存器 和 IP 寄存器:

    经过前面对段的介绍,相信各位朋友对段寄存器应该也有一定的了解了,

    下面将要介绍的是一组非常非常重要的寄存器,即 CS:IP 。

    CS:IP 两个寄存器指示了 CPU 当前将要读取的指令的地址,其中  CS  为代码段寄存器,而   IP  为指令指针寄存器 。

    什么叫做指示了 CPU 当前将要读取的指令呢?在 8086  CPU 中,为什么  CPU  会自动的执行指令呢?

    这些指令肯定是存放在内存中的,但是  CPU  怎么知道这些指令存放在内存的那个位置呢?

    比如,我有下面的两条指令要执行:

        MOV AX,1234H
        MOV BX,AX

    而假设这两条指令在内存中存放为:

    image

    很显然, 1000H:0000H 指向的是  MOV  AX,1234H  的首地址,

    如果 CPU 要读取到我的指令的话,很显然,必须要知道地址  1000H:0000H ,

    然后  CPU  就可以根据这个首地址,将汇编指令  MOV  AX,1234H  所对应的机器码读入到  CPU  的指令寄存器中,

    最后便可以在  CPU  中进行处理了。

    但关键是   CPU  如何知道我的  1000H:0000H  这个首地址?

    其实这就需要使用到  CS:IP  这个寄存器组了 。

    当我们运行一个可执行文件时,很明显,我们需要另外一个程序来将这个可执行文件加载到内存当中,

    关于这个加载可执行文件的程序,我们在这里不管他,点一下即可,

    一般是通过操作系统的外壳程序(也就是传说中的  Shell  程序),

    Shell  将可执行文件加载到内存中以后,就会设置  CPU  中的两个寄存器,

    即设置  CS:IP  两个寄存器指向可执行文件的起始地址,此后  CPU  便从这个起始地址开始读取内存中的指令,并且执行,

    比如我们在写汇编程序时,通常会使用  START  标记,其实这个标记就是用来标记起始地址的,

    当将一个汇编程序编译,连接成可执行文件以后,再通过操作系统的  Shell  程序将可执行文件加载到内存中以后,

    这个  START  所标记处的地址就是整个可执行文件的起始地址了 。

    也就是说,当一个可执行文件加载到内存中以后,CS:IP  两个寄存器便指向了这个可执行文件的起始地址,

    然后  CPU  就可以从这个起始地址开始往下读取指令,

    当读取完指令后,CS:IP  将会自动的改变,基本上是改变  IP ,从而指向下一条要读取的指令,这样就可以执行这个可执行文件了 。

    最后再对  CS:IP  总结一下:

    1. 你想让  CPU  执行哪行指令,你就让  CS:IP  指向保存有指令的那块内存即可。
    2. 任何时候,CS:IP  指向的地址中的内容都是  CPU  当前执行的指令。

    下面我们来看一个  Demo,并详细观察其执行的过程:

    ASSUME CS:CODES
    
    CODES SEGMENT
    	
    START:
        
        MOV AX,1234H
        MOV BX,AX
        
        MOV AH,4CH
        INT 21H
    CODES ENDS
        END START
    语句的执行过程如下:

    image

    从上面的截图中可以看出,当我使用  Shell (在  DOS  下也就是  Command  命令解释器)将可执行文件加载进内存后,

    可以看到,整个程序的起始地址为   0C54H : 0000 H  ,并且,可以看到  CS  的地址为  0C54H ,IP  的地址为  0000H,

    这正好吻合我们上面对  CS:IP  的分析,很明显,CPU  将会读取    MOV    AX ,1234H   到 CPU 中并且执行 ,

    然后我们继续向下看:

    image

    可以看到,我们单步执行后,AX 中的值编成了  1234H ,而  IP  寄存器中的值变成了  0003H,

    对于  AX  中的值的改变,我们是能够理解的,但是   IP  中的值为什么会从  0000H  变到  0003H  呢?

    从最上面的一幅关于指令在内存中的存放可以看出    MOV    AX ,1234H   在内存中需要  3 个内存单元存放,

    也就是  CPU  为了执行    MOV    AX ,1234H   这条指令,已经将内存中相对应的 3  个内存单元读入内存中了,

    执行完这条指令后,自然,CPU  就要将偏移地址向下移动  3  个单元,从而使得  CS:IP  指向下一条需要执行的指令了 ,

    为了更深刻的理解,我们再来继续看执行过程,

    image

    从最上面的一幅关于指令在内存中的存放可以看出    MOV    BX ,AX  在内存中只占  2  个内存单元,

    这也就是为什么  IP  这一次只向下移动了  2  个单元的缘故 。

                      

    关于  CS: IP  的遐想:

    从上面关于  CS:IP  的介绍中,我们可以大胆的猜想,我们只需要通过手动的改变  CS:IP  所指向的内存地址,

    让  CS:IP  指向我们另外的代码,那么我们就可以让  CPU  执行我们自己指定的代码了 。

    即可以通过修改  CS:IP  来达到我们想要让  CPU  干什么它就干什么的目的 。

    上面的虽然是遐想,但是大家要相信,我们写的是汇编,不是  JAVA  也不是  NET  ,

    所以我们还真的可以达到上面的目的,也就是说我们的遐想其实是可以实现的,当然这还是有一定的限制的 ,

    关于这个遐想呢,可能会在我后续的博文中有所介绍,不过感兴趣的当然可以自己去尝试了,蛮有味的哦 。

                  

    SS 寄存器和 SP 寄存器:

    根据前面对栈的介绍,相信各位对栈也肯定是有一定了解了的,更何况,估计大家也是职场打滚多年的,

    要是栈都没用过的话,那也确实蛮悲剧的 ,所以,我在这里也不会对栈做十分详细的介绍了,

    但是,最基本的介绍还是要的,毕竟在底层的话,不像高级语言那么方便,可以直接一个  Stack  就 OK 的,

    在底层涉及的是栈在内存中的具体实现 。

    不知道,大伙有没有注意笔者在本篇博文的上面介绍关于栈的知识时,我并没有提到如何找到这个栈,

    我只提到了一个栈就是先进后出操作,同时可以使用  ”PUSH“ 和  ”POP“ 指令,

    然后就是稍微带了一下  SS 这个寄存器的介绍,

    我们虽然在内存中是可以方便的定义一个栈了,但是,我们为什么要定义这么一个栈呢?

    自然,是为了操作方便,同时提供给  CPU  使用的,

    既然  CPU  要使用的话,自然,CPU  又必须根据一定的方式找到这个栈,

    而这就需要使用  SS 和  SP 寄存器了 。

    同时,一个栈也就是一块内存区域,通过上面的介绍,我们也知道了如果要在一块内存中精确地定位到内存单元的话(寻址),

    我们必须要有基地址(也就是段地址左移  4  位)和偏移地址,自然,要在一个栈中寻址的话,也需要段地址和偏移地址,

    而对于一个栈来说,我们使用的最多的是什么呢?

    当然是栈顶了,因为只有栈顶可以用来存取数据,所以对于一个栈来说,我们只需要有栈顶的段地址和偏移地址即可,

    而对于栈顶的段地址,其是存放在段寄存器  SS  中的,而对于栈顶的偏移地址,其则是存放在  SP  寄存器中的 。

    记住,在任何时刻,SS:SP  都是指向栈顶元素 。

    其实关于栈的使用还是比较简单的,但是要注意的是  8086  CPU  并不会保证我们对栈的操作会不会越界 。

    所以我们在使用栈的时候需要特别注意栈的越界问题 。

    当使用  PUSH 指令向栈中压入 1 个字节单元时,SP = SP - 1;即栈顶元素会发生变化;

    而当使用  PUSH 指令向栈中压入  2 个字节的字单元时,SP = SP – 2 ;即栈顶元素也要发生变化;

    当使用  POP 指令从栈中弹出 1 个字节单元时, SP = SP + 1;即栈顶元素会发生变化;

    当使用  POP 指令从栈中弹出 2 个字节单元的字单元时, SP = SP + 2 ;即栈顶元素会发生变化;

    下面通过一个  Demo 来介绍栈的使用:

    ASSUME CS:CODES
    
    CODES SEGMENT
    	
    START:
        
        MOV AX,1000H		;首先是定义好栈的段地址
        MOV SS,AX    
        MOV AX,10H			;再定义好栈的长度(初始时刻的栈顶偏移地址即栈的长度)
        MOV SP,AX
        
        MOV AX,0001H
        PUSH AX
        MOV AX,0002H
        PUSH AX
        MOV AX,0003H
        PUSH AX
        MOV AX,0004H
        PUSH AX
        MOV AX,0005H
        PUSH AX
        
        POP AX
        POP AX
        POP AX
        POP AX
        POP AX
        
        
        MOV AH,4CH
        INT 21H
    CODES ENDS
        END START
     
    然后我们来看栈在内存中的结构图:

    image

    语句的执行过程如下:

    首先我们来看尚未执行上述任何指令时栈中的数据情况:

    image

    然后我们再来依次执行上述指令:

    image

    从上副截图中可以看出已经设置好了  SS:SP ,也就是栈已经设置 OK 了,

    下面开始往栈中压入数据了,

    image

    由于我们压入栈中的数据为字数据,即占 2 个内存单元,所以,每次  SP = SP – 2 ;

    将 5 个字型数据压入栈中后,我们可以来查看栈中的数据了,

    image

    因此,在内存中的一个好看点的结构图如下所示:

    image

    下面开始进行出栈操作了

    image

    由于我们弹出栈时的数据为字数据,即占 2 个内存单元,所以,每次  SP = SP + 2 ;

    将 5 个字型数据全部弹出栈中后,我们可以来查看栈中的数据了,

    image

    可以看到 SP 变成了初始状态了,也就是说栈中所有的数据已经全部弹出了,虽然我们查看内存时看到的不是 0 ,

    但是我们看到的这些数据都是无效的,我们这里不理会 。

                      

    DS 寄存器和 ES 寄存器:

    DS  寄存器和  ES  寄存器都属于段寄存器,其实它们和  CS  寄存器以及  SS  寄存器用起来区别不大,

    既然是段寄存器的话,自然它们存放的就是某个段地址了 。

    通过上面对基础知识的介绍呢,我们已经知道,如果  CPU  要访问一个内存单元时,

    我们必须要提供一个指向这个内存单元的物理地址给  CPU ,

    而我们也知道在  8086  CPU  中,物理地址是由段地址左移 4  位,然后加上偏移地址形成的,

    所以,我们也就只需要提供段地址和偏移地址即 OK 。

    8086  CPU  呢,提供了一个  DS  寄存器,并且通常都是通过这个  DS  段寄存器来存放要访问的数据的段地址 。

    DS(Data  Segment):很显然,DS 中存放的是数据段的段地址 。

    但是这里不得不再点一下,那就是我们对段的支持是在  CPU  上体现的,而不是在内存中实现了段,

    所以事实上我们使用的段其实是一个逻辑概念,即是我们自己定义的,

    再说白了,我定义一个段,我说它是数据段那它就是数据段,我说它是代码段那么它就是代码段,

    它们其实都是一块连续的内存而已,至于为什么要区分为数据段和代码段,

    很明显,是用来给我们编程提供方便的,即我们在自己的思想上或者说是编码习惯上规定,

    数据放数据段中,代码放代码段中 。而我们在使用数据段的时候,为了方便或者说是代码的编写方便起见,

    我们一般把数据段的段地址放在  DS  寄存器中,当然,如果你硬要觉得  DS  不顺眼,那你可以换个  ES  也是一样的,

    至于  ES(Extra  Segment)  段寄存器的话,自然,是一个附加段寄存器,如果再说得过分点,

    就当它是个扩展吧,当你发现,你几个段寄存器不够用的时候,你可以考虑使用   ES  段寄存器,

    在使用方式上,则和其他的段寄存器没什么区别  。

    下面看一个介绍使用  DS  寄存器的  Demo:

    ASSUME CS:CODES
    
    CODES SEGMENT
       
    START:
    
        MOV AX,1000H
        MOV DS,AX
        MOV AL,1
        MOV BX,0
        
        MOV CX,5			;设计一个循环,让其循环 5 次
        s: MOV [BX],AL		;这里 [BX] 并没有指定段地址哦
           INC AL
           INC BX
           LOOP s            
        
        MOV AH,4CH
        INT 21H
    CODES ENDS
        END START

    上面的代码所做的事情,就是循环将  1,2,3,4,5 写入到地址  1000H:0000H ,1000H:0001H,

    1000H:0002H,1000H:0003H,1000H:0004H  中,

    语句的执行过程如下:

    首先我们来看尚未执行上述任何指令时栈中的数据情况:

    image

    而当循环执行完成以后,我们再来看内存  1000H:0000H 处的值:

    image

    在这里,我们可以看到确实达到了我们预期的效果,但是大家注意看代码:

        s: MOV [BX],AL		;这里 [BX] 并没有指定段地址哦
            INC AL
           INC BX
           LOOP s  

    这里可以看到,我们在  [BX]  中并没有给其指定段地址,而只有一个偏移地址,

    但是根据我们一开始的介绍,必须要有段地址和偏移地址才能够定位内存单元,

    莫非这里出问题了?

    其实不是的,因为我们在最前面定义了段地址   DS  为  1000H,

    当我们定义好段地址后,每一次  CPU  执行到  [BX]  时,便会自动或者说是默认的从  DS  中取值,

    并且将取得的值作为段地址,因此,当  [BX]  为  0001H  时,CPU  会从   DS  中取得一个  1000H ,

    由这两个一合成即可以得到正确的物理地址   1000H:0000H 。

    最后还提醒一点,那就是   8086  CPU  不支持直接将一个数据送入段寄存器中,

    也就是下面的做法是错误的:

        MOV DS,1000H

               

                   

    标志寄存器(FLAG):

    前面呢,已经介绍了  8086  CPU  14 个寄存器中的 13 个了,下面我们将介绍最后一个寄存器也就是  FLAG  寄存器,

    FLAG  寄存器之所以放到最后一个介绍,是因为其和其他的一些寄存器不同,像   AX,BX,CX,DX  这些寄存器来说,

    它们都是用来存放数据的,当然  FLAG  中存放的也是数据啦,

    呵呵,不过,AX,BX 这些寄存器中的数据是作为一个整体使用的,

    最多也就分成一个  AL  和  AH  使用而已,但是在  FLAG  中,数据是按位起作用的,

    也就是说,FLAG  中的每一个位都表示不同的状态,

    由于一个位也就能表示  0  和  1 ,自然,FLAG  中的每一个位就是用来描述状态的,

    而且  FLAG  寄存器中存储的信息通常又被称作程序状态字(PSW) 。

    下面我给出一幅  FLAG  寄存器中各个位的示意图:

    image

    从上面这幅图中可以看出,FLAG  的第  0  个位表示的是 CF  ,第 2 个位表示的是  PF  ,与此类推 . . . . 

    首先,我们来看一个列表:

    image

    上面的这个表怎么看呢?我们通过看下面一幅截图就知道了 。

    image

    从上面的标记中可以看出,从左到右依次代表   OF,DF,SF,ZF,PF,CF  标志位的值,

    再通过与上面的表格相对照可以知道:

    OF = 0 ;

    DF = 0 ;

    SF = 0 ;

    ZF = 0 ;

    PF = 0 ;

    CF = 0  ;

    至于为什么我们在  Debug  模式下,使用  R  命令时,只会列出这几个标志位,我菜的话是因为相对来说,

    列出的这几个标志位更为常用,其他的几个标志位并不经常使用的缘故吧 。

    下面我们就按不同的位来分别介绍这些位所描述的状态,以及它们代表的意义:

    CF(Carry  FLag) - 进位标志(第 0 位):

    CF:    进位标志是用来反映计算时是否产生了由低位向高位的进位,或者产生了从高位到低位的借位 。

    if(运算过程中产生了进位或者借位)
    {
            CF  =  1;
    }
    else
    {
            CF  =  0;
    }
              

    PF(Parity  FLag) - 奇偶标志(第 2 位):

    PF:    奇偶标志是用来记录相关指令执行后,其结果的所有的  Bit  位中  1  的个数是否为偶数 。

    if(运算结果中 1 的个数为偶数)
    {
            PF  =  1;
    }
    else
    {
            PF  =  0;
    }
          

    AF(Auxiliary  Carry  FLag) - 辅助进位标志(第 4 位):

    AF:    用来辅助进位标志 。

    if(字节操作中发生低半个字节向高半个字节借位或者进位  ||  字操作中发生低字节向高字节借位或者进位)
    {
           AF = 1;
    }
    else
    {
           AF = 0;
    }

                 

    ZF(Zero  FLag) – 零标志(第 6 位):

    ZF:    记录的是相关的指令执行完毕后,其执行的结果是否为  0 。

    if(执行的结果  ==  0)
    {
           ZF = 1;
    }
    else
    {
           ZF = 0;
    }

               

    SF(Sign  FLag) - 符号标志(第 7 位):

    SF:    符号标志,其记录相关指令执行完以后,其结果是否为负数 。

    if(运算结果为负数)
    {
            SF  =  1;
    }
    else
    {
            SF  =  0;
    }
         

    TF(Trap  FLag) - 追踪标志(第 8 位):

    TF:    追踪标志,主要是用于调试时使用 。

    if(TF  ==  1)
    {
           CPU 进入单步方式;
    }
         

    IF(Interrupt-Enable  FLag) - 中断允许标志(第 9 位):

    IF:    中断允许标志,其决定  CPU  是否能够响应外部可屏蔽中断请求(以后会做详细介绍) 。

    if(IF  ==  1)
    {
            CPU 能够响应外部的可屏蔽中断请求;
    }
    else
    {
            CPU 不能够响应外部的可屏蔽中断请求;
    }
                

    DF(Direction  FLag) - 方向标志(第 10 位):

    DF:    方向标志,其用于在串处理指令中,用来控制每次操作后  SI  和  DI  是自增还是自减 。

    if(DF == 0)
    {
            SI++;
            DI++;
    }
    else
    {
            SI--;
            DI--;
    }
                   

    OF(OverFlow  FLag) - 溢出标志(第 11 位):

    OF:    溢出标志,其通常记录了有符号数运算的结果是否发生了溢出 。

    if(运算发生溢出)
    {
            OF  =  1;
    }
    else
    {
            OF  =  0;
    }

                

                    

    总结

    上面呢,从最简单的开始,循序渐进的介绍了  8086  CPU  中的各个寄存器,

    同时也通过一些  Demo  来列举了各个寄存器的使用,

    由于写的比较基础,而且量也比较多,所以,造成博文过长了,读者需一定耐心才能看完,

    写本篇博文呢,并不是说将来要用汇编去开发个什么东东,

    实质上,笔者学习汇编的目的也不在此,只是因为先前在接触到底层的寄存器以及内存时,

    笔者总有一丝不爽的感觉,总是感觉不得要领,所以才会开始汇编的学习,

    此次推出本系列博文,本意也并不是说要学习汇编做开发,只是为了提升内功而已 。



    一、数据传输指令

    作用:它们在存贮器和寄存器、寄存器和输入输出端口之间传送数据.
    1. 通用数据传送指令.
       MOV 传送字或字节.
       MOVSX 先符号扩展,再传送.
       MOVZX 先零扩展,再传送.
       PUSH 把字压入堆栈.
       POP 把字弹出堆栈.
       PUSHA 把AX,CX,DX,BX,SP,BP,SI,DI依次压入堆栈.
       POPA 把DI,SI,BP,SP,BX,DX,CX,AX依次弹出堆栈.
       PUSHAD 把EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI依次压入堆栈.
       POPAD 把EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX依次弹出堆栈.
       BSWAP 交换32位寄存器里字节的顺序
       XCHG 交换字或字节.( 至少有一个操作数为寄存器,段寄存器不可作为操作数)
       CMPXCHG 比较并交换操作数.( 第二个操作数必须为累加器AL/AX/EAX )
       XADD 先交换再累加.( 结果在第一个操作数里 )
       XLAT 字节查表转换.
       BX 指向一张 256 字节的表的起点, AL 为表的索引值 (0-255,即 0-FFH); 返回 AL 为查表结果. ( [BX+AL]->AL )
     
    2. 输入输出端口传送指令.
       IN I/O端口输入. ( 语法: IN 累加器, {端口号│DX} )
       OUT I/O端口输出. ( 语法: OUT {端口号│DX},累加器 )
       输入输出端口由立即方式指定时, 其范围是 0-255; 由寄存器 DX 指定时,其范围是 0-65535.
     
    3. 目的地址传送指令.
       LEA 装入有效地址.
         例: LEA DX,string ;把偏移地址存到DX.
       LDS 传送目标指针,把指针内容装入DS.
         例: LDS SI,string ;把段地址:偏移地址存到DS:SI.
       LES 传送目标指针,把指针内容装入ES.
         例: LES DI,string ;把段地址:偏移地址存到ES:DI.
       LFS 传送目标指针,把指针内容装入FS.
         例: LFS DI,string ;把段地址:偏移地址存到FS:DI.
       LGS 传送目标指针,把指针内容装入GS.
         例: LGS DI,string ;把段地址:偏移地址存到GS:DI.
       LSS 传送目标指针,把指针内容装入SS.
         例: LSS DI,string ;把段地址:偏移地址存到SS:DI.
     
    4. 标志传送指令.
       LAHF 标志寄存器传送,把标志装入AH.
       SAHF 标志寄存器传送,把AH内容装入标志寄存器.
      PUSHF 标志入栈.
       POPF 标志出栈.
       PUSHD 32位标志入栈.
       POPD 32位标志出栈.
     
    二、算术运算指令
       ADD 加法.
       ADC 带进位加法.
       INC 加 1.
       AAA 加法的ASCII码调整.
       DAA 加法的十进制调整.
       SUB 减法.
       SBB 带借位减法.
       DEC 减 1.
       NEC 求反(以 0 减之).
       CMP 比较.(两操作数作减法,仅修改标志位,不回送结果).
       AAS 减法的ASCII码调整.
       DAS 减法的十进制调整.
       MUL 无符号乘法.
       IMUL 整数乘法.
         以上两条,结果回送AH和AL(字节运算),或DX和AX(字运算),
       AAM 乘法的ASCII码调整.
       DIV 无符号除法.
       IDIV 整数除法.
         以上两条,结果回送:
         商回送AL,余数回送AH, (字节运算);
         或 商回送AX,余数回送DX, (字运算).
       AAD 除法的ASCII码调整.
       CBW 字节转换为字. (把AL中字节的符号扩展到AH中去)
       CWD 字转换为双字. (把AX中的字的符号扩展到DX中去)
       CWDE 字转换为双字. (把AX中的字符号扩展到EAX中去)
       CDQ 双字扩展. (把EAX中的字的符号扩展到EDX中去)
     
    三、逻辑运算指令
       AND 与运算.
       OR 或运算.
       XOR 异或运算.
       NOT 取反.
       TEST 测试.(两操作数作与运算,仅修改标志位,不回送结果).
       SHL 逻辑左移.
       SAL 算术左移.(=SHL)
       SHR 逻辑右移.
       SAR 算术右移.(=SHR)
       ROL 循环左移.
       ROR 循环右移.
       RCL 通过进位的循环左移.
       RCR 通过进位的循环右移.
         以上八种移位指令,其移位次数可达255次.
         移位一次时, 可直接用操作码. 如 SHL AX,1.
         移位>1次时, 则由寄存器CL给出移位次数.
          如 MOV CL,04
             SHL AX,CL
     
    四、串指令
       DS:SI 源串段寄存器 :源串变址.
       ES:DI 目标串段寄存器:目标串变址.
       CX 重复次数计数器.
       AL/AX 扫描值.
       D标志 0表示重复操作中SI和DI应自动增量; 1表示应自动减量.
    Z标志 用来控制扫描或比较操作的结束.
       MOVS 串传送.
       ( MOVSB 传送字符. MOVSW 传送字. MOVSD 传送双字. )
       CMPS 串比较.
       ( CMPSB 比较字符. CMPSW 比较字. )
       SCAS 串扫描.
         把AL或AX的内容与目标串作比较,比较结果反映在标志位.
       LODS 装入串.
         把源串中的元素(字或字节)逐一装入AL或AX中.
       ( LODSB 传送字符. LODSW 传送字. LODSD 传送双字. )
       STOS 保存串.
       是LODS的逆过程.
       REP 当CX/ECX<>0时重复.
       REPE/REPZ 当ZF=1或比较结果相等,且CX/ECX<>0时重复.
       REPNE/REPNZ 当ZF=0或比较结果不相等,且CX/ECX<>0时重复.
       REPC 当CF=1且CX/ECX<>0时重复.
       REPNC 当CF=0且CX/ECX<>0时重复.
     
    五、程序转移指令
    1>无条件转移指令 (长转移)
       JMP 无条件转移指令
       CALL 过程调用
       RET/RETF过程返回.
    2>条件转移指令 (短转移,-128到+127的距离内)
       ( 当且仅当(SF XOR OF)=1时,OP1<OP2 )
       JA/JNBE 不小于或不等于时转移.
       JAE/JNB 大于或等于转移.
       JB/JNAE 小于转移.
       JBE/JNA 小于或等于转移.
       以上四条,测试无符号整数运算的结果(标志C和Z).
       JG/JNLE 大于转移.
       JGE/JNL 大于或等于转移.
       JL/JNGE 小于转移.
       JLE/JNG 小于或等于转移.
       以上四条,测试带符号整数运算的结果(标志S,O和Z).
       JE/JZ 等于转移.
       JNE/JNZ 不等于时转移.
       JC 有进位时转移.
       JNC 无进位时转移.
       JNO 不溢出时转移.
       JNP/JPO 奇偶性为奇数时转移.
       JNS 符号位为 "0" 时转移.
       JO 溢出转移.
       JP/JPE 奇偶性为偶数时转移.
       JS 符号位为 "1" 时转移.
    3>循环控制指令(短转移)
       LOOP CX不为零时循环.
       LOOPE/LOOPZ CX不为零且标志Z=1时循环.
       LOOPNE/LOOPNZ CX不为零且标志Z=0时循环.
       JCXZ CX为零时转移.
       JECXZ ECX为零时转移.
    4>中断指令
       INT 中断指令
       INTO 溢出中断
       IRET 中断返回
    5>处理器控制指令
       HLT 处理器暂停, 直到出现中断或复位信号才继续.
       WAIT 当芯片引线TEST为高电平时使CPU进入等待状态.
       ESC 转换到外处理器.
       LOCK 封锁总线.
       NOP 空操作.
       STC 置进位标志位.
       CLC 清进位标志位.
       CMC 进位标志取反.
       STD 置方向标志位.
       CLD 清方向标志位.
       STI 置中断允许位.
       CLI 清中断允许位.
     
    六、伪指令
       DW 定义字(2字节).
       PROC 定义过程.
       ENDP 过程结束.
       SEGMENT 定义段.
       ASSUME 建立段寄存器寻址.
       ENDS 段结束.
       END 程序结束.
     
        当然不是所有的指令都能用的上的,我在这里全部写出来是为了让大家认识一下,方便大家以后的学习,我归纳了一下常用的指令,这些指令大家一定要熟练掌握才可以啊。
     
    MOV 数据传送指令
    PUSH,POP 堆栈指令
    CMP 比较指令
    LEA 取地址指令
    XOR 异或指令
    JE,JZ,JMP...(所有的转移指令)


    展开全文
  • 8086汇编寄存器常用指令

    千次阅读 2016-03-23 22:50:00
    计算机寄存器常用指令 一、常用指令 1. 通用数据传送指令. MOV 传送字或字节. MOVSX 先符号扩展,再传送. MOVZX 先零扩展,再传送. PUSH 把字压入堆栈. POP 把字弹出堆栈. PUSHA 把AX,CX,DX,BX,SP,BP,SI,DI...

    计算机寄存器常用指令

    一、常用指令
    1. 通用数据传送指令.
    MOV 传送字或字节.
    MOVSX 先符号扩展,再传送.
    MOVZX 先零扩展,再传送.
    PUSH 把字压入堆栈.
    POP 把字弹出堆栈.
    PUSHA 把AX,CX,DX,BX,SP,BP,SI,DI依次压入堆栈.
    POPA 把DI,SI,BP,SP,BX,DX,CX,AX依次弹出堆栈.
    PUSHAD 把EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI依次压入堆栈.
    POPAD 把EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX依次弹出堆栈.
    BSWAP 交换32位寄存器里字节的顺序
    XCHG 交换字或字节.( 至少有一个操作数为寄存器,段寄存器不可作为操作数)
    CMPXCHG 比较并交换操作数.( 第二个操作数必须为累加器AL/AX/EAX )
    XADD 先交换再累加.( 结果在第一个操作数里 )
    XLAT 字节查表转换.
    BX 指向一张 256 字节的表的起点, AL 为表的索引值 (0-255,即 0-FFH); 返回 AL 为查表结果. ( [BX+AL]->AL )
    2. 输入输出端口传送指令.
    IN I/O端口输入. ( 语法: IN 累加器, {端口号│DX} )
    OUT I/O端口输出. ( 语法: OUT {端口号│DX},累加器 )
    输入输出端口由立即方式指定时, 其范围是 0-255; 由寄存器 DX 指定时,其范围是 0-65535.
    3. 目的地址传送指令.
    LEA 装入有效地址.
    例: LEA DX,string ;把偏移地址存到DX.
    LDS 传送目标指针,把指针内容装入DS.
    例: LDS SI,string ;把段地址:偏移地址存到DS:SI.
    LES 传送目标指针,把指针内容装入ES.
    例: LES DI,string ;把段地址:偏移地址存到ESDI.
    LFS 传送目标指针,把指针内容装入FS.
    例: LFS DI,string ;把段地址:偏移地址存到FSD.
    LGS 传送目标指针,把指针内容装入GS.
    例: LGS DI,string ;把段地址:偏移地址存到GSDI.
    LSS 传送目标指针,把指针内容装入SS.
    例: LSS DI,string ;把段地址:偏移地址存到SSDI.
    4. 标志传送指令.
    LAHF 标志寄存器传送,把标志装入AH.
    SAHF 标志寄存器传送,把AH内容装入标志寄存器.
    PUSHF 标志入栈.
    POPF 标志出栈.
    PUSHD 32位标志入栈.
    POPD 32位标志出栈.

    二、算术运算指令
    ADD 加法.
    ADC 带进位加法.
    INC 加 1.
    AAA 加法的ASCII码调整.
    DAA 加法的十进制调整.
    SUB 减法.
    SBB 带借位减法.
    DEC 减 1.
    NEC 求反(以 0 减之).
    CMP 比较.(两操作数作减法,仅修改标志位,不回送结果).
    AAS 减法的ASCII码调整.
    DAS 减法的十进制调整.
    MUL 无符号乘法.
    IMUL 整数乘法.
    以上两条,结果回送AH和AL(字节运算),或DX和AX(字运算),
    AAM 乘法的ASCII码调整.
    DIV 无符号除法.
    IDIV 整数除法.
    以上两条,结果回送:
    商回送AL,余数回送AH, (字节运算);
    或 商回送AX,余数回送DX, (字运算).
    AAD 除法的ASCII码调整.
    CBW 字节转换为字. (把AL中字节的符号扩展到AH中去)
    CWD 字转换为双字. (把AX中的字的符号扩展到DX中去)
    CWDE 字转换为双字. (把AX中的字符号扩展到EAX中去)
    CDQ 双字扩展. (把EAX中的字的符号扩展到EDX中去)

    三、逻辑运算指令
    AND 与运算.
    OR 或运算.
    XOR 异或运算.
    NOT 取反.
    TEST 测试.(两操作数作与运算,仅修改标志位,不回送结果).
    SHL 逻辑左移.
    SAL 算术左移.(=SHL)
    SHR 逻辑右移.
    SAR 算术右移.(=SHR)
    ROL 循环左移.
    ROR 循环右移.
    RCL 通过进位的循环左移.
    RCR 通过进位的循环右移.
    以上八种移位指令,其移位次数可达255次.
    移位一次时, 可直接用操作码. 如 SHL AX,1.
    移位>1次时, 则由寄存器CL给出移位次数.
    如 MOV CL,04
    SHL AX,CL

    四、串指令
    DS:SI 源串段寄存器 :源串变址.
    ES I 目标串段寄存器:目标串变址.
    CX 重复次数计数器.
    AL/AX 扫描值.
    D标志 0表示重复操作中SI和DI应自动增量; 1表示应自动减量.
    Z标志 用来控制扫描或比较操作的结束.
    MOVS 串传送.
    ( MOVSB 传送字符. MOVSW 传送字. MOVSD 传送双字. )
    CMPS 串比较.
    ( CMPSB 比较字符. CMPSW 比较字. )
    SCAS 串扫描.
    把AL或AX的内容与目标串作比较,比较结果反映在标志位.
    LODS 装入串.
    把源串中的元素(字或字节)逐一装入AL或AX中.
    ( LODSB 传送字符. LODSW 传送字. LODSD 传送双字. )
    STOS 保存串.
    是LODS的逆过程.
    REP 当CX/ECX0时重复.
    REPE/REPZ 当ZF=1或比较结果相等,且CX/ECX0时重复.
    REPNE/REPNZ 当ZF=0或比较结果不相等,且CX/ECX0时重复.
    REPC 当CF=1且CX/ECX0时重复.
    REPNC 当CF=0且CX/ECX0时重复.

    五、程序转移指令
    1.简单的条件转移指令
    JZ(或jE) OPR---------------结果为零转移, 测试条件ZF=1
    JNZ(或jNE) OPR --------------结果不为零转移,测试条件ZF=0
    JS OPR----------------------结果为负转移, 测试条件SF=1
    JNS OPR---------------------结果为正转移, 测试条件SF=0
    JO OPR--------------------- 溢出转移, 测试条件OF=
    JNO OPR --------------------不溢出转移 , 测试条件SF=0
    JP OPR ---------------------结果为偶转移, 测试条件SF=1
    JNP OPR --------------------结果为奇转移 , 测试条件SF=0
    JC OPR -------------------- 有进位转移 , 测试条件SF=1
    JNC OPR --------------------无进位转移, 测试条件SF=0
    2.无符号比较条件转移指令(以下指令经常是CMP OPD,OPS后面的指令根据比较结果来实现转移)
    JB(或JNAE) opd --------------小于或者不大于等于则转移
    JNB(或JAE) opd---------------不小于或者大于等于则转移
    JA(或NJBE) OPD---------------大于或者不小于等于则转移
    JNA(或JBE) OPD---------------不大于或者小于等于则转移
    3.带符号比较条件转移指令
    JL(或JNGE) --------------小于或者不大于等于则转移
    JNL(或JGE)--------------不小于或者大于等于则转移
    JG(或NJLE)---------------大于或者不小于等于则转移
    JNG(或JLE)---------------不大于或者小于等于则转移
    六、调用子程序与返回指令
    CALL 子程序调用指令
    RET 子程序返回指令

    六、其它指令
    OFFSET -------------------- 返回偏移地址
    SEG -------------------- 返回段地址
    EQU(=) -------------------- 等值语句
    PURGE -------------------- 解除语句
    DUP -------------------- 操作数字段用复制操作符
    SEGMENT,ENDS -------------------- 段定义指令
    ASSUME -------------------- 段地址分配指令
    ORG -------------------- 起始偏移地址设置指令
    $ --------------------地址计数器的当前值
    PROC,ENDP -------------------- 过程定义语句
    NAME,TITLE,END -------------------- 程序开始结束语句


    展开全文
  • 这里写目录标题数据处理两类寄存器bx si di bp数据长度div除法指令指令dddup操作符转移指令offset操作符jmp指令目的地址在指令中目的地址在寄存器中目的地址在数据中jcxz指令loop指令 数据处理 两类寄存器 寄存器...

    数据处理

    两类寄存器

    寄存器(reg):ax bx cx dx ah al bh bl ch cl dh dl sp bp si di
    段寄存器(sreg):ds ss cs es

    bx si di bp

    他们可以通过[ ]表示数据。
    他们可以单独出现或以 bx si,bx di,bp si,bp di这四种组合出现。
    bx默认的段地址是ds。
    bp默认的段地址是ss。

    数据长度

    在有寄存器名字时,看寄存器名字。
    在没有寄存起名字时 用word ptr表示字(16位)单元,用byte ptr表示字节(8位)单元。
    push pop只进行字操作。

    div除法指令

    除数:8位或16位,放在reg或内存单元中。
    被除数:除数8位则被除数16位,在ax中存放。除数16位则被除数32位,在ax和dx中存放,dx存放高16位,ax存放低16位。
    结果:除数为8位,al存商,ah存余数。除数16位,ax存商,dx存余数。
    格式:div 除数

    伪指令dd

    dd命令:定义双字节数据。

    dd 1

    dup操作符

    db/dw/dd 重复次数 dup (重复的数据)

    转移指令

    offset操作符

    获取编译器处理符号的偏移地址。
    在这里插入图片描述

    jmp指令

    目的地址在指令中

    段内短转移:-128~127字节
    jmp short 标号
    在这里插入图片描述段内近转移:-32768~32767
    jmp near ptr 标号

    段间转移,远转移
    jmp far ptr 标号

    目的地址在寄存器中

    jmp reg

    目的地址在数据中

    jmp word ptr 数据地址(只取一个字,改变ip)
    jmp dword ptr 数据地址(取两个字,高地址是cs,低地址是ip)

    jcxz指令

    有条件的转移指令,属于段内短转移
    格式:jcxz 标号
    在cx是0的前提条件下才会执行这条指令

    loop指令

    循环指令是段内短转移。

    CALL 和 RET

    ret 和 retf

    ret:修改ip的内容,实现近转移
    格式:ret
    效果:
    pop ip

    retf:修改cs 和 ip 的内容,实现段转移
    格式:retf
    效果:
    pop ip
    pop cs

    call指令

    目的地址在指令中

    段内近转移
    格式:call 标号
    效果:
    push ip
    jmp near ptr 标号

    段间转移
    格式:call far ptr 标号
    效果:
    push cs
    push ip
    jmp far ptr 标号

    目的地址在寄存器中

    格式:call 16位reg
    效果:
    push ip
    jmp 16位reg

    目的地址在寄存器中

    call word ptr 内存单元地址
    效果:
    push ip
    jmp word ptr 内存单元地址

    call dword ptr 内存单元地址
    效果:
    push cs
    push ip
    jmp dword ptr 内存单元地址

    mul乘法指令

    相乘的数为8位或16位
    如果是8位,就放在 al 和 8位reg或内存字节单元中,结果放在ax中
    如果是16位,就放在 ax 和 16位reg或内存字单元中,结果高位在dx中,低位在ax中。
    格式:mul reg/内存单元

    标志寄存器

    他就是flag,他储存的信息被称为程序状态字(PSW)。
    在这里插入图片描述

    标志

    ZF标志:零标志位,记录相关指令执行后结果是否为零,结果是零zf=1,不是零zf=0
    PF标志:奇偶标志位,记录相关指令执行后结果中1的个数是否为偶数,1的个数为偶数pf=1,为奇数pf=0
    SF标志:符号标志位,记录相关指令执行后结果是否为负,结果为负sf=1,非负sf=0
    CF标志:进位标志位,(无符号数运算)记录运算结果的最高有效位向更高位的进位值,或借位值。
    OF标志:溢出标志位,记录(有符号数)运算的结果是否发生溢出,如果发生溢出of=1,没有溢出of=0

    指令

    adc指令
    带进位加法指令,利用cf位。
    格式:adc 操作对象1,操作对象2
    功能:操作对象1=操作对象1+操作对象2+cf

    sbb指令
    带借位减法指令,利用cf位
    格式:sbb 操作对象1,操作对象2
    功能:操作对象1=操作对象1-操作对象2-cf

    cmp指令
    比较指令
    格式:cmp 操作对象1,操作对象2
    功能:计算操作对象1-操作对象2 不保存结果
    通过标志来判断比较结果
    在这里插入图片描述

    条件转移指令

    格式:指令 标号
    指令 条件
    je : zf=1
    jne:zf=0
    jb : cf=1
    jnb:cf=0
    ja : cf=0 且 zf=0
    jna:cf=1 或 zf=1

    DF标志和串传送指令

    DF标志:方向标志位,在串处理指令中,控制si,di增减。df=0 每次操作后si di递增,df=1 每次操作后si di递减

    串传送指令

    movsb:传送字节
    格式:movsb
    功能:将ds:si指向的内存单元的字节送入es:di中,然后根据df的值,将si di递增或递减。

    movsw:传送字
    格式:movsw
    功能:将ds:si指向的内存单元的字节送入es:di中,然后根据df的值,将si di递增2或递减2。

    几条指令

    rep指令:循环指令
    格式:rep …
    功能:循环执行后面的指令,循环次数取决于cx

    cld指令:df=0
    std指令:df=1

    pushf 和 popf

    pushf:将标志存储器数据压栈。
    popf:将栈中数据存入标志存储器。

    标志存储器Debug中表示

    在这里插入图片描述

    展开全文
  • 汇编指令寄存器

    2019-03-02 15:14:41
    寄存器学习记录0. 8086寄存器分类1. 通用寄存器(8个):AX、BX、CX、DX 和 SI、...寄存器详解(DX:数据寄存器) 七种寻址方式(立即寻址、寄存器寻址) 寄存器分类: 通用寄存器-又可以分成2组,一组是数据寄存器(4个...
  • 寄存器 一个典型的CPU由运算器、控制器、寄存器等器件组成,...CPU中主要的部件是寄存器寄存器是CPU中我们可以使用指令读写的部件(通过改变各种寄存器的内容来实现对CPU的控制) 不同的CPU寄存器的个数也不同,...
  • 汇编指令寄存器

    2019-05-28 17:22:00
    8个32位通用寄存器按顺序分别是: EAX 通常用来保存函数的返回值 ...ESI 源变址寄存器 MOVS 或 STOS等指令 EDI 目的变址寄存器 8个16位通用寄存器: AX CX DX BX SP BP SI DI 8个8位通用寄存器: A...
  • 80x86 16bit架构中用了14个寄存器: ┏ 指令指针寄存器 AX BX CX DX SI DI SP BP IP CS SS DS ES PSW/FLAG ┗━━━━━━━━━━━━━━━━━━━┛ ┗━━━━━┛ ┗━━━━━━━┛ ┗━...
  • 指令寄存器

    2017-09-21 10:29:42
    EAX、ECX、EDX、EBX:為ax,bx,cx,dx的延伸,各為32位元 ESI、EDI、ESP、EBP:為si,di,sp,bp的延伸,32位元 eax, ebx, ecx, edx, esi, edi, ebp, esp等都是X86 汇编语言中CPU上的通用寄存器的名称,是32位的...
  • AX,BX,CX,DX 称作为数据寄存器: AX (Accumulator):累加寄存器,也称之为累加器; BX (Base):基地址寄存器; CX (Count):计数器寄存器DX (Data):数据寄存器; SP 和 BP 又称作为指针寄
  • 寄存器

    2019-02-03 22:33:00
    通用寄存器: AX BX CX DX 这4个寄存器通常用来存放一般性的数据. 段寄存器: CS DS SS ES 当8086CPU要访问内存时 这4个段寄存器提供内存单元的段地址. CS 和 IP 是8086两个最关键的寄存器,它们指示了CPU当前要读取...
  • 数据寄存器:ax bx cx dx 指针及变址寄存器:bp sp di si 段寄存器:CS DS SS ES 控制寄存器:IP 数据寄存器 也称为通用寄存器,均为16位,包括AX,BX,CX和DX 16位寄存器的逻辑结构 4个16位...
  • 计算机内部寄存器阵列 l 数据寄存器 l 指针及变址寄存器 l 段寄存器 l 控制寄存器 数据寄存器 包括4个16位的寄存器(AX, BX, CX, DX)或者8个8位寄存器( AH, AL, BH, BL, CH...
  • DX DL 2 EBX BX BL 3 ESP SP AH 4 EBP BP CH 5 ESI SI DH 6 EDI DI BH 7 MOV的语法: 作用:拷贝源操作数到目标操作数 源操作数可以是立即数、通用寄存器、段寄存器、或者内存单元 目标操作数可以是...
  • 常用寄存器

    2021-06-17 16:37:08
    通用寄存器:AX,BX,CX,DX 变址寄存器:SI,DI 指针寄存器:SP,BP 指令指针寄存器:IP 段寄存器:CS,SS,DS,ES 标志寄存器:PSW 8086CPU所有寄存器均为16bit,可存放2Byte,一个16bit寄存器存储一个16bit数据...
  • 通过汇编指令来修改寄存器中的内容,从而来控制CPU,寄存器是CPU中保存地址信息和数据信息的地方,寄存器之间是互相独立的。 以下为通用寄存器: AX = AH + AL BX = BH + BL CX = CH + CL DX = DH + DL 注:H = ...
  • 8086寄存器

    2016-06-12 15:18:40
    可分为通用寄存器指令指针、标志寄存器和段寄存器4类。 通用寄存器: 通用寄存器: AX,BX,CX,DX 称作为数据寄存器: AX (Accumulator):累加寄存器,也称之为累加器; BX (Base):基地址寄存器; CX...
  • 汇编【2】寄存器

    2021-03-13 20:17:33
    一、AX BX CX DX寄存器 1. why 2. 加深印象 2.1 数据与寄存器间 要保证位数一致性 发现:经过了编译 0005为16位数据(2---->16) 0也会编译为16位的0000,因为AX是16位的寄存器 5编译为16位数的05,因为AL...
  • 寄存器详解

    千次阅读 2012-12-07 18:09:14
    AX/EAX: 累加器,算术通用寄存器,与端口的通讯也...DX/EDX: 算术通用寄存器.和累加器结合表示双字长的数,其中累加器用于存低位数. [From]http://blog.csdn.net/huangwei1024/archive/2007/01/29/1496560.aspx 2
  • 0 前言 1 寄存器的分类 我先从其他博客为你找来了8086CPU的全部寄存器及其功能 原文链接在此 通用寄存器: ...dx——data registered——数据寄存器寄存器: cs——code segment——代码段 ds——...
  • 寄存器内容

    2020-12-20 16:01:04
    AX BX CX DX SI DI BP SP 其中4个数据寄存器还可以分为高8位和低8位两个独立的寄存器 8086的8位通用寄存器是: AH BH CH DH AL BL CL DL AX——累加器,用于算术、逻辑运算以及外设传送信息等; BX——基址...
  • 内部寄存器

    千次阅读 2016-03-06 13:05:13
    数据寄存器(AX,BX,CX,DX) AX:累加器。所有I/O指令都通过AX与接口传送信息,中间运算结果也多放于AX中; BX:基址寄存器。在间接寻址中用于存放基地址; CX:计数寄存器。用于在循环或串操作指令中存放计数值...
  • 8086cpu寄存器8086cpu寄存器组通用寄存器指令和变址寄存器段寄存器指令和指针寄存器和标志位寄存器存储地址的分段管理8086的分体结构 8086cpu寄存器组 通用寄存器 四个16位寄存器:AX,BX,CX,DX 每个可拆分成两个8位...
  • 80386寄存器

    2010-03-30 11:21:00
    1.80386的的寄存器: 80386的寄存器可以分为8组:通用寄存器,段寄存器指令指针寄存器,标志寄存器,系统地址寄存器,控制寄存器,调试寄存器,测试寄存器,它们的宽度都是32位的。本篇主要介绍80386的寄存器。 A1...
  • (1)数据寄存器:AX:累计器 , BX:基址寄存器 ,CX:计数器 ,DX:数据寄存器; (2)指针或变址寄存器:SP:堆栈指示器 ,BP:基址指示器 ,SI:源变址器 , DI:目的变址器; 专用寄存器:IP:指令指针寄存器,PSW...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 773
精华内容 309
关键字:

dx寄存器指令