/* Emulation for HuC6261(descendant of the VCE) and the HuC6272(KING) */

#include "pcfx.h"
#include "v810_cpu.h"
#include "vdc.h"
#include "king.h"
#include "scsicd.h"
#include "interrupt.h"
#include "rainbow.h"
#include "soundbox.h"
#include "pad.h"
#include "timer.h"
#include "debug.h"

#include "../video.h"
#include "../clamp.h"

// 16 bit YUV format:  upper 8 bits Y, next 4 bits U, lower 4 bits V, transformed to 8-bit U and 8-bit V by adding 0 or 1, who knows.

typedef struct
{
 uint8 AR;

 //
 unsigned int layer_mask_cache[7]; // 0x00 if layer disabled, 0xFF if enabled.

 uint16 priority[2];	/* uint16 0:
			   	bit   3-0: Legacy VDC BG priority?
				bit   7-4: Legacy VDC SPR priority?
				bit  11-8: RAINBOW(MJPEG) priority
			   uint16 1:
				bit   3-0: KING BG0 priority
				bit   7-4: KING BG1 priority
				bit  11-8: KING BG2 priority
				bit 15-12: KING BG3 priority
			*/

 bool8 odd_field;
 bool8 in_hblank;


 uint16 picture_mode;
 uint32 frame_counter;

 uint16 palette_rw_offset; // Read/write offset
 uint16 palette_rw_latch;

 uint16 palette_offset[4]; // 
			   // BMG1 and BMG 0 in [1](BMG1 in upper 8 bits, BMG0 in lower), BMG2 and 3 in [2]
			   // RAINBOW in lower(?) 8 bits of [3]?

 uint16 palette_table[512]; // The YUV palette, woohoo!
 uint32 palette_table_cache[512 * 2]; // 24-bit YUV cache for SPEED(HAH), * 2 to remove need for & 0x1FF in rendering code

 // Chroma keys, minimum value in lower 8 bits, maximum value in upper 8 bits
 uint16 ChromaKeyY; // Register 0xA
 uint16 ChromaKeyU; // Register 0xB
 uint16 ChromaKeyV; // register 0xC

 uint16 CCR; // Register 0x0D, fixed color register, 16-bit YUV
 uint16 BLE; // Register 0x0E, cellophane setting register
	     //  15   14   13   12   11   10   09   08   07   06   05   04   03   02   01   00
	     // F/B |E/D |   IDU   |CTRL BMG3|CTRL BMG2|CTRL BMG1|CTRL BMG0|  VDP SP | VDP BG
	     // IDU is AKA Rainbow
	     // F/B
	     //	 0 = Front process?
	     //  1 = Back processing?
	     // E/D
	     //  0 = ?
	     //  1 = ?	     
	     // Ctrl format:
	     //  00 = Cellophane disabled.
	     //  01 = Cellophane with 1A/1B(Coeffs 0, 1?)
	     //  02 = Cellophane with 2A/2B(Coeffs 2, 3?)
	     //  03 = Cellophane with 3A/3B(Coeffs 4, 5?)

 uint16 SPBL; // Register 0x0F, Sprite cellophane setting register
	      // Specifies a color palette bank for which cellophane will always be disabled.

 uint16 coefficients[6]; // Cellophane coefficients, YUV, 4-bits each, on the least-significant end(xxxxYYYYUUUUVVVV).
			 // Valid settings: 0(cellophane disabled for layer), 1-8.  9-F are "unsupported".

 uint8 coefficient_mul_table[3][16][256];
} fx_vce_t;

fx_vce_t fx_vce;

static uint32 priority_0_dummybuf[512];

static uint8 happymap[17][17][17][3];

static void BuildCMT(void)
{
 for(int coeff = 0; coeff < 16; coeff++)
 {
  for(int value = 0; value < 256; value++)
  {
   fx_vce.coefficient_mul_table[0][coeff][value] = (value * coeff / 8); // Y
   fx_vce.coefficient_mul_table[1][coeff][value] = ((value - 128) * coeff / 8 + 128); // U
   fx_vce.coefficient_mul_table[2][coeff][value] = ((value - 128) * coeff / 8 + 128); // V

   //fx_vce.coefficient_mul_table[0][coeff][value] = coeff ? (value * (coeff - 1) / 7) : value; // Y
   //fx_vce.coefficient_mul_table[1][coeff][value] = coeff ? ((value - 128) * (coeff - 1) / 7 + 128) : value; // U
   //fx_vce.coefficient_mul_table[2][coeff][value] = coeff ? ((value - 128) * (coeff - 1) / 7 + 128) : value; // V
  }
 }

}

static INLINE void RebuildLayerMaskCache(void)
{
 for(int x = 0; x < 4; x++)
  fx_vce.layer_mask_cache[x] = ((fx_vce.picture_mode >> (10 + x)) & 1) ? 0xFF : 0x00;
 
 fx_vce.layer_mask_cache[4] = (fx_vce.picture_mode & 0x0100) ? 0xFF : 0x00; // VDC BG
 fx_vce.layer_mask_cache[5] = (fx_vce.picture_mode & 0x0200) ? 0xFF : 0x00; // VDC SPR
 fx_vce.layer_mask_cache[6] = (fx_vce.picture_mode & 0x4000) ? 0xFF : 0x00; // Rainbow
}

static INLINE void RedoPaletteCache(int n)
{
 uint32 YUV = fx_vce.palette_table[n];
 uint8 Y = (YUV >> 8) & 0xFF;
 uint8 U = (YUV & 0xF0);
 uint8 V = (YUV & 0x0F) << 4;

 fx_vce.palette_table_cache[n] = 
 fx_vce.palette_table_cache[0x200 | n] = (Y << 16) | (U << 8) | (V << 0);
}

typedef struct
{
	uint8 AR;

	uint16 KRAM[2][262144];
	uint32 KRAMRA, KRAMWA;

	uint32 PageSetting;

	uint16 bgmode; // 4 bits each BG: 3333 2222 1111 0000
		       /* Possible settings:
				0x0:  Invalid?
				0x1:  4-color palette, 1 byte for 4 pixels, transparent on entry 0
				0x2:  16-color palette, 1 byte for 2 pixels, transparent on entry 0
				0x3:  256-color palette, 1 byte for 1 pixel, transparent on entry 0
				0x4:  64K color(Y-8, U-4, V-4), 1 halfword for 1 pixel, transparent on Y=0
				0x5:  16M colors(Y-8, Y-8, U-8, V-8, 4 bytes for 2 pixels), transparent on Y=0

				If & 8, enable palette bank mode(only for 4 and 16-colors)???
					BAT format would be PPPPCCCCCCCCCCCC in this mode.
					4 color: 00PPPPnn 16 color: PPPPnnnn, where "n" is the 2 or 4-bit pixel data
			*/
	uint16 priority;

	uint16 BGScrollMode;	// Register 0x16
	uint16 BGSize[4];
	uint16 BGBATAddr[4];
	uint16 BGCGAddr[4];
	uint16 BGXScroll[4];
	uint16 BGYScroll[4];

	uint16 BG0SubBATAddr, BG0SubCGAddr;

	uint16 ADPCMControl;
	uint16 ADPCMBufferMode[2];

	uint16 ADPCMSAL[2];

	uint32 ADPCMStartAddress[2];
	uint32 ADPCMEndAddress[2];
	uint32 ADPCMPlayAddress[2];
	uint16 ADPCMIntermediateAddress[2];
	uint16 ADPCMStatus[2]; // Register 0x53, a bit maimed :)


	uint16 RAINBOWTransferControl; // Register 0x40
	uint32 RAINBOWKRAMA;	       // Register 0x41
        uint16 RAINBOWTransferStartPosition; // Register 0x42, line number(0-262)
	uint16 RAINBOWTransferBlockCount; // Register 0x43

	uint16 RasterIRQLine; // Register 0x44
	bool8 RasterIRQPending;

	uint32 RAINBOWKRAMReadPos;

	uint32 DMATransferAddrLatch;
	uint32 DMATransferAddr; // Register 0x09
	uint32 DMATransferSize; // Register 0x0A
	uint16 DMAStatus;	// Register 0x0B
	uint8 DMALatch;


	uint16 MPROGControl;    // register 0x15


	bool8 DMAInterrupt;
	uint8 Port02;
	bool8 CDInterrupt;

	uint8 data_cache;
} king_t;

static king_t king;
static uint8 BGLayerDisable;
static bool RAINBOWLayerDisable;

static bool KRAMReadBPE = FALSE;
static bool KRAMWriteBPE = FALSE;

void KING_NotifyOfBPE(bool read, bool write)
{
 KRAMReadBPE = read;
 KRAMWriteBPE = write;

 FXVDC_SetAux0BPBpase(fx_vdc_chips[0], (read || write) ? 0x80000 : ~0);
 FXVDC_SetAux0BPBpase(fx_vdc_chips[1], (read || write) ? 0x90000 : ~0);
}


static uint32 *GfxDecode_Buf = NULL;
static int GfxDecode_Width = 0;
static int GfxDecode_Height = 0;
static int GfxDecode_Line = -1;
static int GfxDecode_Layer = 0;
static int GfxDecode_Scroll = 0;
static int GfxDecode_PBN = 0;
static void DoGfxDecode(void);

static INLINE void REGSETP(uint16 &reg, const uint8 data, const bool msb)
{
 reg &= 0xFF << (msb ? 0 : 8); 
 reg |= data << (msb ? 8 : 0); 
}

static INLINE uint8 REGGETP(const uint16 reg, const bool msb)
{
 uint8 ret = (reg >> (msb ? 8 : 0)) & 0xFF;

 return(ret);
}
static void RedoKINGIRQCheck(void);

uint8 KING_MemPeek(uint32 A)
{
 uint8 ret = king.KRAM[(A & 0x80000) ? 1 : 0][A >> 1] >> ((A & 1) * 8);

 return(ret);
}

uint8 KING_RB_Fetch(void)
{
 int page = (king.PageSetting & 0x1000) ? 1 : 0;
 uint8 ret = king.KRAM[page][king.RAINBOWKRAMReadPos >> 1] >> ((king.RAINBOWKRAMReadPos & 1) * 8);

 //printf("Fetch: %08x %02x\n", king.RAINBOWKRAMReadPos, ret);
 king.RAINBOWKRAMReadPos = (king.RAINBOWKRAMReadPos + 1) & 0x7FFFF;
 return(ret);
}

static void CheckDMA(void)
{
 if(king.DMAStatus & 0x1)
 {
  uint8 db;

  while(SCSICD_StealByte(db))
  {
    if(king.DMATransferAddr & 1)
     king.KRAM[king.PageSetting & 1][(king.DMATransferAddr >> 1) & 0x3ffff] = king.DMALatch | (db << 8);
    else
     king.DMALatch = db;

    king.DMATransferAddr++;
    king.DMATransferSize--;
    if(!king.DMATransferAddr) puts("Oops");

    if(king.DMATransferSize == 1)
    {
     SCSICD_SetACK(FALSE);
     //puts("DMA Done");
     king.DMAInterrupt = TRUE;
     RedoKINGIRQCheck();
     king.DMAStatus &= ~1;
     break;
    }
   
  }
 }
}

uint8 FXVCE_Read8(uint32 A)
{
 int msb = A & 1;

 //if(A & 2)
 // return(0x00);

 switch(A & 0x304)
 {
  // bit  4-0: Register number
  // bit 13-5: Raster/frame counter(22-261 range??)
  // bit 14: In interlace mode and when on odd fields set bit.
  // bit 15: "0" during active screen area, "1" when in h-blank or v-blank
  case 0x300: 
	      {
	       uint16 fullret = 0; // fx_vce.AR | (fx_vce.odd_field ? 0x4000 : 0x0000);

	       fullret |= fx_vce.AR;
	       fullret |= fx_vce.odd_field ? 0x4000 : 0x0000;

	       if(fx_vce.frame_counter < 22) 
	       {
		fullret |= 0x8000;
                fullret |= 0 << 5; // What should be returned when in vblank? 0? 262? 22? 0x1FF?
	       }
	       else 
                fullret |= fx_vce.frame_counter << 5;
	       if(fx_vce.in_hblank) fullret |= 0x8000;

	       return(REGGETP(fullret, msb));
	      }
	      break;
  case 0x304:
	      switch(fx_vce.AR) // No idea which registers are readable, so make them all readable :b
	      {
		default:  printf("VCE Read: %04x\n", fx_vce.AR); break;
	        case 0x00: return(REGGETP(fx_vce.picture_mode, msb));
		case 0x01: return(REGGETP(fx_vce.palette_rw_offset, msb));
		case 0x03: // Boundary Gate reads from 0x03 expecting palette data...
	        case 0x02: 
			   {
				uint8 ret = REGGETP(fx_vce.palette_rw_latch, msb);
				if(msb) 
				{
				 fx_vce.palette_rw_offset = (fx_vce.palette_rw_offset + 1) & 0x1FF;
				 fx_vce.palette_rw_latch = fx_vce.palette_table[fx_vce.palette_rw_offset];
				}
				return(ret);
			   }
	        case 0x04: return(REGGETP(fx_vce.palette_offset[0], msb));
		case 0x05: return(REGGETP(fx_vce.palette_offset[1], msb));
		case 0x06: return(REGGETP(fx_vce.palette_offset[2], msb));
		case 0x07: return(REGGETP(fx_vce.palette_offset[3], msb));
		case 0x08: return(REGGETP(fx_vce.priority[0], msb));
		case 0x09: return(REGGETP(fx_vce.priority[1], msb));
                case 0x0a: return(REGGETP(fx_vce.ChromaKeyY, msb)); break;
                case 0x0b: return(REGGETP(fx_vce.ChromaKeyU, msb)); break;
                case 0x0c: return(REGGETP(fx_vce.ChromaKeyV, msb)); break;

                case 0x0d: return(REGGETP(fx_vce.CCR, msb)); break;
                case 0x0e: return(REGGETP(fx_vce.BLE, msb)); break;
                case 0x0f: return(REGGETP(fx_vce.SPBL, msb)); break;
                case 0x10: return(REGGETP(fx_vce.coefficients[0], msb)); break;
                case 0x11: return(REGGETP(fx_vce.coefficients[1], msb)); break;

                case 0x12: return(REGGETP(fx_vce.coefficients[2], msb)); break;
                case 0x13: return(REGGETP(fx_vce.coefficients[3], msb)); break;

                case 0x14: return(REGGETP(fx_vce.coefficients[4], msb)); break;
                case 0x15: return(REGGETP(fx_vce.coefficients[5], msb)); break;
	      }
	      break;
 
 }

 return(0);
}

