汇编入门(X86)(长文流量预警,更新中)

0 引言

  • why?
    • 机器语言外所能操作的最底层语言
    • 所有语言中效率最高的一种,大量应用于嵌入式开发
    • 更了解硬件之间的协作关系,在学习高级语言时,更加的得心应手

用机器的思维去操作机器

汇编语言是直接工作在硬件上的编程语言,首先要了解硬件系统的结构,才能有效的应用汇编语言对其编程。

研究重点放在如何利用硬件系统的编程结构和指令集有效灵活的控制系统进行工作。

1 基础知识

1.1 机器语言

  • 是机器指令的集合
  • 机器指令就是一台机器可以正确执行的命令,例:
    指令:01010000 (push AX)


    电平脉冲

始祖:
早期程序员--> 纸带机

1.2 汇编语言的产生

  • 主体是汇编指令
  • 汇编指令是机器指令便于记忆的书写格式
  • 汇编指令是机器指令的助记符,eg:
    指令:1000100111011000
    操作:寄存器BX的内容输送到AX
    汇编指令:MOV AX,BX (通用数据传送)

接近于人类的语言,便于阅读和理解

  • 寄存器:简而言之是CPU中可以存储数据的部件,一个CPU中存在多个寄存器(比缓存更低一级)。
    • AX、BX:两个不同寄存器的代号


      汇编语言的执行

1.3 汇编语言的组成

  • 汇编语言的组成
    • 1、汇编指令(机器码的助记符)
    • 2、 伪指令(由编译器执行)
    • 3、其他符号(由编译器识别)

加法:与或门
其他运算可以基于加法达成
详细查阅《编码的奥秘》或相关硬件专著

  • 汇编语言的核心是汇编指令,他决定了汇编语言的特性

1.4 存储器

  • CPU是计算机的核心部件,控制计算机的运作并进行计算,要想让一个计算机工作,就必须向他提供指令和数据
  • 指令和数据存放在存储器中,也就是常说的内存(内存是其中的一个主要存储器)
  • 一台PC中内存的作用仅次于CPU
  • 离开了内存,性能再好的Cpu也无法工作
  • 磁盘不同于内存,磁盘上的数据或程序若无法读取到内存中,就无法被Cpu使用


    数据的交互

1.5 指令和数据

  • 是应用上的概念
  • 在内存或磁盘上,指令和数据没有任何区别,都是二进制信息
  • 二进制信息:

1000100111011000
-->89D8H(数据)

H:Hex(16进制)
进制转换问题查阅任意计算机课本

1000100111011000
-->MOV AX,BX(程序)

1.6 存储单元

  • 存储器被划分为若干个存储单元,从0开始顺序编号;

  • eg:
    一个存储器有128个存储单元,编号0~127

  • 大容量存储一般还有KB、MB、TB等单位(1024进位)

  • 磁盘的容量单位同内存

1.7 CPU的读和写

  • CPU要想读和写,须和外部器件(芯片)进行交互
    • 存储单元地址(地址信息)
    • 器件的选择,读或写(控制信息)
    • 读或写的数据(数据信息)
  • 电子计算机处理和传输信息都是通过导线传递电信号
    • 专门链接CPU和其他芯片的称为总线
      • 物理上:导线的集合
      • 逻辑划分:
        • 地址总线
        • 控制总线
        • 数据总线

运行实例:Intel 8086
机器码:101000000000001100000000
操作:从3号单元读取数据送入AX

1.8 地址总线

  • CPU通过地址总线指定存储单元

  • 地址总线能传送多少个不同信息,CPU就对多少个存储单元进行寻址(CPU的寻址能力)

  • CPU如何寻址?


    寻址
  • CPU有N条地址总线,即该CPU的地址总线宽度为N,可寻址个内存单元

1.9 数据总线

  • CPU通过数据总线与内存或其他器件进行数据传送
  • 总线宽度决定CPU和外界的数据传输速度,eg:
    • 分别使用8088和8086传输89D8H
8088
8086

1.10 控制总线

  • 总称,包含不同控制线的集合。
  • CPU通过控制总线控制外部器件
  • 有多少根,CPU就提供了多少种控制
  • 总线宽度决定了CPU对外部器件的控制能力


    控制总线

此处每条线可以粗略理解为对应一个端口

  • 内存读写命令由几根控制线综合发出
    • 一根读信号输出口控制线负责向外发送读信号,CPU由此输出低电平表示要读取数据;
    • 一根写信号输出可控制线负责向外发送写信号

*小练习

问题

