好的,我想我终于可以回答我自己的问题了(感谢Wyzard的有用评论!)
由于似乎没有任何库调用这样做,显而易见的解决方案是放置一个hlt
内联汇编。不幸的是,这使我的程序崩溃了。寻找原因,是因为使用的默认dpmi
服务器运行的程序在ring 3
...hlt
被保留到ring 0
. 因此,要使用它,您必须修改加载程序存根以加载dpmi
运行您的程序的服务器ring 0
。稍后见。
浏览文档时,我遇到了__dpmi_yield()。如果我们在多任务环境(Win 3.x 或 9x ...)中运行,dpmi
那么操作系统已经提供了一个服务器,当然,在这种情况下,我们希望在等待时放弃我们的时间片尝试特权hlt
。
所以,把它们放在一起,DOS 的源代码现在看起来像这样:
#undef __STRICT_ANSI__
#include <time.h>
#include <dpmi.h>
#include <errno.h>
static uclock_t nextTick;
static uclock_t tickTime;
static int haveYield;
void
ticker_init(void)
{
errno = 0;
__dpmi_yield();
haveYield = errno ? 0 : 1;
}
void
ticker_done(void)
{
}
void
ticker_start(int msec)
{
tickTime = msec * UCLOCKS_PER_SEC / 1000;
nextTick = uclock() + tickTime;
}
void
ticker_stop()
{
}
void
ticker_wait(void)
{
if (haveYield)
{
while (uclock() < nextTick) __dpmi_yield();
}
else
{
while (uclock() < nextTick) __asm__ volatile ("hlt");
}
nextTick += tickTime;
}
为了让它在普通DOS 上工作,编译的可执行文件中的加载程序存根必须像这样修改:
<path to>/stubedit bin/csnake.exe dpmi=CWSDPR0.EXE
CWSDPR0.EXE
是dpmi
运行所有代码的服务器ring 0
。
还有待测试的是,在 win 3.x / 9x 下运行时,yield 是否会影响时间。也许时间片太长了,必须检查一下。更新:使用上面的代码,它在 Windows 95 中运行良好。
该hlt
指令的使用以一种奇怪的方式破坏了兼容性..当试图通过 PDcursesdosbox 0.74
进行阻塞时,程序似乎永远挂起。getch()
然而,这不会发生在真正的 MS-DOS 6.22 in virtualbox
. 更新:这是dosbox 0.74
在当前SVN
树中修复的错误。
鉴于这些发现,我认为这是在 DOS 程序中“很好地”等待的最佳方式。
更新:通过检查所有可用的方法并选择最好的方法,可以做得更好。我发现了一个应该考虑的DOS 空闲调用。策略:
如果支持yield,请使用它(我们在多任务环境中运行)
如果支持空闲,请使用它。可选地,如果我们在 ring-0 中,hlt
每次调用 idle 之前都执行一次,因为 idle 被记录为在没有其他程序准备好运行时立即返回。
否则,在 ring-0 中只需使用简单的hlt
指令。
忙于等待作为最后的手段。
这是一个测试所有可能性的小示例程序(DJGPP):
#include <stdio.h>
#include <dpmi.h>
#include <errno.h>
static unsigned int ring;
static int
haveDosidle(void)
{
__dpmi_regs regs;
regs.x.ax = 0x1680;
__dpmi_int(0x28, ®s);
return regs.h.al ? 0 : 1;
}
int main()
{
puts("checking idle methods:");
fputs("yield (int 0x2f 0x1680): ", stdout);
errno = 0;
__dpmi_yield();
if (errno)
{
puts("not supported.");
}
else
{
puts("supported.");
}
fputs("idle (int 0x28 0x1680): ", stdout);
if (!haveDosidle())
{
puts("not supported.");
}
else
{
puts("supported.");
}
fputs("ring-0 HLT instruction: ", stdout);
__asm__ ("mov %%cs, %0\n\t"
"and $3, %0" : "=r" (ring));
if (ring)
{
printf("not supported. (running in ring-%u)\n", ring);
}
else
{
puts("supported. (running in ring-0)");
}
}
我的 github 存储库中的代码反映了这些更改。