void FXVCE_Write8(uint32 A, uint8 V)
{
 int msb = A & 1;

 //if(A & 2)
 // return;

 switch(A & 0x304)
 {
  case 0x300: if(!msb)
	      {
	       fx_vce.AR = V;
	       fx_vce.AR &= 0x1F;
	      }
	      break;
  case 0x304: switch(fx_vce.AR)
	      {
		case 0x00: REGSETP(fx_vce.picture_mode, V, msb); 
			   //fx_vce.picture_mode &= 0x7FCF;
			   RebuildLayerMaskCache();
			   FXVDC_SetDotClock(fx_vdc_chips[0], (fx_vce.picture_mode & 0x08) ? 1 : 0);
			   FXVDC_SetDotClock(fx_vdc_chips[1], (fx_vce.picture_mode & 0x08) ? 1 : 0);
			   break;
		case 0x01: REGSETP(fx_vce.palette_rw_offset, V, msb); 
			   fx_vce.palette_rw_offset &= 0x1FF;

			   if(msb)
			    fx_vce.palette_rw_latch = fx_vce.palette_table[fx_vce.palette_rw_offset];
			   break;
		case 0x02: REGSETP(fx_vce.palette_rw_latch, V, msb); 
			   if(msb)
			   {
			    fx_vce.palette_table[fx_vce.palette_rw_offset] = fx_vce.palette_rw_latch;
			    RedoPaletteCache(fx_vce.palette_rw_offset);
			    fx_vce.palette_rw_offset = (fx_vce.palette_rw_offset + 1) & 0x1FF;
			   }
			   break;
		case 0x04: REGSETP(fx_vce.palette_offset[0], V, msb); break;
		case 0x05: REGSETP(fx_vce.palette_offset[1], V, msb); break;
		case 0x06: REGSETP(fx_vce.palette_offset[2], V, msb); break;
		case 0x07: REGSETP(fx_vce.palette_offset[3], V, msb); fx_vce.palette_offset[3] &= 0x00FF; break;
		case 0x08: REGSETP(fx_vce.priority[0], V, msb); fx_vce.priority[0] &= 0x0777; break;
		case 0x09: REGSETP(fx_vce.priority[1], V, msb); fx_vce.priority[1] &= 0x7777; break;

		case 0x0a: REGSETP(fx_vce.ChromaKeyY, V, msb); break;
                case 0x0b: REGSETP(fx_vce.ChromaKeyU, V, msb); break;
                case 0x0c: REGSETP(fx_vce.ChromaKeyV, V, msb); break;

		case 0x0d: REGSETP(fx_vce.CCR, V, msb); break;
		case 0x0e: REGSETP(fx_vce.BLE, V, msb); break;
		case 0x0f: REGSETP(fx_vce.SPBL, V, msb); break;

		case 0x10: REGSETP(fx_vce.coefficients[0], V, msb); fx_vce.coefficients[0] &= 0xFFF; break;
                case 0x11: REGSETP(fx_vce.coefficients[1], V, msb); fx_vce.coefficients[1] &= 0xFFF; break;

                case 0x12: REGSETP(fx_vce.coefficients[2], V, msb); fx_vce.coefficients[2] &= 0xFFF; break;
                case 0x13: REGSETP(fx_vce.coefficients[3], V, msb); fx_vce.coefficients[3] &= 0xFFF; break;

                case 0x14: REGSETP(fx_vce.coefficients[4], V, msb); fx_vce.coefficients[4] &= 0xFFF; break;
                case 0x15: REGSETP(fx_vce.coefficients[5], V, msb); fx_vce.coefficients[5] &= 0xFFF; break;
	      }
	      break;
 }
 //if(msb && (A & 0x304) == 0x304 && ((fx_vce.AR >= 0x0A && fx_vce.AR <= 0x15) || fx_vce.AR == 0x04))
 // printf("POFF: %04x, YCK: %04x, UCK: %04x, VCK: %04x, CCR: %04x, BLE: %04x, SPBL: %04x, C0: %04x, C1: %04x, C2: %04x, C3: %04x, C4: %04x, C5: %04x,\n", fx_vce.palette_offset[0], fx_vce.ChromaKeyY, fx_vce.ChromaKeyU, fx_vce.ChromaKeyV, fx_vce.CCR, fx_vce.BLE, fx_vce.SPBL, fx_vce.coefficients[0], fx_vce.coefficients[1], fx_vce.coefficients[2], fx_vce.coefficients[3], fx_vce.coefficients[4], fx_vce.coefficients[5]);
}

static void RedoKINGIRQCheck(void)
{
 bool asserted = 0;

 for(int ch = 0; ch < 2; ch++)
  if((king.ADPCMBufferMode[ch] >> 1) & 0x3 & king.ADPCMStatus[ch]) // Trigger end/intermediate IRQ
  {
   //puts("IRQd!");
   asserted = 1;
  }

 if(king.DMAInterrupt && (king.DMAStatus & 0x2))
 {
  asserted = 1;
 }

 if(king.CDInterrupt)
 {
  asserted = 1;
  puts("Right");
 }
 if(king.RasterIRQPending)
  asserted = 1;

 PCFXIRQ_Assert(13, asserted);
}

void KING_CDIRQ(void)
{
// king.CDInterrupt = TRUE;
// RedoKINGIRQCheck();
}

uint32 KING_Read32(uint32 A)
{
 return(KING_Read16(A) | (KING_Read16(A | 2) << 16));
}

void KING_Write32(uint32 A, uint32 V)
{
 KING_Write16(A, V);
 KING_Write16(A | 2, V >> 16);
}

uint8 KING_Read8(uint32 A)
{
 uint8 ret = KING_Read16(A & ~1) >> ((A & 1) * 8);
 return(ret);
}

static INLINE uint8 AutoRead(void)
{
 if(SCSICD_GetREQ() && !SCSICD_GetACK())
 {
  if(SCSICD_GetIO())
  {
   king.data_cache = SCSICD_GetDB();
   SCSICD_SetACK(TRUE);
   SCSICD_Run();
  }
  return(0x40);
 }
 else if(SCSICD_GetACK() && !SCSICD_GetREQ())
 {
  SCSICD_SetACK(FALSE);
  SCSICD_Run();
  return(0x00);
 }
 return(0x00);
}

uint16 KING_Read16(uint32 A)
{
 int msh = A & 2;

 //printf("KRead16: %08x\n", A);

 switch(A & 0x704)
 {
  case 0x600:   // ?CDR?P??  AAAAAAAA
		// P = 0x0400, something to do with ADPCM interrupts
		// C = 0x4000, something to do with CD-ROM command complete interrupts or maybe data transfer ready?
		// D = 0x2000, something to do with CD-ROM data transfer complete interrupts or DMA?
		// R = 0x1000, CD Interrupt?  raster IRQ?
		//printf("Botolick: %04x\n", A);
	      if(!msh)
	      {
	       uint16 ret = king.AR;

	       //if(king.ADPCMStatus[0])
		//ret |= 0x400;
	       //if(king.ADPCMStatus[1])
		//ret |= 0x800;

	       if(king.ADPCMStatus[0] & 0x3 & (king.ADPCMBufferMode[0] >> 1))
		ret |= 0x400;

	       if(king.ADPCMStatus[1] & 0x3 & (king.ADPCMBufferMode[1] >> 1))
                ret |= 0x400;

	       if(king.DMAInterrupt && (king.DMAStatus & 0x2)) ret |= 0x2000;
	       if(king.CDInterrupt) ret |= 0x4000;
	       if(king.RasterIRQPending) ret |= 0x1000;
	       return(ret);
	      }
	      else
	      {
		uint16 ret = 0;
		ret |= SCSICD_GetIO() ? 0x04 : 0x00;
		ret |= SCSICD_GetCD() ? 0x08 : 0x00;
		ret |= SCSICD_GetMSG() ? 0x10 : 0x00;
		ret |= SCSICD_GetREQ() ? 0x20 : 0x00;
		ret |= SCSICD_GetBSY() ? 0x40 : 0x00;
		ret |= SCSICD_GetATN() ? 0x80 : 0x00;

		return(ret);
 	      }
	      break; // status...

  case 0x604: switch(king.AR)
	      {
		default: printf("Dark16: %02x\n", king.AR);
		case 0x00:
                	//printf("Dark16: %02x\n", king.AR);
			return(SCSICD_GetDB());
		case 0x01:
                        //printf("Dark16: %02x\n", king.AR);
			return((SCSICD_GetkingBSY()  ? 0x1 : 0) | (SCSICD_GetSEL() ? 0x4 : 0) | (SCSICD_GetACK() ? 0x10 : 0) | (SCSICD_GetRST() ? 0x80 : 0));
		case 0x03:
                        //printf("Dark16: %02x\n", king.AR);
			return( (SCSICD_GetkingIO() ? 0x1 : 0) | (SCSICD_GetkingCD() ? 0x2 : 0) | (SCSICD_GetkingMSG() ? 0x4 : 0) );
		case 0x05: 
		        if(msh)
			{
			 return(king.data_cache);
			}
			else
			{
			 return(AutoRead());
			}
			break;
		case 0x07: // Value read in the BIOS always seems to be discarded...  SCSI IRQ acknowledge?
			//printf("Dark16: %02x\n", king.AR);
			king.CDInterrupt = FALSE;
                        RedoKINGIRQCheck();
			return(0x0);

		case 0x08: return(0);

		case 0x0B: // Value read in the BIOS always seems to be discarded...  DMA IRQ acknowledge?
                        king.DMAInterrupt = 0;
                        RedoKINGIRQCheck();
			//printf("Dark16: %02x\n", king.AR);
		 	return(king.DMAStatus);
		case 0x0C: return(REGGETHW(king.KRAMRA, msh));
		case 0x0D: return(REGGETHW(king.KRAMWA, msh));
		case 0x0E:
                         {
                          unsigned int page = (king.KRAMRA & 0x80000000) ? 1 : 0;
                          uint16 ret = king.KRAM[page][king.KRAMRA & 0x3FFFF];

	                  #ifdef WANT_DEBUGGER 
			  if(KRAMReadBPE) 
			   PCFXDBG_CheckBP(BPOINT_AUX_READ, (king.KRAMRA & 0x3FFFF) | (page ? 0x40000 : 0), 1);
			  #endif
	
                          king.KRAMRA = (king.KRAMRA &~ 0x3FFFF) | ((king.KRAMRA + ((king.KRAMRA >> 18) & 0x1FF)) & 0x3FFFF);
			  return(ret);
                          }
                          break;
		case 0x0F: return(king.PageSetting);
                case 0x10: return(REGGETHW(king.bgmode, msh));
		case 0x15: return(king.MPROGControl);

		case 0x53:
			  {
			   uint16 ret = king.ADPCMStatus[0] | (king.ADPCMStatus[1] << 2);
			   king.ADPCMStatus[0] = king.ADPCMStatus[1] = 0;
			   RedoKINGIRQCheck();
			   return(ret);
			  }
			  break;
	      }
	      break;
	      
 }

 return(0);
}

