// モノクロパレットを少しでも高速処理するための改造
#define MONO_OPTIMIZE2

#define u8 uint8

////////////////////////////////////////////////////////////////////////////////
// GPU
////////////////////////////////////////////////////////////////////////////////
//
//
//
//
// 7.04.2002: Fixed sprites order
//
//
//
//
//
//
////////////////////////////////////////////////////////////////////////////////

#include <windows.h>

//#include <time.h>
//#include "log.h"
#include "rom.h"
#include "./nec/nec.h"
#include "io.h"
#include "gpu.h"


#ifdef GPU_ALIGN
#define WS_HEIGHT   256
#define WS_WIDTH    256
#else
#define WS_HEIGHT   224
#define WS_WIDTH    144
#endif


////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
#if 0
extern uint8	*internalRam;
#else
extern uint8	internalRam[0x10000];
#endif

////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
#define RGB555(R,G,B) ((((int)(R))<<10)|(((int)(G))<<5)|((int)(B)))

uint8	ws_gpu_operatingInColor;
uint8	ws_videoMode;
uint8	ws_gpu_scanline=0;

int16	ws_palette[16*4];

//int8	ws_paletteColors[8];
int16	wsc_palette[16*16];
int16	ws_shades[16];

int		ws_gpu_forceColorSystemBool=0;
int		ws_gpu_forceMonoSystemBool=0;
int16   wsMC[8]={0,0,0,0,0,0,0,0};
int16   wsMC64[64];

#if 0
uint8	*ws_tile_cache;
uint8	*ws_hflipped_tile_cache;

uint8	*wsc_tile_cache;
uint8	*wsc_hflipped_tile_cache;

uint8	*ws_modified_tile;
uint8	*wsc_modified_tile;
#else
uint8 ws_tile_cache[512*8*8];
uint8 wsc_tile_cache[1024*8*8];
uint8 ws_hflipped_tile_cache[512*8*8];
uint8 wsc_hflipped_tile_cache[1024*8*8];
uint8 ws_modified_tile[512];
uint8 wsc_modified_tile[1024];
#endif



////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
void ws_gpu_set_colour_scheme(int scheme)
{
    int r,g,b,i;
    int R,G,B;
    
    switch(scheme) {
      case COLOUR_SCHEME_AMBER: r=100; g= 61; b=  0;  break;
      case COLOUR_SCHEME_GREEN: r= 20; g= 90; b= 20;  break;
      default:                  r=g=b=100;            break;
    }
    
    for(i=0;i<16;i++) {
        R = ((30-i*2) * r)/100;
        G = ((30-i*2) * g)/100;
        B = ((30-i*2) * b)/100;
        ws_shades[i] = RGB555(R,G,B);
    }
}


////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
void ws_gpu_forceColorSystem(void)
{
	ws_gpu_forceColorSystemBool=1;
	ws_gpu_forceMonoSystemBool=0;
	ws_gpu_operatingInColor=1;
}


////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
void ws_gpu_forceMonoSystem(void)
{
	ws_gpu_forceColorSystemBool=0;
	ws_gpu_forceMonoSystemBool=1;
	ws_gpu_operatingInColor=0;
}


////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
void ws_gpu_init(void)
{
#if 0
	ws_tile_cache				= (uint8*)malloc(512*8*8);
	wsc_tile_cache				= (uint8*)malloc(1024*8*8);

	ws_hflipped_tile_cache		= (uint8*)malloc(512*8*8);
	wsc_hflipped_tile_cache		= (uint8*)malloc(1024*8*8);

	ws_modified_tile			= (uint8*)malloc(512);
	wsc_modified_tile			= (uint8*)malloc(1024);
#endif

	memset(ws_tile_cache,0x00,512*8*8);
	memset(wsc_tile_cache,0x00,1024*8*8);

	memset(ws_hflipped_tile_cache,0x00,512*8*8);
	memset(wsc_hflipped_tile_cache,0x00,1024*8*8);

	memset(ws_modified_tile,0x01,512);
	memset(wsc_modified_tile,0x01,1024);

	ws_gpu_forceColorSystemBool=0;
	ws_gpu_forceMonoSystemBool=0;
}


////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
void ws_gpu_done(void)
{
#if 0
	free(ws_tile_cache);
	free(wsc_tile_cache);
	free(ws_hflipped_tile_cache);
	free(wsc_hflipped_tile_cache);
	free(ws_modified_tile);
	free(wsc_modified_tile);
#endif
}


////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
void ws_gpu_changeVideoMode(uint8 value)
{
	if (ws_videoMode!=(value>>5))
	{
		ws_videoMode=value>>5;
		if (ws_videoMode==4)
			ws_videoMode=2;
		
		// mark all tiles dirty
		memset(wsc_modified_tile,0x01,1024);
		memset(ws_modified_tile,0x01,512);
	}
}


////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
void ws_gpu_reset(void)
{
	memset(wsc_modified_tile,0x01,1024);
	memset(ws_modified_tile,0x01,512);
	ws_gpu_scanline=0;
	ws_gpu_changeVideoMode(2);
}


////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
void ws_gpu_clearCache(void)
{
	memset(wsc_modified_tile,0x01,1024);
	memset(ws_modified_tile,0x01,512);
}


