在16位汇编语言的源程序中,我们将源程序按照不同的功能和作用划分为若干个逻辑段,如数据段用来存储数据,代码段用来存储代码,堆栈段用来保存临时数据,附加段用来拷贝数据。我们可以把汇编语言的源程序抽象地理解为数据在内存与寄存器之间的移动。如若准确地读写内存单元存储的数据,指定内存单元的地址是关键。16位汇编语言源程序中的地址为逻辑地址,由段值和偏移两部分组成,如CS:IP表示指向CS代码段的IP偏移地址处。具体的寻址方式我们将在第七章详细讲述,此处不再赘述。
本节内容:CPU内部的段寄存器就是用来存储段值的。本节我们将详细讲述8086 CPU内的段寄存器的功能和使用方法。
■存储单元的地址和内容:内存是以字节为单位的线性地址空间。一个字节对应一个地址编号。一个存储单元即一个字节,包含8个数据位。8086 是16位计算机,一次可以读写8位或16位二进制数。
■存储器的分段:8086 计算机1M大小的寻址空间可以人为的划分为若干个逻辑段,最小段为16个字节,最大段为64KB。一个汇编源程序中至少包含一个代码段,通常包含代码段、数据段和堆栈段。此外,附加段用于数据拷贝。
■逻辑地址转换为物理地址:汇编源程序中使用“段值:偏移”这种形式的逻辑地址,段值和偏移采用16位数表示。段值使用段地址标号表示,编译器编译链接后生成以“.exe”为后缀名的二进制可执行程序,加载到DOS操作系统时,将段地址标号替换为具体的段值,以实现不同计算机系统间的代码重载。偏移表示段内的相对位移量。8086 CPU读取指令时,通过CPU内部的地址加法器按照段值左移四位加偏移的算法,将逻辑地址转换为20位物理地址后访问物理内存。
■段寄存器:8086 CPU内包含四个段寄存器。CS存储代码段的段值,DS存储数据段的段值,SS存储堆栈段的段值,ES存储附加段的段值。堆栈内存储数据的规则为Last in First Out,后进先出规则。
6.2.1 存储单元的地址和内容
■存储单元的地址
在8086 计算机中,内存是以字节为单位的线性地址空间。内存存储单元的地址以二进制数表示,从0开始编号,每次加1。存储单元地址为无符号整数,n位二进制数总共可以表示2n个存储单元地址。为了书写方便,存储单元地址以十六进制数表示。
x86CPU是以小端方式存储的,即高高低低的存储原则。我们在前面“2.5节有符号整数无符号整数”中详细讲述过,此处不再赘述。
■存储单元的内容
举例
根据表6-8所示内容,指出下列地址处对应
存储单元内的内容:
- 地址56780H存储单元内容:
字节34H,字1234H,双字FF561234H
- 地址834ABH存储单元内容:
字节80H,字6754H,双字不能确定
表6-8 存储单元的内容
6.2.2 存储器的分段
■8086计算机寻址空间
在8086 CPU中,有16根控制线、16根数据线和20根地址线。如果地址线同样也是16根,那么可直接寻址的内存空间为216=64KB。假如使用64KB的寻址范围,除去中断向量表、DOS系统、显存、和各种硬件设备的ROM内存空间,可供加载应用程序的内存空间将极其有限。在当时的条件下,将地址线增加到了20根,可直接寻址的空间扩大到了220,即1MB,寻址范围是00000H-FFFFFH。相应的措施是,在CPU内部增加一个地址加法器,将一个16位的段值左移4位,加上一个16位的偏移,得到20位的物理地址。
■逻辑段
8086计算机通过对存储器分段和使用段寄存器的方法有效的实现了寻址1MB物理空间。
根据需要将1MB物理地址空间分为若干个逻辑段。每个逻辑段必须满足三个条件:
●逻辑段的开始地址为16的倍数;
●逻辑段的最大长度为64K;
●16位的段内偏移在0~FFFFH之间。
最小逻辑段为16个字节,最大逻辑段为64KB。
1MB地址空间最多划分64K(1MB/16B)个逻辑
段,最少划分为16(1MB/64KB)个逻辑段。
如图6-7所示,逻辑段与逻辑段可以相连,也可
以不相连,还可以重叠。
图6-7 逻辑段的划分
■逻辑地址
逻辑地址(源程序中编写的地址)表示方法:
●段值:偏移
举例
1.例如:CS:IP,CS代码段寄存器存储16位逻辑代码段的段值,IP指令指针寄存器存储16位代码段指令偏移地址。
2.例如:DS:[SI],DS数据段寄存器存储16位逻辑数据段的段值,SI变址寄存器存储16位数据段偏移地址。
■划分逻辑段的优点
●有利于实现寻址1MB空间;
●有利于管理存储空间;
●实现程序的重定位和浮动;
●实现代码数据的隔离;
●充分利用存储空间。
其实上述优点有些言过其辞。这只是针对当时8086 CPU的时代而言。受限于当时8086计算机只有20根地址线,内存寻址空间只有1M大小。如何充分有效的利用现有条件,是首要考虑的问题。对于今天的64位计算机,264个字节的内存寻址空间,远大于我们的实际物理内存大小,不需要再使用段寄存器作为间接寻址的方式。8086计算机采用段值:偏移的寻址方式,显得有些复杂。
对于上述优点的解释,这里不再赘述。再细致的描述也不如读者自己在后续的代码练习中亲身体会。
6.2.3 逻辑地址转换为物理地址
我们在汇编源代码中编写的“段值:偏移”这种组合形式的逻辑地址,仅是人为定义的。计算机要真正执行这些由数据和指令构成的程序,还需要加载到真正的物理内存,并且对物理内存进行读写操作。这就需要我们将代码中的逻辑地址转换为物理地址。
■转换方法
将16位段地址和16位偏移地址合成一个20位地址。
●物理地址=段地址×16+偏移地址
段的本质:基础地址(起始地址×16)+偏移地址
图6-8 间接寻址方式
如图6-8所示,由A点到C点的寻址方式有两种。一种方法是直接寻址,由A点直接到C点;另一种方法是间接寻址,由A点先到B点,然后再由B点到C点。8086计算机采用的是间接寻址方式。
■8086 CPU内部的地址加法器
8086的逻辑地址是由16位的段值和16位的偏移组成,当我们把逻辑地址输入CPU内部后,需要将逻辑地址转换为20位的物理地址,然后才可以真正执行内存的读写操作。这是由CPU内部的地址加法器实现的,如图6-9所示。
图6-9 8086CPU内的地址加法器
■地址加法器的工作过程
图6-10 地址加法器工作过程
如图6-10所示,地址加法器按照段值左移4位+偏移的算法,将逻辑地址转换为20位的物理地址,就可以正确的在物理内存内寻址。注:段值左移4位即段值×16。
注意
8086计算机一个物理地址可以对应多个逻辑地址。
例如:逻辑地址076A:10指向的物理地址为076B0H;
逻辑地址076B:0指向的物理地址同样是076B0H;
提示
逻辑的意思可以理解为是人为定义的。因而逻辑段就是人为定义的段。逻辑地址就是人为定义的地址。
6.2.4 段寄存器
■CS代码段
编程时根据需要,将一组内存单元定义为一个段。我们可以将长度为N(N<=64KB)的一组代码,存放在一组地址连续、起始地址为16的倍数的内存单元中,这段用来存放代码的内存就称为代码段。
■DS数据段
一段用来存放数据的内存称为数据段。
■SS堆栈段
8086 CPU提供相关指令以栈的方式访问的内存空间,称为堆栈。堆栈内存储数据的规则为Last in First Out——后进先出规则。如图6-11所示,最后存入的《Windows》放置在最上层,取得时候也是先取最上层的《Windows》。堆栈段为动态内存分配空间,根据需要,随时可以增加或释放堆栈空间。
运行在8086计算机上的DOS操作系统默认分配的堆栈段为64KB。系统自动分配为栈,程序员也可以自己根据实际需要分配一段内存空间作为堆栈段。Windows系统默认分配的堆栈通常为1M大小,LINUX系统默认分配的堆栈通常为8M大小。
■ES附加段
为了方便复杂数据寻址和操作,增加定义一个附加的数据段空间。80386 CPU多了2个附加段FS和GS。附加段通常用来做数据拷贝。
图6-11 堆栈存储数据规则演示
练习
1、假设段地址为0001H,不改变段值,仅变化偏移地址,CPU的寻址范围为 _______ 到_______H 。
2、有一个数据存放在内存20000H单元中,假设给定段地址为DATA,如果用偏移地址寻址此单元。则DATA应满足的条件是:最小为 _______H ,最大为________H 。
提示:反过来思考,当段地址给定为 1001H 以下和 2000H 以上,CPU无论怎么变化偏移地址都无法寻到20000H单元。
3、8086计算机上运行的程序在某一时刻最多可以访问几个段?程序至少可以有几个段?
4、8086如何寻址1M字节的物理地址空间?在划分段时,必须满足的两个条件是什么?最多可以把1M字节空间划分成几个段?最少可把1M字节地址空间划分为几个段?
5、8086存储单元的逻辑地址是怎样表示的?存储单元的20位物理地址如何构成?
6、当段重叠时,一个存储单元的地址可以表示成多个逻辑地址。请问物理地址12345H可表示多少个不同的逻辑地址?偏移最大的逻辑地址是什么?偏移最小的逻辑地址是什么?
7、8086CPU是如何处理超出1MB寻址范围这种情况的?
本文摘自编程达人系列教材《X86汇编语言基础教程》。