【汇编语言】《汇编语言》王爽-第二章笔记

CPU的组成

一个典型的CPU由运算器、控制器、寄存器等器件构成,这也是CPU的工作原理。
这些器件是由CPU内部总线连接的。

运算器进行信息的处理;寄存器进行信息的储存;控制器控制各种器件进行工作;
内部总线连接各种器件,在他们之间进行数据的传送。

不同的CPU的寄存器数、结构是不同的。 本书介绍的8086CPU共有14个寄存器。

8086CPU的寄存器都是十六位的,一个寄存器能存储两个字节的数据,能存储最大的十进制数字就是\(2^{16}-1=65536\).


通用寄存器

AX,BX,CX,DX这四个寄存器通常用来存放一般性的数据,成为通用寄存器。

其中每个寄存器都可以分为高位和低位,例如AX可分为AL和AH。
AH和AL都是八位,也就是一个字节。

当对AL操作的时候,CPU是把AL和AH当作两个独立的寄存器对待的,无论对AL进行何种操作都不会对AH中存储的数据造成影响。


寄存器内数据的表示

寄存器的数据用二进制表示出来有十六位,阅读起来会非常困难,
用十六进制的数字来表示寄存器的内容会非常简洁明了。

一个十六进制数字可以表示四位二进制数字,那么十六个二进制数字就可以用四个十六进制数字表示。

例如要在寄存器存储的数字是\(20000\),那么它的二进制表示就是\(0100111000100000B\),而用十六进制表示则为:\(4E20H\),非常简洁方便。


几条常用的汇编指令

汇编指令 操作说明
MOV AX,BX AX=BX
ADD AX,BX AX=AX+BX
SUB AX,BX AX=AX-BX

其中BX都是可以变更为具体的值的。

需要注意的是,如果是两个寄存器之间的操作,那么他们之间的位数必须保持一致,例如:

MOV AL,BH 是正确的,他们都是八位的;MOV AX,BH就是错误的,因为AX是十六位的而BH是八位的。


物理地址

每一个存储单元在这个空间中都有唯一的地址,我们称这个唯一的地址为物理地址

CPU通过地址总线送入存储器的是一个存储单元的物理地址,在CPU向地址总线发出物理地址之前必须现在CPU内部生成这个物理地址。


8086CPU给出物理地址的方法

8086CPU是一个十六位的CPU,十六进制的CPU有以下特性:

1.存储器一次最多可以处理十六位的数据;

2.寄存器的最大宽度为十六位;

3.寄存器和运算器之间的通路为十六位。

8086CPU作为一个十六位的CPU,理论上来说它的寻址能力应该仅有64KB,而实际上它的寻址能力却又1MB这么多,也就是二十位这么多,那么它是怎么实现的?

实际上8086CPU的地址总线确实是二十位,否则它也不可能具有1MB的寻址能力。那问题就转换为它是怎么生成的二十位地址。

8086CPU是通过两个十六位地址来合成一个二十位地址,两个十六位地址分别称为:段地址,偏移地址。

合成的具体方法为:物理地址=段地址*16+偏移地址

段地址是一个十六位的二进制数字,它乘以\(16\)也就是\(2^4\)相当于把这个十六进制的数字左移了4位,就变成了一个二十位的二进制数字。

物理地址的表示:

物理地址=段地址*16+偏移地址,假如有一个物理地址为:21F60H,那么它还可以表示为:2000:1F60,其中2000为段地址,1F60为偏移地址。

数据存储在21F60H内存单元,习惯上这么说:

  1. 数据存储在内存2000:1F60单元中;
  2. 数据储存在内存的2000H段中的1F60H单元中。

原理解释:

这是一种用小数字表示大数字的通用方法,比如我有两张纸片,一张红色的一张蓝色的,每张纸片只能写两个十进制数字,现在我想用这这两张卡片给你传达一个三位十进制数字,那么我可以提前和你约定好:红色卡片上的数字乘以十加上蓝色卡片上的数字就是我想要传达给你的三位十进制数字。这样我只需要在红色空片上写15,蓝色卡片上写26,你就知道我想传达给你的数字就是176.

在8086CPU内部具体的实现步骤为:

  1. CPU的相关部件提供两个十六位地址:段地址和偏移地址;
  2. 段地址和偏移地址通过内部总线传入一个成为地址加法器的部件;
  3. 地址加法器将两个十六位地址合成为一个二十位物理地址;
  4. 地址加法器通过内部总线将二十位物理地址传入输入输出控制电路;
  5. 输入输出电路将二十位物理地址送上地址总线;
  6. 二十位物理地址通过地址总线传送到存储器。

对于段的理解

在8086CPU中,由于它是通过段地址*16+偏移地址的方式给出了物理地址,因此可以通过分段的方式管理内存。

在编程的时候,可以将一些连续的内存看作一个段,这个段的起始地址就是段地址*16,然后通过改变偏移地址来定位段中的某个内存单元。

