DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/vga_draw.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 
00020 #include <string.h>
00021 #include <math.h>
00022 #include <stdio.h>
00023 #include "dosbox.h"
00024 #if defined (WIN32)
00025 #include <d3d9.h>
00026 #endif
00027 #include "timer.h"
00028 #include "setup.h"
00029 #include "support.h"
00030 #include "video.h"
00031 #include "render.h"
00032 #include "../gui/render_scalers.h"
00033 #include "vga.h"
00034 #include "pic.h"
00035 #include "menu.h"
00036 #include "timer.h"
00037 #include "config.h"
00038 #include "control.h"
00039 #include "pc98_cg.h"
00040 #include "pc98_gdc.h"
00041 #include "pc98_gdc_const.h"
00042 
00043 bool mcga_double_scan = false;
00044 
00045 const char* const mode_texts[M_MAX] = {
00046     "M_CGA2",           // 0
00047     "M_CGA4",
00048     "M_EGA",
00049     "M_VGA",
00050     "M_LIN4",
00051     "M_LIN8",           // 5
00052     "M_LIN15",
00053     "M_LIN16",
00054     "M_LIN24",
00055     "M_LIN32",
00056     "M_TEXT",           // 10
00057     "M_HERC_GFX",
00058     "M_HERC_TEXT",
00059     "M_CGA16",
00060     "M_TANDY2",
00061     "M_TANDY4",         // 15
00062     "M_TANDY16",
00063     "M_TANDY_TEXT",
00064     "M_AMSTRAD",
00065     "M_PC98",
00066     "M_FM_TOWNS",       // 20 STUB
00067     "M_PACKED4",
00068     "M_ERROR"
00069 };
00070 
00071 #if defined(_MSC_VER)
00072 # pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */
00073 # pragma warning(disable:4305) /* truncation from double to float */
00074 #endif
00075 
00076 //#undef C_DEBUG
00077 //#define C_DEBUG 1
00078 //#define LOG(X,Y) LOG_MSG
00079 
00080 bool et4k_highcolor_half_pixel_rate();
00081 
00082 double vga_fps = 70;
00083 double vga_mode_time_base = -1;
00084 int vga_mode_frames_since_time_base = 0;
00085 
00086 extern bool vga_3da_polled;
00087 extern bool vga_page_flip_occurred;
00088 extern bool vga_enable_hpel_effects;
00089 extern bool vga_enable_hretrace_effects;
00090 extern unsigned int vga_display_start_hretrace;
00091 extern float hretrace_fx_avg_weight;
00092 extern bool ignore_vblank_wraparound;
00093 extern bool vga_double_buffered_line_compare;
00094 
00095 extern bool pc98_31khz_mode;
00096 
00097 void memxor(void *_d,unsigned int byte,size_t count) {
00098     unsigned char *d = (unsigned char*)_d;
00099     while (count-- > 0) *d++ ^= byte;
00100 }
00101 
00102 void memxor_greendotted_16bpp(uint16_t *d,unsigned int count,unsigned int line) {
00103     static const uint16_t greenptrn[2] = { (0x3F << 5), 0 };
00104     line &= 1;
00105     count >>= 1;
00106     while (count-- > 0) {
00107         *d++ ^= greenptrn[line];
00108         *d++ ^= greenptrn[line^1];
00109     }
00110 }
00111 
00112 typedef Bit8u * (* VGA_Line_Handler)(Bitu vidstart, Bitu line);
00113 
00114 static VGA_Line_Handler VGA_DrawLine;
00115 static Bit8u TempLine[SCALER_MAXWIDTH * 4 + 256];
00116 static float hretrace_fx_avg = 0;
00117 
00118 static Bit8u * VGA_Draw_AMS_4BPP_Line(Bitu vidstart, Bitu line) {
00119     const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00120     const Bit8u *lbase;
00121     Bit32u *draw = (Bit32u *)TempLine;
00122     for (Bitu x=vga.draw.blocks;x>0;x--, vidstart++) {
00123         lbase = &base[ (vidstart & (8 * 1024 -1)) ];
00124         Bitu val0 = lbase[ 0 ];
00125         Bitu val1 = lbase[ 16384 ];
00126         Bitu val2 = lbase[ 32768 ];
00127         Bitu val3 = lbase[ 49152 ];
00128         
00129         *draw++=( ( CGA_2_Table[ val0 >> 4 ] << 0 ) | 
00130             ( CGA_2_Table[ val1 >> 4 ] << 1 ) |
00131             ( CGA_2_Table[ val2 >> 4 ] << 2 ) |
00132             ( CGA_2_Table[ val3 >> 4 ] << 3 ) ) & vga.amstrad.mask_plane;
00133         *draw++=( ( CGA_2_Table[ val0 & 0x0F ] << 0 ) | 
00134             ( CGA_2_Table[ val1 & 0x0F ] << 1 ) |
00135             ( CGA_2_Table[ val2 & 0x0F ] << 2 ) |
00136             ( CGA_2_Table[ val3 & 0x0F ] << 3 ) ) & vga.amstrad.mask_plane;
00137     }
00138     return TempLine;
00139 }
00140 
00141 struct vsync_state vsync;
00142 
00143 float uservsyncjolt=0.0f;
00144 
00145 VGA_Vsync vsyncmode_current = VS_Off;
00146 
00147 void VGA_VsyncUpdateMode(VGA_Vsync vsyncmode) {
00148     vsyncmode_current = vsyncmode;
00149 
00150     mainMenu.get_item("vsync_off").check(vsyncmode_current == VS_Off).refresh_item(mainMenu);
00151     mainMenu.get_item("vsync_on").check(vsyncmode_current == VS_On).refresh_item(mainMenu);
00152     mainMenu.get_item("vsync_force").check(vsyncmode_current == VS_Force).refresh_item(mainMenu);
00153     mainMenu.get_item("vsync_host").check(vsyncmode_current == VS_Host).refresh_item(mainMenu);
00154 
00155     switch(vsyncmode) {
00156     case VS_Off:
00157         vsync.manual    = false;
00158         vsync.persistent= false;
00159         vsync.faithful  = false;
00160         break;
00161     case VS_On:
00162         vsync.manual    = true;
00163         vsync.persistent= true;
00164         vsync.faithful  = true;
00165         break;
00166     case VS_Force:
00167     case VS_Host:
00168         vsync.manual    = true;
00169         vsync.persistent= true;
00170         vsync.faithful  = false;
00171         break;
00172     default:
00173         LOG_MSG("VGA_VsyncUpdateMode: Invalid mode, using defaults.");
00174         vsync.manual    = false;
00175         vsync.persistent= false;
00176         vsync.faithful  = false;
00177         break;
00178     }
00179 }
00180 
00181 void VGA_TweakUserVsyncOffset(float val) { uservsyncjolt = val; }
00182 
00183 static Bit8u * VGA_Draw_1BPP_Line(Bitu vidstart, Bitu line) {
00184     const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00185     Bit32u *draw = (Bit32u *)TempLine;
00186     for (Bitu x=vga.draw.blocks;x>0;x--, vidstart++) {
00187         Bitu val = base[(vidstart & (8 * 1024 -1))];
00188         *draw++=CGA_2_Table[val >> 4];
00189         *draw++=CGA_2_Table[val & 0xf];
00190     }
00191     return TempLine;
00192 }
00193 
00194 static Bit8u * VGA_Draw_1BPP_Blend_Line(Bitu vidstart, Bitu line) {
00195     const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00196     Bit32u *draw = (Bit32u *)TempLine;
00197     Bitu carry = 0;
00198     for (Bitu x=vga.draw.blocks;x>0;x--, vidstart++) {
00199         Bitu val1 = base[(vidstart & (8 * 1024 -1))];
00200         Bitu val2 = (val1 >> 1) + carry;
00201         carry = (val1 & 1) << 7;
00202         *draw++=CGA_2_Table[val1 >> 4] + CGA_2_Table[val2 >> 4];
00203         *draw++=CGA_2_Table[val1 & 0xf] + CGA_2_Table[val2 & 0xf];
00204     }
00205     return TempLine;
00206 }
00207 
00208 static Bit8u * EGA_Draw_2BPP_Line_as_EGA(Bitu vidstart, Bitu line) {
00209     const Bit32u *base = (Bit32u*)vga.draw.linear_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00210     Bit8u * draw=(Bit8u *)TempLine;
00211     VGA_Latch pixels;
00212     Bitu val,i;
00213 
00214     for (Bitu x=0;x<vga.draw.blocks;x++) {
00215         pixels.d = base[vidstart & vga.tandy.addr_mask];
00216         vidstart += 1u<<vga.config.addr_shift;
00217 
00218         /* CGA odd/even mode, first plane */
00219         val=pixels.b[0];
00220         for (i=0;i < 4;i++,val <<= 2)
00221             *draw++ = vga.attr.palette[(val>>6)&3];
00222 
00223         /* CGA odd/even mode, second plane */
00224         val=pixels.b[1];
00225         for (i=0;i < 4;i++,val <<= 2)
00226             *draw++ = vga.attr.palette[(val>>6)&3];
00227     }
00228     return TempLine;
00229 }
00230 
00231 static Bit8u * VGA_Draw_2BPP_Line_as_VGA(Bitu vidstart, Bitu line) {
00232     const Bit32u *base = (Bit32u*)vga.draw.linear_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00233     Bit32u * draw=(Bit32u *)TempLine;
00234     VGA_Latch pixels;
00235     Bitu val,i;
00236 
00237     for (Bitu x=0;x<vga.draw.blocks;x++) {
00238         pixels.d = base[vidstart & vga.tandy.addr_mask];
00239         vidstart += 1u<<vga.config.addr_shift;
00240 
00241         /* CGA odd/even mode, first plane */
00242         val=pixels.b[0];
00243         for (i=0;i < 4;i++,val <<= 2)
00244             *draw++ = vga.dac.xlat32[(val>>6)&3];
00245 
00246         /* CGA odd/even mode, second plane */
00247         val=pixels.b[1];
00248         for (i=0;i < 4;i++,val <<= 2)
00249             *draw++ = vga.dac.xlat32[(val>>6)&3];
00250     }
00251     return TempLine;
00252 }
00253 
00254 static Bit8u * EGA_Draw_1BPP_Line_as_EGA(Bitu vidstart, Bitu line) {
00255     const Bit32u *base = (Bit32u*)vga.draw.linear_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00256     Bit8u * draw=(Bit8u *)TempLine;
00257     VGA_Latch pixels;
00258     Bitu val,i;
00259 
00260     for (Bitu x=0;x<vga.draw.blocks;x++) {
00261         pixels.d = base[vidstart & vga.tandy.addr_mask];
00262         vidstart += 1u<<vga.config.addr_shift;
00263 
00264         val=pixels.b[0];
00265         for (i=0;i < 8;i++,val <<= 1)
00266             *draw++ = vga.attr.palette[(val>>7)&1];
00267     }
00268     return TempLine;
00269 }
00270 
00271 static Bit8u * VGA_Draw_1BPP_Line_as_MCGA(Bitu vidstart, Bitu line) {
00272     const Bit8u *base = (Bit8u*)vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00273     Bit32u * draw=(Bit32u *)TempLine;
00274     Bitu val,i;
00275 
00276     for (Bitu x=0;x<vga.draw.blocks;x++) {
00277         val = base[vidstart & vga.tandy.addr_mask];
00278         vidstart++;
00279 
00280         for (i=0;i < 8;i++,val <<= 1)
00281             *draw++ = vga.dac.xlat32[(val>>7)&1];
00282     }
00283     return TempLine;
00284 }
00285 
00286 static Bit8u * VGA_Draw_1BPP_Line_as_VGA(Bitu vidstart, Bitu line) {
00287     const Bit32u *base = (Bit32u*)vga.draw.linear_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00288     Bit32u * draw=(Bit32u *)TempLine;
00289     VGA_Latch pixels;
00290     Bitu val,i;
00291 
00292     for (Bitu x=0;x<vga.draw.blocks;x++) {
00293         pixels.d = base[vidstart & vga.tandy.addr_mask];
00294         vidstart += 1u<<vga.config.addr_shift;
00295 
00296         val=pixels.b[0];
00297         for (i=0;i < 8;i++,val <<= 1)
00298             *draw++ = vga.dac.xlat32[(val>>7)&1];
00299     }
00300     return TempLine;
00301 }
00302 
00303 static Bit8u * VGA_Draw_2BPP_Line_as_MCGA(Bitu vidstart, Bitu line) {
00304     const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00305     Bit32u * draw=(Bit32u *)TempLine;
00306     unsigned char val;
00307     unsigned int i;
00308 
00309     for (Bitu x=0;x<vga.draw.blocks;x++) {
00310         val = base[vidstart & vga.tandy.addr_mask];
00311         vidstart++;
00312 
00313         for (i=0;i < 4;i++,val <<= 2)
00314             *draw++ = vga.dac.xlat32[(val>>6)&3];
00315     }
00316 
00317     return TempLine;
00318 }
00319 
00320 static Bit8u * VGA_Draw_2BPP_Line(Bitu vidstart, Bitu line) {
00321     const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00322     Bit32u * draw=(Bit32u *)TempLine;
00323     for (Bitu x=0;x<vga.draw.blocks;x++) {
00324         Bitu val = base[vidstart & vga.tandy.addr_mask];
00325         vidstart++;
00326         *draw++=CGA_4_Table[val];
00327     }
00328     return TempLine;
00329 }
00330 
00331 static Bit8u * VGA_Draw_2BPPHiRes_Line(Bitu vidstart, Bitu line) {
00332     const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00333     Bit32u * draw=(Bit32u *)TempLine;
00334     for (Bitu x=0;x<vga.draw.blocks;x++) {
00335         Bitu val1 = base[vidstart & vga.tandy.addr_mask];
00336         ++vidstart;
00337         Bitu val2 = base[vidstart & vga.tandy.addr_mask];
00338         ++vidstart;
00339         *draw++=CGA_4_HiRes_Table[(val1>>4)|(val2&0xf0)];
00340         *draw++=CGA_4_HiRes_Table[(val1&0x0f)|((val2&0x0f)<<4)];
00341     }
00342     return TempLine;
00343 }
00344 
00345 static Bitu temp[643]={0};
00346 
00347 static Bit8u * VGA_Draw_CGA16_Line(Bitu vidstart, Bitu line) {
00348     const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00349 #define CGA16_READER(OFF) (base[(vidstart +(OFF))& (8*1024 -1)])
00350     Bit32u * draw=(Bit32u *)TempLine;
00351     //There are 640 hdots in each line of the screen.
00352     //The color of an even hdot always depends on only 4 bits of video RAM.
00353     //The color of an odd hdot depends on 4 bits of video RAM in
00354     //1-hdot-per-pixel modes and 6 bits of video RAM in 2-hdot-per-pixel
00355     //modes. We always assume 6 and use duplicate palette entries in
00356     //1-hdot-per-pixel modes so that we can use the same routine for all
00357     //composite modes.
00358   temp[1] = (CGA16_READER(0) >> 6) & 3;
00359     for(Bitu x = 2; x < 640; x+=2) {
00360         temp[x] = (temp[x-1] & 0xf);
00361         temp[x+1] = (temp[x] << 2) | ((( CGA16_READER(x>>3)) >> (6-(x&6)) )&3);
00362     }
00363     temp[640] = temp[639] & 0xf;
00364     temp[641] = temp[640] << 2;
00365     temp[642] = temp[641] & 0xf;
00366 
00367     Bitu i = 2;
00368     for (Bitu x=0;x<vga.draw.blocks;x++) {
00369         *draw++ = 0xc0708030 | temp[i] | (temp[i+1] << 8) | (temp[i+2] << 16) | (temp[i+3] << 24);
00370         i += 4;
00371         *draw++ = 0xc0708030 | temp[i] | (temp[i+1] << 8) | (temp[i+2] << 16) | (temp[i+3] << 24);
00372         i += 4;
00373     }
00374     return TempLine;
00375 #undef CGA16_READER
00376 }
00377 
00378 static Bit8u * VGA_Draw_4BPP_Line(Bitu vidstart, Bitu line) {
00379     const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00380     Bit8u* draw=TempLine;
00381     Bitu end = vga.draw.blocks*2;
00382     while(end) {
00383         Bit8u byte = base[vidstart & vga.tandy.addr_mask];
00384         *draw++=vga.attr.palette[byte >> 4];
00385         *draw++=vga.attr.palette[byte & 0x0f];
00386         vidstart++;
00387         end--;
00388     }
00389     return TempLine;
00390 }
00391 
00392 static Bit8u * VGA_Draw_4BPP_Line_Double(Bitu vidstart, Bitu line) {
00393     const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00394     Bit8u* draw=TempLine;
00395     Bitu end = vga.draw.blocks;
00396     while(end) {
00397         Bit8u byte = base[vidstart & vga.tandy.addr_mask];
00398         Bit8u data = vga.attr.palette[byte >> 4];
00399         *draw++ = data; *draw++ = data;
00400         data = vga.attr.palette[byte & 0x0f];
00401         *draw++ = data; *draw++ = data;
00402         vidstart++;
00403         end--;
00404     }
00405     return TempLine;
00406 }
00407 
00408 #if SDL_BYTEORDER == SDL_LIL_ENDIAN && defined(MACOSX) /* Mac OS X Intel builds use a weird RGBA order (alpha in the low 8 bits) */
00409 static inline Bit32u guest_bgr_to_macosx_rgba(const Bit32u x) {
00410     /* guest: XRGB      X   R   G   B
00411      * host:  RGBX      B   G   R   X */
00412     return      ((x & 0x000000FFU) << 24U) +      /* BBxxxxxx */
00413                 ((x & 0x0000FF00U) <<  8U) +      /* xxGGxxxx */
00414                 ((x & 0x00FF0000U) >>  8U);       /* xxxxRRxx */
00415 }
00416 #endif
00417 
00418 static Bit8u * VGA_Draw_Linear_Line_24_to_32(Bitu vidstart, Bitu /*line*/) {
00419     Bitu offset = vidstart & vga.draw.linear_mask;
00420     Bitu i;
00421 
00422     /* DOSBox's render/scalar code can't handle 24bpp natively, so we have
00423      * to convert 24bpp -> 32bpp.
00424      *
00425      * WARNING: My clever trick might crash on processors that don't support
00426      *          unaligned memory addressing. To explain what it's doing, is
00427      *          it's using a DWORD read to fetch the 24bpp pixel (plus an
00428      *          extra byte), then overwrites the extra byte with 0xFF to
00429      *          produce a valid RGBA 8:8:8:8 value with the original pixel's
00430      *          RGB plus alpha channel value of 0xFF. */
00431 #if SDL_BYTEORDER == SDL_LIL_ENDIAN && defined(MACOSX) /* Mac OS X Intel builds use a weird RGBA order (alpha in the low 8 bits) */
00432     for (i=0;i < vga.draw.width;i++)
00433         ((uint32_t*)TempLine)[i] = guest_bgr_to_macosx_rgba(*((uint32_t*)(vga.draw.linear_base+offset+(i*3)))) | 0x000000FF;
00434 #else
00435     for (i=0;i < vga.draw.width;i++)
00436         ((uint32_t*)TempLine)[i] = *((uint32_t*)(vga.draw.linear_base+offset+(i*3))) | 0xFF000000;
00437 #endif
00438 
00439     return TempLine;
00440 }
00441 
00442 static Bit8u * VGA_Draw_Linear_Line(Bitu vidstart, Bitu /*line*/) {
00443     Bitu offset = vidstart & vga.draw.linear_mask;
00444     Bit8u* ret = &vga.draw.linear_base[offset];
00445     
00446     // in case (vga.draw.line_length + offset) has bits set that
00447     // are not set in the mask: ((x|y)!=y) equals (x&~y)
00448     if (GCC_UNLIKELY(((vga.draw.line_length + offset) & (~vga.draw.linear_mask)) != 0u)) {
00449         // this happens, if at all, only once per frame (1 of 480 lines)
00450         // in some obscure games
00451         Bitu end = (Bitu)((Bitu)offset + (Bitu)vga.draw.line_length) & (Bitu)vga.draw.linear_mask;
00452         
00453         // assuming lines not longer than 4096 pixels
00454         Bitu wrapped_len = end & 0xFFF;
00455         Bitu unwrapped_len = vga.draw.line_length-wrapped_len;
00456         
00457         // unwrapped chunk: to top of memory block
00458         memcpy(TempLine, &vga.draw.linear_base[offset], unwrapped_len);
00459         // wrapped chunk: from base of memory block
00460         memcpy(&TempLine[unwrapped_len], vga.draw.linear_base, wrapped_len);
00461 
00462         ret = TempLine;
00463     }
00464 
00465 #if !defined(C_UNALIGNED_MEMORY)
00466     if (GCC_UNLIKELY( ((Bitu)ret) & (sizeof(Bitu)-1)) ) {
00467         memcpy( TempLine, ret, vga.draw.line_length );
00468         return TempLine;
00469     }
00470 #endif
00471     return ret;
00472 }
00473 
00474 /* WARNING: This routine assumes (vidstart&3) == 0 */
00475 static Bit8u * VGA_Draw_Xlat32_VGA_CRTC_bmode_Line(Bitu vidstart, Bitu /*line*/) {
00476     Bit32u* temps = (Bit32u*) TempLine;
00477     unsigned int poff = 0;
00478     Bitu skip; /* how much to skip after drawing 4 pixels */
00479 
00480     skip = 4u << vga.config.addr_shift;
00481 
00482     /* *sigh* it looks like DOSBox's VGA scanline code will pass nonzero bits 0-1 in vidstart */
00483     poff += vidstart & 3u;
00484     vidstart &= ~3ul;
00485 
00486     /* hack for Surprise! productions "copper" demo.
00487      * when the demo talks about making the picture waver, what it's doing is diddling
00488      * with the Start Horizontal Retrace register of the CRTC once per scanline.
00489      * ...yeah, really. It's a wonder in retrospect the programmer didn't burn out his
00490      * VGA monitor, and I bet this makes the demo effect unwatchable on LCD flat panels or
00491      * scan converters that rely on the pulses to detect VGA mode changes! */
00492     if (vga_enable_hretrace_effects) {
00493         /* NTS: This is NOT BACKWARDS. It makes sense if you think about it: the monitor
00494          *      begins swinging the electron beam back on horizontal retract, so if the
00495          *      retrace starts sooner, then the blanking on the left side appears to last
00496          *      longer because there are more clocks until active display.
00497          *
00498          *      Also don't forget horizontal total/blank/retrace etc. registers are in
00499          *      character clocks not pixels. In 320x200x256 mode, one character clock is
00500          *      4 pixels.
00501          *
00502          *      Finally, we average it with "weight" because CRTs have "inertia" */
00503         float a = 1.0 / (hretrace_fx_avg_weight + 1);
00504 
00505         hretrace_fx_avg *= 1.0 - a;
00506         hretrace_fx_avg += a * 4 * ((int)vga_display_start_hretrace - (int)vga.crtc.start_horizontal_retrace);
00507         int x = (int)floor(hretrace_fx_avg + 0.5);
00508 
00509         vidstart += (Bitu)((int)skip * (x >> 2));
00510         poff += x & 3;
00511     }
00512 
00513     for(Bitu i = 0; i < ((vga.draw.line_length>>(2/*32bpp*/+2/*4 pixels*/))+((poff+3)>>2)); i++) {
00514         Bit8u *ret = &vga.draw.linear_base[ vidstart & vga.draw.linear_mask ];
00515 
00516         /* one group of 4 */
00517         *temps++ = vga.dac.xlat32[*ret++];
00518         *temps++ = vga.dac.xlat32[*ret++];
00519         *temps++ = vga.dac.xlat32[*ret++];
00520         *temps++ = vga.dac.xlat32[*ret++];
00521         /* and skip */
00522         vidstart += skip;
00523     }
00524 
00525     return TempLine + (poff * 4);
00526 }
00527 
00528 static Bit8u * VGA_Draw_Xlat32_Linear_Line(Bitu vidstart, Bitu /*line*/) {
00529     Bit32u* temps = (Bit32u*) TempLine;
00530 
00531     /* hack for Surprise! productions "copper" demo.
00532      * when the demo talks about making the picture waver, what it's doing is diddling
00533      * with the Start Horizontal Retrace register of the CRTC once per scanline.
00534      * ...yeah, really. It's a wonder in retrospect the programmer didn't burn out his
00535      * VGA monitor, and I bet this makes the demo effect unwatchable on LCD flat panels or
00536      * scan converters that rely on the pulses to detect VGA mode changes! */
00537     if (vga_enable_hretrace_effects) {
00538         /* NTS: This is NOT BACKWARDS. It makes sense if you think about it: the monitor
00539          *      begins swinging the electron beam back on horizontal retract, so if the
00540          *      retrace starts sooner, then the blanking on the left side appears to last
00541          *      longer because there are more clocks until active display.
00542          *
00543          *      Also don't forget horizontal total/blank/retrace etc. registers are in
00544          *      character clocks not pixels. In 320x200x256 mode, one character clock is
00545          *      4 pixels.
00546          *
00547          *      Finally, we average it with "weight" because CRTs have "inertia" */
00548         float a = 1.0 / (hretrace_fx_avg_weight + 1);
00549 
00550         hretrace_fx_avg *= 1.0 - a;
00551         hretrace_fx_avg += a * 4 * ((int)vga_display_start_hretrace - (int)vga.crtc.start_horizontal_retrace);
00552         int x = (int)floor(hretrace_fx_avg + 0.5);
00553 
00554         vidstart += (Bitu)((int)x);
00555     }
00556 
00557     for(Bitu i = 0; i < (vga.draw.line_length>>2); i++)
00558         temps[i]=vga.dac.xlat32[vga.draw.linear_base[(vidstart+i)&vga.draw.linear_mask]];
00559 
00560     return TempLine;
00561 }
00562 
00563 extern Bit32u Expand16Table[4][16];
00564 
00565 static Bit8u * EGA_Draw_VGA_Planar_Xlat8_Line(Bitu vidstart, Bitu /*line*/) {
00566     Bit8u* temps = (Bit8u*) TempLine;
00567     Bit32u t1,t2,tmp;
00568 
00569     if (vga.seq.clocking_mode&4) { /* odd/even mode serialization */
00570         for (Bitu i = 0; i < ((vga.draw.line_length)+vga.draw.panning);) {
00571             if (vidstart > vga.draw.linear_mask)
00572                 vidstart = (vidstart + 4u) & vga.draw.linear_mask;
00573 
00574             t1 = t2 = *((Bit32u*)(&vga.draw.linear_base[ vidstart & vga.draw.linear_mask ]));
00575             t1 = (t1 >> 4) & 0x0f0f0f0f;
00576             t2 &= 0x0f0f0f0f;
00577             vidstart += 4 * 2;
00578 
00579             for (Bitu w = 0;w < 2;w++,t1>>=8,t2>>=8,i+=8) {
00580                 tmp =   Expand16Table[0][(t1>>0)&0xFF] |
00581                     Expand16Table[2][(t1>>16)&0xFF];
00582                 temps[i+0] = vga.attr.palette[(tmp>>0)&vga.attr.color_plane_enable];
00583                 temps[i+1] = vga.attr.palette[(tmp>>8)&vga.attr.color_plane_enable];
00584                 temps[i+2] = vga.attr.palette[(tmp>>16)&vga.attr.color_plane_enable];
00585                 temps[i+3] = vga.attr.palette[(tmp>>24)&vga.attr.color_plane_enable];
00586 
00587                 tmp =   Expand16Table[0][(t2>>0)&0xFF] |
00588                     Expand16Table[2][(t2>>16)&0xFF];
00589                 temps[i+4] = vga.attr.palette[(tmp>>0)&vga.attr.color_plane_enable];
00590                 temps[i+5] = vga.attr.palette[(tmp>>8)&vga.attr.color_plane_enable];
00591                 temps[i+6] = vga.attr.palette[(tmp>>16)&vga.attr.color_plane_enable];
00592                 temps[i+7] = vga.attr.palette[(tmp>>24)&vga.attr.color_plane_enable];
00593             }
00594         }
00595     }
00596     else {
00597         for (Bitu i = 0; i < ((vga.draw.line_length)+vga.draw.panning); i += 8) {
00598             t1 = t2 = *((Bit32u*)(&vga.draw.linear_base[ vidstart & vga.draw.linear_mask ]));
00599             t1 = (t1 >> 4) & 0x0f0f0f0f;
00600             t2 &= 0x0f0f0f0f;
00601             vidstart += 4;
00602 
00603             tmp =   Expand16Table[0][(t1>>0)&0xFF] |
00604                 Expand16Table[1][(t1>>8)&0xFF] |
00605                 Expand16Table[2][(t1>>16)&0xFF] |
00606                 Expand16Table[3][(t1>>24)&0xFF];
00607             temps[i+0] = vga.attr.palette[(tmp>>0)&vga.attr.color_plane_enable];
00608             temps[i+1] = vga.attr.palette[(tmp>>8)&vga.attr.color_plane_enable];
00609             temps[i+2] = vga.attr.palette[(tmp>>16)&vga.attr.color_plane_enable];
00610             temps[i+3] = vga.attr.palette[(tmp>>24)&vga.attr.color_plane_enable];
00611 
00612             tmp =   Expand16Table[0][(t2>>0)&0xFF] |
00613                 Expand16Table[1][(t2>>8)&0xFF] |
00614                 Expand16Table[2][(t2>>16)&0xFF] |
00615                 Expand16Table[3][(t2>>24)&0xFF];
00616             temps[i+4] = vga.attr.palette[(tmp>>0)&vga.attr.color_plane_enable];
00617             temps[i+5] = vga.attr.palette[(tmp>>8)&vga.attr.color_plane_enable];
00618             temps[i+6] = vga.attr.palette[(tmp>>16)&vga.attr.color_plane_enable];
00619             temps[i+7] = vga.attr.palette[(tmp>>24)&vga.attr.color_plane_enable];
00620         }
00621     }
00622 
00623     return TempLine + (vga.draw.panning);
00624 }
00625 
00626 static Bit8u * VGA_Draw_VGA_Packed4_Xlat32_Line(Bitu vidstart, Bitu /*line*/) {
00627     Bit32u* temps = (Bit32u*) TempLine;
00628     Bit8u t;
00629 
00630     for (Bitu i = 0; i < ((vga.draw.line_length>>2)+vga.draw.panning); i += 2) {
00631         t = vga.draw.linear_base[ vidstart & vga.draw.linear_mask ];
00632         vidstart++;
00633 
00634         temps[i+0] = vga.dac.xlat32[(t>>4)&0xF];
00635         temps[i+1] = vga.dac.xlat32[(t>>0)&0xF];
00636     }
00637 
00638     return TempLine + (vga.draw.panning*4);
00639 }
00640 
00641 static Bit8u * VGA_Draw_VGA_Planar_Xlat32_Line(Bitu vidstart, Bitu /*line*/) {
00642     Bit32u* temps = (Bit32u*) TempLine;
00643     Bit32u t1,t2,tmp;
00644 
00645     for (Bitu i = 0; i < ((vga.draw.line_length>>2)+vga.draw.panning); i += 8) {
00646         t1 = t2 = *((Bit32u*)(&vga.draw.linear_base[ vidstart & vga.draw.linear_mask ]));
00647         t1 = (t1 >> 4) & 0x0f0f0f0f;
00648         t2 &= 0x0f0f0f0f;
00649         vidstart += 4;
00650 
00651         tmp =   Expand16Table[0][(t1>>0)&0xFF] |
00652             Expand16Table[1][(t1>>8)&0xFF] |
00653             Expand16Table[2][(t1>>16)&0xFF] |
00654             Expand16Table[3][(t1>>24)&0xFF];
00655         temps[i+0] = vga.dac.xlat32[(tmp>>0)&0xFF];
00656         temps[i+1] = vga.dac.xlat32[(tmp>>8)&0xFF];
00657         temps[i+2] = vga.dac.xlat32[(tmp>>16)&0xFF];
00658         temps[i+3] = vga.dac.xlat32[(tmp>>24)&0xFF];
00659 
00660         tmp =   Expand16Table[0][(t2>>0)&0xFF] |
00661             Expand16Table[1][(t2>>8)&0xFF] |
00662             Expand16Table[2][(t2>>16)&0xFF] |
00663             Expand16Table[3][(t2>>24)&0xFF];
00664         temps[i+4] = vga.dac.xlat32[(tmp>>0)&0xFF];
00665         temps[i+5] = vga.dac.xlat32[(tmp>>8)&0xFF];
00666         temps[i+6] = vga.dac.xlat32[(tmp>>16)&0xFF];
00667         temps[i+7] = vga.dac.xlat32[(tmp>>24)&0xFF];
00668     }
00669 
00670     return TempLine + (vga.draw.panning*4);
00671 }
00672 
00673 //Test version, might as well keep it
00674 /* static Bit8u * VGA_Draw_Chain_Line(Bitu vidstart, Bitu line) {
00675     Bitu i = 0;
00676     for ( i = 0; i < vga.draw.width;i++ ) {
00677         Bitu addr = vidstart + i;
00678         TempLine[i] = vga.mem.linear[((addr&~3)<<2)+(addr&3)];
00679     }
00680     return TempLine;
00681 } */
00682 
00683 static Bit8u * VGA_Draw_VGA_Line_Xlat32_HWMouse( Bitu vidstart, Bitu /*line*/) {
00684     if (!svga.hardware_cursor_active || !svga.hardware_cursor_active())
00685         // HW Mouse not enabled, use the tried and true call
00686         return VGA_Draw_Xlat32_Linear_Line(vidstart, 0);
00687 
00688     Bitu lineat = (vidstart-(vga.config.real_start<<2)) / vga.draw.width;
00689     if ((vga.s3.hgc.posx >= vga.draw.width) ||
00690         (lineat < vga.s3.hgc.originy) ||
00691         (lineat > (vga.s3.hgc.originy + (63U-vga.s3.hgc.posy))) ) {
00692         // the mouse cursor *pattern* is not on this line
00693         return VGA_Draw_Xlat32_Linear_Line(vidstart, 0);
00694     } else {
00695         // Draw mouse cursor: cursor is a 64x64 pattern which is shifted (inside the
00696         // 64x64 mouse cursor space) to the right by posx pixels and up by posy pixels.
00697         // This is used when the mouse cursor partially leaves the screen.
00698         // It is arranged as bitmap of 16bits of bitA followed by 16bits of bitB, each
00699         // AB bits corresponding to a cursor pixel. The whole map is 8kB in size.
00700         Bit32u* temp = (Bit32u*)VGA_Draw_Xlat32_Linear_Line(vidstart, 0);
00701         //memcpy(TempLine, &vga.mem.linear[ vidstart ], vga.draw.width);
00702 
00703         // the index of the bit inside the cursor bitmap we start at:
00704         Bitu sourceStartBit = ((lineat - vga.s3.hgc.originy) + vga.s3.hgc.posy)*64 + vga.s3.hgc.posx;
00705         // convert to video memory addr and bit index
00706         // start adjusted to the pattern structure (thus shift address by 2 instead of 3)
00707         // Need to get rid of the third bit, so "/8 *2" becomes ">> 2 & ~1"
00708         Bitu cursorMemStart = ((sourceStartBit >> 2ul) & ~1ul) + (((Bit32u)vga.s3.hgc.startaddr) << 10ul);
00709         Bitu cursorStartBit = sourceStartBit & 0x7u;
00710         // stay at the right position in the pattern
00711         if (cursorMemStart & 0x2) cursorMemStart--;
00712         Bitu cursorMemEnd = cursorMemStart + (Bitu)((64 - vga.s3.hgc.posx) >> 2);
00713         Bit32u* xat = &temp[vga.s3.hgc.originx]; // mouse data start pos. in scanline
00714         for (Bitu m = cursorMemStart; m < cursorMemEnd; (m&1)?(m+=3):m++) {
00715             // for each byte of cursor data
00716             Bit8u bitsA = vga.mem.linear[m];
00717             Bit8u bitsB = vga.mem.linear[m+2];
00718             for (Bit8u bit=(0x80 >> cursorStartBit); bit != 0; bit >>= 1) {
00719                 // for each bit
00720                 cursorStartBit=0; // only the first byte has some bits cut off
00721                 if (bitsA&bit) {
00722                     if (bitsB&bit) *xat ^= 0xFFFFFFFF; // Invert screen data
00723                     //else Transparent
00724                 } else if (bitsB&bit) {
00725                     *xat = vga.dac.xlat32[vga.s3.hgc.forestack[0]]; // foreground color
00726                 } else {
00727                     *xat = vga.dac.xlat32[vga.s3.hgc.backstack[0]];
00728                 }
00729                 xat++;
00730             }
00731         }
00732         return (Bit8u*)temp;
00733     }
00734 }
00735 
00736 static Bit8u * VGA_Draw_VGA_Line_HWMouse( Bitu vidstart, Bitu /*line*/) {
00737     if (!svga.hardware_cursor_active || !svga.hardware_cursor_active())
00738         // HW Mouse not enabled, use the tried and true call
00739         return &vga.mem.linear[vidstart];
00740 
00741     Bitu lineat = (vidstart-(vga.config.real_start<<2)) / vga.draw.width;
00742     if ((vga.s3.hgc.posx >= vga.draw.width) ||
00743         (lineat < vga.s3.hgc.originy) || 
00744         (lineat > (vga.s3.hgc.originy + (63U-vga.s3.hgc.posy))) ) {
00745         // the mouse cursor *pattern* is not on this line
00746         return &vga.mem.linear[ vidstart ];
00747     } else {
00748         // Draw mouse cursor: cursor is a 64x64 pattern which is shifted (inside the
00749         // 64x64 mouse cursor space) to the right by posx pixels and up by posy pixels.
00750         // This is used when the mouse cursor partially leaves the screen.
00751         // It is arranged as bitmap of 16bits of bitA followed by 16bits of bitB, each
00752         // AB bits corresponding to a cursor pixel. The whole map is 8kB in size.
00753         memcpy(TempLine, &vga.mem.linear[ vidstart ], vga.draw.width);
00754         // the index of the bit inside the cursor bitmap we start at:
00755         Bitu sourceStartBit = ((lineat - vga.s3.hgc.originy) + vga.s3.hgc.posy)*64 + vga.s3.hgc.posx; 
00756         // convert to video memory addr and bit index
00757         // start adjusted to the pattern structure (thus shift address by 2 instead of 3)
00758         // Need to get rid of the third bit, so "/8 *2" becomes ">> 2 & ~1"
00759         Bitu cursorMemStart = ((sourceStartBit >> 2) & ~1ul) + (((Bit32u)vga.s3.hgc.startaddr) << 10ul);
00760         Bitu cursorStartBit = sourceStartBit & 0x7u;
00761         // stay at the right position in the pattern
00762         if (cursorMemStart & 0x2) cursorMemStart--;
00763         Bitu cursorMemEnd = cursorMemStart + (Bitu)((64 - vga.s3.hgc.posx) >> 2);
00764         Bit8u* xat = &TempLine[vga.s3.hgc.originx]; // mouse data start pos. in scanline
00765         for (Bitu m = cursorMemStart; m < cursorMemEnd; (m&1)?(m+=3):m++) {
00766             // for each byte of cursor data
00767             Bit8u bitsA = vga.mem.linear[m];
00768             Bit8u bitsB = vga.mem.linear[m+2];
00769             for (Bit8u bit=(0x80 >> cursorStartBit); bit != 0; bit >>= 1) {
00770                 // for each bit
00771                 cursorStartBit=0; // only the first byte has some bits cut off
00772                 if (bitsA&bit) {
00773                     if (bitsB&bit) *xat ^= 0xFF; // Invert screen data
00774                     //else Transparent
00775                 } else if (bitsB&bit) {
00776                     *xat = vga.s3.hgc.forestack[0]; // foreground color
00777                 } else {
00778                     *xat = vga.s3.hgc.backstack[0];
00779                 }
00780                 xat++;
00781             }
00782         }
00783         return TempLine;
00784     }
00785 }
00786 
00787 /* render 16bpp line DOUBLED horizontally */
00788 static Bit8u * VGA_Draw_LIN16_Line_2x(Bitu vidstart, Bitu /*line*/) {
00789     Bit16u *s = (Bit16u*)(&vga.mem.linear[vidstart]);
00790     Bit16u *d = (Bit16u*)TempLine;
00791 
00792     for (Bitu i = 0; i < (vga.draw.line_length>>2); i++) {
00793         d[0] = d[1] = *s++;
00794         d += 2;
00795     }
00796 
00797     return TempLine;
00798 }
00799 
00800 static Bit8u * VGA_Draw_LIN16_Line_HWMouse(Bitu vidstart, Bitu /*line*/) {
00801     if (!svga.hardware_cursor_active || !svga.hardware_cursor_active())
00802         return &vga.mem.linear[vidstart];
00803 
00804     Bitu lineat = ((vidstart-(vga.config.real_start<<2)) >> 1) / vga.draw.width;
00805     if ((vga.s3.hgc.posx >= vga.draw.width) ||
00806         (lineat < vga.s3.hgc.originy) || 
00807         (lineat > (vga.s3.hgc.originy + (63U-vga.s3.hgc.posy))) ) {
00808         return &vga.mem.linear[vidstart];
00809     } else {
00810         memcpy(TempLine, &vga.mem.linear[ vidstart ], vga.draw.width*2);
00811         Bitu sourceStartBit = ((lineat - vga.s3.hgc.originy) + vga.s3.hgc.posy)*64 + vga.s3.hgc.posx; 
00812         Bitu cursorMemStart = ((sourceStartBit >> 2) & ~1ul) + (((Bit32u)vga.s3.hgc.startaddr) << 10ul);
00813         Bitu cursorStartBit = sourceStartBit & 0x7u;
00814         if (cursorMemStart & 0x2) cursorMemStart--;
00815         Bitu cursorMemEnd = cursorMemStart + (Bitu)((64 - vga.s3.hgc.posx) >> 2);
00816         Bit16u* xat = &((Bit16u*)TempLine)[vga.s3.hgc.originx];
00817         for (Bitu m = cursorMemStart; m < cursorMemEnd; (m&1)?(m+=3):m++) {
00818             // for each byte of cursor data
00819             Bit8u bitsA = vga.mem.linear[m];
00820             Bit8u bitsB = vga.mem.linear[m+2];
00821             for (Bit8u bit=(0x80 >> cursorStartBit); bit != 0; bit >>= 1) {
00822                 // for each bit
00823                 cursorStartBit=0;
00824                 if (bitsA&bit) {
00825                     // byte order doesn't matter here as all bits get flipped
00826                     if (bitsB&bit) *xat ^= ~0U;
00827                     //else Transparent
00828                 } else if (bitsB&bit) {
00829                     // Source as well as destination are Bit8u arrays, 
00830                     // so this should work out endian-wise?
00831                     *xat = *(Bit16u*)vga.s3.hgc.forestack;
00832                 } else {
00833                     *xat = *(Bit16u*)vga.s3.hgc.backstack;
00834                 }
00835                 xat++;
00836             }
00837         }
00838         return TempLine;
00839     }
00840 }
00841 
00842 static Bit8u * VGA_Draw_LIN32_Line_HWMouse(Bitu vidstart, Bitu /*line*/) {
00843 #if SDL_BYTEORDER == SDL_LIL_ENDIAN && defined(MACOSX) /* Mac OS X Intel builds use a weird RGBA order (alpha in the low 8 bits) */
00844     Bitu offset = vidstart & vga.draw.linear_mask;
00845     Bitu i;
00846 
00847     for (i=0;i < vga.draw.width;i++)
00848         ((uint32_t*)TempLine)[i] = guest_bgr_to_macosx_rgba((((uint32_t*)(vga.draw.linear_base+offset))[i]));
00849 
00850     return TempLine;
00851 #else
00852     if (!svga.hardware_cursor_active || !svga.hardware_cursor_active())
00853         return &vga.mem.linear[vidstart];
00854 
00855     Bitu lineat = ((vidstart-(vga.config.real_start<<2)) >> 2) / vga.draw.width;
00856     if ((vga.s3.hgc.posx >= vga.draw.width) ||
00857         (lineat < vga.s3.hgc.originy) || 
00858         (lineat > (vga.s3.hgc.originy + (63U-vga.s3.hgc.posy))) ) {
00859         return &vga.mem.linear[ vidstart ];
00860     } else {
00861         memcpy(TempLine, &vga.mem.linear[ vidstart ], vga.draw.width*4);
00862         Bitu sourceStartBit = ((lineat - vga.s3.hgc.originy) + vga.s3.hgc.posy)*64 + vga.s3.hgc.posx; 
00863         Bitu cursorMemStart = ((sourceStartBit >> 2) & ~1ul) + (((Bit32u)vga.s3.hgc.startaddr) << 10ul);
00864         Bitu cursorStartBit = sourceStartBit & 0x7u;
00865         if (cursorMemStart & 0x2) cursorMemStart--;
00866         Bitu cursorMemEnd = cursorMemStart + (Bitu)((64 - vga.s3.hgc.posx) >> 2);
00867         Bit32u* xat = &((Bit32u*)TempLine)[vga.s3.hgc.originx];
00868         for (Bitu m = cursorMemStart; m < cursorMemEnd; (m&1)?(m+=3):m++) {
00869             // for each byte of cursor data
00870             Bit8u bitsA = vga.mem.linear[m];
00871             Bit8u bitsB = vga.mem.linear[m+2];
00872             for (Bit8u bit=(0x80 >> cursorStartBit); bit != 0; bit >>= 1) { // for each bit
00873                 cursorStartBit=0;
00874                 if (bitsA&bit) {
00875                     if (bitsB&bit) *xat ^= ~0U;
00876                     //else Transparent
00877                 } else if (bitsB&bit) {
00878                     *xat = *(Bit32u*)vga.s3.hgc.forestack;
00879                 } else {
00880                     *xat = *(Bit32u*)vga.s3.hgc.backstack;
00881                 }
00882                 xat++;
00883             }
00884         }
00885         return TempLine;
00886     }
00887 #endif
00888 }
00889 
00890 static const Bit32u* VGA_Planar_Memwrap(Bitu vidstart) {
00891     vidstart &= vga.draw.planar_mask;
00892     return (Bit32u*)(&vga.mem.linear[vidstart << 2]);
00893 }
00894 
00895 static const Bit8u* VGA_Text_Memwrap(Bitu vidstart) {
00896     vidstart &= vga.draw.linear_mask;
00897     Bitu line_end = 2 * vga.draw.blocks;
00898     if (GCC_UNLIKELY((vidstart + line_end) > vga.draw.linear_mask)) {
00899         // wrapping in this line
00900         Bitu break_pos = (vga.draw.linear_mask - vidstart) + 1;
00901         // need a temporary storage - TempLine/2 is ok for a bit more than 132 columns
00902         memcpy(&TempLine[sizeof(TempLine)/2], &vga.tandy.draw_base[vidstart], break_pos);
00903         memcpy(&TempLine[sizeof(TempLine)/2 + break_pos],&vga.tandy.draw_base[0], line_end - break_pos);
00904         return &TempLine[sizeof(TempLine)/2];
00905     } else return &vga.tandy.draw_base[vidstart];
00906 }
00907 
00908 static Bit32u FontMask[2]={0xffffffff,0x0};
00909 static Bit32u CGA_PRND = 1;
00910 
00911 static Bit8u * VGA_CGASNOW_TEXT_Draw_Line(Bitu vidstart, Bitu line) {
00912     Bits font_addr;
00913     Bit32u * draw=(Bit32u *)TempLine;
00914     const Bit8u* vidmem = VGA_Text_Memwrap(vidstart);
00915 
00916     /* HACK: our code does not have render control during VBLANK, zero our
00917      *       noise bits on the first scanline */
00918     if (line == 0)
00919         memset(vga.draw.cga_snow,0,sizeof(vga.draw.cga_snow));
00920 
00921     for (Bitu cx=0;cx<vga.draw.blocks;cx++) {
00922         Bitu chr,col;
00923         chr=vidmem[cx*2];
00924         col=vidmem[cx*2+1];
00925         if ((cx&1) == 0 && cx <= 78) {
00926             /* Trixter's "CGA test" program and reference video seems to suggest
00927              * to me that the CGA "snow" might contain the value written by the CPU. */
00928             if (vga.draw.cga_snow[cx] != 0)
00929                 chr = vga.draw.cga_snow[cx];
00930             if (vga.draw.cga_snow[cx+1] != 0)
00931                 col = vga.draw.cga_snow[cx+1];
00932 
00933             CGA_PRND = ((CGA_PRND+1)*9421)&0xFFFF;
00934         }
00935 
00936         Bitu font=vga.draw.font_tables[(col >> 3)&1][chr*32+line];
00937         Bit32u mask1=TXT_Font_Table[font>>4] & FontMask[col >> 7];
00938         Bit32u mask2=TXT_Font_Table[font&0xf] & FontMask[col >> 7];
00939         Bit32u fg=TXT_FG_Table[col&0xf];
00940         Bit32u bg=TXT_BG_Table[col>>4];
00941         *draw++=(fg&mask1) | (bg&~mask1);
00942         *draw++=(fg&mask2) | (bg&~mask2);
00943     }
00944     memset(vga.draw.cga_snow,0,sizeof(vga.draw.cga_snow));
00945     if (!vga.draw.cursor.enabled || !(vga.draw.cursor.count&0x8)) goto skip_cursor;
00946     font_addr = ((Bits)vga.draw.cursor.address - (Bits)vidstart) >> 1ll;
00947     if (font_addr>=0 && font_addr<(Bits)vga.draw.blocks) {
00948         if (line<vga.draw.cursor.sline) goto skip_cursor;
00949         if (line>vga.draw.cursor.eline) goto skip_cursor;
00950         draw=(Bit32u *)&TempLine[(unsigned long)font_addr*8ul];
00951         Bit32u att=TXT_FG_Table[vga.tandy.draw_base[vga.draw.cursor.address+1ul]&0xfu];
00952         *draw++=att;*draw++=att;
00953     }
00954 skip_cursor:
00955     return TempLine;
00956 }
00957 
00958 static Bit8u * MCGA_TEXT_Draw_Line(Bitu vidstart, Bitu line) {
00959     // keep it aligned:
00960     Bit32u* draw = (Bit32u*)TempLine;
00961     const Bit8u* vidmem = VGA_Text_Memwrap(vidstart);
00962     Bitu blocks = vga.draw.blocks;
00963     if (vga.draw.panning) blocks++; // if the text is panned part of an 
00964                                     // additional character becomes visible
00965     while (blocks--) { // for each character in the line
00966         Bitu chr = *vidmem++;
00967         Bitu attr = *vidmem++;
00968         // the font pattern
00969         Bitu font = vga.draw.font_tables[(attr >> 3u)&1u][(chr<<5u)+line];
00970         
00971         Bitu background = attr >> 4u;
00972         // if blinking is enabled bit7 is not mapped to attributes
00973         if (vga.draw.blinking) background &= ~0x8u;
00974         // choose foreground color if blinking not set for this cell or blink on
00975         Bitu foreground = (vga.draw.blink || (!(attr&0x80)))?
00976             (attr&0xf):background;
00977         // underline: all foreground [freevga: 0x77, previous 0x7]
00978         if (GCC_UNLIKELY(((attr&0x77) == 0x01) &&
00979             (vga.crtc.underline_location&0x1f)==line))
00980                 background = foreground;
00981         if (vga.draw.char9dot) {
00982             font <<=1; // 9 pixels
00983             // extend to the 9th pixel if needed
00984             if ((font&0x2) && (vga.attr.mode_control&0x04) &&
00985                 (chr>=0xc0) && (chr<=0xdf)) font |= 1;
00986             for (Bitu n = 0; n < 9; n++) {
00987                 *draw++ = vga.dac.xlat32[(font&0x100)? foreground:background];
00988                 font <<= 1;
00989             }
00990         } else {
00991             for (Bitu n = 0; n < 8; n++) {
00992                 *draw++ = vga.dac.xlat32[(font&0x80)? foreground:background];
00993                 font <<= 1;
00994             }
00995         }
00996     }
00997 
00998     // cursor appearance is also affected by the MCGA double-scanning
00999     if (mcga_double_scan)
01000         line >>= 1ul;
01001 
01002     // draw the text mode cursor if needed
01003     if ((vga.draw.cursor.count&0x8) && (line >= vga.draw.cursor.sline) &&
01004         (line <= vga.draw.cursor.eline) && vga.draw.cursor.enabled) {
01005         // the adress of the attribute that makes up the cell the cursor is in
01006         Bits attr_addr = ((Bits)vga.draw.cursor.address - (Bits)vidstart) >> (Bits)1; /* <- FIXME: This right? */
01007         if (attr_addr >= 0 && attr_addr < (Bits)vga.draw.blocks) {
01008             Bitu index = (Bitu)attr_addr * (vga.draw.char9dot?9u:8u) * 4u;
01009             draw = (Bit32u*)(&TempLine[index]);
01010             
01011             Bitu foreground = vga.tandy.draw_base[(vga.draw.cursor.address<<1)+1] & 0xf;
01012             for (Bitu i = 0; i < 8; i++) {
01013                 *draw++ = vga.dac.xlat32[foreground];
01014             }
01015         }
01016     }
01017     return TempLine;
01018 }
01019 
01020 static Bit8u * VGA_TEXT_Draw_Line(Bitu vidstart, Bitu line) {
01021     Bits font_addr;
01022     Bit32u * draw=(Bit32u *)TempLine;
01023     const Bit8u* vidmem = VGA_Text_Memwrap(vidstart);
01024     //assert(FontMask[0] == 0xffffffff);
01025     if (FontMask[1] == 0) {
01026         for (Bitu cx=0;cx<vga.draw.blocks;cx++) {
01027             Bitu chr=vidmem[cx*2];
01028             Bitu col=vidmem[cx*2+1];
01029             Bitu font=vga.draw.font_tables[(col >> 3)&1][chr*32+line];
01030             Bit32u font_mask = (Bit32u)((((Bit32s)col) << 24) >> 31);
01031             font_mask = ~font_mask;
01032             Bit32u mask1=TXT_Font_Table[font>>4ul] & font_mask;
01033             Bit32u mask2=TXT_Font_Table[font&0xful] & font_mask;
01034             Bit32u fg=TXT_FG_Table[col&0xful];
01035             Bit32u bg=TXT_BG_Table[col>>4ul];
01036             *draw++=(fg&mask1) | (bg&~mask1);
01037             *draw++=(fg&mask2) | (bg&~mask2);
01038         }
01039     } else {
01040         //assert(FontMask[1] == 0xffffffff);
01041         for (Bitu cx=0;cx<vga.draw.blocks;cx++) {
01042             Bitu chr=vidmem[cx*2];
01043             Bitu col=vidmem[cx*2+1];
01044             Bitu font=vga.draw.font_tables[(col >> 3)&1][chr*32+line];
01045             Bit32u mask1=TXT_Font_Table[font>>4];
01046             Bit32u mask2=TXT_Font_Table[font&0xf];
01047             Bit32u fg=TXT_FG_Table[col&0xf];
01048             Bit32u bg=TXT_BG_Table[col>>4];
01049             *draw++=(fg&mask1) | (bg&~mask1);
01050             *draw++=(fg&mask2) | (bg&~mask2);
01051         }
01052     }
01053     if (!vga.draw.cursor.enabled || !(vga.draw.cursor.count&0x8)) goto skip_cursor;
01054     font_addr = ((Bits)vga.draw.cursor.address-(Bits)vidstart) >> 1ll;
01055     if (font_addr>=0 && font_addr<(Bits)vga.draw.blocks) {
01056         if (line<vga.draw.cursor.sline) goto skip_cursor;
01057         if (line>vga.draw.cursor.eline) goto skip_cursor;
01058         draw=(Bit32u *)&TempLine[(unsigned long)font_addr*8ul];
01059         Bit32u att=TXT_FG_Table[vga.tandy.draw_base[vga.draw.cursor.address+1]&0xf];
01060         *draw++=att;*draw++=att;
01061     }
01062 skip_cursor:
01063     return TempLine;
01064 }
01065 
01066 static Bit8u * VGA_TEXT_Herc_Draw_Line(Bitu vidstart, Bitu line) {
01067     Bits font_addr;
01068     Bit32u * draw=(Bit32u *)TempLine;
01069     const Bit8u* vidmem = VGA_Text_Memwrap(vidstart);
01070 
01071     for (Bitu cx=0;cx<vga.draw.blocks;cx++) {
01072         Bitu chr=vidmem[cx*2];
01073         Bitu attrib=vidmem[cx*2+1];
01074         if (!(attrib&0x77)) {
01075             // 00h, 80h, 08h, 88h produce black space
01076             *draw++=0;
01077             *draw++=0;
01078         } else {
01079             Bit32u bg, fg;
01080             bool underline=false;
01081             if ((attrib&0x77)==0x70) {
01082                 bg = TXT_BG_Table[0x7];
01083                 if (attrib&0x8) fg = TXT_FG_Table[0xf];
01084                 else fg = TXT_FG_Table[0x0];
01085             } else {
01086                 if (((Bitu)(vga.crtc.underline_location&0x1f)==line) && ((attrib&0x77)==0x1)) underline=true;
01087                 bg = TXT_BG_Table[0x0];
01088                 if (attrib&0x8) fg = TXT_FG_Table[0xf];
01089                 else fg = TXT_FG_Table[0x7];
01090             }
01091             Bit32u mask1, mask2;
01092             if (GCC_UNLIKELY(underline)) mask1 = mask2 = FontMask[attrib >> 7];
01093             else {
01094                 Bitu font=vga.draw.font_tables[0][chr*32+line];
01095                 mask1=TXT_Font_Table[font>>4] & FontMask[attrib >> 7]; // blinking
01096                 mask2=TXT_Font_Table[font&0xf] & FontMask[attrib >> 7];
01097             }
01098             *draw++=(fg&mask1) | (bg&~mask1);
01099             *draw++=(fg&mask2) | (bg&~mask2);
01100         }
01101     }
01102     if (!vga.draw.cursor.enabled || !(vga.draw.cursor.count&0x8)) goto skip_cursor;
01103     font_addr = ((Bits)vga.draw.cursor.address - (Bits)vidstart) >> 1ll;
01104     if (font_addr>=0 && font_addr<(Bits)vga.draw.blocks) {
01105         if (line<vga.draw.cursor.sline) goto skip_cursor;
01106         if (line>vga.draw.cursor.eline) goto skip_cursor;
01107         draw=(Bit32u *)&TempLine[(unsigned long)font_addr*8ul];
01108         Bit8u attr = vga.tandy.draw_base[vga.draw.cursor.address+1];
01109         Bit32u cg;
01110         if (attr&0x8) {
01111             cg = TXT_FG_Table[0xf];
01112         } else if ((attr&0x77)==0x70) {
01113             cg = TXT_FG_Table[0x0];
01114         } else {
01115             cg = TXT_FG_Table[0x7];
01116         }
01117         *draw++=cg;*draw++=cg;
01118     }
01119 skip_cursor:
01120     return TempLine;
01121 }
01122 
01123 // combined 8/9-dot wide text mode 16bpp line drawing function
01124 static Bit8u* EGA_TEXT_Xlat8_Draw_Line(Bitu vidstart, Bitu line) {
01125     // keep it aligned:
01126     Bit8u* draw = ((Bit8u*)TempLine) + 16 - vga.draw.panning;
01127     const Bit32u* vidmem = VGA_Planar_Memwrap(vidstart); // pointer to chars+attribs
01128     Bitu blocks = vga.draw.blocks;
01129     if (vga.draw.panning) blocks++; // if the text is panned part of an 
01130                                     // additional character becomes visible
01131     while (blocks--) { // for each character in the line
01132         VGA_Latch pixels;
01133 
01134         pixels.d = *vidmem;
01135         vidmem += (uintptr_t)1U << (uintptr_t)vga.config.addr_shift;
01136 
01137         Bitu chr = pixels.b[0];
01138         Bitu attr = pixels.b[1];
01139         // the font pattern
01140         Bitu font = vga.draw.font_tables[(attr >> 3)&1][(chr<<5)+line];
01141         
01142         Bitu background = attr >> 4u;
01143         // if blinking is enabled bit7 is not mapped to attributes
01144         if (vga.draw.blinking) background &= ~0x8u;
01145         // choose foreground color if blinking not set for this cell or blink on
01146         Bitu foreground = (vga.draw.blink || (!(attr&0x80)))?
01147             (attr&0xf):background;
01148         // underline: all foreground [freevga: 0x77, previous 0x7]
01149         if (GCC_UNLIKELY(((attr&0x77) == 0x01) &&
01150             (vga.crtc.underline_location&0x1f)==line))
01151                 background = foreground;
01152         if (vga.draw.char9dot) {
01153             font <<=1; // 9 pixels
01154             // extend to the 9th pixel if needed
01155             if ((font&0x2) && (vga.attr.mode_control&0x04) &&
01156                 (chr>=0xc0) && (chr<=0xdf)) font |= 1;
01157             for (Bitu n = 0; n < 9; n++) {
01158                 *draw++ = vga.attr.palette[(font&0x100)? foreground:background];
01159                 font <<= 1;
01160             }
01161         } else {
01162             for (Bitu n = 0; n < 8; n++) {
01163                 *draw++ = vga.attr.palette[(font&0x80)? foreground:background];
01164                 font <<= 1;
01165             }
01166         }
01167     }
01168     // draw the text mode cursor if needed
01169     if ((vga.draw.cursor.count&0x8) && (line >= vga.draw.cursor.sline) &&
01170         (line <= vga.draw.cursor.eline) && vga.draw.cursor.enabled) {
01171         // the adress of the attribute that makes up the cell the cursor is in
01172         Bits attr_addr = ((Bits)vga.draw.cursor.address - (Bits)vidstart) >> (Bits)vga.config.addr_shift; /* <- FIXME: This right? */
01173         if (attr_addr >= 0 && attr_addr < (Bits)vga.draw.blocks) {
01174             Bitu index = (Bitu)attr_addr * (vga.draw.char9dot ? 9u : 8u);
01175             draw = (Bit8u*)(&TempLine[index]) + 16 - vga.draw.panning;
01176             
01177             Bitu foreground = vga.tandy.draw_base[(vga.draw.cursor.address<<2ul)+1] & 0xf;
01178             for (Bitu i = 0; i < 8; i++) {
01179                 *draw++ = vga.attr.palette[foreground];
01180             }
01181         }
01182     }
01183     return TempLine+(16);
01184 }
01185 
01186 // combined 8/9-dot wide text mode 16bpp line drawing function
01187 static Bit8u* VGA_TEXT_Xlat32_Draw_Line(Bitu vidstart, Bitu line) {
01188     // keep it aligned:
01189     Bit32u* draw = ((Bit32u*)TempLine) + 16 - vga.draw.panning;
01190     const Bit32u* vidmem = VGA_Planar_Memwrap(vidstart); // pointer to chars+attribs
01191     Bitu blocks = vga.draw.blocks;
01192     if (vga.draw.panning) blocks++; // if the text is panned part of an 
01193                                     // additional character becomes visible
01194     while (blocks--) { // for each character in the line
01195         VGA_Latch pixels;
01196 
01197         pixels.d = *vidmem;
01198         vidmem += (uintptr_t)1U << (uintptr_t)vga.config.addr_shift;
01199 
01200         Bitu chr = pixels.b[0];
01201         Bitu attr = pixels.b[1];
01202         // the font pattern
01203         Bitu font = vga.draw.font_tables[(attr >> 3u)&1u][(chr<<5u)+line];
01204         
01205         Bitu background = attr >> 4u;
01206         // if blinking is enabled bit7 is not mapped to attributes
01207         if (vga.draw.blinking) background &= ~0x8u;
01208         // choose foreground color if blinking not set for this cell or blink on
01209         Bitu foreground = (vga.draw.blink || (!(attr&0x80)))?
01210             (attr&0xf):background;
01211         // underline: all foreground [freevga: 0x77, previous 0x7]
01212         if (GCC_UNLIKELY(((attr&0x77) == 0x01) &&
01213             (vga.crtc.underline_location&0x1f)==line))
01214                 background = foreground;
01215         if (vga.draw.char9dot) {
01216             font <<=1; // 9 pixels
01217             // extend to the 9th pixel if needed
01218             if ((font&0x2) && (vga.attr.mode_control&0x04) &&
01219                 (chr>=0xc0) && (chr<=0xdf)) font |= 1;
01220             for (Bitu n = 0; n < 9; n++) {
01221                 *draw++ = vga.dac.xlat32[(font&0x100)? foreground:background];
01222                 font <<= 1;
01223             }
01224         } else {
01225             for (Bitu n = 0; n < 8; n++) {
01226                 *draw++ = vga.dac.xlat32[(font&0x80)? foreground:background];
01227                 font <<= 1;
01228             }
01229         }
01230     }
01231     // draw the text mode cursor if needed
01232     if ((vga.draw.cursor.count&0x8) && (line >= vga.draw.cursor.sline) &&
01233         (line <= vga.draw.cursor.eline) && vga.draw.cursor.enabled) {
01234         // the adress of the attribute that makes up the cell the cursor is in
01235         Bits attr_addr = ((Bits)vga.draw.cursor.address - (Bits)vidstart) >> (Bits)vga.config.addr_shift; /* <- FIXME: This right? */
01236         if (attr_addr >= 0 && attr_addr < (Bits)vga.draw.blocks) {
01237             Bitu index = (Bitu)attr_addr * (vga.draw.char9dot?9u:8u) * 4u;
01238             draw = (Bit32u*)(&TempLine[index]) + 16 - vga.draw.panning;
01239             
01240             Bitu foreground = vga.tandy.draw_base[(vga.draw.cursor.address<<2)+1] & 0xf;
01241             for (Bitu i = 0; i < 8; i++) {
01242                 *draw++ = vga.dac.xlat32[foreground];
01243             }
01244         }
01245     }
01246     return TempLine+(16*4);
01247 }
01248 
01249 extern bool pc98_attr4_graphic;
01250 extern uint8_t GDC_display_plane;
01251 extern uint8_t GDC_display_plane_pending;
01252 extern bool pc98_graphics_hide_odd_raster_200line;
01253 extern bool pc98_allow_scanline_effect;
01254 extern bool gdc_analog;
01255 
01256 unsigned char       pc98_text_first_row_scanline_start = 0x00;  /* port 70h */
01257 unsigned char       pc98_text_first_row_scanline_end = 0x0F;    /* port 72h */
01258 unsigned char       pc98_text_row_scanline_blank_at = 0x10;     /* port 74h */
01259 unsigned char       pc98_text_row_scroll_lines = 0x00;          /* port 76h */
01260 unsigned char       pc98_text_row_scroll_count_start = 0x00;    /* port 78h */
01261 unsigned char       pc98_text_row_scroll_num_lines = 0x00;      /* port 7Ah */
01262 
01263 // Text layer rendering state.
01264 // Track row, row count, scanline row within the cell,
01265 // for accurate emulation
01266 struct Text_Draw_State {
01267     unsigned int        scroll_vmem;
01268     unsigned char       scroll_scanline_cg;
01269     unsigned char       row_scanline_cg;            /* scanline within row, CG bitmap */
01270     unsigned char       row_char;                   /* character row. scroll region 0 <= num_lines */
01271     unsigned char       row_scroll_countdown;
01272 
01273     void begin_frame(void) {
01274         row_scroll_countdown = 0xFF;
01275         row_scanline_cg = pc98_text_first_row_scanline_start;
01276         row_char = pc98_text_row_scroll_count_start & 0x1Fu;
01277         check_scroll_region();
01278 
01279         if (row_scroll_countdown != 0xFF)
01280             update_scroll_line();
01281     }
01282     void next_line(void) {
01283         if (row_scanline_cg == pc98_text_first_row_scanline_end) {
01284             row_scanline_cg = pc98_text_first_row_scanline_start;
01285             next_character_row();
01286         }
01287         else {
01288             row_scanline_cg = (row_scanline_cg + 1u) & 0x1Fu;
01289         }
01290 
01291         if (row_scroll_countdown != 0xFF)
01292             update_scroll_line();
01293     }
01294     void update_scroll_line(void) {
01295         scroll_vmem = 0;
01296         scroll_scanline_cg = row_scanline_cg;
01297         for (unsigned int i=0;i < pc98_text_row_scroll_lines;i++) {
01298             if (scroll_scanline_cg == pc98_text_first_row_scanline_end) {
01299                 scroll_scanline_cg = pc98_text_first_row_scanline_start;
01300                 scroll_vmem += pc98_gdc[GDC_MASTER].display_pitch;
01301             }
01302             else {
01303                 scroll_scanline_cg = (scroll_scanline_cg + 1u) & 0x1Fu;
01304             }
01305         }
01306     }
01307     void next_character_row(void) {
01308         row_char = (row_char + 1u) & 0x1Fu;
01309         check_scroll_region();
01310     }
01311     void check_scroll_region(void) {
01312         if (row_char == 0) {
01313             /* begin scroll region */
01314             /* NTS: Confirmed on real hardware: The scroll count ADDs to the vertical offset already applied to the text.
01315              *      For example in 20-line text mode (20 pixels high) setting the scroll region offset to 2 pixels cancels
01316              *      out the 2 pixel centering of the text. */
01317             row_scroll_countdown = pc98_text_row_scroll_num_lines & 0x1Fu;
01318         }
01319         else if (row_scroll_countdown == 0) {
01320             /* end scroll region */
01321             row_scroll_countdown = 0xFF;
01322         }
01323         else if (row_scroll_countdown != 0xFF) {
01324             row_scroll_countdown--;
01325         }
01326     }
01327     bool in_scroll_region(void) const {
01328         return row_scroll_countdown != 0xFFu;
01329     }
01330 };
01331 
01332 Text_Draw_State     pc98_text_draw;
01333 
01334 static Bit8u* VGA_PC98_Xlat32_Draw_Line(Bitu vidstart, Bitu line) {
01335     // keep it aligned:
01336     Bit32u* draw = ((Bit32u*)TempLine);
01337     Bitu blocks = vga.draw.blocks;
01338     Bit32u vidmem = vidstart;
01339     Bit16u chr = 0,attr = 0;
01340     Bit16u lineoverlay = 0; // vertical + underline overlay over the character cell, but apparently with a 4-pixel delay
01341     bool doublewide = false;
01342     unsigned char font,foreground;
01343     unsigned char fline;
01344     bool ok_raster = true;
01345 
01346     // 200-line modes: The BIOS or DOS game can elect to hide odd raster lines
01347     if (pc98_gdc[GDC_SLAVE].doublescan && pc98_graphics_hide_odd_raster_200line && pc98_allow_scanline_effect)
01348         ok_raster = (vga.draw.lines_done & 1) == 0;
01349 
01350     // Graphic RAM layer (or blank)
01351     // Think of it as a 3-plane GRB color graphics mode, each plane is 1 bit per pixel.
01352     // G-RAM is addressed 16 bits per RAM cycle.
01353     if (pc98_gdc[GDC_SLAVE].display_enable && ok_raster) {
01354         unsigned int disp_base;
01355         Bit8u g8,r8,b8,e8;
01356 
01357         draw = ((Bit32u*)TempLine);
01358         blocks = vga.draw.blocks;
01359         vidmem = (unsigned int)pc98_gdc[GDC_SLAVE].scan_address << 1u;
01360         disp_base = GDC_display_plane ? 0x20000U : 0x00000U;
01361 
01362         while (blocks--) {
01363             // NTS: Testing on real hardware shows that, when you switch the GDC back to 8-color mode,
01364             //      the 4th bitplane is no longer rendered.
01365             if (gdc_analog)
01366                 e8 = vga.mem.linear[(vidmem & 0x7FFFU) + 0x20000U + disp_base]; /* E0000-E7FFF */
01367             else
01368                 e8 = 0x00;
01369 
01370             g8 = vga.mem.linear[(vidmem & 0x7FFFU) + 0x18000U + disp_base]; /* B8000-BFFFF */
01371             r8 = vga.mem.linear[(vidmem & 0x7FFFU) + 0x10000U + disp_base]; /* B0000-B7FFF */
01372             b8 = vga.mem.linear[(vidmem & 0x7FFFU) + 0x08000U + disp_base]; /* A8000-AFFFF */
01373 
01374             for (unsigned char i=0;i < 8;i++) {
01375                 foreground  = (e8 & 0x80) ? 8 : 0;
01376                 foreground += (g8 & 0x80) ? 4 : 0;
01377                 foreground += (r8 & 0x80) ? 2 : 0;
01378                 foreground += (b8 & 0x80) ? 1 : 0;
01379 
01380                 e8 <<= 1;
01381                 g8 <<= 1;
01382                 r8 <<= 1;
01383                 b8 <<= 1;
01384 
01385                 *draw++ = vga.dac.xlat32[foreground];
01386             }
01387 
01388             vidmem++;
01389         }
01390     }
01391     else {
01392         memset(TempLine,0,4 * 8 * blocks);
01393     }
01394 
01395     // Text RAM layer
01396     if (pc98_gdc[GDC_MASTER].display_enable) {
01397         Bitu gdcvidmem = pc98_gdc[GDC_MASTER].scan_address;
01398 
01399         draw = ((Bit32u*)TempLine);
01400         blocks = vga.draw.blocks;
01401 
01402         vidmem = pc98_gdc[GDC_MASTER].scan_address;
01403         if (pc98_text_draw.in_scroll_region()) {
01404             vidmem += pc98_text_draw.scroll_vmem;
01405             fline = pc98_text_draw.scroll_scanline_cg;
01406         }
01407         else {
01408             fline = pc98_text_draw.row_scanline_cg;
01409         }
01410 
01411         /* NTS: This code will render the cursor in the scroll region with a weird artifact
01412          *      when the character under it is double-wide, and the cursor covers the top half.
01413          *
01414          *      For example:
01415          *
01416          *      AAA
01417          *      AAA
01418          *      AAAA     <- scroll region ends here
01419          *      AAAA
01420          *
01421          *      Placing the cursor on the third row of A's, on the right, will produce an
01422          *      oddly shaped cursor.
01423          *
01424          *      Before filing a bug about this, consider from my testing that real PC-9821
01425          *      hardware will also render a strangely shaped cursor there as well. --J.C. */
01426 
01427         while (blocks--) { // for each character in the line
01428             bool was_doublewide = doublewide;
01429 
01430             /* Amusing question: How does it handle the "simple graphics" in 20-line mode? */
01431 
01432             if (!doublewide) {
01433 interrupted_char_begin:
01434                 chr = ((Bit16u*)vga.mem.linear)[(vidmem & 0xFFFU) + 0x0000U];
01435                 attr = ((Bit16u*)vga.mem.linear)[(vidmem & 0xFFFU) + 0x1000U];
01436 
01437                 if (pc98_attr4_graphic && (attr & 0x10)) {
01438                     /* the "vertical line" attribute (bit 4) can be redefined as a signal
01439                      * to interpret the character as a low res bitmap instead compatible with
01440                      * "simple graphics" of the PC-8001 (ref. Carat) */
01441                     /* Contrary to what you normally expect of a "bitmap", the pixels are
01442                      * in column order.
01443                      *     0 1
01444                      *     col
01445                      *     0 4  r 0
01446                      *     1 5  o 1
01447                      *     2 6  w 2
01448                      *     3 7    3
01449                      */
01450                     /* The only way a direct bitmap can be encoded in 8 bits is if one character
01451                      * cell were 2 pixels wide 4 pixels high, and each pixel was repeated 4 times.
01452                      * In case you're wondering, the high byte doesn't appear to be used in this mode.
01453                      *
01454                      * Setting the high byte seems to blank the cell entirely */
01455                     if ((chr & 0xFF00) == 0x00) {
01456                         unsigned char bits2 = (chr >> (pc98_gdc[GDC_MASTER].row_line >> 2)) & 0x11;
01457 
01458                         font =  ((bits2 & 0x01) ? 0xF0 : 0x00) +
01459                                 ((bits2 & 0x10) ? 0x0F : 0x00);
01460                     }
01461                     else {
01462                         font = 0;
01463                     }
01464                 }
01465                 else {
01466                     // NTS: The display handles single-wide vs double-wide by whether or not the 8 bits are nonzero.
01467                     //      If zero, the char is one cell wide.
01468                     //      If nonzero, the char is two cells wide (doublewide) and the current character is rendered
01469                     //      into both cells (the character code in the next cell is ignored). The attribute (as far
01470                     //      as I know) repeats for both.
01471                     //
01472                     //      NTS: It seems different character ROM is used between single and double wide chars.
01473                     //           Contrary to what this suggests, (chr & 0xFF00) == 0x8000 is doublewide but not the
01474                     //           same as single-wide (chr & 0xFF00) == 0x0000.
01475                     //
01476                     //      Specific ranges that would be fullwidth where bits[6:0] are 0x08 to 0x0B inclusive are
01477                     //      apparently not fullwidth (the halfwidth char repeats) if both cells filled in.
01478                     if ((chr & 0xFF00) != 0 && (chr & 0x7CU) != 0x08) {
01479                         // left half of doublewide char. it appears only bits[14:8] and bits[6:0] have any real effect on which char is displayed.
01480                         doublewide = true;
01481                     }
01482 
01483                     if (fline < pc98_text_row_scanline_blank_at)
01484                         font = pc98_font_char_read(chr,fline,0);
01485                     else
01486                         font = 0;
01487                 }
01488             }
01489             else {
01490                 // right half of doublewide char.
01491                 //
01492                 // NTS: Strange idiosyncratic behavior observed on real hardware shows that MOST fullwidth codes
01493                 //      fill two cells and ignore the other cell, EXCEPT, that specific ranges require you to
01494                 //      enter the same fullwidth code in both cells.
01495                 doublewide = false;
01496 
01497                 // It seems that for any fullwidth char, you need the same code in both cells for bit[6:0] values
01498                 // from 0x08 to 0x0F inclusive. 0x08 to 0x0B inclusive are not fullwidth, apparently.
01499                 // Same applies 0x56 to 0x5F.
01500                 //
01501                 // Real hardware seems to show that this code will show the other half of the character IF the
01502                 // character code matches. If it does not match, then it will show the first half of the new code.
01503                 //
01504                 // This fix is needed for Touhou Project to show some level titles correctly. The reason this fix
01505                 // affects it, is that the text RAM covering the playfield is not space or any traditionally empty
01506                 // cell but a custom character code that is generally empty, but the character cell bitmap is animated
01507                 // (changed per frame) when doing fade/wipe transitions between levels. Some of the level titles
01508                 // are displayed starting at an odd column cell number, which means that the Kanji intended for
01509                 // display "interrupts" the blank custom character cell code. TH02 ~idnight bug fix.
01510                 if ((chr&0x78U) == 0x08 || (chr&0x7FU) >= 0x56) {
01511                     uint16_t n_chr;
01512 
01513                     n_chr = ((Bit16u*)vga.mem.linear)[(vidmem & 0xFFFU) + 0x0000U];
01514                     attr = ((Bit16u*)vga.mem.linear)[(vidmem & 0xFFFU) + 0x1000U];
01515 
01516                     if ((chr&0x7F7F) != (n_chr&0x7F7F))
01517                         goto interrupted_char_begin;
01518                 }
01519 
01520                 if (fline < pc98_text_row_scanline_blank_at)
01521                     font = pc98_font_char_read(chr,fline,1);
01522                 else
01523                     font = 0;
01524             }
01525 
01526             lineoverlay <<= 8;
01527 
01528             /* the character is not rendered if "~secret" (bit 0) is not set */
01529             if (!(attr & 1)) font = 0;
01530 
01531             /* "blink" seems to count at the same speed as the cursor blink rate,
01532              * through a 4-cycle pattern in which the character is invisible only
01533              * at the first count. */
01534             if ((attr & 0x02/*blink*/) && pc98_gdc[GDC_MASTER].cursor_blink_state == 0) font = 0;
01535 
01536             /* reverse attribute. seems to take effect BEFORE vertical & underline attributes */
01537             if (attr & 0x04/*reverse*/) font ^= 0xFF;
01538 
01539             /* based on real hardware, the cursor seems to act like a reverse attribute */
01540             /* if the character is double-wide, and the cursor is on the left half, the cursor affects the right half too. */
01541             if (((gdcvidmem == vga.draw.cursor.address) || (was_doublewide && gdcvidmem == (vga.draw.cursor.address+1))) &&
01542                 pc98_gdc[GDC_MASTER].cursor_enable &&
01543                 ((!pc98_gdc[GDC_MASTER].cursor_blink) || (pc98_gdc[GDC_MASTER].cursor_blink_state&1)) &&
01544                 (line >= vga.draw.cursor.sline) &&
01545                 (line <= vga.draw.cursor.eline)) {
01546                 font ^= 0xFF;
01547             }
01548 
01549             /* "vertical line" bit puts a vertical line on the 4th pixel of the cell */
01550             if (!pc98_attr4_graphic && (attr & 0x10)) lineoverlay |= 1U << 7U;
01551 
01552             /* underline fills the row to underline the text */
01553             if ((attr & 0x08) && line == (vga.crtc.maximum_scan_line & 0x1FU)) lineoverlay |= 0xFFU;
01554 
01555             /* lineoverlay overlays font with 4-pixel delay */
01556             font |= (unsigned char)(((unsigned int)lineoverlay >> 4u) & 0xFFU);
01557 
01558             /* color? */
01559             foreground = ((unsigned int)attr >> 5u) & 7u; /* bits[7:5] are GRB foreground color */
01560 
01561             /* draw it!
01562              * NTS: Based on real hardware (and this is probably why there's no provisions for both fore and background color)
01563              *      any bit in the font overlays the graphic output (after reverse, etc) or else does not output anything. */
01564             /* NTS: Apparently (correct me if I'm wrong) the analog color palette applies to the graphics layer, NOT the text layer. */
01565             for (Bitu n = 0; n < 8; n++) {
01566                 if (font & 0x80)
01567                     *draw++ = pc98_text_palette[foreground];
01568                 else
01569                     draw++;
01570 
01571                 font <<= 1u;
01572             }
01573 
01574             vidmem++;
01575             gdcvidmem++;
01576         }
01577     }
01578 
01579     return TempLine;
01580 }
01581 
01582 static void VGA_ProcessSplit() {
01583     vga.draw.has_split = true;
01584     if (vga.attr.mode_control&0x20) {
01585         vga.draw.address=0;
01586         // reset panning to 0 here so we don't have to check for 
01587         // it in the character draw functions. It will be set back
01588         // to its proper value in v-retrace
01589         vga.draw.panning=0; 
01590     } else {
01591         // In text mode only the characters are shifted by panning, not the address;
01592         // this is done in the text line draw function. EGA/VGA planar is handled the same way.
01593         vga.draw.address = vga.draw.byte_panning_shift*vga.draw.bytes_skip;
01594         if (machine != MCH_EGA) {
01595             switch (vga.mode) {
01596                 case M_PC98:
01597                 case M_TEXT:
01598                 case M_EGA:
01599                 case M_LIN4:
01600                 case M_PACKED4:
01601                     /* ignore */
01602                     break;
01603                 default:
01604                     vga.draw.address += vga.draw.panning;
01605                     break;
01606             }
01607         }
01608     }
01609     vga.draw.address_line=0;
01610 }
01611 
01612 static Bit8u bg_color_index = 0; // screen-off black index
01613 static Bit8u VGA_GetBlankedIndex() {
01614     if (vga.dac.xlat16[bg_color_index] != 0) {
01615         for(Bitu i = 0; i < 256; i++)
01616             if (vga.dac.xlat16[i] == 0) {
01617                 bg_color_index = i;
01618                 break;
01619             }
01620     }
01621     return bg_color_index;
01622 }
01623 
01624 /* this is now called PER LINE because most VGA cards do not double-buffer the value.
01625  * a few demos rely on line compare schenanigans to play with the raster, as does my own VGA test program --J.C. */
01626 void VGA_Update_SplitLineCompare() {
01627     vga.draw.split_line = (vga.config.line_compare + 1) / vga.draw.render_max;
01628     if (svgaCard==SVGA_S3Trio) {
01629         /* FIXME: Is this really necessary? Is this what S3 chipsets do?
01630          *        What is supposed to happen is that line_compare == 0 on normal VGA will cause the first
01631          *        scanline to repeat twice. Do S3 chipsets NOT reproduce that quirk?
01632          *
01633          *        The other theory I have about whoever wrote this code is that they wanted to multiply
01634          *        the scan line by two but had to compensate for the line_compare+1 assignment above.
01635          *        Rather than end up with a splitscreen too far down, they did that.
01636          *
01637          *        I think the proper code to put here would be:
01638          *
01639          *        if (vga.s3.reg_42 & 0x20) {
01640          *            vga.draw.split_line--;
01641          *            vga.draw.split_line *= 2;
01642          *            vga.draw.split_line++;
01643          *        }
01644          *
01645          *        Is that right?
01646          *
01647          *        This behavior is the reason for Issue #40 "Flash productions "monstra" extra white line at top of screen"
01648          *        because it causes line compare to miss and the first scanline of the white bar appears on scanline 2,
01649          *        when the demo coder obviously intended for line_compare == 0 to repeat the first scanline twice so that
01650          *        on line 3, it can begin updating line compare to continue repeating the first scanline.
01651          *
01652          * TODO: Pull out some S3 graphics cards and check split line behavior when line_compare == 0 */
01653         if (vga.config.line_compare==0) vga.draw.split_line=0;
01654         if (vga.s3.reg_42 & 0x20) { // interlaced mode
01655             vga.draw.split_line *= 2;
01656         }
01657     }
01658     vga.draw.split_line -= vga.draw.vblank_skip;
01659 }
01660 
01661 static void VGA_DrawSingleLine(Bitu /*blah*/) {
01662     unsigned int lines = 0;
01663     bool skiprender;
01664 
01665 again:
01666     if (vga.draw.render_step == 0)
01667         skiprender = false;
01668     else
01669         skiprender = true;
01670 
01671     if ((++vga.draw.render_step) >= vga.draw.render_max)
01672         vga.draw.render_step = 0;
01673 
01674     if (!skiprender) {
01675         if (GCC_UNLIKELY(vga.attr.disabled)) {
01676             switch(machine) {
01677                 case MCH_PCJR:
01678                     // Displays the border color when screen is disabled
01679                     bg_color_index = vga.tandy.border_color;
01680                     break;
01681                 case MCH_TANDY:
01682                     // Either the PCJr way or the CGA way
01683                     if (vga.tandy.gfx_control& 0x4) { 
01684                         bg_color_index = vga.tandy.border_color;
01685                     } else if (vga.mode==M_TANDY4) 
01686                         bg_color_index = vga.attr.palette[0];
01687                     else bg_color_index = 0;
01688                     break;
01689                 case MCH_CGA:
01690                 case MCH_MCGA:
01691                     // the background color
01692                     bg_color_index = vga.attr.overscan_color;
01693                     break;
01694                 case MCH_EGA:
01695                 case MCH_VGA:
01696                 case MCH_PC98:
01697                     // DoWhackaDo, Alien Carnage, TV sports Football
01698                     // when disabled by attribute index bit 5:
01699                     //  ET3000, ET4000, Paradise display the border color
01700                     //  S3 displays the content of the currently selected attribute register
01701                     // when disabled by sequencer the screen is black "257th color"
01702 
01703                     // the DAC table may not match the bits of the overscan register
01704                     // so use black for this case too...
01705                     //if (vga.attr.disabled& 2) {
01706                     VGA_GetBlankedIndex();
01707                     //} else 
01708                     //    bg_color_index = vga.attr.overscan_color;
01709                     break;
01710                 default:
01711                     bg_color_index = 0;
01712                     break;
01713             }
01714             if (vga.draw.bpp==8) {
01715                 memset(TempLine, bg_color_index, sizeof(TempLine));
01716             } else if (vga.draw.bpp==16) {
01717                 Bit16u* wptr = (Bit16u*) TempLine;
01718                 Bit16u value = vga.dac.xlat16[bg_color_index];
01719                 for (Bitu i = 0; i < sizeof(TempLine)/2; i++) {
01720                     wptr[i] = value;
01721                 }
01722             } else if (vga.draw.bpp==32) {
01723                 Bit32u* wptr = (Bit32u*) TempLine;
01724                 Bit32u value = vga.dac.xlat32[bg_color_index];
01725                 for (Bitu i = 0; i < sizeof(TempLine)/4; i++) {
01726                     wptr[i] = value;
01727                 }
01728             }
01729 
01730             if (vga_page_flip_occurred) {
01731                 memxor(TempLine,0xFF,vga.draw.width*(vga.draw.bpp>>3));
01732                 vga_page_flip_occurred = false;
01733             }
01734             if (vga_3da_polled) {
01735                 memxor_greendotted_16bpp((uint16_t*)TempLine,(vga.draw.width>>1)*(vga.draw.bpp>>3),vga.draw.lines_done);
01736                 vga_3da_polled = false;
01737             }
01738             RENDER_DrawLine(TempLine);
01739         } else {
01740             Bit8u * data=VGA_DrawLine( vga.draw.address, vga.draw.address_line );
01741             if (vga_page_flip_occurred) {
01742                 memxor(data,0xFF,vga.draw.width*(vga.draw.bpp>>3));
01743                 vga_page_flip_occurred = false;
01744             }
01745             if (vga_3da_polled) {
01746                 memxor_greendotted_16bpp((uint16_t*)TempLine,(vga.draw.width>>1)*(vga.draw.bpp>>3),vga.draw.lines_done);
01747                 vga_3da_polled = false;
01748             }
01749             RENDER_DrawLine(data);
01750         }
01751     }
01752 
01753     vga.draw.address_line++;
01754     if (vga.draw.address_line>=vga.draw.address_line_total) {
01755         vga.draw.address_line=0;
01756         vga.draw.address+=vga.draw.address_add;
01757     }
01758 
01759     if (!skiprender) {
01760         vga.draw.lines_done++;
01761         if (vga.draw.split_line==vga.draw.lines_done) VGA_ProcessSplit();
01762     }
01763 
01764     if (mcga_double_scan) {
01765         if (vga.draw.lines_done < vga.draw.lines_total) {
01766             if (++lines < 2)
01767                 goto again;
01768         }
01769     }
01770 
01771     if (vga.draw.lines_done < vga.draw.lines_total) {
01772         PIC_AddEvent(VGA_DrawSingleLine,(float)vga.draw.delay.singleline_delay);
01773     } else {
01774         vga_mode_frames_since_time_base++;
01775         RENDER_EndUpdate(false);
01776     }
01777 
01778     if (IS_PC98_ARCH) {
01779         for (unsigned int i=0;i < 2;i++)
01780             pc98_gdc[i].next_line();
01781 
01782         pc98_text_draw.next_line();
01783     }
01784 
01785     /* some VGA cards (ATI chipsets especially) do not double-buffer the
01786      * horizontal panning register. some DOS demos take advantage of that
01787      * to make the picture "waver".
01788      *
01789      * We stop doing this though if the attribute controller is setup to set hpel=0 at splitscreen. */
01790     if (IS_VGA_ARCH && vga_enable_hpel_effects) {
01791         /* Attribute Mode Controller: If bit 5 (Pixel Panning Mode) is set, then upon line compare the bottom portion is displayed as if Pixel Shift Count and Byte Panning are set to 0.
01792          * This ensures some demos like Future Crew "Yo" display correctly instead of the bottom non-scroller half jittering because the top half is scrolling. */
01793         if (vga.draw.has_split && (vga.attr.mode_control&0x20))
01794             vga.draw.panning = 0;
01795         else
01796             vga.draw.panning = vga.config.pel_panning;
01797     }
01798 
01799     if (IS_EGAVGA_ARCH && !vga_double_buffered_line_compare) VGA_Update_SplitLineCompare();
01800 }
01801 
01802 static void VGA_DrawEGASingleLine(Bitu /*blah*/) {
01803     bool skiprender;
01804 
01805     if (vga.draw.render_step == 0)
01806         skiprender = false;
01807     else
01808         skiprender = true;
01809 
01810     if ((++vga.draw.render_step) >= vga.draw.render_max)
01811         vga.draw.render_step = 0;
01812 
01813     if (!skiprender) {
01814         if (GCC_UNLIKELY(vga.attr.disabled)) {
01815             memset(TempLine, 0, sizeof(TempLine));
01816             RENDER_DrawLine(TempLine);
01817         } else {
01818             Bitu address = vga.draw.address;
01819             if (machine != MCH_EGA) {
01820                 switch (vga.mode) {
01821                     case M_PC98:
01822                     case M_TEXT:
01823                     case M_EGA:
01824                     case M_LIN4:
01825                     case M_PACKED4:
01826                         /* ignore */
01827                         break;
01828                     default:
01829                         vga.draw.address += vga.draw.panning;
01830                         break;
01831                 }
01832             }
01833             Bit8u * data=VGA_DrawLine(address, vga.draw.address_line ); 
01834             RENDER_DrawLine(data);
01835         }
01836     }
01837 
01838     vga.draw.address_line++;
01839     if (vga.draw.address_line>=vga.draw.address_line_total) {
01840         vga.draw.address_line=0;
01841         vga.draw.address+=vga.draw.address_add;
01842     }
01843 
01844     if (!skiprender) {
01845         vga.draw.lines_done++;
01846         if (vga.draw.split_line==vga.draw.lines_done) VGA_ProcessSplit();
01847     }
01848 
01849     if (vga.draw.lines_done < vga.draw.lines_total) {
01850         PIC_AddEvent(VGA_DrawEGASingleLine,(float)vga.draw.delay.singleline_delay);
01851     } else {
01852         vga_mode_frames_since_time_base++;
01853         RENDER_EndUpdate(false);
01854     }
01855 }
01856 
01857 void VGA_SetBlinking(Bitu enabled) {
01858     Bitu b;
01859     LOG(LOG_VGA,LOG_NORMAL)("Blinking %d",(int)enabled);
01860     if (enabled) {
01861         b=0;vga.draw.blinking=1; //used to -1 but blinking is unsigned
01862         vga.attr.mode_control|=0x08;
01863         vga.tandy.mode_control|=0x20;
01864     } else {
01865         b=8;vga.draw.blinking=0;
01866         vga.attr.mode_control&=~0x08;
01867         vga.tandy.mode_control&=~0x20;
01868     }
01869     for (Bitu i=0;i<8;i++) TXT_BG_Table[i+8]=(b+i) | ((b+i) << 8)| ((b+i) <<16) | ((b+i) << 24);
01870 }
01871 
01872 extern bool                        GDC_vsync_interrupt;
01873 
01874 static void VGA_VertInterrupt(Bitu /*val*/) {
01875     if (IS_PC98_ARCH) {
01876         if (GDC_vsync_interrupt) {
01877             GDC_vsync_interrupt = false;
01878             PIC_ActivateIRQ(2);
01879         }
01880     }
01881     else {
01882         if ((!vga.draw.vret_triggered) && ((vga.crtc.vertical_retrace_end&0x30)==0x10)) {
01883             vga.draw.vret_triggered=true;
01884             if (GCC_UNLIKELY(machine==MCH_EGA)) PIC_ActivateIRQ(9);
01885         }
01886     }
01887 }
01888 
01889 static void VGA_Other_VertInterrupt(Bitu val) {
01890     if (val) PIC_ActivateIRQ(5);
01891     else PIC_DeActivateIRQ(5);
01892 }
01893 
01894 static void VGA_DisplayStartLatch(Bitu /*val*/) {
01895     /* hretrace fx support: store the hretrace value at start of picture so we have
01896      * a point of reference how far to displace the scanline when wavy effects are
01897      * made */
01898     vga_display_start_hretrace = vga.crtc.start_horizontal_retrace;
01899     vga.config.real_start=vga.config.display_start & vga.mem.memmask;
01900     vga.draw.bytes_skip = vga.config.bytes_skip;
01901 
01902     /* TODO: When does 640x480 2-color mode latch foreground/background colors from the DAC? */
01903     if (machine == MCH_MCGA && (vga.other.mcga_mode_control & 2)) {//640x480 2-color mode MCGA
01904         VGA_DAC_UpdateColorPalette();
01905     }
01906 }
01907  
01908 static void VGA_PanningLatch(Bitu /*val*/) {
01909     vga.draw.panning = vga.config.pel_panning;
01910 
01911     if (IS_PC98_ARCH) {
01912         for (unsigned int i=0;i < 2;i++)
01913             pc98_gdc[i].begin_frame();
01914 
01915         pc98_text_draw.begin_frame();
01916     }
01917 }
01918 
01919 static void VGA_VerticalTimer(Bitu /*val*/) {
01920     double current_time = PIC_FullIndex();
01921 
01922     GDC_display_plane = GDC_display_plane_pending;
01923 
01924     vga.draw.delay.framestart = current_time; /* FIXME: Anyone use this?? If not, remove it */
01925     vga_page_flip_occurred = false;
01926     vga.draw.has_split = false;
01927     vga_3da_polled = false;
01928 
01929     // FIXME: While this code is quite good at keeping time, I'm seeing drift "reset" back to
01930     //        14-30ms every video mode change. Is our INT 10h code that slow?
01931     /* compensate for floating point drift, make sure we're keeping the frame rate.
01932      * be very gentle about it. generally the drift is very small, and large adjustments can cause
01933      * DOS games dependent on vsync to fail/hang. */
01934     double shouldbe = (((double)vga_mode_frames_since_time_base * 1000.0) / vga_fps) + vga_mode_time_base;
01935     double vsync_err = shouldbe - current_time; /* < 0 too slow     > 0 too fast */
01936     double vsync_adj = (vsync_err < 0 ? -1 : 1) * vsync_err * vsync_err * 0.05;
01937     if (vsync_adj < -0.1) vsync_adj = -0.1;
01938     else if (vsync_adj > 0.1) vsync_adj = 0.1;
01939 
01940 //  LOG_MSG("Vsync err %.6fms adj=%.6fms",vsync_err,vsync_adj);
01941 
01942     float vsynctimerval;
01943     float vdisplayendtimerval;
01944     if( vsync.manual ) {
01945         static float hack_memory = 0.0f;
01946         if( hack_memory > 0.0f ) {
01947             uservsyncjolt+=hack_memory;
01948             hack_memory = 0.0f;
01949         }
01950 
01951         float faithful_framerate_adjustment_delay = 0.0f;
01952         if( vsync.faithful ) {
01953             static float counter = 0.0f;
01954             const float gfxmode_vsyncrate   = 1000.0f/vga.draw.delay.vtotal;
01955             const float user_vsyncrate      = 1000.0f/vsync.period;
01956             const float framerate_diff      = user_vsyncrate - gfxmode_vsyncrate;
01957             if( framerate_diff >= 0 ) {
01958                 // User vsync rate is greater than the target vsync rate
01959                 const float adjustment_deadline = gfxmode_vsyncrate / framerate_diff;
01960                 counter += 1.0f;
01961                 if(counter >= adjustment_deadline) {
01962                     // double vsync duration this frame to resynchronize with the target vsync timing
01963                     faithful_framerate_adjustment_delay = vsync.period;
01964                     counter -= adjustment_deadline;
01965                 }
01966             } else {
01967                 // User vsync rate is less than the target vsync rate
01968 
01969                 // I don't have a good solution for this right now.
01970                 // I also don't have access to a 60Hz display for proper testing.
01971                 // Making an instant vsync is more difficult than making a long vsync.. because
01972                 // the emulated app's retrace loop must be able to detect that the retrace has both
01973                 // begun and ended.  So it's not as easy as adjusting timer durations.
01974                 // I think adding a hack to cause port 3da's code to temporarily force the
01975                 // vertical retrace bit to cycle could work.. Even then, it's possible that
01976                 // some shearing could be seen when the app draws two new frames during a single
01977                 // host refresh.
01978                 // Anyway, this could be worth dealing with for console ports since they'll be
01979                 // running at either 60 or 50Hz (below 70Hz).
01980                 /*
01981                 const float adjustment_deadline = -(gfxmode_vsyncrate / framerate_diff);
01982                 counter += 1.0f;
01983                 if(counter >= adjustment_deadline) {
01984                     // nullify vsync duration this frame to resynchronize with the target vsync timing
01985                     // TODO(AUG): proper low user vsync rate synchronization
01986                     faithful_framerate_adjustment_delay = -uservsyncperiod + 1.2f;
01987                     vsync_hackval = 10;
01988                     hack_memory = -1.2f;
01989                     counter -= adjustment_deadline;
01990                 }
01991                 */
01992             }
01993         }
01994 
01995         const Bitu persistent_sync_update_interval = 100;
01996         static Bitu persistent_sync_counter = persistent_sync_update_interval;
01997         Bits current_tick = GetTicks();
01998         static Bitu jolt_tick = 0;
01999         if( uservsyncjolt > 0.0f ) {
02000             jolt_tick = (Bitu)current_tick;
02001 
02002             // set the update counter to a low value so that the user will almost
02003             // immediately see the effects of an auto-correction.  This gives the
02004             // user a chance to compensate for it.
02005             persistent_sync_counter = 50;
02006         }
02007 
02008         float real_diff = 0.0f;
02009         if( vsync.persistent ) {
02010             if( persistent_sync_counter == 0 ) {
02011                 float ticks_since_jolt = (signed long)current_tick - (signed long)jolt_tick;
02012                 double num_host_syncs_in_those_ticks = floor(ticks_since_jolt / vsync.period);
02013                 float diff_thing = ticks_since_jolt - (num_host_syncs_in_those_ticks * (double)vsync.period);
02014 
02015                 if( diff_thing > (vsync.period / 2.0f) ) real_diff = diff_thing - vsync.period;
02016                 else real_diff = diff_thing;
02017 
02018 //              LOG_MSG("diff is %f",real_diff);
02019 
02020                 if( ((real_diff > 0.0f) && (real_diff < 1.5f)) || ((real_diff < 0.0f) && (real_diff > -1.5f)) )
02021                     real_diff = 0.0f;
02022 
02023                 persistent_sync_counter = persistent_sync_update_interval;
02024             } else --persistent_sync_counter;
02025         }
02026 
02027 //      vsynctimerval       = uservsyncperiod + faithful_framerate_adjustment_delay + uservsyncjolt;
02028         vsynctimerval       = vsync.period - (real_diff/1.0f);  // formerly /2.0f
02029         vsynctimerval       += faithful_framerate_adjustment_delay + uservsyncjolt;
02030 
02031         // be sure to provide delay between end of one refresh, and start of the next
02032 //      vdisplayendtimerval = vsynctimerval - 1.18f;
02033 
02034         // be sure to provide delay between end of one refresh, and start of the next
02035         vdisplayendtimerval = vsynctimerval - (vga.draw.delay.vtotal - vga.draw.delay.vrstart);
02036 
02037         // in case some calculations above cause this.  this really shouldn't happen though.
02038         if( vdisplayendtimerval < 0.0f ) vdisplayendtimerval = 0.0f;
02039 
02040         uservsyncjolt = 0.0f;
02041     } else {
02042         // Standard vsync behaviour
02043         vsynctimerval       = (float)vga.draw.delay.vtotal;
02044         vdisplayendtimerval = (float)vga.draw.delay.vrstart;
02045     }
02046 
02047     {
02048         double fv;
02049 
02050         fv = vsynctimerval + vsync_adj;
02051         if (fv < 1) fv = 1;
02052         PIC_AddEvent(VGA_VerticalTimer,fv);
02053 
02054         fv = vdisplayendtimerval + vsync_adj;
02055         if (fv < 1) fv = 1;
02056         PIC_AddEvent(VGA_DisplayStartLatch,fv);
02057     }
02058     
02059     switch(machine) {
02060     case MCH_PCJR:
02061     case MCH_TANDY:
02062         // PCJr: Vsync is directly connected to the IRQ controller
02063         // Some earlier Tandy models are said to have a vsync interrupt too
02064         PIC_AddEvent(VGA_Other_VertInterrupt, (float)vga.draw.delay.vrstart, 1);
02065         PIC_AddEvent(VGA_Other_VertInterrupt, (float)vga.draw.delay.vrend, 0);
02066         // fall-through
02067     case MCH_AMSTRAD:
02068     case MCH_CGA:
02069     case MCH_MDA:
02070     case MCH_MCGA:
02071     case MCH_HERC:
02072         // MC6845-powered graphics: Loading the display start latch happens somewhere
02073         // after vsync off and before first visible scanline, so probably here
02074         VGA_DisplayStartLatch(0);
02075         break;
02076     case MCH_VGA:
02077     case MCH_PC98:
02078         PIC_AddEvent(VGA_DisplayStartLatch, (float)vga.draw.delay.vrstart);
02079         PIC_AddEvent(VGA_PanningLatch, (float)vga.draw.delay.vrend);
02080         // EGA: 82c435 datasheet: interrupt happens at display end
02081         // VGA: checked with scope; however disabled by default by jumper on VGA boards
02082         // add a little amount of time to make sure the last drawpart has already fired
02083         PIC_AddEvent(VGA_VertInterrupt,(float)(vga.draw.delay.vdend + 0.005));
02084         break;
02085     case MCH_EGA:
02086         PIC_AddEvent(VGA_DisplayStartLatch, (float)vga.draw.delay.vrend);
02087         PIC_AddEvent(VGA_VertInterrupt,(float)(vga.draw.delay.vdend + 0.005));
02088         break;
02089     default:
02090         //E_Exit("This new machine needs implementation in VGA_VerticalTimer too.");
02091         PIC_AddEvent(VGA_DisplayStartLatch, (float)vga.draw.delay.vrstart);
02092         PIC_AddEvent(VGA_PanningLatch, (float)vga.draw.delay.vrend);
02093         PIC_AddEvent(VGA_VertInterrupt,(float)(vga.draw.delay.vdend + 0.005));
02094         break;
02095     }
02096     // for same blinking frequency with higher frameskip
02097     vga.draw.cursor.count++;
02098 
02099     if (IS_PC98_ARCH) {
02100         for (unsigned int i=0;i < 2;i++)
02101             pc98_gdc[i].cursor_advance();
02102     }
02103 
02104     //Check if we can actually render, else skip the rest
02105     if (vga.draw.vga_override || !RENDER_StartUpdate()) return;
02106 
02107     vga.draw.address_line = vga.config.hlines_skip;
02108     if (IS_EGAVGA_ARCH) VGA_Update_SplitLineCompare();
02109     vga.draw.address = vga.config.real_start;
02110     vga.draw.byte_panning_shift = 0;
02111 
02112     switch (vga.mode) {
02113     case M_EGA:
02114         if (vga.mem.memmask >= 0x1FFFFu) {
02115             /* EGA/VGA Mode control register 0x17 effects on the linear mask */
02116             if ((vga.crtc.maximum_scan_line&0x1fu) == 0) {
02117                 /* WARNING: These hacks only work IF max scanline value == 0 (no doubling).
02118                  *          The bit 0 (bit 13 replacement) mode here is needed for
02119                  *          Prehistorik 2 to display it's mode select/password entry screen
02120                  *          (the one with the scrolling background of various cavemen) */
02121 
02122                 /* if bit 0 is cleared, CGA compatible addressing is enabled.
02123                  * bit 13 is replaced by bit 0 of the row counter */
02124                 if (!(vga.crtc.mode_control&0x1u)) vga.draw.linear_mask &= ~0x8000u;
02125                 else vga.draw.linear_mask |= 0x8000u;
02126 
02127                 /* if bit 1 is cleared, Hercules compatible addressing is enabled.
02128                  * bit 14 is replaced by bit 0 of the row counter */
02129                 if (!(vga.crtc.mode_control&0x2u)) vga.draw.linear_mask &= ~0x10000u;
02130                 else vga.draw.linear_mask |= 0x10000u;
02131             }
02132             else {
02133                 if ((vga.crtc.mode_control&0x03u) != 0x03u) {
02134                     LOG(LOG_VGAMISC,LOG_WARN)("Guest is attempting to use CGA/Hercules compatible display mapping in M_EGA mode with max_scanline != 0, which is not yet supported");
02135                 }
02136             }
02137         }
02138         /* fall through */
02139     case M_LIN4:
02140         vga.draw.byte_panning_shift = 4u;
02141         vga.draw.address += vga.draw.bytes_skip;
02142         vga.draw.address *= vga.draw.byte_panning_shift;
02143         break;
02144     case M_VGA:
02145         /* TODO: Various SVGA chipsets have a bit to enable/disable 256KB wrapping */
02146         vga.draw.linear_mask = 0x3ffffu;
02147         if (svgaCard == SVGA_TsengET3K || svgaCard == SVGA_TsengET4K) {
02148             if (vga.config.addr_shift == 1) /* NTS: Remember the ET4K steps by 4 pixels, one per byteplane, treats BYTE and DWORD modes the same */
02149                 vga.draw.address *= 2u;
02150         }
02151         else if (machine == MCH_MCGA) {
02152             vga.draw.linear_mask = 0xffffu;
02153             vga.draw.address *= 2u;
02154             break;// don't fall through
02155         }
02156         else {
02157             vga.draw.address *= 1u<<vga.config.addr_shift; /* NTS: Remember the bizarre 4 x 4 mode most SVGA chipsets do */
02158         }
02159         /* fall through */
02160     case M_LIN8:
02161     case M_LIN15:
02162     case M_LIN16:
02163     case M_LIN24:
02164     case M_LIN32:
02165         vga.draw.byte_panning_shift = 4;
02166         vga.draw.address += vga.draw.bytes_skip;
02167         vga.draw.address *= vga.draw.byte_panning_shift;
02168         vga.draw.address += vga.draw.panning;
02169         break;
02170     case M_PACKED4:
02171         vga.draw.byte_panning_shift = 4u;
02172         vga.draw.address += vga.draw.bytes_skip;
02173         vga.draw.address *= vga.draw.byte_panning_shift;
02174         break;
02175     case M_PC98:
02176         vga.draw.linear_mask = 0xfff; // 1 page
02177         vga.draw.byte_panning_shift = 2;
02178         vga.draw.address += vga.draw.bytes_skip;
02179         vga.draw.cursor.address = vga.config.cursor_start;
02180         break;
02181     case M_TEXT:
02182         vga.draw.byte_panning_shift = 2;
02183         vga.draw.address += vga.draw.bytes_skip;
02184         // fall-through
02185     case M_TANDY_TEXT:
02186     case M_HERC_TEXT:
02187         if (machine==MCH_HERC || machine==MCH_MDA) vga.draw.linear_mask = 0xfff; // 1 page
02188         else if (IS_EGAVGA_ARCH || machine == MCH_MCGA) vga.draw.linear_mask = 0x7fff; // 8 pages
02189         else vga.draw.linear_mask = 0x3fff; // CGA, Tandy 4 pages
02190         if (IS_EGAVGA_ARCH)
02191             vga.draw.cursor.address=vga.config.cursor_start<<vga.config.addr_shift;
02192         else
02193             vga.draw.cursor.address=vga.config.cursor_start*2;
02194         vga.draw.address *= 2;
02195 
02196         /* check for blinking and blinking change delay */
02197         FontMask[1]=(vga.draw.blinking & (unsigned int)(vga.draw.cursor.count >> 4u)) ?
02198             0 : 0xffffffff;
02199         /* if blinking is enabled, 'blink' will toggle between true
02200          * and false. Otherwise it's true */
02201         vga.draw.blink = ((vga.draw.blinking & (unsigned int)(vga.draw.cursor.count >> 4u))
02202             || !vga.draw.blinking) ? true:false;
02203         break;
02204     case M_HERC_GFX:
02205     case M_CGA4:
02206     case M_CGA2:
02207         vga.draw.address=(vga.draw.address*2u)&0x1fffu;
02208         break;
02209     case M_AMSTRAD: // Base address: No difference?
02210         vga.draw.address=(vga.draw.address*2u)&0xffffu;
02211         break;
02212     case M_CGA16:
02213     case M_TANDY2:case M_TANDY4:case M_TANDY16:
02214         vga.draw.address *= 2u;
02215         break;
02216     default:
02217         break;
02218     }
02219 
02220     /* ET4000 High Sierra DAC programs can change SVGA mode */
02221     if ((vga.mode == M_LIN15 || vga.mode == M_LIN16) && (svgaCard == SVGA_TsengET3K || svgaCard == SVGA_TsengET4K)) {
02222         if (et4k_highcolor_half_pixel_rate())
02223             VGA_DrawLine=VGA_Draw_LIN16_Line_2x;
02224         else
02225             VGA_DrawLine=VGA_Draw_LIN16_Line_HWMouse;
02226     }
02227 
02228     // check if some lines at the top off the screen are blanked
02229     float draw_skip = 0.0;
02230     if (GCC_UNLIKELY(vga.draw.vblank_skip > 0)) {
02231         draw_skip = (float)(vga.draw.delay.htotal * vga.draw.vblank_skip);
02232         vga.draw.address_line += (vga.draw.vblank_skip % vga.draw.address_line_total);
02233         vga.draw.address += vga.draw.address_add * (vga.draw.vblank_skip / vga.draw.address_line_total);
02234     }
02235 
02236     /* do VGA split now if line compare <= 0. NTS: vga.draw.split_line is defined as Bitu (unsigned integer) so we need the typecast. */
02237     if (GCC_UNLIKELY((Bits)vga.draw.split_line <= 0)) {
02238         VGA_ProcessSplit();
02239 
02240         /* if vblank_skip != 0, line compare can become a negative value! Fixes "Warlock" 1992 demo by Warlock */
02241         if (GCC_UNLIKELY((Bits)vga.draw.split_line < 0)) {
02242             vga.draw.address_line += (Bitu)(-((Bits)vga.draw.split_line) % (Bits)vga.draw.address_line_total);
02243             vga.draw.address += vga.draw.address_add * (Bitu)(-((Bits)vga.draw.split_line) / (Bits)vga.draw.address_line_total);
02244         }
02245     }
02246 
02247     // add the draw event
02248     switch (vga.draw.mode) {
02249     case LINE:
02250     case EGALINE:
02251         if (GCC_UNLIKELY(vga.draw.lines_done < vga.draw.lines_total)) {
02252             LOG(LOG_VGAMISC,LOG_NORMAL)( "Lines left: %d", 
02253                 (int)(vga.draw.lines_total-vga.draw.lines_done));
02254             if (vga.draw.mode==EGALINE) PIC_RemoveEvents(VGA_DrawEGASingleLine);
02255             else PIC_RemoveEvents(VGA_DrawSingleLine);
02256             vga_mode_frames_since_time_base++;
02257             RENDER_EndUpdate(true);
02258         }
02259         vga.draw.lines_done = 0;
02260         if (vga.draw.mode==EGALINE)
02261             PIC_AddEvent(VGA_DrawEGASingleLine,(float)(vga.draw.delay.htotal/4.0 + draw_skip));
02262         else PIC_AddEvent(VGA_DrawSingleLine,(float)(vga.draw.delay.htotal/4.0 + draw_skip));
02263         break;
02264     }
02265 }
02266 
02267 void VGA_CheckScanLength(void) {
02268     switch (vga.mode) {
02269     case M_EGA:
02270     case M_LIN4:
02271         if ((machine==MCH_EGA)&&(vga.crtc.mode_control&0x8))
02272             vga.draw.address_add=vga.config.scan_len*16;
02273         else
02274             vga.draw.address_add=vga.config.scan_len*8;
02275 
02276         if (IS_EGA_ARCH && (vga.seq.clocking_mode&4))
02277             vga.draw.address_add*=2;
02278         break;
02279     case M_PACKED4:
02280         vga.draw.address_add=vga.config.scan_len*8;
02281         break;
02282     case M_VGA:
02283     case M_LIN8:
02284     case M_LIN15:
02285     case M_LIN16:
02286     case M_LIN24:
02287     case M_LIN32:
02288         if (vga.mode == M_VGA) {
02289             if (svgaCard == SVGA_TsengET3K || svgaCard == SVGA_TsengET4K) {
02290                 /* Observed ET4000AX behavior:
02291                  *    byte mode OR dword mode scans 256-color 4-pixel groups byte by byte from
02292                  *    planar RAM. word mode scans every other byte (skips by two).
02293                  *    We can just scan the buffer normally as linear because of the address
02294                  *    translation carried out by the ET4000 in chained mode:
02295                  *
02296                  *    plane = (addr & 3)   addr = (addr >> 2)
02297                  *
02298                  *    TODO: Validate that this is correct. */
02299                 vga.draw.address_add=vga.config.scan_len*((vga.config.addr_shift == 1)?16:8);
02300             }
02301             else if (machine == MCH_MCGA) {
02302                 vga.draw.address_add=vga.draw.blocks*4;
02303             }
02304             else {
02305                 /* Most (almost ALL) VGA clones render chained modes as 4 8-bit planes one DWORD apart.
02306                  * They all act as if writes to chained VGA memory are translated as:
02307                  * addr = ((addr & ~3) << 2) + (addr & 3) */
02308                 vga.draw.address_add=(unsigned int)vga.config.scan_len*((unsigned int)(2*4)<<(unsigned int)vga.config.addr_shift);
02309             }
02310         }
02311         else {
02312             /* the rest (SVGA modes) can be rendered with sanity */
02313             vga.draw.address_add=vga.config.scan_len*(unsigned int)(2u<<vga.config.addr_shift);
02314         }
02315         break;
02316     case M_PC98:
02317         vga.draw.address_add=vga.draw.blocks;
02318         break;
02319     case M_TEXT:
02320     case M_CGA2:
02321     case M_CGA4:
02322     case M_CGA16:
02323     case M_AMSTRAD: // Next line.
02324         if (IS_EGAVGA_ARCH || IS_PC98_ARCH)
02325             vga.draw.address_add=vga.config.scan_len*(unsigned int)(2u<<vga.config.addr_shift);
02326         else
02327             vga.draw.address_add=vga.draw.blocks;
02328         break;
02329     case M_TANDY2:
02330         if (machine == MCH_MCGA)
02331             vga.draw.address_add=vga.draw.blocks;
02332         else
02333             vga.draw.address_add=vga.draw.blocks/4u;
02334         break;
02335     case M_TANDY4:
02336         vga.draw.address_add=vga.draw.blocks;
02337         break;
02338     case M_TANDY16:
02339         vga.draw.address_add=vga.draw.blocks;
02340         break;
02341     case M_TANDY_TEXT:
02342         vga.draw.address_add=vga.draw.blocks*2;
02343         break;
02344     case M_HERC_TEXT:
02345         vga.draw.address_add=vga.draw.blocks*2;
02346         break;
02347     case M_HERC_GFX:
02348         vga.draw.address_add=vga.draw.blocks;
02349         break;
02350     default:
02351         vga.draw.address_add=vga.draw.blocks*8;
02352         break;
02353     }
02354 }
02355 
02356 void VGA_ActivateHardwareCursor(void) {
02357     bool hwcursor_active=false;
02358     if (svga.hardware_cursor_active) {
02359         if (svga.hardware_cursor_active()) hwcursor_active=true;
02360     }
02361     if (hwcursor_active && vga.mode != M_LIN24) {
02362         switch(vga.mode) {
02363         case M_LIN32:
02364             VGA_DrawLine=VGA_Draw_LIN32_Line_HWMouse;
02365             break;
02366         case M_LIN15:
02367         case M_LIN16:
02368             if ((svgaCard == SVGA_TsengET3K || svgaCard == SVGA_TsengET4K) && et4k_highcolor_half_pixel_rate())
02369                 VGA_DrawLine=VGA_Draw_LIN16_Line_2x;
02370             else
02371                 VGA_DrawLine=VGA_Draw_LIN16_Line_HWMouse;
02372             break;
02373         case M_LIN8:
02374             VGA_DrawLine=VGA_Draw_VGA_Line_Xlat32_HWMouse;
02375             break;
02376         default:
02377             VGA_DrawLine=VGA_Draw_VGA_Line_HWMouse;
02378             break;
02379         }
02380     } else {
02381         switch(vga.mode) {
02382         case M_LIN8:
02383             VGA_DrawLine=VGA_Draw_Xlat32_Linear_Line;
02384             break;
02385         case M_LIN24:
02386             VGA_DrawLine=VGA_Draw_Linear_Line_24_to_32;
02387             break;
02388 #if SDL_BYTEORDER == SDL_LIL_ENDIAN && defined(MACOSX) /* Mac OS X Intel builds use a weird RGBA order (alpha in the low 8 bits) */
02389         case M_LIN32:
02390             VGA_DrawLine=VGA_Draw_LIN32_Line_HWMouse;
02391             break;
02392 #endif
02393         default:
02394             VGA_DrawLine=VGA_Draw_Linear_Line;
02395             break;
02396         }
02397     }
02398 }
02399 
02400 void VGA_SetupDrawing(Bitu /*val*/) {
02401     if (vga.mode==M_ERROR) {
02402         PIC_RemoveEvents(VGA_VerticalTimer);
02403         PIC_RemoveEvents(VGA_PanningLatch);
02404         PIC_RemoveEvents(VGA_DisplayStartLatch);
02405         return;
02406     }
02407     // user choosable special trick support
02408     // multiscan -- zooming effects - only makes sense if linewise is enabled
02409     // linewise -- scan display line by line instead of 4 blocks
02410     // keep compatibility with other builds of DOSBox for vgaonly.
02411     vga.draw.doublescan_effect = true;
02412     vga.draw.render_step = 0;
02413     vga.draw.render_max = 1;
02414 
02415     // set the drawing mode
02416     switch (machine) {
02417     case MCH_CGA:
02418     case MCH_MCGA:
02419     case MCH_PCJR:
02420     case MCH_TANDY:
02421         vga.draw.mode = LINE;
02422         break;
02423     case MCH_EGA:
02424         // Note: The Paradise SVGA uses the same panning mechanism as EGA
02425         vga.draw.mode = EGALINE;
02426         break;
02427     case MCH_VGA:
02428     case MCH_PC98:
02429         if (svgaCard==SVGA_None) {
02430             vga.draw.mode = LINE;
02431             break;
02432         }
02433         // fall-through
02434     default:
02435         vga.draw.mode = LINE;
02436         break;
02437     }
02438     
02439     /* Calculate the FPS for this screen */
02440     Bitu oscclock = 0, clock;
02441     Bitu htotal, hdend, hbstart, hbend, hrstart, hrend;
02442     Bitu vtotal, vdend, vbstart, vbend, vrstart, vrend;
02443     Bitu hbend_mask, vbend_mask;
02444     Bitu vblank_skip;
02445 
02446     /* NTS: PC-98 emulation re-uses VGA state FOR NOW.
02447      *      This will slowly change to accomodate PC-98 display controller over time
02448      *      and IS_PC98_ARCH will become it's own case statement. */
02449 
02450     if (IS_PC98_ARCH) {
02451         hdend = pc98_gdc[GDC_MASTER].active_display_words_per_line;
02452         hbstart = hdend;
02453         hrstart = hdend + pc98_gdc[GDC_MASTER].horizontal_front_porch_width;
02454         hrend = hrstart + pc98_gdc[GDC_MASTER].horizontal_sync_width;
02455         htotal = hrend + pc98_gdc[GDC_MASTER].horizontal_back_porch_width;
02456         hbend = htotal;
02457 
02458         vdend = pc98_gdc[GDC_MASTER].active_display_lines;
02459         vbstart = vdend;
02460         vrstart = vdend + pc98_gdc[GDC_MASTER].vertical_front_porch_width;
02461         vrend = vrstart + pc98_gdc[GDC_MASTER].vertical_sync_width;
02462         vtotal = vrend + pc98_gdc[GDC_MASTER].vertical_back_porch_width;
02463         vbend = vtotal;
02464 
02465         // perhaps if the game makes a custom mode, it might choose different active display regions
02466         // for text and graphics. allow that here.
02467         // NTS: Remember that the graphics (slave) GDC is programmed in "words" which in graphics mode
02468         //      means 16-pixel wide units.
02469         if (hdend < (pc98_gdc[GDC_SLAVE].active_display_words_per_line * 2U))
02470             hdend = (pc98_gdc[GDC_SLAVE].active_display_words_per_line * 2U);
02471         if (vdend < (pc98_gdc[GDC_SLAVE].active_display_lines))
02472             vdend = (pc98_gdc[GDC_SLAVE].active_display_lines);
02473 
02474         // TODO: The GDC rendering should allow different active display regions to render
02475         //       properly over one another.
02476 
02477         // TODO: Found a monitor document that lists two different scan rates for PC-98:
02478         //
02479         //       640x400  25.175MHz dot clock  70.15Hz refresh  31.5KHz horizontal refresh (basically, VGA)
02480         //       640x400  21.05MHz dot clock   56.42Hz refresh  24.83Hz horizontal refresh (original spec?)
02481 
02482         if (false/*future 15KHz hsync*/) {
02483             oscclock = 14318180;
02484         }
02485         else if (!pc98_31khz_mode/*24KHz hsync*/) {
02486             oscclock = 21052600;
02487         }
02488         else {/*31KHz VGA-like hsync*/
02489             oscclock = 25175000;
02490         }
02491 
02492         clock = oscclock / 8;
02493     }
02494     else if (IS_EGAVGA_ARCH) {
02495         htotal = vga.crtc.horizontal_total;
02496         hdend = vga.crtc.horizontal_display_end;
02497         hbend = vga.crtc.end_horizontal_blanking&0x1F;
02498         hbstart = vga.crtc.start_horizontal_blanking;
02499         hrstart = vga.crtc.start_horizontal_retrace;
02500 
02501         vtotal= vga.crtc.vertical_total | ((vga.crtc.overflow & 1u) << 8u);
02502         vdend = vga.crtc.vertical_display_end | ((vga.crtc.overflow & 2u) << 7u);
02503         vbstart = vga.crtc.start_vertical_blanking | ((vga.crtc.overflow & 0x08u) << 5u);
02504         vrstart = vga.crtc.vertical_retrace_start + ((vga.crtc.overflow & 0x04u) << 6u);
02505         
02506         if (IS_VGA_ARCH || IS_PC98_ARCH) {
02507             // additional bits only present on vga cards
02508             htotal |= (vga.s3.ex_hor_overflow & 0x1u) << 8u;
02509             htotal += 3u;
02510             hdend |= (vga.s3.ex_hor_overflow & 0x2u) << 7u;
02511             hbend |= (vga.crtc.end_horizontal_retrace&0x80u) >> 2u;
02512             hbstart |= (vga.s3.ex_hor_overflow & 0x4u) << 6u;
02513             hrstart |= (vga.s3.ex_hor_overflow & 0x10u) << 4u;
02514             hbend_mask = 0x3fu;
02515             
02516             vtotal |= (vga.crtc.overflow & 0x20u) << 4u;
02517             vtotal |= (vga.s3.ex_ver_overflow & 0x1u) << 10u;
02518             vdend |= (vga.crtc.overflow & 0x40u) << 3u; 
02519             vdend |= (vga.s3.ex_ver_overflow & 0x2u) << 9u;
02520             vbstart |= (vga.crtc.maximum_scan_line & 0x20u) << 4u;
02521             vbstart |= (vga.s3.ex_ver_overflow & 0x4u) << 8u;
02522             vrstart |= ((vga.crtc.overflow & 0x80u) << 2u);
02523             vrstart |= (vga.s3.ex_ver_overflow & 0x10u) << 6u;
02524             vbend_mask = 0xffu;
02525         } else { // EGA
02526             hbend_mask = 0x1fu;
02527             vbend_mask = 0x1fu;
02528         }
02529         htotal += 2;
02530         vtotal += 2;
02531         hdend += 1;
02532         vdend += 1;
02533 
02534         // horitzontal blanking
02535         if (hbend <= (hbstart & hbend_mask)) hbend += hbend_mask + 1;
02536         hbend += hbstart - (hbstart & hbend_mask);
02537         
02538         // horizontal retrace
02539         hrend = vga.crtc.end_horizontal_retrace & 0x1f;
02540         if (hrend <= (hrstart&0x1f)) hrend += 32;
02541         hrend += hrstart - (hrstart&0x1f);
02542         if (hrend > hbend) hrend = hbend; // S3 BIOS (???)
02543         
02544         // vertical retrace
02545         vrend = vga.crtc.vertical_retrace_end & 0xf;
02546         if (vrend <= (vrstart&0xf)) vrend += 16;
02547         vrend += vrstart - (vrstart&0xf);
02548 
02549         // vertical blanking
02550         vbend = vga.crtc.end_vertical_blanking & vbend_mask;
02551         if (vbstart != 0) {
02552         // Special case vbstart==0:
02553         // Most graphics cards agree that lines zero to vbend are
02554         // blanked. ET4000 doesn't blank at all if vbstart==vbend.
02555         // ET3000 blanks lines 1 to vbend (255/6 lines).
02556             vbstart += 1;
02557             if (vbend <= (vbstart & vbend_mask)) vbend += vbend_mask + 1;
02558             vbend += vbstart - (vbstart & vbend_mask);
02559         }
02560         vbend++;
02561 
02562         // TODO: Found a monitor document that lists two different scan rates for PC-98:
02563         //
02564         //       640x400  25.175MHz dot clock  70.15Hz refresh  31.5KHz horizontal refresh (basically, VGA)
02565         //       640x400  21.05MHz dot clock   56.42Hz refresh  24.83Hz horizontal refresh (original spec?)
02566 
02567         if (svga.get_clock) {
02568             oscclock = svga.get_clock();
02569         } else if (vga.mode == M_PC98) {
02570             if (false/*future 15KHz hsync*/) {
02571                 oscclock = 14318180;
02572             }
02573             else if (!pc98_31khz_mode/*24KHz hsync*/) {
02574                 oscclock = 21052600;
02575             }
02576             else {/*31KHz VGA-like hsync*/
02577                 oscclock = 25175000;
02578             }
02579         } else {
02580             switch ((vga.misc_output >> 2) & 3) {
02581             case 0: 
02582                 oscclock = (machine==MCH_EGA) ? (PIT_TICK_RATE*12) : 25175000;
02583                 break;
02584             case 1:
02585             default:
02586                 oscclock = (machine==MCH_EGA) ? 16257000 : 28322000;
02587                 break;
02588             }
02589         }
02590 
02591         /* Check for 8 or 9 character clock mode */
02592         if (vga.seq.clocking_mode & 1 ) clock = oscclock/8; else clock = oscclock/9;
02593         if (vga.mode==M_LIN15 || vga.mode==M_LIN16) clock *= 2;
02594         /* Check for pixel doubling, master clock/2 */
02595         if (vga.seq.clocking_mode & 0x8) clock /=2;
02596 
02597         if (svgaCard==SVGA_S3Trio) {
02598             // support for interlacing used by the S3 BIOS and possibly other drivers
02599             if (vga.s3.reg_42 & 0x20) {
02600                 vtotal *= 2;    vdend *= 2;
02601                 vbstart *= 2;   vbend *= 2;
02602                 vrstart *= 2;   vrend *= 2;
02603                 //clock /= 2;
02604         }
02605         }
02606 
02607         /* FIXME: This is based on correcting the hicolor mode for MFX/Transgression 2.
02608          *        I am not able to test against the Windows drivers at this time. */
02609         if ((svgaCard == SVGA_TsengET3K || svgaCard == SVGA_TsengET4K) && et4k_highcolor_half_pixel_rate())
02610             clock /= 2;
02611     } else {
02612         // not EGAVGA_ARCH
02613         vga.draw.split_line = 0x10000;  // don't care
02614 
02615         htotal = vga.other.htotal + 1u;
02616         hdend = vga.other.hdend;
02617         hbstart = hdend;
02618         hbend = htotal;
02619         hrstart = vga.other.hsyncp;
02620         hrend = hrstart + (vga.other.hsyncw) ;
02621 
02622         vga.draw.address_line_total = vga.other.max_scanline + 1u;
02623 
02624         if (machine == MCH_MCGA) {
02625             // mode 0x11 and 0x13 on real hardware have max_scanline == 0,
02626             // will need doubling again to work properly.
02627             if (vga.other.mcga_mode_control & 3)
02628                 vga.draw.address_line_total *= 2;
02629 
02630             // 80x25 has horizontal timings like 40x25, but the hardware
02631             // knows if 80x25 text is intended by bit 0 of port 3D8h and
02632             // adjusts appropriately.
02633             //
02634             // The BIOS will only set bit 0 for modes 2 and 3 (80x25) and
02635             // will not set it for any other mode.
02636             if (vga.tandy.mode_control & 1) {
02637                 htotal *= 2;
02638                 hdend *= 2;
02639                 hbstart *= 2;
02640                 hbend *= 2;
02641                 hrstart *= 2;
02642                 hrend *= 2;
02643             }
02644         }
02645 
02646         vtotal = vga.draw.address_line_total * (vga.other.vtotal+1u)+vga.other.vadjust;
02647         vdend = vga.draw.address_line_total * vga.other.vdend;
02648         vrstart = vga.draw.address_line_total * vga.other.vsyncp;
02649         vrend = vrstart + 16; // vsync width is fixed to 16 lines on the MC6845 TODO Tandy
02650         vbstart = vdend;
02651         vbend = vtotal;
02652 
02653         switch (machine) {
02654         case MCH_AMSTRAD:
02655             clock=(16000000/2)/8;
02656             break;
02657         case MCH_CGA:
02658         case TANDY_ARCH_CASE:
02659             clock = (PIT_TICK_RATE*12)/8;
02660             if (!(vga.tandy.mode_control & 1)) clock /= 2;
02661             break;
02662         case MCH_MCGA:
02663             clock = 25175000 / 2 / 8;//FIXME: Guess. Verify
02664             if (!(vga.tandy.mode_control & 1)) clock /= 2;
02665             break;
02666         case MCH_MDA:
02667         case MCH_HERC:
02668             clock=16000000/8;
02669             if (vga.herc.mode_control & 0x2) clock/=2;
02670             break;
02671         default:
02672             clock = (PIT_TICK_RATE*12);
02673             break;
02674         }
02675         vga.draw.delay.hdend = hdend*1000.0/clock; //in milliseconds
02676     }
02677 #if C_DEBUG
02678     LOG(LOG_VGA,LOG_NORMAL)("h total %3d end %3d blank (%3d/%3d) retrace (%3d/%3d)",
02679         (int)htotal, (int)hdend, (int)hbstart, (int)hbend, (int)hrstart, (int)hrend );
02680     LOG(LOG_VGA,LOG_NORMAL)("v total %3d end %3d blank (%3d/%3d) retrace (%3d/%3d)",
02681         (int)vtotal, (int)vdend, (int)vbstart, (int)vbend, (int)vrstart, (int)vrend );
02682 #endif
02683     if (!htotal) return;
02684     if (!vtotal) return;
02685     
02686     // The screen refresh frequency
02687     double fps;
02688     extern double vga_force_refresh_rate;
02689     if (vga_force_refresh_rate > 0) {
02690         /* force the VGA refresh rate by setting fps and bending the clock to our will */
02691         LOG(LOG_VGA,LOG_NORMAL)("VGA forced refresh rate in effect, %.3f",vga_force_refresh_rate);
02692         fps=vga_force_refresh_rate;
02693         clock=((double)(vtotal*htotal))*fps;
02694     }
02695     else {
02696         // The screen refresh frequency
02697         fps=(double)clock/(vtotal*htotal);
02698         LOG(LOG_VGA,LOG_NORMAL)("VGA refresh rate is now, %.3f",fps);
02699     }
02700 
02701     /* clip display end to stay within vtotal ("Monolith" demo part 4 320x570 mode fix) */
02702     if (vdend > vtotal) {
02703         LOG(LOG_VGA,LOG_WARN)("VGA display end greater than vtotal!");
02704         vdend = vtotal;
02705     }
02706 
02707     // Horizontal total (that's how long a line takes with whistles and bells)
02708     vga.draw.delay.htotal = htotal*1000.0/clock; //in milliseconds
02709     // Start and End of horizontal blanking
02710     vga.draw.delay.hblkstart = hbstart*1000.0/clock; //in milliseconds
02711     vga.draw.delay.hblkend = hbend*1000.0/clock; 
02712     // Start and End of horizontal retrace
02713     vga.draw.delay.hrstart = hrstart*1000.0/clock;
02714     vga.draw.delay.hrend = hrend*1000.0/clock;
02715     // Start and End of vertical blanking
02716     vga.draw.delay.vblkstart = vbstart * vga.draw.delay.htotal;
02717     vga.draw.delay.vblkend = vbend * vga.draw.delay.htotal;
02718     // Start and End of vertical retrace pulse
02719     vga.draw.delay.vrstart = vrstart * vga.draw.delay.htotal;
02720     vga.draw.delay.vrend = vrend * vga.draw.delay.htotal;
02721 
02722     // Vertical blanking tricks
02723     vblank_skip = 0;
02724     if ((IS_VGA_ARCH || IS_PC98_ARCH) && !ignore_vblank_wraparound) { // others need more investigation
02725         if (vbstart < vtotal) { // There will be no blanking at all otherwise
02726             if (vbend > vtotal) {
02727                 // blanking wraps to the start of the screen
02728                 vblank_skip = vbend&0x7f;
02729                 
02730                 // on blanking wrap to 0, the first line is not blanked
02731                 // this is used by the S3 BIOS and other S3 drivers in some SVGA modes
02732                 if ((vbend&0x7f)==1) vblank_skip = 0;
02733                 
02734                 // it might also cut some lines off the bottom
02735                 if (vbstart < vdend) {
02736                     vdend = vbstart;
02737                 }
02738                 LOG(LOG_VGA,LOG_WARN)("Blanking wrap to line %d", (int)vblank_skip);
02739             } else if (vbstart<=1) {
02740                 // blanking is used to cut lines at the start of the screen
02741                 vblank_skip = vbend;
02742                 LOG(LOG_VGA,LOG_WARN)("Upper %d lines of the screen blanked", (int)vblank_skip);
02743             } else if (vbstart < vdend) {
02744                 if (vbend < vdend) {
02745                     // the game wants a black bar somewhere on the screen
02746                     LOG(LOG_VGA,LOG_WARN)("Unsupported blanking: line %d-%d",(int)vbstart,(int)vbend);
02747                 } else {
02748                     // blanking is used to cut off some lines from the bottom
02749                     vdend = vbstart;
02750                 }
02751             }
02752             vdend -= vblank_skip;
02753         }
02754     }
02755     vga.draw.vblank_skip = vblank_skip;
02756 
02757     // Display end
02758     vga.draw.delay.vdend = vdend * vga.draw.delay.htotal;
02759 
02760     // EGA frequency dependent monitor palette
02761     if (machine == MCH_EGA) {
02762         if (vga.misc_output & 1) {
02763             // EGA card is in color mode
02764             if ((1.0f/vga.draw.delay.htotal) > 19.0f) {
02765                 // 64 color EGA mode
02766                 VGA_ATTR_SetEGAMonitorPalette(EGA);
02767             } else {
02768                 // 16 color CGA mode compatibility
02769                 VGA_ATTR_SetEGAMonitorPalette(CGA);
02770             }
02771         } else {
02772             // EGA card in monochrome mode
02773             // It is not meant to be autodetected that way, you either
02774             // have a monochrome or color monitor connected and
02775             // the EGA switches configured appropriately.
02776             // But this would only be a problem if a program sets 
02777             // the adapter to monochrome mode and still expects color output.
02778             // Such a program should be shot to the moon...
02779             VGA_ATTR_SetEGAMonitorPalette(MONO);
02780         }
02781     }
02782 
02783     /*
02784       6  Horizontal Sync Polarity. Negative if set
02785       7  Vertical Sync Polarity. Negative if set
02786          Bit 6-7 indicates the number of lines on the display:
02787             1:  400, 2: 350, 3: 480
02788     */
02789     //Try to determine the pixel size, aspect correct is based around square pixels
02790 
02791     //Base pixel width around 100 clocks horizontal
02792     //For 9 pixel text modes this should be changed, but we don't support that anyway :)
02793     //Seems regular vga only listens to the 9 char pixel mode with character mode enabled
02794     //Base pixel height around vertical totals of modes that have 100 clocks horizontal
02795     //Different sync values gives different scaling of the whole vertical range
02796     //VGA monitor just seems to thighten or widen the whole vertical range
02797 
02798     vga.draw.resizing=false;
02799     vga.draw.has_split=false;
02800     vga.draw.vret_triggered=false;
02801 
02802     //Check to prevent useless black areas
02803     if (hbstart<hdend) hdend=hbstart;
02804     if ((!(IS_VGA_ARCH || IS_PC98_ARCH)) && (vbstart<vdend)) vdend=vbstart;
02805 
02806     Bitu width=hdend;
02807     Bitu height=vdend;
02808 
02809     if (IS_EGAVGA_ARCH || IS_PC98_ARCH) {
02810         vga.draw.address_line_total=(vga.crtc.maximum_scan_line&0x1fu)+1u;
02811         switch(vga.mode) {
02812         case M_CGA16:
02813         case M_CGA2:
02814         case M_CGA4:
02815         case M_PC98:
02816         case M_TEXT:
02817             // these use line_total internal
02818             // doublescanning needs to be emulated by renderer doubleheight
02819             // EGA has no doublescanning bit at 0x80
02820             if (vga.crtc.maximum_scan_line&0x80) {
02821                 // vga_draw only needs to draw every second line
02822                 height /= 2;
02823             }
02824             break;
02825         default:
02826             vga.draw.doublescan_effect = vga.draw.doublescan_set;
02827 
02828             if (vga.crtc.maximum_scan_line & 0x80)
02829                 vga.draw.address_line_total *= 2;
02830 
02831             /* if doublescan=false and line_total is even, then halve the height.
02832              * the VGA raster scan will skip every other line to accomodate that. */
02833             if ((!vga.draw.doublescan_effect) && (vga.draw.address_line_total & 1) == 0)
02834                 height /= 2;
02835             else
02836                 vga.draw.doublescan_effect = true;
02837 
02838             break;
02839         }
02840     }
02841 
02842     if (!vga.draw.doublescan_effect)
02843         vga.draw.render_max = 2; /* count all lines but render only every other line */
02844 
02845     //Set the bpp
02846     Bitu bpp;
02847     switch (vga.mode) {
02848     case M_LIN15:
02849         bpp = 15;
02850         break;
02851     case M_LIN16:
02852         bpp = 16;
02853         break;
02854     case M_LIN24:
02855     case M_LIN32:
02856         bpp = 32;
02857         break;
02858     default:
02859         bpp = 8;
02860         break;
02861     }
02862     vga.draw.linear_base = vga.mem.linear;
02863     vga.draw.linear_mask = vga.mem.memmask;
02864 
02865     /* Some games and plenty of demoscene productions like to rely on
02866      * the fact that the standard VGA modes wrap around at 256KB even
02867      * on SVGA hardware. Without this check, those demos will show
02868      * credits that scroll upward to blackness before "popping" back
02869      * onto the screen. */
02870     if (IS_VGA_ARCH) {
02871         /* NTS: S3 emulation ties "compatible chain4" to CRTC register 31 bit 3 which controls
02872          *      whether access to > 256KB of video RAM is enabled, which is why it's used here */
02873         if (vga.config.compatible_chain4 || svgaCard == SVGA_None)
02874             vga.draw.linear_mask &= 0x3FFFF;
02875     }
02876     else if (IS_EGA_ARCH) {
02877         vga.draw.linear_mask &= 0x3FFFF;
02878     }
02879 
02880     vga.draw.planar_mask = vga.draw.linear_mask >> 2;
02881 
02882     Bitu pix_per_char = 8;
02883     switch (vga.mode) {
02884     case M_VGA:
02885         // hack for tgr2 -hc high color mode demo
02886         if (vga.dac.reg02==0x80) {
02887             bpp=16;
02888             vga.mode=M_LIN16;
02889             VGA_SetupHandlers();
02890             VGA_DrawLine=VGA_Draw_LIN16_Line_2x;
02891             pix_per_char = 4;
02892             break;
02893         }
02894         bpp = 32;
02895         pix_per_char = 4;
02896         if (vga.mode == M_VGA && (svgaCard == SVGA_TsengET3K || svgaCard == SVGA_TsengET4K)) {
02897             /* ET4000 chipsets handle the chained mode (in my opinion) with sanity and we can scan linearly for it.
02898              * Chained VGA mode maps planar byte addr = (addr >> 2) and plane = (addr & 3) */
02899             VGA_DrawLine = VGA_Draw_Xlat32_Linear_Line;
02900         }
02901         else if (machine == MCH_MCGA) {
02902             pix_per_char = 8;
02903             VGA_DrawLine = VGA_Draw_Xlat32_Linear_Line;
02904             vga.tandy.draw_base = vga.mem.linear;
02905             vga.draw.address_line_total = 1;
02906         }
02907         else {
02908             /* other SVGA chipsets appear to handle chained mode by writing 4 pixels to 4 planes, and showing
02909              * only every 4th byte, which is why when you switch the CRTC to byte or word mode on these chipsets,
02910              * you see 16-pixel groups with 4 pixels from the chained display you expect followed by 12 pixels
02911              * of whatever contents of memory remain. but when you unchain the bitplanes the card will allow
02912              * "planar" writing to all 16 pixels properly. Chained VGA maps like planar byte = (addr & ~3) and
02913              * plane = (addr & 3) */
02914             VGA_DrawLine = VGA_Draw_Xlat32_VGA_CRTC_bmode_Line;
02915         }
02916         break;
02917     case M_LIN8:
02918         bpp = 32;
02919         VGA_DrawLine = VGA_Draw_Xlat32_Linear_Line;
02920 
02921         if ((vga.s3.reg_3a & 0x10)||(svgaCard!=SVGA_S3Trio))
02922             pix_per_char = 8; // TODO fiddle out the bits for other svga cards
02923         else
02924             pix_per_char = 4;
02925 
02926         VGA_ActivateHardwareCursor();
02927         break;
02928     case M_LIN24:
02929     case M_LIN32:
02930         VGA_ActivateHardwareCursor();
02931         break;
02932     case M_LIN15:
02933     case M_LIN16:
02934         pix_per_char = 4; // 15/16 bpp modes double the horizontal values
02935         VGA_ActivateHardwareCursor();
02936         break;
02937     case M_PACKED4:
02938         bpp = 32;
02939         vga.draw.blocks = width;
02940         VGA_DrawLine = VGA_Draw_VGA_Packed4_Xlat32_Line;
02941         break;
02942     case M_LIN4:
02943     case M_EGA:
02944         vga.draw.blocks = width;
02945 
02946         if (IS_EGA_ARCH) {
02947             VGA_DrawLine = EGA_Draw_VGA_Planar_Xlat8_Line;
02948             bpp = 8;
02949         }
02950         else {
02951             VGA_DrawLine = VGA_Draw_VGA_Planar_Xlat32_Line;
02952             bpp = 32;
02953         }
02954         break;
02955     case M_CGA16:
02956         vga.draw.blocks=width*2;
02957         pix_per_char = 16;
02958         VGA_DrawLine=VGA_Draw_CGA16_Line;
02959         break;
02960     case M_CGA4:
02961         if (IS_EGA_ARCH) {
02962             vga.draw.blocks=width;
02963             VGA_DrawLine=EGA_Draw_2BPP_Line_as_EGA;
02964             bpp = 8;
02965         }
02966         else if (IS_EGAVGA_ARCH || IS_PC98_ARCH) {
02967             vga.draw.blocks=width;
02968             VGA_DrawLine=VGA_Draw_2BPP_Line_as_VGA;
02969             bpp = 32;
02970         }
02971         else if (machine == MCH_MCGA) {
02972             vga.draw.blocks=width*2;
02973             VGA_DrawLine=VGA_Draw_2BPP_Line_as_MCGA;
02974             bpp = 32;
02975 
02976             /* MCGA CGA-compatible modes will always refer to the last half of the 64KB of RAM */
02977             vga.tandy.draw_base = vga.mem.linear + 0x8000;
02978         }
02979         else {
02980             vga.draw.blocks=width*2;
02981             VGA_DrawLine=VGA_Draw_2BPP_Line;
02982         }
02983         break;
02984     case M_CGA2:
02985         if (IS_EGA_ARCH) {
02986             vga.draw.blocks=width;
02987             VGA_DrawLine=EGA_Draw_1BPP_Line_as_EGA;
02988             bpp = 8;
02989         }
02990         else if (IS_EGAVGA_ARCH || IS_PC98_ARCH) {
02991             vga.draw.blocks=width;
02992             VGA_DrawLine=VGA_Draw_1BPP_Line_as_VGA;
02993             bpp = 32;
02994         }
02995         else if (machine == MCH_MCGA) {
02996             vga.draw.blocks=width;
02997             VGA_DrawLine=VGA_Draw_1BPP_Line_as_MCGA;
02998             pix_per_char = 16;
02999             bpp = 32;
03000 
03001             /* MCGA CGA-compatible modes will always refer to the last half of the 64KB of RAM */
03002             if (vga.other.mcga_mode_control & 3) // 320x200 256-color or 640x480 2-color
03003                 vga.tandy.draw_base = vga.mem.linear;
03004             else
03005                 vga.tandy.draw_base = vga.mem.linear + 0x8000;
03006 
03007             if (vga.other.mcga_mode_control & 2) // 640x480 2-color
03008                 vga.draw.address_line_total = 1;
03009         }
03010         else {
03011             vga.draw.blocks=width*2;
03012             VGA_DrawLine=VGA_Draw_1BPP_Line;
03013         }
03014         break;
03015     case M_PC98:
03016         vga.draw.blocks=width;
03017         vga.draw.char9dot = false;
03018         VGA_DrawLine=VGA_PC98_Xlat32_Draw_Line;
03019         bpp = 32;
03020         break;
03021     case M_TEXT:
03022         vga.draw.blocks=width;
03023         // if char9_set is true, allow 9-pixel wide fonts
03024         if ((vga.seq.clocking_mode&0x01) || !vga.draw.char9_set) {
03025             // 8-pixel wide
03026             pix_per_char = 8;
03027             vga.draw.char9dot = false;
03028         } else {
03029             // 9-pixel wide
03030             pix_per_char = 9;
03031             vga.draw.char9dot = true;
03032         }
03033 
03034         if (IS_EGA_ARCH) {
03035             VGA_DrawLine = EGA_TEXT_Xlat8_Draw_Line;
03036             bpp = 8;
03037         }
03038         else {
03039             VGA_DrawLine = VGA_TEXT_Xlat32_Draw_Line;
03040             bpp = 32;
03041         }
03042         break;
03043     case M_HERC_GFX:
03044         vga.draw.blocks=width*2;
03045         pix_per_char = 16;
03046         if (vga.herc.blend) VGA_DrawLine=VGA_Draw_1BPP_Blend_Line;
03047         else VGA_DrawLine=VGA_Draw_1BPP_Line;
03048         break;
03049     case M_TANDY2:
03050         if (((machine==MCH_PCJR)&&(vga.tandy.gfx_control & 0x8)) ||
03051             (vga.tandy.mode_control & 0x10)) {
03052             vga.draw.blocks=width * 8;
03053             pix_per_char = 16;
03054         } else {
03055             vga.draw.blocks=width * 4;
03056             pix_per_char = 8;
03057         }
03058         VGA_DrawLine=VGA_Draw_1BPP_Line;
03059 
03060         /* MCGA CGA-compatible modes will always refer to the last half of the 64KB of RAM */
03061         if (machine == MCH_MCGA) {
03062             VGA_DrawLine=VGA_Draw_1BPP_Line_as_MCGA;
03063             vga.draw.blocks=width * 2;
03064             pix_per_char = 16;
03065             bpp = 32;
03066 
03067             /* MCGA CGA-compatible modes will always refer to the last half of the 64KB of RAM */
03068             if (vga.other.mcga_mode_control & 3) // 320x200 256-color or 640x480 2-color
03069                 vga.tandy.draw_base = vga.mem.linear;
03070             else
03071                 vga.tandy.draw_base = vga.mem.linear + 0x8000;
03072 
03073             if (vga.other.mcga_mode_control & 2) // 640x480 2-color
03074                 vga.draw.address_line_total = 1;
03075         }
03076 
03077         break;
03078     case M_TANDY4:
03079         vga.draw.blocks=width * 2;
03080         pix_per_char = 8;
03081         if ((machine==MCH_TANDY && (vga.tandy.gfx_control & 0x8)) ||
03082             (machine==MCH_PCJR && (vga.tandy.mode_control==0x0b)))
03083             VGA_DrawLine=VGA_Draw_2BPPHiRes_Line;
03084         else VGA_DrawLine=VGA_Draw_2BPP_Line;
03085 
03086         /* MCGA CGA-compatible modes will always refer to the last half of the 64KB of RAM */
03087         if (machine == MCH_MCGA) {
03088             vga.tandy.draw_base = vga.mem.linear + 0x8000;
03089             vga.draw.blocks=width * 2;
03090             VGA_DrawLine=VGA_Draw_2BPP_Line_as_MCGA;
03091             bpp = 32;
03092         }
03093 
03094         break;
03095     case M_TANDY16:
03096         if (vga.tandy.mode_control & 0x1) {
03097             if (( machine==MCH_TANDY ) && ( vga.tandy.mode_control & 0x10 )) {
03098                 vga.draw.blocks=width*4;
03099                 pix_per_char = 8;
03100             } else {
03101                 vga.draw.blocks=width*2;
03102                 pix_per_char = 4;
03103             }
03104             VGA_DrawLine=VGA_Draw_4BPP_Line;
03105         } else {
03106             vga.draw.blocks=width*2;
03107             pix_per_char = 8;
03108             VGA_DrawLine=VGA_Draw_4BPP_Line_Double;
03109         }
03110         break;
03111     case M_TANDY_TEXT: /* Also CGA */
03112         vga.draw.blocks=width;
03113         if (machine==MCH_CGA /*&& !doublewidth*/ && enableCGASnow && (vga.tandy.mode_control & 1)/*80-column mode*/)
03114             VGA_DrawLine=VGA_CGASNOW_TEXT_Draw_Line; /* Alternate version that emulates CGA snow */
03115         else
03116             VGA_DrawLine=VGA_TEXT_Draw_Line;
03117 
03118         /* MCGA CGA-compatible modes will always refer to the last half of the 64KB of RAM */
03119         if (machine == MCH_MCGA) {
03120             vga.tandy.draw_base = vga.mem.linear + 0x8000;
03121             VGA_DrawLine = MCGA_TEXT_Draw_Line;
03122             bpp = 32;
03123         }
03124 
03125         break;
03126     case M_HERC_TEXT:
03127         vga.draw.blocks=width;
03128         VGA_DrawLine=VGA_TEXT_Herc_Draw_Line;
03129         break;
03130     case M_AMSTRAD: // Probably OK?
03131         pix_per_char = 16;
03132         vga.draw.blocks=width*2;
03133         VGA_DrawLine=VGA_Draw_AMS_4BPP_Line;
03134 //      VGA_DrawLine=VGA_Draw_4BPP_Line;
03135 /*      doubleheight=true;
03136         vga.draw.blocks = 2*width; width<<=4;
03137         vga.draw.linear_base = vga.mem.linear + VGA_CACHE_OFFSET;
03138         vga.draw.linear_mask = 512 * 1024 - 1; */
03139         break;
03140     default:
03141         LOG(LOG_VGA,LOG_ERROR)("Unhandled VGA mode %d while checking for resolution",vga.mode);
03142         break;
03143     }
03144     width *= pix_per_char;
03145     VGA_CheckScanLength();
03146 
03147     /* for MCGA, need to "double scan" the screen in some cases */
03148     if (vga.other.mcga_mode_control & 2) { // 640x480 2-color
03149         height *= 2;
03150         mcga_double_scan = true;
03151     }
03152     else if (machine == MCH_MCGA && vga.mode == M_TANDY_TEXT) { // MCGA text mode
03153         height *= 2;
03154         mcga_double_scan = true;
03155         vga.draw.address_line_total *= 2;
03156     }
03157     else {
03158         mcga_double_scan = false;
03159     }
03160     
03161     vga.draw.lines_total=height;
03162     vga.draw.line_length = width * ((bpp + 1) / 8);
03163     vga.draw.clock = clock;
03164 
03165     double vratio = ((double)width)/(double)height; // ratio if pixels were square
03166 
03167     // the picture ratio factor
03168     double scanratio =  ((double)hdend/(double)(htotal-(hrend-hrstart)))/
03169                         ((double)vdend/(double)(vtotal-(vrend-vrstart)));
03170     double scanfield_ratio = 4.0/3.0;
03171     switch(machine) {
03172         case MCH_CGA:
03173         case MCH_MCGA:
03174         case MCH_PCJR:
03175         case MCH_TANDY:
03176             scanfield_ratio = 1.382;
03177             break;
03178         case MCH_MDA:
03179         case MCH_HERC:
03180             scanfield_ratio = 1.535;
03181             break;
03182         case MCH_EGA:
03183             switch (vga.misc_output >> 6) {
03184             case 0: // 200 lines:
03185                 scanfield_ratio = 1.085; // DOSBugs
03186                 //scanfield_ratio = 1.375; // IBM EGA BIOS
03187                 break;
03188             case 2: // 350 lines
03189                 // TODO monitors seem to display this with a bit of black borders on top and bottom
03190                 scanfield_ratio = 1.45;
03191                 break;
03192             default:
03193                 // other cases are undefined for EGA - scale them to 4:3
03194                 scanfield_ratio = (4.0/3.0) / scanratio;
03195                 break;
03196             }
03197             break;
03198 
03199         default: // VGA
03200             switch (vga.misc_output >> 6) {
03201             case 0: // VESA: "OTHER" scanline amount
03202                 scanfield_ratio = (4.0/3.0) / scanratio;
03203                 break;
03204             case 1: // 400 lines
03205                 scanfield_ratio = 1.312;
03206                 break;
03207             case 2: // 350 lines
03208                 scanfield_ratio = 1.249;
03209                 break;
03210             case 3: // 480 lines
03211                 scanfield_ratio = 1.345;
03212                 break;
03213             }
03214             break;
03215     }
03216     // calculate screen ratio
03217     double screenratio = scanratio * scanfield_ratio;
03218 
03219     // override screenratio for certain cases:
03220     if (vratio == 1.6) screenratio = 4.0 / 3.0;
03221     else if (vratio == 0.8) screenratio = 4.0 / 3.0;
03222     else if (vratio == 3.2) screenratio = 4.0 / 3.0;
03223     else if (vratio == (4.0/3.0)) screenratio = 4.0 / 3.0;
03224     else if (vratio == (2.0/3.0)) screenratio = 4.0 / 3.0;
03225     else if ((width >= 800)&&(height>=600)) screenratio = 4.0 / 3.0;
03226 
03227 #if C_DEBUG
03228             LOG(LOG_VGA,LOG_NORMAL)("screen: %1.3f, scanfield: %1.3f, scan: %1.3f, vratio: %1.3f",
03229                 screenratio, scanfield_ratio, scanratio, vratio);
03230 #endif
03231 
03232     bool fps_changed = false;
03233 
03234 #if C_DEBUG
03235     LOG(LOG_VGA,LOG_NORMAL)("h total %2.5f (%3.2fkHz) blank(%02.5f/%02.5f) retrace(%02.5f/%02.5f)",
03236         vga.draw.delay.htotal,(1.0/vga.draw.delay.htotal),
03237         vga.draw.delay.hblkstart,vga.draw.delay.hblkend,
03238         vga.draw.delay.hrstart,vga.draw.delay.hrend);
03239     LOG(LOG_VGA,LOG_NORMAL)("v total %2.5f (%3.2fHz) blank(%02.5f/%02.5f) retrace(%02.5f/%02.5f)",
03240         vga.draw.delay.vtotal,(1000.0/vga.draw.delay.vtotal),
03241         vga.draw.delay.vblkstart,vga.draw.delay.vblkend,
03242         vga.draw.delay.vrstart,vga.draw.delay.vrend);
03243 
03244     LOG(LOG_VGA,LOG_NORMAL)("video clock: %3.2fMHz mode %s",
03245         oscclock/1000000.0, mode_texts[vga.mode]);
03246 #endif
03247 
03248     // need to change the vertical timing?
03249     if (vga_mode_time_base < 0 || fabs(vga.draw.delay.vtotal - 1000.0 / fps) > 0.0001)
03250         fps_changed = true;
03251 
03252     // need to resize the output window?
03253     if ((width != vga.draw.width) ||
03254         (height != vga.draw.height) ||
03255         (fabs(screenratio - vga.draw.screen_ratio) > 0.0001) ||
03256         (vga.draw.bpp != bpp) || fps_changed) {
03257 
03258         VGA_KillDrawing();
03259 
03260         vga.draw.width = width;
03261         vga.draw.height = height;
03262         vga.draw.screen_ratio = screenratio;
03263         vga.draw.bpp = bpp;
03264 #if C_DEBUG
03265         LOG(LOG_VGA,LOG_NORMAL)("%dx%d, %3.2fHz, %dbpp, screen %1.3f",(int)width,(int)height,fps,(int)bpp,screenratio);
03266 #endif
03267         if (!vga.draw.vga_override)
03268             RENDER_SetSize(width,height,bpp,(float)fps,screenratio);
03269 
03270         if (fps_changed) {
03271             vga_mode_time_base = PIC_FullIndex();
03272             vga_mode_frames_since_time_base = 0;
03273             PIC_RemoveEvents(VGA_Other_VertInterrupt);
03274             PIC_RemoveEvents(VGA_VerticalTimer);
03275             PIC_RemoveEvents(VGA_PanningLatch);
03276             PIC_RemoveEvents(VGA_DisplayStartLatch);
03277             vga.draw.delay.vtotal = 1000.0 / fps;
03278             vga.draw.lines_done = vga.draw.lines_total;
03279             vga_fps = fps;
03280             VGA_VerticalTimer(0);
03281         }
03282     }
03283     vga.draw.delay.singleline_delay = (float)vga.draw.delay.htotal;
03284 
03285     if (machine == MCH_HERC || machine == MCH_MDA) {
03286         Herc_Palette();
03287     }
03288     else {
03289         /* FIXME: Why is this required to prevent VGA palette errors with Crystal Dream II?
03290          *        What is this code doing to change the palette prior to this point? */
03291         VGA_DAC_UpdateColorPalette();
03292     }
03293 }
03294 
03295 void VGA_KillDrawing(void) {
03296     PIC_RemoveEvents(VGA_DrawSingleLine);
03297     PIC_RemoveEvents(VGA_DrawEGASingleLine);
03298 }
03299 
03300 void VGA_SetOverride(bool vga_override) {
03301     if (vga.draw.vga_override!=vga_override) {
03302         
03303         if (vga_override) {
03304             VGA_KillDrawing();
03305             vga.draw.vga_override=true;
03306         } else {
03307             vga.draw.vga_override=false;
03308             vga.draw.width=0; // change it so the output window gets updated
03309             VGA_SetupDrawing(0);
03310         }
03311     }
03312 }
03313