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
62 //color
\82Ä
\82·
\82Æ
\r
66 int bakax = 0, bakay = 0;
\r
67 cord xx = rand()&0%320, yy = rand()&0%240, sx = 0, sy = 0;
\r
71 * Comment out the following #define if you don't want the testing main()
\r
77 * Define the port addresses of some VGA registers.
\r
79 #define CRTC_ADDR 0x3d4 /* Base port of the CRT Controller (color) */
\r
81 #define SEQU_ADDR 0x3c4 /* Base port of the Sequencer */
\r
82 #define GRAC_ADDR 0x3ce /* Base port of the Graphics Controller */
\r
83 #define STATUS_ADDR 0x3DA
\r
85 unsigned char *RowsX[600];
\r
86 unsigned char write_plane, read_plane;
\r
87 unsigned short text_mask[16] = { 0x0002, 0x0102, 0x0202, 0x0302,
\r
88 0x0402, 0x0502, 0x0602, 0x0702,
\r
89 0x0802, 0x0902, 0x0A02, 0x0B02,
\r
90 0x0C02, 0x0D02, 0x0E02, 0x0F02 };
\r
94 * Make a far pointer to the VGA graphics buffer segment. Your compiler
\r
95 * might not have the MK_FP macro, but you'll figure something out.
\r
97 byte *vga = (byte *) MK_FP(0xA000, 0);
\r
101 * width and height should specify the mode dimensions. widthBytes
\r
102 * specify the width of a line in addressable bytes.
\r
104 unsigned width, height, widthBytes;
\r
107 * actStart specifies the start of the page being accessed by
\r
108 * drawing operations. visStart specifies the contents of the Screen
\r
109 * Start register, i.e. the start of the visible page.
\r
111 unsigned actStart, visStart;
\r
114 * set320x200x256_X()
\r
115 * sets mode 13h, then turns it into an unchained (planar), 4-page
\r
116 * 320x200x256 mode.
\r
118 void set320x200x256_X(void)
\r
122 /* Set VGA BIOS mode 13h: */
\r
124 int86(0x10, &r, &r);
\r
126 /* Turn off the Chain-4 bit (bit 3 at index 4, port 0x3c4): */
\r
127 outpw(SEQU_ADDR, 0x0604);
\r
129 /* Turn off word mode, by setting the Mode Control register
\r
130 of the CRT Controller (index 0x17, port 0x3d4): */
\r
131 outpw(CRTC_ADDR, 0xE317);
\r
133 /* Turn off doubleword mode, by setting the Underline Location
\r
134 register (index 0x14, port 0x3d4): */
\r
135 outpw(CRTC_ADDR, 0x0014);
\r
137 /* Clear entire video memory, by selecting all four planes, then
\r
138 writing 0 to entire segment. */
\r
139 outpw(SEQU_ADDR, 0x0F02);
\r
140 memset(vga+1, 0, 0xffff); /* stupid size_t exactly 1 too small */
\r
143 /* Update the global variables to reflect dimensions of this
\r
144 mode. This is needed by most future drawing operations. */
\r
148 /* Each byte addresses four pixels, so the width of a scan line
\r
149 in *bytes* is one fourth of the number of pixels on a line. */
\r
150 widthBytes = width / 4;
\r
152 /* By default we want screen refreshing and drawing operations
\r
153 to be based at offset 0 in the video segment. */
\r
154 actStart = visStart = 0;
\r
157 --------------------
\r
158 HORIZONTAL SCROLLING
\r
159 --------------------
\r
160 Horizontal scrolling is essentially the same as vertical scrolling, all
\r
161 you do is increment or decrement the VGA offset register by 1 instead of
\r
162 80 as with vertical scrolling.
\r
164 However, horizontal scrolling is complicated by two things
\r
166 1. Incrementing the offset register by one actually scrolls by FOUR
\r
167 pixels (and there are FOUR planes on the VGA, what a coincidence)
\r
169 2. You can't draw the image off the screen and then scroll it on
\r
170 because of the way the VGA wraps to the next row every 80 bytes
\r
171 (80 bytes * 4 planes = 320 pixels), if you tried it, you would
\r
172 actually be drawing to the other side of the screen (which is
\r
175 I'll solve these problems one at a time.
\r
177 Firstly, to get the VGA to scroll by only one pixel you use the horizontal
\r
178 pixel panning (HPP) register. This register resides at
\r
183 and in real life, you use it like this
\r
185 ----------------- Pixel Panning ---------------
\r
186 IN PORT 3DAH (this clears an internal
\r
187 flip-flop of the VGA)
\r
188 OUT 13H TO PORT 3C0H
\r
189 OUT value TO PORT 3C0H (where "value" is the
\r
190 number of pixels to offset)
\r
191 -----------------------------------------------
\r
194 //mxSetVirtualScreen(480,360);
\r
198 * setActiveStart() tells our graphics operations which address in video
\r
199 * memory should be considered the top left corner.
\r
201 void setActiveStart(unsigned offset)
\r
207 * setVisibleStart() tells the VGA from which byte to fetch the first
\r
208 * pixel when starting refresh at the top of the screen. This version
\r
209 * won't look very well in time critical situations (games for
\r
210 * instance) as the register outputs are not synchronized with the
\r
211 * screen refresh. This refresh might start when the high byte is
\r
212 * set, but before the low byte is set, which produces a bad flicker.
\r
214 void setVisibleStart(unsigned offset)
\r
217 outpw(CRTC_ADDR, 0x0C); /* set high byte */
\r
218 outpw(CRTC_ADDR+1, visStart >> 8);
\r
219 outpw(CRTC_ADDR, 0x0D); /* set low byte */
\r
220 outpw(CRTC_ADDR+1, visStart & 0xff);
\r
224 * setXXXPage() sets the specified page by multiplying the page number
\r
225 * with the size of one page at the current resolution, then handing the
\r
226 * resulting offset value over to the corresponding setXXXStart()
\r
227 * function. The first page is number 0.
\r
229 void setActivePage(int page)
\r
231 setActiveStart(page * widthBytes * height);
\r
234 void setVisiblePage(int page)
\r
236 setVisibleStart(page * widthBytes * height);
\r
239 void putPixel_X(int x, int y, byte color)
\r
241 /* Each address accesses four neighboring pixels, so set
\r
242 Write Plane Enable according to which pixel we want
\r
243 to modify. The plane is determined by the two least
\r
244 significant bits of the x-coordinate: */
\r
246 outp(0x3c5, 0x01 << (x & 3));
\r
248 /* The offset of the pixel into the video segment is
\r
249 offset = (width * y + x) / 4, and write the given
\r
250 color to the plane we selected above. Heed the active
\r
251 page start selection. */
\r
252 vga[(unsigned)(widthBytes * y) + (x / 4) + actStart] = color;
\r
256 byte getPixel_X(int x, int y)
\r
258 /* Select the plane from which we must read the pixel color: */
\r
259 outpw(GRAC_ADDR, 0x04);
\r
260 outpw(GRAC_ADDR+1, x & 3);
\r
262 return vga[(unsigned)(widthBytes * y) + (x / 4) + actStart];
\r
266 void set320x240x256_X(void)
\r
268 /* Set the unchained version of mode 13h: */
\r
269 set320x200x256_X();
\r
271 /* Modify the vertical sync polarity bits in the Misc. Output
\r
272 Register to achieve square aspect ratio: */
\r
275 /* Modify the vertical timing registers to reflect the increased
\r
276 vertical resolution, and to center the image as good as
\r
278 outpw(0x3D4, 0x2C11); /* turn off write protect */
\r
279 outpw(0x3D4, 0x0D06); /* vertical total */
\r
280 outpw(0x3D4, 0x3E07); /* overflow register */
\r
281 outpw(0x3D4, 0xEA10); /* vertical retrace start */
\r
282 outpw(0x3D4, 0xAC11); /* vertical retrace end AND wr.prot */
\r
283 outpw(0x3D4, 0xDF12); /* vertical display enable end */
\r
284 outpw(0x3D4, 0xE715); /* start vertical blanking */
\r
285 outpw(0x3D4, 0x0616); /* end vertical blanking */
\r
287 /* Update mode info, so future operations are aware of the
\r
294 /*-----------XXXX-------------*/
\r
296 /////////////////////////////////////////////////////////////////////////////
\r
298 // WaitRetrace() - This waits until you are in a Verticle Retrace. //
\r
300 /////////////////////////////////////////////////////////////////////////////
\r
301 void wait_for_retrace(void)
\r
303 while (!(inp(STATUS_ADDR) & 0x08));
\r
307 //king_crimson's code
\r
308 void putColorBox_X(int x, int y, int w, int h, byte color) {
\r
313 for (curx=x; curx<(x+w); curx++) {
\r
314 outp(0x3c5, 0x01 << (curx & 3));
\r
315 drawptr = (unsigned)(widthBytes * y) + (curx / 4) + actStart;
\r
316 for (cury=0; cury<h; cury++) {
\r
317 vga[drawptr] = color;
\r
318 drawptr += widthBytes;
\r
323 void vScroll(int rows)
\r
325 // Scrolling = current start + (rows * bytes in a row)
\r
326 setVisibleStart(visStart + (rows * width));
\r
329 void scrolly(int bongy)
\r
337 for(int ti=0;ti<TILEWH;ti++)
\r
344 //king_crimson's code
\r
345 void hScroll(int Cols) {
\r
346 wait_for_retrace();
\r
348 outp(0x3C0, Cols & 3);
\r
350 outp(0x3D5, Cols >> 2);
\r
352 //setVisibleStart(visStart + (Cols * height));
\r
353 setVisibleStart(visStart + (Cols * width));
\r
356 /////////////////////////////////////////////////////////////////////////////
\r
358 // setvideo() - This function Manages the video modes //
\r
360 /////////////////////////////////////////////////////////////////////////////
\r
361 void setvideo(/*byte mode, */int vq){
\r
362 union REGS in, out;
\r
364 if(!vq){ // deinit the video
\r
365 // change to the video mode we were in before we switched to mode 13h
\r
366 mxSetMode( MX_TEXT );
\r
368 //in.h.al = old_mode;
\r
369 //int86(0x10, &in, &out);
372 }else if(vq == 1){ // init the video
\r
373 // get old video mode
\r
375 //int86(0x10, &in, &out);
\r
376 //old_mode = out.h.al;
\r
380 mxSetMode( MX_320x240 );
\r
381 mxSetVirtualScreen( 480, 360 );
\r
382 //set320x240x256_X();
\r
383 //mxSetMode(MX_320x240);
\r
384 //mxSetVirtualScreen(560,420);
\r
385 //mxSetVirtualScreen((640-TILEWH),(480-TILEWH));
\r
386 //mxSetClip( TRUE );
\r
390 /////////////////////////////////////////////////////////////////////////////
\r
392 // cls() - This clears the screen to the specified color, on the VGA or on //
\r
393 // the Virtual screen. //
\r
395 /////////////////////////////////////////////////////////////////////////////
\r
396 void cls(byte color, byte *Where){
\r
397 _fmemset(Where, color, width*(height*17));
\r
400 //color
\82Ä
\82·
\82Æ
\r
402 if(gq < NUM_COLORS){
\r
409 //color
\82Ä
\82·
\82Æ
\r
412 //---- cls(gq, vaddr);
\r
419 //slow spectrum down
\r
423 //plotpixel(xx, yy, coor, vga);
\r
424 //ppf(sx, sy, coor, vga);
\r
425 putPixel_X(sx, sy, coor);
\r
426 //printf("%d %d %d %d\n", sx, sy, svq, coor);
\r
431 if(svq == 7) coor++;
\r
432 if(sy == height && svq == 8) coor = rand()%NUM_COLORS;
\r
437 /*-----------ding-------------*/
\r
444 if((height)<yy<(height*2)){
\r
448 if((height*2)<yy<(height*3)){
\r
454 //++++ if(q <= 4 && q!=2 && gq == BONK-1) coor = rand()%HGQ;
\r
459 if(coor < HGQ && coor < LGQ) coor = LGQ;
\r
463 bakax = rand()%3; bakay = rand()%3;
\r
467 if(q==8){ colorz(); return gq; }else
\r
468 if(q==10){ ssd(q); /*printf("%d\n", coor);*/ }else
\r
469 if(q==5){ colortest(); return gq; }else
\r
470 if(q==11){ colorz(); delay(100); return gq; }
\r
472 coor = rand()%NUM_COLORS;
\r
473 //---- cls(coor, vaddr);
\r
481 if(q == 9){ ssd(q); coor++; }
\r
485 if((q<5 && gq<BONK) || (q==16 && gq<BONK)){ // the number variable make the colors more noticable
\r
487 if(xx==width){bakax=0;}
\r
488 if(xx==0){bakax=1;}
\r
489 if(yy==height){bakay=0;}
\r
490 if(yy==0){bakay=1;}
\r
492 if(xx!=width||yy!=height){
\r
493 if(xx==0){bakax=1;bakay=-1;d3y=1;}
\r
494 if(yy==0){bakax=1;bakay=0;d3y=1;}
\r
495 if(xx==width){bakax=-1;bakay=-1;d3y=1;}
\r
496 if(yy==height){bakax=1;bakay=0;d3y=1;}
\r
497 }else if(xx==width&&yy==height) xx=yy=0;
\r
549 // if(xx<0) xx=(width-TILEWH);
\r
550 // if(yy<0) yy=(height-TILEWH);
\r
551 // if(xx>(width-TILEWH)) xx=0;
\r
552 // if(yy>(height-TILEWH)) yy=0;
\r
555 //interesting effects
\r
561 putPixel_X(tx, ty, coor);
\r
562 //drawrect(tx, ty, tx+TILEWH, ty+TILEWH, coor);
\r
563 //printf("%d %d %d %d %d %d\n", xx, yy, tx, ty, TILEWH);
\r
566 //---- ppf(xx, yy, coor, vga);
\r
567 }else /*if(xx>=0 && xx<width && yy>=0 && yy<(height*3))*/{
\r
568 // putColorBox_X(xx, yy, TILEWH, TILEWH, coor);
\r
570 // putPixel_X(xx, yy, coor);
571 mxPutPixel(xx, yy, coor);
\r
574 //---- if(q==2) ppf(rand()%, rand()%height, 0, vga);
\r
575 // if(q==2) putColorBox_X(rand()%width, rand()%(height*3), TILEWH, TILEWH, 0);
\r
577 if(q==2) putPixel_X(rand()%width, rand()%(height*3), 0);
\r
578 if(q==16) putPixel_X(rand()%width, rand()%(height*3), 0);
\r
579 if(q==2||q==4||q==16){ bakax = rand()%3; bakay = rand()%3; }
\r
581 //if(xx<0||xx>320||yy<0||yy>(height*3))
\r
582 // printf("%d %d %d %d %d %d\n", xx, yy, coor, bakax, bakay, getPixel_X(xx,yy));
\r
583 // printf("%d\n", getPixel_X(xx,yy));
\r
585 // drawText(0, 0, 15, getPixel_X(xx,yy));
\r
592 * The library testing routines follows below.
\r
603 int p, x, y, pages;
\r
605 /* This is the way to calculate the number of pages available. */
\r
606 pages = 65536L/(widthBytes*height); // apparently this takes the A000 address
\r
607 // if(height==240) pages++;
\r
609 // printf("%d\n", pages);
\r
611 for (p = 0; p <= pages; ++p)
\r
615 /* On each page draw a single colored border, and dump the palette
\r
616 onto a small square about the middle of the page. */
\r
619 for (x = 0; x <= width; ++x)
\r
621 putPixel_X(x, 0, p+1);
\r
622 if(p!=pages) putPixel_X(x, height-1, p+1);
\r
623 else if(height==240) putPixel_X(x, 99-1, p+1);
\r
626 for (y = 0; y <= height; ++y)
\r
628 putPixel_X(0, y, p+1);
\r
629 if(p!=pages) putPixel_X(width-1, y, p+1);
\r
630 else if(height==240) putPixel_X(width-1, y, p+1);
\r
633 for (x = 0; x < TILEWH; ++x)
\r
634 for (y = 0; y < TILEWH; ++y)
\r
635 putPixel_X(x+(p+2)*16, y+(p+2)*TILEWH, x + y*TILEWH);
\r
640 /* Each pages will now contain a different image. Let the user cycle
\r
641 through all the pages by pressing a key. */
\r
642 for (p = 0; p < pages; ++p)
\r
651 * Library test (program) entry point.
\r
659 d=1; // switch variable
\r
660 key=4; // default screensaver number
\r
663 // puts("First, have a look at the 320x200 mode. I will draw some rubbish");
\r
664 // puts("on all of the four pages, then let you cycle through them by");
\r
665 // puts("hitting a key on each page.");
\r
666 // puts("Press a key when ready...");
\r
671 // puts("Then, check out Mode X, 320x240 with 3 (and a half) pages.");
\r
672 // puts("Press a key when ready...");
\r
677 /*temp = loadfontX("vga8x8.fnt");
\r
680 putstringX(0, 0, "bakapi!", 2);
\r
685 /*while(d!=0){ // on!
\r
686 if(!kbhit()){ // conditions of screen saver
\r
690 // user imput switch
\r
691 printf("Enter 1, 2, 3, 4, or 6 to run a screensaver, or enter 5 to quit.\n", getch()); // prompt the user
\r
693 //if(key==3){xx=yy=0;} // crazy screen saver wwww
\r
698 while(!kbhit()){ // conditions of screen saver
\r
701 //end of screen savers
\r
705 while(!kbhit()){ // conditions of screen saver
\r
710 for(int i=0;i<TILEWH;i++){
\r
719 printf("wwww\n%dx%d\n", width,height);
\r
720 printf("[%d]\n", mxGetVersion());
\r
721 puts("where to next? It's your move! wwww");
\r
722 printf("bakapi ver. 1.04.09.04\nis made by sparky4
\81i
\81\86\83Ö
\81\85\81j feel free to use it ^^\nLicence: GPL v2\n");
\r