使用带有x86汇编和DOS(16位)的实际分段模式时大于64k的程序

时间:2010-09-08 18:22:58

标签: assembly

我想知道在DOS的实际分段模式下进行16位(汇编)编程时,如何处理大于64k的汇编程序。在我正在阅读的书中(Jeff Duntemann的“汇编语言一步一步”),作者提到了一些关于使用多个代码段的内容(但遗憾的是没有详细介绍)。我只是想知道如何做到这一点。我知道这个内存模型现在已经过时了,但出于好奇,我只想知道你将如何实现这一目标。

4 个答案:

答案 0 :(得分:1)

好吧,您只需将工作地址的高位字写入DS,您就可以通过DS:DI使用它。 这样你就可以使用多达300-500kb的ram。

答案 1 :(得分:1)

使用超过64kb的内存本身并不十分复杂(除非你需要处理跨越段边界的数据结构 - 这可能是“有趣的”)。如果您只需要动态内存,只需使用16:16(seg:ofs)FAR指针。

您可以保持DS指向您的主数据段并使用ES(或者,如果在80386或更高版本上执行,甚至在实模式下)FS或GS数据段来保存远指针的段部分。您可以将任何通用寄存器用于偏移部分,DS:SI和ES:DI仅在处理字符串指令(lods / movs / stos)时才是特殊的。另外,请记住,如果不使用显式段覆盖,BP和SP默认为SS段!

如果您需要超过64kb的静态程序数据(哇!),您需要一个具有段/节支持的汇编程序,并且您很可能还需要汇编为对象格式和在链接到.exe之前。如何处理远端指针的静态数据将取决于您选择的汇编程序,但可能会有一些关键字用于引用变量的段部分。

答案 2 :(得分:1)

段寄存器会抵消内存访问的基础。 16位模式下的地址计算如下:

address = ((twenty_bit_t)segment << 4) + offset

其中(虚数)twenty_bit_t是至少20位的类型。

16位实模式下的地址空间为1MB或20位。该段允许您影响那些前四位,并以16字节的粒度进行(通常称为&#34;段落#34;在那些日子里)。将一个值添加到段值会使指针在内存中提前16个字节。

您的偏移量限制为16位值,因此除此之外,您必须使用&#34;远指针&#34;。远指针长32位。但实际上不是32位,高16位实际上只是与偏移重叠的段。

远指针比普通指针贵得多,因为(当时有典型的编译器)每次取消引用它必须加载段寄存器的不同指针。编译器通常会每次都加载段寄存器。

有几个&#34;型号&#34; (正如我们之所以称之为)。它基本上是近/远代码指针,近/远数据指针的所有组合的矩阵(并且IIRC具有> 64KB的全局变量)。

编译器提供了对每个指针是远还是近的细粒度控制。对于访问量很大的代码/数据,使用近代码和近数据进行优化,并在需要的地方添加额外的扩展指令来声明特定的指针或函数。

远程调用和返回并不像数据那样严重影响,因为指针被解除引用的频率远远超过调用函数。

x86 CPU具有特殊指令(和前缀)来处理远程调用和返回以及远指针。即使是32位操作系统也需要至少初始化段寄存器。某些x86系统指令需要使用段寄存器。但是,在32位模式下,段寄存器的值与我上面描述的所有内容完全不同。

关联和加载

目标文件包含代码和数据块,每个代码和数据都注定要进入特定的段(按名称)。每个段也被标记为代码或数据等。链接器确定需要什么,计算每个段的大小,找出所有内容的地址,并记住可执行文件中任何指针的位置。将调用远程调用操作数和初始化的远指针,就像可执行文件的加载地址为零一样,并为每个指针发出重定位条目。

DOS动态处理内存,因此您的程序将被加载到一个不可预测的地址。为了解决这个问题,DOS可执行文件具有&#34; relocations&#34 ;,这是一个指向代码和初始化数据的指针列表。加载时,DOS会遍历该列表,并将加载地址的基本段添加到重定位条目指向的值。例如,代码重定位将指向表示调用指令中的段的字节。这修复了任何初始化的全局远指针和任何远程调用指令操作数。

(从286开始,地址计算实际上几乎变为21位 - 段寄存器为FFFF +偏移量FFFF,地址为10FFEF - (1 MB + 64 KB - 16 B).HIMEM.SYS实际上有一个API调用分配高内存,全部或全部。通常人们让DOS通过在他们的config.sys中放置DOS = HIGH来实现它。)

答案 3 :(得分:0)

这很容易。您有几个代码段,可能分布在多个覆盖文件中(通常为.OVL .OVR或.BIN)。您加载所需的叠加层,然后通过远程跳转/呼叫跳转到它们,这会修改CS:IP。