汇编一 - 简介、总线、内存、寻址

汇编语言简介

汇编语言的发展

  1. 机器语言
    1. 由0和1组成
  2. 汇编语言(Assembly Language)
    1. 用符号代替了0和1,比机器语言便于阅读和记忆
  3. 高级语言
    1. C\C++\Java\Swift等,更接近人类自然语言
  4. 操作:将寄存器BX的内容送入寄存器AX
    1. 机器语言:1000100111011000
    2. 汇编语言:mov ax, bx
    3. 高级语言:ax = bx;
  5. 如下图:

    图1

    1. 汇编语言机器语言一一对应,每一条机器指令都有与之对应的汇编指令
    2. 汇编语言可以通过编译得到机器语言,机器语言可以通过反汇编得到汇编语言
    3. 高级语言可以通过编译得到汇编语言\机器语言,但汇编语言\机器语言几乎不可能还原成高级语言
      1. 因为高级语言写不同的代码可能汇编语言是一样的。

汇编语言的特点

  1. 可以直接访问、控制各种硬件设备,比如存储器、CPU等,能最大限度地发挥硬件的功能
  2. 汇编指令是机器指令的助记符,同机器指令一一对应。每一种CPU都有自己的机器指令集\汇编指令集,所以汇编语言不具备可移植性
  3. 知识点过多,开发者需要对CPU等硬件结构有所了解,不易于编写、调试、维护
  4. 不区分大小写,比如mov和MOV是一样的

汇编语言的用途

  1. 编写驱动程序、操作系统(比如Linux内核的某些关键部分)
  2. 对性能要求极高的程序或者代码片段,可与高级语言混合使用(内联汇编)
  3. 软件安全
    1. 病毒分析与防治
    2. 逆向\加壳\脱壳\破解\外挂\免杀\加密解密\漏洞\黑客
  4. 是理解整个计算机系统的最佳起点和最有效途径
  5. 为编写高效代码打下基础

汇编语言的种类

  1. 目前讨论比较多的汇编语言有
    1. 8086汇编(8086处理器是16bit的CPU)
    2. Win32汇编
    3. Win64汇编
    4. AT&T汇编(Mac、iOS模拟器)
      1. 因为iOS的模拟器是运行在MAC上的,所以是MAC
    5. ARM汇编(嵌入式、iOS设备)
  2. 入门建议先从学些8086汇编开始
    1. 结构简洁、经典
    2. 参考书籍:王爽《汇编语言》

学前须知

  1. 要想学好汇编语言,首先要对CPU等硬件结构有一定的了解
  2. 软件\程序的执行过程如下: 图1
  3. 最为关键的是需要了解CPU和内存
  4. 在学习汇编语言过程中,遇到的绝大部分指令都是跟内存、CPU有关的

总线

  1. 每一个CPU芯片都有许多管脚,这些管脚和总线相连,CPU通过总线跟外部器件进行交互
  2. 总线:一根根导线的集合
  3. 总线的分类
    1. 地址总线
    2. 数据总线
    3. 控制总线
  4. 视图: 图1

  5. 三种总线的使用:
    1. CPU从内存的3号单元读取数据 图1
      1. 通过地址总线将3这个数据输送到内存,内存就会知道要访问第3个单元的数据
        1. 即地址总线用来寻址,寻找某个内存空间
        2. 用数电知识来解析就是存储器的哪些单元的引脚有效
      2. 控制总线告诉内存是要读取还是写这个单元,此时为读
        1. 用于控制读写
        2. 用数电的知识解析就是用来控制数据总线连接的晶闸管正向还是逆向导通。
      3. 这个时候就会通过数据总线将这个数据(08)传送给CPU。
        1. 用来传送数据
        2. 用数电的知识解析就是通过数据总线上的电平高低来获取二进制数据。
  6. 地址总线
    1. 它的宽度决定了CPU的寻址能力
    2. 8086的地址总线宽度是20,所以寻址能力是1M( 2^20  )
  7. 数据总线
    1. 它的宽度决定了CPU的单次数据传送量,也就是数据传送速度
    2. 8086的数据总线宽度是16,所以单次最大传递2个字节的数据
  8. 控制总线
    1. 它的宽度决定了CPU对其他器件的控制能力、能有多少种控制

内存

  1. 各类存储器的逻辑连接情况 图1

    1. RAM与ROM区别?
      1. ROM: read Only memory 只读存储器,一般存放的以设备硬件相关的东西。
      2. RAM: 随机存储器,可读可写的。
  2. 8086的内存 图1

    1. 所有的内存单元都有唯一的地址,叫做物理地址
    2. 各类存储器的物理地址情况
      1. 内存地址空间的大小受CPU地址总线宽度的限制。8086的地址总线宽度为20,可以定位2^20个不同的内存单元(内存地址范围0x00000~0xFFFFF),所以8086的内存空间大小为1MB
      2. 0x00000~0x9FFFF:主存储器。可读可写
      3. 0xA0000~0xBFFFF:向显存中写入数据,这些数据会被显卡输出到显示器。可读可写
      4. 0xC0000~0xFFFFF:存储各种硬件\系统信息。只读
  3. 下图是各类存储器的逻辑连接-物理地址对应图 图1

