Не работает обработчик прерываний в FASM

Рейтинг: 3Ответов: 1Опубликовано: 20.05.2023

Я хочу написать код с использованием своего обработчика прерываний таймера в FASM. Вот упрощённый код того, что у меня получилось:

org 100h
mov word [es:(8*4)], timer_handler
sti

Main:
jmp Main

timer_handler:
pushf
pusha
  mov dx, msg
  mov ah, $09
  int 21h
popa
popf
iret

msg db 'Hello world!$'

Я предполагаю, что 18 раз в секунду у меня выведется сообщение, но ничего не происходит. Только начал пробывать использовать свои обработчики прерываний, но ничего не выходит. Может дело в том что я не по тому адресу засылаю свой обработчик в таблицу прерываний?

Ответы

▲ 5Принят

Всё таки хотел бы разобраться с этим вопросом

При создании своих обработчиков, нужно придерживаться основного правила - всегда возвращать управление оригин.обработчику. При работе с таймером это особо критично, т.к. его может использовать не только сама ОС, но и чипсет мат.платы (учитывая, что это прерывание биос, а не дос). Работу с таблицой векторов прерываний IVT (Interrupt Vector Table) лучше доверить спец.функциям сервиса MS-DOS: AH=35h читает оригин.вектор из IVT в регистры ES:BX, а AH=25h записывает в IVT адрес нового обработчика из пары DS:DX.

Непосредственно свой обработчик можно оформить двумя способами:

  1. Дать отработать оригин.обработчику, после чего принять от него управление. Это наиболее правильный способ перехвата прерываний.
  2. Перехватить управление первым, после чего обязательно возвратить бразды оригин.обработчику.

В случае перехвата системного таймера, длительность работы нашего обработчика не должна превышать одного тика, т.е. 18/60=0,3 сек. Иначе вся система начнёт тормозить, и может даже зависнуть. Вектор(08h) таймера который вы перехватываете в своём примере, по цепочке возвращает управление в int-1Ch. Поэтому когда программам требуется таймер, то в доках по MS-DOS советуют перехватывать именно вектор(1Ch), не трогая при этом int-08h.

В примере ниже именно такой алгоритм. Здесь, приняв управление, наш обработчик сразу возвращает его по оригин.вектору. Когда системный отработает, то своей инструкцией IRET сам возвратит нам руль (не тестировалось, могут быть ошибки):

;// fasm-code
;//--------------
org 100h
jmp Initialize

oldHandler  dw 0,0  ;// под оригин.вектор

Initialize:
push es                   ;// AH=35h портит регистр ES
mov  ax,351Ch             ;// AH = функция, AL = вектор
int  21h                  ;// копируем оригин.вектор из IVT.
mov  [oldHandler],bx      ;// BX = смещение
mov  [oldHandler+2],es    ;// ES = сегмент оригин.обработчика
pop  es

mov  ax,251Ch             ;// AH=25h записывает новый вектор в IVT
mov  dx,newHandler        ;// DS:DX = указатель на наш обработчик 
int  21h  

align 16                  ;// выравнить наш обработчик на границу параграфа памяти
newHandler:
pushf                     ;// имитация инструкции INT
call dword[oldHandler]    ;// сразу возвращаем управление оригин.обработчику!

call @f                   ;// после IRET оригин.обработчика попадаем сюда..
db   'Hello World!$'      ;// строка для вывода
@@:
pop  dx                   ;// DX = адрес строки
mov  ah,9                 ;// AH = печать
int  21h

mov  ah,0Bh    ;// проверить буфер клавиатуры
int  21h       ;// 
or   al,al     ;// если была нажата любая клавиша,
jnz  @exit     ;// то на выход!
iret           ;// иначе, конец обработчика.

@exit:
mov  ax,251Ch           ;// восстановить оригин.вектор в IVT
mov  dx,[oldHandler]    ;// DS:DX = сохранённый вектор
mov  bx,[oldHandler+2]
mov  ds,bx
int  21h
int  20h

Почитать по теме: Зубков - "Ассемблер для Windows, DOS, Linux" (глава 5.8.1) https://lshoshia.science.tsu.ge/assembler.pdf