如何设置定时器

Ome*_*ero 1 x86 assembly dos bios x86-16

有什么办法可以将计时器设置为 60 秒 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"
Run Code Online (Sandbox Code Playgroud)

Mic*_*tch 5

您之前的问题表明您在 DOS 下运行。没有使键盘输入超时的 BIOS 或 DOS 调用。您可以锁存(链)到中断 0x1c 上,这是一个用户中断例程,每秒被调用 18.2 次。一分钟大约有 1092 个这些中断。您的计时器中断可以简单地调用旧的用户中断,然后增加滴答计数。

然后,您的主程序可以使用 BIOS call Int 16h/AH=1检查是否按下了某个键。如果此调用设置了零标志 ( ZF ),则键盘缓冲区中不存在任何键。此调用不会阻止等待字符,它只检查键盘缓冲区是否为空,如果不是,则不返回最近的键而不将其从缓冲区中删除。您将要使用的INT 16h / AH = 0,从键盘缓冲区删除字符如果一个已被按下,然后检查是否有人S。按下的键的 ASCII 值在寄存器AL 中。未能从缓冲区中删除字符将不允许您在将来正确检查键盘缓冲区中的下一个字符。

如果您要查找的键没有被按下,那么您只需将当前全局计时器滴答计数与 1092 进行比较。如果没有达到,您返回并再次检查键盘缓冲区中的字符。

此示例代码设置用户定时器中断处理程序并使用上述基本机制来等待S被按下。如果超时,程序将退出并显示一条消息。如果S在超时到期之前按下 ,程序会打印一条消息,然后退出。在退出回到 DOS 之前,中断向量需要恢复到程序启动时的状态。

.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
Run Code Online (Sandbox Code Playgroud)

注意:由于此代码是在假设这是在 DOS 下编写的,因此我使用 DOS services Int 21h/AH=35h (DOS get current interrupt vector) 和Int 21h/AH=25h (DOS Set Interrupt Vector) 来替换用户计时器用我们自己的中断,然后在返回 DOS 之前将中断向量恢复到其原始状态。您可以通过直接读取/修改实模式中断向量表来替换那些 DOS 调用。在 DOS 下,最好使用 DOS 服务来完成。