home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 December / simtel1292_SIMTEL_1292_Walnut_Creek.iso / msdos / turbo_c / tcc_misc.arc / RDIR.C < prev    next >
C/C++ Source or Header  |  1987-06-14  |  24KB  |  776 lines

  1. /*
  2.  * RDIR.C : A resident directory lister.  This shows how to access DOS from
  3.  *          within a Turbo C resident program.  To completely recompile this
  4.  *          code you must have MASM or a compatible compiler.  Unfortunately
  5.  *          everything but trapping the BIOS Disk service software
  6.  *          interrrupt could be done in Turbo C.  See code commentary.
  7.  *
  8.  *  To activate the program once loaded, press Ctrl and Alt together.
  9.  *  I know this combination gets in the way of SideKick, but I don't use
  10.  *  sidekick <grin> and this is meant as a tutorial anyway.  If you would
  11.  *  like to change this combination see Hot_Combo in the source code.
  12.  *  Next, enter the pattern for the directory search followed either by
  13.  *  Enter or Ctrl-Enter.  Enter will not include directories.  Ctrl-Enter
  14.  *  will include directories.  Directories are displayed in white where
  15.  *  other files are displayed in cyan.  Hidden and system files are included.
  16.  *  The page up and page down keys may be used to see other parts of large
  17.  *  directory listings.
  18.  *
  19.  *  Written by Dean D. McCrory
  20.  *  For Turbo C 1.00
  21.  *  May 14, 1987
  22.  *
  23.  *  Compile with:
  24.  *    masm -mx bioshand.asm;
  25.  *    masm -mx intvid.asm;
  26.  *    tcc -N- rdir.c bioshand.obj intvid.obj
  27.  *
  28.  *  The -N- switch turns stack checking off which is a definite requirement
  29.  *  for writing ISRs.  Have fun <grin>.
  30.  */
  31.  
  32. #include <dos.h>
  33. #include <dir.h>
  34.  
  35. #include "intvid.h"
  36.  
  37. /* function prototypes */
  38. int main (void);
  39. unsigned prgsize (void);
  40. void exit (int);
  41. char far * getdosbusy (void);
  42. void interrupt timer_handler (void);
  43. void do_click (void);
  44. void interrupt special_handler (void);
  45. void interrupt keyboard_handler (void);
  46. extern void interrupt biosdisk_handler (void);
  47. void interrupt break_handler (void);
  48. void list_directory (char);
  49. void set_screen (void);
  50. void get_pattern (void);
  51. void display_entries (void);
  52. void find_entry (void);
  53. void sc_putsa (int, int, char *, int);
  54. void sc_putca (int, int, char, int);
  55. void sc_repvca (int, int, char, int, int);
  56. void sc_rephca (int, int, char, int, int);
  57. void sc_savbox (int, int, int, int, char *);
  58. void sc_resbox (int, int, int, int, char *);
  59. void sc_rptpos (int *, int *);
  60. void sc_setpos (int, int);
  61. char far * sc_cca (int, int);
  62. int getkey ();
  63.  
  64. /* Define the structure which will be attached to interrupt d0 so we can
  65.    determine if rdir is already loaded */
  66. typedef struct s_rdir_cfg
  67.    {
  68.    char iret;        /* iret first, just in case */
  69.    long * signature; /* signature string */
  70.    } t_rdir_cfg;
  71.  
  72. /* Suppress some library functions to conserve space */
  73. _setargv () {}
  74. _setenvp () {}
  75.  
  76. /* defines for various things, in alot of these things we are looking right
  77.    into the BIOS data area. See Peter Norton's Guide to the IBM-PC or
  78.    your Tech. Ref. for an expanation of these data items. */
  79. #define Screen_Ram   ((char far *) 0xb8000000L) + (*(int far *) 0x0000044eL)
  80. #define Display_Page (*(char far *) 0x00000462L)
  81. #define Iret         0xcf     /* for iret in int d1 just in case */
  82. #define Intr         0xd1     /* interrupt for install checking */
  83. #define Timer        0x1c     /* timer interrupt number */
  84. #define Keyboard     0x09     /* keyboard hardware interrupt */
  85. #define Special      0x28     /* special interrupt 28h */
  86. #define Critical     0x24     /* hardware critical interrupt 24h */
  87. #define Break        0x1b     /* bios ctrl-break interrupt */
  88. #define BiosDisk     0x13     /* bios diskette services */
  89. #define NotOk        1        /* already installed return code */
  90. #define Ok           0        /* success return code */
  91. #define Shift_Bits   ((char far *) 0x00000417L)
  92. #define Hot_Combo    ((*Shift_Bits & 12) == 12) /* Our hot key combo */
  93.  
  94. #define  Box_Row     4     /* row of box */
  95. #define  Box_Col     4     /* column of box */
  96. #define  Box_Hgt     17    /* height of box */
  97. #define  Box_Wdth    70    /* widht of box */
  98. #define  Box_Attr    0x4f  /* attribute of box border */
  99. #define  Box_Incr    14    /* number of columns between start of filenames */
  100. #define  Pat_Size    64    /* size of a pattern string */
  101. #define  Norm_Attr   3     /* attribute of normal files */
  102. #define  Dir_Attr    15    /* attribute of directories */
  103. #define  Name_Size   12    /* size of each file name when displayed */
  104. #define  Per_Screen  5 * (Box_Hgt - 2) /* number of dir entries in box */
  105.  
  106. /* Defines for key values as returned by getkey () */
  107. #define  Page_Up_Key    329
  108. #define  Page_Dn_Key    337
  109. #define  Escape_Key     27
  110. #define  BackSpace_Key  8
  111. #define  Enter_Key      13
  112. #define  Ctrl_Enter_Key 10
  113. #define  Break_Key      256
  114.  
  115. char  box_buf[Box_Hgt * Box_Wdth * 2]; /* buffer for saving screen */
  116. int   old_row, old_col;                /* row and col of cursor */
  117. int   current_entry;                   /* last dir entry read */
  118. int   entries_to_skip;                 /* first entry on display */
  119. char  pattern[Pat_Size];               /* pattern for dir searches */
  120. int   attrib;                          /* attrib for dir searches */
  121. struct ffblk ffblk;                    /* file-find block */
  122. int   key, status;                     /* last key, and last ff status */
  123. t_vidreg regs;                         /* global regs structure */
  124. char far * ptr;                        /* general purpose far pointer */
  125. char far * buf_pos;                    /* another of the same */
  126. int   next_line;                       /* used by screen stuff */
  127. int   need_key;                        /* 1 == need to get key in get_pat */
  128.  
  129. /* Our configuration structure... nothing in it but the signature */
  130. t_rdir_cfg rdir_cfg =
  131.    {
  132.    Iret, (long *) "rdir"
  133.    };
  134.  
  135. void interrupt (* old_timer) ();    /* previous timer interrupt vector */
  136. void interrupt (* old_keyboard) (); /* previous keyboard int vector */
  137. void interrupt (* old_special) ();  /* previous int 28h vector */
  138. void interrupt (* old_biosdisk) (); /* previous bios disk svc vector */
  139. void interrupt (* old_critical) (); /* previous int 24h vector */
  140. void interrupt (* old_break) ();    /* pervious int 1bh vector */
  141. char far * old_dta;                 /* disk transfer address save area */
  142.  
  143. static char far * dosbusy_fl;    /* dos maintains this */
  144. char biosbusy_fl = 0;            /* I maintain this */
  145. static int request_fl = 0;       /* 0 - no request
  146.                                     1 - request made
  147.                                     2 - request being serviced
  148.                                   */
  149. int main ()
  150. {
  151.    t_rdir_cfg far * cfg_ptr;
  152.  
  153.    /* get old configuration information (mayebe) */
  154.    cfg_ptr = (t_rdir_cfg far *) getvect (Intr);
  155.  
  156.    /* check to see if we are already installed */
  157.    if (*cfg_ptr->signature != *rdir_cfg.signature)
  158.       {
  159.       /* we were not installed so install ourselves */
  160.       old_timer = getvect (Timer);
  161.       old_keyboard = getvect (Keyboard);
  162.       old_special = getvect (Special);
  163.       old_biosdisk = getvect (BiosDisk);
  164.       setvect (Timer, timer_handler);
  165.       setvect (Special, special_handler);
  166.       setvect (BiosDisk, biosdisk_handler);
  167.       setvect (Keyboard, keyboard_handler);
  168.       setvect (Intr, (void interrupt (*) ()) &rdir_cfg);
  169.       dosbusy_fl = getdosbusy ();
  170.       keep (Ok, prgsize ());
  171.       }
  172.  
  173.    return (NotOk);
  174. }
  175.  
  176. /* prgsize ()
  177.  *
  178.  * Calculates the program size by looking at __brklvl which is set to
  179.  * the end of initialized and uninitialized data whithin the data segment
  180.  * at program startup.  __brklvl is then changed as memory space is
  181.  * malloc'd.  __brklvl is decremented as malloc'd areas are free'd.
  182.  *
  183.  *   ** This function should work in Tiny, Small, and Meduim models **
  184.  */
  185.  
  186. unsigned prgsize ()
  187. {
  188.    extern unsigned __brklvl;     /* current top of heap == sbrk (0) */
  189.    extern unsigned _psp;         /* lowest segment address occupied */
  190.  
  191.    return (_DS + (__brklvl + 15) / 16 - _psp);
  192. }
  193.  
  194. /* exit ()
  195.  *
  196.  * Rewrite exit for memory conservation.  This exit () does not close files
  197.  * or flush buffers, which is fine in this case because we have no open
  198.  * files or buffers which need to be flushed.
  199.  *
  200.  */
  201. void exit (status)
  202.    int status;
  203. {
  204.    _exit (status);
  205. }
  206.  
  207. /* getdosbusy ()
  208.  *
  209.  * Gets the Dos busy flag through interrupt 34h.  This Dos function returnes
  210.  * the busy flag address in es:bx.  This is an UNDOCUMENTED feature of Dos,
  211.  * however it has worked in Dos versions 2.11 - 3.30 for me.
  212.  */
  213. char far * getdosbusy ()
  214. {
  215.    struct SREGS sregs;        /* segment registers */
  216.    union REGS regs;           /* normal registers */
  217.  
  218.    regs.h.ah = 0x34;          /* get dos busy flag address (UNDOCUMENTED) */
  219.    intdosx (®s, ®s, &sregs);
  220.    return (MK_FP (sregs.es, regs.x.bx));
  221. }
  222.  
  223. /* timer_handler ()
  224.  *
  225.  * This function intercepts the hardware timer interrupt.  It checks the
  226.  * request flag set by the keyboard handler and if set pops the directory
  227.  * function up only if it is currently "safe" to do so.
  228.  */
  229. void interrupt timer_handler ()
  230. {
  231.    static int in_fl = 0;
  232.  
  233.    /* if the following statement is NOT coded, the 8259 blocks all hardware
  234.       interrupts including the keyboard interrupt.  Since we wait for a key
  235.       in list_directory (), this causes the PC to lock up.  This one took
  236.       a while to figure out */
  237.    outportb (0x20, 0x20);        /* send eoi to 8259 */
  238.  
  239.    if (! in_fl)
  240.       {
  241.       in_fl = 1;                 /* we are in our ISR */
  242.       if (request_fl == 1)       /* has there been a request for popup? */
  243.          if (! *dosbusy_fl && ! biosbusy_fl)
  244.             list_directory ('T');/* call the directory lister */
  245.          else
  246.             do_click ();         /* click to let user know we are trying */
  247.       in_fl = 0;
  248.       }
  249.  
  250.    (*old_timer) ();           /* chain to previous timer handler */
  251.    return;                    /* return from ISR */
  252. }
  253.  
  254. /* do_click ()
  255.  *
  256.  * I guess I got lazy here.  I wanted to just output a short click like
  257.  * sidekick does when it can't pop up because Dos is busy.  At any rate,
  258.  * the function is here if I want to implement that type of thing.
  259.  */
  260. void do_click ()
  261. {
  262. }
  263.  
  264. /* special_handler ()
  265.  *
  266.  * This interrupt is called from Dos at times when it is "safe" to use Dos
  267.  * functions.  It seems to be called constantly when waiting for keystrokes
  268.  * at the Dos prompt.  Here, we don't have to check the Dos busy flag
  269.  * because it is ALWAYS ok to call Dos from this point.
  270.  */
  271. void interrupt special_handler ()
  272. {
  273.    static int in_fl = 0;
  274.  
  275.    (*old_special) ();         /* chain to previous int 28 handler */
  276.  
  277.    if (! in_fl)
  278.       {
  279.       in_fl = 1;                 /* we are in our ISR */
  280.       if (request_fl == 1)       /* see if rdir has been requested */
  281.          list_directory ('S');   /* ok, list the directory */
  282.       in_fl = 0;
  283.       }
  284.    return;
  285. }
  286.  
  287. /* keyboard_handler ()
  288.  *
  289.  * This is what starts the whole ball rolling!  First we call the old
  290.  * keyboard handler, then check for our hot key.  If our hot key has been
  291.  * pressed, we toggle our internal request flag.  Next, if the request
  292.  * flag is set and it is safe to enter dos, we call the directory lister.
  293.  * If Dos is busy, this request must be handled by either the timer interrupt
  294.  * or the special int 28h interrupt
  295.  */
  296. void interrupt keyboard_handler ()
  297. {
  298.    static int in_fl = 0;
  299.  
  300.    (*old_keyboard) ();
  301.  
  302.    if (! in_fl)
  303.       {
  304.       in_fl = 1;
  305.       if (Hot_Combo && request_fl != 2)
  306.          request_fl = ! request_fl;
  307.  
  308.       if (request_fl == 1 && ! *dosbusy_fl && ! biosbusy_fl) 
  309.          list_directory ('K');
  310.       in_fl = 0;
  311.       }
  312.  
  313.    return;
  314. }
  315.  
  316. /* critical_handler ()
  317.  *
  318.  * This is only active while in list_directory ().  Its purpose is to avoid
  319.  * the possibility of the user entering a pattern on a floppy drive with
  320.  * no floppy in the drive.  If we did not trap this interrupt, Dos could
  321.  * terminate our TSR which would probably crash the system.
  322.  */
  323. int critical_handler ()
  324. {
  325.    return (0);                /* ignore the error */
  326. }
  327.  
  328. /* break_handler ()
  329.  *
  330.  * Again, this is only active when in list_directory ().  The purpose is
  331.  * the same as critical_handler () except we are trapping the BIOS Ctrl_Brk
  332.  * key.
  333.  */
  334. void interrupt break_handler ()
  335. {
  336.    return;                    /* ignore the break */
  337. }
  338.  
  339. /* list_directory ()
  340.  *
  341.  * This is the actual routine which lists the directory.  It can be called
  342.  * by either the keyboard_handler (), timer_handler (), or the
  343.  * special_handler ().  It does not care which.  While looking at this code
  344.  * you may notice the heavy use of global variables instead of autos; this
  345.  * is becuase we my be popped up from somewhere in DOS... we are using the
  346.  * default (current) stack and we don't know how much there is, therefore
  347.  * we should use as little as possible!
  348.  */
  349. void list_directory (type)
  350.    char type;
  351. {
  352.    request_fl = 2;            /* currently servicing the request */
  353.    need_key = 1;
  354.  
  355.    /* save and set critical error handler */
  356.    old_critical = getvect (Critical);
  357.    harderr (critical_handler);
  358.  
  359.    /* save and set ctrl-break handler */
  360.    old_break = getvect (Break);
  361.    setvect (Break, break_handler);
  362.  
  363.    /* save and set the disk transfer address */
  364.    old_dta = getdta ();
  365.    setdta ((char far *) &ffblk);
  366.  
  367.    /* eat any type-ahead keys */
  368.    while (bioskey (1))
  369.       bioskey (0);
  370.  
  371.    set_screen ();
  372.  
  373.    /* FOR DEBUGGING ONLY */
  374.    sc_putca (Box_Row + 1, Box_Col + Box_Wdth - 1, type, Box_Attr);
  375.  
  376.    while (get_pattern (), key != Escape_Key && key != Break_Key)
  377.       {
  378.       attrib = (key == Enter_Key ? FA_HIDDEN | FA_SYSTEM : FA_HIDDEN |
  379.          FA_SYSTEM | FA_DIREC);
  380.       entries_to_skip = 0;    /* start at beginning of dir list */
  381.       current_entry = -1;     /* have to do a find first */
  382.       display_entries ();
  383.       need_key = 0;           /* for next time into get_pattern */
  384.  
  385.       do
  386.          {
  387.          key = getkey ();
  388.          switch (key)
  389.             {
  390.             case Page_Up_Key:
  391.                if (entries_to_skip - Per_Screen >= 0)
  392.                   {
  393.                   current_entry = -1;
  394.                   entries_to_skip -= Per_Screen;
  395.                   display_entries ();
  396.                   }
  397.                break;
  398.  
  399.             case Page_Dn_Key:
  400.                if (status == 0)
  401.                   {
  402.                   entries_to_skip += Per_Screen;
  403.                   display_entries ();
  404.                   }
  405.                break;
  406.  
  407.             case Escape_Key:
  408.                need_key = 1;
  409.                break;
  410.  
  411.             case Break_Key:
  412.                need_key = 1;
  413.                break;
  414.             }
  415.          }
  416.       while (key == Page_Dn_Key || key == Page_Up_Key);
  417.       }
  418.  
  419.    /* restore critical error handler, break handler and dta address */
  420.    setvect (Critical, old_critical);
  421.    setvect (Break, old_break);
  422.    setdta (old_dta);
  423.  
  424.    /* restore the screen, and cursor position */
  425.    sc_resbox (Box_Row, Box_Col, Box_Hgt, Box_Wdth, box_buf);
  426.    sc_setpos (old_row, old_col);
  427.  
  428.    /* request has been filled, and now no request is active */
  429.    request_fl = 0;            /* no request made */
  430. }
  431.  
  432. /* set_screen ()
  433.  *
  434.  * This function sets up the initial screen for list_directory ()
  435.  */
  436. void set_screen ()
  437. {
  438.    /* save area on screen */
  439.    sc_savbox (Box_Row, Box_Col, Box_Hgt, Box_Wdth, box_buf);
  440.    sc_rptpos (&old_row, &old_col);
  441.  
  442.    /* draw our box */
  443.    sc_repvca (Box_Row + 1, Box_Col, '\xb3', Box_Hgt - 2,  Box_Attr);
  444.    sc_repvca (Box_Row + 1, Box_Col + Box_Wdth - 1, '\xb3', Box_Hgt - 2,
  445.       Box_Attr);
  446.    sc_rephca (Box_Row + Box_Hgt - 1, Box_Col + 1, '\xc4', Box_Wdth - 2,
  447.       Box_Attr);
  448.    sc_putca (Box_Row, Box_Col, '\xda', Box_Attr);
  449.    sc_putca (Box_Row, Box_Col + Box_Wdth - 1, '\xbf', Box_Attr);
  450.    sc_putca (Box_Row + Box_Hgt - 1, Box_Col, '\xc0', Box_Attr);
  451.    sc_putca (Box_Row + Box_Hgt - 1, Box_Col + Box_Wdth - 1, '\xd9', Box_Attr);
  452. }
  453.  
  454. /* get_pattern ()
  455.  *
  456.  * This gets the pattern from the user.  The result is put in the global
  457.  * variable pattern.  The only editing key is backspace.
  458.  */
  459. void get_pattern ()
  460. {
  461.    static int pos;
  462.    static char * ptr;
  463.  
  464.    for (pos = Box_Row + 1; pos < Box_Row + Box_Hgt - 1; pos++)
  465.       sc_rephca (pos, Box_Col + 1, ' ', Box_Wdth - 2, Norm_Attr);
  466.    sc_rephca (Box_Row, Box_Col + 1, ' ', Box_Wdth - 2, Box_Attr);
  467.    pos = Box_Col + 2;
  468.    ptr = pattern;
  469.    *ptr = '\0';
  470.    do
  471.       {
  472.       sc_setpos (Box_Row, pos);
  473.       if (need_key)
  474.          key = getkey ();
  475.       need_key = 1;
  476.       switch (key)
  477.          {
  478.          case BackSpace_Key:
  479.             if (pos > Box_Col + 2)
  480.                {
  481.                pos--;
  482.                sc_putca (Box_Row, pos, ' ', Box_Attr);
  483.                *--ptr = '\0';
  484.                }
  485.             break;
  486.  
  487.          case Escape_Key:
  488.          case Enter_Key:
  489.          case Ctrl_Enter_Key:
  490.             break;
  491.  
  492.          default:
  493.             if (key >= 32 && key <= 127 && pos < Pat_Size + Box_Col + 2)
  494.                {
  495.                sc_putca (Box_Row, pos, key, Box_Attr);
  496.                pos++;
  497.                *ptr++ = key;
  498.                *ptr = '\0';
  499.                }
  500.             break;
  501.          }
  502.       }
  503.    while (key != Escape_Key && key != Enter_Key && key != Ctrl_Enter_Key &&
  504.       key != Break_Key);
  505.  
  506.    /* if they didn't type anything, or the last character was a directory
  507.       separator, or a drive specifier, make the pattern *.* */
  508.    if (pattern[0] == '\0' || *(--ptr) == '\\' || *ptr == '/' || *ptr == ':')
  509.       strcat (pattern, "*.*");
  510. }
  511.  
  512. /* display_entries ()
  513.  *
  514.  * Displays or redisplays the directory entries.
  515.  */
  516. void display_entries ()
  517. {
  518.    static int entries_to_display;
  519.    static int row;
  520.    static int col;
  521.    static int attr;
  522.    static int len; 
  523.  
  524.    entries_to_display = Per_Screen;
  525.    row = Box_Row + 1;
  526.    col = Box_Col + 1;
  527.  
  528.    /* First skip to the start of the current page */
  529.    while (current_entry < entries_to_skip)
  530.       find_entry ();
  531.  
  532.    /* For every possible entry on the screen: either display the filename
  533.       or display blanks */
  534.    while (entries_to_display--)
  535.       {
  536.       if (! status)
  537.          {
  538.          attr = Norm_Attr;
  539.          if (ffblk.ff_attrib & FA_DIREC)
  540.             attr = Dir_Attr;
  541.          sc_putsa (row, col, ffblk.ff_name, attr);
  542.          len = strlen (ffblk.ff_name);
  543.          sc_rephca (row, col + len, ' ', Name_Size - len, attr);
  544.          find_entry ();
  545.          }
  546.       else
  547.          sc_rephca (row, col, ' ', Name_Size, Norm_Attr);
  548.       if (++row > Box_Row + Box_Hgt - 2)
  549.          col += Box_Incr, row = Box_Row + 1;
  550.       }
  551. }
  552.  
  553. /* find_entry ()
  554.  *
  555.  * Finds the next entry based on the current entry.  If current entry is
  556.  * -1 then a findfirst () is assumed to be needed, else a findnext () is
  557.  * executed.
  558.  */
  559. void find_entry ()
  560. {
  561.    if (current_entry == -1)
  562.       status = findfirst (pattern, &ffblk, attrib);
  563.    else
  564.       status = findnext (&ffblk);
  565.    current_entry++;
  566. }
  567.  
  568. /* The following functions are used to write to the screen.  They do write
  569.    directly to screen RAM so they will cause "snow" on some CGA systems */
  570.  
  571. /* sc_putsa ()
  572.  *
  573.  * Write a string with the given attribute at row, col
  574.  */
  575. void sc_putsa (row, col, string, attr)
  576.    int row;
  577.    int col;
  578.    register char * string;
  579.    int attr;
  580. {
  581.    /* calculate pointer to screen RAM */
  582.    ptr = sc_cca (row, col);
  583.  
  584.    /* write each of the characters in string to the screen RAM */
  585.    while (*string)
  586.       {
  587.       *ptr++ = *string++;           /* write the character */
  588.       *ptr++ = attr;                /* write the attribute */
  589.       }
  590. }
  591.  
  592. /* sc_putca ()
  593.  *
  594.  * Write a single character with the specified attribute, at row, col
  595.  */
  596. void sc_putca (row, col, ch, attr)
  597.    int row;
  598.    int col;
  599.    char ch;
  600.    int attr;
  601. {
  602.    /* calculate pointer to screen RAM */
  603.    ptr = sc_cca (row, col);
  604.  
  605.    *ptr++ = ch;
  606.    *ptr = attr;
  607. }
  608.  
  609. /* sc_repvca ()
  610.  *
  611.  * Repeat the given character length times with the specified attribute
  612.  * starting from row, col in a vertical direction.
  613.  */
  614. void sc_repvca (row, col, ch, length, attr)
  615.    int row;
  616.    int col;
  617.    char ch;
  618.    int attr;
  619. {
  620.    /* calculate pointer to screen RAM */
  621.    ptr = sc_cca (row, col);
  622.    next_line = 80 * 2 - 2;
  623.  
  624.    while (length--)
  625.       {
  626.       *ptr++ = ch;            /* write the character */
  627.       *ptr++ = attr;          /* write the attribute */
  628.       ptr += next_line;       /* move to the next line */
  629.       }
  630. }
  631.  
  632. /* sc_rephca ()
  633.  *
  634.  * Repeat the given character length times with the specified attribute
  635.  * starting from row, col in a horizontal direction.
  636.  */
  637. void sc_rephca (row, col, ch, length, attr)
  638.    int row;
  639.    int col;
  640.    char ch;
  641.    int length;
  642.    int attr;
  643. {
  644.    /* calculate pointer to screen RAM */
  645.    ptr = sc_cca (row, col);
  646.  
  647.    while (length--)
  648.       {
  649.       *ptr++ = ch;        /* write the character */
  650.       *ptr++ = attr;      /* write the attribute */
  651.       }
  652. }
  653.  
  654. /* sc_savbox ()
  655.  *
  656.  * Saves the screen image into buf.  Row, col specify the upper left hand
  657.  * corner of the box, height and width specify the number of rows and
  658.  * columns to save.
  659.  */
  660. void sc_savbox (row, col, height, width, buf)
  661.    int row;
  662.    int col;
  663.    int height;
  664.    int width;
  665.    char * buf;
  666. {
  667.    /* calculate pointer to screen RAM */
  668.    ptr = sc_cca (row, col);
  669.  
  670.    buf_pos = (char far *) buf;
  671.    width *= 2;
  672.  
  673.    while (height--)          /* while we still have rows to do */
  674.       {
  675.       movedata (FP_SEG (ptr), FP_OFF (ptr), FP_SEG (buf_pos), FP_OFF
  676.          (buf_pos), width);
  677.       buf_pos += width;
  678.       ptr += 160;
  679.       }
  680. }
  681.  
  682. /* sc_resbox ()
  683.  *
  684.  * Performs the opposite function of sc_savbox ()
  685.  */
  686. void sc_resbox (row, col, height, width, buf)
  687.    int row;
  688.    int col;
  689.    int height;
  690.    int width;
  691.    char * buf;
  692. {
  693.    ptr = sc_cca (row, col);
  694.    width *= 2;
  695.    buf_pos = (char far *) buf;
  696.  
  697.    while (height--)
  698.       {
  699.       movedata (FP_SEG (buf_pos), FP_OFF (buf_pos), FP_SEG (ptr), FP_OFF
  700.          (ptr), width);
  701.       buf_pos += width;
  702.       ptr += 160;
  703.       }
  704. }
  705.  
  706. /* sc_rptpos ()
  707.  *
  708.  * Reports the current cursor row and column values.  These values are
  709.  * placed in the integers which are pointed at by row and col.   I had to
  710.  * use my own intvid () routine here instead of int86 because int86 reserves
  711.  * some space on the stack for temporary vars.  It also assumes that the
  712.  * current ss (stack segment) is the same as ds (data segment).  This is
  713.  * not true in this case, so they end up writing to random places in memory
  714.  * and usually crash the machine.  Be carefull with using library routines
  715.  * within an ISR.
  716.  */
  717. void sc_rptpos (row, col)
  718.    register int * row;
  719.    register int * col;
  720. {
  721.    regs.h.ah = 3;
  722.    regs.h.bh = Display_Page;
  723.    intvid (®s, 1); 
  724.    *row = regs.h.dh;
  725.    *col = regs.h.dl;
  726. }
  727.  
  728.  
  729. /* sc_setpos ()
  730.  *
  731.  * Sets the cursor positon at row, col.
  732.  */
  733. void sc_setpos (row, col)
  734.    register int row;
  735.    register int col;
  736. {
  737.    regs.h.ah = 2;
  738.    regs.h.dh = row;
  739.    regs.h.dl = col;
  740.    regs.h.bh = Display_Page;
  741.    intvid (®s, 0); 
  742. }
  743.  
  744.  
  745. /* sc_cca ()
  746.  *
  747.  * Calculates a far pointer into screen RAM based on row, col.
  748.  */
  749. char far * sc_cca (row, col)
  750.    int row;
  751.    int col;
  752. {
  753.    return (Screen_Ram + row * 160 + col * 2);
  754. }
  755.  
  756. /* getkey ()
  757.  *
  758.  * Gets a single key from the keyboard (waits for one if necessary).  If the
  759.  * key is a special key (low byte is 0) the returned value is 256 plus
  760.  * the scan code (high byte).  If it is normal key, just the ASCII code is
  761.  * returned.
  762.  */
  763. int getkey ()
  764. {
  765.    static unsigned key;
  766.  
  767.    key = bioskey (0);
  768.    if (! (key & 0xff))
  769.       key = (key >> 8) | 256;
  770.    else
  771.       key = key & 0xff;
  772.    
  773.    return (key);
  774. }
  775.  
  776.