-\r
-/*\r
- * Define the port addresses of some VGA registers.\r
- */\r
-#define CRTC_ADDR 0x3d4 /* Base port of the CRT Controller (color) */\r
-\r
-#define SEQU_ADDR 0x3c4 /* Base port of the Sequencer */\r
-#define GRAC_ADDR 0x3ce /* Base port of the Graphics Controller */\r
-#define STATUS_ADDR 0x3DA\r
-\r
-unsigned char *RowsX[600];\r
-unsigned char write_plane, read_plane;\r
-unsigned short text_mask[16] = { 0x0002, 0x0102, 0x0202, 0x0302,\r
- 0x0402, 0x0502, 0x0602, 0x0702,\r
- 0x0802, 0x0902, 0x0A02, 0x0B02,\r
- 0x0C02, 0x0D02, 0x0E02, 0x0F02 };\r
-\r
-\r
-/*\r
- * Make a far pointer to the VGA graphics buffer segment. Your compiler\r
- * might not have the MK_FP macro, but you'll figure something out.\r
- */\r
-byte *vga = (byte *) MK_FP(0xA000, 0);\r
-\r
-\r
-/*\r
- * width and height should specify the mode dimensions. widthBytes\r
- * specify the width of a line in addressable bytes.\r
- */\r
-unsigned width, height, widthBytes;\r
-\r
-/*\r
- * actStart specifies the start of the page being accessed by\r
- * drawing operations. visStart specifies the contents of the Screen\r
- * Start register, i.e. the start of the visible page.\r
- */\r
-unsigned actStart, visStart;\r
-\r
-/*\r
- * set320x200x256_X()\r
- * sets mode 13h, then turns it into an unchained (planar), 4-page\r
- * 320x200x256 mode.\r
- */\r
-void set320x200x256_X(void)\r
- {\r
- union REGS r;\r
-\r
- /* Set VGA BIOS mode 13h: */\r
- r.x.ax = 0x0013;\r
- int86(0x10, &r, &r);\r
-\r
- /* Turn off the Chain-4 bit (bit 3 at index 4, port 0x3c4): */\r
- outpw(SEQU_ADDR, 0x0604);\r
-\r
- /* Turn off word mode, by setting the Mode Control register\r
- of the CRT Controller (index 0x17, port 0x3d4): */\r
- outpw(CRTC_ADDR, 0xE317);\r
-\r
- /* Turn off doubleword mode, by setting the Underline Location\r
- register (index 0x14, port 0x3d4): */\r
- outpw(CRTC_ADDR, 0x0014);\r
-\r
- /* Clear entire video memory, by selecting all four planes, then\r
- writing 0 to entire segment. */\r
- outpw(SEQU_ADDR, 0x0F02);\r
- memset(vga+1, 0, 0xffff); /* stupid size_t exactly 1 too small */\r
- vga[0] = 0;\r
-\r
- /* Update the global variables to reflect dimensions of this\r
- mode. This is needed by most future drawing operations. */\r
- width = 320;\r
- height = 200;\r
-\r
- /* Each byte addresses four pixels, so the width of a scan line\r
- in *bytes* is one fourth of the number of pixels on a line. */\r
- widthBytes = width / 4;\r
-\r
- /* By default we want screen refreshing and drawing operations\r
- to be based at offset 0 in the video segment. */\r
- actStart = visStart = 0;\r
-\r
- /*\r
---------------------\r
-HORIZONTAL SCROLLING\r
---------------------\r
-Horizontal scrolling is essentially the same as vertical scrolling, all\r
-you do is increment or decrement the VGA offset register by 1 instead of\r
-80 as with vertical scrolling.\r
-\r
-However, horizontal scrolling is complicated by two things\r
-\r
- 1. Incrementing the offset register by one actually scrolls by FOUR\r
- pixels (and there are FOUR planes on the VGA, what a coincidence)\r
-\r
- 2. You can't draw the image off the screen and then scroll it on\r
- because of the way the VGA wraps to the next row every 80 bytes\r
- (80 bytes * 4 planes = 320 pixels), if you tried it, you would\r
- actually be drawing to the other side of the screen (which is\r
- entirely visible)\r
-\r
-I'll solve these problems one at a time.\r
-\r
-Firstly, to get the VGA to scroll by only one pixel you use the horizontal\r
-pixel panning (HPP) register. This register resides at\r
-\r
- PORT: 3C0H\r
- INDEX: 13h\r
-\r
-and in real life, you use it like this\r
-\r
------------------ Pixel Panning ---------------\r
-IN PORT 3DAH (this clears an internal\r
- flip-flop of the VGA)\r
-OUT 13H TO PORT 3C0H\r
-OUT value TO PORT 3C0H (where "value" is the\r
- number of pixels to offset)\r
------------------------------------------------\r
-*/\r
-\r
-mxSetVirtualScreen(480,360);\r
- }\r
-\r
-/*\r
- * setActiveStart() tells our graphics operations which address in video\r
- * memory should be considered the top left corner.\r
- */\r
-void setActiveStart(unsigned offset)\r
- {\r
- actStart = offset;\r
- }\r
-\r
-/*\r
- * setVisibleStart() tells the VGA from which byte to fetch the first\r
- * pixel when starting refresh at the top of the screen. This version\r
- * won't look very well in time critical situations (games for\r
- * instance) as the register outputs are not synchronized with the\r
- * screen refresh. This refresh might start when the high byte is\r
- * set, but before the low byte is set, which produces a bad flicker.\r
- */\r
-void setVisibleStart(unsigned offset)\r
- {\r
- visStart = offset;\r
- outpw(CRTC_ADDR, 0x0C); /* set high byte */\r
- outpw(CRTC_ADDR+1, visStart >> 8);\r
- outpw(CRTC_ADDR, 0x0D); /* set low byte */\r
- outpw(CRTC_ADDR+1, visStart & 0xff);\r
- }\r
-\r
-/*\r
- * setXXXPage() sets the specified page by multiplying the page number\r
- * with the size of one page at the current resolution, then handing the\r
- * resulting offset value over to the corresponding setXXXStart()\r
- * function. The first page is number 0.\r
- */\r
-void setActivePage(int page)\r
- {\r
- setActiveStart(page * widthBytes * height);\r
- }\r
-\r
-void setVisiblePage(int page)\r
- {\r
- setVisibleStart(page * widthBytes * height);\r
- }\r
-\r
-void putPixel_X(int x, int y, byte color)\r
- {\r
- /* Each address accesses four neighboring pixels, so set\r
- Write Plane Enable according to which pixel we want\r
- to modify. The plane is determined by the two least\r
- significant bits of the x-coordinate: */\r
- outp(0x3c4, 0x02);\r
- outp(0x3c5, 0x01 << (x & 3));\r
-\r
- /* The offset of the pixel into the video segment is\r
- offset = (width * y + x) / 4, and write the given\r
- color to the plane we selected above. Heed the active\r
- page start selection. */\r
- vga[(unsigned)(widthBytes * y) + (x / 4) + actStart] = color;\r
-\r
- }\r
-\r
-byte getPixel_X(int x, int y)\r
- {\r
- /* Select the plane from which we must read the pixel color: */\r
- outpw(GRAC_ADDR, 0x04);\r
- outpw(GRAC_ADDR+1, x & 3);\r
-\r
- return vga[(unsigned)(widthBytes * y) + (x / 4) + actStart];\r
-\r
- }\r
-\r
-void set320x240x256_X(void)\r
- {\r
- /* Set the unchained version of mode 13h: */\r
- set320x200x256_X();\r
-\r
- /* Modify the vertical sync polarity bits in the Misc. Output\r
- Register to achieve square aspect ratio: */\r
- outp(0x3C2, 0xE3);\r
-\r
- /* Modify the vertical timing registers to reflect the increased\r
- vertical resolution, and to center the image as good as\r
- possible: */\r
- outpw(0x3D4, 0x2C11); /* turn off write protect */\r
- outpw(0x3D4, 0x0D06); /* vertical total */\r
- outpw(0x3D4, 0x3E07); /* overflow register */\r
- outpw(0x3D4, 0xEA10); /* vertical retrace start */\r
- outpw(0x3D4, 0xAC11); /* vertical retrace end AND wr.prot */\r
- outpw(0x3D4, 0xDF12); /* vertical display enable end */\r
- outpw(0x3D4, 0xE715); /* start vertical blanking */\r
- outpw(0x3D4, 0x0616); /* end vertical blanking */\r
-\r
- /* Update mode info, so future operations are aware of the\r
- resolution */\r
- height = 240;\r
-\r
- }\r
-\r
-\r
-/*-----------XXXX-------------*/\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// //\r
-// WaitRetrace() - This waits until you are in a Verticle Retrace. //\r
-// //\r
-/////////////////////////////////////////////////////////////////////////////\r
-void wait_for_retrace(void)\r
-{\r
- while (!(inp(STATUS_ADDR) & 0x08));\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// //\r
-// MoveTo() - This moves to position X*4 on a chain 4 screen. //\r
-// Note: As soon as I find documentation, this function //\r
-// will be better documented. - Snowman //\r
-// //\r
-/////////////////////////////////////////////////////////////////////////////\r
-/*\r
-void MoveTo (word X, word Y) {\r
-\r
-// word O = Y*SIZE*2+X;\r
- word O = Y*widthBytes*2+X;\r
-\r
- asm {\r
- mov bx, [O]\r
- mov ah, bh\r
- mov al, 0x0C\r
-\r
- mov dx, 0x3D4\r
- out dx, ax\r
-\r
- mov ah, bl\r
- mov al, 0x0D\r
- mov dx, 0x3D4\r
- out dx, ax\r
- }\r
-\r
-;-----------------------------------------------------------\r
-;\r
-; MXPN.ASM - Panning function\r
-; Copyright (c) 1993,1994 by Alessandro Scotti\r
-;\r
-;-----------------------------------------------------------\r
-;WARN PRO\r
-INCLUDE MODEX.DEF\r
-\r
-PUBLIC mxPan\r
-\r
-EXTRN mxWaitDisplay : FAR\r
-EXTRN mxStartAddress : FAR\r
-\r
-MX_TEXT SEGMENT USE16 PARA PUBLIC 'CODE'\r
- ASSUME cs:MX_TEXT, ds:NOTHING, es:NOTHING\r
-\r
-EXTRN mx_BytesPerLine : WORD\r
-\r
-;-----------------------------------------------------------\r
-;\r
-; Moves the screen.\r
-;\r
-; Input:\r
-; X, Y = new X, Y coordinates of view screen\r
-; Output:\r
-; none\r
-;\r
-mxPan PROC FAR\r
- ARG Y:WORD, \\r
- X:WORD = ARG_SIZE\r
- ASSUME ds:NOTHING\r
- .enter 0\r
-\r
- mov ax, [Y]\r
- mul [mx_BytesPerLine]\r
- mov dx, [X]\r
- shr dx, 1\r
- shr dx, 1\r
- add ax, dx\r
- push ax ; Push the start address\r
- call mxWaitDisplay\r
- call mxStartAddress\r
-\r
- mov dx, 03DAh ; Set the pixel pan register\r
- in al, dx\r
- mov dx, 03C0h\r
- mov al, 33h\r
- out dx, al\r
- mov al, BYTE PTR [X]\r
- and al, 3\r
- shl al, 1\r
- out dx, al\r
-\r
- xor ax, ax\r
- .leave ARG_SIZE\r
-mxPan ENDP\r
-\r
-MX_TEXT ENDS\r
-END\r
-\r
-\r
-}\r
-\r
-//Procedure Play;\r
-void Play()\r
-{\r
- int loop1,loop2;\r
- int xpos,ypos,xdir,ydir;\r
- //int ch;\r
-// for(loop1=1;loop1<=62;loop1++)\r
- //Pal ((char)loop1,(char)loop1,(char)0,(char)(62-loop1)); // { This sets up the pallette for the pic }\r
-\r
- moveto(0,0,Size); // { This moves the view to the top left hand corner }\r
-\r
-// for(loop1=0;loop1<=3;loop1++)\r
-// for(loop2=0;loop2<=5;loop2++)\r
-// Putpic (loop1*160,loop2*66); // { This places the picture all over the\r
- // chain-4 screen }\r
-// getch();\r
-// ch=0x0;\r
-// xpos=rand (78)+1;\r
-// ypos=rand (198)+1; // { Random start positions for the view }\r
- xpos=0;\r
- ypos=0;\r
- xdir=1;\r
- ydir=1;\r
-// while(1)\r
-// {\r
- WaitRetrace(); // { Take this out and watch the screen go crazy! }\r
- moveto (xpos,ypos,Size);\r
- xpos=xpos+xdir;\r
- ypos=ypos+ydir;\r
- if( (xpos>79) || (xpos<1))xdir=-xdir;\r
- if( (ypos>199) || (ypos<1))ydir=-ydir; // { Hit a boundry, change\r
- // direction! }\r
-// if(_bios_keybrd(_KEYBRD_READY))ch=getch();\r
-// if(ch==0x71)break; // 'q'\r
-// if(ch==0x1b)break; // 'ESC'\r
-// }\r
-}\r
-*/\r
-/*tile*/\r
-//king_crimson's code\r
-void putColorBox_X(int x, int y, int w, int h, byte color) {\r
- outp(0x3c4, 0x02);\r
-\r
- int curx, cury;\r
- unsigned drawptr;\r
- for (curx=x; curx<(x+w); curx++) {\r
- outp(0x3c5, 0x01 << (curx & 3));\r
- drawptr = (unsigned)(widthBytes * y) + (curx / 4) + actStart;\r
- for (cury=0; cury<h; cury++) {\r
- vga[drawptr] = color;\r
- drawptr += widthBytes;\r
- }\r
- }\r
-}\r
-\r
-void vScroll(int rows)\r
-{\r
- // Scrolling = current start + (rows * bytes in a row)\r
- setVisibleStart(visStart + (rows * width));\r
-}\r
-\r
-void scrolly(int bongy)\r
-{\r
- int boingy=0;\r
- if(bongy<0)\r
- boingy=-1;\r
- else if(bongy>0)\r
- boingy=1;\r
-\r
- for(int ti=0;ti<TILEWH;ti++)\r
- {\r
- delay(1);\r
- vScroll(boingy);\r
- }\r
-}\r
-\r
-//king_crimson's code\r
-void hScroll(int Cols) {\r
- wait_for_retrace();\r
- outp(0x3C0, 0x13);\r
- outp(0x3C0, Cols & 3);\r
- outp(0x3D4, 0x13);\r
- outp(0x3D5, Cols >> 2);\r
- outp(0x3D4, Cols);\r
- //setVisibleStart(visStart + (Cols * height));\r
- setVisibleStart(visStart + (Cols * width));\r
-}\r
-\r
-/*To implement smooth horizontal scrolling, you would do the following:\r
--------------- Horizontal Scrolling ------------\r
-FOR X = 0 TO 319 DO\r
- SET HPP TO ( X MOD 4 )\r
- SET VGA OFFSET TO ( X/4 )\r
-END FOR\r
-------------------------------------------------\r
-\r
-Okay, no problem at all (although I think you might have to fiddle\r
-around with the HPP a bit to get it right...try different values and\r
-see what works :).\r
-\r
-So, the next problem is with drawing the images off the screen where\r
-they aren't visible and then scrolling them on!!! As it turns out,\r
-there's yet ANOTHER register to accomplish this. This one's called the\r
-offset register (no, not the one I was talking about before, that one\r
-was actually the "start address" register) and it's at\r
-\r
- PORT: 3D4H/3D5H\r
- OFFSET: 13H\r
-\r
-and here's how to use it\r
-\r
--------------- Offset Register ---------------\r
-OUT 13H TO PORT 3D4H\r
-OUT value TO PORT 3D5H\r
-----------------------------------------------\r
-\r
-Now, what my VGA reference says is that this register holds the number\r
-of bytes (not pixels) difference between the start address of each row.\r
-So, in X-mode it normally contains the value 80 (as we remember,\r
-80 bytes * 4 planes = 320 pixels). This register does not affect the\r
-VISIBLE width of the display, only the difference between addresses on\r
-each row.\r
-\r
-When we scroll horizontally, we need a little bit of extra working space\r
-so we can draw off the edge of the screen.\r
-\r
-Perhaps a little diagram will clarify it. The following picture is of a\r
-standard X-mode addressing scheme with the OFFSET register set to 80.\r
-\r
- ROW OFFSET\r
- 0 0 ========================\r
- 1 80 [ ]\r
- 2 160 [ ]\r
- .. .. [ VISIBLE ]\r
- [ SCREEN ]\r
- [ ]\r
- [ ]\r
- .. .. [ ]\r
- 199 15920 ========================\r
-\r
-and the next diagram is of a modified addressing scheme with the OFFSET\r
-register set to 82 (to give us 4 extra pixels on each side of the screen)\r
-\r
-ROW OFFSET\r
-0 0 ------========================------\r
-1 82 | V [ ] V |\r
-2 164 | I [ ] I |\r
-.. .. | N S [ VISIBLE ] N S |\r
- | O I [ SCREEN ] O I |\r
- | T B [ ] T B |\r
- | L [ ] L |\r
-.. .. | E [ ] E |\r
-199 16318 ------========================------\r
-\r
-Beautiful!!!\r
-\r
-As with vertical scrolling, however, you still have the problem of when\r
-you reach the bottom of page 4...and it's fixed in the same manner.\r
-\r
-I haven't actually managed to get infinite horizontal scrolling working,\r
-but the method I have just stated will give you a horizontal scrolling\r
-range of over 200 screens!!!! So if you need more (which is extremely\r
-unlikely), figure it out yourself.\r
-\r
-\r
-------------------\r
-COMBINED SCROLLING\r
-------------------\r
-To do both horizontal and vertical scrolling, all you have to do is combine\r
-the two methods with a few little extras (it's always the way isn't it).\r
-\r
-You have to start off with the original screen on the current page and the\r
-next page as well. When you scroll horizontally, you have to draw the edge\r
-that's coming in to the screen to BOTH pages (that means you'll be drawing\r
-the incoming edge twice, once for each page). You do this so that when you\r
-have scrolled vertically down through a complete page, you can jump back\r
-to the first page and it will (hopefully) have an identical copy, and you\r
-can then continue scrolling again.\r
-\r
-I'm sorry about this being so confusing but it's a bit difficult to explain.\r
-\r
-\r
-*/\r
-int loadfontX(char *fname)\r
-{\r
- FILE *fp;\r
-\r
- fp = fopen(fname, "rb");\r
-\r
- if (fp == NULL) {\r
- return 0;\r
- } else {\r
- fread(Xfont, 8, 256, fp);\r
- fclose(fp);\r
- return 1;\r
- }\r
-}\r
-\r
-void putchX(cord x, cord y, char c, byte color)\r
-{\r
- int i;\r
- byte *vga_ptr;\r
- byte *font_ptr;\r
- byte temp;\r
-\r
- // 8x8 font\r
- vga_ptr = RowsX[y << 3] + (x << 1) + actStart;\r
- write_plane = -1;\r
-\r
- font_ptr = Xfont + (c << 3);\r
-\r
- i=8;\r
- while (i--) {\r
- temp = *font_ptr++;\r
- outpw(SEQU_ADDR, text_mask[temp & 0x0F]);\r
- *vga_ptr++ = color;\r
-\r
- outpw(SEQU_ADDR, text_mask[temp >> 4]);\r
- *vga_ptr-- = color;\r
- vga_ptr += widthBytes;\r
- }\r
-}\r
-\r
-void putstringX(cord x, cord y, char *str, byte color)\r
-{\r
- int i, skip;\r
- byte *vga_ptr;\r
- byte *font_ptr;\r
- byte c, temp;\r
-\r
- // 8x8 font\r
- vga_ptr = RowsX[y << 3] + (x << 1) + actStart;\r
- write_plane = -1;\r
-\r
- skip = 2 - (widthBytes << 3);\r
-\r
- while (c = *str++) {\r
- font_ptr = Xfont + (c << 3);\r
-\r
- i=8;\r
- while (i--) {\r
- temp = *font_ptr++;\r
- outpw(SEQU_ADDR, text_mask[temp & 0x0F]);\r
- *vga_ptr++ = color;\r
-\r
- outpw(SEQU_ADDR, text_mask[temp >> 4]);\r
- *vga_ptr-- = color;\r
- vga_ptr += widthBytes;\r
- }\r
-\r
- vga_ptr += skip;\r
- }\r
-}\r