4

FreeDOS 用の DOS 保護モード アプリケーションを C で書いています。コンパイラは Open Watcom 1.8 です。

タイムアウト変数を開始するタイムアウトルーチンを作成する必要があり、タイムアウト変数がゼロになると、アプリケーションは指定されたミリ秒 (ms) が経過したことを認識します。

これは、ISR を作成することで実行できると思います。ISR は、ISR が呼び出されるたびにタイムアウト変数を減らします。アプリケーションは、タイムアウト変数がゼロかどうかを継続的にチェックします。

今私の質問は:

このようなタイムアウト ISR を実装する信頼できる方法はありますか。0x1C 割り込みベクトルについて読みましたが、55ms しか解決できません。もっと解像度が必要です。たとえば、可能であれば1ミリ秒またはそれ以下です。

これはどのように行うことができますか?アイデアや提案はありますか?

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;
}

デフォルトで 1 秒、2 秒、3 秒を出力し、それぞれ 3 秒ごとにデフォルトのレートを 2 倍および 3 倍にします。

注意してください、このコードは BIOS/DOS のタイミングを台無しにします (どちらもさまざまな遅延のためにタイマー割り込みを使用します)。ベクター 0x1C (ベクター 8 ISR から呼び出される) の代わりにベクター 8 (IRQ0) をフックし、新しい IRQ0 ISR から元の IRQ0 ISR をほぼデフォルトのレートで呼び出すことを回避するには (そのために割り込みをカウントする必要があります)。ISR から呼び出さない場合は、ISR から戻る前に手動で割り込み処理の終了を通知する必要がありますoutportb(0x20, 0x20);(元の 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 に答える