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 extern uint8_t pc98_egc_srcmask[2]; /* host given (Neko: egc.srcmask) */
00515 extern uint8_t pc98_egc_maskef[2]; /* effective (Neko: egc.mask2) */
00516 extern uint8_t pc98_egc_mask[2]; /* host given (Neko: egc.mask) */
00517 extern uint8_t pc98_egc_access;
00518 extern uint8_t pc98_egc_fgc;
00519 extern uint8_t pc98_egc_foreground_color;
00520 extern uint8_t pc98_egc_background_color;
00521 extern uint8_t pc98_egc_lead_plane;
00522 extern uint8_t pc98_egc_compare_lead;
00523 extern uint8_t pc98_egc_lightsource;
00524 extern uint8_t pc98_egc_shiftinput;
00525 extern uint8_t pc98_egc_regload;
00526 extern uint8_t pc98_egc_rop;
00527 
00528 extern bool pc98_egc_shift_descend;
00529 extern uint8_t pc98_egc_shift_destbit;
00530 extern uint8_t pc98_egc_shift_srcbit;
00531 extern uint16_t pc98_egc_shift_length;
00532 
00533 /* I don't think we necessarily need the full 4096 bit buffer
00534  * Neko Project II uses to render things, though that is
00535  * probably faster to execute. It makes it hard to make sense
00536  * of the code though. */
00537 struct pc98_egc_shifter {
00538     pc98_egc_shifter() : decrement(false), remain(0x10), srcbit(0), dstbit(0) { }
00539 
00540     void reinit(void) { /* from global vars set by guest */
00541         decrement = pc98_egc_shift_descend;
00542         remain = pc98_egc_shift_length + 1; /* the register is length - 1 apparently */
00543         dstbit = pc98_egc_shift_destbit;
00544         srcbit = pc98_egc_shift_srcbit;
00545         bufi = bufo = decrement ? (sizeof(buffer) + 3 - (4*4)) : 0;
00546 
00547         if ((srcbit&7) < (dstbit&7)) {
00548             shft8bitr = (dstbit&7) - (srcbit&7);
00549             shft8bitl = 8 - shft8bitr;
00550         }
00551         else if ((srcbit&7) > (dstbit&7)) {
00552             shft8bitl = (srcbit&7) - (dstbit&7);
00553             shft8bitr = 8 - shft8bitl;
00554         }
00555         else {
00556             shft8bitr = 0;
00557             shft8bitl = 0;
00558         }
00559 
00560         shft8load = 0;
00561         o_srcbit = srcbit & 7;
00562         o_dstbit = dstbit & 7;
00563     }
00564 
00565     bool                decrement;
00566     uint16_t            remain;
00567     uint16_t            srcbit;
00568     uint16_t            dstbit;
00569     uint16_t            o_srcbit;
00570     uint16_t            o_dstbit;
00571 
00572     uint8_t             buffer[512]; /* 4096/8 = 512 */
00573     uint16_t            bufi,bufo;
00574 
00575     uint8_t             shft8load;
00576     uint8_t             shft8bitr;
00577     uint8_t             shft8bitl;
00578 
00579     template <class AWT> inline void bi(const uint16_t ofs,const AWT val) {
00580         size_t ip = (bufi + ofs) & (sizeof(buffer) - 1);
00581 
00582         for (size_t i=0;i < sizeof(AWT);) {
00583             buffer[ip] = (uint8_t)(val >> ((AWT)(i * 8U)));
00584             if ((++ip) == sizeof(buffer)) ip = 0;
00585             i++;
00586         }
00587     }
00588 
00589     template <class AWT> inline void bi_adv(void) {
00590         bufi += pc98_egc_shift_descend ? (sizeof(buffer) - sizeof(AWT)) : sizeof(AWT);
00591         bufi &= (sizeof(buffer) - 1);
00592     }
00593 
00594     template <class AWT> inline AWT bo(const uint16_t ofs) {
00595         size_t op = (bufo + ofs) & (sizeof(buffer) - 1);
00596         AWT ret = 0;
00597 
00598         for (size_t i=0;i < sizeof(AWT);) {
00599             ret += ((AWT)buffer[op]) << ((AWT)(i * 8U));
00600             if ((++op) == sizeof(buffer)) op = 0;
00601             i++;
00602         }
00603 
00604         return ret;
00605     }
00606 
00607     template <class AWT> inline void bo_adv(void) {
00608         bufo += pc98_egc_shift_descend ? (sizeof(buffer) - sizeof(AWT)) : sizeof(AWT);
00609         bufo &= (sizeof(buffer) - 1);
00610     }
00611 
00612     template <class AWT> inline void input(const AWT a,const AWT b,const AWT c,const AWT d,uint8_t odd) {
00613         bi<AWT>((pc98_egc_shift_descend ? (sizeof(buffer) + 1 - sizeof(AWT)) : 0) + 0,a);
00614         bi<AWT>((pc98_egc_shift_descend ? (sizeof(buffer) + 1 - sizeof(AWT)) : 0) + 4,b);
00615         bi<AWT>((pc98_egc_shift_descend ? (sizeof(buffer) + 1 - sizeof(AWT)) : 0) + 8,c);
00616         bi<AWT>((pc98_egc_shift_descend ? (sizeof(buffer) + 1 - sizeof(AWT)) : 0) + 12,d);
00617 
00618         if (shft8load <= 16) {
00619             bi_adv<AWT>();
00620 
00621             if (sizeof(AWT) == 2) {
00622                 if (srcbit >= 8) bo_adv<uint8_t>();
00623                 shft8load += (16 - srcbit);
00624                 srcbit = 0;
00625             }
00626             else {
00627                 if (srcbit >= 8)
00628                     srcbit -= 8;
00629                 else {
00630                     shft8load += (8 - srcbit);
00631                     srcbit = 0;
00632                 }
00633             }
00634         }
00635 
00636         *((AWT*)(pc98_egc_srcmask+odd)) = (AWT)(~0ull);
00637     }
00638 
00639     inline uint8_t dstbit_mask(void) {
00640         uint8_t mb;
00641 
00642         /* assume remain > 0 */
00643         if (remain >= 8)
00644             mb = 0xFF;
00645         else if (!pc98_egc_shift_descend)
00646             mb = 0xFF << (uint8_t)(8 - remain); /* 0x80 0xC0 0xE0 0xF0 ... */
00647         else
00648             mb = 0xFF >> (uint8_t)(8 - remain); /* 0x01 0x03 0x07 0x0F ... */
00649 
00650         /* assume dstbit < 8 */
00651         if (!pc98_egc_shift_descend)
00652             return mb >> (uint8_t)dstbit; /* 0xFF 0x7F 0x3F 0x1F ... */
00653         else
00654             return mb << (uint8_t)dstbit; /* 0xFF 0xFE 0xFC 0xF8 ... */
00655     }
00656 
00657     template <class AWT> inline void output(AWT &a,AWT &b,AWT &c,AWT &d,uint8_t odd,bool recursive=false) {
00658         if (sizeof(AWT) == 2) {
00659             if (shft8load < (16 - dstbit)) {
00660                 *((AWT*)(pc98_egc_srcmask+odd)) = 0;
00661                 return;
00662             }
00663             shft8load -= (16 - dstbit);
00664 
00665             /* assume odd == false and output is to even byte offset */
00666             if (pc98_egc_shift_descend) {
00667                 output<uint8_t>(((uint8_t*)(&a))[1],((uint8_t*)(&b))[1],((uint8_t*)(&c))[1],((uint8_t*)(&d))[1],1,true);
00668                 if (remain != 0) output<uint8_t>(((uint8_t*)(&a))[0],((uint8_t*)(&b))[0],((uint8_t*)(&c))[0],((uint8_t*)(&d))[0],0,true);
00669                 else pc98_egc_srcmask[0] = 0;
00670             }
00671             else {
00672                 output<uint8_t>(((uint8_t*)(&a))[0],((uint8_t*)(&b))[0],((uint8_t*)(&c))[0],((uint8_t*)(&d))[0],0,true);
00673                 if (remain != 0) output<uint8_t>(((uint8_t*)(&a))[1],((uint8_t*)(&b))[1],((uint8_t*)(&c))[1],((uint8_t*)(&d))[1],1,true);
00674                 else pc98_egc_srcmask[1] = 0;
00675             }
00676 
00677             if (remain == 0)
00678                 reinit();
00679 
00680             return;
00681         }
00682 
00683         if (!recursive) {
00684             if (shft8load < (8 - dstbit)) {
00685                 *((AWT*)(pc98_egc_srcmask+odd)) = 0;
00686                 return;
00687             }
00688             shft8load -= (8 - dstbit);
00689         }
00690 
00691         if (dstbit >= 8) {
00692             dstbit -= 8;
00693             *((AWT*)(pc98_egc_srcmask+odd)) = 0;
00694             return;
00695         }
00696 
00697         *((AWT*)(pc98_egc_srcmask+odd)) = dstbit_mask();
00698 
00699         if (dstbit > 0) {
00700             const uint8_t bc = 8 - dstbit;
00701 
00702             if (remain >= bc)
00703                 remain -= bc;
00704             else
00705                 remain = 0;
00706         }
00707         else {
00708             if (remain >= 8)
00709                 remain -= 8;
00710             else
00711                 remain = 0;
00712         }
00713 
00714         if (o_srcbit < o_dstbit) {
00715             if (dstbit != 0) {
00716                 if (pc98_egc_shift_descend) {
00717                     a = bo<AWT>( 0) << shft8bitr;
00718                     b = bo<AWT>( 4) << shft8bitr;
00719                     c = bo<AWT>( 8) << shft8bitr;
00720                     d = bo<AWT>(12) << shft8bitr;
00721                 }
00722                 else {
00723                     a = bo<AWT>( 0) >> shft8bitr;
00724                     b = bo<AWT>( 4) >> shft8bitr;
00725                     c = bo<AWT>( 8) >> shft8bitr;
00726                     d = bo<AWT>(12) >> shft8bitr;
00727                 }
00728 
00729                 dstbit = 0;
00730             }
00731             else {
00732                 if (pc98_egc_shift_descend) {
00733                     bo_adv<AWT>();
00734                     a = (bo<AWT>( 0+1) >> shft8bitl) | (bo<AWT>( 0) << shft8bitr);
00735                     b = (bo<AWT>( 4+1) >> shft8bitl) | (bo<AWT>( 4) << shft8bitr);
00736                     c = (bo<AWT>( 8+1) >> shft8bitl) | (bo<AWT>( 8) << shft8bitr);
00737                     d = (bo<AWT>(12+1) >> shft8bitl) | (bo<AWT>(12) << shft8bitr);
00738                 }
00739                 else {
00740                     a = (bo<AWT>( 0) << shft8bitl) | (bo<AWT>( 0+1) >> shft8bitr);
00741                     b = (bo<AWT>( 4) << shft8bitl) | (bo<AWT>( 4+1) >> shft8bitr);
00742                     c = (bo<AWT>( 8) << shft8bitl) | (bo<AWT>( 8+1) >> shft8bitr);
00743                     d = (bo<AWT>(12) << shft8bitl) | (bo<AWT>(12+1) >> shft8bitr);
00744                     bo_adv<AWT>();
00745                 }
00746             }
00747         }
00748         else if (o_srcbit > o_dstbit) {
00749             dstbit = 0;
00750 
00751             if (pc98_egc_shift_descend) {
00752                 bo_adv<AWT>();
00753                 a = (bo<AWT>( 0+1) >> shft8bitl) | (bo<AWT>( 0) << shft8bitr);
00754                 b = (bo<AWT>( 4+1) >> shft8bitl) | (bo<AWT>( 4) << shft8bitr);
00755                 c = (bo<AWT>( 8+1) >> shft8bitl) | (bo<AWT>( 8) << shft8bitr);
00756                 d = (bo<AWT>(12+1) >> shft8bitl) | (bo<AWT>(12) << shft8bitr);
00757             }
00758             else {
00759                 a = (bo<AWT>( 0) << shft8bitl) | (bo<AWT>( 0+1) >> shft8bitr);
00760                 b = (bo<AWT>( 4) << shft8bitl) | (bo<AWT>( 4+1) >> shft8bitr);
00761                 c = (bo<AWT>( 8) << shft8bitl) | (bo<AWT>( 8+1) >> shft8bitr);
00762                 d = (bo<AWT>(12) << shft8bitl) | (bo<AWT>(12+1) >> shft8bitr);
00763                 bo_adv<AWT>();
00764             }
00765         }
00766         else {
00767             dstbit = 0;
00768 
00769             a = bo<AWT>( 0);
00770             b = bo<AWT>( 4);
00771             c = bo<AWT>( 8);
00772             d = bo<AWT>(12);
00773             bo_adv<AWT>();
00774         }
00775 
00776         if (!recursive && remain == 0)
00777             reinit();
00778     }
00779 };
00780 
00781 egc_quad pc98_egc_src;
00782 egc_quad pc98_egc_bgcm;
00783 egc_quad pc98_egc_fgcm;
00784 egc_quad pc98_egc_data;
00785 egc_quad pc98_egc_last_vram;
00786 
00787 pc98_egc_shifter pc98_egc_shift;
00788 
00789 typedef egc_quad & (*PC98_OPEFN)(uint8_t ope, const PhysPt ad);
00790 
00791 void pc98_egc_shift_reinit() {
00792     pc98_egc_shift.reinit();
00793 }
00794 
00795 static egc_quad &ope_xx(uint8_t ope, const PhysPt ad) {
00796     (void)ad;//UNUSED
00797     LOG_MSG("EGC ROP 0x%2x not impl",ope);
00798     return pc98_egc_last_vram;
00799 }
00800 
00801 static egc_quad &ope_np(uint8_t ope, const PhysPt vramoff) {
00802         egc_quad dst;
00803 
00804         dst[0].w = *((uint16_t*)(vga.mem.linear+vramoff+0x08000));
00805         dst[1].w = *((uint16_t*)(vga.mem.linear+vramoff+0x10000));
00806         dst[2].w = *((uint16_t*)(vga.mem.linear+vramoff+0x18000));
00807         dst[3].w = *((uint16_t*)(vga.mem.linear+vramoff+0x20000));
00808 
00809         pc98_egc_data[0].w = 0;
00810         pc98_egc_data[1].w = 0;
00811         pc98_egc_data[2].w = 0;
00812         pc98_egc_data[3].w = 0;
00813 
00814         if (ope & 0x80) {
00815         pc98_egc_data[0].w |= (pc98_egc_src[0].w & dst[0].w);
00816         pc98_egc_data[1].w |= (pc98_egc_src[1].w & dst[1].w);
00817         pc98_egc_data[2].w |= (pc98_egc_src[2].w & dst[2].w);
00818         pc98_egc_data[3].w |= (pc98_egc_src[3].w & dst[3].w);
00819     }
00820         if (ope & 0x20) {
00821         pc98_egc_data[0].w |= (pc98_egc_src[0].w & (~dst[0].w));
00822         pc98_egc_data[1].w |= (pc98_egc_src[1].w & (~dst[1].w));
00823         pc98_egc_data[2].w |= (pc98_egc_src[2].w & (~dst[2].w));
00824         pc98_egc_data[3].w |= (pc98_egc_src[3].w & (~dst[3].w));
00825         }
00826         if (ope & 0x08) {
00827         pc98_egc_data[0].w |= ((~pc98_egc_src[0].w) & dst[0].w);
00828         pc98_egc_data[1].w |= ((~pc98_egc_src[1].w) & dst[1].w);
00829         pc98_egc_data[2].w |= ((~pc98_egc_src[2].w) & dst[2].w);
00830         pc98_egc_data[3].w |= ((~pc98_egc_src[3].w) & dst[3].w);
00831         }
00832         if (ope & 0x02) {
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 
00839         (void)ope;
00840         (void)vramoff;
00841         return pc98_egc_data;
00842 }
00843 
00844 static egc_quad &ope_c0(uint8_t ope, const PhysPt vramoff) {
00845         egc_quad dst;
00846 
00847     /* assume: ad is word aligned */
00848 
00849         dst[0].w = *((uint16_t*)(vga.mem.linear+vramoff+0x08000));
00850         dst[1].w = *((uint16_t*)(vga.mem.linear+vramoff+0x10000));
00851         dst[2].w = *((uint16_t*)(vga.mem.linear+vramoff+0x18000));
00852         dst[3].w = *((uint16_t*)(vga.mem.linear+vramoff+0x20000));
00853 
00854         pc98_egc_data[0].w = pc98_egc_src[0].w & dst[0].w;
00855         pc98_egc_data[1].w = pc98_egc_src[1].w & dst[1].w;
00856         pc98_egc_data[2].w = pc98_egc_src[2].w & dst[2].w;
00857         pc98_egc_data[3].w = pc98_egc_src[3].w & dst[3].w;
00858 
00859         (void)ope;
00860         (void)vramoff;
00861         return pc98_egc_data;
00862 }
00863 
00864 static egc_quad &ope_f0(uint8_t ope, const PhysPt vramoff) {
00865         (void)ope;
00866         (void)vramoff;
00867         return pc98_egc_src;
00868 }
00869 
00870 static egc_quad &ope_fc(uint8_t ope, const PhysPt vramoff) {
00871         egc_quad dst;
00872 
00873     /* assume: ad is word aligned */
00874 
00875         dst[0].w = *((uint16_t*)(vga.mem.linear+vramoff+0x08000));
00876         dst[1].w = *((uint16_t*)(vga.mem.linear+vramoff+0x10000));
00877         dst[2].w = *((uint16_t*)(vga.mem.linear+vramoff+0x18000));
00878         dst[3].w = *((uint16_t*)(vga.mem.linear+vramoff+0x20000));
00879 
00880         pc98_egc_data[0].w  =    pc98_egc_src[0].w;
00881         pc98_egc_data[0].w |= ((~pc98_egc_src[0].w) & dst[0].w);
00882         pc98_egc_data[1].w  =    pc98_egc_src[1].w;
00883         pc98_egc_data[1].w |= ((~pc98_egc_src[1].w) & dst[1].w);
00884         pc98_egc_data[2].w  =    pc98_egc_src[2].w;
00885         pc98_egc_data[2].w |= ((~pc98_egc_src[2].w) & dst[2].w);
00886         pc98_egc_data[3].w  =    pc98_egc_src[3].w;
00887         pc98_egc_data[3].w |= ((~pc98_egc_src[3].w) & dst[3].w);
00888 
00889         (void)ope;
00890         (void)vramoff;
00891         return pc98_egc_data;
00892 }
00893 
00894 static egc_quad &ope_gg(uint8_t ope, const PhysPt vramoff) {
00895     egc_quad pat,dst;
00896 
00897         switch(pc98_egc_fgc) {
00898                 case 1:
00899                         pat[0].w = pc98_egc_bgcm[0].w;
00900                         pat[1].w = pc98_egc_bgcm[1].w;
00901                         pat[2].w = pc98_egc_bgcm[2].w;
00902                         pat[3].w = pc98_egc_bgcm[3].w;
00903                         break;
00904 
00905                 case 2:
00906                         pat[0].w = pc98_egc_fgcm[0].w;
00907                         pat[1].w = pc98_egc_fgcm[1].w;
00908                         pat[2].w = pc98_egc_fgcm[2].w;
00909                         pat[3].w = pc98_egc_fgcm[3].w;
00910                         break;
00911 
00912                 default:
00913                         if (pc98_egc_regload & 1) {
00914                                 pat[0].w = pc98_egc_src[0].w;
00915                                 pat[1].w = pc98_egc_src[1].w;
00916                                 pat[2].w = pc98_egc_src[2].w;
00917                                 pat[3].w = pc98_egc_src[3].w;
00918                         }
00919                         else {
00920                                 pat[0].w = pc98_gdc_tiles[0].w;
00921                                 pat[1].w = pc98_gdc_tiles[1].w;
00922                                 pat[2].w = pc98_gdc_tiles[2].w;
00923                                 pat[3].w = pc98_gdc_tiles[3].w;
00924                         }
00925                         break;
00926         }
00927 
00928         dst[0].w = *((uint16_t*)(vga.mem.linear+vramoff+0x08000));
00929         dst[1].w = *((uint16_t*)(vga.mem.linear+vramoff+0x10000));
00930         dst[2].w = *((uint16_t*)(vga.mem.linear+vramoff+0x18000));
00931         dst[3].w = *((uint16_t*)(vga.mem.linear+vramoff+0x20000));
00932 
00933         pc98_egc_data[0].w = 0;
00934         pc98_egc_data[1].w = 0;
00935         pc98_egc_data[2].w = 0;
00936         pc98_egc_data[3].w = 0;
00937 
00938         if (ope & 0x80) {
00939                 pc98_egc_data[0].w |=  ( pat[0].w  &   pc98_egc_src[0].w &    dst[0].w);
00940                 pc98_egc_data[1].w |=  ( pat[1].w  &   pc98_egc_src[1].w &    dst[1].w);
00941                 pc98_egc_data[2].w |=  ( pat[2].w  &   pc98_egc_src[2].w &    dst[2].w);
00942                 pc98_egc_data[3].w |=  ( pat[3].w  &   pc98_egc_src[3].w &    dst[3].w);
00943         }
00944         if (ope & 0x40) {
00945                 pc98_egc_data[0].w |= ((~pat[0].w) &   pc98_egc_src[0].w &    dst[0].w);
00946                 pc98_egc_data[1].w |= ((~pat[1].w) &   pc98_egc_src[1].w &    dst[1].w);
00947                 pc98_egc_data[2].w |= ((~pat[2].w) &   pc98_egc_src[2].w &    dst[2].w);
00948                 pc98_egc_data[3].w |= ((~pat[3].w) &   pc98_egc_src[3].w &    dst[3].w);
00949         }
00950         if (ope & 0x20) {
00951                 pc98_egc_data[0].w |= (  pat[0].w  &   pc98_egc_src[0].w &  (~dst[0].w));
00952                 pc98_egc_data[1].w |= (  pat[1].w  &   pc98_egc_src[1].w &  (~dst[1].w));
00953                 pc98_egc_data[2].w |= (  pat[2].w  &   pc98_egc_src[2].w &  (~dst[2].w));
00954                 pc98_egc_data[3].w |= (  pat[3].w  &   pc98_egc_src[3].w &  (~dst[3].w));
00955         }
00956         if (ope & 0x10) {
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 & 0x08) {
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 & 0x04) {
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 & 0x02) {
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 & 0x01) {
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 
00987         return pc98_egc_data;
00988 }
00989 
00990 static const PC98_OPEFN pc98_egc_opfn[256] = {
00991                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
00992                         ope_xx, ope_xx, ope_xx, ope_xx, ope_np, ope_xx, ope_xx, ope_xx,
00993                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
00994                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
00995                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
00996                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
00997                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
00998                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
00999                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01000                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01001                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01002                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01003                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01004                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01005                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01006                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01007                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01008                         ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
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_xx, 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_gg, 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_c0, 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_f0, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx, ope_xx,
01022                         ope_xx, ope_xx, ope_xx, ope_xx, ope_fc, ope_xx, ope_xx, ope_xx};
01023 
01024 template <class AWT> static egc_quad &egc_ope(const PhysPt vramoff, const AWT val) {
01025     *((uint16_t*)pc98_egc_maskef) = *((uint16_t*)pc98_egc_mask);
01026 
01027     /* 4A4h
01028      * bits [12:11] = light source
01029      *    11 = invalid
01030      *    10 = write the contents of the palette register
01031      *    01 = write the result of the raster operation
01032      *    00 = write CPU data
01033      *
01034      * 4A2h
01035      * bits [14:13] = foreground, background color
01036      *    11 = invalid
01037      *    10 = foreground color
01038      *    01 = background color
01039      *    00 = pattern register
01040      */
01041     switch (pc98_egc_lightsource) {
01042         case 1: /* 0x0800 */
01043             if (pc98_egc_shiftinput) {
01044                 pc98_egc_shift.input<AWT>(
01045                     val,
01046                     val,
01047                     val,
01048                     val,
01049                     vramoff&1);
01050 
01051                 pc98_egc_shift.output<AWT>(
01052                     *((AWT*)(pc98_egc_src[0].b+(vramoff&1))),
01053                     *((AWT*)(pc98_egc_src[1].b+(vramoff&1))),
01054                     *((AWT*)(pc98_egc_src[2].b+(vramoff&1))),
01055                     *((AWT*)(pc98_egc_src[3].b+(vramoff&1))),
01056                     vramoff&1);
01057             }
01058 
01059             *((uint16_t*)pc98_egc_maskef) &= *((uint16_t*)pc98_egc_srcmask);
01060             return pc98_egc_opfn[pc98_egc_rop](pc98_egc_rop, vramoff & (~1U));
01061         case 2: /* 0x1000 */
01062             if (pc98_egc_fgc == 1)
01063                 return pc98_egc_bgcm;
01064             else if (pc98_egc_fgc == 2)
01065                 return pc98_egc_fgcm;
01066 
01067             if (pc98_egc_shiftinput) {
01068                 pc98_egc_shift.input<AWT>(
01069                     val,
01070                     val,
01071                     val,
01072                     val,
01073                     vramoff&1);
01074 
01075                 pc98_egc_shift.output<AWT>(
01076                     *((AWT*)(pc98_egc_src[0].b+(vramoff&1))),
01077                     *((AWT*)(pc98_egc_src[1].b+(vramoff&1))),
01078                     *((AWT*)(pc98_egc_src[2].b+(vramoff&1))),
01079                     *((AWT*)(pc98_egc_src[3].b+(vramoff&1))),
01080                     vramoff&1);
01081             }
01082  
01083             *((uint16_t*)pc98_egc_maskef) &= *((uint16_t*)pc98_egc_srcmask);
01084             return pc98_egc_src;
01085         default: {
01086             uint16_t tmp = (uint16_t)val;
01087 
01088             if (sizeof(AWT) < 2) {
01089                 tmp &= 0xFFU;
01090                 tmp |= tmp << 8U;
01091             }
01092 
01093             pc98_egc_data[0].w = tmp;
01094             pc98_egc_data[1].w = tmp;
01095             pc98_egc_data[2].w = tmp;
01096             pc98_egc_data[3].w = tmp;
01097             } break;
01098     };
01099 
01100     return pc98_egc_data;
01101 }
01102 
01103 unsigned char pc98_mem_msw_m[8] = {0};
01104 
01105 void pc98_msw3_set_ramsize(const unsigned char b) {
01106     pc98_mem_msw_m[2/*MSW3*/] = b;
01107 }
01108 
01109 unsigned char pc98_mem_msw(unsigned char which) {
01110     return pc98_mem_msw_m[which&7];
01111 }
01112 
01113 /* The NEC display is documented to have:
01114  *
01115  * A0000-A3FFF      T-RAM (text) (8KB WORDs)
01116  *   A0000-A1FFF      Characters (4KB WORDs)
01117  *   A2000-A3FFF      Attributes (4KB WORDs). For each 16-bit WORD only the lower 8 bits are read/writeable.
01118  *   A4000-A5FFF      Unknown ?? (4KB WORDs)
01119  *   A6000-A7FFF      Not present (4KB WORDs)
01120  * A8000-BFFFF      G-RAM (graphics) (96KB)
01121  *
01122  * T-RAM character display RAM is 16-bits per character.
01123  * ASCII text has upper 8 bits zero.
01124  * SHIFT-JIS doublewide characters use the upper byte for non-ASCII. */
01125 
01126 class VGA_PC98_PageHandler : public PageHandler {
01127 public:
01128         VGA_PC98_PageHandler() : PageHandler(PFLAG_NOCODE) {}
01129     template <class AWT> static inline void check_align(const PhysPt addr) {
01130         /* DEBUG: address must be aligned to datatype.
01131          *        Code that calls us must enforce that or subdivide
01132          *        to a small datatype that can follow this rule. */
01133         PhysPt chk = (1UL << (sizeof(AWT) - 1)) - 1;
01134         /* uint8_t:  chk = 0
01135          * uint16_t: chk = 1
01136          * TODO: Do you suppose later generation PC-9821's supported DWORD size bitplane transfers?
01137          *       Or did NEC just give up on anything past 16-bit and focus on the SVGA side of things? */
01138         assert((addr&chk) == 0);
01139     }
01140 
01141     template <class AWT> static inline AWT mode8_r(const unsigned int plane,const PhysPt vramoff) {
01142         AWT r,b;
01143 
01144         b = *((AWT*)(vga.mem.linear + vramoff));
01145         r = b ^ *((AWT*)pc98_gdc_tiles[plane].b);
01146 
01147         return r;
01148     }
01149 
01150     template <class AWT> static inline void mode8_w(const unsigned int plane,const PhysPt vramoff) {
01151         /* Neko Project II code suggests that the first byte is repeated. */
01152         if (sizeof(AWT) > 1)
01153             pc98_gdc_tiles[plane].b[1] = pc98_gdc_tiles[plane].b[0];
01154 
01155         *((AWT*)(vga.mem.linear + vramoff)) = *((AWT*)pc98_gdc_tiles[plane].b);
01156     }
01157 
01158     template <class AWT> static inline void modeC_w(const unsigned int plane,const PhysPt vramoff,const AWT mask,const AWT val) {
01159         AWT t;
01160 
01161         /* Neko Project II code suggests that the first byte is repeated. */
01162         if (sizeof(AWT) > 1)
01163             pc98_gdc_tiles[plane].b[1] = pc98_gdc_tiles[plane].b[0];
01164 
01165         t  = *((AWT*)(vga.mem.linear + vramoff)) & mask;
01166         t |= val & *((AWT*)pc98_gdc_tiles[plane].b);
01167         *((AWT*)(vga.mem.linear + vramoff)) = t;
01168     }
01169 
01170     template <class AWT> static inline AWT modeEGC_r(const PhysPt vramoff,const PhysPt fulloff) {
01171         /* assume: vramoff is even IF AWT is 16-bit wide */
01172         *((AWT*)(pc98_egc_last_vram[0].b+(vramoff&1))) = *((AWT*)(vga.mem.linear+vramoff+0x08000));
01173         *((AWT*)(pc98_egc_last_vram[1].b+(vramoff&1))) = *((AWT*)(vga.mem.linear+vramoff+0x10000));
01174         *((AWT*)(pc98_egc_last_vram[2].b+(vramoff&1))) = *((AWT*)(vga.mem.linear+vramoff+0x18000));
01175         *((AWT*)(pc98_egc_last_vram[3].b+(vramoff&1))) = *((AWT*)(vga.mem.linear+vramoff+0x20000));
01176 
01177         /* bits [10:10] = read source
01178          *    1 = shifter input is CPU write data
01179          *    0 = shifter input is VRAM data */
01180         /* Neko Project II: if ((egc.ope & 0x0400) == 0) ... */
01181         if (!pc98_egc_shiftinput) {
01182             pc98_egc_shift.input<AWT>(
01183                 *((AWT*)(pc98_egc_last_vram[0].b+(vramoff&1))),
01184                 *((AWT*)(pc98_egc_last_vram[1].b+(vramoff&1))),
01185                 *((AWT*)(pc98_egc_last_vram[2].b+(vramoff&1))),
01186                 *((AWT*)(pc98_egc_last_vram[3].b+(vramoff&1))),
01187                 vramoff&1);
01188 
01189             pc98_egc_shift.output<AWT>(
01190                 *((AWT*)(pc98_egc_src[0].b+(vramoff&1))),
01191                 *((AWT*)(pc98_egc_src[1].b+(vramoff&1))),
01192                 *((AWT*)(pc98_egc_src[2].b+(vramoff&1))),
01193                 *((AWT*)(pc98_egc_src[3].b+(vramoff&1))),
01194                 vramoff&1);
01195         }
01196 
01197         /* 0x4A4:
01198          * ...
01199          * bits [9:8] = register load (pc98_egc_regload[1:0])
01200          *    11 = invalid
01201          *    10 = load VRAM data before writing on VRAM write
01202          *    01 = load VRAM data into pattern/tile register on VRAM read
01203          *    00 = Do not change pattern/tile register
01204          * ...
01205          *
01206          * pc98_egc_regload = (val >> 8) & 3;
01207          */
01208         /* Neko Project II: if ((egc.ope & 0x0300) == 0x0100) ... */
01209         if (pc98_egc_regload & 1) { /* load VRAM data into pattern/tile... (or INVALID) */
01210             *((AWT*)(pc98_gdc_tiles[0].b+(vramoff&1))) = *((AWT*)(pc98_egc_last_vram[0].b+(vramoff&1)));
01211             *((AWT*)(pc98_gdc_tiles[1].b+(vramoff&1))) = *((AWT*)(pc98_egc_last_vram[1].b+(vramoff&1)));
01212             *((AWT*)(pc98_gdc_tiles[2].b+(vramoff&1))) = *((AWT*)(pc98_egc_last_vram[2].b+(vramoff&1)));
01213             *((AWT*)(pc98_gdc_tiles[3].b+(vramoff&1))) = *((AWT*)(pc98_egc_last_vram[3].b+(vramoff&1)));
01214         }
01215 
01216         /* 0x4A4:
01217          * bits [13:13] = 0=compare lead plane  1=don't
01218          *
01219          * bits [10:10] = read source
01220          *    1 = shifter input is CPU write data
01221          *    0 = shifter input is VRAM data */
01222         if (pc98_egc_compare_lead) {
01223             if (!pc98_egc_shiftinput)
01224                 return *((AWT*)(pc98_egc_src[pc98_egc_lead_plane&3].b));
01225             else
01226                 return *((AWT*)(vga.mem.linear+vramoff+0x08000+((pc98_egc_lead_plane&3)*0x8000)));
01227         }
01228 
01229         return *((AWT*)(vga.mem.linear+fulloff));
01230     }
01231 
01232     template <class AWT> static inline void modeEGC_w(const PhysPt vramoff,const PhysPt fulloff,const AWT val) {
01233         (void)fulloff;//UNUSED
01234 
01235         /* assume: vramoff is even IF AWT is 16-bit wide */
01236 
01237         /* 0x4A4:
01238          * ...
01239          * bits [9:8] = register load (pc98_egc_regload[1:0])
01240          *    11 = invalid
01241          *    10 = load VRAM data before writing on VRAM write
01242          *    01 = load VRAM data into pattern/tile register on VRAM read
01243          *    00 = Do not change pattern/tile register
01244          * ...
01245          * pc98_egc_regload = (val >> 8) & 3;
01246          */
01247         /* Neko Project II: if ((egc.ope & 0x0300) == 0x0200) ... */
01248         if (pc98_egc_regload & 2) { /* load VRAM data before writing on VRAM write (or INVALID) */
01249             *((AWT*)(pc98_gdc_tiles[0].b+(vramoff&1))) = *((AWT*)(vga.mem.linear+vramoff+0x08000));
01250             *((AWT*)(pc98_gdc_tiles[1].b+(vramoff&1))) = *((AWT*)(vga.mem.linear+vramoff+0x10000));
01251             *((AWT*)(pc98_gdc_tiles[2].b+(vramoff&1))) = *((AWT*)(vga.mem.linear+vramoff+0x18000));
01252             *((AWT*)(pc98_gdc_tiles[3].b+(vramoff&1))) = *((AWT*)(vga.mem.linear+vramoff+0x20000));
01253         }
01254 
01255         egc_quad &ropdata = egc_ope<AWT>(vramoff, val);
01256 
01257         const AWT accmask = *((AWT*)(pc98_egc_maskef+(vramoff&1)));
01258 
01259         if (accmask != 0) {
01260             if (!(pc98_egc_access & 1)) {
01261                 *((AWT*)(vga.mem.linear+vramoff+0x08000)) &= ~accmask;
01262                 *((AWT*)(vga.mem.linear+vramoff+0x08000)) |=  accmask & *((AWT*)(ropdata[0].b+(vramoff&1)));
01263             }
01264             if (!(pc98_egc_access & 2)) {
01265                 *((AWT*)(vga.mem.linear+vramoff+0x10000)) &= ~accmask;
01266                 *((AWT*)(vga.mem.linear+vramoff+0x10000)) |=  accmask & *((AWT*)(ropdata[1].b+(vramoff&1)));
01267             }
01268             if (!(pc98_egc_access & 4)) {
01269                 *((AWT*)(vga.mem.linear+vramoff+0x18000)) &= ~accmask;
01270                 *((AWT*)(vga.mem.linear+vramoff+0x18000)) |=  accmask & *((AWT*)(ropdata[2].b+(vramoff&1)));
01271             }
01272             if (!(pc98_egc_access & 8)) {
01273                 *((AWT*)(vga.mem.linear+vramoff+0x20000)) &= ~accmask;
01274                 *((AWT*)(vga.mem.linear+vramoff+0x20000)) |=  accmask & *((AWT*)(ropdata[3].b+(vramoff&1)));
01275             }
01276         }
01277     }
01278 
01279     template <class AWT> AWT readc(PhysPt addr) {
01280         unsigned int vop_offset = 0;
01281 
01282                 addr = PAGING_GetPhysicalAddress(addr);
01283 
01284         check_align<AWT>(addr);
01285 
01286         if ((addr & (~0x1F)) == 0xA3FE0) {
01287             /* 
01288              * 0xA3FE2      MSW1
01289              * 0xA3FE6      MSW2
01290              * 0xA3FEA      MSW3
01291              * 0xA3FEE      MSW4
01292              * 0xA3FF2      MSW5
01293              * 0xA3FF6      MSW6
01294              * 0xA3FFA      MSW7
01295              * 0xA3FFE      MSW8
01296              */
01297             return pc98_mem_msw((addr >> 2) & 7);
01298         }
01299 
01300         if (addr >= 0xE0000) /* the 4th bitplane (EGC 16-color mode) */
01301             addr = (addr & 0x7FFF) + 0x20000;
01302         else
01303             addr &= 0x1FFFF;
01304 
01305         switch (addr>>13) {
01306             case 0:     /* A0000-A1FFF Character RAM */
01307                 return *((AWT*)(vga.mem.linear+addr));
01308             case 1:     /* A2000-A3FFF Attribute RAM */
01309                 if (addr & 1) return (AWT)(~0ull); /* ignore odd bytes */
01310                 return *((AWT*)(vga.mem.linear+addr)) | 0xFF00; /* odd bytes 0xFF */
01311             case 2:     /* A4000-A5FFF Unknown ?? */
01312                 return *((AWT*)(vga.mem.linear+addr));
01313             case 3:     /* A6000-A7FFF Not present */
01314                 return (AWT)(~0ull);
01315             default:    /* A8000-BFFFF G-RAM */
01316                 vop_offset = (pc98_gdc_vramop & (1 << VOPBIT_ACCESS)) ? 0x20000 : 0;
01317                 break;
01318         };
01319 
01320         switch (pc98_gdc_vramop & 0xF) {
01321             case 0x00:
01322             case 0x01:
01323             case 0x02:
01324             case 0x03:
01325             case 0x04:
01326             case 0x05:
01327             case 0x06:
01328             case 0x07:
01329                 return *((AWT*)(vga.mem.linear+addr+vop_offset));
01330             case 0x08: /* TCR/TDW */
01331             case 0x09:
01332                 {
01333                     AWT r = 0;
01334 
01335                     /* this reads multiple bitplanes at once */
01336                     addr &= 0x7FFF;
01337 
01338                     if (!(pc98_gdc_modereg & 1)) // blue channel
01339                         r |= mode8_r<AWT>(/*plane*/0,addr + 0x8000 + vop_offset);
01340 
01341                     if (!(pc98_gdc_modereg & 2)) // red channel
01342                         r |= mode8_r<AWT>(/*plane*/1,addr + 0x10000 + vop_offset);
01343 
01344                     if (!(pc98_gdc_modereg & 4)) // green channel
01345                         r |= mode8_r<AWT>(/*plane*/2,addr + 0x18000 + vop_offset);
01346 
01347                     if (!(pc98_gdc_modereg & 8)) // extended channel
01348                         r |= mode8_r<AWT>(/*plane*/3,addr + 0x20000 + vop_offset);
01349 
01350                     /* NTS: Apparently returning this value correctly really matters to the
01351                      *      sprite engine in "Edge", else visual errors occur. */
01352                     return ~r;
01353                 }
01354             case 0x0C:
01355             case 0x0D:
01356                 return *((AWT*)(vga.mem.linear+addr+vop_offset));
01357             case 0x0A: /* EGC read */
01358             case 0x0B:
01359             case 0x0E:
01360             case 0x0F:
01361                 /* this reads multiple bitplanes at once */
01362                 return modeEGC_r<AWT>((addr&0x7FFF) + vop_offset,addr + vop_offset);
01363             default: /* should not happen */
01364                 LOG_MSG("PC-98 VRAM read warning: Unsupported opmode 0x%X",pc98_gdc_vramop);
01365                 return *((AWT*)(vga.mem.linear+addr+vop_offset));
01366         };
01367 
01368                 return (AWT)(~0ull);
01369         }
01370 
01371         template <class AWT> void writec(PhysPt addr,AWT val){
01372         unsigned int vop_offset = 0;
01373 
01374                 addr = PAGING_GetPhysicalAddress(addr);
01375 
01376         check_align<AWT>(addr);
01377 
01378         if ((addr & (~0x1F)) == 0xA3FE0)
01379             return;
01380 
01381         if (addr >= 0xE0000) /* the 4th bitplane (EGC 16-color mode) */
01382             addr = (addr & 0x7FFF) + 0x20000;
01383         else
01384             addr &= 0x1FFFF;
01385 
01386         switch (addr>>13) {
01387             case 0:     /* A0000-A1FFF Character RAM */
01388                 *((AWT*)(vga.mem.linear+addr)) = val;
01389                 return;
01390             case 1:     /* A2000-A3FFF Attribute RAM */
01391                 if (addr & 1) return; /* ignore odd bytes */
01392                 *((AWT*)(vga.mem.linear+addr)) = val | 0xFF00;
01393                 return;
01394             case 2:     /* A4000-A5FFF Unknown ?? */
01395                 *((AWT*)(vga.mem.linear+addr)) = val;
01396                 return;
01397             case 3:     /* A6000-A7FFF Not present */
01398                 return;
01399             default:    /* A8000-BFFFF G-RAM */
01400                 vop_offset = (pc98_gdc_vramop & (1 << VOPBIT_ACCESS)) ? 0x20000 : 0;
01401                 break;
01402         };
01403 
01404         switch (pc98_gdc_vramop & 0xF) {
01405             case 0x00:
01406             case 0x01:
01407             case 0x02:
01408             case 0x03:
01409             case 0x04:
01410             case 0x05:
01411             case 0x06:
01412             case 0x07:
01413                 *((AWT*)(vga.mem.linear+addr+vop_offset)) = val;
01414                 break;
01415             case 0x08:  /* TCR/TDW write tile data, no masking */
01416             case 0x09:
01417                 {
01418                     /* this writes to multiple bitplanes at once.
01419                      * notice that the value written has no meaning, only the tile data and memory address. */
01420                     addr &= 0x7FFF;
01421 
01422                     if (!(pc98_gdc_modereg & 1)) // blue channel
01423                         mode8_w<AWT>(0/*plane*/,addr + 0x8000 + vop_offset);
01424 
01425                     if (!(pc98_gdc_modereg & 2)) // red channel
01426                         mode8_w<AWT>(1/*plane*/,addr + 0x10000 + vop_offset);
01427 
01428                     if (!(pc98_gdc_modereg & 4)) // green channel
01429                         mode8_w<AWT>(2/*plane*/,addr + 0x18000 + vop_offset);
01430 
01431                     if (!(pc98_gdc_modereg & 8)) // extended channel
01432                         mode8_w<AWT>(3/*plane*/,addr + 0x20000 + vop_offset);
01433                 }
01434                 break;
01435             case 0x0C:  /* read/modify/write from tile with masking */
01436             case 0x0D:  /* a lot of PC-98 games seem to rely on this for sprite rendering */
01437                 {
01438                     const AWT mask = ~val;
01439 
01440                     /* this writes to multiple bitplanes at once */
01441                     addr &= 0x7FFF;
01442 
01443                     if (!(pc98_gdc_modereg & 1)) // blue channel
01444                         modeC_w<AWT>(0/*plane*/,addr + 0x8000 + vop_offset,mask,val);
01445 
01446                     if (!(pc98_gdc_modereg & 2)) // red channel
01447                         modeC_w<AWT>(1/*plane*/,addr + 0x10000 + vop_offset,mask,val);
01448 
01449                     if (!(pc98_gdc_modereg & 4)) // green channel
01450                         modeC_w<AWT>(2/*plane*/,addr + 0x18000 + vop_offset,mask,val);
01451 
01452                     if (!(pc98_gdc_modereg & 8)) // extended channel
01453                         modeC_w<AWT>(3/*plane*/,addr + 0x20000 + vop_offset,mask,val);
01454                 }
01455                 break;
01456             case 0x0A: /* EGC write */
01457             case 0x0B:
01458             case 0x0E:
01459             case 0x0F:
01460                 /* this reads multiple bitplanes at once */
01461                 modeEGC_w<AWT>((addr&0x7FFF) + vop_offset,addr + vop_offset,val);
01462                 break;
01463             default: /* Should no longer happen */
01464                 LOG_MSG("PC-98 VRAM write warning: Unsupported opmode 0x%X",pc98_gdc_vramop);
01465                 *((AWT*)(vga.mem.linear+addr+vop_offset)) = val;
01466                 break;
01467         };
01468         }
01469 
01470     /* byte-wise */
01471         Bitu readb(PhysPt addr) {
01472         return readc<uint8_t>(addr);
01473     }
01474         void writeb(PhysPt addr,Bitu val) {
01475         writec<uint8_t>(addr,(uint8_t)val);
01476     }
01477 
01478     /* word-wise.
01479      * in the style of the 8086, non-word-aligned I/O is split into byte I/O */
01480         Bitu readw(PhysPt addr) {
01481         if (!(addr & 1)) /* if WORD aligned */
01482             return readc<uint16_t>(addr);
01483         else {
01484             return   (unsigned int)readc<uint8_t>(addr+0U) +
01485                     ((unsigned int)readc<uint8_t>(addr+1U) << 8u);
01486         }
01487     }
01488         void writew(PhysPt addr,Bitu val) {
01489         if (!(addr & 1)) /* if WORD aligned */
01490             writec<uint16_t>(addr,(uint16_t)val);
01491         else {
01492             writec<uint8_t>(addr+0,(uint8_t)val);
01493             writec<uint8_t>(addr+1,(uint8_t)(val >> 8U));
01494         }
01495     }
01496 };
01497 
01498 class VGA_Map_Handler : public PageHandler {
01499 public:
01500         VGA_Map_Handler() : PageHandler(PFLAG_READABLE|PFLAG_WRITEABLE|PFLAG_NOCODE) {}
01501         HostPt GetHostReadPt(Bitu phys_page) {
01502                 phys_page-=vgapages.base;
01503                 return &vga.mem.linear[CHECKED3(vga.svga.bank_read_full+phys_page*4096)];
01504         }
01505         HostPt GetHostWritePt(Bitu phys_page) {
01506                 phys_page-=vgapages.base;
01507                 return &vga.mem.linear[CHECKED3(vga.svga.bank_write_full+phys_page*4096)];
01508         }
01509 };
01510 
01511 class VGA_Slow_CGA_Handler : public PageHandler {
01512 public:
01513         VGA_Slow_CGA_Handler() : PageHandler(PFLAG_NOCODE) {}
01514         void delay() {
01515                 Bits delaycyc = CPU_CycleMax/((Bit32u)(1024/2.80)); 
01516                 if(GCC_UNLIKELY(CPU_Cycles < 3*delaycyc)) delaycyc=0;
01517                 CPU_Cycles -= delaycyc;
01518                 CPU_IODelayRemoved += delaycyc;
01519         }
01520 
01521         Bitu readb(PhysPt addr) {
01522                 delay();
01523                 return vga.tandy.mem_base[addr - 0xb8000];
01524         }
01525         void writeb(PhysPt addr,Bitu val){
01526                 delay();
01527                 vga.tandy.mem_base[addr - 0xb8000] = (Bit8u) val;
01528         }
01529         
01530 };
01531 
01532 class VGA_LFB_Handler : public PageHandler {
01533 public:
01534         VGA_LFB_Handler() : PageHandler(PFLAG_READABLE|PFLAG_WRITEABLE|PFLAG_NOCODE) {}
01535         HostPt GetHostReadPt( Bitu phys_page ) {
01536                 phys_page -= vga.lfb.page;
01537                 phys_page &= (vga.mem.memsize >> 12) - 1;
01538                 return &vga.mem.linear[CHECKED3(phys_page * 4096)];
01539         }
01540         HostPt GetHostWritePt( Bitu phys_page ) {
01541                 return GetHostReadPt( phys_page );
01542         }
01543 };
01544 
01545 extern void XGA_Write(Bitu port, Bitu val, Bitu len);
01546 extern Bitu XGA_Read(Bitu port, Bitu len);
01547 
01548 class VGA_MMIO_Handler : public PageHandler {
01549 public:
01550         VGA_MMIO_Handler() : PageHandler(PFLAG_NOCODE) {}
01551         void writeb(PhysPt addr,Bitu val) {
01552                 VGAMEM_USEC_write_delay();
01553                 Bitu port = PAGING_GetPhysicalAddress(addr) & 0xffff;
01554                 XGA_Write(port, val, 1);
01555         }
01556         void writew(PhysPt addr,Bitu val) {
01557                 VGAMEM_USEC_write_delay();
01558                 Bitu port = PAGING_GetPhysicalAddress(addr) & 0xffff;
01559                 XGA_Write(port, val, 2);
01560         }
01561         void writed(PhysPt addr,Bitu val) {
01562                 VGAMEM_USEC_write_delay();
01563                 Bitu port = PAGING_GetPhysicalAddress(addr) & 0xffff;
01564                 XGA_Write(port, val, 4);
01565         }
01566 
01567         Bitu readb(PhysPt addr) {
01568                 VGAMEM_USEC_read_delay();
01569                 Bitu port = PAGING_GetPhysicalAddress(addr) & 0xffff;
01570                 return XGA_Read(port, 1);
01571         }
01572         Bitu readw(PhysPt addr) {
01573                 VGAMEM_USEC_read_delay();
01574                 Bitu port = PAGING_GetPhysicalAddress(addr) & 0xffff;
01575                 return XGA_Read(port, 2);
01576         }
01577         Bitu readd(PhysPt addr) {
01578                 VGAMEM_USEC_read_delay();
01579                 Bitu port = PAGING_GetPhysicalAddress(addr) & 0xffff;
01580                 return XGA_Read(port, 4);
01581         }
01582 };
01583 
01584 class VGA_TANDY_PageHandler : public PageHandler {
01585 public:
01586         VGA_TANDY_PageHandler() : PageHandler(PFLAG_READABLE|PFLAG_WRITEABLE) {}
01587         HostPt GetHostReadPt(Bitu phys_page) {
01588                 // Odd banks are limited to 16kB and repeated
01589                 if (vga.tandy.mem_bank & 1) 
01590                         phys_page&=0x03;
01591                 else 
01592                         phys_page&=0x07;
01593                 return vga.tandy.mem_base + (phys_page * 4096);
01594         }
01595         HostPt GetHostWritePt(Bitu phys_page) {
01596                 return GetHostReadPt( phys_page );
01597         }
01598 };
01599 
01600 
01601 class VGA_PCJR_Handler : public PageHandler {
01602 public:
01603         VGA_PCJR_Handler() : PageHandler(PFLAG_READABLE|PFLAG_WRITEABLE) {}
01604         HostPt GetHostReadPt(Bitu phys_page) {
01605                 phys_page-=0xb8;
01606                 // The 16kB map area is repeated in the 32kB range
01607                 // On CGA CPU A14 is not decoded so it repeats there too
01608                 phys_page&=0x03;
01609                 return vga.tandy.mem_base + (phys_page * 4096);
01610         }
01611         HostPt GetHostWritePt(Bitu phys_page) {
01612                 return GetHostReadPt( phys_page );
01613         }
01614 };
01615 
01616 class VGA_AMS_Handler : public PageHandler {
01617 public:
01618         template< bool wrapping>
01619         void writeHandler(PhysPt start, Bit8u val) {
01620                 vga.tandy.mem_base[ start ] = val;
01621 #ifdef DIJDIJD
01622                 Bit32u data=ModeOperation(val);
01623                 /* Update video memory and the pixel buffer */
01624                 VGA_Latch pixels;
01625                 pixels.d=((Bit32u*)vga.mem.linear)[start];
01626                 pixels.d&=vga.config.full_not_map_mask;
01627                 pixels.d|=(data & vga.config.full_map_mask);
01628                 ((Bit32u*)vga.mem.linear)[start]=pixels.d;
01629                 Bit8u * write_pixels=&vga.mem.linear[VGA_CACHE_OFFSET+(start<<3)];
01630 
01631                 Bit32u colors0_3, colors4_7;
01632                 VGA_Latch temp;temp.d=(pixels.d>>4) & 0x0f0f0f0f;
01633                         colors0_3 = 
01634                         Expand16Table[0][temp.b[0]] |
01635                         Expand16Table[1][temp.b[1]] |
01636                         Expand16Table[2][temp.b[2]] |
01637                         Expand16Table[3][temp.b[3]];
01638                 *(Bit32u *)write_pixels=colors0_3;
01639                 temp.d=pixels.d & 0x0f0f0f0f;
01640                 colors4_7 = 
01641                         Expand16Table[0][temp.b[0]] |
01642                         Expand16Table[1][temp.b[1]] |
01643                         Expand16Table[2][temp.b[2]] |
01644                         Expand16Table[3][temp.b[3]];
01645                 *(Bit32u *)(write_pixels+4)=colors4_7;
01646                 if (wrapping && GCC_UNLIKELY( start < 512)) {
01647                         *(Bit32u *)(write_pixels+512*1024)=colors0_3;
01648                         *(Bit32u *)(write_pixels+512*1024+4)=colors4_7;
01649                 }
01650 #endif
01651         }
01652 //      template< bool wrapping>
01653         Bit8u readHandler(PhysPt start) {
01654                 return vga.tandy.mem_base[ start ];
01655         }
01656 
01657 public:
01658         VGA_AMS_Handler() {
01659                 //flags=PFLAG_READABLE|PFLAG_WRITEABLE;
01660                 flags=PFLAG_NOCODE;
01661         }
01662         inline PhysPt wrAddr( PhysPt addr )
01663         {
01664                 if( vga.mode != M_AMSTRAD )
01665                 {
01666                         addr -= 0xb8000;
01667                         Bitu phys_page = addr >> 12;
01668                         //test for a unaliged bank, then replicate 2x16kb
01669                         if (vga.tandy.mem_bank & 1) 
01670                                 phys_page&=0x03;
01671                         return ( phys_page * 4096 ) + ( addr & 0x0FFF );
01672                 }
01673                 return ( (PAGING_GetPhysicalAddress(addr) & 0xffff) - 0x8000 ) & ( 32*1024-1 );
01674         }
01675 
01676         void writeb(PhysPt addr,Bitu val) {
01677                 VGAMEM_USEC_write_delay();
01678                 addr = wrAddr( addr );
01679                 Bitu plane = vga.mode==M_AMSTRAD ? vga.amstrad.write_plane : 0x01; // 0x0F?
01680                 if( plane & 0x08 ) writeHandler<false>(addr+49152,(Bit8u)(val >> 0));
01681                 if( plane & 0x04 ) writeHandler<false>(addr+32768,(Bit8u)(val >> 0));
01682                 if( plane & 0x02 ) writeHandler<false>(addr+16384,(Bit8u)(val >> 0));
01683                 if( plane & 0x01 ) writeHandler<false>(addr+0,(Bit8u)(val >> 0));
01684         }
01685         void writew(PhysPt addr,Bitu val) {
01686                 VGAMEM_USEC_write_delay();
01687                 addr = wrAddr( addr );
01688                 Bitu plane = vga.mode==M_AMSTRAD ? vga.amstrad.write_plane : 0x01; // 0x0F?
01689                 if( plane & 0x01 )
01690                 {
01691                         writeHandler<false>(addr+0,(Bit8u)(val >> 0));
01692                         writeHandler<false>(addr+1,(Bit8u)(val >> 8));
01693                 }
01694                 addr += 16384;
01695                 if( plane & 0x02 )
01696                 {
01697                         writeHandler<false>(addr+0,(Bit8u)(val >> 0));
01698                         writeHandler<false>(addr+1,(Bit8u)(val >> 8));
01699                 }
01700                 addr += 16384;
01701                 if( plane & 0x04 )
01702                 {
01703                         writeHandler<false>(addr+0,(Bit8u)(val >> 0));
01704                         writeHandler<false>(addr+1,(Bit8u)(val >> 8));
01705                 }
01706                 addr += 16384;
01707                 if( plane & 0x08 )
01708                 {
01709                         writeHandler<false>(addr+0,(Bit8u)(val >> 0));
01710                         writeHandler<false>(addr+1,(Bit8u)(val >> 8));
01711                 }
01712 
01713         }
01714         void writed(PhysPt addr,Bitu val) {
01715                 VGAMEM_USEC_write_delay();
01716                 addr = wrAddr( addr );
01717                 Bitu plane = vga.mode==M_AMSTRAD ? vga.amstrad.write_plane : 0x01; // 0x0F?
01718                 if( plane & 0x01 )
01719                 {
01720                         writeHandler<false>(addr+0,(Bit8u)(val >> 0));
01721                         writeHandler<false>(addr+1,(Bit8u)(val >> 8));
01722                         writeHandler<false>(addr+2,(Bit8u)(val >> 16));
01723                         writeHandler<false>(addr+3,(Bit8u)(val >> 24));
01724                 }
01725                 addr += 16384;
01726                 if( plane & 0x02 )
01727                 {
01728                         writeHandler<false>(addr+0,(Bit8u)(val >> 0));
01729                         writeHandler<false>(addr+1,(Bit8u)(val >> 8));
01730                         writeHandler<false>(addr+2,(Bit8u)(val >> 16));
01731                         writeHandler<false>(addr+3,(Bit8u)(val >> 24));
01732                 }
01733                 addr += 16384;
01734                 if( plane & 0x04 )
01735                 {
01736                         writeHandler<false>(addr+0,(Bit8u)(val >> 0));
01737                         writeHandler<false>(addr+1,(Bit8u)(val >> 8));
01738                         writeHandler<false>(addr+2,(Bit8u)(val >> 16));
01739                         writeHandler<false>(addr+3,(Bit8u)(val >> 24));
01740                 }
01741                 addr += 16384;
01742                 if( plane & 0x08 )
01743                 {
01744                         writeHandler<false>(addr+0,(Bit8u)(val >> 0));
01745                         writeHandler<false>(addr+1,(Bit8u)(val >> 8));
01746                         writeHandler<false>(addr+2,(Bit8u)(val >> 16));
01747                         writeHandler<false>(addr+3,(Bit8u)(val >> 24));
01748                 }
01749 
01750         }
01751         Bitu readb(PhysPt addr) {
01752                 VGAMEM_USEC_read_delay();
01753                 addr = wrAddr( addr ) + ( vga.amstrad.read_plane * 16384u );
01754                 addr &= (64u*1024u-1u);
01755                 return readHandler(addr);
01756         }
01757         Bitu readw(PhysPt addr) {
01758                 VGAMEM_USEC_read_delay();
01759                 addr = wrAddr( addr ) + ( vga.amstrad.read_plane * 16384u );
01760                 addr &= (64u*1024u-1u);
01761                 return 
01762                         ((Bitu)readHandler(addr+0) << 0u) |
01763                         ((Bitu)readHandler(addr+1) << 8u);
01764         }
01765         Bitu readd(PhysPt addr) {
01766                 VGAMEM_USEC_read_delay();
01767                 addr = wrAddr( addr ) + ( vga.amstrad.read_plane * 16384u );
01768                 addr &= (64u*1024u-1u);
01769                 return 
01770                         ((Bitu)readHandler(addr+0) << 0u)  |
01771                         ((Bitu)readHandler(addr+1) << 8u)  |
01772                         ((Bitu)readHandler(addr+2) << 16u) |
01773                         ((Bitu)readHandler(addr+3) << 24u);
01774         }
01775 
01776 /*
01777         HostPt GetHostReadPt(Bitu phys_page)
01778         {
01779                 if( vga.mode!=M_AMSTRAD )
01780                 {
01781                         phys_page-=0xb8;
01782                         //test for a unaliged bank, then replicate 2x16kb
01783                         if (vga.tandy.mem_bank & 1) 
01784                                 phys_page&=0x03;
01785                         return vga.tandy.mem_base + (phys_page * 4096);
01786                 }
01787                 phys_page-=0xb8;
01788                 return vga.tandy.mem_base + (phys_page*4096) + (vga.amstrad.read_plane * 16384) ;
01789         }
01790 */
01791 /*
01792         HostPt GetHostWritePt(Bitu phys_page) {
01793                 return GetHostReadPt( phys_page );
01794         }
01795 */
01796 };
01797 
01798 class VGA_HERC_Handler : public PageHandler {
01799 public:
01800         VGA_HERC_Handler() {
01801                 flags=PFLAG_READABLE|PFLAG_WRITEABLE;
01802         }
01803         HostPt GetHostReadPt(Bitu phys_page) {
01804         (void)phys_page;//UNUSED
01805                 // The 4kB map area is repeated in the 32kB range
01806                 return &vga.mem.linear[0];
01807         }
01808         HostPt GetHostWritePt(Bitu phys_page) {
01809                 return GetHostReadPt( phys_page );
01810         }
01811 };
01812 
01813 class VGA_Empty_Handler : public PageHandler {
01814 public:
01815         VGA_Empty_Handler() : PageHandler(PFLAG_NOCODE) {}
01816         Bitu readb(PhysPt /*addr*/) {
01817 //              LOG(LOG_VGA, LOG_NORMAL ) ( "Read from empty memory space at %x", addr );
01818                 return 0xff;
01819         } 
01820         void writeb(PhysPt /*addr*/,Bitu /*val*/) {
01821 //              LOG(LOG_VGA, LOG_NORMAL ) ( "Write %x to empty memory space at %x", val, addr );
01822         }
01823 };
01824 
01825 static struct vg {
01826         VGA_Map_Handler                         map;
01827         VGA_Slow_CGA_Handler            slow;
01828 //      VGA_TEXT_PageHandler            text;
01829         VGA_CGATEXT_PageHandler         cgatext;
01830         VGA_TANDY_PageHandler           tandy;
01831 //      VGA_ChainedEGA_Handler          cega;
01832 //      VGA_ChainedVGA_Handler          cvga;
01833         VGA_ChainedVGA_Slow_Handler     cvga_slow;
01834 //      VGA_ET4000_ChainedVGA_Handler           cvga_et4000;
01835         VGA_ET4000_ChainedVGA_Slow_Handler      cvga_et4000_slow;
01836 //      VGA_UnchainedEGA_Handler        uega;
01837         VGA_UnchainedVGA_Handler        uvga;
01838         VGA_PCJR_Handler                        pcjr;
01839         VGA_HERC_Handler                        herc;
01840 //      VGA_LIN4_Handler                        lin4;
01841         VGA_LFB_Handler                         lfb;
01842         VGA_MMIO_Handler                        mmio;
01843         VGA_AMS_Handler                         ams;
01844     VGA_PC98_PageHandler        pc98;
01845         VGA_Empty_Handler                       empty;
01846 } vgaph;
01847 
01848 void VGA_ChangedBank(void) {
01849         VGA_SetupHandlers();
01850 }
01851 
01852 void MEM_ResetPageHandler_Unmapped(Bitu phys_page, Bitu pages);
01853 void MEM_ResetPageHandler_RAM(Bitu phys_page, Bitu pages);
01854 
01855 void VGA_SetupHandlers(void) {
01856         vga.svga.bank_read_full = vga.svga.bank_read*vga.svga.bank_size;
01857         vga.svga.bank_write_full = vga.svga.bank_write*vga.svga.bank_size;
01858 
01859         PageHandler *newHandler;
01860         switch (machine) {
01861         case MCH_CGA:
01862                 if (enableCGASnow && (vga.mode == M_TEXT || vga.mode == M_TANDY_TEXT))
01863                         MEM_SetPageHandler( VGA_PAGE_B8, 8, &vgaph.cgatext );
01864                 else
01865                         MEM_SetPageHandler( VGA_PAGE_B8, 8, &vgaph.slow );
01866                 goto range_done;
01867         case MCH_PCJR:
01868                 MEM_SetPageHandler( VGA_PAGE_B8, 8, &vgaph.pcjr );
01869                 goto range_done;
01870         case MCH_HERC:
01871                 vgapages.base=VGA_PAGE_B0;
01872                 /* NTS: Implemented according to [http://www.seasip.info/VintagePC/hercplus.html#regs] */
01873                 if (vga.herc.enable_bits & 0x2) { /* bit 1: page in upper 32KB */
01874                         vgapages.mask=0xffff;
01875                         /* NTS: I don't know what Hercules graphics cards do if you set bit 1 but not bit 0.
01876                          *      For the time being, I'm assuming that they respond to 0xB8000+ because of bit 1
01877                          *      but only map to the first 4KB because of bit 0. Basically, a configuration no
01878                          *      software would actually use. */
01879                         if (vga.herc.enable_bits & 0x1) /* allow graphics and enable 0xB1000-0xB7FFF */
01880                                 MEM_SetPageHandler(VGA_PAGE_B0,16,&vgaph.map);
01881                         else
01882                                 MEM_SetPageHandler(VGA_PAGE_B0,16,&vgaph.herc);
01883                 } else {
01884                         vgapages.mask=0x7fff;
01885                         // With hercules in 32kB mode it leaves a memory hole on 0xb800
01886                         // and has MDA-compatible address wrapping when graphics are disabled
01887                         if (vga.herc.enable_bits & 0x1)
01888                                 MEM_SetPageHandler(VGA_PAGE_B0,8,&vgaph.map);
01889                         else
01890                                 MEM_SetPageHandler(VGA_PAGE_B0,8,&vgaph.herc);
01891                         MEM_SetPageHandler(VGA_PAGE_B8,8,&vgaph.empty);
01892                 }
01893                 goto range_done;
01894         case MCH_TANDY:
01895                 /* Always map 0xa000 - 0xbfff, might overwrite 0xb800 */
01896                 vgapages.base=VGA_PAGE_A0;
01897                 vgapages.mask=0x1ffff;
01898                 MEM_SetPageHandler(VGA_PAGE_A0, 32, &vgaph.map );
01899                 if ( vga.tandy.extended_ram & 1 ) {
01900                         //You seem to be able to also map different 64kb banks, but have to figure that out
01901                         //This seems to work so far though
01902                         vga.tandy.draw_base = vga.mem.linear;
01903                         vga.tandy.mem_base = vga.mem.linear;
01904                 } else {
01905                         vga.tandy.draw_base = TANDY_VIDBASE( vga.tandy.draw_bank * 16 * 1024);
01906                         vga.tandy.mem_base = TANDY_VIDBASE( vga.tandy.mem_bank * 16 * 1024);
01907                         MEM_SetPageHandler( 0xb8, 8, &vgaph.tandy );
01908                 }
01909                 goto range_done;
01910 //              MEM_SetPageHandler(vga.tandy.mem_bank<<2,vga.tandy.is_32k_mode ? 0x08 : 0x04,range_handler);
01911         case MCH_AMSTRAD: // Memory handler.
01912                 MEM_SetPageHandler( 0xb8, 8, &vgaph.ams );
01913                 goto range_done;
01914         case EGAVGA_ARCH_CASE:
01915     case PC98_ARCH_CASE:
01916                 break;
01917         default:
01918                 LOG_MSG("Illegal machine type %d", machine );
01919                 return;
01920         }
01921 
01922         /* This should be vga only */
01923         switch (vga.mode) {
01924         case M_ERROR:
01925         default:
01926                 return;
01927         case M_LIN15:
01928         case M_LIN16:
01929         case M_LIN24:
01930         case M_LIN32:
01931                 newHandler = &vgaph.map;
01932                 break;
01933         case M_TEXT:
01934         case M_CGA2:
01935         case M_CGA4:
01936         /* EGA/VGA emulate CGA modes as chained */
01937         /* fall through */
01938         case M_LIN8:
01939         case M_LIN4:
01940         case M_VGA:
01941         case M_EGA:
01942         if (vga.config.chained) {
01943             if (vga.config.compatible_chain4) {
01944                 /* NTS: ET4000AX cards appear to have a different chain4 implementation from everyone else:
01945                  *      the planar memory byte address is address >> 2 and bits A0-A1 select the plane,
01946                  *      where all other clones I've tested seem to write planar memory byte (address & ~3)
01947                  *      (one byte per 4 bytes) and bits A0-A1 select the plane. */
01948                 /* FIXME: Different chain4 implementation on ET4000 noted---is it true also for ET3000? */
01949                 if (svgaCard == SVGA_TsengET3K || svgaCard == SVGA_TsengET4K)
01950                     newHandler = &vgaph.cvga_et4000_slow;
01951                 else
01952                     newHandler = &vgaph.cvga_slow;
01953             }
01954             else {
01955                 /* this is needed for SVGA modes (Paradise, Tseng, S3) because SVGA
01956                  * modes do NOT use the chain4 configuration */
01957                 newHandler = &vgaph.map;
01958             }
01959         } else {
01960             newHandler = &vgaph.uvga;
01961         }
01962         break;
01963     case M_PC98:
01964                 newHandler = &vgaph.pc98;
01965 
01966         /* We need something to catch access to E0000-E7FFF IF 16/256-color mode */
01967         if (pc98_gdc_vramop & (1 << VOPBIT_ANALOG))
01968             MEM_SetPageHandler(0xE0, 8, newHandler );
01969         else
01970             MEM_ResetPageHandler_Unmapped(0xE0, 8);
01971 
01972         break;
01973         case M_AMSTRAD:
01974                 newHandler = &vgaph.map;
01975                 break;
01976         }
01977         switch ((vga.gfx.miscellaneous >> 2) & 3) {
01978         case 0:
01979         vgapages.base = VGA_PAGE_A0;
01980         switch (svgaCard) {
01981             case SVGA_TsengET3K:
01982             case SVGA_TsengET4K:
01983                 vgapages.mask = 0x1ffff & vga.mem.memmask;
01984                 break;
01985                 /* NTS: Looking at the official ET4000 programming guide, it does in fact support the full 128KB */
01986             case SVGA_S3Trio:
01987             default:
01988                 vgapages.mask = 0xffff & vga.mem.memmask;
01989                 break;
01990                 }
01991                 MEM_SetPageHandler(VGA_PAGE_A0, 32, newHandler );
01992                 break;
01993         case 1:
01994                 vgapages.base = VGA_PAGE_A0;
01995                 vgapages.mask = 0xffff & vga.mem.memmask;
01996                 MEM_SetPageHandler( VGA_PAGE_A0, 16, newHandler );
01997                 MEM_ResetPageHandler_Unmapped( VGA_PAGE_B0, 16);
01998                 break;
01999         case 2:
02000                 vgapages.base = VGA_PAGE_B0;
02001                 vgapages.mask = 0x7fff & vga.mem.memmask;
02002                 MEM_SetPageHandler( VGA_PAGE_B0, 8, newHandler );
02003         MEM_ResetPageHandler_Unmapped( VGA_PAGE_A0, 16 );
02004         MEM_ResetPageHandler_Unmapped( VGA_PAGE_B8, 8 );
02005         break;
02006         case 3:
02007                 vgapages.base = VGA_PAGE_B8;
02008                 vgapages.mask = 0x7fff & vga.mem.memmask;
02009                 MEM_SetPageHandler( VGA_PAGE_B8, 8, newHandler );
02010         MEM_ResetPageHandler_Unmapped( VGA_PAGE_A0, 16 );
02011         MEM_ResetPageHandler_Unmapped( VGA_PAGE_B0, 8 );
02012         break;
02013         }
02014         if(svgaCard == SVGA_S3Trio && (vga.s3.ext_mem_ctrl & 0x10))
02015                 MEM_SetPageHandler(VGA_PAGE_A0, 16, &vgaph.mmio);
02016 
02017     non_cga_ignore_oddeven_engage = (non_cga_ignore_oddeven && !(vga.mode == M_TEXT || vga.mode == M_CGA2 || vga.mode == M_CGA4));
02018 
02019 range_done:
02020         PAGING_ClearTLB();
02021 }
02022 
02023 void VGA_StartUpdateLFB(void) {
02024         /* please obey the Linear Address Window Size register!
02025          * Windows 3.1 S3 driver will reprogram the linear framebuffer down to 0xA0000 when entering a DOSBox
02026          * and assuming the full VRAM size will cause a LOT of problems! */
02027         Bitu winsz = 0x10000;
02028 
02029         switch (vga.s3.reg_58&3) {
02030                 case 1:
02031                         winsz = 1 << 20;        //1MB
02032                         break;
02033                 case 2:
02034                         winsz = 2 << 20;        //2MB
02035                         break;
02036                 case 3:
02037                         winsz = 4 << 20;        //4MB
02038                         break;
02039                 // FIXME: What about the 8MB window?
02040         }
02041 
02042         /* if the DOS application or Windows 3.1 driver attempts to put the linear framebuffer
02043          * below the top of memory, then we're probably entering a DOS VM and it's probably
02044          * a 64KB window. If it's not a 64KB window then print a warning. */
02045         if ((unsigned long)(vga.s3.la_window << 4UL) < (unsigned long)MEM_TotalPages()) {
02046                 if (winsz != 0x10000) // 64KB window normal for entering a DOS VM in Windows 3.1 or legacy bank switching in DOS
02047                         LOG(LOG_MISC,LOG_WARN)("S3 warning: Window size != 64KB and address conflict with system RAM!");
02048 
02049                 vga.lfb.page = (unsigned int)vga.s3.la_window << 4u;
02050                 vga.lfb.addr = (unsigned int)vga.s3.la_window << 16u;
02051                 vga.lfb.handler = NULL;
02052                 MEM_SetLFB(0,0,NULL,NULL);
02053         }
02054         else {
02055                 vga.lfb.page = (unsigned int)vga.s3.la_window << 4u;
02056                 vga.lfb.addr = (unsigned int)vga.s3.la_window << 16u;
02057                 vga.lfb.handler = &vgaph.lfb;
02058                 MEM_SetLFB((unsigned int)vga.s3.la_window << 4u,(unsigned int)vga.mem.memsize/4096u, vga.lfb.handler, &vgaph.mmio);
02059         }
02060 }
02061 
02062 static bool VGA_Memory_ShutDown_init = false;
02063 
02064 static void VGA_Memory_ShutDown(Section * /*sec*/) {
02065         MEM_SetPageHandler(VGA_PAGE_A0,32,&vgaph.empty);
02066         PAGING_ClearTLB();
02067 
02068         if (vga.mem.linear_orgptr != NULL) {
02069                 delete[] vga.mem.linear_orgptr;
02070                 vga.mem.linear_orgptr = NULL;
02071                 vga.mem.linear = NULL;
02072         }
02073 }
02074 
02075 void VGAMEM_LoadState(Section *sec) {
02076     (void)sec;//UNUSED
02077 
02078     if (MemBase != NULL) {
02079         ZIPFileEntry *ent = savestate_zip.get_entry("vga.memory.bin");
02080         if (ent != NULL) {
02081             ent->rewind();
02082             if (vga.mem.memsize == ent->file_length)
02083                 ent->read(vga.mem.linear, vga.mem.memsize);
02084             else
02085                 LOG_MSG("VGA Memory load state failure: VGA Memory size mismatch");
02086         }
02087     }
02088 }
02089 
02090 void VGAMEM_SaveState(Section *sec) {
02091     (void)sec;//UNUSED
02092 
02093     if (vga.mem.linear != NULL) {
02094         ZIPFileEntry *ent = savestate_zip.new_entry("vga.memory.bin");
02095         if (ent != NULL) {
02096             ent->write(vga.mem.linear, vga.mem.memsize);
02097         }
02098     }
02099 }
02100 
02101 void VGA_SetupMemory() {
02102         vga.svga.bank_read = vga.svga.bank_write = 0;
02103         vga.svga.bank_read_full = vga.svga.bank_write_full = 0;
02104 
02105     if (vga.mem.linear == NULL) {
02106         VGA_Memory_ShutDown(NULL);
02107 
02108         vga.mem.linear_orgptr = new Bit8u[vga.mem.memsize+32u];
02109         memset(vga.mem.linear_orgptr,0,vga.mem.memsize+32u);
02110         vga.mem.linear=(Bit8u*)(((uintptr_t)vga.mem.linear_orgptr + 16ull-1ull) & ~(16ull-1ull));
02111 
02112         /* HACK. try to avoid stale pointers */
02113             vga.draw.linear_base = vga.mem.linear;
02114         vga.tandy.draw_base = vga.mem.linear;
02115         vga.tandy.mem_base = vga.mem.linear;
02116 
02117         /* may be related */
02118         VGA_SetupHandlers();
02119     }
02120 
02121         vga.svga.bank_read = vga.svga.bank_write = 0;
02122         vga.svga.bank_read_full = vga.svga.bank_write_full = 0;
02123         vga.svga.bank_size = 0x10000; /* most common bank size is 64K */
02124 
02125         if (!VGA_Memory_ShutDown_init) {
02126         AddVMEventFunction(VM_EVENT_LOAD_STATE,AddVMEventFunctionFuncPair(VGAMEM_LoadState));
02127         AddVMEventFunction(VM_EVENT_SAVE_STATE,AddVMEventFunctionFuncPair(VGAMEM_SaveState));
02128 
02129                 AddExitFunction(AddExitFunctionFuncPair(VGA_Memory_ShutDown));
02130                 VGA_Memory_ShutDown_init = true;
02131         }
02132 
02133         if (machine==MCH_PCJR) {
02134                 /* PCJr does not have dedicated graphics memory but uses
02135                    conventional memory below 128k */
02136                 //TODO map?     
02137         } 
02138 }
02139