在8086 dos汇编中获取精确时间
Get precise time in 8086 dos assembly
我正在使用视频模式 13h 为 ms-dos 汇编制作乒乓球游戏。我需要在每帧之间添加 1/60 秒的延迟。我尝试使用 AH=0 的中断 1A 来获取系统时间,但这每秒仅更改 18.2 次(因此我的帧速率将为 18.2 fps)。我怎样才能更精确地测量时间?
gameloop:
mov ah, 0
int 1ah ;get system time
cmp dx, prevt
jz gameloop
mov prevt, dx
实际上,您可能不希望帧之间有 1/60 秒的延迟(即,一帧结束到下一帧开始)。
您可能想要的是从一帧开始到下一帧开始的延迟。或者,更简单地说每秒 60 帧 (fps)。
如果 tick 周期小于该值,实际上 不会将您限制在较低的帧速率,除非它是导致渲染代码 运行 的实际 tick(例如有中断)。
您可以使用一些技巧,通过使用自适应等待循环来获得更高的帧速率。
我接触(非 ARM)程序集已经有一段时间了,所以我将用伪代码进行解释。我还将使用 20/秒的滴答周期(请参阅附录了解如何处理其他速率)来简化此处的数学运算。
您需要的是一个尽可能快地 渲染帧的循环 但是,在该循环的某处,包含等待循环的代码:
reg1 = busyTime + 1
while reg1 != 0:
reg1 = reg1 - 1
现在唯一的技巧是弄清楚 busyTime
的值应该是多少,以获得正确的帧速率。
由于您有可用的基准时间和当前时间,并且您实际上可以计算您渲染的帧,您可以计算出当前帧率:
tps = 20
fpsRate = frameCount * tps / (currentTime - baseTime)
tps
或 ticks/second 变量允许您以秒为单位工作,因为当前和基准时间以滴答为单位,以秒为单位更容易思考。
As an aside, I'm actually using this exact calculation in my current work task to figure out the FPS rates we can get out of our embedded system (~74fps with vsync, which isn't too bad).
所以,调整 busyTime
:
就很简单了
- 如果
fpsRate
小于六十,减少busyTime
但不低于零。
- 如果
fpsRate
大于 60,增加 busyTime
但不允许溢出。
- 如果
fpsRate
刚好是六十,请不要理会 busyTime
。
这样,繁忙的循环将自动调整到正确的持续时间以保持每秒所需的帧数。
我在计算时发现的唯一棘手的一点是,在 50fps 下,我的数字很快就变得相当大。
因为我只对最后十秒左右感兴趣,所以我可以即时调整值以避免溢出。一个简单的调整可以是:
// Once we have eleven seconds of data,
// strip away the first second.
while currentTime - baseTime >= tps * 11:
// Get rid of frames.
frameCount = frameCount - fpsRate * tps
// Get rid of time.
baseTime = baseTime + tps
// Then calculate new FPS and adjust busy loop.
fpsRate = frameCount / (currentTime - baseTime)
switch (fpsRate):
< 60: busyTime = busyTime - 1
> 60: busyTime = busyTime + 1
这实际上是 "queue" 最长十秒的数据。它实际上 不是一个队列,因为它不维护个人计数,而是使用平均值来调整数据。但是,除此之外,它的行为就像一个。
附录:仅用整数处理每秒 18.2 个滴答等数字也相对简单。
只在五秒边界而不是一秒边界上工作(每秒 18.2 个滴答正好是每五秒 91 个滴答):
// Once we have twenty seconds of data (four
// five-second slots), strip away the first
// five seconds (one slot).
tp5s = 91
while currentTime - baseTime >= tp5s * 4:
// Get rid of frames.
frameCount = frameCount - fpsRate * tp5s
// Get rid of time.
baseTime = baseTime + tp5s
假设这是实模式 DOS 运行ning 在 PC 上,您可以使用计时器 0(8254 通道 0)进行繁忙循环。这是一个快速计时器。您可能需要连接到 INT 8,因为当计时器环绕时会出现问题。我不记得所有细节,但是如果在读取定时器的指令中出现 INT 8,则需要重新读取。
来自我对旧汇编计时器例程的评论的信息:
; Timer based on 8254 channel 0 and system timer interrupt (8) ;
; Channel 0 runs at 1.19318 mhz or 838.0965 nsecs / cycle ;
; System timer interrupts every 65536 cycles = 54.925 ms ;
; or about 18.2 interrupts / second ;
; 1 ms = 1193.18 cycles ;
; 1 hour = 65536 * 65536 cycles = 3599.59 secs ;
Link 到 运行 固定频率且不随时间漂移的代码示例。它在 C 中,它使用 sleep(),在你的情况下你只是循环,但你会了解如何以固定频率将代码获取到 运行:
how to sleep accurately in a while loop in C (Linux)?
我正在使用视频模式 13h 为 ms-dos 汇编制作乒乓球游戏。我需要在每帧之间添加 1/60 秒的延迟。我尝试使用 AH=0 的中断 1A 来获取系统时间,但这每秒仅更改 18.2 次(因此我的帧速率将为 18.2 fps)。我怎样才能更精确地测量时间?
gameloop:
mov ah, 0
int 1ah ;get system time
cmp dx, prevt
jz gameloop
mov prevt, dx
实际上,您可能不希望帧之间有 1/60 秒的延迟(即,一帧结束到下一帧开始)。
您可能想要的是从一帧开始到下一帧开始的延迟。或者,更简单地说每秒 60 帧 (fps)。
如果 tick 周期小于该值,实际上 不会将您限制在较低的帧速率,除非它是导致渲染代码 运行 的实际 tick(例如有中断)。
您可以使用一些技巧,通过使用自适应等待循环来获得更高的帧速率。
我接触(非 ARM)程序集已经有一段时间了,所以我将用伪代码进行解释。我还将使用 20/秒的滴答周期(请参阅附录了解如何处理其他速率)来简化此处的数学运算。
您需要的是一个尽可能快地 渲染帧的循环 但是,在该循环的某处,包含等待循环的代码:
reg1 = busyTime + 1
while reg1 != 0:
reg1 = reg1 - 1
现在唯一的技巧是弄清楚 busyTime
的值应该是多少,以获得正确的帧速率。
由于您有可用的基准时间和当前时间,并且您实际上可以计算您渲染的帧,您可以计算出当前帧率:
tps = 20
fpsRate = frameCount * tps / (currentTime - baseTime)
tps
或 ticks/second 变量允许您以秒为单位工作,因为当前和基准时间以滴答为单位,以秒为单位更容易思考。
As an aside, I'm actually using this exact calculation in my current work task to figure out the FPS rates we can get out of our embedded system (~74fps with vsync, which isn't too bad).
所以,调整 busyTime
:
- 如果
fpsRate
小于六十,减少busyTime
但不低于零。 - 如果
fpsRate
大于 60,增加busyTime
但不允许溢出。 - 如果
fpsRate
刚好是六十,请不要理会busyTime
。
这样,繁忙的循环将自动调整到正确的持续时间以保持每秒所需的帧数。
我在计算时发现的唯一棘手的一点是,在 50fps 下,我的数字很快就变得相当大。
因为我只对最后十秒左右感兴趣,所以我可以即时调整值以避免溢出。一个简单的调整可以是:
// Once we have eleven seconds of data,
// strip away the first second.
while currentTime - baseTime >= tps * 11:
// Get rid of frames.
frameCount = frameCount - fpsRate * tps
// Get rid of time.
baseTime = baseTime + tps
// Then calculate new FPS and adjust busy loop.
fpsRate = frameCount / (currentTime - baseTime)
switch (fpsRate):
< 60: busyTime = busyTime - 1
> 60: busyTime = busyTime + 1
这实际上是 "queue" 最长十秒的数据。它实际上 不是一个队列,因为它不维护个人计数,而是使用平均值来调整数据。但是,除此之外,它的行为就像一个。
附录:仅用整数处理每秒 18.2 个滴答等数字也相对简单。
只在五秒边界而不是一秒边界上工作(每秒 18.2 个滴答正好是每五秒 91 个滴答):
// Once we have twenty seconds of data (four
// five-second slots), strip away the first
// five seconds (one slot).
tp5s = 91
while currentTime - baseTime >= tp5s * 4:
// Get rid of frames.
frameCount = frameCount - fpsRate * tp5s
// Get rid of time.
baseTime = baseTime + tp5s
假设这是实模式 DOS 运行ning 在 PC 上,您可以使用计时器 0(8254 通道 0)进行繁忙循环。这是一个快速计时器。您可能需要连接到 INT 8,因为当计时器环绕时会出现问题。我不记得所有细节,但是如果在读取定时器的指令中出现 INT 8,则需要重新读取。
来自我对旧汇编计时器例程的评论的信息:
; Timer based on 8254 channel 0 and system timer interrupt (8) ;
; Channel 0 runs at 1.19318 mhz or 838.0965 nsecs / cycle ;
; System timer interrupts every 65536 cycles = 54.925 ms ;
; or about 18.2 interrupts / second ;
; 1 ms = 1193.18 cycles ;
; 1 hour = 65536 * 65536 cycles = 3599.59 secs ;
Link 到 运行 固定频率且不随时间漂移的代码示例。它在 C 中,它使用 sleep(),在你的情况下你只是循环,但你会了解如何以固定频率将代码获取到 运行:
how to sleep accurately in a while loop in C (Linux)?