stuart@bms-at.UUCP (Stuart Gathman) (01/05/89)
First: A tip for writing TSR's in Turbo C 1.5 & 2.0. The setjmp() and longjmp() functions save ss:sp. This allows easy context switching. Non-trivial interrupt handlers usually need to switch to an internal stack to avoid overflowing a caller's stack of unknown size. (And so that pointers to stack variables work as expected for a small model TSR.) A handler plus main for small model looks something like this: -------------------------- #include <setjmp.h> #include <dos.h> #define VECT 0x7f #define DSPARAS 0x1000 /* 64K data segment */ static jmp_buf u_state, state; void interrupt handler() unsigned ...,ax; { _AX = setjmp(u_state); /* save user stack */ if (_AX == 0) longjmp(state,ax); /* switch to internal stack */ } main() { void interrupt (*saveint)(); int op; op = setjmp(state); if (op == 0) { /* should check if already installed, etc. . . . */ saveint = getvect(VECT); setvect(VECT,handler); keep(_DS - _psp + DSPARAS); /* TSR */ } if (op != UNINSTALL) { doit(op); /* do stuff */ longjmp(u_state); /* return to user */ } /* uninstall */ setvect(VECT,saveint); /* restore vector */ freemem(*(unsigned far *)MK_FP(_psp,0x2c)); /* free environment */ freemem(_psp); /* free program */ longjmp(u_state); /* return to user */ } --------------- Nice & high level, huh? The handler doesn't need to be interrupt for huge model (assuming the caller doesn't need to save AX..DX). The interrupt form ensures that _DS is loaded with the local data segment. This could also be accomplished with inline assembler as could pushing only those registers not saved by setjmp(). NOW - my question is this: is there any way to keep files open in a TSR? DOS likes to close them whenever the last process loaded before they were opened exits. (Got that?) Dos does not simply close all files whenever any program exits. It carefully keeps track of which files were opened by which psp (apparently). Unfortunately, TSR's don't count. Is there any way to fool DOS into saving TSR files also? The alternative of keeping an internal file descriptor table with qualified path names and replacements for open/read/write/lseek/close doesn't appeal to me. Strangely enough, file desciptors 0,1,2,3,4 are not closed even for TSR's. Please reply by E-mail (in addition to posting if desired). I read this group, but can't keep up with the thousands of articles. -- Stuart D. Gathman <stuart@bms-at.uucp> <..!{vrdxhq|daitc}!bms-at!stuart>
dixon@sagittarius.steinmetz (walt dixon) (01/05/89)
File handles are an index into a process data structure called the Job File Table (JFT for short). The address of the JFT and usually the JFT itself are located in the Program Segment Prefix (PSP). Each JFT entry contains either a System File Number (SFN) or 0xff indicating the entry is unused. The SFN is an index into a DOS maintained data structure called the System File Table (SFT). There is one SFT no matter how many programs/TSR's are resident. (Actually DOS maintains a separate SFT for FCB access). The SFT entry records the file name (no path), status information (EOF, raw, etc.), file position (characters processed), and a path to the device driver. Other information recorded here include the entry owner (a PSP address) and a reference count. When you perform a handle i/o operation DOS uses the handle as an index into the JFT of the current PSP. DOS maintains an internal global variable which records the PSP address of the current process. DOS int 21H AH=50h or AH=62H return the current PSP address; DOS int 21H AH=51h sets the current PSP to the value contained in BX. The main difference between the close call (int 21H ah=4ch) and the terminate and stay resident call is that the former closes all files and releases all Memory Control Blocks (MCBs) owned by the terminating process. When DOS processes the a close request, it decrements the reference count in the SFT. When this count goes to 0, the SFT entry is dealocated and the file/device is finally closed. The devices/files referenced by handles 0-4 are initially opened by command.com and are inherited by each process. So long as they are only closed once and you don't mess with the JFT, the SFT reference count will never go to 0 and the devices/files will never close. (Only the SFT owner can cause entry deallocation anyway). TSRs should set the current PSP to their own PSP before trying to reactivate. Before attempting any I/O the TSR must be sure that it is safe to do so. Significant portions of DOS are not reentrant. Making an int 21h request at the wrong time can be disasterous. You should also set up critical error and break handlers before initiating an int 21h request from a reactivating TSR. Never try to use another programs JFT to perform I/O; that is an invitation for trouble. TSRs are a very complicated subject. Check out chapter 4 of the MS DOS Developer's Guide (2nd edition) (H. Sams, 1988). This chapter covers TSRs in great detail. NB I am the author of that chapter. I get no royalties; I'm merely citing a good reference. Walt Dixon {ARPA: dixon@ge-crd.arpa } {US Mail: ge-crd } { po box 8 } { schenectady, ny 12345 } {VOICE: 518-387-5798 } Standard disclaimers apply