home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_200 / 201_01 / fdirtsr.c < prev    next >
Text File  |  1979-12-31  |  15KB  |  365 lines

  1.  
  2. /****************************************************************************/
  3. /*                                        */
  4. /*     FDIRTSR.C   Version 1.2    3/2/86               Brian Irvine        */
  5. /*                                        */
  6. /*      Released to the Public Domain for use without profit            */
  7. /*                                        */
  8. /****************************************************************************/
  9. /*                                        */
  10. /* stayres.c - Code which can be used in a general way to create programs   */
  11. /*           in C which will terminate and stay resident.  This code        */
  12. /*           will allow the use of DOS I/O without having the stack and   */
  13. /*           registers clobbered.  This code is written in DeSmet C and   */
  14. /*           uses library functions which may not be available in other   */
  15. /*           C compilers.  It also makes heavy use of the #asm compiler   */
  16. /*           directive to allow in-line assembly language within the C    */
  17. /*           code.  This code provides a general outline for a main()     */
  18. /*           function which can be modified to suit the users needs. All  */
  19. /*           the code necessary to terminate and stay resident is        */
  20. /*           contained in this module; the user's program can be contain- */
  21. /*           ed entirely externally.    The program does not have to be a   */
  22. /*           COM file, and the amount of memory reserved is not limited   */
  23. /*           to 64K. The code has not been tested on program files with   */
  24. /*           greater than 64K of code.                    */
  25. /*                                        */
  26. /*           Brian Irvine                            */
  27. /*           3379 St Marys Place                        */
  28. /*           Santa Clara, CA 95051                        */
  29. /*           [71016,544]                            */
  30. /*                                        */
  31. /****************************************************************************/
  32.  
  33. #include   <stdio.h>
  34.  
  35. /*----- Global variables ---------------------------------------------------*/
  36.  
  37. unsigned   old_kbint_off;           /* save old keyboard service routine */
  38. unsigned   old_kbint_seg;           /* vector here */
  39. unsigned   dos_regs [10];           /* save DOS registers here */
  40. unsigned   dos_dseg;               /* save the DS and SS regs */
  41. unsigned   dos_sseg;               /* for later convenience */
  42. unsigned   dos_sp;               /* storage for DOS stack pointer */
  43. unsigned   c_sseg;               /* save our stack segment here */
  44. unsigned   c_sp;               /* and our stack pointer here */
  45. unsigned   stacksize;               /* size of DOS/local stack */
  46. int       _rax, _rbx, _rcx, _rdx,     /* variables hold register values */
  47.        _rsi, _rdi, _rds, _res;
  48.  
  49. /*--------------------------------------------------------------------------*/
  50. /* note: storage to save the DOS/C data segments must be allocated in the   */
  51. /*     code segment, so variable must be defined inside "#asm - #" area.  */
  52. /*--------------------------------------------------------------------------*/
  53.  
  54. /*----- Constants ----------------------------------------------------------*/
  55.  
  56. #define    KB_INT      0x16    /* BIOS software int keyboard service routine */
  57. #define    DOS_INT     0x21    /* DOS kitchen sink interrupt */
  58.  
  59. #define    PARAGRAPHS  0x1000  /* # of paragraphs of memory to keep */
  60.  
  61. /*----- Externals ----------------------------------------------------------*/
  62.  
  63. extern unsigned _PCB;    /* DeSmet C stores original sp value at _PCB + 2. */
  64. extern void    fdir ();          /* user's program module entry point */
  65. void   back_door();    /* declare this function so we can take its address */
  66.  
  67. /*--------------------------------------------------------------------------*/
  68. /*     Initial program startup code                        */
  69. /*--------------------------------------------------------------------------*/
  70.  
  71. void    stayres ()
  72. {
  73.  
  74. /* Save the C data segment and reserve storage in code segment for later use */
  75.  
  76. #asm
  77.  
  78. wakeup       equ       071H            ;This defines the character used to
  79.                        ;start up the resident code (Alt F-10)
  80.                        ;It can be set at any keypress desired
  81.  
  82.        jmp       get_ds
  83.  
  84. active_:   db       0
  85. c_ds_:       dw       0
  86. dos_ds_:   dw       0
  87. int_16_off_:
  88.        dw       0
  89. int_16_seg_:
  90.        dw       0
  91.  
  92. get_ds:    push    ax
  93.        mov       ax,ds
  94.        mov       cs:c_ds_,ax
  95.        pop       ax
  96.  
  97. #
  98.    /* initialize program stack segment variable */
  99.  
  100.    c_sseg = _showds();
  101.  
  102.  
  103. /*--------------------------------------------------------------------------*/
  104. /*     Do your program initialization here because you won't get hold of    */
  105. /*     it again until it is entered with the wakeup key sequence.        */
  106. /*--------------------------------------------------------------------------*/
  107.  
  108.    /* get address of current INT 16H routine */
  109.  
  110.    _rax = 0x3500 + KB_INT;
  111.    _doint (DOS_INT);
  112.    old_kbint_off = _rbx;
  113.    old_kbint_seg = _res;
  114.  
  115.    /* The segment and offset of the routine are returned in _res and */
  116.    /* _rbx respectively, so copy those values into storage in the code */
  117.    /* segment for future use by the resident code. */
  118.  
  119. #asm
  120.  
  121.        mov       ax,word _rbx_
  122.        mov       int_16_off_ ,ax
  123.        mov       ax,word _res_
  124.        mov       int_16_seg_,ax
  125.  
  126. #
  127.  
  128.    /*  now set the INT 16H vector to point to our new service routine */
  129.  
  130.    _rax = 0x2500 + KB_INT;
  131.    _rds = _showcs();
  132.    _rdx = back_door;
  133.    _doint (DOS_INT);
  134.  
  135.  
  136. /*----- Now terminate while staying resident -------------------------------*/
  137.  
  138.  
  139. /****************************************************************************/
  140. /*                                        */
  141. /*     note:                                    */
  142. /*     DeSmet C lays claim to the entire 64K of the data segment by using   */
  143. /*     it for the stack segment as well.  The data resides at the bottom    */
  144. /*     of the segment while the stack starts at the top and works down.     */
  145. /*     Because of this arrangement, we must reserve at least 64k of space   */
  146. /*     in addition to the amount of memory occupied by code.  Cware to the  */
  147. /*     rescue, however.  The DeSmet bind program allows you to specify a    */
  148. /*     maximum stack size when the modules are combined at link time. By    */
  149. /*     using the "-shhhh" option, you can specify a stack of 4K, which      */
  150. /*     should be more than adequate for most applications.  The object        */
  151. /*     module of this program must be combined with the "fdires.o" module   */
  152. /*     and the "nodupe.o" module with a bind command like:                  */
  153. /*                                        */
  154. /*               bind fdires fdirtsr nodupe -s1000            */
  155. /*                                        */
  156. /*     The stack size is specified in hex.                    */
  157. /*                                        */
  158. /****************************************************************************/
  159.  
  160.  
  161.        /* calculate how much memory we need to reserve (in paragraphs)      */
  162.        /*                                  */
  163.        /*      size = ds - cs + (sp/16) + 0x10 + 0x10 + 1          */
  164.        /*                                  */
  165.        /* ds - cs gives the number of paragraphs of code in our program.  */
  166.        /* sp/16 gives us a fair idea of where the end of the data segment */
  167.        /* is located, but by the time we get to here, the stack pointer   */
  168.        /* will have been moved around so we need to fudge a little.      */
  169.        /* The first "+ 0x10" is to allow for the program segment prefix,  */
  170.        /* the second "+ 0x10" is a fudge factor, so is the "+ 1" because  */
  171.        /*      (1) _showsp () doesn't show the top of the stack           */
  172.        /*      (2) we must always round up with integer division      */
  173.        /* If you run the program and it seems to operate fine, but      */
  174.        /* when you try to run another program above it you get          */
  175.        /* a DOS message "Memory allocation error can't load COMMAND.COM"  */
  176.        /* it means you haven't allocated enough memory for your program   */
  177.        /* and your stack has destroyed the memory allocation linked list. */
  178.        /* Try recompiling with more memory reserved.              */
  179.  
  180.    _rax = 0x3100;
  181.    _rdx = _showds() - _showcs() + ( _showsp() / 16 ) + 0x20 + 1;
  182.    printf ( "\nFDIRES now installed.\nPress Alt-F10 to activate.\n", _rdx );
  183.    _doint (DOS_INT);
  184.  
  185.  
  186. }
  187.  
  188.  
  189. /*----- Interrupt 16H service routine --------------------------------------*/
  190. /*                                        */
  191. /*     This function replaces the standard BIOS INT 16H service routine.    */
  192. /*     It is called by DOS and our application program to get the next        */
  193. /*     character from the queue, check the shift status, or just see if     */
  194. /*     there is a character in the queue.  All type 1 and 2 requests are    */
  195. /*     passed through to the original service routine, through a long        */
  196. /*     jump to that address.  Type 0 requests (get the next character)        */
  197. /*     are handled through an INT 67H call to the original service rou-     */
  198. /*     tine.  The character obtained is checked to see if it is the        */
  199. /*     designated wakeup key.  If not, it is passed on to DOS.    If it is,   */
  200. /*     then part of the DOS stack is saved on the local stack, all the        */
  201. /*     processor registers are saved in memory and the program is        */
  202. /*     started up.  The C program environment and registers are estab-        */
  203. /*     lished and the DOS environment is saved for later restoration.        */
  204. /*     Upon return from the user's program, the DOS stack is restored       */
  205. /*     from the data saved on the local stack, bringing DOS back to        */
  206. /*     where it was before the user program was turned on.            */
  207. /*     These operations allow programs to be written in C without having    */
  208. /*     to worry about the resident routines destroying the DOS registers    */
  209. /*     when file I/O is used.  It also allows the use of printf().        */
  210. /*                                        */
  211. /*--------------------------------------------------------------------------*/
  212.  
  213.  
  214.  
  215. void   back_door()
  216. {
  217. /*
  218.    Current stack contents:
  219.            sp -> DOS ip
  220.        sp + 2 -> DOS cs
  221.        sp + 4 -> DOS flags
  222.  
  223.    C function prologue:
  224.        push    bp
  225.        mov       bp,sp
  226. */
  227.  
  228. #asm
  229.        pop       bp               ;toss out the bp from the prologue
  230.        cmp       cs:active_,1        ;if the program is not currently active,
  231.        jne       not_on           ;go service the request
  232. ;
  233. ; long jump to original INT 16H handler
  234. ;
  235.        ljmp    word cs:int_16_off_ ;else don't go any further
  236. ;
  237. not_on:    cmp       ah,0            ;if this is a character request
  238.        jz       chr_rqst           ;check a little further
  239. ;
  240. ; long jump to original INT 16H handler
  241. ;
  242.        ljmp    word cs:int_16_off_ ;if not, exit immediately
  243. ;
  244. chr_rqst:
  245.        pushf               ;simulate a software interrupt to
  246.        lcall   word cs:int_16_off_ ;get the next character from the queue
  247.        cmp       ah,wakeup           ;if it's not the wakeup character,
  248.        jne       skipall           ;then take it back to the caller
  249.        mov       cs:active_,1        ;else set the 'program active' flag
  250. ;
  251. ;  now we enter the program
  252. ;
  253.        cli                   ;no interrupts for now
  254.        mov       cs:dos_ds_,ds       ;save the DOS data segment
  255.        mov       ds,cs:c_ds_           ;establish our data segment
  256.        push    bp               ;save bp
  257.        mov       bp,offset dos_regs_ ;point bp to dos_regs array
  258.        mov       ds:[bp+0], ax       ;save the DOS machine register status
  259.        mov       ds:[bp+2], bx       ;in an array in the local data segment
  260.        mov       ds:[bp+4], cx
  261.        mov       ds:[bp+6], dx
  262.        pop       ds:[bp+8]           ;get the bp and save it
  263.        mov       ds:[bp+10], si
  264.        mov       ds:[bp+12], di
  265.        push    ax
  266.        mov       ax, cs:dos_ds_      ;save the DOS ds reg
  267.        mov       ds:[bp+14], ax
  268.        pop       ax
  269.        mov       ds:[bp+16], es      ;save es
  270.        pushf               ;save the flags too
  271.        pop       ds:[bp+18]
  272. ;
  273. ;  Now we want to point es:si to the DOS stack and set ss:sp to start a local
  274. ;  stack at the original position the C stack was in when the resident program
  275. ;  was initialized.
  276. ;
  277.        mov       word dos_sseg_,ss   ;save DOS stack segment
  278.        mov       si,ss           ;set es to point to the the DOS stack
  279.        mov       es,si           ;for the move later on
  280.        mov       ss,word c_sseg_     ;set ss to C stack segment
  281.        mov       si,ss:_PCB_ + 2     ;get original sp value for C stack
  282.        mov       di,sp           ;save the current sp value
  283.        xchg    sp,si           ;and set up new stack pointer
  284.        mov       si,di           ;point si to top of DOS stack
  285.        push    [bp+0]           ;save ax
  286.        push    [bp+2]           ;save bx
  287.        push    [bp+4]           ;save cx
  288.        push    [bp+6]           ;save dx
  289.                        ;don't save bp
  290.        push    [bp+10]           ;save si
  291.        push    [bp+12]           ;save di
  292.        push    [bp+14]           ;save ds
  293.        push    [bp+16]           ;save es
  294.                        ;don't save flags
  295.        sub       cx,cx           ;now save 64 words or less from the
  296.        sub       cx,si           ;DOS stack onto the current stack
  297.        shr       cx,1            ;If the stack size is less than 64
  298.        cmp       cx,64           ;save 'stacksize' words, else save 64
  299.        jle       under64
  300.        mov       cx,64
  301. under64:   mov       word stacksize_,cx
  302. restack:   push    es:[si]
  303.        inc       si
  304.        inc       si
  305.        loop    restack
  306.        push    si               ;save the count of bytes pushed on stack
  307.        mov       word c_sp_,sp       ;save current stack pointer
  308.        sti
  309.        push    bp               ;do the C function prologue here
  310.        mov       bp,sp
  311.  
  312. #
  313.  
  314. /*----- Call your program from here ----------------------------------------*/
  315.  
  316.        fdir ();
  317.  
  318. /*----- Restore everything -------------------------------------------------*/
  319.  
  320. #asm
  321. restore:
  322.        pop       bp
  323.        cli                   ;no interrupts
  324.        mov       sp,word c_sp_       ;get the stack pointer back
  325.        pop       si               ;get pointer to top of words to be moved
  326.        mov       cx,word stacksize_  ;get count of bytes to move
  327.        mov       es,word dos_sseg_   ;point es to caller's stack
  328. unstack:   dec       si               ;back up one word
  329.        dec       si
  330.        pop       es:[si]           ;restore the caller's stack from
  331.        loop    unstack           ;the local stack
  332.        mov       bp,si           ;save pointer to top of caller's stack
  333.        pop       es               ;pop registers off the stack
  334.        pop       di               ;don't restore ds just yet
  335.        pop       di
  336.        pop       si
  337.        pop       dx
  338.        pop       cx
  339.        pop       bx
  340.        pop       ax
  341.        mov       sp,bp           ;restore the caller's stack pointer
  342.        mov       ss,word dos_sseg_   ;switch back to the caller's stack
  343.        mov       bp,offset dos_regs_
  344.        push    ds:[bp+18]           ;restore the flags from memory
  345.        popf
  346.        mov       bp,ds:[bp+8]        ;restore the caller's bp reg
  347.        mov       cs:active_,0        ;reset the 'program active' flag
  348.        mov       ds,cs:dos_ds_       ;finally restore caller's data segment
  349. ;
  350. ; return via a long jump to F000:E82E
  351. ; to get the next keypress
  352. ;
  353.        mov       ax,0
  354.        db       0EAH
  355.        dw       0E82EH
  356.        dw       0F000H
  357. ;
  358. skipall:   iret                ;head on back
  359.  
  360. #
  361.  
  362. }
  363.  
  364.  
  365.