Встроенная сборка в C (DOS) - незаконная инструкция


Я пытаюсь перепрограммировать таблицу векторов инструкций. Вот код, который я использую:

#include <stdio.h>

int a=1;
void func();

void keyboard()
{
    printf("nnkeyboard!!!n");
    a=0;
    asm{iret}
}

int main ()
{
    printf("starting...");
    func();
    return 0;
}

      int vectorcs = 0;
 int vectorip = 0;

void func()
{

    printf("n*****n");
    asm{
        cli
        mov ax,0
        mov es,ax
        mov bx,36
        mov ax,word ptr es:[bx]
        mov vectorip,ax
        push ax
         mov ax,word ptr es:[bx+2]
        mov vectorcs,ax
        push ax
        mov ax,cs
        mov word ptr es:[bx],offset keyboard
        mov es:[bx+2],ax
        sti
    }
    printf("n%d %dn",vectorip,vectorcs);

    while (a) {
    }
    asm {
        cli
        mov es,bx
        mov bx,36
        pop ax
        mov word ptr es:[bx+2],ax
    }
    asm{
        pop ax
        mov word ptr es:[bx],ax
        sti
    }
}

Я использую Turbo C++ 3.0 Когда я пытаюсь запустить эту программу, " 16-битная подсистема MS-DOS: процессор NTVDM столкнулся с незаконной инструкцией." появляется. Затем он показывает содержимое регистров CS, OP и IP. Я не могу продолжать программу. Есть предложения?

2 3

2 ответа:

То, что вы делаете, неправильно по нескольким причинам:

    Обычные функции C не могут безопасно использоваться в качестве подпрограмм обслуживания прерываний, поскольку они неправильно сохраняют, загружают и восстанавливают регистры процессора. Они должны быть объявлены с помощью ключевого слова interrupt. И у них будет iret для вас в конце. Переменные, которые могут изменяться в программе асинхронно из подпрограмм прерывания, должны быть объявлены как volatile, иначе вы рискуете получить к ним неверный доступ оптимизирован компилятором.
  1. ваш встроенный ассемблерный код, вероятно, искажает содержимое регистров процессора. Одна из ошибок этого кода заключается в том, что ваши блоки asm путаются с указателем стека. Первый блок выходит с несколькими дополнительными словами в стеке. Это может быть совершенно неожиданно для компилятора и может привести к поломке программы. Могут быть и другие проблемы, но я не собираюсь проверять с помощью документации компилятора, какие регистры должны быть сохранены встроенными блоками сборки. Я бы вообще этого не делал и вместо этого выбрал функцию setvect().
  2. вызов большинства стандартных библиотечных функций из внутренних подпрограмм службы прерываний вызывает проблемы, поскольку эти функции обычно не являются реентерабельными / потокобезопасными. Они могут изменять некоторые глобальные переменные или состояния совершенно неожиданным образом для остальной части программы. То же самое верно и для вызова сервисных функций DOS из подпрограмм обслуживания прерываний (на которые, кстати, опирается printf ()). Вы можете звони только тогда, когда Дос скажет, что все в порядке. Он делает это через переменную флага InDos, и все же не все безопасно вызывать, когда InDos=0.

Смотрите, как изменить векторы прерываний, определить подпрограммы обслуживания прерываний и вызвать из них функции DOS, все с Turbo C, в ответе наэтот вопрос .

Вы также можете найти этот вопрос и его ответы полезными.

EDIT:

Вот как вы это делаете без dos.функциональность h с встроенным asm:

#include <stdio.h>

volatile int a = 1;
void interrupt (*pOldInt9)(void);
void func(void);

void interrupt keyboard(void)
{
    printf("\n\nkeyboard!!!\n");

    asm {
        in  al, 0x60
        in  al, 0x61
        mov ah, al
        or  al, 0x80
        out 0x61, al
        mov al, ah
        out 0x61, al
    }

    a = 0;

    asm {
        mov al, 0x20
        out 0x20, al
    }
}

int main(void)
{
    printf("starting...");
    func();
    return 0;
}

void func(void)
{
    printf("\n*****\n");

    asm {
        push    bx
        push    es

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

        cli

        mov     ax, es:[bx]
        mov     word ptr pOldInt9, ax
        mov     word ptr es:[bx], offset keyboard

        mov     ax, es:[bx + 2]
        mov     word ptr pOldInt9[2], ax
        mov     es:[bx + 2], cs

        sti

        pop     es
        pop     bx
    }

    while (a) {}

    asm {
        push    bx
        push    es

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

        cli

        mov     ax, word ptr pOldInt9
        mov     es:[bx], ax

        mov     ax, word ptr pOldInt9[2]
        mov     es:[bx + 2], ax

        sti

        pop     es
        pop     bx
    }
}
asm {
    cli
    mov es,bx
    mov bx,36
    pop ax
    mov word ptr es:[bx+2],ax
}

Что содержит bx перед этим кодом?

void keyboard()
{
    printf("\n\nkeyboard!!!\n");
    a=0;
    asm{iret}
}

Эта функция настроила кадр стека, который вы неправильно уничтожили.