Dos 中断处理程序锁定程序

Dos Interrupt Handler Locks Up Program

我正在尝试制作一个简单的游戏 engine/library 来创建我自己的 DOS 游戏。我目前正在努力让输入正常工作。我遇到的问题是我处理 int 9(键盘输入)的自定义 ISR 锁定了我的程序。

注意:我通过使用编译器的 _dos_getvect() 和 _dos_setvect() 让它工作;但是,我不知道为什么我自己的自定义 getInterruptVector() 和 setInterruptVector() 不起作用。如果有人能解释原因,我将不胜感激。

在 "interrupt_hooker.c" 中查看有问题的代码。

main.c

#include "input.h"
#include "stdio.h"

int main(int argc, char *argv[])
{
    hookKeyboardISR();

    unsigned char i = 0;

    while(1)
    {
        printf("%i", 1);
        //i = getKey(SCAN_ESC);
        //printf("%i", i);
    }

    unhookKeyboardISR();
    return 0;
}

input.h

#ifndef INPUT_H_INCLUDED
#define INPUT_H_INCLUDED

#include "scan_codes.h"

void            hookKeyboardISR();
void            unhookKeyboardISR();

unsigned char   getKey(unsigned char scan_code);

unsigned char   isExtended();

#endif // INPUT_H_INCLUDED

input.c

#include "input.h"
#include "interrupt_hooker.h"

#ifndef NULL
#define NULL ((void*)0)
#endif // NULL

unsigned char key_array[MAX_SCAN_CODES] = {0};
unsigned char extended_key = 0;

void __interrupt (*oldKeyboardISR)() = NULL;


unsigned char getKey(unsigned char scan_code)
{
    return key_array[scan_code];
}

unsigned char isExtended()
{
    return extended_key;
}

void __interrupt keyboardISR()
{
    unsigned char scan_code = 0;

    unsigned char temp = 0;
    scan_code = inp(0x60);
    outp(0x61,(temp = inp(0x61)) | 0x80);
    outp(0x61,temp);
    /*asm
    {
        in  al, 60h         //get scan code from keyboard controller
        mov scan_code, al   //put it in scan_code

        in  al, 61h
        or  al, 80h
        out 61h, al
        xor al, 80h
        out 61h, al
    }*/

    switch(scan_code)
    {
    //if extended code
    case 0xe0:
        extended_key = 1;
        break;

    case 0xe1:
        break;

    default:
        //check if break code
        if(scan_code & 0x80){
            scan_code &= 0x7f;

            //set key to released
            key_array[scan_code] = 0;

        }
        else{
            //set key to pressed
            key_array[scan_code] = 1;
        }

        extended_key = 0;
        break;
    }

    //send EOI
    outp(0x20, 0x20);
    /*asm
    {
        sti
        mov al, 20h
        out 20h, al
    }*/
}

void hookKeyboardISR()
{
    if(oldKeyboardISR == NULL){
        getInterruptVector(0x09, oldKeyboardISR);
        setInterruptVector(0x09, keyboardISR);
    }
}

void unhookKeyboardISR()
{
    if(oldKeyboardISR != NULL){
        setInterruptVector(0x09, oldKeyboardISR);
        oldKeyboardISR = NULL;
    }
}

interrupt_hooker.h

#ifndef INTERRUPT_HOOKER_H_INCLUDED
#define INTERRUPT_HOOKER_H_INCLUDED

void getInterruptVector(unsigned char int_number, void __interrupt (*isr)());

void setInterruptVector(unsigned char int_number, void __interrupt (*isr)());

#endif // INTERRUPT_HOOKER_H_INCLUDED

interrupt_hooker.c

void getInterruptVector(unsigned char int_number, void __interrupt (*isr)())
{
    asm
    {
        cli
        mov al, int_number
        mov ah, 35h
        int 21h

        mov word ptr [isr], bx
        mov word ptr [isr+2], es
        sti
    }
}

void setInterruptVector(unsigned char int_number, void __interrupt (*isr)())
{
    asm
    {
        cli
        mov dx, word ptr [isr+2]
        mov ds, dx
        mov dx, word ptr [isr]

        mov al, int_number
        mov ah, 25h
        int 21h
        sti
    }
}

mov dx, word ptr [isr] 可能会尝试使用您刚刚破坏的 ds 除非 isr 被解析为 bp+offset 在这种情况下它将使用 ss 并且应该不错。另外,我猜编译器不会高兴你销毁了 ds.

更安全的代码可以是:

cli
push ds
mov al, int_number
mov ah, 25h
mov dx, word ptr [isr]
mov ds, word ptr [isr+2]
int 21h
pop ds
sti

您的 getInterruptVector 已损坏,因为它缺少间接级别 - 您只是在修改 C 中按值传递的参数。您必须将其调用为 getInterruptVector(0x09, &oldKeyboardISR); (注意 &),当然在你的函数中取消引用它,比如:

    mov al, int_number
    mov ah, 35h
    int 21h

    mov di, word ptr [isr]
    mov [di], bx
    mov [di+2], es

(假设是 16 位 near 指针,不确定您的内存模型是什么。)