5 * (C)1993 Ztiff Zox Softwear
\r
7 * Simple graphics library to accompany the article
\r
9 * INTRODUCTION TO MODE X.
\r
11 * This library provides the basic functions for initializing and using
\r
12 * unchained (planar) 256-color VGA modes. Currently supported are:
\r
17 * Functions are provided for:
\r
19 * - initializing one of the available modes
\r
20 * - setting the start address of the VGA refresh data
\r
21 * - setting active and visible display pages
\r
22 * - writing and reading a single pixel to/from video memory
\r
24 * The library is provided as a demonstration only, and is not claimed
\r
25 * to be particularly efficient or suited for any purpose. It has only
\r
26 * been tested with Borland C++ 3.1 by the author. Comments on success
\r
27 * or disaster with other compilers are welcome.
\r
29 * This file is public domain. Do with it whatever you'd like, but
\r
30 * please don't distribute it without the article.
\r
32 * Thanks go out to various helpful netters who spotted the 0xE7 bug
\r
33 * in the set320x240x256() function!
\r
35 * Modified by sparky4 so it can be compiled in open watcom ^^
\r
42 * We 'require' a large data model simply to get rid of explicit 'far'
\r
43 * pointers and compiler specific '_fmemset()' functions and the likes.
\r
45 #if !defined(__COMPACT__)
\r
46 # if !defined(__LARGE__)
\r
47 # if !defined(__HUGE__)
\r
48 # error Large data model required! Try compiling with 'wcc -0 -ml lib.c'.
\r
57 //code from old library!
\r
59 #include "dos_gfx.h"
\r
60 #include "lib\x\modex.h"
\r
63 //color
\82Ä
\82·
\82Æ
\r
67 int bakax = 0, bakay = 0;
\r
68 int xx = rand()&0%320, yy = rand()&0%240, sx = 0, sy = 0;
\r
72 * Comment out the following #define if you don't want the testing main()
\r
78 * Define the port addresses of some VGA registers.
\r
80 #define CRTC_ADDR 0x3d4 /* Base port of the CRT Controller (color) */
\r
82 #define SEQU_ADDR 0x3c4 /* Base port of the Sequencer */
\r
83 #define GRAC_ADDR 0x3ce /* Base port of the Graphics Controller */
\r
87 * Make a far pointer to the VGA graphics buffer segment. Your compiler
\r
88 * might not have the MK_FP macro, but you'll figure something out.
\r
90 byte *vga = (byte *) MK_FP(0xA000, 0);
\r
92 //fontAddr = getFont();
\r
95 * width and height should specify the mode dimensions. widthBytes
\r
96 * specify the width of a line in addressable bytes.
\r
98 unsigned width, height, widthBytes;
\r
101 * actStart specifies the start of the page being accessed by
\r
102 * drawing operations. visStart specifies the contents of the Screen
\r
103 * Start register, i.e. the start of the visible page.
\r
105 unsigned actStart, visStart;
\r
108 * set320x200x256_X()
\r
109 * sets mode 13h, then turns it into an unchained (planar), 4-page
\r
110 * 320x200x256 mode.
\r
112 void set320x200x256_X(void)
\r
116 /* Set VGA BIOS mode 13h: */
\r
118 int86(0x10, &r, &r);
\r
120 /* Turn off the Chain-4 bit (bit 3 at index 4, port 0x3c4): */
\r
121 outpw(SEQU_ADDR, 0x0604);
\r
123 /* Turn off word mode, by setting the Mode Control register
\r
124 of the CRT Controller (index 0x17, port 0x3d4): */
\r
125 outpw(CRTC_ADDR, 0xE317);
\r
127 /* Turn off doubleword mode, by setting the Underline Location
\r
128 register (index 0x14, port 0x3d4): */
\r
129 outpw(CRTC_ADDR, 0x0014);
\r
131 /* Clear entire video memory, by selecting all four planes, then
\r
132 writing 0 to entire segment. */
\r
133 outpw(SEQU_ADDR, 0x0F02);
\r
134 memset(vga+1, 0, 0xffff); /* stupid size_t exactly 1 too small */
\r
137 /* Update the global variables to reflect dimensions of this
\r
138 mode. This is needed by most future drawing operations. */
\r
142 /* Each byte addresses four pixels, so the width of a scan line
\r
143 in *bytes* is one fourth of the number of pixels on a line. */
\r
144 widthBytes = width / 4;
\r
146 /* By default we want screen refreshing and drawing operations
\r
147 to be based at offset 0 in the video segment. */
\r
148 actStart = visStart = 0;
154 Horizontal scrolling is essentially the same as vertical scrolling, all
155 you do is increment or decrement the VGA offset register by 1 instead of
156 80 as with vertical scrolling.
158 However, horizontal scrolling is complicated by two things
160 1. Incrementing the offset register by one actually scrolls by FOUR
161 pixels (and there are FOUR planes on the VGA, what a coincidence)
163 2. You can't draw the image off the screen and then scroll it on
164 because of the way the VGA wraps to the next row every 80 bytes
165 (80 bytes * 4 planes = 320 pixels), if you tried it, you would
166 actually be drawing to the other side of the screen (which is
169 I'll solve these problems one at a time.
171 Firstly, to get the VGA to scroll by only one pixel you use the horizontal
172 pixel panning (HPP) register. This register resides at
177 and in real life, you use it like this
179 ----------------- Pixel Panning ---------------
180 IN PORT 3DAH (this clears an internal
181 flip-flop of the VGA)
183 OUT value TO PORT 3C0H (where "value" is the
184 number of pixels to offset)
185 -----------------------------------------------
194 * setActiveStart() tells our graphics operations which address in video
\r
195 * memory should be considered the top left corner.
\r
197 void setActiveStart(unsigned offset)
\r
203 * setVisibleStart() tells the VGA from which byte to fetch the first
\r
204 * pixel when starting refresh at the top of the screen. This version
\r
205 * won't look very well in time critical situations (games for
\r
206 * instance) as the register outputs are not synchronized with the
\r
207 * screen refresh. This refresh might start when the high byte is
\r
208 * set, but before the low byte is set, which produces a bad flicker.
\r
210 void setVisibleStart(unsigned offset)
\r
213 outpw(CRTC_ADDR, 0x0C); /* set high byte */
\r
214 outpw(CRTC_ADDR+1, visStart >> 8);
\r
215 outpw(CRTC_ADDR, 0x0D); /* set low byte */
\r
216 outpw(CRTC_ADDR+1, visStart & 0xff);
\r
220 * setXXXPage() sets the specified page by multiplying the page number
\r
221 * with the size of one page at the current resolution, then handing the
\r
222 * resulting offset value over to the corresponding setXXXStart()
\r
223 * function. The first page is number 0.
\r
225 void setActivePage(int page)
\r
227 setActiveStart(page * widthBytes * height);
\r
230 void setVisiblePage(int page)
\r
232 setVisibleStart(page * widthBytes * height);
\r
235 void putPixel_X(int x, int y, byte color)
\r
237 /* Each address accesses four neighboring pixels, so set
\r
238 Write Plane Enable according to which pixel we want
\r
239 to modify. The plane is determined by the two least
\r
240 significant bits of the x-coordinate: */
\r
242 outp(0x3c5, 0x01 << (x & 3));
\r
244 /* The offset of the pixel into the video segment is
\r
245 offset = (width * y + x) / 4, and write the given
\r
246 color to the plane we selected above. Heed the active
\r
247 page start selection. */
\r
248 vga[(unsigned)(widthBytes * y) + (x / 4) + actStart] = color;
\r
252 byte getPixel_X(int x, int y)
\r
254 /* Select the plane from which we must read the pixel color: */
\r
255 outpw(GRAC_ADDR, 0x04);
\r
256 outpw(GRAC_ADDR+1, x & 3);
\r
258 return vga[(unsigned)(widthBytes * y) + (x / 4) + actStart];
\r
262 void set320x240x256_X(void)
\r
264 /* Set the unchained version of mode 13h: */
\r
265 set320x200x256_X();
\r
267 /* Modify the vertical sync polarity bits in the Misc. Output
\r
268 Register to achieve square aspect ratio: */
\r
271 /* Modify the vertical timing registers to reflect the increased
\r
272 vertical resolution, and to center the image as good as
\r
274 outpw(0x3D4, 0x2C11); /* turn off write protect */
\r
275 outpw(0x3D4, 0x0D06); /* vertical total */
\r
276 outpw(0x3D4, 0x3E07); /* overflow register */
\r
277 outpw(0x3D4, 0xEA10); /* vertical retrace start */
\r
278 outpw(0x3D4, 0xAC11); /* vertical retrace end AND wr.prot */
\r
279 outpw(0x3D4, 0xDF12); /* vertical display enable end */
\r
280 outpw(0x3D4, 0xE715); /* start vertical blanking */
\r
281 outpw(0x3D4, 0x0616); /* end vertical blanking */
283 /* Update mode info, so future operations are aware of the
\r
290 /*-----------XXXX-------------*/
\r
292 void putColorBox_X(int x, int y, int w, int h, byte color) {
\r
297 for (curx=x; curx<(x+w); curx++) {
\r
298 outp(0x3c5, 0x01 << (curx & 3));
\r
299 drawptr = (unsigned)(widthBytes * y) + (curx / 4) + actStart;
\r
300 for (cury=0; cury<h; cury++) {
\r
301 vga[drawptr] = color;
\r
302 drawptr += widthBytes;
\r
307 void vScroll(int rows)
\r
309 // Scrolling = current start + (rows * bytes in a row)
\r
310 setVisibleStart(visStart + (rows * width));
\r
313 void scrolly(int bong)
\r
322 for(int i=0;i<TILEWH;i++)
329 To implement smooth horizontal scrolling, you would do the following:
331 -------------- Horizontal Scrolling ------------
333 SET HPP TO ( X MOD 4 )
334 SET VGA OFFSET TO ( X/4 )
336 ------------------------------------------------
338 Okay, no problem at all (although I think you might have to fiddle
339 around with the HPP a bit to get it right...try different values and
342 So, the next problem is with drawing the images off the screen where
343 they aren't visible and then scrolling them on!!! As it turns out,
344 there's yet ANOTHER register to accomplish this. This one's called the
345 offset register (no, not the one I was talking about before, that one
346 was actually the "start address" register) and it's at
351 and here's how to use it
353 -------------- Offset Register ---------------
355 OUT value TO PORT 3D5H
356 ----------------------------------------------
358 Now, what my VGA reference says is that this register holds the number
359 of bytes (not pixels) difference between the start address of each row.
360 So, in X-mode it normally contains the value 80 (as we remember,
361 80 bytes * 4 planes = 320 pixels). This register does not affect the
362 VISIBLE width of the display, only the difference between addresses on
365 When we scroll horizontally, we need a little bit of extra working space
366 so we can draw off the edge of the screen.
368 Perhaps a little diagram will clarify it. The following picture is of a
369 standard X-mode addressing scheme with the OFFSET register set to 80.
372 0 0 ========================
380 199 15920 ========================
382 and the next diagram is of a modified addressing scheme with the OFFSET
383 register set to 82 (to give us 4 extra pixels on each side of the screen)
386 0 0 ------========================------
389 .. .. | N S [ VISIBLE ] N S |
390 | O I [ SCREEN ] O I |
394 199 16318 ------========================------
398 As with vertical scrolling, however, you still have the problem of when
399 you reach the bottom of page 4...and it's fixed in the same manner.
401 I haven't actually managed to get infinite horizontal scrolling working,
402 but the method I have just stated will give you a horizontal scrolling
403 range of over 200 screens!!!! So if you need more (which is extremely
404 unlikely), figure it out yourself.
410 To do both horizontal and vertical scrolling, all you have to do is combine
411 the two methods with a few little extras (it's always the way isn't it).
413 You have to start off with the original screen on the current page and the
414 next page as well. When you scroll horizontally, you have to draw the edge
415 that's coming in to the screen to BOTH pages (that means you'll be drawing
416 the incoming edge twice, once for each page). You do this so that when you
417 have scrolled vertically down through a complete page, you can jump back
418 to the first page and it will (hopefully) have an identical copy, and you
419 can then continue scrolling again.
421 I'm sorry about this being so confusing but it's a bit difficult to explain.
425 //---------------------------------------------------
\r
427 // Use the bios to get the address of the 8x8 font
\r
429 // You need a font if you are going to draw text.
\r
438 memset(&rg, 0, sizeof(rg));
\r
447 return (int far *)MK_FP(seg, off);
\r
450 void drawChar(int x, int y, int color, byte c)
\r
454 int far *font = getFont() + (c * 8);
\r
456 for (i = 0; i < 8; i++)
\r
459 for (j = 0; j < 8; j++)
\r
463 //pixel(x + j, y + i, color);
\r
464 putPixel_X(x + j, y + i, color);
\r
472 void drawText(int x, int y, int color, byte string)
\r
476 drawChar(x, y, color, string);
\r
482 /////////////////////////////////////////////////////////////////////////////
\r
484 // setvideo() - This function Manages the video modes //
\r
486 /////////////////////////////////////////////////////////////////////////////
\r
487 void setvideo(/*byte mode, */int vq){
\r
488 union REGS in, out;
\r
490 if(!vq){ // deinit the video
\r
491 // change to the video mode we were in before we switched to mode 13h
\r
493 in.h.al = old_mode;
\r
494 int86(0x10, &in, &out);
\r
496 }else if(vq == 1){ // init the video
\r
497 // get old video mode
\r
499 int86(0x10, &in, &out);
\r
500 old_mode = out.h.al;
\r
503 set320x240x256_X();
\r
507 /////////////////////////////////////////////////////////////////////////////
\r
509 // cls() - This clears the screen to the specified color, on the VGA or on //
\r
510 // the Virtual screen. //
\r
512 /////////////////////////////////////////////////////////////////////////////
\r
513 void cls(byte color, byte *Where){
\r
514 _fmemset(Where, color, width*(height*17));
\r
517 //color
\82Ä
\82·
\82Æ
\r
519 if(gq < NUM_COLORS){
\r
526 //color
\82Ä
\82·
\82Æ
\r
529 //---- cls(gq, vaddr);
\r
536 //slow spectrum down
\r
540 //plotpixel(xx, yy, coor, vga);
\r
541 //ppf(sx, sy, coor, vga);
\r
542 putPixel_X(sx, sy, coor);
\r
543 //printf("%d %d %d %d\n", sx, sy, svq, coor);
\r
548 if(svq == 7) coor++;
\r
549 if(sy == height && svq == 8) coor = rand()%NUM_COLORS;
\r
554 /*-----------ding-------------*/
\r
561 if((height)<yy<(height*2)){
\r
565 if((height*2)<yy<(height*3)){
\r
571 //++++ if(q <= 4 && q!=2 && gq == BONK-1) coor = rand()%HGQ;
\r
575 ) && gq == BONK-1){
\r
576 if(coor < HGQ && coor < LGQ) coor = LGQ;
\r
580 bakax = rand()%3; bakay = rand()%3;
\r
584 if(q == 8){ colorz(); return gq; }else
\r
585 if(q == 10){ ssd(q); /*printf("%d\n", coor);*/ }else
\r
586 if(q == 5){ colortest(); return gq; }else
\r
587 if(q == 11){ colorz(); delay(100); return gq; }
\r
589 coor = rand()%NUM_COLORS;
\r
590 //---- cls(coor, vaddr);
\r
595 if(q == 7 || q== 9){
\r
598 if(q == 9){ ssd(q); coor++; }
\r
602 if((q<5 && gq<BONK) || (q==16 && gq<BONK)){ // the number variable make the colors more noticable
\r
604 if(xx==width){bakax=0;}
\r
605 if(xx==0){bakax=1;}
\r
606 if(yy==height){bakay=0;}
\r
607 if(yy==0){bakay=1;}
\r
609 if(xx!=width||yy!=height){
\r
610 if(xx==0){bakax=1;bakay=-1;d3y=1;}
\r
611 if(yy==0){bakax=1;bakay=0;d3y=1;}
\r
612 if(xx==width){bakax=-1;bakay=-1;d3y=1;}
\r
613 if(yy==height){bakax=1;bakay=0;d3y=1;}
\r
614 }else if(xx==width&&yy==height) xx=yy=0;
\r
663 if(yy<0) yy=(height*3);
\r
665 if(yy>(height*3)) yy=0;
\r
668 //interesting effects
\r
674 putPixel_X(tx, ty, coor);
\r
675 //drawrect(tx, ty, tx+TILEWH, ty+TILEWH, coor);
\r
676 //printf("%d %d %d %d %d %d\n", xx, yy, tx, ty, TILEWH);
\r
679 //---- ppf(xx, yy, coor, vga);
\r
680 }else /*if(xx>=0 && xx<width && yy>=0 && yy<(height*3))*/{
\r
681 putColorBox_X(xx, yy, TILEWH, TILEWH, coor);
\r
682 //++++0000 putPixel_X(xx, yy, coor);
\r
685 //---- if(q==2) ppf(rand()%, rand()%height, 0, vga);
\r
686 if(q==2) putColorBox_X(rand()%width, rand()%(height*3), TILEWH, TILEWH, 0);
\r
687 if(q==16) putPixel_X(rand()%width, rand()%(height*3), 0);
\r
688 if(q==2||q==4||q==16){ bakax = rand()%3; bakay = rand()%3; }
\r
690 //if(xx<0||xx>320||yy<0||yy>240)
\r
691 // printf("%d %d %d %d %d %d\n", xx, yy, coor, bakax, bakay, getPixel_X(xx,yy));
\r
692 // printf("%d\n", getPixel_X(xx,yy));
\r
694 // drawText(0, 0, 15, getPixel_X(xx,yy));
\r
701 * The library testing routines follows below.
\r
712 int p, x, y, pages;
\r
714 /* This is the way to calculate the number of pages available. */
\r
715 pages = 65536L/(widthBytes*height); // apparently this takes the A000 address
\r
717 printf("%d\n", pages);
\r
719 for (p = 0; p <= pages; ++p)
\r
723 /* On each page draw a single colored border, and dump the palette
\r
724 onto a small square about the middle of the page. */
\r
727 for (x = 0; x <= width; ++x)
\r
729 putPixel_X(x, 0, p+1);
\r
730 if(p!=pages) putPixel_X(x, height-1, p+1);
\r
731 else putPixel_X(x, 99-1, p+1);
\r
734 for (y = 0; y <= height; ++y)
\r
736 putPixel_X(0, y, p+1);
\r
737 if(p!=pages) putPixel_X(width-1, y, p+1);
\r
738 else putPixel_X(width-1, y, p+1);
\r
741 for (x = 0; x < 16; ++x)
\r
742 for (y = 0; y < 16; ++y)
\r
743 putPixel_X(x+(p+2)*16, y+(p+2)*16, x + y*16);
\r
746 // drawText(0, 0, 15, p);
\r
750 /* Each pages will now contain a different image. Let the user cycle
\r
751 through all the pages by pressing a key. */
\r
752 for (p = 0; p <= pages; ++p)
\r
755 //drawText(0, 240, 15, "bakapi");
\r
762 * Library test (program) entry point.
\r
769 d=1; // switch variable
\r
770 key=4; // default screensaver number
\r
771 // puts("First, have a look at the 320x200 mode. I will draw some rubbish");
\r
772 // puts("on all of the four pages, then let you cycle through them by");
\r
773 // puts("hitting a key on each page.");
\r
774 // puts("Press a key when ready...");
\r
779 // puts("Then, check out Mode X, 320x240 with 3 (and a half) pages.");
\r
780 // puts("Press a key when ready...");
\r
788 /*while(d!=0){ // on!
\r
789 if(!kbhit()){ // conditions of screen saver
\r
793 // user imput switch
\r
794 printf("Enter 1, 2, 3, 4, or 6 to run a screensaver, or enter 5 to quit.\n", getch()); // prompt the user
\r
796 //if(key==3){xx=yy=0;} // crazy screen saver wwww
\r
801 while(!kbhit()){ // conditions of screen saver
\r
804 //end of screen savers
\r
807 while(!kbhit()){ // conditions of screen saver
\r
814 puts("Where to next? It's your move! wwww");
\r
815 printf("bakapi ver. 1.04.09.02\nis made by sparky4
\81i
\81\86\83Ö
\81\85\81j feel free to use it ^^\nLicence: GPL v2\n");
\r