DOS continually calls Interrupt 28h from the low-order polling functions as they wait for keyboard input. This signal says that DOS is idle and that a TSR may interrupt provided it does not overwrite the I/O stack.
Summary: A TSR may interrupt DOS Functions 01 through 0Ch provided it does not call them.
An active TSR that calls DOS must monitor Interrupt 28h with a handler. When the handler gains control, it checks the TSR request flag. If the flag indicates the TSR has been requested and if system hardware is inactive, the handler executes the TSR. Since control must eventually return to the idle DOS function which has stored data on the I/O stack, the TSR in this case must not call any DOS function that also uses the I/O stack. Table 19.1 shows which functions set up the I/O stack for various versions of DOS.
Table 19.1 DOS Internal Stacks
Function |
Critical Error flag | DOS Version 2.x 3.0 3.1+ |
||||
01–0Ch | Clear Set | I/O* Aux* | I/O Aux | I/O Aux | ||
33h | Clear Set | Disk* Disk | Disk Disk | Caller* Caller | ||
50h–51h | Clear Set | I/O Aux | Caller Caller | Caller Caller | ||
59h | Clear Set | n/a* n/a | I/O Aux | Disk Disk | ||
5D0Ah | Clear Set | n/a n/a | n/a n/a | Disk Disk | ||
62h | Clear Set | n/a n/a | Caller Caller | Caller Caller | ||
All others | Clear Set | Disk Disk | Disk Disk | Disk Disk |
* I/O = I/O stack, Aux = auxiliary stack, Disk = disk stack, Caller = caller's stack, n/a = function not available.
TSRs that perform tasks of long or indefinite duration should themselves call Interrupt 28h. For example, a TSR that polls for keyboard input should include an INT 28h instruction in the polling loop, as shown here:
poll: int 28h ; Signal idle state
mov ah, 1
int 16h ; Key waiting?
jnz poll ; If not, repeat polling loop
sub ah, ah
int 16h ; Otherwise, get key
This courtesy gives other TSRs a chance to execute if the InDos flag happens to be set.