DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/vga_pc98_gdc.cpp
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