在 16 位 dos 机器上,有 FP_SEG 和 FP_OFF 等选项用于将指针转换为线性地址,但由于这些方法在 32 位编译器上不再存在,还有什么其他函数可以在 32 位机器上执行相同的操作? >
最佳答案
幸运的是,不需要它们,因为 32 位模式是不分段的,因此地址始终是线性的(一种简化,但让我们保持简单)。
编辑:第一个版本令人困惑,让我们再试一次。
在 16 位分段模式下(我在这里专门指旧版 DOS 程序,其他 16 位 x86 操作系统可能类似)地址以由 16 位段组成的 32 位格式给出和一个 16 位偏移量。这些组合起来形成一个 20 位线性地址(这就是臭名昭著的 640K 屏障的来源,2**20 = 1MB
和 384K 为系统和 BIOS 保留,剩下约 640K 供用户程序使用)通过将段乘以 16 = 0x10
(相当于左移 4)并添加偏移量。即:线性 = 段*0x10 + 偏移量
。
这意味着2**12
段:地址类型指针将引用同一个线性地址,因此一般情况下没有办法获得用于形成线性地址的32位值.
在使用远分段指针的旧 DOS 程序中(与近指针相反,近指针仅包含偏移量并隐式使用 ds 段寄存器),它们通常被视为 32 位无符号整数值,其中 16 个最高有效位是段,16 个最低有效位是偏移量。这给出了 FP_SEG
和 FP_OFF
的以下宏定义(使用 stdint.h
中的类型):
#define FP_SEG(x) (uint16_t)((uint32_t)(x) >> 16) /* grab 16 most significant bits */
#define FP_OFF(x) (uint16_t)((uint32_t)(x)) /* grab 16 least significant bits */
要将 20 位线性地址转换为分段地址,您有多种选择 (2**12
)。一种方法可能是:
#define LIN_SEG(x) (uint16_t)(((uint32_t)(x)&0xf0000)>>4)
#define LIN_OFF(x) (uint16_t)((uint32_t)(x))
最后一个简单的例子说明了它们是如何协同工作的:
分段地址:a = 0xA000:0x0123
作为 32 位远指针 b = 0xA0000123
20位线性地址:c = 0xA0123
FP_SEG(b) == 0xA000
FP_OFF(b) == 0x0123
LIN_SEG(c) = 0xA000
LIN_OFF(c) = 0x0123
关于c - 用于将指针转换为线性地址的 FP_SEG 和 FP_OFF 的替代方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7318958/