Category : Display Utilities
Archive   : EGACOLRS.ZIP
Filename : EGACOLRS.C

 
Output of file : EGACOLRS.C contained in archive : EGACOLRS.ZIP
/* EGACOLRS --- by Mark Adler 9 Jan 1988 Pasadena, CA */
/* This program may be used and freely copied by anyone except that */
/* it may not be sold by itself or as part of any package without */
/* the permission of the author. */

/* Compile with Turbo C using options -mt (tiny model). */
/* Compilation requires MASM. */

#pragma inline /* Tell compiler there is assembly code here */

char notice[] = "EGACOLRS - Copyright (c) 1988 Mark Adler";

/* Key values returned by keyin() */
#define UP 0x4800
#define DOWN 0x5000
#define RIGHT 0x4d00
#define LEFT 0x4b00
#define HOME 0x4700
#define END 0x4f00
#define PGUP 0x4900
#define PGDN 0x5100
#define SPACE 0x3920

/* Default pallette for EGA---used to restore pallette when done. */
char egapal[] = {0x00,0x01,0x02,0x03,0x04,0x05,0x14,0x07,
0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x00};

main()
/**********************************************************************/
/* */
/* EGACOLRS - displays all 64 colors the EGA can produce at once on */
/* the screen. The EGA makes 64 colors by mixing four intensities */
/* each of red, green, and blue (4*4*4 = 64). The colors are shown */
/* in four 4x4 blocks, corresponding to an imaginary 4x4x4 three- */
/* dimensional array. So, each edge of this 4x4x4 cube corresponds */
/* to one of the three colors, red, green, or blue. There are six */
/* possible ways to assign three colors to three axes. Hitting the */
/* space bar steps through all six ways, rearranging the colors on */
/* the screen each time. At any time, one of the blocks is selected */
/* and this is indicated by the block blinking (unless it is the */
/* black block, in which case you just can't tell it's blinking). */
/* The color value of that block (the byte you would put in the EGA */
/* pallette for that color) is displayed in the lower right corner */
/* of the screen, next to the bright white block. (It is left as an */
/* exercise to the reader why that block does not move when the */
/* axes are permuted. What other blocks don't move?) Different */
/* blocks are selected with the cursor keys. Also, the Home and End */
/* keys go to the far left and far right on the current row, and the */
/* PgUp and PgDn keys go to top and bottom of the current column. */
/* Any of Esc, 'q', or 'Q' exits the program. */
/* */
/* The EGA can only display 16 of the 64 possible colors at any one */
/* time. (Well, in the supported modes anyway, and not counting the */
/* overscan color, but let's not get picky.) Which 16, is selected */
/* by a software redefinable table called the "pallette". This */
/* program tricks the EGA into displaying all 64 possible colors by */
/* taking the phrase "at any one time" above quite literally. The */
/* key point is that it takes quite a while (from the point of view */
/* of the computer) to put up one frame on your monitor's screen */
/* (about 1/60th of a second). During that time, that software */
/* definable pallette is changed by this software several times. */
/* In that way, different parts of the screen have different */
/* pallettes. Specifically, eight sections of the screen have eight */
/* different pallettes, and in each of those sections is eight */
/* colored boxes. Only the last eight of the 16 colors in the */
/* pallette need to be changed to set the colors of those eight */
/* boxes, and in fact that is what is done. */
/* */
/**********************************************************************/
{
int i, j, k;
int a, b, c;
int f;
char *p, pal[8*8*4 + 1 + 2*8 + 1];


/* Set up pallettes table */
/* Format of table: low byte of scan line number preceding scan line
to change the pallette on, followed by 3 bytes
to send to the pallette address and data port.
The 3 bytes are a color number, and a color value
followed by a 0x20 to re-enable the pallette. */
p = pal; /* Change pallette at eight scan */
for (i = 0; i < 8; i++) /* lines (every three char lines) */
for (j = 8, k = 42 * i + (i > 4) * 14 + 1; j < 16; j++, k++)
{
*p++ = k; /* Low byte of scan line */
*p++ = j; /* Color number */
p++; /* Color---filled in by colors() */
*p++ = 0x20; /* Re-enable pallette */
}
*p++ = 351; /* Non-existent line */
for (j = 8; j < 16; j++) /* Kill colors at vertical retrace */
{
*p++ = j;
*p++ = 0; /* Black */
}
*p = 0x20;

/* Set up initial axes, fill in pallette table */
a = 2; b = 1; c = 0; /* Initial choice of color axes */
colors(pal, a, b, c); /* Set colors */


/* Put colors on screen */
mode(3); /* Select 80x24 color---clear screen */
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++)
for (k = 0; k < 4; k++)
wrblk(i, j, k, 0); /* Write 64 colored blocks */
cpos(24, 80); /* Hide cursor (one char after end) */