void KING_Write8(uint32 A, uint8 V)
{
 switch(A & 0x70F)
 {
  case 0x604:
	      switch(king.AR)
	      {
	       default: printf("Wr8: %04x %02x\n", king.AR, V); break;
	       case 0x40: king.RAINBOWTransferControl = V & 0x3; break;
	       case 0x0B:
	       case 0x15:
	       case 0x50: 
	       case 0x51:
	       case 0x52: KING_Write16(A, V); break;
	      }
	      break;
 }

}
void KING_Write16(uint32 A, uint16 V)
{
 //if(A == 0x600 || A == 0x602 || king.AR != 0xe)
 // printf("KWrite16: %08x %04x\n", A, V);

 int msh = A & 0x2;
 switch(A & 0x704)
 {
  //default: printf("BNORK: %08x %04x, %08x\n", A, V, king.AR); break;
  case 0x600: if(!msh) 
		king.AR = V & 0x7F; 
	      break;

  case 0x604: switch(king.AR)
	      {
		default: printf("KING: %02x %04x %d\n", king.AR, V, msh); break;
		case 0x00: if(!msh) 
			    SCSICD_SetDB(V);
			   break;
		case 0x01: if(!msh)
			   {
			    SCSICD_SetBSY(V & 1); 
			    SCSICD_SetATN(V & 2);
			    SCSICD_SetSEL(V & 4);
			    SCSICD_SetACK(V & 0x10);
			    SCSICD_SetRST(V & 0x80);
			    SCSICD_Run();
			    //if(V & 0x10) puts("SetACK"); else puts("ClearACK");
			   }
			   break;
		case 0x02: // Starts transfer sequence??
			   //printf("Choo: %02x\n", V);
			   if(!msh)
			   {
			    if(king.Port02 && !V) { SCSICD_SetACK(0); SCSICD_Run(); }
			    king.Port02 = V;
			   }
			   break;
		case 0x03: if(!msh)
			   {
			    SCSICD_SetIO(V & 1);
			    SCSICD_SetCD(V & 2);
			    SCSICD_SetMSG(V & 4);
			    SCSICD_Run();
			   }
			   break;

		case 0x05: if(msh) // Strange, used for DATA OUT phase for MODE SELECT command...
			   {
			    SCSICD_SetDB(V);

			    if(!SCSICD_GetIO())
			    {
                             if(SCSICD_GetREQ() && !SCSICD_GetACK())
			     {
                              SCSICD_SetACK(TRUE);
			      SCSICD_Run();
			     }
                             else if(SCSICD_GetACK() && !SCSICD_GetREQ())
			     {
                              SCSICD_SetACK(FALSE);                             
			      SCSICD_Run();
			     }
			    }
			   }
			   break;
		case 0x07: // Unknown
			   break;
		case 0x08: // Unknown
			   break;
                case 0x09: REGSETHW(king.DMATransferAddrLatch, V, msh); 
			   king.DMATransferAddr = (king.DMATransferAddrLatch & 0x3FFFF) << 1; 
			   break;
                case 0x0A: REGSETHW(king.DMATransferSize, V, msh); 
			   break;
		case 0x0B: REGSETHW(king.DMAStatus, V, msh);
			   king.DMAStatus &= 0x3;
			   king.DMAInterrupt = 0;
			   RedoKINGIRQCheck();
			   //printf("DMAControlWrite: %02x %08x %08x, PS: %08x\n", V, king.DMATransferAddrLatch, king.DMATransferSize, king.PageSetting);
			   break;
                case 0x0C: REGSETHW(king.KRAMRA, V, msh); if( (king.KRAMWA &~(0x8003FFFF | (0xFF << 18)))) printf("SRA: %08x\n", king.KRAMRA); break;
                case 0x0D: REGSETHW(king.KRAMWA, V, msh); if( (king.KRAMWA &~(0x8003FFFF | (0xFF << 18)))) printf("SWA: %08x\n", king.KRAMWA); break;
		case 0x0E: 
			  {
			   unsigned int page = (king.KRAMWA & 0x80000000) ? 1 : 0;

		           #ifdef WANT_DEBUGGER 
                           if(KRAMWriteBPE)
                            PCFXDBG_CheckBP(BPOINT_AUX_WRITE, (king.KRAMWA & 0x3FFFF) | (page ? 0x40000 : 0), 1);
			   #endif

			   //printf("Write: %08x %08x, %04x, %04x\n", king.KRAMWA & 0x3FFFF, king.KRAMWA, ((king.KRAMWA >> 18) & 0x1FF), V);
			   king.KRAM[page][king.KRAMWA & 0x3FFFF] = V;
			   king.KRAMWA = (king.KRAMWA &~ 0x3FFFF) | ((king.KRAMWA + ((king.KRAMWA >> 18) & 0x1FF)) & 0x3FFFF);
			  }
			  break;
		case 0x0F: REGSETHW(king.PageSetting, V, msh); break;
		case 0x10: REGSETHW(king.bgmode, V, msh); break;
		case 0x12: REGSETHW(king.priority, V, msh); break;

		case 0x13: break; // Microprogram address
		case 0x14: break; // Microprogram data
		case 0x15: REGSETHW(king.MPROGControl, V, msh); break;

		case 0x16: REGSETHW(king.BGScrollMode, V, msh); king.BGScrollMode &= 0xF; break;

		case 0x20: REGSETHW(king.BGBATAddr[0], V, msh); king.BGBATAddr[0] &= 0x1FF; break;
		case 0x21: REGSETHW(king.BGCGAddr[0], V, msh); king.BGCGAddr[0] &= 0x1FF; break;
		case 0x22: REGSETHW(king.BG0SubBATAddr, V, msh); king.BG0SubBATAddr &= 0x1FF; break;
	  	case 0x23: REGSETHW(king.BG0SubCGAddr, V, msh); king.BG0SubCGAddr &= 0x1FF; break;

		case 0x24: REGSETHW(king.BGBATAddr[1], V, msh); king.BGBATAddr[1] &= 0x1FF; break;
		case 0x25: REGSETHW(king.BGCGAddr[1], V, msh); king.BGCGAddr[1] &= 0x1FF; break;
		case 0x28: REGSETHW(king.BGBATAddr[2], V, msh); king.BGBATAddr[2] &= 0x1FF; break;
		case 0x29: REGSETHW(king.BGCGAddr[2], V, msh); king.BGCGAddr[2] &= 0x1FF; break;
		case 0x2A: REGSETHW(king.BGBATAddr[3], V, msh); king.BGBATAddr[3] &= 0x1FF; break;
		case 0x2B: REGSETHW(king.BGCGAddr[3], V, msh); king.BGCGAddr[3] &= 0x1FF; break;

		case 0x2C: REGSETHW(king.BGSize[0], V, msh); break;
		case 0x2D: REGSETHW(king.BGSize[1], V, msh); king.BGSize[1] &= 0x00FF; break;
		case 0x2E: REGSETHW(king.BGSize[2], V, msh); king.BGSize[2] &= 0x00FF; break;
		case 0x2F: REGSETHW(king.BGSize[3], V, msh); king.BGSize[3] &= 0x00FF; break;

		// FIXME:  How many bits should the scroll registers have?
		case 0x30: REGSETHW(king.BGXScroll[0], V, msh); break;
		case 0x31: REGSETHW(king.BGYScroll[0], V, msh); break;

		case 0x32: REGSETHW(king.BGXScroll[1], V, msh); break;
		case 0x33: REGSETHW(king.BGYScroll[1], V, msh); break;

		case 0x34: REGSETHW(king.BGXScroll[2], V, msh); break;
		case 0x35: REGSETHW(king.BGYScroll[2], V, msh); break;

		case 0x36: REGSETHW(king.BGXScroll[3], V, msh); break;
		case 0x37: REGSETHW(king.BGYScroll[3], V, msh); break;

		// Are these the rotation registers??
		case 0x38: break;
		case 0x39: break;
		case 0x3a: break;
		case 0x3b: break;
		case 0x3c: break;
		case 0x3d: break;

		case 0x40: // ------IE
			   // I = 1, interrupt enable??
			   // E = 1, rainbow transer start
			   if(!msh)
			   {
			    king.RAINBOWTransferControl = V & 0x3;
			   }
			   king.RasterIRQPending = FALSE;
			   RedoKINGIRQCheck();
			   break;
                case 0x41: REGSETHW(king.RAINBOWKRAMA, V, msh); king.RAINBOWKRAMA &= 0x3FFFF; break; // Rainbow transfer address
		case 0x42: if(!msh) king.RAINBOWTransferStartPosition = V & 0x1FF; break; // 0-262
		case 0x43: REGSETHW(king.RAINBOWTransferBlockCount, V, msh); break;
		case 0x44: if(!msh) king.RasterIRQLine = V & 0x1FF; 
			   break; // Raster IRQ line

		case 0x50: 
			   if(!msh)
			   {
			    if(!(king.ADPCMControl & 1) && (V & 1))
			    {
			     king.ADPCMStartAddress[0] = king.ADPCMPlayAddress[0] = king.ADPCMSAL[0] * 256;
			     king.ADPCMStatus[0] = 0;
			    }
                            if(!(king.ADPCMControl & 2) && (V & 2))
                            {
                             king.ADPCMStartAddress[1] = king.ADPCMPlayAddress[1] = king.ADPCMSAL[1] * 256;
			     king.ADPCMStatus[1] = 0;
                            }
			    //printf("ADPCMControl: %04x %04x\n", V, king.ADPCMControl);
			    king.ADPCMControl = V; 
			    RedoKINGIRQCheck();
			    SoundBox_SetKINGADPCMControl(king.ADPCMControl);
			   }
			   break;
		case 0x51: REGSETHW(king.ADPCMBufferMode[0], V, msh); 
			   //printf("BM0: %08x\n", king.ADPCMBufferMode[0]);
			   RedoKINGIRQCheck(); 
			   break;
		case 0x52: REGSETHW(king.ADPCMBufferMode[1], V, msh); RedoKINGIRQCheck(); break;

		//case 0x53:
		//case 0x54:
		//case 0x55:
		//case 0x56:
		//case 0x57:

		case 0x58: REGSETHW(king.ADPCMSAL[0], V, msh); king.ADPCMSAL[0] &= 0x3FF; 
			   //if(!msh) printf("SAL: %08x, CPA: %08x\n", V << 8, king.ADPCMPlayAddress[0]); 
			   break;
		case 0x59: REGSETHW(king.ADPCMEndAddress[0], V, msh); king.ADPCMEndAddress[0] &= 0x3FFFF; 
			   //if(msh) printf("ENDADD: %08x\n", king.ADPCMEndAddress[0]);
			   break;
		case 0x5A: REGSETHW(king.ADPCMIntermediateAddress[0], V, msh); king.ADPCMIntermediateAddress[0] &= 0xFFF; 
			   //if(!msh) printf("Intermediate write: %08x\n", king.ADPCMIntermediateAddress[0] << 6);
			   break;
		
		case 0x5C: REGSETHW(king.ADPCMSAL[1], V, msh); king.ADPCMSAL[1] &= 0x3FF; break;
		case 0x5D: REGSETHW(king.ADPCMEndAddress[1], V, msh); king.ADPCMEndAddress[1] &= 0x3FFFF; break;
		case 0x5E: REGSETHW(king.ADPCMIntermediateAddress[1], V, msh); king.ADPCMIntermediateAddress[1] &= 0xFFF; break;


		case 0x61: break; // The official WTF register!
	      }
	      break;

 }
}

uint16 KING_GetADPCMHalfWord(int ch)
{
 int page = (king.PageSetting & 0x0100) ? 1 : 0;
 uint16 ret = king.KRAM[page][(king.ADPCMPlayAddress[ch] & 0x3FFFF)];

 //king.ADPCMPlayAddress[ch] = (king.ADPCMPlayAddress[ch] & 0x20000) | ((king.ADPCMPlayAddress[ch] + 1) & 0x1FFFF); //(king.ADPCMPlayAddress[ch] + 1) & 0x3FFFF;
 king.ADPCMPlayAddress[ch] = (king.ADPCMPlayAddress[ch] + 1) & 0x3FFFF;

 if(king.ADPCMPlayAddress[ch] == ((king.ADPCMEndAddress[ch] + 1) & 0x3FFFF) )
 {
  //printf("End: %d %d %08x %08x\n", ch, king.ADPCMBufferMode[ch], king.ADPCMSAL[ch] << 8, king.ADPCMStartAddress[ch]);
  if(!(king.ADPCMBufferMode[ch] & 1))
  {
   king.ADPCMControl &= ~(1 << ch);
   SoundBox_SetKINGADPCMControl(king.ADPCMControl);
  }
  else
  {
   king.ADPCMPlayAddress[ch] = king.ADPCMSAL[ch] << 8;
  }
  king.ADPCMStatus[ch] |= 1;
  RedoKINGIRQCheck();
 }
 else if(king.ADPCMPlayAddress[ch] == ((uint32)king.ADPCMIntermediateAddress[ch] << 6) )
 {
  king.ADPCMStatus[ch] |= 2;
  RedoKINGIRQCheck();
 }
 else
 {

 }

 // MDFN_DispMessage((UTF8*)"%08x", king.ADPCMPlayAddress[ch]);

 return(ret);
}

static uint32 TTRANS_LUT[256];
static const uint32 TRANS_OR = 0x1 << 24;
static const uint32 TRANS_OR_SHIFT = 24;

#define TTRANS(_testvar) TTRANS_LUT[_testvar] //((_testvar) ? 0 : TRANS_OR)

bool KING_Init(void)
{
 BGLayerDisable = 0;

 BuildCMT();
 for(int x = 0; x < 512; x++)
  priority_0_dummybuf[x] = TRANS_OR;

 // Build happy map!
 for(int bg_prio = 0; bg_prio < 17; bg_prio++)
  for(int vdc_prio = 0; vdc_prio < 17; vdc_prio++)
   for(int rainbow_prio = 0; rainbow_prio < 17; rainbow_prio++)
   {
    if(!bg_prio || bg_prio > 8)
     happymap[bg_prio][vdc_prio][rainbow_prio][0] = 3;
    else
    {
     if(bg_prio < vdc_prio && bg_prio < rainbow_prio)
      happymap[bg_prio][vdc_prio][rainbow_prio][0] = 0;
     else if(bg_prio > vdc_prio && bg_prio > rainbow_prio)
      happymap[bg_prio][vdc_prio][rainbow_prio][0] = 2;
     else 
      happymap[bg_prio][vdc_prio][rainbow_prio][0] = 1;
    }

    if(!vdc_prio || vdc_prio > 8)
     happymap[bg_prio][vdc_prio][rainbow_prio][1] = 3;
    else
    {
     if(vdc_prio < bg_prio && vdc_prio < rainbow_prio)
      happymap[bg_prio][vdc_prio][rainbow_prio][1] = 0;
     else if(vdc_prio > bg_prio && vdc_prio > rainbow_prio)
      happymap[bg_prio][vdc_prio][rainbow_prio][1] = 2;
     else
      happymap[bg_prio][vdc_prio][rainbow_prio][1] = 1;
    }

    if(!rainbow_prio || rainbow_prio > 8)
     happymap[bg_prio][vdc_prio][rainbow_prio][2] = 3;
    else
    {
     if(rainbow_prio < bg_prio && rainbow_prio < vdc_prio)     
      happymap[bg_prio][vdc_prio][rainbow_prio][2] = 0;
     else if(rainbow_prio > bg_prio && rainbow_prio > vdc_prio)
      happymap[bg_prio][vdc_prio][rainbow_prio][2] = 2;
     else
      happymap[bg_prio][vdc_prio][rainbow_prio][2] = 1;
    }
   }

 for(int x = 0; x < 256; x++)
  TTRANS_LUT[x] = x ? 0 : TRANS_OR;

 SCSICD_Init();

 return(1);
}

static INLINE void DRAWBG8x1_4(uint32 *target, unsigned int pbn, uint16 cg, uint32 *palette_ptr, uint32 layer_or)
{
 target[0] = TTRANS((cg >> 14) & 0x3) | palette_ptr[pbn | ((cg >> 14) & 0x3)] | layer_or;
 target[1] = TTRANS((cg >> 12) & 0x3) | palette_ptr[pbn | ((cg >> 12) & 0x3)] | layer_or;
 target[2] = TTRANS((cg >> 10) & 0x3) | palette_ptr[pbn | ((cg >> 10) & 0x3)] | layer_or;
 target[3] = TTRANS((cg >> 8) & 0x3) | palette_ptr[pbn | ((cg >> 8) & 0x3)] | layer_or;
 target[4] = TTRANS((cg >> 6) & 0x3) | palette_ptr[pbn | ((cg >> 6) & 0x3)] | layer_or;
 target[5] = TTRANS((cg >> 4) & 0x3) | palette_ptr[pbn | ((cg >> 4) & 0x3)] | layer_or;
 target[6] = TTRANS((cg >> 2) & 0x3) | palette_ptr[pbn | ((cg >> 2) & 0x3)] | layer_or;
 target[7] = TTRANS((cg >> 0) & 0x3) | palette_ptr[pbn | ((cg >> 0) & 0x3)] | layer_or;
}

static INLINE void DRAWBG8x1_16(uint32 *target, unsigned int pbn, uint16 *cgptr, uint32 *palette_ptr, uint32 layer_or)
{
 target[0] = TTRANS((cgptr[0] >> 12) & 0xF) | palette_ptr[(pbn + ((cgptr[0] >> 12) & 0xF))] | layer_or;
 target[1] = TTRANS((cgptr[0] >> 8) & 0xF) | palette_ptr[(pbn + ((cgptr[0] >> 8) & 0xF))] | layer_or;
 target[2] = TTRANS((cgptr[0] >> 4) & 0xF) | palette_ptr[(pbn + ((cgptr[0] >> 4) & 0xF))] | layer_or;
 target[3] = TTRANS((cgptr[0] >> 0) & 0xF) | palette_ptr[(pbn + ((cgptr[0] >> 0) & 0xF))] | layer_or;
 target[4] = TTRANS((cgptr[1] >> 12) & 0xF) | palette_ptr[(pbn + ((cgptr[1] >> 12) & 0xF))] | layer_or;
 target[5] = TTRANS((cgptr[1] >> 8) & 0xF) | palette_ptr[(pbn + ((cgptr[1] >> 8) & 0xF))] | layer_or;
 target[6] = TTRANS((cgptr[1] >> 4) & 0xF) | palette_ptr[(pbn + ((cgptr[1] >> 4) & 0xF))] | layer_or;
 target[7] = TTRANS((cgptr[1] >> 0) & 0xF) | palette_ptr[(pbn + ((cgptr[1] >> 0) & 0xF))] | layer_or;
}

