home *** CD-ROM | disk | FTP | other *** search
- /*
- TWEAK 0.95 - Mold your own VGA modes
-
- by Robert Schmidt of Ztiff Zox Softwear, 1992-93
-
- For documentation, see TWEAK.DOC.
-
-
- This file is formatted with a tab-width of 4.
- It's been compiled with Borland C++ 3.0 and 3.1, large model.
- It might work with older Turbo versions, or even Microsoft C++, DJGPP,
- Zortech and the lot. What would I know, I can't afford more than
- one compiler.
-
- Please send me any changes you do to enhance TWEAK or make it compatible
- with other compilers, and I will start including conditional sections
- for each supported compiler. Do *not* re-release the changed source
- without my permission. Well, how can I stop ya...?
-
- TWEAK was based on information found in George Sutty & Steve Blair's
- "Advanced Programmer's Guide to the EGA/VGA" (Brady). While not a
- magnificent book, it covers the basics and all registers. No mention
- of tweaking of any kind, but all register values for the standard
- EGA/VGA modes are appendixed.
-
- Disclaimer:
-
- I don't think this is neccessary in PD stuff, but anyways: This
- product, TWEAK, referred to as THE WORK OF ART, is provided as is,
- as they say. From now on, I'll refer to myself as THE ARTIST, and
- you as THE ART LOVER. THE ARTIST doesn't know what 'as is' means to
- THE ART LOVERS, but to THE ARTIST it means that THE ART LOVER ain't
- got no friggin' right to hold THE ARTIST responsible for anything
- mean, nice, cool, devastating, awesome or terrible happening to
- THE ART LOVER, THE ART LOVER's computer or any part within and
- outside, THE ART LOVER's family, ex-family and their kin,
- THE ART LOVER's sex life, THE ART LOVER's house, car, boat, in short:
- THE ART LOVER's anything, as a consequence of using, not using, or
- abusing THE WORK OF ART or any part contained within, including
- executable, documentation, source, recommendations and references.
- Phew.
-
- Some time ago, putting illegal or unsupported values or combinations
- of such into the video card registers might prove hazardous to both
- your monitor and your health. I have *never* claimed that bad things
- can't happen if you use TWEAK, although I'm pretty sure it won't.
- I've never heard of any damage arising from trying out TWEAK, or from
- fiddling with the VGA registers in general.
-
- History:
-
- 0.9
- First version available to the public.
- Aw, what the heck, I'll admit this is the first version ever...
- I know the usage of C++ classes may look a little stupid,
- but it works. Another stupid, *stupid* thing is the mixed use
- of conio and iostream, but hey, it works too!
- This version is not commented whatsoever.
- Pretty lame interface - no help.
- Test screens assume text start at segment 0xb800, and graphics
- at 0xa000.
- 1.0
- Commented heavily, and beautified the code.
- Changed use of abort() to exit(1).
- Text test pattern now fills entire text buffer from 0xb800:0x0000
- to 0xb800:0xffff.
- TWEAK now captures whatever screen mode is active at startup,
- instead of defaulting to BIOS mode 3 (80x25).
- Added 'S' and 'L' as synonyms for F9 and F10.
-
-
- */
-
- #ifndef __LARGE__
- # ifndef __COMPACT__
- # ifndef __HUGE__
- # error A large data model is required!
- # endif
- # endif
- #endif
-
-
- #include <stdio.h>
- #include <dos.h>
- #include <stdlib.h>
- #include <mem.h>
- #include <conio.h>
- #include <iostream.h>
- #include <string.h>
- #include <io.h>
-
-
- // This is the size of our working screen. I chose 80x25 for this because
- // I just happen to like 'screen cram'.
-
- const tempScrSize = 2*80*25;
-
- // Now for the screens used in TWEAK.
-
- // This one points to the standard VGA text screen buffer. textscr[80]
- // addresses the first character/attribute pair on the second line,
- // if the current mode is an 80-column one, for example.
-
- unsigned *textScr = (unsigned far *)MK_FP(0xb800,0);
-
- // graphScr points to the standard VGA graphics buffer, being 64Kb.
-
- char *graphScr = (char far *)MK_FP(0xa000,0);
-
- // tempScr points to the temporary screen when it is in use. It is used
- // for saving the editing screen when testing tweaked modes, and is thus
- // allocated dynamically.
-
- char *tempScr = NULL;
-
-
- // testType declares the possible types of screen patterns to test your
- // wonderful modes with. Note that 'tests' is there just to provide a
- // constant equaling the number of tests.
-
- enum testType
- { testText, test4x16, test1x256, test4x256, tests };
-
- // Note the elegant use of 'tests' here! I think this was pretty clever.
- // These are the text strings identifying each test to the user.
-
- char *testString[tests] =
- {
- "Text/attribute screen",
- "4 planes, 16 colors",
- "1 plane, 256 colors",
- "4 planes, 256 colors"
- };
-
- // I've tried generalizing register usage as much as possible - no flames
- // please. This struct contains what I consider neccessary: the port
- // number, the index of the register, and the name of the register,
- // as per the 'Programmer's Guide' mentioned above.
- // What I have *not* solved especially elegantly, is how the different
- // ports use different methods to be accessed. More on that later.
-
- struct vgaRegisterInfo
- {
- char *name;
- unsigned port;
- unsigned char index;
- };
-
- // This table contains all the registers editable by this
- // program. Feel free to add new ones if there's room on the screen,
- // but I think I've covered the essentials.
-
- vgaRegisterInfo table[] =
- {
- {"Misc. output", 0x3c2, 0x00},
-
- {"Horizontal total", 0x3d4, 0x00},
- {"Horizontal disp. enable", 0x3d4, 0x01},
- {"Horizontal blank start", 0x3d4, 0x02},
- {"Horizontal blank end", 0x3d4, 0x03},
- {"Horizontal retrace start",0x3d4, 0x04},
- {"Horizontal retrace end", 0x3d4, 0x05},
- {"Vertical total", 0x3d4, 0x06},
- {"Overflow register", 0x3d4, 0x07},
- {"Preset row scan", 0x3d4, 0x08},
- {"Max scan line/char ht.", 0x3d4, 0x09},
-
- {"Vertical retrace start", 0x3d4, 0x10},
- {"Vertical retrace end", 0x3d4, 0x11},
- {"Vert. disp. enable end", 0x3d4, 0x12},
- {"Offset/Logical width", 0x3d4, 0x13},
- {"Underline location", 0x3d4, 0x14},
- {"Vertical blank start", 0x3d4, 0x15},
- {"Vertical blank end", 0x3d4, 0x16},
- {"Mode control", 0x3d4, 0x17},
-
- {"Clock mode register", 0x3c4, 0x01},
- {"Color plane write enable",0x3c4, 0x02},
- {"Character gen. select", 0x3c4, 0x03},
- {"Memory mode register", 0x3c4, 0x04},
-
- {"Set/reset register", 0x3ce, 0x00},
- {"Set/reset enable", 0x3ce, 0x01},
- {"Color compare", 0x3ce, 0x02},
- {"Data rotate & function", 0x3ce, 0x03},
- {"Mode register", 0x3ce, 0x05},
- {"Miscellaneous register", 0x3ce, 0x06},
- {"Color don't care", 0x3ce, 0x07},
- {"Bit mask register", 0x3ce, 0x08},
-
- {"Mode control", 0x3c0, 0x10},
- {"Screen border colour", 0x3c0, 0x11},
- {"Color plane enable", 0x3c0, 0x12},
- {"Horizontal panning", 0x3c0, 0x13},
- {"Color select", 0x3c0, 0x14}
- };
-
- // Now calculate the number of registers supported. Damned elegant!
-
- const registers = sizeof (table) / sizeof (vgaRegisterInfo);
-
- // This function reads a byte from the specified register number.
- // Note that 'regNo' is an index into the table above, not the actual
- // register number!
-
- unsigned char inFrom(unsigned char regNo)
- {
- // Handle each port as special cases:
- int portNo = table[regNo].port;
- switch (portNo)
- {
- case 0x3c2:
- return inportb(0x3cc); // 0x3c2 is write-only, reading
- // must be mapped to 0x3cc
-
- case 0x3c3: // I guess you can figure this one
- return inportb(0x3c3); // out!
-
- case 0x3c0: // This 1 is odd. First do a read to
- inportb(0x3da); // reset the index/data flip-flop.
- // Then give it the index, but:
- // set bit 5! If cleared, VGA
- // output is disabled!
- outportb(0x3c0, table[regNo].index | 0x20);
- return inportb(0x3c1);
-
- case 0x3c4: // These 3 are similar. Give it the
- case 0x3ce: // register index, then read the
- case 0x3d4: // byte from port+1.
- outportb(portNo, table[regNo].index);
- return inportb(portNo+1);
- }
-
- // If we get down here, 'regNo' indexes a bad register info structure.
- cerr << "Don't know how to handle port " << table[regNo].port
- << endl;
- exit(1);
- return 0; // will never reach here
- }
-
- // This function asks the user to insert their mother-in-law in the
- // A:-drive, then does a low-level format, ignoring bad sectors.
-
- unsigned char outTo(unsigned char regNo, unsigned char value)
- {
- int portNo = table[regNo].port;
- switch (portNo)
- {
- case 0x3c2:
- case 0x3c3:
- outportb(portNo, value);
- return value;
- case 0x3c0:
- inportb(0x3da); // ensure index comes first
- outportb(0x3c0, table[regNo].index | 0x20);
- // ensure VGA output is enabled
- outportb(0x3c0, value);
- return value;
- case 0x3c4:
- case 0x3ce:
- case 0x3d4:
- outportb(portNo, table[regNo].index);
- outportb(portNo+1, value);
- return value;
- }
- cerr << "Don't know how to handle port " << portNo << endl;
- exit(1);
- return 0; // will never reach here
- }
-
-
- // Now we're getting object-oriented. This class is an interface to the
- // table and VGA register primitives above. It contains the values
- // in all supported registers, and provides member functions for reading
- // and writing all registers at once.
-
- class vgaRegTable
- {
- unsigned char value[registers];
- unsigned char selectedReg;
- public:
- vgaRegTable() { in(); } // this constructor just fills the
- // table with the current VGA state.
- void out();
- void in();
- void print(unsigned char selected);
- void printOne(unsigned char r, int isSelected);
- // This indexing operator returns the address of the value of register
- // number 'n'.
- unsigned char& operator [] (unsigned char n)
- { return value[n]; }
- };
-
-
- // vgaRegTable::out() sets the entire VGA state, by writing all registers
- // to the VGA.
-
- void vgaRegTable::out()
- {
- outportb(0x3d4,0x11); // Ensure CRT regs. 0-7 are writable!
- int v = inportb(0x3d5); // That is, clear bit 7 of port
- v &= 0x7f; // 0x3D4, index 0x11.
- outportb(0x3d4,0x11);
- outportb(0x3d5,v);
-
- for (int r=0; r<registers; r++) // Then put all register values to
- outTo(r, value[r]); // the VGA.
- }
-
- // vgaRegTable::in() gets the entire VGA state.
-
- void vgaRegTable::in()
- {
- for (int r=0; r<registers; r++) // All VGA registers are immediately
- value[r] = inFrom(r); // readbale.
- }
-
- // vgaRegTable::printOne() is specialized for use in TWEAK. It prints
- // the register info corresponding to the given register table index 'r'
- // at it's proper position on screen. If 'isSelected' is nonzero, the
- // cursor is printed surrounding the output.
-
- void vgaRegTable::printOne(unsigned char r, int isSelected)
- {
- // This gotoxy divides the registers into two columns, 25 lines each.
- gotoxy(40*(r / 25) +1, r % 25 +1);
-
- // Optionally print the left cursor.
- cprintf(isSelected ? "\20" : " ");
-
- // Then put out the meat.
- cprintf("%03hx (%02hx) %24s : %02hx", table[r].port, table[r].index,
- table[r].name, value[r]);
-
- // And possibly the right cursor.
- cprintf(isSelected ? "\21" : " ");
-
- // This gotoxy just puts the hardware cursor where it won't distract you.
- gotoxy(40*(r / 25)+38, r % 25 +1);
- }
-
- // vgaRegTable::print() puts the entire register table on the screen.
- // 'selected' tells which register number to emphasize by printing the
- // cursor around it.
-
- void vgaRegTable::print(unsigned char selected)
- {
- for (unsigned char r = 0; r<registers; r++)
- printOne(r, r==selected);
- }
-
- // setscreen() sets the given standard BIOS mode. No magic here!
-
- inline void setscreen(int modeno)
- {
- _AX = modeno;
- geninterrupt(0x10);
- }
-
- // waitForRetrace() waits for vertical retrace. If you don't know what
- // that means, don't worry, you won't need it to tweak.
-
- void waitForRetrace()
- {
- asm {
- mov dx,0x3da
- in al,dx
- test al,8
- jz short waiton
- };
- waitoff:
- asm {
- in al,dx
- test al,8
- jnz short waitoff
- };
- waiton:
- asm {
- in al,dx
- test al,8
- jz short waiton
- };
- };
-
-
- // The following two functions saves and restores the temporary screen.
- // The tempScr buffer is allocated and destroyed each time.
-
- void saveTempScreen(void)
- {
- if (!(tempScr = new char[tempScrSize]))
- {
- cout << "Out of memory for swap screen!" << endl;
- exit(1);
- }
- memcpy(tempScr, textScr, tempScrSize);
- }
-
- void restoreTempScreen(void)
- {
- if (tempScr)
- {
- memcpy(textScr, tempScr, tempScrSize);
- delete[] tempScr;
- }
- }
-
- // testPattern() puts the given test onto the screen.
-
- void testPattern(testType test)
- {
- unsigned a,c;
- unsigned long offset;
-
- outportb(0x3c4,0x02); //get write plane enable
- unsigned plane=inportb(0x3c5);
-
- outportb(0x3d4,0x13); //get screen width in words
- unsigned width=inportb(0x3d5)*2; //convert to bytes
-
- // Now select the correct initialization method:
-
- switch (test)
- {
- case test1x256:
- case test4x16:
- case test4x256:
- // All graphics modes: clear the screen, but take care of
- // write enabling all planes.
- outport(0x3c4,0x0f02);
- memset(graphScr, 0, 0xffff);
- outportb(0x3c4,0x02);
- outportb(0x3c5,plane);
- break;
- case testText:
- // Just blank the text screen.
- memset(textScr, 0, 8000);
- }
-
- // Now for the selected pattern. None of them are very impressive,
- // but it kinda gives you an idea of the mode. If you have ideas
- // for more informative tests, please let me know!
-
- switch (test)
- {
- case testText:
-
- // Fill top line with the sequence "0123456789" lt grey/black:
- a = 0;
- for (c=0; c<width; c++)
- textScr[a++] = ('0'+(c+1)%10) | 0x0700;
-
- // Then fill 4 lines with the ASCII set in white on blue:
- for (c=0; c<5*width; c++)
- textScr[a++] = c | 0x1f00;
-
- // Now fill the rest with the sequence "ABCDEFGHIJ" in all color
- // combinations (except blinking!):
- c = 0;
- while (a < 0x4800)
- textScr[a++] = ('A'+c%('K'-'A')) | (c<<8&0x7f), c++;
- break;
-
- case test1x256:
-
- // Fill the first 32 lines with 1 pixel wide colored vertical
- // lines.
- for (c=0; c<width*4; c++)
- for (a=0; a<32; a++)
- graphScr[a*width*4+c]=c;
-
- // Fill the rest with 1 pixel high horizontal lines.
- c=0;
- offset=32*4*width;
- do {
- memset(graphScr+offset, c++, width*4);
- //horizontal lines, 1 color each
- offset+=width<<2;
- }
- while (offset < (0xffff-width<<2));
- break;
-
- case test4x256:
-
- // This test is affected by the Write Plane Enable register.
- // First put up 32 horizontal lines in the 32 first colors.
- for (c=0; c<(width<<5); c++)
- graphScr[c]=c/width;
-
- // Then fill the rest with vertical lines. This is too slow!
- offset=c;
- c=0;
- a=1;
- do {
- outportb(0x3c5,a); //Set write plane enable
- graphScr[offset]=c;
-
- if ((a<<=1)>8)
- {
- a=1;
- ++offset;
- }
- if ((++c)==width<<2)
- c=0;
- }
- while (offset <= 0xffff);
- break;
- case test4x16:
-
- // Fill first 32 lines with thick vertical stripes alternating
- // between black and the color selected by Write Plane Enable.
- for (c=0; c<(width<<5); c++)
- graphScr[c]=0x0f;
-
- // Fill the rest with various bit patterns, in all colors.
- for (a=0; a<256; a++)
- {
- outportb(0x3c5,a);
- memset(graphScr+4000+a*width, a, width);
- }
- break;
- }
- }
-
-
- // tellTest() puts the name of the current test at the correct position on
- // the edit screen.
-
- void tellTest(testType test)
- {
- gotoxy(42,25);
- cout << "Current test: " << testString[test];
- clreol();
- }
-
- // showBitMask() updates the bit pattern display with the value 'v'.
-
- void showBitMask(unsigned char v)
- {
- gotoxy(42,21);
- cout << "Bit mask: 7 6 5 4 3 2 1 0";
- gotoxy(51,22);
- for (int e=7; e>=0; e--)
- cout << (v&(1<<e) ? " 1" : " 0");
- }
-
-
- // The main program starts here.
-
- main(void)
- {
- cout << "TWEAK version 1.0 - mold your own screen modes, text or graphics!\n";
- cout << "By Robert Schmidt of Ztiff Zox Softwear, 1992-93.\n";
- cout << "e-mail: robert@solan.unit.no\n";
- cout << "Donated to the Public Domain, with source and all.\n";
- cout << endl;
- cout << "Quick instructions:\n";
- cout << "\tup/down:\tselect VGA register to modify\n";
- cout << "\t0-9,A-F:\tenter new value directly, always hex\n";
- cout << "\t+/-:\t\tincrease/decrease value respectively\n";
- cout << "\tF1-F8:\t\ttoggle individual bits, F1=MSB, F8=LSB\n";
- cout << "\tTAB:\t\tchoose test pattern type\n";
- cout << "\tENTER:\t\tview screen mode\n";
- cout << "\tS/L or\n";
- cout << "\tF9/F10:\t\tsave/load register set, respectively\n";
- cout << "\tM:\t\tchoose BIOS mode to start tweaking from\n";
- cout << "\tESC:\t\tquit program\n";
- cout << endl;
- cout << "Press any command key, or SPACE to begin...";
- while (!kbhit());
-
- // No initialize the scratch register table. The constructor will
- // capture whatever register values are in effect at startup.
-
- vgaRegTable tweak;
-
- // Then set our editing screen mode.
-
- setscreen(3);
-
- unsigned char regNo = 0; // The register number we're currently
- // editing.
- unsigned char quit = 0; // Non-zero when a quit command is
- // initiated.
- testType testNo = testText; // Default test pattern.
- int key = 0; // The last key pressed.
-
- // Build the editing screen:
-
- showBitMask(tweak[regNo]);
- tweak.print(regNo);
- tellTest(testNo);
-
- // Start the main keyboard polling loop:
-
- while (!quit)
- {
- int key = getch();
- if (!key)
- key = getch() << 8;
- int prevRegNo = regNo;
-
- keyentry:
- switch (key)
- {
- case 0x4700: //HOME
- regNo = 0;
- break;
- case 0x4800: //UP
- if (regNo > 0)
- regNo--;
- else
- regNo = registers-1;
- break;
- case 0x4F00: //END
- regNo = registers-1;
- break;
- case 0x5000: //DOWN
- if (regNo < registers-1)
- regNo++;
- else
- regNo = 0;
- break;
-
- // Function keys - toggle single bit in selected reg.
- case 0x3B00: //F1
- case 0x3C00:
- case 0x3D00:
- case 0x3E00:
- case 0x3F00:
- case 0x4000:
- case 0x4100:
- case 0x4200: //F8
- tweak[regNo] ^= (1<<7-(key-59));
- break;
- case 0x4300: //F9
- case 0x4400: //F10
-
- // Handle file operations (save/load):
- char fname[13];
- FILE *f;
- int r;
- int save=(key==67);
-
- // Prompt for filename.
- gotoxy(42,24);
- cout << (save?"Save":"Load") << " file name: ";
- clreol();
- gets(fname);
-
- // Open file in selected mode.
- if (!(f=fopen(fname,save?"wb":"rb")))
- {
- gotoxy(42,24);
- perror(fname);
- }
- else
- // Read/write file:
- for (r=0; r<registers; r++)
- if ((save?fwrite(&(tweak[r]),1,1,f)
- :fread(&(tweak[r]),1,1,f)) == 0)
- {
- gotoxy(42,24);
- perror(fname);
- }
- fclose(f);
-
- // Update screen to reflect changes if any.
- tweak.print(regNo);
- tellTest(testNo);
- break;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- case 'A':
- case 'B':
- case 'C':
- case 'D':
- case 'E':
- case 'F':
- case 'a':
- case 'b':
- case 'c':
- case 'd':
- case 'e':
- case 'f':
-
- // Input hexadecimal number:
- gotoxy(40*(regNo/25)+38, regNo%25+1);
- ungetch(key);
- cprintf("%c \b", key);
- unsigned char newValue;
- cscanf("%2hx", &newValue);
- tweak[regNo] = newValue;
- break;
-
- // Alternate file I/O keys:
- case 'S':
- case 's':
- key = 0x4300;
- goto keyentry;
- case 'L':
- case 'l':
- key = 0x4400;
- goto keyentry;
-
- case 'M':
- case 'm':
-
- // Select a BIOS mode to work out from.
- int baseMode;
- gotoxy(42,23);
- cout << "Base BIOS screen mode: 0x";
- clreol();
- cscanf("%2hx",&baseMode);
- saveTempScreen();
-
- // Set the selected mode, and grab register values.
- setscreen(baseMode);
- tweak.in();
- setscreen(3);
- restoreTempScreen();
- break;
- case 27: //ESC
- // Quit TWEAK:
- quit = 1;
- break;
- case '-':
- // Decrease register value
- --tweak[regNo];
- break;
- case '+':
- // Increase register value
- ++tweak[regNo];
- break;
- // case 8: //Backspace
- case 13: //ENTER
-
- // Test the screen mode.
- saveTempScreen();
- tweak.out();
- testPattern(testNo);
-
- // Uncomment the following and the 'case 8' above for an undocumented
- // 'feature'. It's just some fancy effect I wanted to test.
-
- /* if (key==8)
- {
- for (int i=0x1F; i>=0x10; i--)
- {
- waitForRetrace();
- outportb(0x3d4,0x09);
- outportb(0x3d5,i|0x80);
- }
- for (i=0x1F; i>=0; i--)
- {
- waitForRetrace();
- outportb(0x3d4,0x09);
- outportb(0x3d5,i);
- }
- }
- */
- getch();
- setscreen(3);
- restoreTempScreen();
- break;
- case 9: //TAB
-
- // Select another test pattern:
- if ((++testNo)==tests)
- testNo = testText;
- tellTest(testNo);
- break;
-
- }
- // Update the bit pattern display:
- showBitMask(tweak[regNo]);
-
- // Put cursor at selected register:
- tweak.printOne(prevRegNo, 0);
- tweak.printOne(regNo, 1);
- }
-
- // If we get here, the user wanted to quit, probably...
- // I ought to restore the screen mode saved at startup, but for now
- // we quit into the usual mode 3 (80x25).
- clrscr();
-
- return 0;
- }
-