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 "util_units.h" 00031 #include "control.h" 00032 #include "pc98_cg.h" 00033 #include "pc98_dac.h" 00034 #include "pc98_gdc.h" 00035 #include "pc98_gdc_const.h" 00036 #include "mixer.h" 00037 00038 #include <string.h> 00039 #include <stdlib.h> 00040 #include <string> 00041 #include <stdio.h> 00042 00043 using namespace std; 00044 00045 extern bool gdc_5mhz_mode; 00046 extern bool gdc_5mhz_mode_initial; 00047 extern bool GDC_vsync_interrupt; 00048 extern bool pc98_256kb_boundary; 00049 extern uint8_t GDC_display_plane; 00050 extern uint8_t GDC_display_plane_pending; 00051 extern uint8_t GDC_display_plane_wait_for_vsync; 00052 00053 double gdc_proc_delay = 0.001; /* time from FIFO to processing in GDC (1us) FIXME: Is this right? */ 00054 bool gdc_proc_delay_set = false; 00055 struct PC98_GDC_state pc98_gdc[2]; 00056 00057 void pc98_update_display_page_ptr(void) { 00058 if (pc98_256kb_boundary) { 00059 /* GDC display plane has no effect in 256KB boundary mode */ 00060 pc98_pgraph_current_display_page = vga.mem.linear + 00061 PC98_VRAM_GRAPHICS_OFFSET; 00062 } 00063 else { 00064 if (pc98_gdc_vramop & (1 << VOPBIT_VGA)) { 00065 pc98_pgraph_current_display_page = vga.mem.linear + 00066 PC98_VRAM_GRAPHICS_OFFSET + 00067 (GDC_display_plane * PC98_VRAM_PAGEFLIP256_SIZE); 00068 } 00069 else { 00070 pc98_pgraph_current_display_page = vga.mem.linear + 00071 PC98_VRAM_GRAPHICS_OFFSET + 00072 (GDC_display_plane * PC98_VRAM_PAGEFLIP_SIZE); 00073 } 00074 } 00075 } 00076 00077 void pc98_update_cpu_page_ptr(void) { 00078 if (pc98_gdc_vramop & (1 << VOPBIT_VGA)) { 00079 /* "Drawing screen selection register" is not valid in extended modes 00080 * [http://hackipedia.org/browse.cgi/Computer/Platform/PC%2c%20NEC%20PC%2d98/Collections/Undocumented%209801%2c%209821%20Volume%202%20%28webtech.co.jp%29/io%5fdisp%2etxt] */ 00081 pc98_pgraph_current_cpu_page = vga.mem.linear + 00082 PC98_VRAM_GRAPHICS_OFFSET; 00083 } 00084 else { 00085 pc98_pgraph_current_cpu_page = vga.mem.linear + 00086 PC98_VRAM_GRAPHICS_OFFSET + 00087 ((pc98_gdc_vramop & (1 << VOPBIT_ACCESS)) ? PC98_VRAM_PAGEFLIP_SIZE : 0); 00088 } 00089 } 00090 00091 void pc98_update_page_ptrs(void) { 00092 pc98_update_display_page_ptr(); 00093 pc98_update_cpu_page_ptr(); 00094 } 00095 00096 void gdc_proc_schedule_delay(void); 00097 void gdc_proc_schedule_cancel(void); 00098 void gdc_proc_schedule_done(void); 00099 void GDC_ProcDelay(Bitu /*val*/); 00100 void PC98_show_cursor(bool show); 00101 void pc98_port6A_command_write(unsigned char b); 00102 void pc98_port68_command_write(unsigned char b); 00103 00104 PC98_GDC_state::PC98_GDC_state() { 00105 memset(param_ram,0,sizeof(param_ram)); 00106 memset(cmd_parm_tmp, 0, sizeof(cmd_parm_tmp)); 00107 memset(rfifo, 0, sizeof(rfifo)); 00108 memset(fifo, 0, sizeof(fifo)); 00109 00110 // make a display partition area to cover the screen, whatever it is. 00111 param_ram[0] = 0x00; // SAD=0 00112 param_ram[1] = 0x00; // SAD=0 00113 param_ram[2] = 0xF0; // LEN=3FF 00114 param_ram[3] = 0x3F; // LEN=3FF WD1=0 00115 00116 IM_bit = false; 00117 display_partition_mask = 3; 00118 doublescan = false; 00119 param_ram_wptr = 0; 00120 display_partition = 0; 00121 display_partition_rem_lines = 0; 00122 active_display_lines = 0; 00123 active_display_words_per_line = 0; 00124 row_line = 0; 00125 row_height = 16; 00126 scan_address = 0; 00127 current_command = 0xFF; 00128 proc_step = 0xFF; 00129 display_enable = true; 00130 display_mode = 0; 00131 display_pitch = 0; 00132 cursor_enable = true; 00133 cursor_blink_state = 0; 00134 cursor_blink_count = 0; 00135 cursor_blink_rate = 0x20; 00136 video_framing = 0; 00137 master_sync = false; 00138 draw_only_during_retrace = 0; 00139 dynamic_ram_refresh = 0; 00140 cursor_blink = true; 00141 idle = false; 00142 horizontal_sync_width = 0; 00143 vertical_sync_width = 0; 00144 horizontal_front_porch_width = 0; 00145 horizontal_back_porch_width = 0; 00146 vertical_front_porch_width = 0; 00147 vertical_back_porch_width = 0; 00148 reset_fifo(); 00149 reset_rfifo(); 00150 } 00151 00152 size_t PC98_GDC_state::fifo_can_read(void) { 00153 return (size_t)((unsigned int)fifo_write - (unsigned int)fifo_read); 00154 } 00155 00156 void PC98_GDC_state::take_reset_sync_parameters(void) { 00157 /* P1 = param[0] = 0 0 C F I D G S 00158 * CG = [1:0] = display mode 00159 * IS = [1:0] = video framing 00160 * F = drawing time window 00161 * D = dynamic RAM refresh cycles enable */ 00162 draw_only_during_retrace = !!(cmd_parm_tmp[0] & 0x10); /* F */ 00163 dynamic_ram_refresh = !!(cmd_parm_tmp[0] & 0x04); /* D */ 00164 display_mode = /* CG = [1:0] */ 00165 ((cmd_parm_tmp[0] & 0x20) ? 2 : 0) + 00166 ((cmd_parm_tmp[0] & 0x02) ? 1 : 0); 00167 video_framing = /* IS = [1:0] */ 00168 ((cmd_parm_tmp[0] & 0x08) ? 2 : 0) + 00169 ((cmd_parm_tmp[0] & 0x01) ? 1 : 0); 00170 00171 /* P2 = param[1] = AW = active display words per line - 2. must be even number. */ 00172 active_display_words_per_line = (uint16_t)cmd_parm_tmp[1] + 2u; 00173 00174 /* P3 = param[2] = 00175 * VS(L)[2:0] = [7:5] = low bits of VS 00176 * HS = [4:0] = horizontal sync width - 1 */ 00177 horizontal_sync_width = (cmd_parm_tmp[2] & 0x1F) + 1; 00178 vertical_sync_width = (cmd_parm_tmp[2] >> 5); 00179 00180 /* P4 = param[3] = 00181 * HFP = [7:2] = horizontal front porch width - 1 00182 * VS(H)[4:3] = [1:0] = high bits of VS 00183 * 00184 * VS = vertical sync width */ 00185 vertical_sync_width += (cmd_parm_tmp[3] & 3) << 3; 00186 horizontal_front_porch_width = (cmd_parm_tmp[3] >> 2) + 1; 00187 00188 /* P5 = param[4] = 00189 * 0 = [7:6] = 0 00190 * HBP = [5:0] = horizontal back porch width - 1 */ 00191 horizontal_back_porch_width = (cmd_parm_tmp[4] & 0x3F) + 1; 00192 00193 /* P6 = param[5] = 00194 * 0 = [7:6] = 0 00195 * VFP = [5:0] = vertical front porch width */ 00196 vertical_front_porch_width = (cmd_parm_tmp[5] & 0x3F); 00197 00198 /* P7 = param[6] = 00199 * AL(L)[7:0] = [7:0] = Active Display Lines per video field, low bits */ 00200 active_display_lines = (cmd_parm_tmp[6] & 0xFF); 00201 00202 /* P8 = parm[7] = 00203 * VBP = [7:2] = vertical back porch width 00204 * AL(H)[9:8] = [1:0] = Active Display Lines per video field, high bits */ 00205 active_display_lines += (cmd_parm_tmp[7] & 3) << 8; 00206 vertical_back_porch_width = cmd_parm_tmp[7] >> 2; 00207 00208 LOG_MSG("GDC: RESET/SYNC MASTER=%u DOOR=%u DRAM=%u DISP=%u VFRAME=%u AW=%u HS=%u VS=%u HFP=%u HBP=%u VFP=%u AL=%u VBP=%u", 00209 master_sync, 00210 draw_only_during_retrace?1:0, 00211 dynamic_ram_refresh?1:0, 00212 display_mode, 00213 video_framing, 00214 active_display_words_per_line, 00215 horizontal_sync_width, 00216 vertical_sync_width, 00217 horizontal_front_porch_width, 00218 horizontal_back_porch_width, 00219 vertical_front_porch_width, 00220 active_display_lines, 00221 vertical_back_porch_width); 00222 00223 VGA_StartResize(); 00224 } 00225 00226 void PC98_GDC_state::cursor_advance(void) { 00227 cursor_blink_count++; 00228 if (cursor_blink_count == cursor_blink_rate) { 00229 cursor_blink_count = 0; 00230 00231 if ((++cursor_blink_state) >= 4) 00232 cursor_blink_state = 0; 00233 } 00234 else if (cursor_blink_count & 0x40) { 00235 cursor_blink_count = 0; 00236 } 00237 } 00238 00239 void PC98_GDC_state::take_cursor_pos(unsigned char bi) { 00240 /* P1 = param[0] = EAD(L) = address[7:0] 00241 * 00242 * P2 = param[1] = EAD(M) = address[15:0] 00243 * 00244 * P3 = param[2] 00245 * dAD = [7:4] = Dot address within the word 00246 * 0 = [3:2] = 0 00247 * EAD(H) = [1:0] = address[17:16] */ 00248 if (bi == 1) { 00249 vga.config.cursor_start &= ~(0xFFu << 0u); 00250 vga.config.cursor_start |= cmd_parm_tmp[0] << 0u; 00251 } 00252 else if (bi == 2) { 00253 vga.config.cursor_start &= ~(0xFFu << 8u); 00254 vga.config.cursor_start |= (unsigned int)cmd_parm_tmp[1] << 8u; 00255 } 00256 else if (bi == 3) { 00257 vga.config.cursor_start &= ~(0x03u << 16u); 00258 vga.config.cursor_start |= (cmd_parm_tmp[2] & 3u) << 16u; 00259 00260 // TODO: "dot address within the word" 00261 } 00262 } 00263 00264 void PC98_GDC_state::take_cursor_char_setup(unsigned char bi) { 00265 /* P1 = param[0] = 00266 * DC = [7:7] = display cursor if set 00267 * 0 = [6:5] = 0 00268 * LR = [4:0] = lines per character row - 1 */ 00269 if (bi == 1) { 00270 cursor_enable = !!(cmd_parm_tmp[0] & 0x80); 00271 00272 vga.crtc.maximum_scan_line = cmd_parm_tmp[0] & 0x1F; 00273 vga.draw.address_line_total = vga.crtc.maximum_scan_line + 1u; 00274 row_height = (uint8_t)vga.draw.address_line_total; 00275 if (!master_sync) doublescan = (row_height > 1); 00276 } 00277 00278 /* P2 = param[1] = 00279 * BR[1:0] = [7:6] = blink rate 00280 * SC = [5:5] = 1=steady cursor 0=blinking cursor 00281 * CTOP = [4:0] = cursor top line number in the row */ 00282 00283 /* P3 = param[2] = 00284 * CBOT = [7:3] = cursor bottom line number in the row CBOT < LR 00285 * BR[4:2] = [2:0] = blink rate */ 00286 if (bi == 3) { 00287 cursor_blink_rate = (cmd_parm_tmp[1] >> 6) & 3; 00288 cursor_blink_rate += (cmd_parm_tmp[2] & 7) << 2; 00289 if (cursor_blink_rate == 0) cursor_blink_rate = 0x20; 00290 cursor_blink_rate *= 2; 00291 00292 cursor_blink = !(cmd_parm_tmp[1] & 0x20); 00293 00294 vga.crtc.cursor_start = (cmd_parm_tmp[1] & 0x1F); 00295 vga.draw.cursor.sline = vga.crtc.cursor_start; 00296 00297 vga.crtc.cursor_end = (cmd_parm_tmp[2] >> 3) & 0x1F; 00298 vga.draw.cursor.eline = vga.crtc.cursor_end; 00299 } 00300 00301 /* blink-on time + blink-off time = 2 x BR (video frames). 00302 * attribute blink rate is 3/4 on 1/4 off duty cycle. 00303 * for interlaced graphics modes, set BR[1:0] = 3 */ 00304 } 00305 00306 void PC98_GDC_state::idle_proc(void) { 00307 Bit16u val; 00308 00309 if (fifo_empty()) 00310 return; 00311 00312 val = read_fifo(); 00313 if (val & 0x100) { // command 00314 current_command = val & 0xFF; 00315 proc_step = 0; 00316 00317 switch (current_command) { 00318 case GDC_CMD_RESET: // 0x00 0 0 0 0 0 0 0 0 00319 LOG_MSG("GDC: reset"); 00320 display_enable = false; 00321 idle = true; 00322 reset_fifo(); 00323 reset_rfifo(); 00324 break; 00325 case GDC_CMD_DISPLAY_BLANK: // 0x0C 0 0 0 0 1 1 0 DE 00326 case GDC_CMD_DISPLAY_BLANK+1:// 0x0D DE=display enable 00327 display_enable = !!(current_command & 1); // bit 0 = display enable 00328 current_command &= ~1; 00329 break; 00330 case GDC_CMD_SYNC: // 0x0E 0 0 0 0 0 0 0 DE 00331 case GDC_CMD_SYNC+1:// 0x0F DE=display enable 00332 display_enable = !!(current_command & 1); // bit 0 = display enable 00333 current_command &= ~1; 00334 LOG_MSG("GDC: sync"); 00335 break; 00336 case GDC_CMD_PITCH_SPEC: // 0x47 0 1 0 0 0 1 1 1 00337 break; 00338 case GDC_CMD_CURSOR_POSITION: // 0x49 0 1 0 0 1 0 0 1 00339 // LOG_MSG("GDC: cursor pos"); 00340 break; 00341 case GDC_CMD_CURSOR_CHAR_SETUP: // 0x4B 0 1 0 0 1 0 1 1 00342 // LOG_MSG("GDC: cursor setup"); 00343 break; 00344 case GDC_CMD_START_DISPLAY: // 0x6B 0 1 1 0 1 0 1 1 00345 display_enable = true; 00346 idle = false; 00347 break; 00348 case GDC_CMD_VERTICAL_SYNC_MODE: // 0x6E 0 1 1 0 1 1 1 M 00349 case GDC_CMD_VERTICAL_SYNC_MODE+1:// 0x6F M=generate and output vertical sync (0=or else accept external vsync) 00350 master_sync = !!(current_command & 1); 00351 current_command &= ~1; 00352 LOG_MSG("GDC: vsyncmode master=%u",master_sync); 00353 break; 00354 case GDC_CMD_PARAMETER_RAM_LOAD: // 0x70 0 1 1 1 S S S S 00355 case GDC_CMD_PARAMETER_RAM_LOAD+1: // 0x71 S=starting byte in parameter RAM 00356 case GDC_CMD_PARAMETER_RAM_LOAD+2: // 0x72 S=starting byte in parameter RAM 00357 case GDC_CMD_PARAMETER_RAM_LOAD+3: // 0x73 S=starting byte in parameter RAM 00358 case GDC_CMD_PARAMETER_RAM_LOAD+4: // 0x74 S=starting byte in parameter RAM 00359 case GDC_CMD_PARAMETER_RAM_LOAD+5: // 0x75 S=starting byte in parameter RAM 00360 case GDC_CMD_PARAMETER_RAM_LOAD+6: // 0x76 S=starting byte in parameter RAM 00361 case GDC_CMD_PARAMETER_RAM_LOAD+7: // 0x77 S=starting byte in parameter RAM 00362 case GDC_CMD_PARAMETER_RAM_LOAD+8: // 0x78 S=starting byte in parameter RAM 00363 case GDC_CMD_PARAMETER_RAM_LOAD+9: // 0x79 S=starting byte in parameter RAM 00364 case GDC_CMD_PARAMETER_RAM_LOAD+10:// 0x7A S=starting byte in parameter RAM 00365 case GDC_CMD_PARAMETER_RAM_LOAD+11:// 0x7B S=starting byte in parameter RAM 00366 case GDC_CMD_PARAMETER_RAM_LOAD+12:// 0x7C S=starting byte in parameter RAM 00367 case GDC_CMD_PARAMETER_RAM_LOAD+13:// 0x7D S=starting byte in parameter RAM 00368 case GDC_CMD_PARAMETER_RAM_LOAD+14:// 0x7E S=starting byte in parameter RAM 00369 case GDC_CMD_PARAMETER_RAM_LOAD+15:// 0x7F S=starting byte in parameter RAM 00370 param_ram_wptr = current_command & 0xF; 00371 current_command = GDC_CMD_PARAMETER_RAM_LOAD; 00372 break; 00373 case GDC_CMD_CURSOR_ADDRESS_READ: // 0xE0 1 1 1 0 0 0 0 0 00374 write_rfifo((unsigned char)( vga.config.cursor_start & 0xFFu)); 00375 write_rfifo((unsigned char)((vga.config.cursor_start >> 8u) & 0xFFu)); 00376 write_rfifo((unsigned char)((vga.config.cursor_start >> 16u) & 0xFFu)); 00377 write_rfifo(0x00); // TODO 00378 write_rfifo(0x00); // TODO 00379 break; 00380 default: 00381 LOG_MSG("GDC: %s: Unknown command 0x%x",master_sync?"master":"slave",current_command); 00382 break; 00383 } 00384 } 00385 else { 00386 /* parameter parsing */ 00387 switch (current_command) { 00388 /* RESET and SYNC take the same 8 byte parameters */ 00389 case GDC_CMD_RESET: 00390 case GDC_CMD_SYNC: 00391 if (proc_step < 8) { 00392 cmd_parm_tmp[proc_step] = (uint8_t)val; 00393 if ((++proc_step) == 8) { 00394 take_reset_sync_parameters(); 00395 } 00396 } 00397 break; 00398 case GDC_CMD_PITCH_SPEC: 00399 if (proc_step < 1) 00400 display_pitch = (val != 0) ? val : 0x100; 00401 break; 00402 case GDC_CMD_CURSOR_POSITION: 00403 if (proc_step < 3) { 00404 cmd_parm_tmp[proc_step++] = (uint8_t)val; 00405 take_cursor_pos(proc_step); 00406 } 00407 break; 00408 case GDC_CMD_CURSOR_CHAR_SETUP: 00409 if (proc_step < 3) { 00410 cmd_parm_tmp[proc_step++] = (uint8_t)val; 00411 if (proc_step == 1 || proc_step == 3) { 00412 take_cursor_char_setup(proc_step); 00413 } 00414 } 00415 break; 00416 case GDC_CMD_PARAMETER_RAM_LOAD: 00417 param_ram[param_ram_wptr] = (uint8_t)val; 00418 if ((++param_ram_wptr) >= 16) param_ram_wptr = 0; 00419 break; 00420 } 00421 } 00422 00423 if (!fifo_empty()) 00424 gdc_proc_schedule_delay(); 00425 } 00426 00427 bool PC98_GDC_state::fifo_empty(void) { 00428 return (fifo_read >= fifo_write); 00429 } 00430 00431 Bit16u PC98_GDC_state::read_fifo(void) { 00432 Bit16u val; 00433 00434 val = fifo[fifo_read]; 00435 if (fifo_read < fifo_write) 00436 fifo_read++; 00437 00438 return val; 00439 } 00440 00441 void PC98_GDC_state::next_line(void) { 00442 /* */ 00443 00444 row_line++; 00445 if (row_line == row_height || /*HACK! See comments!*/(!master_sync/*graphics layer*/ && (pc98_gdc_vramop & (1u << VOPBIT_VGA)))) { 00446 /* NTS: According to real PC-9821 hardware, doublescan is ignored entirely in 256-color mode. 00447 * The bits are still there in the GDC, and doublescan comes right back when 256-color mode is switched off. 00448 * Perhaps the hardware uses an entirely different address counting register during video raster? 00449 * Forcing this case at all times for 256-color mode is meant to emulate that fact. 00450 * This fixes issues with booting an HDI image of "Alone in the Dark" */ 00451 scan_address += display_pitch >> (IM_bit ? 1u : 0u); 00452 row_line = 0; 00453 } 00454 else if (row_line & 0x20) { 00455 row_line = 0; 00456 } 00457 00458 if (--display_partition_rem_lines == 0) { 00459 next_display_partition(); 00460 load_display_partition(); 00461 } 00462 } 00463 00464 void PC98_GDC_state::begin_frame(void) { 00465 row_line = 0; 00466 scan_address = 0; 00467 display_partition = 0; 00468 00469 /* the actual starting address is determined by the display partition in paramter RAM */ 00470 load_display_partition(); 00471 } 00472 00473 void PC98_GDC_state::load_display_partition(void) { 00474 unsigned char *pram = param_ram + (display_partition * 4); 00475 00476 scan_address = pram[0]; 00477 scan_address += pram[1] << 8; 00478 scan_address += (pram[2] & 0x03) << 16; 00479 00480 display_partition_rem_lines = pram[2] >> 4; 00481 display_partition_rem_lines += (pram[3] & 0x3F) << 4; 00482 if (display_partition_rem_lines == 0) 00483 display_partition_rem_lines = 0x400; 00484 00485 if (master_sync) { /* character mode */ 00486 /* RAM+0 = SAD1 (L) 00487 * 00488 * RAM+1 = 0 0 0 SAH1 (M) [4:0] 00489 * 00490 * RAM+2 = LEN1 (L) [7:4] 0 0 0 0 00491 * 00492 * RAM+3 = WD1 0 LEN1 (H) [5:0] */ 00493 scan_address &= 0x1FFF; 00494 IM_bit = false; 00495 } 00496 else { /* graphics mode */ 00497 /* RAM+0 = SAD1 (L) 00498 * 00499 * RAM+1 = SAH1 (M) 00500 * 00501 * RAM+2 = LEN1 (L) [7:4] 0 0 SAD1 (H) [1:0] 00502 * 00503 * RAM+3 = WD1 IM LEN1 (H) [5:0] */ 00504 IM_bit = !!(pram[3] & 0x40); /* increment the address every other cycle if set, mixed text/graphics only */ 00505 } 00506 } 00507 00508 void PC98_GDC_state::force_fifo_complete(void) { 00509 while (!fifo_empty()) 00510 idle_proc(); 00511 } 00512 00513 void PC98_GDC_state::next_display_partition(void) { 00514 display_partition = (display_partition + 1) & display_partition_mask; 00515 } 00516 00517 void PC98_GDC_state::reset_fifo(void) { 00518 fifo_read = fifo_write = 0; 00519 } 00520 00521 void PC98_GDC_state::reset_rfifo(void) { 00522 rfifo_read = rfifo_write = 0; 00523 } 00524 00525 void PC98_GDC_state::flush_fifo_old(void) { 00526 if (fifo_read != 0) { 00527 unsigned int sz = (fifo_read <= fifo_write) ? ((unsigned int)fifo_write - (unsigned int)fifo_read) : 0u; 00528 00529 for (unsigned int i=0;i < sz;i++) 00530 fifo[i] = fifo[i+fifo_read]; 00531 00532 fifo_read = 0; 00533 fifo_write = sz; 00534 } 00535 } 00536 00537 bool PC98_GDC_state::write_rfifo(const uint8_t c) { 00538 if (rfifo_write >= PC98_GDC_FIFO_SIZE) 00539 return false; 00540 00541 rfifo[rfifo_write++] = c; 00542 return true; 00543 } 00544 00545 bool PC98_GDC_state::write_fifo(const uint16_t c) { 00546 if (fifo_write >= PC98_GDC_FIFO_SIZE) 00547 flush_fifo_old(); 00548 if (fifo_write >= PC98_GDC_FIFO_SIZE) 00549 return false; 00550 00551 fifo[fifo_write++] = c; 00552 gdc_proc_schedule_delay(); 00553 return true; 00554 } 00555 00556 bool PC98_GDC_state::write_fifo_command(const unsigned char c) { 00557 return write_fifo(c | GDC_COMMAND_BYTE); 00558 } 00559 00560 bool PC98_GDC_state::write_fifo_param(const unsigned char c) { 00561 return write_fifo(c); 00562 } 00563 00564 bool PC98_GDC_state::rfifo_has_content(void) { 00565 return (rfifo_read < rfifo_write); 00566 } 00567 00568 uint8_t PC98_GDC_state::read_status(void) { 00569 double timeInFrame = PIC_FullIndex()-vga.draw.delay.framestart; 00570 double timeInLine=fmod(timeInFrame,vga.draw.delay.htotal); 00571 uint8_t ret; 00572 00573 ret = 0x00; // light pen not present 00574 00575 if (timeInLine >= vga.draw.delay.hblkstart && 00576 timeInLine <= vga.draw.delay.hblkend) 00577 ret |= 0x40; // horizontal blanking 00578 00579 if (timeInFrame >= vga.draw.delay.vrstart && 00580 timeInFrame <= vga.draw.delay.vrend) 00581 ret |= 0x20; // vertical retrace 00582 00583 // TODO: 0x10 bit 4 DMA execute 00584 00585 // TODO: 0x08 bit 3 drawing in progress 00586 00587 if (fifo_write >= PC98_GDC_FIFO_SIZE) 00588 flush_fifo_old(); 00589 00590 if (fifo_read == fifo_write) 00591 ret |= 0x04; // FIFO empty 00592 if (fifo_write >= PC98_GDC_FIFO_SIZE) 00593 ret |= 0x02; // FIFO full 00594 if (rfifo_has_content()) 00595 ret |= 0x01; // data ready 00596 00597 return ret; 00598 } 00599 00600 uint8_t PC98_GDC_state::rfifo_read_data(void) { 00601 uint8_t ret; 00602 00603 ret = rfifo[rfifo_read]; 00604 if (rfifo_read < rfifo_write) { 00605 if (++rfifo_read >= rfifo_write) { 00606 rfifo_read = rfifo_write = 0; 00607 rfifo[0] = ret; 00608 } 00609 } 00610 00611 return ret; 00612 } 00613 00614 void gdc_proc_schedule_delay(void) { 00615 if (!gdc_proc_delay_set) { 00616 PIC_AddEvent(GDC_ProcDelay,(float)gdc_proc_delay); 00617 gdc_proc_delay_set = false; 00618 } 00619 } 00620 00621 void gdc_proc_schedule_cancel(void) { 00622 if (gdc_proc_delay_set) { 00623 PIC_RemoveEvents(GDC_ProcDelay); 00624 gdc_proc_delay_set = false; 00625 } 00626 } 00627 00628 void gdc_proc_schedule_done(void) { 00629 gdc_proc_delay_set = false; 00630 } 00631 00632 void PC98_show_cursor(bool show) { 00633 pc98_gdc[GDC_MASTER].force_fifo_complete(); 00634 00635 pc98_gdc[GDC_MASTER].cursor_enable = show; 00636 00637 /* NTS: Showing/hiding the cursor involves sending a GDC command that 00638 * sets both the cursor visibility bit and the "lines per character 00639 * row" field. 00640 * 00641 * The PC-98 BIOS will re-read this from the BIOS data area */ 00642 pc98_gdc[GDC_MASTER].row_height = (mem_readb(0x53B) & 0x1F) + 1; 00643 } 00644 00645 void GDC_ProcDelay(Bitu /*val*/) { 00646 gdc_proc_schedule_done(); 00647 00648 for (unsigned int i=0;i < 2;i++) 00649 pc98_gdc[i].idle_proc(); // may schedule another delayed proc 00650 } 00651 00652 bool gdc_5mhz_according_to_bios(void) { 00653 return !!(mem_readb(0x54D) & 0x04); 00654 } 00655 00656 void gdc_5mhz_mode_update_vars(void) { 00657 unsigned char b; 00658 00659 b = mem_readb(0x54D); 00660 00661 /* bit[5:5] = GDC at 5.0MHz at boot up (copy of DIP switch 2-8 at startup) 1=yes 0=no 00662 * bit[2:2] = GDC clock 1=5MHz 0=2.5MHz */ 00663 00664 if (gdc_5mhz_mode_initial) 00665 b |= 0x20; 00666 else 00667 b &= ~0x20; 00668 00669 if (gdc_5mhz_mode) 00670 b |= 0x04; 00671 else 00672 b &= ~0x04; 00673 00674 mem_writeb(0x54D,b); 00675 } 00676 00677 /*==================================================*/ 00678 00679 void pc98_gdc_write(Bitu port,Bitu val,Bitu iolen) { 00680 (void)iolen;//UNUSED 00681 PC98_GDC_state *gdc; 00682 00683 if (port >= 0xA0) 00684 gdc = &pc98_gdc[GDC_SLAVE]; 00685 else 00686 gdc = &pc98_gdc[GDC_MASTER]; 00687 00688 switch (port&0xE) { 00689 case 0x00: /* 0x60/0xA0 param write fifo */ 00690 if (!gdc->write_fifo_param((unsigned char)val)) 00691 LOG_MSG("GDC warning: FIFO param overrun"); 00692 break; 00693 case 0x02: /* 0x62/0xA2 command write fifo */ 00694 if (!gdc->write_fifo_command((unsigned char)val)) 00695 LOG_MSG("GDC warning: FIFO command overrun"); 00696 break; 00697 case 0x04: /* 0x64: set trigger to signal vsync interrupt (IRQ 2) */ 00698 /* 0xA4: Bit 0 select display "plane" */ 00699 if (port == 0x64) 00700 GDC_vsync_interrupt = true; 00701 else { 00702 /* NTS: Testing on real hardware suggests that this bit is NOT double buffered, 00703 * therefore you can cause a tearline flipping the bit mid-screen without 00704 * waiting for vertical blanking. 00705 * 00706 * But: For the user's preference, we do offer a hack to delay display plane 00707 * change until vsync to try to alleviate tearlines. */ 00708 GDC_display_plane_pending = (val&1); 00709 if (!GDC_display_plane_wait_for_vsync) { 00710 GDC_display_plane = GDC_display_plane_pending; 00711 pc98_update_display_page_ptr(); 00712 } 00713 } 00714 break; 00715 case 0x06: /* 0x66: ?? 00716 0xA6: Bit 0 select CPU access "plane" */ 00717 if (port == 0xA6) { 00718 pc98_gdc_vramop &= ~(1 << VOPBIT_ACCESS); 00719 pc98_gdc_vramop |= (val&1) << VOPBIT_ACCESS; 00720 pc98_update_cpu_page_ptr(); 00721 } 00722 else { 00723 goto unknown; 00724 } 00725 break; 00726 case 0x08: /* 0xA8: One of two meanings, depending on 8 or 16/256--color mode */ 00727 /* 8-color: 0xA8-0xAB are 8 4-bit packed fields remapping the 3-bit GRB colors. This defines colors #3 [7:4] and #7 [3:0] 00728 * 16-color: GRB color palette index */ 00729 /* 0x68: A command */ 00730 /* NTS: Sadly, "undocumented PC-98" reference does not mention the analog 16-color palette. */ 00731 if (port == 0xA8) { 00732 if (gdc_analog) { /* 16/256-color mode */ 00733 pc98_16col_analog_rgb_palette_index = (uint8_t)val; /* it takes all 8 bits I assume because of 256-color mode */ 00734 } 00735 else { 00736 pc98_set_digpal_pair(3,(unsigned char)val); 00737 } 00738 } 00739 else { 00740 pc98_port68_command_write((unsigned char)val); 00741 } 00742 break; 00743 case 0x0A: /* 0xAA: 00744 8-color: Defines color #1 [7:4] and color #5 [3:0] (FIXME: Or is it 2 and 6, by undocumented PC-98???) 00745 16-color: 4-bit green intensity. Color index is low 4 bits of palette index. 00746 256-color: 4-bit green intensity. Color index is 8-bit palette index. */ 00747 if (port == 0xAA) { /* TODO: If 8-color... else if 16-color... else if 256-color... */ 00748 if (gdc_analog) { /* 16/256-color mode */ 00749 if (pc98_gdc_vramop & (1 << VOPBIT_VGA)) { 00750 pc98_pal_vga[(3*pc98_16col_analog_rgb_palette_index) + 0] = (uint8_t)val; 00751 vga.dac.rgb[pc98_16col_analog_rgb_palette_index].green = (Bit8u)val; 00752 VGA_DAC_UpdateColor(pc98_16col_analog_rgb_palette_index); 00753 } 00754 else { 00755 pc98_pal_analog[(3*(pc98_16col_analog_rgb_palette_index&0xF)) + 0] = val&0x0F; 00756 vga.dac.rgb[pc98_16col_analog_rgb_palette_index & 0xF].green = dac_4to6(val&0xF); /* re-use VGA DAC */ 00757 VGA_DAC_UpdateColor(pc98_16col_analog_rgb_palette_index & 0xF); 00758 } 00759 } 00760 else { 00761 pc98_set_digpal_pair(1,(unsigned char)val); 00762 } 00763 } 00764 else { 00765 pc98_port6A_command_write((unsigned char)val); 00766 } 00767 break; 00768 case 0x0C: /* 0xAC: 00769 8-color: Defines color #2 [7:4] and color #6 [3:0] (FIXME: Or is it 1 and 4, by undocumented PC-98???) 00770 16-color: 4-bit red intensity. Color index is low 4 bits of palette index. 00771 256-color: 4-bit red intensity. Color index is 8-bit palette index. */ 00772 if (port == 0xAC) { /* TODO: If 8-color... else if 16-color... else if 256-color... */ 00773 if (gdc_analog) { /* 16/256-color mode */ 00774 if (pc98_gdc_vramop & (1 << VOPBIT_VGA)) { 00775 pc98_pal_vga[(3*pc98_16col_analog_rgb_palette_index) + 1] = (uint8_t)val; 00776 vga.dac.rgb[pc98_16col_analog_rgb_palette_index].red = (Bit8u)val; 00777 VGA_DAC_UpdateColor(pc98_16col_analog_rgb_palette_index); 00778 } 00779 else { 00780 pc98_pal_analog[(3*(pc98_16col_analog_rgb_palette_index&0xF)) + 1] = val&0x0F; 00781 vga.dac.rgb[pc98_16col_analog_rgb_palette_index & 0xF].red = dac_4to6(val&0xF); /* re-use VGA DAC */ 00782 VGA_DAC_UpdateColor(pc98_16col_analog_rgb_palette_index & 0xF); 00783 } 00784 } 00785 else { 00786 pc98_set_digpal_pair(2,(unsigned char)val); 00787 } 00788 } 00789 else { 00790 goto unknown; 00791 } 00792 break; 00793 case 0x0E: /* 0xAE: 00794 8-color: Defines color #2 [7:4] and color #6 [3:0] (FIXME: Or is it 1 and 4, by undocumented PC-98???) 00795 16-color: 4-bit blue intensity. Color index is low 4 bits of palette index. 00796 256-color: 4-bit blue intensity. Color index is 8-bit palette index. */ 00797 if (port == 0xAE) { /* TODO: If 8-color... else if 16-color... else if 256-color... */ 00798 if (gdc_analog) { /* 16/256-color mode */ 00799 if (pc98_gdc_vramop & (1 << VOPBIT_VGA)) { 00800 pc98_pal_vga[(3*pc98_16col_analog_rgb_palette_index) + 2] = (uint8_t)val; 00801 vga.dac.rgb[pc98_16col_analog_rgb_palette_index].blue = (Bit8u)val; 00802 VGA_DAC_UpdateColor(pc98_16col_analog_rgb_palette_index); 00803 } 00804 else { 00805 pc98_pal_analog[(3*(pc98_16col_analog_rgb_palette_index&0xF)) + 2] = val&0x0F; 00806 vga.dac.rgb[pc98_16col_analog_rgb_palette_index & 0xF].blue = dac_4to6(val&0xF); /* re-use VGA DAC */ 00807 VGA_DAC_UpdateColor(pc98_16col_analog_rgb_palette_index & 0xF); 00808 } 00809 } 00810 else { 00811 pc98_set_digpal_pair(0,(unsigned char)val); 00812 } 00813 } 00814 else { 00815 goto unknown; 00816 } 00817 break; 00818 default: 00819 unknown: 00820 LOG_MSG("GDC unexpected write to port 0x%x val=0x%x",(unsigned int)port,(unsigned int)val); 00821 break; 00822 } 00823 } 00824 00825 Bitu pc98_gdc_read(Bitu port,Bitu iolen) { 00826 (void)iolen;//UNUSED 00827 PC98_GDC_state *gdc; 00828 00829 if (port >= 0xA0) 00830 gdc = &pc98_gdc[GDC_SLAVE]; 00831 else 00832 gdc = &pc98_gdc[GDC_MASTER]; 00833 00834 switch (port&0xE) { 00835 case 0x00: /* 0x60/0xA0 read status */ 00836 return gdc->read_status(); 00837 case 0x02: /* 0x62/0xA2 read fifo */ 00838 if (!gdc->rfifo_has_content()) 00839 return gdc->read_status();//FIXME this stops "Battle Skin Panic" from getting stuck is this correct behavior? 00840 00841 return gdc->rfifo_read_data(); 00842 case 0x04: /* 0x64: nothing */ 00843 /* 0xA4: Bit 0 select display "plane" */ 00844 if (port == 0x64) { 00845 goto unknown; 00846 } 00847 else { 00848 return GDC_display_plane_pending; 00849 } 00850 break; 00851 case 0x06: /* 0x66: ?? 00852 0xA6: Bit 0 indicates current CPU access "plane" */ 00853 if (port == 0xA6) { 00854 return (pc98_gdc_vramop & (1 << VOPBIT_ACCESS)) ? 1 : 0; 00855 } 00856 else { 00857 goto unknown; 00858 } 00859 break; 00860 case 0x08: 00861 if (port == 0xA8) { 00862 if (gdc_analog) { /* 16/256-color mode */ 00863 return pc98_16col_analog_rgb_palette_index; 00864 } 00865 else { 00866 return pc98_get_digpal_pair(3); 00867 } 00868 } 00869 else { 00870 goto unknown; 00871 } 00872 break; 00873 case 0x0A: 00874 if (port == 0xAA) { /* TODO: If 8-color... else if 16-color... else if 256-color... */ 00875 if (gdc_analog) { /* 16/256-color mode */ 00876 if (pc98_gdc_vramop & (1 << VOPBIT_VGA)) 00877 return pc98_pal_vga[(3*pc98_16col_analog_rgb_palette_index) + 0]; 00878 else 00879 return pc98_pal_analog[(3*(pc98_16col_analog_rgb_palette_index&0xF)) + 0]; 00880 } 00881 else { 00882 return pc98_get_digpal_pair(1); 00883 } 00884 } 00885 else { 00886 goto unknown; 00887 } 00888 break; 00889 case 0x0C: 00890 if (port == 0xAC) { /* TODO: If 8-color... else if 16-color... else if 256-color... */ 00891 if (gdc_analog) { /* 16/256-color mode */ 00892 if (pc98_gdc_vramop & (1 << VOPBIT_VGA)) 00893 return pc98_pal_vga[(3*pc98_16col_analog_rgb_palette_index) + 1]; 00894 else 00895 return pc98_pal_analog[(3*(pc98_16col_analog_rgb_palette_index&0xF)) + 1]; 00896 } 00897 else { 00898 return pc98_get_digpal_pair(2); 00899 } 00900 } 00901 else { 00902 goto unknown; 00903 } 00904 break; 00905 case 0x0E: 00906 if (port == 0xAE) { /* TODO: If 8-color... else if 16-color... else if 256-color... */ 00907 if (gdc_analog) { /* 16/256-color mode */ 00908 if (pc98_gdc_vramop & (1 << VOPBIT_VGA)) 00909 return pc98_pal_vga[(3*pc98_16col_analog_rgb_palette_index) + 2]; 00910 else 00911 return pc98_pal_analog[(3*(pc98_16col_analog_rgb_palette_index&0xF)) + 2]; 00912 } 00913 else { 00914 return pc98_get_digpal_pair(0); 00915 } 00916 } 00917 else { 00918 goto unknown; 00919 } 00920 break; 00921 default: 00922 unknown: 00923 LOG_MSG("GDC unexpected read from port 0x%x",(unsigned int)port); 00924 break; 00925 } 00926 00927 return ~0ul; 00928 } 00929