Как заставить таймер работать? Вызов int 4ah через 5 секунд после запуска


Я создаю программу, которая должна печатать «Привет от обработчика» через пять секунд после запуска. Сначала я создал прерывание 4ah с помощью процедуры под названием create_interrupt. Это прерывание вызывает int_handler, который печатает строку «Привет от обработчика». Затем proc "будильник" получает текущее время, добавляет к нему 5 секунд и устанавливает будильник с помощью func 06h из int 1ah.

Этот будильник должен вызвать int 4ah через 5 секунд после запуска, но он не работает, и я не знаю, почему. Если я вызываю 4ah "вручную", просто добавляя "int 4ah" работает, значит прерывание создано и работает корректно. Но мне нужно вызвать это прерывание по тревоге.

SSEG    segment stack       
    db 256 dup(0)
SSEG    ends

DSEG    segment         
    mess   db 'Hello',0dh,0ah,'$'   
    mess2   db 'Hello from handler!',0dh,0ah,'$'
    mess3   db 'Ассемблер это жопа, я уже устал''$'
DSEG    ends

CSEG    segment         
    assume cs:CSEG,ds:DSEG,ss:SSEG
begin:
    mov ax, DSEG    
    mov ds,ax       

    mov ah,09h                               
    mov dx,offset mess  
    int 21h         

    ;call far ptr int_handler
    call far ptr create_interrupt
    ;int 4ah
    call far ptr alarm



    mov ah,01h      
    int 21h                               
    mov ah,4ch      
    int 21h  

create_interrupt proc far
    push 0
    pop es
    ;pushf
    ;cli
    mov word ptr es:[4ah*4],offset int_handler
    mov word ptr es:[4ah*4+2],seg int_handler
    ;sei
    iret
create_interrupt endp

alarm proc far          
    ;pushf

    mov ah,02h      ;get current time
    int 1ah

    mov ah,06h      
    ;mov ch,ch      ;hour
    ;mov cl,cl      ;min
    ;mov dh,dh      ;sec
    ;mov dl,dl      ;mlsec
    add dh,05h      ;add 5 to seconds
    int 1ah         ;ah=06h, so this int sets alarm
    ;mov ah,01h     
    ;int 21h
    iret
alarm endp

int_handler proc far
    mov ax,DSEG     ;
    mov ds,ax       ;in ds addres of Data segment
    mov ah,09h      
    mov dx,offset mess2
    int 21h
    iret
int_handler endp




CSEG    ends        
end begin

person Klepov Aleksandr    schedule 09.06.2019    source источник
comment
Это программа для ДОС? Пожалуйста, укажите операционную систему.   -  person fuz    schedule 09.06.2019
comment
В программе много недоработок (например, слишком много IRET, прерывания DOS не реентерабельны, 5 секунд опасно мало), но главная проблема: Int1A/Fn06h не работает в DosBox. Может сигнализация используется где-то еще.   -  person rkhb    schedule 09.06.2019


Ответы (1)


DOSBox не дает доступа к часам реального времени.

Такие функции, как int 1Ah AH=06h (BIOS.SetSystemAlarm) и int 21h AH=2Dh (DOS.SetSystemTime), работают некорректно!
Почему? Что ж, DOSBox — это эмулятор, предназначенный для воспроизведения (существующих) игр DOS. Обычно игры не устанавливают часы (реального времени) и не используют будильник часов реального времени. Игры скорее имеют дело с задержками всех видов. Это объясняет, почему DOSBox не поддерживает такую ​​функциональность.
Хотя мы должны принять выбор разработчика, было бы неплохо, если бы он предоставил нам задокументированные коды возврата, сигнализирующие об ошибке.

  • int 1Ah AH=06h (BIOS.SetSystemAlarm) лучше вернуть CF=1
  • int 21h AH=2Dh (DOS.SetSystemTime) лучше вернуть AL=FFh

К счастью, Timer Tick работает нормально.

Чтобы создать программу, которая печатает «Привет от обработчика!», мы можем успешно использовать прерывание 1Ch. Задержка в 5 секунд будет означать 91 такт таймера, потому что в каждой секунде около 18,2 тика. При использовании этого 1-канального прерывания очень важно привязать его к исходному (предыдущему) обработчику, чтобы другие процессы могли по-прежнему управлять своим бизнесом.

Ниже мой вариант этой задачи:

; Create .COM program. We'll have CS=DS=ES=SS.
    org     256
; Show we're alive.
    mov     dx, Msg
    mov     ah, 09h
    int     21h
