/*  Copyright 2003-2005 Guillaume Duhamel
    Copyright 2004-2007 Theo Berkau

    This file is part of Yabause.

    Yabause is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Yabause is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Yabause; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/

#include <stdlib.h>
#include "vdp2.h"
#include "debug.h"
#include "scu.h"
#include "sh2core.h"
#include "vdp1.h"
#include "yabause.h"
#include "movie.h"

char OSDMessage[32] = "";	//adelikat: For showing on screen messages such as savestate loaded/saved
int OSDMessageTimer = 120;

u8 * Vdp2Ram;
u8 * Vdp2ColorRam;
Vdp2 * Vdp2Regs;
Vdp2Internal_struct Vdp2Internal;
Vdp2External_struct Vdp2External;

static int autoframeskipenab=0;
static int framestoskip=0;
static int framesskipped=0;
static int throttlespeed=0;
static int skipnextframe=0;
u64 lastticks=0;
static u64 curticks=0;
static u64 diffticks=0;
static u32 framecount=0;
static u64 onesecondticks=0;
static int fps;
static int fpsframecount=0;
static u64 fpsticks;
static int fpstoggle=0;

//////////////////////////////////////////////////////////////////////////////
int GetOSDToggle(void)
{
	return fpstoggle;
}

//////////////////////////////////////////////////////////////////////////////
void SetOSDToggle(int toggle)
{
	fpstoggle = toggle;
}

//////////////////////////////////////////////////////////////////////////////
//adelikat: This function will handle the OSDMessage variable properly, it should be used by other functions for displaying information to the user
void DisplayMessage(const char* str)
{
	strcpy(OSDMessage, str);
	OSDMessageTimer = 120;
}

//////////////////////////////////////////////////////////////////////////////

u8 FASTCALL Vdp2RamReadByte(u32 addr) {
   addr &= 0x7FFFF;
   return T1ReadByte(Vdp2Ram, addr);
}

//////////////////////////////////////////////////////////////////////////////

u16 FASTCALL Vdp2RamReadWord(u32 addr) {
   addr &= 0x7FFFF;
   return T1ReadWord(Vdp2Ram, addr);
}

//////////////////////////////////////////////////////////////////////////////

u32 FASTCALL Vdp2RamReadLong(u32 addr) {
   addr &= 0x7FFFF;
   return T1ReadLong(Vdp2Ram, addr);
}

//////////////////////////////////////////////////////////////////////////////

void FASTCALL Vdp2RamWriteByte(u32 addr, u8 val) {
   addr &= 0x7FFFF;
   T1WriteByte(Vdp2Ram, addr, val);
}

//////////////////////////////////////////////////////////////////////////////

void FASTCALL Vdp2RamWriteWord(u32 addr, u16 val) {
   addr &= 0x7FFFF;
   T1WriteWord(Vdp2Ram, addr, val);
}

//////////////////////////////////////////////////////////////////////////////

void FASTCALL Vdp2RamWriteLong(u32 addr, u32 val) {
   addr &= 0x7FFFF;
   T1WriteLong(Vdp2Ram, addr, val);
}

//////////////////////////////////////////////////////////////////////////////

u8 FASTCALL Vdp2ColorRamReadByte(u32 addr) {
   addr &= 0xFFF;
   return T2ReadByte(Vdp2ColorRam, addr);
}

//////////////////////////////////////////////////////////////////////////////

u16 FASTCALL Vdp2ColorRamReadWord(u32 addr) {
   addr &= 0xFFF;
   return T2ReadWord(Vdp2ColorRam, addr);
}

//////////////////////////////////////////////////////////////////////////////

u32 FASTCALL Vdp2ColorRamReadLong(u32 addr) {
   addr &= 0xFFF;
   return T2ReadLong(Vdp2ColorRam, addr);
}

//////////////////////////////////////////////////////////////////////////////

void FASTCALL Vdp2ColorRamWriteByte(u32 addr, u8 val) {
   addr &= 0xFFF;
   T2WriteByte(Vdp2ColorRam, addr, val);
}

//////////////////////////////////////////////////////////////////////////////

void FASTCALL Vdp2ColorRamWriteWord(u32 addr, u16 val) {
   addr &= 0xFFF;
   T2WriteWord(Vdp2ColorRam, addr, val);
//   if (Vdp2Internal.ColorMode == 0)
//      T1WriteWord(Vdp2ColorRam, addr + 0x800, val);
}

//////////////////////////////////////////////////////////////////////////////

void FASTCALL Vdp2ColorRamWriteLong(u32 addr, u32 val) {
   addr &= 0xFFF;
   T2WriteLong(Vdp2ColorRam, addr, val);
}

//////////////////////////////////////////////////////////////////////////////

int Vdp2Init(void) {
   if ((Vdp2Regs = (Vdp2 *) calloc(1, sizeof(Vdp2))) == NULL)
      return -1;

   if ((Vdp2Ram = T1MemoryInit(0x80000)) == NULL)
      return -1;

   if ((Vdp2ColorRam = T2MemoryInit(0x1000)) == NULL)
      return -1;

   Vdp2Reset();
   return 0;
}

//////////////////////////////////////////////////////////////////////////////

void Vdp2DeInit(void) {
   if (Vdp2Regs)
      free(Vdp2Regs);
   Vdp2Regs = NULL;

   if (Vdp2Ram)
      T1MemoryDeInit(Vdp2Ram);
   Vdp2Ram = NULL;

   if (Vdp2ColorRam)
      T2MemoryDeInit(Vdp2ColorRam);
   Vdp2ColorRam = NULL;
}

//////////////////////////////////////////////////////////////////////////////

void Vdp2Reset(void) {
   Vdp2Regs->TVMD = 0x0000;
   Vdp2Regs->EXTEN = 0x0000;
   Vdp2Regs->TVSTAT = Vdp2Regs->TVSTAT & 0x1;
   Vdp2Regs->VRSIZE = 0x0000; // fix me(version should be set)
   Vdp2Regs->RAMCTL = 0x0000;
   Vdp2Regs->BGON = 0x0000;
   Vdp2Regs->CHCTLA = 0x0000;
   Vdp2Regs->CHCTLB = 0x0000;
   Vdp2Regs->BMPNA = 0x0000;
   Vdp2Regs->MPOFN = 0x0000;
   Vdp2Regs->MPABN2 = 0x0000;
   Vdp2Regs->MPCDN2 = 0x0000;
   Vdp2Regs->SCXIN0 = 0x0000;
   Vdp2Regs->SCXDN0 = 0x0000;
   Vdp2Regs->SCYIN0 = 0x0000;
   Vdp2Regs->SCYDN0 = 0x0000;
   Vdp2Regs->ZMXN0.all = 0x00000000;
   Vdp2Regs->ZMYN0.all = 0x00000000;
   Vdp2Regs->SCXIN1 = 0x0000;
   Vdp2Regs->SCXDN1 = 0x0000;
   Vdp2Regs->SCYIN1 = 0x0000;
   Vdp2Regs->SCYDN1 = 0x0000;
   Vdp2Regs->ZMXN1.all = 0x00000000;
   Vdp2Regs->ZMYN1.all = 0x00000000;
   Vdp2Regs->SCXN2 = 0x0000;
   Vdp2Regs->SCYN2 = 0x0000;
   Vdp2Regs->SCXN3 = 0x0000;
   Vdp2Regs->SCYN3 = 0x0000;
   Vdp2Regs->ZMCTL = 0x0000;
   Vdp2Regs->SCRCTL = 0x0000;
   Vdp2Regs->VCSTA.all = 0x00000000;
   Vdp2Regs->BKTAU = 0x0000;
   Vdp2Regs->BKTAL = 0x0000;
   Vdp2Regs->RPMD = 0x0000;
   Vdp2Regs->RPRCTL = 0x0000;
   Vdp2Regs->KTCTL = 0x0000;
   Vdp2Regs->KTAOF = 0x0000;
   Vdp2Regs->OVPNRA = 0x0000;
   Vdp2Regs->OVPNRB = 0x0000;
   Vdp2Regs->WPSX0 = 0x0000;
   Vdp2Regs->WPSY0 = 0x0000;
   Vdp2Regs->WPEX0 = 0x0000;
   Vdp2Regs->WPEY0 = 0x0000;
   Vdp2Regs->WPSX1 = 0x0000;
   Vdp2Regs->WPSY1 = 0x0000;
   Vdp2Regs->WPEX1 = 0x0000;
   Vdp2Regs->WPEY1 = 0x0000;
   Vdp2Regs->WCTLA = 0x0000;
   Vdp2Regs->WCTLB = 0x0000;
   Vdp2Regs->WCTLC = 0x0000;
   Vdp2Regs->WCTLD = 0x0000;
   Vdp2Regs->SPCTL = 0x0000;
   Vdp2Regs->SDCTL = 0x0000;
   Vdp2Regs->CRAOFA = 0x0000;
   Vdp2Regs->CRAOFB = 0x0000;
   Vdp2Regs->LNCLEN = 0x0000;
   Vdp2Regs->SFPRMD = 0x0000;
   Vdp2Regs->CCCTL = 0x0000;
   Vdp2Regs->SFCCMD = 0x0000;
   Vdp2Regs->PRISA = 0x0000;
   Vdp2Regs->PRISB = 0x0000;
   Vdp2Regs->PRISC = 0x0000;
   Vdp2Regs->PRISD = 0x0000;
   Vdp2Regs->PRINA = 0x0000;
   Vdp2Regs->PRINB = 0x0000;
   Vdp2Regs->PRIR = 0x0000;
   Vdp2Regs->CCRNA = 0x0000;
   Vdp2Regs->CCRNB = 0x0000;
   Vdp2Regs->CLOFEN = 0x0000;
   Vdp2Regs->CLOFSL = 0x0000;
   Vdp2Regs->COAR = 0x0000;
   Vdp2Regs->COAG = 0x0000;
   Vdp2Regs->COAB = 0x0000;
   Vdp2Regs->COBR = 0x0000;
   Vdp2Regs->COBG = 0x0000;
   Vdp2Regs->COBB = 0x0000;

   yabsys.VBlankLineCount = 224;
   Vdp2Internal.ColorMode = 0;

   Vdp2External.disptoggle = 0xFF;
}

//////////////////////////////////////////////////////////////////////////////
// VDP2 Debug vvv

typedef struct _vdp2_regs_log_t
{
    char siz;     /* Register size (in bytes).                  */
    u32  msk;     /* Mask when reading from 32bits value.       */
    u32* val;     /* Pointer to value in yabause internal data. */
    char str[32]; /* String in a way like "REG = 0x1234".       */
    u32  adr;     /* Register address.                          */
    char nam[32]; /* Register name.                             */
} vdp2_regs_log_t;





