1 /*****************************************************************************
\r
3 Chris Giese <geezer@execpc.com> http://my.execpc.com/~geezer
\r
5 This code is public domain (no copyright).
\r
6 You can do whatever you want with it.
\r
8 This code uses the BIOS to set graphics mode, and uses the BIOS font.
\r
9 Should compile cleanly with Turbo C++ 1.0, Turbo C++ 3.0, 16- or 32-bit
\r
10 Watcom C, or DJGPP. DJGPP version will not work in Windows NT/2000/XP
\r
13 Some additional things you could do with this:
\r
14 - Write a function tblit1(), similar to blit1(), that uses an on-off
\r
15 transparency mask. Use this function to blit non-rectangular objects
\r
16 such as a mouse cursor.
\r
17 - Write blit_plane(): a fast function to blit from monochrome to monochrome
\r
18 or 4-plane bitmaps. Use an external shift() function, written in asm
\r
19 - Support VBE 1.x banked framebuffer
\r
20 - Support VBE 2.x linear framebuffer (pmode only, not at A000h:0000)
\r
21 - Support greater color depths: 15 bpp, 16 bpp, 24 bpp, 32 bpp
\r
22 - Color reduction, e.g. Heckbert (median-cut) algorithm
\r
23 - Clipping engine that lets you draw a window that is partially
\r
24 obscured by "closer" windows
\r
25 - Mouse, keyboard, and timer events
\r
26 - Widgets: push button, checkbox, radio buttons, listbox, dialog, etc.
\r
27 *****************************************************************************/
\r
28 #include <string.h> /* [_f]memset() */
\r
29 /********************************* TURBO C **********************************/
\r
30 #if defined(__TURBOC__)
\r
31 #include <dos.h> /* struct REGPACK, intr() */
\r
33 /* The framebuffer is far outside the 16-bit data segment. The only way to
\r
34 make the framebuffer work like in-memory bitmaps is to use far pointers.
\r
36 We still use the SMALL memory model. */
\r
38 #define FARPTR(S, O) MK_FP(S, O)
\r
40 #define outportw(P,V) outport(P,V)
\r
47 #define trap(N,R) intr(N,R)
\r
49 typedef struct REGPACK regs_t;
\r
51 #if __TURBOC__<0x300
\r
52 void vmemset(unsigned char FAR *s, unsigned c, unsigned n)
\r
61 void vmemset(unsigned char FAR *s, unsigned c, unsigned n)
\r
67 /********************************* DJGPP ************************************/
\r
68 #elif defined(__DJGPP__)
\r
69 #include <dpmi.h> /* __dpmi_... */
\r
70 #include <dos.h> /* inportb(), outportb() */
\r
72 #define FAR /* nothing */
\r
73 #define FARPTR(S, O) (unsigned char *)((S) * 16L + (O) + \
\r
74 __djgpp_conventional_base)
\r
76 /* near pointers; not supported in Windows NT/2k/XP DOS box */
\r
77 #include <sys/nearptr.h> /* __djgpp_conventional_base, __djgpp_nearptr_enable() */
\r
78 #include <stdio.h> /* printf() */
\r
79 #include <crt0.h> /* _CRT0_FLAG_NEARPTR, _crt0_startup_flags */
\r
86 #define trap(N,R) __dpmi_int(N,R)
\r
88 typedef __dpmi_regs regs_t;
\r
90 void vmemset(unsigned char FAR *s, unsigned c, unsigned n)
\r
95 /******************************** WATCOM C **********************************/
\r
96 #elif defined(__WATCOMC__)
\r
97 #include <dos.h> /* union REGPACK, MK_FP(), intr() */
\r
99 #if defined(__386__)
\r
100 #define FAR /* nothing */
\r
101 #define FARPTR(S, O) (unsigned char *)((S) * 16L + (O))
\r
103 void vmemset(unsigned char FAR *s, unsigned c, unsigned n)
\r
110 #define FARPTR(S, O) MK_FP(S, O)
\r
112 void vmemset(unsigned char FAR *s, unsigned c, unsigned n)
\r
118 #define inportb(P) inp(P)
\r
119 #define outportb(P,V) outp(P,V)
\r
120 #define outportw(P,V) outpw(P,V)
\r
127 /* WARNING: for 32-bit code, unused fields of regs_t
\r
128 must be zeroed before using this macro */
\r
129 #define trap(N,R) intr(N,R)
\r
131 typedef union REGPACK regs_t;
\r
134 #error Not Turbo C, not DJGPP, not Watcom C. Sorry.
\r
137 #include <conio.h> /* getch() */
\r
139 /* need direct access to some VGA registers to select plane,
\r
140 enable Mode X, and fix screwy CGA addressing */
\r
141 #define VGA_SEQ_INDEX 0x3C4
\r
142 #define VGA_SEQ_DATA 0x3C5
\r
143 #define VGA_GC_INDEX 0x3CE
\r
144 #define VGA_GC_DATA 0x3CF
\r
145 #define VGA_CRTC_INDEX 0x3D4
\r
146 #define VGA_CRTC_DATA 0x3D5
\r
148 /* bitmap "class" */
\r
152 unsigned char FAR *raster;
\r
153 unsigned fore_color, back_color;
\r
154 /* "member functions" */
\r
155 const struct _driver *ops;
\r
158 typedef struct _driver
\r
160 /* "pure virtual functions": color drivers MUST implement these */
\r
161 void (*write_pixel)(bmp_t *bmp, unsigned x, unsigned y, unsigned c);
\r
162 unsigned (*read_pixel)(bmp_t *bmp, unsigned x, unsigned y);
\r
163 /* "virtual functions": drivers MAY implement these, for speed
\r
164 fill rectangular area with solid color */
\r
165 void (*fill_rect)(bmp_t *bmp, int x, int y, int wd, int ht);
\r
166 /* copy monochrome bitmap to this bitmap (used to display text) */
\r
167 void (*blit1)(bmp_t *src, bmp_t *dst, unsigned dst_x, unsigned dst_y);
\r
168 /* copy all or part of one bitmap to another (both of the same depth) */
\r
169 void (*blit)(bmp_t *src, bmp_t *dst, unsigned dst_x, unsigned dst_y);
\r
171 /*============================================================================
\r
173 ============================================================================*/
\r
174 /*****************************************************************************
\r
175 *****************************************************************************/
\r
176 void set_plane(unsigned p)
\r
178 static unsigned curr_p = -1u;
\r
180 unsigned char pmask;
\r
188 outportb(VGA_GC_INDEX, 4);
\r
189 outportb(VGA_GC_DATA, p);
\r
190 outportb(VGA_SEQ_INDEX, 2);
\r
191 outportb(VGA_SEQ_DATA, pmask);
\r
193 /* this is a little faster... */
\r
194 outportw(VGA_GC_INDEX, (p << 8) | 4);
\r
195 outportw(VGA_SEQ_INDEX, (pmask << 8) | 2);
\r
198 /*****************************************************************************
\r
199 fast planar (monochrome or 16-color) rectangle fill
\r
200 *****************************************************************************/
\r
201 void fill_plane(bmp_t *bmp, int x, int y, int wd, int ht, unsigned c)
\r
203 unsigned w, wd_in_bytes, off;
\r
204 unsigned char lmask, rmask;
\r
208 w = (x2 >> 3) - (x >> 3) + 1;
\r
209 lmask = 0x00FF >> (x & 7); /* FF 7F 3F 1F 0F 07 03 01 */
\r
210 rmask = 0xFF80 >> (x2 & 7);/* 80 C0 E0 F0 F8 FC FE FF */
\r
213 wd_in_bytes = bmp->wd / 8;
\r
214 off = wd_in_bytes * y + x / 8;
\r
216 /* for each row... */
\r
217 for(y2 = y; y2 < y + ht; y2++)
\r
219 /* do partial byte on left */
\r
220 bmp->raster[off] |= lmask;
\r
221 /* do solid bytes in middle */
\r
223 vmemset(bmp->raster + off + 1, 0xFF, w - 2);
\r
224 /* do partial byte on right */
\r
226 bmp->raster[off + w - 1] |= rmask;
\r
228 off += wd_in_bytes;
\r
234 for(y2 = y; y2 < y + ht; y2++)
\r
236 bmp->raster[off] &= lmask;
\r
238 vmemset(bmp->raster + off + 1, 0, w - 2);
\r
240 bmp->raster[off + w - 1] &= rmask;
\r
241 off += wd_in_bytes;
\r
245 /*****************************************************************************
\r
247 *****************************************************************************/
\r
248 void blit_plane(bmp_t *src, bmp_t *dst, unsigned dst_x, unsigned dst_y)
\r
250 /* left as an exercise for the reader :)
\r
252 You may need an external, assembly-language function to shift (left or
\r
253 right) a long string of bytes. No need to shift by more than 7 bits. */
\r
255 /*============================================================================
\r
256 driver for monochrome (1-bit) graphics
\r
257 ============================================================================*/
\r
258 /*****************************************************************************
\r
259 *****************************************************************************/
\r
260 static void write_pixel1(bmp_t *bmp, unsigned x, unsigned y, unsigned c)
\r
262 unsigned wd_in_bytes;
\r
263 unsigned off, mask;
\r
265 c = (c & 1) * 0xFF;
\r
266 wd_in_bytes = bmp->wd / 8;
\r
267 off = wd_in_bytes * y + x / 8;
\r
270 bmp->raster[off] = (bmp->raster[off] & ~mask) | (c & mask);
\r
272 /*****************************************************************************
\r
273 *****************************************************************************/
\r
274 static unsigned read_pixel1(bmp_t *bmp, unsigned x, unsigned y)
\r
276 unsigned wd_in_bytes;
\r
277 unsigned off, mask;
\r
279 wd_in_bytes = bmp->wd / 8;
\r
280 off = wd_in_bytes * y + x / 8;
\r
283 return (bmp->raster[off] & mask) != 0;
\r
285 /*****************************************************************************
\r
286 *****************************************************************************/
\r
287 static void fill_rect1(bmp_t *bmp, int x, int y, int wd, int ht)
\r
289 fill_plane(bmp, x, y, wd, ht, bmp->fore_color & 1);
\r
291 /*****************************************************************************
\r
292 *****************************************************************************/
\r
293 const ops_t g_ops1 =
\r
301 /*============================================================================
\r
302 driver for 2-bit packed pixel (4-color CGA) graphics
\r
303 ============================================================================*/
\r
304 /*****************************************************************************
\r
305 *****************************************************************************/
\r
306 static void write_pixel2(bmp_t *bmp, unsigned x, unsigned y, unsigned c)
\r
308 unsigned wd_in_bytes, off, mask;
\r
310 c = (c & 3) * 0x55;
\r
311 wd_in_bytes = bmp->wd / 4;
\r
312 off = wd_in_bytes * y + x / 4;
\r
315 bmp->raster[off] = (bmp->raster[off] & ~mask) | (c & mask);
\r
317 /*****************************************************************************
\r
318 *****************************************************************************/
\r
319 const ops_t g_ops2 =
\r
322 NULL, /* read_pixel */
\r
323 NULL, /* fill_rect */
\r
327 /*============================================================================
\r
328 driver for 4-plane 16-color graphics
\r
329 ============================================================================*/
\r
330 /*****************************************************************************
\r
331 *****************************************************************************/
\r
332 static void write_pixel4p(bmp_t *bmp, unsigned x, unsigned y, unsigned c)
\r
334 unsigned wd_in_bytes, off, mask, p, pmask;
\r
336 wd_in_bytes = bmp->wd / 8;
\r
337 off = wd_in_bytes * y + x / 8;
\r
341 for(p = 0; p < 4; p++)
\r
345 bmp->raster[off] |= mask;
\r
347 bmp->raster[off] &= ~mask;
\r
351 /*****************************************************************************
\r
352 pixel-by-pixel fill is too slow, so use this optimized function:
\r
353 *****************************************************************************/
\r
354 static void fill_rect4p(bmp_t *bmp, int x, int y, int wd, int ht)
\r
356 unsigned char p, pmask;
\r
359 for(p = 0; p < 4; p++)
\r
362 fill_plane(bmp, x, y, wd, ht, bmp->fore_color & pmask);
\r
366 /*****************************************************************************
\r
367 *****************************************************************************/
\r
368 const ops_t g_ops4p =
\r
371 NULL, /* read_pixel */
\r
376 /*============================================================================
\r
377 driver for 8-bit 256-color graphics
\r
378 ============================================================================*/
\r
379 /*****************************************************************************
\r
380 *****************************************************************************/
\r
381 static void write_pixel8(bmp_t *bmp, unsigned x, unsigned y, unsigned c)
\r
383 unsigned wd_in_bytes;
\r
386 wd_in_bytes = bmp->wd;
\r
387 off = wd_in_bytes * y + x;
\r
388 bmp->raster[off] = c;
\r
390 /*****************************************************************************
\r
391 *****************************************************************************/
\r
392 static void fill_rect8(bmp_t *bmp, int x, int y, int wd, int ht)
\r
394 unsigned wd_in_bytes, off, y2;
\r
396 wd_in_bytes = bmp->wd;
\r
397 off = wd_in_bytes * y + x;
\r
398 for(y2 = y; y2 < y + ht; y2++)
\r
400 vmemset(bmp->raster + off, bmp->fore_color, wd);
\r
401 off += wd_in_bytes;
\r
404 /*****************************************************************************
\r
405 *****************************************************************************/
\r
406 const ops_t g_ops8 =
\r
409 NULL, /* read_pixel */
\r
414 /*============================================================================
\r
415 driver for 8-bit 256-color Mode-X graphics
\r
416 ============================================================================*/
\r
417 /*****************************************************************************
\r
418 *****************************************************************************/
\r
419 static void write_pixel8x(bmp_t *bmp, unsigned x, unsigned y, unsigned c)
\r
421 unsigned wd_in_bytes;
\r
424 wd_in_bytes = bmp->wd / 4;
\r
425 off = wd_in_bytes * y + x / 4;
\r
427 bmp->raster[off] = c;
\r
429 /*****************************************************************************
\r
430 *****************************************************************************/
\r
431 const ops_t g_ops8x =
\r
434 NULL, /* read_pixel */
\r
435 NULL, /* fill_rect */
\r
439 /*============================================================================
\r
440 depth-independent routines, which call the depth-dependent routines
\r
441 ============================================================================*/
\r
442 /*****************************************************************************
\r
443 *****************************************************************************/
\r
444 unsigned read_pixel(bmp_t *bmp, unsigned x, unsigned y)
\r
446 if(x >= bmp->wd || y >= bmp->ht)
\r
448 if(bmp->ops->read_pixel == NULL)
\r
449 return 0; /* uh-oh */
\r
450 return bmp->ops->read_pixel(bmp, x, y);
\r
452 /*****************************************************************************
\r
453 *****************************************************************************/
\r
454 void write_pixel(bmp_t *bmp, unsigned x, unsigned y, unsigned c)
\r
456 if(x >= bmp->wd || y >= bmp->ht)
\r
458 if(bmp->ops->write_pixel == NULL)
\r
459 return; /* uh-oh */
\r
460 bmp->ops->write_pixel(bmp, x, y, c);
\r
462 /*****************************************************************************
\r
463 *****************************************************************************/
\r
464 void fill_rect(bmp_t *bmp, int x, int y, int wd, int ht)
\r
476 if(x + wd >= (int)bmp->wd)
\r
478 if(x >= (int)bmp->wd)
\r
489 if(y + ht >= (int)bmp->ht)
\r
491 if(y >= (int)bmp->ht)
\r
495 /* use fast routine if available */
\r
496 if(bmp->ops->fill_rect != NULL)
\r
498 bmp->ops->fill_rect(bmp, x, y, wd, ht);
\r
501 for(y2 = y; y2 < y + ht; y2++)
\r
502 for(x2 = x; x2 < x + wd; x2++)
\r
503 write_pixel(bmp, x2, y2, bmp->fore_color);
\r
505 /*****************************************************************************
\r
506 *****************************************************************************/
\r
507 void hline(bmp_t *bmp, int x, int y, unsigned wd)
\r
509 fill_rect(bmp, x, y, wd, 1);
\r
511 /*****************************************************************************
\r
512 *****************************************************************************/
\r
513 void vline(bmp_t *bmp, int x, int y, unsigned ht)
\r
515 fill_rect(bmp, x, y, 1, ht);
\r
517 /*****************************************************************************
\r
518 blit1 = blit from monochrome bitmap to bitmap of any color depth
\r
519 *****************************************************************************/
\r
520 void blit1(bmp_t *src, bmp_t *dst, unsigned dst_x, unsigned dst_y)
\r
524 /* source bitmap _must_ be monochrome */
\r
525 if(src->ops != &g_ops1)
\r
527 /* use fast routine if available */
\r
528 if(src->ops->blit1 != NULL)
\r
530 src->ops->blit1(src, dst, dst_x, dst_y);
\r
533 for(y = 0; y < src->ht; y++)
\r
534 for(x = 0; x < src->wd; x++)
\r
536 c = read_pixel(src, x, y);
\r
537 /* xxx - on-off transparency?
\r
541 c = dst->fore_color;
\r
543 c = dst->back_color;
\r
544 write_pixel(dst, dst_x + x, dst_y + y, c);
\r
547 /*****************************************************************************
\r
548 blit = copy from one bitmap to another, both of the same color depth
\r
549 *****************************************************************************/
\r
550 void blit(bmp_t *src, bmp_t *dst, unsigned dst_x, unsigned dst_y)
\r
554 /* they must be the same depth */
\r
555 if(src->ops != dst->ops)
\r
557 /* use fast routine if available */
\r
558 if(src->ops->blit != NULL)
\r
560 src->ops->blit(src, dst, dst_x, dst_y);
\r
563 for(y = 0; y < src->ht; y++)
\r
564 for(x = 0; x < src->wd; x++)
\r
566 c = read_pixel(src, x, y);
\r
567 write_pixel(dst, dst_x + x, dst_y + y, c);
\r
570 /*****************************************************************************
\r
571 find 8x8 font in VGA BIOS ROM
\r
572 *****************************************************************************/
\r
573 unsigned char FAR *bios_8x8_font(void)
\r
575 unsigned char FAR *font;
\r
578 /* use BIOS INT 10h AX=1130h to find font #3 (8x8) in ROM */
\r
579 memset(®s, 0, sizeof(regs)); /* for Watcom C */
\r
580 regs.R_AX = 0x1130;
\r
581 regs.R_BX = 0x0300;
\r
583 /* CauseWay DOS extender seems to return a selector in ES,
\r
584 instead of real-mode segment value (usu. 0xC000) */
\r
585 #if defined(__WATCOMC__)&&defined(__386__)
\r
586 font = FARPTR(0xC000, regs.R_BP);
\r
588 font = FARPTR(regs.R_ES, regs.R_BP);
\r
592 /*****************************************************************************
\r
593 *****************************************************************************/
\r
594 void bputs(bmp_t *bmp, unsigned x, unsigned y, const char *s)
\r
596 unsigned char FAR *font;
\r
599 font = bios_8x8_font();
\r
603 for(; *s != '\0'; s++)
\r
605 src.raster = font + 8 * (*s);
\r
606 blit1(&src, bmp, x, y);
\r
610 /*============================================================================
\r
612 ============================================================================*/
\r
613 /*****************************************************************************
\r
614 *****************************************************************************/
\r
615 static void border3d(bmp_t *bmp, int x, int y, unsigned wd, unsigned ht,
\r
620 bmp->fore_color = 8;
\r
621 hline(bmp, x + 0, y + 0, wd - 1);
\r
622 vline(bmp, x + 0, y + 0, ht - 1);
\r
623 bmp->fore_color = 0;
\r
624 hline(bmp, x + 1, y + 1, wd - 3);
\r
625 vline(bmp, x + 1, y + 1, ht - 3);
\r
626 bmp->fore_color = 7;
\r
627 hline(bmp, x + 1, y + ht - 2, wd - 2);
\r
628 vline(bmp, x + wd - 2, y + 1, ht - 2);
\r
629 bmp->fore_color = 15;
\r
630 hline(bmp, x + 0, y + ht - 1, wd);
\r
631 vline(bmp, x + wd - 1, y + 0, ht);
\r
635 bmp->fore_color = 7;
\r
636 hline(bmp, x + 0, y + 0, wd - 1);
\r
637 vline(bmp, x + 0, y + 0, ht - 1);
\r
638 bmp->fore_color = 15;
\r
639 hline(bmp, x + 1, y + 1, wd - 3);
\r
640 vline(bmp, x + 1, y + 1, ht - 3);
\r
641 bmp->fore_color = 8;
\r
642 hline(bmp, x + 1, y + ht - 2, wd - 2);
\r
643 vline(bmp, x + wd - 2, y + 1, ht - 2);
\r
644 bmp->fore_color = 0;
\r
645 hline(bmp, x + 0, y + ht - 1, wd);
\r
646 vline(bmp, x + wd - 1, y + 0, ht);
\r
649 /*****************************************************************************
\r
650 *****************************************************************************/
\r
651 static void demo(bmp_t *bmp, const char *title)
\r
653 unsigned x = 10, y = 10, wd = 180, ht = 50;
\r
655 /* erase screen to blue */
\r
656 bmp->fore_color = 1;
\r
657 fill_rect(bmp, 0, 0, bmp->wd, bmp->ht);
\r
658 /* draw gray window with 3D border */
\r
659 bmp->fore_color = 7;
\r
660 fill_rect(bmp, x, y, wd, ht);
\r
661 border3d(bmp, x, y, wd, ht, 0);
\r
662 /* draw white-on-green title bar */
\r
663 bmp->fore_color = 2;
\r
664 fill_rect(bmp, x + 2, y + 2, wd - 4, 10);
\r
665 bmp->back_color = 2;
\r
666 bmp->fore_color = 15;
\r
667 bputs(bmp, x + 3, y + 3, title);
\r
668 /* draw menu bar on existing gray background */
\r
669 bmp->back_color = 7;
\r
670 bmp->fore_color = 0;
\r
671 bputs(bmp, x + 3, y + 13, "File Edit");
\r
672 /* draw white inner area with 3D border */
\r
673 bmp->fore_color = 15;
\r
674 fill_rect(bmp, x + 3, y + 21, wd - 6, ht - 24);
\r
675 border3d(bmp, x + 3, y + 21, wd - 6, ht - 24, 1);
\r
676 /* await key pressed */
\r
679 /*****************************************************************************
\r
680 *****************************************************************************/
\r
683 static const unsigned wd[] =
\r
685 640, 320, 640, 320, 320
\r
687 static const unsigned ht[] =
\r
689 480, 200, 480, 200, 200
\r
691 static const ops_t *ops[] =
\r
693 &g_ops1, &g_ops2, &g_ops4p, &g_ops8, &g_ops8x
\r
695 static const unsigned mode[] =
\r
697 0x11, 5, 0x12, 0x13, 0x13
\r
699 static const char *title[] =
\r
701 "640x480x2", "320x200x4", "640x480x16", "320x200x256",
\r
702 "320x200x256 ModeX"
\r
709 #if defined(__DJGPP__)
\r
710 if(!(_crt0_startup_flags & _CRT0_FLAG_NEARPTR))
\r
712 if(!__djgpp_nearptr_enable())
\r
714 printf("Could not enable nearptr access "
\r
715 "(Windows NT/2000/XP?)\n");
\r
719 for(i = 0; i < sizeof(wd) / sizeof(wd[0]); i++)
\r
721 bmp.raster = FARPTR(0xA000, 0);
\r
726 memset(®s, 0, sizeof(regs)); /* for Watcom C */
\r
727 regs.R_AX = mode[i];
\r
729 /* to make CGA graphics work like other graphics modes... */
\r
730 if(mode[i] == 0x05)
\r
732 /* 1) turn off screwy CGA addressing */
\r
733 outportb(VGA_CRTC_INDEX, 0x17);
\r
734 outportb(VGA_CRTC_DATA, inportb(VGA_CRTC_DATA) | 1);
\r
735 /* 2) turn off doublescan */
\r
736 outportb(VGA_CRTC_INDEX, 9);
\r
737 outportb(VGA_CRTC_DATA, inportb(VGA_CRTC_DATA) & ~0x80);
\r
738 /* 3) move the framebuffer from B800:0000 to A000:0000 */
\r
739 outportb(VGA_GC_INDEX, 6);
\r
740 outportb(VGA_GC_DATA, inportb(VGA_GC_INDEX) & ~0x0C);
\r
742 /* to convert mode 13h to Mode X... */
\r
745 /* 1) turn off Chain-4 addressing */
\r
746 outportb(VGA_SEQ_INDEX, 0x04);
\r
747 outportb(VGA_SEQ_DATA, inportb(VGA_SEQ_DATA) & ~0x08);
\r
748 /* 2) turn off doubleword clocking */
\r
749 outportb(VGA_CRTC_INDEX, 0x14);
\r
750 outportb(VGA_CRTC_DATA, inportb(VGA_CRTC_DATA) & ~0x40);
\r
751 /* 3) turn off word clocking in case it's on */
\r
752 outportb(VGA_CRTC_INDEX, 0x17);
\r
753 outportb(VGA_CRTC_DATA, inportb(VGA_CRTC_DATA) | 0x40);
\r
755 demo(&bmp, title[i]);
\r
757 /* return to text mode */
\r
758 memset(®s, 0, sizeof(regs)); /* for Watcom C */
\r