|
|
Microsoft OS/2 SDK 03-01-1988
/* mdraw.c - display Mandelbrot set on EGA
*
* Created by Microsoft Corp. 1986
*/
/** Mdraw displays the elements of the Mandelbrot set
*
* z = z**2 + c
*
* where z and c are in the complex plane and z = 0 + 0i for the first
* iteration. The elements of the set are displayed in black and
* elements not in the set are displayed in color. The data to be
* displayed is computed by the program mandel.
*/
/** Call
*
* mdraw file file ...
*
* where
* file a .cnt file created by the computation program mandel
*
* All of the files are read and displayed in order. After all of
* the images are displayed, keystrokes have the following meaning.
* e edit palette registers
* up/down arrow - select register
* right/left arrow - select color
* i reinitialize palette registers
* n advance to next display
* p backup to previous display
* q cleanup and exit
* r decrement palette register ripple
* R increment palette register ripple
* z continous forward zoom through images
* Z continous backward zoom through images
*
* All other characters are ignored.
*/
/*
* The .cnt file has the format
* int number of points along the real axis
* int number of points along the imaginary axis
* int maximum iteration point for each point
* double real coordinate of upper left
* double imaginary coordinate of upper left
* double real coordinate of lower right
* double imaginary coordinate lower rightft
* double increment between points on real axis
* double increment between points on imaginary axis
* long (loop + 1) counters for histogram values
*
* The remainder of the file is the run length encoded scan
* lines encoded as:
* int number of words in scan line encoded as:
* +int actual count value for pixel
* -int int The first value is the run length and
* second value is the run value
*/
#define INCL_SUB
#define INCL_DOSMEMMGR
#define INCL_DOSPROCESS
#define INCL_DOSSEMAPHORES
#include <os2def.h>
#include <stdio.h>
#include <bse.h>
#include "mdraw.h"
/* The routines that are in the IOPL segment must be declared
* as pascal routines. When the IOPL routine is called through
* the intersegment call gate, the parameters are copied from the
* ring 3 user's stack to the ring 2 IOPL stack. The parameters
* must be popped from the IOPL stack (and the ring 3 stack) by the
* ret n instruction of the IOPL routine. Otherwise, the ring 2
* stack is left in an invalid state. For more information, refer
* to the 80286 Programmer's Manual for
* intersegment calls
* parameter passing through call gates
* CALLs to and RETurns from inner protection rings
*
* Note also that the number of words of passed parameters defined in
* each of these functions must exactly match the word counts defined
* in the EXPORTS statements in the mdraw.def linker definitions file.
*/
extern void APIENTRY SetDVideo ();
extern void APIENTRY SetEVideo ();
extern void APIENTRY SetScanClear ();
extern void APIENTRY SetScan (int, int, char *);
extern void APIENTRY SetScanSave (unsigned far *);
extern void APIENTRY SetScanSFont (unsigned);
extern void APIENTRY SetScanRFont (unsigned);
extern void APIENTRY SetScanRestore (unsigned far *);
extern void APIENTRY SetCursor (char far *);
void far mode_wait (void); /* starting address for a thread */
void far redraw_wait (void); /* starting address for another thread */
/* Structures for VIO calls */
struct VIOSTATE {
unsigned length;
unsigned req_type;
unsigned double_defined;
unsigned palette0;
unsigned palette1;
unsigned palette2;
unsigned palette3;
unsigned palette4;
unsigned palette5;
unsigned palette6;
unsigned palette7;
unsigned palette8;
unsigned palette9;
unsigned palette10;
unsigned palette11;
unsigned palette12;
unsigned palette13;
unsigned palette14;
unsigned palette15;
};
VIOCONFIGINFO config; /* Display configuration data */
VIOMODEINFO mode; /* Display mode data */
VIOMODEINFO grmode; /* graphics display mode data */
VIOPHYSBUF physbuf; /* Physical buffer data */
VIOCURSORINFO cursor; /* Cursor data structure */
KBDKEYINFO key; /* Keystroke data */
struct VIOSTATE vstate; /* Set vio state */
char pmand[60] = "mandel.cnt";
FILE *fmand;
char chvalid = FALSE; /* key valid if true */
char cinit[15] = {WHITE, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, IWHITE,
DGRAY, LBLUE, LGREEN, LCYAN, LRED, LMAGENTA, YELLOW};
char firstread = FALSE; /* first character read from keyboard if true */
char flip = FALSE; /* flip display pages if true */
char graphics = FALSE; /* display is in graphics mode if true */
char mapped[MAXREAL]; /* character array holding mapped interation counters */
char initpal[16] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F
};
char palette[16] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F
};
char *screenbuf; /* pointer to buffer to hold text screen */
int *bp; /* pointer to next iteration counter in buf */
int buf[BUFFER]; /* run length encoded iteration counters */
int count = 0; /* number of integers remaining in buf */
long delay = 0; /* ripple delay in milliseconds */
long drawsem = 0; /* RAM semaphore to control drawing */
long hist[MAXLOOP + 1] = {0}; /* histogram counters */
struct image timage = {0}; /* local image descriptor structure */
struct ilist *head = NULL; /* pointer to head of ilist */
struct ilist *curr = NULL; /* pointer to current ilist */
struct ilist *tail = NULL; /* pointer to tail of ilist */
USHORT curcol; /* cursor column position */
USHORT currow; /* cursor row position */
SEL descsel; /* selector for image structure */
SEL fontsel; /* selectors for font saves */
unsigned dpoffset = 0; /* display page offset 0x0000 or 0x8000 */
SEL *psel; /* display memory selector */
USHORT screenlen; /* length of screen buffer */
main (argc, argv)
int argc;
char **argv;
{
int c;
int i;
int temp;
int pal;
UCHAR *stack1, /* stack for mode_wait thread */
*stack2; /* stack for redraw_wait thread */
TID ThreadID;
/* Validate the display configuration. It must be an EGA on an
* EGA adapter with at least 128k of memory.
*/
config.cb = 10;
if ((temp = VioGetConfig (0, &config, 0)) != 0) {
printf ("Unable to get display configuration data - %d\n", temp);
exit (1);
}
if ((config.adapter != 2) || (config.display != 2) ||
config.cbMemory < 0x20000) {
printf ("Display is not EGA on 128k EGA adapter %d %d %ld\n",
config.adapter,config.display, config.cbMemory);
exit (1);
}
if (config.cbMemory == 0x40000)
flip = TRUE;
/* Save information about the current display mode. This includes
* the current mode as known by VIO, the cursor type data, the
* current cursor position and the text screen data.
*/
mode.cb = sizeof (VIOMODEINFO);
if ((temp = VioGetMode (&mode, 0)) != 0) {
printf ("Unable to get display mode data - %d\n", temp);
exit (1);
}
if (VioGetCurType (&cursor, 0) != 0) {
printf ("Unable to get current cursor data\n");
exit (1);
}
if (VioGetCurPos (&currow, &curcol, 0) != 0) {
printf ("Unable to get current cursor position\n");
exit (1);
}
/* Allocate buffer to hold text screen data. Note that the buffer
* size is doubled because of the attribute bytes.
*/
screenlen = mode.col * mode.row * 2;
if ((screenbuf = (char *)malloc (screenlen)) == NULL) {
printf ("Unable to allocate memory for text screen buffer\n");
exit (1);
}
if (VioReadCellStr (screenbuf, &screenlen,
0, 0, 0) != 0) {
printf ("Unable to read text screen data\n");
exit (1);
}
/* Obtain selectors to the display memory */
physbuf.pBuf = (PBYTE)0xa0000;
physbuf.cb = 0x20000;
if ((temp = VioGetPhysBuf (&physbuf, 0)) != 0) {
printf ("Unable to get physical buffer mapping - %d\n", temp);
exit (1);
}
if (DosAllocSeg (0,&fontsel, 0) != 0) {
printf ("Unable to allocate text font save segment\n");
exit (1);
}
/* Allocate memory for separate thread execution */
stack1 = (unsigned char *)malloc (STACKSIZE);
stack2 = (unsigned char *)malloc (STACKSIZE);
if (!stack1 || !stack2) {
printf ("Out of memory\n");
exit (1);
}
stack1 += STACKSIZE;
stack2 += STACKSIZE;
/* create thread to execute the mode wait */
DosCreateThread (mode_wait, &ThreadID, stack1);
/* create thread that will execute the redraw_wait () */
DosCreateThread (redraw_wait, &ThreadID, stack2);
/* Pass pointer to the selectors for display memory and the
* selector to the font save segment to the ring 2 IOPL code.
*/
psel = physbuf.asel;
/* Save the font tables */
SetScanSFont (fontsel);
/* Set the EGA to 640 x 350 graphics mode */
grmode = mode;
grmode.fbType = grmode.fbType | 0x02;
grmode.hres = 640;
grmode.vres = 350;
VioSetMode (&grmode, 0);
DosSemRequest ((HSEM)&drawsem, -1L);
setpalette (palette);
DosSemClear ((HSEM)&drawsem);
/* Set the file name if specified and open the file for reading */
argc--;
argv++;
graphics = TRUE;
printf ("Clearing screen\n");
SetScanClear ();
printf ("Screen cleared\n");
while (argc > 0) {
/* set file name */
strcpy (pmand, *argv);
strcat (pmand, ".cnt");
if ((fmand = fopen (pmand, "rb")) == NULL) {
cleandisplay ();
printf ("Unable to open count file %s\n", pmand);
exit (3);
}
readimage ();
fclose (fmand);
argc--;
argv++;
}
/* Wait for keyboard input to switch display or terminate. This
* thread must delay between checks for keyboard input. Otherwise,
* the code will execute a CPU bound loop and all other threads
* and processes will execute at a reduced rate.
*/
while (TRUE) {
if (nextchar (&c, 1)) {
switch (c) {
case 'e':
/* edit palette registers */
paledit ();
break;
case 'i':
/* reinitialize palette registers */
for (i = 0; i < 16; i++)
palette[i] = initpal[i];
DosSemRequest ((HSEM)&drawsem, -1L);
setpalette (palette);
DosSemClear ((HSEM)&drawsem);
break;
case 'n':
/* advance to next display */
nextimage ();
break;
case 'p':
/* backup to previous display */
previmage ();
break;
case 'q':
/* cleanup and exit */
cleandisplay ();
exit ();
case 'r':
/* decrement palette register ripple */
delay += SCANTIME;
break;
case 'R':
/* increment palette register ripple */
delay -= SCANTIME;
break;
case 'z':
/* continous forward zoom through images */
while (!nextchar (&c, 1))
nextimage ();
ungetchar ();
break;
case 'Z':
/* continous backward zoom through images */
while (!nextchar (&c, 1))
previmage ();
ungetchar ();
break;
default:
/* ignore character */
break;
}
}
/* delay to prevent CPU bound loop */
if (delay == 0)
DosSleep (100L);
else if (delay > 0) {
ripple (RIPPLE_UP);
DosSleep (delay);
}
else {
ripple (RIPPLE_DN);
DosSleep ((long)(-delay));
}
}
}
/** mdisp - display Mandelbrot set
*
* mdisp (im);
*
*/
mdisp (im)
struct image far *im;
{
int i;
int ni; /* scan row counter */
BYTE temp;
int len;
char *pmapped;
VioScrLock (1, &temp, 0);
DosSemRequest ((HSEM)&drawsem, -1L);
SetScanClear ();
DosSemClear ((HSEM)&drawsem);
for (ni = 0; ni < im->lni; ni++) {
pmapped = mapped;
if (!nextvalue (&len))
break;
while ((--len >= 0) && (pmapped - mapped < im->lnr) && nextvalue (&i)) {
if (i >= 0)
*pmapped++ = im->cmap[i];
else {
if (!nextvalue (&temp))
break;
len--;
temp = im->cmap[temp];
for (; (i < 0) && (pmapped - mapped < im->lnr); i++)
*pmapped++ = temp;
}
}
/* The scan line drawing is locked with a RAM semaphore so
* the redraw-wait routine can save and restore the screen
*/
DosSemRequest ((HSEM)&drawsem, -1L);
SetScan (ni, im->lnr, mapped);
DosSemClear ((HSEM)&drawsem);
}
/* Save screen image and unlock the screen */
DosSemRequest ((HSEM)&drawsem, -1L);
SetScanSave (curr->desc->savesel);
DosSemClear ((HSEM)&drawsem);
VioScrUnLock (0);
}
/** nextvalue - return next value from file
*
* flag = nextvalue (ptr);
*
* Entry ptr = pointer to integer to receive value
* Exit *ptr = next value from file
* Return TRUE if next value returned
* FALSE if end of file
*/
int nextvalue (p)
int *p;
{
if (count == 0) {
count = fread ((char *)buf, sizeof (int), BUFFER, fmand);
bp = buf;
}
if (count == EOF)
return (FALSE);
*p = *bp++;
count--;
return (TRUE);
}
/** mode_wait - wait for mode reset request
*
* This routine is executed by another thread that is started
* by the main routine.
*
* This routine calls VIOmode_wait requesting to be notified
* after the completion of an application or hard error popup.
* On such a notification, it puts the screen in the desired
* graphics mode.
*/
void mode_wait ()
{
USHORT NotifyType;
while (TRUE) {
/* wait for notification to restore mode */
VioModeWait (0, &NotifyType, 0);
if (NotifyType == 0 && graphics)
VioSetMode (&grmode,0);
}
}
/** redraw_wait - wait for mode reset request
*
* This routine is executed by another thread that is started
* by the main routine.
*
* This routine calls VioSavRedrawWait requesting to be notified
* for both save and redraw. On save notification, it copies the
* EGA adapter memory to allocated memory segments. On redraw
* notification, it puts the screen in the desired graphics mode
* and then redraws the Mandelbrot set.
*/
void redraw_wait ()
{
USHORT NotifyType;
while (TRUE) {
VioSavRedrawWait (0, &NotifyType, 0);
if (graphics) {
if (NotifyType == 0) {
/* save graphics screen */
SetScanSave (curr->desc->savesel);
SetScanClear ();
continue;
}
else if (NotifyType == 1) {
/* restore graphics screen */
dpoffset = 0;
VioSetMode (&grmode, 0);
SetScanClear ();
SetDVideo ();
SetScanRestore (curr->desc->savesel);
SetEVideo ();
}
}
}
}
/** readimage - read and display next image
*
* readimage ();
*
*/
readimage ()
{
int i;
struct ilist *tlist;
int nr;
/* Allocate memory for image structure and link to chain of images */
if ((tlist = (struct ilist *)malloc (sizeof (struct ilist))) == NULL) {
cleandisplay ();
printf ("Unable to allocate memory for ilist structure\n");
exit (1);
}
if (DosAllocSeg (sizeof (struct image),&descsel, 0) != 0) {
cleandisplay ();
printf ("Unable to allocate image descriptor segment of %d bytes\n", sizeof (struct image));
exit (1);
}
SELECTOROF(tlist->desc) = descsel;
OFFSETOF (tlist->desc) = 0;
/* Read description of the computed Mandelbrot set from the file
* and perform simple validation checks. Limit the display rows and
* columns to the display maximums.
*/
if (fread ((char *)&timage.nreal, sizeof (int), 1, fmand) != 1) {
cleandisplay ();
printf ("Error reading maximum number of reals %d\n", timage.nreal);
exit (2);
}
timage.lnr = (timage.nreal > 640)? 640: timage.nreal;
if (fread ((char *)&timage.nimag, sizeof (int), 1, fmand) != 1) {
cleandisplay ();
printf ("Error reading maximum number of imaginaries %d\n", timage.nimag);
exit (2);
}
timage.lni = (timage.nimag > 350)? 350: timage.nimag;
if (fread ((char *)&timage.mloop, sizeof (int), 1, fmand) != 1) {
cleandisplay ();
printf ("Error reading maximum loop count %d\n", timage.mloop);
exit (2);
}
if (fread ((char *)&timage.ul, sizeof (timage.ul), 1, fmand) != 1) {
cleandisplay ();
printf ("Error reading upper left coordinates\n");
exit (2);
}
if (fread ((char *)&timage.lr, sizeof (timage.lr), 1, fmand) != 1) {
cleandisplay ();
printf ("Error reading lower right coordinates\n");
exit (2);
}
if (fread ((char *)&timage.rinc, sizeof (timage.rinc), 1, fmand) != 1) {
cleandisplay ();
printf ("Error reading real increment\n");
exit (2);
}
if (fread ((char *)&timage.iinc, sizeof (timage.iinc), 1, fmand) != 1) {
cleandisplay ();
printf ("Error reading imaginary increment\n");
exit (2);
}
if (fread ((char *)&timage.aspect, sizeof (timage.aspect), 1, fmand) != 1) {
cleandisplay ();
printf ("Error reading aspect ratio\n");
exit (2);
}
if (fread ((char *)timage.hist, sizeof (long), timage.mloop + 1, fmand) != timage.mloop + 1) {
cleandisplay ();
printf ("Error reading histogram\n");
exit (2);
}
/* Assign colors to each iteration count. Black is reserved for
* points within the Mandelbrot set.
*/
for (nr = 0; nr < timage.mloop; nr++) {
timage.cmap[nr] = (nr % 15) + 1;
}
timage.cmap[timage.mloop] = 0;
/* Allocate memory for segments to save graphics screen and
* EGA font tables.
*/
for ( i = 0; i < 2; i++) {
if (DosAllocSeg (56020, (PSEL)&timage.savesel[i], 0) != 0) {
cleandisplay ();
printf ("Unable to allocate screen save segments\n");
exit (1);
}
}
/* Link image to chain of images */
DosSemRequest ((HSEM)&drawsem, -1L);
/* copy structure to far segment */
*(tlist->desc) = timage;
tlist->next = NULL;
tlist->prev = tail;
if (head == NULL)
head = tlist;
else
tail->next = tlist;
tail = tlist;
curr = tlist;
DosSemClear ((HSEM)&drawsem);
/* Lock the screen, set the display to graphics mode
* and draw the Mandelbrot set
*/
mdisp (curr->desc);
}
/**
*
*
*/
cleandisplay ()
{
/* cancel mode wait and redraw wait threads */
VioModeUndo (GIVEUPOWNER, KILLTHREAD, RESERVED);
VioSavRedrawUndo (GIVEUPOWNER, KILLTHREAD, RESERVED);
/* Return the EGA adapter to a default mode that hopefully
* matches the mode that was present when we went into graphics
* mode. Then restore mode data, text screen data, cursor
* position and cursor type.
*/
SetDVideo ();
if (graphics) {
/* clear display memory */
dpoffset = 0;
SetScanClear ();
if (flip)
dpoffset = PAGESIZE;
SetScanClear ();
SetScanRFont (fontsel);
}
SetEVideo ();
VioSetMode (&mode, 0);
if (VioWrtCellStr (screenbuf, screenlen, 0, 0, 0) != 0) {
printf ("Unable to write text screen data\n");
exit (1);
}
if (VioSetCurPos (currow, curcol, 0) != 0) {
printf ("Unable to set current cursor position\n");
exit (1);
}
if (VioSetCurType (&cursor, 0) != 0) {
printf ("Unable to set current cursor data\n");
exit (1);
}
}
/** nextimage
*
*/
nextimage ()
{
int temp;
if (head != tail) {
DosSemRequest ((HSEM)&drawsem, -1L);
if (curr->next == NULL)
curr = head;
else
curr = curr->next;
if (flip)
dpoffset ^= PAGESIZE;
SetScanRestore (curr->desc->savesel);
DosSemClear ((HSEM)&drawsem);
}
}
/** previmage
*
*/
previmage ()
{
BYTE temp;
if (head != tail) {
DosSemRequest ((HSEM)&drawsem, -1L);
VioScrLock (1, &temp, 0);
if (curr->prev == NULL)
curr = tail;
else
curr = curr->prev;
if (flip)
dpoffset ^= PAGESIZE;
SetScanRestore (curr->desc->savesel);
VioScrUnLock (0);
DosSemClear ((HSEM)&drawsem);
}
}
/** ripple - ripple palette registers
*
* ripple (direction);
*
* Entry direction = 0 if rotate palette registers up
* direction = 1 if rotate palette registers down
* Exit palette registers rotated by one
* Returns none
*/
ripple (dir)
int dir;
{
int temp;
int i;
switch (dir) {
case RIPPLE_UP:
temp = palette[0x0f];
for (i = 0x0f; i > 1; i--)
palette[i] = palette[i - 1];
palette[1] = temp;
break;
case RIPPLE_DN:
temp = palette[1];
for (i = 1; i < 0x0f; i++)
palette[i] = palette[i + 1];
palette[0x0f] = temp;
break;
}
DosSemRequest ((HSEM)&drawsem, -1L);
setpalette (palette);
DosSemClear ((HSEM)&drawsem);
}
/** flicker - flicker palette value
*
* flicker (pal);
*
* Entry pal = index of register to flicker
* Exit palette register toggled between value and black
* until keyboard input
* Returns none
*/
flicker (pal)
int pal;
{
int temp;
int c;
if ((pal > 0) && (pal < 0x10)) {
temp = palette[pal];
while (!nextchar (&c, 1)) {
palette[pal] = BLACK;
DosSemRequest ((HSEM)&drawsem, -1L);
setpalette (palette);
DosSemClear ((HSEM)&drawsem);
DosSleep (100L);
palette[pal] = temp;
DosSemRequest ((HSEM)&drawsem, -1L);
setpalette (palette);
DosSemClear ((HSEM)&drawsem);
DosSleep (100L);
}
ungetchar ();
}
}
/** paledit - edit palette registers
*
* paledit ();
*
* Entry none
*/
paledit ()
{
int pal;
int c;
/* select palette register */
pal = 1;
flicker (pal);
while (TRUE) {
nextchar (&c, 0);
switch (-c) {
case UP:
if (pal++ > 0x0f)
pal = 1;
flicker (pal);
break;
case DOWN:
if (pal-- < 1)
pal = 0x0f;
flicker (pal);
break;
case RIGHT:
if (++palette[pal] > 0x3f)
palette[pal] = 1;
DosSemRequest ((HSEM)&drawsem, -1L);
setpalette (palette);
DosSemClear ((HSEM)&drawsem);
break;
case LEFT:
if (--palette[pal] == 0)
palette[pal] = 0x3f;
DosSemRequest ((HSEM)&drawsem, -1L);
setpalette (palette);
DosSemClear ((HSEM)&drawsem);
break;
default:
ungetchar ();
return;
}
}
}
/** nextchar - get next character from keyboard
*
* flag = nextchar (pchar, wait);
*
* Entry char = pointer to character return location
* wait = 0 if delay until character entered
* wait = 1 if immediate return if no character
* Exit char = next character if found
* char = -scan code if extend character
* Return flag = TRUE if character read from keyboard
* flag = FALSE if no character read and immediate return
*/
nextchar (pchar, wait)
int *pchar;
int wait;
{
int ret;
if (!chvalid) {
if ((ret = KbdCharIn (&key, wait, 0)) != 0) {
cleandisplay ();
printf ("Error %d return from KbdCharIn\n", ret);
exit (1);
}
firstread = TRUE;
if ((key.fbStatus & 0x40) != 0x40)
return (FALSE);
}
chvalid = FALSE;
if (key.chChar == 0x00)
/* process extended ASCII character */
*pchar = -(int)key.chScan;
else
*pchar = (int)key.chChar;
return (TRUE);
}
/** ungetchar - unget character from keyboard
*
* ungetchar ();
*
* Entry none
* Exit previous character returned to keyboard
* Return none
*/
ungetchar ()
{
if (firstread)
chvalid = TRUE;
}
/** setpalette - set EGA palette registers
*
* setpalette (palette);
*
* Entry palette = byte array of palette registers
* Exit palette registers written to EGA
* Return none
*/
setpalette (p)
char *p;
{
int *ip;
vstate.length = sizeof (struct VIOSTATE);
vstate.req_type = 0;
vstate.double_defined = 0;
for (ip = &vstate.palette0; ip <= &vstate.palette15; )
*ip++ = *p++;
if (VioSetState ((PVIOPALSTATE)&vstate, 0) != 0) {
cleandisplay ();
printf ("Unable to set palette registers\n");
exit (1);
}
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.