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