如何在 16 位保护模式下对超过 64kb 的数据进行编程?

How to program in 16 bit protected mode with more than 64kb of data?

我想为 16 位保护模式编写一些代码,特别是一个带有一些程序的简单操作系统。我知道这听起来很愚蠢,而且可能确实如此,但我有兴趣了解如何在这些约束下编写程序。

我想知道在 16 位保护模式下工作的各种操作系统(例如 OS/2 和 Win 3.1)采用了哪些约定。他们使用什么 ABI?远指针是如何传递的?是否有针对不同代码模型的多个 ABI?

澄清一下,我知道远指针是什么以及它们在 API 级别上的使用方式。我想知道的是这在装配级别上是如何工作的。远指针的段是否在堆栈上传递?有什么特殊约定吗?

大多数 16 位保护模式 APIs 将远指针作为参数。远指针是一个 32 位值,包含 16 位偏移量(低位字)和 16 位选择器(高位字,选择器指的是段)。它像任何其他参数一样通过将它放在堆栈上来按值传递。通常这些指针只能引用最大 65536 字节的内存区域,但不同的远指针可以引用不同的内存区域,允许使用超过 64K 的内存。

例如,在 16 位 Windows API (Win16) 中,函数 GetClientRect 具有以下记录接口:

void GetClientRect(hwnd, lprc)

HWND hwnd;    /* handle of window */
RECT FAR* lprc;   /* address of structure for rectangle   */

符号 FAR 是一个宏,在使用 16 位 Windows API 时扩展为 far 关键字。今天这个 API 函数被记录为采用 LPRECT 参数,这意味着被读取为 "long (far) pointer to RECT"。符号 LPRECT 在 32 位和 64 位 Windows API 中定义为 RECT * 的类型定义。如果仍然支持 16 位 API,那么它将是 RECT far * 的 typedef。

没有针对不同内存模型(小型、中型、紧凑型、大型)的单独 API,因为在指针(以及函数本身)上使用了 far 关键字API 可从所有内存模型访问。编译器会看到它采用了远指针并根据需要提升任何近(16 位)指针。

据我所知,以前引入80286架构的AT系统时,内存由64 KB段组成,这是使用16位地址寄存器(实模式)可以寻址的最大地址。当在汇编中完成编码时,近跳和远跳可能分别发生在段内或段外。发生远跳转时,在汇编中,首先要给出要跳转的段,然后是段内的本地地址。

在保护模式下,描述符 table 可用于扩展段的寻址范围,从而增加机器上可用的内存量。

如果代码实现为可重入,则可以使用适当的 BIOS 和 OS 中断来实现多任务以及终止和驻留 (TSR) 程序。