; Hook the 1Ch interrupt.
    xor     ax, ax
    mov     es, ax
    cli
    mov     ax, MyInt1C
    xchg    ax, [es:001Ch*4]
    mov     [Int1C+1], ax             ; Patch the 'jmpf' instruction (offset)
    mov     ax, cs
    xchg    ax, [es:001Ch*4+2]
    mov     [Int1C+3], ax             ; Patch the 'jmpf' instruction (segment)
    sti
; Wait for a key. Bulk of the program happens in the DOS kernel!
    mov     ah, 01h
    int     21h
; Restore the 1Ch interrupt.
    cli
    mov     ax, [Int1C+1]
    mov     [es:001Ch*4], ax
    mov     ax, [Int1C+3]
    mov     [es:001Ch*4+2], ax
    sti
; Terminate.
    mov     ax, 4C00h   
    int     21h
; -----------------------------------
MyInt1C:
    cmp     word [cs:TimeOut], 0     ; Zero disables this functionality
    je      Int1C
    dec     word [cs:TimeOut]
    jnz     Int1C                    ; Time not yet elapsed
    push    ds
    push    dx
    push    ax
    push    cs
    pop     ds
    mov     dx, Msg_
    mov     ah, 09h
    int     21h
    pop     ax
    pop     dx
    pop     ds
Int1C:
    jmpf    0:0                      ; Chain to the original handler
; -----------------------------------
TimeOut     dw  273                  ; 15 seconds x 18.2 = 273 ticks
Msg         db  'Sep says to wait 15 seconds...',13,10,'$'
Msg_        db  '15 seconds have elapsed. Press any key.',13,10,'$'

Майкл Петч сделал этот ценный комментарий о повторном входе в DOS или его отсутствии.

Я протестировал вышеуказанную программу под DOSBox без каких-либо проблем, потому что, цитируя мое руководство программиста,

Когда DOS ожидает ввода с клавиатуры, она бездействует в цикле, считывая символы по мере их поступления. Пока DOS ожидает в этот момент, она может использовать обработку файлов и другие функции, даже если флаг InDOS указывает на обратное.

Приведенная выше программа ничего не делает, кроме ожидания ввода с клавиатуры, так что все в порядке. В более сложной программе можно было бы и проверить флаг InDOS, и перехватить int 28h.
Однако остается простое решение, чтобы вообще не использовать DOS и выводить сообщение, используя, например, БИОС.Телетайп:

MyInt1C:
    cmp     word [cs:TimeOut], 0     ; Zero disables this functionality
    je      Int1C
    dec     word [cs:TimeOut]
    jnz     Int1C                    ; Time not yet elapsed
    push    ax
    push    bx
    push    si
    mov     bx, 0007h
    mov     si, Msg_
    cld
    jmps    .b
.a: mov     ah, 0Eh
    int     10h
.b: lods    byte [cs:si]
    test    al, al
    jnz     .a
    pop     si
    pop     bx
    pop     ax
Int1C:
    jmpf    0:0                      ; Chain to the original handler
; -----------------------------------
TimeOut     dw  273                  ; 15 seconds x 18.2 = 273 ticks
Msg         db  'Sep says to wait 15 seconds...',13,10,'$'
Msg_        db  '15 seconds have elapsed. Press any key.',13,10,0
person Sep Roland    schedule 16.06.2019
comment
Возможно, стоит отметить, что, поскольку DOS не является реэнтерабельной и вы не проверили флаг InDOS, Int 21h/ah=9h может испортить DOS, если таймер прервется в середине DOS, выполняющей другую работу. - person Michael Petch; 16.06.2019
comment
Конечно, BIOS не считается повторным входом. PS: я голосую за. - person Michael Petch; 16.06.2019
comment
Что касается Int 28h (обработчик простоя), SNAFU заключается в том, что DOSBox фактически не вызывает это прерывание, когда он находится в режиме ожидания. Недавно я узнал об этом, комментируя другой вопрос (я попробовал тестовый код Int 28h, который так и не был вызван). Мне пришлось пойти и посмотреть на код DOSBox, чтобы подтвердить это. - person Michael Petch; 16.06.2019
comment
@MichaelPetch Сколько раз я мечтал, чтобы DOSBox был скорее эмулятором настоящей DOS, чем игровой средой! - person Sep Roland; 16.06.2019
comment
Действительно ли BOCHS эмулирует ПК? Я так и думал. Есть ли причина, по которой вы бы не использовали BOCHS, если это то, чего вы хотите? - person Peter Cordes; 17.06.2019