static INLINE void DRAWBG8x1_256(uint32 *target, uint16 *cgptr, uint32 *palette_ptr, uint32 layer_or)
{
 target[0] = TTRANS(cgptr[0] >> 0x8) | palette_ptr[(cgptr[0] >> 0x8)] | layer_or;
 target[1] = TTRANS(cgptr[0] & 0xFF) | palette_ptr[(cgptr[0] & 0xFF)] | layer_or;
 target[2] = TTRANS(cgptr[1] >> 0x8) | palette_ptr[(cgptr[1] >> 0x8)] | layer_or;
 target[3] = TTRANS(cgptr[1] & 0xFF) | palette_ptr[(cgptr[1] & 0xFF)] | layer_or;
 target[4] = TTRANS(cgptr[2] >> 0x8) | palette_ptr[(cgptr[2] >> 0x8)] | layer_or;
 target[5] = TTRANS(cgptr[2] & 0xFF) | palette_ptr[(cgptr[2] & 0xFF)] | layer_or;
 target[6] = TTRANS(cgptr[3] >> 0x8) | palette_ptr[(cgptr[3] >> 0x8)] | layer_or;
 target[7] = TTRANS(cgptr[3] & 0xFF) | palette_ptr[(cgptr[3] & 0xFF)] | layer_or;
}

static INLINE void DRAWBG8x1_64K(uint32 *target, uint16 *cgptr, uint32 *palette_ptr, uint32 layer_or)
{
 target[0] = TTRANS(cgptr[0] >> 8) | ((cgptr[0x0] & 0x00F0) << 8) | ((cgptr[0] & 0x000F)<<4) | ((cgptr[0] & 0xFF00) << 8) | layer_or;
 target[1] = TTRANS(cgptr[1] >> 8) | ((cgptr[0x1] & 0x00F0) << 8) | ((cgptr[1] & 0x000F)<<4) | ((cgptr[1] & 0xFF00) << 8) | layer_or;
 target[2] = TTRANS(cgptr[2] >> 8) | ((cgptr[0x2] & 0x00F0) << 8) | ((cgptr[2] & 0x000F)<<4) | ((cgptr[2] & 0xFF00) << 8) | layer_or;
 target[3] = TTRANS(cgptr[3] >> 8) | ((cgptr[0x3] & 0x00F0) << 8) | ((cgptr[3] & 0x000F)<<4) | ((cgptr[3] & 0xFF00) << 8) | layer_or;
 target[4] = TTRANS(cgptr[4] >> 8) | ((cgptr[0x4] & 0x00F0) << 8) | ((cgptr[4] & 0x000F)<<4) | ((cgptr[4] & 0xFF00) << 8) | layer_or;
 target[5] = TTRANS(cgptr[5] >> 8) | ((cgptr[0x5] & 0x00F0) << 8) | ((cgptr[5] & 0x000F)<<4) | ((cgptr[5] & 0xFF00) << 8) | layer_or;
 target[6] = TTRANS(cgptr[6] >> 8) | ((cgptr[0x6] & 0x00F0) << 8) | ((cgptr[6] & 0x000F)<<4) | ((cgptr[6] & 0xFF00) << 8) | layer_or;
 target[7] = TTRANS(cgptr[7] >> 8) | ((cgptr[0x7] & 0x00F0) << 8) | ((cgptr[7] & 0x000F)<<4) | ((cgptr[7] & 0xFF00) << 8) | layer_or;
}

static INLINE void DRAWBG8x1_16M(uint32 *target, uint16 *cgptr, uint32 *palette_ptr, uint32 layer_or)
{
 target[0] = TTRANS(cgptr[0] >> 8) | ((cgptr[0x0] & 0xFF00) << 8) | (cgptr[1] & 0xFF00) | (cgptr[1] & 0xFF) | layer_or;
 target[1] = TTRANS(cgptr[0] >> 8) | ((cgptr[0x0] & 0x00FF) << 16) | (cgptr[1] & 0xFF00) | (cgptr[1] & 0xFF) | layer_or;
 target[2] = TTRANS(cgptr[2] >> 8) | ((cgptr[0x2] & 0xFF00) << 8) | (cgptr[3] & 0xFF00) | (cgptr[3] & 0xFF) | layer_or;
 target[3] = TTRANS(cgptr[2] >> 8) | ((cgptr[0x2] & 0x00FF) << 16) | (cgptr[3] & 0xFF00) | (cgptr[3] & 0xFF) | layer_or;
 target[4] = TTRANS(cgptr[4] >> 8) | ((cgptr[0x4] & 0xFF00) << 8) | (cgptr[5] & 0xFF00) | (cgptr[5] & 0xFF) | layer_or;
 target[5] = TTRANS(cgptr[4] >> 8) | ((cgptr[0x4] & 0x00FF) << 16) | (cgptr[5] & 0xFF00) | (cgptr[5] & 0xFF) | layer_or;
 target[6] = TTRANS(cgptr[6] >> 8) | ((cgptr[0x6] & 0xFF00) << 8) | (cgptr[7] & 0xFF00) | (cgptr[7] & 0xFF) | layer_or;       
 target[7] = TTRANS(cgptr[6] >> 8) | ((cgptr[0x6] & 0x00FF) << 16) | (cgptr[7] & 0xFF00) | (cgptr[7] & 0xFF) | layer_or;
}

// Loop postfix endless
#define DRAWBG8x1_LPE() bat_x = (bat_x + 1) & bat_width_mask;

// Loop postfix non-endless
#define DRAWBG8x1_LPNE() bat_x++; if(bat_x >= bat_width) { x += 8; for(; x < end; x++) {target[x] = TRANS_OR | layer_or;} break; }

static const unsigned int ClockModeWidths[2] = { 288, 384 };

static void DrawBG(uint32 *target, int n, bool sub)
{
 uint16 bgmode = (king.bgmode >> (n * 4)) & 0xF;
 bool endless = (king.BGScrollMode >> n) & 0x1;
 uint32 XScroll = king.BGXScroll[n];
 uint32 YScroll = king.BGYScroll[n];
 uint32 bat_offset = king.BGBATAddr[n] * 1024;
 uint32 cg_offset = king.BGCGAddr[n] * 1024;
 uint32 bat_and_cg_bank = (king.PageSetting & 0x0010) ? 1 : 0;

 const int start = 8;
 const int end = 8 + 256;

 int bat_width, bat_width_mask, bat_width_shift;
 int bat_height, bat_height_mask, bat_height_shift;

 if(sub)
 {
  bat_offset = king.BG0SubBATAddr * 1024;
  cg_offset = king.BG0SubCGAddr * 1024;
  endless = TRUE;
  //XScroll = 0;
  //YScroll = 0;
 }

 if(sub)
 {
  bat_width_shift = (king.BGSize[n] & 0xF000) >> 12;
  bat_height_shift = (king.BGSize[n] & 0x0F00) >> 8;
 }
 else
 {
  bat_width_shift = (king.BGSize[n] & 0xF0) >> 4;
  bat_height_shift = king.BGSize[n] & 0x0F;
 }

 if(bat_width_shift < 3) bat_width_shift = 3;
 if(bat_width_shift > 9) bat_width_shift = 9;

 bat_width = (1 << bat_width_shift) >> 3;
 bat_width_mask = bat_width - 1;

 if(bat_height_shift < 3) bat_height_shift = 3;
 if(bat_height_shift > 9) bat_height_shift = 9;

 bat_height = (1 << bat_height_shift) >> 3;
 bat_height_mask = bat_height - 1;


 uint32 YOffset = (YScroll + (fx_vce.frame_counter - 22)) & 0xFFFF;
 uint32 layer_or = n << 28;

 int first_end = start - (XScroll & 0x7);

 //int bat_y = (((YOffset >> 3) & bat_height_mask) << bat_width_shift) >> 3;
 //int bat_x = (XScroll >> 3); // & bat_width_mask;

 int bat_y = YOffset >> 3;
 int bat_x = XScroll >> 3;
 int ysmall = YOffset & 0x7;

 if(!endless && (bat_y >= bat_height || bat_x >= bat_width)) // Draw this line as transparency and return?
 {
  for(int x = first_end; x < end; x++) target[x] = TRANS_OR | layer_or;
  return;
 }

 if(endless)
  bat_x &= bat_width_mask;

 // Adjust/corrupt bat_y to be faster in our blitting code
 bat_y = ((bat_y & bat_height_mask) << bat_width_shift) >> 3;

 uint32 palette_offset = fx_vce.palette_offset[1 + (n >> 1)] >> ((n & 1) ? 8 : 0);
 palette_offset <<= 1;
 palette_offset &= 0x1FF;

 uint32 *palette_ptr = &fx_vce.palette_table_cache[palette_offset];

 if(!(bgmode & 0x7) || (bgmode & 0x7) > 5)
 {
  for(int x = first_end; x < end; x++) 
   target[x] = TRANS_OR | layer_or;
 }
 else if(!(bgmode & 0x8))
 {
  // Access the CG directly.
  int wmul = (1 << bat_width_shift), wmask = (1 << bat_width_shift) - 1;
  int sexy_y_pos = (YOffset & wmask) * wmul;
  

  switch(bgmode & 0x7)
  {
   case 0x01: // 4 color, 1/4 byte per pixel :b
        sexy_y_pos >>= 3;
	if(endless)
 	 for(int x = first_end; x < end; x+= 8)
	 {
          uint16 cg = king.KRAM[bat_and_cg_bank][(cg_offset + (bat_x * 1) + sexy_y_pos) & 0x3ffff];
	  DRAWBG8x1_4(target + x, 0, cg, palette_ptr, layer_or);
	  DRAWBG8x1_LPE();
	 }
	else
         for(int x = first_end; x < end; x+= 8)
         {
          uint16 cg = king.KRAM[bat_and_cg_bank][(cg_offset + (bat_x * 1) + sexy_y_pos) & 0x3ffff];
          DRAWBG8x1_4(target + x, 0, cg, palette_ptr, layer_or);
          DRAWBG8x1_LPNE();
         }
	break;
   case 0x02: // 16 color, 1/2 byte per pixel
	sexy_y_pos >>= 2;
	if(endless)
	 for(int x = first_end; x < end; x+= 8)
         {
          uint16 *cgptr = &king.KRAM[bat_and_cg_bank][(cg_offset + (bat_x * 2) + sexy_y_pos) & 0x3ffff];

          DRAWBG8x1_16(target + x, 0, cgptr, palette_ptr, layer_or);
          DRAWBG8x1_LPE();
         }
	else
         for(int x = first_end; x < end; x+= 8)
         {
          uint16 *cgptr = &king.KRAM[bat_and_cg_bank][(cg_offset + (bat_x * 2) + sexy_y_pos) & 0x3ffff];

          DRAWBG8x1_16(target + x, 0, cgptr, palette_ptr, layer_or);
          DRAWBG8x1_LPNE();
         }
        break;
   case 0x03: // 256 color, 1 byte per pixel palettized - OK
	sexy_y_pos >>= 1;
	if(endless)
	 for(int x = first_end; x < end; x+= 8)
	 {
	  uint16 *cgptr = &king.KRAM[bat_and_cg_bank][(cg_offset + (bat_x * 4) + sexy_y_pos) & 0x3ffff];

          DRAWBG8x1_256(target + x, cgptr, palette_ptr, layer_or);
          DRAWBG8x1_LPE();
	 }
	else
         for(int x = first_end; x < end; x+= 8)
         {
          uint16 *cgptr = &king.KRAM[bat_and_cg_bank][(cg_offset + (bat_x * 4) + sexy_y_pos) & 0x3ffff];

          DRAWBG8x1_256(target + x, cgptr, palette_ptr, layer_or);
          DRAWBG8x1_LPNE();
         }
	break;

   case 0x04: // 64K color, 2 bytes per pixel - OK
	if(endless)
	 for(int x = first_end; x < end; x+=8)
	 {
          uint16 *cgptr = &king.KRAM[bat_and_cg_bank][(cg_offset + (bat_x * 8) + sexy_y_pos) & 0x3ffff];


          DRAWBG8x1_64K(target + x, cgptr, palette_ptr, layer_or);
          DRAWBG8x1_LPE();
         }
	else
         for(int x = first_end; x < end; x+=8)
         {
          uint16 *cgptr = &king.KRAM[bat_and_cg_bank][(cg_offset + (bat_x * 8) + sexy_y_pos) & 0x3ffff];

          DRAWBG8x1_64K(target + x, cgptr, palette_ptr, layer_or);
          DRAWBG8x1_LPNE();
         }
	break;
   case 0x05: // 16M color, 2 bytes per pixel - OK
	if(endless)	
	 for(int x = first_end; x < end; x+=8)
	 {
	  uint16 *cgptr = &king.KRAM[bat_and_cg_bank][(cg_offset + (bat_x * 8) + sexy_y_pos) & 0x3ffff];

          DRAWBG8x1_16M(target + x, cgptr, palette_ptr, layer_or);
          DRAWBG8x1_LPE();
 	 }
	else
         for(int x = first_end; x < end; x+=8)
         {
          uint16 *cgptr = &king.KRAM[bat_and_cg_bank][(cg_offset + (bat_x * 8) + sexy_y_pos) & 0x3ffff];

          DRAWBG8x1_16M(target + x, cgptr, palette_ptr, layer_or);
          DRAWBG8x1_LPNE();
         }
	break;
  }
 }
 else
 {
  switch(bgmode & 0x7)
  {
    case 0x01: // 4 color
	if(endless)
	 for(int x = first_end; x < end; x+=8)
	 {
	  uint16 bat = king.KRAM[bat_and_cg_bank][(bat_offset + (bat_x + bat_y)) & 0x3ffff];
	  uint8 pbn = (bat >> 12) << 2;
	  bat &= 0x0FFF;

	  uint16 cg = king.KRAM[bat_and_cg_bank][(cg_offset + (bat * 8) + ysmall) & 0x3ffff];

          DRAWBG8x1_4(target + x, pbn, cg, palette_ptr, layer_or);
          DRAWBG8x1_LPE();
	 }
	else
         for(int x = first_end; x < end; x+=8)
         {
          uint16 bat = king.KRAM[bat_and_cg_bank][(bat_offset + (bat_x + bat_y)) & 0x3ffff];
          uint8 pbn = (bat >> 12) << 2;
          bat &= 0x0FFF;

          uint16 cg = king.KRAM[bat_and_cg_bank][(cg_offset + (bat * 8) + ysmall) & 0x3ffff];

          DRAWBG8x1_4(target + x, pbn, cg, palette_ptr, layer_or);
          DRAWBG8x1_LPNE();
	 }
        break;

    case 0x02: // 16 color - OK
	if(endless)
	 for(int x = first_end; x < end; x+=8)
	 {
	  uint16 bat = king.KRAM[bat_and_cg_bank][(bat_offset + (bat_x + bat_y)) & 0x3ffff];
	  uint32 pbn = ((bat >> 12) << 4);
	  bat &= 0x0FFF;
	  uint16 *cgptr = &king.KRAM[bat_and_cg_bank][(cg_offset + (bat * 16) + ysmall * 2) & 0x3ffff];

          DRAWBG8x1_16(target + x, pbn, cgptr, palette_ptr, layer_or);
          DRAWBG8x1_LPE();
	 }
	else
         for(int x = first_end; x < end; x+=8)
         {
          uint16 bat = king.KRAM[bat_and_cg_bank][(bat_offset + (bat_x + bat_y)) & 0x3ffff];
          uint32 pbn = ((bat >> 12) << 4);
          bat &= 0x0FFF;
          uint16 *cgptr = &king.KRAM[bat_and_cg_bank][(cg_offset + (bat * 16) + ysmall * 2) & 0x3ffff];

          DRAWBG8x1_16(target + x, pbn, cgptr, palette_ptr, layer_or);
          DRAWBG8x1_LPNE();
         }
     break;
    case 0x03: // 256 color - OK
	if(endless)
	 for(int x = first_end; x < end; x+=8)
	 {
	  uint16 bat = king.KRAM[bat_and_cg_bank][(bat_offset + (bat_x + bat_y)) & 0x3ffff];
	  uint16 *cgptr = &king.KRAM[bat_and_cg_bank][(cg_offset + (bat * 32) + ysmall * 4) & 0x3ffff];

          DRAWBG8x1_256(target + x, cgptr, palette_ptr, layer_or);
          DRAWBG8x1_LPE();
         }
	else
         for(int x = first_end; x < end; x+=8)
         {
          uint16 bat = king.KRAM[bat_and_cg_bank][(bat_offset + (bat_x + bat_y)) & 0x3ffff];
          uint16 *cgptr = &king.KRAM[bat_and_cg_bank][(cg_offset + (bat * 32) + ysmall * 4) & 0x3ffff];

          DRAWBG8x1_256(target + x, cgptr, palette_ptr, layer_or);
          DRAWBG8x1_LPNE();
         }
     break;
    case 0x04: // 64K color, YUV, (bit 15)YYYYYYYY UUUUVVVV(bit 0)
	if(endless)
	 for(int x = first_end; x < end; x+=8)
	 { 
	  uint16 bat = king.KRAM[bat_and_cg_bank][(bat_offset + (bat_x + bat_y)) & 0x3ffff];
	  uint16 *cgptr = &king.KRAM[bat_and_cg_bank][(cg_offset + (bat * 64) + ysmall * 8) & 0x3ffff];

          DRAWBG8x1_64K(target + x, cgptr, palette_ptr, layer_or);
          DRAWBG8x1_LPE();
	 }
	else
         for(int x = first_end; x < end; x+=8)
         {
          uint16 bat = king.KRAM[bat_and_cg_bank][(bat_offset + (bat_x + bat_y)) & 0x3ffff];
          uint16 *cgptr = &king.KRAM[bat_and_cg_bank][(cg_offset + (bat * 64) + ysmall * 8) & 0x3ffff];

          DRAWBG8x1_64K(target + x, cgptr, palette_ptr, layer_or);
          DRAWBG8x1_LPNE();
         }
     break;
   case 0x05: // 16M color
	if(endless)
	 for(int x = first_end; x < end; x+=8)
	 {
	  uint16 bat = king.KRAM[bat_and_cg_bank][(bat_offset + (bat_x + bat_y)) & 0x3ffff];
	  uint16 *cgptr = &king.KRAM[bat_and_cg_bank][(cg_offset + (bat * 64) + ysmall * 8) & 0x3ffff];

          DRAWBG8x1_16M(target + x, cgptr, palette_ptr, layer_or);
          DRAWBG8x1_LPE();
         }
	else
         for(int x = first_end; x < end; x+=8)
         {
          uint16 bat = king.KRAM[bat_and_cg_bank][(bat_offset + (bat_x + bat_y)) & 0x3ffff];
          uint16 *cgptr = &king.KRAM[bat_and_cg_bank][(cg_offset + (bat * 64) + ysmall * 8) & 0x3ffff];

          DRAWBG8x1_16M(target + x, cgptr, palette_ptr, layer_or);
          DRAWBG8x1_LPNE();
         }
     break;
  }
 }
}

