【问题标题】:8086 DOS or BIOS function to sleep for less than 1 second, like 0.75s?8086 DOS 或 BIOS 功能休眠不到 1 秒,比如 0.75 秒?
【发布时间】:2018-12-12 03:37:54
【问题描述】:

所以基本上我的游戏运行良好,蛇移动的默认延迟为 1 秒。

我已经看到int 1Ah/AH=00 中断进行延迟以使其移动得更慢,但是如何让它移动得更快呢?还是我理解中断错误?这个想法是创建关卡,这样当你达到某个分数时,蛇会移动得更快。假设我想要蛇移动 0.75 秒的延迟,然后是 0.5 秒,等等。

【问题讨论】:

  • 根据members.tripod.com/vitaly_filatov/ng/asm/asm_029.1.htmlint 1ah/ah=0只是读取当前时间,计数器每秒滴答18.2次。所以你正忙着等待它改变?你应该能够等待任何你想要的间隔到那个粒度。
  • IIRC,没有办法做到这一点,所以 DOS 游戏使用繁忙循环来猜测正确的时钟周期数。这就是为什么 DOSBox 模拟器需要模拟许多不同的时钟速度,而不仅仅是 DOS 运行的最高时钟速度; 80 年代中期的游戏在 90 年代早期的硬件上运行时会尖叫,更不用说现代的了;没有人能真正以现代处理器运行繁忙循环的速度播放它们。
  • @ShadowRanger - 在标准 PC 上,定时器通道 0 以 1.19318 mhz 或 838.0965 nsecs / 周期运行,提供快速准确的定时器,但它是一个 16 位定时器(读取它取决于它的模式2 或模式 3),每秒循环 18.2 次,因此使用定时器中断来增加 32 位计数器的高 16 位。许多(但不是全部)游戏使用这种方法,并且对 cpu 速度不敏感。这种游戏的一个例子是最初的极品飞车赛车游戏。
  • @rcgldr:谢谢!我站得更正了。我知道很多游戏都做错了,我认为这是因为不可能把它做对。我没有正确解释不良的开发实践。 :-)
  • @ShadowRanger - 有些游戏确实搞错了,比如 Wing Commander (1990) 的原始版本,它的运行速度与 CPU 和显卡允许的速度一样快。

标签: assembly delay dos x86-16 bios


【解决方案1】:

我已经看到了 int 1Ah/AH=00 中断来进行延迟以使其移动得更慢,但是如何让它移动得更快呢?还是我理解中断错误?

此 BIOS 函数返回一个“自午夜以来的滴答声”值,其中每个滴答声约为 55 毫秒,每天大约有 1573040 个滴答声。

创建 N 毫秒的延迟;将 N 除以“约 55”,然后将自午夜以来的当前刻度添加到它。这将是您的到期刻度。一般来说,你想要像while(current_tick < expiry_tick) { HLT(); } 这样的东西,其中HLT() 是CPU 的HLT 指令,它等待一个IRQ。但是,这并不是那么简单,因为您必须关心当前的刻度在午夜滚动。

要解决这个问题,您实际上需要更多类似的东西:

#define TICKS_PER_DAY    1573040
#define MS_PER_TICK      55

int milliSecondWait(unsigned int milliseconds) {

    // Calculate expiry time

     (midnightFlag, tick) = get_tick();
     expiryTick = tick + milliseconds / MS_PER_TICK;


    // Wait for the right day

    while(expiryTick > TICKS_PER_DAY) {
        (midnightFlag, tick) = get_tick();
        if( !midnightFlag) {
           HLT();
        } else {
            expiryTick -= TICKS_PER_DAY;
        }
    }

    // Wait for the right tick

    do {
        (midnightFlag, tick) = get_tick();
        if(midnightFlag) {
            break;      // Tick rolled over skipping the expiry time
        }
    } while(tick < expiryTick);

注意:这不是有效的 C(它是伪代码,我假设您将用某种汇编语言实现),(midnightFlag, tick) = get_tick(); 应该是一个返回 2 个值的函数(如 BIOS 函数确实)。

这可以更精确地完成(例如浮点数学,更准确MS_PER_TICK),可能包括将定时器/PIT 芯片配置为以更快的频率运行(默认的 18.2 Hz 频率是芯片可以运行的最慢频率)运行)并欺骗 BIOS 使其仍然正常工作(通过使用计数器实现时钟分频器并仍然以 18.2 Hz 调用 BIOS 的 IRQ 处理程序);但我猜你不需要所有这些并发症。 ;)

【讨论】:

  • 计时器以 1.19318 兆赫兹运行,计数器设置为 65536 个周期,间隔为 ~= 54.9255 毫秒。将计数器设置为 1193 将导致约 1 毫秒的滴答声,并通过模式调用 BIOS IRQ:14 次@ 55 滴答刻 + 1 次@ 54 滴答将在 0.000814% 以内,或每 149147.5 秒约慢 1 秒 ~ 1.72天。
  • 所以像这样的东西可能是 delaytime db 5 ;... lessdelay proc mov ah, 00 int 1Ah mov bx, dx delay: int 1Ah sub dx, bx cmp dl, delaytime jl delay ret lessdelay endp I我猜这是我称之为 rotine 的地方,它使它“明显”正常工作,但说我已经测试过它 - 当游戏模式开始时 - 当蛇生长时 - 当它读取蛇的位置并且它并没有真正改变1s,我如何检查它是否真的在改变刻度? ps:格式很抱歉
  • 我会试试的。谢谢!
  • @Jen - 计数器在午夜重置(DX 在午夜从 0AFH 变为 00H),所以只需等待 dx 更改 n 次: [ mov si,n | mov 啊,0 |诠释 1ah | mov bx,dx | dly0: mov 啊,0 |诠释 1ah | cmp bx,dx | je dly0 | mov bx,dx |十二月 | jnz dly0].
  • @Jen - 我刚刚在 Windows 7 虚拟 PC 中测试了这个运行 MSDOS 6.22 并且它可以工作。
猜你喜欢
  • 2011-05-10
  • 1970-01-01
  • 2011-08-13
  • 1970-01-01
  • 1970-01-01
  • 2011-09-06
  • 1970-01-01
  • 2013-02-15
  • 1970-01-01
相关资源
最近更新 更多