【问题标题】:Programs larger than 64k when using real segmented mode with x86 assembly and DOS(16-bit)使用带有 x86 汇编和 DOS(16 位)的实分段模式时大于 64k 的程序
【发布时间】:2010-09-08 18:22:57
【问题描述】:

我想知道当您在 DOS 的真实分段模式下进行 16 位(汇编)编程时,您需要如何处理大于 64k 的汇编程序。在我正在关注的书中(Jeff Duntemann 的“Assembly Language Step by Step”)中,作者提到了使用多个代码段的一些事情(但不幸的是没有详细介绍)。我只是想知道如何做到这一点。我知道这个内存模型现在已经过时了,但出于好奇,我只是想知道你将如何实现这一点。

【问题讨论】:

    标签: assembly


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

      • 其实我特指的是一个代码超过64kb的程序,不是数据。我相信在代码段中,用于指示机器偏移量的默认(且不可修改)寄存器是 IP。我想知道如果你有一个非常大的装配项目,其中有几个链接在一起的文件需要超过 64kb 的 CODE 段,你会怎么做。
      • @user442710 这根本不是真的。您不能直接使用 mov 指令修改 CS 寄存器,但可以使用远调用、远跳转指令、远返回、中断或异常来修改它。 Far 调用只需要一个 4 字节的指针,其中两半是段和指针。
      • @user442710 我在“链接和加载”下的回答中有多个段时解决了您关于如何处理修复地址的问题。
      【解决方案3】:

      段寄存器偏移内存访问的基数。 16位模式下的地址计算如下:

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

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

      16 位实模式下的地址空间为 1MB,即 20 位。该段允许您影响前四位,并且以 16 字节的粒度(在当时通常称为“段落”)进行。向段值添加 1 会使指针在内存中提前 16 个字节。

      您的偏移量被限制为 16 位值,因此要访问超出该值,您必须使用“远指针”。远指针的长度为 32 位。但不是真正的 32 位,高 16 位实际上只是段,与偏移量重叠。

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

      有几个“模型”(我们当时称之为)。它基本上是近/远代码指针、近/远数据指针的所有组合的矩阵(以及 IIRC 一种拥有 > 64KB 全局变量的方法)。

      编译器提供了对每个指针是远还是近的细粒度控制。对频繁访问的代码/数据使用近代码和近数据是一种优化,并在需要时放置额外的扩展指令来声明特定的指针或函数。

      Far 调用和返回的影响并不像 far 数据那么严重,因为指针被取消引用的次数比函数被调用的次数多。

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

      链接和加载

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

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

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-07-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多