unsigned long _ulFrameID = 0;

/* Number of frames between 2 debug request polls. */
unsigned long ulPollInterval = 10;

#include <windows.h>
#include <stdio.h>
#include <stdarg.h>
Vdp2 _Vdp2Regs_prev;
#define VDPLOG_INIFILE    "D:\\log\\sat_dbg.ini"
#define VDPLOG_INISECTION "yabause"

//http://mamedev.org/source/src/mame/drivers/stv.c.html
//   67  0x05e00000, 0x05e7ffff  VDP2
//   68  0x05e80000, 0x05efffff  VDP2 Extra RAM,accessible thru the VRAMSZ register
//   69  0x05f00000, 0x05f00fff  VDP2 Color RAM
#define VDP2_D1_STT 0x05e00000
#define VDP2_D1_LEN (0x05efffff - VDP2_D1_STT + 1)

#define VDP2_D2_STT 0x05f00000
#define VDP2_D2_LEN (0x05f00fff - VDP2_D2_STT + 1)

void vdp2_save_regs(void)
{
    Vdp2* Vdp2RegsP = &_Vdp2Regs_prev;
    char filename[MAX_PATH];
    int i;
    FILE* fp = NULL;



    if(_ulFrameID % ulPollInterval == 0)
    {
        OutputDebugString("Perform debug poll");

        /* Update poll interval value. */
        ulPollInterval = GetPrivateProfileInt(VDPLOG_INISECTION, "interval", 100, VDPLOG_INIFILE);


vdp2_regs_log_t vdp2_regs_log[] = {
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->TVMD),   "TVMD   = 0x%04X;", 0x25F80000, "TVMD"  }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->EXTEN),  "EXTEN  = 0x%04X;", 0x25F80002, "EXTEN" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->TVSTAT), "TVSTAT = 0x%04X;", 0x25F80004, "TVSTAT"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->VRSIZE), "VRSIZE = 0x%04X;", 0x25F80006, "VRSIZE"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->HCNT),   "HCNT   = 0x%04X;", 0x25F80008, "HCNT"  }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->VCNT),   "VCNT   = 0x%04X;", 0x25F8000A, "VCNT"  }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->RAMCTL), "RAMCTL = 0x%04X;", 0x25F8000E, "RAMCTL"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CYCA0L), "CYCA0L = 0x%04X;", 0x25F80010, "CYCA0L"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CYCA0U), "CYCA0U = 0x%04X;", 0x25F80012, "CYCA0U"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CYCA1L), "CYCA1L = 0x%04X;", 0x25F80014, "CYCA1L"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CYCA1U), "CYCA1U = 0x%04X;", 0x25F80016, "CYCA1U"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CYCB0L), "CYCB0L = 0x%04X;", 0x25F80018, "CYCB0L"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CYCB0U), "CYCB0U = 0x%04X;", 0x25F8001A, "CYCB0U"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CYCB1L), "CYCB1L = 0x%04X;", 0x25F8001C, "CYCB1L"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CYCB1U), "CYCB1U = 0x%04X;", 0x25F8001E, "CYCB1U"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->BGON),   "BGON   = 0x%04X;", 0x25F80020, "BGON"  }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MZCTL),  "MZCTL  = 0x%04X;", 0x25F80022, "MZCTL" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->SFSEL),  "SFSEL  = 0x%04X;", 0x25F80024, "SFSEL" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->SFCODE), "SFCODE = 0x%04X;", 0x25F80026, "SFCODE"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CHCTLA), "CHCTLA = 0x%04X;", 0x25F80028, "CHCTLA"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CHCTLB), "CHCTLB = 0x%04X;", 0x25F8002A, "CHCTLB"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->BMPNA),  "BMPNA  = 0x%04X;", 0x25F8002C, "BMPNA" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->BMPNB),  "BMPNB  = 0x%04X;", 0x25F8002E, "BMPNB" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->PNCN0),  "PNCN0  = 0x%04X;", 0x25F80030, "PNCN0" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->PNCN1),  "PNCN1  = 0x%04X;", 0x25F80032, "PNCN1" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->PNCN2),  "PNCN2  = 0x%04X;", 0x25F80034, "PNCN2" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->PNCN3),  "PNCN3  = 0x%04X;", 0x25F80036, "PNCN3" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->PNCR),   "PNCR   = 0x%04X;", 0x25F80038, "PNCR"  }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->PLSZ),   "PLSZ   = 0x%04X;", 0x25F8003A, "PLSZ"  }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPOFN),  "MPOFN  = 0x%04X;", 0x25F8003C, "MPOFN" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPOFR),  "MPOFR  = 0x%04X;", 0x25F8003E, "MPOFR" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPABN0), "MPABN0 = 0x%04X;", 0x25F80040, "MPABN0"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPCDN0), "MPCDN0 = 0x%04X;", 0x25F80042, "MPCDN0"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPABN1), "MPABN1 = 0x%04X;", 0x25F80044, "MPABN1"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPCDN1), "MPCDN1 = 0x%04X;", 0x25F80046, "MPCDN1"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPABN2), "MPABN2 = 0x%04X;", 0x25F80048, "MPABN2"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPCDN2), "MPCDN2 = 0x%04X;", 0x25F8004A, "MPCDN2"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPABN3), "MPABN3 = 0x%04X;", 0x25F8004C, "MPABN3"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPCDN3), "MPCDN3 = 0x%04X;", 0x25F8004E, "MPCDN3"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPABRA), "MPABRA = 0x%04X;", 0x25F80050, "MPABRA"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPCDRA), "MPCDRA = 0x%04X;", 0x25F80052, "MPCDRA"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPEFRA), "MPEFRA = 0x%04X;", 0x25F80054, "MPEFRA"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPGHRA), "MPGHRA = 0x%04X;", 0x25F80056, "MPGHRA"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPIJRA), "MPIJRA = 0x%04X;", 0x25F80058, "MPIJRA"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPKLRA), "MPKLRA = 0x%04X;", 0x25F8005A, "MPKLRA"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPMNRA), "MPMNRA = 0x%04X;", 0x25F8005C, "MPMNRA"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPOPRA), "MPOPRA = 0x%04X;", 0x25F8005E, "MPOPRA"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPABRB), "MPABRB = 0x%04X;", 0x25F80060, "MPABRB"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPCDRB), "MPCDRB = 0x%04X;", 0x25F80062, "MPCDRB"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPEFRB), "MPEFRB = 0x%04X;", 0x25F80064, "MPEFRB"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPGHRB), "MPGHRB = 0x%04X;", 0x25F80066, "MPGHRB"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPIJRB), "MPIJRB = 0x%04X;", 0x25F80068, "MPIJRB"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPKLRB), "MPKLRB = 0x%04X;", 0x25F8006A, "MPKLRB"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPMNRB), "MPMNRB = 0x%04X;", 0x25F8006C, "MPMNRB"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->MPOPRB), "MPOPRB = 0x%04X;", 0x25F8006E, "MPOPRB"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->SCXIN0), "SCXIN0 = 0x%04X;", 0x25F80070, "SCXIN0"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->SCXDN0), "SCXDN0 = 0x%04X;", 0x25F80072, "SCXDN0"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->SCYIN0), "SCYIN0 = 0x%04X;", 0x25F80074, "SCYIN0"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->SCYDN0), "SCYDN0 = 0x%04X;", 0x25F80076, "SCYDN0"}, 
        {4, 0xFFFFFFFF,    &(Vdp2Regs->ZMXN0.all), "ZMXN0  = 0x%08X;", 0x25F80078, "ZMXN0" }, 
        {4, 0xFFFFFFFF,    &(Vdp2Regs->ZMYN0.all), "ZMYN0  = 0x%08X;", 0x25F8007C, "ZMYN0" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->SCXIN1), "SCXIN1 = 0x%04X;", 0x25F80080, "SCXIN1"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->SCXDN1), "SCXDN1 = 0x%04X;", 0x25F80082, "SCXDN1"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->SCYIN1), "SCYIN1 = 0x%04X;", 0x25F80084, "SCYIN1"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->SCYDN1), "SCYDN1 = 0x%04X;", 0x25F80086, "SCYDN1"}, 
        {4, 0xFFFFFFFF,    &(Vdp2Regs->ZMXN1.all), "ZMXN1  = 0x%08X;", 0x25F80088, "ZMXN1" }, 
        {4, 0xFFFFFFFF,    &(Vdp2Regs->ZMYN1.all), "ZMYN1  = 0x%08X;", 0x25F8008C, "ZMYN1" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->SCXN2 ), "SCXN2  = 0x%04X;", 0x25F80090, "SCXN2" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->SCYN2 ), "SCYN2  = 0x%04X;", 0x25F80092, "SCYN2" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->SCXN3 ), "SCXN3  = 0x%04X;", 0x25F80094, "SCXN3" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->SCYN3 ), "SCYN3  = 0x%04X;", 0x25F80096, "SCYN3" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->ZMCTL ), "ZMCTL  = 0x%04X;", 0x25F80098, "ZMCTL" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->SCRCTL), "SCRCTL = 0x%04X;", 0x25F8009A, "SCRCTL"}, 
        {4, 0xFFFFFFFF,    &(Vdp2Regs->VCSTA.all), "VCSTA  = 0x%08X;", 0x25F8009C, "VCSTA" }, 
        {4, 0xFFFFFFFF,    &(Vdp2Regs->LSTA0.all), "LSTA0  = 0x%08X;", 0x25F800A0, "LSTA0" }, 
        {4, 0xFFFFFFFF,    &(Vdp2Regs->LSTA1.all), "LSTA1  = 0x%08X;", 0x25F800A4, "LSTA1" }, 
        {4, 0xFFFFFFFF,    &(Vdp2Regs->LCTA.all ), "LCTA   = 0x%08X;", 0x25F800A8, "LCTA"  }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->BKTAU) , "BKTAU  = 0x%04X;", 0x25F800AC, "BKTAU" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->BKTAL) , "BKTAL  = 0x%04X;", 0x25F800AE, "BKTAL" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->RPMD)  , "RPMD   = 0x%04X;", 0x25F800B0, "RPMD"  }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->RPRCTL), "RPRCTL = 0x%04X;", 0x25F800B2, "RPRCTL"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->KTCTL) , "KTCTL  = 0x%04X;", 0x25F800B4, "KTCTL" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->KTAOF) , "KTAOF  = 0x%04X;", 0x25F800B6, "KTAOF" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->OVPNRA), "OVPNRA = 0x%04X;", 0x25F800B8, "OVPNRA"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->OVPNRB), "OVPNRB = 0x%04X;", 0x25F800BA, "OVPNRB"}, 
        {4, 0xFFFFFFFF,     &(Vdp2Regs->RPTA.all), "RPTA   = 0x%08X;", 0x25F800BC, "RPTA"  }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->WPSX0) , "WPSX0  = 0x%04X;", 0x25F800C0, "WPSX0" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->WPSY0) , "WPSY0  = 0x%04X;", 0x25F800C2, "WPSY0" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->WPEX0) , "WPEX0  = 0x%04X;", 0x25F800C4, "WPEX0" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->WPEY0) , "WPEY0  = 0x%04X;", 0x25F800C6, "WPEY0" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->WPSX1) , "WPSX1  = 0x%04X;", 0x25F800C8, "WPSX1" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->WPSY1) , "WPSY1  = 0x%04X;", 0x25F800CA, "WPSY1" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->WPEX1) , "WPEX1  = 0x%04X;", 0x25F800CC, "WPEX1" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->WPEY1) , "WPEY1  = 0x%04X;", 0x25F800CE, "WPEY1" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->WCTLA) , "WCTLA  = 0x%04X;", 0x25F800D0, "WCTLA" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->WCTLB) , "WCTLB  = 0x%04X;", 0x25F800D2, "WCTLB" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->WCTLC) , "WCTLC  = 0x%04X;", 0x25F800D4, "WCTLC" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->WCTLD) , "WCTLD  = 0x%04X;", 0x25F800D6, "WCTLD" }, 
        {4, 0xFFFFFFFF,    &(Vdp2Regs->LWTA0.all), "LWTA0  = 0x%08X;", 0x25F800D8, "LWTA0" }, 
        {4, 0xFFFFFFFF,    &(Vdp2Regs->LWTA1.all), "LWTA1  = 0x%08X;", 0x25F800DC, "LWTA1" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->SPCTL) , "SPCTL  = 0x%04X;", 0x25F800E0, "SPCTL" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->SDCTL) , "SDCTL  = 0x%04X;", 0x25F800E2, "SDCTL" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CRAOFA), "CRAOFA = 0x%04X;", 0x25F800E4, "CRAOFA"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CRAOFB), "CRAOFB = 0x%04X;", 0x25F800E6, "CRAOFB"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->LNCLEN), "LNCLEN = 0x%04X;", 0x25F800E8, "LNCLEN"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->SFPRMD), "SFPRMD = 0x%04X;", 0x25F800EA, "SFPRMD"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CCCTL) , "CCCTL  = 0x%04X;", 0x25F800EC, "CCCTL" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->SFCCMD), "SFCCMD = 0x%04X;", 0x25F800EE, "SFCCMD"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->PRISA) , "PRISA  = 0x%04X;", 0x25F800F0, "PRISA" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->PRISB) , "PRISB  = 0x%04X;", 0x25F800F2, "PRISB" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->PRISC) , "PRISC  = 0x%04X;", 0x25F800F4, "PRISC" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->PRISD) , "PRISD  = 0x%04X;", 0x25F800F6, "PRISD" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->PRINA) , "PRINA  = 0x%04X;", 0x25F800F8, "PRINA" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->PRINB) , "PRINB  = 0x%04X;", 0x25F800FA, "PRINB" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->PRIR)  , "PRIR   = 0x%04X;", 0x25F800FC, "PRIR"  }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CCRSA) , "CCRSA  = 0x%04X;", 0x25F80100, "CCRSA" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CCRSB) , "CCRSB  = 0x%04X;", 0x25F80102, "CCRSB" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CCRSC) , "CCRSC  = 0x%04X;", 0x25F80104, "CCRSC" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CCRSD) , "CCRSD  = 0x%04X;", 0x25F80106, "CCRSD" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CCRNA) , "CCRNA  = 0x%04X;", 0x25F80108, "CCRNA" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CCRNB) , "CCRNB  = 0x%04X;", 0x25F8010A, "CCRNB" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CCRR)  , "CCRR   = 0x%04X;", 0x25F8010C, "CCRR"  }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CCRLB) , "CCRLB  = 0x%04X;", 0x25F8010E, "CCRLB" }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CLOFEN), "CLOFEN = 0x%04X;", 0x25F80110, "CLOFEN"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->CLOFSL), "CLOFSL = 0x%04X;", 0x25F80112, "CLOFSL"}, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->COAR)  , "COAR   = 0x%04X;", 0x25F80114, "COAR"  }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->COAG)  , "COAG   = 0x%04X;", 0x25F80116, "COAG"  }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->COAB)  , "COAB   = 0x%04X;", 0x25F80118, "COAB"  }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->COBR)  , "COBR   = 0x%04X;", 0x25F8011A, "COBR"  }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->COBG)  , "COBG   = 0x%04X;", 0x25F8011C, "COBG"  }, 
        {2, 0x0000FFFF, (u32*)&(Vdp2Regs->COBB)  , "COBB   = 0x%04X;", 0x25F8011E, "COBB"  }, 
        {0,          0,                         0,                 "",          0,       ""} /* Last entry */
};

        /* Init the previous registers states. */
        if(_ulFrameID == 0)
        {
            OutputDebugString(" Init the previous registers states");
            memcpy((void*)Vdp2RegsP, (void*)Vdp2Regs, sizeof(Vdp2));
        }


        int iLog = GetPrivateProfileInt(VDPLOG_INISECTION, "vdp2log", 0, VDPLOG_INIFILE);
        if(iLog != 0)
        {
            OutputDebugString(" PERFORM LOG");
            /* Perform a screenshot. */
            //sprintf(filename, "D:\\log\\log_%08X_scr.bmp", _ulFrameID);
            //YuiTempPause();
            //YuiCaptureScreen(filename);
            //YuiTempUnPause();

            /* Log each regs. */
            sprintf(filename, "D:\\log\\log_%08X_reg.txt", _ulFrameID);
            fp = fopen(filename, "wb");
            if(fp)
            {
                /* Display all the registers states ? */
                int iAllDisp = GetPrivateProfileInt(VDPLOG_INISECTION, "alldisp", 0, VDPLOG_INIFILE);
                if(iAllDisp != 0)
                {
                    OutputDebugString(" -> Display all registers states");
                    fprintf(fp, "#if 0\r\n");
                    i=0; do
                    {
                        u32 val;
                        val = *(vdp2_regs_log[i].val);
                        val = val & vdp2_regs_log[i].msk;
                        
                        fprintf(fp, vdp2_regs_log[i].str, val);
                        fprintf(fp, "\r\n");

                        i++;
                    } while(vdp2_regs_log[i].adr != 0);
                    fprintf(fp, "#endif\r\n");
                    fprintf(fp, "\r\n");
                } // All Disp


                /* Display Only registers that changed ? */
                int iDiffDisp = GetPrivateProfileInt(VDPLOG_INISECTION, "diffdisp", 0, VDPLOG_INIFILE);
                if(iDiffDisp != 0)
                {
                    OutputDebugString(" -> Display changed registers states");
                    fprintf(fp, "// VDP2 register set STT\r\n");
                    i=0; do
                    {
                        int offset = (int)((unsigned char*)(vdp2_regs_log[i].val) - (unsigned char*)Vdp2Regs);
                        u32* val1 = (u32*)(((unsigned char*)Vdp2Regs ) + offset);
                        u32* val2 = (u32*)(((unsigned char*)Vdp2RegsP) + offset);
                        if(((*val1) & vdp2_regs_log[i].msk) != ((*val2) & vdp2_regs_log[i].msk))
                        {
                            u32 val = *val1;
                            val = val & vdp2_regs_log[i].msk;

                            if(vdp2_regs_log[i].siz == 2)
                            {
                                fprintf(fp, "(*(volatile unsigned short *)(0x%08X)) = 0x%04X; // %s\r\n", vdp2_regs_log[i].adr, val, vdp2_regs_log[i].nam);
                            }
                            else
                            {
                                fprintf(fp, "(*(volatile unsigned long  *)(0x%08X)) = 0x%08X; // %s\r\n", vdp2_regs_log[i].adr, val, vdp2_regs_log[i].nam);
                            }
                        }

                        i++;
                    } while(vdp2_regs_log[i].adr != 0);
                    fprintf(fp, "// VDP2 register set END\r\n");
                    fprintf(fp, "\r\n");
                } // Diff Disp


                /* Need to dump VDP2 RAM ? */
                int iDump = GetPrivateProfileInt(VDPLOG_INISECTION, "dump", 0, VDPLOG_INIFILE);
                if(iDump != 0)
                {
                    OutputDebugString(" -> Dump VDP2 RAM");
                    u32 i, j;
                    u8* buff = NULL;
                    u32 dump_stt, dump_len;
                    FILE* fq = NULL;

                    fprintf(fp, "// VDP2 dump information:\r\n");
                    for(i=0; i<2; i++)
                    {
                        dump_stt = (i==0 ? VDP2_D1_STT : VDP2_D2_STT);
                        dump_len = (i==0 ? VDP2_D1_LEN : VDP2_D2_LEN);
                        buff = (u8*)malloc(dump_len);
                        if(buff)
                        {
                            for(j=0; j<dump_len; j++)
                            {
                                if(i == 0)
                                {
                                    buff[j] = Vdp2RamReadByte(dump_stt + j);
                                }
                                else
                                {
                                    buff[j] = Vdp2ColorRamReadByte(dump_stt + j);
                                }
                            }

                            sprintf(filename, "D:\\log\\D%d%06X.BIN", i, _ulFrameID);
                            fq = fopen(filename, "wb");
                            if(fq)
                            {
                                fwrite((void*)buff, 1, dump_len, fq);
                                fclose(fq);

                                fprintf(fp, "#define VDP2_D%d_FIL \"D%d%06X.BIN\"\r\n", i, i, _ulFrameID);
                                fprintf(fp, "#define VDP2_D%d_STT (0x%08X)\r\n", i, dump_stt);
                                fprintf(fp, "#define VDP2_D%d_LEN (0x%08X)\r\n", i, dump_len);
                            }
                            free(buff);
                        }
                    }
                    fprintf(fp, "\r\n");
                } // Dump VDP2




                fclose(fp);
            } // Log file open



            /* Need to save the previous registers states ? */
            int iSaveRef = GetPrivateProfileInt(VDPLOG_INISECTION, "saveref", 0, VDPLOG_INIFILE);
            if(iSaveRef != 0)
            {
                OutputDebugString(" Save previous registers states");
                memcpy((void*)Vdp2RegsP, (void*)Vdp2Regs, sizeof(Vdp2));
            }
        } // Log request
    } // Poll


    _ulFrameID++;
}

