|
|
Microsoft OS/2 SDK 03-01-1988
/***
* Title:
*
* LIFE - An OS/2 Game of Life
*
* Created by Microsoft Corp. 1987
*
* Description:
*
* This is a OS/2 implementation of the game of Life. It is designed
* to be bound to allow it to operate in both the protect mode of OS/2
* and in earlier MS-DOS versions. The program uses a mouse if one
* is installed, but one is not needed.
*
* Keyboard commands: B(lank) - redraw screen
* G(o) - step through generations until a key or
* button is hit
* H(alt) - freeze at current generation
* Q(uit) - exit program
* R(ead) - read a game board from the disk
* S(step) - advance a single generation
* W(rite) - write the current board to disk
* D(own-speed) - slow down GOs
* U(p-speed) - speed up GOs
*
*/
#define INCL_DOS /* includes all OS/2 calls in */
#define INCL_SUB /* bsedos.h and bsesub.h */
#include <os2def.h> /* definitions for OS/2 calls and */
#include <bse.h> /* associated data structures */
/* defines */
#define SCR_WID 80 /* screen buffer row width in bytes */
#define SIGNATURE 0x5342 /* 1st word of all files from this */
/* these defines are all coordinates to put various messsages */
#define COM_ROW 23 /* alpha row for command line */
#define COM_COL 0 /* column for same */
#define PROMPT_ROW 24 /* alpha row for prompts */
#define PROMPT_COL 0 /* column for same */
#define GEN_COL 13 /* column to put generation number on*/
#define FILE_COL 37 /* column to put filespec */
/* misc. defines */
#define FONT_ROW 8 /* pixel rows in the font */
#define BIT 8 /* number of bits in a byte */
#define BYTEINCOLS 8 /* no. of interal columns in a byte */
/**** all functions in this file ******/
int main(); /* setup and master loop for life game*/
void blank(); /* blank internal and screen grid*/
void go(); /* steps through generations until a key is pressed*/
void halt(); /* does nothing */
void quit(); /* exit after prompting for certainty*/
void diskread(); /* read new grid from disk*/
int step(); /* advance one generation on screen and internally*/
void diskwrite(); /* write internal grid to disk*/
void up(); /* speeds up GO if possible */
void down(); /* slows down GO */
void beep(); /* beep the speaker for bad commands*/
void showingrid(); /* display internal grid on screen*/
int getfilespec(); /* get a file name from the user*/
void putgen(); /* puts the current generation counter on prompt line*/
void fill(); /* fill in a grid cell on the screen and internally*/
void remove(); /* clear a grid cell on the screen and internal grids*/
int kbdreadeventque(); /* simulates MouReadEventQue with the keyboard*/
void xorptr(); /* xors the mouse ptr on the screen*/
void gputs(); /* put a character string on the graphics screen*/
void gputchar(); /* put a character on the graphics screen*/
void anerror(); /* error handler*/
char gethit(); /* waits for a key hit or mouse button hit*/
void wait4release(); /* wait until mouse buttons are up before returning*/
void far pascal exitlife();/* exit program, resetting original screen mode*/
/* global data */
/* global variables for the internal and screen grids */
int InRow = 45; /* rows & columns of cells w/ default*/
int InCol = 80; /* in internal grid. Col must be /8*/
int ScrRow = 45; /* rows & column on screen grid */
int ScrCol = 79;
int SizeScrRow = 4; /* pixel rows per screen grid row */
int SizeScrCol = 8; /* pixels cols per screen grid column*/
PSZ InGrid; /* pointer to internal grid, a simple
* bit map of the space and cells */
PSZ InGrid2; /* pointer for secondary map used
* to calculate next generation */
/* screen related global data */
static VIOMODEINFO Highres = {12, 3, 1, 80, 25, 640, 200}; /* 640x200 b/w*/
VIOMODEINFO Savemode = {12}; /* place to save old screen mode */
unsigned ScrSeg; /* screen buffer segment address */
char Cell[]={32,7}; /* blank for clear screen */
unsigned OddPage=0x2000; /* offset on Cga of odd row bit plane*/
/* should be 0 on non-Cga modes */
unsigned Cga=2; /* if using a Cga, screen adresses on
* must be divided by 2 because of
* the odd and even row bit planes
* if using non-Cga mode, should be 1*/
/* mouse related global data */
HMOU Mouse = 0; /* handle if mouse is present, else */
int MouBoundRow = COM_ROW*FONT_ROW+6; /* last row mouse is allowed on */
int MouBoundCol = 631; /* last column mouse is allowed on */
/* misc. global data */
char Filespec[79-FILE_COL]; /* file name holder (allows default)*/
unsigned Generation; /* current generation count */
char Logo[] = " \
Microsoft LIFE"; /* logo used to clear prompt line */
int Slow=0; /* number of slow-down loops for Go */
/* Data for commands. Column of command on COM_ROW for printed name,
* name for printing on command line, flag if not 0 then the command
* can be executed within a Go command without stopping execution,
* and the function that does the command. */
struct Commands {
int Col;
NPSZ Name;
char GoAble;
void (*Fun)();
} ComLine[] = { 0, "Command:", 0, beep,
10, "Blank", 0, blank,
16, "Go", 0, go,
19, "Halt", 0, halt,
24, "Quit", 0, quit,
29, "Read", 0, diskread,
34, "Step", 0, step,
39, "Write", 0, diskwrite,
46, "Down-speed", 1, down,
57, "Up-speed", 1, 0,
67, 0, 0
};
/*** main - setup and master loop for life game
*
* First this sets the screen mode, gets the address of the screen buffer,
* sets the ctrl-C handle to a routine to reset the screen mode on exit,
* and allocates the data structures for the internal representation of
* the life grid. The main loop of the program continuously reads from
* the keyboard and the mouse and translates the key or location of the
* mouse with the ComLine structure into a command to execute.
*/
main () {
KBDKEYINFO kbd; /* return for KBD call */
VIOPHYSBUF get_phys; /* return for GetPhysBufData */
USHORT throwaway; /* for returns I don't use */
int i; /* just a counter */
unsigned far *ptr; /*used to clear internal grid*/
/* data for the mouse (or keyboard emulator) */
int ptrrow = 86; /* current pointer position */
int ptrcol = 316;
USHORT status = 0x100; /* for MouSetDevStatus */
USHORT type=0; /* for no waits on mouse read */
MOUEVENTINFO event; /* return for mouse reads */
PTRLOC loc; /* data for MouSetPtrPos */
/* try to open and initialize mouse. If none installed, Mouse will
* stay 0 to show keyboard emulation must be used */
if (!MouOpen (0L, &Mouse)) /* get handle */
/* mouse is here and well, so initialize it */
MouSetDevStatus (&status, Mouse);
/* get current screen mode and save it for restoring on exit */
if (VioGetMode (&Savemode, 0)) {
printf ("Error setting screen mode, exitting\n");
DosExit (EXIT_PROCESS, 0); /* exit if error */
}
/* set screen mode to 640x200 b/w (Cga high resolution */
if (VioSetMode (&Highres, 0)) {
printf ("Error setting screen mode, exitting\n");
DosExit (EXIT_PROCESS, 0); /* exit if error */
}
/* set mouse pointer to middle of screen */
if (Mouse) {
loc.row=ptrrow;
loc.col=ptrcol;
MouSetPtrPos (&loc, Mouse);
}
/* get screen buffer segment */
get_phys.pBuf=(PBYTE)0xb8000L;
get_phys.cb=16*1024L;
if (VioGetPhysBuf (&get_phys, 0)) {
/* if error here, restore screen and exit */
VioSetMode (&Savemode, 0);
printf ("Error accessing screen memory, exitting\n");
DosExit (EXIT_PROCESS, 0);
}
ScrSeg = get_phys.asel[0]; /* store in global */
/* set ctrl-C to quit() to reset screen at even a break */
DosSetSigHandler (exitlife, (PFNSIGHANDLER FAR *)&throwaway,
&throwaway, 2, 1);
/* allocate a segment to hold internal grid representation */
if (DosAllocSeg (InRow*InCol/BYTEINCOLS, (PSEL) &InGrid, 0))
/* from here on use anerror() to report errors */
anerror ("Error allocating memory", 1);
(long) InGrid *= 0x10000L; /* move selector to high word*/
/* allocate a segment to hold second buffer used in stepping */
if (DosAllocSeg(InRow*InCol/BYTEINCOLS, (PSEL) &InGrid2, 0))
anerror ("Error allocating memory", 1);
(long) InGrid2 *= 0x10000L; /* move selector to high word*/
/* blank internal grid and draw screen */
for (i=InRow*InCol/16, ptr=(unsigned far *) InGrid ; i--;)
*ptr++ = 0;
showingrid ();
xorptr (ptrrow, ptrcol); /* show cursor */
/* main input loop, polling mouse and keyboard */
while (1) {
/* try to get mouse event from keyboard or mouse */
kbd.chChar=0; /* clear key buffer residue */
kbdreadeventque (&event, ptrrow, ptrcol);
if (Mouse)
MouReadEventQue (&event, &type, Mouse);
/* if any mouse-like events, do this */
if (event.fs) {
xorptr (ptrrow, ptrcol); /* hide cursor */
if (event.fs & 1+2+8) { /* if any motion */
/* update position, if off screen grid,ptr=max or min*/
ptrrow = event.row;
ptrcol = event.col;
if (ptrrow > MouBoundRow)
ptrrow = MouBoundRow;
if (ptrcol > MouBoundCol)
ptrcol = MouBoundCol;
}
/* if a button was hit */
if (event.fs & (2|4|8|16)) {
if (ptrrow < COM_ROW*FONT_ROW-SizeScrRow) { /* if on grid */
Generation=0; /* reset Gen if */
putgen(); /* grid modified*/
if (event.fs & (2 | 4)) /* if left down*/
fill (ptrcol/SizeScrCol, ptrrow/SizeScrRow);
else if (event.fs & (8 | 16)) /* if right */
remove (ptrcol/SizeScrCol, ptrrow/SizeScrRow);
}
else { /* on command line*/
/* if pointing at command word, execute it, highlighting */
for(i=0; ComLine[i].Fun != 0; i++)
if (ptrcol/BIT >= ComLine[i].Col &&
ptrcol/BIT < ComLine[i+1].Col-1) {
gputs(ComLine[i].Name,COM_ROW,ComLine[i].Col,0xff);
wait4release (); /* execute on release*/
(*(ComLine[i].Fun))();
if (ComLine[i].Fun) /*restore name if not removed*/
gputs(ComLine[i].Name,COM_ROW,ComLine[i].Col,0);
}
}
}
xorptr (ptrrow, ptrcol); /* show cursor */
}
/* check keys, no wait */
KbdPeek (&kbd, 0);
if (kbd.chChar) { /* get key only if not regular char */
KbdCharIn (&kbd, 1, 0); /*get command*/
xorptr (ptrrow, ptrcol); /* avoid overwrites */
/* walk command structure and execute function, highlighting */
for(i=0; ComLine[i].Fun != 0; i++)
if ((kbd.chChar & 0xdf) == *(ComLine[i].Name)) {
gputs(ComLine[i].Name,COM_ROW,ComLine[i].Col,0xff);
(*(ComLine[i].Fun))();
if (ComLine[i].Fun) /*restore name if not removed*/
gputs(ComLine[i].Name,COM_ROW,ComLine[i].Col,0);
}
xorptr (ptrrow, ptrcol); /* restore pointer */
}
}
}
/*** blank - blank internal and screen grid
*
* Sets the internal grid to all 0's and calls draw_grid() to
* clear the screen and put up a screen grid. blank() then draws
* the command line and the program logo.
*
* Entry: ScrSeg = current screen segment
* InGrid points to internal grid defined by InCol and InRow
* Logo points to program logo that also clears the prompt line
*
* Exit: Generation = 0
*
* Calls: gputs(), putgen(), draw_grid [assembler routine]
*/
void
blank (){
unsigned far *ptr;
int i;
gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
gputs ("Are you sure?", PROMPT_ROW, PROMPT_COL, 0);
if (gethit () == 'Y') {
/* if yes, blank screen and internal grid */
draw_grid (); /* draw blank grid on screen */
for (i=InRow*InCol/16, ptr=(unsigned far *) InGrid ; i--;)
*ptr++ = 0; /* blank internal grid */
for (i=0; ComLine[i].Fun != 0; i++) /* print command line */
gputs (ComLine[i].Name, COM_ROW, ComLine[i].Col, 0);
Generation=0; /* reset generation count */
}
gputs (Logo, PROMPT_ROW, PROMPT_COL, 0); /* put on logo */
putgen (); /* initial generation message*/
}
/*** go - steps through generations until a key is pressed
*
* Calls step() until a key is hit, which terminates the loop
* and the key is left on the buffer for main() to process
* as a command.
* step()'s return is checked to see if this program has just
* moved from background to foreground, in which case the
* the screen will need manual updating with showingrid().
* If the D or U keys are hit, down() or up() is executed.
*
* Entry: none
*
* Exit: None
*
* Calls: step(), showingrid()
*/
void
go () {
KBDKEYINFO kbd; /* return from KBD call */
int background=0; /* 0 if forground, 1 backgrd */
MOUEVENTINFO event; /* return from mouse read */
USHORT type=0; /* for no waits on mouse read*/
MOUQUEINFO num; /* return for GetNumQueEl */
unsigned x,y; /* for slowing down loops */
/* step until key or button is hit, executing GoAble commands */
kbd.chScan=0;
event.fs=0;
while (!kbd.chScan) {
if (step ())
background=1;
else if (background==1) {
background=0;
showingrid();
}
if (Mouse) {
/* read all events on que */
MouGetNumQueEl (&num, Mouse);
while (num.cEvents--) {
MouReadEventQue (&event, &type, Mouse);
if (event.fs & (2|4|8|16)) { /* leave if mouse hit*/
wait4release();
return;
}
}
}
/* check keys, no wait. Execute if GoAble command */
KbdPeek (&kbd, 0);
if (kbd.chScan) {
/* walk command structure and execute function, highlighting */
for(x=0; ComLine[x].Fun != 0; x++)
if ((kbd.chChar & 0xdf) == *(ComLine[x].Name)
&& ComLine[x].GoAble) {
gputs(ComLine[x].Name,COM_ROW,ComLine[x].Col,0xff);
KbdCharIn (&kbd, 1, 0);
kbd.chScan=0;
(*(ComLine[x].Fun))();
if (ComLine[x].Fun) /*restore name if not removed*/
gputs(ComLine[x].Name,COM_ROW,ComLine[x].Col,0);
}
}
if (Slow) /* slow down if requested */
for (x=Slow; x--;)
for (y=50000; y--;);
}
}
/*** halt - does nothing
*/
void
halt () {
int i;
for (i=30000; i--;); /* short pause */
}
/*** quit - exit after prompting for certainty
*
* Entry: None
*
* Exit: None
*/
void
quit () {
gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
gputs ("Are you sure?", PROMPT_ROW, PROMPT_COL, 0);
if (gethit () == 'Y')
exitlife (); /* if yes, do quit routine */
gputs (Logo, PROMPT_ROW, PROMPT_COL, 0); /* else, continue */
putgen(); /* put back gen count*/
}
/*** diskread - read new grid from disk
*
* getfilespec() is called to get a file name from the user which
* is put in Filespec.
* Then an internal grid is read from the disk which is in the form:
* WORD SIGNATURE ; Life file signature
* WORD rows
* WORD columns
* WORD generation
* (rows*columns/BYTEINCOLS) BYTES of the bit mapped grid
*
* InGrid is ReAlloced to the size of the newly read grid and
* InCol, InRow, and Generation are all set the values contained
* in the file read.
*
* Entry: InGrid points to internal grid defined by InCol and InRow
*
* Exit: InGrid is ReAlloced to size of newly read grid.
* InCol, InRow, and Generation are all set the values
* contained in the file read.
* d
* Calls: getfilespec ();
*/
void
diskread () {
HFILE handle; /* file handle */
USHORT action; /* return for file calls */
unsigned signature; /* life file signature word */
/* get file name into Filespec */
if (getfilespec ())
return; /* return if user hit ESC */
/* open file, fail if it doesn't exist */
if (DosOpen ((PSZ) Filespec, &handle,
&action, 0L, 0, 0x01, 0x42, 0L)) {
anerror ("Can't open file", 0);
Filespec[0]=0; /* clear bad file name */
return;
}
/* read appropriate from file */
if (DosRead (handle, (PSZ) &signature, sizeof (signature),
&action) || action != sizeof (signature))
anerror ("Error writing to file", 0);
else if (signature != SIGNATURE) {
anerror ("Not a life file", 0);
if (DosClose (handle))
anerror ("Error closing file", 0);
return;
}
else if (DosRead (handle, (PSZ) &InRow, sizeof (InRow),
&action) || action != sizeof (InRow))
anerror ("Error reading from file", 0);
else if (DosRead (handle, (PSZ) &InCol, sizeof (InCol),
&action) || action != sizeof (InCol))
anerror ("Error reading from file", 0);
else if (DosRead (handle, (PSZ) &Generation,
sizeof (Generation), &action) ||
action != sizeof (Generation))
anerror ("Error reading from file", 0);
/* change size of *InGrid to match saved pattern */
else if (DosReallocSeg (InRow*InCol/BYTEINCOLS, (unsigned)
((long) InGrid/0x10000L)))
anerror ("Error allocating memory", 0);
else if (DosRead (handle, InGrid, InRow*InCol/BYTEINCOLS,
&action) || action != InRow*InCol/BYTEINCOLS)
anerror ("Error reading from file", 0);
/* show the newly loaded pattern on the screen */
showingrid ();
/* close file */
if (DosClose (handle))
anerror ("Error closing file", 0);
}
/*** step - advance one generation on screen and internally
*
* Uses dostep to advance the current internal and screen grid
* to the next generation. dostep() returns 1 if the screen
* was not available and thus no update was made, this allows
* the program to continue to execute in the background.
* The return from dostep() is passed back to step()'s caller.
*
* Entry: None
*
* Exit: Returns 1 if executing in background and there was thus
* no screen update.
* Else, returns 0 if in forground
* Generation is incremented.
*
* Calls: putgen(), dostep [assembler routine]
*/
int
step() {
int rc; /* return code from dostep */
/* do the stepping using an assembler routine for speed */
rc=dostep(InGrid, InGrid2, InRow, InCol); /* do the step */
Generation++; /* advance the count */
putgen(); /* and display gen */
return(rc);
}
/*** diskwrite - write internal grid to disk
*
* getfilespec() is called to get a file name from the user which
* is put in Filespec.
* Then the internal grid is saved to disk in the form:
* WORD SIGNATURE ; Life file signature
* WORD rows
* WORD columns
* WORD generation
* (rows*columns/BYTEINCOLS) BYTES of the bit mapped grid
*
* Entry: InGrid points to internal grid defined by InCol and InRow
*
* Exit: None
*
* Calls: getfilespec ();
*/
void
diskwrite () {
HFILE handle; /* file handle */
USHORT action; /* return for file calls */
unsigned signature=SIGNATURE; /* life file signature word */
/* get file name into Filespec */
if (getfilespec ())
return; /* return if user hit ESC */
/* open file and truncate or create it if it doesn't exist */
if (DosOpen ((PSZ) Filespec, &handle,
&action, 0L, 0, 0x12, 0x42, 0L)) {
anerror ("Can't open file", 0);
Filespec[0]=0; /* clear bad file name */
return;
}
/* write appropriate info to file */
if (DosWrite (handle, (PSZ) &signature, sizeof (signature),
&action) || action != sizeof (signature))
anerror ("Error writing to file", 0);
else if (DosWrite (handle, (PSZ) &InRow, sizeof (InRow),
&action) || action != sizeof (InRow))
anerror ("Error writing to file", 0);
else if (DosWrite (handle, (PSZ) &InCol, sizeof (InCol),
&action) || action != sizeof (InCol))
anerror ("Error writing to file", 0);
else if (DosWrite (handle, (PSZ) &Generation,
sizeof (Generation), &action) ||
action != sizeof (Generation))
anerror ("Error writing to file", 0);
else if (DosWrite (handle, InGrid, InRow*InCol/BYTEINCOLS,
&action) || action != InRow*InCol/BYTEINCOLS)
anerror ("Error writing to file", 0);
/* close file */
if (DosClose (handle))
anerror ("Error closing file", 0);
}
/*** up - speeds up GOs (decrement Slow)
*/
void
up() {
int i;
if (Slow)
Slow--;
if (!Slow) {
/* if at top speed take out message */
for(i=0; *(ComLine[i].Name) != 'U'; i++);
ComLine[i].Fun=0;
gputs (" ", COM_ROW, ComLine[i].Col, 0);
}
for (i=30000; i--;); /* short pause */
}
/*** down - slows down GOs (increment Slow)
*/
void
down() {
int i;
Slow++;
for (i=30000; i--;); /* short pause */
if (Slow){ /* if at top speed, display UP */
for(i=0; *(ComLine[i].Name) != 'U'; i++);
ComLine[i].Fun=up;
for (i=0; ComLine[i].Fun != 0; i++) /* print command line */
gputs (ComLine[i].Name, COM_ROW, ComLine[i].Col, 0);
}
}
/*** beep - beep the speaker for bad commands
*/
void
beep () {
DosBeep (500, 50);
}
/*** showingrid - display internal grid on screen
*
* Clears the screen using the draw_grid routine and then
* puts the internal grid on the screen using fill() so as to
* not be resolution dependent.
*
* Entry: InGrid points to internal grid defined by InCol and InRow.
* ScrSeg points to the screen buffer.
* ScrRow and ScrCol describe the dimensions of the screen grid.
* Logo points to program logo that also clears the prompt line
*
* Exit: None
*
* Calls: fill(), putgen(), gputs(), draw_grid [assembler routine]
*/
void
showingrid () {
int x, y; /* cell coordinates for loop */
BYTE retcode; /* for VIOSCRLOCK */
/* prepare blank screen so we only have to fill in cells that are on */
draw_grid (); /* draw blank grid on screen */
/* print command line */
for (x=0; ComLine[x].Fun != 0; x++)
gputs (ComLine[x].Name, COM_ROW, ComLine[x].Col, 0);
gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
putgen ();
/* loop through the screen sized area of the grid */
VioScrLock (1, &retcode, 0); /* get screen access */
for (y=ScrRow; y--;)
for (x=ScrCol; x--;)
/* if the internal cell is on, turn on the screen one*/
if (*(InGrid + (x + y*InCol)/BYTEINCOLS) & (0x80 >> (x&7)))
fill (x, y);
VioScrUnLock (0);
}
/*** getfilespec - get a file name from the user
*
* Prompts user for a file name for use with diskread() or diskwrite().
* The last file name used is shown on the screen as a default
* and the user can hit enter (or left mouse button) to specify the
* default name. If any other key is pressed, this routine takes the
* input until an enter and returns in Filespec. If ESC is hit any time
* during typeing, the input will be aborted and the buffer cleared.
*
* Entry: Filespec points to default file name
* Logo points to program logo that also clears the prompt line
*
* Exit: Filespec points to new file name
* returns 0x1b if ESC was hit during entry, else 0
*
* Calls: gputs(), putgen()
*/
int
getfilespec () {
int c=0; /* index for Filespec[] */
KBDKEYINFO kbd; /* return for KBD call */
unsigned space=0x0020; /* space character for print */
MOUEVENTINFO event; /* return for mouse */
USHORT type=0; /* mouse reads w/no wait */
kbd.chScan=0;
event.fs=0;
/* put up default file name if one exists */
if (Filespec[0]) {
gputs ("Type file name or enter for default: ", PROMPT_ROW,
PROMPT_COL,0);
gputs (Filespec, PROMPT_ROW, FILE_COL, 0);
/* wait for first key or button to see if they accept default */
while (!kbd.chScan && !(event.fs & (2|4|8|16))) {
if (Mouse)
MouReadEventQue (&event, &type, Mouse);
KbdPeek (&kbd, 0);
}
}
if (event.fs & (8 | 16)) { /* if right button hit, same as ESC */
kbd.chChar = 0x1b;
wait4release();
}
if (event.fs & (2 | 4)) { /* if left button hit, same as \r */
wait4release();
gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
putgen();
return (0);
}
if (kbd.chChar == 0x1b) { /* if ESC, return */
gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
putgen();
return (0x1b); /* return escape */
}
gputs(Logo, PROMPT_ROW, PROMPT_COL, 0);
gputs("Type file name, followed by enter: ",PROMPT_ROW,PROMPT_COL,0);
/* read chars and print them until \r */
while (!(KbdCharIn (&kbd, 0, 0)) &&
kbd.chChar != '\r') {
if (kbd.chChar == '\b') { /* if backspace, backspace */
if (c > 0) { /* prevent underflow*/
Filespec[--c]=0;
/* erase backed-out character*/
gputs (&space, PROMPT_ROW, FILE_COL+c,0);
}
else /* if overflow, yell */
beep();
}
else if (kbd.chChar == 0x1b) { /* if escape was hit */
Filespec[0]=0; /* blank name */
gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
putgen();
return (0x1b); /* return escape */
}
else if (kbd.chChar <46 || kbd.chChar > 122)
beep(); /* if not char, beep */
else
if (c < sizeof (Filespec)) { /* prevent overflow */
Filespec[c]=kbd.chChar; /* add to string */
Filespec[++c]=0; /* null terminal */
}
else /* if overflow, yell */
beep();
gputs (Filespec, PROMPT_ROW, FILE_COL, 0); /* print new name */
}
/* return generation count to screen */
gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
putgen();
return (0);
}
/*** putgen - puts the current generation counter on prompt line
*
* Entry: Generation = current generation number
*
* Exit: None
*
* Calls: gputs()
*/
void
putgen ()
{
char num[8]; /* number string to print */
int i, g;
gputs ("Generation: ", PROMPT_ROW, PROMPT_COL,0);/* gen label*/
if (Generation == 0)
gputs ("0", PROMPT_ROW,GEN_COL,0);/*if special case need this*/
else { /* not 0, do this: */
for (g=Generation, i=sizeof(num)-1; i-- >= 0 && g; g/=10)
num[i]=(g%10)+'0'; /* convert int to string */
num[sizeof(num)-1]= 0; /* zero terminated for gputs */
gputs (&num[i+1], PROMPT_ROW, GEN_COL,0); /* print number */
}
}
/*** fill - fill in a grid cell on the screen and internally
*
* fill (x, y)
*
* Entry: x = grid horizontal coordinate
* y = grid vertical coordinate
* ScrSeg = screen buffer segment
* VioScrLock is active
* InGrid points to internal grid defined by InCol and InRow
* ScrRow and ScrCol define limits of screen grid
* Currently, must use 79x25 grid drawn by draw_grid()
* Currently, screen must be in 640x200 b/w mode
*
* Exit: None
*/
void
fill (x, y)
int x,y;
{
char far *gridptr;
/* only do if within screen grid limits */
if (x < ScrCol && y < ScrRow) {
/* set gridptr to & in screen buff of cell to fill */
(long) gridptr = ScrSeg*0x10000; /* fill in address */
(unsigned) gridptr = (x*SizeScrCol/BIT)+(y*SizeScrRow/Cga*SCR_WID);
/* fill in screen cell */
*(gridptr+OddPage)=0x7f;
*(gridptr+SCR_WID)=0x7f;
*(gridptr+OddPage+SCR_WID)=0x7f;
}
/* fill in internal cell*/
/*InGrid= &internal grid of x,y*/
gridptr = InGrid + x/BYTEINCOLS + y*InCol/BYTEINCOLS;
*gridptr = *gridptr | (0x80 >> (x & 7)); /* fill bit */
}
/*** remove - clear a grid cell on the 79x45 grid screen and internally
*
* remove (x, y)
*
* Entry: x = grid horizontal coordinate
* y = grid vertical coordinate
* ScrSeg = screen buffer segment
* VioScrLock is active
* InGrid points to internal grid defined by InCol and InRow
* ScrRow and ScrCol define limits of screen grid
* Currently, must use 79x25 grid drawn by draw_grid()
* Currently, screen must be in 640x200 b/w mode
*
* Exit: None
*/
void
remove (x, y)
int x,y;
{
char far *gridptr;
/* only do if within screen grid limits */
if (x < ScrCol && y < ScrRow) {
/* set gridptr to & in screen buff of cell to fill */
(long) gridptr = ScrSeg*0x10000; /* fill in address */
(unsigned) gridptr = (x*SizeScrCol/BIT)+(y*SizeScrRow/Cga*SCR_WID);
/* fill in screen cell */
*(gridptr+OddPage)=0x80;
*(gridptr+SCR_WID)=0x80;
*(gridptr+OddPage+SCR_WID)=0x80;
}
/* remove internal cell*/
/*InGrid= &internal grid of x,y*/
gridptr = InGrid + x/BYTEINCOLS + y*InCol/BYTEINCOLS;
*gridptr = *gridptr & ~((0x80 >> (x & 7))); /* remove bit */
}
/*** kbdreadeventque - simulates MouReadEventQue with the keyboard
*
* F9 key is responded to as left button, F10 key as right.
* Pointer location is advanced by SizeScrCol or SizeScrRow according
* to direction of direction key hits.
* Does not wait for input, instead returns 0 event fs.
* Does not check MouDevStatus, instead always returns pixel coordinates
* Event buffer is filled according to MouReadEventQue
*
* Entry: event - structure to return data
* ptrrow - current point row
* ptrcol - current pointer column
*
* Exit: None
* *event contains any event that may have occurred
*/
int
kbdreadeventque (event, ptrrow, ptrcol)
MOUEVENTINFO *event;
int ptrrow, ptrcol;
{
KBDKEYINFO kbd; /* return for KBD call */
kbd.chChar=0; /* clear data struct residue*/
kbd.chScan=0;
/* peek at key buffer */
KbdPeek (&kbd, 0);
/* if it is an extended key code, process it */
if (kbd.chChar == 0 && kbd.chScan) {
KbdCharIn (&kbd, 1, 0); /* take char */
event->row=ptrrow; /* initial position */
event->col=ptrcol;
switch (kbd.chScan) { /* check second byte of code */
case 0x48: /* cursor up */
event->row -= SizeScrRow;
event->fs=1;
break;
case 0x50: /* cursor down */
event->row += SizeScrRow;
event->fs=1;
break;
case 0x4b: /* cursor left */
event->col -= SizeScrCol;
event->fs=1;
break;
case 0x4d: /* cursor right */
event->col += SizeScrCol;
event->fs=1;
break;
case 0x43: /* left button (F9) */
event->fs=4;
break;
case 0x52: /* left button (INS) */
event->fs=4;
break;
case 0x44: /* right button (F10) */
event->fs=16;
break;
case 0x53: /* right button (DEL) */
event->fs=16;
break;
default: /* anything else is no good */
event->fs=0;
break;
}
}
else
event->fs=0; /* if no extended key */
}
/*** xorptr - xors the mouse ptr on the screen
*
* xorptr (ptrrow, ptrcol)
*
* Entry: ptrrow = pixel row on screen
* ptrcol = pixel column on screen
* ScrSeg = screen buffer segment
*
* Exit: None
*/
void
xorptr (ptrrow, ptrcol)
unsigned ptrrow, ptrcol;
{
char far *gridptr;
unsigned mask=0; /* bit mask for pointer shape*/
int x=8; /* count for rows of pointer */
BYTE retcode; /* return from VioScrLock */
/* set gridptr to & in screen buff to xor */
ptrrow++; /* 1st move ptr to next row*/
(long) gridptr = ScrSeg*0x10000; /* fill in segment address */
(unsigned) gridptr = ptrcol/x+(ptrrow/Cga*SCR_WID);/* fill offset*/
/* fill in screen pointer */
VioScrLock (1, &retcode, 0); /* get screen */
while (x--) {
mask = mask >> 1; /* make mask thicker */
mask |= 0x8000; /* at bottom */
if (ptrrow & 1 && Cga == 2) /* if odd bit plane */
gridptr += OddPage;
/* xor mask to scr, being sure to reverse bytes in word */
*(gridptr+1) ^= (char) (mask >> (ptrcol & 7));
*(gridptr) ^= (char) ((mask >> (ptrcol & 7)) / 256);
if (ptrrow & 1 && Cga == 2) /* if odd bit plane */
gridptr -= OddPage; /* plane, next row*/
ptrrow++; /* next row */
if (!(ptrrow & 1) && Cga == 2) /*only advance on odd*/
gridptr += SCR_WID; /* row w/o CGA */
}
VioScrUnLock (0); /* give back screen */
}
/*** gputs - put a character string on the graphics screen
*
* gputs (string, row, col)
*
* Draws the passed asciiz string on the screen unless the screen
* is not available (i.e., if running in background screen group)
* in which case this routine does nothing so as to not hold up
* the program.
*
* Entry: string - address of asciiz string to print
* row - alpha row to print at
* col - alpha col to print at
* mask - xor'ed with bytes of character as printed
*
* Exit: None
*
* Calls: gputchar()
*
* Warning: Prints graphics characters for ascii control codes (<32).
* Does not wrap-around at line end.
*/
void
gputs (string, row, col, mask)
char *string;
unsigned col, row;
char mask;
{
static VIOFONTINFO fontdata = {14,1,FONT_ROW,8,0L,0};/*for GetFont*/
char c;
BYTE retcode; /* for VioScrLock */
char far *scr_addr; /* address in screen buffer
to print at */
/* try lock without waiting for availability */
VioScrLock (0, &retcode, 0); /* get screen access */
/* do display only if screen available now */
if (retcode == 0) {
/* get address of font table if haven't yet */
if (!fontdata.pbData)
VioGetFont (&fontdata, 0);
(long) scr_addr = ScrSeg*0x10000; /* fill in segment address*/
(unsigned)scr_addr = col+(row*SCR_WID*(FONT_ROW/Cga));/*offset*/
while ((c=*(string++)) != 0) /* print each char */
gputchar(scr_addr++,
(ULONG)fontdata.pbData+c*FONT_ROW,mask);
VioScrUnLock (0); /* give up screen */
}
}
/*** gputchar - put a character on the graphics screen
*
* gputchar (scr_addr, font_addr, mask)
*
* Entry: scr_addr - far address in screen buffer to place character
* must be on even Cga bit plane
* font_addr - far address in font table bit pattern
* mask - xor'ed with each byte put on screen
* VioScrLock is active
*
* Exit: None
*/
void
gputchar(scr_addr, font_addr, mask)
char far *scr_addr;
char far *font_addr;
char mask;
{
int i=FONT_ROW; /* count to draw whole character */
while (i--) {
if (i & 1 && Cga == 2) /* if odd bit plane */
scr_addr += OddPage-SCR_WID;
*scr_addr = (*(font_addr++) ^ mask);/* put byte on even plane*/
if (i & 1 && Cga == 2) /* if odd bit plane */
scr_addr -= OddPage;
scr_addr+=SCR_WID; /* move down a line */
}
}
/*** anerror - error handler
*
* anerror (message, fatal);
*
* Prints passed error message and waits for a key hit to allow
* user to read it. If fatal flag is 0, the routine then returns,
* otherwise the routine restores the original screen mode, clears
* the screen, and exits.
*
* Entry: message - string to print describing error
* fatal - flag, if == 0, this procedure prompts for any key hit
* then returns. If != 0, error is fatal & program exits.
* Logo points to program logo that also clears the prompt line
*
* Exit: None
*
* Calls: gputs(), putgen()
*/
void
anerror (message, fatal)
char message[];
int fatal;
{
KBDKEYINFO kbd; /* return for KBD call */
gputs (Logo, PROMPT_ROW, PROMPT_COL,0);
gputs (message, PROMPT_ROW, PROMPT_COL, 0);/* print passed message */
if (fatal) {
gputs(": Press any key to exit",PROMPT_ROW,strlen(message),0);
gethit ();
exitlife();
}
else {
gputs (": Press any key to continue",PROMPT_ROW,
strlen(message), 0);
gethit ();
gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
putgen();
}
}
/*** gethit - waits for a key hit or mouse button hit
*
* Entry: None
*
* Exit: Returns character hit (forced upper case) or 'Y' for left
* mouse button and 'N' for right mouse
*/
char
gethit () {
KBDKEYINFO kbd; /* for keyboard */
MOUEVENTINFO event; /* for mouse */
USHORT type=0;
char rc; /* return code */
/* loop till a hit */
kbd.chScan=0;
event.fs=0;
while (!kbd.chScan && !(event.fs & (2|4|8|16))) {
if (Mouse)
MouReadEventQue (&event, &type, Mouse);
KbdCharIn (&kbd, 1, 0);
}
if (kbd.chScan) /* if key, return char */
rc=kbd.chChar & 0xdf;
else if (event.fs & (8 | 16)) { /* if right */
rc='N';
wait4release ();
}
else if (event.fs & (2 | 4)) { /* if left down*/
rc='Y';
wait4release ();
}
return (rc);
}
/*** wait4release - wait until mouse buttons are up before returning
*
* Entry: Mouse = mouse handle or 0 if no mouse
*/
void
wait4release () {
MOUEVENTINFO event;
USHORT type = 0; /* if OS/2, wait for events */
event.fs=2; /* force 1st read */
if (Mouse) /* loop till no button events */
while ((event.fs & (2|4|8|16)) || event.Time==0)
MouReadEventQue (&event, &type, Mouse);
}
/*** exitlife - exit program, resetting original screen mode
*
* Called by a Ctrl-C
*
* Entry: Savemode contains original screen mode info
*
* Exit: None
*/
void far pascal
exitlife() {
VioSetMode (&Savemode, 0); /* restore*/
VioScrollUp (0,0,-1,-1,-1, (char far *) Cell,0); /* cls */
DosExit (EXIT_PROCESS, 0); /* exit all threads */
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.