DOSBox-X
|
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 }