home *** CD-ROM | disk | FTP | other *** search
/ Best Objectech Shareware Selections / UNTITLED.iso / boss / grap / util / 020 / tweak095.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-20  |  21.3 KB  |  798 lines

  1. /*
  2.     TWEAK 0.95 - Mold your own VGA modes
  3.  
  4.     by Robert Schmidt of Ztiff Zox Softwear, 1992-93
  5.  
  6.     For documentation, see TWEAK.DOC.
  7.  
  8.  
  9.     This file is formatted with a tab-width of 4.
  10.     It's been compiled with Borland C++ 3.0 and 3.1, large model.
  11.     It might work with older Turbo versions, or even Microsoft C++, DJGPP,
  12.     Zortech and the lot.  What would I know, I can't afford more than
  13.     one compiler.
  14.  
  15.     Please send me any changes you do to enhance TWEAK or make it compatible
  16.     with other compilers, and I will start including conditional sections
  17.     for each supported compiler.  Do *not* re-release the changed source
  18.     without my permission.  Well, how can I stop ya...?
  19.  
  20.     TWEAK was based on information found in George Sutty & Steve Blair's
  21.     "Advanced Programmer's Guide to the EGA/VGA" (Brady).  While not a
  22.     magnificent book, it covers the basics and all registers.  No mention
  23.     of tweaking of any kind, but all register values for the standard
  24.     EGA/VGA modes are appendixed.
  25.  
  26.     Disclaimer:
  27.  
  28.     I don't think this is neccessary in PD stuff, but anyways:  This
  29.     product, TWEAK, referred to as THE WORK OF ART, is provided as is,
  30.     as they say.  From now on, I'll refer to myself as THE ARTIST, and
  31.     you as THE ART LOVER.  THE ARTIST doesn't know what 'as is' means to
  32.     THE ART LOVERS, but to THE ARTIST it means that THE ART LOVER ain't
  33.     got no friggin' right to hold THE ARTIST responsible for anything
  34.     mean, nice, cool, devastating, awesome or terrible happening to
  35.     THE ART LOVER, THE ART LOVER's computer or any part    within and
  36.     outside, THE ART LOVER's family, ex-family and their kin,
  37.     THE ART LOVER's sex life, THE ART LOVER's house, car, boat, in short:
  38.     THE ART LOVER's anything, as a consequence of using, not using, or
  39.     abusing THE WORK OF ART or any part contained within, including
  40.     executable, documentation, source, recommendations and references.
  41.     Phew.
  42.  
  43.     Some time ago, putting illegal or unsupported values or combinations
  44.     of such into the video card registers might prove hazardous to both
  45.     your monitor and your health.  I have *never* claimed that bad things
  46.     can't happen if you use TWEAK, although I'm pretty sure it won't.
  47.     I've never heard of any damage arising from trying out TWEAK, or from
  48.     fiddling with the VGA registers in general.
  49.  
  50.     History:
  51.  
  52.     0.9
  53.         First version available to the public.
  54.         Aw, what the heck, I'll admit this is the first version ever...
  55.         I know the usage of C++ classes may look a little stupid,
  56.         but it works.  Another stupid, *stupid* thing is the mixed use
  57.         of conio and iostream, but hey, it works too!
  58.         This version is not commented whatsoever.
  59.         Pretty lame interface - no help.
  60.         Test screens assume text start at segment 0xb800, and graphics
  61.         at 0xa000.
  62.     1.0
  63.         Commented heavily, and beautified the code.
  64.         Changed use of abort() to exit(1).
  65.         Text test pattern now fills entire text buffer from 0xb800:0x0000
  66.         to 0xb800:0xffff.
  67.         TWEAK now captures whatever screen mode is active at startup,
  68.         instead of defaulting to BIOS mode 3 (80x25).
  69.         Added 'S' and 'L' as synonyms for F9 and F10.
  70.  
  71.  
  72.  */
  73.  
  74. #ifndef __LARGE__
  75. # ifndef __COMPACT__
  76. #  ifndef __HUGE__
  77. #   error A large data model is required!
  78. #  endif
  79. # endif
  80. #endif
  81.  
  82.  
  83. #include <stdio.h>
  84. #include <dos.h>
  85. #include <stdlib.h>
  86. #include <mem.h>
  87. #include <conio.h>
  88. #include <iostream.h>
  89. #include <string.h>
  90. #include <io.h>
  91.  
  92.  
  93. // This is the size of our working screen.  I chose 80x25 for this because
  94. //    I just happen to like 'screen cram'.
  95.  
  96. const tempScrSize = 2*80*25;
  97.  
  98. // Now for the screens used in TWEAK.
  99.  
  100. // This one points to the standard VGA text screen buffer.  textscr[80]
  101. //    addresses the first character/attribute pair on the second line,
  102. //    if the current mode is an 80-column one, for example.
  103.  
  104. unsigned *textScr = (unsigned far *)MK_FP(0xb800,0);
  105.  
  106. // graphScr points to the standard VGA graphics buffer, being 64Kb.
  107.  
  108. char *graphScr = (char far *)MK_FP(0xa000,0);
  109.  
  110. // tempScr points to the temporary screen when it is in use.  It is used
  111. //    for saving the editing screen when testing tweaked modes, and is thus
  112. //    allocated dynamically.
  113.  
  114. char *tempScr = NULL;
  115.  
  116.  
  117. // testType declares the possible types of screen patterns to test your
  118. //    wonderful modes with.  Note that 'tests' is there just to provide a
  119. //    constant equaling the number of tests.
  120.  
  121. enum testType
  122.     { testText, test4x16, test1x256, test4x256, tests };
  123.  
  124. // Note the elegant use of 'tests' here!  I think this was pretty clever.
  125. //    These are the text strings identifying each test to the user.
  126.  
  127. char *testString[tests] =
  128.     {
  129.     "Text/attribute screen",
  130.     "4 planes, 16 colors",
  131.     "1 plane, 256 colors",
  132.     "4 planes, 256 colors"
  133.     };
  134.  
  135. // I've tried generalizing register usage as much as possible - no flames
  136. //    please.  This struct contains what I consider neccessary:  the port
  137. //    number, the index of the register, and the name of the register,
  138. //    as per the 'Programmer's Guide' mentioned above.
  139. // What I have *not* solved especially elegantly, is how the different
  140. //    ports use different methods to be accessed.  More on that later.
  141.  
  142. struct vgaRegisterInfo
  143.     {
  144.     char *name;
  145.     unsigned port;
  146.     unsigned char index;
  147.     };
  148.  
  149. // This table contains all the registers editable by this
  150. //    program.  Feel free to add new ones if there's room on the screen,
  151. //    but I think I've covered the essentials.
  152.  
  153. vgaRegisterInfo table[] =
  154.     {
  155.         {"Misc. output",             0x3c2,    0x00},
  156.  
  157.         {"Horizontal total",        0x3d4,  0x00},
  158.         {"Horizontal disp. enable",    0x3d4,  0x01},
  159.         {"Horizontal blank start",    0x3d4,  0x02},
  160.         {"Horizontal blank end",    0x3d4,  0x03},
  161.         {"Horizontal retrace start",0x3d4,  0x04},
  162.         {"Horizontal retrace end",    0x3d4,  0x05},
  163.         {"Vertical total",            0x3d4,  0x06},
  164.         {"Overflow register",        0x3d4,    0x07},
  165.         {"Preset row scan",            0x3d4,    0x08},
  166.         {"Max scan line/char ht.",    0x3d4,    0x09},
  167.  
  168.         {"Vertical retrace start",    0x3d4,    0x10},
  169.         {"Vertical retrace end",    0x3d4,    0x11},
  170.         {"Vert. disp. enable end",    0x3d4,    0x12},
  171.         {"Offset/Logical width",    0x3d4,    0x13},
  172.         {"Underline location",        0x3d4,    0x14},
  173.         {"Vertical blank start",    0x3d4,    0x15},
  174.         {"Vertical blank end",        0x3d4,    0x16},
  175.         {"Mode control",            0x3d4,    0x17},
  176.  
  177.         {"Clock mode register",        0x3c4,    0x01},
  178.         {"Color plane write enable",0x3c4,    0x02},
  179.         {"Character gen. select",    0x3c4,    0x03},
  180.         {"Memory mode register",    0x3c4,    0x04},
  181.  
  182.         {"Set/reset register",        0x3ce,    0x00},
  183.         {"Set/reset enable",        0x3ce,    0x01},
  184.         {"Color compare",            0x3ce,    0x02},
  185.         {"Data rotate & function",    0x3ce,    0x03},
  186.         {"Mode register",            0x3ce,    0x05},
  187.         {"Miscellaneous register",    0x3ce,    0x06},
  188.         {"Color don't care",        0x3ce,    0x07},
  189.         {"Bit mask register",        0x3ce,    0x08},
  190.  
  191.         {"Mode control",            0x3c0,    0x10},
  192.         {"Screen border colour",    0x3c0,    0x11},
  193.         {"Color plane enable",        0x3c0,    0x12},
  194.         {"Horizontal panning",        0x3c0,    0x13},
  195.         {"Color select",            0x3c0,    0x14}
  196.     };
  197.  
  198. // Now calculate the number of registers supported.  Damned elegant!
  199.  
  200. const registers = sizeof (table) / sizeof (vgaRegisterInfo);
  201.  
  202. // This function reads a byte from the specified register number.
  203. //    Note that 'regNo' is an index into the table above, not the actual
  204. //    register number!
  205.  
  206. unsigned char inFrom(unsigned char regNo)
  207.     {
  208.     // Handle each port as special cases:
  209.     int portNo = table[regNo].port;
  210.     switch (portNo)
  211.         {
  212.         case 0x3c2:
  213.             return inportb(0x3cc);        // 0x3c2 is write-only, reading
  214.                                         // must be mapped to 0x3cc
  215.  
  216.         case 0x3c3:                        // I guess you can figure this one
  217.             return inportb(0x3c3);      //    out!
  218.  
  219.         case 0x3c0:                        // This 1 is odd.  First do a read to
  220.             inportb(0x3da);             //    reset the index/data flip-flop.
  221.                                         //    Then give it the index, but:
  222.                                         //    set bit 5!  If cleared, VGA
  223.                                         //    output is disabled!
  224.             outportb(0x3c0, table[regNo].index | 0x20);
  225.             return inportb(0x3c1);
  226.  
  227.         case 0x3c4:                        // These 3 are similar.  Give it the
  228.         case 0x3ce:                        //    register index, then read the
  229.         case 0x3d4:                        //    byte from port+1.
  230.             outportb(portNo, table[regNo].index);
  231.             return inportb(portNo+1);
  232.         }
  233.  
  234.     // If we get down here, 'regNo' indexes a bad register info structure.
  235.     cerr << "Don't know how to handle port " << table[regNo].port
  236.          << endl;
  237.     exit(1);
  238.     return 0;                            // will never reach here
  239.     }
  240.  
  241. // This function asks the user to insert their mother-in-law in the
  242. //    A:-drive, then does a low-level format, ignoring bad sectors.
  243.  
  244. unsigned char outTo(unsigned char regNo, unsigned char value)
  245.     {
  246.     int portNo = table[regNo].port;
  247.     switch (portNo)
  248.         {
  249.         case 0x3c2:
  250.         case 0x3c3:
  251.             outportb(portNo, value);
  252.             return value;
  253.         case 0x3c0:
  254.             inportb(0x3da);             // ensure index comes first
  255.             outportb(0x3c0, table[regNo].index | 0x20);
  256.                                         // ensure VGA output is enabled
  257.             outportb(0x3c0, value);
  258.             return value;
  259.         case 0x3c4:
  260.         case 0x3ce:
  261.         case 0x3d4:
  262.             outportb(portNo, table[regNo].index);
  263.             outportb(portNo+1, value);
  264.             return value;
  265.         }
  266.     cerr << "Don't know how to handle port " << portNo << endl;
  267.     exit(1);
  268.     return 0;                            // will never reach here
  269.     }
  270.  
  271.  
  272. // Now we're getting object-oriented.  This class is an interface to the
  273. //    table and VGA register primitives above.  It contains the values
  274. //    in all supported registers, and provides member functions for reading
  275. //    and writing all registers at once.
  276.  
  277. class vgaRegTable
  278.     {
  279.     unsigned char value[registers];
  280.     unsigned char selectedReg;
  281. public:
  282.     vgaRegTable()    { in(); }            // this constructor just fills the
  283.                                         //    table with the current VGA state.
  284.     void out();
  285.     void in();
  286.     void print(unsigned char selected);
  287.     void printOne(unsigned char r, int isSelected);
  288.     // This indexing operator returns the address of the value of register
  289.     //    number 'n'.
  290.     unsigned char& operator [] (unsigned char n)
  291.                     { return value[n]; }
  292.     };
  293.  
  294.  
  295. // vgaRegTable::out() sets the entire VGA state, by writing all registers
  296. //    to the VGA.
  297.  
  298. void vgaRegTable::out()
  299.     {
  300.     outportb(0x3d4,0x11);                // Ensure CRT regs. 0-7 are writable!
  301.     int v = inportb(0x3d5);                //    That is, clear bit 7 of port
  302.     v &= 0x7f;                            //    0x3D4, index 0x11.
  303.     outportb(0x3d4,0x11);
  304.     outportb(0x3d5,v);
  305.  
  306.     for (int r=0; r<registers; r++)        // Then put all register values to
  307.         outTo(r, value[r]);                //    the VGA.
  308.     }
  309.  
  310. // vgaRegTable::in() gets the entire VGA state.
  311.  
  312. void vgaRegTable::in()
  313.     {
  314.     for (int r=0; r<registers; r++)        // All VGA registers are immediately
  315.         value[r] = inFrom(r);            //    readbale.
  316.     }
  317.  
  318. // vgaRegTable::printOne() is specialized for use in TWEAK.  It prints
  319. //    the register info corresponding to the given register table index 'r'
  320. //    at it's proper position on screen.  If 'isSelected' is nonzero, the
  321. //    cursor is printed surrounding the output.
  322.  
  323. void vgaRegTable::printOne(unsigned char r, int isSelected)
  324.     {
  325.     // This gotoxy divides the registers into two columns, 25 lines each.
  326.     gotoxy(40*(r / 25) +1, r % 25 +1);
  327.  
  328.     // Optionally print the left cursor.
  329.     cprintf(isSelected ? "\20" : " ");
  330.  
  331.     // Then put out the meat.
  332.     cprintf("%03hx (%02hx) %24s : %02hx", table[r].port, table[r].index,
  333.         table[r].name, value[r]);
  334.  
  335.     // And possibly the right cursor.
  336.     cprintf(isSelected ? "\21" : " ");
  337.  
  338.     // This gotoxy just puts the hardware cursor where it won't distract you.
  339.     gotoxy(40*(r / 25)+38, r % 25 +1);
  340.     }
  341.  
  342. // vgaRegTable::print() puts the entire register table on the screen.
  343. //    'selected' tells which register number to emphasize by printing the
  344. //    cursor around it.
  345.  
  346. void vgaRegTable::print(unsigned char selected)
  347.     {
  348.     for (unsigned char r = 0; r<registers; r++)
  349.         printOne(r, r==selected);
  350.     }
  351.  
  352. // setscreen() sets the given standard BIOS mode.  No magic here!
  353.  
  354. inline void setscreen(int modeno)
  355.     {
  356.     _AX = modeno;
  357.     geninterrupt(0x10);
  358.     }
  359.  
  360. // waitForRetrace() waits for vertical retrace.  If you don't know what
  361. //    that means, don't worry, you won't need it to tweak.
  362.  
  363. void waitForRetrace()
  364.     {
  365.     asm    {
  366.         mov dx,0x3da
  367.         in al,dx
  368.         test al,8
  369.         jz short waiton
  370.         };
  371. waitoff:
  372.     asm    {
  373.         in al,dx
  374.         test al,8
  375.         jnz short waitoff
  376.         };
  377. waiton:
  378.     asm    {
  379.         in al,dx
  380.         test al,8
  381.         jz short waiton
  382.         };
  383.     };
  384.  
  385.  
  386. // The following two functions saves and restores the temporary screen.
  387. //    The tempScr buffer is allocated and destroyed each time.
  388.  
  389. void saveTempScreen(void)
  390.     {
  391.     if (!(tempScr = new char[tempScrSize]))
  392.         {
  393.         cout << "Out of memory for swap screen!" << endl;
  394.         exit(1);
  395.         }
  396.     memcpy(tempScr, textScr, tempScrSize);
  397.     }
  398.  
  399. void restoreTempScreen(void)
  400.     {
  401.     if (tempScr)
  402.         {
  403.         memcpy(textScr, tempScr, tempScrSize);
  404.         delete[] tempScr;
  405.         }
  406.     }
  407.  
  408. // testPattern() puts the given test onto the screen.
  409.  
  410. void testPattern(testType test)
  411.     {
  412.     unsigned a,c;
  413.     unsigned long offset;
  414.  
  415.     outportb(0x3c4,0x02);                //get write plane enable
  416.     unsigned plane=inportb(0x3c5);
  417.  
  418.     outportb(0x3d4,0x13);                //get screen width in words
  419.     unsigned width=inportb(0x3d5)*2;    //convert to bytes
  420.  
  421.     // Now select the correct initialization method:
  422.  
  423.     switch (test)
  424.         {
  425.         case test1x256:
  426.         case test4x16:
  427.         case test4x256:
  428.             // All graphics modes: clear the screen, but take care of
  429.             //    write enabling all planes.
  430.             outport(0x3c4,0x0f02);
  431.             memset(graphScr, 0, 0xffff);
  432.             outportb(0x3c4,0x02);
  433.             outportb(0x3c5,plane);
  434.             break;
  435.         case testText:
  436.             // Just blank the text screen.
  437.             memset(textScr, 0, 8000);
  438.         }
  439.  
  440.     // Now for the selected pattern.  None of them are very impressive,
  441.     //    but it kinda gives you an idea of the mode.  If you have ideas
  442.     //    for more informative tests, please let me know!
  443.  
  444.     switch (test)
  445.         {
  446.         case testText:
  447.  
  448.             // Fill top line with the sequence "0123456789" lt grey/black:
  449.             a = 0;
  450.             for (c=0; c<width; c++)
  451.                 textScr[a++] = ('0'+(c+1)%10) | 0x0700;
  452.  
  453.             // Then fill 4 lines with the ASCII set in white on blue:
  454.             for (c=0; c<5*width; c++)
  455.                 textScr[a++] = c | 0x1f00;
  456.  
  457.             // Now fill the rest with the sequence "ABCDEFGHIJ" in all color
  458.             //    combinations (except blinking!):
  459.             c = 0;
  460.             while (a < 0x4800)
  461.                 textScr[a++] = ('A'+c%('K'-'A')) | (c<<8&0x7f), c++;
  462.             break;
  463.  
  464.         case test1x256:
  465.  
  466.             // Fill the first 32 lines with 1 pixel wide colored vertical
  467.             //    lines.
  468.             for (c=0; c<width*4; c++)
  469.                 for (a=0; a<32; a++)
  470.                     graphScr[a*width*4+c]=c;
  471.  
  472.             // Fill the rest with 1 pixel high horizontal lines.
  473.             c=0;
  474.             offset=32*4*width;
  475.             do    {
  476.                 memset(graphScr+offset, c++, width*4);
  477.                                     //horizontal lines, 1 color each
  478.                 offset+=width<<2;
  479.                 }
  480.             while (offset < (0xffff-width<<2));
  481.             break;
  482.  
  483.         case test4x256:
  484.  
  485.             // This test is affected by the Write Plane Enable register.
  486.             // First put up 32 horizontal lines in the 32 first colors.
  487.             for (c=0; c<(width<<5); c++)
  488.                 graphScr[c]=c/width;
  489.  
  490.             // Then fill the rest with vertical lines.  This is too slow!
  491.             offset=c;
  492.             c=0;
  493.             a=1;
  494.             do    {
  495.                 outportb(0x3c5,a);    //Set write plane enable
  496.                 graphScr[offset]=c;
  497.  
  498.                 if ((a<<=1)>8)
  499.                     {
  500.                     a=1;
  501.                     ++offset;
  502.                     }
  503.                 if ((++c)==width<<2)
  504.                     c=0;
  505.                 }
  506.             while (offset <= 0xffff);
  507.             break;
  508.         case test4x16:
  509.  
  510.             // Fill first 32 lines with thick vertical stripes alternating
  511.             //    between black and the color selected by Write Plane Enable.
  512.             for (c=0; c<(width<<5); c++)
  513.                 graphScr[c]=0x0f;
  514.  
  515.             // Fill the rest with various bit patterns, in all colors.
  516.             for (a=0; a<256; a++)
  517.                 {
  518.                 outportb(0x3c5,a);
  519.                 memset(graphScr+4000+a*width, a, width);
  520.                 }
  521.             break;
  522.         }
  523.     }
  524.  
  525.  
  526. // tellTest() puts the name of the current test at the correct position on
  527. //    the edit screen.
  528.  
  529. void tellTest(testType test)
  530.     {
  531.     gotoxy(42,25);
  532.     cout << "Current test: " << testString[test];
  533.     clreol();
  534.     }
  535.  
  536. // showBitMask() updates the bit pattern display with the value 'v'.
  537.  
  538. void showBitMask(unsigned char v)
  539.     {
  540.     gotoxy(42,21);
  541.     cout << "Bit mask: 7 6 5 4 3 2 1 0";
  542.     gotoxy(51,22);
  543.     for (int e=7; e>=0; e--)
  544.         cout << (v&(1<<e) ? " 1" : " 0");
  545.     }
  546.  
  547.  
  548. // The main program starts here.
  549.  
  550. main(void)
  551.     {
  552.     cout << "TWEAK version 1.0 - mold your own screen modes, text or graphics!\n";
  553.     cout << "By Robert Schmidt of Ztiff Zox Softwear, 1992-93.\n";
  554.     cout << "e-mail: robert@solan.unit.no\n";
  555.     cout << "Donated to the Public Domain, with source and all.\n";
  556.     cout << endl;
  557.     cout << "Quick instructions:\n";
  558.     cout << "\tup/down:\tselect VGA register to modify\n";
  559.     cout << "\t0-9,A-F:\tenter new value directly, always hex\n";
  560.     cout << "\t+/-:\t\tincrease/decrease value respectively\n";
  561.     cout << "\tF1-F8:\t\ttoggle individual bits, F1=MSB, F8=LSB\n";
  562.     cout << "\tTAB:\t\tchoose test pattern type\n";
  563.     cout << "\tENTER:\t\tview screen mode\n";
  564.     cout << "\tS/L or\n";
  565.     cout << "\tF9/F10:\t\tsave/load register set, respectively\n";
  566.     cout << "\tM:\t\tchoose BIOS mode to start tweaking from\n";
  567.     cout << "\tESC:\t\tquit program\n";
  568.     cout << endl;
  569.     cout << "Press any command key, or SPACE to begin...";
  570.     while (!kbhit());
  571.  
  572.     // No initialize the scratch register table.  The constructor will
  573.     //    capture whatever register values are in effect at startup.
  574.  
  575.     vgaRegTable tweak;
  576.  
  577.     // Then set our editing screen mode.
  578.  
  579.     setscreen(3);
  580.  
  581.     unsigned char regNo = 0;        // The register number we're currently
  582.                                     //    editing.
  583.     unsigned char quit = 0;            // Non-zero when a quit command is
  584.                                     //    initiated.
  585.     testType testNo = testText;        // Default test pattern.
  586.     int key = 0;                    // The last key pressed.
  587.  
  588.     // Build the editing screen:
  589.  
  590.     showBitMask(tweak[regNo]);
  591.     tweak.print(regNo);
  592.     tellTest(testNo);
  593.  
  594.     // Start the main keyboard polling loop:
  595.  
  596.     while (!quit)
  597.         {
  598.         int key = getch();
  599.         if (!key)
  600.             key = getch() << 8;
  601.         int prevRegNo = regNo;
  602.  
  603. keyentry:
  604.         switch (key)
  605.             {
  606.             case 0x4700:            //HOME
  607.                 regNo = 0;
  608.                 break;
  609.             case 0x4800:            //UP
  610.                 if (regNo > 0)
  611.                     regNo--;
  612.                 else
  613.                     regNo = registers-1;
  614.                 break;
  615.             case 0x4F00:            //END
  616.                 regNo = registers-1;
  617.                 break;
  618.             case 0x5000:            //DOWN
  619.                 if (regNo < registers-1)
  620.                     regNo++;
  621.                 else
  622.                     regNo = 0;
  623.                 break;
  624.  
  625.             // Function keys - toggle single bit in selected reg.
  626.             case 0x3B00:            //F1
  627.             case 0x3C00:
  628.             case 0x3D00:
  629.             case 0x3E00:
  630.             case 0x3F00:
  631.             case 0x4000:
  632.             case 0x4100:
  633.             case 0x4200:            //F8
  634.                 tweak[regNo] ^= (1<<7-(key-59));
  635.                 break;
  636.             case 0x4300:            //F9
  637.             case 0x4400:            //F10
  638.  
  639.                 // Handle file operations (save/load):
  640.                 char fname[13];
  641.                 FILE *f;
  642.                 int r;
  643.                 int save=(key==67);
  644.  
  645.                 // Prompt for filename.
  646.                 gotoxy(42,24);
  647.                 cout << (save?"Save":"Load") << " file name: ";
  648.                 clreol();
  649.                 gets(fname);
  650.  
  651.                 // Open file in selected mode.
  652.                 if (!(f=fopen(fname,save?"wb":"rb")))
  653.                     {
  654.                     gotoxy(42,24);
  655.                     perror(fname);
  656.                     }
  657.                 else
  658.                     // Read/write file:
  659.                     for (r=0; r<registers; r++)
  660.                         if ((save?fwrite(&(tweak[r]),1,1,f)
  661.                                  :fread(&(tweak[r]),1,1,f)) == 0)
  662.                             {
  663.                             gotoxy(42,24);
  664.                             perror(fname);
  665.                             }
  666.                 fclose(f);
  667.  
  668.                 // Update screen to reflect changes if any.
  669.                 tweak.print(regNo);
  670.                 tellTest(testNo);
  671.                 break;
  672.             case '0':
  673.             case '1':
  674.             case '2':
  675.             case '3':
  676.             case '4':
  677.             case '5':
  678.             case '6':
  679.             case '7':
  680.             case '8':
  681.             case '9':
  682.             case 'A':
  683.             case 'B':
  684.             case 'C':
  685.             case 'D':
  686.             case 'E':
  687.             case 'F':
  688.             case 'a':
  689.             case 'b':
  690.             case 'c':
  691.             case 'd':
  692.             case 'e':
  693.             case 'f':
  694.  
  695.                 // Input hexadecimal number:
  696.                 gotoxy(40*(regNo/25)+38, regNo%25+1);
  697.                 ungetch(key);
  698.                 cprintf("%c \b", key);
  699.                 unsigned char newValue;
  700.                 cscanf("%2hx", &newValue);
  701.                 tweak[regNo] = newValue;
  702.                 break;
  703.  
  704.             // Alternate file I/O keys:
  705.             case 'S':
  706.             case 's':
  707.                 key = 0x4300;
  708.                 goto keyentry;
  709.             case 'L':
  710.             case 'l':
  711.                 key = 0x4400;
  712.                 goto keyentry;
  713.  
  714.             case 'M':
  715.             case 'm':
  716.  
  717.                 // Select a BIOS mode to work out from.
  718.                 int baseMode;
  719.                 gotoxy(42,23);
  720.                 cout << "Base BIOS screen mode: 0x";
  721.                 clreol();
  722.                 cscanf("%2hx",&baseMode);
  723.                 saveTempScreen();
  724.  
  725.                 // Set the selected mode, and grab register values.
  726.                 setscreen(baseMode);
  727.                 tweak.in();
  728.                 setscreen(3);
  729.                 restoreTempScreen();
  730.                 break;
  731.             case 27:    //ESC
  732.                 // Quit TWEAK:
  733.                 quit = 1;
  734.                 break;
  735.             case '-':
  736.                 // Decrease register value
  737.                 --tweak[regNo];
  738.                 break;
  739.             case '+':
  740.                 // Increase register value
  741.                 ++tweak[regNo];
  742.                 break;
  743. //            case 8:        //Backspace
  744.             case 13:    //ENTER
  745.  
  746.                 // Test the screen mode.
  747.                 saveTempScreen();
  748.                 tweak.out();
  749.                 testPattern(testNo);
  750.  
  751. // Uncomment the following and the 'case 8' above for an undocumented
  752. //    'feature'.  It's just some fancy effect I wanted to test.
  753.  
  754. /*                if (key==8)
  755.                     {
  756.                     for (int i=0x1F; i>=0x10; i--)
  757.                         {
  758.                         waitForRetrace();
  759.                         outportb(0x3d4,0x09);
  760.                         outportb(0x3d5,i|0x80);
  761.                         }
  762.                     for (i=0x1F; i>=0; i--)
  763.                         {
  764.                         waitForRetrace();
  765.                         outportb(0x3d4,0x09);
  766.                         outportb(0x3d5,i);
  767.                         }
  768.                     }
  769. */
  770.                 getch();
  771.                 setscreen(3);
  772.                 restoreTempScreen();
  773.                 break;
  774.             case 9:        //TAB
  775.  
  776.                 // Select another test pattern:
  777.                 if ((++testNo)==tests)
  778.                     testNo = testText;
  779.                 tellTest(testNo);
  780.                 break;
  781.  
  782.             }
  783.         // Update the bit pattern display:
  784.         showBitMask(tweak[regNo]);
  785.  
  786.         // Put cursor at selected register:
  787.         tweak.printOne(prevRegNo, 0);
  788.         tweak.printOne(regNo, 1);
  789.         }
  790.  
  791.     // If we get here, the user wanted to quit, probably...
  792.     //    I ought to restore the screen mode saved at startup, but for now
  793.     //    we quit into the usual mode 3 (80x25).
  794.     clrscr();
  795.  
  796.     return 0;
  797.     }
  798.