DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/vga_draw.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 
00020 #include <string.h>
00021 #include <math.h>
00022 #include <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 bool pc98_display_enable = true;
00087 
00088 extern bool pc98_40col_text;
00089 extern bool vga_3da_polled;
00090 extern bool vga_page_flip_occurred;
00091 extern bool vga_enable_hpel_effects;
00092 extern bool vga_enable_hretrace_effects;
00093 extern unsigned int vga_display_start_hretrace;
00094 extern float hretrace_fx_avg_weight;
00095 extern bool ignore_vblank_wraparound;
00096 extern bool vga_double_buffered_line_compare;
00097 extern bool pc98_crt_mode;      // see port 6Ah command 40h/41h.
00098 
00099 extern bool pc98_31khz_mode;
00100 
00101 void memxor(void *_d,unsigned int byte,size_t count) {
00102     unsigned char *d = (unsigned char*)_d;
00103     while (count-- > 0) *d++ ^= byte;
00104 }
00105 
00106 void memxor_greendotted_16bpp(uint16_t *d,unsigned int count,unsigned int line) {
00107     static const uint16_t greenptrn[2] = { (0x3F << 5), 0 };
00108     line &= 1;
00109     count >>= 1;
00110     while (count-- > 0) {
00111         *d++ ^= greenptrn[line];
00112         *d++ ^= greenptrn[line^1];
00113     }
00114 }
00115 
00116 void memxor_greendotted_32bpp(uint32_t *d,unsigned int count,unsigned int line) {
00117     static const uint32_t greenptrn[2] = { (0xFF << 8), 0 };
00118     line &= 1;
00119     count >>= 2;
00120     while (count-- > 0) {
00121         *d++ ^= greenptrn[line];
00122         *d++ ^= greenptrn[line^1];
00123     }
00124 }
00125 
00126 typedef Bit8u * (* VGA_Line_Handler)(Bitu vidstart, Bitu line);
00127 
00128 static VGA_Line_Handler VGA_DrawLine;
00129 static Bit8u TempLine[SCALER_MAXWIDTH * 4 + 256];
00130 static float hretrace_fx_avg = 0;
00131 
00132 void VGA_MarkCaptureAcquired(void);
00133 void VGA_MarkCaptureInProgress(bool en);
00134 void pc98_update_display_page_ptr(void);
00135 bool VGA_CaptureValidateCurrentFrame(void);
00136 void VGA_CaptureStartNextFrame(void);
00137 void VGA_MarkCaptureRetrace(void);
00138 void VGA_CaptureMarkError(void);
00139 bool VGA_IsCaptureEnabled(void);
00140 bool VGA_IsCapturePending(void);
00141 void VGA_CaptureWriteScanline(const uint8_t *raw);
00142 void VGA_ProcessScanline(const uint8_t *raw);
00143 bool VGA_IsCaptureInProgress(void);
00144 
00145 static Bit8u * VGA_Draw_AMS_4BPP_Line(Bitu vidstart, Bitu line) {
00146     const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00147     const Bit8u *lbase;
00148     Bit32u *draw = (Bit32u *)TempLine;
00149     for (Bitu x=vga.draw.blocks;x>0;x--, vidstart++) {
00150         lbase = &base[ vidstart & (8 * 1024 -1) ];
00151         Bitu val0 = lbase[ 0 ];
00152         Bitu val1 = lbase[ 16384 ];
00153         Bitu val2 = lbase[ 32768 ];
00154         Bitu val3 = lbase[ 49152 ];
00155         
00156         *draw++=( ( CGA_2_Table[ val0 >> 4 ] << 0 ) | 
00157             ( CGA_2_Table[ val1 >> 4 ] << 1 ) |
00158             ( CGA_2_Table[ val2 >> 4 ] << 2 ) |
00159             ( CGA_2_Table[ val3 >> 4 ] << 3 ) ) & vga.amstrad.mask_plane;
00160         *draw++=( ( CGA_2_Table[ val0 & 0x0F ] << 0 ) | 
00161             ( CGA_2_Table[ val1 & 0x0F ] << 1 ) |
00162             ( CGA_2_Table[ val2 & 0x0F ] << 2 ) |
00163             ( CGA_2_Table[ val3 & 0x0F ] << 3 ) ) & vga.amstrad.mask_plane;
00164     }
00165     return TempLine;
00166 }
00167 
00168 struct vsync_state vsync;
00169 
00170 float uservsyncjolt=0.0f;
00171 
00172 VGA_Vsync vsyncmode_current = VS_Off;
00173 
00174 void VGA_VsyncUpdateMode(VGA_Vsync vsyncmode) {
00175     vsyncmode_current = vsyncmode;
00176 
00177     mainMenu.get_item("vsync_off").check(vsyncmode_current == VS_Off).refresh_item(mainMenu);
00178     mainMenu.get_item("vsync_on").check(vsyncmode_current == VS_On).refresh_item(mainMenu);
00179     mainMenu.get_item("vsync_force").check(vsyncmode_current == VS_Force).refresh_item(mainMenu);
00180     mainMenu.get_item("vsync_host").check(vsyncmode_current == VS_Host).refresh_item(mainMenu);
00181 
00182     switch(vsyncmode) {
00183     case VS_Off:
00184         vsync.manual    = false;
00185         vsync.persistent= false;
00186         vsync.faithful  = false;
00187         break;
00188     case VS_On:
00189         vsync.manual    = true;
00190         vsync.persistent= true;
00191         vsync.faithful  = true;
00192         break;
00193     case VS_Force:
00194     case VS_Host:
00195         vsync.manual    = true;
00196         vsync.persistent= true;
00197         vsync.faithful  = false;
00198         break;
00199     default:
00200         LOG_MSG("VGA_VsyncUpdateMode: Invalid mode, using defaults.");
00201         vsync.manual    = false;
00202         vsync.persistent= false;
00203         vsync.faithful  = false;
00204         break;
00205     }
00206 }
00207 
00208 void VGA_TweakUserVsyncOffset(float val) { uservsyncjolt = val; }
00209 
00210 void VGA_Draw2_Recompute_CRTC_MaskAdd(void) {
00211     if (IS_PC98_ARCH) {
00212         // nothing yet
00213     }
00214     else if (IS_EGAVGA_ARCH) {
00215         /* mem masking can be generalized for ALL VGA/SVGA modes */
00216         size_t new_mask = vga.mem.memmask >> (2ul + vga.config.addr_shift);
00217         size_t new_add = 0;
00218 
00219         if (vga.config.compatible_chain4 || svgaCard == SVGA_None)
00220             new_mask &= 0xFFFFul >> vga.config.addr_shift; /* 64KB planar (256KB linear when byte mode) */
00221 
00222         /* CGA/Hercules compatible interlacing, unless SVGA graphics mode.
00223          * Note that ET4000 and ET3000 emulation will NOT set compatible_chain4 */
00224         if (vga.config.compatible_chain4 || svgaCard == SVGA_None || svgaCard == SVGA_TsengET3K || svgaCard == SVGA_TsengET4K) {
00225             /* MAP13: If zero, bit 13 is taken from bit 0 of row scan counter (CGA compatible) */
00226             /* MAP14: If zero, bit 14 is taken from bit 1 of row scan counter (Hercules compatible) */
00227             if ((vga.crtc.mode_control & 3u) != 3u) {
00228                 const unsigned int shift = 13u - vga.config.addr_shift;
00229                 const unsigned char mask = (vga.crtc.mode_control & 3u) ^ 3u;
00230 
00231                 new_mask &= (size_t)(~(size_t(mask) << shift));
00232                 new_add  += (size_t)(vga.draw_2[0].vert.current_char_pixel & mask) << shift;
00233             }
00234         }
00235 
00236         /* 4 bitplanes are represented in emulation as 32 bits per planar byte */
00237         vga.draw_2[0].draw_base = vga.mem.linear;
00238         vga.draw_2[0].crtc_mask = (unsigned int)new_mask;  // 8KB character clocks (16KB bytes)
00239         vga.draw_2[0].crtc_add = (unsigned int)new_add;
00240     }
00241     else if (machine == MCH_HERC) {
00242         vga.draw_2[0].draw_base = vga.tandy.mem_base;
00243 
00244         if (vga.herc.mode_control & 2) { /* graphics */
00245             vga.draw_2[0].crtc_mask = 0xFFFu;  // 4KB character clocks (8KB bytes)
00246             vga.draw_2[0].crtc_add = (vga.draw_2[0].vert.current_char_pixel & 3u) << 12u;
00247         }
00248         else { /* text */
00249             vga.draw_2[0].crtc_mask = 0x7FFu;  // 2KB character clocks (4KB bytes)
00250             vga.draw_2[0].crtc_add = 0;
00251         }
00252     }
00253     else if (machine == MCH_MDA) {
00254         /* MDA/Hercules is emulated as 16 bits per character clock */
00255         vga.draw_2[0].draw_base = vga.mem.linear;
00256         vga.draw_2[0].crtc_mask = 0x7FFu;  // 2KB character clocks (4KB bytes)
00257         vga.draw_2[0].crtc_add = 0;
00258     }
00259     else {
00260         /* TODO: PCjr/Tandy 16-color extended modes */
00261 
00262         /* CGA/MCGA/PCJr/Tandy is emulated as 16 bits per character clock */
00263         /* PCJr uses system memory < 128KB for video memory.
00264          * Tandy has an alternate location as well. */
00265         if (machine == MCH_TANDY || machine == MCH_PCJR)
00266             vga.draw_2[0].draw_base = vga.tandy.mem_base;
00267         else
00268             vga.draw_2[0].draw_base = vga.mem.linear;
00269 
00270         if (vga.tandy.mode_control & 0x2) { /*graphics*/
00271             vga.draw_2[0].crtc_mask = 0xFFFu;  // 4KB character clocks (8KB bytes)
00272             vga.draw_2[0].crtc_add = (vga.draw_2[0].vert.current_char_pixel & 1u) << 12u;
00273         }
00274         else { /*text*/
00275             vga.draw_2[0].crtc_mask = 0x1FFFu;  // 8KB character clocks (16KB bytes)
00276             vga.draw_2[0].crtc_add = 0;
00277         }
00278     }
00279 }
00280 
00281 static Bit8u * VGA_Draw_1BPP_Line(Bitu vidstart, Bitu line) {
00282     const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00283     Bit32u *draw = (Bit32u *)TempLine;
00284     for (Bitu x=vga.draw.blocks;x>0;x--, vidstart++) {
00285         Bitu val = base[vidstart & (8 * 1024 -1)];
00286         *draw++=CGA_2_Table[val >> 4];
00287         *draw++=CGA_2_Table[val & 0xf];
00288     }
00289     return TempLine;
00290 }
00291 
00292 static Bit8u * VGA_Draw_1BPP_Blend_Line(Bitu vidstart, Bitu line) {
00293     const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00294     Bit32u *draw = (Bit32u *)TempLine;
00295     Bitu carry = 0;
00296     for (Bitu x=vga.draw.blocks;x>0;x--, vidstart++) {
00297         Bitu val1 = base[vidstart & (8 * 1024 -1)];
00298         Bitu val2 = (val1 >> 1) + carry;
00299         carry = (val1 & 1) << 7;
00300         *draw++=CGA_2_Table[val1 >> 4] + CGA_2_Table[val2 >> 4];
00301         *draw++=CGA_2_Table[val1 & 0xf] + CGA_2_Table[val2 & 0xf];
00302     }
00303     return TempLine;
00304 }
00305 
00306 static Bit8u * EGA_Draw_2BPP_Line_as_EGA(Bitu vidstart, Bitu line) {
00307     const Bit32u *base = (Bit32u*)vga.draw.linear_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00308     Bit8u * draw=(Bit8u *)TempLine;
00309     VGA_Latch pixels;
00310 
00311     for (Bitu x=0;x<vga.draw.blocks;x++) {
00312         pixels.d = base[vidstart & vga.tandy.addr_mask];
00313         vidstart += (Bitu)1u << (Bitu)vga.config.addr_shift;
00314 
00315         /* CGA odd/even mode, first plane */
00316         Bitu val=pixels.b[0];
00317         for (Bitu i=0;i < 4;i++,val <<= 2)
00318             *draw++ = vga.attr.palette[(val>>6)&3];
00319 
00320         /* CGA odd/even mode, second plane */
00321         val=pixels.b[1];
00322         for (Bitu i=0;i < 4;i++,val <<= 2)
00323             *draw++ = vga.attr.palette[(val>>6)&3];
00324     }
00325     return TempLine;
00326 }
00327 
00328 static Bit8u * VGA_Draw_2BPP_Line_as_VGA(Bitu vidstart, Bitu line) {
00329     const Bit32u *base = (Bit32u*)vga.draw.linear_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00330     Bit32u * draw=(Bit32u *)TempLine;
00331     VGA_Latch pixels;
00332 
00333     for (Bitu x=0;x<vga.draw.blocks;x++) {
00334         pixels.d = base[vidstart & vga.tandy.addr_mask];
00335         vidstart += (Bitu)1u << (Bitu)vga.config.addr_shift;
00336 
00337         /* CGA odd/even mode, first plane */
00338         Bitu val=pixels.b[0];
00339         for (Bitu i=0;i < 4;i++,val <<= 2)
00340             *draw++ = vga.dac.xlat32[(val>>6)&3];
00341 
00342         /* CGA odd/even mode, second plane */
00343         val=pixels.b[1];
00344         for (Bitu i=0;i < 4;i++,val <<= 2)
00345             *draw++ = vga.dac.xlat32[(val>>6)&3];
00346     }
00347     return TempLine;
00348 }
00349 
00350 static Bit8u * EGA_Draw_1BPP_Line_as_EGA(Bitu vidstart, Bitu line) {
00351     const Bit32u *base = (Bit32u*)vga.draw.linear_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00352     Bit8u * draw=(Bit8u *)TempLine;
00353     VGA_Latch pixels;
00354 
00355     for (Bitu x=0;x<vga.draw.blocks;x++) {
00356         pixels.d = base[vidstart & vga.tandy.addr_mask];
00357         vidstart += (Bitu)1u << (Bitu)vga.config.addr_shift;
00358 
00359         Bitu val=pixels.b[0];
00360         for (Bitu i=0;i < 8;i++,val <<= 1)
00361             *draw++ = vga.attr.palette[(val>>7)&1];
00362     }
00363     return TempLine;
00364 }
00365 
00366 static Bit8u * VGA_Draw_1BPP_Line_as_MCGA(Bitu vidstart, Bitu line) {
00367     const Bit8u *base = (Bit8u*)vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00368     Bit32u * draw=(Bit32u *)TempLine;
00369 
00370     for (Bitu x=0;x<vga.draw.blocks;x++) {
00371         Bitu val = base[vidstart & vga.tandy.addr_mask];
00372         vidstart++;
00373 
00374         for (Bitu i=0;i < 8;i++,val <<= 1)
00375             *draw++ = vga.dac.xlat32[(val>>7)&1];
00376     }
00377     return TempLine;
00378 }
00379 
00380 static Bit8u * VGA_Draw_1BPP_Line_as_VGA(Bitu vidstart, Bitu line) {
00381     const Bit32u *base = (Bit32u*)vga.draw.linear_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00382     Bit32u * draw=(Bit32u *)TempLine;
00383     VGA_Latch pixels;
00384 
00385     for (Bitu x=0;x<vga.draw.blocks;x++) {
00386         pixels.d = base[vidstart & vga.tandy.addr_mask];
00387         vidstart += (Bitu)1u << (Bitu)vga.config.addr_shift;
00388 
00389         Bitu val=pixels.b[0];
00390         for (Bitu i=0;i < 8;i++,val <<= 1)
00391             *draw++ = vga.dac.xlat32[(val>>7)&1];
00392     }
00393     return TempLine;
00394 }
00395 
00396 static Bit8u * VGA_Draw_2BPP_Line_as_MCGA(Bitu vidstart, Bitu line) {
00397     const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00398     Bit32u * draw=(Bit32u *)TempLine;
00399 
00400     for (Bitu x=0;x<vga.draw.blocks;x++) {
00401         unsigned char val = base[vidstart & vga.tandy.addr_mask];
00402         vidstart++;
00403 
00404         for (unsigned int i=0;i < 4;i++,val <<= 2)
00405             *draw++ = vga.dac.xlat32[(val>>6)&3];
00406     }
00407 
00408     return TempLine;
00409 }
00410 
00411 static Bit8u * VGA_Draw_2BPP_Line(Bitu vidstart, Bitu line) {
00412     const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00413     Bit32u * draw=(Bit32u *)TempLine;
00414     for (Bitu x=0;x<vga.draw.blocks;x++) {
00415         Bitu val = base[vidstart & vga.tandy.addr_mask];
00416         vidstart++;
00417         *draw++=CGA_4_Table[val];
00418     }
00419     return TempLine;
00420 }
00421 
00422 static Bit8u * VGA_Draw_2BPPHiRes_Line(Bitu vidstart, Bitu line) {
00423     const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00424     Bit32u * draw=(Bit32u *)TempLine;
00425     for (Bitu x=0;x<vga.draw.blocks;x++) {
00426         Bitu val1 = base[vidstart & vga.tandy.addr_mask];
00427         ++vidstart;
00428         Bitu val2 = base[vidstart & vga.tandy.addr_mask];
00429         ++vidstart;
00430         *draw++=CGA_4_HiRes_Table[(val1>>4)|(val2&0xf0)];
00431         *draw++=CGA_4_HiRes_Table[(val1&0x0f)|((val2&0x0f)<<4)];
00432     }
00433     return TempLine;
00434 }
00435 
00436 static Bitu temp[643]={0};
00437 
00438 static Bit8u * VGA_Draw_CGA16_Line(Bitu vidstart, Bitu line) {
00439     const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00440 #define CGA16_READER(OFF) (base[(vidstart +(OFF))& (8*1024 -1)])
00441     Bit32u * draw=(Bit32u *)TempLine;
00442     //There are 640 hdots in each line of the screen.
00443     //The color of an even hdot always depends on only 4 bits of video RAM.
00444     //The color of an odd hdot depends on 4 bits of video RAM in
00445     //1-hdot-per-pixel modes and 6 bits of video RAM in 2-hdot-per-pixel
00446     //modes. We always assume 6 and use duplicate palette entries in
00447     //1-hdot-per-pixel modes so that we can use the same routine for all
00448     //composite modes.
00449   temp[1] = (CGA16_READER(0) >> 6) & 3;
00450     for(Bitu x = 2; x < 640; x+=2) {
00451         temp[x] = (temp[x-1] & 0xf);
00452         temp[x+1] = (temp[x] << 2) | ((( CGA16_READER(x>>3)) >> (6-(x&6)) )&3);
00453     }
00454     temp[640] = temp[639] & 0xf;
00455     temp[641] = temp[640] << 2;
00456     temp[642] = temp[641] & 0xf;
00457 
00458     Bitu i = 2;
00459     for (Bitu x=0;x<vga.draw.blocks;x++) {
00460         *draw++ = 0xc0708030 | temp[i] | (temp[i+1] << 8) | (temp[i+2] << 16) | (temp[i+3] << 24);
00461         i += 4;
00462         *draw++ = 0xc0708030 | temp[i] | (temp[i+1] << 8) | (temp[i+2] << 16) | (temp[i+3] << 24);
00463         i += 4;
00464     }
00465     return TempLine;
00466 #undef CGA16_READER
00467 }
00468 
00469 static Bit8u * VGA_Draw_4BPP_Line(Bitu vidstart, Bitu line) {
00470     const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00471     Bit8u* draw=TempLine;
00472     Bitu end = vga.draw.blocks*2;
00473     while(end) {
00474         Bit8u byte = base[vidstart & vga.tandy.addr_mask];
00475         *draw++=vga.attr.palette[byte >> 4];
00476         *draw++=vga.attr.palette[byte & 0x0f];
00477         vidstart++;
00478         end--;
00479     }
00480     return TempLine;
00481 }
00482 
00483 static Bit8u * VGA_Draw_4BPP_Line_Double(Bitu vidstart, Bitu line) {
00484     const Bit8u *base = vga.tandy.draw_base + ((line & vga.tandy.line_mask) << vga.tandy.line_shift);
00485     Bit8u* draw=TempLine;
00486     Bitu end = vga.draw.blocks;
00487     while(end) {
00488         Bit8u byte = base[vidstart & vga.tandy.addr_mask];
00489         Bit8u data = vga.attr.palette[byte >> 4];
00490         *draw++ = data; *draw++ = data;
00491         data = vga.attr.palette[byte & 0x0f];
00492         *draw++ = data; *draw++ = data;
00493         vidstart++;
00494         end--;
00495     }
00496     return TempLine;
00497 }
00498 
00499 #if SDL_BYTEORDER == SDL_LIL_ENDIAN && defined(MACOSX) /* Mac OS X Intel builds use a weird RGBA order (alpha in the low 8 bits) */
00500 static inline Bit32u guest_bgr_to_macosx_rgba(const Bit32u x) {
00501     /* guest: XRGB      X   R   G   B
00502      * host:  RGBX      B   G   R   X */
00503     return      ((x & 0x000000FFU) << 24U) +      /* BBxxxxxx */
00504                 ((x & 0x0000FF00U) <<  8U) +      /* xxGGxxxx */
00505                 ((x & 0x00FF0000U) >>  8U);       /* xxxxRRxx */
00506 }
00507 #endif
00508 
00509 static Bit8u * VGA_Draw_Linear_Line_24_to_32(Bitu vidstart, Bitu /*line*/) {
00510     Bitu offset = vidstart & vga.draw.linear_mask;
00511     Bitu i;
00512 
00513     /* DOSBox's render/scalar code can't handle 24bpp natively, so we have
00514      * to convert 24bpp -> 32bpp.
00515      *
00516      * WARNING: My clever trick might crash on processors that don't support
00517      *          unaligned memory addressing. To explain what it's doing, is
00518      *          it's using a DWORD read to fetch the 24bpp pixel (plus an
00519      *          extra byte), then overwrites the extra byte with 0xFF to
00520      *          produce a valid RGBA 8:8:8:8 value with the original pixel's
00521      *          RGB plus alpha channel value of 0xFF. */
00522 #if SDL_BYTEORDER == SDL_LIL_ENDIAN && defined(MACOSX) /* Mac OS X Intel builds use a weird RGBA order (alpha in the low 8 bits) */
00523     for (i=0;i < vga.draw.width;i++)
00524         ((uint32_t*)TempLine)[i] = guest_bgr_to_macosx_rgba(*((uint32_t*)(vga.draw.linear_base+offset+(i*3)))) | 0x000000FF;
00525 #else
00526     for (i=0;i < vga.draw.width;i++)
00527         ((uint32_t*)TempLine)[i] = *((uint32_t*)(vga.draw.linear_base+offset+(i*3))) | 0xFF000000;
00528 #endif
00529 
00530     return TempLine;
00531 }
00532 
00533 static Bit8u * VGA_Draw_Linear_Line(Bitu vidstart, Bitu /*line*/) {
00534     Bitu offset = vidstart & vga.draw.linear_mask;
00535     Bit8u* ret = &vga.draw.linear_base[offset];
00536     
00537     // in case (vga.draw.line_length + offset) has bits set that
00538     // are not set in the mask: ((x|y)!=y) equals (x&~y)
00539     if (GCC_UNLIKELY(((vga.draw.line_length + offset) & (~vga.draw.linear_mask)) != 0u)) {
00540         // this happens, if at all, only once per frame (1 of 480 lines)
00541         // in some obscure games
00542         Bitu end = (Bitu)((Bitu)offset + (Bitu)vga.draw.line_length) & (Bitu)vga.draw.linear_mask;
00543         
00544         // assuming lines not longer than 4096 pixels
00545         Bitu wrapped_len = end & 0xFFF;
00546         Bitu unwrapped_len = vga.draw.line_length-wrapped_len;
00547         
00548         // unwrapped chunk: to top of memory block
00549         memcpy(TempLine, &vga.draw.linear_base[offset], unwrapped_len);
00550         // wrapped chunk: from base of memory block
00551         memcpy(&TempLine[unwrapped_len], vga.draw.linear_base, wrapped_len);
00552 
00553         ret = TempLine;
00554     }
00555 
00556 #if !defined(C_UNALIGNED_MEMORY)
00557     if (GCC_UNLIKELY( ((Bitu)ret) & (sizeof(Bitu)-1)) ) {
00558         memcpy( TempLine, ret, vga.draw.line_length );
00559         return TempLine;
00560     }
00561 #endif
00562     return ret;
00563 }
00564 
00565 static void Alt_VGA_256color_CharClock(Bit32u* &temps,const VGA_Latch &pixels) {
00566     /* one group of 4 */
00567     *temps++ = vga.dac.xlat32[pixels.b[0]];
00568     *temps++ = vga.dac.xlat32[pixels.b[1]];
00569     *temps++ = vga.dac.xlat32[pixels.b[2]];
00570     *temps++ = vga.dac.xlat32[pixels.b[3]];
00571 }
00572 
00573 static Bit8u * Alt_VGA_256color_Draw_Line_Tseng_ET4000(Bitu /*vidstart*/, Bitu /*line*/) {
00574     Bit32u* temps = (Bit32u*) TempLine;
00575     Bitu count = vga.draw.blocks;
00576 
00577     // Tseng ET4000 cards in 256-color mode appear to treat DWORD mode the same as BYTE mode,
00578     // which is why you can directly draw into the first 128KB and make it visible and even
00579     // pan to it. Most SVGA cards have DWORD mode ON and wrap 64KB in the stock 256-color mode.
00580     const unsigned int shift = (vga.config.addr_shift & 1);
00581 
00582     while (count > 0u) {
00583         const unsigned int addr = vga.draw_2[0].crtc_addr_fetch_and_advance();
00584         VGA_Latch pixels(*vga.draw_2[0].drawptr<Bit32u>(addr << shift));
00585         Alt_VGA_256color_CharClock(temps,pixels);
00586         count--;
00587     }
00588 
00589     return TempLine;
00590 }
00591 
00592 static Bit8u * Alt_VGA_256color_Draw_Line(Bitu /*vidstart*/, Bitu /*line*/) {
00593     Bit32u* temps = (Bit32u*) TempLine;
00594     Bitu count = vga.draw.blocks;
00595 
00596     while (count > 0u) {
00597         const unsigned int addr = vga.draw_2[0].crtc_addr_fetch_and_advance();
00598         VGA_Latch pixels(*vga.draw_2[0].drawptr<Bit32u>(addr << vga.config.addr_shift));
00599         Alt_VGA_256color_CharClock(temps,pixels);
00600         count--;
00601     }
00602 
00603     return TempLine;
00604 }
00605 
00606 #define LOAD_NEXT_PIXEL(n)  nex = pixels.b[n]
00607 #define SHIFTED_PIXEL       *temps++ = vga.dac.xlat32[((cur << 4u) + (nex >> 4u)) & 0xFFu]
00608 #define UNSHIFTED_PIXEL     *temps++ = vga.dac.xlat32[nex]; cur = nex
00609 
00610 template <const unsigned int pixelcount> static inline void Alt_VGA_256color_2x4bit_Draw_CharClock(Bit32u* &temps,const VGA_Latch &pixels,unsigned char &cur,unsigned char &nex) {
00611 /* NTS:
00612  *   pixels == 7 for first char clock on the line
00613  *   pixels == 8 for the rest of the char clocks
00614  *   pixels == 1 for the char clock on the end */
00615 
00616 /* Real VGA hardware appears to have the first 8-bit pixel fully latched for
00617  * the first pixel on the scanline when display enable starts. In that case,
00618  * pixels == 7.
00619  *
00620  * After that, intermediate states are visible across the scan line,
00621  * pixels == 8.
00622  *
00623  * The first pixel past end of active display (normally not visible), it's
00624  * top nibble can be seen as the last clocked out pixel before end of
00625  * active display, pixels == 1 */
00626 
00627     if (pixelcount == 1) {
00628         LOAD_NEXT_PIXEL(0);
00629         SHIFTED_PIXEL;
00630     }
00631     else if (pixelcount == 7) {
00632         LOAD_NEXT_PIXEL(0);
00633         UNSHIFTED_PIXEL;
00634     }
00635     else {
00636         LOAD_NEXT_PIXEL(0);
00637         SHIFTED_PIXEL;
00638         UNSHIFTED_PIXEL;
00639     }
00640 
00641     if (pixelcount >= 7) {
00642         LOAD_NEXT_PIXEL(1);
00643         SHIFTED_PIXEL;
00644         UNSHIFTED_PIXEL;
00645 
00646         LOAD_NEXT_PIXEL(2);
00647         SHIFTED_PIXEL;
00648         UNSHIFTED_PIXEL;
00649 
00650         LOAD_NEXT_PIXEL(3);
00651         SHIFTED_PIXEL;
00652         UNSHIFTED_PIXEL;
00653     }
00654 }
00655 
00656 #undef LOAD_NEXT_PIXEL
00657 #undef SHIFTED_PIXEL
00658 #undef UNSHIFTED_PIXEL
00659 
00660 /* 256-color mode with 8BIT=0, in which the intermediate shift states are visible between
00661  * each 8-bit pixel, producing a weird 640x200 256-color mode.
00662  *
00663  * Not all SVGA cards emulate this. Tseng ET4000 for example will react by just rendering
00664  * the 320 pixels horizontally squeezed on the left half of the screen and nothing on the right. */
00665 static Bit8u * Alt_VGA_256color_2x4bit_Draw_Line(Bitu /*vidstart*/, Bitu /*line*/) {
00666     Bit32u* temps = (Bit32u*) TempLine;
00667     Bitu count = vga.draw.blocks;
00668 
00669     if (count > 0u) {
00670         unsigned char cur,nex;
00671         /* on VGA hardware I've seen, the first pixel is the full 8-bit pixel value of the FIRST pixel in memory. */
00672         unsigned int addr = vga.draw_2[0].crtc_addr_fetch_and_advance();
00673         VGA_Latch pixels(*vga.draw_2[0].drawptr<Bit32u>(addr << vga.config.addr_shift));
00674         Alt_VGA_256color_2x4bit_Draw_CharClock<7>(temps,pixels,cur,nex);
00675         count--;
00676 
00677         while (count > 0u) {
00678             addr = vga.draw_2[0].crtc_addr_fetch_and_advance();
00679             VGA_Latch pixels2(*vga.draw_2[0].drawptr<Bit32u>(addr << vga.config.addr_shift));
00680             Alt_VGA_256color_2x4bit_Draw_CharClock<8>(temps,pixels2,cur,nex);
00681             count--;
00682         }
00683 
00684         /* the top nibble of the first pixel past the end is visible on real hardware */
00685         {
00686             addr = vga.draw_2[0].crtc_addr_fetch_and_advance();
00687             VGA_Latch pixels2(*vga.draw_2[0].drawptr<Bit32u>(addr << vga.config.addr_shift));
00688             Alt_VGA_256color_2x4bit_Draw_CharClock<1>(temps,pixels,cur,nex);
00689         }
00690     }
00691 
00692     return TempLine;
00693 }
00694 
00695 /* WARNING: This routine assumes (vidstart&3) == 0 */
00696 static Bit8u * VGA_Draw_Xlat32_VGA_CRTC_bmode_Line(Bitu vidstart, Bitu /*line*/) {
00697     Bit32u* temps = (Bit32u*) TempLine;
00698     unsigned int poff = 0;
00699     Bitu skip; /* how much to skip after drawing 4 pixels */
00700 
00701     skip = 4u << vga.config.addr_shift;
00702 
00703     /* *sigh* it looks like DOSBox's VGA scanline code will pass nonzero bits 0-1 in vidstart */
00704     poff += vidstart & 3u;
00705     vidstart &= ~3ul;
00706 
00707     /* hack for Surprise! productions "copper" demo.
00708      * when the demo talks about making the picture waver, what it's doing is diddling
00709      * with the Start Horizontal Retrace register of the CRTC once per scanline.
00710      * ...yeah, really. It's a wonder in retrospect the programmer didn't burn out his
00711      * VGA monitor, and I bet this makes the demo effect unwatchable on LCD flat panels or
00712      * scan converters that rely on the pulses to detect VGA mode changes! */
00713     if (vga_enable_hretrace_effects) {
00714         /* NTS: This is NOT BACKWARDS. It makes sense if you think about it: the monitor
00715          *      begins swinging the electron beam back on horizontal retract, so if the
00716          *      retrace starts sooner, then the blanking on the left side appears to last
00717          *      longer because there are more clocks until active display.
00718          *
00719          *      Also don't forget horizontal total/blank/retrace etc. registers are in
00720          *      character clocks not pixels. In 320x200x256 mode, one character clock is
00721          *      4 pixels.
00722          *
00723          *      Finally, we average it with "weight" because CRTs have "inertia" */
00724         float a = 1.0 / (hretrace_fx_avg_weight + 1);
00725 
00726         hretrace_fx_avg *= 1.0 - a;
00727         hretrace_fx_avg += a * 4 * ((int)vga_display_start_hretrace - (int)vga.crtc.start_horizontal_retrace);
00728         int x = (int)floor(hretrace_fx_avg + 0.5);
00729 
00730         vidstart += (Bitu)((int)skip * (x >> 2));
00731         poff += x & 3;
00732     }
00733 
00734     for(Bitu i = 0; i < ((vga.draw.line_length>>(2/*32bpp*/+2/*4 pixels*/))+((poff+3)>>2)); i++) {
00735         Bit8u *ret = &vga.draw.linear_base[ vidstart & vga.draw.linear_mask ];
00736 
00737         /* one group of 4 */
00738         *temps++ = vga.dac.xlat32[*ret++];
00739         *temps++ = vga.dac.xlat32[*ret++];
00740         *temps++ = vga.dac.xlat32[*ret++];
00741         *temps++ = vga.dac.xlat32[*ret++];
00742         /* and skip */
00743         vidstart += skip;
00744     }
00745 
00746     return TempLine + (poff * 4);
00747 }
00748 
00749 static Bit8u * VGA_Draw_Xlat32_Linear_Line(Bitu vidstart, Bitu /*line*/) {
00750     Bit32u* temps = (Bit32u*) TempLine;
00751 
00752     /* hack for Surprise! productions "copper" demo.
00753      * when the demo talks about making the picture waver, what it's doing is diddling
00754      * with the Start Horizontal Retrace register of the CRTC once per scanline.
00755      * ...yeah, really. It's a wonder in retrospect the programmer didn't burn out his
00756      * VGA monitor, and I bet this makes the demo effect unwatchable on LCD flat panels or
00757      * scan converters that rely on the pulses to detect VGA mode changes! */
00758     if (vga_enable_hretrace_effects) {
00759         /* NTS: This is NOT BACKWARDS. It makes sense if you think about it: the monitor
00760          *      begins swinging the electron beam back on horizontal retract, so if the
00761          *      retrace starts sooner, then the blanking on the left side appears to last
00762          *      longer because there are more clocks until active display.
00763          *
00764          *      Also don't forget horizontal total/blank/retrace etc. registers are in
00765          *      character clocks not pixels. In 320x200x256 mode, one character clock is
00766          *      4 pixels.
00767          *
00768          *      Finally, we average it with "weight" because CRTs have "inertia" */
00769         float a = 1.0 / (hretrace_fx_avg_weight + 1);
00770 
00771         hretrace_fx_avg *= 1.0 - a;
00772         hretrace_fx_avg += a * 4 * ((int)vga_display_start_hretrace - (int)vga.crtc.start_horizontal_retrace);
00773         int x = (int)floor(hretrace_fx_avg + 0.5);
00774 
00775         vidstart += (Bitu)((int)x);
00776     }
00777 
00778     for(Bitu i = 0; i < (vga.draw.line_length>>2); i++)
00779         temps[i]=vga.dac.xlat32[vga.draw.linear_base[(vidstart+i)&vga.draw.linear_mask]];
00780 
00781     return TempLine;
00782 }
00783 
00784 extern Bit32u Expand16Table[4][16];
00785 
00786 template <const unsigned int card,typename templine_type_t> static inline templine_type_t EGA_Planar_Common_Block_xlat(const Bit8u t) {
00787     if (card == MCH_VGA)
00788         return vga.dac.xlat32[t];
00789     else if (card == MCH_EGA)
00790         return vga.attr.palette[t&vga.attr.color_plane_enable];
00791 
00792     return 0;
00793 }
00794 
00795 template <const unsigned int card,typename templine_type_t> static inline void EGA_Planar_Common_Block(templine_type_t * const temps,const Bit32u t1,const Bit32u t2) {
00796     Bit32u tmp;
00797 
00798     tmp =   Expand16Table[0][(t1>>0)&0xFF] |
00799             Expand16Table[1][(t1>>8)&0xFF] |
00800             Expand16Table[2][(t1>>16)&0xFF] |
00801             Expand16Table[3][(t1>>24)&0xFF];
00802     temps[0] = EGA_Planar_Common_Block_xlat<card,templine_type_t>((tmp>> 0ul)&0xFFul);
00803     temps[1] = EGA_Planar_Common_Block_xlat<card,templine_type_t>((tmp>> 8ul)&0xFFul);
00804     temps[2] = EGA_Planar_Common_Block_xlat<card,templine_type_t>((tmp>>16ul)&0xFFul);
00805     temps[3] = EGA_Planar_Common_Block_xlat<card,templine_type_t>((tmp>>24ul)&0xFFul);
00806 
00807     tmp =   Expand16Table[0][(t2>>0)&0xFF] |
00808             Expand16Table[1][(t2>>8)&0xFF] |
00809             Expand16Table[2][(t2>>16)&0xFF] |
00810             Expand16Table[3][(t2>>24)&0xFF];
00811     temps[4] = EGA_Planar_Common_Block_xlat<card,templine_type_t>((tmp>> 0ul)&0xFFul);
00812     temps[5] = EGA_Planar_Common_Block_xlat<card,templine_type_t>((tmp>> 8ul)&0xFFul);
00813     temps[6] = EGA_Planar_Common_Block_xlat<card,templine_type_t>((tmp>>16ul)&0xFFul);
00814     temps[7] = EGA_Planar_Common_Block_xlat<card,templine_type_t>((tmp>>24ul)&0xFFul);
00815 }
00816 
00817 template <const unsigned int card,typename templine_type_t> static inline void Alt_EGA_Planar_Common_Block(templine_type_t * &temps,const Bit32u t) {
00818     Bit32u tmp;
00819 
00820     tmp =   Expand16Table[0][(t >>  4)&0xF] |
00821             Expand16Table[1][(t >> 12)&0xF] |
00822             Expand16Table[2][(t >> 20)&0xF] |
00823             Expand16Table[3][(t >> 28)&0xF];
00824     temps[0] = EGA_Planar_Common_Block_xlat<card,templine_type_t>((tmp>> 0ul)&0xFFul);
00825     temps[1] = EGA_Planar_Common_Block_xlat<card,templine_type_t>((tmp>> 8ul)&0xFFul);
00826     temps[2] = EGA_Planar_Common_Block_xlat<card,templine_type_t>((tmp>>16ul)&0xFFul);
00827     temps[3] = EGA_Planar_Common_Block_xlat<card,templine_type_t>((tmp>>24ul)&0xFFul);
00828 
00829     tmp =   Expand16Table[0][(t >>  0)&0xF] |
00830             Expand16Table[1][(t >>  8)&0xF] |
00831             Expand16Table[2][(t >> 16)&0xF] |
00832             Expand16Table[3][(t >> 24)&0xF];
00833     temps[4] = EGA_Planar_Common_Block_xlat<card,templine_type_t>((tmp>> 0ul)&0xFFul);
00834     temps[5] = EGA_Planar_Common_Block_xlat<card,templine_type_t>((tmp>> 8ul)&0xFFul);
00835     temps[6] = EGA_Planar_Common_Block_xlat<card,templine_type_t>((tmp>>16ul)&0xFFul);
00836     temps[7] = EGA_Planar_Common_Block_xlat<card,templine_type_t>((tmp>>24ul)&0xFFul);
00837 
00838     temps += 8;
00839 }
00840 
00841 template <const unsigned int card,typename templine_type_t> static Bit8u * EGA_Planar_Common_Line(Bitu vidstart, Bitu /*line*/) {
00842     templine_type_t* temps = (templine_type_t*)TempLine;
00843     Bitu count = vga.draw.blocks + ((vga.draw.panning + 7u) >> 3u);
00844     Bitu i = 0;
00845 
00846     while (count > 0u) {
00847         Bit32u t1,t2;
00848         t1 = t2 = *((Bit32u*)(&vga.draw.linear_base[ vidstart & vga.draw.linear_mask ]));
00849         t1 = (t1 >> 4) & 0x0f0f0f0f;
00850         t2 &= 0x0f0f0f0f;
00851         vidstart += (uintptr_t)4 << (uintptr_t)vga.config.addr_shift;
00852         EGA_Planar_Common_Block<card,templine_type_t>(temps+i,t1,t2);
00853         count--;
00854         i += 8;
00855     }
00856 
00857     return TempLine + (vga.draw.panning*sizeof(templine_type_t));
00858 }
00859 
00860 static Bit8u * EGA_Draw_VGA_Planar_Xlat8_Line(Bitu vidstart, Bitu line) {
00861     return EGA_Planar_Common_Line<MCH_EGA,Bit8u>(vidstart,line);
00862 }
00863 
00864 static Bit8u * VGA_Draw_VGA_Planar_Xlat32_Line(Bitu vidstart, Bitu line) {
00865     return EGA_Planar_Common_Line<MCH_VGA,Bit32u>(vidstart,line);
00866 }
00867 
00868 template <const unsigned int card,typename templine_type_t> static Bit8u * Alt_EGA_Planar_Common_Line() {
00869     templine_type_t* temps = (templine_type_t*)TempLine;
00870     Bitu count = vga.draw.blocks + ((vga.draw.panning + 7u) >> 3u);
00871 
00872     while (count > 0u) {
00873         const unsigned int addr = vga.draw_2[0].crtc_addr_fetch_and_advance();
00874         VGA_Latch pixels(*vga.draw_2[0].drawptr<Bit32u>(addr << vga.config.addr_shift));
00875         Alt_EGA_Planar_Common_Block<card,templine_type_t>(temps,pixels.d);
00876         count--;
00877     }
00878 
00879     return TempLine + (vga.draw.panning*sizeof(templine_type_t));
00880 }
00881 
00882 static Bit8u * Alt_EGA_Planar_Draw_Line(Bitu /*vidstart*/, Bitu /*line*/) {
00883     return Alt_EGA_Planar_Common_Line<MCH_EGA,Bit8u>();
00884 }
00885 
00886 static Bit8u * Alt_VGA_Planar_Draw_Line(Bitu /*vidstart*/, Bitu /*line*/) {
00887     return Alt_EGA_Planar_Common_Line<MCH_VGA,Bit32u>();
00888 }
00889 
00890 static Bit8u * VGA_Draw_VGA_Packed4_Xlat32_Line(Bitu vidstart, Bitu /*line*/) {
00891     Bit32u* temps = (Bit32u*) TempLine;
00892 
00893     for (Bitu i = 0; i < ((vga.draw.line_length>>2)+vga.draw.panning); i += 2) {
00894         Bit8u t = vga.draw.linear_base[ vidstart & vga.draw.linear_mask ];
00895         vidstart++;
00896 
00897         temps[i+0] = vga.dac.xlat32[(t>>4)&0xF];
00898         temps[i+1] = vga.dac.xlat32[(t>>0)&0xF];
00899     }
00900 
00901     return TempLine + (vga.draw.panning*4);
00902 }
00903 
00904 //Test version, might as well keep it
00905 /* static Bit8u * VGA_Draw_Chain_Line(Bitu vidstart, Bitu line) {
00906     Bitu i = 0;
00907     for ( i = 0; i < vga.draw.width;i++ ) {
00908         Bitu addr = vidstart + i;
00909         TempLine[i] = vga.mem.linear[((addr&~3)<<2)+(addr&3)];
00910     }
00911     return TempLine;
00912 } */
00913 
00914 static Bit8u * VGA_Draw_VGA_Line_Xlat32_HWMouse( Bitu vidstart, Bitu /*line*/) {
00915     if (!svga.hardware_cursor_active || !svga.hardware_cursor_active())
00916         // HW Mouse not enabled, use the tried and true call
00917         return VGA_Draw_Xlat32_Linear_Line(vidstart, 0);
00918 
00919     Bitu lineat = (vidstart-(vga.config.real_start<<2)) / vga.draw.width;
00920     if ((vga.s3.hgc.posx >= vga.draw.width) ||
00921         (lineat < vga.s3.hgc.originy) ||
00922         (lineat > (vga.s3.hgc.originy + (63U-vga.s3.hgc.posy))) ) {
00923         // the mouse cursor *pattern* is not on this line
00924         return VGA_Draw_Xlat32_Linear_Line(vidstart, 0);
00925     } else {
00926         // Draw mouse cursor: cursor is a 64x64 pattern which is shifted (inside the
00927         // 64x64 mouse cursor space) to the right by posx pixels and up by posy pixels.
00928         // This is used when the mouse cursor partially leaves the screen.
00929         // It is arranged as bitmap of 16bits of bitA followed by 16bits of bitB, each
00930         // AB bits corresponding to a cursor pixel. The whole map is 8kB in size.
00931         Bit32u* temp2 = (Bit32u*)VGA_Draw_Xlat32_Linear_Line(vidstart, 0);
00932         //memcpy(TempLine, &vga.mem.linear[ vidstart ], vga.draw.width);
00933 
00934         // the index of the bit inside the cursor bitmap we start at:
00935         Bitu sourceStartBit = ((lineat - vga.s3.hgc.originy) + vga.s3.hgc.posy)*64 + vga.s3.hgc.posx;
00936         // convert to video memory addr and bit index
00937         // start adjusted to the pattern structure (thus shift address by 2 instead of 3)
00938         // Need to get rid of the third bit, so "/8 *2" becomes ">> 2 & ~1"
00939         Bitu cursorMemStart = ((sourceStartBit >> 2ul) & ~1ul) + (((Bit32u)vga.s3.hgc.startaddr) << 10ul);
00940         Bitu cursorStartBit = sourceStartBit & 0x7u;
00941         // stay at the right position in the pattern
00942         if (cursorMemStart & 0x2) cursorMemStart--;
00943         Bitu cursorMemEnd = cursorMemStart + (Bitu)((64 - vga.s3.hgc.posx) >> 2);
00944         Bit32u* xat = &temp2[vga.s3.hgc.originx]; // mouse data start pos. in scanline
00945         for (Bitu m = cursorMemStart; m < cursorMemEnd; (m&1)?(m+=3):m++) {
00946             // for each byte of cursor data
00947             Bit8u bitsA = vga.mem.linear[m];
00948             Bit8u bitsB = vga.mem.linear[m+2];
00949             for (Bit8u bit=(0x80 >> cursorStartBit); bit != 0; bit >>= 1) {
00950                 // for each bit
00951                 cursorStartBit=0; // only the first byte has some bits cut off
00952                 if (bitsA&bit) {
00953                     if (bitsB&bit) *xat ^= 0xFFFFFFFF; // Invert screen data
00954                     //else Transparent
00955                 } else if (bitsB&bit) {
00956                     *xat = vga.dac.xlat32[vga.s3.hgc.forestack[0]]; // foreground color
00957                 } else {
00958                     *xat = vga.dac.xlat32[vga.s3.hgc.backstack[0]];
00959                 }
00960                 xat++;
00961             }
00962         }
00963         return (Bit8u*)temp2;
00964     }
00965 }
00966 
00967 static Bit8u * VGA_Draw_VGA_Line_HWMouse( Bitu vidstart, Bitu /*line*/) {
00968     if (!svga.hardware_cursor_active || !svga.hardware_cursor_active())
00969         // HW Mouse not enabled, use the tried and true call
00970         return &vga.mem.linear[vidstart];
00971 
00972     Bitu lineat = (vidstart-(vga.config.real_start<<2)) / vga.draw.width;
00973     if ((vga.s3.hgc.posx >= vga.draw.width) ||
00974         (lineat < vga.s3.hgc.originy) || 
00975         (lineat > (vga.s3.hgc.originy + (63U-vga.s3.hgc.posy))) ) {
00976         // the mouse cursor *pattern* is not on this line
00977         return &vga.mem.linear[ vidstart ];
00978     } else {
00979         // Draw mouse cursor: cursor is a 64x64 pattern which is shifted (inside the
00980         // 64x64 mouse cursor space) to the right by posx pixels and up by posy pixels.
00981         // This is used when the mouse cursor partially leaves the screen.
00982         // It is arranged as bitmap of 16bits of bitA followed by 16bits of bitB, each
00983         // AB bits corresponding to a cursor pixel. The whole map is 8kB in size.
00984         memcpy(TempLine, &vga.mem.linear[ vidstart ], vga.draw.width);
00985         // the index of the bit inside the cursor bitmap we start at:
00986         Bitu sourceStartBit = ((lineat - vga.s3.hgc.originy) + vga.s3.hgc.posy)*64 + vga.s3.hgc.posx; 
00987         // convert to video memory addr and bit index
00988         // start adjusted to the pattern structure (thus shift address by 2 instead of 3)
00989         // Need to get rid of the third bit, so "/8 *2" becomes ">> 2 & ~1"
00990         Bitu cursorMemStart = ((sourceStartBit >> 2) & ~1ul) + (((Bit32u)vga.s3.hgc.startaddr) << 10ul);
00991         Bitu cursorStartBit = sourceStartBit & 0x7u;
00992         // stay at the right position in the pattern
00993         if (cursorMemStart & 0x2) cursorMemStart--;
00994         Bitu cursorMemEnd = cursorMemStart + (Bitu)((64 - vga.s3.hgc.posx) >> 2);
00995         Bit8u* xat = &TempLine[vga.s3.hgc.originx]; // mouse data start pos. in scanline
00996         for (Bitu m = cursorMemStart; m < cursorMemEnd; (m&1)?(m+=3):m++) {
00997             // for each byte of cursor data
00998             Bit8u bitsA = vga.mem.linear[m];
00999             Bit8u bitsB = vga.mem.linear[m+2];
01000             for (Bit8u bit=(0x80 >> cursorStartBit); bit != 0; bit >>= 1) {
01001                 // for each bit
01002                 cursorStartBit=0; // only the first byte has some bits cut off
01003                 if (bitsA&bit) {
01004                     if (bitsB&bit) *xat ^= 0xFF; // Invert screen data
01005                     //else Transparent
01006                 } else if (bitsB&bit) {
01007                     *xat = vga.s3.hgc.forestack[0]; // foreground color
01008                 } else {
01009                     *xat = vga.s3.hgc.backstack[0];
01010                 }
01011                 xat++;
01012             }
01013         }
01014         return TempLine;
01015     }
01016 }
01017 
01018 /* render 16bpp line DOUBLED horizontally */
01019 static Bit8u * VGA_Draw_LIN16_Line_2x(Bitu vidstart, Bitu /*line*/) {
01020     Bit16u *s = (Bit16u*)(&vga.mem.linear[vidstart]);
01021     Bit16u *d = (Bit16u*)TempLine;
01022 
01023     for (Bitu i = 0; i < (vga.draw.line_length>>2); i++) {
01024         d[0] = d[1] = *s++;
01025         d += 2;
01026     }
01027 
01028     return TempLine;
01029 }
01030 
01031 static Bit8u * VGA_Draw_LIN16_Line_HWMouse(Bitu vidstart, Bitu /*line*/) {
01032     if (!svga.hardware_cursor_active || !svga.hardware_cursor_active())
01033         return &vga.mem.linear[vidstart];
01034 
01035     Bitu lineat = ((vidstart-(vga.config.real_start<<2)) >> 1) / vga.draw.width;
01036     if ((vga.s3.hgc.posx >= vga.draw.width) ||
01037         (lineat < vga.s3.hgc.originy) || 
01038         (lineat > (vga.s3.hgc.originy + (63U-vga.s3.hgc.posy))) ) {
01039         return &vga.mem.linear[vidstart];
01040     } else {
01041         memcpy(TempLine, &vga.mem.linear[ vidstart ], vga.draw.width*2);
01042         Bitu sourceStartBit = ((lineat - vga.s3.hgc.originy) + vga.s3.hgc.posy)*64 + vga.s3.hgc.posx; 
01043         Bitu cursorMemStart = ((sourceStartBit >> 2) & ~1ul) + (((Bit32u)vga.s3.hgc.startaddr) << 10ul);
01044         Bitu cursorStartBit = sourceStartBit & 0x7u;
01045         if (cursorMemStart & 0x2) cursorMemStart--;
01046         Bitu cursorMemEnd = cursorMemStart + (Bitu)((64 - vga.s3.hgc.posx) >> 2);
01047         Bit16u* xat = &((Bit16u*)TempLine)[vga.s3.hgc.originx];
01048         for (Bitu m = cursorMemStart; m < cursorMemEnd; (m&1)?(m+=3):m++) {
01049             // for each byte of cursor data
01050             Bit8u bitsA = vga.mem.linear[m];
01051             Bit8u bitsB = vga.mem.linear[m+2];
01052             for (Bit8u bit=(0x80 >> cursorStartBit); bit != 0; bit >>= 1) {
01053                 // for each bit
01054                 cursorStartBit=0;
01055                 if (bitsA&bit) {
01056                     // byte order doesn't matter here as all bits get flipped
01057                     if (bitsB&bit) *xat ^= ~0U;
01058                     //else Transparent
01059                 } else if (bitsB&bit) {
01060                     // Source as well as destination are Bit8u arrays, 
01061                     // so this should work out endian-wise?
01062                     *xat = *(Bit16u*)vga.s3.hgc.forestack;
01063                 } else {
01064                     *xat = *(Bit16u*)vga.s3.hgc.backstack;
01065                 }
01066                 xat++;
01067             }
01068         }
01069         return TempLine;
01070     }
01071 }
01072 
01073 static Bit8u * VGA_Draw_LIN32_Line_HWMouse(Bitu vidstart, Bitu /*line*/) {
01074 #if SDL_BYTEORDER == SDL_LIL_ENDIAN && defined(MACOSX) /* Mac OS X Intel builds use a weird RGBA order (alpha in the low 8 bits) */
01075     Bitu offset = vidstart & vga.draw.linear_mask;
01076     Bitu i;
01077 
01078     for (i=0;i < vga.draw.width;i++)
01079         ((uint32_t*)TempLine)[i] = guest_bgr_to_macosx_rgba((((uint32_t*)(vga.draw.linear_base+offset))[i]));
01080 
01081     return TempLine;
01082 #else
01083     if (!svga.hardware_cursor_active || !svga.hardware_cursor_active())
01084         return &vga.mem.linear[vidstart];
01085 
01086     Bitu lineat = ((vidstart-(vga.config.real_start<<2)) >> 2) / vga.draw.width;
01087     if ((vga.s3.hgc.posx >= vga.draw.width) ||
01088         (lineat < vga.s3.hgc.originy) || 
01089         (lineat > (vga.s3.hgc.originy + (63U-vga.s3.hgc.posy))) ) {
01090         return &vga.mem.linear[ vidstart ];
01091     } else {
01092         memcpy(TempLine, &vga.mem.linear[ vidstart ], vga.draw.width*4);
01093         Bitu sourceStartBit = ((lineat - vga.s3.hgc.originy) + vga.s3.hgc.posy)*64 + vga.s3.hgc.posx; 
01094         Bitu cursorMemStart = ((sourceStartBit >> 2) & ~1ul) + (((Bit32u)vga.s3.hgc.startaddr) << 10ul);
01095         Bitu cursorStartBit = sourceStartBit & 0x7u;
01096         if (cursorMemStart & 0x2) cursorMemStart--;
01097         Bitu cursorMemEnd = cursorMemStart + (Bitu)((64 - vga.s3.hgc.posx) >> 2);
01098         Bit32u* xat = &((Bit32u*)TempLine)[vga.s3.hgc.originx];
01099         for (Bitu m = cursorMemStart; m < cursorMemEnd; (m&1)?(m+=3):m++) {
01100             // for each byte of cursor data
01101             Bit8u bitsA = vga.mem.linear[m];
01102             Bit8u bitsB = vga.mem.linear[m+2];
01103             for (Bit8u bit=(0x80 >> cursorStartBit); bit != 0; bit >>= 1) { // for each bit
01104                 cursorStartBit=0;
01105                 if (bitsA&bit) {
01106                     if (bitsB&bit) *xat ^= ~0U;
01107                     //else Transparent
01108                 } else if (bitsB&bit) {
01109                     *xat = *(Bit32u*)vga.s3.hgc.forestack;
01110                 } else {
01111                     *xat = *(Bit32u*)vga.s3.hgc.backstack;
01112                 }
01113                 xat++;
01114             }
01115         }
01116         return TempLine;
01117     }
01118 #endif
01119 }
01120 
01121 static const Bit32u* VGA_Planar_Memwrap(Bitu vidstart) {
01122     return (const Bit32u*)vga.mem.linear + (vidstart & vga.draw.planar_mask);
01123 }
01124 
01125 static const Bit8u* VGA_Text_Memwrap(Bitu vidstart) {
01126     vidstart &= vga.draw.linear_mask;
01127     Bitu line_end = 2 * vga.draw.blocks;
01128     if (GCC_UNLIKELY((vidstart + line_end) > vga.draw.linear_mask)) {
01129         // wrapping in this line
01130         Bitu break_pos = (vga.draw.linear_mask - vidstart) + 1;
01131         // need a temporary storage - TempLine/2 is ok for a bit more than 132 columns
01132         memcpy(&TempLine[sizeof(TempLine)/2], &vga.tandy.draw_base[vidstart], break_pos);
01133         memcpy(&TempLine[sizeof(TempLine)/2 + break_pos],&vga.tandy.draw_base[0], line_end - break_pos);
01134         return &TempLine[sizeof(TempLine)/2];
01135     } else return &vga.tandy.draw_base[vidstart];
01136 }
01137 
01138 static Bit32u FontMask[2]={0xffffffff,0x0};
01139 
01140 template <const bool snow> static Bit8u * CGA_COMMON_TEXT_Draw_Line(Bitu vidstart, Bitu line) {
01141     Bits font_addr;
01142     Bit32u * draw=(Bit32u *)TempLine;
01143     const Bit8u* vidmem = VGA_Text_Memwrap(vidstart);
01144 
01145     if (snow) {
01146         /* HACK: our code does not have render control during VBLANK, zero our
01147          *       noise bits on the first scanline */
01148         if (line == 0)
01149             memset(vga.draw.cga_snow,0,sizeof(vga.draw.cga_snow));
01150     }
01151 
01152     for (Bitu cx=0;cx<vga.draw.blocks;cx++) {
01153         Bitu chr,col;
01154         chr=vidmem[cx*2];
01155         col=vidmem[cx*2+1];
01156         if (snow && (cx&1) == 0 && cx <= 78) {
01157             /* Trixter's "CGA test" program and reference video seems to suggest
01158              * to me that the CGA "snow" might contain the value written by the CPU. */
01159             if (vga.draw.cga_snow[cx] != 0)
01160                 chr = vga.draw.cga_snow[cx];
01161             if (vga.draw.cga_snow[cx+1] != 0)
01162                 col = vga.draw.cga_snow[cx+1];
01163         }
01164 
01165         Bitu font=vga.draw.font_tables[(col >> 3)&1][chr*32+line];
01166         Bit32u mask1=TXT_Font_Table[font>>4] & FontMask[col >> 7];
01167         Bit32u mask2=TXT_Font_Table[font&0xf] & FontMask[col >> 7];
01168         Bit32u fg=TXT_FG_Table[col&0xf];
01169         Bit32u bg=TXT_BG_Table[col>>4];
01170         *draw++=(fg&mask1) | (bg&~mask1);
01171         *draw++=(fg&mask2) | (bg&~mask2);
01172     }
01173 
01174     if (snow)
01175         memset(vga.draw.cga_snow,0,sizeof(vga.draw.cga_snow));
01176 
01177     if (!vga.draw.cursor.enabled || !(vga.draw.cursor.count&0x8)) goto skip_cursor;
01178     font_addr = ((Bits)vga.draw.cursor.address - (Bits)vidstart) >> 1ll;
01179     if (font_addr>=0 && font_addr<(Bits)vga.draw.blocks) {
01180         if (line<vga.draw.cursor.sline) goto skip_cursor;
01181         if (line>vga.draw.cursor.eline) goto skip_cursor;
01182         draw=(Bit32u *)&TempLine[(unsigned long)font_addr*8ul];
01183         Bit32u att=TXT_FG_Table[vga.tandy.draw_base[vga.draw.cursor.address+1ul]&0xfu];
01184         *draw++=att;*draw++=att;
01185     }
01186 skip_cursor:
01187     return TempLine;
01188 }
01189 
01190 static Bit8u * VGA_TEXT_Draw_Line(Bitu vidstart, Bitu line) {
01191     return CGA_COMMON_TEXT_Draw_Line<false>(vidstart,line);
01192 }
01193 
01194 static Bit8u * VGA_CGASNOW_TEXT_Draw_Line(Bitu vidstart, Bitu line) {
01195     return CGA_COMMON_TEXT_Draw_Line<true>(vidstart,line);
01196 }
01197 
01198 template <const unsigned int card,typename templine_type_t> static inline void Alt_EGAVGA_Common_2BPP_Draw_Line_CharClock(templine_type_t* &draw,const VGA_Latch &pixels) {
01199     unsigned int val,val2;
01200 
01201     /* CGA odd/even mode, first plane */
01202     val = pixels.b[0];
01203     val2 = (unsigned int)pixels.b[2] << 2u;
01204     for (unsigned int i=0;i < 4;i++,val <<= 2,val2 <<= 2)
01205         *draw++ = EGA_Planar_Common_Block_xlat<card,templine_type_t>(((val>>6)&0x3) + ((val2>>6)&0xC));
01206 
01207     /* CGA odd/even mode, second plane */
01208     val = pixels.b[1];
01209     val2 = (unsigned int)pixels.b[3] << 2u;
01210     for (unsigned int i=0;i < 4;i++,val <<= 2,val2 <<= 2)
01211         *draw++ = EGA_Planar_Common_Block_xlat<card,templine_type_t>(((val>>6)&0x3) + ((val2>>6)&0xC));
01212 }
01213 
01214 template <const unsigned int card,typename templine_type_t> static inline Bit8u *Alt_EGAVGA_Common_2BPP_Draw_Line(void) {
01215     templine_type_t* draw = (templine_type_t*)TempLine;
01216     Bitu blocks = vga.draw.blocks;
01217 
01218     while (blocks--) { // for each character in the line
01219         const unsigned int addr = vga.draw_2[0].crtc_addr_fetch_and_advance();
01220         VGA_Latch pixels(*vga.draw_2[0].drawptr<Bit32u>(addr << vga.config.addr_shift));
01221         Alt_EGAVGA_Common_2BPP_Draw_Line_CharClock<card,templine_type_t>(draw,pixels);
01222     }
01223 
01224     return TempLine;
01225 }
01226 
01227 static Bit8u *Alt_EGA_2BPP_Draw_Line(Bitu /*vidstart*/, Bitu /*line*/) {
01228     return Alt_EGAVGA_Common_2BPP_Draw_Line<MCH_EGA,Bit8u>();
01229 }
01230 
01231 static Bit8u *Alt_VGA_2BPP_Draw_Line(Bitu /*vidstart*/, Bitu /*line*/) {
01232     return Alt_EGAVGA_Common_2BPP_Draw_Line<MCH_VGA,Bit32u>();
01233 }
01234 
01235 static Bit8u *Alt_CGA_2color_Draw_Line(Bitu /*vidstart*/, Bitu /*line*/) {
01236     Bit32u* draw = (Bit32u*)TempLine; // NTS: This is typecast in this way only to write 4 pixels at once at 8bpp
01237     Bitu blocks = vga.draw.blocks;
01238 
01239     while (blocks--) { // for each character in the line
01240         const unsigned int addr = vga.draw_2[0].crtc_addr_fetch_and_advance();
01241         CGA_Latch pixels(*vga.draw_2[0].drawptr<Bit16u>(addr));
01242 
01243         *draw++=CGA_2_Table[pixels.b[0] >> 4];
01244         *draw++=CGA_2_Table[pixels.b[0] & 0xf];
01245 
01246         *draw++=CGA_2_Table[pixels.b[1] >> 4];
01247         *draw++=CGA_2_Table[pixels.b[1] & 0xf];
01248     }
01249 
01250     return TempLine;
01251 }
01252 
01253 static Bit8u *Alt_CGA_4color_Draw_Line(Bitu /*vidstart*/, Bitu /*line*/) {
01254     Bit32u* draw = (Bit32u*)TempLine; // NTS: This is typecast in this way only to write 4 pixels at once at 8bpp
01255     Bitu blocks = vga.draw.blocks;
01256 
01257     while (blocks--) { // for each character in the line
01258         const unsigned int addr = vga.draw_2[0].crtc_addr_fetch_and_advance();
01259         CGA_Latch pixels(*vga.draw_2[0].drawptr<Bit16u>(addr));
01260 
01261         *draw++=CGA_4_Table[pixels.b[0]];
01262         *draw++=CGA_4_Table[pixels.b[1]];
01263     }
01264 
01265     return TempLine;
01266 }
01267 
01268 static inline unsigned int Alt_CGA_TEXT_Load_Font_Bitmap(const unsigned char chr,const unsigned char attr,const unsigned int line) {
01269     return vga.draw.font_tables[((unsigned int)attr >> 3u) & 1u][((unsigned int)chr << 5u) + line];
01270 }
01271 
01272 static inline bool Alt_CGA_TEXT_In_Cursor_Row(const unsigned int line) {
01273     return
01274         ((vga.draw.cursor.count&0x8) && (line >= vga.draw.cursor.sline) &&
01275         (line <= vga.draw.cursor.eline) && vga.draw.cursor.enabled);
01276 }
01277 
01278 // NTS: 8bpp typecast as Bit32u to speedily draw characters
01279 static inline void Alt_CGA_TEXT_Combined_Draw_Line_RenderBMP(Bit32u* &draw,unsigned int font,unsigned char attr) {
01280     const Bit32u mask1=TXT_Font_Table[font>>4] & FontMask[attr >> 7];
01281     const Bit32u mask2=TXT_Font_Table[font&0xf] & FontMask[attr >> 7];
01282     const Bit32u fg=TXT_FG_Table[attr&0xf];
01283     const Bit32u bg=TXT_BG_Table[attr>>4];
01284 
01285     *draw++=(fg&mask1) | (bg&~mask1);
01286     *draw++=(fg&mask2) | (bg&~mask2);
01287 }
01288 
01289 static inline unsigned char Alt_CGA_TEXT_Load_Font_Bitmap(const unsigned char chr,const unsigned char attr,const unsigned char line,const unsigned int addr,const bool in_cursor_row) {
01290     if (GCC_UNLIKELY(in_cursor_row) && addr == vga.config.cursor_start) // cursor
01291         return 0xff;
01292     else // the font pattern
01293         return Alt_CGA_TEXT_Load_Font_Bitmap(chr,attr,line);
01294 }
01295 
01296 template <const bool snow> static Bit8u * Alt_CGA_COMMON_TEXT_Draw_Line(void) {
01297     // keep it aligned:
01298     Bit32u* draw = (Bit32u*)TempLine; // NTS: This is typecast in this way only to write 4 pixels at once at 8bpp
01299     Bitu blocks = vga.draw.blocks;
01300 
01301     const unsigned int line = vga.draw_2[0].vert.current_char_pixel & 7;
01302     const bool in_cursor_row = Alt_CGA_TEXT_In_Cursor_Row(line);
01303 
01304     unsigned int cx = 0;
01305 
01306     if (snow) {
01307         /* HACK: our code does not have render control during VBLANK, zero our
01308          *       noise bits on the first scanline */
01309         if (vga.draw_2[0].vert.current.pixels == 0)
01310             memset(vga.draw.cga_snow,0,sizeof(vga.draw.cga_snow));
01311     }
01312 
01313     while (blocks--) { // for each character in the line
01314         const unsigned int addr = vga.draw_2[0].crtc_addr_fetch_and_advance();
01315         CGA_Latch pixels(*vga.draw_2[0].drawptr<Bit16u>(addr));
01316 
01317         unsigned char chr = pixels.b[0];
01318         unsigned char attr = pixels.b[1];
01319 
01320         if (snow && (cx&1) == 0 && cx <= 78) {
01321             /* Trixter's "CGA test" program and reference video seems to suggest
01322              * to me that the CGA "snow" might contain the value written by the CPU. */
01323             if (vga.draw.cga_snow[cx] != 0)
01324                 chr = vga.draw.cga_snow[cx];
01325             if (vga.draw.cga_snow[cx+1] != 0)
01326                 attr = vga.draw.cga_snow[cx+1];
01327         }
01328 
01329         Alt_CGA_TEXT_Combined_Draw_Line_RenderBMP(draw,
01330             Alt_CGA_TEXT_Load_Font_Bitmap(chr,attr,line,addr,in_cursor_row),attr);
01331 
01332         cx++;
01333     }
01334 
01335     if (snow)
01336         memset(vga.draw.cga_snow,0,sizeof(vga.draw.cga_snow));
01337 
01338     return TempLine;
01339 }
01340 
01341 static Bit8u * Alt_CGA_TEXT_Draw_Line(Bitu /*vidstart*/, Bitu /*line*/) {
01342     return Alt_CGA_COMMON_TEXT_Draw_Line<false>();
01343 }
01344 
01345 static Bit8u * Alt_CGA_CGASNOW_TEXT_Draw_Line(Bitu /*vidstart*/, Bitu /*line*/) {
01346     return Alt_CGA_COMMON_TEXT_Draw_Line<true>();
01347 }
01348 
01349 static Bit8u * MCGA_TEXT_Draw_Line(Bitu vidstart, Bitu line) {
01350     // keep it aligned:
01351     Bit32u* draw = (Bit32u*)TempLine;
01352     const Bit8u* vidmem = VGA_Text_Memwrap(vidstart);
01353     Bitu blocks = vga.draw.blocks;
01354     if (vga.draw.panning) blocks++; // if the text is panned part of an 
01355                                     // additional character becomes visible
01356     while (blocks--) { // for each character in the line
01357         Bitu chr = *vidmem++;
01358         Bitu attr = *vidmem++;
01359         // the font pattern
01360         Bitu font = vga.draw.font_tables[(attr >> 3u)&1u][(chr<<5u)+line];
01361         
01362         Bitu background = attr >> 4u;
01363         // if blinking is enabled bit7 is not mapped to attributes
01364         if (vga.draw.blinking) background &= ~0x8u;
01365         // choose foreground color if blinking not set for this cell or blink on
01366         Bitu foreground = (vga.draw.blink || (!(attr&0x80)))?
01367             (attr&0xf):background;
01368         // underline: all foreground [freevga: 0x77, previous 0x7]
01369         if (GCC_UNLIKELY(((attr&0x77) == 0x01) &&
01370             (vga.crtc.underline_location&0x1f)==line))
01371                 background = foreground;
01372         if (vga.draw.char9dot) {
01373             font <<=1; // 9 pixels
01374             // extend to the 9th pixel if needed
01375             if ((font&0x2) && (vga.attr.mode_control&0x04) &&
01376                 (chr>=0xc0) && (chr<=0xdf)) font |= 1;
01377             for (Bitu n = 0; n < 9; n++) {
01378                 *draw++ = vga.dac.xlat32[(font&0x100)? foreground:background];
01379                 font <<= 1;
01380             }
01381         } else {
01382             for (Bitu n = 0; n < 8; n++) {
01383                 *draw++ = vga.dac.xlat32[(font&0x80)? foreground:background];
01384                 font <<= 1;
01385             }
01386         }
01387     }
01388 
01389     // cursor appearance is also affected by the MCGA double-scanning
01390     if (mcga_double_scan)
01391         line >>= 1ul;
01392 
01393     // draw the text mode cursor if needed
01394     if ((vga.draw.cursor.count&0x8) && (line >= vga.draw.cursor.sline) &&
01395         (line <= vga.draw.cursor.eline) && vga.draw.cursor.enabled) {
01396         // the adress of the attribute that makes up the cell the cursor is in
01397         Bits attr_addr = ((Bits)vga.draw.cursor.address - (Bits)vidstart) >> (Bits)1; /* <- FIXME: This right? */
01398         if (attr_addr >= 0 && attr_addr < (Bits)vga.draw.blocks) {
01399             Bitu index = (Bitu)attr_addr * (vga.draw.char9dot?9u:8u) * 4u;
01400             draw = (Bit32u*)(&TempLine[index]);
01401             
01402             Bitu foreground = vga.tandy.draw_base[(vga.draw.cursor.address<<1)+1] & 0xf;
01403             for (Bitu i = 0; i < 8; i++) {
01404                 *draw++ = vga.dac.xlat32[foreground];
01405             }
01406         }
01407     }
01408     return TempLine;
01409 }
01410 
01411 static Bit8u * VGA_TEXT_Herc_Draw_Line(Bitu vidstart, Bitu line) {
01412     Bits font_addr;
01413     Bit32u * draw=(Bit32u *)TempLine;
01414     const Bit8u* vidmem = VGA_Text_Memwrap(vidstart);
01415 
01416     for (Bitu cx=0;cx<vga.draw.blocks;cx++) {
01417         Bitu chr=vidmem[cx*2];
01418         Bitu attrib=vidmem[cx*2+1];
01419         if (!(attrib&0x77)) {
01420             // 00h, 80h, 08h, 88h produce black space
01421             *draw++=0;
01422             *draw++=0;
01423         } else {
01424             Bit32u bg, fg;
01425             bool underline=false;
01426             if ((attrib&0x77)==0x70) {
01427                 bg = TXT_BG_Table[0x7];
01428                 if (attrib&0x8) fg = TXT_FG_Table[0xf];
01429                 else fg = TXT_FG_Table[0x0];
01430             } else {
01431                 if (((Bitu)(vga.crtc.underline_location&0x1f)==line) && ((attrib&0x77)==0x1)) underline=true;
01432                 bg = TXT_BG_Table[0x0];
01433                 if (attrib&0x8) fg = TXT_FG_Table[0xf];
01434                 else fg = TXT_FG_Table[0x7];
01435             }
01436             Bit32u mask1, mask2;
01437             if (GCC_UNLIKELY(underline)) mask1 = mask2 = FontMask[attrib >> 7];
01438             else {
01439                 Bitu font=vga.draw.font_tables[0][chr*32+line];
01440                 mask1=TXT_Font_Table[font>>4] & FontMask[attrib >> 7]; // blinking
01441                 mask2=TXT_Font_Table[font&0xf] & FontMask[attrib >> 7];
01442             }
01443             *draw++=(fg&mask1) | (bg&~mask1);
01444             *draw++=(fg&mask2) | (bg&~mask2);
01445         }
01446     }
01447     if (!vga.draw.cursor.enabled || !(vga.draw.cursor.count&0x8)) goto skip_cursor;
01448     font_addr = ((Bits)vga.draw.cursor.address - (Bits)vidstart) >> 1ll;
01449     if (font_addr>=0 && font_addr<(Bits)vga.draw.blocks) {
01450         if (line<vga.draw.cursor.sline) goto skip_cursor;
01451         if (line>vga.draw.cursor.eline) goto skip_cursor;
01452         draw=(Bit32u *)&TempLine[(unsigned long)font_addr*8ul];
01453         Bit8u attr = vga.tandy.draw_base[vga.draw.cursor.address+1];
01454         Bit32u cg;
01455         if (attr&0x8) {
01456             cg = TXT_FG_Table[0xf];
01457         } else if ((attr&0x77)==0x70) {
01458             cg = TXT_FG_Table[0x0];
01459         } else {
01460             cg = TXT_FG_Table[0x7];
01461         }
01462         *draw++=cg;*draw++=cg;
01463     }
01464 skip_cursor:
01465     return TempLine;
01466 }
01467 
01468 static inline unsigned int Alt_MDA_TEXT_Load_Font_Bitmap(const unsigned char chr,const unsigned int line) {
01469     return vga.draw.font_tables[0u][((unsigned int)chr << 5u) + line];
01470 }
01471 
01472 static inline bool Alt_MDA_TEXT_In_Cursor_Row(const unsigned int line) {
01473     return
01474         ((vga.draw.cursor.count&0x8) && (line >= vga.draw.cursor.sline) &&
01475         (line <= vga.draw.cursor.eline) && vga.draw.cursor.enabled);
01476 }
01477 
01478 // NTS: 8bpp typecast as Bit32u to speedily draw characters
01479 static inline void Alt_MDA_TEXT_Combined_Draw_Line_RenderBMP(Bit32u* &draw,unsigned int font,unsigned char attrib) {
01480     if (!(attrib&0x77)) {
01481         // 00h, 80h, 08h, 88h produce black space
01482         *draw++=0;
01483         *draw++=0;
01484     } else {
01485         Bit32u bg, fg;
01486 
01487         if ((attrib&0x77)==0x70) {
01488             bg = TXT_BG_Table[0x7];
01489             if (attrib&0x8) fg = TXT_FG_Table[0xf];
01490             else fg = TXT_FG_Table[0x0];
01491         } else {
01492             bg = TXT_BG_Table[0x0];
01493             if (attrib&0x8) fg = TXT_FG_Table[0xf];
01494             else fg = TXT_FG_Table[0x7];
01495         }
01496 
01497         const Bit32u mask1=TXT_Font_Table[font>>4] & FontMask[attrib >> 7]; // blinking
01498         const Bit32u mask2=TXT_Font_Table[font&0xf] & FontMask[attrib >> 7];
01499         *draw++=(fg&mask1) | (bg&~mask1);
01500         *draw++=(fg&mask2) | (bg&~mask2);
01501     }
01502 }
01503 
01504 static inline unsigned char Alt_MDA_TEXT_Load_Font_Bitmap(const unsigned char chr,const unsigned char attrib,const unsigned char line,const unsigned int addr,const bool in_cursor_row) {
01505     if (GCC_UNLIKELY(in_cursor_row) && addr == vga.config.cursor_start) // cursor
01506         return 0xff;
01507     else if ((attrib&0x77) == 0x01 && ((Bitu)(vga.crtc.underline_location&0x1f)==line)) // underline
01508         return 0xff;
01509     else // the font pattern
01510         return Alt_MDA_TEXT_Load_Font_Bitmap(chr,line);
01511 }
01512 
01513 static Bit8u * Alt_MDA_COMMON_TEXT_Draw_Line(void) {
01514     // keep it aligned:
01515     Bit32u* draw = (Bit32u*)TempLine; // NTS: This is typecast in this way only to write 4 pixels at once at 8bpp
01516     Bitu blocks = vga.draw.blocks;
01517 
01518     const unsigned int line = vga.draw_2[0].vert.current_char_pixel & 15;
01519     const bool in_cursor_row = Alt_MDA_TEXT_In_Cursor_Row(line);
01520 
01521     while (blocks--) { // for each character in the line
01522         const unsigned int addr = vga.draw_2[0].crtc_addr_fetch_and_advance();
01523         CGA_Latch pixels(*vga.draw_2[0].drawptr<Bit16u>(addr));
01524 
01525         unsigned char chr = pixels.b[0];
01526         unsigned char attr = pixels.b[1];
01527 
01528         Alt_MDA_TEXT_Combined_Draw_Line_RenderBMP(draw,
01529             Alt_MDA_TEXT_Load_Font_Bitmap(chr,attr,line,addr,in_cursor_row),attr);
01530     }
01531 
01532     return TempLine;
01533 }
01534 
01535 static Bit8u * Alt_MDA_TEXT_Draw_Line(Bitu /*vidstart*/, Bitu /*line*/) {
01536     return Alt_MDA_COMMON_TEXT_Draw_Line();
01537 }
01538 
01539 template <const unsigned int card,typename templine_type_t> static inline Bit8u* EGAVGA_TEXT_Combined_Draw_Line(Bitu vidstart,Bitu line) {
01540     // keep it aligned:
01541     templine_type_t* draw = ((templine_type_t*)TempLine) + 16 - vga.draw.panning;
01542     const Bit32u* vidmem = VGA_Planar_Memwrap(vidstart); // pointer to chars+attribs
01543     Bitu blocks = vga.draw.blocks;
01544     if (vga.draw.panning) blocks++; // if the text is panned part of an 
01545                                     // additional character becomes visible
01546 
01547     while (blocks--) { // for each character in the line
01548         VGA_Latch pixels;
01549 
01550         pixels.d = *vidmem;
01551         vidmem += (uintptr_t)1U << (uintptr_t)vga.config.addr_shift;
01552 
01553         Bitu chr = pixels.b[0];
01554         Bitu attr = pixels.b[1];
01555         // the font pattern
01556         Bitu font = vga.draw.font_tables[(attr >> 3)&1][(chr<<5)+line];
01557         
01558         Bitu background = attr >> 4u;
01559         // if blinking is enabled bit7 is not mapped to attributes
01560         if (vga.draw.blinking) background &= ~0x8u;
01561         // choose foreground color if blinking not set for this cell or blink on
01562         Bitu foreground = (vga.draw.blink || (!(attr&0x80)))?
01563             (attr&0xf):background;
01564         // underline: all foreground [freevga: 0x77, previous 0x7]
01565         if (GCC_UNLIKELY(((attr&0x77) == 0x01) &&
01566             (vga.crtc.underline_location&0x1f)==line))
01567                 background = foreground;
01568         if (vga.draw.char9dot) {
01569             font <<=1; // 9 pixels
01570             // extend to the 9th pixel if needed
01571             if ((font&0x2) && (vga.attr.mode_control&0x04) &&
01572                 (chr>=0xc0) && (chr<=0xdf)) font |= 1;
01573             for (Bitu n = 0; n < 9; n++) {
01574                 if (card == MCH_VGA)
01575                     *draw++ = vga.dac.xlat32[(font&0x100)? foreground:background];
01576                 else /*MCH_EGA*/
01577                     *draw++ = vga.attr.palette[(font&0x100)? foreground:background];
01578 
01579                 font <<= 1;
01580             }
01581         } else {
01582             for (Bitu n = 0; n < 8; n++) {
01583                 if (card == MCH_VGA)
01584                     *draw++ = vga.dac.xlat32[(font&0x80)? foreground:background];
01585                 else /*MCH_EGA*/
01586                     *draw++ = vga.attr.palette[(font&0x80)? foreground:background];
01587 
01588                 font <<= 1;
01589             }
01590         }
01591     }
01592     // draw the text mode cursor if needed
01593     if ((vga.draw.cursor.count&0x8) && (line >= vga.draw.cursor.sline) &&
01594         (line <= vga.draw.cursor.eline) && vga.draw.cursor.enabled) {
01595         // the adress of the attribute that makes up the cell the cursor is in
01596         Bits attr_addr = ((Bits)vga.draw.cursor.address - (Bits)vidstart) >> (Bits)vga.config.addr_shift; /* <- FIXME: This right? */
01597         if (attr_addr >= 0 && attr_addr < (Bits)vga.draw.blocks) {
01598             Bitu index = (Bitu)attr_addr * (vga.draw.char9dot ? 9u : 8u);
01599             draw = (((templine_type_t*)TempLine) + index) + 16 - vga.draw.panning;
01600             
01601             Bitu foreground = vga.tandy.draw_base[(vga.draw.cursor.address<<2ul)+1] & 0xf;
01602             for (Bitu i = 0; i < 8; i++) {
01603                 if (card == MCH_VGA)
01604                     *draw++ = vga.dac.xlat32[foreground];
01605                 else /*MCH_EGA*/
01606                     *draw++ = vga.attr.palette[foreground];
01607             }
01608         }
01609     }
01610 
01611     return TempLine+(16*sizeof(templine_type_t));
01612 }
01613 
01614 // combined 8/9-dot wide text mode 16bpp line drawing function
01615 static Bit8u* EGA_TEXT_Xlat8_Draw_Line(Bitu vidstart, Bitu line) {
01616     return EGAVGA_TEXT_Combined_Draw_Line<MCH_EGA,Bit8u>(vidstart,line);
01617 }
01618 
01619 // combined 8/9-dot wide text mode 16bpp line drawing function
01620 static Bit8u* VGA_TEXT_Xlat32_Draw_Line(Bitu vidstart, Bitu line) {
01621     return EGAVGA_TEXT_Combined_Draw_Line<MCH_VGA,Bit32u>(vidstart,line);
01622 }
01623 
01624 template <const unsigned int card,typename templine_type_t,const unsigned int pixels> static inline void Alt_EGAVGA_TEXT_Combined_Draw_Line_RenderBMP(templine_type_t* &draw,unsigned int font,const unsigned char foreground,const unsigned char background) {
01625     const unsigned int fontmask = 1u << (pixels - 1u);
01626 
01627     for (unsigned int n = 0; n < pixels; n++) {
01628         if (card == MCH_VGA)
01629             *draw++ = vga.dac.xlat32[(font&fontmask)? foreground:background];
01630         else /*MCH_EGA*/
01631             *draw++ = vga.attr.palette[(font&fontmask)? foreground:background];
01632 
01633         font <<= 1;
01634     }
01635 }
01636 
01637 template <const unsigned int card,const unsigned int pixelsperchar> inline unsigned int Alt_VGA_Alpha8to9Expand(unsigned int font,const unsigned char chr) {
01638     if (pixelsperchar == 9) {
01639         font <<= 1; // 9 pixels
01640 
01641         // extend to the 9th pixel if needed
01642         if ((font&0x2) && (vga.attr.mode_control&0x04) && (chr>=0xc0) && (chr<=0xdf)) font |= 1;
01643     }
01644 
01645     return font;
01646 }
01647  
01648 template <const unsigned int card> static inline unsigned int Alt_EGAVGA_TEXT_Load_Font_Bitmap(const unsigned char chr,const unsigned char attr,const unsigned int line) {
01649     return vga.draw.font_tables[(attr >> 3)&1][(chr<<5)+line];
01650 }
01651 
01652 template <const unsigned int card> static inline void Alt_EGAVGA_TEXT_GetFGBG(unsigned char &foreground,unsigned char &background,const unsigned char attr,const unsigned char line,const bool in_cursor_row,const unsigned int addr) {
01653     // if blinking is enabled bit7 is not mapped to attributes
01654     background = attr >> 4u;
01655     if (vga.draw.blinking) background &= ~0x8u;
01656 
01657     // choose foreground color if blinking not set for this cell or blink on
01658     foreground = (vga.draw.blink || (!(attr&0x80))) ? (attr&0xf) : background;
01659 
01660     // underline: all foreground [freevga: 0x77, previous 0x7]
01661     if (GCC_UNLIKELY(((attr&0x77) == 0x01) && (vga.crtc.underline_location&0x1f)==line))
01662         background = foreground;
01663 
01664     // text cursor
01665     if (GCC_UNLIKELY(in_cursor_row) && addr == vga.config.cursor_start)
01666         background = foreground;
01667 }
01668 
01669 template <const unsigned int card> static inline bool Alt_EGAVGA_TEXT_In_Cursor_Row(const unsigned int line) {
01670     return
01671         ((vga.draw.cursor.count&0x8) && (line >= vga.draw.cursor.sline) &&
01672         (line <= vga.draw.cursor.eline) && vga.draw.cursor.enabled);
01673 }
01674 
01675 template <const unsigned int card,typename templine_type_t,const unsigned int pixelsperchar> static inline Bit8u* Alt_EGAVGA_TEXT_Combined_Draw_Line(void) {
01676     // keep it aligned:
01677     templine_type_t* draw = ((templine_type_t*)TempLine) + 16 - vga.draw.panning;
01678     Bitu blocks = vga.draw.blocks;
01679     if (vga.draw.panning) blocks++; // if the text is panned part of an 
01680                                     // additional character becomes visible
01681 
01682     const unsigned int line = vga.draw_2[0].vert.current_char_pixel & vga.draw_2[0].vert.char_pixel_mask;
01683     const bool in_cursor_row = Alt_EGAVGA_TEXT_In_Cursor_Row<card>(line);
01684 
01685     unsigned char foreground,background;
01686 
01687     while (blocks--) { // for each character in the line
01688         const unsigned int addr = vga.draw_2[0].crtc_addr_fetch_and_advance();
01689         VGA_Latch pixels(*vga.draw_2[0].drawptr<Bit32u>(addr << vga.config.addr_shift));
01690 
01691         const unsigned char chr = pixels.b[0];
01692         const unsigned char attr = pixels.b[1];
01693 
01694         // the font pattern
01695         unsigned int font = Alt_EGAVGA_TEXT_Load_Font_Bitmap<card>(chr,attr,line);
01696         Alt_EGAVGA_TEXT_GetFGBG<card>(foreground,background,attr,line,in_cursor_row,addr);
01697 
01698         // Draw it
01699         Alt_EGAVGA_TEXT_Combined_Draw_Line_RenderBMP<card,templine_type_t,pixelsperchar>
01700             (draw,Alt_VGA_Alpha8to9Expand<card,pixelsperchar>(font,chr),foreground,background);
01701     }
01702 
01703     return TempLine+(16*sizeof(templine_type_t));
01704 }
01705 
01706 template <const unsigned int card,typename templine_type_t> static inline Bit8u* Alt_EGAVGA_TEXT_Combined_Draw_Line(void) {
01707     if (vga.draw.char9dot)
01708         return Alt_EGAVGA_TEXT_Combined_Draw_Line<card,templine_type_t,9>();
01709     else
01710         return Alt_EGAVGA_TEXT_Combined_Draw_Line<card,templine_type_t,8>();
01711 }
01712 
01713 // combined 8/9-dot wide text mode 16bpp line drawing function
01714 static Bit8u* Alt_EGA_TEXT_Xlat8_Draw_Line(Bitu /*vidstart*/, Bitu /*line*/) {
01715     return Alt_EGAVGA_TEXT_Combined_Draw_Line<MCH_EGA,Bit8u>();
01716 }
01717 
01718 // combined 8/9-dot wide text mode 16bpp line drawing function
01719 static Bit8u* Alt_VGA_TEXT_Xlat32_Draw_Line(Bitu /*vidstart*/, Bitu /*line*/) {
01720     return Alt_EGAVGA_TEXT_Combined_Draw_Line<MCH_VGA,Bit32u>();
01721 }
01722 
01723 extern bool pc98_attr4_graphic;
01724 extern uint8_t GDC_display_plane;
01725 extern uint8_t GDC_display_plane_pending;
01726 extern bool pc98_graphics_hide_odd_raster_200line;
01727 extern bool pc98_allow_scanline_effect;
01728 extern bool gdc_analog;
01729 
01730 unsigned char       pc98_text_first_row_scanline_start = 0x00;  /* port 70h */
01731 unsigned char       pc98_text_first_row_scanline_end = 0x0F;    /* port 72h */
01732 unsigned char       pc98_text_row_scanline_blank_at = 0x10;     /* port 74h */
01733 unsigned char       pc98_text_row_scroll_lines = 0x00;          /* port 76h */
01734 unsigned char       pc98_text_row_scroll_count_start = 0x00;    /* port 78h */
01735 unsigned char       pc98_text_row_scroll_num_lines = 0x00;      /* port 7Ah */
01736 
01737 // Text layer rendering state.
01738 // Track row, row count, scanline row within the cell,
01739 // for accurate emulation
01740 struct Text_Draw_State {
01741     unsigned int        scroll_vmem;
01742     unsigned char       scroll_scanline_cg;
01743     unsigned char       row_scanline_cg;            /* scanline within row, CG bitmap */
01744     unsigned char       row_char;                   /* character row. scroll region 0 <= num_lines */
01745     unsigned char       row_scroll_countdown;
01746 
01747     void begin_frame(void) {
01748         row_scroll_countdown = 0xFF;
01749         row_scanline_cg = pc98_text_first_row_scanline_start;
01750         row_char = pc98_text_row_scroll_count_start & 0x1Fu;
01751         check_scroll_region();
01752 
01753         if (row_scroll_countdown != 0xFF)
01754             update_scroll_line();
01755     }
01756     void next_line(void) {
01757         if (row_scanline_cg == pc98_text_first_row_scanline_end) {
01758             row_scanline_cg = pc98_text_first_row_scanline_start;
01759             next_character_row();
01760         }
01761         else {
01762             row_scanline_cg = (row_scanline_cg + 1u) & 0x1Fu;
01763         }
01764 
01765         if (row_scroll_countdown != 0xFF)
01766             update_scroll_line();
01767     }
01768     void update_scroll_line(void) {
01769         scroll_vmem = 0;
01770         scroll_scanline_cg = row_scanline_cg;
01771         for (unsigned int i=0;i < pc98_text_row_scroll_lines;i++) {
01772             if (scroll_scanline_cg == pc98_text_first_row_scanline_end) {
01773                 scroll_scanline_cg = pc98_text_first_row_scanline_start;
01774                 scroll_vmem += pc98_gdc[GDC_MASTER].display_pitch;
01775             }
01776             else {
01777                 scroll_scanline_cg = (scroll_scanline_cg + 1u) & 0x1Fu;
01778             }
01779         }
01780     }
01781     void next_character_row(void) {
01782         row_char = (row_char + 1u) & 0x1Fu;
01783         check_scroll_region();
01784     }
01785     void check_scroll_region(void) {
01786         if (row_char == 0) {
01787             /* begin scroll region */
01788             /* NTS: Confirmed on real hardware: The scroll count ADDs to the vertical offset already applied to the text.
01789              *      For example in 20-line text mode (20 pixels high) setting the scroll region offset to 2 pixels cancels
01790              *      out the 2 pixel centering of the text. */
01791             row_scroll_countdown = pc98_text_row_scroll_num_lines & 0x1Fu;
01792         }
01793         else if (row_scroll_countdown == 0) {
01794             /* end scroll region */
01795             row_scroll_countdown = 0xFF;
01796         }
01797         else if (row_scroll_countdown != 0xFF) {
01798             row_scroll_countdown--;
01799         }
01800     }
01801     bool in_scroll_region(void) const {
01802         return row_scroll_countdown != 0xFFu;
01803     }
01804 };
01805 
01806 Text_Draw_State     pc98_text_draw;
01807 
01808 /* NEC PC-9821Lt2 memory layout notes:
01809  *
01810  * - At first glance, the 8/16 color modes appear to be a sequence of bytes that are read
01811  *   in parallel to form 8 or 16 colors.
01812  * - Switching on 256-color mode disables the planar memory mapping, and appears to reveal
01813  *   how the planar memory is ACTUALLY laid out.
01814  * - Much like how VGA planar memory could be thought of as 32 bits per unit, 8 bits per plane,
01815  *   PC-98 planar memory seems to behave as 64 bits per unit, 16 bits per plane.
01816  * - 256-color mode seems to reveal that the bitplanes exposed at A800, B000, B800, E000
01817  *   are in fact just interleaved WORDS in video memory in BGRE order, meaning that the
01818  *   first 4 WORDs visible in 256-color mode are the same as, in 16-color mode, the first
01819  *   word of (in this order) A800, B800, B000, E000. The traditional E/G/R/B
01820  *   (E000, B800, B000, A800) planar order is actually stored in memory in BGRE order.
01821  *
01822  *   Example:
01823  *
01824  *      1. Enable 16-color mode
01825  *      2. Turn OFF 256-color mode
01826  *      3. Write 0x11 0x22 to A800:0000
01827  *      4. Write 0x33 0x44 to B000:0000
01828  *      5. Write 0x55 0x66 to B800:0000
01829  *      6. Write 0x77 0x88 to E000:0000
01830  *      7. Turn ON 256-color mode. Memory map will change.
01831  *      8. Observe at A800:0000 the values 0x11 0x22 0x55 0x66 0x33 0x44 0x77 0x88
01832  *
01833  * Also, if you use I/O port A6h to write to page 1 instead of page 0, what you write will
01834  * appear in 256-color mode at SVGA memory bank 4 (offset 128KB) instead of memory bank 0
01835  * (offset 0KB). Considering 4 planes * 32KB = 128KB this makes sense.
01836  *
01837  * So either the video memory is planar in design, and the 256-color mode is just the bitplanes
01838  * "chained" together (same as 256-color VGA mode), OR, the 256-color mode reflects how memory
01839  * is actually laid out and the planar memory is just emulated by packing each bitplane's WORDs
01840  * together like that.
01841  *
01842  * Proper emulation will require determining which is true and rewriting the draw and memory
01843  * access code to reflect the true layout so mode changes behave in DOSBox-X exactly as they
01844  * behave on real hardware.
01845  *
01846  * I have a hunch it's the case of 4 16-bit bitplanes shifted a byte at a time since doing that
01847  * allows 256-color mode without having to reprogram any other parameters of the GDC or change
01848  * anything significant about the hardware.
01849  *
01850  * Think about it: VGA has planar memory, and each 8-bit byte is shifted out one bit at a time
01851  * in parallel to produce a 4-bit value for each pixel, but VGA also permits the hardware to
01852  * switch to shifting out each planar byte instead to produce a 256-color packed mode.
01853  *
01854  * As noted elsewhere in this source code, VGA memory is 4 planes tied together, even in standard
01855  * 256-color mode where the planes are chained together to give the CPU the impression of a linear
01856  * packed framebuffer.
01857  *
01858  * Since PC-98 also has planar memory, it wouldn't surprise me if the 256-color mode is just the
01859  * same trick except with 16-bit quantitites loaded from memory instead of VGA's 8-bit quantities.
01860  *
01861  * The behavior of the hardware suggest to me that it also allowed NEC to change as little about
01862  * the video hardware as possible, except how it shifts and interprets the planar word data.
01863  *
01864  * The distorted screen that the PC-98 version of Windows 3.1 presents if you select the 640x400
01865  * 256-color driver seems to confirm my theory, along with the fact that you can apparently use
01866  * EGC ROPs in 256-color mode.
01867  *
01868  * However it's very likely the few PC-98 games that use the 256-color mode only care about the
01869  * mode as it exists, and that they don't care about what the prior contents of video memory look
01870  * like, so this isn't a problem in running the games, only a minor problem in emulation accuracy.
01871  *
01872  * Very likely, the same as IBM PC games that set up VGA unchained modes and do not necessarily
01873  * care what happens to the display of prior video memory contents (except of course some lazy
01874  * written code in the Demoscene that switches freely between the two modes).
01875  *
01876  * Please note this behavior so far has been noted from one PC-9821 laptop. It may be consistent
01877  * across other models I have available for testing, or it may not. --J.C.
01878  */
01879 
01880 extern bool                 pc98_256kb_boundary;
01881 extern bool                 gdc_5mhz_mode;
01882 
01883 static Bit8u* VGA_PC98_Xlat32_Draw_Line(Bitu vidstart, Bitu line) {
01884     // keep it aligned:
01885     Bit32u* draw = ((Bit32u*)TempLine);
01886     Bitu blocks = vga.draw.blocks;
01887     Bit32u vidmem = vidstart;
01888     Bit16u chr = 0,attr = 0;
01889     Bit16u lineoverlay = 0; // vertical + underline overlay over the character cell, but apparently with a 4-pixel delay
01890     bool doublewide = false;
01891     unsigned int disp_off = 0;
01892     unsigned char font,foreground;
01893     unsigned char fline;
01894     bool ok_raster = true;
01895 
01896     // simulate 1-pixel shift in CRT mode by offsetting everything by 1 then rendering text without the offset
01897     disp_off = pc98_crt_mode ? 1 : 0;
01898 
01899     // 200-line modes: The BIOS or DOS game can elect to hide odd raster lines
01900     // NTS: Doublescan seems to be ignored in 256-color mode, thus the HACK! below, according to real hardware.
01901     if (pc98_gdc[GDC_SLAVE].doublescan && pc98_graphics_hide_odd_raster_200line && pc98_allow_scanline_effect &&
01902         /*HACK!*/(pc98_gdc_vramop & (1u << VOPBIT_VGA)) == 0)
01903         ok_raster = (vga.draw.lines_done & 1) == 0;
01904 
01905     // Generally the master and slave GDC are given the same active display area, timing, etc.
01906     // however some games reprogram the slave (graphics) GDC to reduce the active display area.
01907     //
01908     // Without this consideration, graphics display will be incorrect relative to actual hardware.
01909     //
01910     // This will NOT cause correct display if other parameters like blanking area are changed!
01911     //
01912     // Examples:
01913     //  - "First Queen" and "First Queen II" (reduces active lines count to 384 to display status bar at the bottom of the screen)
01914     if (vga.draw.lines_done >= pc98_gdc[GDC_SLAVE].active_display_lines)
01915         ok_raster = false;
01916 
01917     // Graphic RAM layer (or blank)
01918     // Think of it as a 3-plane GRB color graphics mode, each plane is 1 bit per pixel.
01919     // G-RAM is addressed 16 bits per RAM cycle.
01920     if (pc98_gdc[GDC_SLAVE].display_enable && ok_raster && pc98_display_enable) {
01921         draw = ((Bit32u*)TempLine) + disp_off;
01922         blocks = vga.draw.blocks;
01923 
01924         if (pc98_gdc_vramop & (1 << VOPBIT_VGA)) {
01925             /* WARNING: This code ASSUMES the port A4h page flip emulation will always
01926              *          set current_display_page to the same base graphics memory address
01927              *          when the 256KB boundary is enabled! If that assumption is WRONG,
01928              *          this code will read 256KB past the end of the buffer and possibly
01929              *          segfault. */
01930             const unsigned long vmask = pc98_256kb_boundary ? 0x7FFFFu : 0x3FFFFu;
01931 
01932             vidmem = (unsigned int)pc98_gdc[GDC_SLAVE].scan_address << (1u+3u); /* as if reading across bitplanes */
01933 
01934             while (blocks--) {
01935                 const unsigned char *s = (const unsigned char*)(&pc98_pgraph_current_display_page[vidmem & vmask]);
01936                 for (unsigned char i=0;i < 8;i++) *draw++ = vga.dac.xlat32[*s++];
01937 
01938                 vidmem += 8;
01939             }
01940         }
01941         else {
01942             /* NTS: According to real hardware, the 128KB/256KB boundary control bit ONLY works in 256-color mode.
01943              *      It has no effect in 8/16-color planar modes, which is probably why the BIOS on such systems
01944              *      will not allow a 640x480 16-color mode since the VRAM required exceeds 32KB per bitplane. */
01945             const unsigned long vmask = 0x7FFFu;
01946 
01947             vidmem = (unsigned int)pc98_gdc[GDC_SLAVE].scan_address << 1u;
01948 
01949             while (blocks--) {
01950                 // NTS: Testing on real hardware shows that, when you switch the GDC back to 8-color mode,
01951                 //      the 4th bitplane is no longer rendered.
01952                 Bit8u e8;
01953 
01954                 if (gdc_analog)
01955                     e8 = pc98_pgraph_current_display_page[(vidmem & vmask) + pc98_pgram_bitplane_offset(3)];  /* E0000-E7FFF */
01956                 else
01957                     e8 = 0x00;
01958 
01959                 Bit8u g8 = pc98_pgraph_current_display_page[(vidmem & vmask) + pc98_pgram_bitplane_offset(2)];      /* B8000-BFFFF */
01960                 Bit8u r8 = pc98_pgraph_current_display_page[(vidmem & vmask) + pc98_pgram_bitplane_offset(1)];      /* B0000-B7FFF */
01961                 Bit8u b8 = pc98_pgraph_current_display_page[(vidmem & vmask) + pc98_pgram_bitplane_offset(0)];      /* A8000-AFFFF */
01962 
01963                 for (unsigned char i=0;i < 8;i++) {
01964                     foreground  = (e8 & 0x80) ? 8 : 0;
01965                     foreground += (g8 & 0x80) ? 4 : 0;
01966                     foreground += (r8 & 0x80) ? 2 : 0;
01967                     foreground += (b8 & 0x80) ? 1 : 0;
01968 
01969                     e8 <<= 1;
01970                     g8 <<= 1;
01971                     r8 <<= 1;
01972                     b8 <<= 1;
01973 
01974                     *draw++ = vga.dac.xlat32[foreground];
01975                 }
01976 
01977                 vidmem++;
01978             }
01979         }
01980     }
01981     else {
01982         memset(TempLine,0,4 * 8 * blocks);
01983     }
01984 
01985     // Text RAM layer
01986     if (pc98_gdc[GDC_MASTER].display_enable && pc98_display_enable) {
01987         Bitu gdcvidmem = pc98_gdc[GDC_MASTER].scan_address;
01988 
01989         draw = ((Bit32u*)TempLine);/* without the disp_off, to emulate 1-pixel cutoff in CRT mode */
01990         blocks = vga.draw.blocks;
01991 
01992         vidmem = pc98_gdc[GDC_MASTER].scan_address;
01993         if (pc98_text_draw.in_scroll_region()) {
01994             vidmem += pc98_text_draw.scroll_vmem;
01995             fline = pc98_text_draw.scroll_scanline_cg;
01996         }
01997         else {
01998             fline = pc98_text_draw.row_scanline_cg;
01999         }
02000 
02001         /* NTS: This code will render the cursor in the scroll region with a weird artifact
02002          *      when the character under it is double-wide, and the cursor covers the top half.
02003          *
02004          *      For example:
02005          *
02006          *      AAA
02007          *      AAA
02008          *      AAAA     <- scroll region ends here
02009          *      AAAA
02010          *
02011          *      Placing the cursor on the third row of A's, on the right, will produce an
02012          *      oddly shaped cursor.
02013          *
02014          *      Before filing a bug about this, consider from my testing that real PC-9821
02015          *      hardware will also render a strangely shaped cursor there as well. --J.C. */
02016 
02017         while (blocks--) { // for each character in the line
02018             bool was_doublewide = doublewide;
02019 
02020             /* Amusing question: How does it handle the "simple graphics" in 20-line mode? */
02021 
02022             if (!doublewide) {
02023 interrupted_char_begin:
02024                 chr = ((Bit16u*)vga.mem.linear)[(vidmem & 0xFFFU) + 0x0000U];
02025                 attr = ((Bit16u*)vga.mem.linear)[(vidmem & 0xFFFU) + 0x1000U];
02026 
02027                 if (pc98_attr4_graphic && (attr & 0x10)) {
02028                     /* the "vertical line" attribute (bit 4) can be redefined as a signal
02029                      * to interpret the character as a low res bitmap instead compatible with
02030                      * "simple graphics" of the PC-8001 (ref. Carat) */
02031                     /* Contrary to what you normally expect of a "bitmap", the pixels are
02032                      * in column order.
02033                      *     0 1
02034                      *     col
02035                      *     0 4  r 0
02036                      *     1 5  o 1
02037                      *     2 6  w 2
02038                      *     3 7    3
02039                      */
02040                     /* The only way a direct bitmap can be encoded in 8 bits is if one character
02041                      * cell were 2 pixels wide 4 pixels high, and each pixel was repeated 4 times.
02042                      * In case you're wondering, the high byte doesn't appear to be used in this mode.
02043                      *
02044                      * Setting the high byte seems to blank the cell entirely */
02045                     if ((chr & 0xFF00) == 0x00) {
02046                         unsigned char bits2 = (chr >> (pc98_gdc[GDC_MASTER].row_line >> 2)) & 0x11;
02047 
02048                         font =  ((bits2 & 0x01) ? 0xF0 : 0x00) +
02049                                 ((bits2 & 0x10) ? 0x0F : 0x00);
02050                     }
02051                     else {
02052                         font = 0;
02053                     }
02054                 }
02055                 else {
02056                     // NTS: The display handles single-wide vs double-wide by whether or not the 8 bits are nonzero.
02057                     //      If zero, the char is one cell wide.
02058                     //      If nonzero, the char is two cells wide (doublewide) and the current character is rendered
02059                     //      into both cells (the character code in the next cell is ignored). The attribute (as far
02060                     //      as I know) repeats for both.
02061                     //
02062                     //      NTS: It seems different character ROM is used between single and double wide chars.
02063                     //           Contrary to what this suggests, (chr & 0xFF00) == 0x8000 is doublewide but not the
02064                     //           same as single-wide (chr & 0xFF00) == 0x0000.
02065                     //
02066                     //      Specific ranges that would be fullwidth where bits[6:0] are 0x08 to 0x0B inclusive are
02067                     //      apparently not fullwidth (the halfwidth char repeats) if both cells filled in.
02068                     if ((chr & 0xFF00) != 0 && (chr & 0x7CU) != 0x08) {
02069                         // left half of doublewide char. it appears only bits[14:8] and bits[6:0] have any real effect on which char is displayed.
02070                         doublewide = true;
02071                     }
02072 
02073                     if (fline < pc98_text_row_scanline_blank_at)
02074                         font = pc98_font_char_read(chr,fline,0);
02075                     else
02076                         font = 0;
02077                 }
02078             }
02079             else {
02080                 // right half of doublewide char.
02081                 //
02082                 // NTS: Strange idiosyncratic behavior observed on real hardware shows that MOST fullwidth codes
02083                 //      fill two cells and ignore the other cell, EXCEPT, that specific ranges require you to
02084                 //      enter the same fullwidth code in both cells.
02085                 doublewide = false;
02086 
02087                 // It seems that for any fullwidth char, you need the same code in both cells for bit[6:0] values
02088                 // from 0x08 to 0x0F inclusive. 0x08 to 0x0B inclusive are not fullwidth, apparently.
02089                 // Same applies 0x56 to 0x5F.
02090                 //
02091                 // Real hardware seems to show that this code will show the other half of the character IF the
02092                 // character code matches. If it does not match, then it will show the first half of the new code.
02093                 //
02094                 // This fix is needed for Touhou Project to show some level titles correctly. The reason this fix
02095                 // affects it, is that the text RAM covering the playfield is not space or any traditionally empty
02096                 // cell but a custom character code that is generally empty, but the character cell bitmap is animated
02097                 // (changed per frame) when doing fade/wipe transitions between levels. Some of the level titles
02098                 // are displayed starting at an odd column cell number, which means that the Kanji intended for
02099                 // display "interrupts" the blank custom character cell code. TH02 ~idnight bug fix.
02100                 if ((chr&0x78U) == 0x08 || (chr&0x7FU) >= 0x56) {
02101                     uint16_t n_chr;
02102 
02103                     n_chr = ((Bit16u*)vga.mem.linear)[(vidmem & 0xFFFU) + 0x0000U];
02104                     attr = ((Bit16u*)vga.mem.linear)[(vidmem & 0xFFFU) + 0x1000U];
02105 
02106                     if ((chr&0x7F7F) != (n_chr&0x7F7F))
02107                         goto interrupted_char_begin;
02108                 }
02109 
02110                 if (fline < pc98_text_row_scanline_blank_at)
02111                     font = pc98_font_char_read(chr,fline,1);
02112                 else
02113                     font = 0;
02114             }
02115 
02116             lineoverlay <<= 8;
02117 
02118             /* the character is not rendered if "~secret" (bit 0) is not set */
02119             if (!(attr & 1)) font = 0;
02120 
02121             /* "blink" seems to count at the same speed as the cursor blink rate,
02122              * through a 4-cycle pattern in which the character is invisible only
02123              * at the first count. */
02124             if ((attr & 0x02/*blink*/) && pc98_gdc[GDC_MASTER].cursor_blink_state == 0) font = 0;
02125 
02126             /* reverse attribute. seems to take effect BEFORE vertical & underline attributes */
02127             if (attr & 0x04/*reverse*/) font ^= 0xFF;
02128 
02129             /* based on real hardware, the cursor seems to act like a reverse attribute */
02130             /* if the character is double-wide, and the cursor is on the left half, the cursor affects the right half too. */
02131             if (((gdcvidmem == vga.draw.cursor.address) || (was_doublewide && gdcvidmem == (vga.draw.cursor.address+1))) &&
02132                 pc98_gdc[GDC_MASTER].cursor_enable &&
02133                 ((!pc98_gdc[GDC_MASTER].cursor_blink) || (pc98_gdc[GDC_MASTER].cursor_blink_state&1)) &&
02134                 (line >= vga.draw.cursor.sline) &&
02135                 (line <= vga.draw.cursor.eline)) {
02136                 font ^= 0xFF;
02137             }
02138 
02139             /* "vertical line" bit puts a vertical line on the 4th pixel of the cell */
02140             if (!pc98_attr4_graphic && (attr & 0x10)) lineoverlay |= 1U << 7U;
02141 
02142             /* underline fills the row to underline the text */
02143             if ((attr & 0x08) && line == (vga.crtc.maximum_scan_line & 0x1FU)) lineoverlay |= 0xFFU;
02144 
02145             /* lineoverlay overlays font with 4-pixel delay */
02146             font |= (unsigned char)(((unsigned int)lineoverlay >> 4u) & 0xFFU);
02147 
02148             /* color? */
02149             foreground = ((unsigned int)attr >> 5u) & 7u; /* bits[7:5] are GRB foreground color */
02150 
02151             /* draw it!
02152              * NTS: Based on real hardware (and this is probably why there's no provisions for both fore and background color)
02153              *      any bit in the font overlays the graphic output (after reverse, etc) or else does not output anything. */
02154             if (!pc98_40col_text) {
02155                 /* 80-col */
02156                 for (Bitu n = 0; n < 8; n++) {
02157                     if (font & 0x80)
02158                         *draw++ = pc98_text_palette[foreground];
02159                     else
02160                         draw++;
02161 
02162                     font <<= 1u;
02163                 }
02164 
02165                 vidmem++;
02166                 gdcvidmem++;
02167             }
02168             else {
02169                 /* 40-col */
02170                 for (Bitu n = 0; n < 8; n++) {
02171                     if (font & 0x80)
02172                         draw[0] = draw[1] = pc98_text_palette[foreground];
02173 
02174                     font <<= 1u;
02175                     draw += 2;
02176                 }
02177 
02178                 vidmem += 2;
02179                 gdcvidmem += 2;
02180                 if (blocks > 0) blocks--;
02181             }
02182         }
02183     }
02184 
02185     return TempLine + (disp_off * 4);
02186 }
02187 
02188 static void VGA_ProcessSplit() {
02189     vga.draw.has_split = true;
02190     if (vga.attr.mode_control&0x20) {
02191         vga.draw.address=0;
02192         // reset panning to 0 here so we don't have to check for 
02193         // it in the character draw functions. It will be set back
02194         // to its proper value in v-retrace
02195         vga.draw.panning=0; 
02196     } else {
02197         // In text mode only the characters are shifted by panning, not the address;
02198         // this is done in the text line draw function. EGA/VGA planar is handled the same way.
02199         vga.draw.address = vga.draw.byte_panning_shift*vga.draw.bytes_skip;
02200         if (machine != MCH_EGA) {
02201             switch (vga.mode) {
02202                 case M_PC98:
02203                 case M_TEXT:
02204                 case M_EGA:
02205                 case M_LIN4:
02206                 case M_PACKED4:
02207                     /* ignore */
02208                     break;
02209                 default:
02210                     vga.draw.address += vga.draw.panning;
02211                     break;
02212             }
02213         }
02214     }
02215     vga.draw.address_line=0;
02216 }
02217 
02218 static Bit8u bg_color_index = 0; // screen-off black index
02219 static Bit8u VGA_GetBlankedIndex() {
02220     if (vga.dac.xlat16[bg_color_index] != 0) {
02221         for(Bitu i = 0; i < 256; i++)
02222             if (vga.dac.xlat16[i] == 0) {
02223                 bg_color_index = i;
02224                 break;
02225             }
02226     }
02227     return bg_color_index;
02228 }
02229 
02230 /* this is now called PER LINE because most VGA cards do not double-buffer the value.
02231  * a few demos rely on line compare schenanigans to play with the raster, as does my own VGA test program --J.C. */
02232 void VGA_Update_SplitLineCompare() {
02233     if (vga_alt_new_mode)
02234         vga.draw.split_line = vga.config.line_compare + 1;
02235     else
02236         vga.draw.split_line = (vga.config.line_compare + 1) / vga.draw.render_max;
02237 
02238     if (svgaCard==SVGA_S3Trio) {
02239         /* FIXME: Is this really necessary? Is this what S3 chipsets do?
02240          *        What is supposed to happen is that line_compare == 0 on normal VGA will cause the first
02241          *        scanline to repeat twice. Do S3 chipsets NOT reproduce that quirk?
02242          *
02243          *        The other theory I have about whoever wrote this code is that they wanted to multiply
02244          *        the scan line by two but had to compensate for the line_compare+1 assignment above.
02245          *        Rather than end up with a splitscreen too far down, they did that.
02246          *
02247          *        I think the proper code to put here would be:
02248          *
02249          *        if (vga.s3.reg_42 & 0x20) {
02250          *            vga.draw.split_line--;
02251          *            vga.draw.split_line *= 2;
02252          *            vga.draw.split_line++;
02253          *        }
02254          *
02255          *        Is that right?
02256          *
02257          *        This behavior is the reason for Issue #40 "Flash productions "monstra" extra white line at top of screen"
02258          *        because it causes line compare to miss and the first scanline of the white bar appears on scanline 2,
02259          *        when the demo coder obviously intended for line_compare == 0 to repeat the first scanline twice so that
02260          *        on line 3, it can begin updating line compare to continue repeating the first scanline.
02261          *
02262          * TODO: Pull out some S3 graphics cards and check split line behavior when line_compare == 0 */
02263         if (vga.config.line_compare==0) vga.draw.split_line=0;
02264         if (vga.s3.reg_42 & 0x20) { // interlaced mode
02265             vga.draw.split_line *= 2;
02266         }
02267     }
02268     vga.draw.split_line -= vga.draw.vblank_skip;
02269 }
02270 
02271 void VGA_Alt_CheckSplit(void) {
02272     if (vga.draw_2[0].raster_scanline == vga.draw.split_line) {
02273         /* VGA line compare. split line */
02274         vga.draw.has_split = true;
02275         if (vga.attr.mode_control&0x20) {
02276             vga.draw_2[0].vert.crtc_addr = 0;
02277             vga.draw.panning = 0;
02278         }
02279         else {
02280             vga.draw_2[0].vert.crtc_addr = 0 + vga.draw.bytes_skip;
02281         }
02282         vga.draw_2[0].vert.current_char_pixel = 0;
02283     }
02284 }
02285 
02286 void VGA_Alt_UpdateCRTCPixels(void) {
02287     if (IS_EGAVGA_ARCH) {
02288         vga.draw_2[0].horz.char_pixels = (vga.attr.mode_control & 4/*9 pixels/char*/) ? 9 : 8;
02289         vga.draw_2[0].vert.char_pixels = (vga.crtc.maximum_scan_line & vga.draw_2[0].vert.char_pixel_mask) + 1u;
02290     }
02291     else {
02292         vga.draw_2[0].horz.char_pixels = 8;
02293         vga.draw_2[0].vert.char_pixels = (vga.other.max_scanline & vga.draw_2[0].vert.char_pixel_mask) + 1u;
02294     }
02295 }
02296 
02297 void VGA_Alt_UpdateCRTCAdd(void) {
02298     if (IS_EGAVGA_ARCH) {
02299         vga.draw_2[0].horz.crtc_addr_add = 1;
02300         vga.draw_2[0].vert.crtc_addr_add = vga.crtc.offset * 2u;
02301     }
02302     else {
02303         vga.draw_2[0].horz.crtc_addr_add = 1;
02304         vga.draw_2[0].vert.crtc_addr_add = vga.other.hdend;
02305     }
02306 }
02307 
02308 void VGA_Alt_NextLogScanLine(void) {
02309     vga.draw_2[0].horz.current = 0;
02310     vga.draw_2[0].vert.current.pixels++;
02311 
02312     VGA_Alt_UpdateCRTCPixels();
02313     VGA_Alt_UpdateCRTCAdd();
02314 
02315     vga.draw_2[0].vert.current_char_pixel++;
02316 
02317     // TODO: DOSBox SVN and DOSBox-X main VGA emulation go to next line if row char line >= max.
02318     //       Real hardware suggests that it only happens when line == max, meaning if you reprogram
02319     //       the max scanline register in such a way the card misses it, it will count through all
02320     //       5 bits of the row counter before coming back around to match it again.
02321     //
02322     //       It might be a nice emulation option to select comparator function, whether >= or == .
02323     if ((vga.draw_2[0].vert.current_char_pixel & vga.draw_2[0].vert.char_pixel_mask) ==
02324         (vga.draw_2[0].vert.char_pixels        & vga.draw_2[0].vert.char_pixel_mask)) {
02325         vga.draw_2[0].vert.current_char_pixel = 0;
02326         vga.draw_2[0].vert.crtc_addr += vga.draw_2[0].vert.crtc_addr_add;
02327     }
02328 
02329     if (IS_EGAVGA_ARCH)
02330         VGA_Alt_CheckSplit();
02331 
02332     vga.draw_2[0].horz.crtc_addr = vga.draw_2[0].vert.crtc_addr;
02333     vga.draw_2[0].horz.current_char_pixel = 0;
02334 
02335     VGA_Draw2_Recompute_CRTC_MaskAdd();
02336 }
02337 
02338 void VGA_Alt_NextScanLine(void) {
02339     /* track actual raster line to output */
02340     vga.draw_2[0].raster_scanline++;
02341 
02342     /* do not advance the vertical count nor carry out new scanline functions
02343      * if doublescan is set and this is the EVEN scan line */
02344     if (vga.draw_2[0].doublescan_count >= vga.draw_2[0].doublescan_max) {
02345         vga.draw_2[0].doublescan_count = 0;
02346         VGA_Alt_NextLogScanLine();
02347     }
02348     else {
02349         vga.draw_2[0].doublescan_count++;
02350 
02351         if (IS_EGAVGA_ARCH)
02352             VGA_Alt_CheckSplit();
02353 
02354         vga.draw_2[0].horz.crtc_addr = vga.draw_2[0].vert.crtc_addr;
02355         vga.draw_2[0].horz.current_char_pixel = 0;
02356 
02357         VGA_Draw2_Recompute_CRTC_MaskAdd();
02358     }
02359 }
02360 
02361 static void VGA_DrawSingleLine(Bitu /*blah*/) {
02362     unsigned int lines = 0;
02363     bool skiprender;
02364 
02365 again:
02366     if (vga.draw.render_step == 0)
02367         skiprender = false;
02368     else
02369         skiprender = true;
02370 
02371     if ((++vga.draw.render_step) >= vga.draw.render_max)
02372         vga.draw.render_step = 0;
02373 
02374     if (!skiprender) {
02375         if (GCC_UNLIKELY(vga.attr.disabled)) {
02376             switch(machine) {
02377                 case MCH_PCJR:
02378                     // Displays the border color when screen is disabled
02379                     bg_color_index = vga.tandy.border_color;
02380                     break;
02381                 case MCH_TANDY:
02382                     // Either the PCJr way or the CGA way
02383                     if (vga.tandy.gfx_control& 0x4) { 
02384                         bg_color_index = vga.tandy.border_color;
02385                     } else if (vga.mode==M_TANDY4) 
02386                         bg_color_index = vga.attr.palette[0];
02387                     else bg_color_index = 0;
02388                     break;
02389                 case MCH_CGA:
02390                 case MCH_MCGA:
02391                     // the background color
02392                     bg_color_index = vga.attr.overscan_color;
02393                     break;
02394                 case MCH_EGA:
02395                 case MCH_VGA:
02396                 case MCH_PC98:
02397                     // DoWhackaDo, Alien Carnage, TV sports Football
02398                     // when disabled by attribute index bit 5:
02399                     //  ET3000, ET4000, Paradise display the border color
02400                     //  S3 displays the content of the currently selected attribute register
02401                     // when disabled by sequencer the screen is black "257th color"
02402 
02403                     // the DAC table may not match the bits of the overscan register
02404                     // so use black for this case too...
02405                     //if (vga.attr.disabled& 2) {
02406                     VGA_GetBlankedIndex();
02407                     //} else 
02408                     //    bg_color_index = vga.attr.overscan_color;
02409                     break;
02410                 default:
02411                     bg_color_index = 0;
02412                     break;
02413             }
02414             if (vga.draw.bpp==8) {
02415                 memset(TempLine, bg_color_index, sizeof(TempLine));
02416             } else if (vga.draw.bpp==16) {
02417                 Bit16u* wptr = (Bit16u*) TempLine;
02418                 Bit16u value = vga.dac.xlat16[bg_color_index];
02419                 for (Bitu i = 0; i < sizeof(TempLine)/2; i++) {
02420                     wptr[i] = value;
02421                 }
02422             } else if (vga.draw.bpp==32) {
02423                 Bit32u* wptr = (Bit32u*) TempLine;
02424                 Bit32u value = vga.dac.xlat32[bg_color_index];
02425                 for (Bitu i = 0; i < sizeof(TempLine)/4; i++) {
02426                     wptr[i] = value;
02427                 }
02428             }
02429 
02430             if (vga_page_flip_occurred) {
02431                 memxor(TempLine,0xFF,vga.draw.width*(vga.draw.bpp>>3));
02432                 vga_page_flip_occurred = false;
02433             }
02434             if (vga_3da_polled) {
02435                 if (vga.draw.bpp==32)
02436                     memxor_greendotted_32bpp((uint32_t*)TempLine,(vga.draw.width>>1)*(vga.draw.bpp>>3),vga.draw.lines_done);
02437                 else
02438                     memxor_greendotted_16bpp((uint16_t*)TempLine,(vga.draw.width>>1)*(vga.draw.bpp>>3),vga.draw.lines_done);
02439                 vga_3da_polled = false;
02440             }
02441             RENDER_DrawLine(TempLine);
02442         } else {
02443             Bit8u * data=VGA_DrawLine( vga.draw.address, vga.draw.address_line );
02444             if (vga_page_flip_occurred) {
02445                 memxor(data,0xFF,vga.draw.width*(vga.draw.bpp>>3));
02446                 vga_page_flip_occurred = false;
02447             }
02448             if (vga_3da_polled) {
02449                 if (vga.draw.bpp==32)
02450                     memxor_greendotted_32bpp((uint32_t*)data,(vga.draw.width>>1)*(vga.draw.bpp>>3),vga.draw.lines_done);
02451                 else
02452                     memxor_greendotted_16bpp((uint16_t*)data,(vga.draw.width>>1)*(vga.draw.bpp>>3),vga.draw.lines_done);
02453                 vga_3da_polled = false;
02454             }
02455 
02456             if (VGA_IsCaptureEnabled())
02457                 VGA_ProcessScanline(data);
02458 
02459             RENDER_DrawLine(data);
02460         }
02461     }
02462 
02463     /* parallel system */
02464     if (vga_alt_new_mode)
02465         VGA_Alt_NextScanLine();
02466 
02467     vga.draw.address_line++;
02468     if (vga.draw.address_line>=vga.draw.address_line_total) {
02469         vga.draw.address_line=0;
02470         vga.draw.address+=vga.draw.address_add;
02471     }
02472 
02473     if (!skiprender) {
02474         vga.draw.lines_done++;
02475         if (vga.draw.split_line==vga.draw.lines_done && !vga_alt_new_mode) VGA_ProcessSplit();
02476     }
02477 
02478     if (mcga_double_scan) {
02479         if (vga.draw.lines_done < vga.draw.lines_total) {
02480             if (++lines < 2)
02481                 goto again;
02482         }
02483     }
02484 
02485     if (vga.draw.lines_done < vga.draw.lines_total) {
02486         PIC_AddEvent(VGA_DrawSingleLine,(float)vga.draw.delay.singleline_delay);
02487     } else {
02488         vga_mode_frames_since_time_base++;
02489 
02490         if (VGA_IsCaptureEnabled())
02491             VGA_ProcessScanline(NULL);
02492 
02493         RENDER_EndUpdate(false);
02494     }
02495 
02496     if (IS_PC98_ARCH) {
02497         for (unsigned int i=0;i < 2;i++)
02498             pc98_gdc[i].next_line();
02499 
02500         pc98_text_draw.next_line();
02501     }
02502 
02503     /* some VGA cards (ATI chipsets especially) do not double-buffer the
02504      * horizontal panning register. some DOS demos take advantage of that
02505      * to make the picture "waver".
02506      *
02507      * We stop doing this though if the attribute controller is setup to set hpel=0 at splitscreen. */
02508     if (IS_VGA_ARCH && vga_enable_hpel_effects) {
02509         /* 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.
02510          * This ensures some demos like Future Crew "Yo" display correctly instead of the bottom non-scroller half jittering because the top half is scrolling. */
02511         if (vga.draw.has_split && (vga.attr.mode_control&0x20))
02512             vga.draw.panning = 0;
02513         else
02514             vga.draw.panning = vga.config.pel_panning;
02515     }
02516 
02517     if (IS_EGAVGA_ARCH && !vga_double_buffered_line_compare) VGA_Update_SplitLineCompare();
02518 }
02519 
02520 static void VGA_DrawEGASingleLine(Bitu /*blah*/) {
02521     bool skiprender;
02522 
02523     if (vga.draw.render_step == 0)
02524         skiprender = false;
02525     else
02526         skiprender = true;
02527 
02528     if ((++vga.draw.render_step) >= vga.draw.render_max)
02529         vga.draw.render_step = 0;
02530 
02531     if (!skiprender) {
02532         if (GCC_UNLIKELY(vga.attr.disabled)) {
02533             memset(TempLine, 0, sizeof(TempLine));
02534             RENDER_DrawLine(TempLine);
02535         } else {
02536             Bitu address = vga.draw.address;
02537             if (machine != MCH_EGA) {
02538                 switch (vga.mode) {
02539                     case M_PC98:
02540                     case M_TEXT:
02541                     case M_EGA:
02542                     case M_LIN4:
02543                     case M_PACKED4:
02544                         /* ignore */
02545                         break;
02546                     default:
02547                         vga.draw.address += vga.draw.panning;
02548                         break;
02549                 }
02550             }
02551             Bit8u * data=VGA_DrawLine(address, vga.draw.address_line ); 
02552 
02553             if (VGA_IsCaptureEnabled())
02554                 VGA_ProcessScanline(data);
02555 
02556             RENDER_DrawLine(data);
02557         }
02558     }
02559 
02560     /* parallel system */
02561     if (vga_alt_new_mode)
02562         VGA_Alt_NextScanLine();
02563 
02564     vga.draw.address_line++;
02565     if (vga.draw.address_line>=vga.draw.address_line_total) {
02566         vga.draw.address_line=0;
02567         vga.draw.address+=vga.draw.address_add;
02568     }
02569 
02570     if (!skiprender) {
02571         vga.draw.lines_done++;
02572         if (vga.draw.split_line==vga.draw.lines_done && !vga_alt_new_mode) VGA_ProcessSplit();
02573     }
02574 
02575     if (vga.draw.lines_done < vga.draw.lines_total) {
02576         PIC_AddEvent(VGA_DrawEGASingleLine,(float)vga.draw.delay.singleline_delay);
02577     } else {
02578         vga_mode_frames_since_time_base++;
02579 
02580         if (VGA_IsCaptureEnabled())
02581             VGA_ProcessScanline(NULL);
02582 
02583         RENDER_EndUpdate(false);
02584     }
02585 }
02586 
02587 void VGA_SetBlinking(Bitu enabled) {
02588     Bitu b;
02589     LOG(LOG_VGA,LOG_NORMAL)("Blinking %d",(int)enabled);
02590     if (enabled) {
02591         b=0;vga.draw.blinking=1; //used to -1 but blinking is unsigned
02592         vga.attr.mode_control|=0x08;
02593         vga.tandy.mode_control|=0x20;
02594     } else {
02595         b=8;vga.draw.blinking=0;
02596         vga.attr.mode_control&=~0x08;
02597         vga.tandy.mode_control&=~0x20;
02598     }
02599     for (Bitu i=0;i<8;i++) TXT_BG_Table[i+8]=(b+i) | ((b+i) << 8)| ((b+i) <<16) | ((b+i) << 24);
02600 }
02601 
02602 extern bool                        GDC_vsync_interrupt;
02603 
02604 static void VGA_VertInterrupt(Bitu /*val*/) {
02605     if (IS_PC98_ARCH) {
02606         if (GDC_vsync_interrupt) {
02607             GDC_vsync_interrupt = false;
02608             PIC_ActivateIRQ(2);
02609         }
02610     }
02611     else {
02612         if ((!vga.draw.vret_triggered) && ((vga.crtc.vertical_retrace_end&0x30)==0x10)) {
02613             vga.draw.vret_triggered=true;
02614             if (GCC_UNLIKELY(machine==MCH_EGA)) PIC_ActivateIRQ(9);
02615         }
02616     }
02617 }
02618 
02619 static void VGA_Other_VertInterrupt(Bitu val) {
02620     if (val) PIC_ActivateIRQ(5);
02621     else PIC_DeActivateIRQ(5);
02622 }
02623 
02624 static void VGA_DisplayStartLatch(Bitu /*val*/) {
02625     /* hretrace fx support: store the hretrace value at start of picture so we have
02626      * a point of reference how far to displace the scanline when wavy effects are
02627      * made */
02628     vga_display_start_hretrace = vga.crtc.start_horizontal_retrace;
02629     vga.config.real_start=vga.config.display_start & vga.mem.memmask;
02630     vga.draw.bytes_skip = vga.config.bytes_skip;
02631 
02632     /* TODO: When does 640x480 2-color mode latch foreground/background colors from the DAC? */
02633     if (machine == MCH_MCGA && (vga.other.mcga_mode_control & 2)) {//640x480 2-color mode MCGA
02634         VGA_DAC_UpdateColorPalette();
02635     }
02636 }
02637  
02638 static void VGA_PanningLatch(Bitu /*val*/) {
02639     vga.draw.panning = vga.config.pel_panning;
02640 
02641     if (IS_PC98_ARCH) {
02642         for (unsigned int i=0;i < 2;i++)
02643             pc98_gdc[i].begin_frame();
02644 
02645         pc98_text_draw.begin_frame();
02646     }
02647 }
02648 
02649 extern SDL_Rect                            vga_capture_current_rect;
02650 extern uint32_t                            vga_capture_current_address;
02651 extern uint32_t                            vga_capture_write_address;
02652 
02653 void VGA_ProcessScanline(const uint8_t *raw) {
02654     if (raw == NULL) { // end of the frame
02655         if (VGA_IsCaptureInProgress()) {
02656             VGA_MarkCaptureInProgress(false);
02657             VGA_MarkCaptureAcquired();
02658         }
02659 
02660         return;
02661     }
02662 
02663     // assume VGA_IsCaptureEnabled()
02664     if (!VGA_IsCaptureInProgress()) {
02665         if (vga_capture_current_address != (uint32_t)0 && (unsigned int)vga.draw.lines_done == (unsigned int)vga_capture_current_rect.y) { // start
02666             VGA_MarkCaptureInProgress(true);
02667             VGA_CaptureWriteScanline(raw);
02668         }
02669     }
02670     else {
02671         if ((unsigned int)vga.draw.lines_done == ((unsigned int)vga_capture_current_rect.y+vga_capture_current_rect.h)) { // first line past end
02672             VGA_MarkCaptureInProgress(false);
02673             VGA_MarkCaptureAcquired();
02674         }
02675         else {
02676             VGA_CaptureWriteScanline(raw);
02677         }
02678     }
02679 }
02680 
02681 extern uint32_t GFX_Rmask;
02682 extern unsigned char GFX_Rshift;
02683 extern uint32_t GFX_Gmask;
02684 extern unsigned char GFX_Gshift;
02685 extern uint32_t GFX_Bmask;
02686 extern unsigned char GFX_Bshift;
02687 extern uint32_t GFX_Amask;
02688 extern unsigned char GFX_Ashift;
02689 extern unsigned char GFX_bpp;
02690 extern uint32_t vga_capture_stride;
02691 
02692 template <const unsigned int bpp,typename BPPT> uint32_t VGA_CaptureConvertPixel(const BPPT raw) {
02693     unsigned char r,g,b;
02694 
02695     /* FIXME: Someday this code will have to deal with 10:10:10 32-bit RGB.
02696      * Also the 32bpp case shows how hacky this codebase is with regard to 32bpp color order support */
02697     if (bpp == 32) {
02698         if (GFX_bpp >= 24) {
02699             r = ((uint32_t)raw & (uint32_t)GFX_Rmask) >> (uint32_t)GFX_Rshift;
02700             g = ((uint32_t)raw & (uint32_t)GFX_Gmask) >> (uint32_t)GFX_Gshift;
02701             b = ((uint32_t)raw & (uint32_t)GFX_Bmask) >> (uint32_t)GFX_Bshift;
02702         }
02703         else {
02704             // hack alt, see vga_dac.cpp
02705             return raw;
02706         }
02707     }
02708     else if (bpp == 16) {
02709         /* 5:5:5 or 5:6:5 */
02710         r = ((uint16_t)raw & (uint16_t)GFX_Rmask) >> (uint16_t)GFX_Rshift;
02711         g = ((uint16_t)raw & (uint16_t)GFX_Gmask) >> (uint16_t)GFX_Gshift;
02712         b = ((uint16_t)raw & (uint16_t)GFX_Bmask) >> (uint16_t)GFX_Bshift;
02713 
02714         r <<= 3;
02715         g <<= (GFX_Gmask == 0x3F ? 2/*5:6:5*/ : 3/*5:5:5*/);
02716         b <<= 3;
02717     }
02718     else if (bpp == 8) {
02719         r = render.pal.rgb[raw].red;
02720         g = render.pal.rgb[raw].green;
02721         b = render.pal.rgb[raw].blue;
02722     }
02723     else {
02724         r = g = b = 0;
02725     }
02726 
02727     /* XRGB */
02728     return  ((uint32_t)r << (uint32_t)16ul) +
02729             ((uint32_t)g << (uint32_t) 8ul) +
02730             ((uint32_t)b                  );
02731 }
02732 
02733 template <const unsigned int bpp,typename BPPT> void VGA_CaptureWriteScanlineChecked(const BPPT *raw) {
02734     raw += vga_capture_current_rect.x;
02735 
02736     /* output is ALWAYS 32-bit XRGB */
02737     for (unsigned int i=0;(int)i < vga_capture_current_rect.w;i++)
02738         phys_writed(vga_capture_write_address+(i*4),
02739             VGA_CaptureConvertPixel<bpp,BPPT>(raw[i]));
02740 
02741     vga_capture_write_address += vga_capture_stride;
02742 }
02743 
02744 void VGA_CaptureWriteScanline(const uint8_t *raw) {
02745     // NTS: phys_writew() will cause a segfault if the address is beyond the end of memory,
02746     //      because it computes MemBase+addr
02747     PhysPt MemMax = (PhysPt)MEM_TotalPages() * (PhysPt)4096ul;
02748 
02749     if (vga_capture_write_address != (uint32_t)0 &&
02750         vga_capture_write_address < 0xFFFF0000ul &&
02751         (vga_capture_write_address + (vga_capture_current_rect.w*4ul)) <= MemMax) {
02752         switch (vga.draw.bpp) {
02753             case 32:    VGA_CaptureWriteScanlineChecked<32>((uint32_t*)raw); break;
02754             case 16:    VGA_CaptureWriteScanlineChecked<16>((uint16_t*)raw); break;
02755             case 15:    VGA_CaptureWriteScanlineChecked<16>((uint16_t*)raw); break;
02756             case 8:     VGA_CaptureWriteScanlineChecked< 8>((uint8_t *)raw); break;
02757         }
02758     }
02759     else {
02760         VGA_CaptureMarkError();
02761     }
02762 }
02763 
02764 static void VGA_VerticalTimer(Bitu /*val*/) {
02765     double current_time = PIC_GetCurrentEventTime();
02766 
02767     if (IS_PC98_ARCH) {
02768         GDC_display_plane = GDC_display_plane_pending;
02769         pc98_update_display_page_ptr();
02770     }
02771 
02772     if (VGA_IsCaptureEnabled()) {
02773         if (VGA_IsCaptureInProgress()) {
02774             VGA_MarkCaptureInProgress(false);
02775             VGA_MarkCaptureAcquired();
02776         }
02777 
02778         VGA_MarkCaptureRetrace();
02779         VGA_CaptureStartNextFrame();
02780         if (!VGA_CaptureValidateCurrentFrame())
02781             VGA_CaptureMarkError();
02782     }
02783 
02784     vga.draw.delay.framestart = current_time; /* FIXME: Anyone use this?? If not, remove it */
02785     vga_page_flip_occurred = false;
02786     vga.draw.has_split = false;
02787     vga_3da_polled = false;
02788 
02789     // FIXME: While this code is quite good at keeping time, I'm seeing drift "reset" back to
02790     //        14-30ms every video mode change. Is our INT 10h code that slow?
02791     /* compensate for floating point drift, make sure we're keeping the frame rate.
02792      * be very gentle about it. generally the drift is very small, and large adjustments can cause
02793      * DOS games dependent on vsync to fail/hang. */
02794     double shouldbe = (((double)vga_mode_frames_since_time_base * 1000.0) / vga_fps) + vga_mode_time_base;
02795     double vsync_err = shouldbe - current_time; /* < 0 too slow     > 0 too fast */
02796     double vsync_adj = vsync_err * 0.25;
02797     if (vsync_adj < -0.1) vsync_adj = -0.1;
02798     else if (vsync_adj > 0.1) vsync_adj = 0.1;
02799 
02800 //  LOG_MSG("Vsync err %.6fms adj=%.6fms",vsync_err,vsync_adj);
02801 
02802     float vsynctimerval;
02803     float vdisplayendtimerval;
02804     if( vsync.manual ) {
02805         static float hack_memory = 0.0f;
02806         if( hack_memory > 0.0f ) {
02807             uservsyncjolt+=hack_memory;
02808             hack_memory = 0.0f;
02809         }
02810 
02811         float faithful_framerate_adjustment_delay = 0.0f;
02812         if( vsync.faithful ) {
02813             const float gfxmode_vsyncrate   = 1000.0f/vga.draw.delay.vtotal;
02814             const float user_vsyncrate      = 1000.0f/vsync.period;
02815             const float framerate_diff      = user_vsyncrate - gfxmode_vsyncrate;
02816             if( framerate_diff >= 0 ) {
02817                 static float counter = 0.0f;
02818                 // User vsync rate is greater than the target vsync rate
02819                 const float adjustment_deadline = gfxmode_vsyncrate / framerate_diff;
02820                 counter += 1.0f;
02821                 if(counter >= adjustment_deadline) {
02822                     // double vsync duration this frame to resynchronize with the target vsync timing
02823                     faithful_framerate_adjustment_delay = vsync.period;
02824                     counter -= adjustment_deadline;
02825                 }
02826             } else {
02827                 // User vsync rate is less than the target vsync rate
02828 
02829                 // I don't have a good solution for this right now.
02830                 // I also don't have access to a 60Hz display for proper testing.
02831                 // Making an instant vsync is more difficult than making a long vsync.. because
02832                 // the emulated app's retrace loop must be able to detect that the retrace has both
02833                 // begun and ended.  So it's not as easy as adjusting timer durations.
02834                 // I think adding a hack to cause port 3da's code to temporarily force the
02835                 // vertical retrace bit to cycle could work.. Even then, it's possible that
02836                 // some shearing could be seen when the app draws two new frames during a single
02837                 // host refresh.
02838                 // Anyway, this could be worth dealing with for console ports since they'll be
02839                 // running at either 60 or 50Hz (below 70Hz).
02840                 /*
02841                 const float adjustment_deadline = -(gfxmode_vsyncrate / framerate_diff);
02842                 counter += 1.0f;
02843                 if(counter >= adjustment_deadline) {
02844                     // nullify vsync duration this frame to resynchronize with the target vsync timing
02845                     // TODO(AUG): proper low user vsync rate synchronization
02846                     faithful_framerate_adjustment_delay = -uservsyncperiod + 1.2f;
02847                     vsync_hackval = 10;
02848                     hack_memory = -1.2f;
02849                     counter -= adjustment_deadline;
02850                 }
02851                 */
02852             }
02853         }
02854 
02855         const Bitu persistent_sync_update_interval = 100;
02856         static Bitu persistent_sync_counter = persistent_sync_update_interval;
02857         Bitu current_tick = GetTicks();
02858         static Bitu jolt_tick = 0;
02859         if( uservsyncjolt > 0.0f ) {
02860             jolt_tick = (Bitu)current_tick;
02861 
02862             // set the update counter to a low value so that the user will almost
02863             // immediately see the effects of an auto-correction.  This gives the
02864             // user a chance to compensate for it.
02865             persistent_sync_counter = 50;
02866         }
02867 
02868         float real_diff = 0.0f;
02869         if( vsync.persistent ) {
02870             if( persistent_sync_counter == 0 ) {
02871                 float ticks_since_jolt = (signed long)current_tick - (signed long)jolt_tick;
02872                 double num_host_syncs_in_those_ticks = floor(ticks_since_jolt / vsync.period);
02873                 float diff_thing = ticks_since_jolt - (num_host_syncs_in_those_ticks * (double)vsync.period);
02874 
02875                 if( diff_thing > (vsync.period / 2.0f) ) real_diff = diff_thing - vsync.period;
02876                 else real_diff = diff_thing;
02877 
02878 //              LOG_MSG("diff is %f",real_diff);
02879 
02880                 if( ((real_diff > 0.0f) && (real_diff < 1.5f)) || ((real_diff < 0.0f) && (real_diff > -1.5f)) )
02881                     real_diff = 0.0f;
02882 
02883                 persistent_sync_counter = persistent_sync_update_interval;
02884             } else --persistent_sync_counter;
02885         }
02886 
02887 //      vsynctimerval       = uservsyncperiod + faithful_framerate_adjustment_delay + uservsyncjolt;
02888         vsynctimerval       = vsync.period - (real_diff/1.0f);  // formerly /2.0f
02889         vsynctimerval       += faithful_framerate_adjustment_delay + uservsyncjolt;
02890 
02891         // be sure to provide delay between end of one refresh, and start of the next
02892 //      vdisplayendtimerval = vsynctimerval - 1.18f;
02893 
02894         // be sure to provide delay between end of one refresh, and start of the next
02895         vdisplayendtimerval = vsynctimerval - (vga.draw.delay.vtotal - vga.draw.delay.vrstart);
02896 
02897         // in case some calculations above cause this.  this really shouldn't happen though.
02898         if( vdisplayendtimerval < 0.0f ) vdisplayendtimerval = 0.0f;
02899 
02900         uservsyncjolt = 0.0f;
02901     } else {
02902         // Standard vsync behaviour
02903         vsynctimerval       = (float)vga.draw.delay.vtotal;
02904         vdisplayendtimerval = (float)vga.draw.delay.vrstart;
02905     }
02906 
02907     {
02908         double fv;
02909 
02910         fv = vsynctimerval + vsync_adj;
02911         if (fv < 1) fv = 1;
02912         PIC_AddEvent(VGA_VerticalTimer,fv);
02913 
02914         fv = vdisplayendtimerval + vsync_adj;
02915         if (fv < 1) fv = 1;
02916         PIC_AddEvent(VGA_DisplayStartLatch,fv);
02917     }
02918     
02919     switch(machine) {
02920     case MCH_PCJR:
02921     case MCH_TANDY:
02922         // PCJr: Vsync is directly connected to the IRQ controller
02923         // Some earlier Tandy models are said to have a vsync interrupt too
02924         PIC_AddEvent(VGA_Other_VertInterrupt, (float)vga.draw.delay.vrstart, 1);
02925         PIC_AddEvent(VGA_Other_VertInterrupt, (float)vga.draw.delay.vrend, 0);
02926         // fall-through
02927     case MCH_AMSTRAD:
02928     case MCH_CGA:
02929     case MCH_MDA:
02930     case MCH_MCGA:
02931     case MCH_HERC:
02932         // MC6845-powered graphics: Loading the display start latch happens somewhere
02933         // after vsync off and before first visible scanline, so probably here
02934         VGA_DisplayStartLatch(0);
02935         break;
02936     case MCH_VGA:
02937         PIC_AddEvent(VGA_DisplayStartLatch, (float)vga.draw.delay.vrstart);
02938         PIC_AddEvent(VGA_PanningLatch, (float)vga.draw.delay.vrend);
02939         // EGA: 82c435 datasheet: interrupt happens at display end
02940         // VGA: checked with scope; however disabled by default by jumper on VGA boards
02941         // add a little amount of time to make sure the last drawpart has already fired
02942         PIC_AddEvent(VGA_VertInterrupt,(float)(vga.draw.delay.vdend + 0.005));
02943         break;
02944     case MCH_PC98:
02945         PIC_AddEvent(VGA_PanningLatch, (float)vga.draw.delay.vrend);
02946         PIC_AddEvent(VGA_VertInterrupt,(float)(vga.draw.delay.vrstart + 0.0001));
02947         break;
02948     case MCH_EGA:
02949         PIC_AddEvent(VGA_DisplayStartLatch, (float)vga.draw.delay.vrend);
02950         PIC_AddEvent(VGA_VertInterrupt,(float)(vga.draw.delay.vdend + 0.005));
02951         break;
02952     default:
02953         //E_Exit("This new machine needs implementation in VGA_VerticalTimer too.");
02954         PIC_AddEvent(VGA_DisplayStartLatch, (float)vga.draw.delay.vrstart);
02955         PIC_AddEvent(VGA_PanningLatch, (float)vga.draw.delay.vrend);
02956         PIC_AddEvent(VGA_VertInterrupt,(float)(vga.draw.delay.vdend + 0.005));
02957         break;
02958     }
02959     // for same blinking frequency with higher frameskip
02960     vga.draw.cursor.count++;
02961 
02962     if (IS_PC98_ARCH) {
02963         for (unsigned int i=0;i < 2;i++)
02964             pc98_gdc[i].cursor_advance();
02965     }
02966 
02967     //Check if we can actually render, else skip the rest
02968     if (vga.draw.vga_override || !RENDER_StartUpdate()) return;
02969 
02970     vga.draw.address_line = vga.config.hlines_skip;
02971     if (IS_EGAVGA_ARCH) VGA_Update_SplitLineCompare();
02972     vga.draw.address = vga.config.real_start;
02973     vga.draw.byte_panning_shift = 0;
02974 
02975     /* parallel system */
02976     if (vga_alt_new_mode) {
02977         /* the doublescan bit can be changed between frames, it can happen!
02978          *
02979          * "Show" by Majic 12: Two parts use 320x200 16-color planar mode, which by default
02980          *                     is programmed by INT 10h to use the doublescan bit and max scanline == 0.
02981          *                     These two parts then reprogram that register to turn off doublescan
02982          *                     and set max scanline == 1. This compensation is needed for those two
02983          *                     parts to show correctly. */
02984         if (IS_VGA_ARCH && (vga.crtc.maximum_scan_line & 0x80))
02985             vga.draw_2[0].doublescan_max = 1;
02986         else
02987             vga.draw_2[0].doublescan_max = 0;
02988 
02989         vga.draw_2[0].raster_scanline = 0;
02990         vga.draw_2[0].doublescan_count = 0;
02991 
02992         if (IS_EGAVGA_ARCH) {
02993             vga.draw_2[0].horz.current = 0;
02994             vga.draw_2[0].vert.current = 0;
02995 
02996             vga.draw_2[0].horz.current_char_pixel = 0;
02997             vga.draw_2[0].vert.current_char_pixel = vga.config.hlines_skip;
02998 
02999             VGA_Alt_UpdateCRTCPixels();
03000             VGA_Alt_UpdateCRTCAdd();
03001 
03002             vga.draw_2[0].vert.crtc_addr = vga.config.real_start + vga.draw.bytes_skip;
03003             vga.draw_2[0].horz.crtc_addr = vga.draw_2[0].vert.crtc_addr;
03004 
03005             VGA_Draw2_Recompute_CRTC_MaskAdd();
03006             VGA_Alt_CheckSplit();
03007         }
03008         else {
03009             vga.draw_2[0].horz.current = 0;
03010             vga.draw_2[0].vert.current = 0;
03011 
03012             vga.draw_2[0].horz.current_char_pixel = 0;
03013             vga.draw_2[0].vert.current_char_pixel = 0;
03014 
03015             VGA_Alt_UpdateCRTCPixels();
03016             VGA_Alt_UpdateCRTCAdd();
03017 
03018             vga.draw_2[0].vert.crtc_addr = vga.config.real_start;
03019             vga.draw_2[0].horz.crtc_addr = vga.draw_2[0].vert.crtc_addr;
03020 
03021             VGA_Draw2_Recompute_CRTC_MaskAdd();
03022         }
03023     }
03024 
03025     switch (vga.mode) {
03026     case M_EGA:
03027         if (vga.mem.memmask >= 0x1FFFFu) {
03028             /* EGA/VGA Mode control register 0x17 effects on the linear mask */
03029             if ((vga.crtc.maximum_scan_line&0x1fu) == 0) {
03030                 /* WARNING: These hacks only work IF max scanline value == 0 (no doubling).
03031                  *          The bit 0 (bit 13 replacement) mode here is needed for
03032                  *          Prehistorik 2 to display it's mode select/password entry screen
03033                  *          (the one with the scrolling background of various cavemen) */
03034 
03035                 /* if bit 0 is cleared, CGA compatible addressing is enabled.
03036                  * bit 13 is replaced by bit 0 of the row counter */
03037                 if (!(vga.crtc.mode_control&0x1u)) vga.draw.linear_mask &= ~0x8000u;
03038                 else vga.draw.linear_mask |= 0x8000u;
03039 
03040                 /* if bit 1 is cleared, Hercules compatible addressing is enabled.
03041                  * bit 14 is replaced by bit 0 of the row counter */
03042                 if (!(vga.crtc.mode_control&0x2u)) vga.draw.linear_mask &= ~0x10000u;
03043                 else vga.draw.linear_mask |= 0x10000u;
03044             }
03045             else {
03046                 if ((vga.crtc.mode_control&0x03u) != 0x03u) {
03047                     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");
03048                 }
03049             }
03050         }
03051         /* fall through */
03052     case M_LIN4:
03053         vga.draw.byte_panning_shift = 4u;
03054         vga.draw.address += vga.draw.bytes_skip;
03055         vga.draw.address *= vga.draw.byte_panning_shift;
03056         break;
03057     case M_VGA:
03058         /* TODO: Various SVGA chipsets have a bit to enable/disable 256KB wrapping */
03059         vga.draw.linear_mask = 0x3ffffu;
03060         if (svgaCard == SVGA_TsengET3K || svgaCard == SVGA_TsengET4K) {
03061             if (vga.config.addr_shift == 1) /* NTS: Remember the ET4K steps by 4 pixels, one per byteplane, treats BYTE and DWORD modes the same */
03062                 vga.draw.address *= 2u;
03063         }
03064         else if (machine == MCH_MCGA) {
03065             vga.draw.linear_mask = 0xffffu;
03066             vga.draw.address *= 2u;
03067             break;// don't fall through
03068         }
03069         else {
03070             vga.draw.address *= (Bitu)1u << (Bitu)vga.config.addr_shift; /* NTS: Remember the bizarre 4 x 4 mode most SVGA chipsets do */
03071         }
03072         /* fall through */
03073     case M_LIN8:
03074     case M_LIN15:
03075     case M_LIN16:
03076     case M_LIN24:
03077     case M_LIN32:
03078         vga.draw.byte_panning_shift = 4;
03079         vga.draw.address += vga.draw.bytes_skip;
03080         vga.draw.address *= vga.draw.byte_panning_shift;
03081         vga.draw.address += vga.draw.panning;
03082         break;
03083     case M_PACKED4:
03084         vga.draw.byte_panning_shift = 4u;
03085         vga.draw.address += vga.draw.bytes_skip;
03086         vga.draw.address *= vga.draw.byte_panning_shift;
03087         break;
03088     case M_PC98:
03089         vga.draw.linear_mask = 0xfff; // 1 page
03090         vga.draw.byte_panning_shift = 2;
03091         vga.draw.address += vga.draw.bytes_skip;
03092         vga.draw.cursor.address = vga.config.cursor_start;
03093         break;
03094     case M_TEXT:
03095         vga.draw.byte_panning_shift = 2;
03096         vga.draw.address += vga.draw.bytes_skip;
03097         // fall-through
03098     case M_TANDY_TEXT:
03099     case M_HERC_TEXT:
03100         if (machine==MCH_HERC || machine==MCH_MDA) vga.draw.linear_mask = 0xfff; // 1 page
03101         else if (IS_EGAVGA_ARCH || machine == MCH_MCGA) {
03102             if (vga.config.compatible_chain4 || svgaCard == SVGA_None)
03103                 vga.draw.linear_mask = vga.mem.memmask & 0x3ffff;
03104             else
03105                 vga.draw.linear_mask = vga.mem.memmask; // SVGA text mode
03106         }
03107         else vga.draw.linear_mask = 0x3fff; // CGA, Tandy 4 pages
03108         if (IS_EGAVGA_ARCH)
03109             vga.draw.cursor.address=vga.config.cursor_start<<vga.config.addr_shift;
03110         else
03111             vga.draw.cursor.address=vga.config.cursor_start*2;
03112         vga.draw.address *= 2;
03113 
03114         /* check for blinking and blinking change delay */
03115         FontMask[1]=(vga.draw.blinking & (unsigned int)(vga.draw.cursor.count >> 4u)) ?
03116             0 : 0xffffffff;
03117         /* if blinking is enabled, 'blink' will toggle between true
03118          * and false. Otherwise it's true */
03119         vga.draw.blink = ((vga.draw.blinking & (unsigned int)(vga.draw.cursor.count >> 4u))
03120             || !vga.draw.blinking) ? true:false;
03121         break;
03122     case M_HERC_GFX:
03123     case M_CGA4:
03124     case M_CGA2:
03125         vga.draw.address=(vga.draw.address*2u)&0x1fffu;
03126         break;
03127     case M_AMSTRAD: // Base address: No difference?
03128         vga.draw.address=(vga.draw.address*2u)&0xffffu;
03129         break;
03130     case M_CGA16:
03131     case M_TANDY2:case M_TANDY4:case M_TANDY16:
03132         vga.draw.address *= 2u;
03133         break;
03134     default:
03135         break;
03136     }
03137 
03138     if (IS_EGAVGA_ARCH)
03139         vga.draw.planar_mask = vga.draw.linear_mask >> 2;
03140     else
03141         vga.draw.planar_mask = vga.draw.linear_mask >> 1;
03142 
03143     /* ET4000 High Sierra DAC programs can change SVGA mode */
03144     if ((vga.mode == M_LIN15 || vga.mode == M_LIN16) && (svgaCard == SVGA_TsengET3K || svgaCard == SVGA_TsengET4K)) {
03145         if (et4k_highcolor_half_pixel_rate())
03146             VGA_DrawLine=VGA_Draw_LIN16_Line_2x;
03147         else
03148             VGA_DrawLine=VGA_Draw_LIN16_Line_HWMouse;
03149     }
03150 
03151     // check if some lines at the top off the screen are blanked
03152     float draw_skip = 0.0;
03153     if (GCC_UNLIKELY(vga.draw.vblank_skip > 0)) {
03154         draw_skip = (float)(vga.draw.delay.htotal * vga.draw.vblank_skip);
03155         vga.draw.address_line += (vga.draw.vblank_skip % vga.draw.address_line_total);
03156         vga.draw.address += vga.draw.address_add * (vga.draw.vblank_skip / vga.draw.address_line_total);
03157     }
03158 
03159     /* do VGA split now if line compare <= 0. NTS: vga.draw.split_line is defined as Bitu (unsigned integer) so we need the typecast. */
03160     if (GCC_UNLIKELY((Bits)vga.draw.split_line <= 0) && !vga_alt_new_mode) {
03161         VGA_ProcessSplit();
03162 
03163         /* if vblank_skip != 0, line compare can become a negative value! Fixes "Warlock" 1992 demo by Warlock */
03164         if (GCC_UNLIKELY((Bits)vga.draw.split_line < 0)) {
03165             vga.draw.address_line += (Bitu)(-((Bits)vga.draw.split_line) % (Bits)vga.draw.address_line_total);
03166             vga.draw.address += vga.draw.address_add * (Bitu)(-((Bits)vga.draw.split_line) / (Bits)vga.draw.address_line_total);
03167         }
03168     }
03169 
03170     // add the draw event
03171     switch (vga.draw.mode) {
03172     case DRAWLINE:
03173     case EGALINE:
03174         if (GCC_UNLIKELY(vga.draw.lines_done < vga.draw.lines_total)) {
03175             LOG(LOG_VGAMISC,LOG_NORMAL)( "Lines left: %d", 
03176                 (int)(vga.draw.lines_total-vga.draw.lines_done));
03177             if (vga.draw.mode==EGALINE) PIC_RemoveEvents(VGA_DrawEGASingleLine);
03178             else PIC_RemoveEvents(VGA_DrawSingleLine);
03179             vga_mode_frames_since_time_base++;
03180 
03181             if (VGA_IsCaptureEnabled())
03182                 VGA_ProcessScanline(NULL);
03183 
03184             RENDER_EndUpdate(true);
03185         }
03186         vga.draw.lines_done = 0;
03187         if (vga.draw.mode==EGALINE)
03188             PIC_AddEvent(VGA_DrawEGASingleLine,(float)(vga.draw.delay.htotal/4.0 + draw_skip));
03189         else PIC_AddEvent(VGA_DrawSingleLine,(float)(vga.draw.delay.htotal/4.0 + draw_skip));
03190         break;
03191     }
03192 }
03193 
03194 void VGA_CheckScanLength(void) {
03195     switch (vga.mode) {
03196     case M_EGA:
03197     case M_LIN4:
03198         if ((machine==MCH_EGA)&&(vga.crtc.mode_control&0x8))
03199             vga.draw.address_add=vga.config.scan_len*16; // TODO
03200         else
03201             vga.draw.address_add=vga.config.scan_len*(8u<<(unsigned int)vga.config.addr_shift);
03202 
03203         if (IS_EGA_ARCH && (vga.seq.clocking_mode&4))
03204             vga.draw.address_add*=2;
03205         break;
03206     case M_PACKED4:
03207         vga.draw.address_add=vga.config.scan_len*8;
03208         break;
03209     case M_VGA:
03210     case M_LIN8:
03211     case M_LIN15:
03212     case M_LIN16:
03213     case M_LIN24:
03214     case M_LIN32:
03215         if (vga.mode == M_VGA) {
03216             if (svgaCard == SVGA_TsengET3K || svgaCard == SVGA_TsengET4K) {
03217                 /* Observed ET4000AX behavior:
03218                  *    byte mode OR dword mode scans 256-color 4-pixel groups byte by byte from
03219                  *    planar RAM. word mode scans every other byte (skips by two).
03220                  *    We can just scan the buffer normally as linear because of the address
03221                  *    translation carried out by the ET4000 in chained mode:
03222                  *
03223                  *    plane = (addr & 3)   addr = (addr >> 2)
03224                  *
03225                  *    TODO: Validate that this is correct. */
03226                 vga.draw.address_add=vga.config.scan_len*((vga.config.addr_shift == 1)?16:8);
03227             }
03228             else if (machine == MCH_MCGA) {
03229                 vga.draw.address_add=vga.draw.blocks*4;
03230             }
03231             else {
03232                 /* Most (almost ALL) VGA clones render chained modes as 4 8-bit planes one DWORD apart.
03233                  * They all act as if writes to chained VGA memory are translated as:
03234                  * addr = ((addr & ~3) << 2) + (addr & 3) */
03235                 vga.draw.address_add=(unsigned int)vga.config.scan_len*((unsigned int)(2*4)<<(unsigned int)vga.config.addr_shift);
03236             }
03237         }
03238         else {
03239             /* the rest (SVGA modes) can be rendered with sanity */
03240             if (svgaCard == SVGA_TsengET3K || svgaCard == SVGA_TsengET4K || svgaCard == SVGA_S3Trio) {
03241                 // Real hardware testing (Tseng ET4000) shows that in SVGA modes the
03242                 // standard VGA byte/word/dword mode bits have no effect.
03243                 //
03244                 // This was verified by using TMOTSENG.EXE on real hardware, setting
03245                 // mode 0x2F (640x400 256-color mode) and then playing with the
03246                 // word/dword/byte mode bits.
03247                 //
03248                 // Also noted is that the "count memory clock by 4" bit is set. If
03249                 // you clear it, the hardware acts as if it completes the scanline
03250                 // early and 3/4ths of the screen is the last 4-pixel block repeated.
03251                 //
03252                 // S3 Trio: Testing on a real S3 Virge PCI card shows that the
03253                 //          byte/word/dword bits have no effect on SVGA modes other
03254                 //          than the 16-color 800x600 SVGA mode.
03255                 vga.draw.address_add=vga.config.scan_len*(unsigned int)(2u<<2u);
03256             }
03257             else {
03258                 // Other cards (?)
03259                 vga.draw.address_add=vga.config.scan_len*(unsigned int)(2u<<vga.config.addr_shift);
03260             }
03261         }
03262         break;
03263     case M_PC98:
03264         vga.draw.address_add=vga.draw.blocks;
03265         break;
03266     case M_TEXT:
03267     case M_CGA2:
03268     case M_CGA4:
03269     case M_CGA16:
03270     case M_AMSTRAD: // Next line.
03271         if (IS_EGAVGA_ARCH)
03272             vga.draw.address_add=vga.config.scan_len*(unsigned int)(2u<<vga.config.addr_shift);
03273         else
03274             vga.draw.address_add=vga.draw.blocks;
03275         break;
03276     case M_TANDY2:
03277         if (machine == MCH_MCGA)
03278             vga.draw.address_add=vga.draw.blocks;
03279         else
03280             vga.draw.address_add=vga.draw.blocks/4u;
03281         break;
03282     case M_TANDY4:
03283         vga.draw.address_add=vga.draw.blocks;
03284         break;
03285     case M_TANDY16:
03286         vga.draw.address_add=vga.draw.blocks;
03287         break;
03288     case M_TANDY_TEXT:
03289         vga.draw.address_add=vga.draw.blocks*2;
03290         break;
03291     case M_HERC_TEXT:
03292         vga.draw.address_add=vga.draw.blocks*2;
03293         break;
03294     case M_HERC_GFX:
03295         vga.draw.address_add=vga.draw.blocks;
03296         break;
03297     default:
03298         vga.draw.address_add=vga.draw.blocks*8;
03299         break;
03300     }
03301 }
03302 
03303 void VGA_ActivateHardwareCursor(void) {
03304     bool hwcursor_active=false;
03305     if (svga.hardware_cursor_active) {
03306         if (svga.hardware_cursor_active()) hwcursor_active=true;
03307     }
03308     if (hwcursor_active && vga.mode != M_LIN24) {
03309         switch(vga.mode) {
03310         case M_LIN32:
03311             VGA_DrawLine=VGA_Draw_LIN32_Line_HWMouse;
03312             break;
03313         case M_LIN15:
03314         case M_LIN16:
03315             if ((svgaCard == SVGA_TsengET3K || svgaCard == SVGA_TsengET4K) && et4k_highcolor_half_pixel_rate())
03316                 VGA_DrawLine=VGA_Draw_LIN16_Line_2x;
03317             else
03318                 VGA_DrawLine=VGA_Draw_LIN16_Line_HWMouse;
03319             break;
03320         case M_LIN8:
03321             VGA_DrawLine=VGA_Draw_VGA_Line_Xlat32_HWMouse;
03322             break;
03323         default:
03324             VGA_DrawLine=VGA_Draw_VGA_Line_HWMouse;
03325             break;
03326         }
03327     } else {
03328         switch(vga.mode) {
03329         case M_LIN8:
03330             VGA_DrawLine=VGA_Draw_Xlat32_Linear_Line;
03331             break;
03332         case M_LIN24:
03333             VGA_DrawLine=VGA_Draw_Linear_Line_24_to_32;
03334             break;
03335 #if SDL_BYTEORDER == SDL_LIL_ENDIAN && defined(MACOSX) /* Mac OS X Intel builds use a weird RGBA order (alpha in the low 8 bits) */
03336         case M_LIN32:
03337             VGA_DrawLine=VGA_Draw_LIN32_Line_HWMouse;
03338             break;
03339 #endif
03340         default:
03341             VGA_DrawLine=VGA_Draw_Linear_Line;
03342             break;
03343         }
03344     }
03345 }
03346 
03347 void VGA_SetupDrawing(Bitu /*val*/) {
03348     if (vga.mode==M_ERROR) {
03349         PIC_RemoveEvents(VGA_VerticalTimer);
03350         PIC_RemoveEvents(VGA_PanningLatch);
03351         PIC_RemoveEvents(VGA_DisplayStartLatch);
03352         return;
03353     }
03354     // user choosable special trick support
03355     // multiscan -- zooming effects - only makes sense if linewise is enabled
03356     // linewise -- scan display line by line instead of 4 blocks
03357     // keep compatibility with other builds of DOSBox for vgaonly.
03358     vga.draw.doublescan_effect = true;
03359     vga.draw.render_step = 0;
03360     vga.draw.render_max = 1;
03361 
03362     // set the drawing mode
03363     switch (machine) {
03364     case MCH_CGA:
03365     case MCH_MCGA:
03366     case MCH_PCJR:
03367     case MCH_TANDY:
03368         vga.draw.mode = DRAWLINE;
03369         break;
03370     case MCH_EGA:
03371         // Note: The Paradise SVGA uses the same panning mechanism as EGA
03372         vga.draw.mode = EGALINE;
03373         break;
03374     case MCH_VGA:
03375     case MCH_PC98:
03376         if (svgaCard==SVGA_None) {
03377             vga.draw.mode = DRAWLINE;
03378             break;
03379         }
03380         // fall-through
03381     default:
03382         vga.draw.mode = DRAWLINE;
03383         break;
03384     }
03385     
03386     /* Calculate the FPS for this screen */
03387     Bitu oscclock = 0, clock;
03388     Bitu htotal, hdend, hbstart, hbend, hrstart, hrend;
03389     Bitu vtotal, vdend, vbstart, vbend, vrstart, vrend;
03390     Bitu hbend_mask, vbend_mask;
03391     Bitu vblank_skip;
03392 
03393     /* NTS: PC-98 emulation re-uses VGA state FOR NOW.
03394      *      This will slowly change to accomodate PC-98 display controller over time
03395      *      and IS_PC98_ARCH will become it's own case statement. */
03396 
03397     if (IS_PC98_ARCH) {
03398         hdend = pc98_gdc[GDC_MASTER].active_display_words_per_line;
03399         hbstart = hdend;
03400         hrstart = hdend + pc98_gdc[GDC_MASTER].horizontal_front_porch_width;
03401         hrend = hrstart + pc98_gdc[GDC_MASTER].horizontal_sync_width;
03402         htotal = hrend + pc98_gdc[GDC_MASTER].horizontal_back_porch_width;
03403         hbend = htotal;
03404 
03405         vdend = pc98_gdc[GDC_MASTER].active_display_lines;
03406         vbstart = vdend;
03407         vrstart = vdend + pc98_gdc[GDC_MASTER].vertical_front_porch_width;
03408         vrend = vrstart + pc98_gdc[GDC_MASTER].vertical_sync_width;
03409         vtotal = vrend + pc98_gdc[GDC_MASTER].vertical_back_porch_width;
03410         vbend = vtotal;
03411 
03412         // perhaps if the game makes a custom mode, it might choose different active display regions
03413         // for text and graphics. allow that here.
03414         // NTS: Remember that the graphics (slave) GDC is programmed in "words" which in graphics mode
03415         //      means 16-pixel wide units.
03416         if (hdend < (pc98_gdc[GDC_SLAVE].active_display_words_per_line * 2U))
03417             hdend = (pc98_gdc[GDC_SLAVE].active_display_words_per_line * 2U);
03418         if (vdend < (pc98_gdc[GDC_SLAVE].active_display_lines))
03419             vdend = (pc98_gdc[GDC_SLAVE].active_display_lines);
03420 
03421         // TODO: The GDC rendering should allow different active display regions to render
03422         //       properly over one another.
03423 
03424         // TODO: Found a monitor document that lists two different scan rates for PC-98:
03425         //
03426         //       640x400  25.175MHz dot clock  70.15Hz refresh  31.5KHz horizontal refresh (basically, VGA)
03427         //       640x400  21.05MHz dot clock   56.42Hz refresh  24.83Hz horizontal refresh (original spec?)
03428 
03429         if (false/*future 15KHz hsync*/) {
03430             oscclock = 14318180;
03431         }
03432         else if (!pc98_31khz_mode/*24KHz hsync*/) {
03433             oscclock = 21052600;
03434         }
03435         else {/*31KHz VGA-like hsync*/
03436             oscclock = 25175000;
03437         }
03438 
03439         clock = oscclock / 8;
03440     }
03441     else if (IS_EGAVGA_ARCH) {
03442         htotal = vga.crtc.horizontal_total;
03443         hdend = vga.crtc.horizontal_display_end;
03444         hbend = vga.crtc.end_horizontal_blanking&0x1F;
03445         hbstart = vga.crtc.start_horizontal_blanking;
03446         hrstart = vga.crtc.start_horizontal_retrace;
03447 
03448         vtotal= vga.crtc.vertical_total | ((vga.crtc.overflow & 1u) << 8u);
03449         vdend = vga.crtc.vertical_display_end | ((vga.crtc.overflow & 2u) << 7u);
03450         vbstart = vga.crtc.start_vertical_blanking | ((vga.crtc.overflow & 0x08u) << 5u);
03451         vrstart = vga.crtc.vertical_retrace_start + ((vga.crtc.overflow & 0x04u) << 6u);
03452         
03453         if (IS_VGA_ARCH || IS_PC98_ARCH) {
03454             // additional bits only present on vga cards
03455             htotal |= (vga.s3.ex_hor_overflow & 0x1u) << 8u;
03456             htotal += 3u;
03457             hdend |= (vga.s3.ex_hor_overflow & 0x2u) << 7u;
03458             hbend |= (vga.crtc.end_horizontal_retrace&0x80u) >> 2u;
03459             hbstart |= (vga.s3.ex_hor_overflow & 0x4u) << 6u;
03460             hrstart |= (vga.s3.ex_hor_overflow & 0x10u) << 4u;
03461             hbend_mask = 0x3fu;
03462             
03463             vtotal |= (vga.crtc.overflow & 0x20u) << 4u;
03464             vtotal |= (vga.s3.ex_ver_overflow & 0x1u) << 10u;
03465             vdend |= (vga.crtc.overflow & 0x40u) << 3u; 
03466             vdend |= (vga.s3.ex_ver_overflow & 0x2u) << 9u;
03467             vbstart |= (vga.crtc.maximum_scan_line & 0x20u) << 4u;
03468             vbstart |= (vga.s3.ex_ver_overflow & 0x4u) << 8u;
03469             vrstart |= ((vga.crtc.overflow & 0x80u) << 2u);
03470             vrstart |= (vga.s3.ex_ver_overflow & 0x10u) << 6u;
03471             vbend_mask = 0xffu;
03472         } else { // EGA
03473             hbend_mask = 0x1fu;
03474             vbend_mask = 0x1fu;
03475         }
03476         htotal += 2;
03477         vtotal += 2;
03478         hdend += 1;
03479         vdend += 1;
03480 
03481         // horitzontal blanking
03482         if (hbend <= (hbstart & hbend_mask)) hbend += hbend_mask + 1;
03483         hbend += hbstart - (hbstart & hbend_mask);
03484         
03485         // horizontal retrace
03486         hrend = vga.crtc.end_horizontal_retrace & 0x1f;
03487         if (hrend <= (hrstart&0x1f)) hrend += 32;
03488         hrend += hrstart - (hrstart&0x1f);
03489         if (hrend > hbend) hrend = hbend; // S3 BIOS (???)
03490         
03491         // vertical retrace
03492         vrend = vga.crtc.vertical_retrace_end & 0xf;
03493         if (vrend <= (vrstart&0xf)) vrend += 16;
03494         vrend += vrstart - (vrstart&0xf);
03495 
03496         // vertical blanking
03497         vbend = vga.crtc.end_vertical_blanking & vbend_mask;
03498         if (vbstart != 0) {
03499         // Special case vbstart==0:
03500         // Most graphics cards agree that lines zero to vbend are
03501         // blanked. ET4000 doesn't blank at all if vbstart==vbend.
03502         // ET3000 blanks lines 1 to vbend (255/6 lines).
03503             vbstart += 1;
03504             if (vbend <= (vbstart & vbend_mask)) vbend += vbend_mask + 1;
03505             vbend += vbstart - (vbstart & vbend_mask);
03506         }
03507         vbend++;
03508 
03509         // TODO: Found a monitor document that lists two different scan rates for PC-98:
03510         //
03511         //       640x400  25.175MHz dot clock  70.15Hz refresh  31.5KHz horizontal refresh (basically, VGA)
03512         //       640x400  21.05MHz dot clock   56.42Hz refresh  24.83Hz horizontal refresh (original spec?)
03513 
03514         if (svga.get_clock) {
03515             oscclock = svga.get_clock();
03516         } else if (vga.mode == M_PC98) {
03517             if (false/*future 15KHz hsync*/) {
03518                 oscclock = 14318180;
03519             }
03520             else if (!pc98_31khz_mode/*24KHz hsync*/) {
03521                 oscclock = 21052600;
03522             }
03523             else {/*31KHz VGA-like hsync*/
03524                 oscclock = 25175000;
03525             }
03526         } else {
03527             switch ((vga.misc_output >> 2) & 3) {
03528             case 0: 
03529                 oscclock = (machine==MCH_EGA) ? (PIT_TICK_RATE*12) : 25175000;
03530                 break;
03531             case 1:
03532             default:
03533                 oscclock = (machine==MCH_EGA) ? 16257000 : 28322000;
03534                 break;
03535             }
03536         }
03537 
03538         /* Check for 8 or 9 character clock mode */
03539         if (vga.seq.clocking_mode & 1 ) clock = oscclock/8; else clock = oscclock/9;
03540         if (vga.mode==M_LIN15 || vga.mode==M_LIN16) clock *= 2;
03541         /* Check for pixel doubling, master clock/2 */
03542         /* NTS: VGA 256-color mode has a dot clock NOT divided by two, because real hardware reveals
03543          *      that internally the card processes pixels 4 bits per cycle through a 8-bit shift
03544          *      register and a bit is set to latch the 8-bit value out to the DAC every other cycle. */
03545         if (vga.seq.clocking_mode & 0x8) {
03546             clock /=2;
03547             oscclock /= 2;
03548         }
03549 
03550         if (svgaCard==SVGA_S3Trio) {
03551             // support for interlacing used by the S3 BIOS and possibly other drivers
03552             if (vga.s3.reg_42 & 0x20) {
03553                 vtotal *= 2;    vdend *= 2;
03554                 vbstart *= 2;   vbend *= 2;
03555                 vrstart *= 2;   vrend *= 2;
03556                 //clock /= 2;
03557         }
03558         }
03559 
03560         /* FIXME: This is based on correcting the hicolor mode for MFX/Transgression 2.
03561          *        I am not able to test against the Windows drivers at this time. */
03562         if ((svgaCard == SVGA_TsengET3K || svgaCard == SVGA_TsengET4K) && et4k_highcolor_half_pixel_rate())
03563             clock /= 2;
03564     } else {
03565         // not EGAVGA_ARCH
03566         vga.draw.split_line = 0x10000;  // don't care
03567 
03568         htotal = vga.other.htotal + 1u;
03569         hdend = vga.other.hdend;
03570 
03571         if (machine == MCH_MCGA) {
03572             // it seems MCGA follows the EGA/VGA model of encoding active display
03573             // as N - 1 rather than CGA/MDA model of N.
03574             //
03575             // TODO: Verify this on real hardware. Some code in DOSLIB hw/vga2/test5.c
03576             //       attempts to set active display width which can confirm this.
03577             //
03578             //       This is so far based on CRTC register dumps from real MCGA hardware
03579             //       for each mode.
03580             hdend++;
03581         }
03582 
03583         hbstart = hdend;
03584         hbend = htotal;
03585         hrstart = vga.other.hsyncp;
03586         hrend = hrstart + (vga.other.hsyncw) ;
03587 
03588         vga.draw.address_line_total = vga.other.max_scanline + 1u;
03589 
03590         if (machine == MCH_MCGA) {
03591             // mode 0x11 and 0x13 on real hardware have max_scanline == 0,
03592             // will need doubling again to work properly.
03593             if (vga.other.mcga_mode_control & 3)
03594                 vga.draw.address_line_total *= 2;
03595 
03596             // 80x25 has horizontal timings like 40x25, but the hardware
03597             // knows if 80x25 text is intended by bit 0 of port 3D8h and
03598             // adjusts appropriately.
03599             //
03600             // The BIOS will only set bit 0 for modes 2 and 3 (80x25) and
03601             // will not set it for any other mode.
03602             if (vga.tandy.mode_control & 1) {
03603                 htotal *= 2;
03604                 hdend *= 2;
03605                 hbstart *= 2;
03606                 hbend *= 2;
03607                 hrstart *= 2;
03608                 hrend *= 2;
03609             }
03610         }
03611 
03612         vtotal = vga.draw.address_line_total * (vga.other.vtotal+1u)+vga.other.vadjust;
03613         vdend = vga.draw.address_line_total * vga.other.vdend;
03614         vrstart = vga.draw.address_line_total * vga.other.vsyncp;
03615         vrend = vrstart + 16; // vsync width is fixed to 16 lines on the MC6845 TODO Tandy
03616         vbstart = vdend;
03617         vbend = vtotal;
03618 
03619         switch (machine) {
03620         case MCH_AMSTRAD:
03621             clock=(16000000/2)/8;
03622             break;
03623         case MCH_CGA:
03624         case TANDY_ARCH_CASE:
03625             clock = (PIT_TICK_RATE*12)/8;
03626             // FIXME: This is wrong for Tandy/PCjr 16-color modes and 640-wide 4-color mode
03627             if (vga.mode != M_TANDY2) {
03628                 if (!(vga.tandy.mode_control & 1)) clock /= 2;
03629             }
03630             oscclock = clock * 8;
03631             break;
03632         case MCH_MCGA:
03633             clock = 25175000 / 2 / 8;//FIXME: Guess. Verify
03634             if (!(vga.tandy.mode_control & 1)) clock /= 2;
03635             oscclock = clock * 2 * 8;
03636             break;
03637         case MCH_MDA:
03638         case MCH_HERC:
03639             oscclock=16257000;
03640             if (vga.mode == M_HERC_GFX)
03641                 clock=oscclock/8;
03642             else
03643                 clock=oscclock/9;
03644 
03645             if (vga.herc.mode_control & 0x2) clock /= 2;
03646             break;
03647         default:
03648             clock = (PIT_TICK_RATE*12)/8;
03649             oscclock = clock * 8;
03650             break;
03651         }
03652         vga.draw.delay.hdend = hdend*1000.0/clock; //in milliseconds
03653     }
03654 #if C_DEBUG
03655     LOG(LOG_VGA,LOG_NORMAL)("h total %3d end %3d blank (%3d/%3d) retrace (%3d/%3d)",
03656         (int)htotal, (int)hdend, (int)hbstart, (int)hbend, (int)hrstart, (int)hrend );
03657     LOG(LOG_VGA,LOG_NORMAL)("v total %3d end %3d blank (%3d/%3d) retrace (%3d/%3d)",
03658         (int)vtotal, (int)vdend, (int)vbstart, (int)vbend, (int)vrstart, (int)vrend );
03659 #endif
03660     if (!htotal) return;
03661     if (!vtotal) return;
03662     
03663     // The screen refresh frequency
03664     double fps;
03665     extern double vga_force_refresh_rate;
03666     if (vga_force_refresh_rate > 0) {
03667         /* force the VGA refresh rate by setting fps and bending the clock to our will */
03668         LOG(LOG_VGA,LOG_NORMAL)("VGA forced refresh rate in effect, %.3f",vga_force_refresh_rate);
03669         fps=vga_force_refresh_rate;
03670         clock=((double)(vtotal*htotal))*fps;
03671     }
03672     else {
03673         // The screen refresh frequency
03674         fps=(double)clock/(vtotal*htotal);
03675         LOG(LOG_VGA,LOG_NORMAL)("VGA refresh rate is now, %.3f",fps);
03676     }
03677 
03678     /* clip display end to stay within vtotal ("Monolith" demo part 4 320x570 mode fix) */
03679     if (vdend > vtotal) {
03680         LOG(LOG_VGA,LOG_WARN)("VGA display end greater than vtotal!");
03681         vdend = vtotal;
03682     }
03683 
03684     // Horizontal total (that's how long a line takes with whistles and bells)
03685     vga.draw.delay.htotal = htotal*1000.0/clock; //in milliseconds
03686     // Start and End of horizontal blanking
03687     vga.draw.delay.hblkstart = hbstart*1000.0/clock; //in milliseconds
03688     vga.draw.delay.hblkend = hbend*1000.0/clock; 
03689     // Start and End of horizontal retrace
03690     vga.draw.delay.hrstart = hrstart*1000.0/clock;
03691     vga.draw.delay.hrend = hrend*1000.0/clock;
03692     // Start and End of vertical blanking
03693     vga.draw.delay.vblkstart = vbstart * vga.draw.delay.htotal;
03694     vga.draw.delay.vblkend = vbend * vga.draw.delay.htotal;
03695     // Start and End of vertical retrace pulse
03696     vga.draw.delay.vrstart = vrstart * vga.draw.delay.htotal;
03697     vga.draw.delay.vrend = vrend * vga.draw.delay.htotal;
03698 
03699     // Vertical blanking tricks
03700     vblank_skip = 0;
03701     if ((IS_VGA_ARCH || IS_PC98_ARCH) && !ignore_vblank_wraparound) { // others need more investigation
03702         if (vbstart < vtotal) { // There will be no blanking at all otherwise
03703             if (vbend > vtotal) {
03704                 // blanking wraps to the start of the screen
03705                 vblank_skip = vbend&0x7f;
03706                 
03707                 // on blanking wrap to 0, the first line is not blanked
03708                 // this is used by the S3 BIOS and other S3 drivers in some SVGA modes
03709                 if ((vbend&0x7f)==1) vblank_skip = 0;
03710                 
03711                 // it might also cut some lines off the bottom
03712                 if (vbstart < vdend) {
03713                     vdend = vbstart;
03714                 }
03715                 LOG(LOG_VGA,LOG_WARN)("Blanking wrap to line %d", (int)vblank_skip);
03716             } else if (vbstart<=1) {
03717                 // blanking is used to cut lines at the start of the screen
03718                 vblank_skip = vbend;
03719                 LOG(LOG_VGA,LOG_WARN)("Upper %d lines of the screen blanked", (int)vblank_skip);
03720             } else if (vbstart < vdend) {
03721                 if (vbend < vdend) {
03722                     // the game wants a black bar somewhere on the screen
03723                     LOG(LOG_VGA,LOG_WARN)("Unsupported blanking: line %d-%d",(int)vbstart,(int)vbend);
03724                 } else {
03725                     // blanking is used to cut off some lines from the bottom
03726                     vdend = vbstart;
03727                 }
03728             }
03729             vdend -= vblank_skip;
03730         }
03731     }
03732     vga.draw.vblank_skip = vblank_skip;
03733 
03734     // Display end
03735     vga.draw.delay.vdend = vdend * vga.draw.delay.htotal;
03736 
03737     // EGA frequency dependent monitor palette
03738     if (machine == MCH_EGA) {
03739         if (vga.misc_output & 1) {
03740             // EGA card is in color mode
03741             if ((1.0f/vga.draw.delay.htotal) > 19.0f) {
03742                 // 64 color EGA mode
03743                 VGA_ATTR_SetEGAMonitorPalette(EGA);
03744             } else {
03745                 // 16 color CGA mode compatibility
03746                 VGA_ATTR_SetEGAMonitorPalette(CGA);
03747             }
03748         } else {
03749             // EGA card in monochrome mode
03750             // It is not meant to be autodetected that way, you either
03751             // have a monochrome or color monitor connected and
03752             // the EGA switches configured appropriately.
03753             // But this would only be a problem if a program sets 
03754             // the adapter to monochrome mode and still expects color output.
03755             // Such a program should be shot to the moon...
03756             VGA_ATTR_SetEGAMonitorPalette(MONO);
03757         }
03758     }
03759 
03760     /*
03761       6  Horizontal Sync Polarity. Negative if set
03762       7  Vertical Sync Polarity. Negative if set
03763          Bit 6-7 indicates the number of lines on the display:
03764             1:  400, 2: 350, 3: 480
03765     */
03766     //Try to determine the pixel size, aspect correct is based around square pixels
03767 
03768     //Base pixel width around 100 clocks horizontal
03769     //For 9 pixel text modes this should be changed, but we don't support that anyway :)
03770     //Seems regular vga only listens to the 9 char pixel mode with character mode enabled
03771     //Base pixel height around vertical totals of modes that have 100 clocks horizontal
03772     //Different sync values gives different scaling of the whole vertical range
03773     //VGA monitor just seems to thighten or widen the whole vertical range
03774 
03775     vga.draw.resizing=false;
03776     vga.draw.has_split=false;
03777     vga.draw.vret_triggered=false;
03778 
03779     if (vga_alt_new_mode) {
03780         vga.draw_2[0].doublescan_count = 0;
03781         vga.draw_2[0].doublescan_max = 0;
03782     }
03783 
03784     //Check to prevent useless black areas
03785     if (hbstart<hdend) hdend=hbstart;
03786     if ((!(IS_VGA_ARCH || IS_PC98_ARCH)) && (vbstart<vdend)) vdend=vbstart;
03787 
03788     Bitu width=hdend;
03789     Bitu height=vdend;
03790 
03791     if (IS_EGAVGA_ARCH || IS_PC98_ARCH) {
03792         vga.draw.address_line_total=(vga.crtc.maximum_scan_line&0x1fu)+1u;
03793         switch(vga.mode) {
03794         case M_CGA16:
03795         case M_CGA2:
03796         case M_CGA4:
03797         case M_PC98:
03798         case M_TEXT:
03799             if (!vga_alt_new_mode) {
03800                 // these use line_total internal
03801                 // doublescanning needs to be emulated by renderer doubleheight
03802                 // EGA has no doublescanning bit at 0x80
03803                 if (vga.crtc.maximum_scan_line&0x80) {
03804                     // vga_draw only needs to draw every second line
03805                     height /= 2;
03806                 }
03807                 break;
03808             }
03809             /* fall through if vga_alt_new_mode */
03810         default:
03811             vga.draw.doublescan_effect = vga.draw.doublescan_set;
03812 
03813             if (vga_alt_new_mode) {
03814                 if (IS_VGA_ARCH && (vga.crtc.maximum_scan_line & 0x80))
03815                     vga.draw_2[0].doublescan_max = 1;
03816                 else
03817                     vga.draw_2[0].doublescan_max = 0;
03818 
03819                 if (!vga.draw.doublescan_effect) {
03820                     if (IS_VGA_ARCH && (vga.crtc.maximum_scan_line & 0x80)) /* CGA/EGA modes on VGA */
03821                         height /= 2;
03822                     else if ((vga.crtc.maximum_scan_line & 1) == 1) /* multiple of 2, 256-color mode on VGA, for example */
03823                         height /= 2;
03824                     else
03825                         vga.draw.doublescan_effect = true;
03826                 }
03827             }
03828             else {
03829                 if (vga.crtc.maximum_scan_line & 0x80)
03830                     vga.draw.address_line_total *= 2;
03831 
03832                 /* if doublescan=false and line_total is even, then halve the height.
03833                  * the VGA raster scan will skip every other line to accomodate that. */
03834                 if ((!vga.draw.doublescan_effect) && (vga.draw.address_line_total & 1) == 0)
03835                     height /= 2;
03836                 else
03837                     vga.draw.doublescan_effect = true;
03838             }
03839 
03840             break;
03841         }
03842     }
03843 
03844     if (!vga.draw.doublescan_effect)
03845         vga.draw.render_max = 2; /* count all lines but render only every other line */
03846 
03847     //Set the bpp
03848     Bitu bpp;
03849     switch (vga.mode) {
03850     case M_LIN15:
03851         bpp = 15;
03852         break;
03853     case M_LIN16:
03854         bpp = 16;
03855         break;
03856     case M_LIN24:
03857     case M_LIN32:
03858         bpp = 32;
03859         break;
03860     default:
03861         bpp = 8;
03862         break;
03863     }
03864     vga.draw.linear_base = vga.mem.linear;
03865     vga.draw.linear_mask = vga.mem.memmask;
03866 
03867     /* parallel system */
03868     if (vga_alt_new_mode)
03869         VGA_Draw2_Recompute_CRTC_MaskAdd();
03870 
03871     /* Some games and plenty of demoscene productions like to rely on
03872      * the fact that the standard VGA modes wrap around at 256KB even
03873      * on SVGA hardware. Without this check, those demos will show
03874      * credits that scroll upward to blackness before "popping" back
03875      * onto the screen. */
03876     if (IS_VGA_ARCH) {
03877         /* NTS: S3 emulation ties "compatible chain4" to CRTC register 31 bit 3 which controls
03878          *      whether access to > 256KB of video RAM is enabled, which is why it's used here */
03879         if (vga.config.compatible_chain4 || svgaCard == SVGA_None)
03880             vga.draw.linear_mask &= 0x3FFFF;
03881     }
03882     else if (IS_EGA_ARCH) {
03883         vga.draw.linear_mask &= 0x3FFFF;
03884     }
03885 
03886     if (IS_EGAVGA_ARCH)
03887         vga.draw.planar_mask = vga.draw.linear_mask >> 2;
03888     else
03889         vga.draw.planar_mask = vga.draw.linear_mask >> 1;
03890 
03891     Bitu pix_per_char = 8;
03892     switch (vga.mode) {
03893     case M_VGA:
03894         // hack for tgr2 -hc high color mode demo
03895         if (vga.dac.reg02==0x80) {
03896             bpp=16;
03897             vga.mode=M_LIN16;
03898             VGA_SetupHandlers();
03899             VGA_DrawLine=VGA_Draw_LIN16_Line_2x;
03900             pix_per_char = 4;
03901             break;
03902         }
03903         bpp = 32;
03904         pix_per_char = 4;
03905         if (vga.mode == M_VGA && (svgaCard == SVGA_TsengET3K || svgaCard == SVGA_TsengET4K)) {
03906             /* ET4000 chipsets handle the chained mode (in my opinion) with sanity and we can scan linearly for it.
03907              * Chained VGA mode maps planar byte addr = (addr >> 2) and plane = (addr & 3) */
03908             if (vga_alt_new_mode) {
03909                 vga.draw.blocks = width;
03910                 VGA_DrawLine = Alt_VGA_256color_Draw_Line_Tseng_ET4000;
03911             }
03912             else {
03913                 VGA_DrawLine = VGA_Draw_Xlat32_Linear_Line;
03914             }
03915         }
03916         else if (machine == MCH_MCGA) {
03917             pix_per_char = 8;
03918             VGA_DrawLine = VGA_Draw_Xlat32_Linear_Line;
03919             vga.tandy.draw_base = vga.mem.linear;
03920             vga.draw.address_line_total = 1;
03921         }
03922         else {
03923             /* other SVGA chipsets appear to handle chained mode by writing 4 pixels to 4 planes, and showing
03924              * only every 4th byte, which is why when you switch the CRTC to byte or word mode on these chipsets,
03925              * you see 16-pixel groups with 4 pixels from the chained display you expect followed by 12 pixels
03926              * of whatever contents of memory remain. but when you unchain the bitplanes the card will allow
03927              * "planar" writing to all 16 pixels properly. Chained VGA maps like planar byte = (addr & ~3) and
03928              * plane = (addr & 3) */
03929             if (vga_alt_new_mode) {
03930                 vga.draw.blocks = width;
03931 
03932                 /* NTS: 8BIT (bit 6) is normally set for 256-color mode. What it does when enabled
03933                  *      is latch every other pixel clock an 8-bit value to the DAC. It is needed
03934                  *      because VGA hardware appears to generate a 4-bit (16-color) value per pixel
03935                  *      clock internally. For 256-color mode, it shifts 4 bits through an 8-bit
03936                  *      register per pixel clock. You're supposed to set 8BIT so that it latches
03937                  *      the 8-bit value only when it's completed two 4-bit values to get a proper
03938                  *      256-color mode. If you turn off 8BIT, then the 8-bit values and the
03939                  *      intermediate shifted values are emitted to the screen as a sort of weird
03940                  *      640x200 256-color mode. */
03941                 if (vga.attr.mode_control & 0x40) { /* 8BIT=1 (normal) 256-color mode */
03942                     VGA_DrawLine = Alt_VGA_256color_Draw_Line;
03943                 }
03944                 else {
03945                     VGA_DrawLine = Alt_VGA_256color_2x4bit_Draw_Line;
03946                     pix_per_char = 8;
03947                 }
03948             }
03949             else {
03950                 VGA_DrawLine = VGA_Draw_Xlat32_VGA_CRTC_bmode_Line;
03951             }
03952         }
03953         break;
03954     case M_LIN8:
03955         bpp = 32;
03956         VGA_DrawLine = VGA_Draw_Xlat32_Linear_Line;
03957 
03958         if ((vga.s3.reg_3a & 0x10)||(svgaCard!=SVGA_S3Trio))
03959             pix_per_char = 8; // TODO fiddle out the bits for other svga cards
03960         else
03961             pix_per_char = 4;
03962 
03963         VGA_ActivateHardwareCursor();
03964         break;
03965     case M_LIN24:
03966     case M_LIN32:
03967         VGA_ActivateHardwareCursor();
03968         break;
03969     case M_LIN15:
03970     case M_LIN16:
03971         pix_per_char = 4; // 15/16 bpp modes double the horizontal values
03972         VGA_ActivateHardwareCursor();
03973         break;
03974     case M_PACKED4:
03975         bpp = 32;
03976         vga.draw.blocks = width;
03977         VGA_DrawLine = VGA_Draw_VGA_Packed4_Xlat32_Line;
03978         break;
03979     case M_LIN4:
03980     case M_EGA:
03981         vga.draw.blocks = width;
03982 
03983         if (IS_EGA_ARCH) {
03984             if (vga_alt_new_mode)
03985                 VGA_DrawLine = Alt_EGA_Planar_Draw_Line;
03986             else
03987                 VGA_DrawLine = EGA_Draw_VGA_Planar_Xlat8_Line;
03988 
03989             bpp = 8;
03990         }
03991         else {
03992             if (vga_alt_new_mode)
03993                 VGA_DrawLine = Alt_VGA_Planar_Draw_Line;
03994             else
03995                 VGA_DrawLine = VGA_Draw_VGA_Planar_Xlat32_Line;
03996 
03997             bpp = 32;
03998         }
03999         break;
04000     case M_CGA16:
04001         vga.draw.blocks=width*2;
04002         pix_per_char = 16;
04003         VGA_DrawLine=VGA_Draw_CGA16_Line;
04004         break;
04005     case M_CGA4:
04006         if (IS_EGA_ARCH) {
04007             vga.draw.blocks=width;
04008             if (vga_alt_new_mode)
04009                 VGA_DrawLine=Alt_EGA_2BPP_Draw_Line;
04010             else
04011                 VGA_DrawLine=EGA_Draw_2BPP_Line_as_EGA;
04012 
04013             bpp = 8;
04014         }
04015         else if (IS_EGAVGA_ARCH || IS_PC98_ARCH) {
04016             vga.draw.blocks=width;
04017             if (vga_alt_new_mode)
04018                 VGA_DrawLine=Alt_VGA_2BPP_Draw_Line;
04019             else
04020                 VGA_DrawLine=VGA_Draw_2BPP_Line_as_VGA;
04021 
04022             bpp = 32;
04023         }
04024         else if (machine == MCH_MCGA) {
04025             vga.draw.blocks=width*2;
04026             VGA_DrawLine=VGA_Draw_2BPP_Line_as_MCGA;
04027             bpp = 32;
04028 
04029             /* MCGA CGA-compatible modes will always refer to the last half of the 64KB of RAM */
04030             vga.tandy.draw_base = vga.mem.linear + 0x8000;
04031         }
04032         else {
04033             if (vga_alt_new_mode) {
04034                 VGA_DrawLine=Alt_CGA_4color_Draw_Line;
04035                 vga.draw.blocks=width;
04036             }
04037             else {
04038                 VGA_DrawLine=VGA_Draw_2BPP_Line;
04039                 vga.draw.blocks=width*2;
04040             }
04041         }
04042         break;
04043     case M_CGA2:
04044         // CGA 2-color mode on EGA/VGA is just EGA 16-color planar mode with one bitplane enabled and a
04045         // color palette to match. Therefore CGA 640x200 2-color mode can be rendered correctly using
04046         // the 16-color planar renderer. The MEM13 bit is configured to replace address bit 13 with
04047         // character row counter bit 0 to match CGA memory layout, doublescan is set (as if 320x200),
04048         // max_scanline is set to 1 (2 lines).
04049         if (IS_EGA_ARCH) {
04050             vga.draw.blocks=width;
04051             if (vga_alt_new_mode)
04052                 VGA_DrawLine=Alt_EGA_Planar_Draw_Line;
04053             else
04054                 VGA_DrawLine=EGA_Draw_1BPP_Line_as_EGA;
04055 
04056             bpp = 8;
04057         }
04058         else if (IS_EGAVGA_ARCH) {
04059             vga.draw.blocks=width;
04060             if (vga_alt_new_mode)
04061                 VGA_DrawLine=Alt_VGA_Planar_Draw_Line;
04062             else
04063                 VGA_DrawLine=VGA_Draw_1BPP_Line_as_VGA;
04064 
04065             bpp = 32;
04066         }
04067         else if (machine == MCH_MCGA) {
04068             vga.draw.blocks=width;
04069             VGA_DrawLine=VGA_Draw_1BPP_Line_as_MCGA;
04070             pix_per_char = 16;
04071             bpp = 32;
04072 
04073             /* MCGA CGA-compatible modes will always refer to the last half of the 64KB of RAM */
04074             if (vga.other.mcga_mode_control & 3) // 320x200 256-color or 640x480 2-color
04075                 vga.tandy.draw_base = vga.mem.linear;
04076             else
04077                 vga.tandy.draw_base = vga.mem.linear + 0x8000;
04078 
04079             if (vga.other.mcga_mode_control & 2) // 640x480 2-color
04080                 vga.draw.address_line_total = 1;
04081         }
04082         else {
04083             if (vga_alt_new_mode) {
04084                 VGA_DrawLine=Alt_CGA_2color_Draw_Line;
04085                 vga.draw.blocks=width;
04086             }
04087             else {
04088                 VGA_DrawLine=VGA_Draw_1BPP_Line;
04089                 vga.draw.blocks=width*2;
04090             }
04091         }
04092         break;
04093     case M_PC98:
04094         vga.draw.blocks=width;
04095         vga.draw.char9dot = false;
04096         VGA_DrawLine=VGA_PC98_Xlat32_Draw_Line;
04097         bpp = 32;
04098         break;
04099     case M_TEXT:
04100         vga.draw.blocks=width;
04101         // if char9_set is true, allow 9-pixel wide fonts
04102         if ((vga.seq.clocking_mode&0x01) || !vga.draw.char9_set) {
04103             // 8-pixel wide
04104             pix_per_char = 8;
04105             vga.draw.char9dot = false;
04106         } else {
04107             // 9-pixel wide
04108             pix_per_char = 9;
04109             vga.draw.char9dot = true;
04110         }
04111 
04112         if (IS_EGA_ARCH) {
04113             if (vga_alt_new_mode)
04114                 VGA_DrawLine = Alt_EGA_TEXT_Xlat8_Draw_Line;
04115             else
04116                 VGA_DrawLine = EGA_TEXT_Xlat8_Draw_Line;
04117             bpp = 8;
04118         }
04119         else {
04120             if (vga_alt_new_mode)
04121                 VGA_DrawLine = Alt_VGA_TEXT_Xlat32_Draw_Line;
04122             else
04123                 VGA_DrawLine = VGA_TEXT_Xlat32_Draw_Line;
04124             bpp = 32;
04125         }
04126         break;
04127     case M_HERC_GFX:
04128         if (vga_alt_new_mode) {
04129             vga.draw.blocks = width;
04130             VGA_DrawLine = Alt_CGA_2color_Draw_Line;
04131         }
04132         else {
04133             vga.draw.blocks=width*2;
04134             if (vga.herc.blend) VGA_DrawLine=VGA_Draw_1BPP_Blend_Line;
04135             else VGA_DrawLine=VGA_Draw_1BPP_Line;
04136         }
04137         pix_per_char = 16;
04138         break;
04139     case M_TANDY2:
04140         if (((machine==MCH_PCJR)&&(vga.tandy.gfx_control & 0x8)) ||
04141             (vga.tandy.mode_control & 0x10)) {
04142             vga.draw.blocks=width * 8;
04143             pix_per_char = 16;
04144         } else {
04145             vga.draw.blocks=width * 4;
04146             pix_per_char = 8;
04147         }
04148 
04149         if (vga_alt_new_mode) {
04150             vga.draw.blocks = width;
04151             VGA_DrawLine=Alt_CGA_2color_Draw_Line;
04152         }
04153         else {
04154             VGA_DrawLine=VGA_Draw_1BPP_Line;
04155         }
04156 
04157         /* MCGA CGA-compatible modes will always refer to the last half of the 64KB of RAM */
04158         if (machine == MCH_MCGA) {
04159             VGA_DrawLine=VGA_Draw_1BPP_Line_as_MCGA;
04160             vga.draw.blocks=width * 2;
04161             pix_per_char = 16;
04162             bpp = 32;
04163 
04164             /* MCGA CGA-compatible modes will always refer to the last half of the 64KB of RAM */
04165             if (vga.other.mcga_mode_control & 3) // 320x200 256-color or 640x480 2-color
04166                 vga.tandy.draw_base = vga.mem.linear;
04167             else
04168                 vga.tandy.draw_base = vga.mem.linear + 0x8000;
04169 
04170             if (vga.other.mcga_mode_control & 2) // 640x480 2-color
04171                 vga.draw.address_line_total = 1;
04172         }
04173 
04174         break;
04175     case M_TANDY4:
04176         vga.draw.blocks=width * 2;
04177         pix_per_char = 8;
04178         if ((machine==MCH_TANDY && (vga.tandy.gfx_control & 0x8)) ||
04179             (machine==MCH_PCJR && (vga.tandy.mode_control==0x0b))) {
04180             VGA_DrawLine=VGA_Draw_2BPPHiRes_Line;
04181         }
04182         else {
04183             if (vga_alt_new_mode) {
04184                 VGA_DrawLine=Alt_CGA_4color_Draw_Line;
04185                 vga.draw.blocks=width;
04186             }
04187             else {
04188                 VGA_DrawLine=VGA_Draw_2BPP_Line;
04189             }
04190         }
04191 
04192         /* MCGA CGA-compatible modes will always refer to the last half of the 64KB of RAM */
04193         if (machine == MCH_MCGA) {
04194             vga.tandy.draw_base = vga.mem.linear + 0x8000;
04195             vga.draw.blocks=width * 2;
04196             VGA_DrawLine=VGA_Draw_2BPP_Line_as_MCGA;
04197             bpp = 32;
04198         }
04199 
04200         break;
04201     case M_TANDY16:
04202         if (vga.tandy.mode_control & 0x1) {
04203             if (( machine==MCH_TANDY ) && ( vga.tandy.mode_control & 0x10 )) {
04204                 vga.draw.blocks=width*4;
04205                 pix_per_char = 8;
04206             } else {
04207                 vga.draw.blocks=width*2;
04208                 pix_per_char = 4;
04209             }
04210             VGA_DrawLine=VGA_Draw_4BPP_Line;
04211         } else {
04212             vga.draw.blocks=width*2;
04213             pix_per_char = 8;
04214             VGA_DrawLine=VGA_Draw_4BPP_Line_Double;
04215         }
04216         break;
04217     case M_TANDY_TEXT: /* Also CGA */
04218         vga.draw.blocks=width;
04219         if (vga_alt_new_mode) {
04220             if (machine==MCH_CGA /*&& !doublewidth*/ && enableCGASnow && (vga.tandy.mode_control & 1)/*80-column mode*/)
04221                 VGA_DrawLine=Alt_CGA_CGASNOW_TEXT_Draw_Line; /* Alternate version that emulates CGA snow */
04222             else
04223                 VGA_DrawLine=Alt_CGA_TEXT_Draw_Line;
04224         }
04225         else {
04226             if (machine==MCH_CGA /*&& !doublewidth*/ && enableCGASnow && (vga.tandy.mode_control & 1)/*80-column mode*/)
04227                 VGA_DrawLine=VGA_CGASNOW_TEXT_Draw_Line; /* Alternate version that emulates CGA snow */
04228             else
04229                 VGA_DrawLine=VGA_TEXT_Draw_Line;
04230         }
04231 
04232         /* MCGA CGA-compatible modes will always refer to the last half of the 64KB of RAM */
04233         if (machine == MCH_MCGA) {
04234             vga.tandy.draw_base = vga.mem.linear + 0x8000;
04235             VGA_DrawLine = MCGA_TEXT_Draw_Line;
04236             bpp = 32;
04237         }
04238 
04239         break;
04240     case M_HERC_TEXT:
04241         vga.draw.blocks=width;
04242         if (vga_alt_new_mode)
04243             VGA_DrawLine=Alt_MDA_TEXT_Draw_Line;
04244         else
04245             VGA_DrawLine=VGA_TEXT_Herc_Draw_Line;
04246         break;
04247     case M_AMSTRAD: // Probably OK?
04248         pix_per_char = 16;
04249         vga.draw.blocks=width*2;
04250         VGA_DrawLine=VGA_Draw_AMS_4BPP_Line;
04251 //      VGA_DrawLine=VGA_Draw_4BPP_Line;
04252 /*      doubleheight=true;
04253         vga.draw.blocks = 2*width; width<<=4;
04254         vga.draw.linear_base = vga.mem.linear + VGA_CACHE_OFFSET;
04255         vga.draw.linear_mask = 512 * 1024 - 1; */
04256         break;
04257     default:
04258         LOG(LOG_VGA,LOG_ERROR)("Unhandled VGA mode %d while checking for resolution",vga.mode);
04259         break;
04260     }
04261     width *= pix_per_char;
04262     VGA_CheckScanLength();
04263 
04264     /* for MCGA, need to "double scan" the screen in some cases */
04265     if (vga.other.mcga_mode_control & 2) { // 640x480 2-color
04266         height *= 2;
04267         mcga_double_scan = true;
04268     }
04269     else if (machine == MCH_MCGA && vga.mode == M_TANDY_TEXT) { // MCGA text mode
04270         height *= 2;
04271         mcga_double_scan = true;
04272         vga.draw.address_line_total *= 2;
04273     }
04274     else {
04275         mcga_double_scan = false;
04276     }
04277     
04278     vga.draw.lines_total=height;
04279     vga.draw.line_length = width * ((bpp + 1) / 8);
04280     vga.draw.oscclock = oscclock;
04281     vga.draw.clock = clock;
04282 
04283     double vratio = ((double)width)/(double)height; // ratio if pixels were square
04284 
04285     // the picture ratio factor
04286     double scanratio =  ((double)hdend/(double)(htotal-(hrend-hrstart)))/
04287                         ((double)vdend/(double)(vtotal-(vrend-vrstart)));
04288     double scanfield_ratio = 4.0/3.0;
04289     switch(machine) {
04290         case MCH_CGA:
04291         case MCH_MCGA:
04292         case MCH_PCJR:
04293         case MCH_TANDY:
04294             scanfield_ratio = 1.382;
04295             break;
04296         case MCH_MDA:
04297         case MCH_HERC:
04298             scanfield_ratio = 1.535;
04299             break;
04300         case MCH_EGA:
04301             switch (vga.misc_output >> 6) {
04302             case 0: // 200 lines:
04303                 scanfield_ratio = 1.085; // DOSBugs
04304                 //scanfield_ratio = 1.375; // IBM EGA BIOS
04305                 break;
04306             case 2: // 350 lines
04307                 // TODO monitors seem to display this with a bit of black borders on top and bottom
04308                 scanfield_ratio = 1.45;
04309                 break;
04310             default:
04311                 // other cases are undefined for EGA - scale them to 4:3
04312                 scanfield_ratio = (4.0/3.0) / scanratio;
04313                 break;
04314             }
04315             break;
04316 
04317         default: // VGA
04318             switch (vga.misc_output >> 6) {
04319             case 0: // VESA: "OTHER" scanline amount
04320                 scanfield_ratio = (4.0/3.0) / scanratio;
04321                 break;
04322             case 1: // 400 lines
04323                 scanfield_ratio = 1.312;
04324                 break;
04325             case 2: // 350 lines
04326                 scanfield_ratio = 1.249;
04327                 break;
04328             case 3: // 480 lines
04329                 scanfield_ratio = 1.345;
04330                 break;
04331             }
04332             break;
04333     }
04334     // calculate screen ratio
04335     double screenratio = scanratio * scanfield_ratio;
04336 
04337     // override screenratio for certain cases:
04338     if (vratio == 1.6) screenratio = 4.0 / 3.0;
04339     else if (vratio == 0.8) screenratio = 4.0 / 3.0;
04340     else if (vratio == 3.2) screenratio = 4.0 / 3.0;
04341     else if (vratio == (4.0/3.0)) screenratio = 4.0 / 3.0;
04342     else if (vratio == (2.0/3.0)) screenratio = 4.0 / 3.0;
04343     else if ((width >= 800)&&(height>=600)) screenratio = 4.0 / 3.0;
04344 
04345 #if C_DEBUG
04346             LOG(LOG_VGA,LOG_NORMAL)("screen: %1.3f, scanfield: %1.3f, scan: %1.3f, vratio: %1.3f",
04347                 screenratio, scanfield_ratio, scanratio, vratio);
04348 #endif
04349 
04350     bool fps_changed = false;
04351 
04352 #if C_DEBUG
04353     LOG(LOG_VGA,LOG_NORMAL)("h total %2.5f (%3.2fkHz) blank(%02.5f/%02.5f) retrace(%02.5f/%02.5f)",
04354         vga.draw.delay.htotal,(1.0/vga.draw.delay.htotal),
04355         vga.draw.delay.hblkstart,vga.draw.delay.hblkend,
04356         vga.draw.delay.hrstart,vga.draw.delay.hrend);
04357     LOG(LOG_VGA,LOG_NORMAL)("v total %2.5f (%3.2fHz) blank(%02.5f/%02.5f) retrace(%02.5f/%02.5f)",
04358         vga.draw.delay.vtotal,(1000.0/vga.draw.delay.vtotal),
04359         vga.draw.delay.vblkstart,vga.draw.delay.vblkend,
04360         vga.draw.delay.vrstart,vga.draw.delay.vrend);
04361 
04362     LOG(LOG_VGA,LOG_NORMAL)("video clock: %3.2fMHz mode %s",
04363         oscclock/1000000.0, mode_texts[vga.mode]);
04364 #endif
04365 
04366     // need to change the vertical timing?
04367     if (vga_mode_time_base < 0 || fabs(vga.draw.delay.vtotal - 1000.0 / fps) > 0.0001)
04368         fps_changed = true;
04369 
04370     // need to resize the output window?
04371     if ((width != vga.draw.width) ||
04372         (height != vga.draw.height) ||
04373         (fabs(screenratio - vga.draw.screen_ratio) > 0.0001) ||
04374         (vga.draw.bpp != bpp) || fps_changed) {
04375 
04376         VGA_KillDrawing();
04377 
04378         vga.draw.width = width;
04379         vga.draw.height = height;
04380         vga.draw.screen_ratio = screenratio;
04381         vga.draw.bpp = bpp;
04382 #if C_DEBUG
04383         LOG(LOG_VGA,LOG_NORMAL)("%dx%d, %3.2fHz, %dbpp, screen %1.3f",(int)width,(int)height,fps,(int)bpp,screenratio);
04384 #endif
04385         if (!vga.draw.vga_override)
04386             RENDER_SetSize(width,height,bpp,(float)fps,screenratio);
04387 
04388         if (fps_changed) {
04389             vga_mode_time_base = PIC_GetCurrentEventTime();
04390             vga_mode_frames_since_time_base = 0;
04391             PIC_RemoveEvents(VGA_Other_VertInterrupt);
04392             PIC_RemoveEvents(VGA_VerticalTimer);
04393             PIC_RemoveEvents(VGA_PanningLatch);
04394             PIC_RemoveEvents(VGA_DisplayStartLatch);
04395             vga.draw.delay.vtotal = 1000.0 / fps;
04396             vga.draw.lines_done = vga.draw.lines_total;
04397             vga_fps = fps;
04398             VGA_VerticalTimer(0);
04399         }
04400     }
04401     vga.draw.delay.singleline_delay = (float)vga.draw.delay.htotal;
04402 
04403     if (machine == MCH_HERC || machine == MCH_MDA) {
04404         Herc_Palette();
04405     }
04406     else {
04407         /* FIXME: Why is this required to prevent VGA palette errors with Crystal Dream II?
04408          *        What is this code doing to change the palette prior to this point? */
04409         VGA_DAC_UpdateColorPalette();
04410     }
04411 }
04412 
04413 void VGA_KillDrawing(void) {
04414     PIC_RemoveEvents(VGA_DrawSingleLine);
04415     PIC_RemoveEvents(VGA_DrawEGASingleLine);
04416 }
04417 
04418 void VGA_SetOverride(bool vga_override) {
04419     if (vga.draw.vga_override!=vga_override) {
04420         
04421         if (vga_override) {
04422             VGA_KillDrawing();
04423             vga.draw.vga_override=true;
04424         } else {
04425             vga.draw.vga_override=false;
04426             vga.draw.width=0; // change it so the output window gets updated
04427             VGA_SetupDrawing(0);
04428         }
04429     }
04430 }
04431 
04432 uint32_t VGA_QuerySizeIG(void) {
04433     return  ((uint32_t)vga.draw.height << (uint32_t)16ul) |
04434              (uint32_t)vga.draw.width;
04435 }
04436 
04437 // save state support
04438 void *VGA_DisplayStartLatch_PIC_Event = (void*)((uintptr_t)VGA_DisplayStartLatch);
04439 void *VGA_DrawEGASingleLine_PIC_Event = (void*)((uintptr_t)VGA_DrawEGASingleLine);
04440 //void *VGA_DrawPart_PIC_Event = (void*)VGA_DrawPart;
04441 void *VGA_DrawSingleLine_PIC_Event = (void*)((uintptr_t)VGA_DrawSingleLine);
04442 void *VGA_Other_VertInterrupt_PIC_Event = (void*)((uintptr_t)VGA_Other_VertInterrupt);
04443 void *VGA_PanningLatch_PIC_Event = (void*)((uintptr_t)VGA_PanningLatch);
04444 void *VGA_VertInterrupt_PIC_Event = (void*)((uintptr_t)VGA_VertInterrupt);
04445 void *VGA_VerticalTimer_PIC_Event = (void*)((uintptr_t)VGA_VerticalTimer);
04446 
04447 
04448 void POD_Save_VGA_Draw( std::ostream& stream )
04449 {
04450         Bit8u linear_base_idx;
04451         Bit8u font_tables_idx[2];
04452         Bit8u drawline_idx;
04453 
04454 
04455         if(0) {}
04456         else if( vga.draw.linear_base == vga.mem.linear ) linear_base_idx = 0;
04457         //else if( vga.draw.linear_base == vga.fastmem ) linear_base_idx = 1;
04458 
04459 
04460         for( int lcv=0; lcv<2; lcv++ ) {
04461                 if(0) {}
04462                 else if( vga.draw.font_tables[lcv] == &(vga.draw.font[0*1024]) ) font_tables_idx[lcv] = 0;
04463                 else if( vga.draw.font_tables[lcv] == &(vga.draw.font[8*1024]) ) font_tables_idx[lcv] = 1;
04464                 else if( vga.draw.font_tables[lcv] == &(vga.draw.font[16*1024]) ) font_tables_idx[lcv] = 2;
04465                 else if( vga.draw.font_tables[lcv] == &(vga.draw.font[24*1024]) ) font_tables_idx[lcv] = 3;
04466                 else if( vga.draw.font_tables[lcv] == &(vga.draw.font[32*1024]) ) font_tables_idx[lcv] = 4;
04467                 else if( vga.draw.font_tables[lcv] == &(vga.draw.font[40*1024]) ) font_tables_idx[lcv] = 5;
04468                 else if( vga.draw.font_tables[lcv] == &(vga.draw.font[48*1024]) ) font_tables_idx[lcv] = 6;
04469                 else if( vga.draw.font_tables[lcv] == &(vga.draw.font[56*1024]) ) font_tables_idx[lcv] = 7;
04470         }
04471 
04472 
04473         if(0) {}
04474         else if( VGA_DrawLine == VGA_Draw_1BPP_Line ) drawline_idx = 1;
04475         else if( VGA_DrawLine == VGA_Draw_2BPP_Line ) drawline_idx = 3;
04476         else if( VGA_DrawLine == VGA_Draw_2BPPHiRes_Line ) drawline_idx = 4;
04477         else if( VGA_DrawLine == VGA_Draw_CGA16_Line ) drawline_idx = 5;
04478         else if( VGA_DrawLine == VGA_Draw_4BPP_Line ) drawline_idx = 6;
04479         else if( VGA_DrawLine == VGA_Draw_4BPP_Line_Double ) drawline_idx = 7;
04480         else if( VGA_DrawLine == VGA_Draw_Linear_Line ) drawline_idx = 8;
04481         else if( VGA_DrawLine == VGA_Draw_Xlat32_Linear_Line ) drawline_idx = 9;
04482         else if( VGA_DrawLine == VGA_Draw_VGA_Line_HWMouse ) drawline_idx = 11;
04483         else if( VGA_DrawLine == VGA_Draw_LIN16_Line_HWMouse ) drawline_idx = 12;
04484         else if( VGA_DrawLine == VGA_Draw_LIN32_Line_HWMouse ) drawline_idx = 13;
04485         else if( VGA_DrawLine == VGA_TEXT_Draw_Line ) drawline_idx = 14;
04486         else if( VGA_DrawLine == VGA_TEXT_Herc_Draw_Line ) drawline_idx = 15;
04487         else if( VGA_DrawLine == VGA_TEXT_Xlat32_Draw_Line ) drawline_idx = 17;
04488 
04489         //**********************************************
04490         //**********************************************
04491 
04492         // - near-pure (struct) data
04493         WRITE_POD( &vga.draw, vga.draw );
04494 
04495 
04496         // - reloc ptr
04497         WRITE_POD( &linear_base_idx, linear_base_idx );
04498         WRITE_POD( &font_tables_idx, font_tables_idx );
04499 
04500         //**********************************************
04501         //**********************************************
04502 
04503         // static globals
04504 
04505         // - reloc function ptr
04506         WRITE_POD( &drawline_idx, drawline_idx );
04507 
04508 
04509         // - pure data
04510         WRITE_POD( &TempLine, TempLine );
04511 
04512 
04513         // - system data
04514         //WRITE_POD( &vsync, vsync );
04515         //WRITE_POD( &uservsyncjolt, uservsyncjolt );
04516 
04517 
04518         // - pure data
04519         WRITE_POD( &temp, temp );
04520         WRITE_POD( &FontMask, FontMask );
04521         WRITE_POD( &bg_color_index, bg_color_index );
04522 }
04523 
04524 
04525 void POD_Load_VGA_Draw( std::istream& stream )
04526 {
04527         Bit8u linear_base_idx;
04528         Bit8u font_tables_idx[2];
04529         Bit8u drawline_idx;
04530 
04531         //**********************************************
04532         //**********************************************
04533 
04534         // - near-pure (struct) data
04535         READ_POD( &vga.draw, vga.draw );
04536 
04537 
04538         // - reloc ptr
04539         READ_POD( &linear_base_idx, linear_base_idx );
04540         READ_POD( &font_tables_idx, font_tables_idx );
04541 
04542         //**********************************************
04543         //**********************************************
04544 
04545         // static globals
04546 
04547         // - reloc function ptr
04548         READ_POD( &drawline_idx, drawline_idx );
04549 
04550 
04551         // - pure data
04552         READ_POD( &TempLine, TempLine );
04553 
04554 
04555         // - system data
04556         //READ_POD( &vsync, vsync );
04557         //READ_POD( &uservsyncjolt, uservsyncjolt );
04558 
04559 
04560         // - pure data
04561         READ_POD( &temp, temp );
04562         READ_POD( &FontMask, FontMask );
04563         READ_POD( &bg_color_index, bg_color_index );
04564 
04565         //**********************************************
04566         //**********************************************
04567 
04568         switch( linear_base_idx ) {
04569                 case 0: vga.draw.linear_base = vga.mem.linear; break;
04570                 //case 1: vga.draw.linear_base = vga.fastmem; break;
04571         }
04572 
04573 
04574         for( int lcv=0; lcv<2; lcv++ ) {
04575                 switch( font_tables_idx[lcv] ) {
04576                         case 0: vga.draw.font_tables[lcv] = &(vga.draw.font[0*1024]); break;
04577                         case 1: vga.draw.font_tables[lcv] = &(vga.draw.font[8*1024]); break;
04578                         case 2: vga.draw.font_tables[lcv] = &(vga.draw.font[16*1024]); break;
04579                         case 3: vga.draw.font_tables[lcv] = &(vga.draw.font[24*1024]); break;
04580                         case 4: vga.draw.font_tables[lcv] = &(vga.draw.font[32*1024]); break;
04581                         case 5: vga.draw.font_tables[lcv] = &(vga.draw.font[40*1024]); break;
04582                         case 6: vga.draw.font_tables[lcv] = &(vga.draw.font[48*1024]); break;
04583                         case 7: vga.draw.font_tables[lcv] = &(vga.draw.font[56*1024]); break;
04584                 }
04585         }
04586 
04587 
04588         switch( drawline_idx ) {
04589                 case 1: VGA_DrawLine = VGA_Draw_1BPP_Line; break;
04590                 case 3: VGA_DrawLine = VGA_Draw_2BPP_Line; break;
04591                 case 4: VGA_DrawLine = VGA_Draw_2BPPHiRes_Line; break;
04592                 case 5: VGA_DrawLine = VGA_Draw_CGA16_Line; break;
04593                 case 6: VGA_DrawLine = VGA_Draw_4BPP_Line; break;
04594                 case 7: VGA_DrawLine = VGA_Draw_4BPP_Line_Double; break;
04595                 case 8: VGA_DrawLine = VGA_Draw_Linear_Line; break;
04596                 case 9: VGA_DrawLine = VGA_Draw_Xlat32_Linear_Line; break;
04597                 case 11: VGA_DrawLine = VGA_Draw_VGA_Line_HWMouse; break;
04598                 case 12: VGA_DrawLine = VGA_Draw_LIN16_Line_HWMouse; break;
04599                 case 13: VGA_DrawLine = VGA_Draw_LIN32_Line_HWMouse; break;
04600                 case 14: VGA_DrawLine = VGA_TEXT_Draw_Line; break;
04601                 case 15: VGA_DrawLine = VGA_TEXT_Herc_Draw_Line; break;
04602                 case 17: VGA_DrawLine = VGA_TEXT_Xlat32_Draw_Line; break;
04603         }
04604 }