static int16 UVLUT[65536][3];
static uint8 RGBDeflower[1152]; // 0 is at 384

static void RebuildUVLUT(void)
{
 for(int ur = 0; ur < 256; ur++)
  for(int vr = 0; vr < 256; vr++)
  {
   int r, g, b;
   int u, v;
   
   u = ur - 128;
   v = vr - 128;

   // FIXME:  Use lrint() ?
   r = (int)(0 - 0.000039457070707 * u + 1.139827967171717 * v);
   g = (int)(0 - 0.394610164141414 * u - 0.580500315656566 * v);
   b = (int)(0 + 2.031999684343434 * u - 0.000481376262626 * v);

   UVLUT[vr + ur * 256][0] = r;
   UVLUT[vr + ur * 256][1] = g;
   UVLUT[vr + ur * 256][2] = b;
  }
 for(int x = 0; x < 1152; x++)
 {
  if(x < 384) RGBDeflower[x] = 0;
  else if(x > (384 + 255)) RGBDeflower[x] = 255;
  else
   RGBDeflower[x] = x - 384;
 }
}

static uint32 INLINE YUV888_TO_RGB888(uint32 yuv)
{
 int32 r, g, b;
 uint8 y = yuv >> 16;

 r = y + UVLUT[yuv & 0xFFFF][0];
 g = y + UVLUT[yuv & 0xFFFF][1];
 b = y + UVLUT[yuv & 0xFFFF][2];

 r = clamp_to_u8(r);
 g = clamp_to_u8(g);
 b = clamp_to_u8(b);

 return((r << FSettings.rshift) | (g << FSettings.gshift) | (b << FSettings.bshift));
}

