home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / os2sdk / os2sdk10 / apps / life / life.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-08-11  |  37.4 KB  |  1,188 lines

  1. /***
  2.  *  Title:
  3.  *
  4.  *    LIFE - An OS/2 Game of Life
  5.  *
  6.  *
  7.  *  Author:
  8.  *
  9.  *    Brian J. Smith
  10.  *    (c) Microsoft Corporation
  11.  *    1987
  12.  *
  13.  *
  14.  *  Description:
  15.  *
  16.  *    This is a OS/2 implementation of the game of Life.  It is designed
  17.  *    to be bound to allow it to operate in both the protect mode of OS/2
  18.  *    and in earlier MS-DOS versions.  The program uses a mouse if one
  19.  *    is installed, but one is not needed.
  20.  *
  21.  *    Keyboard commands:   B(lank) - redraw screen
  22.  *                 G(o)    - step through generations until a key or
  23.  *                       button is hit
  24.  *                 H(alt)  - freeze at current generation
  25.  *                 Q(uit)  - exit program
  26.  *                 R(ead)  - read a game board from the disk
  27.  *                 S(step) - advance a single generation
  28.  *                 W(rite) - write the current board to disk
  29.  *                 D(own-speed) - slow down GOs
  30.  *                 U(p-speed) - speed up GOs
  31.  *
  32.  */
  33.  
  34.  
  35. #include <doscalls.h>            /* definitions for DOS calls and */
  36. #include <subcalls.h>            /*    associated data structures */
  37.  
  38. /* defines */
  39. #define SCR_WID 80            /* screen buffer row width in bytes */
  40. #define SIGNATURE 0x5342        /* 1st word of all files from this */
  41.  
  42. /* these defines are all coordinates to put various messsages */
  43. #define COM_ROW 23            /* alpha row for command line */
  44. #define COM_COL 0            /* column for same */
  45. #define PROMPT_ROW 24            /* alpha row for prompts */
  46. #define PROMPT_COL 0            /* column for same */
  47. #define GEN_COL 13            /* column to put generation number on*/
  48. #define FILE_COL 37            /* column to put filespec */
  49.  
  50. /* misc. defines */
  51. #define FONT_ROW 8            /* pixel rows in the font */
  52. #define BIT 8                /* number of bits in a byte */
  53. #define BYTEINCOLS 8            /* no. of interal columns in a byte */
  54.  
  55. /****    all functions in this file ******/
  56. int main();         /* setup and master loop for life game*/
  57. void blank();         /* blank internal and screen grid*/
  58. void go();         /* steps through generations until a key is pressed*/
  59. void halt();         /* does nothing */
  60. void quit();         /* exit after prompting for certainty*/
  61. void diskread();     /* read new grid from disk*/
  62. int step();         /* advance one generation on screen and internally*/
  63. void diskwrite();     /* write internal grid to disk*/
  64. void up();         /* speeds up GO if possible */
  65. void down();         /* slows down GO */
  66. void beep();         /* beep the speaker for bad commands*/
  67. void showingrid();     /* display internal grid on screen*/
  68. int getfilespec();     /* get a file name from the user*/
  69. void putgen();         /* puts the current generation counter on prompt line*/
  70. void fill();         /*  fill in a grid cell on the screen and internally*/
  71. void remove();         /* clear a grid cell on the screen and internal grids*/
  72. int kbdreadeventque();     /* simulates MouReadEventQue with the keyboard*/
  73. void xorptr();         /* xors the mouse ptr on the screen*/
  74. void gputs();         /* put a character string on the graphics screen*/
  75. void gputchar();     /* put a character on the graphics screen*/
  76. void anerror();      /* error handler*/
  77. char gethit();         /* waits for a key hit or mouse button hit*/
  78. void wait4release();     /* wait until mouse buttons are up before returning*/
  79. void far pascal exitlife();/* exit program, resetting original screen mode*/
  80.  
  81.  
  82. /* global data */
  83. /* global variables for the internal and screen grids */
  84. int InRow = 45;             /* rows & columns of cells w/ default*/
  85. int InCol = 80;             /*   in internal grid. Col must be /8*/
  86. int ScrRow = 45;            /* rows & column on screen grid */
  87. int ScrCol = 79;
  88. int SizeScrRow = 4;            /* pixel rows per screen grid row */
  89. int SizeScrCol = 8;            /* pixels cols per screen grid column*/
  90. char far *InGrid;            /* pointer to internal grid, a simple
  91.                      * bit map of the space and cells */
  92. char far *InGrid2;            /* pointer for secondary map used
  93.                      *   to calculate next generation */
  94.  
  95. /* screen related global data */
  96. static struct ModeData Highres = {12, 3, 1, 80, 25, 640, 200}; /* 640x200 b/w*/
  97. struct ModeData Savemode = {12};    /* place to save old screen mode */
  98. unsigned ScrSeg;            /* screen buffer segment address */
  99. char Cell[]={32,7};            /* blank for clear screen */
  100. unsigned OddPage=0x2000;        /* offset on Cga of odd row bit plane*/
  101.                     /*   should be 0 on non-Cga modes */
  102. unsigned Cga=2;             /* if using a Cga, screen adresses on
  103.                      *   must be divided by 2 because of
  104.                      *   the odd and even row bit planes
  105.                      * if using non-Cga mode, should be 1*/
  106. /* mouse related global data */
  107. unsigned Mouse = 0;            /* handle if mouse is present, else */
  108. int MouBoundRow = COM_ROW*FONT_ROW+6;    /* last row mouse is allowed on */
  109. int MouBoundCol = 631;            /* last column mouse is allowed on */
  110.  
  111. /* misc. global data */
  112. char Filespec[79-FILE_COL];        /* file name holder (allows default)*/
  113. unsigned Generation;            /* current generation count */
  114. char Logo[] = "                                                             \
  115.     Microsoft LIFE";                    /* logo used to clear prompt line */
  116. int Slow=0;                /* number of slow-down loops for Go */
  117.  
  118. /* Data for commands.  Column of command on COM_ROW for printed name,
  119.  *    name for printing on command line, flag if not 0 then the command
  120.  *    can be executed within a Go command without stopping execution,
  121.  *    and the function that does the command. */
  122. struct Commands {
  123.     int Col;
  124.     char *Name;
  125.     char GoAble;
  126.     void (*Fun)();
  127. } ComLine[] = { 0, "Command:", 0, beep,
  128.         10, "Blank", 0, blank,
  129.         16, "Go", 0, go,
  130.         19, "Halt", 0, halt,
  131.         24, "Quit", 0, quit,
  132.         29, "Read", 0, diskread,
  133.         34, "Step", 0, step,
  134.         39, "Write", 0, diskwrite,
  135.         46, "Down-speed", 1, down,
  136.         57, "Up-speed", 1, 0,
  137.         67, 0, 0
  138.           };
  139.  
  140.  
  141. /***    main - setup and master loop for life game
  142.  *
  143.  *    First this sets the screen mode, gets the address of the screen buffer,
  144.  *    sets the ctrl-C handle to a routine to reset the screen mode on exit,
  145.  *    and allocates the data structures for the internal representation of
  146.  *    the life grid.    The main loop of the program continuously reads from
  147.  *    the keyboard and the mouse and translates the key or location of the
  148.  *    mouse with the ComLine structure into a command to execute.
  149.  */
  150. main () {
  151.     struct KeyData kbd;             /* return for KBD call */
  152.     struct PhysBufData get_phys;        /* return for GetPhysBufData */
  153.     unsigned long throwaway;            /* for returns I don't use */
  154.     int i;                    /* just a counter */
  155.     unsigned far *ptr;                /*used to clear internal grid*/
  156.  
  157.     /* data for the mouse (or keyboard emulator) */
  158.     int ptrrow = 86;        /* current pointer position */
  159.     int ptrcol = 316;
  160.     unsigned status = 0x100;    /* for MouSetDevStatus */
  161.     int type=0;         /* for no waits on mouse read */
  162.     struct EventInfo event;    /* return for mouse reads */
  163.     struct PtrLoc loc;        /* data for MouSetPtrPos */
  164.  
  165.  
  166.     /* try to open and initialize mouse.  If none installed, Mouse will
  167.      *        stay 0 to show keyboard emulation must be used */
  168.     if (!MOUOPEN (0L, (unsigned far *) &Mouse))   /* get handle */
  169.         /* mouse is here and well, so initialize it */
  170.         MOUSETDEVSTATUS ((unsigned far *) &status, Mouse);
  171.  
  172.     /* get current screen mode and save it for restoring on exit */
  173.     if (VIOGETMODE ((struct ModeData far *) &Savemode, 0)) {
  174.         printf ("Error setting screen mode, exitting\n");
  175.         DOSEXIT (1, 0);       /* exit if error */
  176.     }
  177.     /* set screen mode to 640x200 b/w (Cga high resolution */
  178.     if (VIOSETMODE ((struct ModeData far *) &Highres, 0)) {
  179.         printf ("Error setting screen mode, exitting\n");
  180.         DOSEXIT (1, 0);       /* exit if error */
  181.     }
  182.  
  183.     /* set mouse pointer to middle of screen */
  184.     if (Mouse) {
  185.     loc.RowPos=ptrrow;
  186.     loc.ColPos=ptrcol;
  187.     MOUSETPTRPOS ((struct PtrLoc far *) &loc, Mouse);
  188.     }
  189.  
  190.     /* get screen buffer segment */
  191.     get_phys.buf_start=0xb8000L;
  192.     get_phys.buf_length=16*1024L;
  193.     if (VIOGETPHYSBUF ((struct PhysBufData far *) &get_phys, 0)) {
  194.         /* if error here, restore screen and exit */
  195.         VIOSETMODE ((struct ModeData far *) &Savemode, 0);
  196.         printf ("Error accessing screen memory, exitting\n");
  197.         DOSEXIT (1, 0);
  198.     }
  199.     ScrSeg = get_phys.selectors[0];        /* store in global */
  200.  
  201.     /* set ctrl-C to quit() to reset screen at even a break */
  202.     DOSSETSIGHANDLER (exitlife, &throwaway, (unsigned far *)&throwaway, 2, 1);
  203.  
  204.     /* allocate a segment to hold internal grid representation */
  205.     if (DOSALLOCSEG (InRow*InCol/BYTEINCOLS, (unsigned far *) &InGrid, 0))
  206.         /* from here on use anerror() to report errors */
  207.         anerror ("Error allocating memory", 1);
  208.     (long) InGrid *= 0x10000L;            /* move selector to high word*/
  209.  
  210.     /* allocate a segment to hold second buffer used in stepping */
  211.     if (DOSALLOCSEG(InRow*InCol/BYTEINCOLS, (unsigned far *) &InGrid2, 0))
  212.         anerror ("Error allocating memory", 1);
  213.     (long) InGrid2 *= 0x10000L;         /* move selector to high word*/
  214.  
  215.     /* blank internal grid and draw screen */
  216.     for (i=InRow*InCol/16, ptr=(unsigned far *) InGrid ; i--;)
  217.         *ptr++ = 0;
  218.     showingrid ();
  219.     xorptr (ptrrow, ptrcol);            /* show cursor */
  220.  
  221.  
  222.     /* main input loop, polling mouse and keyboard */
  223.     while (1) {
  224.     /* try to get mouse event from keyboard or mouse */
  225.     kbd.char_code=0;            /* clear key buffer residue */
  226.     kbdreadeventque (&event, ptrrow, ptrcol);
  227.     if (Mouse)
  228.         MOUREADEVENTQUE ((struct EventInfo far *) &event,
  229.                  (unsigned far *) &type, Mouse);
  230.  
  231.     /* if any mouse-like events, do this */
  232.     if (event.Mask) {
  233.         xorptr (ptrrow, ptrcol);        /* hide cursor */
  234.         if (event.Mask & 1+2+8) {        /* if any motion */
  235.             /* update position, if off screen grid,ptr=max or min*/
  236.             ptrrow = event.Row;
  237.             ptrcol = event.Col;
  238.             if (ptrrow > MouBoundRow)
  239.             ptrrow = MouBoundRow;
  240.             if (ptrcol > MouBoundCol)
  241.             ptrcol = MouBoundCol;
  242.         }
  243.         /* if a button was hit */
  244.         if (event.Mask & (2|4|8|16)) {
  245.         if (ptrrow < COM_ROW*FONT_ROW-SizeScrRow) { /* if on grid */
  246.             Generation=0;               /* reset Gen if     */
  247.             putgen();                   /*   grid modified*/
  248.             if (event.Mask & (2 | 4))           /* if left down*/
  249.             fill (ptrcol/SizeScrCol, ptrrow/SizeScrRow);
  250.             else if (event.Mask & (8 | 16))    /* if right */
  251.             remove (ptrcol/SizeScrCol, ptrrow/SizeScrRow);
  252.         }
  253.         else {                       /* on command line*/
  254.             /* if pointing at command word, execute it, highlighting */
  255.             for(i=0; ComLine[i].Fun != 0; i++)
  256.             if (ptrcol/BIT >= ComLine[i].Col &&
  257.                 ptrcol/BIT < ComLine[i+1].Col-1) {
  258.                 gputs(ComLine[i].Name,COM_ROW,ComLine[i].Col,0xff);
  259.                 wait4release ();        /* execute on release*/
  260.                 (*(ComLine[i].Fun))();
  261.                 if (ComLine[i].Fun) /*restore name if not removed*/
  262.                    gputs(ComLine[i].Name,COM_ROW,ComLine[i].Col,0);
  263.             }
  264.         }
  265.         }
  266.         xorptr (ptrrow, ptrcol);           /* show cursor */
  267.     }
  268.  
  269.     /* check keys, no wait */
  270.     KBDPEEK ((struct KeyData far *) &kbd, 0);
  271.     if (kbd.char_code) {        /* get key only if not regular char */
  272.         KBDCHARIN ((struct KeyData far *) &kbd, 1, 0);    /*get command*/
  273.         xorptr (ptrrow, ptrcol);        /* avoid overwrites */
  274.         /* walk command structure and execute function, highlighting */
  275.         for(i=0; ComLine[i].Fun != 0; i++)
  276.             if ((kbd.char_code & 0xdf) == *(ComLine[i].Name)) {
  277.             gputs(ComLine[i].Name,COM_ROW,ComLine[i].Col,0xff);
  278.             (*(ComLine[i].Fun))();
  279.             if (ComLine[i].Fun) /*restore name if not removed*/
  280.                 gputs(ComLine[i].Name,COM_ROW,ComLine[i].Col,0);
  281.             }
  282.         xorptr (ptrrow, ptrcol);        /* restore pointer */
  283.     }
  284.     }
  285. }
  286.  
  287.  
  288. /***    blank - blank internal and screen grid
  289.  *
  290.  *    Sets the internal grid to all 0's and calls draw_grid() to
  291.  *    clear the screen and put up a screen grid.  blank() then draws
  292.  *    the command line and the program logo.
  293.  *
  294.  *    Entry:    ScrSeg = current screen segment
  295.  *        InGrid points to internal grid defined by InCol and InRow
  296.  *        Logo points to program logo that also clears the prompt line
  297.  *
  298.  *    Exit:    Generation = 0
  299.  *
  300.  *    Calls:    gputs(), putgen(), draw_grid [assembler routine]
  301.  */
  302. void
  303. blank (){
  304.     unsigned far *ptr;
  305.     int i;
  306.  
  307.     gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
  308.     gputs ("Are you sure?", PROMPT_ROW, PROMPT_COL, 0);
  309.     if (gethit () == 'Y') {
  310.         /* if yes, blank screen and internal grid */
  311.         draw_grid ();            /* draw blank grid on screen */
  312.         for (i=InRow*InCol/16, ptr=(unsigned far *) InGrid ; i--;)
  313.             *ptr++ = 0;        /* blank internal grid */
  314.         for (i=0; ComLine[i].Fun != 0; i++) /* print command line */
  315.             gputs (ComLine[i].Name, COM_ROW, ComLine[i].Col, 0);
  316.         Generation=0;            /* reset generation count */
  317.     }
  318.     gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);   /* put on logo */
  319.     putgen ();                /* initial generation message*/
  320. }
  321.  
  322.  
  323. /***    go - steps through generations until a key is pressed
  324.  *
  325.  *    Calls step() until a key is hit, which terminates the loop
  326.  *    and the key is left on the buffer for main() to process
  327.  *    as a command.
  328.  *    step()'s return is checked to see if this program has just
  329.  *    moved from background to foreground, in which case the
  330.  *    the screen will need manual updating with showingrid().
  331.  *    If the D or U keys are hit, down() or up() is executed.
  332.  *
  333.  *    Entry:    none
  334.  *
  335.  *    Exit:    None
  336.  *
  337.  *    Calls:    step(), showingrid()
  338.  */
  339. void
  340. go () {
  341.     struct KeyData kbd;             /* return from KBD call */
  342.     int background=0;                /* 0 if forground, 1 backgrd */
  343.     struct EventInfo event;            /* return from mouse read */
  344.     int type=0;                 /* for no waits on mouse read*/
  345.     struct QueInfo num;             /* return for GetNumQueEl */
  346.     unsigned x,y;                /* for slowing down loops */
  347.  
  348.     /* step until key or button is hit, executing GoAble commands */
  349.     kbd.scan_code=0;
  350.     event.Mask=0;
  351.     while (!kbd.scan_code) {
  352.     if (step ())
  353.         background=1;
  354.     else if (background==1) {
  355.         background=0;
  356.         showingrid();
  357.     }
  358.     if (Mouse) {
  359.         /* read all events on que */
  360.         MOUGETNUMQUEEL ((struct QueInfo far *) &num, Mouse);
  361.         while (num.Events--) {
  362.             MOUREADEVENTQUE ((struct EventInfo far *) &event,
  363.                  (unsigned far *) &type, Mouse);
  364.             if (event.Mask & (2|4|8|16)) {    /* leave if mouse hit*/
  365.                 wait4release();
  366.                 return;
  367.             }
  368.         }
  369.     }
  370.  
  371.     /* check keys, no wait.  Execute if GoAble command */
  372.     KBDPEEK ((struct KeyData far *) &kbd, 0);
  373.     if (kbd.scan_code) {
  374.         /* walk command structure and execute function, highlighting */
  375.         for(x=0; ComLine[x].Fun != 0; x++)
  376.             if ((kbd.char_code & 0xdf) == *(ComLine[x].Name)
  377.                    && ComLine[x].GoAble) {
  378.             gputs(ComLine[x].Name,COM_ROW,ComLine[x].Col,0xff);
  379.             KBDCHARIN ((struct KeyData far *) &kbd, 1, 0);
  380.             kbd.scan_code=0;
  381.             (*(ComLine[x].Fun))();
  382.             if (ComLine[x].Fun) /*restore name if not removed*/
  383.                 gputs(ComLine[x].Name,COM_ROW,ComLine[x].Col,0);
  384.             }
  385.     }
  386.     if (Slow)           /* slow down if requested */
  387.         for (x=Slow; x--;)
  388.             for (y=50000; y--;);
  389.     }
  390. }
  391.  
  392.  
  393. /***    halt - does nothing
  394.  */
  395. void
  396. halt () {
  397.     int i;
  398.  
  399.     for (i=30000; i--;);        /* short pause */
  400. }
  401.  
  402.  
  403. /***    quit - exit after prompting for certainty
  404.  *
  405.  *    Entry:    None
  406.  *
  407.  *    Exit:    None
  408.  */
  409. void
  410. quit () {
  411.     gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
  412.     gputs ("Are you sure?", PROMPT_ROW, PROMPT_COL, 0);
  413.     if (gethit () == 'Y')
  414.         exitlife ();            /* if yes, do quit routine */
  415.     gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);   /* else, continue */
  416.     putgen();                /* put back gen count*/
  417. }
  418.  
  419.  
  420. /***    diskread - read new grid from disk
  421.  *
  422.  *    getfilespec() is called to get a file name from the user which
  423.  *    is put in Filespec.
  424.  *    Then an internal grid is read from the disk which is in the form:
  425.  *                WORD SIGNATURE ; Life file signature
  426.  *                WORD rows
  427.  *                WORD columns
  428.  *                WORD generation
  429.  *    (rows*columns/BYTEINCOLS) BYTES of the bit mapped grid
  430.  *
  431.  *    InGrid is ReAlloced to the size of the newly read grid and
  432.  *    InCol, InRow, and Generation are all set the values contained
  433.  *    in the file read.
  434.  *
  435.  *    Entry:    InGrid points to internal grid defined by InCol and InRow
  436.  *
  437.  *    Exit:    InGrid is ReAlloced to size of newly read grid.
  438.  *        InCol, InRow, and Generation are all set the values
  439.  *            contained in the file read.
  440.  *                         d
  441.  *    Calls:    getfilespec ();
  442.  */
  443. void
  444. diskread () {
  445.     unsigned handle;            /* file handle */
  446.     unsigned action;            /* return for file calls */
  447.     unsigned signature;            /* life file signature word */
  448.  
  449.     /* get file name into Filespec */
  450.     if (getfilespec ())
  451.         return;             /* return if user hit ESC */
  452.  
  453.     /* open file, fail if it doesn't exist */
  454.     if (DOSOPEN ((char far *) Filespec, (unsigned far *) &handle,
  455.         (unsigned far *) &action, 0L, 0, 0x01, 0x42, 0L)) {
  456.         anerror ("Can't open file", 0);
  457.         Filespec[0]=0;    /* clear bad file name */
  458.         return;
  459.     }
  460.  
  461.     /* read appropriate from file */
  462.     if (DOSREAD (handle, (char far *) &signature, sizeof (signature),
  463.         (unsigned far *) &action) || action != sizeof (signature))
  464.         anerror ("Error writing to file", 0);
  465.  
  466.     else if (signature != SIGNATURE) {
  467.         anerror ("Not a life file", 0);
  468.         if (DOSCLOSE (handle))
  469.             anerror ("Error closing file", 0);
  470.         return;
  471.     }
  472.     else if (DOSREAD (handle, (char far *) &InRow, sizeof (InRow),
  473.         (unsigned far *) &action) || action != sizeof (InRow))
  474.         anerror ("Error reading from file", 0);
  475.  
  476.     else if (DOSREAD (handle, (char far *) &InCol, sizeof (InCol),
  477.         (unsigned far *) &action) || action != sizeof (InCol))
  478.         anerror ("Error reading from file", 0);
  479.  
  480.     else if (DOSREAD (handle, (char far *) &Generation,
  481.         sizeof (Generation), (unsigned far *) &action) ||
  482.         action != sizeof (Generation))
  483.         anerror ("Error reading from file", 0);
  484.  
  485.     /* change size of *InGrid to match saved pattern */
  486.     else if (DOSREALLOCSEG (InRow*InCol/BYTEINCOLS, (unsigned)
  487.         ((long) InGrid/0x10000L)))
  488.         anerror ("Error allocating memory", 0);
  489.  
  490.     else if (DOSREAD (handle, InGrid, InRow*InCol/BYTEINCOLS,
  491.         (unsigned far *) &action) || action != InRow*InCol/BYTEINCOLS)
  492.         anerror ("Error reading from file", 0);
  493.  
  494.     /* show the newly loaded pattern on the screen */
  495.     showingrid ();
  496.  
  497.     /* close file */
  498.     if (DOSCLOSE (handle))
  499.         anerror ("Error closing file", 0);
  500. }
  501.  
  502.  
  503. /***    step - advance one generation on screen and internally
  504.  *
  505.  *    Uses dostep to advance the current internal and screen grid
  506.  *    to the next generation.  dostep() returns 1 if the screen
  507.  *    was not available and thus no update was made, this allows
  508.  *    the program to continue to execute in the background.
  509.  *    The return from dostep() is passed back to step()'s caller.
  510.  *
  511.  *    Entry:    None
  512.  *
  513.  *    Exit:    Returns 1 if executing in background and there was thus
  514.  *              no screen update.
  515.  *        Else, returns 0 if in forground
  516.  *        Generation is incremented.
  517.  *
  518.  *    Calls:    putgen(), dostep [assembler routine]
  519.  */
  520. int
  521. step() {
  522.     int    rc;                /* return code from dostep */
  523.  
  524.     /* do the stepping using an assembler routine for speed */
  525.     rc=dostep(InGrid, InGrid2, InRow, InCol);  /* do the step */
  526.  
  527.     Generation++;                /* advance the count */
  528.     putgen();                /* and display gen */
  529.     return(rc);
  530. }
  531.  
  532.  
  533. /***    diskwrite - write internal grid to disk
  534.  *
  535.  *    getfilespec() is called to get a file name from the user which
  536.  *    is put in Filespec.
  537.  *    Then the internal grid is saved to disk in the form:
  538.  *                WORD SIGNATURE ; Life file signature
  539.  *                WORD rows
  540.  *                WORD columns
  541.  *                WORD generation
  542.  *    (rows*columns/BYTEINCOLS) BYTES of the bit mapped grid
  543.  *
  544.  *    Entry:    InGrid points to internal grid defined by InCol and InRow
  545.  *
  546.  *    Exit:    None
  547.  *
  548.  *    Calls:    getfilespec ();
  549.  */
  550. void
  551. diskwrite () {
  552.     unsigned handle;            /* file handle */
  553.     unsigned action;            /* return for file calls */
  554.     unsigned signature=SIGNATURE;        /* life file signature word */
  555.  
  556.     /* get file name into Filespec */
  557.     if (getfilespec ())
  558.         return;             /* return if user hit ESC */
  559.  
  560.     /* open file and truncate or create it if it doesn't exist */
  561.     if (DOSOPEN ((char far *) Filespec, (unsigned far *) &handle,
  562.         (unsigned far *) &action, 0L, 0, 0x12, 0x42, 0L)) {
  563.         anerror ("Can't open file", 0);
  564.         Filespec[0]=0;    /* clear bad file name */
  565.         return;
  566.     }
  567.  
  568.     /* write appropriate info to file */
  569.     if (DOSWRITE (handle, (char far *) &signature, sizeof (signature),
  570.         (unsigned far *) &action)    || action != sizeof (signature))
  571.         anerror ("Error writing to file", 0);
  572.  
  573.     else if (DOSWRITE (handle, (char far *) &InRow, sizeof (InRow),
  574.         (unsigned far *) &action) || action != sizeof (InRow))
  575.         anerror ("Error writing to file", 0);
  576.  
  577.     else if (DOSWRITE (handle, (char far *) &InCol, sizeof (InCol),
  578.         (unsigned far *) &action) || action != sizeof (InCol))
  579.         anerror ("Error writing to file", 0);
  580.  
  581.     else if (DOSWRITE (handle, (char far *) &Generation,
  582.         sizeof (Generation), (unsigned far *) &action) ||
  583.         action != sizeof (Generation))
  584.         anerror ("Error writing to file", 0);
  585.  
  586.     else if (DOSWRITE (handle, InGrid, InRow*InCol/BYTEINCOLS,
  587.         (unsigned far *) &action) || action != InRow*InCol/BYTEINCOLS)
  588.         anerror ("Error writing to file", 0);
  589.  
  590.     /* close file */
  591.     if (DOSCLOSE (handle))
  592.         anerror ("Error closing file", 0);
  593. }
  594.  
  595.  
  596. /***    up - speeds up GOs (decrement Slow)
  597.  */
  598. void
  599. up() {
  600.     int i;
  601.  
  602.     if (Slow)
  603.         Slow--;
  604.     if (!Slow) {
  605.         /* if at top speed take out message */
  606.         for(i=0; *(ComLine[i].Name) != 'U'; i++);
  607.         ComLine[i].Fun=0;
  608.         gputs ("        ", COM_ROW, ComLine[i].Col, 0);
  609.     }
  610.     for (i=30000; i--;);        /* short pause */
  611. }
  612.  
  613.  
  614. /***    down - slows down GOs (increment Slow)
  615.  */
  616. void
  617. down() {
  618.     int i;
  619.  
  620.     Slow++;
  621.     for (i=30000; i--;);        /* short pause */
  622.     if (Slow){            /* if at top speed, display UP */
  623.         for(i=0; *(ComLine[i].Name) != 'U'; i++);
  624.         ComLine[i].Fun=up;
  625.         for (i=0; ComLine[i].Fun != 0; i++) /* print command line */
  626.             gputs (ComLine[i].Name, COM_ROW, ComLine[i].Col, 0);
  627.     }
  628. }
  629.  
  630.  
  631. /***    beep - beep the speaker for bad commands
  632.  */
  633. void
  634. beep () {
  635.     DOSBEEP (500, 50);
  636. }
  637.  
  638.  
  639. /***    showingrid - display internal grid on screen
  640.  *
  641.  *    Clears the screen using the draw_grid routine and then
  642.  *    puts the internal grid on the screen using fill() so as to
  643.  *    not be resolution dependent.
  644.  *
  645.  *    Entry:    InGrid points to internal grid defined by InCol and InRow.
  646.  *        ScrSeg points to the screen buffer.
  647.  *        ScrRow and ScrCol describe the dimensions of the screen grid.
  648.  *        Logo points to program logo that also clears the prompt line
  649.  *
  650.  *    Exit:    None
  651.  *
  652.  *    Calls:    fill(), putgen(), gputs(), draw_grid [assembler routine]
  653.  */
  654. void
  655. showingrid () {
  656.     int x, y;                /* cell coordinates for loop */
  657.     char retcode;                /* for VIOSCRLOCK */
  658.  
  659.     /* prepare blank screen so we only have to fill in cells that are on */
  660.     draw_grid ();                /* draw blank grid on screen */
  661.     /* print command line */
  662.     for (x=0; ComLine[x].Fun != 0; x++)
  663.         gputs (ComLine[x].Name, COM_ROW, ComLine[x].Col, 0);
  664.     gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
  665.     putgen ();
  666.  
  667.     /* loop through the screen sized area of the grid */
  668.     VIOSCRLOCK (1, (char far *) &retcode, 0);   /* get screen access */
  669.     for (y=ScrRow; y--;)
  670.         for (x=ScrCol; x--;)
  671.             /* if the internal cell is on, turn on the screen one*/
  672.             if (*(InGrid + (x + y*InCol)/BYTEINCOLS) & (0x80 >> (x&7)))
  673.                 fill (x, y);
  674.     VIOSCRUNLOCK (0);
  675. }
  676.  
  677.  
  678. /***    getfilespec - get a file name from the user
  679.  *
  680.  *    Prompts user for a file name for use with diskread() or diskwrite().
  681.  *    The last file name used is shown on the screen as a default
  682.  *    and the user can hit enter (or left mouse button) to specify the
  683.  *    default name.  If any other key is pressed, this routine takes the
  684.  *    input until an enter and returns in Filespec.  If ESC is hit any time
  685.  *    during typeing, the input will be aborted and the buffer cleared.
  686.  *
  687.  *    Entry:    Filespec points to default file name
  688.  *        Logo points to program logo that also clears the prompt line
  689.  *
  690.  *    Exit:    Filespec points to new file name
  691.  *        returns 0x1b if ESC was hit during entry, else 0
  692.  *
  693.  *    Calls:    gputs(), putgen()
  694.  */
  695. int
  696. getfilespec () {
  697.     int c=0;                /* index for Filespec[] */
  698.     struct KeyData kbd;            /* return for KBD call */
  699.     unsigned space=0x0020;            /* space character for print */
  700.     struct EventInfo event;         /* return for mouse */
  701.     int type=0;                /* mouse reads w/no wait */
  702.  
  703.     kbd.scan_code=0;
  704.     event.Mask=0;
  705.     /* put up default file name if one exists */
  706.     if (Filespec[0]) {
  707.         gputs ("Type file name or enter for default:  ", PROMPT_ROW,
  708.                                  PROMPT_COL,0);
  709.         gputs (Filespec, PROMPT_ROW, FILE_COL, 0);
  710.         /* wait for first key or button to see if they accept default */
  711.         while (!kbd.scan_code && !(event.Mask & (2|4|8|16))) {
  712.             if (Mouse)
  713.                 MOUREADEVENTQUE ((struct EventInfo far *) &event,
  714.                          (unsigned far *) &type, Mouse);
  715.             KBDPEEK ((struct KeyData far *) &kbd, 0);
  716.         }
  717.     }
  718.     if (event.Mask & (8 | 16)) {    /* if right button hit, same as ESC */
  719.         kbd.char_code = 0x1b;
  720.         wait4release();
  721.     }
  722.     if (event.Mask & (2 | 4)) {    /* if left button hit, same as \r */
  723.         wait4release();
  724.         gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
  725.         putgen();
  726.         return (0);
  727.     }
  728.     if (kbd.char_code == 0x1b) {    /* if ESC, return */
  729.         gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
  730.         putgen();
  731.         return (0x1b);               /*    return escape */
  732.     }
  733.  
  734.     gputs(Logo, PROMPT_ROW, PROMPT_COL, 0);
  735.     gputs("Type file name,  followed by enter:  ",PROMPT_ROW,PROMPT_COL,0);
  736.  
  737.     /* read chars and print them until \r */
  738.     while (!(KBDCHARIN ((struct KeyData far *) &kbd, 0, 0)) &&
  739.        kbd.char_code != '\r') {
  740.          if (kbd.char_code == '\b') { /* if backspace, backspace */
  741.              if (c > 0) {            /* prevent underflow*/
  742.                  Filespec[--c]=0;
  743.                  /* erase backed-out character*/
  744.                  gputs (&space, PROMPT_ROW, FILE_COL+c,0);
  745.              }
  746.              else                /* if overflow, yell */
  747.                  beep();
  748.          }
  749.          else if (kbd.char_code == 0x1b) {        /* if escape was hit */
  750.              Filespec[0]=0;            /*   blank name */
  751.              gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
  752.              putgen();
  753.              return (0x1b);            /*   return escape */
  754.          }
  755.          else if (kbd.char_code <46 || kbd.char_code > 122)
  756.              beep();                /* if not char, beep */
  757.          else
  758.              if (c < sizeof (Filespec)) {    /* prevent overflow */
  759.                  Filespec[c]=kbd.char_code;  /* add to string */
  760.                  Filespec[++c]=0;         /* null terminal */
  761.              }
  762.              else                /* if overflow, yell */
  763.                  beep();
  764.  
  765.          gputs (Filespec, PROMPT_ROW, FILE_COL, 0);   /* print new name */
  766.     }
  767.     /* return generation count to screen */
  768.     gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
  769.     putgen();
  770.     return (0);
  771. }
  772.  
  773.  
  774. /***    putgen - puts the current generation counter on prompt line
  775.  *
  776.  *    Entry:    Generation = current generation number
  777.  *
  778.  *    Exit:    None
  779.  *
  780.  *    Calls:    gputs()
  781.  */
  782. void
  783. putgen ()
  784. {
  785.     char num[8];                /* number string to print */
  786.     int i, g;
  787.  
  788.     gputs ("Generation:         ", PROMPT_ROW, PROMPT_COL,0);/* gen label*/
  789.  
  790.     if (Generation == 0)
  791.         gputs ("0", PROMPT_ROW,GEN_COL,0);/*if special case need this*/
  792.     else {                    /* not 0, do this: */
  793.         for (g=Generation, i=sizeof(num)-1; i-- >= 0 && g; g/=10)
  794.             num[i]=(g%10)+'0';      /* convert int to string */
  795.         num[sizeof(num)-1]= 0;         /* zero terminated for gputs */
  796.         gputs (&num[i+1], PROMPT_ROW, GEN_COL,0); /* print number */
  797.     }
  798. }
  799.  
  800.  
  801. /***    fill -    fill in a grid cell on the screen and internally
  802.  *
  803.  *    fill (x, y)
  804.  *
  805.  *    Entry:    x = grid horizontal coordinate
  806.  *        y = grid vertical coordinate
  807.  *        ScrSeg = screen buffer segment
  808.  *        VioScrLock is active
  809.  *        InGrid points to internal grid defined by InCol and InRow
  810.  *        ScrRow and ScrCol define limits of screen grid
  811.  *        Currently, must use 79x25 grid drawn by draw_grid()
  812.  *        Currently, screen must be in 640x200 b/w mode
  813.  *
  814.  *    Exit:    None
  815.  */
  816. void
  817. fill (x, y)
  818. int x,y;
  819. {
  820.     char far *gridptr;
  821.  
  822.     /* only do if within screen grid limits */
  823.     if (x < ScrCol && y < ScrRow) {
  824.         /* set gridptr to & in screen buff of cell to fill */
  825.         (long) gridptr = ScrSeg*0x10000;      /* fill in address */
  826.         (unsigned) gridptr = (x*SizeScrCol/BIT)+(y*SizeScrRow/Cga*SCR_WID);
  827.         /* fill in screen cell */
  828.         *(gridptr+OddPage)=0x7f;
  829.         *(gridptr+SCR_WID)=0x7f;
  830.         *(gridptr+OddPage+SCR_WID)=0x7f;
  831.     }
  832.     /* fill in internal cell*/
  833.     /*InGrid= &internal grid of x,y*/
  834.     gridptr = InGrid + x/BYTEINCOLS + y*InCol/BYTEINCOLS;
  835.     *gridptr = *gridptr | (0x80 >> (x & 7));   /* fill bit */
  836. }
  837.  
  838.  
  839. /***    remove - clear a grid cell on the 79x45 grid screen and internally
  840.  *
  841.  *    remove (x, y)
  842.  *
  843.  *    Entry:    x = grid horizontal coordinate
  844.  *        y = grid vertical coordinate
  845.  *        ScrSeg = screen buffer segment
  846.  *        VioScrLock is active
  847.  *        InGrid points to internal grid defined by InCol and InRow
  848.  *        ScrRow and ScrCol define limits of screen grid
  849.  *        Currently, must use 79x25 grid drawn by draw_grid()
  850.  *        Currently, screen must be in 640x200 b/w mode
  851.  *
  852.  *    Exit:    None
  853.  */
  854. void
  855. remove (x, y)
  856. int x,y;
  857. {
  858.     char far *gridptr;
  859.  
  860.     /* only do if within screen grid limits */
  861.     if (x < ScrCol && y < ScrRow) {
  862.         /* set gridptr to & in screen buff of cell to fill */
  863.         (long) gridptr = ScrSeg*0x10000;      /* fill in address */
  864.         (unsigned) gridptr = (x*SizeScrCol/BIT)+(y*SizeScrRow/Cga*SCR_WID);
  865.         /* fill in screen cell */
  866.         *(gridptr+OddPage)=0x80;
  867.         *(gridptr+SCR_WID)=0x80;
  868.         *(gridptr+OddPage+SCR_WID)=0x80;
  869.     }
  870.  
  871.     /* remove internal cell*/
  872.     /*InGrid= &internal grid of x,y*/
  873.     gridptr = InGrid + x/BYTEINCOLS + y*InCol/BYTEINCOLS;
  874.     *gridptr = *gridptr & ~((0x80 >> (x & 7)));   /* remove bit */
  875. }
  876.  
  877.  
  878. /***    kbdreadeventque - simulates MouReadEventQue with the keyboard
  879.  *
  880.  *    F9 key is responded to as left button, F10 key as right.
  881.  *    Pointer location is advanced by SizeScrCol or SizeScrRow according
  882.  *    to direction of direction key hits.
  883.  *    Does not wait for input, instead returns 0 event mask.
  884.  *    Does not check MouDevStatus, instead always returns pixel coordinates
  885.  *    Event buffer is filled according to MouReadEventQue
  886.  *
  887.  *    Entry:    event  - structure to return data
  888.  *        ptrrow - current point row
  889.  *        ptrcol - current pointer column
  890.  *
  891.  *    Exit:    None
  892.  *        *event contains any event that may have occurred
  893.  */
  894. int
  895. kbdreadeventque (event, ptrrow, ptrcol)
  896. struct EventInfo *event;
  897. int ptrrow, ptrcol;
  898. {
  899.     struct KeyData kbd;            /* return for KBD call */
  900.  
  901.     kbd.char_code=0;            /* clear data struct residue*/
  902.     kbd.scan_code=0;
  903.     /* peek at key buffer */
  904.     KBDPEEK ((struct KeyData far *) &kbd, 0);
  905.  
  906.     /* if it is an extended key code, process it */
  907.     if (kbd.char_code == 0 && kbd.scan_code) {
  908.         KBDCHARIN ((struct KeyData far *) &kbd, 1, 0); /* take char */
  909.         event->Row=ptrrow;        /* initial position */
  910.         event->Col=ptrcol;
  911.         switch (kbd.scan_code) {    /* check second byte of code */
  912.             case 0x48:            /* cursor up */
  913.             event->Row -= SizeScrRow;
  914.             event->Mask=1;
  915.             break;
  916.             case 0x50:            /* cursor down */
  917.             event->Row += SizeScrRow;
  918.             event->Mask=1;
  919.             break;
  920.             case 0x4b:            /* cursor left */
  921.             event->Col -= SizeScrCol;
  922.             event->Mask=1;
  923.             break;
  924.             case 0x4d:            /* cursor right */
  925.             event->Col += SizeScrCol;
  926.             event->Mask=1;
  927.             break;
  928.             case 0x43:            /* left button (F9) */
  929.             event->Mask=4;
  930.             break;
  931.             case 0x52:            /* left button (INS) */
  932.             event->Mask=4;
  933.             break;
  934.             case 0x44:             /* right button (F10) */
  935.             event->Mask=16;
  936.             break;
  937.             case 0x53:             /* right button (DEL) */
  938.             event->Mask=16;
  939.             break;
  940.             default:            /* anything else is no good */
  941.             event->Mask=0;
  942.             break;
  943.         }
  944.     }
  945.     else
  946.         event->Mask=0;            /* if no extended key */
  947. }
  948.  
  949.  
  950. /***    xorptr - xors the mouse ptr on the screen
  951.  *
  952.  *    xorptr (ptrrow, ptrcol)
  953.  *
  954.  *    Entry:    ptrrow = pixel row on screen
  955.  *        ptrcol = pixel column on screen
  956.  *        ScrSeg = screen buffer segment
  957.  *
  958.  *    Exit:    None
  959.  */
  960. void
  961. xorptr (ptrrow, ptrcol)
  962. unsigned ptrrow, ptrcol;
  963. {
  964.     char far *gridptr;
  965.     unsigned mask=0;            /* bit mask for pointer shape*/
  966.     int x=8;                /* count for rows of pointer */
  967.     char retcode;                /* return from VioScrLock */
  968.  
  969.     /* set gridptr to & in screen buff to xor */
  970.     ptrrow++;                /* 1st move ptr to next row*/
  971.     (long) gridptr = ScrSeg*0x10000;    /* fill in segment address */
  972.     (unsigned) gridptr = ptrcol/x+(ptrrow/Cga*SCR_WID);/* fill offset*/
  973.  
  974.     /* fill in screen pointer */
  975.     VIOSCRLOCK (1, (char far *) &retcode, 0);    /* get screen */
  976.     while (x--) {
  977.         mask = mask >> 1;            /* make mask thicker */
  978.         mask |= 0x8000;             /*    at bottom */
  979.         if (ptrrow & 1 && Cga == 2)        /* if odd bit plane */
  980.             gridptr += OddPage;
  981.         /* xor mask to scr, being sure to reverse bytes in word */
  982.         *(gridptr+1) ^= (char) (mask >> (ptrcol & 7));
  983.         *(gridptr) ^= (char) ((mask >> (ptrcol & 7)) / 256);
  984.         if (ptrrow & 1 && Cga == 2)        /* if odd bit plane */
  985.             gridptr -= OddPage;        /*    plane, next row*/
  986.         ptrrow++;                /* next row */
  987.         if (!(ptrrow & 1) && Cga == 2)        /*only advance on odd*/
  988.             gridptr += SCR_WID;        /*   row w/o CGA */
  989.      }
  990.     VIOSCRUNLOCK (0);                /* give back screen */
  991. }
  992.  
  993.  
  994. /***    gputs - put a character string on the graphics screen
  995.  *
  996.  *    gputs (string, row, col)
  997.  *
  998.  *    Draws the passed asciiz string on the screen unless the screen
  999.  *    is not available (i.e., if running in background screen group)
  1000.  *    in which case this routine does nothing so as to not hold up
  1001.  *    the program.
  1002.  *
  1003.  *    Entry:    string - address of asciiz string to print
  1004.  *        row    - alpha row to print at
  1005.  *        col    - alpha col to print at
  1006.  *        mask   - xor'ed with bytes of character as printed
  1007.  *
  1008.  *    Exit:    None
  1009.  *
  1010.  *    Calls:    gputchar()
  1011.  *
  1012.  *    Warning: Prints graphics characters for ascii control codes (<32).
  1013.  *         Does not wrap-around at line end.
  1014.  */
  1015. void
  1016. gputs (string, row, col, mask)
  1017. char *string;
  1018. unsigned col, row;
  1019. char mask;
  1020. {
  1021.     static struct VIOFONT fontdata = {14,1,FONT_ROW,8,0L,0};/*for GetFont*/
  1022.     char c;
  1023.     char retcode;                /* for VIOSCRLOCK */
  1024.     char far *scr_addr;            /* address in screen buffer
  1025.                            to print at */
  1026.  
  1027.     /* try lock without waiting for availability */
  1028.     VIOSCRLOCK (0, (char far *) &retcode, 0);    /* get screen access */
  1029.  
  1030.     /* do display only if screen available now */
  1031.     if (retcode == 0) {
  1032.         /* get address of font table if haven't yet */
  1033.            if (!fontdata.font_data)
  1034.                VIOGETFONT ((struct VIOFONT far *) &fontdata, 0);
  1035.  
  1036.            (long) scr_addr = ScrSeg*0x10000;   /* fill in segment address*/
  1037.            (unsigned)scr_addr = col+(row*SCR_WID*(FONT_ROW/Cga));/*offset*/
  1038.            while ((c=*(string++)) != 0)           /* print each char */
  1039.               gputchar(scr_addr++, fontdata.font_data+c*FONT_ROW,mask);
  1040.            VIOSCRUNLOCK (0);               /* give up screen */
  1041.     }
  1042. }
  1043.  
  1044.  
  1045. /***    gputchar - put a character on the graphics screen
  1046.  *
  1047.  *    gputchar (scr_addr, font_addr, mask)
  1048.  *
  1049.  *    Entry:    scr_addr  - far address in screen buffer to place character
  1050.  *                must be on even Cga bit plane
  1051.  *        font_addr - far address in font table bit pattern
  1052.  *        mask      - xor'ed with each byte put on screen
  1053.  *        VIOSCRLOCK is active
  1054.  *
  1055.  *    Exit:    None
  1056.  */
  1057. void
  1058. gputchar(scr_addr, font_addr, mask)
  1059. char far *scr_addr;
  1060. char far *font_addr;
  1061. char mask;
  1062. {
  1063.     int i=FONT_ROW;         /* count to draw whole character */
  1064.  
  1065.     while (i--) {
  1066.         if (i & 1 && Cga == 2)        /* if odd bit plane */
  1067.             scr_addr += OddPage-SCR_WID;
  1068.         *scr_addr = (*(font_addr++) ^ mask);/* put byte on even plane*/
  1069.         if (i & 1 && Cga == 2)        /* if odd bit plane */
  1070.             scr_addr -= OddPage;
  1071.         scr_addr+=SCR_WID;        /* move down a line */
  1072.     }
  1073. }
  1074.  
  1075.  
  1076. /***    anerror - error handler
  1077.  *
  1078.  *    anerror (message, fatal);
  1079.  *
  1080.  *    Prints passed error message and waits for a key hit to allow
  1081.  *    user to read it.  If fatal flag is 0, the routine then returns,
  1082.  *    otherwise the routine restores the original screen mode, clears
  1083.  *    the screen, and exits.
  1084.  *
  1085.  *    Entry:    message - string to print describing error
  1086.  *        fatal - flag, if == 0, this procedure prompts for any key hit
  1087.  *            then returns.  If != 0, error is fatal & program exits.
  1088.  *        Logo points to program logo that also clears the prompt line
  1089.  *
  1090.  *    Exit:    None
  1091.  *
  1092.  *    Calls:    gputs(), putgen()
  1093.  */
  1094. void
  1095. anerror (message, fatal)
  1096. char message[];
  1097. int fatal;
  1098. {
  1099.     struct KeyData kbd;            /* return for KBD call */
  1100.  
  1101.     gputs (Logo, PROMPT_ROW, PROMPT_COL,0);
  1102.     gputs (message, PROMPT_ROW, PROMPT_COL, 0);/* print passed message */
  1103.  
  1104.     if (fatal) {
  1105.         gputs(":  Press any key to exit",PROMPT_ROW,strlen(message),0);
  1106.         gethit ();
  1107.         exitlife();
  1108.     }
  1109.     else  {
  1110.         gputs (":  Press any key to continue",PROMPT_ROW,
  1111.                               strlen(message), 0);
  1112.         gethit ();
  1113.         gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
  1114.         putgen();
  1115.     }
  1116. }
  1117.  
  1118.  
  1119. /***    gethit - waits for a key hit or mouse button hit
  1120.  *
  1121.  *    Entry:    None
  1122.  *
  1123.  *    Exit:    Returns character hit (forced upper case) or 'Y' for left
  1124.  *        mouse button and 'N' for right mouse
  1125.  */
  1126. char
  1127. gethit () {
  1128.     struct KeyData kbd;            /* for keyboard */
  1129.     struct EventInfo event;         /* for mouse */
  1130.     int type=0;
  1131.     char rc;                /* return code */
  1132.  
  1133.     /* loop till a hit */
  1134.     kbd.scan_code=0;
  1135.     event.Mask=0;
  1136.     while (!kbd.scan_code && !(event.Mask & (2|4|8|16))) {
  1137.         if (Mouse)
  1138.             MOUREADEVENTQUE ((struct EventInfo far *) &event,
  1139.                      (unsigned far *) &type, Mouse);
  1140.         KBDCHARIN ((struct KeyData far *) &kbd, 1, 0);
  1141.     }
  1142.     if (kbd.scan_code)            /* if key, return char */
  1143.         rc=kbd.char_code & 0xdf;
  1144.     else if (event.Mask & (8 | 16)) {    /* if right */
  1145.         rc='N';
  1146.         wait4release ();
  1147.     }
  1148.     else if (event.Mask & (2 | 4))    {    /* if left down*/
  1149.         rc='Y';
  1150.         wait4release ();
  1151.     }
  1152.     return (rc);
  1153. }
  1154.  
  1155.  
  1156. /***    wait4release - wait until mouse buttons are up before returning
  1157.  *
  1158.  *    Entry:    Mouse = mouse handle or 0 if no mouse
  1159.  */
  1160. void
  1161. wait4release () {
  1162.     struct EventInfo event;
  1163.     unsigned type = 0;        /* if OS/2, wait for events */
  1164.  
  1165.     event.Mask=2;             /* force 1st read */
  1166.     if (Mouse)            /* loop till no button events */
  1167.         while ((event.Mask & (2|4|8|16)) || event.Time==0)
  1168.             MOUREADEVENTQUE ((struct EventInfo far *) &event,
  1169.                      (unsigned far *) &type, Mouse);
  1170. }
  1171.  
  1172.  
  1173. /***    exitlife - exit program, resetting original screen mode
  1174.  *
  1175.  *    Called by a Ctrl-C
  1176.  *
  1177.  *    Entry:    Savemode contains original screen mode info
  1178.  *
  1179.  *    Exit:    None
  1180.  */
  1181. void far pascal
  1182. exitlife() {
  1183.         VIOSETMODE ((struct ModeData far *) &Savemode, 0); /* restore*/
  1184.         VIOSCROLLUP (0,0,-1,-1,-1, (char far *) Cell,0);   /* cls */
  1185.         DOSEXIT (1, 0);     /* exit all threads */
  1186. }
  1187. 
  1188.