8086的寻址方式

  1. CPU访问内存单元时,要给出内存单元的地址
  2. 8086有20位地址总线,可以传送20位的地址,1M的寻址能力
  3. 但它又是16位结构的CPU,它内部能够一次性处理、传输、暂时存储的地址为16位。如果将地址从内部简单地发出,那么它只能送出16位的地址,表现出来的寻址能力只有64KB

    图1

  4. 8086采用一种在内部用2个16位地址合成的方法来生成1个20位的物理地址 图1

    1. CUP中的相关部件提供2个16位的地址,一个称为段地址,另一个称为偏移地址
    2. 段地址和偏移地址通过内部总线送入一个称为地址加法器的部件
    3. 地址加法器将两个16位地址合成为一个20位物理地址
    4. 地址加法器通过内部总线将20位物理地址送入输入输出控制电路
    5. 输入输出控制电路将20位物理地址送上地址总线
    6. 20位物理地址被地址总线传送到存储器
  5. 地址加法器采用物理地址= 段地址*16 + 偏移地址的方法,用段地址和偏移地址合成物理地址。

内存: 实模型下的存储器寻址(重要!!!)

  1. 要点:
    1. 内存分段管理思想
    2. 实模式下的内存地址变换
    3. 段寄存器的应用
    4. 堆栈段的概念

内存储器管理

  1. 8086CPU是16位体系结构的微处理器
  2. 可以同时处理(产生)16位的二进制码
    1. 可以产生64k个编码
    2. 直接管理64k个内存单元
  3. 8086CPU需要管理1MB内存
    1. 需要能够产生1M个地址编码,那就需要20位的处理器
    2. 那用16位的该如何实现呢?—内存分段管理
      1. 将内存分为n个逻辑段(相当于把楼分为n层)
      2. 每个逻辑段的编号即唯一码叫做段基地址,也用16位表示
      3. 再用一个16位表示段的偏移地址(房间号)
      4. 那么内存的唯一地址(每个房间)就可以通过段地址(楼层)与段偏移地址(房间号)合成表示出来
      5. 若要产生20位的地址,那么就需要将段地址(16位)、段偏移地址(16位)合成一个20位的地址
      6. 合成方法: 段地址 * 16 + 段偏移地址 = 物理地址
      7. 总结,8086CPU是通过将两个16位地址通过地址加法器合成20位来实现管理1M的内存

内存地址变换

  1. 欲实现对1MB内存空间的正确访问,每个内存单元在整个内存空间中必须具备唯一地址—物理地址
  2. 内存地址变换的作用: 如何将将直接产生的16位编码变换为20位物理地址
  3. 内存单元的编址:
    1. 每个内存单元的地址在逻辑上都由两部分组成
      1. 段(基)地址
        1. 指示存储单元在整个内存空间中处于哪个区域
      2. 段内地址(相对地址、偏移地址)
        1. 指示存储单元在段中的相对位置(与段中第一个单元的距离)
      3. 举例:
        1. 段:相当于楼层号
        2. 段内地址:相当于某个楼层的第几个放假
        3. 305:第三层,第5个房间
    2. 8086为16位结构,所以段地址和偏移地址均为16位
    3. 段基地址
      1. 决定存储单元在内存中的位置
    4. 相对地址(偏移地址)
      1. 该存储单元相对段内第一个单元的距离
    5. 逻辑段的起始地址称为段首
      1. 每个逻辑段内的第一个单元
      2. 段首的偏移地址为:0000H
    6. 内存的物理地址由段基地址和偏移地址组成
      1. 物理地址: 内存单元在整个内存空间中的唯一地址
      2. 物理地址 = 段地址*16(左移动4位)+ 偏移地址。
  4. 逻辑段与逻辑地址
    1. 内存的分段是逻辑分段,不是物理分段。 各个逻辑段在地址上可以不相连、可以部分重合,也可以完全重合。(逻辑段比物理段节省内存)
    2. 每个内存单元具有唯一物理地址,但可能具有多个逻辑地址。即:
      1. 一个内存单元可以同时处于2个逻辑段
      2. 一个内存单元可以在不同的时刻属于相同(或不同)类型的段。
      3. 一个内存单元在同一时刻可以属于不同类型的段

为什么要分成逻辑段?

  1. 计算机都是有操作系统统一管理的,操作系统把你编好的程序调入到内存的时候,总体原则是见缝插针式的,看见内存那块有空就插入
  2. 我们编写好的程序都是放在硬盘中,要想运行程序,必须要加载到内存,成为进程才有可能执行,到内存后会根据你程序的大小以及内存的现实情况,见缝插针的放入,内存中有可能不是一整块区域的,因此要把你的程序切分分别存放,但是每块之间是关联的。
  3. 综上所述,当你的程序由操作系统加载到内存后,段基地址也是有操作系统决定的,他决定把你的程序插入哪里,不是人为能控制的,即:段基地址是我们程序员无法控制的,因为段基地址决定了你的程序存储在那个区域里,而存储的过程是操作系统决定的。
  4. 因此我们编写程序的时候我们无法编辑段基地址,我们只能操作偏移地址。

逻辑段说明;

  1. 同一程序模块装入主存时,不同类型的段可以装入在相同、不同的物理空间(两个逻辑段完全或部分重合)
  2. 两个不同程序模块装入主存时,同一类型的逻辑段也可以装入到相同或不同的物理空间中

堆栈段及堆栈段的使用

  1. 堆栈
    1. 内存中一个特殊的区域,用于存放暂时不用或者需要保护的数据
    2. 常用于响应中断或子程序调用
Table of Contents