/* Display colors, process keystrokes */
i = j = k = 0; /* Initial selected block */
do { /* Process keystrokes */
/* Blink selected block and show its color value in hex */
wrblk(i, j, k, 0x80); /* Blink the selected block */
f = pal[padr(i, j, k)]; /* Get color of selected block */
wrstr(24, 69, "color 0x", 0x0f); /* Show color in hex */
wrchr(24, 77, "0123456789ABCDEF"[f >> 4], 0x0f);
wrchr(24, 78, "0123456789ABCDEF"[f & 0x0f], 0x0f);

/* Change pallettes on the fly, waiting for keystroke */
trick(pal);

/* Get the key hit and process it */
wrblk(i, j, k, 0); /* Unblink last selected block */
switch (f = keyin()) /* Get key, see what it is */
{
case RIGHT: if (k < 3) k++; else { i ^= 1; k = 0; } break;
case LEFT: if (k > 0) k--; else { i ^= 1; k = 3; } break;
case DOWN: if (j < 3) j++; else { i ^= 2; j = 0; } break;
case UP: if (j > 0) j--; else { i ^= 2; j = 3; } break;
case HOME: i &= 2; k = 0; break;
case END: i |= 1; k = 3; break;
case PGUP: i &= 1; j = 0; break;
case PGDN: i |= 2; j = 3; break;
case SPACE: /* Toggle color axes */
if ((b - a + 3) % 3 == 2) /* Step through all */
{ f = a; a = b; b = f; /* permutations of 3 things */
f = i; i = j; j = f; } /* Keep same selected color. */
else
{ f = b; b = c; c = f;
f = j; j = k; k = f; }
colors(pal, a, b, c); /* Change color pallettes */
}

/* If Esc, 'q', or 'Q' hit, then exit */
} while ((f & 0x7f) != 27 && (f & 0x5f) != 'Q');

/* Done---restore default pallette and exit */
pallette(egapal);
cpos(24, 0); /* Put prompt on next line */
}


colors(p, a, b, c)
char p[]; /* Pallettes table */
int a, b, c; /* a,b,c is some permutation of 0,1,2. */
/**********************************************************************/
/* */
/* Set the colors in the pallettes table to the desired permutation */
/* of the color axes. a is the first axis---it spans the four large */
/* 4x4 blocks. b is the second axis---it is the vertical axis for */
/* each 4x4 block. c is the last axis---it is the horizontal axis */
/* for each 4x4 block. a, b, and c should be some permutation of */
/* 0, 1, and 2. The value 2 picks red for that axis, 1 picks green, */
/* and 0 picks blue. */
/* */
/**********************************************************************/
{
register int j, k;
int i;

for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++)
for (k = 0; k < 4; k++)
p[padr(i, j, k)] = (i >> 1 & 1) << a | (i & 1) << a+3 |
(j >> 1 & 1) << b | (j & 1) << b+3 |
(k >> 1 & 1) << c | (k & 1) << c+3;
}


padr(i, j, k)
int i, j, k; /* Indices for 1st, 2nd, and 3rd axes respectively */
/**********************************************************************/
/* */
/* Return the offset in the pallettes table for the color byte of the */
/* (i,j,k) block. The index 'i' selects which 4x4 group the block */
/* is in, 'j' is which row in the 4x4 group selected by 'i', and 'k' */
/* is which block in the row selected by 'i' and 'j'. */
/* */
/**********************************************************************/
{
return (i & 2) << 6 | j << 5 | (i & 1) << 4 | k << 2 | 2;
}


