Почему этот код, меняющий обработчик прерывания клавиатуры, не работает на реальном компьютере (выводит символ 'A' только 1 раз), хотя нормально работает в эмуляторах bochs и qemu? Используется компилятор gcc, архитектура x86.
asm(".code16gcc
");
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef struct __attribute__((__packed__)) FullAddr{
ushort offset;
ushort seg;
} FullAddr;
#define MAIN_CODE_START 0x9200
asm (
"xorw %ax, %ax
\t"
"movw %ax, %ds
\t"
/*По этому адресу компоновщик помещает начало секции .data*/
"movw (0x9202), %ax
\t"
"movw %ax, %ds
\t"
"movw %ax, %ss
\t"
"movw $0xFFFB, %sp
\t"
"jmp main"
);
void print_char(char c){
asm volatile("int $0x10" : : "a"(0x0E00 | c), "b"(7));
}
void get_int_addr(ushort interrupt, FullAddr *addr)
{
asm volatile(
"pushw %%ds
\t"
"movw %w3, %%ds
\t"
"movw (%w2), %w0
\t"
"movw 2(%w2), %w1
\t"
"popw %%ds"
: "=c"(addr->offset), "=a"(addr->seg):"b"(interrupt*4),"a"(0)
);
}
void set_int_addr(ushort interrupt, uint func){
asm volatile(
"cli
\t"
"pushw %%ds
\t"
"movw %w2, %%ds
\t"
"movw %w0, (%w1)
\t"
"movw %%cs, 2(%w1)
\t"
"popw %%ds
\t"
"sti"
: : "c"(func-MAIN_CODE_START), "b"(interrupt*4), "a"(0):
);
}
void wait(uint usec)
{
asm volatile("int $0x15": : "a"(0x8600), "c"(usec>>16), "d"(usec&0xFFFF));
}
FullAddr addr;
void handler_func(){
print_char('A');
}
void handler();
asm(
"handler:
\t"
"pushal
\t"
"call handler_func
\t"
"popal
\t"
"ljmp *addr
\t"
"iret
\t"
);
void main(){
get_int_addr(9, &addr);
set_int_addr(9, (uint)handler);
while(1){
wait(1000);
}
}
На всякий случай выкладываю полный код проекта , включающий образ дискеты (может, кто-нибудь запустит). Для компиляции нужно запустить файлы build.sh и build_main.sh.
Ответ
На английской версии SO подсказали, что проблема в значении сегмента %ds, которое может измениться внутри обработчика прерывания 0x15 ( функция wait) в BIOS. И в этот момент может сработать прерывание 0x9, которое я перехватываю. А в обработчике последнего, как раз используется сегмент %ds (ljmp *addr). То есть, переход будет не туда, куда нужно. Решение: каждый раз в функции handler менять %ds на своё значение, а в конце функции возвращать старое. Код:
void handler();
asm(
"handler:
\t"
"pushw %ds
\t"
"pushal
\t"
"xorw %ax, %ax
\t"
"movw %ax, %ds
\t"
"movw (0x9202), %ax
\t"
"movw %ax, %ds
\t"
"call handler_func
\t"
"pushfw
\t"
"lcallw *addr
\t"
"popal
\t"
"popw %ds
\t"
"iretw
\t"
);
Комментариев нет:
Отправить комментарий