DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/vga_memory.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
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 
00020 #include <math.h>
00021 #include <stdio.h>
00022 #include <stdlib.h>
00023 #include <string.h>
00024 #include "dosbox.h"
00025 #include "mem.h"
00026 #include "vga.h"
00027 #include "paging.h"
00028 #include "pic.h"
00029 #include "inout.h"
00030 #include "setup.h"
00031 #include "cpu.h"
00032 #include "pc98_cg.h"
00033 #include "pc98_gdc.h"
00034 #include "zipfile.h"
00035 
00036 unsigned char pc98_pegc_mmio[0x200] = {0}; /* PC-98 memory-mapped PEGC registers at E0000h */
00037 uint32_t pc98_pegc_banks[2] = {0x0000,0x0000}; /* bank switching offsets */
00038 
00039 extern bool non_cga_ignore_oddeven;
00040 extern bool non_cga_ignore_oddeven_engage;
00041 extern bool enable_pc98_256color_planar;
00042 extern bool enable_pc98_256color;
00043 
00044 #ifndef C_VGARAM_CHECKED
00045 #define C_VGARAM_CHECKED 1
00046 #endif
00047 
00048 #if C_VGARAM_CHECKED
00049 // Checked linear offset
00050 #define CHECKED(v) ((v)&vga.mem.memmask)
00051 // Checked planar offset (latched access)
00052 #define CHECKED2(v) ((v)&(vga.mem.memmask>>2))
00053 #else
00054 #define CHECKED(v) (v)
00055 #define CHECKED2(v) (v)
00056 #endif
00057 
00058 #define CHECKED3(v) ((v)&vga.mem.memmask)
00059 #define CHECKED4(v) ((v)&(vga.mem.memmask>>2))
00060 
00061 #define TANDY_VIDBASE(_X_)  &MemBase[ 0x80000 + (_X_)]
00062 
00063 /* how much delay to add to VGA memory I/O in nanoseconds */
00064 int vga_memio_delay_ns = 1000;
00065 
00066 void VGAMEM_USEC_read_delay() {
00067         if (vga_memio_delay_ns > 0) {
00068                 Bits delaycyc = (CPU_CycleMax * vga_memio_delay_ns) / 1000000;
00069 //              if(GCC_UNLIKELY(CPU_Cycles < 3*delaycyc)) delaycyc = 0; //Else port acces will set cycles to 0. which might trigger problem with games which read 16 bit values
00070                 CPU_Cycles -= delaycyc;
00071                 CPU_IODelayRemoved += delaycyc;
00072         }
00073 }
00074 
00075 void VGAMEM_USEC_write_delay() {
00076         if (vga_memio_delay_ns > 0) {
00077                 Bits delaycyc = (CPU_CycleMax * vga_memio_delay_ns * 3) / (1000000 * 4);
00078 //              if(GCC_UNLIKELY(CPU_Cycles < 3*delaycyc)) delaycyc = 0; //Else port acces will set cycles to 0. which might trigger problem with games which read 16 bit values
00079                 CPU_Cycles -= delaycyc;
00080                 CPU_IODelayRemoved += delaycyc;
00081         }
00082 }
00083 
00084 template <class Size>
00085 static INLINE void hostWrite(HostPt off, Bitu val) {
00086         if ( sizeof( Size ) == 1)
00087                 host_writeb( off, (Bit8u)val );
00088         else if ( sizeof( Size ) == 2)
00089                 host_writew( off, (Bit16u)val );
00090         else if ( sizeof( Size ) == 4)
00091                 host_writed( off, (Bit32u)val );
00092 }
00093 
00094 template <class Size>
00095 static INLINE Bitu  hostRead(HostPt off ) {
00096         if ( sizeof( Size ) == 1)
00097                 return host_readb( off );
00098         else if ( sizeof( Size ) == 2)
00099                 return host_readw( off );
00100         else if ( sizeof( Size ) == 4)
00101                 return host_readd( off );
00102         return 0;
00103 }
00104 
00105 
00106 void VGA_MapMMIO(void);
00107 //Nice one from DosEmu
00108 INLINE static Bit32u RasterOp(Bit32u input,Bit32u mask) {
00109         switch (vga.config.raster_op) {
00110         case 0x00:      /* None */
00111                 return (input & mask) | (vga.latch.d & ~mask);
00112         case 0x01:      /* AND */
00113                 return (input | ~mask) & vga.latch.d;
00114         case 0x02:      /* OR */
00115                 return (input & mask) | vga.latch.d;
00116         case 0x03:      /* XOR */
00117                 return (input & mask) ^ vga.latch.d;
00118         }
00119         return 0;
00120 }
00121 
00122 INLINE static Bit32u ModeOperation(Bit8u val) {
00123         Bit32u full;
00124         switch (vga.config.write_mode) {
00125         case 0x00:
00126                 // Write Mode 0: In this mode, the host data is first rotated as per the Rotate Count field, then the Enable Set/Reset mechanism selects data from this or the Set/Reset field. Then the selected Logical Operation is performed on the resulting data and the data in the latch register. Then the Bit Mask field is used to select which bits come from the resulting data and which come from the latch register. Finally, only the bit planes enabled by the Memory Plane Write Enable field are written to memory. 
00127                 val=((val >> vga.config.data_rotate) | (val << (8-vga.config.data_rotate)));
00128                 full=ExpandTable[val];
00129                 full=(full & vga.config.full_not_enable_set_reset) | vga.config.full_enable_and_set_reset; 
00130                 full=RasterOp(full,vga.config.full_bit_mask);
00131                 break;
00132         case 0x01:
00133                 // Write Mode 1: In this mode, data is transferred directly from the 32 bit latch register to display memory, affected only by the Memory Plane Write Enable field. The host data is not used in this mode. 
00134                 full=vga.latch.d;
00135                 break;
00136         case 0x02:
00137                 //Write Mode 2: In this mode, the bits 3-0 of the host data are replicated across all 8 bits of their respective planes. Then the selected Logical Operation is performed on the resulting data and the data in the latch register. Then the Bit Mask field is used to select which bits come from the resulting data and which come from the latch register. Finally, only the bit planes enabled by the Memory Plane Write Enable field are written to memory. 
00138                 full=RasterOp(FillTable[val&0xF],vga.config.full_bit_mask);
00139                 break;
00140         case 0x03:
00141                 // Write Mode 3: In this mode, the data in the Set/Reset field is used as if the Enable Set/Reset field were set to 1111b. Then the host data is first rotated as per the Rotate Count field, then logical ANDed with the value of the Bit Mask field. The resulting value is used on the data obtained from the Set/Reset field in the same way that the Bit Mask field would ordinarily be used. to select which bits come from the expansion of the Set/Reset field and which come from the latch register. Finally, only the bit planes enabled by the Memory Plane Write Enable field are written to memory.
00142                 val=((val >> vga.config.data_rotate) | (val << (8-vga.config.data_rotate)));
00143                 full=RasterOp(vga.config.full_set_reset,ExpandTable[val] & vga.config.full_bit_mask);
00144                 break;
00145         default:
00146                 LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:Unsupported write mode %d",vga.config.write_mode);
00147                 full=0;
00148                 break;
00149         }
00150         return full;
00151 }
00152 
00153 bool pc98_pegc_linear_framebuffer_enabled(void) {
00154     return !!(pc98_pegc_mmio[0x102] & 1);
00155 }
00156 
00157 // TODO: This code may have to handle 16-bit MMIO reads
00158 Bit8u pc98_pegc_mmio_read(unsigned int reg) {
00159     if (reg >= 0x200)
00160         return 0x00;
00161 
00162     return pc98_pegc_mmio[reg];
00163 }
00164 
00165 // TODO: This code may have to handle 16-bit MMIO writes
00166 void pc98_pegc_mmio_write(unsigned int reg,Bit8u val) {
00167     if (reg >= 0x200)
00168         return;
00169 
00170     Bit8u pval = pc98_pegc_mmio[reg];
00171 
00172     switch (reg) {
00173         case 0x004: // bank 0
00174             pc98_pegc_banks[0] = (val & 0xFu) << 15u;
00175             pc98_pegc_mmio[reg] = val;
00176             break;
00177         case 0x005: // unknown (WORD size write seen by bank switched (battle skin) and LFB (DOOM, DOOM2) games)
00178             // ignore
00179             break;
00180         case 0x006: // bank 1
00181             pc98_pegc_banks[1] = (val & 0xFu) << 15u;
00182             pc98_pegc_mmio[reg] = val;
00183             break;
00184         case 0x007: // unknown (WORD size write seen by bank switched (battle skin) and LFB (DOOM, DOOM2) games)
00185             // ignore
00186             break;
00187         case 0x100: // 256-color memory access  (0=packed  1=planar)
00188             // WE DO NOT SUPPORT 256-planar MEMORY ACCESS AT THIS TIME!
00189             // FUTURE SUPPORT IS PLANNED.
00190             // ignore
00191             if (enable_pc98_256color_planar) {
00192                 val &= 1;
00193                 if (val & 1) {
00194                     pc98_gdc_vramop |= (1 << VOPBIT_PEGC_PLANAR);
00195                     LOG_MSG("PC-98 PEGC warning: Guest application/OS attempted to enable "
00196                             "256-color planar mode, which is not yet fully functional");
00197                 }
00198                 else {
00199                     pc98_gdc_vramop &= ~(1 << VOPBIT_PEGC_PLANAR);
00200                 }
00201             }
00202             else {
00203                 if (val & 1)
00204                     LOG_MSG("PC-98 PEGC warning: Guest application/OS attempted to enable "
00205                             "256-color planar mode, which is not enabled in your configuration");
00206  
00207                 val = 0;
00208             }
00209             pc98_pegc_mmio[reg] = val;
00210             if ((val^pval)&1/*if bit 0 changed*/)
00211                 VGA_SetupHandlers();
00212             break;
00213         case 0x102: // linear framebuffer (at F00000-F7FFFFh) enable/disable
00214             val &= 1; // as seen on real hardware: only bit 0 can be changed
00215             pc98_pegc_mmio[reg] = val;
00216             if ((val^pval)&1/*if bit 0 changed*/)
00217                 VGA_SetupHandlers();
00218             // FIXME: One PC-9821 laptop seems to allow bit 0 and bit 1 to be set.
00219             //        What does bit 1 control?
00220             break;
00221         default:
00222             LOG_MSG("PC-98 PEGC warning: Unhandled write to %xh val %xh",reg+0xE0000u,val);
00223             break;
00224     }
00225 }
00226 
00227 /* Gonna assume that whoever maps vga memory, maps it on 32/64kb boundary */
00228 
00229 #define VGA_PAGES               (128/4)
00230 #define VGA_PAGE_A0             (0xA0000/4096)
00231 #define VGA_PAGE_B0             (0xB0000/4096)
00232 #define VGA_PAGE_B8             (0xB8000/4096)
00233 
00234 static struct {
00235         Bitu base, mask;
00236 } vgapages;
00237 
00238 static inline Bitu VGA_Generic_Read_Handler(PhysPt planeaddr,PhysPt rawaddr,unsigned char plane) {
00239     const unsigned char hobit_n = (vga.seq.memory_mode&2/*Extended Memory*/) ? 16u : 14u;
00240 
00241     /* Sequencer Memory Mode Register (04h)
00242      * bits[3:3] = Chain 4 enable
00243      * bits[2:2] = Odd/Even Host Memory Write Addressing Disable
00244      * bits[1:1] = Extended memory (when EGA cards have > 64KB of RAM)
00245      * 
00246      * NTS: Real hardware experience says that despite the name, the Odd/Even bit affects reading as well */
00247     if (!(vga.seq.memory_mode&4) && !non_cga_ignore_oddeven_engage)/* Odd Even Host Memory Write Addressing Disable (is not set) */
00248         plane = (plane & ~1u) + (rawaddr & 1u);
00249 
00250     /* Graphics Controller: Miscellaneous Graphics Register register (06h)
00251      * bits[3:2] = memory map select
00252      * bits[1:1] = Chain Odd/Even Enable
00253      * bits[0:0] = Alphanumeric Mode Disable
00254      *
00255      * http://www.osdever.net/FreeVGA/vga/graphreg.htm
00256      *
00257      * When enabled, address bit A0 (bit 0) becomes bit 0 of the plane index.
00258      * Then when addressing VRAM A0 is replaced by a "higher order bit", which is
00259      * probably A14 or A16 depending on Extended Memory bit 1 in Sequencer register 04h memory mode */
00260     if ((vga.gfx.miscellaneous&2) && !non_cga_ignore_oddeven_engage) {/* Odd/Even enable */
00261         const PhysPt mask = (vga.config.compatible_chain4 ? 0u : ~0xFFFFu) + (1u << hobit_n) - 2u;
00262         const PhysPt hobit = (planeaddr >> hobit_n) & 1u;
00263         /* 1 << 14 =     0x4000
00264          * 1 << 14 - 1 = 0x3FFF
00265          * 1 << 14 - 2 = 0x3FFE
00266          * The point is to mask upper bit AND the LSB */
00267         planeaddr = (planeaddr & mask & (vga.mem.memmask >> 2u)) + hobit;
00268     }
00269     else {
00270         const PhysPt mask = (vga.config.compatible_chain4 ? 0u : ~0xFFFFu) + (1u << hobit_n) - 1u;
00271         planeaddr &= mask & (vga.mem.memmask >> 2u);
00272     }
00273 
00274     vga.latch.d=((Bit32u*)vga.mem.linear)[planeaddr];
00275     switch (vga.config.read_mode) {
00276         case 0:
00277             return (vga.latch.b[plane]);
00278         case 1:
00279             VGA_Latch templatch;
00280             templatch.d=(vga.latch.d & FillTable[vga.config.color_dont_care]) ^ FillTable[vga.config.color_compare & vga.config.color_dont_care];
00281             return (Bit8u)~(templatch.b[0] | templatch.b[1] | templatch.b[2] | templatch.b[3]);
00282     }
00283 
00284     return 0;
00285 }
00286 
00287 template <const bool chained> static inline void VGA_Generic_Write_Handler(PhysPt planeaddr,PhysPt rawaddr,Bit8u val) {
00288     const unsigned char hobit_n = (vga.seq.memory_mode&2/*Extended Memory*/) ? 16u : 14u;
00289     Bit32u mask = vga.config.full_map_mask;
00290 
00291     /* Sequencer Memory Mode Register (04h)
00292      * bits[3:3] = Chain 4 enable
00293      * bits[2:2] = Odd/Even Host Memory Write Addressing Disable
00294      * bits[1:1] = Extended memory (when EGA cards have > 64KB of RAM)
00295      * 
00296      * NTS: Real hardware experience says that despite the name, the Odd/Even bit affects reading as well */
00297     if (chained) {
00298         if (!(vga.seq.memory_mode&4) && !non_cga_ignore_oddeven_engage)/* Odd Even Host Memory Write Addressing Disable (is not set) */
00299             mask &= 0xFF00FFu << ((rawaddr & 1u) * 8u);
00300         else
00301             mask &= 0xFFu << ((rawaddr & 3u) * 8u);
00302     }
00303     else {
00304         if (!(vga.seq.memory_mode&4) && !non_cga_ignore_oddeven_engage)/* Odd Even Host Memory Write Addressing Disable (is not set) */
00305             mask &= 0xFF00FFu << ((rawaddr & 1u) * 8u);
00306     }
00307 
00308     /* Graphics Controller: Miscellaneous Graphics Register register (06h)
00309      * bits[3:2] = memory map select
00310      * bits[1:1] = Chain Odd/Even Enable
00311      * bits[0:0] = Alphanumeric Mode Disable
00312      *
00313      * http://www.osdever.net/FreeVGA/vga/graphreg.htm
00314      *
00315      * When enabled, address bit A0 (bit 0) becomes bit 0 of the plane index.
00316      * Then when addressing VRAM A0 is replaced by a "higher order bit", which is
00317      * probably A14 or A16 depending on Extended Memory bit 1 in Sequencer register 04h memory mode */
00318     if ((vga.gfx.miscellaneous&2) && !non_cga_ignore_oddeven_engage) {/* Odd/Even enable */
00319         const PhysPt mask = (vga.config.compatible_chain4 ? 0u : ~0xFFFFu) + (1u << hobit_n) - 2u;
00320         const PhysPt hobit = (planeaddr >> hobit_n) & 1u;
00321         /* 1 << 14 =     0x4000
00322          * 1 << 14 - 1 = 0x3FFF
00323          * 1 << 14 - 2 = 0x3FFE
00324          * The point is to mask upper bit AND the LSB */
00325         planeaddr = (planeaddr & mask & (vga.mem.memmask >> 2u)) + hobit;
00326     }
00327     else {
00328         const PhysPt mask = (vga.config.compatible_chain4 ? 0u : ~0xFFFFu) + (1u << hobit_n) - 1u;
00329         planeaddr &= mask & (vga.mem.memmask >> 2u);
00330     }
00331 
00332     Bit32u data=ModeOperation(val);
00333     VGA_Latch pixels;
00334 
00335     pixels.d =((Bit32u*)vga.mem.linear)[planeaddr];
00336     pixels.d&=~mask;
00337     pixels.d|=(data & mask);
00338 
00339     /* FIXME: A better method (I think) is to have the VGA text drawing code
00340      *        directly reference the font data in bitplane #2 instead of
00341      *        this hack */
00342     vga.draw.font[planeaddr] = pixels.b[2];
00343 
00344     ((Bit32u*)vga.mem.linear)[planeaddr]=pixels.d;
00345 }
00346 
00347 // Slow accurate emulation.
00348 // This version takes the Graphics Controller bitmask and ROPs into account.
00349 // This is needed for demos that use the bitmask to do color combination or bitplane "page flipping" tricks.
00350 // This code will kick in if running in a chained VGA mode and the graphics controller bitmask register is
00351 // changed to anything other than 0xFF.
00352 //
00353 // Impact Studios "Legend"
00354 //  - The rotating objects, rendered as dots, needs this hack because it uses a combination of masking off
00355 //    bitplanes using the VGA DAC pel mask and drawing on the hidden bitplane using the Graphics Controller
00356 //    bitmask. It also relies on loading the VGA latches with zeros as a form of "overdraw". Without this
00357 //    version the effect will instead become a glowing ball of flickering yellow/red.
00358 class VGA_ChainedVGA_Slow_Handler : public PageHandler {
00359 public:
00360         VGA_ChainedVGA_Slow_Handler() : PageHandler(PFLAG_NOCODE) {}
00361         static INLINE Bitu readHandler8(PhysPt addr ) {
00362         // planar byte offset = addr & ~3u      (discard low 2 bits)
00363         // planer index = addr & 3u             (use low 2 bits as plane index)
00364         // FIXME: Does chained mode use the lower 2 bits of the CPU address or does it use the read mode select???
00365         return VGA_Generic_Read_Handler(addr&~3u, addr, (Bit8u)(addr&3u));
00366         }
00367         static INLINE void writeHandler8(PhysPt addr, Bitu val) {
00368         // planar byte offset = addr & ~3u      (discard low 2 bits)
00369         // planer index = addr & 3u             (use low 2 bits as plane index)
00370         return VGA_Generic_Write_Handler<true/*chained*/>(addr&~3u, addr, (Bit8u)val);
00371         }
00372         Bit8u readb(PhysPt addr ) {
00373                 VGAMEM_USEC_read_delay();
00374                 addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
00375                 addr += (PhysPt)vga.svga.bank_read_full;
00376 //              addr = CHECKED(addr);
00377                 return (Bit8u)readHandler8( addr );
00378         }
00379         Bit16u readw(PhysPt addr ) {
00380                 VGAMEM_USEC_read_delay();
00381                 addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
00382                 addr += (PhysPt)vga.svga.bank_read_full;
00383 //              addr = CHECKED(addr);
00384                 Bit16u ret = (Bit16u)(readHandler8( addr+0 ) << 0 );
00385                 ret     |= (readHandler8( addr+1 ) << 8 );
00386                 return ret;
00387         }
00388         Bit32u readd(PhysPt addr ) {
00389                 VGAMEM_USEC_read_delay();
00390                 addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
00391                 addr += (PhysPt)vga.svga.bank_read_full;
00392 //              addr = CHECKED(addr);
00393                 Bit32u ret = (Bit32u)(readHandler8( addr+0 ) << 0 );
00394                 ret     |= (readHandler8( addr+1 ) << 8 );
00395                 ret     |= (readHandler8( addr+2 ) << 16 );
00396                 ret     |= (readHandler8( addr+3 ) << 24 );
00397                 return ret;
00398         }
00399         void writeb(PhysPt addr, Bit8u val ) {
00400                 VGAMEM_USEC_write_delay();
00401                 addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
00402                 addr += (PhysPt)vga.svga.bank_write_full;
00403 //              addr = CHECKED(addr);
00404                 writeHandler8( addr, val );
00405         }
00406         void writew(PhysPt addr,Bit16u val) {
00407                 VGAMEM_USEC_write_delay();
00408                 addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
00409                 addr += (PhysPt)vga.svga.bank_write_full;
00410 //              addr = CHECKED(addr);
00411                 writeHandler8( addr+0, (Bit8u)(val >> 0u) );
00412                 writeHandler8( addr+1, (Bit8u)(val >> 8u) );
00413         }
00414         void writed(PhysPt addr,Bit32u val) {
00415                 VGAMEM_USEC_write_delay();
00416                 addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
00417                 addr += (PhysPt)vga.svga.bank_write_full;
00418 //              addr = CHECKED(addr);
00419                 writeHandler8( addr+0, (Bit8u)(val >> 0u) );
00420                 writeHandler8( addr+1, (Bit8u)(val >> 8u) );
00421                 writeHandler8( addr+2, (Bit8u)(val >> 16u) );
00422                 writeHandler8( addr+3, (Bit8u)(val >> 24u) );
00423         }
00424 };
00425 
00426 class VGA_ET4000_ChainedVGA_Slow_Handler : public PageHandler {
00427 public:
00428         VGA_ET4000_ChainedVGA_Slow_Handler() : PageHandler(PFLAG_NOCODE) {}
00429         static INLINE Bitu readHandler8(PhysPt addr ) {
00430         // planar byte offset = addr >> 2       (shift 2 bits to the right)
00431         // planer index = addr & 3u             (use low 2 bits as plane index)
00432         return VGA_Generic_Read_Handler(addr>>2u, addr, (Bit8u)(addr&3u));
00433         }
00434         static INLINE void writeHandler8(PhysPt addr, Bitu val) {
00435         // planar byte offset = addr >> 2       (shift 2 bits to the right)
00436         // planer index = addr & 3u             (use low 2 bits as plane index)
00437         return VGA_Generic_Write_Handler<true/*chained*/>(addr>>2u, addr, (Bit8u)val);
00438         }
00439         Bit8u readb(PhysPt addr ) {
00440                 VGAMEM_USEC_read_delay();
00441                 addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
00442                 addr += (PhysPt)vga.svga.bank_read_full;
00443 //              addr = CHECKED(addr);
00444                 return (Bit8u)readHandler8( addr );
00445         }
00446         Bit16u readw(PhysPt addr ) {
00447                 VGAMEM_USEC_read_delay();
00448                 addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
00449                 addr += (PhysPt)vga.svga.bank_read_full;
00450 //              addr = CHECKED(addr);
00451                 Bit16u ret = (Bit16u)(readHandler8( addr+0 ) << 0 );
00452                 ret     |= (readHandler8( addr+1 ) << 8 );
00453                 return ret;
00454         }
00455         Bit32u readd(PhysPt addr ) {
00456                 VGAMEM_USEC_read_delay();
00457                 addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
00458                 addr += (PhysPt)vga.svga.bank_read_full;
00459 //              addr = CHECKED(addr);
00460                 Bit32u ret = (Bit32u)(readHandler8( addr+0 ) << 0 );
00461                 ret     |= (readHandler8( addr+1 ) << 8 );
00462                 ret     |= (readHandler8( addr+2 ) << 16 );
00463                 ret     |= (readHandler8( addr+3 ) << 24 );
00464                 return ret;
00465         }
00466         void writeb(PhysPt addr, Bit8u val ) {
00467                 VGAMEM_USEC_write_delay();
00468                 addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
00469                 addr += (PhysPt)vga.svga.bank_write_full;
00470 //              addr = CHECKED(addr);
00471                 writeHandler8( addr, val );
00472         }
00473         void writew(PhysPt addr,Bit16u val) {
00474                 VGAMEM_USEC_write_delay();
00475                 addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
00476                 addr += (PhysPt)vga.svga.bank_write_full;
00477 //              addr = CHECKED(addr);
00478                 writeHandler8( addr+0, (Bit8u)(val >> 0u) );
00479                 writeHandler8( addr+1, (Bit8u)(val >> 8u) );
00480         }
00481         void writed(PhysPt addr,Bit32u val) {
00482                 VGAMEM_USEC_write_delay();
00483                 addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
00484                 addr += (PhysPt)vga.svga.bank_write_full;
00485 //              addr = CHECKED(addr);
00486                 writeHandler8( addr+0, (Bit8u)(val >> 0u) );
00487                 writeHandler8( addr+1, (Bit8u)(val >> 8u) );
00488                 writeHandler8( addr+2, (Bit8u)(val >> 16u) );
00489                 writeHandler8( addr+3, (Bit8u)(val >> 24u) );
00490         }
00491 };
00492 
00493 class VGA_UnchainedVGA_Handler : public PageHandler {
00494 public:
00495         Bitu readHandler(PhysPt start) {
00496         return VGA_Generic_Read_Handler(start, start, vga.config.read_map_select);
00497         }
00498 public:
00499         Bit8u readb(PhysPt addr) {
00500                 VGAMEM_USEC_read_delay();
00501                 addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
00502                 addr += (PhysPt)vga.svga.bank_read_full;
00503 //              addr = CHECKED2(addr);
00504                 return (Bit8u)readHandler(addr);
00505         }
00506         Bit16u readw(PhysPt addr) {
00507                 VGAMEM_USEC_read_delay();
00508                 addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
00509                 addr += (PhysPt)vga.svga.bank_read_full;
00510 //              addr = CHECKED2(addr);
00511                 Bit16u ret = (Bit16u)(readHandler(addr+0) << 0);
00512                 ret     |= (readHandler(addr+1) << 8);
00513                 return  ret;
00514         }
00515         Bit32u readd(PhysPt addr) {
00516                 VGAMEM_USEC_read_delay();
00517                 addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
00518                 addr += (PhysPt)vga.svga.bank_read_full;
00519 //              addr = CHECKED2(addr);
00520                 Bit32u ret = (Bit32u)(readHandler(addr+0) << 0);
00521                 ret     |= (readHandler(addr+1) << 8);
00522                 ret     |= (readHandler(addr+2) << 16);
00523                 ret     |= (readHandler(addr+3) << 24);
00524                 return ret;
00525         }
00526 public:
00527         void writeHandler(PhysPt start, Bit8u val) {
00528         VGA_Generic_Write_Handler<false/*chained*/>(start, start, val);
00529         }
00530 public:
00531         VGA_UnchainedVGA_Handler() : PageHandler(PFLAG_NOCODE) {}
00532         void writeb(PhysPt addr,Bit8u val) {
00533                 VGAMEM_USEC_write_delay();
00534                 addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
00535                 addr += (PhysPt)vga.svga.bank_write_full;
00536 //              addr = CHECKED2(addr);
00537                 writeHandler(addr+0,(Bit8u)(val >> 0));
00538         }
00539         void writew(PhysPt addr,Bit16u val) {
00540                 VGAMEM_USEC_write_delay();
00541                 addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
00542                 addr += (PhysPt)vga.svga.bank_write_full;
00543 //              addr = CHECKED2(addr);
00544                 writeHandler(addr+0,(Bit8u)(val >> 0));
00545                 writeHandler(addr+1,(Bit8u)(val >> 8));
00546         }
00547         void writed(PhysPt addr,Bit32u val) {
00548                 VGAMEM_USEC_write_delay();
00549                 addr = PAGING_GetPhysicalAddress(addr) & vgapages.mask;
00550                 addr += (PhysPt)vga.svga.bank_write_full;
00551 //              addr = CHECKED2(addr);
00552                 writeHandler(addr+0,(Bit8u)(val >> 0));
00553                 writeHandler(addr+1,(Bit8u)(val >> 8));
00554                 writeHandler(addr+2,(Bit8u)(val >> 16));
00555                 writeHandler(addr+3,(Bit8u)(val >> 24));
00556         }
00557 };
00558 
00559 #include <stdio.h>
00560 
00561 class VGA_CGATEXT_PageHandler : public PageHandler {
00562 public:
00563         VGA_CGATEXT_PageHandler() {
00564                 flags=PFLAG_NOCODE;
00565         }
00566         Bit8u readb(PhysPt addr) {
00567                 addr = PAGING_GetPhysicalAddress(addr) & 0x3FFF;
00568                 VGAMEM_USEC_read_delay();
00569                 return vga.tandy.mem_base[addr];
00570         }
00571         void writeb(PhysPt addr,Bit8u val){
00572                 VGAMEM_USEC_write_delay();
00573 
00574                 if (enableCGASnow) {
00575                         /* NTS: We can't use PIC_FullIndex() exclusively because it's not precise enough
00576                          *      with respect to when DOSBox CPU emulation is writing. We have to use other
00577                          *      variables like CPU_Cycles to gain additional precision */
00578                         double timeInFrame = PIC_FullIndex()-vga.draw.delay.framestart;
00579                         double timeInLine = fmod(timeInFrame,vga.draw.delay.htotal);
00580 
00581                         /* we're in active area. which column should the snow show up on? */
00582                         Bit32u x = (Bit32u)((timeInLine * 80) / vga.draw.delay.hblkstart);
00583                         if ((unsigned)x < 80) vga.draw.cga_snow[x] = val;
00584                 }
00585 
00586                 addr = PAGING_GetPhysicalAddress(addr) & 0x3FFF;
00587                 vga.tandy.mem_base[addr] = val;
00588         }
00589 };
00590 
00591 class VGA_MCGATEXT_PageHandler : public PageHandler {
00592 public:
00593         VGA_MCGATEXT_PageHandler() {
00594                 flags=PFLAG_NOCODE;
00595         }
00596         Bit8u readb(PhysPt addr) {
00597                 addr = PAGING_GetPhysicalAddress(addr) & 0xFFFF;
00598                 VGAMEM_USEC_read_delay();
00599                 return vga.tandy.mem_base[addr];
00600         }
00601         void writeb(PhysPt addr,Bit8u val){
00602                 VGAMEM_USEC_write_delay();
00603 
00604                 addr = PAGING_GetPhysicalAddress(addr) & 0xFFFF;
00605                 vga.tandy.mem_base[addr] = val;
00606         }
00607 };
00608 
00609 extern uint8_t pc98_egc_srcmask[2]; /* host given (Neko: egc.srcmask) */
00610 extern uint8_t pc98_egc_maskef[2]; /* effective (Neko: egc.mask2) */
00611 extern uint8_t pc98_egc_mask[2]; /* host given (Neko: egc.mask) */
00612 extern uint8_t pc98_egc_access;
00613 extern uint8_t pc98_egc_fgc;
00614 extern uint8_t pc98_egc_foreground_color;
00615 extern uint8_t pc98_egc_background_color;
00616 extern uint8_t pc98_egc_lead_plane;
00617 extern uint8_t pc98_egc_compare_lead;
00618 extern uint8_t pc98_egc_lightsource;
00619 extern uint8_t pc98_egc_shiftinput;
00620 extern uint8_t pc98_egc_regload;
00621 extern uint8_t pc98_egc_rop;
00622 
00623 extern bool pc98_egc_shift_descend;
00624 extern uint8_t pc98_egc_shift_destbit;
00625 extern uint8_t pc98_egc_shift_srcbit;
00626 extern uint16_t pc98_egc_shift_length;
00627 
00628 /* I don't think we necessarily need the full 4096 bit buffer
00629  * Neko Project II uses to render things, though that is
00630  * probably faster to execute. It makes it hard to make sense
00631  * of the code though. */
00632 struct pc98_egc_shifter {
00633     pc98_egc_shifter() : decrement(false), remain(0x10), srcbit(0), dstbit(0) { }
00634 
00635     void reinit(void) { /* from global vars set by guest */
00636         decrement = pc98_egc_shift_descend;
00637         remain = pc98_egc_shift_length + 1; /* the register is length - 1 apparently */
00638         dstbit = pc98_egc_shift_destbit;
00639         srcbit = pc98_egc_shift_srcbit;
00640         bufi = bufo = decrement ? (sizeof(buffer) + 3 - (4*4)) : 0;
00641 
00642         if ((srcbit&7) < (dstbit&7)) {
00643             shft8bitr = (dstbit&7) - (srcbit&7);
00644             shft8bitl = 8 - shft8bitr;
00645         }
00646         else if ((srcbit&7) > (dstbit&7)) {
00647             shft8bitl = (srcbit&7) - (dstbit&7);
00648             shft8bitr = 8 - shft8bitl;
00649         }
00650         else {
00651             shft8bitr = 0;
00652             shft8bitl = 0;
00653         }
00654 
00655         shft8load = 0;
00656         o_srcbit = srcbit & 7;
00657         o_dstbit = dstbit & 7;
00658     }
00659 
00660     bool                decrement;
00661     uint16_t            remain;
00662     uint16_t            srcbit;
00663     uint16_t            dstbit;
00664     uint16_t            o_srcbit = 0;
00665     uint16_t            o_dstbit = 0;
00666 
00667     uint8_t             buffer[512] = {}; /* 4096/8 = 512 */
00668     uint16_t            bufi = 0, bufo = 0;
00669 
00670     uint8_t             shft8load = 0;
00671     uint8_t             shft8bitr = 0;
00672     uint8_t             shft8bitl = 0;
00673 
00674     std::string debug_status(void) {
00675         char tmp[512];
00676 
00677         sprintf(tmp,"decrement=%u remain=%u srcbit=%u dstbit=%u shf8l=%u shf8br=%u shf8bl=%u",
00678             decrement?1:0,
00679             remain,
00680             srcbit,
00681             dstbit,
00682             shft8load,
00683             shft8bitr,
00684             shft8bitl);
00685 
00686         return std::string(tmp);
00687     }
00688 
00689     template <class AWT> inline void bi(const uint16_t ofs,const AWT val) {
00690         size_t ip = (bufi + ofs) & (sizeof(buffer) - 1);
00691 
00692         for (size_t i=0;i < sizeof(AWT);) {
00693             buffer[ip] = (uint8_t)(val >> ((AWT)(i * 8U)));
00694             if ((++ip) == sizeof(buffer)) ip = 0;
00695             i++;
00696         }
00697     }
00698 
00699     template <class AWT> inline void bi_adv(void) {
00700         bufi += pc98_egc_shift_descend ? (sizeof(buffer) - sizeof(AWT)) : sizeof(AWT);
00701         bufi &= (sizeof(buffer) - 1);
00702     }
00703 
00704     template <class AWT> inline AWT bo(const uint16_t ofs) {
00705         size_t op = (bufo + ofs) & (sizeof(buffer) - 1);
00706         AWT ret = 0;
00707 
00708         for (size_t i=0;i < sizeof(AWT);) {
00709             ret += ((AWT)buffer[op]) << ((AWT)(i * 8U));
00710             if ((++op) == sizeof(buffer)) op = 0;
00711             i++;
00712         }
00713 
00714         return ret;
00715     }
00716 
00717     template <class AWT> inline void bo_adv(void) {
00718         bufo += pc98_egc_shift_descend ? (sizeof(buffer) - sizeof(AWT)) : sizeof(AWT);
00719         bufo &= (sizeof(buffer) - 1);
00720     }
00721 
00722     template <class AWT> inline void input(const AWT a,const AWT b,const AWT c,const AWT d,uint8_t odd) {
00723         bi<AWT>((pc98_egc_shift_descend ? (sizeof(buffer) + 1 - sizeof(AWT)) : 0) + 0,a);
00724         bi<AWT>((pc98_egc_shift_descend ? (sizeof(buffer) + 1 - sizeof(AWT)) : 0) + 4,b);
00725         bi<AWT>((pc98_egc_shift_descend ? (sizeof(buffer) + 1 - sizeof(AWT)) : 0) + 8,c);
00726         bi<AWT>((pc98_egc_shift_descend ? (sizeof(buffer) + 1 - sizeof(AWT)) : 0) + 12,d);
00727 
00728         if (shft8load <= 16) {
00729             bi_adv<AWT>();
00730 
00731             if (sizeof(AWT) == 2) {
00732                 if (srcbit >= 8) bo_adv<uint8_t>();
00733                 shft8load += (16 - srcbit);
00734                 srcbit = 0;
00735             }
00736             else {
00737                 if (srcbit >= 8)
00738                     srcbit -= 8;
00739                 else {
00740                     shft8load += (8 - srcbit);
00741                     srcbit = 0;
00742                 }
00743             }
00744         }
00745 
00746         *((AWT*)(pc98_egc_srcmask+odd)) = (AWT)(~0ull);
00747     }
00748 
00749     inline uint8_t dstbit_mask(void) {
00750         uint8_t mb;
00751 
00752         /* assume remain > 0 */
00753         if (remain >= 8)
00754             mb = 0xFF;
00755         else if (!pc98_egc_shift_descend)
00756             mb = 0xFF << (uint8_t)(8 - remain); /* 0x80 0xC0 0xE0 0xF0 ... */
00757         else
00758             mb = 0xFF >> (uint8_t)(8 - remain); /* 0x01 0x03 0x07 0x0F ... */
00759 
00760         /* assume dstbit < 8 */
00761         if (!pc98_egc_shift_descend)
00762             return mb >> (uint8_t)dstbit; /* 0xFF 0x7F 0x3F 0x1F ... */
00763         else
00764             return mb << (uint8_t)dstbit; /* 0xFF 0xFE 0xFC 0xF8 ... */
00765     }
00766 
00767     template <class AWT> inline void output(AWT &a,AWT &b,AWT &c,AWT &d,uint8_t odd,bool recursive=false) {
00768         if (sizeof(AWT) == 2) {
00769             if (shft8load < (16 - dstbit)) {
00770                 *((AWT*)(pc98_egc_srcmask+odd)) = 0;
00771                 return;
00772             }
00773             shft8load -= (16 - dstbit);
00774 
00775             /* assume odd == false and output is to even byte offset */
00776             if (pc98_egc_shift_descend) {
00777                 output<uint8_t>(((uint8_t*)(&a))[1],((uint8_t*)(&b))[1],((uint8_t*)(&c))[1],((uint8_t*)(&d))[1],1,true);
00778                 if (remain != 0) output<uint8_t>(((uint8_t*)(&a))[0],((uint8_t*)(&b))[0],((uint8_t*)(&c))[0],((uint8_t*)(&d))[0],0,true);
00779                 else pc98_egc_srcmask[0] = 0;
00780             }
00781             else {
00782                 output<uint8_t>(((uint8_t*)(&a))[0],((uint8_t*)(&b))[0],((uint8_t*)(&c))[0],((uint8_t*)(&d))[0],0,true);
00783                 if (remain != 0) output<uint8_t>(((uint8_t*)(&a))[1],((uint8_t*)(&b))[1],((uint8_t*)(&c))[1],((uint8_t*)(&d))[1],1,true);
00784                 else pc98_egc_srcmask[1] = 0;
00785             }
00786 
00787             if (remain == 0)
00788                 reinit();
00789 
00790             return;
00791         }
00792 
00793         if (!recursive) {
00794             if (shft8load < (8 - dstbit)) {
00795                 *((AWT*)(pc98_egc_srcmask+odd)) = 0;
00796                 return;
00797             }
00798             shft8load -= (8 - dstbit);
00799         }
00800 
00801         if (dstbit >= 8) {
00802             dstbit -= 8;
00803             *((AWT*)(pc98_egc_srcmask+odd)) = 0;
00804             return;
00805         }
00806 
00807         *((AWT*)(pc98_egc_srcmask+odd)) = dstbit_mask();
00808 
00809         if (dstbit > 0) {
00810             const uint8_t bc = 8 - dstbit;
00811 
00812             if (remain >= bc)
00813                 remain -= bc;
00814             else
00815                 remain = 0;
00816         }
00817         else {
00818             if (remain >= 8)
00819                 remain -= 8;
00820             else
00821                 remain = 0;
00822         }
00823 
00824         if (o_srcbit < o_dstbit) {
00825             if (dstbit != 0) {
00826                 if (pc98_egc_shift_descend) {
00827                     a = bo<AWT>( 0) << shft8bitr;
00828                     b = bo<AWT>( 4) << shft8bitr;
00829                     c = bo<AWT>( 8) << shft8bitr;
00830                     d = bo<AWT>(12) << shft8bitr;
00831                 }
00832                 else {
00833                     a = bo<AWT>( 0) >> shft8bitr;
00834                     b = bo<AWT>( 4) >> shft8bitr;
00835                     c = bo<AWT>( 8) >> shft8bitr;
00836                     d = bo<AWT>(12) >> shft8bitr;
00837                 }
00838 
00839                 dstbit = 0;
00840             }
00841             else {
00842                 if (pc98_egc_shift_descend) {
00843                     bo_adv<AWT>();
00844                     a = (bo<AWT>( 0+1) >> shft8bitl) | (bo<AWT>( 0) << shft8bitr);
00845                     b = (bo<AWT>( 4+1) >> shft8bitl) | (bo<AWT>( 4) << shft8bitr);
00846                     c = (bo<AWT>( 8+1) >> shft8bitl) | (bo<AWT>( 8) << shft8bitr);
00847                     d = (bo<AWT>(12+1) >> shft8bitl) | (bo<AWT>(12) << shft8bitr);
00848                 }
00849                 else {
00850                     a = (bo<AWT>( 0) << shft8bitl) | (bo<AWT>( 0+1) >> shft8bitr);
00851                     b = (bo<AWT>( 4) << shft8bitl) | (bo<AWT>( 4+1) >> shft8bitr);
00852                     c = (bo<AWT>( 8) << shft8bitl) | (bo<AWT>( 8+1) >> shft8bitr);
00853                     d = (bo<AWT>(12) << shft8bitl) | (bo<AWT>(12+1) >> shft8bitr);
00854                     bo_adv<AWT>();
00855                 }
00856             }
00857         }
00858         else if (o_srcbit > o_dstbit) {
00859             dstbit = 0;
00860 
00861             if (pc98_egc_shift_descend) {
00862                 bo_adv<AWT>();
00863                 a = (bo<AWT>( 0+1) >> shft8bitl) | (bo<AWT>( 0) << shft8bitr);
00864                 b = (bo<AWT>( 4+1) >> shft8bitl) | (bo<AWT>( 4) << shft8bitr);
00865                 c = (bo<AWT>( 8+1) >> shft8bitl) | (bo<AWT>( 8) << shft8bitr);
00866                 d = (bo<AWT>(12+1) >> shft8bitl) | (bo<AWT>(12) << shft8bitr);
00867             }
00868             else {
00869                 a = (bo<AWT>( 0) << shft8bitl) | (bo<AWT>( 0+1) >> shft8bitr);
00870                 b = (bo<AWT>( 4) << shft8bitl) | (bo<AWT>( 4+1) >> shft8bitr);
00871                 c = (bo<AWT>( 8) << shft8bitl) | (bo<AWT>( 8+1) >> shft8bitr);
00872                 d = (bo<AWT>(12) << shft8bitl) | (bo<AWT>(12+1) >> shft8bitr);
00873                 bo_adv<AWT>();
00874             }
00875         }
00876         else {
00877             dstbit = 0;
00878 
00879             a = bo<AWT>( 0);
00880             b = bo<AWT>( 4);
00881             c = bo<AWT>( 8);
00882             d = bo<AWT>(12);
00883             bo_adv<AWT>();
00884         }
00885 
00886         if (!recursive && remain == 0)
00887             reinit();
00888     }
00889 };
00890 
00891 egc_quad pc98_egc_src;
00892 egc_quad pc98_egc_bgcm;
00893 egc_quad pc98_egc_fgcm;
00894 egc_quad pc98_egc_data;
00895 egc_quad pc98_egc_last_vram;
00896 
00897 pc98_egc_shifter pc98_egc_shift;
00898 
00899 std::string pc98_egc_shift_debug_status(void) {
00900     return pc98_egc_shift.debug_status();
00901 }
00902 
00903 typedef egc_quad & (*PC98_OPEFN)(uint8_t ope, const PhysPt ad);
00904 
00905 void pc98_egc_shift_reinit() {
00906     pc98_egc_shift.reinit();
00907 }
00908 
00909 template <class AWT> static inline void egc_fetch_planar(egc_quad &dst,const PhysPt vramoff) {
00910     dst[0].w = *((uint16_t*)(pc98_pgraph_current_cpu_page+vramoff+pc98_pgram_bitplane_offset(0)));
00911     dst[1].w = *((uint16_t*)(pc98_pgraph_current_cpu_page+vramoff+pc98_pgram_bitplane_offset(1)));
00912     dst[2].w = *((uint16_t*)(pc98_pgraph_current_cpu_page+vramoff+pc98_pgram_bitplane_offset(2)));
00913     dst[3].w = *((uint16_t*)(pc98_pgraph_current_cpu_page+vramoff+pc98_pgram_bitplane_offset(3)));
00914 }
00915 
00916 static egc_quad &ope_xx(uint8_t ope, const PhysPt ad) {
00917     (void)ad;//UNUSED
00918     LOG_MSG("EGC ROP 0x%2x not impl",ope);
00919     return pc98_egc_last_vram;
00920 }
00921 
00922 static egc_quad &ope_00(uint8_t ope, const PhysPt vramoff) {
00923         (void)vramoff;
00924         (void)ope;
00925 
00926         pc98_egc_data[0].w = 0;
00927         pc98_egc_data[1].w = 0;
00928         pc98_egc_data[2].w = 0;
00929         pc98_egc_data[3].w = 0;
00930 
00931         return pc98_egc_data;
00932 }
00933 
00934 static egc_quad &ope_0f(uint8_t ope, const PhysPt vramoff) {
00935         (void)vramoff;
00936         (void)ope;
00937 
00938         pc98_egc_data[0].w = ~pc98_egc_src[0].w;
00939         pc98_egc_data[1].w = ~pc98_egc_src[1].w;
00940         pc98_egc_data[2].w = ~pc98_egc_src[2].w;
00941         pc98_egc_data[3].w = ~pc98_egc_src[3].w;
00942 
00943         return pc98_egc_data;
00944 }
00945 
00946 static egc_quad &ope_ff(uint8_t ope, const PhysPt vramoff) {
00947         (void)vramoff;
00948         (void)ope;
00949 
00950         pc98_egc_data[0].w = (Bit16u)(~0u);
00951         pc98_egc_data[1].w = (Bit16u)(~0u);
00952         pc98_egc_data[2].w = (Bit16u)(~0u);
00953         pc98_egc_data[3].w = (Bit16u)(~0u);
00954 
00955         return pc98_egc_data;
00956 }
00957 
00958 static egc_quad &ope_np(uint8_t ope, const PhysPt vramoff) {
00959         egc_quad dst;
00960 
00961     egc_fetch_planar<uint16_t>(/*&*/dst,vramoff);
00962 
00963         pc98_egc_data[0].w = 0;
00964         pc98_egc_data[1].w = 0;
00965         pc98_egc_data[2].w = 0;
00966         pc98_egc_data[3].w = 0;
00967 
00968         if (ope & 0x80) {
00969         pc98_egc_data[0].w |= (pc98_egc_src[0].w & dst[0].w);
00970         pc98_egc_data[1].w |= (pc98_egc_src[1].w & dst[1].w);
00971         pc98_egc_data[2].w |= (pc98_egc_src[2].w & dst[2].w);
00972         pc98_egc_data[3].w |= (pc98_egc_src[3].w & dst[3].w);
00973     }
00974         if (ope & 0x20) {
00975         pc98_egc_data[0].w |= (pc98_egc_src[0].w & (~dst[0].w));
00976         pc98_egc_data[1].w |= (pc98_egc_src[1].w & (~dst[1].w));
00977         pc98_egc_data[2].w |= (pc98_egc_src[2].w & (~dst[2].w));
00978         pc98_egc_data[3].w |= (pc98_egc_src[3].w & (~dst[3].w));
00979         }
00980         if (ope & 0x08) {
00981         pc98_egc_data[0].w |= ((~pc98_egc_src[0].w) & dst[0].w);
00982         pc98_egc_data[1].w |= ((~pc98_egc_src[1].w) & dst[1].w);
00983         pc98_egc_data[2].w |= ((~pc98_egc_src[2].w) & dst[2].w);
00984         pc98_egc_data[3].w |= ((~pc98_egc_src[3].w) & dst[3].w);
00985         }
00986         if (ope & 0x02) {
00987         pc98_egc_data[0].w |= ((~pc98_egc_src[0].w) & (~dst[0].w));
00988         pc98_egc_data[1].w |= ((~pc98_egc_src[1].w) & (~dst[1].w));
00989         pc98_egc_data[2].w |= ((~pc98_egc_src[2].w) & (~dst[2].w));
00990         pc98_egc_data[3].w |= ((~pc98_egc_src[3].w) & (~dst[3].w));
00991         }
00992 
00993         (void)ope;
00994         (void)vramoff;
00995         return pc98_egc_data;
00996 }
00997 
00998 static egc_quad &ope_nd(uint8_t ope, const PhysPt vramoff) {
00999         egc_quad pat;
01000 
01001         switch(pc98_egc_fgc) {
01002                 case 1:
01003                         pat[0].w = pc98_egc_bgcm[0].w;
01004                         pat[1].w = pc98_egc_bgcm[1].w;
01005                         pat[2].w = pc98_egc_bgcm[2].w;
01006                         pat[3].w = pc98_egc_bgcm[3].w;
01007                         break;
01008 
01009                 case 2:
01010                         pat[0].w = pc98_egc_fgcm[0].w;
01011                         pat[1].w = pc98_egc_fgcm[1].w;
01012                         pat[2].w = pc98_egc_fgcm[2].w;
01013                         pat[3].w = pc98_egc_fgcm[3].w;
01014                         break;
01015 
01016                 default:
01017                         if (pc98_egc_regload & 1) {
01018                                 pat[0].w = pc98_egc_src[0].w;
01019                                 pat[1].w = pc98_egc_src[1].w;
01020                                 pat[2].w = pc98_egc_src[2].w;
01021                                 pat[3].w = pc98_egc_src[3].w;
01022                         }
01023                         else {
01024                                 pat[0].w = pc98_gdc_tiles[0].w;
01025                                 pat[1].w = pc98_gdc_tiles[1].w;
01026                                 pat[2].w = pc98_gdc_tiles[2].w;
01027                                 pat[3].w = pc98_gdc_tiles[3].w;
01028                         }
01029                         break;
01030         }
01031 
01032         pc98_egc_data[0].w = 0;
01033         pc98_egc_data[1].w = 0;
01034         pc98_egc_data[2].w = 0;
01035         pc98_egc_data[3].w = 0;
01036 
01037         if (ope & 0x80) {
01038         pc98_egc_data[0].w |= (pat[0].w & pc98_egc_src[0].w);
01039         pc98_egc_data[1].w |= (pat[1].w & pc98_egc_src[1].w);
01040         pc98_egc_data[2].w |= (pat[2].w & pc98_egc_src[2].w);
01041         pc98_egc_data[3].w |= (pat[3].w & pc98_egc_src[3].w);
01042     }
01043         if (ope & 0x40) {
01044         pc98_egc_data[0].w |= ((~pat[0].w) & pc98_egc_src[0].w);
01045         pc98_egc_data[1].w |= ((~pat[1].w) & pc98_egc_src[1].w);
01046         pc98_egc_data[2].w |= ((~pat[2].w) & pc98_egc_src[2].w);
01047         pc98_egc_data[3].w |= ((~pat[3].w) & pc98_egc_src[3].w);
01048     }
01049         if (ope & 0x08) {
01050         pc98_egc_data[0].w |= (pat[0].w & (~pc98_egc_src[0].w));
01051         pc98_egc_data[1].w |= (pat[1].w & (~pc98_egc_src[1].w));
01052         pc98_egc_data[2].w |= (pat[2].w & (~pc98_egc_src[2].w));
01053         pc98_egc_data[3].w |= (pat[3].w & (~pc98_egc_src[3].w));
01054     }
01055         if (ope & 0x04) {
01056         pc98_egc_data[0].w |= ((~pat[0].w) & (~pc98_egc_src[0].w));
01057         pc98_egc_data[1].w |= ((~pat[1].w) & (~pc98_egc_src[1].w));
01058         pc98_egc_data[2].w |= ((~pat[2].w) & (~pc98_egc_src[2].w));
01059         pc98_egc_data[3].w |= ((~pat[3].w) & (~pc98_egc_src[3].w));
01060     }
01061 
01062         (void)ope;
01063         (void)vramoff;
01064         return pc98_egc_data;
01065 }
01066 
01067 static egc_quad &ope_c0(uint8_t ope, const PhysPt vramoff) {
01068         egc_quad dst;
01069 
01070     /* assume: ad is word aligned */
01071 
01072     egc_fetch_planar<uint16_t>(/*&*/dst,vramoff);
01073 
01074         pc98_egc_data[0].w = pc98_egc_src[0].w & dst[0].w;
01075         pc98_egc_data[1].w = pc98_egc_src[1].w & dst[1].w;
01076         pc98_egc_data[2].w = pc98_egc_src[2].w & dst[2].w;
01077         pc98_egc_data[3].w = pc98_egc_src[3].w & dst[3].w;
01078 
01079         (void)ope;
01080         (void)vramoff;
01081         return pc98_egc_data;
01082 }
01083 
01084 static egc_quad &ope_f0(uint8_t ope, const PhysPt vramoff) {
01085         (void)ope;
01086         (void)vramoff;
01087         return pc98_egc_src;
01088 }
01089 
01090 static egc_quad &ope_fc(uint8_t ope, const PhysPt vramoff) {
01091         egc_quad dst;
01092 
01093     /* assume: ad is word aligned */
01094 
01095     egc_fetch_planar<uint16_t>(/*&*/dst,vramoff);
01096 
01097         pc98_egc_data[0].w  =    pc98_egc_src[0].w;
01098         pc98_egc_data[0].w |= ((~pc98_egc_src[0].w) & dst[0].w);
01099         pc98_egc_data[1].w  =    pc98_egc_src[1].w;
01100         pc98_egc_data[1].w |= ((~pc98_egc_src[1].w) & dst[1].w);
01101         pc98_egc_data[2].w  =    pc98_egc_src[2].w;
01102         pc98_egc_data[2].w |= ((~pc98_egc_src[2].w) & dst[2].w);
01103         pc98_egc_data[3].w  =    pc98_egc_src[3].w;
01104         pc98_egc_data[3].w |= ((~pc98_egc_src[3].w) & dst[3].w);
01105 
01106         (void)ope;
01107         (void)vramoff;
01108         return pc98_egc_data;
01109 }
01110 
01111 static egc_quad &ope_gg(uint8_t ope, const PhysPt vramoff) {
01112     egc_quad pat,dst;
01113 
01114         switch(pc98_egc_fgc) {
01115                 case 1:
01116                         pat[0].w = pc98_egc_bgcm[0].w;
01117                         pat[1].w = pc98_egc_bgcm[1].w;
01118                         pat[2].w = pc98_egc_bgcm[2].w;
01119                         pat[3].w = pc98_egc_bgcm[3].w;
01120                         break;
01121 
01122                 case 2:
01123                         pat[0].w = pc98_egc_fgcm[0].w;
01124                         pat[1].w = pc98_egc_fgcm[1].w;
01125                         pat[2].w = pc98_egc_fgcm[2].w;
01126                         pat[3].w = pc98_egc_fgcm[3].w;
01127                         break;
01128 
01129                 default:
01130                         if (pc98_egc_regload & 1) {
01131                                 pat[0].w = pc98_egc_src[0].w;
01132                                 pat[1].w = pc98_egc_src[1].w;
01133                                 pat[2].w = pc98_egc_src[2].w;
01134                                 pat[3].w = pc98_egc_src[3].w;
01135                         }
01136                         else {
01137                                 pat[0].w = pc98_gdc_tiles[0].w;
01138                                 pat[1].w = pc98_gdc_tiles[1].w;
01139                                 pat[2].w = pc98_gdc_tiles[2].w;
01140                                 pat[3].w = pc98_gdc_tiles[3].w;
01141                         }
01142                         break;
01143         }
01144 
01145     egc_fetch_planar<uint16_t>(/*&*/dst,vramoff);
01146 
01147         pc98_egc_data[0].w = 0;
01148         pc98_egc_data[1].w = 0;
01149         pc98_egc_data[2].w = 0;
01150         pc98_egc_data[3].w = 0;
01151 
01152         if (ope & 0x80) {
01153                 pc98_egc_data[0].w |=  ( pat[0].w  &   pc98_egc_src[0].w &    dst[0].w);
01154                 pc98_egc_data[1].w |=  ( pat[1].w  &   pc98_egc_src[1].w &    dst[1].w);
01155                 pc98_egc_data[2].w |=  ( pat[2].w  &   pc98_egc_src[2].w &    dst[2].w);
01156                 pc98_egc_data[3].w |=  ( pat[3].w  &   pc98_egc_src[3].w &    dst[3].w);
01157         }
01158         if (ope & 0x40) {
01159                 pc98_egc_data[0].w |= ((~pat[0].w) &   pc98_egc_src[0].w &    dst[0].w);
01160                 pc98_egc_data[1].w |= ((~pat[1].w) &   pc98_egc_src[1].w &    dst[1].w);
01161                 pc98_egc_data[2].w |= ((~pat[2].w) &   pc98_egc_src[2].w &    dst[2].w);
01162                 pc98_egc_data[3].w |= ((~pat[3].w) &   pc98_egc_src[3].w &    dst[3].w);
01163         }
01164         if (ope & 0x20) {
01165                 pc98_egc_data[0].w |= (  pat[0].w  &   pc98_egc_src[0].w &  (~dst[0].w));
01166                 pc98_egc_data[1].w |= (  pat[1].w  &   pc98_egc_src[1].w &  (~dst[1].w));
01167                 pc98_egc_data[2].w |= (  pat[2].w  &   pc98_egc_src[2].w &  (~dst[2].w));
01168                 pc98_egc_data[3].w |= (  pat[3].w  &   pc98_egc_src[3].w &  (~dst[3].w));
01169         }
01170         if (ope & 0x10) {
01171                 pc98_egc_data[0].w |= ((~pat[0].w) &   pc98_egc_src[0].w &  (~dst[0].w));
01172                 pc98_egc_data[1].w |= ((~pat[1].w) &   pc98_egc_src[1].w &  (~dst[1].w));
01173                 pc98_egc_data[2].w |= ((~pat[2].w) &   pc98_egc_src[2].w &  (~dst[2].w));
01174                 pc98_egc_data[3].w |= ((~pat[3].w) &   pc98_egc_src[3].w &  (~dst[3].w));
01175         }
01176         if (ope & 0x08) {
01177                 pc98_egc_data[0].w |= (  pat[0].w  & (~pc98_egc_src[0].w) &   dst[0].w);
01178                 pc98_egc_data[1].w |= (  pat[1].w  & (~pc98_egc_src[1].w) &   dst[1].w);
01179                 pc98_egc_data[2].w |= (  pat[2].w  & (~pc98_egc_src[2].w) &   dst[2].w);
01180                 pc98_egc_data[3].w |= (  pat[3].w  & (~pc98_egc_src[3].w) &   dst[3].w);
01181         }
01182         if (ope & 0x04) {
01183                 pc98_egc_data[0].w |= ((~pat[0].w) & (~pc98_egc_src[0].w) &   dst[0].w);
01184                 pc98_egc_data[1].w |= ((~pat[1].w) & (~pc98_egc_src[1].w) &   dst[1].w);
01185                 pc98_egc_data[2].w |= ((~pat[2].w) & (~pc98_egc_src[2].w) &   dst[2].w);
01186                 pc98_egc_data[3].w |= ((~pat[3].w) & (~pc98_egc_src[3].w) &   dst[3].w);
01187         }
01188         if (ope & 0x02) {
01189                 pc98_egc_data[0].w |= (  pat[0].w  & (~pc98_egc_src[0].w) & (~dst[0].w));
01190                 pc98_egc_data[1].w |= (  pat[1].w  & (~pc98_egc_src[1].w) & (~dst[1].w));
01191                 pc98_egc_data[2].w |= (  pat[2].w  & (~pc98_egc_src[2].w) & (~dst[2].w));
01192                 pc98_egc_data[3].w |= (  pat[3].w  & (~pc98_egc_src[3].w) & (~dst[3].w));
01193         }
01194         if (ope & 0x01) {
01195                 pc98_egc_data[0].w |= ((~pat[0].w) & (~pc98_egc_src[0].w) & (~dst[0].w));
01196                 pc98_egc_data[1].w |= ((~pat[1].w) & (~pc98_egc_src[1].w) & (~dst[1].w));
01197                 pc98_egc_data[2].w |= ((~pat[2].w) & (~pc98_egc_src[2].w) & (~dst[2].w));
01198                 pc98_egc_data[3].w |= ((~pat[3].w) & (~pc98_egc_src[3].w) & (~dst[3].w));
01199         }
01200 
01201         return pc98_egc_data;
01202 }
01203 
01204 static const PC98_OPEFN pc98_egc_opfn[256] = {
01205                         ope_00, ope_xx, ope_xx, ope_np, ope_xx, ope_nd, ope_xx, ope_xx,
01206                         ope_xx, ope_xx, ope_nd, ope_xx, ope_np, ope_xx, ope_xx, ope_0f,
01207                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01208                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01209                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01210                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01211                         ope_np, ope_xx, ope_xx, ope_np, ope_xx, ope_xx, ope_xx, ope_xx,
01212                         ope_xx, ope_xx, ope_xx, ope_xx, ope_np, ope_xx, ope_xx, ope_np,
01213                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01214                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01215                         ope_nd, ope_xx, ope_xx, ope_xx, ope_xx, ope_nd, ope_xx, ope_xx,
01216                         ope_xx, ope_xx, ope_nd, ope_xx, ope_xx, ope_xx, ope_xx, ope_nd,
01217                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_gg, ope_xx,
01218                         ope_xx, ope_xx, ope_xx, ope_xx, ope_gg, ope_xx, ope_xx, ope_xx,
01219                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01220                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01221                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01222                         ope_gg, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01223                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01224                         ope_xx, ope_xx, ope_xx, ope_xx, ope_gg, ope_xx, ope_xx, ope_xx,
01225                         ope_nd, ope_xx, ope_xx, ope_xx, ope_xx, ope_nd, ope_xx, ope_xx,
01226                         ope_xx, ope_xx, ope_nd, ope_xx, ope_gg, ope_xx, ope_xx, ope_nd,
01227                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01228                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01229                         ope_c0, ope_xx, ope_xx, ope_np, ope_xx, ope_xx, ope_xx, ope_xx,
01230                         ope_gg, ope_xx, ope_gg, ope_xx, ope_np, ope_gg, ope_xx, ope_np,
01231                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01232                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01233                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01234                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_gg, ope_xx,
01235                         ope_f0, ope_xx, ope_xx, ope_np, ope_xx, ope_nd, ope_xx, ope_xx,
01236                         ope_xx, ope_xx, ope_nd, ope_xx, ope_fc, ope_xx, ope_xx, ope_ff};
01237 
01238 template <class AWT> static egc_quad &egc_ope(const PhysPt vramoff, const AWT val) {
01239     *((uint16_t*)pc98_egc_maskef) = *((uint16_t*)pc98_egc_mask);
01240 
01241     /* 4A4h
01242      * bits [12:11] = light source
01243      *    11 = invalid
01244      *    10 = write the contents of the palette register
01245      *    01 = write the result of the raster operation
01246      *    00 = write CPU data
01247      *
01248      * 4A2h
01249      * bits [14:13] = foreground, background color
01250      *    11 = invalid
01251      *    10 = foreground color
01252      *    01 = background color
01253      *    00 = pattern register
01254      */
01255     switch (pc98_egc_lightsource) {
01256         case 1: /* 0x0800 */
01257             if (pc98_egc_shiftinput) {
01258                 pc98_egc_shift.input<AWT>(
01259                     val,
01260                     val,
01261                     val,
01262                     val,
01263                     vramoff&1);
01264 
01265                 pc98_egc_shift.output<AWT>(
01266                     *((AWT*)(pc98_egc_src[0].b+(vramoff&1))),
01267                     *((AWT*)(pc98_egc_src[1].b+(vramoff&1))),
01268                     *((AWT*)(pc98_egc_src[2].b+(vramoff&1))),
01269                     *((AWT*)(pc98_egc_src[3].b+(vramoff&1))),
01270                     vramoff&1);
01271             }
01272 
01273             *((uint16_t*)pc98_egc_maskef) &= *((uint16_t*)pc98_egc_srcmask);
01274             return pc98_egc_opfn[pc98_egc_rop](pc98_egc_rop, vramoff & (~1U));
01275         case 2: /* 0x1000 */
01276             if (pc98_egc_fgc == 1)
01277                 return pc98_egc_bgcm;
01278             else if (pc98_egc_fgc == 2)
01279                 return pc98_egc_fgcm;
01280 
01281             if (pc98_egc_shiftinput) {
01282                 pc98_egc_shift.input<AWT>(
01283                     val,
01284                     val,
01285                     val,
01286                     val,
01287                     vramoff&1);
01288 
01289                 pc98_egc_shift.output<AWT>(
01290                     *((AWT*)(pc98_egc_src[0].b+(vramoff&1))),
01291                     *((AWT*)(pc98_egc_src[1].b+(vramoff&1))),
01292                     *((AWT*)(pc98_egc_src[2].b+(vramoff&1))),
01293                     *((AWT*)(pc98_egc_src[3].b+(vramoff&1))),
01294                     vramoff&1);
01295             }
01296  
01297             *((uint16_t*)pc98_egc_maskef) &= *((uint16_t*)pc98_egc_srcmask);
01298             return pc98_egc_src;
01299         default: {
01300             uint16_t tmp = (uint16_t)val;
01301 
01302             if (sizeof(AWT) < 2) {
01303                 tmp &= 0xFFU;
01304                 tmp |= tmp << 8U;
01305             }
01306 
01307             pc98_egc_data[0].w = tmp;
01308             pc98_egc_data[1].w = tmp;
01309             pc98_egc_data[2].w = tmp;
01310             pc98_egc_data[3].w = tmp;
01311             } break;
01312     }
01313 
01314     return pc98_egc_data;
01315 }
01316 
01317 unsigned char pc98_mem_msw_m[8] = {0};
01318 
01319 void pc98_msw3_set_ramsize(const unsigned char b) {
01320     pc98_mem_msw_m[2/*MSW3*/] = b;
01321 }
01322 
01323 unsigned char pc98_mem_msw(unsigned char which) {
01324     return pc98_mem_msw_m[which&7];
01325 }
01326 
01327 void pc98_mem_msw_write(unsigned char which,unsigned char val) {
01328     LOG_MSG("WARNING: PC-98 NVRAM write to 0x%x value 0x%x, not implemented yet",which,val);
01329     // TODO: Add code to write NVRAM.
01330     //       According to documentation writing is only enabled if a register is written elsewhere to allow it.
01331 }
01332 
01333 /* The NEC display is documented to have:
01334  *
01335  * A0000-A3FFF      T-RAM (text) (8KB WORDs)
01336  *   A0000-A1FFF      Characters (4KB WORDs)
01337  *   A2000-A3FFF      Attributes (4KB WORDs). For each 16-bit WORD only the lower 8 bits are read/writeable.
01338  *   A4000-A5FFF      Unknown ?? (4KB WORDs)
01339  *   A6000-A7FFF      Not present (4KB WORDs)
01340  * A8000-BFFFF      G-RAM (graphics) (96KB)
01341  *
01342  * T-RAM character display RAM is 16-bits per character.
01343  * ASCII text has upper 8 bits zero.
01344  * SHIFT-JIS doublewide characters use the upper byte for non-ASCII. */
01345 
01346 /* A0000-A3FFF text character + attribute RAM */
01347 /* 
01348  * 0xA3FE2      MSW1
01349  * 0xA3FE6      MSW2
01350  * 0xA3FEA      MSW3
01351  * 0xA3FEE      MSW4
01352  * 0xA3FF2      MSW5
01353  * 0xA3FF6      MSW6
01354  * 0xA3FFA      MSW7
01355  * 0xA3FFE      MSW8
01356  *
01357  * TODO: Study real hardware to determine what the bytes between the NVRAM bytes are.
01358  *       Are they repeats of the MSW bytes, some other value, or just 0xFF?
01359  */
01360 class VGA_PC98_TEXT_PageHandler : public PageHandler {
01361 public:
01362         VGA_PC98_TEXT_PageHandler() : PageHandler(PFLAG_NOCODE) {}
01363         Bit8u readb(PhysPt addr) {
01364         addr &= 0x3FFFu;
01365 
01366         if (addr >= 0x3FE0u)
01367             return pc98_mem_msw((addr >> 2u) & 7u);
01368         else if ((addr & 0x2001u) == 0x2001u)
01369             return (Bit8u)(~0u); /* Odd bytes of attribute RAM do not exist, apparently */
01370 
01371         return VRAM98_TEXT[addr];
01372     }
01373         void writeb(PhysPt addr,Bit8u val) {
01374         addr &= 0x3FFFu;
01375 
01376         if (addr >= 0x3FE0u)
01377             return pc98_mem_msw_write((addr >> 2u) & 7u,(unsigned char)val);
01378         else if ((addr & 0x2001u) == 0x2001u)
01379             return;             /* Odd bytes of attribute RAM do not exist, apparently */
01380 
01381         VRAM98_TEXT[addr] = (unsigned char)val;
01382     }
01383 };
01384 
01385 extern uint16_t a1_font_load_addr;
01386 
01387 /* A4000-A4FFF character generator memory-mapped I/O */
01388 /* 0xA4000-0xA4FFF is word-sized access to the character generator.
01389  *
01390  * Some games, though not many, appear to prefer this memory-mapped I/O
01391  * rather than the I/O ports.
01392  *
01393  * This fixes:
01394  *   - Eve Burst Error
01395  *
01396  * Also noted: Disassembling the CG functions of the BIOS on an actual
01397  *             PC9821 laptop reveals that the BIOS also uses this method,
01398  *             using REP MOVSW
01399  *
01400  * Also noted: On real hardware, A4000-A4FFF seems to latch to the CG.
01401  *             A5000-A5FFF seems to latch to nothing. */
01402 
01403 /* according to real hardware, memory address does not affect char offset (port 0xA5) */
01404 class VGA_PC98_CG_PageHandler : public PageHandler {
01405 public:
01406         VGA_PC98_CG_PageHandler() : PageHandler(PFLAG_NOCODE) {}
01407         Bit8u readb(PhysPt addr) {
01408         return pc98_font_char_read(a1_font_load_addr,(addr >> 1) & 0xF,addr & 1);
01409     }
01410         void writeb(PhysPt addr,Bit8u val) {
01411         pc98_font_char_write(a1_font_load_addr,(addr >> 1) & 0xF,addr & 1,val);
01412     }
01413 };
01414 
01415 /* 256-color control registers, memory mapped I/O */
01416 class VGA_PC98_256MMIO_PageHandler : public PageHandler {
01417 public:
01418         VGA_PC98_256MMIO_PageHandler() : PageHandler(PFLAG_NOCODE) {}
01419         Bit8u readb(PhysPt addr) {
01420         return pc98_pegc_mmio_read(addr & 0x7FFFu);
01421     }
01422     void writeb(PhysPt addr,Bit8u val) {
01423         pc98_pegc_mmio_write(addr & 0x7FFFu,val);
01424     }
01425 };
01426 
01427 // A8000h-B7FFFh is 256-color planar (????)
01428 // I don't THINK the bank switching registers have any effect. Not sure.
01429 // However it makes sense to make it a 64KB region because 8 planes x 64KB = 512KB of RAM. Right?
01430 // By the way real PEGC hardware seems to prefer WORD (16-bit) sized read/write aligned on WORD boundaries.
01431 // In fact Windows 3.1's 256-color driver never uses byte-sized read/write in this planar mode.
01432 class VGA_PC98_256Planar_PageHandler : public PageHandler {
01433 public:
01434         VGA_PC98_256Planar_PageHandler() : PageHandler(PFLAG_NOCODE) {}
01435         Bit8u readb(PhysPt addr) {
01436         (void)addr;
01437 
01438 //        LOG_MSG("PEGC 256-color planar warning: Readb from %lxh",(unsigned long)addr);
01439         return (Bit8u)(~0);
01440     }
01441         void writeb(PhysPt addr,Bit8u val) {
01442         (void)addr;
01443         (void)val;
01444 
01445 //        LOG_MSG("PEGC 256-color planar warning: Writeb to %lxh val %02xh",(unsigned long)addr,(unsigned int)val);
01446     }
01447         Bit16u readw(PhysPt addr) {
01448         (void)addr;
01449 
01450 //        LOG_MSG("PEGC 256-color planar warning: Readw from %lxh",(unsigned long)addr);
01451         return (Bit16u)(~0);
01452     }
01453         void writew(PhysPt addr,Bit16u val) {
01454         (void)addr;
01455         (void)val;
01456 
01457 //        LOG_MSG("PEGC 256-color planar warning: Writew to %lxh val %04xh",(unsigned long)addr,(unsigned int)val);
01458     }
01459 };
01460 
01461 // A8000h is bank 0
01462 // B0000h is bank 1
01463 template <const unsigned int bank> class VGA_PC98_256BANK_PageHandler : public PageHandler {
01464 public:
01465         VGA_PC98_256BANK_PageHandler() : PageHandler(PFLAG_NOCODE) {}
01466         Bit8u readb(PhysPt addr) {
01467         return pc98_vram_256bank_from_window(bank)[addr & 0x7FFFu];
01468     }
01469         void writeb(PhysPt addr,Bit8u val) {
01470         pc98_vram_256bank_from_window(bank)[addr & 0x7FFFu] = val;
01471     }
01472 };
01473 
01474 namespace pc98pgmio {
01475 
01476     template <class AWT> static inline void check_align(const PhysPt addr) {
01477 #if 0
01478         /* DEBUG: address must be aligned to datatype.
01479          *        Code that calls us must enforce that or subdivide
01480          *        to a small datatype that can follow this rule. */
01481         PhysPt chk = (1UL << (sizeof(AWT) - 1)) - 1;
01482         /* uint8_t:  chk = 0
01483          * uint16_t: chk = 1
01484          * TODO: Do you suppose later generation PC-9821's supported DWORD size bitplane transfers?
01485          *       Or did NEC just give up on anything past 16-bit and focus on the SVGA side of things? */
01486         assert((addr&chk) == 0);
01487 #else
01488         (void)addr;
01489 #endif
01490     }
01491 
01492 }
01493 
01494 class VGA_PC98_PageHandler : public PageHandler {
01495 public:
01496         VGA_PC98_PageHandler() : PageHandler(PFLAG_NOCODE) {}
01497 
01498     template <class AWT> static inline AWT mode8_r(const unsigned int plane,const PhysPt vramoff) {
01499         AWT r,b;
01500 
01501         b = *((AWT*)(pc98_pgraph_current_cpu_page + vramoff));
01502         r = b ^ *((AWT*)pc98_gdc_tiles[plane].b);
01503 
01504         return r;
01505     }
01506 
01507     template <class AWT> static inline void mode8_w(const unsigned int plane,const PhysPt vramoff) {
01508         AWT tb;
01509 
01510         /* Neko Project II code suggests that the first byte is repeated. */
01511         if (sizeof(AWT) > 1)
01512             tb = pc98_gdc_tiles[plane].b[0] | (pc98_gdc_tiles[plane].b[0] << 8u);
01513         else
01514             tb = pc98_gdc_tiles[plane].b[0];
01515 
01516         *((AWT*)(pc98_pgraph_current_cpu_page + vramoff)) = tb;
01517     }
01518 
01519     template <class AWT> static inline void modeC_w(const unsigned int plane,const PhysPt vramoff,const AWT mask,const AWT val) {
01520         AWT t,tb;
01521 
01522         /* Neko Project II code suggests that the first byte is repeated. */
01523         if (sizeof(AWT) > 1)
01524             tb = pc98_gdc_tiles[plane].b[0] | (pc98_gdc_tiles[plane].b[0] << 8u);
01525         else
01526             tb = pc98_gdc_tiles[plane].b[0];
01527 
01528         t  = *((AWT*)(pc98_pgraph_current_cpu_page + vramoff)) & mask;
01529         t |= val & tb;
01530         *((AWT*)(pc98_pgraph_current_cpu_page + vramoff)) = t;
01531     }
01532 
01533     template <class AWT> static inline AWT modeEGC_r(const PhysPt vramoff,const PhysPt fulloff) {
01534         /* assume: vramoff is even IF AWT is 16-bit wide */
01535         *((AWT*)(pc98_egc_last_vram[0].b+(vramoff&1))) = *((AWT*)(pc98_pgraph_current_cpu_page+vramoff+pc98_pgram_bitplane_offset(0)));
01536         *((AWT*)(pc98_egc_last_vram[1].b+(vramoff&1))) = *((AWT*)(pc98_pgraph_current_cpu_page+vramoff+pc98_pgram_bitplane_offset(1)));
01537         *((AWT*)(pc98_egc_last_vram[2].b+(vramoff&1))) = *((AWT*)(pc98_pgraph_current_cpu_page+vramoff+pc98_pgram_bitplane_offset(2)));
01538         *((AWT*)(pc98_egc_last_vram[3].b+(vramoff&1))) = *((AWT*)(pc98_pgraph_current_cpu_page+vramoff+pc98_pgram_bitplane_offset(3)));
01539 
01540         /* bits [10:10] = read source
01541          *    1 = shifter input is CPU write data
01542          *    0 = shifter input is VRAM data */
01543         /* Neko Project II: if ((egc.ope & 0x0400) == 0) ... */
01544         if (!pc98_egc_shiftinput) {
01545             pc98_egc_shift.input<AWT>(
01546                 *((AWT*)(pc98_egc_last_vram[0].b+(vramoff&1))),
01547                 *((AWT*)(pc98_egc_last_vram[1].b+(vramoff&1))),
01548                 *((AWT*)(pc98_egc_last_vram[2].b+(vramoff&1))),
01549                 *((AWT*)(pc98_egc_last_vram[3].b+(vramoff&1))),
01550                 vramoff&1);
01551 
01552             pc98_egc_shift.output<AWT>(
01553                 *((AWT*)(pc98_egc_src[0].b+(vramoff&1))),
01554                 *((AWT*)(pc98_egc_src[1].b+(vramoff&1))),
01555                 *((AWT*)(pc98_egc_src[2].b+(vramoff&1))),
01556                 *((AWT*)(pc98_egc_src[3].b+(vramoff&1))),
01557                 vramoff&1);
01558         }
01559 
01560         /* 0x4A4:
01561          * ...
01562          * bits [9:8] = register load (pc98_egc_regload[1:0])
01563          *    11 = invalid
01564          *    10 = load VRAM data before writing on VRAM write
01565          *    01 = load VRAM data into pattern/tile register on VRAM read
01566          *    00 = Do not change pattern/tile register
01567          * ...
01568          *
01569          * pc98_egc_regload = (val >> 8) & 3;
01570          */
01571         /* Neko Project II: if ((egc.ope & 0x0300) == 0x0100) ... */
01572         if (pc98_egc_regload & 1) { /* load VRAM data into pattern/tile... (or INVALID) */
01573             *((AWT*)(pc98_gdc_tiles[0].b+(vramoff&1))) = *((AWT*)(pc98_egc_last_vram[0].b+(vramoff&1)));
01574             *((AWT*)(pc98_gdc_tiles[1].b+(vramoff&1))) = *((AWT*)(pc98_egc_last_vram[1].b+(vramoff&1)));
01575             *((AWT*)(pc98_gdc_tiles[2].b+(vramoff&1))) = *((AWT*)(pc98_egc_last_vram[2].b+(vramoff&1)));
01576             *((AWT*)(pc98_gdc_tiles[3].b+(vramoff&1))) = *((AWT*)(pc98_egc_last_vram[3].b+(vramoff&1)));
01577         }
01578 
01579         /* 0x4A4:
01580          * bits [13:13] = 0=compare lead plane  1=don't
01581          *
01582          * bits [10:10] = read source
01583          *    1 = shifter input is CPU write data
01584          *    0 = shifter input is VRAM data */
01585         if (pc98_egc_compare_lead) {
01586             if (!pc98_egc_shiftinput)
01587                 return *((AWT*)(pc98_egc_src[pc98_egc_lead_plane&3].b));
01588             else
01589                 return *((AWT*)(pc98_pgraph_current_cpu_page+vramoff+((pc98_egc_lead_plane&3)*PC98_VRAM_BITPLANE_SIZE)));
01590         }
01591 
01592         return *((AWT*)(pc98_pgraph_current_cpu_page+fulloff));
01593     }
01594 
01595     template <class AWT> static inline void modeEGC_w(const PhysPt vramoff,const AWT val) {
01596         /* assume: vramoff is even IF AWT is 16-bit wide */
01597 
01598         /* 0x4A4:
01599          * ...
01600          * bits [9:8] = register load (pc98_egc_regload[1:0])
01601          *    11 = invalid
01602          *    10 = load VRAM data before writing on VRAM write
01603          *    01 = load VRAM data into pattern/tile register on VRAM read
01604          *    00 = Do not change pattern/tile register
01605          * ...
01606          * pc98_egc_regload = (val >> 8) & 3;
01607          */
01608         /* Neko Project II: if ((egc.ope & 0x0300) == 0x0200) ... */
01609         if (pc98_egc_regload & 2) { /* load VRAM data before writing on VRAM write (or INVALID) */
01610             *((AWT*)(pc98_gdc_tiles[0].b+(vramoff&1))) = *((AWT*)(pc98_pgraph_current_cpu_page+vramoff+pc98_pgram_bitplane_offset(0)));
01611             *((AWT*)(pc98_gdc_tiles[1].b+(vramoff&1))) = *((AWT*)(pc98_pgraph_current_cpu_page+vramoff+pc98_pgram_bitplane_offset(1)));
01612             *((AWT*)(pc98_gdc_tiles[2].b+(vramoff&1))) = *((AWT*)(pc98_pgraph_current_cpu_page+vramoff+pc98_pgram_bitplane_offset(2)));
01613             *((AWT*)(pc98_gdc_tiles[3].b+(vramoff&1))) = *((AWT*)(pc98_pgraph_current_cpu_page+vramoff+pc98_pgram_bitplane_offset(3)));
01614         }
01615 
01616         egc_quad &ropdata = egc_ope<AWT>(vramoff, val);
01617 
01618         const AWT accmask = *((AWT*)(pc98_egc_maskef+(vramoff&1)));
01619 
01620         if (accmask != 0) {
01621             if (!(pc98_egc_access & 1)) {
01622                 *((AWT*)(pc98_pgraph_current_cpu_page+vramoff+pc98_pgram_bitplane_offset(0))) &= ~accmask;
01623                 *((AWT*)(pc98_pgraph_current_cpu_page+vramoff+pc98_pgram_bitplane_offset(0))) |=  accmask & *((AWT*)(ropdata[0].b+(vramoff&1)));
01624             }
01625             if (!(pc98_egc_access & 2)) {
01626                 *((AWT*)(pc98_pgraph_current_cpu_page+vramoff+pc98_pgram_bitplane_offset(1))) &= ~accmask;
01627                 *((AWT*)(pc98_pgraph_current_cpu_page+vramoff+pc98_pgram_bitplane_offset(1))) |=  accmask & *((AWT*)(ropdata[1].b+(vramoff&1)));
01628             }
01629             if (!(pc98_egc_access & 4)) {
01630                 *((AWT*)(pc98_pgraph_current_cpu_page+vramoff+pc98_pgram_bitplane_offset(2))) &= ~accmask;
01631                 *((AWT*)(pc98_pgraph_current_cpu_page+vramoff+pc98_pgram_bitplane_offset(2))) |=  accmask & *((AWT*)(ropdata[2].b+(vramoff&1)));
01632             }
01633             if (!(pc98_egc_access & 8)) {
01634                 *((AWT*)(pc98_pgraph_current_cpu_page+vramoff+pc98_pgram_bitplane_offset(3))) &= ~accmask;
01635                 *((AWT*)(pc98_pgraph_current_cpu_page+vramoff+pc98_pgram_bitplane_offset(3))) |=  accmask & *((AWT*)(ropdata[3].b+(vramoff&1)));
01636             }
01637         }
01638     }
01639 
01640     template <class AWT> AWT readc(PhysPt addr) {
01641         pc98pgmio::check_align<AWT>(addr);
01642 
01643         unsigned int plane = ((addr >> 15u) + 3u) & 3u;
01644         addr &= 0x7FFF;
01645 
01646         /* reminder:
01647          *
01648          * bit 1: VOPBIT_EGC
01649          * bit 0: VOPBIT_ACCESS
01650          * From GRGC bits:
01651          * bit 3: VOPBIT_GRCG  1=GRGC active  0=GRGC invalid  (from bit 7)
01652          * bit 2: VOPBIT_GRCG  1=Read/Modify/Write when writing  0=TCR mode at read, TDW mode at write  (from bit 6) */
01653         switch (pc98_gdc_vramop & 0xF) {
01654             case 0x00:
01655             case 0x01:
01656             case 0x02:
01657             case 0x03:
01658             case 0x04:
01659             case 0x05:
01660             case 0x06:
01661             case 0x07:
01662             case 0x0C:
01663             case 0x0D:
01664                 return *((AWT*)(pc98_pgraph_current_cpu_page+addr+pc98_pgram_bitplane_offset(plane)));
01665             case 0x08: /* TCR/TDW */
01666             case 0x09:
01667                 {
01668                     AWT r = 0;
01669 
01670                     /* this reads multiple bitplanes at once */
01671                     if (!(pc98_gdc_modereg & 1)) // blue channel
01672                         r |= mode8_r<AWT>(/*plane*/0,addr + pc98_pgram_bitplane_offset(0));
01673 
01674                     if (!(pc98_gdc_modereg & 2)) // red channel
01675                         r |= mode8_r<AWT>(/*plane*/1,addr + pc98_pgram_bitplane_offset(1));
01676 
01677                     if (!(pc98_gdc_modereg & 4)) // green channel
01678                         r |= mode8_r<AWT>(/*plane*/2,addr + pc98_pgram_bitplane_offset(2));
01679 
01680                     if (!(pc98_gdc_modereg & 8)) // extended channel
01681                         r |= mode8_r<AWT>(/*plane*/3,addr + pc98_pgram_bitplane_offset(3));
01682 
01683                     /* NTS: Apparently returning this value correctly really matters to the
01684                      *      sprite engine in "Edge", else visual errors occur. */
01685                     return ~r;
01686                 }
01687             case 0x0A: /* EGC read */
01688             case 0x0B:
01689             case 0x0E:
01690             case 0x0F:
01691                 /* this reads multiple bitplanes at once */
01692                 return modeEGC_r<AWT>(addr,addr);
01693             default: /* should not happen */
01694                 break;
01695         }
01696 
01697                 return (AWT)(~0ull);
01698         }
01699 
01700         template <class AWT> void writec(PhysPt addr,AWT val) {
01701         pc98pgmio::check_align<AWT>(addr);
01702 
01703         unsigned int plane = ((addr >> 15u) + 3u) & 3u;
01704         addr &= 0x7FFF;
01705 
01706         /* reminder:
01707          *
01708          * bit 1: VOPBIT_EGC
01709          * bit 0: VOPBIT_ACCESS
01710          * From GRGC bits:
01711          * bit 3: VOPBIT_GRCG  1=GRGC active  0=GRGC invalid  (from bit 7)
01712          * bit 2: VOPBIT_GRCG  1=Read/Modify/Write when writing  0=TCR mode at read, TDW mode at write  (from bit 6) */
01713         switch (pc98_gdc_vramop & 0xF) {
01714             case 0x00:
01715             case 0x01:
01716             case 0x02:
01717             case 0x03:
01718             case 0x04:
01719             case 0x05:
01720             case 0x06:
01721             case 0x07:
01722                 *((AWT*)(pc98_pgraph_current_cpu_page+addr+pc98_pgram_bitplane_offset(plane))) = val;
01723                 break;
01724             case 0x08:  /* TCR/TDW write tile data, no masking */
01725             case 0x09:
01726                 {
01727                     /* this writes to multiple bitplanes at once.
01728                      * notice that the value written has no meaning, only the tile data and memory address. */
01729                     if (!(pc98_gdc_modereg & 1)) // blue channel
01730                         mode8_w<AWT>(0/*plane*/,addr + pc98_pgram_bitplane_offset(0));
01731 
01732                     if (!(pc98_gdc_modereg & 2)) // red channel
01733                         mode8_w<AWT>(1/*plane*/,addr + pc98_pgram_bitplane_offset(1));
01734 
01735                     if (!(pc98_gdc_modereg & 4)) // green channel
01736                         mode8_w<AWT>(2/*plane*/,addr + pc98_pgram_bitplane_offset(2));
01737 
01738                     if (!(pc98_gdc_modereg & 8)) // extended channel
01739                         mode8_w<AWT>(3/*plane*/,addr + pc98_pgram_bitplane_offset(3));
01740                 }
01741                 break;
01742             case 0x0C:  /* read/modify/write from tile with masking */
01743             case 0x0D:  /* a lot of PC-98 games seem to rely on this for sprite rendering */
01744                 {
01745                     const AWT mask = ~val;
01746 
01747                     /* this writes to multiple bitplanes at once */
01748                     if (!(pc98_gdc_modereg & 1)) // blue channel
01749                         modeC_w<AWT>(0/*plane*/,addr + pc98_pgram_bitplane_offset(0),mask,val);
01750 
01751                     if (!(pc98_gdc_modereg & 2)) // red channel
01752                         modeC_w<AWT>(1/*plane*/,addr + pc98_pgram_bitplane_offset(1),mask,val);
01753 
01754                     if (!(pc98_gdc_modereg & 4)) // green channel
01755                         modeC_w<AWT>(2/*plane*/,addr + pc98_pgram_bitplane_offset(2),mask,val);
01756 
01757                     if (!(pc98_gdc_modereg & 8)) // extended channel
01758                         modeC_w<AWT>(3/*plane*/,addr + pc98_pgram_bitplane_offset(3),mask,val);
01759                 }
01760                 break;
01761             case 0x0A: /* EGC write */
01762             case 0x0B:
01763             case 0x0E:
01764             case 0x0F:
01765                 /* this reads multiple bitplanes at once */
01766                 modeEGC_w<AWT>(addr,val);
01767                 break;
01768             default: /* Should not happen */
01769                 break;
01770         }
01771         }
01772 
01773     /* byte-wise */
01774         Bit8u readb(PhysPt addr) {
01775         return readc<uint8_t>( PAGING_GetPhysicalAddress(addr) );
01776     }
01777         void writeb(PhysPt addr,Bit8u val) {
01778         writec<uint8_t>( PAGING_GetPhysicalAddress(addr), val );
01779     }
01780 
01781     /* word-wise.
01782      * in the style of the 8086, non-word-aligned I/O is split into byte I/O */
01783         Bit16u readw(PhysPt addr) {
01784         addr = PAGING_GetPhysicalAddress(addr);
01785         if (!(addr & 1)) /* if WORD aligned */
01786             return readc<uint16_t>(addr);
01787         else {
01788             return   (unsigned int)readc<uint8_t>(addr+0U) +
01789                     ((unsigned int)readc<uint8_t>(addr+1U) << 8u);
01790         }
01791     }
01792         void writew(PhysPt addr,Bit16u val) {
01793         addr = PAGING_GetPhysicalAddress(addr);
01794         if (!(addr & 1)) /* if WORD aligned */
01795             writec<uint16_t>(addr,val);
01796         else {
01797             writec<uint8_t>(addr+0,(uint8_t)val);
01798             writec<uint8_t>(addr+1,(uint8_t)(val >> 8U));
01799         }
01800     }
01801 };
01802 
01803 class VGA_PC98_LFB_Handler : public PageHandler {
01804 public:
01805         VGA_PC98_LFB_Handler() : PageHandler(PFLAG_READABLE|PFLAG_WRITEABLE|PFLAG_NOCODE) {}
01806         HostPt GetHostReadPt(Bitu phys_page) {
01807                 return &vga.mem.linear[(phys_page&0x7F)*4096 + PC98_VRAM_GRAPHICS_OFFSET]; /* 512KB mapping */
01808         }
01809         HostPt GetHostWritePt(Bitu phys_page) {
01810                 return &vga.mem.linear[(phys_page&0x7F)*4096 + PC98_VRAM_GRAPHICS_OFFSET]; /* 512KB mapping */
01811         }
01812 };
01813 
01814 class VGA_Map_Handler : public PageHandler {
01815 public:
01816         VGA_Map_Handler() : PageHandler(PFLAG_READABLE|PFLAG_WRITEABLE|PFLAG_NOCODE) {}
01817         HostPt GetHostReadPt(Bitu phys_page) {
01818                 phys_page-=vgapages.base;
01819                 return &vga.mem.linear[CHECKED3(vga.svga.bank_read_full+phys_page*4096)];
01820         }
01821         HostPt GetHostWritePt(Bitu phys_page) {
01822                 phys_page-=vgapages.base;
01823                 return &vga.mem.linear[CHECKED3(vga.svga.bank_write_full+phys_page*4096)];
01824         }
01825 };
01826 
01827 class VGA_Slow_CGA_Handler : public PageHandler {
01828 public:
01829         VGA_Slow_CGA_Handler() : PageHandler(PFLAG_NOCODE) {}
01830         void delay() {
01831                 Bits delaycyc = (Bits)(CPU_CycleMax/((cpu_cycles_count_t)(1024/2.80)));
01832                 if(GCC_UNLIKELY(CPU_Cycles < 3*delaycyc)) delaycyc=0;
01833                 CPU_Cycles -= delaycyc;
01834                 CPU_IODelayRemoved += delaycyc;
01835         }
01836 
01837         Bit8u readb(PhysPt addr) {
01838                 delay();
01839                 return vga.tandy.mem_base[(addr - 0xb8000) & 0x3FFF];
01840         }
01841         void writeb(PhysPt addr,Bit8u val){
01842                 delay();
01843                 vga.tandy.mem_base[(addr - 0xb8000) & 0x3FFF] = val;
01844         }
01845         
01846 };
01847 
01848 class VGA_LFB_Handler : public PageHandler {
01849 public:
01850         VGA_LFB_Handler() : PageHandler(PFLAG_READABLE|PFLAG_WRITEABLE|PFLAG_NOCODE) {}
01851         HostPt GetHostReadPt( Bitu phys_page ) {
01852                 phys_page -= vga.lfb.page;
01853                 phys_page &= (vga.mem.memsize >> 12) - 1;
01854                 return &vga.mem.linear[CHECKED3(phys_page * 4096)];
01855         }
01856         HostPt GetHostWritePt( Bitu phys_page ) {
01857                 return GetHostReadPt( phys_page );
01858         }
01859 };
01860 
01861 extern void XGA_Write(Bitu port, Bitu val, Bitu len);
01862 extern Bitu XGA_Read(Bitu port, Bitu len);
01863 
01864 class VGA_MMIO_Handler : public PageHandler {
01865 public:
01866         VGA_MMIO_Handler() : PageHandler(PFLAG_NOCODE) {}
01867         void writeb(PhysPt addr,Bit8u val) {
01868                 VGAMEM_USEC_write_delay();
01869                 Bitu port = PAGING_GetPhysicalAddress(addr) & 0xffff;
01870                 XGA_Write(port, val, 1);
01871         }
01872         void writew(PhysPt addr,Bit16u val) {
01873                 VGAMEM_USEC_write_delay();
01874                 Bitu port = PAGING_GetPhysicalAddress(addr) & 0xffff;
01875                 XGA_Write(port, val, 2);
01876         }
01877         void writed(PhysPt addr,Bit32u val) {
01878                 VGAMEM_USEC_write_delay();
01879                 Bitu port = PAGING_GetPhysicalAddress(addr) & 0xffff;
01880                 XGA_Write(port, val, 4);
01881         }
01882 
01883         Bit8u readb(PhysPt addr) {
01884                 VGAMEM_USEC_read_delay();
01885                 Bitu port = PAGING_GetPhysicalAddress(addr) & 0xffff;
01886                 return (Bit8u)XGA_Read(port, 1);
01887         }
01888         Bit16u readw(PhysPt addr) {
01889                 VGAMEM_USEC_read_delay();
01890                 Bitu port = PAGING_GetPhysicalAddress(addr) & 0xffff;
01891                 return (Bit16u)XGA_Read(port, 2);
01892         }
01893         Bit32u readd(PhysPt addr) {
01894                 VGAMEM_USEC_read_delay();
01895                 Bitu port = PAGING_GetPhysicalAddress(addr) & 0xffff;
01896                 return (Bit32u)XGA_Read(port, 4);
01897         }
01898 };
01899 
01900 class VGA_TANDY_PageHandler : public PageHandler {
01901 public:
01902         VGA_TANDY_PageHandler() : PageHandler(PFLAG_READABLE|PFLAG_WRITEABLE) {}
01903         HostPt GetHostReadPt(Bitu phys_page) {
01904                 // Odd banks are limited to 16kB and repeated
01905                 if (vga.tandy.mem_bank & 1) 
01906                         phys_page&=0x03;
01907                 else 
01908                         phys_page&=0x07;
01909                 return vga.tandy.mem_base + (phys_page * 4096);
01910         }
01911         HostPt GetHostWritePt(Bitu phys_page) {
01912                 return GetHostReadPt( phys_page );
01913         }
01914 };
01915 
01916 
01917 class VGA_PCJR_Handler : public PageHandler {
01918 public:
01919         VGA_PCJR_Handler() : PageHandler(PFLAG_READABLE|PFLAG_WRITEABLE) {}
01920         HostPt GetHostReadPt(Bitu phys_page) {
01921                 phys_page-=0xb8;
01922                 // The 16kB map area is repeated in the 32kB range
01923                 // On CGA CPU A14 is not decoded so it repeats there too
01924                 phys_page&=0x03;
01925                 return vga.tandy.mem_base + (phys_page * 4096);
01926         }
01927         HostPt GetHostWritePt(Bitu phys_page) {
01928                 return GetHostReadPt( phys_page );
01929         }
01930 };
01931 
01932 class VGA_AMS_Handler : public PageHandler {
01933 public:
01934         template< bool wrapping>
01935         void writeHandler(PhysPt start, Bit8u val) {
01936                 vga.tandy.mem_base[ start ] = val;
01937 #ifdef DIJDIJD
01938                 Bit32u data=ModeOperation(val);
01939                 /* Update video memory and the pixel buffer */
01940                 VGA_Latch pixels;
01941                 pixels.d=((Bit32u*)vga.mem.linear)[start];
01942                 pixels.d&=vga.config.full_not_map_mask;
01943                 pixels.d|=(data & vga.config.full_map_mask);
01944                 ((Bit32u*)vga.mem.linear)[start]=pixels.d;
01945                 Bit8u * write_pixels=&vga.mem.linear[VGA_CACHE_OFFSET+(start<<3)];
01946 
01947                 Bit32u colors0_3, colors4_7;
01948                 VGA_Latch temp;temp.d=(pixels.d>>4) & 0x0f0f0f0f;
01949                         colors0_3 = 
01950                         Expand16Table[0][temp.b[0]] |
01951                         Expand16Table[1][temp.b[1]] |
01952                         Expand16Table[2][temp.b[2]] |
01953                         Expand16Table[3][temp.b[3]];
01954                 *(Bit32u *)write_pixels=colors0_3;
01955                 temp.d=pixels.d & 0x0f0f0f0f;
01956                 colors4_7 = 
01957                         Expand16Table[0][temp.b[0]] |
01958                         Expand16Table[1][temp.b[1]] |
01959                         Expand16Table[2][temp.b[2]] |
01960                         Expand16Table[3][temp.b[3]];
01961                 *(Bit32u *)(write_pixels+4)=colors4_7;
01962                 if (wrapping && GCC_UNLIKELY( start < 512)) {
01963                         *(Bit32u *)(write_pixels+512*1024)=colors0_3;
01964                         *(Bit32u *)(write_pixels+512*1024+4)=colors4_7;
01965                 }
01966 #endif
01967         }
01968 //      template< bool wrapping>
01969         Bit8u readHandler(PhysPt start) {
01970                 return vga.tandy.mem_base[ start ];
01971         }
01972 
01973 public:
01974         VGA_AMS_Handler() {
01975                 //flags=PFLAG_READABLE|PFLAG_WRITEABLE;
01976                 flags=PFLAG_NOCODE;
01977         }
01978         inline PhysPt wrAddr( PhysPt addr )
01979         {
01980                 if( vga.mode != M_AMSTRAD )
01981                 {
01982                         addr -= 0xb8000;
01983                         PhysPt phys_page = addr >> 12;
01984                         //test for a unaliged bank, then replicate 2x16kb
01985                         if (vga.tandy.mem_bank & 1) 
01986                                 phys_page&=0x03;
01987                         return ( phys_page * 4096 ) + ( addr & 0x0FFF );
01988                 }
01989                 return ( (PAGING_GetPhysicalAddress(addr) & 0xffff) - 0x8000 ) & ( 32*1024-1 );
01990         }
01991 
01992         void writeb(PhysPt addr,Bit8u val) {
01993                 VGAMEM_USEC_write_delay();
01994                 addr = wrAddr( addr );
01995                 Bitu plane = vga.mode==M_AMSTRAD ? vga.amstrad.write_plane : 0x01; // 0x0F?
01996                 if( plane & 0x08 ) writeHandler<false>(addr+49152,(Bit8u)(val >> 0));
01997                 if( plane & 0x04 ) writeHandler<false>(addr+32768,(Bit8u)(val >> 0));
01998                 if( plane & 0x02 ) writeHandler<false>(addr+16384,(Bit8u)(val >> 0));
01999                 if( plane & 0x01 ) writeHandler<false>(addr+0,(Bit8u)(val >> 0));
02000         }
02001         void writew(PhysPt addr,Bit16u val) {
02002                 VGAMEM_USEC_write_delay();
02003                 addr = wrAddr( addr );
02004                 Bitu plane = vga.mode==M_AMSTRAD ? vga.amstrad.write_plane : 0x01; // 0x0F?
02005                 if( plane & 0x01 )
02006                 {
02007                         writeHandler<false>(addr+0,(Bit8u)(val >> 0));
02008                         writeHandler<false>(addr+1,(Bit8u)(val >> 8));
02009                 }
02010                 addr += 16384;
02011                 if( plane & 0x02 )
02012                 {
02013                         writeHandler<false>(addr+0,(Bit8u)(val >> 0));
02014                         writeHandler<false>(addr+1,(Bit8u)(val >> 8));
02015                 }
02016                 addr += 16384;
02017                 if( plane & 0x04 )
02018                 {
02019                         writeHandler<false>(addr+0,(Bit8u)(val >> 0));
02020                         writeHandler<false>(addr+1,(Bit8u)(val >> 8));
02021                 }
02022                 addr += 16384;
02023                 if( plane & 0x08 )
02024                 {
02025                         writeHandler<false>(addr+0,(Bit8u)(val >> 0));
02026                         writeHandler<false>(addr+1,(Bit8u)(val >> 8));
02027                 }
02028 
02029         }
02030         void writed(PhysPt addr,Bit32u val) {
02031                 VGAMEM_USEC_write_delay();
02032                 addr = wrAddr( addr );
02033                 Bitu plane = vga.mode==M_AMSTRAD ? vga.amstrad.write_plane : 0x01; // 0x0F?
02034                 if( plane & 0x01 )
02035                 {
02036                         writeHandler<false>(addr+0,(Bit8u)(val >> 0));
02037                         writeHandler<false>(addr+1,(Bit8u)(val >> 8));
02038                         writeHandler<false>(addr+2,(Bit8u)(val >> 16));
02039                         writeHandler<false>(addr+3,(Bit8u)(val >> 24));
02040                 }
02041                 addr += 16384;
02042                 if( plane & 0x02 )
02043                 {
02044                         writeHandler<false>(addr+0,(Bit8u)(val >> 0));
02045                         writeHandler<false>(addr+1,(Bit8u)(val >> 8));
02046                         writeHandler<false>(addr+2,(Bit8u)(val >> 16));
02047                         writeHandler<false>(addr+3,(Bit8u)(val >> 24));
02048                 }
02049                 addr += 16384;
02050                 if( plane & 0x04 )
02051                 {
02052                         writeHandler<false>(addr+0,(Bit8u)(val >> 0));
02053                         writeHandler<false>(addr+1,(Bit8u)(val >> 8));
02054                         writeHandler<false>(addr+2,(Bit8u)(val >> 16));
02055                         writeHandler<false>(addr+3,(Bit8u)(val >> 24));
02056                 }
02057                 addr += 16384;
02058                 if( plane & 0x08 )
02059                 {
02060                         writeHandler<false>(addr+0,(Bit8u)(val >> 0));
02061                         writeHandler<false>(addr+1,(Bit8u)(val >> 8));
02062                         writeHandler<false>(addr+2,(Bit8u)(val >> 16));
02063                         writeHandler<false>(addr+3,(Bit8u)(val >> 24));
02064                 }
02065 
02066         }
02067         Bit8u readb(PhysPt addr) {
02068                 VGAMEM_USEC_read_delay();
02069                 addr = wrAddr( addr ) + ( vga.amstrad.read_plane * 16384u );
02070                 addr &= (64u*1024u-1u);
02071                 return readHandler(addr);
02072         }
02073         Bit16u readw(PhysPt addr) {
02074                 VGAMEM_USEC_read_delay();
02075                 addr = wrAddr( addr ) + ( vga.amstrad.read_plane * 16384u );
02076                 addr &= (64u*1024u-1u);
02077                 return 
02078                         (readHandler(addr+0) << 0u) |
02079                         (readHandler(addr+1) << 8u);
02080         }
02081         Bit32u readd(PhysPt addr) {
02082                 VGAMEM_USEC_read_delay();
02083                 addr = wrAddr( addr ) + ( vga.amstrad.read_plane * 16384u );
02084                 addr &= (64u*1024u-1u);
02085                 return 
02086                         (Bit32u)(readHandler(addr+0) << 0u)  |
02087                         (Bit32u)(readHandler(addr+1) << 8u)  |
02088                         (Bit32u)(readHandler(addr+2) << 16u) |
02089                         (Bit32u)(readHandler(addr+3) << 24u);
02090         }
02091 
02092 /*
02093         HostPt GetHostReadPt(Bitu phys_page)
02094         {
02095                 if( vga.mode!=M_AMSTRAD )
02096                 {
02097                         phys_page-=0xb8;
02098                         //test for a unaliged bank, then replicate 2x16kb
02099                         if (vga.tandy.mem_bank & 1) 
02100                                 phys_page&=0x03;
02101                         return vga.tandy.mem_base + (phys_page * 4096);
02102                 }
02103                 phys_page-=0xb8;
02104                 return vga.tandy.mem_base + (phys_page*4096) + (vga.amstrad.read_plane * 16384) ;
02105         }
02106 */
02107 /*
02108         HostPt GetHostWritePt(Bitu phys_page) {
02109                 return GetHostReadPt( phys_page );
02110         }
02111 */
02112 };
02113 
02114 class VGA_HERC_Handler : public PageHandler {
02115 public:
02116         VGA_HERC_Handler() {
02117                 flags=PFLAG_READABLE|PFLAG_WRITEABLE;
02118         }
02119         HostPt GetHostReadPt(Bitu phys_page) {
02120         (void)phys_page;//UNUSED
02121                 // The 4kB map area is repeated in the 32kB range
02122                 return &vga.mem.linear[0];
02123         }
02124         HostPt GetHostWritePt(Bitu phys_page) {
02125                 return GetHostReadPt( phys_page );
02126         }
02127 };
02128 
02129 class VGA_Empty_Handler : public PageHandler {
02130 public:
02131         VGA_Empty_Handler() : PageHandler(PFLAG_NOCODE) {}
02132         Bit8u readb(PhysPt /*addr*/) {
02133 //              LOG(LOG_VGA, LOG_NORMAL ) ( "Read from empty memory space at %x", addr );
02134                 return 0xff;
02135         } 
02136         void writeb(PhysPt /*addr*/,Bit8u /*val*/) {
02137 //              LOG(LOG_VGA, LOG_NORMAL ) ( "Write %x to empty memory space at %x", val, addr );
02138         }
02139 };
02140 
02141 static struct vg {
02142         VGA_PC98_LFB_Handler            map_lfb_pc98;
02143         VGA_Map_Handler                         map;
02144         VGA_Slow_CGA_Handler            slow;
02145 //      VGA_TEXT_PageHandler            text;
02146         VGA_CGATEXT_PageHandler         cgatext;
02147         VGA_MCGATEXT_PageHandler        mcgatext;
02148         VGA_TANDY_PageHandler           tandy;
02149 //      VGA_ChainedEGA_Handler          cega;
02150 //      VGA_ChainedVGA_Handler          cvga;
02151         VGA_ChainedVGA_Slow_Handler     cvga_slow;
02152 //      VGA_ET4000_ChainedVGA_Handler           cvga_et4000;
02153         VGA_ET4000_ChainedVGA_Slow_Handler      cvga_et4000_slow;
02154 //      VGA_UnchainedEGA_Handler        uega;
02155         VGA_UnchainedVGA_Handler        uvga;
02156         VGA_PCJR_Handler                        pcjr;
02157         VGA_HERC_Handler                        herc;
02158 //      VGA_LIN4_Handler                        lin4;
02159         VGA_LFB_Handler                         lfb;
02160         VGA_MMIO_Handler                        mmio;
02161         VGA_AMS_Handler                         ams;
02162     VGA_PC98_PageHandler        pc98;
02163     VGA_PC98_TEXT_PageHandler   pc98_text;
02164     VGA_PC98_CG_PageHandler     pc98_cg;
02165     VGA_PC98_256MMIO_PageHandler pc98_256mmio;
02166     VGA_PC98_256BANK_PageHandler<0> pc98_256bank0;
02167     VGA_PC98_256BANK_PageHandler<1> pc98_256bank1;
02168     VGA_PC98_256Planar_PageHandler pc98_256planar;
02169         VGA_Empty_Handler                       empty;
02170 } vgaph;
02171 
02172 void VGA_ChangedBank(void) {
02173         VGA_SetupHandlers();
02174 }
02175 
02176 void MEM_ResetPageHandler_Unmapped(Bitu phys_page, Bitu pages);
02177 void MEM_ResetPageHandler_RAM(Bitu phys_page, Bitu pages);
02178 
02179 void VGA_SetupHandlers(void) {
02180         vga.svga.bank_read_full = vga.svga.bank_read*vga.svga.bank_size;
02181         vga.svga.bank_write_full = vga.svga.bank_write*vga.svga.bank_size;
02182 
02183         PageHandler *newHandler;
02184         switch (machine) {
02185         case MCH_CGA:
02186                 if (enableCGASnow && (vga.mode == M_TEXT || vga.mode == M_TANDY_TEXT))
02187                         MEM_SetPageHandler( VGA_PAGE_B8, 8, &vgaph.cgatext );
02188                 else
02189                         MEM_SetPageHandler( VGA_PAGE_B8, 8, &vgaph.slow );
02190                 goto range_done;
02191         case MCH_MCGA://Based on real hardware, A0000-BFFFF is the 64KB of RAM mapped twice
02192                 MEM_SetPageHandler( VGA_PAGE_A0, 16, &vgaph.mcgatext );     // A0000-AFFFF is the 64KB of video RAM
02193         MEM_ResetPageHandler_Unmapped( VGA_PAGE_B0, 8 );            // B0000-B7FFF is unmapped
02194                 MEM_SetPageHandler( VGA_PAGE_B8, 8, &vgaph.mcgatext );      // B8000-BFFFF is the last 32KB half of video RAM, alias
02195                 goto range_done;
02196         case MCH_PCJR:
02197                 MEM_SetPageHandler( VGA_PAGE_A0, 16, &vgaph.empty );
02198                 MEM_SetPageHandler( VGA_PAGE_B0, 8, &vgaph.empty );
02199                 MEM_SetPageHandler( VGA_PAGE_B8, 8, &vgaph.pcjr );
02200                 goto range_done;
02201         case MCH_MDA:
02202         case MCH_HERC:
02203                 MEM_SetPageHandler( VGA_PAGE_A0, 16, &vgaph.empty );
02204                 vgapages.base=VGA_PAGE_B0;
02205                 /* NTS: Implemented according to [http://www.seasip.info/VintagePC/hercplus.html#regs] */
02206                 if (vga.herc.enable_bits & 0x2) { /* bit 1: page in upper 32KB */
02207                         vgapages.mask=0xffff;
02208                         /* NTS: I don't know what Hercules graphics cards do if you set bit 1 but not bit 0.
02209                          *      For the time being, I'm assuming that they respond to 0xB8000+ because of bit 1
02210                          *      but only map to the first 4KB because of bit 0. Basically, a configuration no
02211                          *      software would actually use. */
02212                         if (vga.herc.enable_bits & 0x1) /* allow graphics and enable 0xB1000-0xB7FFF */
02213                                 MEM_SetPageHandler(VGA_PAGE_B0,16,&vgaph.map);
02214                         else
02215                                 MEM_SetPageHandler(VGA_PAGE_B0,16,&vgaph.herc);
02216                 } else {
02217                         vgapages.mask=0x7fff;
02218                         // With hercules in 32kB mode it leaves a memory hole on 0xb800
02219                         // and has MDA-compatible address wrapping when graphics are disabled
02220                         if (vga.herc.enable_bits & 0x1)
02221                                 MEM_SetPageHandler(VGA_PAGE_B0,8,&vgaph.map);
02222                         else
02223                                 MEM_SetPageHandler(VGA_PAGE_B0,8,&vgaph.herc);
02224                         MEM_SetPageHandler(VGA_PAGE_B8,8,&vgaph.empty);
02225                 }
02226                 goto range_done;
02227         case MCH_TANDY:
02228                 /* Always map 0xa000 - 0xbfff, might overwrite 0xb800 */
02229                 vgapages.base=VGA_PAGE_A0;
02230                 vgapages.mask=0x1ffff;
02231                 MEM_SetPageHandler(VGA_PAGE_A0, 32, &vgaph.map );
02232                 if ( vga.tandy.extended_ram & 1 ) {
02233                         //You seem to be able to also map different 64kb banks, but have to figure that out
02234                         //This seems to work so far though
02235                         vga.tandy.draw_base = vga.mem.linear;
02236                         vga.tandy.mem_base = vga.mem.linear;
02237                 } else {
02238                         vga.tandy.draw_base = TANDY_VIDBASE( vga.tandy.draw_bank * 16 * 1024);
02239                         vga.tandy.mem_base = TANDY_VIDBASE( vga.tandy.mem_bank * 16 * 1024);
02240                         MEM_SetPageHandler( VGA_PAGE_B8, 8, &vgaph.tandy );
02241                 }
02242                 goto range_done;
02243 //              MEM_SetPageHandler(vga.tandy.mem_bank<<2,vga.tandy.is_32k_mode ? 0x08 : 0x04,range_handler);
02244         case MCH_AMSTRAD: // Memory handler.
02245                 MEM_SetPageHandler( 0xb8, 8, &vgaph.ams );
02246                 goto range_done;
02247         case EGAVGA_ARCH_CASE:
02248         break;
02249     case PC98_ARCH_CASE:
02250         MEM_SetPageHandler(             VGA_PAGE_A0 + 0x00, 0x02, &vgaph.pc98_text );/* A0000-A1FFFh text layer, character data */
02251         MEM_SetPageHandler(             VGA_PAGE_A0 + 0x02, 0x02, &vgaph.pc98_text );/* A2000-A3FFFh text layer, attribute data + non-volatile RAM */
02252         MEM_SetPageHandler(             VGA_PAGE_A0 + 0x04, 0x01, &vgaph.pc98_cg );  /* A4000-A4FFFh character generator memory-mapped I/O */
02253         MEM_ResetPageHandler_Unmapped(  VGA_PAGE_A0 + 0x05, 0x03);                   /* A5000-A7FFFh not mapped */
02254 
02255         if (pc98_gdc_vramop & (1 << VOPBIT_VGA)) {
02256             if (pc98_gdc_vramop & (1 << VOPBIT_PEGC_PLANAR)) {
02257                 MEM_SetPageHandler(             VGA_PAGE_A0 + 0x08, 0x10, &vgaph.pc98_256planar );/* A8000-B7FFFh planar graphics (???) */
02258                 MEM_ResetPageHandler_Unmapped(  VGA_PAGE_A0 + 0x18, 0x08);                        /* B8000-BFFFFh graphics layer, not mapped */
02259             }
02260             else {
02261                 MEM_SetPageHandler(             VGA_PAGE_A0 + 0x08, 0x08, &vgaph.pc98_256bank0 );/* A8000-AFFFFh graphics layer, bank 0 */
02262                 MEM_SetPageHandler(             VGA_PAGE_A0 + 0x10, 0x08, &vgaph.pc98_256bank1 );/* B0000-B7FFFh graphics layer, bank 1 */
02263                 MEM_ResetPageHandler_Unmapped(  VGA_PAGE_A0 + 0x18, 0x08);                       /* B8000-BFFFFh graphics layer, not mapped */
02264             }
02265         }
02266         else {
02267             MEM_SetPageHandler(             VGA_PAGE_A0 + 0x08, 0x08, &vgaph.pc98 );/* A8000-AFFFFh graphics layer, B bitplane */
02268             MEM_SetPageHandler(             VGA_PAGE_A0 + 0x10, 0x08, &vgaph.pc98 );/* B0000-B7FFFh graphics layer, R bitplane */
02269             MEM_SetPageHandler(             VGA_PAGE_A0 + 0x18, 0x08, &vgaph.pc98 );/* B8000-BFFFFh graphics layer, G bitplane */
02270         }
02271 
02272         /* E0000-E7FFFh graphics layer
02273          *  - In 8-color mode, E0000-E7FFFh is not mapped
02274          *  - In 16-color mode, E0000-E7FFFh is the 4th bitplane (E)
02275          *  - In 256-color mode, E0000-E7FFFh is memory-mapped I/O that controls the 256-color mode */
02276         if (pc98_gdc_vramop & (1 << VOPBIT_VGA))
02277             MEM_SetPageHandler(0xE0, 8, &vgaph.pc98_256mmio );
02278         else if (pc98_gdc_vramop & (1 << VOPBIT_ANALOG))
02279             MEM_SetPageHandler(0xE0, 8, &vgaph.pc98 );
02280         else
02281             MEM_ResetPageHandler_Unmapped(0xE0, 8);
02282 
02283         // TODO: What about PC-9821 systems with more than 15MB of RAM? Do they maintain a "hole"
02284         //       in memory for this linear framebuffer? Intel motherboard chipsets of that era do
02285         //       support a 15MB memory hole.
02286         if (MEM_TotalPages() <= 0xF00/*FIXME*/) {
02287             /* F00000-FF7FFFh linear framebuffer (256-packed)
02288              *  - Does not exist except in 256-color mode.
02289              *  - Switching from 256-color mode immediately unmaps this linear framebuffer.
02290              *  - Switching to 256-color mode will immediately map the linear framebuffer if the enable bit is set in the PEGC MMIO registers */
02291             if ((pc98_gdc_vramop & (1 << VOPBIT_VGA)) && pc98_pegc_linear_framebuffer_enabled())
02292                 MEM_SetPageHandler(0xF00, 512/*kb*/ / 4/*kb*/, &vgaph.map_lfb_pc98 );
02293             else
02294                 MEM_ResetPageHandler_Unmapped(0xF00, 512/*kb*/ / 4/*kb*/);
02295         }
02296 
02297         goto range_done;
02298         default:
02299                 LOG_MSG("Illegal machine type %d", machine );
02300                 return;
02301         }
02302 
02303         /* This should be vga only */
02304         switch (vga.mode) {
02305         case M_ERROR:
02306         default:
02307                 return;
02308         case M_LIN15:
02309         case M_LIN16:
02310         case M_LIN24:
02311         case M_LIN32:
02312     case M_PACKED4:
02313                 newHandler = &vgaph.map;
02314                 break;
02315         case M_TEXT:
02316         case M_CGA2:
02317         case M_CGA4:
02318         /* EGA/VGA emulate CGA modes as chained */
02319         /* fall through */
02320         case M_LIN8:
02321         case M_LIN4:
02322         case M_VGA:
02323         case M_EGA:
02324         if (vga.config.chained) {
02325             if (vga.config.compatible_chain4) {
02326                 /* NTS: ET4000AX cards appear to have a different chain4 implementation from everyone else:
02327                  *      the planar memory byte address is address >> 2 and bits A0-A1 select the plane,
02328                  *      where all other clones I've tested seem to write planar memory byte (address & ~3)
02329                  *      (one byte per 4 bytes) and bits A0-A1 select the plane. */
02330                 /* FIXME: Different chain4 implementation on ET4000 noted---is it true also for ET3000? */
02331                 if (svgaCard == SVGA_TsengET3K || svgaCard == SVGA_TsengET4K)
02332                     newHandler = &vgaph.cvga_et4000_slow;
02333                 else
02334                     newHandler = &vgaph.cvga_slow;
02335             }
02336             else {
02337                 /* this is needed for SVGA modes (Paradise, Tseng, S3) because SVGA
02338                  * modes do NOT use the chain4 configuration */
02339                 newHandler = &vgaph.map;
02340             }
02341         } else {
02342             newHandler = &vgaph.uvga;
02343         }
02344         break;
02345         case M_AMSTRAD:
02346                 newHandler = &vgaph.map;
02347                 break;
02348         }
02349         switch ((vga.gfx.miscellaneous >> 2) & 3) {
02350         case 0:
02351         vgapages.base = VGA_PAGE_A0;
02352         switch (svgaCard) {
02353             case SVGA_TsengET3K:
02354             case SVGA_TsengET4K:
02355                 vgapages.mask = 0x1ffff & vga.mem.memmask;
02356                 break;
02357                 /* NTS: Looking at the official ET4000 programming guide, it does in fact support the full 128KB */
02358             case SVGA_S3Trio:
02359             default:
02360                 vgapages.mask = 0xffff & vga.mem.memmask;
02361                 break;
02362                 }
02363                 MEM_SetPageHandler(VGA_PAGE_A0, 32, newHandler );
02364                 break;
02365         case 1:
02366                 vgapages.base = VGA_PAGE_A0;
02367                 vgapages.mask = 0xffff & vga.mem.memmask;
02368                 MEM_SetPageHandler( VGA_PAGE_A0, 16, newHandler );
02369                 MEM_SetPageHandler( VGA_PAGE_B0, 16, &vgaph.empty );
02370                 break;
02371         case 2:
02372                 vgapages.base = VGA_PAGE_B0;
02373                 vgapages.mask = 0x7fff & vga.mem.memmask;
02374                 MEM_SetPageHandler( VGA_PAGE_B0, 8, newHandler );
02375                 MEM_SetPageHandler( VGA_PAGE_A0, 16, &vgaph.empty );
02376                 MEM_SetPageHandler( VGA_PAGE_B8, 8, &vgaph.empty );
02377         break;
02378         case 3:
02379                 vgapages.base = VGA_PAGE_B8;
02380                 vgapages.mask = 0x7fff & vga.mem.memmask;
02381                 MEM_SetPageHandler( VGA_PAGE_B8, 8, newHandler );
02382                 MEM_SetPageHandler( VGA_PAGE_A0, 16, &vgaph.empty );
02383                 MEM_SetPageHandler( VGA_PAGE_B0, 8, &vgaph.empty );
02384         break;
02385         }
02386         if(svgaCard == SVGA_S3Trio && (vga.s3.ext_mem_ctrl & 0x10))
02387                 MEM_SetPageHandler(VGA_PAGE_A0, 16, &vgaph.mmio);
02388 
02389     non_cga_ignore_oddeven_engage = (non_cga_ignore_oddeven && !(vga.mode == M_TEXT || vga.mode == M_CGA2 || vga.mode == M_CGA4));
02390 
02391 range_done:
02392         PAGING_ClearTLB();
02393 }
02394 
02395 void VGA_StartUpdateLFB(void) {
02396         /* please obey the Linear Address Window Size register!
02397          * Windows 3.1 S3 driver will reprogram the linear framebuffer down to 0xA0000 when entering a DOSBox
02398          * and assuming the full VRAM size will cause a LOT of problems! */
02399         Bitu winsz = 0x10000;
02400 
02401         switch (vga.s3.reg_58&3) {
02402                 case 1:
02403                         winsz = 1 << 20;        //1MB
02404                         break;
02405                 case 2:
02406                         winsz = 2 << 20;        //2MB
02407                         break;
02408                 case 3:
02409                         winsz = 4 << 20;        //4MB
02410                         break;
02411                 // FIXME: What about the 8MB window?
02412         }
02413 
02414     /* The LFB register has an enable bit */
02415     if (!(vga.s3.reg_58 & 0x10)) {
02416         vga.lfb.page = (unsigned int)vga.s3.la_window << 4u;
02417         vga.lfb.addr = (unsigned int)vga.s3.la_window << 16u;
02418         vga.lfb.handler = NULL;
02419         MEM_SetLFB(0,0,NULL,NULL);
02420     }
02421     /* if the DOS application or Windows 3.1 driver attempts to put the linear framebuffer
02422          * below the top of memory, then we're probably entering a DOS VM and it's probably
02423          * a 64KB window. If it's not a 64KB window then print a warning. */
02424     else if ((unsigned long)(vga.s3.la_window << 4UL) < (unsigned long)MEM_TotalPages()) {
02425                 if (winsz != 0x10000) // 64KB window normal for entering a DOS VM in Windows 3.1 or legacy bank switching in DOS
02426                         LOG(LOG_MISC,LOG_WARN)("S3 warning: Window size != 64KB and address conflict with system RAM!");
02427 
02428                 vga.lfb.page = (unsigned int)vga.s3.la_window << 4u;
02429                 vga.lfb.addr = (unsigned int)vga.s3.la_window << 16u;
02430                 vga.lfb.handler = NULL;
02431                 MEM_SetLFB(0,0,NULL,NULL);
02432         }
02433         else {
02434                 vga.lfb.page = (unsigned int)vga.s3.la_window << 4u;
02435                 vga.lfb.addr = (unsigned int)vga.s3.la_window << 16u;
02436                 vga.lfb.handler = &vgaph.lfb;
02437                 MEM_SetLFB((unsigned int)vga.s3.la_window << 4u,(unsigned int)vga.mem.memsize/4096u, vga.lfb.handler, &vgaph.mmio);
02438         }
02439 }
02440 
02441 static bool VGA_Memory_ShutDown_init = false;
02442 
02443 static void VGA_Memory_ShutDown(Section * /*sec*/) {
02444         MEM_SetPageHandler(VGA_PAGE_A0,32,&vgaph.empty);
02445         PAGING_ClearTLB();
02446 
02447         if (vga.mem.linear_orgptr != NULL) {
02448                 delete[] vga.mem.linear_orgptr;
02449                 vga.mem.linear_orgptr = NULL;
02450                 vga.mem.linear = NULL;
02451         }
02452 }
02453 
02454 void VGA_SetupMemory() {
02455         vga.svga.bank_read = vga.svga.bank_write = 0;
02456         vga.svga.bank_read_full = vga.svga.bank_write_full = 0;
02457 
02458     if (vga.mem.linear == NULL) {
02459         VGA_Memory_ShutDown(NULL);
02460 
02461         vga.mem.linear_orgptr = new Bit8u[vga.mem.memsize+32u];
02462         memset(vga.mem.linear_orgptr,0,vga.mem.memsize+32u);
02463         vga.mem.linear=(Bit8u*)(((uintptr_t)vga.mem.linear_orgptr + 16ull-1ull) & ~(16ull-1ull));
02464 
02465         /* HACK. try to avoid stale pointers */
02466             vga.draw.linear_base = vga.mem.linear;
02467         vga.tandy.draw_base = vga.mem.linear;
02468         vga.tandy.mem_base = vga.mem.linear;
02469 
02470         /* PC-98 pointers */
02471         pc98_pgraph_current_cpu_page = vga.mem.linear + PC98_VRAM_GRAPHICS_OFFSET;
02472         pc98_pgraph_current_display_page = vga.mem.linear + PC98_VRAM_GRAPHICS_OFFSET;
02473 
02474         /* parallel system */
02475         if (vga_alt_new_mode) {
02476             for (size_t si=0;si < VGA_Draw_2_elem;si++)
02477                 vga.draw_2[si].draw_base = vga.mem.linear;
02478 
02479             vga.draw_2[0].horz.char_pixel_mask = 0xFFu;
02480             vga.draw_2[0].vert.char_pixel_mask = 0x1Fu;
02481         }
02482 
02483         /* may be related */
02484         VGA_SetupHandlers();
02485     }
02486 
02487         vga.svga.bank_read = vga.svga.bank_write = 0;
02488         vga.svga.bank_read_full = vga.svga.bank_write_full = 0;
02489         vga.svga.bank_size = 0x10000; /* most common bank size is 64K */
02490 
02491         if (!VGA_Memory_ShutDown_init) {
02492                 AddExitFunction(AddExitFunctionFuncPair(VGA_Memory_ShutDown));
02493                 VGA_Memory_ShutDown_init = true;
02494         }
02495 
02496         if (machine==MCH_PCJR) {
02497                 /* PCJr does not have dedicated graphics memory but uses
02498                    conventional memory below 128k */
02499                 //TODO map?     
02500         }
02501 }
02502 
02503 // save state support
02504 void *VGA_PageHandler_Func[16] =
02505 {
02506         (void *) &vgaph.map,
02507         //(void *) &vgaph.changes,
02508         //(void *) &vgaph.text,
02509         (void *) &vgaph.tandy,
02510         //(void *) &vgaph.cega,
02511         //(void *) &vgaph.cvga,
02512         //(void *) &vgaph.uega,
02513         (void *) &vgaph.uvga,
02514         (void *) &vgaph.pcjr,
02515         (void *) &vgaph.herc,
02516         //(void *) &vgaph.lin4,
02517         (void *) &vgaph.lfb,
02518         //(void *) &vgaph.lfbchanges,
02519         (void *) &vgaph.mmio,
02520         (void *) &vgaph.empty,
02521 };
02522 
02523 void POD_Save_VGA_Memory( std::ostream& stream )
02524 {
02525         // - static ptrs
02526         //Bit8u* linear;
02527         //Bit8u* linear_orgptr;
02528 
02529 
02530         // - pure data
02531         WRITE_POD_SIZE( vga.mem.linear, sizeof(Bit8u) * vga.mem.memsize);
02532 
02533         //***************************************************
02534         //***************************************************
02535 
02536         // static globals
02537 
02538         // - pure struct data
02539         WRITE_POD( &vgapages, vgapages );
02540 
02541         // - static classes
02542         //WRITE_POD( &vgaph, vgaph );
02543 }
02544 
02545 
02546 void POD_Load_VGA_Memory( std::istream& stream )
02547 {
02548         // - static ptrs
02549         //Bit8u* linear;
02550         //Bit8u* linear_orgptr;
02551 
02552 
02553         // - pure data
02554         READ_POD_SIZE( vga.mem.linear, sizeof(Bit8u) * vga.mem.memsize);
02555 
02556         //***************************************************
02557         //***************************************************
02558 
02559         // static globals
02560 
02561         // - pure struct data
02562         READ_POD( &vgapages, vgapages );
02563 
02564         // - static classes
02565         //READ_POD( &vgaph, vgaph );
02566 }