汇编中一些简单的知识

下面的笔记是根据鱼c工作室里的汇编教学视频里的总结的,方便复习

1: Debug的使用:
配置好DosBox后,打开运行,输入e:,回车(之前配置的)。然后就可以使用Debug了

2: 命令:
.R命令查看、改变cpu寄存器的内容
执行后,会看到寄存器的内容,然后输入r ax,就可以在下面输入值,ax就会是改变后的值

. D命令查看内存中的内容

. E命令改写内存中的内容(机器指令的格式)
    //比如-e 1000:0 23 11 22 66就是将段地址为1000,偏移地址为0的数据写为23,1000:1为11,1000:2为22...

. U命令将内存中的机器指令翻译成汇编指令

. T命令执行一条机器指令

. A命令以汇编指令的格式在内存中写入一条机器指令
  1. 编写程序时,比如想将ffff赋值给ax,不可以mov ax,ffff,因为编译器不认识以字母开头的值,必须在前面加上0

  2. 汇编中可以用loop循环,cx保存循环的次数,每次循环,cx-1,cx为0时结束循环,比如88*99,可以
    mov ax,0
    mov cx,99

    s: add ax,88 
    loop s
    

5: 难道88*99真的要循环那么多次吗?于是乎,引入g命令来解决!也可以是p命令
比如执行时,查看一下cs和ip的值,用-u指令查看汇编指令的地址,然后直接-g 后面加上偏移地址
就可以直接跳转到该地址,相当于vs中在循环下面加个断点,然后跳到断点执行,就跳过了循环了

6:一段安全空间
并不是所有的内存空间都可以拿来用的,有些空间中可能有系统或其他程序的数据或代码,所以写入的话会引发错误
需要直接向内存写入内容时,就使用0:300-02ff这段空间

7:在8086CPU中,只有下面4个寄存器(bx,si,di,bp)可以用在”[]”中来进行内存单元的寻址
而且这四个寄存器可以单个出现,或者只能以四中组合出现:
bx和si,bx和di,bp和si,bp和di

* bp默认段地址在ss中

8:指令中要处理的数据是有长度的,比如c,c++里面int,char,double等等,汇编中通长有db(字节型),dw(字型),dd(双字型 )这几种类型,那么如何知道要处理的数据到底多长呢?

1)通过寄存器名指明:
 mov ax,bx  //字型
     mov al,bl  //字节型

2) 在没有寄存器名存在的情况下,用操作符X ptr指明内存单元的长度,X在汇编指令中可以为word或byte
 mov word ptr ds:[bx],1
     mov byte ptr ds:[bx],2 

注意,有些指令只能进行字单元操作或者字节操作,比如push只能对字单元操作,因为每次push前都会sp=sp-2

3)其他...

9:div(division)指令
注意:
1):除数,8为或16位,在寄存器或内存单元中

 2):被除数,(默认)放在AX或AX和DX中
       !!为什么是AX 或者 AX和DX 呢?
           除数   被除数
    8位   16位(AX)
    16位  32位(AX+DX,AX存放低16位,DX存放高16位)  

      除数:8位 16位
  商:  AL     AX
      余数:AH  DX

     3): 指令格式
    div reg       //div加上一个寄存器
    div 内存单元

10:伪指令dup
格式: db(或者dw,dd等) 重复的次数 (重复的数据)

实例:
# db 3 dup(0)
  相当于db 0,0,0

# db 3 dup(0,1,2)
      相当于db 0,1,2,0,1,2,0,1,2

dup是编译器区识别的,比如要定义容量为30个字节的栈段,可以
   stack segment

       db 30 dup(0)

   stack ends
这样就很方便了

