2

私はプロジェクトを持っています。それが「フォーリングブロック」というシンプルなゲームです。ゲームエリアは、20x20 サイズのグリッドと見なされます。画面の上部からブロックが落ちてきて、下部にブロックを撃つヒーローがいます。ゲームの目的は、ブロックが最終ラインに到達する前にブロックを撃つことです。彼は常に最下層にとどまっています。ユーザーがキーボードのスペース ボタンを押すたびに弾丸が生成され、主人公は左右の矢印キーで一番下の行を移動します。Turbo C ++ 3.0 でこれらのキーボード割り込みを処理する方法についてはわかりません。また、"dos.h" や "int 21H" を使用することも禁止されています。これらのプロジェクトについてのヒントを教えてください。

編集:私はこの情報を見つけましたが、それを実装する方法を理解できませんでした:

キーボードでキーが押されると、「メイク コード」という名前のスキャン コードとともに割り込みが生成され、キーが離されると、キーボード コントローラーによって「ブレーク コード」が生成されます。PC では、キーボードはチップによって制御され、ポート番号 60h と 61h に割り当てられます。キーボードでキーが押されると、スキャン値が 60h のレジスタに入れられます。次のコマンドでこのスキャン コードを取得できます: in al,60h スキャン コードを取得した後、次のコマンドで 61h でチップのコマンド レジスタをプログラミングするキーボードをリセットする必要があります: in al,61h または al,82h out 61h ,al and al,7fh out 61h,al 各割り込みサービス ルーチンの最後に、PIC サービス ビットをクリアし、割り込み終了 (EOI) コマンド 20h をアドレス 20h の PIC ポートに送信します。mov al,20h out 20h,al

4

2 に答える 2

3

ファイルkbdc.c :

#include <stdio.h>

extern void SetNewIrq9Isr(void);
extern void RestoreOldIrq9Isr(void);

#define SCAN_BUF_SIZE 1024

extern volatile unsigned char ScanBuf[SCAN_BUF_SIZE];
extern volatile unsigned ScanReadIdx;
extern volatile unsigned ScanWriteIdx;

const char ScanToChar[] =
  "??1234567890-=??"
  "QWERTYUIOP[]??AS"
  "DFGHJKL;\"`?\\ZXCV"
  "BNM,./??? ";


int IsScanCodeAvailable(void)
{
  return ((ScanWriteIdx - ScanReadIdx) & (SCAN_BUF_SIZE - 1)) != 0;
}

unsigned char GetScanCode(void)
{
  unsigned char code;

  while (!IsScanCodeAvailable());

  code = ScanBuf[ScanReadIdx];

  ScanReadIdx++;
  ScanReadIdx &= SCAN_BUF_SIZE - 1;

  return code;
}

int main(void)
{
  SetNewIrq9Isr();

  printf("Press keys to see scan codes.\nPress ESC to exit.\n");

  for (;;)
  {
    unsigned code, symbol;

    code = GetScanCode();

    symbol = code & 0x7F;
    symbol = (symbol < sizeof(ScanToChar)) ? ScanToChar[symbol] : '?';

    printf("scan code: 0x%02X, symbol: \"%c\"\n", code, (char)symbol);

    if (code == 1)
    {
      break;
    }
  }

  RestoreOldIrq9Isr();
  return 0;
}

ファイルkbda.asm :

GLOBAL _SetNewIrq9Isr, _RestoreOldIrq9Isr
GLOBAL _ScanBuf, _ScanReadIdx, _ScanWriteIdx

SEGMENT _TEXT PUBLIC CLASS=CODE USE16

; void SetNewIrq9Isr(void);
_SetNewIrq9Isr:
        push    bx
        push    es

        mov     bx, 9 * 4
        mov     ax, 0
        mov     es, ax

        cli

        mov     ax, [es:bx]
        mov     [_pOldIrq9Isr], ax
        mov     word [es:bx], _NewIrq9Isr

        mov     ax, [es:bx + 2]
        mov     [_pOldIrq9Isr + 2], ax
        mov     [es:bx + 2], cs

        sti

        pop     es
        pop     bx
        ret

; void RestoreOldIrq9Isr(void);
_RestoreOldIrq9Isr:
        push    bx
        push    es

        mov     bx, 9 * 4
        mov     ax, 0
        mov     es, ax

        cli

        mov     ax, [_pOldIrq9Isr]
        mov     [es:bx], ax

        mov     ax, [_pOldIrq9Isr + 2]
        mov     [es:bx + 2], ax

        sti

        pop     es
        pop     bx
        ret

_NewIrq9Isr:
        pusha
        push    ds

        mov     ax, _DATA
        mov     ds, ax

        in      al, 60h
        push    ax

        in      al, 061h
        mov     ah, al
        or      al, 080h
        out     061h, al
        mov     al, ah
        out     061h, al

        pop     ax

        ; ScanBuf[ScanWriteIdx] = scan code;
        ; ScanWriteIdx = (ScanWriteIdx + 1) & (SCAN_BUF_SIZE - 1);
        mov     bx, [_ScanWriteIdx]
        mov     [_ScanBuf + bx], al
        inc     bx
        and     bx, 1023
        mov     [_ScanWriteIdx], bx

        mov     al, 20h
        out     20h, al

        pop     ds
        popa
        iret

SEGMENT _DATA PUBLIC CLASS=DATA

_pOldIrq9Isr      resd    1

; #define SCAN_BUF_SIZE 1024
; volatile unsigned char ScanBuf[SCAN_BUF_SIZE];
; volatile unsigned ScanReadIdx = 0;
; volatile unsigned ScanWriteIdx = 0;
_ScanBuf          resb    1024
_ScanReadIdx      dw      0
_ScanWriteIdx     dw      0