////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
uint8 *ws_tileCache_getTileRow(uint32 tileIndex, uint32 line, 
							   uint32 vFlip, uint32 hFlip, uint32 bank)
{
    int i;
    
    if (ws_gpu_operatingInColor) {

        tileIndex += (bank)?512:0;
        
        // need to update tile cache ?
        // 4 colors tiles
        if ((ws_videoMode==2)&&(ws_modified_tile[tileIndex])) {
			uint8	*TICP   = &wsc_tile_cache[tileIndex<<6];
			uint8	*hfTICP = &wsc_hflipped_tile_cache[tileIndex<<6];
			uint16	*tIRP   = (uint16*)&internalRam[0x2000+(tileIndex<<4)];
			uint16	tL;

			for (i=0;i<8;i++) {
				tL=*tIRP++;
#if defined(HARDWRAP)
                uint32 tLP;
                tLP = tL & 0x8080; hfTICP[7] = TICP[0] = ((u8)(tLP>>7))|((u8)(tLP>>14));
				tLP = tL & 0x4040; hfTICP[6] = TICP[1] = ((u8)(tLP>>6))|((u8)(tLP>>13));
				tLP = tL & 0x2020; hfTICP[5] = TICP[2] = ((u8)(tLP>>5))|((u8)(tLP>>12));
				tLP = tL & 0x1010; hfTICP[4] = TICP[3] = ((u8)(tLP>>4))|((u8)(tLP>>11));
				tLP = tL & 0x0808; hfTICP[3] = TICP[4] = ((u8)(tLP>>3))|((u8)(tLP>>10));
				tLP = tL & 0x0404; hfTICP[2] = TICP[5] = ((u8)(tLP>>2))|((u8)(tLP>> 9));
				tLP = tL & 0x0202; hfTICP[1] = TICP[6] = ((u8)(tLP>>1))|((u8)(tLP>> 8));
                tLP = tL & 0x0101; hfTICP[0] = TICP[7] = ((u8)(tLP   ))|((u8)(tLP>> 7));
#else
                hfTICP[7] = TICP[0] = ((tL>>7)&1)|((tL>>14)&2);
				hfTICP[6] = TICP[1] = ((tL>>6)&1)|((tL>>13)&2);
				hfTICP[5] = TICP[2] = ((tL>>5)&1)|((tL>>12)&2);
				hfTICP[4] = TICP[3] = ((tL>>4)&1)|((tL>>11)&2);
				hfTICP[3] = TICP[4] = ((tL>>3)&1)|((tL>>10)&2);
				hfTICP[2] = TICP[5] = ((tL>>2)&1)|((tL>> 9)&2);
				hfTICP[1] = TICP[6] = ((tL>>1)&1)|((tL>> 8)&2);
                hfTICP[0] = TICP[7] = ((tL   )&1)|((tL>> 7)&2);
#endif
                TICP+=8;
				hfTICP+=8;
			}				
			ws_modified_tile[tileIndex]=0;
		}
		else if (wsc_modified_tile[tileIndex]) {
            // 16 colors by tile layered mode
            if (ws_videoMode==6) {
                uint8	*TICP	= &wsc_tile_cache[tileIndex<<6];
                uint8	*hfTICP = &wsc_hflipped_tile_cache[tileIndex<<6];
                uint32	*tIRP	= (uint32*)&internalRam[0x4000+(tileIndex<<5)];
                uint32	tL;

                for (i=0;i<8;i++) {
                    tL=*tIRP++;

#if defined(HARDWRAP)
                    uint32 tLP;
                    tLP = tL & 0x80808080; hfTICP[7] = TICP[0] = ((u8)(tLP>>7))|((u8)(tLP>>14))|((u8)(tLP>>21))|((u8)(tLP>>28));
                    tLP = tL & 0x40404040; hfTICP[6] = TICP[1] = ((u8)(tLP>>6))|((u8)(tLP>>13))|((u8)(tLP>>20))|((u8)(tLP>>27));
                    tLP = tL & 0x20202020; hfTICP[5] = TICP[2] = ((u8)(tLP>>5))|((u8)(tLP>>12))|((u8)(tLP>>19))|((u8)(tLP>>26));
                    tLP = tL & 0x10101010; hfTICP[4] = TICP[3] = ((u8)(tLP>>4))|((u8)(tLP>>11))|((u8)(tLP>>18))|((u8)(tLP>>25));
                    tLP = tL & 0x08080808; hfTICP[3] = TICP[4] = ((u8)(tLP>>3))|((u8)(tLP>>10))|((u8)(tLP>>17))|((u8)(tLP>>24));
                    tLP = tL & 0x04040404; hfTICP[2] = TICP[5] = ((u8)(tLP>>2))|((u8)(tLP>> 9))|((u8)(tLP>>16))|((u8)(tLP>>23));
                    tLP = tL & 0x02020202; hfTICP[1] = TICP[6] = ((u8)(tLP>>1))|((u8)(tLP>> 8))|((u8)(tLP>>15))|((u8)(tLP>>22));
                    tLP = tL & 0x01010101; hfTICP[0] = TICP[7] = ((u8)(tLP   ))|((u8)(tLP>> 7))|((u8)(tLP>>14))|((u8)(tLP>>21));
#else
                    hfTICP[7] = TICP[0] = ((tL>>7)&1) | ((tL>>14)&2) | ((tL>>21)&4) | ((tL>>28)&8);
                    hfTICP[6] = TICP[1] = ((tL>>6)&1) | ((tL>>13)&2) | ((tL>>20)&4) | ((tL>>27)&8);
                    hfTICP[5] = TICP[2] = ((tL>>5)&1) | ((tL>>12)&2) | ((tL>>19)&4) | ((tL>>26)&8);
                    hfTICP[4] = TICP[3] = ((tL>>4)&1) | ((tL>>11)&2) | ((tL>>18)&4) | ((tL>>25)&8);
                    hfTICP[3] = TICP[4] = ((tL>>3)&1) | ((tL>>10)&2) | ((tL>>17)&4) | ((tL>>24)&8);
                    hfTICP[2] = TICP[5] = ((tL>>2)&1) | ((tL>> 9)&2) | ((tL>>16)&4) | ((tL>>23)&8);
                    hfTICP[1] = TICP[6] = ((tL>>1)&1) | ((tL>> 8)&2) | ((tL>>15)&4) | ((tL>>22)&8);
                    hfTICP[0] = TICP[7] = ((tL>>0)&1) | ((tL>> 7)&2) | ((tL>>14)&4) | ((tL>>21)&8);
#endif
					TICP+=8;
					hfTICP+=8;
				}
			}
			else
			// 16 colors by tile packed mode
			if (ws_videoMode==7)
			{
				uint8	*TICP = &wsc_tile_cache[tileIndex<<6];
				uint8	*hfTICP = &wsc_hflipped_tile_cache[tileIndex<<6];
				uint32	*tIRP   = (uint32*)&internalRam[0x4000+(tileIndex<<5)];
				uint32	tL;

				for (i=0;i<8;i++) {
					tL=*tIRP++;
					hfTICP[7] = TICP[0] = (tL>> 4)&0x0f;
					hfTICP[6] = TICP[1] = (tL    )&0x0f;
					hfTICP[5] = TICP[2] = (tL>>12)&0x0f;
					hfTICP[4] = TICP[3] = (tL>> 8)&0x0f;
					hfTICP[3] = TICP[4] = (tL>>20)&0x0f;
					hfTICP[2] = TICP[5] = (tL>>16)&0x0f;
					hfTICP[1] = TICP[6] = (tL>>28)     ;
                    hfTICP[0] = TICP[7] = (tL>>24)&0x0f;
					TICP+=8;
					hfTICP+=8;
					
				}				
			}
			else
			{
				// unknown mode 
			}
			// tile cache updated
			wsc_modified_tile[tileIndex]=0;
		}	
		if (vFlip)
			line=7-line;
		if (hFlip)
			return(&wsc_hflipped_tile_cache[(tileIndex<<6)+(line<<3)]);
		else
			return(&wsc_tile_cache[(tileIndex<<6)+(line<<3)]);
	
	} else {
		// need to update tile cache ?
        if (ws_modified_tile[tileIndex]) {
			uint8  *TICP    = &ws_tile_cache[tileIndex<<6];
			uint8  *hfTICP  = &ws_hflipped_tile_cache[(tileIndex<<6)];
            uint32 *tIRP    = (uint32*)&internalRam[0x2000+(tileIndex<<4)];
			uint32 tL;

			for (i=0;i<4;i++) {
				tL=*tIRP++;
#if defined(HARDWRAP)
                uint32 tLP;
                tLP = tL & 0x8080; hfTICP[7] = TICP[0] = ((u8)(tLP>> 7))|((u8)(tLP>>14));
                tLP = tL & 0x4040; hfTICP[6] = TICP[1] = ((u8)(tLP>> 6))|((u8)(tLP>>13));
                tLP = tL & 0x2020; hfTICP[5] = TICP[2] = ((u8)(tLP>> 5))|((u8)(tLP>>12));
                tLP = tL & 0x1010; hfTICP[4] = TICP[3] = ((u8)(tLP>> 4))|((u8)(tLP>>11));
                tLP = tL & 0x0808; hfTICP[3] = TICP[4] = ((u8)(tLP>> 3))|((u8)(tLP>>10));
                tLP = tL & 0x0404; hfTICP[2] = TICP[5] = ((u8)(tLP>> 2))|((u8)(tLP>> 9));
                tLP = tL & 0x0202; hfTICP[1] = TICP[6] = ((u8)(tLP>> 1))|((u8)(tLP>> 8));
                tLP = tL & 0x0101; hfTICP[0] = TICP[7] = ((u8)(tLP    ))|((u8)(tLP>> 7));
                hfTICP+=8;  TICP+=8;    tL>>=16;
                tLP = tL & 0x8080; hfTICP[7] = TICP[0] = ((u8)(tLP>> 7))|((u8)(tLP>>14));
                tLP = tL & 0x4040; hfTICP[6] = TICP[1] = ((u8)(tLP>> 6))|((u8)(tLP>>13));
                tLP = tL & 0x2020; hfTICP[5] = TICP[2] = ((u8)(tLP>> 5))|((u8)(tLP>>12));
                tLP = tL & 0x1010; hfTICP[4] = TICP[3] = ((u8)(tLP>> 4))|((u8)(tLP>>11));
                tLP = tL & 0x0808; hfTICP[3] = TICP[4] = ((u8)(tLP>> 3))|((u8)(tLP>>10));
                tLP = tL & 0x0404; hfTICP[2] = TICP[5] = ((u8)(tLP>> 2))|((u8)(tLP>> 9));
                tLP = tL & 0x0202; hfTICP[1] = TICP[6] = ((u8)(tLP>> 1))|((u8)(tLP>> 8));
                tLP = tL & 0x0101; hfTICP[0] = TICP[7] = ((u8)(tLP    ))|((u8)(tLP>> 7));
                hfTICP+=8;  TICP+=8;
#else
                hfTICP[7] = TICP[0] = ((tL>> 7)&1)|((tL>>14)&2);
				hfTICP[6] = TICP[1] = ((tL>> 6)&1)|((tL>>13)&2);
				hfTICP[5] = TICP[2] = ((tL>> 5)&1)|((tL>>12)&2);
				hfTICP[4] = TICP[3] = ((tL>> 4)&1)|((tL>>11)&2);
				hfTICP[3] = TICP[4] = ((tL>> 3)&1)|((tL>>10)&2);
				hfTICP[2] = TICP[5] = ((tL>> 2)&1)|((tL>> 9)&2);
				hfTICP[1] = TICP[6] = ((tL>> 1)&1)|((tL>> 8)&2);
				hfTICP[0] = TICP[7] = ((tL    )&1)|((tL>> 7)&2);
                hfTICP+=8;  TICP+=8;    tL>>=16;
				hfTICP[7] = TICP[0] = ((tL>> 7)&1)|((tL>>14)&2);
				hfTICP[6] = TICP[1] = ((tL>> 6)&1)|((tL>>13)&2);
				hfTICP[5] = TICP[2] = ((tL>> 5)&1)|((tL>>12)&2);
				hfTICP[4] = TICP[3] = ((tL>> 4)&1)|((tL>>11)&2);
				hfTICP[3] = TICP[4] = ((tL>> 3)&1)|((tL>>10)&2);
				hfTICP[2] = TICP[5] = ((tL>> 2)&1)|((tL>> 9)&2);
				hfTICP[1] = TICP[6] = ((tL>> 1)&1)|((tL>> 8)&2);
				hfTICP[0] = TICP[7] = ((tL    )&1)|((tL>> 7)&2);
				hfTICP+=8;  TICP+=8;
#endif
            }				
			// tile cache updated
			ws_modified_tile[tileIndex]=0;
		}
		if (vFlip)
			line=7-line;
		if (hFlip)
			return (&ws_hflipped_tile_cache[(tileIndex<<6)+(line<<3)]);
		else
			return (&ws_tile_cache[(tileIndex<<6)+(line<<3)]);
	}
	return(NULL);
}


