Мой вопрос, почему моя подпрограмма работает нормально, когда она вызывается в независимом цикле, а не когда я вызываю ее в службе прерываний?
Int 08h вызывается системными часами примерно 18,2 раза в секунду. Поскольку это прерывание вызывается каждые 55 мсек, обработчик для него должен выполняться как можно быстрее. Поэтому в этом обработчике прерываний не рекомендуется выполнять большую работу.
Вполне возможно и очень вероятно, что DOS может быть занят, когда активирован int 08h. Если это происходит и ваш обработчик замены вызывает функцию DOS, вы получаете то, что называется повторным входом. Но с учетом того, что по замыслу DOS нет рецидивирующих проблем, со временем возникнут проблемы!
Ваш код замены для int 08h также отклоняет большую часть работы, которую необходимо выполнить здесь:
- Продвижение индикатора времени дня в 00:40: 006 Ч
- Обеспечение автоматического отключения дисков
- Вызов вектора прерывания ловушки пользователя 1Ch
- Подтверждение прерывания
- ...
Это причины, по которым ваша программа должна перехватывать прерывание 1Ch . Он имеет тот же самый приоритет, что и int 08h, но ваше взаимодействие с ним намного проще.
Обычный способ справиться с этим важным обработчиком - просто установить флаг, который основная программа может выбрать для любой обработки, которая потребуется позже, когда все будет сохранено в отношении прерываний.
Ниже приведен пример этого:
; --------------------------------------- Code section
Start:
mov [SaveInt1C + 2], cs ; Completing the far pointer
push es
push 0
pop es ; Point to IVT base
mov eax, [SaveInt1C]
xchg [es:1Ch*4], eax ; Hook int 1Ch
mov [SaveInt1C], eax ; Save vector so it can be restored!
pop es
MainLoop:
cmp byte [cs:TimerFlag], -1 ; Is flag set ?
jne NoTick
not byte [cs:TimerFlag] ; Reset flag -1 -> 0
call DisplayUpdatedTime
NoTick:
... everything else in your program ...
jmp MainLoop
Quit:
mov eax, [SaveInt1C]
push 0
pop ds ; Point to IVT base
mov [1Ch*4], eax ; Restore int 1Ch
mov ax, 4C00h ; DOS.Terminate
int 21h
TimerInterrupt:
mov byte [cs:TimerFlag], -1 ; Set flag
iret ; Complete take-over
TimerFlag db 0
; --------------------------------------- Data section
SaveInt1C dw TimerInterrupt, 0
EnableTimerDisplay db -1
... и затем нажмите кнопку, чтобы приостановить этот таймер, и нажмите эту же кнопку, чтобы возобновить этот таймер.
Не пытайтесь втиснуть это в обработчик прерываний.
Следующее, что вы можете сделать из основного цикла программы:
Проверка наличия ключа
mov ah, 01h ; BIOS.TestKey
int 16h ; -> AX ZF
Если да, то получить его
jz NoKey
mov ah, 00h ; BIOS.GetKey
int 16h ; -> AX
Если это назначенный ключ, например, p затем переключить бит разрешения
or al, 32 ; LCase
cmp al, 'p'
jne NotMyKey
not byte [EnableTimerDisplay]
Call DisplayUpdatedTime на основе этого бита включения
cmp byte [cs:TimerFlag], -1 ; Is flag set ?
jne NoTick
not byte [cs:TimerFlag] ; Reset flag -1 -> 0
cmp byte [EnableTimerDisplay], -1
jne NoTick
call DisplayUpdatedTime
NoTick:
Обычно есть два способа перехвата прерывания:
Цепочка дает другим уже существующим процессам возможность продолжать выполнять свою работу. Если мы полностью возьмем обработчик, то эти процессы будут выведены из цикла.
Пример 1 с использованием позднего связывания со старым обработчиком:
; --------------------------------------- Code section
Start:
mov [cs:SaveInt1C + 2], cs ; Completing the far pointer
push es
push 0
pop es ; Point to IVT base
mov eax, [cs:SaveInt1C]
xchg [es:1Ch*4], eax ; Hook int 1Ch
mov [cs:SaveInt1C], eax ; Save vector so it can be restored!
pop es
MainLoop:
cmp byte [cs:TimerFlag], -1 ; Is flag set ?
jne NoTick
not byte [cs:TimerFlag] ; Reset flag -1 -> 0
call DisplayUpdatedTime
NoTick:
... everything else in your program ...
jmp MainLoop
Quit:
mov eax, [cs:SaveInt1C]
push 0
pop ds ; Point to IVT base
mov [1Ch*4], eax ; Restore int 1Ch
mov ax, 4C00h ; DOS.Terminate
int 21h
TimerInterrupt:
mov byte [cs:TimerFlag], -1 ; Set flag
jmp far [cs:SaveInt1C] ; Chaining to old handler
TimerFlag db 0
SaveInt1C dw TimerInterrupt, 0
; --------------------------------------- Data section
EnableTimerDisplay db -1
Пример 2 с использованием раннего связывания со старым обработчиком:
; --------------------------------------- Code section
Start:
mov [cs:SaveInt1C + 2], cs ; Completing the far pointer
push es
push 0
pop es ; Point to IVT base
mov eax, [cs:SaveInt1C]
xchg [es:1Ch*4], eax ; Hook int 1Ch
mov [cs:SaveInt1C], eax ; Save vector so it can be restored!
pop es
MainLoop:
cmp byte [cs:TimerFlag], -1 ; Is flag set ?
jne NoTick
not byte [cs:TimerFlag] ; Reset flag -1 -> 0
call DisplayUpdatedTime
NoTick:
... everything else in your program ...
jmp MainLoop
Quit:
mov eax, [cs:SaveInt1C]
push 0
pop ds ; Point to IVT base
mov [1Ch*4], eax ; Restore int 1Ch
mov ax, 4C00h ; DOS.Terminate
int 21h
TimerInterrupt:
pushf
call far [cs:SaveInt1C] ; Chaining to old handler
mov byte [cs:TimerFlag], -1 ; Set flag
iret
TimerFlag db 0
SaveInt1C dw TimerInterrupt, 0
; --------------------------------------- Data section
EnableTimerDisplay db -1