zoukankan      html  css  js  c++  java
  • 汇编学习

    2015年6月29日 22:40:52
    一、基础
    1、寄存器:CPU里面的存储数据的器件,一个CPU里面可以有多个寄存器,AX是其中一个寄存器的代号,BX也是。

    1.7 CPU对存储器的读写
    CPU要想进行数据的读写,必须和外部器件(标准的说法是芯片)进行三类信息的交互:

    存储单元的地址(地址信息)
    器件的选择,读或写命令(控制信息)
    读或写的数据(数据信息)

    1.8 地址总线
    CPU是通过地址总线来指定存储单元的。

    地址总线上能传送多少个不同的信息,CPU就可以对多少个存储单元进行寻址。
    一个CPU有N根地址总线,则可以说这个CPU的地址总线的宽度为N。

    这样的CPU最多可以寻找2的N次方个内存单元。

    1.9 数据总线
    CPU与内存或其它器件之间的数据传送是通过数据总线来进行的。

    数据总线的宽度决定了CPU和外界的数据传送速度。

    1.10 控制总线
    CPU对外部器件的控制是通过控制总线来进行的。在这里控制总线是个总称,控制总线是一些不同控制线的集合。
    有多少根控制总线,就意味着CPU提供了对外部器件的多少种控制。
     所以,控制总线的宽度决定了CPU对外部器件的控制能力。

    控制总线上发送的控制信息

    小结
    (1)汇编指令是机器指令的助记符,同机器指令一一对应。
    (2)每一种CPU都有自己的汇编指令集。
    (3)CPU可以直接使用的信息在存储器中存放。
    (4)在存储器中指令和数据没有任何区别,都是二进制信息。
    (8)每一个CPU芯片都有许多管脚,这些管脚和总线相连。也可以说,这些管脚引出总线。一个CPU可以引出三种总线的宽度标志了这个CPU的不同方面的性能:

      地址总线的宽度决定了CPU的寻址能力;
      数据总线的宽度决定了CPU与其它器件进行数据传送时的一次数据传送量;
      控制总线宽度决定了CPU对系统中其它器件的控制能力。
    

    二、寄存器
    2.1
    一个典型的CPU由运算器、控制器、寄存器等器件组成,这些器件靠内部总线相连。
    区别:
    内部总线实现CPU内部各个器件之间的联系。
    外部总线实现CPU和主板上其它器件的联系。

    8086CPU有14个寄存器 它们的名称为:
    AX、BX、CX、DX、SI、DI、SP、BP、
    IP、CS、SS、DS、ES、PSW。

    AX、BX、CX、DX 通常用来存放一般性数据被称为通用寄存器。

    8086上一代CPU中的寄存器都是8位的;
    为保证兼容性,这四个寄存器都可以分为两个独立的8位寄存器使用。
    AX可以分为AH和AL;
    BX可以分为BH和BL;
    CX可以分为CH和CL;
    DX可以分为DH和DL。

    2.2 字在寄存器中的存储

    一个字可以存在一个16位寄存器中,这个字的高位字节和低位字节自然就存在这个寄存器的高8位寄存器和低8位寄存器中。

    2.5 16位结构的CPU
    概括的讲,16位结构描述了一个CPU具有以下几个方面特征:
    1、运算器一次最多可以处理16位的数据。

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

    3、寄存器和运算器之间的通路是16位的。

    8086有20位地址总线,可传送20位地址,寻址能力为1M。

    8086内部为16位结构,它只能传送16位的地址,表现出的寻址能力却只有64K。
    问题:那么,8086CPU如何用内部16位的数据转换成20位的地址呢?
    8086CPU采用一种在内部用两个16位地址合成的方法来形成一个20位的物理地址~

    8086CPU读写内存时,发生了这么一些事:
    CPU中的相关部件提供两个16位的地址,一个称为段地址,另一个称为偏移地址;
    段地址和偏移地址通过内部总线送入一个称为地址加法器的部件;
    地址加法器将两个16位地址合并成一个20位的地址;
    ……

    地址加法器合成物理地址的方法:物理地址=段地址×16+偏移地址

    2.8 段的概念
    错误认识:内存被划分成了一个一个的段,每一个段有一个段地址。
    其实:内存并没有分段,段的划分来自于CPU,由于8086CPU用“(段地址×16)+偏移地址=物理地址”的方式给出内存单元的物理地址,使得我们可以用分段的方式来管理内存。

    以后,在编程时可以根据需要,将若干地址连续的内存单元看作一个段,用段地址×16定位段的起始地址(基础地址),用偏移地址定位段中的内存单元。
    (1)段地址×16 必然是 16的倍数,所以一个段的起始地址也一定是16的倍数;
    (2)偏移地址为16位,16 位地址的寻址能力为 64K,所以一个段的长度最大为64K。

    结论:CPU可以用不同的段地址和偏移地址形成同一个物理地址。

    内存单元地址小结
    在8086PC机中,存储单元的地址用两个元素来描述。即段地址和偏移地址。

    “数据在21F60H内存单元中。”对于8086PC机的两种描述:
    (a)数据存在内存2000:1F60单元中;
    (b)数据存在内存的2000段中的1F60H单元中。

    可根据需要,将地址连续、起始地址为16的倍数的一组内存单元定义为一个段。

    2.9 段寄存器
    段寄存器就是提供段地址的。
    8086CPU有4个段寄存器:CS(代码段寄存器)、DS(数据段寄存器)、SS(堆栈段寄存器)、ES(附加段寄存器)
    当8086CPU要访问内存时,由这4个段寄存器提供内存单元的段地址。

    CS和IP是8086CPU中最关键的寄存器,它们指示了CPU当前要读取指令的地址。

    CS为代码段寄存器;
    IP为指令指针寄存器。
    

    在任何时候,CPU将CS、IP中的内容当作指令的段地址和偏移地址,用它们合成指令的物理地址,到内存中读取指令码,执行。
    如果说,内存中的一段信息曾被CPU执行过的话,那么,它所在的内存单元必然被CS:IP指向过。

    2.11 修改CS、IP的指令
    在CPU中,程序员能够用指令读写的部件只有寄存器,程序员可以通过改变寄存器中的内容实现对CPU的控制。

    CPU从何处执行指令是由CS、IP中的内容决定的,程序员可以通过改变CS、IP中的内容来控制CPU执行目标指令。

    我们如何改变CS、IP的值呢?

    同时修改CS、IP的内容:
    转移指令jmp 段地址:偏移地址
    jmp 2AE3:3
    jmp 3:0B16

    功能:用指令中给出的段地址修改CS,偏移地址修改IP。
    

    仅修改IP的内容:

    jmp 某一合法寄存器
    	jmp ax   (类似于 mov IP,ax)
    	jmp bx
    
    功能:用寄存器中的值修改IP。
    

    8086机中,任意时刻,CPU将CS:IP指向的内容当作指令执行。

    3、8086CPU的工作过程:
    (1)从CS:IP指向内存单元读取指令,读取的指令进入指令缓冲器;
    (2)IP指向下一条指令;
    (3)执行指令。(转到步骤(1),重复这个过程。)

    4、8086CPU提供转移指令修改CS、IP的内容。

    2.22 DEBUG
    1、运行---cmd---debug进入
    R命令查看、改变CPU寄存器的内容;
    D命令查看内存中的内容;
    E命令改写内存中的内容;
    U命令将内存中的机器指令翻译成汇编指令;
    T命令执行一条机器指令;
    A命令以汇编指令的格式在内存中写入一条机器指令。

    到了 int 21,我们要用P命令执行:

    g命令调试时能跳到想去的地址,特别是调试循环代码时
    进入循环时用p命令也可以退出循环

    3.6 栈
    栈有两个基本的操作:入栈和出栈。
    入栈:将一个新的元素放到栈顶;
    出栈:从栈顶取出一个元素。
    PUSH(入栈)
    POP (出栈)

    push ax:将寄存器ax中的数据送入栈中;
    pop ax :从栈顶取出数据送入ax。
    8086CPU的入栈和出栈操作都是以字为单位进行的。

    CPU如何指导当前要执行的指令所在的位置?

    答:寄存器CS和IP中存放着当前指令的段地址和 偏移地址。

      8086CPU中,有两个寄存器:
    段寄存器SS  存放栈顶的段地址
    寄存器SP  存放栈顶的偏移地址
    任意时刻,SS:SP指向栈顶元素。

    push ax
    (1)SP=SP–2;
    (2)将ax中的内容送入SS:SP指向的内存单元处,SS:SP此时指向新栈顶。
    pop ax
    (1)先把SS:SP地址的值给ax
    (2)SP=SP+2

    问题3.6:如果我们将10000H~1000FH 这段空间当作栈,初始状态栈是空的,此时,SS=1000H,SP=?
    指向10010H,最高地址单元的下一个地址

    我们将10000H~1000FH 这段空间当作栈段,SS=1000H,栈空间大小为16 字节 ,栈最底部的字单元地址为1000:000E。
     
    任意时刻,SS:SP指向栈顶,当栈中只有一个元素的时候,
    SS = 1000H,SP=000EH。

    栈为空,就相当于栈中唯一的元素出栈,出栈后,SP=SP+2 ,SP 原来为 000EH,加 2 后SP=10H

    所以,当栈为空的时候,SS=1000H,SP=10H。

    3.8 栈顶超界的问题

    SS和SP只记录了栈顶的地址,依靠SS和SP可以保证在入栈和出栈时找到栈顶。
    可是,如何能够保证在入栈、出栈时,栈顶不会超出栈空间?
    当栈满的时候再使用push指令入栈,
    栈空的时候再使用pop指令出栈,
    都将发生栈顶超界问题。

    栈顶超界是危险的。因为我们既然将一段空间安排为栈 ,那么在栈空间之外的空间里很可能存放了具有其他用途的数据、代码等,这些数据、代码可能是我们自己的程序中的,也可能是别的程序中的。
    (毕竟一个计算机系统并不是只有我们自己的程序在运行)

    结论:
    我们在编程的时候要自己操心栈顶超界的问题 ,要根据可能用到的最大栈空间,来安排栈的大小,防止入栈的数据太多而导致的超界;

    执行出栈操作的时候也要注意,以防栈空的时候继续出栈而导致的超界。

    3.9 push、pop指令
    push和pop指令是可以在寄存器和内存之间传送数据的。
    栈空间当然也是内存空间的一部分,它只是一段可以以一种特殊的方式进行访问的内存空间。
    push和pop指令的格式(1)
    push 寄存器:将一个寄存器中的数据入栈
    pop寄存器:出栈,用一个寄存器接收出栈的数据

      例如:push ax
            pop bx
    

    push和pop指令的格式(2)
    push 段寄存器:将一个段寄存器中的数据入栈
    pop段寄存器:出栈,用一个段寄存器接收出栈的数据

      例如:push ds
            pop es
    

    push和pop指令的格式(3)
    push内存单元:将一个内存单元处的字入栈(栈操作都是以字为单位)
    pop 内存单元:出栈,用一个内存字单元接收出栈的数据
    例如:push [0]
    pop [2]

    指令执行时 ,CPU 要知道内存单元的地址,可以在 push、pop 指令中给出内存单元的偏移地址,段地址在指令执行时,CPU从ds中取得
    

    结论
    push、pop 实质上就是一种内存传送指令,可以在寄存器和内存之间传送数据,与mov指令不同的是,push和pop指令访问的内存单元的地址不是在指令中给出的,而是由SS:SP指 出的。

    同时,push和pop指令还要改变 SP 中的内容。

    push、pop 等栈操作指令,修改的只是SP。也就是说,栈顶的变化范围最大为:0~FFFFH。

    提供:SS、SP指示栈顶;改变SP后写内存的入栈指令;读内存后改变SP的出栈指令。

    这就是8086CPU提供的栈操作机制。

    栈的综述
    (1)8086CPU提供了栈操作机制,方案如下:
    在SS,SP中存放栈顶的段地址和偏移地址;
    提供入栈和出栈指令,他们根据SS:SP指示的地址,按照栈的方式访问内存单元。

    (2)push指令的执行步骤:
    1)SP=SP-2;
    2)向SS:SP指向的字单元中送入数据。
    (3)pop指令的执行步骤:
    1)从SS:SP指向的字单元中读取数据;
    2)SP=SP-2。
    (4)任意时刻,SS:SP指向栈顶元素。
    (5)8086CPU只记录栈顶,栈空间的大小我们要自己管理。
    (6)用栈来暂存以后需要恢复的寄存器的内容时 ,寄存器出栈的顺序要和 入栈的顺序相反。
    (7)push、pop实质上是一种内存传送指令,注意它们的灵活应用。

    栈是一种非常重要的机制,一定要深入理解,灵活掌握。

    3.10 栈段
    前面讲过,对于8086PC机,在编程时,我们可以根据需要 ,将一组内存单元定义为一个段。

    我们可以将长度为 N(N ≤64K )的一组地址连续、起始地址为16的倍数的内存单元,当作栈来用,从而定义了一个栈段。
    比如我们将10010H~1001FH 这段长度为 16 字节的内存空间当作栈来用,以栈的方式进行访问。

    这段空间就可以成为栈段,段地址为1000H,大小为16字节。

    段的综述
    我们可以将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内的单元。这完全是我们自己的安排。

    我们可以用一个段存放数据,将它定义为“数据段”;
    我们可以用一个段存放代码,将它定义为“代码段”;
    我们可以用一个段当作栈,将它定义为“栈段”;
    我们可以这样安排,但若要让CPU按照我们的安排来访问这些段,就要:

    对于数据段,将它的段地址放在 DS中,用mov、add、sub等访问内存单元的指令时,CPU就将我们定义的数据段中的内容当作数据段来访问;
    我们可以这样安排,但若要让CPU按照我们的安排来访问这些段,就要:

    对于数据段,将它的段地址放在 DS中,用mov、add、sub等访问内存单元的指令时,CPU就将我们定义的数据段中的内容当作数据段来访问;
    对于代码段,将它的段地址放在 CS中,将段中第一条指令的偏移地址放在IP中,这样CPU就将执行我们定义的代码段中的指令;

    对于栈段,将它的段地址放在SS中,将栈顶单元的偏移地置放在 SP 中,这样CPU在需要进行栈操作的时候,比如执行 push、pop 指令等,就将我们定义的栈段当作栈空间来用。

    可见,不管我们如何安排 ,CPU 将内存中的某段内存当作代码 ,是因为CS:IP指向了那里;CPU将某段内存当作栈 ,是因为 SS:IP 指向了那里。

    我们一定要清楚 ,什么是我们的安排,以及如何让CPU按我们的安排行事。

    要非常的清楚CPU的工作机理,才能在控制CPU来按照我们的安排运行的时候做到游刃有余。

  • 相关阅读:
    macOS 上配置 Lua
    Oracle.ManagedDataAccess.dll
    offer
    Costura.Fody
    日志系统
    实战框架ABP
    什么是算法?
    HTTP状态码->HTTP Status Code
    How to untar a TAR file using Apache Commons
    python实践3:cursor() — 数据库连接操作
  • 原文地址:https://www.cnblogs.com/poli/p/4609179.html
Copyright © 2011-2022 走看看