我最近写了一个小小的诅咒游戏,因为它只需要一些计时器机制和诅咒的实现,尝试为DOS构建它的想法是很自然的。pdcurses为DOS提供了诅咒。
POSIX和Win32之间的时间已经不同了,所以我定义了这个接口:
#ifndef CSNAKE_TICKER_H
#define CSNAKE_TICKER_H
void ticker_init(void);
void ticker_done(void);
void ticker_start(int msec);
void ticker_stop(void);
void ticker_wait(void);
#endif游戏有一次调用ticker_init()和ticker_done(),当它需要滴答和等待下一个滴答的时候,ticker_start()的间隔为毫秒。
在DOS上使用与POSIX平台相同的实现,使用setitimer()不起作用。原因之一是djgpp附带的C库没有实现waitsig()。因此,我为DOS创建了一个新的接口实现:
#undef __STRICT_ANSI__
#include <time.h>
uclock_t tick;
uclock_t nextTick;
uclock_t tickTime;
void
ticker_init(void)
{
}
void
ticker_done(void)
{
}
void
ticker_start(int msec)
{
tickTime = msec * UCLOCKS_PER_SEC / 1000;
tick = uclock();
nextTick = tick + tickTime;
}
void
ticker_stop()
{
}
void
ticker_wait(void)
{
while ((tick = uclock()) < nextTick);
nextTick = tick + tickTime;
}这在dosbox中很有魅力(我现在还没有真正的DOS系统)。但我担心的是:在这个平台上忙着等待真的是我所能做的最好的吗?我想有一个解决方案,让CPU至少节省一些能源。
供参考,这是整个消息来源。
发布于 2015-07-25 19:17:18
好的,我想我终于可以回答我自己的问题了(谢谢怀扎德的有益评论!)
显而易见的解决方案是在内联程序集中放置一个hlt,因为似乎没有任何库调用这样做。不幸的是,这破坏了我的程序。究其原因,这是因为默认的dpmi服务器运行ring 3 .中的程序. hlt是为ring 0保留的。因此,要使用它,必须修改加载程序存根,以加载在dpmi中运行程序的ring 0服务器。回头再看。
在浏览文档时,我偶然发现了产量()。如果我们在多任务处理环境(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.EXECWSDPR0.EXE是一个运行ring 0中所有代码的dpmi服务器。
还有待检验的是,在win 3.x / 9x下运行时,收益率是否会影响时间。也许时间太长了,得检查一下。Windows :上面的代码在Windows95中工作得很好。
hlt指令的使用以一种奇怪的方式破坏了与dosbox 0.74的兼容性。当试图通过getch()进行阻塞时,程序似乎永远挂起。然而,在virtualbox中真正的MS 6.22上并不会发生这种情况。更新:这是当前SVN树中修复的dosbox 0.74中的一个bug。
鉴于这些发现,我认为这是在DOS程序中“很好地”等待的最佳方式。
更新:--通过检查所有可用的方法并选择最好的方法,可以做得更好。我找到了一个应该考虑的DOS空闲呼叫。战略:
hlt,因为在没有其他程序准备运行时,可以立即返回空闲。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回购程序中的代码反映了这些变化。
https://stackoverflow.com/questions/31625723
复制相似问题