////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
void ws_drawClippedSpriteLine(int16 *framebuffer, uint16 scanline,
							  uint32 x, uint32 y, uint32 tileIndex, uint32 paletteIndex,
							  uint32 vFlip, uint32 hFlip,
							  uint32 clip_x0, uint32 clip_y0, uint32 clip_x1, uint32 clip_y1)
{
    if ((scanline<y)||(scanline>(y+7))) return;
    if ((x+7<clip_x0)||(x>=clip_x1))    return;
    if ((y+7<clip_y0)||(y>=clip_y1))    return;
    
    uint8	*wsTR=ws_tileCache_getTileRow(tileIndex,(scanline-y)&0x07,hFlip,vFlip, 0);
    uint16	 nbPixels=8;

    if (x<clip_x0) {
        wsTR+=clip_x0-x;
        nbPixels-=clip_x0-x;
        x=clip_x0;
    }
    
    if (x+nbPixels>clip_x1) {
        nbPixels=(clip_x1-x);
    }
    
	framebuffer+=x;

	if (ws_gpu_operatingInColor) {
        
        int16 *wsc_palA = &wsc_palette[paletteIndex<<4];
        
        while (nbPixels) {
            if (*wsTR) {
                *framebuffer=wsc_palA[*wsTR];
            }
            framebuffer++;
            wsTR++;
            nbPixels--;
		}
	}
    else {
#if defined(MONO_OPTIMIZE2)
        int16 *wsMono = &wsMC64[paletteIndex<<2];

        if (paletteIndex&0x04) {
            while (nbPixels) {
                if (*wsTR) *framebuffer=wsMono[*wsTR]; 
                framebuffer++;	wsTR++;
				nbPixels--;
			}
		} else {
            while (nbPixels) {
                *framebuffer=wsMono[*wsTR]; 
                framebuffer++;	wsTR++;
                nbPixels--;
            }
        }
#else
		int16 *wsPA=&ws_palette[paletteIndex<<2];

        if (paletteIndex&0x04) {
            while (nbPixels) {
                if (*wsTR) *framebuffer=wsMC[wsPA[*wsTR]]; 
                framebuffer++;	wsTR++;
				nbPixels--;
			}
		} else {
            while (nbPixels) {
                *framebuffer=wsMC[wsPA[*wsTR]]; 
                framebuffer++;	wsTR++;
                nbPixels--;
            }
        }
#endif
    }
}

#define BG_OPTIMIZE

////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
void ws_gpu_renderScanline(int16 *framebuffer)
{
	int i;
    int nbTiles;
    
    if (ws_gpu_scanline>143) return;
    
    framebuffer+=(WS_WIDTH*ws_gpu_scanline);
    
#if !defined(BG_OPTIMIZE)
    // fill with background color
    int16 backgroundColor;

    if (ws_gpu_operatingInColor) {
        backgroundColor=wsc_palette[ws_ioRam[0x01]];
    } else {
#if defined(MONO_OPTIMIZE2)
        backgroundColor=wsMC64[((ws_ioRam[0x01]&0xf0)>>2)+(ws_ioRam[0x01]&0x03)];
#else
        backgroundColor=wsMC[ws_palette[((ws_ioRam[0x01]&0xf0)>>2)+(ws_ioRam[0x01]&0x03)]];
#endif
    }
    
    for(i=0;i<224;i++) {
        framebuffer[i]=backgroundColor;
    }
#endif
    
    // render background layer
    if (ws_ioRam[0x00]&0x01) {
        int ws_bgScroll_x=ws_ioRam[0x10];
        int ws_bgScroll_y=ws_ioRam[0x11];
		
		// seek to the first tile
		ws_bgScroll_y=(ws_bgScroll_y+ws_gpu_scanline)&0xff;

		// note: byte ordering assumptions!
		int	ws_currentTile=(ws_bgScroll_x>>3);
        uint16	*ws_bgScrollRamBase=(uint16*)(internalRam+(((uint32)ws_ioRam[0x07]&0x0f)<<11)+
                                              ((ws_bgScroll_y&0xfff8)<<3));
        
        int	lineInTile   = ws_bgScroll_y&0x07;
        int columnInTile = ws_bgScroll_x&0x07;
        
        int16 *scanlinePtr=framebuffer;
        
        if (ws_gpu_operatingInColor) {
            // render the first clipped tile
            if (columnInTile) {
                uint16	tileInfo=ws_bgScrollRamBase[ws_currentTile&0x1f];
                ws_currentTile++;
                uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile, tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);
                int16	*wsc_paletteAlias=&wsc_palette[((tileInfo>>9)&0x0f)<<4];
                wsTR+=columnInTile;
                
                for (i=columnInTile;i<8;i++) {
#if defined(BG_OPTIMIZE)
                    *scanlinePtr=wsc_paletteAlias[*wsTR];
#else // BG_OPTIMIZE
                    if(*wsTR) {
                        *scanlinePtr=wsc_paletteAlias[*wsTR];
                    }
#endif// BG_OPTIMIZE
                    scanlinePtr++; 
                    wsTR++;
                }
            }
            
            // render the tiles between them
             nbTiles=28;
            if (columnInTile) {
                nbTiles=27;
            }
            
            for (i=0;i<nbTiles;i++) {
                uint16	tileInfo=ws_bgScrollRamBase[ws_currentTile&0x1f];
                ws_currentTile++;
                uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile, tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);
                int16	*wsc_paletteAlias=&wsc_palette[((tileInfo>>9)&0x0f)<<4];
                
#if defined(BG_OPTIMIZE)
                scanlinePtr[0]=wsc_paletteAlias[wsTR[0]];
                scanlinePtr[1]=wsc_paletteAlias[wsTR[1]];
                scanlinePtr[2]=wsc_paletteAlias[wsTR[2]];
				scanlinePtr[3]=wsc_paletteAlias[wsTR[3]];
                scanlinePtr[4]=wsc_paletteAlias[wsTR[4]];
                scanlinePtr[5]=wsc_paletteAlias[wsTR[5]];
                scanlinePtr[6]=wsc_paletteAlias[wsTR[6]];
                scanlinePtr[7]=wsc_paletteAlias[wsTR[7]];
#else//     BG_OPTIMIZE
                if(wsTR[0]) scanlinePtr[0]=wsc_paletteAlias[wsTR[0]];
				if(wsTR[1]) scanlinePtr[1]=wsc_paletteAlias[wsTR[1]];
				if(wsTR[2]) scanlinePtr[2]=wsc_paletteAlias[wsTR[2]];
				if(wsTR[3]) scanlinePtr[3]=wsc_paletteAlias[wsTR[3]];
				if(wsTR[4]) scanlinePtr[4]=wsc_paletteAlias[wsTR[4]];
				if(wsTR[5]) scanlinePtr[5]=wsc_paletteAlias[wsTR[5]];
				if(wsTR[6]) scanlinePtr[6]=wsc_paletteAlias[wsTR[6]];
				if(wsTR[7]) scanlinePtr[7]=wsc_paletteAlias[wsTR[7]];
#endif//    BG_OPTIMIZE
                scanlinePtr+=8;
                wsTR+=8;
            }

			// render the last clipped tile
            if (columnInTile) {
                uint16	tileInfo=ws_bgScrollRamBase[ws_currentTile&0x1f];
                ws_currentTile++;
                uint8 *wsTR=ws_tileCache_getTileRow( tileInfo&0x1ff, lineInTile,
                                                     tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);
                int16 *wsc_paletteAlias=&wsc_palette[((tileInfo>>9)&0x0f)<<4];
                
                for (i=0;i<columnInTile;i++) {
#if defined(BG_OPTIMIZE)
                    *scanlinePtr=wsc_paletteAlias[*wsTR];
#else//     BG_OPTIMIZE
                    if (*wsTR) {
                        *scanlinePtr=wsc_paletteAlias[*wsTR];
                    }
#endif//    BG_OPTIMIZE
                    scanlinePtr++; 
                    wsTR++;
				}
			}
		}
		else
		{
			// render the first clipped tile
            if (columnInTile) {
                uint16	tileInfo=ws_bgScrollRamBase[ws_currentTile&0x1f];
                ws_currentTile++;
                uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile,
                                                        tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);

#if defined(MONO_OPTIMIZE2)
                int16 *wsMono=&wsMC64[((tileInfo>>9)&0x0f)<<2];
                wsTR+=columnInTile;

				if ((tileInfo>>9)&0x04) {
					for (i=columnInTile;i<8;i++) {
                        *scanlinePtr=wsMono[*wsTR];
                        scanlinePtr++; 
						wsTR++;
					}
				} else {
					for (i=columnInTile;i<8;i++) {
                        *scanlinePtr=wsMono[*wsTR];
                        scanlinePtr++; 
                        wsTR++;
                    }
                }
#else//MONO_OPTIMIZE2
                int16	*wsPA=&ws_palette[((tileInfo>>9)&0x0f)<<2];
                wsTR+=columnInTile;

				if ((tileInfo>>9)&0x04) {
					for (i=columnInTile;i<8;i++) {
#if defined(BG_OPTIMIZE)
                        *scanlinePtr=wsMC[wsPA[*wsTR]];
#else//     BG_OPTIMIZE
                        if (*wsTR) {
                            *scanlinePtr=wsMC[wsPA[*wsTR]];
                        }
#endif//    BG_OPTIMIZE
                        scanlinePtr++; 
						wsTR++;
					}
				} else {
					for (i=columnInTile;i<8;i++) {
                        *scanlinePtr=wsMC[wsPA[*wsTR]]; 
                        scanlinePtr++; 
                        wsTR++;
                    }
                }
#endif//MONO_OPTIMIZE2
            }
            
            nbTiles=28;
            if (columnInTile) {
                nbTiles=27;
            }
			
			for (i=0;i<nbTiles;i++) {
                uint16	tileInfo=ws_bgScrollRamBase[ws_currentTile&0x1f];
                ws_currentTile++;
                uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile,
                                                        tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);