// VDP2 Debug ^^^
//////////////////////////////////////////////////////////////////////////////

void Vdp2VBlankIN(void) {
vdp2_save_regs();
   Vdp2Regs->TVSTAT |= 0x0008;
   ScuSendVBlankIN();

   if (yabsys.IsSSH2Running)
      SH2SendInterrupt(SSH2, 0x43, 0x6);
}

//////////////////////////////////////////////////////////////////////////////

void Vdp2HBlankIN(void) {
   Vdp2Regs->TVSTAT |= 0x0004;
   ScuSendHBlankIN();

   if (yabsys.IsSSH2Running)
      SH2SendInterrupt(SSH2, 0x41, 0x2);
}

//////////////////////////////////////////////////////////////////////////////

void Vdp2HBlankOUT(void) {
   Vdp2Regs->TVSTAT &= ~0x0004;
}

//////////////////////////////////////////////////////////////////////////////

static void FPSDisplay(void)
{
   if (fpstoggle)
   {
      if (!OSDMessageTimer)
		OSDMessage[0] = 0;
	  VIDCore->OnScreenDebugMessage("%02d/%02d FPS %d %d %s %s %s", fps, yabsys.IsPal ? 50 : 60, framecounter, lagframecounter, MovieStatus, InputDisplayString, OSDMessage);
	  if (OSDMessageTimer > 0)
		  OSDMessageTimer--;
      fpsframecount++;
      if(YabauseGetTicks() >= fpsticks + yabsys.tickfreq)
      {
         fps = fpsframecount;
         fpsframecount = 0;
         fpsticks = YabauseGetTicks();
      }
   }
}

