Assembly DOS program

I need to write a program in Assembly for DOSBOX using MASM.
Task: Write a resident (TSR) program for DOS that changes the keyboard repeat rate every second in a cyclic manner, from the slowest to the fastest setting. The program must unload the initialization section upon completion.
Program requirements:
DOS + MASM environment.
Terminate the program using: INT 27h.
Interrupt handler installation method: Manually modify the Interrupt Vector Table using the MOV instruction.
Calling the previous interrupt handler: Use a far CALL with the flags register pushed onto the stack beforehand.
Position of the call to the previous handler: At the end of the new interrupt handler.
My code:
.MODEL small
.STACK 100h
.DATA
old_handler_offset DW 0
old_handler_segment DW 0
TickCounter db 0
CurrentRate db 0
RepeatRates db 32 dup (?)
.CODE
InitRepeatRates proc
mov cx, 32
mov si, offset RepeatRates
mov al, 1Fh
FillLoop:
mov [si], al
dec al
inc si
loop FillLoop
ret
InitRepeatRates endp
int_handler proc far
push ax
push bx
push dx
push si0
push es
push di
inc cs:TickCounter
cmp cs:TickCounter, 18
jl skip_update
mov cs:TickCounter, 0
xor si, si
mov al, cs:CurrentRate
mov si, x
mov al, cs:RepeatRates[si]
inc cs:CurrentRate
cmp cs:CurrentRate, 32
jb no_reset
mov cs:CurrentRate, 0
no_reset:
wait_input_ready:
in al, 64h
test al, 02h
jnz wait_input_ready
mov al, 0F3h
out 60h, al
wait_data_ready:
in al, 64h
test al, 02h
jnz wait_data_ready
mov al, cs:RepeatRates[si]
out 60h, al
skip_update:
pushf
push word ptr cs:old_handler_segment
push word ptr cs:old_handler_offset
retf
pop di
pop es
pop si
pop dx
pop bx
pop ax
iret
int_handler endp
start:
mov ax, @DATA
mov ds, ax
call InitRepeatRates
xor ax, ax
mov es, ax
mov ax, es:[1Ch*4]
mov old_handler_offset, ax
mov ax, es:[1Ch*4+2]
mov old_handler_segment, ax
cli
mov word ptr es:[1Ch*4], offset int_handler
mov ax, seg int_handler
mov word ptr es:[1Ch*4+2], ax
sti
mov dx, offset start
int 27h
END start
The program compiles successfully, but after I run and exit it, DOS stops responding to keyboard input. Please help, i don`t know what to do with it. Or offer general advice for coding TSR's in DOS? Thanks in advance, any help is very much appreciated!
Answer
The conditions for using int 27h
are not met
Your program is an .EXE executable for which CS does not point at the PSP, and that is a requirement for using int 27h
.
Even if you solved this detail, you would still be left with invalid accesses to your program's variables through the cs:
segment override prefix as in your executable CS does not point at the .DATA section.
The best solution would be to use the .COM executable file format. All the segment registers start out equal to each other and conveniently pointing at the PSP.
Calling the previous interrupt handler: Use a far CALL with the flags register pushed onto the stack beforehand.
pushf push word ptr cs:old_handler_segment push word ptr cs:old_handler_offset retf
You don't actually call the previous handler! The retf
instruction consumes the 2 pushes, so the old handler only 'sees' the pushed flags.
The 'old_handler' will return through the iret
instruction but there will be no far return address waiting on the stack.
...
pushf
push cs
push offset Back
push word ptr cs:old_handler_segment
push word ptr cs:old_handler_offset
retf
Back:
pop di
...
or use a far call:
...
pushf
callf dword ptr cs:old_handler_offset
pop di
...
The .COM file (I did not yet look at the keyboard stuff):
ORG 256
jmp start
old_handler_offset DW 0
old_handler_segment DW 0
TickCounter db 0
CurrentRate db 0
RepeatRates db 32 dup (0)
InitRepeatRates:
mov cx, 32
mov si, offset RepeatRates
mov al, 1Fh
FillLoop:
mov [si], al
dec al
inc si
loop FillLoop
ret
int_handler:
push ax
push bx
push dx
push si
push es
push di
inc cs:TickCounter
cmp cs:TickCounter, 18
jl skip_update
mov cs:TickCounter, 0
xor si, si
mov al, cs:CurrentRate
mov si, x <<<<<<<< ??????
mov al, cs:RepeatRates[si]
inc cs:CurrentRate
cmp cs:CurrentRate, 32
jb no_reset
mov cs:CurrentRate, 0
no_reset:
wait_input_ready:
in al, 64h
test al, 02h
jnz wait_input_ready
mov al, 0F3h
out 60h, al
wait_data_ready:
in al, 64h
test al, 02h
jnz wait_data_ready
mov al, cs:RepeatRates[si]
out 60h, al
skip_update:
pushf
push cs
push offset Back
push word ptr cs:old_handler_segment
push word ptr cs:old_handler_offset
retf
Back:
pop di
pop es
pop si
pop dx
pop bx
pop ax
iret
; -----------------------------
start:
mov ax, @DATA
mov ds, ax
call InitRepeatRates
xor ax, ax
mov es, ax
mov ax, es:[1Ch*4]
mov old_handler_offset, ax
mov ax, es:[1Ch*4+2]
mov old_handler_segment, ax
cli
mov word ptr es:[1Ch*4], offset int_handler
mov ax, seg int_handler
mov word ptr es:[1Ch*4+2], ax
sti
mov dx, offset start
int 27h
Enjoyed this question?
Check out more content on our blog or follow us on social media.
Browse more questions