如何设定计时器

时间:2019-04-29 10:48:10

标签: assembly x86 dos x86-16 bios

有什么办法,我可以在xor ah,ah中设置计时器60秒

 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"

2 个答案:

答案 0 :(得分:3)

您先前的问题建议您在DOS下运行。没有BIOS或DOS调用会使键盘输入超时。您可以闩锁(连锁)到Interrupt 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集)中断向量),以我们自己的用户计时器中断来代替,然后将中断向量恢复到其原始状态,然后再返回DOS。您可以通过直接读取/修改实模式中断向量表来替换那些DOS调用。在DOS下,最好使用DOS服务。

答案 1 :(得分:0)

您不能使用INT16设置计时器。 INT16只是从键盘上读取一个字符。

异或啊,啊将啊寄存器清零,以便您正在调用INT16函数0,读取键盘字符。要获取时间,请检出INT21,功能0x2C,获取系统时间。