DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/vga_other.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 
00020 #include <string.h>
00021 #include <math.h>
00022 #include "dosbox.h"
00023 #include "inout.h"
00024 #include "vga.h"
00025 #include "mem.h"
00026 #include "pic.h"
00027 #include "render.h"
00028 #include "mapper.h"
00029 
00030 #define crtc(blah) vga.crtc.blah
00031 
00032 static Bitu read_cga(Bitu /*port*/,Bitu /*iolen*/);
00033 static void write_cga(Bitu port,Bitu val,Bitu /*iolen*/);
00034 
00035 void UpdateCGAFromSaveState(void) {
00036         if (machine==MCH_CGA || machine==MCH_MCGA || machine==MCH_AMSTRAD) {
00037         write_cga(0x3D8,vga.tandy.mode_control,1); // restore CGA
00038         write_cga(0x3D9,vga.tandy.color_select,1); // restore CGA
00039     }
00040 }
00041 
00042 static unsigned char mcga_crtc_dat_org = 0x00;
00043 
00044 static void write_crtc_index_other(Bitu /*port*/,Bitu val,Bitu /*iolen*/) {
00045         vga.other.index=(Bit8u)(val & 0x1f);
00046 
00047     if (machine == MCH_MCGA) {
00048         /* odd real hardware behavior.
00049          * registers 0x20-0x3F are a mirror of 0x00-0x1F ORed by 0x40 */
00050         mcga_crtc_dat_org = (val & 0x20) ? 0x40 : 0x00;
00051     }
00052 }
00053 
00054 static Bitu read_crtc_index_other(Bitu /*port*/,Bitu /*iolen*/) {
00055         return vga.other.index;
00056 }
00057 
00058 static void write_crtc_data_other(Bitu /*port*/,Bitu val,Bitu /*iolen*/) {
00059         switch (vga.other.index) {
00060         case 0x00:              //Horizontal total
00061                 if (vga.other.htotal ^ val) VGA_StartResize();
00062                 vga.other.htotal=(Bit8u)val;
00063                 break;
00064         case 0x01:              //Horizontal displayed chars
00065                 if (vga.other.hdend ^ val) VGA_StartResize();
00066                 vga.other.hdend=(Bit8u)val;
00067                 break;
00068         case 0x02:              //Horizontal sync position
00069                 vga.other.hsyncp=(Bit8u)val;
00070                 break;
00071         case 0x03:              //Horizontal sync width
00072                 if (machine==MCH_TANDY) vga.other.vsyncw=(Bit8u)(val >> 4);
00073                 else vga.other.vsyncw = 16; // The MC6845 has a fixed v-sync width of 16 lines
00074                 vga.other.hsyncw=(Bit8u)(val & 0xf);
00075                 break;
00076         case 0x04:              //Vertical total
00077                 if (vga.other.vtotal ^ val) VGA_StartResize();
00078                 vga.other.vtotal=(Bit8u)(val&0x7f);
00079                 break;
00080         case 0x05:              //Vertical display adjust
00081                 if (vga.other.vadjust ^ val) VGA_StartResize();
00082                 vga.other.vadjust=(Bit8u)val;
00083                 break;
00084         case 0x06:              //Vertical rows
00085                 if (vga.other.vdend ^ val) VGA_StartResize();
00086                 vga.other.vdend=(Bit8u)(val&0x7f);
00087                 break;
00088         case 0x07:              //Vertical sync position
00089                 vga.other.vsyncp=(Bit8u)val;
00090                 break;
00091         case 0x09:              //Max scanline
00092                 val &= 0x1f; // VGADOC says bit 0-3 but the MC6845 datasheet says bit 0-4
00093                 if (vga.other.max_scanline ^ val) VGA_StartResize();
00094                 vga.other.max_scanline=(Bit8u)val;
00095                 break;
00096         case 0x0A:      /* Cursor Start Register */
00097                 vga.other.cursor_start = (Bit8u)(val & 0x3f);
00098                 vga.draw.cursor.sline = (Bit8u)(val&0x1f);
00099                 vga.draw.cursor.enabled = ((val & 0x60) != 0x20);
00100                 break;
00101         case 0x0B:      /* Cursor End Register */
00102                 vga.other.cursor_end = (Bit8u)(val&0x1f);
00103                 vga.draw.cursor.eline = (Bit8u)(val&0x1f);
00104                 break;
00105         case 0x0C:      /* Start Address High Register */
00106                 // Bit 12 (depending on video mode) and 13 are actually masked too,
00107                 // but so far no need to implement it.
00108         if (machine == MCH_MCGA)
00109             vga.config.display_start=(vga.config.display_start & 0x00FF) | ((val&0xFF) << 8);
00110         else
00111             vga.config.display_start=(vga.config.display_start & 0x00FF) | ((val&0x3F) << 8);
00112                 break;
00113         case 0x0D:      /* Start Address Low Register */
00114                 vga.config.display_start=(vga.config.display_start & 0xFF00) | val;
00115                 break;
00116         case 0x0E:      /*Cursor Location High Register */
00117                 vga.config.cursor_start&=0x00ffu;
00118                 vga.config.cursor_start|=(unsigned int)(((Bit8u)val) << 8u);
00119                 break;
00120         case 0x0F:      /* Cursor Location Low Register */
00121                 vga.config.cursor_start&=0xff00u;
00122                 vga.config.cursor_start|=(Bit8u)val;
00123                 break;
00124         case 0x10:      /* Light Pen High */
00125                 // MC6845 datasheet says the light pen registers are only readable
00126                 vga.other.lightpen &= 0xff;
00127                 vga.other.lightpen |= (val & 0x3f)<<8;          // only 6 bits
00128                 break;
00129         case 0x11:      /* Light Pen Low */
00130                 vga.other.lightpen &= 0xff00;
00131                 vga.other.lightpen |= (Bit8u)val;
00132                 break;
00133         default:
00134                 LOG(LOG_VGAMISC,LOG_NORMAL)("MC6845:Write %X to illegal index %x",(int)val,(int)vga.other.index);
00135         }
00136 }
00137 static Bitu read_crtc_data_other(Bitu /*port*/,Bitu /*iolen*/) {
00138         switch (vga.other.index) {
00139         case 0x00:              //Horizontal total
00140                 return vga.other.htotal;
00141         case 0x01:              //Horizontal displayed chars
00142                 return vga.other.hdend;
00143         case 0x02:              //Horizontal sync position
00144                 return vga.other.hsyncp;
00145         case 0x03:              //Horizontal and vertical sync width
00146                 if (machine==MCH_TANDY)
00147                         return (unsigned int)vga.other.hsyncw | (unsigned int)(vga.other.vsyncw << 4u);
00148                 else return vga.other.hsyncw;
00149         case 0x04:              //Vertical total
00150                 return vga.other.vtotal;
00151         case 0x05:              //Vertical display adjust
00152                 return vga.other.vadjust;
00153         case 0x06:              //Vertical rows
00154                 return vga.other.vdend;
00155         case 0x07:              //Vertical sync position
00156                 return vga.other.vsyncp;
00157         case 0x09:              //Max scanline
00158                 return vga.other.max_scanline;
00159         case 0x0A:      /* Cursor Start Register */
00160                 return vga.other.cursor_start;
00161         case 0x0B:      /* Cursor End Register */
00162                 return vga.other.cursor_end;
00163         case 0x0C:      /* Start Address High Register */
00164                 return (Bit8u)(vga.config.display_start >> 8u);
00165         case 0x0D:      /* Start Address Low Register */
00166                 return (Bit8u)(vga.config.display_start & 0xffu);
00167         case 0x0E:      /*Cursor Location High Register */
00168                 return (Bit8u)(vga.config.cursor_start >> 8u);
00169         case 0x0F:      /* Cursor Location Low Register */
00170                 return (Bit8u)(vga.config.cursor_start & 0xffu);
00171         case 0x10:      /* Light Pen High */
00172                 return (Bit8u)(vga.other.lightpen >> 8u);
00173         case 0x11:      /* Light Pen Low */
00174                 return (Bit8u)(vga.other.lightpen & 0xffu);
00175         default:
00176                 LOG(LOG_VGAMISC,LOG_NORMAL)("MC6845:Read from illegal index %x",vga.other.index);
00177         }
00178         return (Bitu)(~0);
00179 }
00180 
00181 static void write_crtc_data_mcga(Bitu port,Bitu val,Bitu iolen) {
00182     if (vga.other.index < 0x10) {
00183         /* MCGA has a write protect, just like VGA */
00184                 if (vga.other.index <= 0x07 && crtc(read_only)) return;
00185 
00186         /* 0x00 through 0x0F are the same as CGA */
00187         write_crtc_data_other(port,val,iolen);
00188     }
00189     else {
00190         switch (vga.other.index) {
00191             case 0x10: /* MCGA Mode Control */
00192                 {
00193                     const Bit8u changed = (vga.other.mcga_mode_control ^ (Bit8u)val);
00194 
00195                     /* bit 0: 1=select 320x200 256-color mode    0=all else
00196                      * bit 1: 1=select 640x480 2-color mode      0=all else
00197                      * bit 2: reserved
00198                      * bit 3: 1=horizontal timing parameters computed in hardware for video mode   0=...from timing in registers 0-3
00199                      * bit 4: 1=enable dot clock
00200                      * bit 5: reserved
00201                      * bit 6: inverse of bit 8 of vertical displayed register 0x06
00202                      * bit 7: 1=write protect registers 0-7 */
00203                     /* NTS: According to real hardware, bit 6 is more than just the 8th bit, clearing it automatically enables
00204                      *      the scanline doubling in hardware apparently. If other parameters are not adjusted, weird results
00205                      *      happen. */
00206                     vga.other.mcga_mode_control = (Bit8u)val;
00207                     if (val & 0x80)
00208                         crtc(read_only) = true;
00209                     else
00210                         crtc(read_only) = false;
00211 
00212                     if (vga.other.mcga_mode_control & 3) {
00213                         for (unsigned int i=0;i < 16;i++)
00214                             VGA_DAC_CombineColor(i,i);
00215 
00216                         VGA_DAC_UpdateColorPalette();
00217                     }
00218 
00219                     if (vga.other.mcga_mode_control & 1) { // MCGA 256-color mode
00220                                             VGA_SetMode(M_VGA);
00221                     }
00222                     else {
00223                         if (vga.other.mcga_mode_control & 2) { // MCGA 640x480 2-color
00224                                                 VGA_SetMode(M_TANDY2);
00225                                 vga.tandy.addr_mask = 0xFFFF;
00226                         }
00227                         else {
00228                             write_cga(0x3D8,vga.tandy.mode_control,1); // restore CGA
00229                                 vga.tandy.addr_mask = 8*1024 - 1;
00230                         }
00231 
00232                         write_cga(0x3D9,vga.tandy.color_select,1); // restore CGA
00233                     }
00234 
00235                     if (changed & 0x0B)
00236                         VGA_StartResize();
00237                 }
00238                 break;
00239             default:
00240                 LOG(LOG_VGAMISC,LOG_NORMAL)("MC6845:MCGA Write %X to illegal index %x",(int)val,(int)vga.other.index);
00241                 break;
00242         }
00243     }
00244 }
00245 static Bitu read_crtc_data_mcga(Bitu port,Bitu iolen) {
00246     if (vga.other.index < 0x10) {
00247         /* 0x00 through 0x0F are the same as CGA */
00248         return read_crtc_data_other(port,iolen) | mcga_crtc_dat_org;
00249     }
00250     else {
00251         switch (vga.other.index) {
00252             case 0x10: /* MCGA Mode Control */
00253                 return vga.other.mcga_mode_control | mcga_crtc_dat_org;
00254             default:
00255                         LOG(LOG_VGAMISC,LOG_NORMAL)("MC6845:MCGA Read from illegal index %x",vga.other.index);
00256                 break;
00257         }
00258     }
00259 
00260     /* real hardware returns 0x00 not 0xFF */
00261         return (Bitu)(0 | mcga_crtc_dat_org);
00262 }
00263 
00264 static void write_lightpen(Bitu port,Bitu val,Bitu) {
00265     (void)val;//UNUSED
00266         switch (port) {
00267         case 0x3db:     // Clear lightpen latch
00268                 vga.other.lightpen_triggered = false;
00269                 break;
00270         case 0x3dc:     // Preset lightpen latch
00271                 if (!vga.other.lightpen_triggered) {
00272                         vga.other.lightpen_triggered = true; // TODO: this shows at port 3ba/3da bit 1
00273                         
00274                         double timeInFrame = PIC_FullIndex()-vga.draw.delay.framestart;
00275                         double timeInLine = fmod(timeInFrame,vga.draw.delay.htotal);
00276                         Bitu current_scanline = (Bitu)(timeInFrame / vga.draw.delay.htotal);
00277                         
00278                         vga.other.lightpen = (Bit16u)((vga.draw.address_add/2) * (current_scanline/2));
00279                         vga.other.lightpen += (Bit16u)((timeInLine / vga.draw.delay.hdend) *
00280                                 ((float)(vga.draw.address_add/2)));
00281                 }
00282                 break;
00283         }
00284 }
00285 
00286 Bit8u cga_comp = 0;
00287 bool new_cga = 0;
00288 
00289 static double hue_offset = 0.0;
00290 
00291 static Bit8u cga16_val = 0;
00292 static void update_cga16_color(void);
00293 static Bit8u herc_pal = 0;
00294 static Bit8u mono_cga_pal = 0;
00295 static Bit8u mono_cga_bright = 0;
00296 static Bit8u const mono_cga_palettes[8][16][3] =
00297 {
00298         { // 0 - green, 4-color-optimized contrast
00299                 {0x00,0x00,0x00},{0x00,0x0d,0x03},{0x01,0x17,0x05},{0x01,0x1a,0x06},{0x02,0x28,0x09},{0x02,0x2c,0x0a},{0x03,0x39,0x0d},{0x03,0x3c,0x0e},
00300                 {0x00,0x07,0x01},{0x01,0x13,0x04},{0x01,0x1f,0x07},{0x01,0x23,0x08},{0x02,0x31,0x0b},{0x02,0x35,0x0c},{0x05,0x3f,0x11},{0x0d,0x3f,0x17},
00301         },
00302         { // 1 - green, 16-color-optimized contrast
00303                 {0x00,0x00,0x00},{0x00,0x0d,0x03},{0x01,0x15,0x05},{0x01,0x17,0x05},{0x01,0x21,0x08},{0x01,0x24,0x08},{0x02,0x2e,0x0b},{0x02,0x31,0x0b},
00304                 {0x01,0x22,0x08},{0x02,0x28,0x09},{0x02,0x30,0x0b},{0x02,0x32,0x0c},{0x03,0x39,0x0d},{0x03,0x3b,0x0e},{0x09,0x3f,0x14},{0x0d,0x3f,0x17},
00305         },
00306         { // 2 - amber, 4-color-optimized contrast
00307                 {0x00,0x00,0x00},{0x15,0x05,0x00},{0x20,0x0b,0x00},{0x24,0x0d,0x00},{0x33,0x18,0x00},{0x37,0x1b,0x00},{0x3f,0x26,0x01},{0x3f,0x2b,0x06},
00308                 {0x0b,0x02,0x00},{0x1b,0x08,0x00},{0x29,0x11,0x00},{0x2e,0x14,0x00},{0x3b,0x1e,0x00},{0x3e,0x21,0x00},{0x3f,0x32,0x0a},{0x3f,0x38,0x0d},
00309         },
00310         { // 3 - amber, 16-color-optimized contrast
00311                 {0x00,0x00,0x00},{0x15,0x05,0x00},{0x1e,0x09,0x00},{0x21,0x0b,0x00},{0x2b,0x12,0x00},{0x2f,0x15,0x00},{0x38,0x1c,0x00},{0x3b,0x1e,0x00},
00312                 {0x2c,0x13,0x00},{0x32,0x17,0x00},{0x3a,0x1e,0x00},{0x3c,0x1f,0x00},{0x3f,0x27,0x01},{0x3f,0x2a,0x04},{0x3f,0x36,0x0c},{0x3f,0x38,0x0d},
00313         },
00314         { // 4 - grey, 4-color-optimized contrast
00315                 {0x00,0x00,0x00},{0x0d,0x0d,0x0d},{0x15,0x15,0x15},{0x18,0x18,0x18},{0x24,0x24,0x24},{0x27,0x27,0x27},{0x33,0x33,0x33},{0x37,0x37,0x37},
00316                 {0x08,0x08,0x08},{0x10,0x10,0x10},{0x1c,0x1c,0x1c},{0x20,0x20,0x20},{0x2c,0x2c,0x2c},{0x2f,0x2f,0x2f},{0x3b,0x3b,0x3b},{0x3f,0x3f,0x3f},
00317         },
00318         { // 5 - grey, 16-color-optimized contrast
00319                 {0x00,0x00,0x00},{0x0d,0x0d,0x0d},{0x12,0x12,0x12},{0x15,0x15,0x15},{0x1e,0x1e,0x1e},{0x20,0x20,0x20},{0x29,0x29,0x29},{0x2c,0x2c,0x2c},
00320                 {0x1f,0x1f,0x1f},{0x23,0x23,0x23},{0x2b,0x2b,0x2b},{0x2d,0x2d,0x2d},{0x34,0x34,0x34},{0x36,0x36,0x36},{0x3d,0x3d,0x3d},{0x3f,0x3f,0x3f},
00321         },
00322         { // 6 - paper-white, 4-color-optimized contrast
00323                 {0x00,0x00,0x00},{0x0e,0x0f,0x10},{0x15,0x17,0x18},{0x18,0x1a,0x1b},{0x24,0x25,0x25},{0x27,0x28,0x28},{0x33,0x34,0x32},{0x37,0x38,0x35},
00324                 {0x09,0x0a,0x0b},{0x11,0x12,0x13},{0x1c,0x1e,0x1e},{0x20,0x22,0x22},{0x2c,0x2d,0x2c},{0x2f,0x30,0x2f},{0x3c,0x3c,0x38},{0x3f,0x3f,0x3b},
00325         },
00326         { // 7 - paper-white, 16-color-optimized contrast
00327                 {0x00,0x00,0x00},{0x0e,0x0f,0x10},{0x13,0x14,0x15},{0x15,0x17,0x18},{0x1e,0x20,0x20},{0x20,0x22,0x22},{0x29,0x2a,0x2a},{0x2c,0x2d,0x2c},
00328                 {0x1f,0x21,0x21},{0x23,0x25,0x25},{0x2b,0x2c,0x2b},{0x2d,0x2e,0x2d},{0x34,0x35,0x33},{0x37,0x37,0x34},{0x3e,0x3e,0x3a},{0x3f,0x3f,0x3b},
00329         },
00330 };
00331 
00332 static void cga16_color_select(Bit8u val) {
00333         cga16_val = val;
00334         update_cga16_color();
00335 }
00336 
00337 static void update_cga16_color(void) {
00338 // New algorithm based on code by reenigne
00339 // Works in all CGA graphics modes/color settings and can simulate older and newer CGA revisions
00340         static const double tau = 6.28318531; // == 2*pi
00341         static const double ns = 567.0/440;  // degrees of hue shift per nanosecond
00342 
00343         double tv_brightness = 0.0; // hardcoded for simpler implementation
00344         double tv_saturation = (new_cga ? 0.7 : 0.6);
00345 
00346         bool bw = (vga.tandy.mode_control&4) != 0;
00347         bool color_sel = (cga16_val&0x20) != 0;
00348         bool background_i = (cga16_val&0x10) != 0;      // Really foreground intensity, but this is what the CGA schematic calls it.
00349         bool bpp1 = (vga.tandy.mode_control&0x10) != 0;
00350         Bit8u overscan = cga16_val&0x0f;  // aka foreground colour in 1bpp mode
00351 
00352         double chroma_coefficient = new_cga ? 0.29 : 0.72;
00353         double b_coefficient = new_cga ? 0.07 : 0;
00354         double g_coefficient = new_cga ? 0.22 : 0;
00355         double r_coefficient = new_cga ? 0.1 : 0;
00356         double i_coefficient = new_cga ? 0.32 : 0.28;
00357         double rgbi_coefficients[0x10];
00358         for (int c = 0; c < 0x10; c++) {
00359                 double v = 0;
00360                 if ((c & 1) != 0)
00361                         v += b_coefficient;
00362                 if ((c & 2) != 0)
00363                         v += g_coefficient;
00364                 if ((c & 4) != 0)
00365                         v += r_coefficient;
00366                 if ((c & 8) != 0)
00367                         v += i_coefficient;
00368                 rgbi_coefficients[c] = v;
00369         }
00370 
00371         // The pixel clock delay calculation is not accurate for 2bpp, but the difference is small and a more accurate calculation would be too slow.
00372         static const double rgbi_pixel_delay = 15.5*ns;
00373         static const double chroma_pixel_delays[8] = {
00374                 0,        // Black:   no chroma
00375                 35*ns,    // Blue:    no XORs
00376                 44.5*ns,  // Green:   XOR on rising and falling edges
00377                 39.5*ns,  // Cyan:    XOR on falling but not rising edge
00378                 44.5*ns,  // Red:     XOR on rising and falling edges
00379                 39.5*ns,  // Magenta: XOR on falling but not rising edge
00380                 44.5*ns,  // Yellow:  XOR on rising and falling edges
00381                 39.5*ns}; // White:   XOR on falling but not rising edge
00382         double pixel_clock_delay;
00383         int o = overscan == 0 ? 15 : overscan;
00384         if (overscan == 8)
00385                 pixel_clock_delay = rgbi_pixel_delay;
00386         else {
00387                 double d = rgbi_coefficients[o];
00388                 pixel_clock_delay = (chroma_pixel_delays[o & 7]*chroma_coefficient + rgbi_pixel_delay*d)/(chroma_coefficient + d);
00389         }
00390         pixel_clock_delay -= 21.5*ns;  // correct for delay of color burst
00391 
00392         double hue_adjust = (-(90-33)-hue_offset+pixel_clock_delay)*tau/360.0;
00393         double chroma_signals[8][4];
00394         for (Bit8u i=0; i<4; i++) {
00395                 chroma_signals[0][i] = 0;
00396                 chroma_signals[7][i] = 1;
00397                 for (Bit8u j=0; j<6; j++) {
00398                         static const double phases[6] = {
00399                                 270 - 21.5*ns,  // blue
00400                                 135 - 29.5*ns,  // green
00401                                 180 - 21.5*ns,  // cyan
00402                                   0 - 21.5*ns,  // red
00403                                 315 - 29.5*ns,  // magenta
00404                                  90 - 21.5*ns}; // yellow/burst
00405                         // All the duty cycle fractions are the same, just under 0.5 as the rising edge is delayed 2ns more than the falling edge.
00406                         static const double duty = 0.5 - 2*ns/360.0;
00407 
00408                         // We have a rectangle wave with period 1 (in units of the reciprocal of the color burst frequency) and duty
00409                         // cycle fraction "duty" and phase "phase". We band-limit this wave to frequency 2 and sample it at intervals of 1/4.
00410                         // We model our band-limited wave with 4 frequency components:
00411                         //   f(x) = a + b*sin(x*tau) + c*cos(x*tau) + d*sin(x*2*tau)
00412                         // Then:
00413                         //   a =   integral(0, 1, f(x)*dx) = duty
00414                         //   b = 2*integral(0, 1, f(x)*sin(x*tau)*dx) = 2*integral(0, duty, sin(x*tau)*dx) = 2*(1-cos(x*tau))/tau
00415                         //   c = 2*integral(0, 1, f(x)*cos(x*tau)*dx) = 2*integral(0, duty, cos(x*tau)*dx) = 2*sin(duty*tau)/tau
00416                         //   d = 2*integral(0, 1, f(x)*sin(x*2*tau)*dx) = 2*integral(0, duty, sin(x*4*pi)*dx) = 2*(1-cos(2*tau*duty))/(2*tau)
00417                         double a = duty;
00418                         double b = 2.0*(1.0-cos(duty*tau))/tau;
00419                         double c = 2.0*sin(duty*tau)/tau;
00420                         double d = 2.0*(1.0-cos(duty*2*tau))/(2*tau);
00421 
00422                         double x = (phases[j] + 21.5*ns + pixel_clock_delay)/360.0 + i/4.0;
00423 
00424                         chroma_signals[j+1][i] = a + b*sin(x*tau) + c*cos(x*tau) + d*sin(x*2*tau);
00425                 }
00426         }
00427         Bitu CGApal[4] = {
00428                 overscan,
00429                 (Bitu)(2 + (color_sel||bw ? 1 : 0) + (background_i ? 8 : 0)),
00430                 (Bitu)(4 + (color_sel&&!bw? 1 : 0) + (background_i ? 8 : 0)),
00431                 (Bitu)(6 + (color_sel||bw ? 1 : 0) + (background_i ? 8 : 0))
00432         };
00433         for (Bit8u x=0; x<4; x++) {      // Position of pixel in question
00434                 bool even = (x & 1) == 0;
00435                 for (Bit8u bits=0; bits<(even ? 0x10 : 0x40); ++bits) {
00436                         double Y=0, I=0, Q=0;
00437                         for (Bit8u p=0; p<4; p++) {  // Position within color carrier cycle
00438                                 // generate pixel pattern.
00439                                 Bit8u rgbi;
00440                                 if (bpp1)
00441                                         rgbi = ((bits >> (3-p)) & (even ? 1 : 2)) != 0 ? overscan : 0;
00442                                 else
00443                                         if (even)
00444                                                 rgbi = (Bit8u)CGApal[(bits >> (2-(p&2)))&3];
00445                                         else
00446                                                 rgbi = (Bit8u)CGApal[(bits >> (4-((p+1)&6)))&3];
00447                                 Bit8u c = rgbi & 7;
00448                                 if (bw && c != 0)
00449                                         c = 7;
00450 
00451                                 // calculate composite output
00452                                 double chroma = chroma_signals[c][(p+x)&3]*chroma_coefficient;
00453                                 double composite = chroma + rgbi_coefficients[rgbi];
00454 
00455                                 Y+=composite;
00456                                 if (!bw) { // burst on
00457                                         I+=composite*2*cos(hue_adjust + (p+x)*tau/4.0);
00458                                         Q+=composite*2*sin(hue_adjust + (p+x)*tau/4.0);
00459                                 }
00460                         }
00461 
00462                         double contrast = 1 - tv_brightness;
00463 
00464                         Y = (contrast*Y/4.0) + tv_brightness; if (Y>1.0) Y=1.0; if (Y<0.0) Y=0.0;
00465                         I = (contrast*I/4.0) * tv_saturation; if (I>0.5957) I=0.5957; if (I<-0.5957) I=-0.5957;
00466                         Q = (contrast*Q/4.0) * tv_saturation; if (Q>0.5226) Q=0.5226; if (Q<-0.5226) Q=-0.5226;
00467 
00468                         static const double gamma = 2.2;
00469 
00470                         double R = Y + 0.9563*I + 0.6210*Q;     R = (R - 0.075) / (1-0.075); if (R<0) R=0; if (R>1) R=1;
00471                         double G = Y - 0.2721*I - 0.6474*Q;     G = (G - 0.075) / (1-0.075); if (G<0) G=0; if (G>1) G=1;
00472                         double B = Y - 1.1069*I + 1.7046*Q;     B = (B - 0.075) / (1-0.075); if (B<0) B=0; if (B>1) B=1;
00473                         R = pow(R, gamma);
00474                         G = pow(G, gamma);
00475                         B = pow(B, gamma);
00476 
00477                         int r = static_cast<int>(255*pow( 1.5073*R -0.3725*G -0.0832*B, 1/gamma)); if (r<0) r=0; if (r>255) r=255;
00478                         int g = static_cast<int>(255*pow(-0.0275*R +0.9350*G +0.0670*B, 1/gamma)); if (g<0) g=0; if (g>255) g=255;
00479                         int b = static_cast<int>(255*pow(-0.0272*R -0.0401*G +1.1677*B, 1/gamma)); if (b<0) b=0; if (b>255) b=255;
00480 
00481                         Bit8u index = bits | ((x & 1) == 0 ? 0x30 : 0x80) | ((x & 2) == 0 ? 0x40 : 0);
00482                         RENDER_SetPal(index,r,g,b);
00483                 }
00484         }
00485 }
00486 
00487 static void IncreaseHue(bool pressed) {
00488         if (!pressed)
00489                 return;
00490         hue_offset += 5.0;
00491         update_cga16_color();
00492         LOG_MSG("Hue at %f",hue_offset); 
00493 }
00494 
00495 static void DecreaseHue(bool pressed) {
00496         if (!pressed)
00497                 return;
00498         hue_offset -= 5.0;
00499         update_cga16_color();
00500         LOG_MSG("Hue at %f",hue_offset); 
00501 }
00502 
00503 static void write_cga_color_select(Bitu val) {
00504         vga.tandy.color_select=(Bit8u)val;
00505 
00506     if (vga.other.mcga_mode_control & 1) /* ignore COMPLETELY in 256-color MCGA mode */
00507         return;
00508 
00509     switch (vga.mode) {
00510         case  M_TANDY4: {
00511                 Bit8u base = (val & 0x10) ? 0x08 : 0;
00512                 Bit8u bg = val & 0xf;
00513                 if (vga.tandy.mode_control & 0x4)       // cyan red white
00514                         VGA_SetCGA4Table(bg, 3+base, 4+base, 7+base);
00515                 else if (val & 0x20)                            // cyan magenta white
00516                         VGA_SetCGA4Table(bg, 3+base, 5+base, 7+base);
00517                 else                                                            // green red brown
00518                         VGA_SetCGA4Table(bg, 2+base, 4+base, 6+base);
00519                 vga.tandy.border_color = bg;
00520                 vga.attr.overscan_color = bg;
00521                 break;
00522         }
00523         case M_TANDY2:
00524                 VGA_SetCGA2Table(0,val & 0xf);
00525                 vga.attr.overscan_color = 0;
00526                 break;
00527         case M_CGA16:
00528                 cga16_color_select((Bit8u)val);
00529                 break;
00530         case M_TEXT:
00531                 vga.tandy.border_color = val & 0xf;
00532                 vga.attr.overscan_color = 0;
00533                 break;
00534         case M_AMSTRAD: // Amstrad "palette". 0x3D9
00535                 break;
00536         default:
00537                 break;
00538         }
00539 }
00540 
00541 static Bitu read_cga(Bitu port,Bitu /*iolen*/) {
00542     if (machine == MCH_MCGA) { // On MCGA, ports 3D8h and 3D9h are also readable
00543         switch (port) {
00544             case 0x3d8:
00545                 return vga.tandy.mode_control;
00546             case 0x3d9: // color select
00547                 return vga.tandy.color_select;
00548         }
00549     }
00550 
00551     return ~0UL;
00552 }
00553 
00554 static void write_cga(Bitu port,Bitu val,Bitu /*iolen*/) {
00555     Bitu changed;
00556 
00557         switch (port) {
00558         case 0x3d8:
00559         changed = vga.tandy.mode_control ^ val;
00560 
00561                 vga.tandy.mode_control=(Bit8u)val;
00562                 vga.attr.disabled = (val&0x8)? 0: 1; 
00563         if (vga.other.mcga_mode_control & 3) { // MCGA 256-color mode or 2-color 640x480
00564             // do nothing
00565         }
00566         else if (vga.tandy.mode_control & 0x2) {                // graphics mode
00567                         if (vga.tandy.mode_control & 0x10) {// highres mode
00568                                 if (machine == MCH_AMSTRAD) {
00569                                         VGA_SetMode(M_AMSTRAD);                 //Amstrad 640x200x16 video mode.
00570                                 } else if (machine != MCH_MCGA && (cga_comp==1 || (cga_comp==0 && !(val&0x4))) && !mono_cga) {  // composite display
00571                                         VGA_SetMode(M_CGA16);           // composite ntsc 640x200 16 color mode
00572                                 } else {
00573                                         VGA_SetMode(M_TANDY2);
00574                                 }
00575                         } else {                                                        // lowres mode
00576                                 if (machine != MCH_MCGA && cga_comp==1) {                               // composite display
00577                                         VGA_SetMode(M_CGA16);           // composite ntsc 640x200 16 color mode
00578                                 } else {
00579                                         VGA_SetMode(M_TANDY4);
00580                                 }
00581                         }
00582 
00583                         write_cga_color_select(vga.tandy.color_select);
00584                 } else {
00585                         VGA_SetMode(M_TANDY_TEXT);
00586                 }
00587                 VGA_SetBlinking(val & 0x20);
00588 
00589         /* MCGA: Changes to this bit are important to track, because
00590          *       horizontal timings do not change between 40x25 and 80x25 */
00591         if (changed & 1) /* 80x25 enable bit changed */
00592             VGA_StartResize();
00593 
00594                 break;
00595         case 0x3d9: // color select
00596                 write_cga_color_select(val);
00597                 if( machine==MCH_AMSTRAD ) {
00598                         vga.amstrad.mask_plane = ( val | ( val << 8 ) | ( val << 16 ) | ( val << 24 ) ) & 0x0F0F0F0F;
00599                 }
00600                 break;
00601         case 0x3dd:
00602                 vga.amstrad.write_plane = val & 0x0F;
00603                 break;
00604         case 0x3de:
00605                 vga.amstrad.read_plane = val & 0x03;
00606                 break;
00607         case 0x3df:
00608                 vga.amstrad.border_color = val & 0x0F;
00609                 break;
00610         }
00611 }
00612 
00613 static void CGAModel(bool pressed) {
00614         if (!pressed) return;
00615         new_cga = !new_cga;
00616         update_cga16_color();
00617         LOG_MSG("%s model CGA selected", new_cga ? "Late" : "Early");
00618 }
00619  
00620 static void Composite(bool pressed) {
00621         if (!pressed) return;
00622         if (++cga_comp>2) cga_comp=0;
00623         LOG_MSG("Composite output: %s",(cga_comp==0)?"auto":((cga_comp==1)?"on":"off"));
00624         // switch RGB and Composite if in graphics mode
00625         if (vga.tandy.mode_control & 0x2)
00626                 write_cga(0x3d8,vga.tandy.mode_control,1);
00627 }
00628 
00629 static void tandy_update_palette() {
00630         // TODO mask off bits if needed
00631         if (machine == MCH_TANDY) {
00632                 switch (vga.mode) {
00633                 case M_TANDY2:
00634                         VGA_SetCGA2Table(vga.attr.palette[0],
00635                                 vga.attr.palette[vga.tandy.color_select&0xf]);
00636                         break;
00637                 case M_TANDY4:
00638                         if (vga.tandy.gfx_control & 0x8) {
00639                                 // 4-color high resolution - might be an idea to introduce M_TANDY4H
00640                                 VGA_SetCGA4Table( // function sets both medium and highres 4color tables
00641                                         vga.attr.palette[0], vga.attr.palette[1],
00642                                         vga.attr.palette[2], vga.attr.palette[3]);
00643                         } else {
00644                                 Bit8u color_set = 0;
00645                                 Bit8u r_mask = 0xf;
00646                                 if (vga.tandy.color_select & 0x10) color_set |= 8; // intensity
00647                                 if (vga.tandy.color_select & 0x20) color_set |= 1; // Cyan Mag. White
00648                                 if (vga.tandy.mode_control & 0x04) {                    // Cyan Red White
00649                                         color_set |= 1; 
00650                                         r_mask &= ~1;
00651                                 }
00652                                 VGA_SetCGA4Table(
00653                                         vga.attr.palette[vga.tandy.color_select&0xf],
00654                                         vga.attr.palette[(2|color_set)& vga.tandy.palette_mask],
00655                                         vga.attr.palette[(4|(color_set& r_mask))& vga.tandy.palette_mask],
00656                                         vga.attr.palette[(6|color_set)& vga.tandy.palette_mask]);
00657                         }
00658                         break;
00659                 default:
00660                         break;
00661                 }
00662         } else {
00663                 // PCJr
00664                 switch (vga.mode) {
00665                 case M_TANDY2:
00666                         VGA_SetCGA2Table(vga.attr.palette[0],vga.attr.palette[1]);
00667                         break;
00668                 case M_TANDY4:
00669                         VGA_SetCGA4Table(
00670                                 vga.attr.palette[0], vga.attr.palette[1],
00671                                 vga.attr.palette[2], vga.attr.palette[3]);
00672                         break;
00673                 default:
00674                         break;
00675                 }
00676         }
00677 }
00678 
00679 void VGA_SetModeNow(VGAModes mode);
00680 
00681 static void TANDY_FindMode(void) {
00682         if (vga.tandy.mode_control & 0x2) {
00683                 if (vga.tandy.gfx_control & 0x10) {
00684                         if (vga.mode==M_TANDY4) {
00685                                 VGA_SetModeNow(M_TANDY16);
00686                         } else VGA_SetMode(M_TANDY16);
00687                 }
00688                 else if (vga.tandy.gfx_control & 0x08) {
00689                         VGA_SetMode(M_TANDY4);
00690                 }
00691                 else if (vga.tandy.mode_control & 0x10)
00692                         VGA_SetMode(M_TANDY2);
00693                 else {
00694                         if (vga.mode==M_TANDY16) {
00695                                 VGA_SetModeNow(M_TANDY4);
00696                         } else VGA_SetMode(M_TANDY4);
00697                 }
00698                 tandy_update_palette();
00699         } else {
00700                 VGA_SetMode(M_TANDY_TEXT);
00701         }
00702 }
00703 
00704 static void PCJr_FindMode(void) {
00705         if (vga.tandy.mode_control & 0x2) {
00706                 if (vga.tandy.mode_control & 0x10) {
00707                         /* bit4 of mode control 1 signals 16 colour graphics mode */
00708                         if (vga.mode==M_TANDY4) VGA_SetModeNow(M_TANDY16); // TODO lowres mode only
00709                         else VGA_SetMode(M_TANDY16);
00710                 } else if (vga.tandy.gfx_control & 0x08) {
00711                         /* bit3 of mode control 2 signals 2 colour graphics mode */
00712                         VGA_SetMode(M_TANDY2);
00713                 } else {
00714                         /* otherwise some 4-colour graphics mode */
00715                         if (vga.mode==M_TANDY16) VGA_SetModeNow(M_TANDY4);
00716                         else VGA_SetMode(M_TANDY4);
00717                 }
00718                 tandy_update_palette();
00719         } else {
00720                 VGA_SetMode(M_TANDY_TEXT);
00721         }
00722 }
00723 
00724 static void TandyCheckLineMask(void ) {
00725         if ( vga.tandy.extended_ram & 1 ) {
00726                 vga.tandy.line_mask = 0;
00727         } else if ( vga.tandy.mode_control & 0x2) {
00728                 vga.tandy.line_mask |= 1;
00729         }
00730         if ( vga.tandy.line_mask ) {
00731                 vga.tandy.line_shift = 13;
00732                 vga.tandy.addr_mask = (1 << 13) - 1;
00733         } else {
00734                 vga.tandy.addr_mask = (Bitu)(~0);
00735                 vga.tandy.line_shift = 0;
00736         }
00737 }
00738 
00739 static void write_tandy_reg(Bit8u val) {
00740         switch (vga.tandy.reg_index) {
00741         case 0x0:
00742                 if (machine==MCH_PCJR) {
00743                         vga.tandy.mode_control=val;
00744                         VGA_SetBlinking(val & 0x20);
00745                         PCJr_FindMode();
00746                         if (val&0x8) vga.attr.disabled &= ~1;
00747                         else vga.attr.disabled |= 1;
00748                 } else {
00749                         LOG(LOG_VGAMISC,LOG_NORMAL)("Unhandled Write %2X to tandy reg %X",val,vga.tandy.reg_index);
00750                 }
00751                 break;
00752         case 0x1:       /* Palette mask */
00753                 vga.tandy.palette_mask = val;
00754                 tandy_update_palette();
00755                 break;
00756         case 0x2:       /* Border color */
00757                 vga.tandy.border_color=val;
00758                 break;
00759         case 0x3:       /* More control */
00760                 vga.tandy.gfx_control=val;
00761                 if (machine==MCH_TANDY) TANDY_FindMode();
00762                 else PCJr_FindMode();
00763                 break;
00764         case 0x5:       /* Extended ram page register */
00765                 // Bit 0 enables extended ram
00766                 // Bit 7 Switches clock, 0 -> cga 28.6 , 1 -> mono 32.5
00767                 vga.tandy.extended_ram = val;
00768                 //This is a bit of a hack to enable mapping video memory differently for highres mode
00769                 TandyCheckLineMask();
00770                 VGA_SetupHandlers();
00771                 break;
00772         default:
00773                 if ((vga.tandy.reg_index & 0xf0) == 0x10) { // color palette
00774                         vga.attr.palette[vga.tandy.reg_index-0x10] = val&0xf;
00775                         tandy_update_palette();
00776                 } else
00777                         LOG(LOG_VGAMISC,LOG_NORMAL)("Unhandled Write %2X to tandy reg %X",val,vga.tandy.reg_index);
00778         }
00779 }
00780 
00781 static void write_tandy(Bitu port,Bitu val,Bitu /*iolen*/) {
00782         switch (port) {
00783         case 0x3d8:
00784                 val &= 0x3f; // only bits 0-6 are used
00785                 if (vga.tandy.mode_control ^ val) {
00786                         vga.tandy.mode_control=(Bit8u)val;
00787                         if (val&0x8) vga.attr.disabled &= ~1;
00788                         else vga.attr.disabled |= 1;
00789                         TandyCheckLineMask();
00790                         VGA_SetBlinking(val & 0x20);
00791                         TANDY_FindMode();
00792                         VGA_StartResize();
00793                 }
00794                 break;
00795         case 0x3d9:
00796                 vga.tandy.color_select=(Bit8u)val;
00797                 tandy_update_palette();
00798                 break;
00799         case 0x3da:
00800                 vga.tandy.reg_index=(Bit8u)val;
00801                 //if (val&0x10) vga.attr.disabled |= 2;
00802                 //else vga.attr.disabled &= ~2;
00803                 break;
00804 //      case 0x3dd:     //Extended ram page address register:
00805 //              break;
00806         case 0x3de:
00807                 write_tandy_reg((Bit8u)val);
00808                 break;
00809         case 0x3df:
00810                 // CRT/processor page register
00811                 // See the comments on the PCJr version of this register.
00812                 // A difference to it is:
00813                 // Bit 3-5: Processor page CPU_PG
00814                 // The remapped range is 32kB instead of 16. Therefore CPU_PG bit 0
00815                 // appears to be ORed with CPU A14 (to preserve some sort of
00816                 // backwards compatibility?), resulting in odd pages being mapped
00817                 // as 2x16kB. Implemeted in vga_memory.cpp Tandy handler.
00818 
00819                 vga.tandy.line_mask = (Bit8u)(val >> 6);
00820                 vga.tandy.draw_bank = val & ((vga.tandy.line_mask&2) ? 0x6 : 0x7);
00821                 vga.tandy.mem_bank = (val >> 3) & 7;
00822                 TandyCheckLineMask();
00823                 VGA_SetupHandlers();
00824                 break;
00825         }
00826 }
00827 
00828 static void write_pcjr(Bitu port,Bitu val,Bitu /*iolen*/) {
00829         switch (port) {
00830         case 0x3da:
00831                 if (vga.tandy.pcjr_flipflop) write_tandy_reg((Bit8u)val);
00832                 else {
00833                         vga.tandy.reg_index=(Bit8u)val;
00834                         if (vga.tandy.reg_index & 0x10)
00835                                 vga.attr.disabled |= 2;
00836                         else vga.attr.disabled &= ~2;
00837                 }
00838                 vga.tandy.pcjr_flipflop=!vga.tandy.pcjr_flipflop;
00839                 break;
00840         case 0x3df:
00841                 // CRT/processor page register
00842                 
00843                 // Bit 0-2: CRT page PG0-2
00844                 // In one- and two bank modes, bit 0-2 select the 16kB memory
00845                 // area of system RAM that is displayed on the screen.
00846                 // In 4-banked modes, bit 1-2 select the 32kB memory area.
00847                 // Bit 2 only has effect when the PCJR upgrade to 128k is installed.
00848                 
00849                 // Bit 3-5: Processor page CPU_PG
00850                 // Selects the 16kB area of system RAM that is mapped to
00851                 // the B8000h IBM PC video memory window. Since A14-A16 of the 
00852                 // processor are unconditionally replaced with these bits when
00853                 // B8000h is accessed, the 16kB area is mapped to the 32kB
00854                 // range twice in a row. (Scuba Venture writes across the boundary)
00855                 
00856                 // Bit 6-7: Video Address mode
00857                 // 0: CRTC addresses A0-12 directly, accessing 8k characters
00858                 //    (+8k attributes). Used in text modes (one bank).
00859                 //    PG0-2 in effect. 16k range.
00860                 // 1: CRTC A12 is replaced with CRTC RA0 (see max_scanline).
00861                 //    This results in the even/odd scanline two bank system.
00862                 //    PG0-2 in effect. 16k range.
00863                 // 2: Documented as unused. CRTC addresses A0-12, PG0 is replaced
00864                 //    with RA1. Looks like nonsense.
00865                 //    PG1-2 in effect. 32k range which cannot be used completely.
00866                 // 3: CRTC A12 is replaced with CRTC RA0, PG0 is replaced with
00867                 //    CRTC RA1. This results in the 4-bank mode.
00868                 //    PG1-2 in effect. 32k range.
00869 
00870                 vga.tandy.line_mask = (Bit8u)(val >> 6);
00871                 vga.tandy.draw_bank = val & ((vga.tandy.line_mask&2) ? 0x6 : 0x7);
00872                 vga.tandy.mem_bank = (val >> 3) & 7;
00873                 vga.tandy.draw_base = &MemBase[vga.tandy.draw_bank * 16 * 1024];
00874                 vga.tandy.mem_base = &MemBase[vga.tandy.mem_bank * 16 * 1024];
00875                 TandyCheckLineMask();
00876                 VGA_SetupHandlers();
00877                 break;
00878         }
00879 }
00880 
00881 static void CycleHercPal(bool pressed) {
00882         if (!pressed) return;
00883         if (++herc_pal>3) herc_pal=0;
00884         Herc_Palette();
00885 }
00886 
00887 static void CycleMonoCGAPal(bool pressed) {
00888         if (!pressed) return;
00889         if (++mono_cga_pal>3) mono_cga_pal=0;
00890         Mono_CGA_Palette();
00891 }
00892 
00893 static void CycleMonoCGABright(bool pressed) {
00894         if (!pressed) return;
00895         if (++mono_cga_bright>1) mono_cga_bright=0;
00896         Mono_CGA_Palette();
00897 }
00898         
00899 void Herc_Palette(void) {       
00900         switch (herc_pal) {
00901         case 0: // White
00902                 VGA_DAC_SetEntry(0x7,0x2a,0x2a,0x2a);
00903                 VGA_DAC_SetEntry(0xf,0x3f,0x3f,0x3f);
00904                 break;
00905         case 1: // Amber
00906                 VGA_DAC_SetEntry(0x7,0x34,0x20,0x00);
00907                 VGA_DAC_SetEntry(0xf,0x3f,0x34,0x00);
00908                 break;
00909         case 2: // Paper-white
00910                 VGA_DAC_SetEntry(0x7,0x2c,0x2d,0x2c);
00911                 VGA_DAC_SetEntry(0xf,0x3f,0x3f,0x3b);
00912                 break;
00913         case 3: // Green
00914                 VGA_DAC_SetEntry(0x7,0x00,0x26,0x00);
00915                 VGA_DAC_SetEntry(0xf,0x00,0x3f,0x00);
00916                 break;
00917         }
00918         VGA_DAC_CombineColor(1,0x7);
00919         VGA_DAC_CombineColor(2,0xf);
00920 }
00921 
00922 static void HercBlend(bool pressed) {
00923         if (!pressed) return;
00924         vga.herc.blend = !vga.herc.blend;
00925         VGA_SetupDrawing(0);
00926 }
00927 
00928 void Mono_CGA_Palette(void) {   
00929         for (Bit8u ct=0;ct<16;ct++) {
00930                 VGA_DAC_SetEntry(ct,
00931                                                  mono_cga_palettes[2*mono_cga_pal+mono_cga_bright][ct][0],
00932                                                  mono_cga_palettes[2*mono_cga_pal+mono_cga_bright][ct][1],
00933                                                  mono_cga_palettes[2*mono_cga_pal+mono_cga_bright][ct][2]
00934                 );
00935                 VGA_DAC_CombineColor(ct,ct);
00936         }
00937 }
00938 
00939 static void write_hercules(Bitu port,Bitu val,Bitu /*iolen*/) {
00940         switch (port) {
00941         case 0x3b8: {
00942                 // the protected bits can always be cleared but only be set if the 
00943                 // protection bits are set
00944                 if (vga.herc.mode_control&0x2) {
00945                         // already set
00946                         if (!(val&0x2)) {
00947                                 vga.herc.mode_control &= ~0x2;
00948                                 VGA_SetMode(M_HERC_TEXT);
00949                         }
00950                 } else {
00951                         // not set, can only set if protection bit is set
00952                         if ((val & 0x2) && (vga.herc.enable_bits & 0x1)) {
00953                                 vga.herc.mode_control |= 0x2;
00954                                 VGA_SetMode(M_HERC_GFX);
00955                         }
00956                 }
00957                 if (vga.herc.mode_control&0x80) {
00958                         if (!(val&0x80)) {
00959                                 vga.herc.mode_control &= ~0x80;
00960                                 vga.tandy.draw_base = &vga.mem.linear[0];
00961                         }
00962                 } else {
00963                         if ((val & 0x80) && (vga.herc.enable_bits & 0x2)) {
00964                                 vga.herc.mode_control |= 0x80;
00965                                 vga.tandy.draw_base = &vga.mem.linear[32*1024];
00966                         }
00967                 }
00968                 vga.draw.blinking = (val&0x20)!=0;
00969                 vga.herc.mode_control &= 0x82;
00970                 vga.herc.mode_control |= val & ~0x82u;
00971                 break;
00972                 }
00973         case 0x3bf:
00974                 if ( vga.herc.enable_bits ^ val) {
00975                         vga.herc.enable_bits=(Bit8u)val;
00976                         // Bit 1 enables the upper 32k of video memory,
00977                         // so update the handlers
00978                         VGA_SetupHandlers();
00979                 }
00980                 break;
00981         }
00982 }
00983 
00984 /* static Bitu read_hercules(Bitu port,Bitu iolen) {
00985         LOG_MSG("read from Herc port %x",port);
00986         return 0;
00987 } */
00988 
00989 Bitu read_herc_status(Bitu /*port*/,Bitu /*iolen*/) {
00990         // 3BAh (R):  Status Register
00991         // bit   0  Horizontal sync
00992         //       1  Light pen status (only some cards)
00993         //       3  Video signal
00994         //     4-6      000: Hercules
00995         //                      001: Hercules Plus
00996         //                      101: Hercules InColor
00997         //                      111: Unknown clone
00998         //       7  Vertical sync inverted
00999 
01000         double timeInFrame = PIC_FullIndex()-vga.draw.delay.framestart;
01001         Bit8u retval=0x72; // Hercules ident; from a working card (Winbond W86855AF)
01002                                         // Another known working card has 0x76 ("KeysoGood", full-length)
01003 
01004     if (machine == MCH_HERC) {
01005         /* NTS: Vertical retrace bit is hercules-specific, as documented.
01006          *      DOSLIB uses this to detect MDA vs Hercules.
01007          *
01008          *      This (and DOSLIB) will be revised when I get around to
01009          *      plugging in my old MDA in one machine and Hercules card
01010          *      in another machine to double-check ---J.C. */
01011         if (timeInFrame < vga.draw.delay.vrstart ||
01012                 timeInFrame > vga.draw.delay.vrend) retval |= 0x80;
01013     }
01014     else {
01015         retval |= 0x80; // bit 7 always set on MDA (right??)
01016     }
01017 
01018         double timeInLine=fmod(timeInFrame,vga.draw.delay.htotal);
01019         if (timeInLine >= vga.draw.delay.hrstart &&
01020                 timeInLine <= vga.draw.delay.hrend) retval |= 0x1;
01021 
01022     if (machine == MCH_HERC) {
01023         // 688 Attack sub checks bit 3 - as a workaround have the bit enabled
01024         // if no sync active (corresponds to a completely white screen)
01025         if ((retval&0x81)==0x80) retval |= 0x8;
01026     }
01027 
01028         return retval;
01029 }
01030 
01031 
01032 void VGA_SetupOther(void) {
01033         memset( &vga.tandy, 0, sizeof( vga.tandy ));
01034         vga.attr.disabled = 0;
01035         vga.config.bytes_skip=0;
01036 
01037         //Initialize monochrome pal and bright
01038         herc_pal = mono_cga_pal = vga.draw.monochrome_pal;
01039         mono_cga_bright = vga.draw.monochrome_bright;
01040 
01041         //Initialize values common for most machines, can be overwritten
01042         vga.tandy.draw_base = vga.mem.linear;
01043         vga.tandy.mem_base = vga.mem.linear;
01044         vga.tandy.addr_mask = 8*1024 - 1;
01045         vga.tandy.line_mask = 3;
01046         vga.tandy.line_shift = 13;
01047 
01048         if (machine==MCH_CGA || machine==MCH_AMSTRAD || IS_TANDY_ARCH) {
01049                 extern Bit8u int10_font_08[256 * 8];
01050                 for (Bitu i=0;i<256;i++)        memcpy(&vga.draw.font[i*32],&int10_font_08[i*8],8);
01051                 vga.draw.font_tables[0]=vga.draw.font_tables[1]=vga.draw.font;
01052         }
01053     if (machine==MCH_MCGA) { // MCGA uses a 8x16 font, through double-scanning as if 8x8 CGA text mode
01054         extern Bit8u int10_font_16[256 * 16];
01055         for (Bitu i=0;i<256;i++)        memcpy(&vga.draw.font[i*32],&int10_font_16[i*16],16);
01056         vga.draw.font_tables[0]=vga.draw.font_tables[1]=vga.draw.font;
01057     }
01058         if (machine==MCH_CGA || IS_TANDY_ARCH || machine==MCH_HERC || machine==MCH_MDA) {
01059                 IO_RegisterWriteHandler(0x3db,write_lightpen,IO_MB);
01060                 IO_RegisterWriteHandler(0x3dc,write_lightpen,IO_MB);
01061         }
01062         if (machine==MCH_HERC || machine==MCH_MDA) {
01063                 extern Bit8u int10_font_14[256 * 14];
01064                 for (Bitu i=0;i<256;i++)        memcpy(&vga.draw.font[i*32],&int10_font_14[i*14],14);
01065                 vga.draw.font_tables[0]=vga.draw.font_tables[1]=vga.draw.font;
01066                 MAPPER_AddHandler(HercBlend,MK_nothing,0,"hercblend","Herc Blend");
01067                 MAPPER_AddHandler(CycleHercPal,MK_nothing,0,"hercpal","Herc Pal");
01068         }
01069         if (machine==MCH_CGA || machine==MCH_MCGA || machine==MCH_AMSTRAD) {
01070                 vga.amstrad.mask_plane = 0x07070707;
01071                 vga.amstrad.write_plane = 0x0F;
01072                 vga.amstrad.read_plane = 0x00;
01073                 vga.amstrad.border_color = 0x00;
01074 
01075                 IO_RegisterWriteHandler(0x3d8,write_cga,IO_MB);
01076                 IO_RegisterWriteHandler(0x3d9,write_cga,IO_MB);
01077 
01078         if (machine == MCH_MCGA) { /* ports 3D8h-3D9h are readable on MCGA */
01079             IO_RegisterReadHandler(0x3d8,read_cga,IO_MB);
01080             IO_RegisterReadHandler(0x3d9,read_cga,IO_MB);
01081         }
01082 
01083                 if (machine == MCH_AMSTRAD) {
01084                         IO_RegisterWriteHandler(0x3dd,write_cga,IO_MB);
01085                         IO_RegisterWriteHandler(0x3de,write_cga,IO_MB);
01086                         IO_RegisterWriteHandler(0x3df,write_cga,IO_MB);
01087                 }
01088 
01089                 if(!mono_cga) {
01090             MAPPER_AddHandler(IncreaseHue,MK_nothing,0,"inchue","Inc Hue");
01091             MAPPER_AddHandler(DecreaseHue,MK_nothing,0,"dechue","Dec Hue");
01092             MAPPER_AddHandler(CGAModel,MK_nothing,0,"cgamodel","CGA Model");
01093             MAPPER_AddHandler(Composite,MK_nothing,0,"cgacomp","CGA Comp");
01094         } else {
01095             MAPPER_AddHandler(CycleMonoCGAPal,MK_nothing,0,"monocgapal","Mono CGA Pal"); 
01096             MAPPER_AddHandler(CycleMonoCGABright,MK_nothing,0,"monocgabright","Mono CGA Bright"); 
01097         }
01098         }
01099         if (machine==MCH_TANDY) {
01100                 write_tandy( 0x3df, 0x0, 0 );
01101                 IO_RegisterWriteHandler(0x3d8,write_tandy,IO_MB);
01102                 IO_RegisterWriteHandler(0x3d9,write_tandy,IO_MB);
01103                 IO_RegisterWriteHandler(0x3da,write_tandy,IO_MB);
01104                 IO_RegisterWriteHandler(0x3de,write_tandy,IO_MB);
01105                 IO_RegisterWriteHandler(0x3df,write_tandy,IO_MB);
01106         }
01107         if (machine==MCH_PCJR) {
01108                 //write_pcjr will setup base address
01109                 write_pcjr( 0x3df, 0x7 | (0x7 << 3), 0 );
01110                 IO_RegisterWriteHandler(0x3da,write_pcjr,IO_MB);
01111                 IO_RegisterWriteHandler(0x3df,write_pcjr,IO_MB);
01112         }
01113         if (machine==MCH_HERC || machine==MCH_MDA) {
01114                 Bitu base=0x3b0;
01115                 for (Bitu i = 0; i < 4; i++) {
01116                         // The registers are repeated as the address is not decoded properly;
01117                         // The official ports are 3b4, 3b5
01118                         IO_RegisterWriteHandler(base+i*2,write_crtc_index_other,IO_MB);
01119                         IO_RegisterWriteHandler(base+i*2+1,write_crtc_data_other,IO_MB);
01120                         IO_RegisterReadHandler(base+i*2,read_crtc_index_other,IO_MB);
01121                         IO_RegisterReadHandler(base+i*2+1,read_crtc_data_other,IO_MB);
01122                 }
01123                 vga.herc.blend=false;
01124                 vga.herc.enable_bits=0;
01125 
01126         if (machine==MCH_HERC)
01127             vga.herc.mode_control=0xa; // first mode written will be text mode
01128         else
01129             vga.herc.mode_control=0x8; // first mode written will be text mode
01130 
01131                 vga.crtc.underline_location = 13;
01132                 IO_RegisterReadHandler(0x3ba,read_herc_status,IO_MB);
01133         IO_RegisterWriteHandler(0x3b8,write_hercules,IO_MB);
01134     }
01135         if (machine==MCH_HERC) {
01136                 IO_RegisterWriteHandler(0x3bf,write_hercules,IO_MB);
01137         }
01138         if (machine==MCH_MDA) {
01139         VGA_SetMode(M_HERC_TEXT); // HACK
01140     }
01141         if (machine==MCH_CGA || machine==MCH_PCJR || machine==MCH_TANDY) {
01142                 Bitu base=0x3d0;
01143                 for (Bitu port_ct=0; port_ct<4; port_ct++) {
01144                         IO_RegisterWriteHandler(base+port_ct*2,write_crtc_index_other,IO_MB);
01145                         IO_RegisterWriteHandler(base+port_ct*2+1,write_crtc_data_other,IO_MB);
01146                         IO_RegisterReadHandler(base+port_ct*2,read_crtc_index_other,IO_MB);
01147                         IO_RegisterReadHandler(base+port_ct*2+1,read_crtc_data_other,IO_MB);
01148                 }
01149         }
01150         if (machine==MCH_AMSTRAD) {
01151                 Bitu base=0x3d4;
01152                 IO_RegisterWriteHandler(base,write_crtc_index_other,IO_MB);
01153                 IO_RegisterWriteHandler(base+1,write_crtc_data_other,IO_MB);
01154                 IO_RegisterReadHandler(base,read_crtc_index_other,IO_MB);
01155                 IO_RegisterReadHandler(base+1,read_crtc_data_other,IO_MB);
01156 
01157         // TODO: Does MCH_AMSTRAD need to emulate CGA mirroring of I/O ports?
01158     }
01159     if (machine==MCH_MCGA) {
01160                 Bitu base=0x3d0;
01161                 for (Bitu port_ct=0; port_ct<4; port_ct++) {
01162                         IO_RegisterWriteHandler(base+port_ct*2,write_crtc_index_other,IO_MB);
01163                         IO_RegisterWriteHandler(base+port_ct*2+1,write_crtc_data_mcga,IO_MB);
01164                         IO_RegisterReadHandler(base+port_ct*2,read_crtc_index_other,IO_MB);
01165                         IO_RegisterReadHandler(base+port_ct*2+1,read_crtc_data_mcga,IO_MB);
01166                 }
01167         }
01168 }
01169 
01170 // save state support
01171 void POD_Save_VGA_Other( std::ostream& stream )
01172 {
01173         // - pure struct data
01174         WRITE_POD( &vga.other, vga.other );
01175 
01176         //****************************************
01177         //****************************************
01178 
01179         // static globals
01180 
01181         // - system + user data
01182         WRITE_POD( &hue_offset, hue_offset );
01183         WRITE_POD( &cga16_val, cga16_val );
01184         WRITE_POD( &herc_pal, herc_pal );
01185 }
01186 
01187 
01188 void POD_Load_VGA_Other( std::istream& stream )
01189 {
01190         // - pure struct data
01191         READ_POD( &vga.other, vga.other );
01192 
01193         //****************************************
01194         //****************************************
01195 
01196         // static globals
01197 
01198         // - system + user data
01199         READ_POD( &hue_offset, hue_offset );
01200         READ_POD( &cga16_val, cga16_val );
01201         READ_POD( &herc_pal, herc_pal );
01202 }