出力:

Press keys to see scan codes.
Press ESC to exit.
scan code: 0x10, symbol: "Q"
scan code: 0x90, symbol: "Q"
scan code: 0x11, symbol: "W"
scan code: 0x91, symbol: "W"
scan code: 0x12, symbol: "E"
scan code: 0x92, symbol: "E"
scan code: 0x02, symbol: "1"
scan code: 0x82, symbol: "1"
scan code: 0x03, symbol: "2"
scan code: 0x83, symbol: "2"
scan code: 0x04, symbol: "3"
scan code: 0x84, symbol: "3"
scan code: 0x01, symbol: "?"

さて、これをコンパイルする方法についていくつかの言葉。

を使用してNASMでアセンブリ ファイルをコンパイルしますnasm.exe -f obj kbda.asm。生成しkbda.objます。Borland/Turbo C/C++ IDE でプロジェクトを作成し、その中kbdc.ckbda.obj. コードがスモール メモリ モデルまたはタイニー メモリ モデルでコンパイルされることを確認します (基本的には、ニア関数として呼び出されることを確認する必要があります) SetNewIrq9Isr()RestoreOldIrq9Isr()コンパイルします。

いくつかの注意事項があります。

まず、と の間で呼び出された場合getc()gets()scanf()、 などの関数はどれも機能しません。彼らはプログラムをハングアップさせます。SetNewIrq9Isr()RestoreOldIrq9Isr()

shift第 2 に、コードは,controlおよびaltキーを追跡しません。これが意味することは、IDE 内から を押してこのプログラムを実行すると、プログラムが終了したときに、IDEは がまだ押されているctrl+F9と考える可能性が高いということです。ctrlキーボードを「ロック解除」するには、 を押して離す必要がありますctrl。このプログラムの起動時に他の同様のキーを押したままにすると、同じことが当てはまる場合があります。shift、 、controlおよびのすべてaltが解放されるまで待機する追加のコードを含めることができます。BIOS データ領域で現在の状態を確認できると思います。

もちろん、アセンブリ ファイルを NASM 構文から TASM 構文に変換し、TASM でコンパイルすることもできます。私は無料のツール、Turbo C++ 1.01 と NASM を使用しているだけです。

更新: TASM の asm ファイルは次のとおりです。

PUBLIC _SetNewIrq9Isr, _RestoreOldIrq9Isr
PUBLIC _ScanBuf, _ScanReadIdx, _ScanWriteIdx

        .386

_TEXT SEGMENT PUBLIC 'CODE' USE16
        ASSUME CS:_TEXT, DS:_DATA

; void SetNewIrq9Isr(void);
_SetNewIrq9Isr PROC NEAR
        push    bx
        push    es

        mov     bx, 9 * 4
        mov     ax, 0
        mov     es, ax

        cli

        mov     ax, es:[bx]
        mov     _pOldIrq9IsrOfs, ax
        mov     word ptr es:[bx], offset _NewIrq9Isr

        mov     ax, es:[bx + 2]
        mov     _pOldIrq9IsrSeg, ax
        mov     es:[bx + 2], cs

        sti

        pop     es
        pop     bx
        ret
_SetNewIrq9Isr ENDP

; void RestoreOldIrq9Isr(void);
_RestoreOldIrq9Isr PROC NEAR
        push    bx
        push    es

        mov     bx, 9 * 4
        mov     ax, 0
        mov     es, ax

        cli

        mov     ax, _pOldIrq9IsrOfs
        mov     es:[bx], ax

        mov     ax, _pOldIrq9IsrSeg
        mov     es:[bx + 2], ax

        sti

        pop     es
        pop     bx
        ret
_RestoreOldIrq9Isr ENDP

_NewIrq9Isr PROC NEAR
        pusha
        push    ds

        mov     ax, _DATA
        mov     ds, ax

        in      al, 60h
        push    ax

        in      al, 061h
        mov     ah, al
        or      al, 080h
        out     061h, al
        mov     al, ah
        out     061h, al

        pop     ax

        ; ScanBuf[ScanWriteIdx] = scan code;
        ; ScanWriteIdx = (ScanWriteIdx + 1) & (SCAN_BUF_SIZE - 1);
        mov     bx, _ScanWriteIdx
        mov     _ScanBuf[bx], al
        inc     bx
        and     bx, 1023
        mov     _ScanWriteIdx, bx

        mov     al, 20h
        out     20h, al

        pop     ds
        popa
        iret
_NewIrq9Isr ENDP

_TEXT ENDS

_DATA SEGMENT PUBLIC 'DATA' USE16

_pOldIrq9IsrOfs   dw      ?
_pOldIrq9IsrSeg   dw      ?

; #define SCAN_BUF_SIZE 1024
; volatile unsigned char ScanBuf[SCAN_BUF_SIZE];
; volatile unsigned ScanReadIdx = 0;
; volatile unsigned ScanWriteIdx = 0;
_ScanBuf          db      1024 dup (?)
_ScanReadIdx      dw      0
_ScanWriteIdx     dw      0

_DATA ENDS

END

を使用してコンパイルしtasm.exe /ml kbda.asmます。残りは同じです。

于 2011-12-03T13:38:33.700 に答える
0

私も昔、似たようなコースを受講しました。基本的に必要なことは、システムのキーボード割り込みハンドラーによって処理される前に、キーボード割り込みをキャッチすることです。独自の割り込みハンドラを作成し、それをキーボード割り込みにバインドする必要があります。作業が完了したら、元のシステム キーボード割り込みハンドラーを呼び出します。

于 2011-12-03T11:57:21.810 に答える