11:转移指令
# 8086cpu的转移指令分为以下几类:
* 无条件转移指令(如:jmp)
* 条件转移指令
* 循环指令(如:loop)
* 过程(相当于函数)
* 中断

 # 操作符offset在汇编语言中是由编译器处理的符号,它的功能是取得标号的偏移地址


 a. 无条件转移:
   # jmp为无条件转移,可以只改变IP,也可以同时修改CS和IP
 jmp指令要提供两条信息:
    * 转移的目的地址
    * 转移的距离(段间距离,段内短转移,段内近转移)

 //==============================段内转移====================================
    1)jmp short 标号 (转到标号处执行指令)
        这种格式的jmp指令实现的是段内短转移,它对IP的修改范围为-128-127,即向前转移最多                    可以越过128个字节,向后转移最多可以越过127个字节

            assume cs:codesg
        codesg segment
         start: mov ax,0
            jmp short s
            add ax,1    //此处ax+1未执行,由jmp直接跳过了,所以最终ax为1
            s:inc ax
        codesg ends
        end start

        实际上,指令"jmp short 标号"的功能为ip=ip+8位位移
            * 8位位移="标号"处的地址->jmp指令后的第一个字节的地址
            * short指令表明此处的位移为8位位移
            * 8位位移的范围为-128-127,用补码表示
            * 8位位移由编译程序在编译时算出

    2) jmp near ptr 标号(和jmp short 标号类似,不过是段内近转移)

            指令"jmp near ptr 标号"的功能为ip=ip+16位位移
            * 16位位移="标号"处的地址->jmp指令后的第一个字节的地址
            * near指令表明此处的位移为16位位移
            * 16位位移的范围为-32769-32768,用补码表示
            * 16位位移由编译程序在编译时算出

    3)jmp 16位寄存器
        功能:IP=16位寄存器

    4)jmp word ptr 内存单元地址
        功能:从内存单元地址处开始存放一个字,是转移的目的的偏移地址

 //==============================段间转移=======================================
     1)jmp far ptr 标号(实现的是段间转移,又称为远转移)
        功能:
            * cs=标号所在段的段地址
            * ip=标号所在段中的偏移地址    

    2)jmp dword ptr 内存单元地址
        功能:从内存单元地址处开始存放两个字,高地址处的字是转移目的的段地址,低地址处是                 偏移地址

b.有条件转移

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

    指令格式: jcxz 标号
           如果cx=0,则转移到标号处执行,相当于(if(cx==0) jmp short 标号)

2)loop
    loop为循环指令,所有的循环指令都是短转移,在对应的机器码中包含转移的位移,对ip的修改范围
    为-128-127

    指令格式:loop 标号
         每次执行后cx=cx-1,如果cx!=0,则转移到标号处执行,与jcxz相反,相当于
        if(--cx!=0)
            jmp short 标号

12 call和ret指令
# call和ret指令都是转移指令,他们都修改IP,或同时修改CS和IP

1)ret指令用栈中的数据,修改IP的内容,从而实现近转移
     Cpu执行ret指令时,进行下面两步操作
    a: IP=ss*16+sp
    b: sp=sp+2
    //相当于pop IP 

2) retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移
         Cpu执行ret指令时,进行下面四步操作
    a: IP=ss*16+sp
    b: sp=sp+2  
    c: CS=ss*16+sp
    d: sp=sp=2
    //相当于pop IP
        pop CS

3)call指令经常跟ret指令配合使用,因此cpu执行call指令,进行两步操作
    a:将当前的IP或CS和IP压入栈中
    b:转移(jmp)
     # call指令不能实现短转移,除此之外,call指令实现转移的方法和jmp指令的原理相同 

    格式:* call 标号(将当前IP压入栈后,转移到标号处执行指令)
        cpu执行此种格式的call指令时,进行如下操作:
            sp=sp-2
            ss*16+sp=ip

            ip=ip+16位位移(“标号”处的地址减去call指令后的第一个字节的地址)
            //相当于push IP
                jmp near ptr 标号                     

              * call far ptr 标号 (实现的是段间转移)
        cpu执行此种格式的call指令时,进行如下操作:
            sp=sp-2
            ss*16+sp=cs
            sp=sp-2
            ss*16+sp=ip

            cs=标号所在的段地址
            ip=标号所在的偏移地址

            //相当于push CS
                push IP 

                jmp far ptr 标号  



          * call 16位寄存器
        功能:
            sp=sp-2
            ss*16+sp=ip
            ip=16位寄存器

            相当于push IP
                  jmp 16位寄存器    


       # 转移地址在内存中的call指令(有两种格式)
        * call word ptr 内存单元地址
            相当于  push IP
                    jmp word ptr 内存单元地址

        * call dword ptr 内存单元地址
            相当于  push CS
                push IP
                    jmp dword ptr 内存单元地址

 #可以写一个具有一定功能的程序段,我们称其为子程序,在需要的时候,用call指令转去执行,可是执行完子程序之后,如何     让cpu继续向下执行呢?就是ret了,call指令后面的指定的地址被存储在栈中,所以可以在子程序到后面使用ret指令,用栈中       的数据设置IP的值从而转到call指令后面的代码处继续执行,这样就可以利用call和ret来实现子程序的机制

