Встроенная сборка в 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 ответа:
То, что вы делаете, неправильно по нескольким причинам:
Обычные функции C не могут безопасно использоваться в качестве подпрограмм обслуживания прерываний, поскольку они неправильно сохраняют, загружают и восстанавливают регистры процессора. Они должны быть объявлены с помощью ключевого слова
interrupt
. И у них будетiret
для вас в конце. Переменные, которые могут изменяться в программе асинхронно из подпрограмм прерывания, должны быть объявлены какvolatile
, иначе вы рискуете получить к ним неверный доступ оптимизирован компилятором.- ваш встроенный ассемблерный код, вероятно, искажает содержимое регистров процессора. Одна из ошибок этого кода заключается в том, что ваши блоки
asm
путаются с указателем стека. Первый блок выходит с несколькими дополнительными словами в стеке. Это может быть совершенно неожиданно для компилятора и может привести к поломке программы. Могут быть и другие проблемы, но я не собираюсь проверять с помощью документации компилятора, какие регистры должны быть сохранены встроенными блоками сборки. Я бы вообще этого не делал и вместо этого выбрал функциюsetvect()
.- вызов большинства стандартных библиотечных функций из внутренних подпрограмм службы прерываний вызывает проблемы, поскольку эти функции обычно не являются реентерабельными / потокобезопасными. Они могут изменять некоторые глобальные переменные или состояния совершенно неожиданным образом для остальной части программы. То же самое верно и для вызова сервисных функций 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 } }