//////////////////////////////////////////////////////////////////////////////

void ToggleFPS(void)
{
   fpstoggle ^= 1;
}

//////////////////////////////////////////////////////////////////////////////

void SpeedThrottleEnable(void) {
   throttlespeed = 1;
}

//////////////////////////////////////////////////////////////////////////////

void SpeedThrottleDisable(void) {
   throttlespeed = 0;
}

//////////////////////////////////////////////////////////////////////////////

void Vdp2VBlankOUT(void) {
   Vdp2Regs->TVSTAT = (Vdp2Regs->TVSTAT & ~0x0008) | 0x0002;

   if (!skipnextframe)
   {
      VIDCore->Vdp2DrawStart();

      if (Vdp2Regs->TVMD & 0x8000) {
         VIDCore->Vdp2DrawScreens();
         Vdp1Draw();
      }
      else
         Vdp1NoDraw();

      FPSDisplay();
      VIDCore->Vdp2DrawEnd();

      framesskipped = 0;

      if (framestoskip > 0)
         skipnextframe = 1;
   }
   else
   {
      framestoskip--;

      if (framestoskip < 1)
         skipnextframe = 0;
      else
         skipnextframe = 1;
      Vdp1NoDraw();

      framesskipped++;
   }

   // Do Frame Skip/Frame Limiting/Speed Throttling here
   if (throttlespeed)
   {
      // Should really depend on how fast we're rendering the frames
      if (framestoskip < 1)
         framestoskip = 6;
   }
   //when in frame advance, disable frame skipping
   else if (autoframeskipenab && FrameAdvanceVariable == 0)
   {
      framecount++;

      if (framecount > (yabsys.IsPal ? 50 : 60))
      {
         framecount = 1;
         onesecondticks = 0;
      }

      curticks = YabauseGetTicks();
      diffticks = curticks-lastticks;

      if ((onesecondticks+diffticks) > ((yabsys.OneFrameTime * (u64)framecount) + (yabsys.OneFrameTime / 2)) &&
          framesskipped < 9)
      {
         // Skip the next frame
         skipnextframe = 1;

         // How many frames should we skip?
         framestoskip = 1;
      }
      else if ((onesecondticks+diffticks) < ((yabsys.OneFrameTime * (u64)framecount) - (yabsys.OneFrameTime / 2)))
      {
         // Check to see if we need to limit speed at all
         for (;;)
         {
            curticks = YabauseGetTicks();
            diffticks = curticks-lastticks;
            if ((onesecondticks+diffticks) >= (yabsys.OneFrameTime * (u64)framecount))
               break;
         }
      }

      onesecondticks += diffticks;
      lastticks = curticks;
   }

   ScuSendVBlankOUT();
}