#if defined(MONO_OPTIMIZE2)
                int16 *wsMono=&wsMC64[((tileInfo>>9)&0x0f)<<2];
                
                scanlinePtr[0]=wsMono[wsTR[0]];
                scanlinePtr[1]=wsMono[wsTR[1]];
                scanlinePtr[2]=wsMono[wsTR[2]];
                scanlinePtr[3]=wsMono[wsTR[3]];
                scanlinePtr[4]=wsMono[wsTR[4]];
                scanlinePtr[5]=wsMono[wsTR[5]];
                scanlinePtr[6]=wsMono[wsTR[6]];
                scanlinePtr[7]=wsMono[wsTR[7]];
                scanlinePtr+=8;
                wsTR+=8;


#else// MONO_OPTIMIZE2
				int16	*wsPA=&ws_palette[((tileInfo>>9)&0x0f)<<2];
                
#if defined(BG_OPTIMIZE)
                scanlinePtr[0]=wsMC[wsPA[wsTR[0]]];
                scanlinePtr[1]=wsMC[wsPA[wsTR[1]]];
                scanlinePtr[2]=wsMC[wsPA[wsTR[2]]];
                scanlinePtr[3]=wsMC[wsPA[wsTR[3]]];
                scanlinePtr[4]=wsMC[wsPA[wsTR[4]]];
                scanlinePtr[5]=wsMC[wsPA[wsTR[5]]];
                scanlinePtr[6]=wsMC[wsPA[wsTR[6]]];
                scanlinePtr[7]=wsMC[wsPA[wsTR[7]]];
                scanlinePtr+=8;
                wsTR+=8;
#else//     BG_OPTIMIZE
                if ((tileInfo>>9)&0x04) {
                    if(wsTR[0]) scanlinePtr[0]=wsMC[wsPA[wsTR[0]]];
                    if(wsTR[1]) scanlinePtr[1]=wsMC[wsPA[wsTR[1]]];
                    if(wsTR[2]) scanlinePtr[2]=wsMC[wsPA[wsTR[2]]];
                    if(wsTR[3]) scanlinePtr[3]=wsMC[wsPA[wsTR[3]]];
					if(wsTR[4]) scanlinePtr[4]=wsMC[wsPA[wsTR[4]]];
                    if(wsTR[5]) scanlinePtr[5]=wsMC[wsPA[wsTR[5]]];
					if(wsTR[6]) scanlinePtr[6]=wsMC[wsPA[wsTR[6]]];
					if(wsTR[7]) scanlinePtr[7]=wsMC[wsPA[wsTR[7]]];
                    scanlinePtr+=8;
                    wsTR+=8;
				} else {
					scanlinePtr[0]=wsMC[wsPA[wsTR[0]]];
					scanlinePtr[1]=wsMC[wsPA[wsTR[1]]];
					scanlinePtr[2]=wsMC[wsPA[wsTR[2]]];
					scanlinePtr[3]=wsMC[wsPA[wsTR[3]]];
					scanlinePtr[4]=wsMC[wsPA[wsTR[4]]];
					scanlinePtr[5]=wsMC[wsPA[wsTR[5]]];
					scanlinePtr[6]=wsMC[wsPA[wsTR[6]]];
					scanlinePtr[7]=wsMC[wsPA[wsTR[7]]];
                    scanlinePtr+=8;
                    wsTR+=8;
                }
#endif//    BG_OPTIMIZE
#endif// MONO_OPTIMIZE2
                
            }

			// render the last clipped tile
			if (columnInTile) {
				uint16	tileInfo=ws_bgScrollRamBase[ws_currentTile&0x1f];
				ws_currentTile++;
				uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile,
													tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);

#if defined(MONO_OPTIMIZE2)
                int16	*wsMono=&wsMC64[((tileInfo>>9)&0x0f)<<2];
				
                for (i=0;i<columnInTile;i++) {
                    *scanlinePtr=wsMono[*wsTR];
                    scanlinePtr++; 
                    wsTR++;
                }
#else//MONO_OPTIMIZE
                int16	*wsPA=&ws_palette[((tileInfo>>9)&0x0f)<<2];
				
#if defined(BG_OPTIMIZE)
                for (i=0;i<columnInTile;i++) {
                    *scanlinePtr=wsMC[wsPA[*wsTR]];
                    scanlinePtr++; 
                    wsTR++;
                }
#else //   (BG_OPTIMIZE)
                if ((tileInfo>>9)&0x04) {
                    for (i=0;i<columnInTile;i++) {
                        if (*wsTR) {
                            *scanlinePtr=wsMC[wsPA[*wsTR]]; 
                        }
                        scanlinePtr++; 
                        wsTR++;
                    }
                } else {
                    for (i=0;i<columnInTile;i++) {
                        *scanlinePtr=wsMC[wsPA[*wsTR]]; 
                        scanlinePtr++; 
                        wsTR++;
                    }
                }
#endif//   (BG_OPTIMIZE)
#endif// MONO_OPTIMIZE
            }
		}
    }
#if defined(BG_OPTIMIZE)
    else {
        // fill with background color
        int16 backgroundColor;
        
        if (ws_gpu_operatingInColor) {
            backgroundColor=wsc_palette[ws_ioRam[0x01]];
        } else {
#if defined(MONO_OPTIMIZE2)
            backgroundColor=wsMC64[((ws_ioRam[0x01]&0xf0)>>2)+(ws_ioRam[0x01]&0x03)];
#else
            backgroundColor=wsMC[ws_palette[((ws_ioRam[0x01]&0xf0)>>2)+(ws_ioRam[0x01]&0x03)]];
#endif
        }
        
        for(i=0;i<224;i++) {
            framebuffer[i]=backgroundColor;
        }
    }
