I'm creating a program, that should print "Hello from handler" five seconds after start. At first I created interrupt 4ah by proc called create_interrupt
.
This interrupt causes int_handler
, that prints string "Hello from handler".
Then proc "alarm" get curent time, add 5 second to it and set alarm by func 06h of int 1ah.
This alarm should call int 4ah after 5 seconds from start, but it doesn't works and I don't know why. If I call 4ah "by hand", just by adding "int 4ah" it works, it means that interruption created and works correctly. But I need to call this interrupt by alarm.
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
DOSBox does not give access to the Real Time Clock.
Functions like int 1Ah AH=06h
(BIOS.SetSystemAlarm) and int 21h AH=2Dh
(DOS.SetSystemTime) don't operate correctly!
Why is this? Well, DOSBox is an emulator that targets playing (existing) DOS games.
Typically games don't set the (real time) clock or use a real time clock alarm. Games rather deal with delays of all kinds. That explains why DOSBox does not support this kind of functionality.
Although we have to accept the developer's choice, it would have been nice if they had provided us with the documented return codes that signal an error.
int 1Ah AH=06h
(BIOS.SetSystemAlarm) had better returned CF=1
int 21h AH=2Dh
(DOS.SetSystemTime) had better returned AL=FFh
Luckily the Timer Tick works fine.
In order to create a program that prints "Hello from handler!", we can succesfully use the 1Ch interrupt. A delay of 5 seconds would translate to 91 timer ticks because there're about 18.2 ticks in every second. When using this 1Ch interrupt it's very important to chain to the original (previous) handler so other processes can still manage their business.
Below is my version of this task:
; 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,'$'
Michael Petch made this valuable comment about DOS re-entrancy or the lack thereof.
I've tested the above program under DOSBox with no problems at all because, quoting from my programmer's manual,
When DOS waits for keyboard input, it idles in a loop, reading characters as they come in. As long as DOS is waiting at this point, it is save to use the file handling and other functions, even though the InDOS flag indicates otherwise.
The above program does nothing but waiting for keyboard input so this is fine. In a more elaborate program one could both check the InDOS flag and intercept int 28h
.
However a simple solution remains to avoid using DOS altogether and output the message using e.g. BIOS.Teletype:
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