//////////////////////////////////////////////////////////////////////////////

u8 FASTCALL Vdp2ReadByte(u32 addr) {
   LOG("VDP2 register byte read = %08X\n", addr);
   addr &= 0x1FF;
   return 0;
}

//////////////////////////////////////////////////////////////////////////////

u16 FASTCALL Vdp2ReadWord(u32 addr) {
   addr &= 0x1FF;

   switch (addr)
   {
      case 0x000:
         return Vdp2Regs->TVMD;
      case 0x002:
         if (!(Vdp2Regs->EXTEN & 0x200))
         {
            // Latch HV counter on read
            // Vdp2Regs->HCNT = ?;
            Vdp2Regs->VCNT = yabsys.LineCount;
            Vdp2Regs->TVSTAT |= 0x200;
         }

         return Vdp2Regs->EXTEN;
      case 0x004:
         // Clear External latch and sync flags
         Vdp2Regs->TVSTAT &= 0xFCFF;

         // if TVMD's DISP bit is cleared, TVSTAT's VBLANK bit is always set
         if (Vdp2Regs->TVMD & 0x8000)
            return Vdp2Regs->TVSTAT;
         else
            return (Vdp2Regs->TVSTAT | 0x8);
      case 0x006:         
         return Vdp2Regs->VRSIZE;
      case 0x008:
         return Vdp2Regs->HCNT;
      case 0x00A:
         return Vdp2Regs->VCNT;
      default:
      {
         LOG("Unhandled VDP2 word read: %08X\n", addr);
         break;
      }
   }

   return 0;
}

//////////////////////////////////////////////////////////////////////////////

u32 FASTCALL Vdp2ReadLong(u32 addr) {
   LOG("VDP2 register long read = %08X\n", addr);
   addr &= 0x1FF;
   return 0;
}

//////////////////////////////////////////////////////////////////////////////

void FASTCALL Vdp2WriteByte(u32 addr, UNUSED u8 val) {
   LOG("VDP2 register byte write = %08X\n", addr);
   addr &= 0x1FF;
}

//////////////////////////////////////////////////////////////////////////////

