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