gpt4 book ai didi

assembly - 在 MS-DOS/FreeDOS 应用程序中实现超时

转载 作者:行者123 更新时间:2023-12-03 02:42:00 25 4
gpt4 key购买 nike

我正在用 C 语言为 FreeDOS 编写一个 DOS 保护模式应用程序。编译器是 Open Watcom 1.8。

我需要编写一个超时例程,它启动一个超时变量,然后当超时变量变为零时,应用程序将知道已经过了指定的毫秒数。

我认为这可以通过编写 ISR 来完成。每次调用 ISR 时,ISR 都会递减超时变量。应用程序不断检查超时变量是否为零。

现在我的问题是:

是否有任何可靠的方法来实现这种超时ISR。我读到了有关 0x1C 中断向量的内容,但它提供的分辨率仅为 55ms。我需要更多的决心。例如1ms 甚至更低(如果可能的话)。

如何做到这一点?有什么想法或建议吗?

最佳答案

以下演示展示了如何将默认计时器速率从 18.2 Hz 更改为其他值。使用 Borland Turbo C++(作为实模式 EXE 应用程序)和 Open Watcom C++ 1.9(作为 DPMI/dos4gw 应用程序)进行编译。

// file: tmr.c
#include <stddef.h>
#include <stdio.h>
#include <dos.h>
#include <conio.h>

#ifdef __WATCOMC__
// Define Borland C aliases:
#define getvect _dos_getvect
#define setvect _dos_setvect
#define outportb outp
#define inportb inp
#define disable _disable
#define enable _enable
#endif

typedef unsigned uint;
typedef unsigned char uchar;

void interrupt (*pOldInt1C)(void) = NULL;

volatile uint int1Ccnt = 0;

void interrupt NewInt1C(void)
{
int1Ccnt++;
pOldInt1C();
}

void SetPitResolutionInHz(uint ResolutionInHz)
{
uint count;

if (ResolutionInHz < 18 || ResolutionInHz >= 65535)
return;

count = (ResolutionInHz == 18) ? 0 : (uint)(1193181 / ResolutionInHz);

disable();

outportb(0x43, 0x34);
outportb(0x40, (uchar)(count & 0xFF));
outportb(0x40, (uchar)(count >> 8));

enable();
}

int main(void)
{
pOldInt1C = getvect(0x1C);
setvect(0x1C, &NewInt1C);

printf("3 seconds delay, default timer rate...\n");
while (int1Ccnt < 18*1*3)
{
static uint last = 0;
if (last != int1Ccnt)
{
printf("1"); fflush(stdout);
last = int1Ccnt;
}
}
printf("\n");

SetPitResolutionInHz(18*2);
printf("3 seconds delay, double timer rate...\n");
int1Ccnt = 0;
while (int1Ccnt < 18*2*3)
{
static uint last = 0;
if (last != int1Ccnt)
{
printf("2"); fflush(stdout);
last = int1Ccnt;
}
}
printf("\n");

SetPitResolutionInHz(18*3);
printf("3 seconds delay, triple timer rate...\n");
int1Ccnt = 0;
while (int1Ccnt < 18*3*3)
{
static uint last = 0;
if (last != int1Ccnt)
{
printf("3"); fflush(stdout);
last = int1Ccnt;
}
}
printf("\n");

// Set default rate: 1193181 MHz / 65536 = 18.2 Hz
SetPitResolutionInHz(18*1);
printf("3 seconds delay, default timer rate...\n");
int1Ccnt = 0;
while (int1Ccnt < 18*1*3)
{
static uint last = 0;
if (last != int1Ccnt)
{
printf("1"); fflush(stdout);
last = int1Ccnt;
}
}
printf("\n");

setvect(0x1C, pOldInt1C);

return 0;
}

它将以默认速率打印 1 秒、2 秒和 3 秒,默认速率的两倍和三倍,各 3 秒。

请注意,这段代码会搞乱 BIOS/DOS 计时(两者都使用定时器中断来实现各种延迟)。要解决此问题,您需要 Hook 向量 8 (IRQ0) 而不是向量 0x1C(它是从向量 8 ISR 调用的),并以大约默认速率从新 IRQ0 ISR 调用原始 IRQ0 ISR(您需要为此计算中断数)。当您不从 ISR 中调用它时,您需要在从 ISR 返回之前通过执行 outportb(0x20, 0x20); 手动发出中断处理结束信号(原始 ISR 会这样做,但是如果你不调用它,这是你的责任)。

编辑:请注意,在虚拟化环境中,计时器中断可能会丢失或以不规则的间隔传递,特别是如果您设置了较高的中断率并且您的 PC 正忙于其他任务。此外,即使在物理机上,您也可能会遇到高定时器频率的问题,系统管理中断 (SMI) 会在中断传递中引入抖动/可变延迟。您无法摆脱它们,它们由 BIOS 透明地处理。

关于assembly - 在 MS-DOS/FreeDOS 应用程序中实现超时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11499991/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com