问题
Is there any way I can set timer 60 seconds with xor ah,ah
Enter_Again:
xor ah, ah ; I should put 60 seconds here
int 16h ; The user should press S before 60 seconds
mov bl,al
cmp al,"S"
回答1:
Your previous questions suggest you are running under DOS. There is no BIOS or DOS call that times out keyboard input. You can latch (chain) onto Interrupt 0x1c which is a user interrupt routine that gets called about 18.2 times a second. One minute is about 1092 of these interrupts. Your timer interrupt can simply call the old user interrupt and then increment a tick count.
Your main program can then check if a key has been pressed with BIOS call Int 16h/AH=1. 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 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.
If the key you are looking for hasn't been pressed then you simply compare the current global timer tick count with 1092. If it hasn't been reached you go back and check the keyboard buffer again for a character.
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 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
Note: Since this code was written with the assumption this was under DOS, I use DOS services Int 21h/AH=35h (DOS get current interrupt vector) and Int 21h/AH=25h (DOS Set Interrupt Vector) to replace the user timer interrupt with our own and then restore the interrupt vector back to its original state before returning to DOS. You could replace those DOS calls by reading/modifying the real mode interrupt vector table directly. Under DOS it is preferable to do it using the DOS services.
回答2:
You cannot use INT16 to set a timer. INT16 simply reads a character from the keyboard.
xor ah, ah zeros the ah register so that you are calling INT16 function 0, Read Keyboard Character. To get the time, checkout INT21, function 0x2C, Get System Time.
来源:https://stackoverflow.com/questions/55901791/how-to-set-timer