void KING_RunFrame(fx_vdc_t **vdc_chips, uint32 *pXBuf, MDFN_Rect *LineWidths, uint32 *OVBuf, MDFN_Rect *OVLineWidths, int skip)
{
 //MDFN_DispMessage((UTF8*)"P0:%06x P1:%06x; I0: %06x I1: %06x", king.ADPCMPlayAddress[0], king.ADPCMPlayAddress[1], king.ADPCMIntermediateAddress[0] << 6, king.ADPCMIntermediateAddress[1] << 6);

 for(fx_vce.frame_counter = 0; fx_vce.frame_counter < 263; fx_vce.frame_counter++)
 {
  uint32 vdc_linebuffers[2][512];
  uint32 vdc_linebuffer[320];
  uint32 vdc_linebuffer_yuved[320];

  uint32 bg_linebuffer[256];
  unsigned int width = (fx_vce.picture_mode & 0x08) ? 320 : 256;

  int start, end;

  if(GfxDecode_Line == fx_vce.frame_counter)
  {
   DoGfxDecode();
  }

  if(width == 256)
   start = 8 + 2 * 8;
  else
   start = 8 + 40; //64; //64;

  end = start + width;

  CheckDMA();

  if(fx_vce.frame_counter == king.RasterIRQLine && (king.RAINBOWTransferControl & 0x2))
  {
   //printf("Wovely: %d\n", fx_vce.frame_counter);
   //king.RasterIRQPending = TRUE;
   //RedoKINGIRQCheck();
  }

  if(fx_vce.frame_counter == king.RAINBOWTransferStartPosition && (king.RAINBOWTransferControl & 1))
  {
   king.RAINBOWKRAMReadPos = king.RAINBOWKRAMA << 1;
   RAINBOW_TransferStart(king.RAINBOWTransferBlockCount);
  }

  int pre_run = 85 * 4;

  int chia = fx_vce.frame_counter - 3;
  if(chia < 0) chia += 263;

  FXVDC_DoLine(vdc_chips[0], chia, vdc_linebuffers[0], skip);
  FXVDC_DoLine(vdc_chips[1], chia, vdc_linebuffers[1], skip);

  uint8 rb_data[256 * 4]; // *2 for YUV data
  int rb_type = -1;

  if((king.RAINBOWTransferControl & 1))
   rb_type = RAINBOW_FetchRaster(rb_data);

  if(fx_vce.frame_counter >= 22 && fx_vce.frame_counter < 262)
  {
   // 8 * 2 for left + right padding for scrolling
   uint32 bg_linebuffers[4][256 + 8 + 8];
   uint32 bg_linebuffer_sub[256 + 8 + 8];
   uint32 rainbow_linebuffer[256];

   uint32 *rearranged[4];
   int rearr = 0;

   MDFNGameInfo->DisplayRect.w = (width == 320) ? 320 : width;
   MDFNGameInfo->DisplayRect.x = 0;

   LineWidths[fx_vce.frame_counter - 22] = MDFNGameInfo->DisplayRect;
   if(width == 320)
   {
    MDFN_Rect tmp_rect;
    tmp_rect.y = 0; tmp_rect.h = 0; // Unused
    tmp_rect.x = 0;
    tmp_rect.w = 256;

    OVLineWidths[fx_vce.frame_counter - 22] = tmp_rect;
   }
   if(!skip)
   {
    uint32 vdc_poffset[2];
    bool SpriteComboOn = FALSE;
 
    vdc_poffset[0] = ((fx_vce.palette_offset[0] >> 0) & 0xFF) << 1; // BG
    vdc_poffset[1] = ((fx_vce.palette_offset[0] >> 8) & 0xFF) << 1; // SPR

    if((fx_vce.picture_mode & 0x80)) // && (width == 320 || !(fx_vce.BLE & 0x0C)))
     SpriteComboOn = TRUE;

    for(int x = start; x < end; x++)
    {
     static const uint32 layer_num[2] = { 4 << 28, 5 << 28};
     uint32 zort[2];

     zort[0] = vdc_linebuffers[0][x];
     zort[1] = vdc_linebuffers[1][x];

     // SPR combination
     if(SpriteComboOn && (zort[0] & 0x100) && (zort[1] & 0x100))
     {
      uint8 cheepix = (zort[1] & 0xF) | ((zort[0] & 0xF) << 4);

      zort[0] = zort[1] = cheepix | vdc_poffset[1] | 0x200; //(zort[1] & 0x30F) + (((zort[0] & 0xF) << 4) | vdc_poffset[0]);

      if(!cheepix) { zort[0] |= 0x400; zort[1] |= 0x400; }
     }
     // BG combination
     else if((fx_vce.picture_mode & 0x40) && !(zort[0] & 0x100) && !(zort[1] & 0x100))
     {
      uint8 cheepix = (zort[1] & 0xF) | ((zort[0] & 0xF) << 4);

      zort[0] = zort[1] = cheepix | vdc_poffset[0]; //(zort[1] & 0x30F) + (((zort[0] & 0xF) << 4) | vdc_poffset[0]);

      if(!cheepix) { zort[0] |= 0x400; zort[1] |= 0x400; }
     }
     else
     {
      zort[0] = ((vdc_poffset[(zort[0] >> 8) & 1] + (zort[0]&0xFF)) & 0x1FF) | ((zort[0] & 0x300) << 1);
      zort[1] = ((vdc_poffset[(zort[1] >> 8) & 1] + (zort[1]&0xFF)) & 0x1FF) | ((zort[1] & 0x300) << 1);
     }

     vdc_linebuffer[x - start] = vdc_linebuffers[0][x];

     if(!(zort[1] & 0x400))
     {
      vdc_linebuffer[x - start] = vdc_linebuffers[1][x];
      zort[0] = zort[1];
     }
     // Original:
     //vdc_linebuffer_yuved[x] = fx_vce.palette_table_cache[zort[0] & 0x1FF] | ((zort[0] & 0x200) ? 5 << 28 : 4 << 28) | ((zort[0] & 0x400) ? TRANS_OR : 0);
     // Optimized:
     vdc_linebuffer_yuved[x - start] = fx_vce.palette_table_cache[zort[0] & 0x1FF] | layer_num[(zort[0] >> 9) & 1] | ((zort[0] & 0x400) << (TRANS_OR_SHIFT - 10));
    }

    // Disable it because it's borked.
    if(rb_type == 1)
     rb_type = -1;

    if(rb_type == 1) // YUV
    {
     uint32 *meow = (uint32 *)rb_data;

     for(int x = 0; x < 256; x++)
      rainbow_linebuffer[x] = meow[x] | (6 << 28);
    }
    else if(rb_type == 0)
    {
     uint32 *rb_pptr = &fx_vce.palette_table_cache[((fx_vce.palette_offset[3] >> 0) & 0xFF) << 1];

     for(int x = 0; x < 256; x++)
     {
      rainbow_linebuffer[x] = rb_pptr[rb_data[x]] | TTRANS(rb_data[x]) | (6 << 28);
     }
    }
    else
     for(int x = 0; x < 256; x++)
      rainbow_linebuffer[x] = TRANS_OR | (6 << 28);

    DrawBG(bg_linebuffers[0], 0, 0);

    // Only draw BG0sub if BG0 is in non-endless scroll mode(I don't know if this is correct...)
    //if(!(king.BGScrollMode & 1))
    //{
    // DrawBG(bg_linebuffer_sub, 0, 1);
    // for(int x = 8; x < (256 + 8); x++)
    //  if(bg_linebuffers[0][x] & TRANS_OR)
    //   bg_linebuffers[0][x] = bg_linebuffer_sub[x];
    //}

    DrawBG(bg_linebuffers[1], 1, 0);
    DrawBG(bg_linebuffers[2], 2, 0);
    DrawBG(bg_linebuffers[3], 3, 0);
   
    /*
        4 = Foremost
        1 = Hindmost
        0 = Hidden
    */

    for(int prio = 1; prio <= 4; prio++)
    {
     for(int x = 0; x < 4; x++)
     {
      int thisprio = (king.priority >> (x * 3)) & 0x7;

      if(thisprio >= 5) // Miraculum has to be FUNNY
	thisprio -= 4;

      if(BGLayerDisable & (1 << x)) continue;

      if(thisprio == prio)
       rearranged[rearr++] = bg_linebuffers[x] + 8;
     }
    }

    if(!rearr) // No layers on, HMM.
     for(int x = 0; x < 256; x++)
     {
      bg_linebuffer[x] = TRANS_OR;
     }
    else
    {
     if(rearr == 1)
      for(int x = 0; x < 256; x++)
      {
       bg_linebuffer[x] = rearranged[0][x];
      }
     else if(rearr == 2)
      for(int x = 0; x < 256; x++)
      {
       uint32 replacement = rearranged[0][x];

       if(!(rearranged[1][x] & TRANS_OR))
        replacement = rearranged[1][x];
       bg_linebuffer[x] = replacement;
      }
     else if(rearr == 3)
      for(int x = 0; x < 256; x++)
      {
       uint32 replacement = rearranged[0][x];

       if(!(rearranged[1][x] & TRANS_OR))
        replacement = rearranged[1][x];
       if(!(rearranged[2][x] & TRANS_OR))
        replacement = rearranged[2][x];
       bg_linebuffer[x] = replacement;
      }
     else
      for(int x = 0; x < 256; x++)
      {
       uint32 replacement = rearranged[0][x];

       if(!(rearranged[1][x] & TRANS_OR))
        replacement = rearranged[1][x];
       if(!(rearranged[2][x] & TRANS_OR))
        replacement = rearranged[2][x];
       if(!(rearranged[3][x] & TRANS_OR))
        replacement = rearranged[3][x];
       bg_linebuffer[x] = replacement;
      }
    }
    // Now we have to mix everything together... I'm scared, mommy.
    // We have, vdc_linebuffer[0] and bg_linebuffer
    // Which layer is specified in bits 28-31:
    //  0 : BG0, 1: BG1, 2: BG2, 3: BG3
    //  4 : VDC BG, 5: VDC SPR
    //  6 : RAINBOW
    uint32 priority_remap[7];
    uint32 ble_cache[8];

    //MDFN_DispMessage((UTF8*)"%04x %04x, %04x-%04x %04x-%04x %04x-%04x", fx_vce.BLE, fx_vce.CCR, fx_vce.coefficients[0], fx_vce.coefficients[1], fx_vce.coefficients[2], fx_vce.coefficients[3], fx_vce.coefficients[4], fx_vce.coefficients[5]);
    for(int x = 0; x < 4; x++)
    {
     priority_remap[x] = (((fx_vce.priority[1] >> (x * 4)) & 0xF) + 1) & fx_vce.layer_mask_cache[x];
     //printf("RM: %d %d\n", x, priority_remap[x]);
    }
    priority_remap[4] = ((fx_vce.priority[0] & 0xF) + 1) & fx_vce.layer_mask_cache[4];
    priority_remap[5] = (((fx_vce.priority[0] >> 4) & 0xF) + 1) & fx_vce.layer_mask_cache[5];
    priority_remap[6] = (((fx_vce.priority[0] >> 8) & 0xF) + 1) & fx_vce.layer_mask_cache[6];

    // Rainbow layer disabled?
    if(rb_type == -1 || RAINBOWLayerDisable)
     priority_remap[6] = 0;

    for(int x = 0; x < 4; x++)
     ble_cache[x] = (fx_vce.BLE >> (4 + x * 2)) & 0x3;

    ble_cache[4] = (fx_vce.BLE >> 0) & 0x3;
    ble_cache[5] = (fx_vce.BLE >> 2) & 0x3;
    ble_cache[6] = (fx_vce.BLE >> 12) & 0x3;
    ble_cache[7] = 0;

    uint32 *target = pXBuf + (MDFNGameInfo->pitch >> 2) * (fx_vce.frame_counter - 22);
    uint32 *ovtarget = OVBuf + (MDFNGameInfo->pitch >> 2) * (fx_vce.frame_counter - 22);

#define DOCELLO(pixpoo) \
	if((pixel[pixpoo] >> 28) != 5 || ((fx_vce.SPBL >> ((vdc_linebuffer[x] & 0xF0)>> 4)) & 1))	\
        {	\
         int which_co = (ble_cache[pixel[pixpoo] >> 28] - 1) * 2;	\
         uint8 back_y = fx_vce.coefficient_mul_table[0][(fx_vce.coefficients[which_co + 1] >> 8) & 0xF][(zeout >> 16) & 0xFF];	\
         uint8 back_u = fx_vce.coefficient_mul_table[1][(fx_vce.coefficients[which_co + 1] >> 4) & 0xF][(zeout >>  8) & 0xFF];	\
         uint8 back_v = fx_vce.coefficient_mul_table[1][(fx_vce.coefficients[which_co + 1] >> 0) & 0xF][(zeout >>  0) & 0xFF];	\
         uint8 fore_y = fx_vce.coefficient_mul_table[0][(fx_vce.coefficients[which_co + 0] >> 8) & 0xF][(pixel[pixpoo] >> 16) & 0xFF];	\
         uint8 fore_u = fx_vce.coefficient_mul_table[1][(fx_vce.coefficients[which_co + 0] >> 4) & 0xF][(pixel[pixpoo] >>  8) & 0xFF];	\
         uint8 fore_v = fx_vce.coefficient_mul_table[1][(fx_vce.coefficients[which_co + 0] >> 0) & 0xF][(pixel[pixpoo] >>  0) & 0xFF];	\
         back_y = (RGBDeflower + 384)[back_y + fore_y];	\
         back_u = (RGBDeflower + 384)[(back_u - 128) + (fore_u - 128) + 128];	\
         back_v = (RGBDeflower + 384)[(back_v - 128) + (fore_v - 128) + 128];	\
         zeout = (pixel[pixpoo] & 0xFF000000) | (back_y << 16) | (back_u << 8) | (back_v << 0);	\
        } else zeout = pixel[pixpoo];	\

#define DOCELLOSPECIAL() \
        {       \
         uint8 y = fx_vce.coefficient_mul_table[0][(fx_vce.coefficients[1] >> 8) & 0xF][(zeout >> 16) & 0xFF];    \
         uint8 u = fx_vce.coefficient_mul_table[1][(fx_vce.coefficients[1] >> 4) & 0xF][(zeout >>  8) & 0xFF];    \
         uint8 v = fx_vce.coefficient_mul_table[1][(fx_vce.coefficients[1] >> 0) & 0xF][(zeout >>  0) & 0xFF];    \
         zeout = (zeout & 0xFF000000) | ((RGBDeflower + 384)[CCR_Y + y] << 16) | ((RGBDeflower + 384)[(CCR_U - 128) + (u - 128) + 128] << 8) | 	\
			((RGBDeflower + 384)[(CCR_V - 128) + (v - 128) + 128] << 0); \
        }

    uint8 CCR_Y = fx_vce.coefficient_mul_table[0][(fx_vce.coefficients[0] >> 8) & 0xF][(fx_vce.CCR >> 8) & 0xFF];
    uint8 CCR_U = fx_vce.coefficient_mul_table[1][(fx_vce.coefficients[0] >> 4) & 0xF][(fx_vce.CCR & 0xF0)];
    uint8 CCR_V = fx_vce.coefficient_mul_table[1][(fx_vce.coefficients[0] >> 0) & 0xF][(fx_vce.CCR << 4) & 0xF0];

    if(fx_vce.picture_mode & 0x08) // No cellophane in 320 pixel mode
    {
     for(int x = 0; x < 256; x++) ovtarget[x] = 0x00 << FSettings.ashift;
     for(unsigned int x = 0; x < width; x++)
     {
      uint32 pixel[4]; // 0-2 are real pixels, 3 is dummy pixel
      uint32 prio[3];
      uint32 zeout = 0x008080 /* fx_vce.CCR (rand() & 0xFFFFFF)*/ | TRANS_OR | (7 << 28);

      prio[0] = priority_remap[bg_linebuffer[x * 256 / (320)] >> 28];
      prio[1] = priority_remap[vdc_linebuffer_yuved[x] >> 28];
      prio[2] = priority_remap[rainbow_linebuffer[x * 256 / (320)] >> 28];

      pixel[0] = zeout;
      pixel[1] = zeout;
      pixel[2] = zeout;

      // Do a transparency check, to work around the issue of 2 or 3 layers having the same priority(in that case, which chip has the higher
      // priority for non-opaque pixels?  BMG or RAINBOW?  BMG or VDC?  VDC or RAINBOW?

      {
       uint8 pi0 = happymap[prio[0]][prio[1]][prio[2]][0];
       uint8 pi1 = happymap[prio[0]][prio[1]][prio[2]][1];
       uint8 pi2 = happymap[prio[0]][prio[1]][prio[2]][2];

       if(pixel[pi0] & TRANS_OR) pixel[pi0] = bg_linebuffer[x * 256 / (320)];
       if(pixel[pi1] & TRANS_OR) pixel[pi1] = vdc_linebuffer_yuved[x ];
       if(pixel[pi2] & TRANS_OR) pixel[pi2] = rainbow_linebuffer[x * 256 / (320)];
      }

      if(!(pixel[0] & TRANS_OR))
       zeout = pixel[0];
      if(!(pixel[1] & TRANS_OR))
        zeout = pixel[1];
      if(!(pixel[2] & TRANS_OR))
        zeout = pixel[2];

      if((zeout >> 28) < 4 || (zeout >> 28) == 6) 
      {
       ovtarget[x * 256 / 320] = (0xFF << FSettings.ashift) | YUV888_TO_RGB888(zeout);
       zeout = pixel[1];
       if((zeout >> 28) < 4 || (zeout >> 28) == 6) zeout = pixel[0];
       if((zeout >> 28) < 4 || (zeout >> 28) == 6) zeout = 0x008080;

      }
      target[x] = YUV888_TO_RGB888(zeout);
     }
    }
    else if((fx_vce.BLE & 0xC000) == 0xC000)
    {
     for(unsigned int x = 0; x < width; x++)
     {
      uint32 pixel[4]; // 0-2 are real pixels, 3 is dummy pixel
      uint32 prio[3];
      uint32 zeout = 0x008080 /* fx_vce.CCR (rand() & 0xFFFFFF)*/ | TRANS_OR | (7 << 28);

      prio[0] = priority_remap[bg_linebuffer[x] >> 28];
      prio[1] = priority_remap[vdc_linebuffer_yuved[x] >> 28];
      prio[2] = priority_remap[rainbow_linebuffer[x] >> 28];

      pixel[0] = zeout;
      pixel[1] = zeout;
      pixel[2] = zeout;

      {
       uint8 pi0 = happymap[prio[0]][prio[1]][prio[2]][0];
       uint8 pi1 = happymap[prio[0]][prio[1]][prio[2]][1];
       uint8 pi2 = happymap[prio[0]][prio[1]][prio[2]][2];

       if(pixel[pi0] & TRANS_OR) pixel[pi0] = bg_linebuffer[x];
       if(pixel[pi1] & TRANS_OR) pixel[pi1] = vdc_linebuffer_yuved[x ];
       if(pixel[pi2] & TRANS_OR) pixel[pi2] = rainbow_linebuffer[x];
      }
      //pixel[happymap[prio[0]][prio[1]][prio[2]][0]] = bg_linebuffer[x];
      //pixel[happymap[prio[0]][prio[1]][prio[2]][1]] = vdc_linebuffer_yuved[x];
      //pixel[happymap[prio[0]][prio[1]][prio[2]][2]] = rainbow_linebuffer[x];

      if(!(pixel[0] & TRANS_OR))
       zeout = pixel[0];

      if(!(pixel[1] & TRANS_OR))
      {
       if(ble_cache[pixel[1] >> 28] && !(zeout & TRANS_OR)) // && (zeout & (0xF << 28)) != (0x7 << 28))
       {
        DOCELLO(1);
       }
       else
        zeout = pixel[1];
      }
      if(!(pixel[2] & TRANS_OR))
      {
       if(ble_cache[pixel[2] >> 28] && !(zeout & TRANS_OR)) // && (zeout & (0xF << 28)) != (0x7 << 28))
       {
        DOCELLO(2);
       }
       else
        zeout = pixel[2];
      }
      DOCELLOSPECIAL();
      target[x] = YUV888_TO_RGB888(zeout);
     }
    }
    else if((fx_vce.BLE & 0xC000) == 0x8000)
    {
     for(unsigned int x = 0; x < width; x++)
     {
      uint32 pixel[4]; // 0-2 are real pixels, 3 is dummy pixel
      uint32 prio[3];
      uint32 zeout = 0x008080 /* fx_vce.CCR (rand() & 0xFFFFFF)*/ | TRANS_OR | (7 << 28);

      prio[0] = priority_remap[bg_linebuffer[x] >> 28];
      prio[1] = priority_remap[vdc_linebuffer_yuved[x] >> 28];
      prio[2] = priority_remap[rainbow_linebuffer[x] >> 28];

      pixel[0] = zeout;
      pixel[1] = zeout;
      pixel[2] = zeout;

      {
       uint8 pi0 = happymap[prio[0]][prio[1]][prio[2]][0];
       uint8 pi1 = happymap[prio[0]][prio[1]][prio[2]][1];
       uint8 pi2 = happymap[prio[0]][prio[1]][prio[2]][2];

       if(pixel[pi0] & TRANS_OR) pixel[pi0] = bg_linebuffer[x];
       if(pixel[pi1] & TRANS_OR) pixel[pi1] = vdc_linebuffer_yuved[x];
       if(pixel[pi2] & TRANS_OR) pixel[pi2] = rainbow_linebuffer[x];
      }

      //pixel[happymap[prio[0]][prio[1]][prio[2]][0]] = bg_linebuffer[x];
      //pixel[happymap[prio[0]][prio[1]][prio[2]][1]] = vdc_linebuffer_yuved[x];
      //pixel[happymap[prio[0]][prio[1]][prio[2]][2]] = rainbow_linebuffer[x];

      if(!(pixel[0] & TRANS_OR))
       zeout = pixel[0];

      if(!(pixel[1] & TRANS_OR))
      {
       if(ble_cache[pixel[1] >> 28] && !(zeout & TRANS_OR)) // && (zeout & (0xF << 28)) != (0x7 << 28))
       {
        DOCELLO(1);
       }
       else
        zeout = pixel[1];
      }
      if(!(pixel[2] & TRANS_OR))
      {
       if(ble_cache[pixel[2] >> 28] && !(zeout & TRANS_OR)) // && (zeout & (0xF << 28)) != (0x7 << 28))
       {
        DOCELLO(2);
       }
       else
        zeout = pixel[2];
      }
      target[x] = YUV888_TO_RGB888(zeout);
     }
    }
    else
     for(unsigned int x = 0; x < width; x++)
     {
      uint32 pixel[4]; // 0-2 are real pixels, 3 is dummy pixel
      uint32 prio[3];
      uint32 zeout = 0x008080 /* fx_vce.CCR (rand() & 0xFFFFFF)*/ | TRANS_OR | (7 << 28);

      prio[0] = priority_remap[bg_linebuffer[x] >> 28];
      prio[1] = priority_remap[vdc_linebuffer_yuved[x] >> 28];
      prio[2] = priority_remap[rainbow_linebuffer[x] >> 28];

      pixel[0] = zeout;
      pixel[1] = zeout;
      pixel[2] = zeout;

      {
       uint8 pi0 = happymap[prio[0]][prio[1]][prio[2]][0];
       uint8 pi1 = happymap[prio[0]][prio[1]][prio[2]][1];
       uint8 pi2 = happymap[prio[0]][prio[1]][prio[2]][2];

       if(pixel[pi0] & TRANS_OR) pixel[pi0] = bg_linebuffer[x];
       if(pixel[pi1] & TRANS_OR) pixel[pi1] = vdc_linebuffer_yuved[x];
       if(pixel[pi2] & TRANS_OR) pixel[pi2] = rainbow_linebuffer[x];
      }

      //pixel[happymap[prio[0]][prio[1]][prio[2]][0]] = bg_linebuffer[x];
      //pixel[happymap[prio[0]][prio[1]][prio[2]][1]] = vdc_linebuffer_yuved[x];
      //pixel[happymap[prio[0]][prio[1]][prio[2]][2]] = rainbow_linebuffer[x];

      if(!(pixel[0] & TRANS_OR))
       zeout = pixel[0];

      if(!(pixel[1] & TRANS_OR))
      {
       if(ble_cache[pixel[1] >> 28] && !(zeout & TRANS_OR))
       {
        DOCELLO(1);
       }
       else
        zeout = pixel[1];
      }

      if(!(pixel[2] & TRANS_OR))
      {
       if(ble_cache[pixel[2] >> 28] && !(zeout & TRANS_OR))
       {
        DOCELLO(2);
       }
       else
        zeout = pixel[2];
      }
	//zeout = bg_linebuffer[x];
	//zeout = vdc_linebuffer_yuved[x];
      target[x] = YUV888_TO_RGB888(zeout);
     }

   }
  }
  fx_vce.in_hblank = TRUE;
  v810_run(pre_run);
  fx_vce.in_hblank = FALSE;

  FXVDC_DoLinePost(vdc_chips[0], chia);
  FXVDC_DoLinePost(vdc_chips[1], chia);

  v810_run(1365 - pre_run);
 }
}