#endif

    // render sprites which are between both layers
	if (ws_ioRam[0x00]&0x04)
	{
		int ws_sprWindow_x0=ws_ioRam[0x0c];
		int ws_sprWindow_y0=ws_ioRam[0x0d];
		int ws_sprWindow_x1=ws_ioRam[0x0e];
		int ws_sprWindow_y1=ws_ioRam[0x0f];
		uint32	*ws_sprRamBase=(uint32*)(internalRam+(((uint32)ws_ioRam[0x04])<<9));
		
		// seek to first sprite
		ws_sprRamBase+=ws_ioRam[0x06]-1;

		for (i=ws_ioRam[0x06];i>ws_ioRam[0x05];i--)
		{
			uint32 spr=*ws_sprRamBase--;

			if (!(spr&0x2000)) {
                // sprite window on ?
                if ((ws_ioRam[0x00]&0x08)&&(spr&0x1000)&&(ws_sprWindow_x0!=ws_sprWindow_x1)) {
                    ws_drawClippedSpriteLine(framebuffer,ws_gpu_scanline,(spr&0xff000000)>>24, (spr&0x00ff0000)>>16,
                                             spr&0x1ff,8+((spr&0xe00)>>9),spr&0x4000,spr&0x8000,
                                             ws_sprWindow_x0,ws_sprWindow_y0,ws_sprWindow_x1,ws_sprWindow_y1);
                } else {
                    ws_drawClippedSpriteLine(framebuffer,ws_gpu_scanline,(spr&0xff000000)>>24, (spr&0x00ff0000)>>16,
                                             spr&0x1ff,8+((spr&0xe00)>>9),spr&0x4000,spr&0x8000,
                                             0,0,224,144);
                }
            }
        }
    }
    
    // render foreground layer
	if (ws_ioRam[0x00]&0x02) {
        int ws_fgWindow_x0=ws_ioRam[0x08];
        int ws_fgWindow_y0=ws_ioRam[0x09];
        int ws_fgWindow_x1=ws_ioRam[0x0a];
        int ws_fgWindow_y1=ws_ioRam[0x0b];
        int ws_fgScroll_x=ws_ioRam[0x12];
		int ws_fgScroll_y=ws_ioRam[0x13];

		int windowMode=ws_ioRam[0x00]&0x30;
				
		// seek to the first tile
		ws_fgScroll_y=(ws_fgScroll_y+ws_gpu_scanline)&0xff;

		// note: byte ordering assumptions!
        int	ws_currentTile=(ws_fgScroll_x>>3);
        uint16	*ws_fgScrollRamBase=(uint16*)(internalRam+(((uint32)ws_ioRam[0x07]&0xf0)<<7)+
                                              ((ws_fgScroll_y&0xfff8)<<3));
        
        int	lineInTile   = ws_fgScroll_y&0x07;
        int columnInTile = ws_fgScroll_x&0x07;
        
        int16	*scanlinePtr=framebuffer;
        
        
		// window disabled
        if (!windowMode) {
            if (ws_gpu_operatingInColor) {
                // render the first clipped tile
                if (columnInTile) {
                    uint16	tileInfo=ws_fgScrollRamBase[ws_currentTile&0x1f];
                    ws_currentTile++;
                    uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile,
                                                                    tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);
                    int16	*wsc_paletteAlias=&wsc_palette[((tileInfo>>9)&0x0f)<<4];
                    
                    wsTR+=columnInTile;
                    
                    for (i=columnInTile;i<8;i++) {
                        if (*wsTR) {
                            *scanlinePtr=wsc_paletteAlias[*wsTR];
                        }
                        scanlinePtr++; 
						wsTR++;
					}
				}

				// render the tiles between them
                nbTiles=28;
                if (columnInTile) {
                    nbTiles=27;
                }
				
				for (i=0;i<nbTiles;i++) {
                    uint16	tileInfo=ws_fgScrollRamBase[ws_currentTile&0x1f];
                    ws_currentTile++;
                    uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile,
                                                                    tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);
                    int16	*wsc_paletteAlias=&wsc_palette[((tileInfo>>9)&0x0f)<<4];
                                        
                    if (*wsTR) *scanlinePtr=wsc_paletteAlias[*wsTR]; scanlinePtr++;	wsTR++;
                    if (*wsTR) *scanlinePtr=wsc_paletteAlias[*wsTR]; scanlinePtr++;	wsTR++;
                    if (*wsTR) *scanlinePtr=wsc_paletteAlias[*wsTR]; scanlinePtr++;	wsTR++;
                    if (*wsTR) *scanlinePtr=wsc_paletteAlias[*wsTR]; scanlinePtr++;	wsTR++;
                    if (*wsTR) *scanlinePtr=wsc_paletteAlias[*wsTR]; scanlinePtr++;	wsTR++;
                    if (*wsTR) *scanlinePtr=wsc_paletteAlias[*wsTR]; scanlinePtr++;	wsTR++;
                    if (*wsTR) *scanlinePtr=wsc_paletteAlias[*wsTR]; scanlinePtr++;	wsTR++;
                    if (*wsTR) *scanlinePtr=wsc_paletteAlias[*wsTR]; scanlinePtr++;	wsTR++;
                }
                
                // render the last clipped tile
				if (columnInTile) {
                    uint16	tileInfo=ws_fgScrollRamBase[ws_currentTile&0x1f];
                    ws_currentTile++;
                    uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile,
                                                                    tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);
                    int16	*wsc_paletteAlias=&wsc_palette[((tileInfo>>9)&0x0f)<<4];
                    
                    
                    for (i=0;i<columnInTile;i++) {
                        if (*wsTR) {
                            *scanlinePtr=wsc_paletteAlias[*wsTR];
                        }
                        scanlinePtr++; 
						wsTR++;
					}
				}
			} else {
				// render the first clipped tile
				if (columnInTile) {
                    uint16	tileInfo=ws_fgScrollRamBase[ws_currentTile&0x1f];
                    ws_currentTile++;
                    uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile,
                                                                    tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);

#if defined(MONO_OPTIMIZE2)
                    int16	*wsMono=&wsMC64[((tileInfo>>9)&0x0f)<<2];
                    wsTR+=columnInTile;
                    if ((tileInfo>>9)&0x04) {
                        for (i=columnInTile;i<8;i++) {
                            if (*wsTR) {
                                *scanlinePtr=wsMono[*wsTR];
                            }
                            scanlinePtr++; 
                            wsTR++;
                        }
                    }
                    else {
                        for (i=columnInTile;i<8;i++) {
                            *scanlinePtr=wsMono[*wsTR]; 
                            scanlinePtr++; 
                            wsTR++;
                        }
                    }
#else//MONO_OPTIMIZE2
                    int16	*wsPA=&ws_palette[((tileInfo>>9)&0x0f)<<2];
                    wsTR+=columnInTile;
                    if ((tileInfo>>9)&0x04) {
                        for (i=columnInTile;i<8;i++) {
                            if (*wsTR) {
                                *scanlinePtr=wsMC[wsPA[*wsTR]]; 
                            }
                            scanlinePtr++; 
                            wsTR++;
                        }
                    }
                    else {
                        for (i=columnInTile;i<8;i++) {
                            *scanlinePtr=wsMC[wsPA[*wsTR]]; 
                            scanlinePtr++; 
                            wsTR++;
                        }
                    }
#endif//MONO_OPTIMIZE2
                }

                nbTiles=28;
                if (columnInTile) {
                    nbTiles=27;
                }
                
                for (i=0;i<nbTiles;i++) {
                    uint16	tileInfo=ws_fgScrollRamBase[ws_currentTile&0x1f];
                    ws_currentTile++;
                    uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile,
                                                                    tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);
#if defined(MONO_OPTIMIZE2)
                    int16	*wsMono=&wsMC64[((tileInfo>>9)&0x0f)<<2];
                    
                    if ((tileInfo>>9)&0x04) {
                        if (wsTR[0]) scanlinePtr[0]=wsMono[wsTR[0]];
                        if (wsTR[1]) scanlinePtr[1]=wsMono[wsTR[1]];
                        if (wsTR[2]) scanlinePtr[2]=wsMono[wsTR[2]];
                        if (wsTR[3]) scanlinePtr[3]=wsMono[wsTR[3]];
                        if (wsTR[4]) scanlinePtr[4]=wsMono[wsTR[4]];
                        if (wsTR[5]) scanlinePtr[5]=wsMono[wsTR[5]];
                        if (wsTR[6]) scanlinePtr[6]=wsMono[wsTR[6]];
                        if (wsTR[7]) scanlinePtr[7]=wsMono[wsTR[7]];
                        scanlinePtr+=8;
                        wsTR+=8;
                    }
                    else {
                        scanlinePtr[0]=wsMono[wsTR[0]];
                        scanlinePtr[1]=wsMono[wsTR[1]];
                        scanlinePtr[2]=wsMono[wsTR[2]];
                        scanlinePtr[3]=wsMono[wsTR[3]];
                        scanlinePtr[4]=wsMono[wsTR[4]];
                        scanlinePtr[5]=wsMono[wsTR[5]];
                        scanlinePtr[6]=wsMono[wsTR[6]];
                        scanlinePtr[7]=wsMono[wsTR[7]];
                        scanlinePtr+=8;
                        wsTR+=8;
                    }
#else//MONO_OPTIMIZE2
                    int16	*wsPA=&ws_palette[((tileInfo>>9)&0x0f)<<2];
                    
                    if ((tileInfo>>9)&0x04) {
                        if (wsTR[0])	scanlinePtr[0]=wsMC[wsPA[wsTR[0]]];
                        if (wsTR[1])	scanlinePtr[1]=wsMC[wsPA[wsTR[1]]];
                        if (wsTR[2])	scanlinePtr[2]=wsMC[wsPA[wsTR[2]]];
                        if (wsTR[3])	scanlinePtr[3]=wsMC[wsPA[wsTR[3]]];
                        if (wsTR[4])	scanlinePtr[4]=wsMC[wsPA[wsTR[4]]];
                        if (wsTR[5])	scanlinePtr[5]=wsMC[wsPA[wsTR[5]]];
                        if (wsTR[6])	scanlinePtr[6]=wsMC[wsPA[wsTR[6]]];
                        if (wsTR[7])	scanlinePtr[7]=wsMC[wsPA[wsTR[7]]];
                        scanlinePtr+=8;
                        wsTR+=8;
                    }
                    else {
                        scanlinePtr[0]=wsMC[wsPA[wsTR[0]]];
                        scanlinePtr[1]=wsMC[wsPA[wsTR[1]]];
                        scanlinePtr[2]=wsMC[wsPA[wsTR[2]]];
                        scanlinePtr[3]=wsMC[wsPA[wsTR[3]]];
                        scanlinePtr[4]=wsMC[wsPA[wsTR[4]]];
                        scanlinePtr[5]=wsMC[wsPA[wsTR[5]]];
                        scanlinePtr[6]=wsMC[wsPA[wsTR[6]]];
                        scanlinePtr[7]=wsMC[wsPA[wsTR[7]]];
                        scanlinePtr+=8;
                        wsTR+=8;
                    }
#endif//MONO_OPTIMIZE2
                }
                
                // render the last clipped tile
                if (columnInTile) {
                    uint16	tileInfo=ws_fgScrollRamBase[ws_currentTile&0x1f];
                    ws_currentTile++;
                    uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile,
                                                            tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);
