|
|
1.1 root 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:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.