void KING_SetPixelFormat(int rshift, int gshift, int bshift)
{
 RebuildUVLUT();
}

bool KING_ToggleLayer(int which)
{
 // "BG0\0BG1\0BG2\0BG3\0VDC-A BG\0VDC-A SPR\0VDC-B BG\0VDC-B SPR\0RAINBOW\0",
 if(which < 4)
 {
  BGLayerDisable ^= 1 << which;
  return( !((BGLayerDisable >> which) & 1));
 }
 else if(which == 4 || which == 5)
 {
  return(FXVDC_ToggleLayer(fx_vdc_chips[0], which - 4));
 }
 else if(which == 6 || which == 7)
 {
  return(FXVDC_ToggleLayer(fx_vdc_chips[1], which - 6));
 }
 else if(which == 8)
 {
  RAINBOWLayerDisable = !RAINBOWLayerDisable;
  return(!RAINBOWLayerDisable);
 }
 else
  return(0);
}


int KING_StateAction(StateMem *sm, int load, int data_only)
{
 SFORMAT KINGStateRegs[] =
 {
  SFVARN(king.AR, "AR"),
  SFARRAY16N(king.KRAM[0], 262144, "KRAM0"),
  SFARRAY16N(king.KRAM[1], 262144, "KRAM1"),
  SFVARN(king.KRAMWA, "KRAMWA"),
  SFVARN(king.KRAMRA, "KRAMRA"),
  SFVARN(king.PageSetting, "PageSetting"),
  SFVARN(king.bgmode, "bgmode"),
  SFVARN(king.priority, "priority"),
  SFVARN(king.BGScrollMode, "BGScrollMode"),
  SFARRAY16N(king.BGSize, 4, "BGSize"),
  SFARRAY16N(king.BGBATAddr, 4, "BGBATAddr"),
  SFARRAY16N(king.BGCGAddr, 4, "BGCGAddr"),
  SFARRAY16N(king.BGXScroll, 4, "BGXScroll"),
  SFARRAY16N(king.BGYScroll, 4, "BGYScroll"),
  SFVARN(king.BG0SubBATAddr, "BG0SubBATAddr"),
  SFVARN(king.BG0SubCGAddr, "BG0SubCGAddr"),
  SFVARN(king.ADPCMControl, "ADPCMControl"),
  SFARRAY16N(king.ADPCMBufferMode, 2, "ADPCMBufferMode"),
  SFARRAY16N(king.ADPCMSAL, 2, "ADPCMSAL"),
  SFARRAY32N(king.ADPCMEndAddress, 2, "ADPCMEndAddress"),
  SFARRAY32N(king.ADPCMPlayAddress, 2, "ADPCMPlayAddress"),
  SFARRAY16N(king.ADPCMIntermediateAddress, 2, "ADPCMIntermediateAddress"),
  SFARRAY16N(king.ADPCMStatus, 2, "ADPCMStatus"),
  SFVARN(king.RAINBOWTransferControl, "RAINBOWTransferControl"),
  SFVARN(king.RAINBOWKRAMA, "RAINBOWKRAMA"),
  SFVARN(king.RAINBOWTransferStartPosition, "RAINBOWTransferStartPosition"),
  SFVARN(king.RAINBOWTransferBlockCount, "RAINBOWTransferBlockCount"),
  SFVARN(king.RasterIRQLine, "RasterIRQLine"),
  SFVARN(king.RasterIRQPending, "RasterIRQPending"),
  SFVARN(king.RAINBOWKRAMReadPos, "RAINBOWKRAMReadPos"),
  SFVARN(king.DMATransferAddrLatch, "DMATransferAddrLatch"),
  SFVARN(king.DMATransferAddr, "DMATransferAddr"),
  SFVARN(king.DMATransferSize, "DMATransferSize"),
  SFVARN(king.DMAStatus, "DMAStatus"),
  SFVARN(king.DMAInterrupt, "DMAInterrupt"),
  SFVARN(king.DMALatch, "DMALatch"),
  SFVARN(king.MPROGControl, "MPROGControl"),
  SFVARN(king.Port02, "Port02"),
  SFVARN(king.CDInterrupt, "CDInterrupt"),
  SFVARN(king.data_cache, "data_cache"),
  SFEND
 };

 SFORMAT VCEStateRegs[] =
 {
  SFVARN(fx_vce.AR, "AR"),
  SFARRAY16N(fx_vce.priority, 2, "priority"),
  SFVARN(fx_vce.odd_field, "odd_field"),
  SFVARN(fx_vce.in_hblank, "in_hblank"),
  SFVARN(fx_vce.picture_mode, "picture_mode"),
  SFVARN(fx_vce.frame_counter, "frame_counter"),
  SFVARN(fx_vce.palette_rw_offset, "palette_rw_offset"),
  SFVARN(fx_vce.palette_rw_latch, "palette_rw_latch"),
  SFARRAY16N(fx_vce.palette_offset, 4, "palette_offset"),
  SFARRAY16N(fx_vce.palette_table, 512, "palette_table"),
  SFVARN(fx_vce.ChromaKeyY, "ChromaKeyY"),
  SFVARN(fx_vce.ChromaKeyU, "ChromaKeyU"),
  SFVARN(fx_vce.ChromaKeyV, "ChromaKeyV"),
  SFVARN(fx_vce.CCR, "CCR"),
  SFVARN(fx_vce.BLE, "BLE"),
  SFVARN(fx_vce.SPBL, "SPBL"),
  SFARRAY16N(fx_vce.coefficients, 6, "coefficients"),
  SFEND
 };

 int ret = MDFNSS_StateAction(sm, load, data_only, KINGStateRegs, "KING");
 
 ret &= MDFNSS_StateAction(sm, load, data_only, VCEStateRegs, "VCE");

 if(load)
 {
  fx_vce.palette_rw_offset &= 0x1FF;
  fx_vce.palette_offset[3] &= 0x00FF;
  fx_vce.priority[0] &= 0x0777;
  fx_vce.priority[1] &= 0x7777;

  for(int x = 0; x < 6; x++)
   fx_vce.coefficients[x] &= 0x0FFF;

  RebuildLayerMaskCache();
  for(int x = 0; x < 0x200; x++)
   RedoPaletteCache(x);

  RedoKINGIRQCheck();
  FXVDC_SetDotClock(fx_vdc_chips[0], (fx_vce.picture_mode & 0x08) ? 1 : 0);
  FXVDC_SetDotClock(fx_vdc_chips[1], (fx_vce.picture_mode & 0x08) ? 1 : 0);
  SoundBox_SetKINGADPCMControl(king.ADPCMControl);
 }
 return(ret);
}


bool KING_GetRegister(const std::string &name, uint32 &value, std::string *special)
{
 if(name == "MPROG")
 {
  value = king.MPROGControl;

  // Oops, this is for microprogram codes
  #ifdef MOO
  if(special)
  {
   char buf[256];
   static const char *atypes[4] = { "CG", "CG of BAT", "BAT", "???" };

   snprintf(buf, 256, "Offset: %d, Access Type: %s, Rotation: %d, BG Number: %d, NOP: %d",
	value & 0x3, atypes[(value >> 3) & 0x3], (int)(bool)(value & 0x20),
	(value >> 6) & 0x3, (int)(bool)(value & 0x100));

   *special = std::string(buf);
  }
  #endif
 }
 else if(name == "BGMODE")
 {
  value = king.bgmode;
  if(special)
  {
   static const char *bgmodes[16] = { "Disabled", "4-color", "16-color", "256-color", "64K-color", "16M-color", "Undefined", "Undefined",
				      "Disabled", "4-color w/BAT", "16-color w/BAT", "256-color w/BAT", "64K-color w/BAT", "16M-color w/BAT", "Undefined", "Undefined"
				    };
   char buf[256];
   snprintf(buf, 256, "BG0: %2d(%s), BG1: %2d(%s), BG2: %2d(%s), BG3: %2d(%s)", value & 0xF, bgmodes[value & 0xF], (value >> 4) & 0xF, bgmodes[(value >> 4) & 0xF], (value >> 8) & 0xF, bgmodes[(value >> 8) & 0xf], (value >> 12) & 0xF, bgmodes[(value >> 12) & 0xf]);
   *special = std::string(buf);
  }
 }
 else if(name == "BGPRIO")
  value = king.priority;
 else if(name == "BGSCRM")
  value = king.BGScrollMode;

 else if(name == "BGSIZ0")
  value = king.BGSize[0];

 else if(name == "BGSIZ1")
  value = king.BGSize[1];

 else if(name == "BGSIZ2")
  value = king.BGSize[2];

 else if(name == "BGSIZ3")
  value = king.BGSize[3];

 else if(name == "BGXSC0")
  value = king.BGXScroll[0];
 else if(name == "BGXSC1")
  value = king.BGXScroll[1];
 else if(name == "BGXSC2")
  value = king.BGXScroll[2];
 else if(name == "BGXSC3")
  value = king.BGXScroll[3];

 else if(name == "BGYSC0")
  value = king.BGYScroll[0];
 else if(name == "BGYSC1")
  value = king.BGYScroll[1];
 else if(name == "BGYSC2")
  value = king.BGYScroll[2];
 else if(name == "BGYSC3")
  value = king.BGYScroll[3];

 else if(name ==  "BGBAT0")
  value = king.BGBATAddr[0];
 else if(name ==  "BGBATS")
  value = king.BG0SubBATAddr;
 else if(name ==  "BGBAT1")
  value = king.BGBATAddr[1];
 else if(name ==  "BGBAT2")
  value = king.BGBATAddr[2];
 else if(name ==  "BGBAT3")
  value = king.BGBATAddr[3];

 else if(name == "BGCG0" || name == "BGCGS" || name == "BGCG1" || name == "BGCG2" || name == "BGCG3")
 {
  char which = name[4];
  if(which == 'S')
   value = king.BG0SubCGAddr;
  else
   value = king.BGCGAddr[which - '0'];
  if(special)
  {
   char buf[256];
   snprintf(buf, 256, "0x%04x * 1024 = 0x%05x", value, (value * 1024) & 0x3FFFF);
   *special = std::string(buf);
  }
 }
 else if(name == "VCEPRIO0")
 {
  value = fx_vce.priority[0];
  if(special)
  {
   char buf[256];
   snprintf(buf, 256, "VDC BG: %2d, VDC SPR: %2d, RAINBOW: %2d", value & 0xF, (value >> 4) & 0xF, (value >> 8) & 0xF);
   *special = std::string(buf);
  }
 }
 else if(name == "VCEPRIO1")
 {
  value = fx_vce.priority[1];
  if(special)
  {
   char buf[256];
   snprintf(buf, 256, "BG0: %2d, BG1: %2d, BG2: %2d, BG3: %2d", value & 0xF, (value >> 4) & 0xF, (value >> 8) & 0xF, (value >> 12) & 0xF);
   *special = std::string(buf);
  }
 }
 else if(name == "VCEPICMODE")
 {
  value = fx_vce.picture_mode;
  if(special)
  {
   char buf[256];
   snprintf(buf, 256, "BG0: %s, BG1: %s, BG2: %s, BG3: %s, VDC BG: %s%s, VDC SPR: %s%s, RAINBOW: %s, VDC Dot Clock: %sMHz",
	(value & (1 << 10)) ? "On" : "Off", (value & (1 << 11)) ? "On": "Off",
        (value & (1 << 12)) ? "On" : "Off", (value & (1 << 13)) ? "On": "Off",
	(value & 0x0100) ? "On" : "Off", (value & 0x0040) ? "+merge mode" : "", (value & 0x0200) ? "On" : "Off", (value & 0x0080) ? "+merge mode" : "",
	(value & 0x4000) ? "On" : "Off", (value & 0x0008) ? "7.16":"5.37");
   *special = std::string(buf);
  }
 }
 else if(name == "Frame Cntr")
  value = fx_vce.frame_counter;
 else if(name == "VCEPALRWOF")
  value = fx_vce.palette_rw_offset;
 else if(name == "VCEPALRWLA")
  value = fx_vce.palette_rw_latch;
 else if(name == "VCEPALOFS0")
  value = fx_vce.palette_offset[0];
 else if(name == "VCEPALOFS1")
  value = fx_vce.palette_offset[1];
 else if(name == "VCEPALOFS2")
  value = fx_vce.palette_offset[2];
 else if(name == "VCEPALOFS3")
  value = fx_vce.palette_offset[3];
 else if(name == "ChromaKeyY")
  value = fx_vce.ChromaKeyY;
 else if(name == "ChromaKeyU")
  value = fx_vce.ChromaKeyU;
 else if(name == "ChromaKeyV")
  value = fx_vce.ChromaKeyV;

 else if(name == "RTCTRL")
  value = king.RAINBOWTransferControl;
 else if(name == "RKRAMA")
  value = king.RAINBOWKRAMA;
 else if(name == "RSTART")
  value = king.RAINBOWTransferStartPosition;
 else if(name == "RCOUNT")
  value = king.RAINBOWTransferBlockCount;
 else if(name == "DMATAL")
  value = king.DMATransferAddrLatch;
 else if(name == "DMATA")
  value = king.DMATransferAddr;
 else if(name == "DMATS")
  value = king.DMATransferSize;
 else if(name == "VCECCR")
  value = fx_vce.CCR;
 else if(name == "VCEBLE")
  value = fx_vce.BLE;
 else if(name == "VCESPBL")
  value = fx_vce.SPBL;
 else if(!strncasecmp(name.c_str(), "VCECOEFF", strlen("VCECOEFF")))
 {
  int which = name.c_str()[strlen("VCECOEFF")] - '0';
  value = fx_vce.coefficients[which];
  if(special)
  {
   char buf[256];
   snprintf(buf, 256, "Y: %1d, U: %1d, V: %1d", (value >> 8) & 0xF, (value >> 4) & 0xf, value & 0xf);
   *special = std::string(buf);
  }
 } 
 else if(name[name.size() - 2] == '-')
 {
  std::string neoname = name.substr(0, name.size() - 2);
  int wv = (name[name.size() - 1] == 'A') ? 0 : 1;

  if(neoname == "MAWR")
   value = fx_vdc_chips[wv]->MAWR;
  else if(neoname == "MARR")
   value = fx_vdc_chips[wv]->MARR;
  else if(neoname == "CR")
  {
   value = fx_vdc_chips[wv]->CR;
   if(special)
   {
    char buf[256];
    snprintf(buf, 256, "Sprite Hit IRQ: %s, Sprite Overflow IRQ: %s, RCR IRQ: %s, VBlank IRQ: %s, Sprites: %s, Background: %s", (value & 1) ? "On" : "Off", (value & 2) ? "On" : "Off",
	(value & 4) ? "On" : "Off", (value & 8) ? "On" : "Off", (value & 0x40) ? "On" : "Off", (value & 0x80) ? "On" : "Off");
    *special = std::string(buf);
   }
  }
  else if(neoname == "RCR")
   value = fx_vdc_chips[wv]->RCR;
  else if(neoname == "BXR")
   value = fx_vdc_chips[wv]->BXR;
  else if(neoname == "BYR")
   value = fx_vdc_chips[wv]->BYR;
  else if(neoname == "MWR")
   value = fx_vdc_chips[wv]->MWR;
  else if(neoname == "HSR")
   value = fx_vdc_chips[wv]->HSR;
  else if(neoname == "HDR")
   value = fx_vdc_chips[wv]->HDR;
  else if(neoname == "VSR")
   value = fx_vdc_chips[wv]->VSR;
  else if(neoname == "VCR")
   value = fx_vdc_chips[wv]->VCR;
  else if(neoname == "DCR")
   value = fx_vdc_chips[wv]->DCR;
  else if(neoname == "SOUR")
   value = fx_vdc_chips[wv]->SOUR;
  else if(neoname == "DESR")
   value = fx_vdc_chips[wv]->DESR;
  else if(neoname == "LENR")
   value = fx_vdc_chips[wv]->LENR;
  else if(neoname == "SATB")
   value = fx_vdc_chips[wv]->SATB;
 }

 #ifdef CHOO
        { "MAWR-A", "Memory Write Address, VDC-A", 2 },
        { "MARR-A", "Memory Read Address, VDC-A", 2 },
        { "CR-A", "Control, VDC-A", 2 },
        { "RCR-A", "Raster Counter, VDC-A", 2 },
        { "BXR-A", "X Scroll, VDC-A", 2 },
        { "BYR-A", "Y Scroll, VDC-A", 2 },
        { "MWR-A", "Memory Width, VDC-A", 2 },

        { "HSR-A", "HSR, VDC-A", 2 },
        { "HDR-A", "HDR, VDC-A", 2 },
        { "VSR-A", "VSR, VDC-A", 2 },
        { "VDR-A", "VDR, VDC-A", 2 },

        { "VCR-A", "VCR, VDC-A", 2 },
        { "DCR-A", "DMA Control, VDC-A", 2 },
        { "SOUR-A", "VRAM DMA Source Address, VDC-A", 2 },
        { "DESR-A", "VRAM DMA Dest Address, VDC-A", 2 },
        { "LENR-A", "VRAM DMA Length, VDC-A", 2 },
        { "SATB-A", "SATB Update Address, VDC-A", 2 },
#endif

 else
  return(0);

 return(1);
}

