|
Hello World as a TSR fileLet me start this page by warning you that THIS CODE IS UNSTABLE Why? Well, as we discussed in class, DOS interrupts (services in 21h) have a problem with reentrancy. This program does not check if we have a reentrancy problem or not. By running this program at the command prompt you should type the name of the program, press ESC a few times and then plan on rebooting your machine. If you never press ESC you can continue to run just fine as the keyboard handler will be transparent to every key except for ESC.
Take a look at the code, and I'll discuss some of what is happening below
TITLE hw_tsr.asm
;=======================================================================
; prints hello world to the screen every time the user presses ESC
;=======================================================================
_TEXT SEGMENT USE16 WORD PUBLIC 'CODE'
ASSUME cs:_TEXT, ds:_TEXT
Start:
StartRes EQU $
jmp begin ; jump to start of install code
; (i.e. jump over resident code)
orig80hVector LABEL DWORD
orig80hOffset DW ?
orig80hSegment DW ?
orig09hVector LABEL DWORD
orig09hOffset DW ?
orig09hSegment DW ?
msg DB "Hello world :)", 0Ah, 0Dh, "$"
;=======================================================================
; newInt80h - resident code that will print the hello world message
; to the screen in a DANGEROUS WAY - you really need
; to verify that you are not going to reenter the int 21h
; services. However, to keep this example *very* simple
; I have ommited these checks.
;=======================================================================
; PreCond -- Cannot be in an int 21h service
; PostCond -- Hello world is printed to the screen, or the machine
; crashes due to a reentrancy problem.
;=======================================================================
PROC newInt80h FAR
push ax
push dx
push ds
push cs
pop ds
mov dx, OFFSET msg
mov ah, 09h
int 21h
pop ds
pop dx
pop ax
iret
newInt80h ENDP
;=======================================================================
; newInt09h - new keyboard handler that will trap the ESC hotkey
; and pass everything else on
;=======================================================================
; PreCond -- None
; PostCond -- if ESC is pressed, int80 will be generated
; otherwise the key is passed on to int09 as normal
;=======================================================================
PROC newInt09h FAR
cli
push ax ; save registers
in al, 60h ; read PORT A
cmp al, 81h ; see if the key released was ESC
je runOurHandler
pop ax ; restore stack and chain to
jmp cs:orig09hVector ; the old interrupt handler
runOurHandler:
int 80h ; print hello world message
in al, 61h ; toggle bit 7 to ACK key event
or al, 80h
out 61h, al
and al, 7Fh
out 61h, al
mov al, 20h ; ACK hardware (int controller)
out 20h, al
pop ax ; restore registers and return
iret
newInt09h ENDP
EndRes EQU $
begin:
mov ax, _TEXT ; set our ASSUMEd segements
mov ds, ax
;----------------------------------------------------------------
; Get Old Vectors
;----------------------------------------------------------------
mov al, 80h ; vector to retrieve
mov ah, 35h ; function: get interrupt vector
int 21h
mov orig80hOffset, bx ; save the old seg:off of 80h vector
mov orig80hSegment, es
mov al, 09h ; vector to retrieve
mov ah, 35h ; function: get interrupt vector
int 21h
mov orig09hOffset, bx ; save the old seg:off of 09h vector
mov orig09hSegment, es
;----------------------------------------------------------------
; Set New Vectors
;----------------------------------------------------------------
mov dx, OFFSET newInt80h
mov ax, SEG newInt80h
mov ds, ax ; ds:dx points to our interrupt routine
mov al, 80h ; interrupt function to get
mov ah, 25h ; function: set interrupt vector
int 21h
mov dx, OFFSET newInt09h
mov ax, SEG newInt09h
mov ds, ax ; ds:dx points to our interrupt routine
mov al, 09h ; interrupt function to get
mov ah, 25h ; function: set interrupt vector
int 21h
;----------------------------------------------------------------
; Exit Program but leave the resident code!!!
;----------------------------------------------------------------
mov dx, (100h+EndRes-StartRes+15)/16
mov ax, 3100h ; function: TSR exit to DOS
int 21h
_TEXT ENDS
_STACK SEGMENT USE16 STACK 'STACK'
DW 100 dup(?)
_STACK ENDS
END Start
Download the above file here First, it should be noted that there is no The basic layout of the file is as follows
_TEXT ....
ASSUME ...
Start:
jmp begin
; resident data
; resident code
Begin:
; installation code
; installation data
_TEXT ENDS
So what happens? Well, your program runs starting at
When you press a key, you trigger interrupt 09h. In the code above, if the key is matched to the ESC key, then our main function is called. To make life more complicated, I have implemented this function as another interrupt to show that interrupts may be nested, typically you want your overall time spent in an interrupt to be as brief as possible. The important point is that when you press a key (or release a key) you generate interrupt 09h. If the key pressed/released does not match ESC the old interrupt handler is chained to and we can pretend that our code never ran. Of course, if the ESC key is pressed, then we do not want to pass that keystroke to the system and we need to handle all of the hardware ACKs.
Our int 09h code generates another interrupt (int 80h) which prints our message to the screen using interrupt 21h services. This is typically a bad idea, unless you ensure that you will NOT have a reentrancy problem. Again, I warn you, run this code, but plan on rebooting your machine.
A few things to note, in addition to having no We will be talking about this in class - but hopefully you will be able to bring questions to ask. Please read the chapter in your book regarding TSR programming. It does an excellent job of covering the basics of TSRs. Last Modified: January 26, 1999 - Barry E. Mapen |