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