解答:
(1)1KB=1024B,8KB=1024B8=,N=13。
(2)存储器的容量是以字节为最小单位来计算的,1KB=1024B。
(3)8Bit=1Byte,1024Byte=1KB(1KB=1024B=1024B
8Bit)。
(4)1GB=1073741824B(即)1MB=1048576B(即)1KB=1024B(即)。
(5)一个CPU有N根地址线,则可以说这个CPU的地址总线的宽度为N。这样的CPU最多可以寻找个内存单元。(一个内存单元=1Byte)。
(6)8根数据总线一次可以传送8位二进制数据(即一个字节)。
(7)8086的数据总线宽度为16根(即一次传送的数据为2B)1024B/2B=512,同理1024B/4B=256。
(8)在存储器中指令和数据没有任何区别,都是二进制信息。

1.11 内存地址空间(概述)

  • 若一个CPU地址线宽度10,则可以寻址1024个内存单元。此时,这1024个可寻址到的内存单元就构成了这个CPU的内存地址空间

1.12 主板和接口

  • 每台PC中都有一个载有核心器件和一些主要部件的主板
  • 这些器件通过(三类)总线相连

1.13 接口卡

  • 计算机系统中,所有可用程序控制其工作的设备,必须受到CPU的控制

  • CPU对外部设备不能直接控制,如显示器,音响,打印机等。直接控制这些设备工作的是插在扩展插槽上的接口卡

    1.14 各类存储器芯片

  • 读写属性分类:

    • 随机存储器RAM(断电后,数据会遗失(内存))
    • 只读存储器ROM(数据长期保存,除非被刷掉)
  • 功能和连接分类

    • 随机存储器RAM
    • 装有BIOS的ROM
      • BIOS:Basic Input/Output System,基本输入输出系统
        BIOS是由主板和各类接口卡(显卡、网卡等)厂商提供的软件系统,可以通过他利用该硬件设备进行最基本的输入输出。在主板和某些接口卡上插有存储相应BIOS的ROM
    • 接口卡上的RAM

    PC中各类存储器的逻辑链接情况:


    存储器间的逻辑连接

1.15 内存地址空间

  • 上述的那些存储器在物理上是独立的器件(逻辑上是相连的)

  • 但存在以下两点上的相同

    • 都通过总线与CPU相连接
    • CPU对他们进行读或写的时候都通过控制总线发出内存读写命令
  • 将各类存储器看作一个逻辑存储器

    • 所有的物理存储器可以看作一个由若干存储单元组成的逻辑存储器(看成一个整体)
    • 每个物理存储器在这个逻辑存储器中占有一个地址段,即一段地址空间
    • CPU在这段地址空间中读写数据,实际就是在对应的物理存储器中读写数据
数据的读写
  • 不同的计算机系统的内存地址空间分配情况不同,eg:
    • 8086CPU