#ifdef MOO
static uint32 *GfxDecode_Buf = NULL;
static int GfxDecode_Width = 0;
static int GfxDecode_Height = 0;
static int GfxDecode_Line = -1;
static void DoGfxDecode(void);
#endif

void KING_SetGraphicsDecode(int line, int which, int w, int h, int xscroll, int yscroll, int pbn)
{
 if(line == -1)
 {
  if(GfxDecode_Buf)
  {
   free(GfxDecode_Buf);
   GfxDecode_Buf = NULL;
  }
 }
 else
  GfxDecode_Buf = (uint32*)realloc(GfxDecode_Buf, w * h * sizeof(uint32) * 3); // *2 for extra address info.

 GfxDecode_Line = line;
 GfxDecode_Width = w;
 GfxDecode_Height = h;
 GfxDecode_Layer = which;
 GfxDecode_Scroll = yscroll;
 GfxDecode_PBN = pbn;

 if(GfxDecode_Line == 0xB00B13)
  DoGfxDecode();
}

uint32 *KING_GetGraphicsDecodeBuffer(void)
{
 return(GfxDecode_Buf);
}

static void DoGfxDecode(void)
{
 int pbn = GfxDecode_PBN;

 if(GfxDecode_Layer >= 4 && GfxDecode_Layer <= 7)
 {
  uint32 palette_offset = fx_vce.palette_offset[0] >> (((GfxDecode_Layer - 4) & 1) * 8);
  palette_offset <<= 1;
  palette_offset &= 0x1FF;
  uint32 *palette_ptr = &fx_vce.palette_table_cache[palette_offset];
  uint32 neo_palette[16];

  pbn &= 0x0F;
  palette_ptr += pbn * 16;

  for(int x = 0; x < 16; x++) neo_palette[x] = YUV888_TO_RGB888(palette_ptr[x]) | MK_COLORA(0, 0, 0, 0xFF);
  FXVDC_DoGfxDecode(fx_vdc_chips[(GfxDecode_Layer - 4) / 2], neo_palette, GfxDecode_Buf, GfxDecode_Width, GfxDecode_Height, GfxDecode_Scroll, (GfxDecode_Layer - 4) & 1);
 }
 else if(GfxDecode_Layer < 4)
 {
  int n = GfxDecode_Layer;
  uint16 bgmode = (king.bgmode >> (n * 4)) & 0xF;
  uint32 cg_offset = king.BGCGAddr[n] * 1024;
  uint32 bat_and_cg_bank = (king.PageSetting & 0x0010) ? 1 : 0;
  uint32 *target = GfxDecode_Buf;
  uint32 layer_or = 0;
  uint32 page_addr_or = bat_and_cg_bank ? 0x40000 : 0x00000;
  uint32 palette_offset = fx_vce.palette_offset[1 + (n >> 1)] >> ((n & 1) ? 8 : 0);
  palette_offset <<= 1;
  palette_offset &= 0x1FF;
  uint32 *palette_ptr = &fx_vce.palette_table_cache[palette_offset];

  switch(bgmode & 0x7)
  {
   default: memset(target, 0, GfxDecode_Width * GfxDecode_Height * sizeof(uint32) * 3); break;
   case 0x01: // 4
   {
    pbn *= 4;
    pbn &= 0x1FF;

    for(int y = 0; y < GfxDecode_Height; y++)
    {
     for(int x = 0; x < GfxDecode_Width; x+=8)
     {
      int which_tile = (x / 8) + (GfxDecode_Scroll + (y / 8)) * (GfxDecode_Width / 8);
      uint16 cg = king.KRAM[bat_and_cg_bank][(cg_offset + (which_tile * 8) + (y & 0x7)) & 0x3FFFF];

      DRAWBG8x1_4(target + x, pbn, cg, palette_ptr, layer_or);

      target[x + GfxDecode_Width*2 + 0] = target[x + GfxDecode_Width*2 + 1] = target[x + GfxDecode_Width*2 + 2] = target[x + GfxDecode_Width*2 + 3] =
      target[x + GfxDecode_Width*2 + 4] = target[x + GfxDecode_Width*2 + 5] = target[x + GfxDecode_Width*2 + 6] = target[x + GfxDecode_Width*2 + 7] = ((cg_offset + (which_tile * 8)) & 0x3FFFF) | page_addr_or;

      target[x + GfxDecode_Width*1 + 0]=target[x + GfxDecode_Width*1 + 1]=target[x + GfxDecode_Width*1 + 2]=target[x + GfxDecode_Width*1 + 3] =
      target[x + GfxDecode_Width*1 + 4]=target[x + GfxDecode_Width*1 + 5]=target[x + GfxDecode_Width*1 + 6]=target[x + GfxDecode_Width*1 + 7] = which_tile;
     }
     for(int x = 0; x < GfxDecode_Width; x++)
      target[x] = YUV888_TO_RGB888(target[x]) | MK_COLORA(0, 0, 0, 0xFF);
     target += GfxDecode_Width * 3;
    }
   }
   break;

   case 0x02: // 16
   {
    pbn *= 8;
    pbn &= 0x1FF;
    for(int y = 0; y < GfxDecode_Height; y++)
    {
     for(int x = 0; x < GfxDecode_Width; x+=8)
     {
      int which_tile = (x / 8) + (GfxDecode_Scroll + (y / 8)) * (GfxDecode_Width / 8);
      uint16 *cgptr = &king.KRAM[bat_and_cg_bank][(cg_offset + (which_tile * 16) + (y & 0x7) * 2) & 0x3FFFF];

      DRAWBG8x1_16(target + x, pbn, cgptr, palette_ptr, layer_or);

      target[x + GfxDecode_Width*2 + 0] = target[x + GfxDecode_Width*2 + 1] = target[x + GfxDecode_Width*2 + 2] = target[x + GfxDecode_Width*2 + 3] =
      target[x + GfxDecode_Width*2 + 4] = target[x + GfxDecode_Width*2 + 5] = target[x + GfxDecode_Width*2 + 6] = target[x + GfxDecode_Width*2 + 7] = ((cg_offset + (which_tile * 16)) & 0x3FFFF) | page_addr_or;

      target[x + GfxDecode_Width*1 + 0]=target[x + GfxDecode_Width*1 + 1]=target[x + GfxDecode_Width*1 + 2]=target[x + GfxDecode_Width*1 + 3] =
      target[x + GfxDecode_Width*1 + 4]=target[x + GfxDecode_Width*1 + 5]=target[x + GfxDecode_Width*1 + 6]=target[x + GfxDecode_Width*1 + 7] = which_tile;
     }
     for(int x = 0; x < GfxDecode_Width; x++)
      target[x] = YUV888_TO_RGB888(target[x]) | MK_COLORA(0, 0, 0, 0xFF);
     target += GfxDecode_Width * 3;
    }
   }
   break;

   case 0x03: // 256
   {
    for(int y = 0; y < GfxDecode_Height; y++)
    {
     for(int x = 0; x < GfxDecode_Width; x+=8)
     {
      int which_tile = (x / 8) + (GfxDecode_Scroll + (y / 8)) * (GfxDecode_Width / 8);
      uint16 *cgptr = &king.KRAM[bat_and_cg_bank][(cg_offset + (which_tile * 32) + (y & 0x7) * 4) & 0x3FFFF];

      DRAWBG8x1_256(target + x, cgptr, palette_ptr, layer_or);

      target[x + GfxDecode_Width*2 + 0] = target[x + GfxDecode_Width*2 + 1] = target[x + GfxDecode_Width*2 + 2] = target[x + GfxDecode_Width*2 + 3] =
      target[x + GfxDecode_Width*2 + 4] = target[x + GfxDecode_Width*2 + 5] = target[x + GfxDecode_Width*2 + 6] = target[x + GfxDecode_Width*2 + 7] = ((cg_offset + (which_tile * 32)) & 0x3FFFF) | page_addr_or;

      target[x + GfxDecode_Width*1 + 0]=target[x + GfxDecode_Width*1 + 1]=target[x + GfxDecode_Width*1 + 2]=target[x + GfxDecode_Width*1 + 3] =
      target[x + GfxDecode_Width*1 + 4]=target[x + GfxDecode_Width*1 + 5]=target[x + GfxDecode_Width*1 + 6]=target[x + GfxDecode_Width*1 + 7] = which_tile;
     }

     for(int x = 0; x < GfxDecode_Width; x++)
      target[x] = YUV888_TO_RGB888(target[x]) | MK_COLORA(0, 0, 0, 0xFF);

     target += GfxDecode_Width * 3;
    }
   }
   break;

   case 0x04: // 64K
    for(int y = 0; y < GfxDecode_Height; y++)
    {
     for(int x = 0; x < GfxDecode_Width; x+=8)
     {
      int which_tile = (x / 8) + (GfxDecode_Scroll + (y / 8)) * (GfxDecode_Width / 8);
      uint16 *cgptr = &king.KRAM[bat_and_cg_bank][(cg_offset + (which_tile * 64) + (y & 0x7) * 8) & 0x3FFFF];

      DRAWBG8x1_64K(target + x, cgptr, palette_ptr, layer_or);
     }

     for(int x = 0; x < GfxDecode_Width; x++)
      target[x] = YUV888_TO_RGB888(target[x]) | MK_COLORA(0, 0, 0, 0xFF);

     target += GfxDecode_Width * 3;
    }
   break;

   case 0x05: // 16M
    for(int y = 0; y < GfxDecode_Height; y++)
    {
     for(int x = 0; x < GfxDecode_Width; x+=8)
     {
      int which_tile = (x / 8) + (GfxDecode_Scroll + (y / 8)) * (GfxDecode_Width / 8);
      uint16 *cgptr = &king.KRAM[bat_and_cg_bank][(cg_offset + (which_tile * 64) + (y & 0x7) * 8) & 0x3FFFF];

      DRAWBG8x1_16M(target + x, cgptr, palette_ptr, layer_or);
     }

     for(int x = 0; x < GfxDecode_Width; x++)
      target[x] = YUV888_TO_RGB888(target[x]) | MK_COLORA(0, 0, 0, 0xFF);

     target += GfxDecode_Width * 3;
    }
   break;

  }
 }
 else
  memset(GfxDecode_Buf, 0, GfxDecode_Width * GfxDecode_Height * sizeof(uint32) * 3);
}
