home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Interactive Guide / c-cplusplus-interactive-guide.iso / c_ref / csource4 / 201_01 / stayres.c < prev    next >
Encoding:
C/C++ Source or Header  |  1979-12-31  |  12.9 KB  |  342 lines

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