Ваши предыдущие вопросы предполагают, что вы работаете под DOS.Нет вызова BIOS или DOS, который прерывает ввод с клавиатуры.Вы можете фиксировать (цепочку) на Прерывание 0x1c , которое является пользовательской процедурой прерывания, которая вызывается примерно 18,2 раза в секунду.Одна минута - это около 1092 таких прерываний.Ваше прерывание по таймеру может просто вызвать старое пользовательское прерывание, а затем увеличить счетчик тиков.
Ваша основная программа может затем проверить, была ли нажата клавиша с вызовом BIOS Int 16h / AH = 1 ,Если в этом вызове установлен нулевой флаг ( ZF ), то в буфере клавиатуры нет клавиши.Этот вызов не блокирует ожидание символов, он только проверяет, пуст ли буфер клавиатуры и возвращает ли он самый последний ключ без удаления его из буфера.Вам нужно будет использовать Int 16h / AH = 0 , чтобы удалить символ из буфера клавиатуры IF , один из которых был нажат, а затем проверить, был ли он S .Значение ASCII нажатой клавиши находится в регистре AL .Невозможность удалить символ из буфера не позволит вам в будущем правильно проверить буфер клавиатуры на наличие следующего символа.
Если искомая клавиша не была нажата, вы просто сравниваете текущийсчетчик общего таймера с 1092. Если он не был достигнут, вернитесь назад и снова проверьте буфер клавиатуры на наличие символа.
В этом примере кода настраивается обработчик прерываний таймера пользователя и используется базовый механизм, описанный выше.ждать нажатия S .Если время ожидания истекло, программа завершает работу с сообщением об этом.Если до истечения времени ожидания нажать кнопку S , программа напечатает сообщение об этом и затем выйдет.Перед выходом обратно в DOS векторы прерываний должны быть восстановлены до того состояния, в котором они находились при запуске программы.
.model small
.stack 100h
KBD_TIMEOUT EQU 1092 ; 1092 = ~60 seconds (18.2hz*60)
; Max timer value is 65535 which is approximately
; 3600 seconds (one hour)
.data
s_in_time_str db "'S' pressed within 60 seconds$"
s_not_in_time_str db "'S' NOT pressed within 60 seconds$"
.code
; User timer interrupt handler called by Int 08h
; It occurs approximately every 18.2 times a second
; Upon entry CS is the only register that has an expected value
; CS is the code segment where the interrupt handler and the
; interrupt handler data reside
user_timer_int PROC
; Call (chain) to the original interrupt vector
; by pushing flags register and doing a FAR CALL to old vector
pushf
call dword ptr [cs:int1c_old_ofs]
; Increase timer tick by 1
inc word ptr [cs:timer_tick]
iret
user_timer_int ENDP
; Setup interrupt handlers needed by this program
set_interrupts PROC
push ds
; Hook our timer interrupt handler to the user interrupt timer vector
mov ax, 351ch ; AH=35h (Get interrupt vector)
; AL=1Ch (User timer interrupt vector)
int 21h ; Get interrupt vector
; Int 21h/ah=35 will return interrupt vector address in ES:BX
mov [cs:int1c_old_ofs], bx
mov ax, es
mov [cs:int1c_old_seg], ax
mov ax, 251ch ; AH=25h (Set interrupt vector)
; AL=1Ch (User timer interrupt vector)
; Set DS:DX to our user interrupt routine
; DS:DX = CS:user_timer_int
push cs
pop ds
mov dx, offset user_timer_int
int 21h ; Set interrupt vector
pop ds
ret
set_interrupts ENDP
; Restore interrupts to original state
restore_interrupts PROC
push ds
; Restore user timer interrupt vector to original routine
mov ax, 251ch ; AH=25h (Set interrupt vector)
; AL=1Ch (User timer interrupt vector)
; Set DS:DX to our user interrupt routine
; DS:DX = CS:user_timer_int
mov dx, [cs:int1c_old_ofs]
mov cx, [cs:int1c_old_seg]
mov ds, cx
int 21h ; Set interrupt vector
pop ds
ret
restore_interrupts ENDP
main PROC
mov ax, @data
mov ds, ax ; Initialize the data segment
call set_interrupts
; Reset timer to 0
mov word ptr [cs:timer_tick], 0
sti ; Ensure interrupts are enabled
key_chk_loop:
hlt ; Wait (HLT) until next interrupt occurs
mov ah, 1
int 16h ; AH=1 BIOS Check if keystroke pressed
; ZF flag set if no key pressed, AL=ASCII char pressed
jz no_key ; If no key pressed check if we have timed out
mov ah, 0
int 16h ; AH=0 BIOS get keystroke (removes it from keyboard buffer)
; If a key has been pressed we need to remove it from the
; keyboard buffer with Int 16/AH=0.
cmp al, 'S' ; If a key has been pressed was it 'S'?
je s_in_time ; If so print pressed message and exit
no_key:
; Check if the counter has reached the timeout
cmp word ptr [cs:timer_tick], KBD_TIMEOUT
jb key_chk_loop ; If time out hasn't been reached go back&check kbd again
timed_out:
; Print timed out message and exit
mov ah, 9h
mov dx, offset s_not_in_time_str
int 21h
jmp finished
s_in_time:
; Print success message and exit
mov ah, 9h
mov dx, offset s_in_time_str
int 21h
finished:
; Restore interrupts to original state before returning to DOS
call restore_interrupts
; Exit back to DOS
mov ax, 4c00h
int 21h
main ENDP
; Place the interrupt data in the code segment instead of the data segment
; to simplify the interrupt handler
int1c_old_ofs dw 0 ; Offset of original int 1c vector
int1c_old_seg dw 0 ; Segment of original int 1c vector
timer_tick dw 0 ; Timer tick count (incremented 18.2 times a second)
END main
Примечание : поскольку этот код был написан с допущением, что он находился под DOS, Я использую службы DOS Int 21h / AH = 35h (DOS получает текущий вектор прерывания) и Int 21h / AH = 25h (DOS Set Interrupt Vector) для замены прерывания таймера пользователя нанаш собственный, а затем восстановить вектор прерывания обратно в исходное состояние, прежде чем вернуться в DOS.Вы можете заменить эти вызовы DOS, читая / изменяя таблицу векторов прерываний реального режима напрямую.Под DOS предпочтительно делать это с помощью служб DOS.