由于偏移地址为十六位地址,因此一个段的最大长度就是\(2^16\)也就是64KB。


CS和IP

CS和IP是8086CPU的两个寄存器,CS为代码段寄存器,IP为指令指针寄存器,他们指示了CPU当前要读取指令的地址,也就是说CPU将CS:IP指向的内容当作指令执行。

如果说内存中的一段信息曾被CPU执行过的话,那么它所在的内存单元必然被CS:IP指向过。

CS:IP的修改

对于CS:IP的修改不能使用MOV,ADD等常规指令实现。

在8086CPU中有专门的对CS:IP进行修改的指令,这种命令称为转移指令,
这里给出这其中最简单的指令JMP的介绍。

如果要同时修改CS:IP可以执行 JMP 段地址:偏移地址 指令完成,例如:

JMP 2AE2:3 执行后CS=2AE2HIP=0003H

若仅仅想修改IP的内容,可以执行 JMP 某个合法寄存器 指令完成,例如:

AX=2AE3H,则 JMP AX 执行后IP=2AE3H.

补充:

8086CPU除了CS这个段寄存器外还有三个段寄存器,分别为:DS、SS、ES。


对于命令执行的一些疑问

  1. 命令是存储在存储器中的,但是每条命令的长度并不都是相同的,那么CPU是如何知道每条命令的长度的呢?

  2. CPU是按照顺序从前往后依次执行存储在存储器中的命令的,那么CPU是如何知道执行到哪一条命令才停止的呢?

  3. 书中提到“8086机中,任意时刻,CPU将CS:IP指向的内容当作指令执行”,那么这个任意时刻是如何定义的呢?也就是说,CPU是不断地执行CS:IP指向的内容吗?如果是,这个“不断的”是怎样的频率呢?


Debug的基本使用

Debug是DOS(Disk Operating System)、Windows都提供的实模式(8086方式)程序的调试工具。

使用它可以查看CPU各种寄存器中的内容、内存的情况和机器码级跟踪程序的运行。

常用命令

R:改变CPU寄存器的内容;

D:查看内存中的内容;

E:改写内存中的内容;

U:将内存中的机器指令翻译成汇编指令;

T:执行一条机器指令;

A:以汇编指令的格式在内存中写入一条机器指令。

R命令的基本使用:

  1. - R 可以查看CPU中寄存器的内容。在下方,Debug还会列出CS:IP所指向的内存单元存放的机器码,并将它翻译为汇编指令。

  2. - R AX,按回车键,终端就会显示 : ,这样就可以改变AX的值。

比如要将 AX的值改变为13H,就可以执行:

- R AX
: 13

AX的值就被改变成13了。

值得注意的是,R命令可以直接修改CS:IP的内容,没有限制。

D命令的基本使用:

  1. - D 段地址:偏移地址 可以查看内存中段地址:偏移地址开始之后128个内存单元的内容。

  2. 如果想指定D命令的查看范围,可以执行D 段地址:起始偏移地址 截止偏移地址

比如想要查看1000:00001000:0009中的内容,可以用1000:0000 0009实现。

E命令的基本使用:

  1. 使用 E 起始地址 数据1 数据2 数据3 ... 指令,可以将从起始地址开始的存储单元依次赋值为数据1,数据2,数据3,...

  2. 采用提问的方式依次改变存储单元中的数据。 向Debug中输入E 1000:0,按回车键,之后Debug会显示起始地址1000:0000以及起始地址对应存储单元存储的值,在.后面可以输入值按空格,这个存储单元的值就改变了,或者不输入值直接按空格当前的存储单元的值就不会被改变。按下空格之后Debug就会显示下一个存储单元的原始内容并提示修改。当所有要修改的值都修改完毕后,按回车键E命令操作就会结束。

  3. 向内存单元中写入字符/字符串。用上面第一种命令,里面的数据可以是一个字符。

- E 1000:0000 1 2 3 4 'a' 'B' 'c'

- E 1000:0000 "hello, world!"

U命令的基本使用:

使用U 1000:0000即可查看1000:0开始的内存存储的命令。

查询结果由三部分组成,最左边是每个命令的存储地址,中间是命令对应的机器指令,最右边是每个机器指令对应的汇编指令。

T命令的基本使用:

每执行一次T指令,就会执行一次CS:IP指向的指令,之后CS:IP就会自动指向下一条命令的位置。

A命令的基本使用:

输入A 1000:0000之后按回车,之后就可以依次输入汇编指令,输入完毕按回车键,Debug会将这些汇编指令翻译为对应的机器指令存储在对应的位置,之后自动将位置调整到下一个能存储指令的位置。若不输入任何内容直接按回车键,则A命令操作就会结束。

posted @ 2021-02-10 17:45  牟翔宇  阅读(352)  评论(0编辑  收藏  举报