13:mul指令(乘法指令)
* 相乘的两个数,要么都是8位,要么都是16位
8位:AL中和8位寄存器或内存字节单元中,结果在AX中
16位:AX中和16位寄存器或内存字单元中,结果在DX(高位)和AX(低位)中
* 格式:
mul reg
mul 内存单元
int(): 描述性运算符,取商,比如int(11/3)=9
rem(): 描述性运算符,取余数,比如rem(11/3)=2

13:标志寄存器
* 8086cpu的flag寄存器的结构
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
OP DF IF TF SF ZF AF PF CF

1):ZF标志  (Zero)
    falg的第6位,零标志位。它记录相关指令执行后,结果为0,则ZF=1,否则为0
    注意:在8086cpu的指令集中,有的指令的执行是影响标志位的,比如:add,sub,mul,div,inc,or,and等,它们大多
    都是运算指令(进行逻辑或算数运算);而有的指令没有影响,比如:mov,push,pop等,大都是传送指令

2):PF标志  (Parity n.平价,价值对等; 同等,平等; 奇偶性; )
    falg的第2位,奇偶标志位。记录指令执行后,结果的最低位字节中“1”的个数若为偶数,则PF=1,否则为0

3):SF标志 (sign)
    falg的第7位,符号标志位。它记录执行指令后,若结果为负,SF=1,否则SF=0,所以说,SF是cpu对有符号数元算             结果的一种记录,它记录数据的正负,无符号数就不用管此标志了

4): CF标志
    falg的第0位,进位标志位。主要用来反映运算符是否产生进位或借位,如果进位,CF=1,否则为0

5):OF标志
    falg的第11位,溢出标志位。
    * CF和OF到区别:
        # CF是对无符号数运算有意义的标志位;
        # 而OF是对有符号数运算有意义的标志位            

6) : adc指令
    带进位加法指令。它利用了CF位上记录的进位值。
        格式: adc 操作对象1,操作对象2
        功能: 操作对象1=操作对象1+操作对象2+CF 
7):sbb指令
    带借位减法指令,它利用了CF位上记录的借位值
        格式:sbb 操作对象1,操作对象2
        功能:操作对象1=操作对象1-操作对象2-CF 

8):cmp指令
    格式:cmp 操作对象1,操作对策2
    功能:计算操作对象1-操作对象2但并不保存结果,仅仅根据计算结果对标志寄存器进行设置
          * 根据无符号数的比较结果进行转移的条件转移指令,他们检测ZF,CF的值
          * 根据有符号数的比较结果进行转移的条件转移指令,他们检测SF,OF和ZF的值


9):检测比较结果的条件转移指令
//=================检测比较结果的条件转移指令===========================
                             条件转移指令小结
        指令      含义          检测的相关标志位
        je      等于则转移       ZF=1    jump equal
        jne     不等于则转移      ZF=0    jump not equal
        jb      低于则转移       CF=1    jump below 
        jnb     不低于则转移      CF=0
        ja      高于则转移       CF=0,ZF=0  jump above
        jna     不高于则转移      CF=1或ZF=1
        *上面这些都是检测无符号数的指令

