假设我的 API 是从一个只能在实模式或大实模式下工作的系统调用的。我的 API 应该显示当前的系统模式。那么它怎么知道当前模式是实模式还是大实模式呢?
笔记:
- 在大实模式下,保护模式启用位
CR0
被禁用,因此检查它没有任何区别。 - 即使启用了 A20 地址线,也并不意味着它处于大实模式。
如果你执行这个:
mov ebx, 0x10000
mov al, [ebx]
并得到 a #GP
,则段描述符DS
的原始限制为 0xFFFF,这是普通实地址模式和虚拟 8086 模式的情况。
如果您没有得到#GP
from mov al, [ebx]
,则原始限制已超出 0xFFFF(通常为 0xFFFFFFFF,但不一定如此)。
顺便说一句,在尝试上述操作之前可以并且可能应该检查 v86 模式(以防您的主机操作系统没有正确地向您的处理程序反映异常)。执行smsw
获取cr0.pe
。它将在 v86 模式下设置为 1,在实地址模式下设置为 0。cr0
直接用 with读取会在 v86 模式下mov
生成 a ,这就是为什么是首选方法。#GP
smsw
在 90 年代初期,我们使用了一种未记录的方法,让我们能够以 REAL 模式(现在可能称为 BIG REAL MODE)访问 4Gig。方法是进入保护模式,将粒度位更改为 1(表示 4K 粒度而不是 1 字节粒度),然后返回实模式并将所有段寄存器设置为 0。然后您可以使用 ebx 等访问4G 内存。
因此,如果这就是您所说的,请尝试进入保护模式并检查粒度位的设置。对不起,我所有的旧手册都在阁楼上。如果您需要这些信息,我可以将它们挖掘出来。
在大实模式下,实模式地址别名会失败。假设您处于大实模式,并且您的 DS 的值为 0x6000。由于 DS 的描述符缓存已重新加载(大实模式的定义),因此 DS:0 的物理地址不是 0x60000。如果你用另一个段寄存器点击 0x60000,那将不是同一个内存位置。
所以这里有一个食谱。在数据段中创建一个临时变量。注意它offset
与 DS 的关系。用 DS-1 的值加载 ES,更改变量的值,看看是否在 ES:( offset
+0x10) 处得到相同的值。为防止出现假阴性,请重复两次。
这不会检测到虚拟 86 模式。此外,如果 ES 也通过缓存描述符指向高内存,那么当您重新加载 ES 时,这将丢失。确保调用者代码不会依赖 ES 保持不变。
大实模式本身不是 CPU 模式 - 没有寄存器位显示“我们现在处于大实模式”。这只是使用香草实模式的 i386 特定逻辑访问高内存的一种方式。上面的过程只检查 DS 是否被这样处理;根据实现的不同,DOS 扩展器可能已经通过 ES 描述符重新加载实现了 big real,同时保持 DS vanilla。Wikipedia 说,有时甚至 CS 也会以这种方式别名,尽管由于中断,这是一个棘手的命题。