DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/vga_dac.cpp
00001 /*
00002  *  Copyright (C) 2002-2015  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
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 #include "dosbox.h"
00020 #include "inout.h"
00021 #include "render.h"
00022 #include "vga.h"
00023 
00024 extern bool vga_enable_3C6_ramdac;
00025 
00026 /*
00027 3C6h (R/W):  PEL Mask
00028 bit 0-7  This register is anded with the palette index sent for each dot.
00029          Should be set to FFh.
00030 
00031 3C7h (R):  DAC State Register
00032 bit 0-1  0 indicates the DAC is in Write Mode and 3 indicates Read mode.
00033 
00034 3C7h (W):  PEL Address Read Mode
00035 bit 0-7  The PEL data register (0..255) to be read from 3C9h.
00036 Note: After reading the 3 bytes at 3C9h this register will increment,
00037       pointing to the next data register.
00038 
00039 3C8h (R/W):  PEL Address Write Mode
00040 bit 0-7  The PEL data register (0..255) to be written to 3C9h.
00041 Note: After writing the 3 bytes at 3C9h this register will increment, pointing
00042       to the next data register.
00043 
00044 3C9h (R/W):  PEL Data Register
00045 bit 0-5  Color value
00046 Note:  Each read or write of this register will cycle through first the
00047        registers for Red, Blue and Green, then increment the appropriate
00048        address register, thus the entire palette can be loaded by writing 0 to
00049        the PEL Address Write Mode register 3C8h and then writing all 768 bytes
00050        of the palette to this register.
00051 */
00052 
00053 enum {DAC_READ,DAC_WRITE};
00054 
00055 static void VGA_DAC_SendColor( Bitu index, Bitu src ) {
00056     /* NTS: Don't forget red/green/blue are 6-bit RGB not 8-bit RGB */
00057     const Bit8u red = vga.dac.rgb[src].red;
00058     const Bit8u green = vga.dac.rgb[src].green;
00059     const Bit8u blue = vga.dac.rgb[src].blue;
00060 
00061     /* FIXME: CGA composite mode calls RENDER_SetPal itself, which conflicts with this code */
00062     if (vga.mode == M_CGA16)
00063         return;
00064 
00065     /* FIXME: Can someone behind the GCC project explain how (unsigned int) OR (unsigned int) somehow becomes (signed int)?? */
00066 
00067     if (GFX_bpp >= 24) /* FIXME: Assumes 8:8:8. What happens when desktops start using the 10:10:10 format? */
00068         vga.dac.xlat32[index] =
00069             (uint32_t)(blue << (2u + GFX_Bshift)) |
00070             (uint32_t)(green << (2u + GFX_Gshift)) |
00071             (uint32_t)(red<<(2u + GFX_Rshift)) |
00072             (uint32_t)GFX_Amask;
00073     else {
00074         /* FIXME: Assumes 5:6:5. I need to test against 5:5:5 format sometime. Perhaps I could dig out some older VGA cards and XFree86 drivers that support that format? */
00075         vga.dac.xlat16[index] =
00076             (uint16_t)(((blue&0x3fu)>>1u)<<GFX_Bshift) |
00077             (uint16_t)((green&0x3fu)<<GFX_Gshift) |
00078             (uint16_t)(((red&0x3fu)>>1u)<<GFX_Rshift) |
00079             (uint16_t)GFX_Amask;
00080 
00081         /* PC-98 mode always renders 32bpp, therefore needs this fix */
00082         if (GFX_Bshift == 0)
00083             vga.dac.xlat32[index] = (uint32_t)(blue << 2U) | (uint32_t)(green << 10U) | (uint32_t)(red << 18U);
00084         else
00085             vga.dac.xlat32[index] = (uint32_t)(blue << 18U) | (uint32_t)(green << 10U) | (uint32_t)(red << 2U);
00086     }
00087 
00088     RENDER_SetPal( index, (red << 2u) | ( red >> 4u ), (green << 2u) | ( green >> 4u ), (blue << 2u) | ( blue >> 4u ) );
00089 }
00090 
00091 void VGA_DAC_UpdateColor( Bitu index ) {
00092     Bitu maskIndex;
00093 
00094     if (IS_VGA_ARCH) {
00095         if (vga.mode == M_VGA || vga.mode == M_LIN8) {
00096             /* WARNING: This code assumes index < 256 */
00097             switch (VGA_AC_remap) {
00098                 case AC_4x4:
00099                 default: // <- just in case
00100                     /* Standard VGA hardware (including the original IBM PS/2 hardware) */
00101                     maskIndex  =  vga.dac.combine[index&0xF] & 0x0F;
00102                     maskIndex += (vga.dac.combine[index>>4u] & 0x0F) << 4u;
00103                     maskIndex &=  vga.dac.pel_mask;
00104                     break;
00105                 case AC_low4:
00106                     /* Tseng ET4000 behavior, according to the SVGA card I have where only the low 4 bits are translated. --J.C. */
00107                     maskIndex  =  vga.dac.combine[index&0xF] & 0x0F;
00108 
00109                     /* FIXME: TEST THIS ON THE ACTUAL ET4000. This seems to make COPPER.EXE work correctly.
00110                      *        Is this what actual ET4000 hardware does in 256-color mode with Color Select? */
00111                     if (vga.attr.mode_control & 0x80)
00112                         maskIndex += vga.attr.color_select << 4;
00113                     else
00114                         maskIndex += index & 0xF0;
00115 
00116                     maskIndex &=  vga.dac.pel_mask;
00117                     break;
00118             }
00119         }
00120         else {
00121             maskIndex = vga.dac.combine[index&0xF] & vga.dac.pel_mask;
00122         }
00123 
00124         VGA_DAC_SendColor( index, maskIndex );
00125     }
00126     else if (machine == MCH_MCGA) {
00127         if (vga.mode == M_VGA || vga.mode == M_LIN8)
00128             maskIndex = index & vga.dac.pel_mask;
00129         else
00130             maskIndex = vga.dac.combine[index&0xF] & vga.dac.pel_mask;
00131 
00132         VGA_DAC_SendColor( index, maskIndex );
00133     }
00134     else {
00135         VGA_DAC_SendColor( index, index );
00136     }
00137 }
00138 
00139 void VGA_DAC_UpdateColorPalette() {
00140     for ( Bitu i = 0;i<256;i++) 
00141         VGA_DAC_UpdateColor( i );
00142 }
00143 
00144 void write_p3c6(Bitu port,Bitu val,Bitu iolen) {
00145     (void)iolen;//UNUSED
00146     (void)port;//UNUSED
00147     if((IS_VGA_ARCH) && (vga.dac.hidac_counter>3)) {
00148         vga.dac.reg02=val;
00149         vga.dac.hidac_counter=0;
00150         VGA_StartResize();
00151         return;
00152     }
00153     if ( vga.dac.pel_mask != val ) {
00154         LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:DCA:Pel Mask set to %X", (int)val);
00155         vga.dac.pel_mask = val;
00156 
00157         // TODO: MCGA 640x480 2-color mode appears to latch the DAC at retrace
00158         //       for background/foreground. Does that apply to the PEL mask too?
00159 
00160         VGA_DAC_UpdateColorPalette();
00161     }
00162 }
00163 
00164 
00165 Bitu read_p3c6(Bitu port,Bitu iolen) {
00166     (void)iolen;//UNUSED
00167     (void)port;//UNUSED
00168     if (vga_enable_3C6_ramdac)
00169         vga.dac.hidac_counter++;
00170 
00171     return vga.dac.pel_mask;
00172 }
00173 
00174 
00175 void write_p3c7(Bitu port,Bitu val,Bitu iolen) {
00176     (void)iolen;//UNUSED
00177     (void)port;//UNUSED
00178     vga.dac.hidac_counter=0;
00179     vga.dac.pel_index=0;
00180     vga.dac.state=DAC_READ;
00181     vga.dac.read_index=val;         /* NTS: Paradise SVGA behavior, read index = x, write index = x + 1 */
00182     vga.dac.write_index=val + 1;
00183 }
00184 
00185 Bitu read_p3c7(Bitu port,Bitu iolen) {
00186     (void)iolen;//UNUSED
00187     (void)port;//UNUSED
00188     vga.dac.hidac_counter=0;
00189     if (vga.dac.state==DAC_READ) return 0x3;
00190     else return 0x0;
00191 }
00192 
00193 void write_p3c8(Bitu port,Bitu val,Bitu iolen) {
00194     (void)iolen;//UNUSED
00195     (void)port;//UNUSED
00196     vga.dac.hidac_counter=0;
00197     vga.dac.pel_index=0;
00198     vga.dac.state=DAC_WRITE;
00199     vga.dac.write_index=val;        /* NTS: Paradise SVGA behavior, this affects write index, but not read index */
00200 }
00201 
00202 Bitu read_p3c8(Bitu port, Bitu iolen){
00203     (void)iolen;//UNUSED
00204     (void)port;//UNUSED
00205     vga.dac.hidac_counter=0;
00206     return vga.dac.write_index;
00207 }
00208 
00209 extern bool vga_palette_update_on_full_load;
00210 
00211 static unsigned char tmp_dac[3] = {0,0,0};
00212 
00213 void write_p3c9(Bitu port,Bitu val,Bitu iolen) {
00214     bool update = false;
00215 
00216     (void)iolen;//UNUSED
00217     (void)port;//UNUSED
00218     vga.dac.hidac_counter=0;
00219     val&=0x3f;
00220 
00221     if (vga.dac.pel_index < 3) {
00222         tmp_dac[vga.dac.pel_index]=val;
00223 
00224         if (!vga_palette_update_on_full_load) {
00225             /* update palette right away, partial change */
00226             switch (vga.dac.pel_index) {
00227                 case 0:
00228                     vga.dac.rgb[vga.dac.write_index].red=tmp_dac[0];
00229                     break;
00230                 case 1:
00231                     vga.dac.rgb[vga.dac.write_index].green=tmp_dac[1];
00232                     break;
00233                 case 2:
00234                     vga.dac.rgb[vga.dac.write_index].blue=tmp_dac[2];
00235                     break;
00236             }
00237             update = true;
00238         }
00239         else if (vga.dac.pel_index == 2) {
00240             /* update palette ONLY when all three are given */
00241             vga.dac.rgb[vga.dac.write_index].red=tmp_dac[0];
00242             vga.dac.rgb[vga.dac.write_index].green=tmp_dac[1];
00243             vga.dac.rgb[vga.dac.write_index].blue=tmp_dac[2];
00244             update = true;
00245         }
00246 
00247         if ((++vga.dac.pel_index) >= 3)
00248             vga.dac.pel_index = 0;
00249     }
00250 
00251     if (update) {
00252         // As seen on real hardware: 640x480 2-color is the ONLY video mode
00253         // where the MCGA hardware appears to latch foreground and background
00254         // colors from the DAC at retrace, instead of always reading through
00255         // the DAC.
00256         //
00257         // Perhaps IBM couldn't get the DAC to run fast enough for 640x480 2-color mode.
00258         if (machine == MCH_MCGA && (vga.other.mcga_mode_control & 2)) {
00259             /* do not update the palette right now.
00260              * MCGA double-buffers foreground and background colors */
00261         }
00262         else {
00263             VGA_DAC_UpdateColorPalette(); // FIXME: Yes, this is very inefficient. Will improve later.
00264         }
00265 
00266         /* only if we just completed a color should we advance */
00267         if (vga.dac.pel_index == 0)
00268             vga.dac.read_index = vga.dac.write_index++;                           // NTS: Paradise SVGA behavior
00269     }
00270 }
00271 
00272 Bitu read_p3c9(Bitu port,Bitu iolen) {
00273     (void)iolen;//UNUSED
00274     (void)port;//UNUSED
00275     vga.dac.hidac_counter=0;
00276     Bit8u ret;
00277     switch (vga.dac.pel_index) {
00278     case 0:
00279         ret=vga.dac.rgb[vga.dac.read_index].red;
00280         vga.dac.pel_index=1;
00281         break;
00282     case 1:
00283         ret=vga.dac.rgb[vga.dac.read_index].green;
00284         vga.dac.pel_index=2;
00285         break;
00286     case 2:
00287         ret=vga.dac.rgb[vga.dac.read_index].blue;
00288         vga.dac.pel_index=0;
00289         vga.dac.read_index=vga.dac.write_index++;                           // NTS: Paradise SVGA behavior
00290         break;
00291     default:
00292         LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:DAC:Illegal Pel Index");           //If this can actually happen that will be the day
00293         ret=0;
00294         break;
00295     }
00296     return ret;
00297 }
00298 
00299 void VGA_DAC_CombineColor(Bit8u attr,Bit8u pal) {
00300     vga.dac.combine[attr] = pal;
00301 
00302     if (IS_VGA_ARCH) {
00303         if (vga.mode == M_VGA || vga.mode == M_LIN8) {
00304             switch (VGA_AC_remap) {
00305                 case AC_4x4:
00306                 default: // <- just in case
00307                     /* Standard VGA hardware (including the original IBM PS/2 hardware) */
00308                     for (unsigned int i=(unsigned int)attr;i < 0x100;i += 0x10)
00309                         VGA_DAC_UpdateColor( i );
00310                     for (unsigned int i=0;i < 0x10;i++)
00311                         VGA_DAC_UpdateColor( i + (attr<<4u) );
00312                     break;
00313                 case AC_low4:
00314                     /* Tseng ET4000 behavior, according to the SVGA card I have where only the low 4 bits are translated. --J.C. */
00315                     for (unsigned int i=(unsigned int)attr;i < 0x100;i += 0x10)
00316                         VGA_DAC_UpdateColor( i );
00317                     break;
00318             }
00319         }
00320         else {
00321             VGA_DAC_UpdateColor( attr );
00322         }
00323     }
00324     else if (machine == MCH_MCGA) {
00325         VGA_DAC_UpdateColor( attr );
00326     }
00327     else {
00328         VGA_DAC_SendColor( attr, pal );
00329     }
00330 }
00331 
00332 void VGA_DAC_SetEntry(Bitu entry,Bit8u red,Bit8u green,Bit8u blue) {
00333     //Should only be called in machine != vga
00334     vga.dac.rgb[entry].red=red;
00335     vga.dac.rgb[entry].green=green;
00336     vga.dac.rgb[entry].blue=blue;
00337     for (Bitu i=0;i<16;i++) 
00338         if (vga.dac.combine[i]==entry)
00339             VGA_DAC_SendColor( i, i );
00340 }
00341 
00342 void VGA_SetupDAC(void) {
00343     vga.dac.first_changed=256;
00344     vga.dac.bits=6;
00345     vga.dac.pel_mask=0xff;
00346     vga.dac.pel_index=0;
00347     vga.dac.state=DAC_READ;
00348     vga.dac.read_index=0;
00349     vga.dac.write_index=0;
00350     vga.dac.hidac_counter=0;
00351     vga.dac.reg02=0;
00352     if (IS_VGA_ARCH || machine == MCH_MCGA) {
00353         /* Setup the DAC IO port Handlers */
00354         if (svga.setup_dac) {
00355             svga.setup_dac();
00356         } else {
00357             IO_RegisterWriteHandler(0x3c6,write_p3c6,IO_MB);
00358             IO_RegisterReadHandler(0x3c6,read_p3c6,IO_MB);
00359             IO_RegisterWriteHandler(0x3c7,write_p3c7,IO_MB);
00360             IO_RegisterReadHandler(0x3c7,read_p3c7,IO_MB);
00361             IO_RegisterWriteHandler(0x3c8,write_p3c8,IO_MB);
00362             IO_RegisterReadHandler(0x3c8,read_p3c8,IO_MB);
00363             IO_RegisterWriteHandler(0x3c9,write_p3c9,IO_MB);
00364             IO_RegisterReadHandler(0x3c9,read_p3c9,IO_MB);
00365         }
00366     }
00367 }
00368 
00369 void VGA_UnsetupDAC(void) {
00370     IO_FreeWriteHandler(0x3c6,IO_MB);
00371     IO_FreeReadHandler(0x3c6,IO_MB);
00372     IO_FreeWriteHandler(0x3c7,IO_MB);
00373     IO_FreeReadHandler(0x3c7,IO_MB);
00374     IO_FreeWriteHandler(0x3c8,IO_MB);
00375     IO_FreeReadHandler(0x3c8,IO_MB);
00376     IO_FreeWriteHandler(0x3c9,IO_MB);
00377     IO_FreeReadHandler(0x3c9,IO_MB);
00378 }
00379