_GRAPHICS PROGRAMMING COLUMN_ by Michael Abrash [LISTING ONE] /* Utility to force an ET4000-based VGA into 16- or 8-bit operation even if a monochrome adapter is in the system. (Note that only 16-bit memory access, not 16-bit I/O, is enabled; the I/O state is not altered.) The monochrome adapter won't work properly while SETBUS 16 is in effect. Tested with Borland C++ 3.0. Courtesy of Charles Marslett of STB. Commented and reformatted by Michael Abrash. */ /***************************************************************************** * This utility isn't known to cause problems, but use it at your own risk, as * the monochrome and VGA adapters will both respond to accesses to monochrome * display memory while SETBUS 16 is in effect, resulting in bus contention. ****************************************************************************/ #include #include #include void main(int argc, char *argv[]) { int crtc, val; union REGS regset; if (inp(0x3CC) & 0x01) /* decide where to address the CRT */ crtc = 0x3D4; /* Controller by reading the I/O */ else /* Address Select bit of the */ crtc = 0x3B4; /* Miscellaneous Output register */ outportb(0x3BF, 0x03); /* key sequence to enable access to */ outportb(crtc+4, 0xA0); /* ET4000-specific registers */ outportb(crtc, 0x36); /* get the current setting of the Video */ val = inp(crtc+1); /* System Configuration 1 register */ /* Decide whether 16- or 8-bit access desired, and configure accordingly */ if (argc == 2) { if (strcmp(argv[1], "16") == 0) { outportb(crtc+1, val | 0x40); /* 16-bit memory access */ goto ModeSet; } else if (strcmp(argv[1], "8") == 0) { outportb(crtc+1, val & 0xBF); /* 8-bit memory access */ ModeSet: regset.x.ax = 0x0003; int86(0x10, ®set, ®set); /* do a text mode mode set */ exit(0); } } fprintf(stderr, "Usage: SETBUS 16\n"); fprintf(stderr, " or SETBUS 8\n"); exit(1); } [LISTING TWO] /* Function to tell the BIOS to set up properly sized characters for 25 rows of 16 pixel high text in 640x400 graphics mode. Call immediately after mode set. Based on a contribution by Bill Lindley. */ #include void Set640x400() { union REGS regs; regs.h.ah = 0x11; /* character generator function */ regs.h.al = 0x24; /* use ROM 8x16 character set for graphics */ regs.h.bl = 2; /* 25 rows */ int86(0x10, ®s, ®s); /* invoke the BIOS video interrupt to set up the text */ } [LISTING THREE] /* Draws the 8x8 monochrome icon pointed to by IconPtr at coordinates (X,Y) in the DestWidth-wide bitmap starting at DestSeg:0, using the transparent replace raster op and color Color. Destination bitmap must be an 8-bit-per-pixel bitmap. 1-bits in the pattern are converted to drawing color and drawn, and 0-bits are skipped over, preserving destination (that is, are transparent). Tested with Borland C++ 3.0; when USE_C is 0, uses BC++ dependent inline assembly. */ #define USE_C 0 /* set to 1 to compile C code, 0 to compile assembler */ #if !USE_C #pragma inline /* tell the compiler there's inline ASM code */ #else #include #endif void DrawReplaceXpar(unsigned int X, unsigned int Y, unsigned int DestSeg, unsigned int DestWidth, unsigned int Color, unsigned char *IconPtr) { #if USE_C unsigned char far *ScreenPtr, Temp; int i,j; /* Point to the first destination pixel */ ScreenPtr = MK_FP(DestSeg, Y*DestWidth+X); for (i=0; i<8; i++) { /* do the 8 icon rows */ Temp = *IconPtr++; /* get the next icon row */ for (j=0; j<8; j++) { /* do the 8 pixels per icon row */ if (Temp & 0x80) /* draw this pixel if the */ *ScreenPtr = Color; /* corresponding icon bit is 1 */ ScreenPtr++; /* point to the next destination pixel */ Temp <<= 1; /* shift the next icon bit into place */ } /* Point to the start of the next row */ ScreenPtr += DestWidth - 8; } #else asm cld /* make LODSB increment SI */ asm mov es,DestSeg /* point ES to the bitmap */ asm mov ax,Y asm mov bx,DestWidth asm mul bx /* DestWidth*Y+X = offset of first */ asm add ax,X /* dest pixel */ asm mov di,ax /* point ES:DI to the first dest pixel */ asm mov si,IconPtr /* point DS:SI to the first icon byte */ asm mov dx,8 /* we'll do 8 rows */ asm mov ah,byte ptr Color /* color we'll draw with */ asm sub bx,8 /* offset from end of one dest row to start of next */ RowLoop: asm lodsb /* get the next icon row */ asm mov cx,8 /* we'll do 8 pixels on each row */ PixelLoop: asm shl al,1 /* shift the next icon bit into CF */ asm jnc NoDraw /* bit is 0, so don't draw */ asm mov es:[di],ah /* bit is 1, so draw this pixel */ NoDraw: asm inc di /* point to the next destination pixel */ asm loop PixelLoop /* do the next pixel */ asm add di,bx /* point to the start of the next dest row */ asm dec dx /* count down rows */ asm jnz RowLoop /* do the next row */ #endif } [LISTING FOUR] /* Compiled bitblit code for drawing the 8x8 monochrome icon pointed to by IconPtr at coordinates (X,Y) in the DestWidth-wide bitmap starting at DestSeg:0, using the transparent replace raster op and color Color. CompileReplaceXpar() generates code to perform desired bitblit, and ExecuteCompiled() executes that code to perform bitblit. Generally faster than a standard approach when drawing the same icon many times, because this way color expansion, transparency, and reading the source are only performed once, at expansion time, rather than every time an icon is drawn. Destination bitmap must be an 8-bit-per-pixel bitmap. 1-bits in pattern are converted to drawing color and drawn, and 0-bits are skipped over, preserving destination (that is, are transparent). Tested with Borland C++ 3.0; uses BC++ dependent inline assembly. */ #pragma inline /* tell the compiler there's inline ASM code */ /* Generates far-callable code to bitblit the specified icon, and stores code in BufferToCompileInto. Code is simply a series of MOV [DI+xxxx],AL instructions, where xxxx is the offset from upper left corner of icon of each pixel to be drawn. */ void CompileReplaceXpar(unsigned int DestWidth, unsigned char *IconPtr, unsigned char *BufferToCompileInto) { unsigned int i, j, PixelOffset = 0; unsigned char Temp; for (i=0; i<8; i++) { /* do the 8 icon rows */ Temp = *IconPtr++; /* get the next icon row */ for (j=0; j<8; j++) { /* do the 8 pixels per icon row */ if (Temp & 0x80) { /* generate the code to draw this pixel if the corresponding icon bit is 1. Code is the hex bytes for the instruction MOV [DI+PixelOffset],AL */ *BufferToCompileInto++ = 0x88; /* MOV opcode */ *BufferToCompileInto++ = 0x85; /* mod-reg-rm byte */ *((unsigned int *)BufferToCompileInto)++ = PixelOffset; /* addressing displacement */ } PixelOffset++; /* point to the next destination pixel */ Temp <<= 1; /* shift the next icon bit into place */ } /* Point to the start of the next row in the destination */ PixelOffset += DestWidth - 8; } /* Put a RET at the end to return to the calling code, and done */ *BufferToCompileInto = 0xCB; /* RETF instruction = 0xCB */ } void ExecuteCompiled(unsigned int X, unsigned int Y, unsigned int DestSeg, unsigned int DestWidth, unsigned int Color, unsigned char far *BufferToExecute) { asm push ds /* preserve the default data segment */ asm mov ds,DestSeg /* point ES to the bitmap */ asm mov ax,Y asm mul word ptr DestWidth /* DestWidth*Y+X = offset of */ asm add ax,X /* first dest pixel */ asm mov di,ax /* point ES:DI to the first dest pixel */ asm mov al,Color /* color with which to draw */ asm call dword ptr BufferToExecute /* perform a far call to execute the compiled bitblit code, and done */ asm pop ds /* restore the default data segment */ } [LISTING FIVE] /* Sample program to tile screen with an 8x8 monochrome icon. Tested with Borland C++ 3.0. */ #include #include #define USE_COMPILED_BITBLITS 1 /* set to 1 and link to Listing 4 to use compiled biblits, set to 0 and link to Listing 3 to use conventional bitblits */ #define SCREEN_WIDTH 320 #define SCREEN_HEIGHT 200 #define SCREEN_SEGMENT 0xa000 #if USE_COMPILED_BITBLITS extern void CompileReplaceXpar(unsigned int, unsigned char *, unsigned char *); extern void ExecuteCompiled(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned char far *); #else extern void DrawReplaceXpar(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned char *); #endif static unsigned char TestIcon[8] = {0x88, 0x44, 0x22, 0x11, 0x11, 0x22, 0x44, 0x88}; #if USE_COMPILED_BITBLITS /* Storage for the compiled icon-drawing code; must be large enough for the largest possible code size, because no error checking is performed */ static unsigned char CompiledBuffer[1000]; #endif void main() { unsigned int x, y; union REGS regset; #if USE_COMPILED_BITBLITS unsigned char far *CompiledBufferPtr; #endif regset.x.ax = 0x0013; /* AL = 0x13 selects 320x200 */ int86(0x10, ®set, ®set); /* 256-color graphics mode */ #if USE_COMPILED_BITBLITS /* Generate the code for drawing the icon with the transparent replace raster op, and store the code in CompiledBuffer */ CompileReplaceXpar(SCREEN_WIDTH, TestIcon, CompiledBuffer); CompiledBufferPtr = MK_FP(_DS, CompiledBuffer); #endif /* Tile TestIcon over the entire screen */ for (y=0; y