#if defined(MONO_OPTIMIZE2)
                    int16	*wsMono=&wsMC64[((tileInfo>>9)&0x0f)<<2];
                    
                    if ((tileInfo>>9)&0x04) {
                        for (i=0;i<columnInTile;i++) {
                            if (*wsTR) {
                                *scanlinePtr=wsMono[*wsTR]; 
                            }
                            scanlinePtr++; 
                            wsTR++;
                        }
                    }
                    else {
                        for (i=0;i<columnInTile;i++) {
                            scanlinePtr[i]=wsMono[wsTR[i]]; 
                        }
                    }
#else//MONO_OPTIMIZE2
                    int16	*wsPA=&ws_palette[((tileInfo>>9)&0x0f)<<2];
                    
                    if ((tileInfo>>9)&0x04) {
                        for (i=0;i<columnInTile;i++) {
                            if (*wsTR) {
                                *scanlinePtr=wsMC[wsPA[*wsTR]]; 
                            }
                            scanlinePtr++; 
                            wsTR++;
                        }
                    }
                    else {
                        for (i=0;i<columnInTile;i++) {
                            scanlinePtr[i]=wsMC[wsPA[wsTR[i]]]; 
                        }
                    }
#endif//MONO_OPTIMIZE2
                }
            }
        }
        // foreground layer displayed only inside the window
		else if (windowMode==0x20) {
			int column=0;
			if (ws_gpu_operatingInColor) {
                // render the first clipped tile
				if (columnInTile){
                    uint16	tileInfo=ws_fgScrollRamBase[ws_currentTile&0x1f];
                    ws_currentTile++;
                    uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile,
                                                                    tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);
                    int16	*wsc_paletteAlias=&wsc_palette[((tileInfo>>9)&0x0f)<<4];
                    
                    wsTR+=columnInTile;
                    
                    for (i=columnInTile;i<8;i++) {
                        if ((*wsTR)&&(column>=ws_fgWindow_x0)&&(column<=ws_fgWindow_x1)) {
                            *scanlinePtr=wsc_paletteAlias[*wsTR];
                        }
                        column++;
						scanlinePtr++; 
						wsTR++;
					}
				}

				// render the tiles between them
                nbTiles=28;
                
                if (columnInTile) {
                    nbTiles=27;
                }
				
				for (i=0;i<nbTiles;i++) {
                    uint16	tileInfo=ws_fgScrollRamBase[ws_currentTile&0x1f];
                    ws_currentTile++;
                    uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile,
                                                                    tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);
                    int16	*wsc_paletteAlias=&wsc_palette[((tileInfo>>9)&0x0f)<<4];
                    
                    if ((*wsTR)&&(column>=ws_fgWindow_x0)&&(column<=ws_fgWindow_x1)) *scanlinePtr=wsc_paletteAlias[*wsTR]; scanlinePtr++;	wsTR++; column++;
					if ((*wsTR)&&(column>=ws_fgWindow_x0)&&(column<=ws_fgWindow_x1)) *scanlinePtr=wsc_paletteAlias[*wsTR]; scanlinePtr++;	wsTR++; column++;
					if ((*wsTR)&&(column>=ws_fgWindow_x0)&&(column<=ws_fgWindow_x1)) *scanlinePtr=wsc_paletteAlias[*wsTR]; scanlinePtr++;	wsTR++; column++;
					if ((*wsTR)&&(column>=ws_fgWindow_x0)&&(column<=ws_fgWindow_x1)) *scanlinePtr=wsc_paletteAlias[*wsTR]; scanlinePtr++;	wsTR++; column++;
					if ((*wsTR)&&(column>=ws_fgWindow_x0)&&(column<=ws_fgWindow_x1)) *scanlinePtr=wsc_paletteAlias[*wsTR]; scanlinePtr++;	wsTR++; column++;
					if ((*wsTR)&&(column>=ws_fgWindow_x0)&&(column<=ws_fgWindow_x1)) *scanlinePtr=wsc_paletteAlias[*wsTR]; scanlinePtr++;	wsTR++; column++;
					if ((*wsTR)&&(column>=ws_fgWindow_x0)&&(column<=ws_fgWindow_x1)) *scanlinePtr=wsc_paletteAlias[*wsTR]; scanlinePtr++;	wsTR++; column++;
					if ((*wsTR)&&(column>=ws_fgWindow_x0)&&(column<=ws_fgWindow_x1)) *scanlinePtr=wsc_paletteAlias[*wsTR]; scanlinePtr++;	wsTR++; column++;
				}

				// render the last clipped tile
				if (columnInTile) {
                    uint16	tileInfo=ws_fgScrollRamBase[ws_currentTile&0x1f];
                    ws_currentTile++;
                    uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile,
                                                                    tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);
                    int16	*wsc_paletteAlias=&wsc_palette[((tileInfo>>9)&0x0f)<<4];
                    
                    for (i=0;i<columnInTile;i++) {
                        if ((*wsTR)&&(column>=ws_fgWindow_x0)&&(column<=ws_fgWindow_x1)) {
                            *scanlinePtr=wsc_paletteAlias[*wsTR];
                        }
                        column++;
                        scanlinePtr++; 
                        wsTR++;
                    }
                }
            }
            else {
                // render the first clipped tile
                if (columnInTile) {
                    uint16	tileInfo=ws_fgScrollRamBase[ws_currentTile&0x1f];
                    ws_currentTile++;
                    uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile,
                                                                    tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);

#if defined(MONO_OPTIMIZE2)
                    int16 *wsMono=&wsMC64[((tileInfo>>9)&0x0f)<<2];
                    wsTR+=columnInTile;
                    
                    for (i=columnInTile;i<8;i++) {
                        if ((*wsTR)&&(column>=ws_fgWindow_x0)&&(column<=ws_fgWindow_x1)) {
                            *scanlinePtr=wsMono[*wsTR];
                        }
                        column++;
                        scanlinePtr++; 
                        wsTR++;
                    }
#else//MONO_OPTIMIZE2
                    int16	*wsPA=&ws_palette[((tileInfo>>9)&0x0f)<<2];
                    wsTR+=columnInTile;
                    
                    for (i=columnInTile;i<8;i++) {
                        if ((*wsTR)&&(column>=ws_fgWindow_x0)&&(column<=ws_fgWindow_x1)) {
                            *scanlinePtr=wsMC[wsPA[*wsTR]]; 
                        }
                        column++;
                        scanlinePtr++; 
                        wsTR++;
                    }
#endif//MONO_OPTIMIZE2
                }

                nbTiles=28;
                if (columnInTile) {
                    nbTiles=27;
                }
                
                for (i=0;i<nbTiles;i++) {
                    uint16	tileInfo=ws_fgScrollRamBase[ws_currentTile&0x1f];
                    ws_currentTile++;
                    uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile,
                                                            tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);
#if defined(MONO_OPTIMIZE2)
                    int16	*wsMono=&wsMC64[((tileInfo>>9)&0x0f)<<2];
                    
                    if ((wsTR[0])&&((column+0)>=ws_fgWindow_x0)&&((column+0)<=ws_fgWindow_x1))	scanlinePtr[0]=wsMono[wsTR[0]];
                    if ((wsTR[1])&&((column+1)>=ws_fgWindow_x0)&&((column+1)<=ws_fgWindow_x1))	scanlinePtr[1]=wsMono[wsTR[1]];
                    if ((wsTR[2])&&((column+2)>=ws_fgWindow_x0)&&((column+2)<=ws_fgWindow_x1))	scanlinePtr[2]=wsMono[wsTR[2]];
                    if ((wsTR[3])&&((column+3)>=ws_fgWindow_x0)&&((column+3)<=ws_fgWindow_x1))	scanlinePtr[3]=wsMono[wsTR[3]];
                    if ((wsTR[4])&&((column+4)>=ws_fgWindow_x0)&&((column+4)<=ws_fgWindow_x1))	scanlinePtr[4]=wsMono[wsTR[4]];
                    if ((wsTR[5])&&((column+5)>=ws_fgWindow_x0)&&((column+5)<=ws_fgWindow_x1))	scanlinePtr[5]=wsMono[wsTR[5]];
                    if ((wsTR[6])&&((column+6)>=ws_fgWindow_x0)&&((column+6)<=ws_fgWindow_x1))	scanlinePtr[6]=wsMono[wsTR[6]];
                    if ((wsTR[7])&&((column+7)>=ws_fgWindow_x0)&&((column+7)<=ws_fgWindow_x1))	scanlinePtr[7]=wsMono[wsTR[7]];
