DOSBox-X
|
00001 /* 00002 * Copyright (C) 2018-2020 Jon Campbell 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License along 00015 * with this program; if not, write to the Free Software Foundation, Inc., 00016 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 */ 00018 00019 #include "dosbox.h" 00020 #include "setup.h" 00021 #include "video.h" 00022 #include "pic.h" 00023 #include "vga.h" 00024 #include "inout.h" 00025 #include "programs.h" 00026 #include "support.h" 00027 #include "setup.h" 00028 #include "timer.h" 00029 #include "mem.h" 00030 #include "menu.h" 00031 #include "util_units.h" 00032 #include "control.h" 00033 #include "pc98_cg.h" 00034 #include "pc98_dac.h" 00035 #include "pc98_gdc.h" 00036 #include "pc98_gdc_const.h" 00037 #include "mixer.h" 00038 00039 void pc98_update_page_ptrs(void); 00040 00041 extern bool pc98_40col_text; 00042 extern bool pc98_31khz_mode; 00043 extern bool pc98_attr4_graphic; 00044 extern bool pc98_display_enable; 00045 extern bool pc98_graphics_hide_odd_raster_200line; 00046 extern bool pc98_crt_mode; // see port 6Ah command 40h/41h. 00047 00048 // TODO: Other parts that change gdc_5mhz_mode should update these too! 00049 bool gdc_clock_1 = false; 00050 bool gdc_clock_2 = false; 00051 00052 bool gdc_5mhz_mode = false; 00053 bool enable_pc98_egc = true; 00054 bool enable_pc98_grcg = true; 00055 bool enable_pc98_16color = true; 00056 bool enable_pc98_256color = true; 00057 bool enable_pc98_256color_planar = true; 00058 bool enable_pc98_188usermod = true; 00059 bool pc98_256kb_boundary = false; /* port 6Ah command 68h/69h */ 00060 bool GDC_vsync_interrupt = false; 00061 bool gdc_5mhz_mode_initial = false; 00062 uint8_t GDC_display_plane_wait_for_vsync = false; 00063 uint8_t GDC_display_plane_pending = false; 00064 uint8_t GDC_display_plane = false; 00065 00066 uint8_t pc98_gdc_tile_counter=0; 00067 uint8_t pc98_gdc_modereg=0; 00068 uint8_t pc98_gdc_vramop=0; 00069 egc_quad pc98_gdc_tiles; 00070 00071 extern unsigned char pc98_text_first_row_scanline_start; /* port 70h */ 00072 extern unsigned char pc98_text_first_row_scanline_end; /* port 72h */ 00073 extern unsigned char pc98_text_row_scanline_blank_at; /* port 74h */ 00074 extern unsigned char pc98_text_row_scroll_lines; /* port 76h */ 00075 extern unsigned char pc98_text_row_scroll_count_start; /* port 78h */ 00076 extern unsigned char pc98_text_row_scroll_num_lines; /* port 7Ah */ 00077 00078 // the guest can change the GDC to 5MHz by setting both GDC clock bits. 00079 // NTS: For whatever reason, Windows 3.1 will set the GDC to 5MHz when entering a DOS application fullscreen. 00080 void gdc_clock_check(void) { 00081 bool nresult = gdc_clock_1 && gdc_clock_2; 00082 00083 if (gdc_5mhz_mode != nresult) { 00084 gdc_5mhz_mode = nresult; 00085 LOG_MSG("PC-98: Guest changed GDC clock to %s",gdc_5mhz_mode?"5MHz":"2.5MHz"); 00086 mainMenu.get_item("pc98_5mhz_gdc").check(gdc_5mhz_mode).refresh_item(mainMenu); 00087 } 00088 } 00089 00090 void pc98_crtc_write(Bitu port,Bitu val,Bitu iolen) { 00091 (void)iolen;//UNUSED 00092 switch (port&0xE) { 00093 case 0x00: // 0x70: Character row, CG start scanline 00094 pc98_text_first_row_scanline_start = (unsigned char)val & 0x1F; 00095 break; 00096 case 0x02: // 0x72: Character row, CG end scanline 00097 pc98_text_first_row_scanline_end = (unsigned char)val & 0x1F; 00098 break; 00099 case 0x04: // 0x74: Character row, number of CG scanlines to display 00100 pc98_text_row_scanline_blank_at = (unsigned char)val & 0x1F; 00101 break; 00102 case 0x06: 00103 pc98_text_row_scroll_lines = (unsigned char)val & 0x1F; 00104 break; 00105 case 0x08: 00106 pc98_text_row_scroll_count_start = (unsigned char)val & 0x1F; 00107 break; 00108 case 0x0A: 00109 pc98_text_row_scroll_num_lines = (unsigned char)val & 0x1F; 00110 break; 00111 case 0x0C: // 0x7C: mode reg / vram operation mode (also, reset tile counter) 00112 if (enable_pc98_grcg) { 00113 pc98_gdc_tile_counter = 0; 00114 pc98_gdc_modereg = (uint8_t)val; 00115 /* bit 7: 1=GRGC active 0=GRGC invalid 00116 * bit 6: 1=Read/Modify/Write when writing 0=TCR mode at read, TDW mode at write */ 00117 pc98_gdc_vramop &= ~(3 << VOPBIT_GRCG); 00118 pc98_gdc_vramop |= (val & 0xC0) >> (6 - VOPBIT_GRCG); 00119 } 00120 break; 00121 case 0x0E: // 0x7E: tile data 00122 if (enable_pc98_grcg) { 00123 pc98_gdc_tiles[pc98_gdc_tile_counter].b[0] = (uint8_t)val; 00124 pc98_gdc_tiles[pc98_gdc_tile_counter].b[1] = (uint8_t)val; 00125 pc98_gdc_tile_counter = (pc98_gdc_tile_counter + 1) & 3; 00126 } 00127 break; 00128 default: 00129 LOG_MSG("PC98 CRTC w: port=0x%02X val=0x%02X unknown",(unsigned int)port,(unsigned int)val); 00130 break; 00131 } 00132 } 00133 00134 Bitu pc98_crtc_read(Bitu port,Bitu iolen) { 00135 (void)iolen;//UNUSED 00136 switch (port&0xE) { 00137 case 0x00: // 0x70: Character row, CG start scanline 00138 return pc98_text_first_row_scanline_start; 00139 case 0x02: // 0x72: Character row, CG end scanline 00140 return pc98_text_first_row_scanline_end; 00141 case 0x04: // 0x74: Character row, number of CG scanlines to display 00142 return pc98_text_row_scanline_blank_at; 00143 case 0x06: 00144 return pc98_text_row_scroll_lines; 00145 case 0x08: 00146 return pc98_text_row_scroll_count_start; 00147 case 0x0A: 00148 return pc98_text_row_scroll_num_lines; 00149 default: 00150 LOG_MSG("PC98 CRTC r: port=0x%02X unknown",(unsigned int)port); 00151 break; 00152 } 00153 00154 return ~0ul; 00155 } 00156 00157 bool egc_enable_enable = false; 00158 00159 void update_gdc_analog(void) { 00160 /* gdc analog mode if analog (16-color) or 256-color mode */ 00161 gdc_analog = (pc98_gdc_vramop & ((1 << VOPBIT_ANALOG)|(1 << VOPBIT_VGA))) != 0; 00162 } 00163 00164 /* Port 0x6A command handling */ 00165 void pc98_port6A_command_write(unsigned char b) { 00166 switch (b) { 00167 case 0x00: // 16-color (analog) disable 00168 pc98_gdc_vramop &= ~(1 << VOPBIT_ANALOG); 00169 update_gdc_analog(); 00170 VGA_SetupHandlers(); // confirmed on real hardware: this disables access to E000:0000 00171 pc98_update_palette(); // Testing on real hardware shows that the "digital" and "analog" palettes are completely different. 00172 // They're both there in hardware, but one or another is active depending on analog enable. 00173 // Also, the 4th bitplane at E000:0000 disappears when switched off from the display and from CPU access. 00174 break; 00175 case 0x01: // or enable 00176 if (enable_pc98_16color) { 00177 pc98_gdc_vramop |= (1 << VOPBIT_ANALOG); 00178 update_gdc_analog(); 00179 VGA_SetupHandlers(); // confirmed on real hardware: this enables access to E000:0000 00180 pc98_update_palette(); // Testing on real hardware shows that the "digital" and "analog" palettes are completely different. 00181 // They're both there in hardware, but one or another is active depending on analog enable. 00182 // Also, the 4th bitplane at E000:0000 disappears when switched off from the display and from CPU access. 00183 } 00184 break; 00185 case 0x04: 00186 if (egc_enable_enable) 00187 pc98_gdc_vramop &= ~(1 << VOPBIT_EGC); 00188 break; 00189 case 0x05: 00190 if (enable_pc98_egc && egc_enable_enable) 00191 pc98_gdc_vramop |= (1 << VOPBIT_EGC); 00192 break; 00193 case 0x06: 00194 egc_enable_enable = false; 00195 break; 00196 case 0x07: 00197 egc_enable_enable = true; 00198 break; 00199 case 0x0A: // TODO 00200 case 0x0B: // TODO 00201 // TODO 00202 break; 00203 case 0x20: // 256-color mode disable 00204 if (enable_pc98_egc && egc_enable_enable) { 00205 pc98_gdc_vramop &= ~(1 << VOPBIT_VGA); 00206 update_gdc_analog(); 00207 VGA_SetupHandlers(); // memory mapping presented to the CPU changes 00208 pc98_update_palette(); 00209 pc98_update_page_ptrs(); 00210 } 00211 break; 00212 case 0x21: // 256-color mode enable 00213 if (enable_pc98_egc && egc_enable_enable && enable_pc98_256color) { 00214 pc98_gdc_vramop |= (1 << VOPBIT_VGA); 00215 update_gdc_analog(); 00216 VGA_SetupHandlers(); // memory mapping presented to the CPU changes 00217 pc98_update_palette(); 00218 pc98_update_page_ptrs(); 00219 } 00220 break; 00221 case 0x40: // CRT mode 00222 case 0x41: // Plasma/LCD mode 00223 pc98_crt_mode = (b&1)==0; 00224 break; 00225 case 0x68: // 128KB VRAM boundary 00226 // TODO: Any conditions? 00227 pc98_256kb_boundary = false; 00228 VGA_SetupHandlers(); // memory mapping presented to the CPU changes 00229 break; 00230 case 0x69: // 256KB VRAM boundary 00231 // TODO: Any conditions? 00232 pc98_256kb_boundary = true; 00233 VGA_SetupHandlers(); // memory mapping presented to the CPU changes 00234 break; 00235 case 0x82: // GDC Clock #1 0=2.5MHz 1=5MHz 00236 case 0x83: 00237 gdc_clock_1 = !!(b&1); 00238 gdc_clock_check(); 00239 break; 00240 case 0x84: // GDC Clock #2 0=2.5MHz 1=5MHz 00241 case 0x85: 00242 gdc_clock_2 = !!(b&1); 00243 gdc_clock_check(); 00244 break; 00245 // TODO: 0x8E/0x8F VRAM use selection 0=PC-98 graphics 1=Cirrus Logic CL-GD graphics (VRAM is shared?) 00246 default: 00247 LOG_MSG("PC-98 port 6Ah unknown command 0x%02x",b); 00248 break; 00249 } 00250 } 00251 00252 /* Port 0x68 command handling */ 00253 void pc98_port68_command_write(unsigned char b) { 00254 switch (b) { 00255 case 0x00: // text screeen attribute bit 4 meaning: 0=vertical line 00256 case 0x01: // 1=simple graphic 00257 pc98_attr4_graphic = !!(b&1); 00258 break; 00259 case 0x04: // 40-column mode 0=80-column 00260 case 0x05: // 1=40-column 00261 pc98_40col_text = !!(b&1); 00262 break; 00263 case 0x08: // 200-line mode: show odd raster 00264 case 0x09: // don't show odd raster 00265 pc98_graphics_hide_odd_raster_200line = !!(b&1); 00266 break; 00267 case 0x0A: // TODO 00268 case 0x0B: // TODO 00269 // TODO 00270 break; 00271 case 0x0E: // Display enable 00272 case 0x0F: 00273 pc98_display_enable = !!(b&1); 00274 break; 00275 default: 00276 LOG_MSG("PC-98 port 68h unknown command 0x%02x",b); 00277 break; 00278 } 00279 } 00280 00281 unsigned char sel_9a0 = 0; 00282 00283 /* Port 0x9A0 readback. 00284 * This is needed to report the GDC setting so that Windows 3.1 doesn't change the 00285 * GDC to 5MHz arbitrarily and leave PC-98 games confused afterward */ 00286 Bitu pc98_read_9a0(Bitu /*port*/,Bitu /*iolen*/) { 00287 Bitu retval = 0; 00288 00289 /* bit 0 depends on what was selected by writing to port 9a0. 00290 * according to undocumented 9821 (not verified), unknown registers and 0x00 will return 0xff here. */ 00291 switch (sel_9a0) { 00292 case 0x01: // color/monochrome 00293 if (true) retval |= 1u;//FIXME 00294 break; 00295 case 0x02: // odd raster mask 00296 if (pc98_graphics_hide_odd_raster_200line) retval |= 1u; 00297 break; 00298 case 0x03: // display on/off 00299 if (pc98_display_enable) retval |= 1u; 00300 break; 00301 case 0x04: // palette mode (used by Sim City 2000) 00302 if (pc98_gdc_vramop & (1 << VOPBIT_ANALOG)) retval |= 1u; 00303 break; 00304 default: 00305 retval |= 0xFF;//FIXME: Is this true? 00306 break; 00307 } 00308 00309 /* bit 1: graphic GDC clock frequency as set in hardware at this moment */ 00310 if (gdc_5mhz_mode) 00311 retval |= 0x02; 00312 00313 return retval; 00314 } 00315 00316 void pc98_write_9a0(Bitu port,Bitu val,Bitu iolen) { 00317 (void)port; 00318 (void)iolen; 00319 sel_9a0 = (unsigned char)val; // what to read back in bit 0 00320 } 00321 00322 /* Port 0x9A8 00323 * 00324 * bit[1:0] 00325 * 11 = invalid 00326 * 10 = invalid 00327 * 01 = 31.47KHz 00328 * 00 = 24.83KHz */ 00329 Bitu pc98_read_9a8(Bitu /*port*/,Bitu /*iolen*/) { 00330 Bitu retval = 0; 00331 00332 if (pc98_31khz_mode) 00333 retval |= 0x01;/*31khz*/ 00334 00335 return retval; 00336 } 00337 00338 void pc98_write_9a8(Bitu /*port*/,Bitu val,Bitu /*iolen*/) { 00339 if ((val&1) != (pc98_31khz_mode?1:0)) { 00340 pc98_31khz_mode = !!(val&1); 00341 VGA_SetupDrawing(0); 00342 } 00343 } 00344