指令

div指令

div除法指令,需要注意一下几点

  • 除数:有8位和16位两种,存放在reg或内存单元中
  • 被除数:默认放在AX或DX和AX中,如果除数为8位被除数则为16位,默认在AX中存放,如果除数为16位被除数则为32位默认在DX和AX中存放DX存放高16位AX存放低16位
  • 结果:如果除数为 8 位,则 AL存储除法操作的商,AH 存储除法操作的余数;
    如果除数为16位,则AX存储除法操作的商,DX存储除法操作的余数。
    1
    2
    3
    4
    5
    6
    7
    div byte ptr ds:[1]
    al = (ax)/(ds*16+1)//地位存放商
    ah = (ax)%(ds*16+1)//高位存放余
    -----------------------------------------
    div word ptr es:[1]
    ax = (dx*10000H+ax)/(es*16+1)//ax存放商
    dx = (dx*10000H+ax)%(es*16+1)//dx存放余
    例如100001/100
  • 首先100001大于65535则需要32位寄存器
  • 100小于255但是被除数为32位所以要16位寄存器
    1
    2
    3
    4
    mov dx,1
    mov ax,86A1H
    mov bx,100
    div bx

    伪指令

  • db定义字节型数据
  • dw定义字型数据
  • dd 是用来定义 dword(doubleword,双字)型数据的。
  • dup 是一个操作符,在汇编语言中同 db、dw、dd 等一样,也是由编译器识别处理的符号。它是和 db、dw、dd 等数据定义伪指令配合使用的,用来进行数据的重复。
    1
    2
    3
    4
    db 3 dup (0)
    定义了3个字节,它们的值都是0,相当于 db 0,0,0。
    db 3 dup (0,1,2)
    定义了 9个字节,它们是 0、1、2、0、1、2、0、1、2,相当于 db 0,1,2,0,1,2,0,1,2。

    jmp指令

  • jmp指令无条件转移指令,可以修改ip也可以修改cs和ip
    jmp 指令要给出两种信息:
  • 转移的目的地址
  • 转移的距离(段间转移、段内短转移,段内近转移)

    jcxz指令

    jcxz 指令为有条件转移指令,所有的有条件转移指令都是短转移,在对应的机器码中包含转移的位移,而不是目的地址。对 IP 的修改范围都为:-128~127。

    loop指令

    loop指令为循环指令,所有的有loop指令都是短指令,在对应的机器码中包含转移的位移,而不是目的地址。对 IP 的修改范围都为:-128~127。

    ret指令

  • ret指令用栈中的数据,修改ip的内容,从而实现近转移
  • retf指令用栈中的数据,修改ip的内容,从而实现远转移

    指令转移的原理

    可以修改IP,或同时修改CS和IP的指令统称为转移指令。概括地讲,转移指令就是可以控制CPU执行内存中某处代码的指令。
    8086CPU的转移行为有以下几类。
  • 只修改IP时,称为[段内转移]{.yellow},比如:jmp ax。
  • 同时修改CS和IP时,称为[段间转移]{.yellow},比如:jmp 1000:0。
    由于转移指令对IP的修改范围不同,段内转移又分为:短转移和近转移
  • [短转移]{.yellow}IP的修改范围为-128~127。
  • [近转移]{.yellow}IP 的修改范围为-32768~32767。
    8086CPU的转移指令分为以下几类。
  • 无条件转移指令(如:jmp)
  • 条件转移指令
  • 循环指令(如:loop)
  • 过程
  • 中断

    ret和retf

  • ret指令用栈中的数据,修改IP的内容,从而实现近转移;
  • retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移。
    CUP执行ret指令时,进行下面两步操作
    (IP) = ((SS) 16)+SP
    (SP) = (SP)+2
    CUP执行retf指令时,进行下面四步操作
    (CS) = (SS)
    16+SP
    (SP) = (SP) + 2
    (IP) = (SS)* 16+SP
    (SP) = (SP)+2
    可以看出,如果我们用汇编语法来解释 ret和retf指令,则:
    CPU 执行 ret 指令时,相当于进行:
    pop IP
    CPU 执行 retf指令时,相当于进行:
    pop IP
    pop CS

    Call

    CPU执行call指令时,进行两步操作:
  1. 将当前的IP或CS和IP压入栈中;
  2. 转移。
    call 指令不能实现短转移,除此之外,call 指令实现转移的方法和 jmp 指令的原理相
    同。
    call 标号(将当前的 IP 压栈后,转到标号处执行指令)
    CPU 执行此种格式的 call 指令时,进行如下的操作:
    (sp)=(sp)-2
    (ss)* 16+(sp))=(IP)
    (IP)=(IP)+16位位移。
    可以看出,如果我们用汇编语法来解释 call指令,则:
    CPU 执行 call 指令时,相当于进行:
    push IP
    CPU 执行 call far ptr指令时,相当于进行:
    push IP
    push CS

    mul

    因下面要用到,这里介绍一下mul指令,mul是乘法指令,使用mul做乘法的时候,
    注意以下两点。
    (1) 两个相乘的数:两个相乘的数,要么都是8位,要么都是16位。如果是8位,一个默认放在AL中,另一个放在8位
    reg或内存字节单元中;如果是16位,一个默认在AX中,另一个放在 16 位 reg 或内存字单元中。
    (2) 结果:如果是8位乘法,结果默认放在 AX 中;如果是 16 位乘法,结果高位默认在DX中存放,低位在AX中放。
    1
    2
    3
    格式如下:
    mul reg
    mul 内存单元
    1
    2
    3
    4
    5
    6
    7
    内存单元可以用不同的寻址方式给出,比如:
    mul byte ptr ds:[0]
    含义:
    (ax)=(al)*((ds)*16+0);
    mul word ptr [bx+si+8]
    含义:(ax)=(ax)*((ds)*16+(bx)+(si)+8)结果的低 16 位。
    (dx)=(ax)*((ds)*16+(bx)+(si)+8)结果的高 16 位。

    abc指令

    adc 是带进位加法指令,它利用了 CF 位上记录的进位值。
    指令格式:adc 操作对象1,操作对象2
    功能:操作对象1=操作对象1+操作对象2+CF
    比如指令adc ax,bx 实现的功能是:(ax)=(ax)+(bx)+ CF
    1
    2
    3
    4
    mov ax,2
    mov bx,1
    sub ax,bx(CF=1)
    abc ax,1 ;(ax)+1+CF=2+1+1=4
    1
    2
    3
    4
    mov ax,1
    add bx,1
    add ax,bx ;(CF=0)
    abc ax,1 ;(ax)+1+CF=2+1+0=3
    可以看出,adc指令比add指令多加了一个CF位的值。
    为什么要加上CF的值呢?CPU为什么要提供这样一条指令呢?
  • 加法可以分两步来进行:
    ①低位相加;
    ②高位相加再加上低位相加产生的进位值。;
    下面的指令和addax,bx具有相同的结果:
    1
    2
    add al,bl
    adc ah,bh
    编程,计算 1EF000H+201000H,结果放在ax(高16位)和bx(低16位)中。
    先将低 16 位相加,然后将高 16 位和进位值相加。程序如下。
    1
    2
    3
    4
    mov ax,001EH
    mov bx,0F000H
    add bx,1000H
    adc ax,0020H

    sbb指令

    sbb 是带借位减法指令,它利用了CF位上记录的借位值。
    指令格式:sbb操作对象1,操作对象2
    功能:操作对象1=操作对象1-操作对象2-CF
    比如指令 sbb ax,bx 实现的功能是:
    (ax)=(ax)-(bx)-CF
    sbb 指令执行后,将对 CF 进行设置。利用 sbb 指令可以对任意大的数据进行减法运算。
    比如,计算003E1000H-00202000H,结果放在ax,bx中,程序如下:
    1
    2
    3
    4
    mov bx,1000H
    mov ax,003EH
    sub bx,2000H
    sbb ax,0020H
    :::warning
    乘除ax存放低位dx存放高位
    abc和sbb运算ax存放高位,bx存放地位
    :::

    cmp指令

    cmp 是比较指令,cmp的功能相当于减法指令,只是不保存结果。cmp 指令执行后,将对标志寄存器产生影响。
    其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。
    下面的指令:
    1
    2
    3
    4
    5
    6
    mov ax,8
    mov bx,3
    cmp ax,bx
    执行后:
    (ax)=8,zf=0,pf=1,sf=0,cf=0,of=0。
    ZF=0(非零),PF=1(1的个数为偶数),SF(非负),CF(没借位),OF(没溢出)
    其实,我们通过cmp指令执行后,相关标志位的值就可以看出比较的结果。
  1. 如果 sf=1,而 of=0
    of=0,说明没有溢出,逻辑上真正结果的正负=实际结果的正负
    因sf=1,实际结果为负,所以逻辑上真正的结果为负,所以(ah)<(bh)。
  2. 如果 sf=1,而 of=l:
    of=1,说明有溢出,逻辑上真正结果的正负≠实际结果的正负
    因sf=1,实际结果为负。
    实际结果为负,而又有溢出,这说明是由于溢出导致了实际结果为负,简单分析下,就可以看出,如果因为溢出导致了实际结果为负,那么逻辑上真正的结果必然为正。
    这样,sf=1,of=1,说明了(ah)>(bh)。

    比较结果的条件转移指令

    下面是常用的根据无符号数的比较结果进行转移的条件转移指令。
    1
    2
    3
    4
    5
    6
    7
    指令格式      	含义					检测的相关标志位
    je 等于则转移 zf=1
    jne 不等于则转移 zf=0
    jb 低于则转移 cf=1
    jnb 不低于则转移 cf=0
    ja 高于则转移 cf=0 且 zf=O
    jna 不高于则转移 cf=1 或 zf=1
    这些指令比较常用,它们都很好记忆,它们的第一个字母都是 j,表示 jump;后面的
    字母表示意义如下。
    e:表示equal
    ne:表示 not equal
    b:表示 below
    nb:表示 not below
    a:表示above
    na:表示 not above
    案列
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    实现:如果ah=bh则ax+ax否则ax加bx
    cmp ah,bh
    je s
    add ah,bh
    jmp ok
    s:
    add ah,ah
    ok:
    mov ah,4ch
    int 21h

    Pushf和Popf

  • pushf的功能是将标志寄存器的值压栈,而popf是从栈中弹出数据,送入标志寄存器中。
  • pushf和popf,为直接访问标志寄存器提供了一种方法。
    :::warning
    标志寄存器与标志要分清
    :::

    标志寄存器

    ZF标志

    flag 的第6位是ZF,零标志位。它记录相关指令执行后,其结果是否为0。如果结果
    为0,那么zf=1;如果结果不为0,那么zf=0。
    1
    2
    3
    4
    5
    6
    mov ax,1
    sub ax,1
    执行后ax为0则ZF = 1
    mov ax,2
    sub ax,1
    执行后ax为1则ZF = 0

    PF标志

    flag 的第2位是PF,奇偶标志位。它记录相关指令执行后,其结果的所有bit位中1的个数是否为偶数。如果1的个数为偶数,pf=1,如果为奇数,那么 pf=0。
    1
    2
    3
    4
    5
    6
    7
    8
    比如,指令:
    mov al,1
    add al,10
    执行后,结果为00001011B,
    其中有 3(奇数)个 1,则 pf=0;
    mov al,1
    or al,2
    执行后,结果为 00000011B,其中有 2(偶数)个 1,则 pf=1;

    SF标志

    flag 的第 7 位是 SF,符号标志位。它记录相关指令执行后,其结果是否为负。如果
    结果为负,sf=1;如果非负,sf=0。
    计算机中通常用补码来表示有符号数据。计算机中的一个数据可以看作是有符号数,
    也可以看成是无符号数。比如:
  • 00000001B,可以看作为无符号数1,或有符号数+1;
  • 10000001B,可以看作为无符号数 129,也可以看作有符号数-127。

    CF标志

    flag 的第 0 位是 CF,进位标志位。一般情况下,在进行无符号数运算的时候,它记录了运算结果的最高有效位向更高位的进位值,或从更高位的借位值。
    对于位数为 N 的无符号数来说,其对应的二进制信息的最高位,即第 N-1 位,就是
    它的最高有效位,而假想存在的第 N 位,就是相对于最高有效位的更高位。
    1
    2
    3
    mov al,97h
    sub al,98h ;执行后:(al)=FFH,CF=1,CF记录了向更高位的借位值
    sub al,al ;执行后:(al)=0,CF=0,CF记录了向更高位的借位值

    OF标志

    flag 的第 11 位是 OF,溢出标志位。一般情况下,OF 记录了有符号数运算的结果是否发生了溢出。如果发生溢出,OF=1;如果没有,OF=0。
    一定要注意CF和OF的区别:CF是对无符号数运算有意义的标志位,而OF是对有符号数运算有意义的标志位。
    比如:
    1
    2
    mov al,98
    add al,99
  • 结果al为197超出了8位寄存器的范围(-128 ~ 127)发生溢出OF=1
  • 对于无符号数运算,98+99 没有进位CF=0;对于有符号数运算,98+98发生溢出OF=1
    1
    2
    mov al,0F0H
    add al,88H
    add 指令执行后:CF=1,OF=1。对于无符号数运算,0F0H+88H(101111000B) 有进位,CF=1;对
    于有符号数运算,0F0H+88H发生溢出,OF=1。
    1
    2
    mov al,0F0H
    add al,78H
    add 指令执行后:CF=1,OF=0。对于无符号运算,0F0H+78H(101101000B) 有进位,CF=1;对于
    有符号数运算,0F0H+78H不发生溢出,OF=0。
  • 我们可以看出,CF 和OF所表示的进位和溢出,是分别对无符号数和有符号数运算
    而言的,它们之间没有任何关系。

    DF标志

    flag的第10位是DF,方向标志位。在串处理指令中,控制每次操作后si、di的增减。
    df=0 每次操作后 si、di 递增;
    df=1 每次操作后 si、di 递减。
  • 如果df=0则:(si)=(si)+1 (di)=(di)+1
  • 如果df=1则:(si)=(si)-1 (di)=(di)-1
  • cld指令:将标志寄存器的df位置0
  • std指令:将标志寄存器的df位置1
    用汇编语言描述movsb
    1
    mov es:[di],byte ptr ds:[si]  ;汇编并不支持这种书写,这只是个描述
    了解movsb后复制字符串就变得简单多了
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    assume cs:codes,ds:datas

    datas segment
    db 'Welcome to masm!'
    db dup (0)
    datas ends

    codes segment
    mov ax,datas
    mov ds,ax
    mov si,0
    mov es,ax
    mov di,16
    mov cx,16
    cld ;将df设位0,正向传输
    rep movsb

    codes ends
    end

    debug对标志位的表示

    | 标志 | 值为1 | 值为零 |
    |-|-|-|
    | OF | OV | NV |
    | SF | NG | PL |
    | ZF | ZR | NZ |
    | PF | PE | PO |
    | CF | CY | NC |
    | DF | DN | UP |