首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用djgpp在DOS中等待--取代繁忙等待的替代方案?

使用djgpp在DOS中等待--取代繁忙等待的替代方案?
EN

Stack Overflow用户
提问于 2015-07-25 10:54:52
回答 1查看 490关注 0票数 2

我最近写了一个小小的诅咒游戏,因为它只需要一些计时器机制和诅咒的实现,尝试为DOS构建它的想法是很自然的。pdcurses为DOS提供了诅咒。

POSIX和Win32之间的时间已经不同了,所以我定义了这个接口:

代码语言:javascript
复制
#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创建了一个新的接口实现:

代码语言:javascript
复制
#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至少节省一些能源。

供参考,这是整个消息来源

EN

回答 1

Stack Overflow用户

发布于 2015-07-25 19:17:18

好的,我想我终于可以回答我自己的问题了(谢谢怀扎德的有益评论!)

显而易见的解决方案是在内联程序集中放置一个hlt,因为似乎没有任何库调用这样做。不幸的是,这破坏了我的程序。究其原因,这是因为默认的dpmi服务器运行ring 3 .中的程序. hlt是为ring 0保留的。因此,要使用它,必须修改加载程序存根,以加载在dpmi中运行程序的ring 0服务器。回头再看。

在浏览文档时,我偶然发现了产量()。如果我们在多任务处理环境(Win 3.x或9x .)中运行,操作系统将提供一个dpmi服务器,当然,在这种情况下,我们希望在等待时放弃时间,而不是尝试特权hlt

因此,综合起来,DOS的源代码现在看起来如下:

代码语言:javascript
复制
#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上工作,必须对编译后的可执行文件中的加载程序存根进行如下修改:

代码语言:javascript
复制
<path to>/stubedit bin/csnake.exe dpmi=CWSDPR0.EXE

CWSDPR0.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空闲呼叫。战略:

  1. 如果支持产量,请使用此方法(我们正在多任务处理环境中运行)
  2. 如果支持空闲,请使用此方法。或者,如果我们处于ring-0,每次调用空闲之前执行一个hlt,因为在没有其他程序准备运行时,可以立即返回空闲。
  3. 否则,在ring-0中只需使用普通的hlt指令即可。
  4. 忙着-最后的办法是等待。

下面是一个测试所有可能性的小示例程序(DJGPP):

代码语言:javascript
复制
#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, &regs);
    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回购程序中的代码反映了这些变化。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31625723

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档