void FASTCALL Vdp2WriteWord(u32 addr, u16 val) {
   addr &= 0x1FF;

   switch (addr)
   {
      case 0x000:
         VIDCore->Vdp2SetResolution(val);
         Vdp2Regs->TVMD = val;
         yabsys.VBlankLineCount = 224+(val & 0x30);
         return;
      case 0x002:
         Vdp2Regs->EXTEN = val;
         return;
      case 0x004:
         // TVSTAT is read-only
         return;
      case 0x006:
         Vdp2Regs->VRSIZE = val;
         return;
      case 0x008:
         // HCNT is read-only
         return;
      case 0x00A:
         // VCNT is read-only
         return;
      case 0x00C:
         // Reserved
         return;
      case 0x00E:
         Vdp2Regs->RAMCTL = val;
         Vdp2Internal.ColorMode = (val >> 12) & 0x3;
         return;
      case 0x010:
         Vdp2Regs->CYCA0L = val;
         return;
      case 0x012:
         Vdp2Regs->CYCA0U = val;
         return;
      case 0x014:
         Vdp2Regs->CYCA1L = val;
         return;
      case 0x016:
         Vdp2Regs->CYCA1U = val;
         return;
      case 0x018:
         Vdp2Regs->CYCB0L = val;
         return;
      case 0x01A:
         Vdp2Regs->CYCB0U = val;
         return;
      case 0x01C:
         Vdp2Regs->CYCB1L = val;
         return;
      case 0x01E:
         Vdp2Regs->CYCB1U = val;
         return;
      case 0x020:
         Vdp2Regs->BGON = val;
         return;
      case 0x022:
         Vdp2Regs->MZCTL = val;
         return;
      case 0x024:
         Vdp2Regs->SFSEL = val;
         return;
      case 0x026:
         Vdp2Regs->SFCODE = val;
         return;
      case 0x028:
         Vdp2Regs->CHCTLA = val;
         return;
      case 0x02A:
         Vdp2Regs->CHCTLB = val;
         return;
      case 0x02C:
         Vdp2Regs->BMPNA = val;
         return;
      case 0x02E:
         Vdp2Regs->BMPNB = val;
         return;
      case 0x030:
         Vdp2Regs->PNCN0 = val;
         return;
      case 0x032:
         Vdp2Regs->PNCN1 = val;
         return;
      case 0x034:
         Vdp2Regs->PNCN2 = val;
         return;
      case 0x036:
         Vdp2Regs->PNCN3 = val;
         return;
      case 0x038:
         Vdp2Regs->PNCR = val;
         return;
      case 0x03A:
         Vdp2Regs->PLSZ = val;
         return;
      case 0x03C:
         Vdp2Regs->MPOFN = val;
         return;
      case 0x03E:
         Vdp2Regs->MPOFR = val;
         return;
      case 0x040:
         Vdp2Regs->MPABN0 = val;
         return;
      case 0x042:
         Vdp2Regs->MPCDN0 = val;
         return;
      case 0x044:
         Vdp2Regs->MPABN1 = val;
         return;
      case 0x046:
         Vdp2Regs->MPCDN1 = val;
         return;
      case 0x048:
         Vdp2Regs->MPABN2 = val;
         return;
      case 0x04A:
         Vdp2Regs->MPCDN2 = val;
         return;
      case 0x04C:
         Vdp2Regs->MPABN3 = val;
         return;
      case 0x04E:
         Vdp2Regs->MPCDN3 = val;
         return;
      case 0x050:
         Vdp2Regs->MPABRA = val;
         return;
      case 0x052:
         Vdp2Regs->MPCDRA = val;
         return;
      case 0x054:
         Vdp2Regs->MPEFRA = val;
         return;
      case 0x056:
         Vdp2Regs->MPGHRA = val;
         return;
      case 0x058:
         Vdp2Regs->MPIJRA = val;
         return;
      case 0x05A:
         Vdp2Regs->MPKLRA = val;
         return;
      case 0x05C:
         Vdp2Regs->MPMNRA = val;
         return;
      case 0x05E:
         Vdp2Regs->MPOPRA = val;
         return;
      case 0x060:
         Vdp2Regs->MPABRB = val;
         return;
      case 0x062:
         Vdp2Regs->MPCDRB = val;
         return;
      case 0x064:
         Vdp2Regs->MPEFRB = val;
         return;
      case 0x066:
         Vdp2Regs->MPGHRB = val;
         return;
      case 0x068:
         Vdp2Regs->MPIJRB = val;
         return;
      case 0x06A:
         Vdp2Regs->MPKLRB = val;
         return;
      case 0x06C:
         Vdp2Regs->MPMNRB = val;
         return;
      case 0x06E:
         Vdp2Regs->MPOPRB = val;
         return;
      case 0x070:
         Vdp2Regs->SCXIN0 = val;
         return;
      case 0x072:
         Vdp2Regs->SCXDN0 = val;
         return;
      case 0x074:
         Vdp2Regs->SCYIN0 = val;
         return;
      case 0x076:
         Vdp2Regs->SCYDN0 = val;
         return;
      case 0x078:
         Vdp2Regs->ZMXN0.part.I = val;
         return;
      case 0x07A:
         Vdp2Regs->ZMXN0.part.D = val;
         return;
      case 0x07C:
         Vdp2Regs->ZMYN0.part.I = val;
         return;
      case 0x07E:
         Vdp2Regs->ZMYN0.part.D = val;
         return;
      case 0x080:
         Vdp2Regs->SCXIN1 = val;
         return;
      case 0x082:
         Vdp2Regs->SCXDN1 = val;
         return;
      case 0x084:
         Vdp2Regs->SCYIN1 = val;
         return;
      case 0x086:
         Vdp2Regs->SCYDN1 = val;
         return;
      case 0x088:
         Vdp2Regs->ZMXN1.part.I = val;
         return;
      case 0x08A:
         Vdp2Regs->ZMXN1.part.D = val;
         return;
      case 0x08C:
         Vdp2Regs->ZMYN1.part.I = val;
         return;
      case 0x08E:
         Vdp2Regs->ZMYN1.part.D = val;
         return;
      case 0x090:
         Vdp2Regs->SCXN2 = val;
         return;
      case 0x092:
         Vdp2Regs->SCYN2 = val;
         return;
      case 0x094:
         Vdp2Regs->SCXN3 = val;
         return;
      case 0x096:
         Vdp2Regs->SCYN3 = val;
         return;
      case 0x098:
         Vdp2Regs->ZMCTL = val;
         return;
      case 0x09A:
         Vdp2Regs->SCRCTL = val;
         return;
      case 0x09C:
         Vdp2Regs->VCSTA.part.U = val;
         return;
      case 0x09E:
         Vdp2Regs->VCSTA.part.L = val;
         return;
      case 0x0A0:
         Vdp2Regs->LSTA0.part.U = val;
         return;
      case 0x0A2:
         Vdp2Regs->LSTA0.part.L = val;
         return;
      case 0x0A4:
         Vdp2Regs->LSTA1.part.U = val;
         return;
      case 0x0A6:
         Vdp2Regs->LSTA1.part.L = val;
         return;
      case 0x0A8:
         Vdp2Regs->LCTA.part.U = val;
         return;
      case 0x0AA:
         Vdp2Regs->LCTA.part.L = val;
         return;
      case 0x0AC:
         Vdp2Regs->BKTAU = val;
         return;
      case 0x0AE:
         Vdp2Regs->BKTAL = val;
         return;
      case 0x0B0:
         Vdp2Regs->RPMD = val;
         return;
      case 0x0B2:
         Vdp2Regs->RPRCTL = val;
         return;
      case 0x0B4:
         Vdp2Regs->KTCTL = val;
         return;
      case 0x0B6:
         Vdp2Regs->KTAOF = val;
         return;
      case 0x0B8:
         Vdp2Regs->OVPNRA = val;
         return;
      case 0x0BA:
         Vdp2Regs->OVPNRB = val;
         return;
      case 0x0BC:
         Vdp2Regs->RPTA.part.U = val;
         return;
      case 0x0BE:
         Vdp2Regs->RPTA.part.L = val;
         return;
      case 0x0C0:
         Vdp2Regs->WPSX0 = val;
         return;
      case 0x0C2:
         Vdp2Regs->WPSY0 = val;
         return;
      case 0x0C4:
         Vdp2Regs->WPEX0 = val;
         return;
      case 0x0C6:
         Vdp2Regs->WPEY0 = val;
         return;
      case 0x0C8:
         Vdp2Regs->WPSX1 = val;
         return;
      case 0x0CA:
         Vdp2Regs->WPSY1 = val;
         return;
      case 0x0CC:
         Vdp2Regs->WPEX1 = val;
         return;
      case 0x0CE:
         Vdp2Regs->WPEY1 = val;
         return;
      case 0x0D0:
         Vdp2Regs->WCTLA = val;
         return;
      case 0x0D2:
         Vdp2Regs->WCTLB = val;
         return;
      case 0x0D4:
         Vdp2Regs->WCTLC = val;
         return;
      case 0x0D6:
         Vdp2Regs->WCTLD = val;
         return;
      case 0x0D8:
         Vdp2Regs->LWTA0.part.U = val;
         return;
      case 0x0DA:
         Vdp2Regs->LWTA0.part.L = val;
         return;
      case 0x0DC:
         Vdp2Regs->LWTA1.part.U = val;
         return;
      case 0x0DE:
         Vdp2Regs->LWTA1.part.L = val;
         return;
      case 0x0E0:
         Vdp2Regs->SPCTL = val;
         return;
      case 0x0E2:
         Vdp2Regs->SDCTL = val;
         return;
      case 0x0E4:
         Vdp2Regs->CRAOFA = val;
         return;
      case 0x0E6:
         Vdp2Regs->CRAOFB = val;
         return;     
      case 0x0E8:
         Vdp2Regs->LNCLEN = val;
         return;
      case 0x0EA:
         Vdp2Regs->SFPRMD = val;
         return;
      case 0x0EC:
         Vdp2Regs->CCCTL = val;
         return;     
      case 0x0EE:
         Vdp2Regs->SFCCMD = val;
         return;
      case 0x0F0:
         Vdp2Regs->PRISA = val;
         return;
      case 0x0F2:
         Vdp2Regs->PRISB = val;
         return;
      case 0x0F4:
         Vdp2Regs->PRISC = val;
         return;
      case 0x0F6:
         Vdp2Regs->PRISD = val;
         return;
      case 0x0F8:
         VIDCore->Vdp2SetPriorityNBG0(val & 0x7);
         VIDCore->Vdp2SetPriorityNBG1((val >> 8) & 0x7);
         Vdp2Regs->PRINA = val;
         return;
      case 0x0FA:
         VIDCore->Vdp2SetPriorityNBG2(val & 0x7);
         VIDCore->Vdp2SetPriorityNBG3((val >> 8) & 0x7);
         Vdp2Regs->PRINB = val;
         return;
      case 0x0FC:
         VIDCore->Vdp2SetPriorityRBG0(val & 0x7);
         Vdp2Regs->PRIR = val;
         return;
      case 0x0FE:
         // Reserved
         return;
      case 0x100:
         Vdp2Regs->CCRSA = val;
         return;
      case 0x102:
         Vdp2Regs->CCRSB = val;
         return;
      case 0x104:
         Vdp2Regs->CCRSC = val;
         return;
      case 0x106:
         Vdp2Regs->CCRSD = val;
         return;
      case 0x108:
         Vdp2Regs->CCRNA = val;
         return;
      case 0x10A:
         Vdp2Regs->CCRNB = val;
         return;
      case 0x10C:
         Vdp2Regs->CCRR = val;
         return;
      case 0x10E:
         Vdp2Regs->CCRLB = val;
         return;
      case 0x110:
         Vdp2Regs->CLOFEN = val;
         return;
      case 0x112:
         Vdp2Regs->CLOFSL = val;
         return;
      case 0x114:
         Vdp2Regs->COAR = val;
         return;
      case 0x116:
         Vdp2Regs->COAG = val;
         return;
      case 0x118:
         Vdp2Regs->COAB = val;
         return;
      case 0x11A:
         Vdp2Regs->COBR = val;
         return;
      case 0x11C:
         Vdp2Regs->COBG = val;
         return;
      case 0x11E:
         Vdp2Regs->COBB = val;
         return;
      default:
      {
         LOG("Unhandled VDP2 word write: %08X\n", addr);
         break;
      }
   }
}

