4

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

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

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

现在我的问题是:

是否有任何可靠的方法来实现这种超时 ISR。我读到了 0x1C 中断向量,但它提供的分辨率只有 55 毫秒。我需要更多的分辨率。例如1ms 或者如果可能的话甚至更低。

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

4

2 回答 2

3

这是一个演示如何将默认计时器速率从 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;
}

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

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

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

于 2012-07-16T09:11:15.050 回答
1

您可以挂钩定时器中断 (08h) 并配置 PIT 以获得高达 1.2 Mhz 的速率。

这是一些旧的 TASM 风格的程序集,展示了如何做到这一点:

tmTimerHandler PROC
    push ds
    mov ds,cs:tmDataSeg
    add ds:tmTicker,65536
    pop ds
    jmp cs:tmOldTimer
tmTimerHandler ENDP


tmInit PROC
    mov tmDataSeg,ds
    mov tmTicker,65536
    push es
    ; Save the old timer interrupt vector
    mov ax,3508h
    int 21h
    mov dword ptr tmOldTimer+0,ebx
    mov word ptr tmOldTimer+4,es
    pop es

    ; Install our own timer interrupt vector
    push ds
    mov ax,2508h
    push cs
    pop ds
    mov edx,OFFSET tmTimerHandler
    int 21h
    pop ds

    ; Configure the PIT to generate interrupts
    ; at the maximum rate
    mov al,34h
    out 43h,al
    xor al,al  ; zero divisor
    out 40h,al
    out 40h,al
    ret
tmInit ENDP


tmClose PROC
   push ds
   mov ax,2508h
   lds edx,tmOldTimer
   int 21h
   pop ds
   ret
tmClose ENDP


; Returns the current tick count in eax
tmGetTimer PROC
   pushf
   cli
   xor eax,eax
   out 43h,al
   in al,40h
   mov ah,al
   in al,40h
   xchg ah,al
   neg eax
   add eax,tmTicker
   popf
   ret
tmGetTimer ENDP


.data
  tmOldTimer    df 0
  tmDataSeg dw 0
  tmTicker  dd 0
于 2012-07-16T08:25:13.353 回答