内存地址空间分配
  • 内存地址空间
    • 最终运行程序的是CPU,用汇编语言编程时,要从CPU的角度考虑问题(核心思维

思考题:使用VC6编译器执行下列程序,结果中的q值为多少?

void main()
{
    int i=5,j=5,q,p;
    p=(i++)+(i++)+(i++);
    q=(++j)+(++j)+(++j);
    printf("%d\n,%d\n,%d\n,%d\n",p,q,i,j);
}

答:以下是反编译结果

8:q=(++j)+(++j)+(++j);
mov ecx,dword ptr[ebp-8]
add ecx,1
mov dword ptr [ebp-8],ecx
mov edx,dword ptr [ebp-8]
add edx,1
mov dword ptr [ebp-8],edx
mov eax,dword ptr [ebp-8]
add eax,dword ptr [ebp-8]
mov ecx,dword ptr [ebp-8]
add ecx,1
mov dword ptr [ebp-8],ecx
add eax,dword ptr [ebp-8]
mov dword ptr [ebp-0Ch],eax

原因在于VC编译器的优化习惯,会将前两个(++j)作为系数自增两次之后再相加(2~9行),也就是q=7+7+8=22

  • 对于CPU来讲,系统中的所有寄存器中的存储单元都处于一个统一的逻辑存储器中,他的容量受到CPU寻址能力的限制,这个逻辑存储器即是我们所说的内存地址空间

2 寄存器(CPU工作原理)

2.0 基本概述

CPU

  • 典型的CPU由运算器、控制器、寄存器等器件组成,他们依靠内部总线相连接
    • 区别
      • 内部总线实现CPU内部各个器件之间的通信
      • 外部总线实现CPU和主板上其他器件的通信

寄存器

  • 以8086CPU为例,内含14个寄存器:
    • AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW

2.1 通用寄存器

  • 8086CPU所有的寄存器都是16位的,可以存放两个字节
  • AX、BX、CX、DX通常用于存放一般数据,称为通用寄存器
  • 以AX为例,16位寄存器的逻辑[图片上传中...(image-20201020005603769.png-c308e8-1603296222659-0)]
    结构如图


    16位寄存器的逻辑结构
  • 一个16位寄存器可以存储一个16位的数据
    • 数据:18
    • 二进制表示:10010
    • 在寄存器AX中的存储
  • AH和AL寄存器是可以独立使用的8位寄存器
  • 8位寄存器所能存储的数据最大为

2.2 字(两个字节)在寄存器中的存储

  • 一个字可以存在一个16位寄存器中,这个字的高位字节和低位字节自然就存在这个寄存器的高8位寄存器和低8位寄存器中
    • 一般习惯于将二进制转换为16进制使用(每四位二进制数可以转换为一位十六进制数)

数制

  • 由于一个内存单元可以存放8位数据,CPU中的寄存器又可以存放N个8位数据,即,计算机中的数据大多是由1~N个8位数据构成
  • 用十六进制来表示数据可以直观的看出数据由哪些8位数据构成的,每两位对应一个八进制

数制相关推荐《编码》

2.3 几条汇编指令

  • 示例:汇编指令不区分大小写

赋值操作:从右向左

思考题:CPU执行下表中的数据段的每条指令后,对寄存器中的数据进行的改变

  • 1.

答:8226H+8226H=1044CH,但因为只能存放16位,最高位会溢出丢失,故ax处为044CH

答:al+93H=C5H+93H=0158H,但是由于al是8位寄存器,最高位会溢出丢失,故al处为0058H

  • 这里的丢失,是指进制位不能再8位寄存器中保存,CPU并不是真的丢弃这个进位值。

*小练习

2.4 物理地址

  • CPU访问内存单元时要给出内存单元的地址。所有的内存单元构成的存储空间是一个一维的线性空间
  • 我们将这个唯一的地址称为物理地址

2.5 16位结构CPU

  • 16位结构描述了以下特征
    • 1、运算器一次最多可以处理16位数据
    • 2、寄存器的最大宽度为16位(通用寄存器)
    • 3、寄存器和运算器之间的通路是16位的

2.6 8086CPU给出物理地址的方法

  • 8086有20位地址总线,可传送20位地址,寻址能力位1M
  • 8086内部为16位结构,他只能传送16位的地址,表现得寻址能力却仅有64K
  • 疑问:8086如何通过内部16位的数据转换成20位的地址呢?
    • 8086采用一种在内部用两个16位地址合成的方式,形成一个20位的物理地址。

8086CPU读写内存时,

1、CPU中得相关部件提供两个16位地址,一个称为段地址,一个称为偏移地址

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

3、地址加法器将两个16位地址合并成一个20位地址

4...

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

1、"段地址*16"即数据左移4位(二进制位)

  • 一个二进制数左移一位,即该数乘以2
  • 一个十进制数左移一位,即该数乘以10
  • 一个二进制数左移N位,相当于该数乘以

2.7 “段地址*16+偏移地址=物理地址”的本质含义(核心 )

  • “基础地址+偏移地址=物理地址”的思想
    • 小明从学校到体育馆和图书馆的举例如下,有几种方式描述图书馆的位置?
  • “段地址*16+偏移地址=物理地址”的思想

    • 仍旧上例,假设我们只能通过纸条交流,显然要可以容纳四位数的纸条写数据
  • 继续假设只有可以写下三位数的纸条


2.8 段的概念

  • 内存并没有分段,段的划分来自于CPU,由于8086CPU使用“段地址*16+偏移地址=物理地址”的方式给出内存单元的物理地址,使得我们可以用分段的方式管理内存


  • 在编程时可以根据需要,将若干地址连续的内存单元看作一个段,用段地址*16定位段的起始地址(基础地址),用偏移地址定位段中的内存单元

    • 段地址*16必然是16的倍数,所以一个段的起始地址也一定是16的倍数
    • 偏移地址为16位,16位地址的寻址能力为64K,所以一个段的长度最大为64K

*内存单元地址小结

  • CPU访问内存单元时,必须像内存提供内存单元的物理地址
    • 在8086PC机中,存储单元的地址用两个元素来描述。即段地址和偏移地址
    • “数据在21F60H内存单元中”可以有多种描述法
      • 数据在内存2000:1F60单元中
      • 数据在内存2000段的1F60H单元中
    • 可根据需要,将地址连续、起始地址为16的倍数的一组内存单元定义为一个段
  • 8086CPU在内存用段地址和偏移地址移位相加的方法形成最终的物理地址
    • CPU可以用不同的段地址和偏移地址形成同一个物理地址
    • 偏移地址16位,变化范围为0~FFFFH,仅用偏移地址来寻址最多可寻64K个内存单元

*小练习


答:

(1) 给定段地址为0001H,仅通过变化偏移地址寻址,CPU的寻址范围为 0010H 到 1000FH 。

解题过程:

物理地址=SA*16+EA  
EA的变化范围为0h~ffffh  
物理地址范围为(SA*16+0h)~(SA*16+ffffh)  
现在SA=0001h,那么寻址范围为   
(0001h*16+0h)~(0001h*16+ffffh) =0010h~1000fh   

(2) 有一数据存放在内存20000H单元中,现给定段地址为SA,若想用偏移地址寻到此单元。则SA应满足的条件是:最小为 1001H ,最大为 2000H 。

解题过程:

当段地址给定为 1001H 以下和 2000H 以上,CPU无论怎么变化偏移地址都无法寻到20000H单元。
物理地址=SA*16+EA   
20000h=SA*16+EA   
SA=(20000h-EA)/16=2000h-EA/16   
EA取最大值时,SA=2000h-ffffh/16=1001h,SA为最小值   
EA取最小值时,SA=2000h-0h/16=2000h,SA为最大值 

2.9 段寄存器

  • 用于提供段地址

    • 8086CPU有四个段寄存器:
      • CS(Code Segment)代码段寄存器
      • DS(Data Segment):数据段寄存器
      • SS(Stack Segment):堆栈段寄存器
      • ES(Extra Segment):附加段寄存器
  • 当8086CPU要访问内存时,由这四个段寄存器提供内存单元的段地址

2.10 CS和IP

  • CS和IP是8086CPU中最关键的寄存器,他们指示了CPU当前要读取指令的地址
    • CS为代码段寄存器
    • IP为指令指针寄存器
  • 8086PC读取和执行指令相关部件
  • 执行过程简述
    • 从CS:IP指向内存单元读取指令,读取的指令进入指令缓冲器
    • IP=IP+所读取指令的长度,从而指向下一条指令
    • 执行指令。转到步骤一,重复这个过程
  • 在8086CPU加电启动或复位后(即CPU刚开始工作时)CS和IP被设置为CS=FFFFH,IP=0000H
    • 即,8086CPU刚刚启动时,CPU从FFFF0H单元中读取指令执行
    • FFFF0H单元中的指令是8086PC开机后执行的第一条指令
  • 在任何时候,CPU将CS、IP中的内容当作指令的段地址和偏移地址,用它们合成指令的物理地址,到内存中读取指令码,执行
    • 如果说,内存中的一段信息曾被CPU执行过的话,那么,他所在的内存单元必然被CS:IP指向过

2.11.1 修改CS、IP的指令

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

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

    • 8086Cpu必须提供相应的指令
    • 回顾如何修改的AX中的值
      • mov指令,如:mov ax,123
    • mov指令可以改变8086CPU大部分寄存器的值,被称为传送指令
    • 能够通过mov指令改变CS、IP的值吗??
  • mov指令不能用于设置CS、IP的值,8086CPU没有提供这样的功能

  • 8086CPU为CS、IP提供了另外的指令来改变它们的值:转移指令

    • 同时修改CS、IP的内容

      • jmp段地址:偏移地址

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

# 跳转至2AE33
jmp 2AE3:3  
# 跳转至B46H
jmp 3:0B16
# 仅修改IP的内容
jmp 某一合法寄存器
    jmp ax (类似于mov IP,ax)
    jmp bx

问题分析

  • 内存中存放的机器码和对应汇编指令情况

    (初始:CS=2000H,IP=0000H)


!从右跳到左后执行死循环

2.11 代码段

  • 对于8086PC,在编程时,可以根据需要,将一组内存单元定义为一个段

    • 可以将长度为N(N<=64KB)的一组代码,存在一组地址连续、起始位置为16的倍数的内存单元中,这段内存是用来存放代码的,从而定义了一个代码段,例如:

这段长度为10字节的指令,存在从123B0H~123B9H的一组内存单元中,我们就可以认为,123B0H~123B9H这段内存单元是用来存放代码的,是一个代码段,他的段地址为123BH,长度为10字节。

  • 如何使得代码段中的指令被执行??
    • 将一段内存当作代码段,仅仅是我们在编程时的一种安排,CPU并不会由于这种安排,就自动的将我们定义的代码段中的指令当作指令来执行
    • CPU只认被CS:IP指向的内存单元中的内容为指令,故要将CS:IP指向所定义的代码段中的第一条指令的首地址

你可能感兴趣的:(汇编入门(X86)(长文流量预警,更新中))