//////////////////////////////////////////////////////////////////////////////

void FASTCALL Vdp2WriteLong(u32 addr, u32 val) {
   addr &= 0x1FF;

   switch (addr)
   {
      case 0x000:
         Vdp2Regs->TVMD = val >> 16;
         Vdp2Regs->EXTEN = val & 0xFFFF;
         VIDCore->Vdp2SetResolution(Vdp2Regs->TVMD);
         yabsys.VBlankLineCount = 224+(Vdp2Regs->TVMD & 0x30);
         return;
      case 0x004:
         Vdp2Regs->VRSIZE = val & 0xFFFF;
         return;
      case 0x008:
         // read-only
         return;
      case 0x00C:
         Vdp2Regs->RAMCTL = val & 0xFFFF;
         return;
      case 0x010:
         Vdp2Regs->CYCA0L = val >> 16;
         Vdp2Regs->CYCA0U = val & 0xFFFF;
         return;
      case 0x014:
         Vdp2Regs->CYCA1L = val >> 16;
         Vdp2Regs->CYCA1U = val & 0xFFFF;
         return;
      case 0x018:
         Vdp2Regs->CYCB0L = val >> 16;
         Vdp2Regs->CYCB0U = val & 0xFFFF;
         return;
      case 0x01C:
         Vdp2Regs->CYCB1L = val >> 16;
         Vdp2Regs->CYCB1U = val & 0xFFFF;
         return;
      case 0x020:
         Vdp2Regs->BGON = val >> 16;
         Vdp2Regs->MZCTL = val & 0xFFFF;
         return;
      case 0x024:
         Vdp2Regs->SFSEL = val >> 16;
         Vdp2Regs->SFCODE = val & 0xFFFF;
         return;
      case 0x028:
         Vdp2Regs->CHCTLA = val >> 16;
         Vdp2Regs->CHCTLB = val & 0xFFFF;
         return;
      case 0x02C:
         Vdp2Regs->BMPNA = val >> 16;
         Vdp2Regs->BMPNB = val & 0xFFFF;
         return;
      case 0x030:
         Vdp2Regs->PNCN0 = val >> 16;
         Vdp2Regs->PNCN1 = val & 0xFFFF;
         return;
      case 0x034:
         Vdp2Regs->PNCN2 = val >> 16;
         Vdp2Regs->PNCN3 = val & 0xFFFF;
         return;
      case 0x038:
         Vdp2Regs->PNCR = val >> 16;
         Vdp2Regs->PLSZ = val & 0xFFFF;
         return;
      case 0x03C:
         Vdp2Regs->MPOFN = val >> 16;
         Vdp2Regs->MPOFR = val & 0xFFFF;
         return;
      case 0x040:
         Vdp2Regs->MPABN0 = val >> 16;
         Vdp2Regs->MPCDN0 = val & 0xFFFF;
         return;
      case 0x044:
         Vdp2Regs->MPABN1 = val >> 16;
         Vdp2Regs->MPCDN1 = val & 0xFFFF;
         return;
      case 0x048:
         Vdp2Regs->MPABN2 = val >> 16;
         Vdp2Regs->MPCDN2 = val & 0xFFFF;
         return;
      case 0x04C:
         Vdp2Regs->MPABN3 = val >> 16;
         Vdp2Regs->MPCDN3 = val & 0xFFFF;
         return;
      case 0x050:
         Vdp2Regs->MPABRA = val >> 16;
         Vdp2Regs->MPCDRA = val & 0xFFFF;
         return;
      case 0x054:
         Vdp2Regs->MPEFRA = val >> 16;
         Vdp2Regs->MPGHRA = val & 0xFFFF;
         return;
      case 0x058:
         Vdp2Regs->MPIJRA = val >> 16;
         Vdp2Regs->MPKLRA = val & 0xFFFF;
         return;
      case 0x05C:
         Vdp2Regs->MPMNRA = val >> 16;
         Vdp2Regs->MPOPRA = val & 0xFFFF;
         return;
      case 0x060:
         Vdp2Regs->MPABRB = val >> 16;
         Vdp2Regs->MPCDRB = val & 0xFFFF;
         return;
      case 0x064:
         Vdp2Regs->MPEFRB = val >> 16;
         Vdp2Regs->MPGHRB = val & 0xFFFF;
         return;
      case 0x068:
         Vdp2Regs->MPIJRB = val >> 16;
         Vdp2Regs->MPKLRB = val & 0xFFFF;
         return;
      case 0x06C:
         Vdp2Regs->MPMNRB = val >> 16;
         Vdp2Regs->MPOPRB = val & 0xFFFF;
         return;
      case 0x070:
         Vdp2Regs->SCXIN0 = val >> 16;
         // SCXDN0 here
         return;
      case 0x074:
         Vdp2Regs->SCYIN0 = val >> 16;
         // SCYDN0 here
         return;
      case 0x078:
         Vdp2Regs->ZMXN0.all = val;
         return;
      case 0x07C:
         Vdp2Regs->ZMYN0.all = val;
         return;
      case 0x080:
         Vdp2Regs->SCXIN1 = val >> 16;
         // SCXDN1 here
         return;
      case 0x084:
         Vdp2Regs->SCYIN1 = val >> 16;
         // SCYDN1 here
         return;
      case 0x088:
         Vdp2Regs->ZMXN1.all = val;
         return;
      case 0x08C:
         Vdp2Regs->ZMYN1.all = val;
         return;
      case 0x090:
         Vdp2Regs->SCXN2 = val >> 16;
         Vdp2Regs->SCYN2 = val & 0xFFFF;
         return;
      case 0x094:
         Vdp2Regs->SCXN3 = val >> 16;
         Vdp2Regs->SCYN3 = val & 0xFFFF;
         return;
      case 0x09C:
         Vdp2Regs->VCSTA.all = val;
         return;
      case 0x0A0:
         Vdp2Regs->LSTA0.all = val;
         return;
      case 0x0A4:
         Vdp2Regs->LSTA1.all = val;
         return;
      case 0x0AC:
         Vdp2Regs->BKTAU = val >> 16;
         Vdp2Regs->BKTAL = val & 0xFFFF;
         return;
      case 0x0D0:
         Vdp2Regs->WCTLA = val >> 16;
         Vdp2Regs->WCTLB = val & 0xFFFF;
         return;
      case 0x0D8:
         Vdp2Regs->LWTA0.all = val;
         return;
      case 0x0DC:
         Vdp2Regs->LWTA1.all = val;
         return;
      case 0x0E0:
         Vdp2Regs->SPCTL = val >> 16;
         Vdp2Regs->SDCTL = val & 0xFFFF;
         return;
      case 0x0E4:
         Vdp2Regs->CRAOFA = val >> 16;
         Vdp2Regs->CRAOFB = val & 0xFFFF;
         return;
      case 0x0E8:
         Vdp2Regs->LNCLEN = val >> 16;
         Vdp2Regs->SFPRMD = val & 0xFFFF;
         return;
      case 0x0EC:
         Vdp2Regs->CCCTL = val >> 16;
         Vdp2Regs->SFCCMD = val & 0xFFFF;
         return;
      case 0x0F0:
         Vdp2Regs->PRISA = val >> 16;
         Vdp2Regs->PRISB = val & 0xFFFF;
         return;
      case 0x0F4:
         Vdp2Regs->PRISC = val >> 16;
         Vdp2Regs->PRISD = val & 0xFFFF;
         return;
      case 0x0F8:
         Vdp2Regs->PRINA = val >> 16;
         Vdp2Regs->PRINB = val & 0xFFFF;
         VIDCore->Vdp2SetPriorityNBG0(Vdp2Regs->PRINA & 0x7);
         VIDCore->Vdp2SetPriorityNBG1((Vdp2Regs->PRINA >> 8) & 0x7);
         VIDCore->Vdp2SetPriorityNBG2(Vdp2Regs->PRINB & 0x7);
         VIDCore->Vdp2SetPriorityNBG3((Vdp2Regs->PRINB >> 8) & 0x7);
         return;
      case 0x100:
         Vdp2Regs->CCRSA = val >> 16;
         Vdp2Regs->CCRSB = val & 0xFFFF;
         return;
      case 0x104:
         Vdp2Regs->CCRSC = val >> 16;
         Vdp2Regs->CCRSD = val & 0xFFFF;
         return;
      case 0x108:
         Vdp2Regs->CCRNA = val >> 16;
         Vdp2Regs->CCRNB = val & 0xFFFF;
         return;
      case 0x10C:
         Vdp2Regs->CCRR = val >> 16;
         Vdp2Regs->CCRLB = val & 0xFFFF;
         return;
      case 0x110:
         Vdp2Regs->CLOFEN = val >> 16;
         Vdp2Regs->CLOFSL = val & 0xFFFF;
         return;
      case 0x114:
         Vdp2Regs->COAR = val >> 16;
         Vdp2Regs->COAG = val & 0xFFFF;
         return;
      case 0x118:
         Vdp2Regs->COAB = val >> 16;
         Vdp2Regs->COBR = val & 0xFFFF;
         return;
      case 0x11C:
         Vdp2Regs->COBG = val >> 16;
         Vdp2Regs->COBB = val & 0xFFFF;
         return;
      case 0x120:
      case 0x124:
      case 0x128:
      case 0x12C:
      case 0x130:
      case 0x134:
      case 0x138:
      case 0x13C:
      case 0x140:
      case 0x144:
      case 0x148:
      case 0x14C:
      case 0x150:
      case 0x154:
      case 0x158:
      case 0x15C:
      case 0x160:
      case 0x164:
      case 0x168:
      case 0x16C:
      case 0x170:
      case 0x174:
      case 0x178:
      case 0x17C:
      case 0x180:
      case 0x184:
      case 0x188:
      case 0x18C:
      case 0x190:
      case 0x194:
      case 0x198:
      case 0x19C:
      case 0x1A0:
      case 0x1A4:
      case 0x1A8:
      case 0x1AC:
      case 0x1B0:
      case 0x1B4:
      case 0x1B8:
      case 0x1BC:
      case 0x1C0:
      case 0x1C4:
      case 0x1C8:
      case 0x1CC:
      case 0x1D0:
      case 0x1D4:
      case 0x1D8:
      case 0x1DC:
      case 0x1E0:
      case 0x1E4:
      case 0x1E8:
      case 0x1EC:
      case 0x1F0:
      case 0x1F4:
      case 0x1F8:
      case 0x1FC:
         // Reserved
         return;
      default:
      {
         LOG("Unhandled VDP2 long write: %08X\n", addr);
         break;
      }
   }
}