10):DF标志和串传送指令
    falg的第10位,方向标志位。
    # 在串处理指令中,控制每次操作后si,di的增减
      * DF=0时:每次操作后,si,di递增
      * DF=1时,每次操作后,si,di递减 
    格式1:movsb
    功能:(以字节为单位传送)
        a. es*16+di=ds*16+si
        b. 如果DF=0,则si=si+1
                  di=di+1
           否则:     si=si-1
                  di=di-1   
        movsb的功能是将ds:si指向的内存单元的字节送入es:di中,然后根据标志寄存器DF位的值,将si和di
        递增或递减

    格式2:movsw
    功能:(以字为单位传送)
    将ds:si指向的内存单元的字单元送入es:di中,然后根据标志寄存器DF位的值,将si和di
        递增2或递减2

    一般来说,movsb和movsw都和rep配合使用,格式:rep movsb
    rep作用是根据cx的值,重复执行后面传传送指令,这rep movsb可以循环实现cx个字符的传送,那么有没有设置DF
    的值的指令呢?下面两条指令就是:
        cld指令:将标志寄存器的DF位设置为0
        std指令:将标志寄存器的DF位设置为1

11):pushf和popf
    pushf:将标志寄存器的值压栈
    popf:从栈中弹出数据,送入标志寄存器

12):标志寄存器在Debug中的表示

14:内中断*
1):内中断的产生
根据中断源的不同,可以把中断分为硬件中断和软件中断两大类,而硬件中断又可以分为外部中断和内部中断两类 ,主要讲解硬件中断。外部中断(如:键盘中断,打印机中断等)是可以屏蔽的中断,内部中断(如:除数为0,突然断电 ,运算溢出等)不可以屏蔽。
软件中断其实并不是真正的中断,只是可被调用执行的一般程序以及Dos系统功能调用(INT 21h)等,都是软件中断

2):中断处理程序
    中断产生后,要定位中断处理程序(中断类型码就是用来定位中断处理程序的),需要知道其段地址和偏移地址,但             是如何根据中断类型码得到中断处理程序的段地址和偏移地址呢?这就要引入“中断向量表”了。

3):中断向量表
    cpu用8位中断类型码通过中断向量表找到相应的中断处理程序的入口地址,中断向量表在内存中存放,其中存放着
    256个中断源所对应的中断处理程序的入口,在8086cpu中,中断向量表在内存0000:0000到0000:03ff处的1024个字          节,(一个物理地址由段地址和偏移地址组成,即2个字,4个字节,4*256=1024)

4): 中断过程
    a.(从中断信息中)取得中断类型码N
    b. 标志寄存器的值入栈(保护标志位)(pushf)
    c.设置标志寄存器的第8位TF和第9位IF的值为0(这样做的目的后面再说)(TF=0,IF=0)
    d.CS的内容入栈(push CS)
    e.IP的内容入栈(push IP)
    f.从内存地址为中断类型码*4和中断类型码*4+2的两个字单元中读取中断处理程序的入口地址设置IP和CS
      ((IP)=(N*4),(CS)=(N*4+2))     

5): 中断处理程序
    常规的步骤:
        a. 保存用到的寄存器
        b. 处理中断
        c. 恢复用到的寄存器
        d. 用iret指令返回
    * iret指令的功能用汇编语法描述为:
        pop IP
        pip CS
        popf

6):单步中断
    cpu在执行完一条指令之后,如果检测到标志寄存器的TF位为1,则产生单步中断,引发中断过程

15: int指令
1)int指令
int格式:int n //n为中断类型码,功能是引发中断过程

2)编写供应用程序调用的中断例程
3)对int,iret和栈的深入理解
4)BIOS和DOS所提供的中断例程
5)BIOS和DOS中断例程的安装过程

6)BIOS中断例程应用
    int 10h中断例程是BIOS提供的中断例程,其中包含了多个和屏幕输出相关的子程序
    BIOS和DOS提供的中断例程,都是用ah来传递内部子程序的编号    

7)DOS中断例程应用
    int 21h中断例程是DOS提供的中断例程,其中包含了DOS提供给程序员在编程时调用的子程序
    之前一直是这么用的:
        mov ah,4ch  //程序返回
        mov al,0    //返回值
        int 21h

16:端口
cpu可以直接读写3个地方的数据(cpu内部寄存器,内存单元,端口)

1)端口的读写
    * 对端口的读写只有两条(不能用mov,push,pop等内存读写指令),in和out,注意:在int和out指令中,只能用
      ax或al来存放从端口中读取或写入的数据,访问8位端口用al,16位端口用ax

    * 对0-255以内的端口进行读写:
        int al,20h  //从20h端口读入一个字节
        out 20h,al  //向20h断口写入一个字节

    * 对256-65535的端口进行读写时,端口号放在dx中
        mov dx,3f8h //将端口号3f8h送入dx
        in al,dx    //从3f8h端口读入一个字节
        out dx,al   //向3f8h端口写入一个字节