#else//MONO_OPTIMIZE2
                    int16	*wsPA=&ws_palette[((tileInfo>>9)&0x0f)<<2];
                    
                    if ((wsTR[0])&&((column+0)>=ws_fgWindow_x0)&&((column+0)<=ws_fgWindow_x1))	scanlinePtr[0]=wsMC[wsPA[wsTR[0]]];
                    if ((wsTR[1])&&((column+1)>=ws_fgWindow_x0)&&((column+1)<=ws_fgWindow_x1))	scanlinePtr[1]=wsMC[wsPA[wsTR[1]]];
                    if ((wsTR[2])&&((column+2)>=ws_fgWindow_x0)&&((column+2)<=ws_fgWindow_x1))	scanlinePtr[2]=wsMC[wsPA[wsTR[2]]];
                    if ((wsTR[3])&&((column+3)>=ws_fgWindow_x0)&&((column+3)<=ws_fgWindow_x1))	scanlinePtr[3]=wsMC[wsPA[wsTR[3]]];
                    if ((wsTR[4])&&((column+4)>=ws_fgWindow_x0)&&((column+4)<=ws_fgWindow_x1))	scanlinePtr[4]=wsMC[wsPA[wsTR[4]]];
                    if ((wsTR[5])&&((column+5)>=ws_fgWindow_x0)&&((column+5)<=ws_fgWindow_x1))	scanlinePtr[5]=wsMC[wsPA[wsTR[5]]];
                    if ((wsTR[6])&&((column+6)>=ws_fgWindow_x0)&&((column+6)<=ws_fgWindow_x1))	scanlinePtr[6]=wsMC[wsPA[wsTR[6]]];
                    if ((wsTR[7])&&((column+7)>=ws_fgWindow_x0)&&((column+7)<=ws_fgWindow_x1))	scanlinePtr[7]=wsMC[wsPA[wsTR[7]]];
#endif//MONO_OPTIMIZE2
                    column+=8;
                    scanlinePtr+=8;
                    wsTR+=8;
                }
                
                // render the last clipped tile
                if (columnInTile) {
                    uint16	tileInfo=ws_fgScrollRamBase[ws_currentTile&0x1f];
                    ws_currentTile++;
                    uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile,
                                                            tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);
#if defined(MONO_OPTIMIZE2)
                    int16	*wsMono=&wsMC64[((tileInfo>>9)&0x0f)<<2];
                    
                    for (i=0;i<columnInTile;i++) {
                        if ((*wsTR)&&(column>=ws_fgWindow_x0)&&(column<=ws_fgWindow_x1)) {
                            *scanlinePtr=wsMono[*wsTR];
                        }
                        column++;
                        scanlinePtr++; 
                        wsTR++;
                    }
#else//MONO_OPTIMIZE2
                    int16	*wsPA=&ws_palette[((tileInfo>>9)&0x0f)<<2];
                    
                    for (i=0;i<columnInTile;i++) {
                        if ((*wsTR)&&(column>=ws_fgWindow_x0)&&(column<=ws_fgWindow_x1)) {
                            *scanlinePtr=wsMC[wsPA[*wsTR]]; 
                        }
                        column++;
                        scanlinePtr++; 
                        wsTR++;
                    }
#endif//MONO_OPTIMIZE2
                }
            }
        }
        // foreground layer displayed only outside the window
		else if (windowMode==0x30) {
            int column=0;
            if (ws_gpu_operatingInColor) {
                // render the first clipped tile
                if (columnInTile) {
                    uint16	tileInfo=ws_fgScrollRamBase[ws_currentTile&0x1f];
                    ws_currentTile++;
                    uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile,
                                                                    tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);
                    int16	*wsc_paletteAlias=&wsc_palette[((tileInfo>>9)&0x0f)<<4];
                    
                    wsTR+=columnInTile;
                    
                    for (i=columnInTile;i<8;i++) {
                        if ((*wsTR)&&((column<ws_fgWindow_x0)||(column>ws_fgWindow_x1))) {
                            *scanlinePtr=wsc_paletteAlias[*wsTR];
                        }
						column++;
						scanlinePtr++; 
						wsTR++;
					}
				}

				// render the tiles between them
                nbTiles=28;
                if (columnInTile) {
                    nbTiles=27;
                }
				
				for (i=0;i<nbTiles;i++) {
                    uint16	tileInfo=ws_fgScrollRamBase[ws_currentTile&0x1f];
                    ws_currentTile++;
                    uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile,
                                                                    tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);
                    int16	*wsc_paletteAlias=&wsc_palette[((tileInfo>>9)&0x0f)<<4];
                    
                    if ((wsTR[0])&&(((column+0)<ws_fgWindow_x0)||((column+0)>ws_fgWindow_x1))) scanlinePtr[0]=wsc_paletteAlias[wsTR[0]];
                    if ((wsTR[1])&&(((column+1)<ws_fgWindow_x0)||((column+1)>ws_fgWindow_x1))) scanlinePtr[1]=wsc_paletteAlias[wsTR[1]];
                    if ((wsTR[2])&&(((column+2)<ws_fgWindow_x0)||((column+2)>ws_fgWindow_x1))) scanlinePtr[2]=wsc_paletteAlias[wsTR[2]];
                    if ((wsTR[3])&&(((column+3)<ws_fgWindow_x0)||((column+3)>ws_fgWindow_x1))) scanlinePtr[3]=wsc_paletteAlias[wsTR[3]];
                    if ((wsTR[4])&&(((column+4)<ws_fgWindow_x0)||((column+4)>ws_fgWindow_x1))) scanlinePtr[4]=wsc_paletteAlias[wsTR[4]];
                    if ((wsTR[5])&&(((column+5)<ws_fgWindow_x0)||((column+5)>ws_fgWindow_x1))) scanlinePtr[5]=wsc_paletteAlias[wsTR[5]];
                    if ((wsTR[6])&&(((column+6)<ws_fgWindow_x0)||((column+6)>ws_fgWindow_x1))) scanlinePtr[6]=wsc_paletteAlias[wsTR[6]];
                    if ((wsTR[7])&&(((column+7)<ws_fgWindow_x0)||((column+7)>ws_fgWindow_x1))) scanlinePtr[7]=wsc_paletteAlias[wsTR[7]];
                    wsTR+=8;
                    column+=8;
                    scanlinePtr+=8;
                }
                
                // render the last clipped tile
                if (columnInTile) {
                    uint16	tileInfo=ws_fgScrollRamBase[ws_currentTile&0x1f];
                    ws_currentTile++;
                    uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile,
                                                                    tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);
                    int16	*wsc_paletteAlias=&wsc_palette[((tileInfo>>9)&0x0f)<<4];
                    
                    for (i=0;i<columnInTile;i++) {
                        if ((*wsTR)&&((column<ws_fgWindow_x0)||(column>ws_fgWindow_x1))) { 
                            *scanlinePtr=wsc_paletteAlias[*wsTR];
                        }
                        column++;
						scanlinePtr++; 
						wsTR++;
					}
				}
			}
			else {
                // render the first clipped tile
                if (columnInTile) {
                    uint16	tileInfo=ws_fgScrollRamBase[ws_currentTile&0x1f];
                    ws_currentTile++;
                    uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile,
                                                            tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);
#if defined(MONO_OPTIMIZE2)
                    int16	*wsMono=&wsMC64[((tileInfo>>9)&0x0f)<<2];
                    wsTR+=columnInTile;
                    
                    for(i=columnInTile;i<8;i++) {
                        if ((*wsTR)&&((column<ws_fgWindow_x0)||(column>ws_fgWindow_x1))) {
                            *scanlinePtr=wsMono[*wsTR];
                        }
                        column++;
                        scanlinePtr++; 
                        wsTR++;
                    }
#else//MONO_OPTIMIZE2
                    int16	*wsPA=&ws_palette[((tileInfo>>9)&0x0f)<<2];
                    wsTR+=columnInTile;
                    
                    for(i=columnInTile;i<8;i++) {
                        if ((*wsTR)&&((column<ws_fgWindow_x0)||(column>ws_fgWindow_x1))) {
                            *scanlinePtr=wsMC[wsPA[*wsTR]];
                        }
                        column++;
                        scanlinePtr++; 
                        wsTR++;
                    }
#endif//MONO_OPTIMIZE2
                }
                
                nbTiles=28;
                if (columnInTile) {
                    nbTiles=27;
                }
                
                for (i=0;i<nbTiles;i++) {
                    uint16	tileInfo=ws_fgScrollRamBase[ws_currentTile&0x1f];
                    ws_currentTile++;
                    uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile, tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);
#if defined(MONO_OPTIMIZE2)
                    int16 *wsMono=&wsMC64[((tileInfo>>9)&0x0f)<<2];
                    
                    if ((wsTR[0])&&(((column+0)<ws_fgWindow_x0)||((column+0)>ws_fgWindow_x1))) scanlinePtr[0]=wsMono[wsTR[0]];
                    if ((wsTR[1])&&(((column+1)<ws_fgWindow_x0)||((column+1)>ws_fgWindow_x1))) scanlinePtr[1]=wsMono[wsTR[1]];
                    if ((wsTR[2])&&(((column+2)<ws_fgWindow_x0)||((column+2)>ws_fgWindow_x1))) scanlinePtr[2]=wsMono[wsTR[2]];
                    if ((wsTR[3])&&(((column+3)<ws_fgWindow_x0)||((column+3)>ws_fgWindow_x1))) scanlinePtr[3]=wsMono[wsTR[3]];
                    if ((wsTR[4])&&(((column+4)<ws_fgWindow_x0)||((column+4)>ws_fgWindow_x1))) scanlinePtr[4]=wsMono[wsTR[4]];
                    if ((wsTR[5])&&(((column+5)<ws_fgWindow_x0)||((column+5)>ws_fgWindow_x1))) scanlinePtr[5]=wsMono[wsTR[5]];
                    if ((wsTR[6])&&(((column+6)<ws_fgWindow_x0)||((column+6)>ws_fgWindow_x1))) scanlinePtr[6]=wsMono[wsTR[6]];
                    if ((wsTR[7])&&(((column+7)<ws_fgWindow_x0)||((column+7)>ws_fgWindow_x1))) scanlinePtr[7]=wsMono[wsTR[7]];
