Handling A Keyboard Interrupt with Turbo C ++ 3.0

后端 未结 2 1777
自闭症患者
自闭症患者 2021-01-07 03:08

I have a project. That is a simple game , \"Falling Blocks\" . The game area is considered as a grid, which has 20x20 size. There will be falling blocks from top of the scre

相关标签:
2条回答
  • 2021-01-07 03:45

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

    File 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
    

    Output:

    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: "?"
    

    Now, some words on how to compile this.

    Compile the assembly file with NASM using nasm.exe -f obj kbda.asm. It'll produce kbda.obj. Create a project in Borland/Turbo C/C++ IDE, include in it kbdc.c and kbda.obj. Make sure the code is going to be compiled in the small or tiny memory model (basically, we need to make sure SetNewIrq9Isr() and RestoreOldIrq9Isr() are going to be called as near functions). Compile it.

    There are a few caveats.

    First, none of the getc(), gets(), scanf(), etc functions will work if called between SetNewIrq9Isr() and RestoreOldIrq9Isr(). They will hang the program.

    Second, the code doesn't keep track of the shift, control and alt keys. What it means to you is that, if you run this program from within the IDE by pressing ctrl+F9, when the program finishes, the IDE will most likely think ctrl is still being held down. To "unlock" the keyboard you'll have to press and release ctrl. The same may apply to other similar keys if they're held down when this program starts. You can include extra code to wait until all of shift, control and alt are released. I believe you can find their current state in the BIOS data area.

    You can, of course, convert the assembly file from NASM syntax to TASM syntax and compile it with TASM. I'm simply using free tools, Turbo C++ 1.01 and NASM.

    UPDATE: Here's the asm file for TASM:

    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
    

    You compile it using tasm.exe /ml kbda.asm. The rest is the same.

    0 讨论(0)
  • 2021-01-07 03:53

    I also took a similar course back in the days. Basically what you need to do, is to catch the keyboard interrupt before it handled by the system keyboard interrupt handler. You need to create your own interrupt handler, and the bind it to the keyboard interrupt. once you're done with your work, call the original system keyboard interrupt handler.

    0 讨论(0)
提交回复
热议问题