wrblk(i, j, k, b)
int i, j, k, b; /* (b = 0x80 to blink) */
/**********************************************************************/
/* */
/* Write a block of characters on the screen consisting of pure fore- */
/* ground color, and setting the color attribute of the characters */
/* to select the proper color in the pallette for that area of the */
/* screen. */
/* */
/**********************************************************************/
{
register int n, m;
int c;
static char s[] = "\333\333\333\333\333"; /* All foreground */

n = (i & 2 ? 14 : 1) + 3 * j; /* Row number of upper left */
m = (i & 1 ? 41 : 10) + 7 * k; /* Column number of same */
c = (i & 1 ? 12 : 8) + k + b; /* Color for this block */
wrstr(n, m, s, c); /* Do first row of six */
wrstr(n+1, m, s, c); /* Do second row of six */
}


wrstr(n, m, s, a)
int n, m, a;
char *s;
/**********************************************************************/
/* */
/* Write a string of characters to the screen with an attribute. It */
/* is assumed that the string will not go past the end of the line. */
/* */
/**********************************************************************/
{
while (*s)
wrchr(n, m++, *s++, a);
}


trick(p)
char *p; /* pallettes */
/**********************************************************************/
/* */
/* trick() watches the horizontal and vertical retraces and uses the */
/* information to change the color pallette of the EGA on the */
/* desired scan lines. This allows displaying all 64 possible */
/* colors at a time, albeit in a restricted way. */
/* */
/* 'p[]' is a sequence of instructions that specify how to change */
/* the colors. Each instruction is four bytes---the first byte is */
/* the low byte of the number of the scan line to change the color */
/* on, the second byte is the pallette color to change (0..15 or 17 */
/* for overscan), the third byte is the color to change it to */
/* (0..63), and the fourth byte is always 0x20 to reenable the */
/* pallette. The last three bytes are sent to port 0x3c0 on the */
/* line specified by the first byte. The format of the color byte */
/* is from most significant to least significant bit: 00rgbRGB. */
/* The letters refer to the colors red, green, and blue, and the */
/* upper case letters are "stronger" than the low case. That is */
/* 00000100 is a stronger red than 00100000. */
/* */
/* After the last pallette change, the next byte should be a scan */
/* line that does not occur. On vertical retrace, the 17 bytes */
/* that follow are sent to the pallette port. This allows setting */
/* part of the pallette to black to avoid color flashes while */
/* processing keystrokes. */
/* */
/* At each vertical retrace, interrupts are allowed and if a key was */
/* hit, the process terminates. Else, it starts over at the top of */
/* of the screen. */
/* */
/* The timing is so tight on 4.77 MHz PC's with 8088's, it is */
/* necessary to avoid jumps that reload the queue and to disable the */
/* DMA refreshes to guarantee that the horizontal retrace will be */
/* caught. Jumps are avoided by repeating the code to check for */
/* horizontal retrace instead of allowing it to loop on itself. The */
/* DMA is not actually disabled, but instead it is synchronized to */
/* the horizontal lines. Refreshes must be done on the average */
/* every 15.625 uS. A horizontal line is 45.765 uS, so three */
/* refreshes per line is more than sufficient. The DMA refresh is */
/* disabled before looking for the horizontal retrace. After */
/* finding the retrace, refreshes are set to a high enough rate to */
/* get three in before the next time the refresh is disabled. On */
/* vertical retrace, the refresh time is set back to normal. */
/* */
/**********************************************************************/
{
/* Get Input Status Register 1 port for retrace information */
asm mov BL,10h /* Get port addresses for EGA */
asm mov AH,12h
asm int 10h
asm mov DX,03DAh /* Usually set up like color card */
asm test BH,BH
asm jz color
asm mov DL,0BAh /* Set up like mono card */
color:

/* Change pallette at specified lines on screen until key hit */
again:
asm cld /* String instructions increment */
asm mov SI,p /* Point to pallettes table */
asm lodsb /* Get first scan number low byte */
asm mov AH,AL /* Save in AH */
asm sub BX,BX /* Initialize count */
asm mov DI,DX /* Put port in DI also */

/* Wait for end of vertical retrace */
asm cli /* Kill interrupts until screen end */
vwt:
asm in AL,DX
asm test AL,8 /* Check vertical retrace bit */
asm jz vwt /* Wait for vertical retrace */
nvwt:
asm in AL,DX
asm test AL,8 /* Check vertical retrace bit */
asm jnz nvwt /* Wait for not vertical retrace */

/* Go through loop once for each horizontal line */
llp:

/* Synchronize refreshes to lines */
asm mov AL,54h /* Turn off refresh until retrace */
asm out 43h,AL

/* Wait for end of line---hard to catch on slow machines */
/* (there are 25 of these lines:) */
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
asm in AL,DX ; asm test AL,1 ; asm jnz horiz
/* (If we got this far, machine is fast enough to loop.) */
hwt:
asm in AL,DX
asm test AL,1 /* Check display enable bit */
asm jz hwt /* Wait for horizontal retrace */
horiz:

/* Set refresh rate to get three in before next turn off */
asm mov AL,8 /* Have about 31 clocks of code */
asm out 41h,AL /* before turn off */

/* Check if need to change color (BX is the next line's #) */
asm cmp BL,AH /* Only need to check low byte */
asm jne noout /* If not, check for vertical retrace */

/* Do pallette change */
asm mov DL,0C0h /* Point to pallete registers */
asm lodsb /* Output 3 bytes */
asm out DX,AL
asm lodsb
asm out DX,AL
asm lodsw /* Also gets next scan low byte in AH */
asm out DX,AL
asm mov DX,DI /* Restore DX */
asm inc BX /* Increment line count */
asm jmp llp /* Wait for next line */

/* Wait for line (probably already there) or vertical retrace */
noout:
ewt:
asm in AL,DX
asm and AL,9 /* Pick out blank, horiz bits */
asm cmp AL,1 /* See if horizontal retrace */
asm je ewt /* Wait for NOT horizontal retrace */
asm test AL,8 /* See if vertical retrace */
asm jnz done /* If so, then at end of screen */
asm nop /* Equalize to other branch (approx) */
asm nop
asm nop
asm nop
asm inc BX /* Increment line count */
asm jmp llp /* Wait for next line */
done:

/* Vertical retrace just started---fix pallette */
asm mov AL,18 /* Restore refresh timing */
asm out 41h,AL
asm mov DL,0C0h /* Point to pallete registers */
asm mov CX,17 /* Output 17 bytes */
olp:
asm lodsb
asm out DX,AL
asm loop olp
asm sti /* Allow interrupts at vert retrace */

/* See if key hit */
asm push DI /* Save port */
asm mov AH,1 /* See if key hit */
asm int 16h
asm pop DX /* Restore port */
asm jnz leave /* If key hit, return to process it */
asm jmp again /* Else, do next screen */
leave:
asm mov AX,BX /* Else done--return line count */
}


