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