#c #ассемблер #gcc #прерывания
Почему этот код, меняющий обработчик прерывания клавиатуры, не работает на реальном компьютере (выводит символ 'A' только 1 раз), хотя нормально работает в эмуляторах bochs и qemu? Используется компилятор gcc, архитектура x86. asm(".code16gcc\n"); 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\n\t" "movw %ax, %ds\n\t" /*По этому адресу компоновщик помещает начало секции .data*/ "movw (0x9202), %ax\n\t" "movw %ax, %ds\n\t" "movw %ax, %ss\n\t" "movw $0xFFFB, %sp\n\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\n\t" "movw %w3, %%ds\n\t" "movw (%w2), %w0\n\t" "movw 2(%w2), %w1\n\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\n\t" "pushw %%ds\n\t" "movw %w2, %%ds\n\t" "movw %w0, (%w1)\n\t" "movw %%cs, 2(%w1)\n\t" "popw %%ds\n\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:\n\t" "pushal\n\t" "call handler_func\n\t" "popal\n\t" "ljmp *addr\n\t" "iret\n\t" ); void main(){ get_int_addr(9, &addr); set_int_addr(9, (uint)handler); while(1){ wait(1000); } } На всякий случай выкладываю полный код проекта , включающий образ дискеты (может, кто-нибудь запустит). Для компиляции нужно запустить файлы build.sh и build_main.sh.
Ответы
Ответ 1
На английской версии SO подсказали, что проблема в значении сегмента %ds, которое может измениться внутри обработчика прерывания 0x15 ( функция wait) в BIOS. И в этот момент может сработать прерывание 0x9, которое я перехватываю. А в обработчике последнего, как раз используется сегмент %ds (ljmp *addr). То есть, переход будет не туда, куда нужно. Решение: каждый раз в функции handler менять %ds на своё значение, а в конце функции возвращать старое. Код: void handler(); asm( "handler:\n\t" "pushw %ds\n\t" "pushal\n\t" "xorw %ax, %ax\n\t" "movw %ax, %ds\n\t" "movw (0x9202), %ax\n\t" "movw %ax, %ds\n\t" "call handler_func\n\t" "pushfw\n\t" "lcallw *addr\n\t" "popal\n\t" "popw %ds\n\t" "iretw\n\t" );
Комментариев нет:
Отправить комментарий