2)CMOS RAM芯片
    pc机中有一个CMOS RAM芯片,其特征如下:
        * 包含一个实时钟和一个有128个存储单元(存储的就是一个开机信息,该信息给BIOS主板)的RAM存储器。(            早期的计算机为64个字节)
        * 该芯片主要靠电池供电,因此,关机后其内部的实时钟任可工作,RAM的信息不会丢失
        * 128个字节的RAM中,内部实时钟占用0-0dh单元来保存时间信息,其余大部分单元用于保存系统配置信息                 供系统启动时BIOS读取
        * 该芯片内部有两个端口,端口地址为70h和71h,cpu通过这两个端口读写CMOS RAM
        * 70h为地址端口,存放要访问的CMOS RAM单元的地址:71h为数据端口,存放从选定的CMOS RAM单元中读                    取的数据,或者写入到其中的数据


3) shl和shr指令     
    shl和shr是逻辑移位指令,后面会用到移位指令,这里先讲一下      

    * shl:逻辑左移指令,功能:
        a.将一个寄存器或内存单元中的数据向左移位
        b.将最后移出的一位写入CF中
        c.最低位用0补充       
        例如:
            mov al,01001000b
            shl al,1    ;将al中的数据左移一位
        执行后,(al)=10010000b,CF=0(最高位移出的放入CF)

        注意:如果移动位数大于1时,必须将移动位数放在cl中,比如:
        mov al,01010001b
        mov cl,3
        shl al,cl
        执行后,(al)=10001000b,这里移出的是前面010三位,所以最后移出的一位就是右边从0,所以CF=0、

    * shr:自己体会,不用写了

4)CMOS RAM存储的时间信息
    * 在CMOS RAM中,存放着当前时间:
        秒:  00h
        分:      02h
        时:    04h
        日:  07h
        月:  08h
        年:  09h
       这6个信息的长度都为1个字节,这些数据以BCD码(会计用到比较多)的方式存放:
        数码:0        1   2   3   4

        BCD码:0000   0001    0010    0011    0100

        数码:5        6   7   8   9

        BCD码:0101   0010    0111    1000    1001

        比如十进制数26,对应的BCD码为:0010 0010
        所以,一个字节可以表示两个BCD码,CMOS RAM存储    时间信息的单元中,存储了两个BCD码表示的两位十            进制数,高4位的BCD码表示十位,低4位的BCD码表示个位 

17:外中断
* 以前我们讨论的都是CPU对指令的执行。我们知道,CPUA在计算机系统中,除了能够执行指令,进行运算之外,还应该能够
对外部设备进行控制,接收它们的输入,向它们输出。也就是说,CPU除了有运算能力外,还要有I/O能力
1):接口芯片和端口
2):外中断信息
在PC系统中,外中断源一共有两类:
a. 可屏蔽中断
b. 不可屏蔽中断

    * 可屏蔽中断时CPU可以不响应的中断,CPU是否响应可屏蔽中断,要看标志寄存器的IF位的设置,现在知道了之前
      内中断将IF设置为0的原因了吧,原因就是在进入中断处理程序后,禁止其他的可屏蔽中断
    * 8086cpu提供的设置IF的指令如下:
        sti,用于设置IF=1
        cli,用于设置IF=0

    * 外中断中,不可屏蔽中断的中断类型码固定为2,所以中断过程中,不必像内中断那样取中断类型码
    * 不可屏蔽中断的中断过程:
        a. 标志寄存器入栈,IF=0,TF=0
        b. CS,IP入栈
        c. (IP)=(8),(CS)=(0AH)  //国定的一个地址

3):pc机键盘出处理过程
    a.键盘输入
    b.引发9号中断
    c.执行int9中断例程
    * BIOS提供了int 9中断例程,用来进行基本的键盘输入处理

4):编写int9中断例程(对键盘所处理的一个中断)
5):安装新的int'9中断例程
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值