#else//MONO_OPTIMIZE2
                    int16	*wsPA=&ws_palette[((tileInfo>>9)&0x0f)<<2];
                    
                    if ((wsTR[0])&&(((column+0)<ws_fgWindow_x0)||((column+0)>ws_fgWindow_x1))) scanlinePtr[0]=wsMC[wsPA[wsTR[0]]];
                    if ((wsTR[1])&&(((column+1)<ws_fgWindow_x0)||((column+1)>ws_fgWindow_x1))) scanlinePtr[1]=wsMC[wsPA[wsTR[1]]];
                    if ((wsTR[2])&&(((column+2)<ws_fgWindow_x0)||((column+2)>ws_fgWindow_x1))) scanlinePtr[2]=wsMC[wsPA[wsTR[2]]];
                    if ((wsTR[3])&&(((column+3)<ws_fgWindow_x0)||((column+3)>ws_fgWindow_x1))) scanlinePtr[3]=wsMC[wsPA[wsTR[3]]];
                    if ((wsTR[4])&&(((column+4)<ws_fgWindow_x0)||((column+4)>ws_fgWindow_x1))) scanlinePtr[4]=wsMC[wsPA[wsTR[4]]];
                    if ((wsTR[5])&&(((column+5)<ws_fgWindow_x0)||((column+5)>ws_fgWindow_x1))) scanlinePtr[5]=wsMC[wsPA[wsTR[5]]];
                    if ((wsTR[6])&&(((column+6)<ws_fgWindow_x0)||((column+6)>ws_fgWindow_x1))) scanlinePtr[6]=wsMC[wsPA[wsTR[6]]];
                    if ((wsTR[7])&&(((column+7)<ws_fgWindow_x0)||((column+7)>ws_fgWindow_x1))) scanlinePtr[7]=wsMC[wsPA[wsTR[7]]];
#endif//MONO_OPTIMIZE2
                    column+=8;
                    scanlinePtr+=8;
                    wsTR+=8;
                }

                // render the last clipped tile
                if (columnInTile) {
                    uint16	tileInfo=ws_fgScrollRamBase[ws_currentTile&0x1f];
                    ws_currentTile++;
                    uint8	*wsTR=ws_tileCache_getTileRow(	tileInfo&0x1ff, lineInTile,
                                                                    tileInfo&0x8000, tileInfo&0x4000, tileInfo&0x2000);
#if defined(MONO_OPTIMIZE2)
                    int16 *wsMono=&wsMC64[((tileInfo>>9)&0x0f)<<2];
                    
                    for (i=0;i<columnInTile;i++) {
                        if ((*wsTR)&&((column<ws_fgWindow_x0)||(column>ws_fgWindow_x1))) {
                            *scanlinePtr=wsMono[*wsTR];
                        }
                        column++;
                        scanlinePtr++; 
                        wsTR++;
                    }
#else//MONO_OPTIMIZE2
                    int16	*wsPA=&ws_palette[((tileInfo>>9)&0x0f)<<2];
                    
                    for (i=0;i<columnInTile;i++) {
                        if ((*wsTR)&&((column<ws_fgWindow_x0)||(column>ws_fgWindow_x1))) {
                            *scanlinePtr=wsMC[wsPA[*wsTR]];
                        }
                        column++;
                        scanlinePtr++; 
                        wsTR++;
                    }
#endif//MONO_OPTIMIZE2
                }
            }
        }
        else {
            // unknown
        }
    }
    
    // render sprites
    if (ws_ioRam[0x00]&0x04) {
        int ws_sprWindow_x0=ws_ioRam[0x0c];
        int ws_sprWindow_y0=ws_ioRam[0x0d];
        int ws_sprWindow_x1=ws_ioRam[0x0e];
        int ws_sprWindow_y1=ws_ioRam[0x0f];
        uint32	*ws_sprRamBase=(uint32*)(internalRam+(((uint32)ws_ioRam[0x04])<<9));
        
        // seek to first sprite
        ws_sprRamBase+=ws_ioRam[0x06]-1;
        
        for (i=ws_ioRam[0x06];i>ws_ioRam[0x05];i--) {
            uint32 spr=*ws_sprRamBase--;
            
            if (spr&0x2000) {
                // sprite window on ?
                if ((ws_ioRam[0x00]&0x08)&&(spr&0x1000)&&(ws_sprWindow_x0!=ws_sprWindow_x1)) {
                    ws_drawClippedSpriteLine(framebuffer,ws_gpu_scanline,(spr&0xff000000)>>24, (spr&0x00ff0000)>>16,
                                             spr&0x1ff,8+((spr&0xe00)>>9),spr&0x4000,spr&0x8000,
                                             ws_sprWindow_x0,ws_sprWindow_y0,ws_sprWindow_x1,ws_sprWindow_y1);
                }
                else {
                    ws_drawClippedSpriteLine(framebuffer,ws_gpu_scanline,(spr&0xff000000)>>24, (spr&0x00ff0000)>>16,
                                             spr&0x1ff,8+((spr&0xe00)>>9),spr&0x4000,spr&0x8000,
                                             0,0,224,144);
                }
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// 
////////////////////////////////////////////////////////////////////////////////
void ws_gpu_write_byte(DWORD offset, BYTE value)
{
    if( offset<0x2000 ) {}
    
    else if(offset<0x4000) {
        ws_modified_tile[(offset&0x1fff)>>4]=1;
    }
#if 0 /* OPTIMIZE */
    else if( offset<0xC000 ) {
        wsc_modified_tile[(offset&0x7fff)>>5]=1;
    }
#else
    else if(offset<0x8000) {
        wsc_modified_tile[(offset&0x3fff)>>5]=1;
    }
    else if(offset<0xC000) {
        wsc_modified_tile[512+((offset&0x3fff)>>5)]=1;
    }
#endif
    else if(offset<0xFE00) {  }

    else if(offset<0x10000){
        int adr = offset & 0xfffe;
        int pal =(offset & 0x1ff)>>1;
        uint16 color = ((internalRam[adr+1]<<8) | internalRam[adr])<<1;
        uint16 rgb   = RGB555((color&0x1e),((color>>4)&0x1e),((color>>8)&0x1e));

#if defined(BG_OPTIMIZE)
        // パレット[0]を設定された場合は全てのパレットを同じ色にする。
        // 透明色なのでBG描画する際にパレット[0]と違うと透明色判定が
        // 必要になり処理時間がもったいない。
        if(pal==0) {
            int i;
            for(i=0;i<16;i++) {
                wsc_palette[i*16] = rgb;
            }
        }
        else if((pal&15)) {
            wsc_palette[pal]=rgb;
        }
#else
        wsc_palette[pal]=rgb;
#endif
    }
}


////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
void ws_gpu_port_write(DWORD port,BYTE value)
{
    switch (port) {
      case 0x60:
        ws_gpu_changeVideoMode(value);
        return;

      case 0x1c:
      case 0x1d:
      case 0x1e:
      case 0x1f: {
          int pal = (port-0x1c)*2;
          int v1  = 15 & value;
          int v2  = 15 & (value>>4);
          
          wsMC[pal  ] = ws_shades[v1];
          wsMC[pal+1] = ws_shades[v2];
          
#if defined(MONO_OPTIMIZE2)
          {
              int i;
              for(i=0;i<64;i++) {
                  if(ws_palette[i]==(pal))   wsMC64[i]=wsMC[pal];
                  if(ws_palette[i]==(pal+1)) wsMC64[i]=wsMC[pal+1];
              }
          }
#endif//MONO_OPTIMIZE2
        }
        return;
        
      default:
        // PORT : 0x20 to 0x3F : 32*2=64(pal)
        if ((port>=0x20)&&(port<=0x3f)) {
            port-=0x20;
            int idx = port<<1;
            ws_palette[idx+0] = 7 & value;
            ws_palette[idx+1] = 7 & (value>>4);
            
#if defined(MONO_OPTIMIZE2)
            wsMC64[idx+0] = wsMC[ 7 & value ];
            wsMC64[idx+1] = wsMC[ 7 &(value>>8) ];
#endif
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
BYTE ws_gpu_port_read(BYTE port)
{
    if (ws_gpu_forceColorSystemBool)
      return ws_ioRam[0xa0]|2;
    else
      if (ws_gpu_forceMonoSystemBool)
        return ws_ioRam[0xa0]&(~0x02);
      else {
          if (ws_gpu_operatingInColor) 
            return ws_ioRam[0xa0]|2; 
          else 
            return ws_ioRam[0xa0]&(~0x02);
      }
}

