您之前的问题表明您正在 DOS 下运行。没有 BIOS 或 DOS 调用导致键盘输入超时。你可以锁住(链条)中断0x1c http://vitaly_filatov.tripod.com/ng/asm/asm_001.14.html这是一个用户中断例程,每秒调用大约 18.2 次。一分钟大约有 1092 个这样的中断。您的计时器中断可以简单地调用旧的用户中断,然后增加滴答计数。
Your main program can then check if a key has been pressed with BIOS call Int 16h/AH=1 http://www.ctyme.com/intr/rb-1755.htm. If the Zero Flag (ZF) is set by this call no key is present in the keyboard buffer. This call doesn't block waiting for characters, it only checks if the keyboard buffer is empty and if it isn't returns the most recent key without removing it from the buffer. You will want to use Int 16h/AH=0 http://www.ctyme.com/intr/rb-1754.htm to remove the character from the keyboard buffer IF one has been pressed and then check if it was S. The ASCII value of the key pressed is in register AL. Failure to remove the character from the buffer will not allow you to properly check the keyboard buffer in the future for the next character.
如果您要查找的键尚未被按下,那么您只需将当前全局计时器滴答计数与 1092 进行比较。如果尚未达到,您将返回并再次检查键盘缓冲区中的字符。
This sample code sets up a user timer interrupt handler and uses the basic mechanism described above to wait for S to be pressed. If it times out the program exits with a message indicating that. If S is pressed before the time out expires the program prints a message to that effect and then exits. Before exiting back to DOS the interrupt vectors need to be restored to what they were when the program started.
.model small
.stack 100h
KBD_TIMEOUT EQU 60*182/10 ; 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
Note:由于此代码是在 DOS 下假设编写的,因此我使用 DOS 服务国际21小时/AH=35小时 http://www.ctyme.com/intr/rb-2740.htm(DOS获取当前中断向量)和国际21小时/AH=25小时 http://www.ctyme.com/intr/rb-2602.htm(DOS设置中断向量)用我们自己的中断向量替换用户定时器中断,然后在返回DOS之前将中断向量恢复到原始状态。您可以通过直接读取/修改实模式中断向量表来替换这些 DOS 调用。在 DOS 下,最好使用 DOS 服务来完成此操作。