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