mode(m) /* Set video mode */
int m; /* Video mode */
{
asm mov AL,m
asm mov AH,0
asm int 10h
}


pallette(p) /* Set pallette to 17 byte table */
char *p; /* 17 byte table */
{
asm mov AX,DS /* Point ES:DX to 17 byte table. */
asm mov ES,AX
asm mov DX,p
asm mov AX,1002h /* Set all pallette registers and overscan. */
asm int 10h
}


cpos(r, c) /* Set cursor position */
int r, c; /* Row and column, numbered from 0 */
{
asm mov DH,r
asm mov DL,c
asm mov BH,0 /* Select page 0 */
asm mov AH,2
asm int 10h
}


wrchr(r, c, b, a) /* Write char and attr to screen */
int r; /* Row */
int c; /* Column */
int b; /* Character */
int a; /* Attribute---low four bits are foreground color */
{
/* Do it direct to avoid BIOS delay */
/* Get regen segment based on mono or color */
asm xor AX,AX /* Point to BIOS data */
asm mov ES,AX
asm mov AL,ES:[0410h] /* Get low byte of equipment */
asm and AL,30h /* Check for mono */
asm cmp AL,30h
asm mov AX,0B000h /* Segment for mono */
asm je mono
asm mov AH,0B8h /* Segment for color */
mono:
asm mov ES,AX

/* Compute offset of character/attribute in segment */
asm mov AL,80
asm mul byte ptr r /* Multiply row times cols/row */
asm add AX,c /* Add column */
asm shl AX,1 /* Double for 2 bytes/position */
asm mov DI,AX

/* Put character and attribute there */
asm mov AL,b /* Character */
asm mov AH,a /* Attribute */
asm stosw /* Store it */
}


keyrdy() /* Return true if key ready */
{
asm mov AH,1
asm int 16h
asm mov AX,0
asm jz notrdy
asm inc AX
notrdy:
}


keyin() /* Wait for and return key code */
{
asm mov AH,0
asm int 16h
}


  3 Responses to “Category : Display Utilities
Archive   : EGACOLRS.ZIP
Filename : EGACOLRS.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/