//////////////////////////////////////////////////////////////////////////////

int Vdp2SaveState(FILE *fp)
{
   int offset;
   IOCheck_struct check;

   offset = StateWriteHeader(fp, "VDP2", 1);

   // Write registers
   ywrite(&check, (void *)Vdp2Regs, sizeof(Vdp2), 1, fp);

   // Write VDP2 ram
   ywrite(&check, (void *)Vdp2Ram, 0x80000, 1, fp);

   // Write CRAM
   ywrite(&check, (void *)Vdp2ColorRam, 0x1000, 1, fp);

   // Write internal variables
   ywrite(&check, (void *)&Vdp2Internal, sizeof(Vdp2Internal_struct), 1, fp);

   return StateFinishHeader(fp, offset);
}

//////////////////////////////////////////////////////////////////////////////

int Vdp2LoadState(FILE *fp, UNUSED int version, int size)
{
   IOCheck_struct check;

   // Read registers
   yread(&check, (void *)Vdp2Regs, sizeof(Vdp2), 1, fp);

   // Read VDP2 ram
   yread(&check, (void *)Vdp2Ram, 0x80000, 1, fp);

   // Read CRAM
   yread(&check, (void *)Vdp2ColorRam, 0x1000, 1, fp);

   // Read internal variables
   yread(&check, (void *)&Vdp2Internal, sizeof(Vdp2Internal_struct), 1, fp);

   VIDCore->Vdp2SetResolution(Vdp2Regs->TVMD);
   VIDCore->Vdp2SetPriorityNBG0(Vdp2Regs->PRINA & 0x7);
   VIDCore->Vdp2SetPriorityNBG1((Vdp2Regs->PRINA >> 8) & 0x7);
   VIDCore->Vdp2SetPriorityNBG2(Vdp2Regs->PRINB & 0x7);
   VIDCore->Vdp2SetPriorityNBG3((Vdp2Regs->PRINB >> 8) & 0x7);
   VIDCore->Vdp2SetPriorityRBG0(Vdp2Regs->PRIR & 0x7);

   return size;
}

//////////////////////////////////////////////////////////////////////////////

void ToggleNBG0(void)
{
   Vdp2External.disptoggle ^= 0x1;
}

//////////////////////////////////////////////////////////////////////////////

void ToggleNBG1(void)
{
   Vdp2External.disptoggle ^= 0x2;
}

//////////////////////////////////////////////////////////////////////////////

void ToggleNBG2(void)
{
   Vdp2External.disptoggle ^= 0x4;
}

//////////////////////////////////////////////////////////////////////////////

void ToggleNBG3(void)
{
   Vdp2External.disptoggle ^= 0x8;
}

//////////////////////////////////////////////////////////////////////////////

void ToggleRBG0(void)
{
   Vdp2External.disptoggle ^= 0x10;
}

//////////////////////////////////////////////////////////////////////////////

void ToggleFullScreen(void)
{
   if (VIDCore->IsFullscreen())
   {
      VIDCore->Resize(320, 224, 0);
   }
   else
   {
      VIDCore->Resize(640, 480, 1);
   }
}

//////////////////////////////////////////////////////////////////////////////

void EnableAutoFrameSkip(void)
{
   autoframeskipenab = 1;
   lastticks = YabauseGetTicks();
}

//////////////////////////////////////////////////////////////////////////////

void DisableAutoFrameSkip(void)
{
   autoframeskipenab = 0;
}

//////////////////////////////////////////////////////////////////////////////
