//////////////////////////////////////////////////////////////////////////
//                                                                      //
//      NES Memory Management Unit                                      //
//                                                           Norix      //
//                                               written     2001/02/21 //
//                                               last modify ----/--/-- //
//////////////////////////////////////////////////////////////////////////
#define	WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "typedef.h"
#include "macro.h"
#include "DebugOut.h"

#include "mmu.h"

BYTE	nnn;

// CPU メモリバンク
LPBYTE	CPU_MEM_BANK[8];	// 8K単位
BYTE	CPU_MEM_TYPE[8];
INT	CPU_MEM_PAGE[8];	// ステートセーブ用

// PPU メモリバンク
LPBYTE	PPU_MEM_BANK[12];	// 1K単位
BYTE	PPU_MEM_TYPE[12];
INT		PPU_MEM_PAGE[12];	// ステートセーブ用
BYTE	CRAM_USED[16];		// ステートセーブ用
PBYTE	VROM_WRITED;		// for mapper 74

// NESメモリ
BYTE	RAM [  8*1024];		// NES内臓RAM
BYTE	WRAM[128*1024];		// ワーク/バックアップRAM
BYTE	DRAM[ 40*1024];		// ディスクシステムRAM
BYTE	XRAM[  8*1024];		// ダミーバンク
BYTE	ERAM[ 32*1024];		// 拡張機器用RAM

BYTE	VRAM[  4*1024];		// ネームテーブル/アトリビュートRAM
BYTE	CRAM[ 32*1024];		// キャラクタパターンRAM

BYTE	YWRAM[1024*1024];	// for YuXing 98/F 1024K PRam
BYTE	YSRAM[  32*1024];	// for YuXing 98/F 32K SRam
BYTE	YCRAM[ 128*1024];	// for YuXing 98/F 128K CRam

BYTE	BDRAM[ 512*1024];	// for BBK 512K PRam
BYTE	BSRAM[  32*1024];	// for BBK 32K SRam
BYTE	BCRAM[ 512*1024];	// for BBK 512K CRam

BYTE	JDRAM[ 512*1024];	// for DrPCJr 512K PRam
BYTE	JSRAM[   8*1024];	// for DrPCJr 8K SRam
BYTE	JCRAM[ 512*1024];	// for DrPCJr 512K CRam

BYTE	tempRAM[ 4*1024];

BYTE	WAVRAM[256];

BYTE	SPRAM[0x100];		// スプライトRAM
BYTE	BGPAL[0x10];		// BGパレット
BYTE	SPPAL[0x10];		// SPパレット

// レジスタ
BYTE	CPUREG[0x18];		// Nes $4000-$4017
BYTE	PPUREG[0x04];		// Nes $2000-$2003

// Frame-IRQレジスタ($4017)
BYTE	FrameIRQ;

// PPU内部レジスタ
BYTE	PPU56Toggle;		// $2005-$2006 Toggle
BYTE	PPU7_Temp;		// $2007 read buffer
WORD	loopy_t;		// same as $2005/$2006
WORD	loopy_v;		// same as $2005/$2006
WORD	loopy_x;		// tile x offset

// ROMデータポインタ
LPBYTE	PROM;		// PROM ptr
LPBYTE	VROM;		// VROM ptr

// For dis...
LPBYTE	PROM_ACCESS = NULL;

// ROM バンクサイズ
INT	PROM_8K_SIZE, PROM_16K_SIZE, PROM_32K_SIZE;
INT	VROM_1K_SIZE, VROM_2K_SIZE, VROM_4K_SIZE,  VROM_8K_SIZE;

//
// 全メモリ/レジスタ等の初期化
//
void	NesSub_MemoryInitial()
{
INT	i;

	// メモリクリア
	ZEROMEMORY( RAM,  sizeof(RAM) );
	ZEROMEMORY( WRAM, sizeof(WRAM) );
	ZEROMEMORY( DRAM, sizeof(DRAM) );
	ZEROMEMORY( ERAM, sizeof(ERAM) );
	ZEROMEMORY( XRAM, sizeof(XRAM) );
	ZEROMEMORY( CRAM, sizeof(CRAM) );
	ZEROMEMORY( VRAM, sizeof(VRAM) );

	ZEROMEMORY( YWRAM, sizeof(YWRAM) );
	ZEROMEMORY( YSRAM, sizeof(YSRAM) );
	ZEROMEMORY( YCRAM, sizeof(YCRAM) );

	ZEROMEMORY( BDRAM, sizeof(BDRAM) );
	ZEROMEMORY( BSRAM, sizeof(BSRAM) );
	ZEROMEMORY( BCRAM, sizeof(BCRAM) );

	ZEROMEMORY( JDRAM, sizeof(JDRAM) );
	ZEROMEMORY( JSRAM, sizeof(JSRAM) );
	ZEROMEMORY( JCRAM, sizeof(JCRAM) );

	ZEROMEMORY( SPRAM, sizeof(SPRAM) );
	ZEROMEMORY( BGPAL, sizeof(BGPAL) );
	ZEROMEMORY( SPPAL, sizeof(SPPAL) );

	ZEROMEMORY( CPUREG, sizeof(CPUREG) );
	ZEROMEMORY( PPUREG, sizeof(PPUREG) );

	FrameIRQ = 0xC0;

	PROM = VROM = NULL;

	// 0 除算防止対策
	PROM_8K_SIZE = PROM_16K_SIZE = PROM_32K_SIZE = 1;
	VROM_1K_SIZE = VROM_2K_SIZE = VROM_4K_SIZE = VROM_8K_SIZE = 1;

	// デフォルトバンク設定
	for( i = 0; i < 8; i++ ) {
		CPU_MEM_BANK[i] = NULL;
		CPU_MEM_TYPE[i] = BANKTYPE_ROM;
		CPU_MEM_PAGE[i] = 0;
	}

	// 内臓RAM/WRAM
	SetPROM_Bank( 0, RAM,  BANKTYPE_RAM );
	SetPROM_Bank( 3, WRAM, BANKTYPE_RAM );

	// ダミー
	SetPROM_Bank( 1, XRAM, BANKTYPE_ROM );
	SetPROM_Bank( 2, XRAM, BANKTYPE_ROM );

	for( i = 0; i < 8; i++ ) {
		CRAM_USED[i] = 0;
	}
	VROM_WRITED = CRAM+28*1024;

	// PPU VROMバンク設定
//	SetVRAM_Mirror( VRAM_MIRROR4 );
}

// CPU ROM bank
void	SetPROM_Bank( BYTE page, LPBYTE ptr, BYTE type )
{
	CPU_MEM_BANK[page] = ptr;
	CPU_MEM_TYPE[page] = type;
	CPU_MEM_PAGE[page] = 0;
}

void	SetPROM_8K_Bank( BYTE page, INT bank )
{
	bank %= PROM_8K_SIZE;
	CPU_MEM_BANK[page] = PROM+0x2000*bank;
	CPU_MEM_TYPE[page] = BANKTYPE_ROM;
	CPU_MEM_PAGE[page] = bank;
}

void	SetPROM_16K_Bank( BYTE page, INT bank )
{
	SetPROM_8K_Bank( page+0, bank*2+0 );
	SetPROM_8K_Bank( page+1, bank*2+1 );
}

void	SetPROM_32K_Bank( INT bank )
{
	SetPROM_8K_Bank( 4, bank*4+0 );
	SetPROM_8K_Bank( 5, bank*4+1 );
	SetPROM_8K_Bank( 6, bank*4+2 );
	SetPROM_8K_Bank( 7, bank*4+3 );
}

void	SetPROM_32K_Bank( INT bank0, INT bank1, INT bank2, INT bank3 )
{
	SetPROM_8K_Bank( 4, bank0 );
	SetPROM_8K_Bank( 5, bank1 );
	SetPROM_8K_Bank( 6, bank2 );
	SetPROM_8K_Bank( 7, bank3 );
}

// PPU VROM bank
void	SetVROM_Bank( BYTE page, LPBYTE ptr, BYTE type )
{
	PPU_MEM_BANK[page] = ptr;
	PPU_MEM_TYPE[page] = type;
	PPU_MEM_PAGE[page] = 0;
}

void	SetVROM_1K_Bank( BYTE page, INT bank )
{
	bank %= VROM_1K_SIZE;
	PPU_MEM_BANK[page] = VROM+0x0400*bank;
	PPU_MEM_TYPE[page] = BANKTYPE_VROM;
	PPU_MEM_PAGE[page] = bank;
}

void	SetVROM_2K_Bank( BYTE page, INT bank )
{
	SetVROM_1K_Bank( page+0, bank*2+0 );
	SetVROM_1K_Bank( page+1, bank*2+1 );
}

void	SetVROM_4K_Bank( BYTE page, INT bank )
{
	SetVROM_1K_Bank( page+0, bank*4+0 );
	SetVROM_1K_Bank( page+1, bank*4+1 );
	SetVROM_1K_Bank( page+2, bank*4+2 );
	SetVROM_1K_Bank( page+3, bank*4+3 );
}

void	SetVROM_8K_Bank( INT bank )
{
	for( INT i = 0; i < 8; i++ ) {
		SetVROM_1K_Bank( i, bank*8+i );
	}
}

void	SetVROM_8K_Bank( INT bank0, INT bank1, INT bank2, INT bank3,
			 INT bank4, INT bank5, INT bank6, INT bank7 )
{
	SetVROM_1K_Bank( 0, bank0 );
	SetVROM_1K_Bank( 1, bank1 );
	SetVROM_1K_Bank( 2, bank2 );
	SetVROM_1K_Bank( 3, bank3 );
	SetVROM_1K_Bank( 4, bank4 );
	SetVROM_1K_Bank( 5, bank5 );
	SetVROM_1K_Bank( 6, bank6 );
	SetVROM_1K_Bank( 7, bank7 );
}

void	SetCRAM_1K_Bank( BYTE page, INT bank )
{
	bank &= 0x1F;
	PPU_MEM_BANK[page] = CRAM+0x0400*bank;
	PPU_MEM_TYPE[page] = BANKTYPE_CRAM;
	PPU_MEM_PAGE[page] = bank;

	CRAM_USED[bank>>2] = 0xFF;	// CRAM使用フラグ
}

void	SetCRAM_2K_Bank( BYTE page, INT bank )
{
	SetCRAM_1K_Bank( page+0, bank*2+0 );
	SetCRAM_1K_Bank( page+1, bank*2+1 );
}

void	SetCRAM_4K_Bank( BYTE page, INT bank )
{
	SetCRAM_1K_Bank( page+0, bank*4+0 );
	SetCRAM_1K_Bank( page+1, bank*4+1 );
	SetCRAM_1K_Bank( page+2, bank*4+2 );
	SetCRAM_1K_Bank( page+3, bank*4+3 );
}

void	SetCRAM_8K_Bank( INT bank )
{
	for( INT i = 0; i < 8; i++ ) {
		SetCRAM_1K_Bank( i, bank*8+i );	// fix
	}
}

void	SetVRAM_1K_Bank( BYTE page, INT bank )
{
	bank &= 3;
	PPU_MEM_BANK[page] = VRAM+0x0400*bank;
	PPU_MEM_TYPE[page] = BANKTYPE_VRAM;
	PPU_MEM_PAGE[page] = bank;
}

void	SetVRAM_Bank( INT bank0, INT bank1, INT bank2, INT bank3 )
{
	SetVRAM_1K_Bank(  8, bank0 );
	SetVRAM_1K_Bank(  9, bank1 );
	SetVRAM_1K_Bank( 10, bank2 );
	SetVRAM_1K_Bank( 11, bank3 );
}

void	SetVRAM_Mirror( INT type )
{
	switch( type ) {
		case	VRAM_HMIRROR:
			SetVRAM_Bank( 0, 0, 1, 1 );
			break;
		case	VRAM_VMIRROR:
			SetVRAM_Bank( 0, 1, 0, 1 );
			break;
		case	VRAM_MIRROR4L:
			SetVRAM_Bank( 0, 0, 0, 0 );
			break;
		case	VRAM_MIRROR3H:
			SetVRAM_Bank( 0, 1, 1, 1 );
			break;
		case	VRAM_MIRROR4H:
			SetVRAM_Bank( 1, 1, 1, 1 );
			break;
		case	VRAM_MIRROR4:
			SetVRAM_Bank( 0, 1, 2, 3 );
			break;
	}
}

void	SetVRAM_Mirror( INT bank0, INT bank1, INT bank2, INT bank3 )
{
	SetVRAM_1K_Bank(  8, bank0 );
	SetVRAM_1K_Bank(  9, bank1 );
	SetVRAM_1K_Bank( 10, bank2 );
	SetVRAM_1K_Bank( 11, bank3 );
}

// for YuXing 98/F 1024K PRam
void	SetYWRAM_8K_Bank( BYTE page, INT bank )
{
	bank %= 0x80;
	CPU_MEM_BANK[page] = YWRAM+0x2000*bank;
	CPU_MEM_TYPE[page] = BANKTYPE_RAM;
	CPU_MEM_PAGE[page] = bank;
}
void	SetYWRAM_16K_Bank( BYTE page, INT bank )
{
	SetYWRAM_8K_Bank( page+0, bank*2+0 );
	SetYWRAM_8K_Bank( page+1, bank*2+1 );
}
void	SetYWRAM_32K_Bank( INT bank )
{
	SetYWRAM_8K_Bank( 4, bank*4+0 );
	SetYWRAM_8K_Bank( 5, bank*4+1 );
	SetYWRAM_8K_Bank( 6, bank*4+2 );
	SetYWRAM_8K_Bank( 7, bank*4+3 );
}
void	SetYWRAM_32K_Bank( INT bank0, INT bank1, INT bank2, INT bank3 )
{
	SetYWRAM_8K_Bank( 4, bank0 );
	SetYWRAM_8K_Bank( 5, bank1 );
	SetYWRAM_8K_Bank( 6, bank2 );
	SetYWRAM_8K_Bank( 7, bank3 );
}

// for YuXing 98/F 128K CRam
// Up for CoolBoy 256K
void	SetYCRAM_1K_Bank( BYTE page, INT bank )
{
//	bank &= 0x7F;
	bank &= 0xFF;
	PPU_MEM_BANK[page] = YCRAM+0x0400*bank;
	PPU_MEM_TYPE[page] = BANKTYPE_YCRAM;
	PPU_MEM_PAGE[page] = bank;
}
void	SetYCRAM_2K_Bank( BYTE page, INT bank )
{
	SetYCRAM_1K_Bank( page+0, bank*2+0 );
	SetYCRAM_1K_Bank( page+1, bank*2+1 );
}
void	SetYCRAM_4K_Bank( BYTE page, INT bank )
{
	SetYCRAM_1K_Bank( page+0, bank*4+0 );
	SetYCRAM_1K_Bank( page+1, bank*4+1 );
	SetYCRAM_1K_Bank( page+2, bank*4+2 );
	SetYCRAM_1K_Bank( page+3, bank*4+3 );
}
void	SetYCRAM_8K_Bank( INT bank )
{
	for( INT i = 0; i < 8; i++ ) {
		SetYCRAM_1K_Bank( i, bank*8+i );
	}
}

//
void	SetBDRAM_8K_Bank( BYTE page, INT bank )
{
	bank %= 0x40;
	CPU_MEM_BANK[page] = BDRAM+0x2000*bank;
	CPU_MEM_TYPE[page] = BANKTYPE_RAM;
	CPU_MEM_PAGE[page] = bank;
}
void	SetBDRAM_16K_Bank( BYTE page, INT bank )
{
	SetBDRAM_8K_Bank( page+0, bank*2+0 );
	SetBDRAM_8K_Bank( page+1, bank*2+1 );
}
void	SetBDRAM_32K_Bank( INT bank )
{
	SetBDRAM_8K_Bank( 4, bank*4+0 );
	SetBDRAM_8K_Bank( 5, bank*4+1 );
	SetBDRAM_8K_Bank( 6, bank*4+2 );
	SetBDRAM_8K_Bank( 7, bank*4+3 );
}

//------------------------------------------------------------

void	SetJDRAM_8K_Bank( BYTE page, INT bank )
{
	bank %= 0x40;
	CPU_MEM_BANK[page] = JDRAM+0x2000*bank;
	CPU_MEM_TYPE[page] = BANKTYPE_RAM;
	CPU_MEM_PAGE[page] = bank;
}
void	SetJDRAM_32K_Bank( INT bank )
{
	SetJDRAM_8K_Bank( 3, bank*4+0 );
	SetJDRAM_8K_Bank( 4, bank*4+1 );
	SetJDRAM_8K_Bank( 5, bank*4+2 );
	SetJDRAM_8K_Bank( 6, bank*4+3 );
}

void	SetJCRAM_1K_Bank( BYTE page, INT bank )
{
	bank %= 0x200;
	PPU_MEM_BANK[page] = JCRAM+0x0400*bank;
	PPU_MEM_TYPE[page] = BANKTYPE_JCRAM;
	PPU_MEM_PAGE[page] = bank;
}
void	SetJCRAM_2K_Bank( BYTE page, INT bank )
{
	SetJCRAM_1K_Bank( page+0, bank*2+0 );
	SetJCRAM_1K_Bank( page+1, bank*2+1 );
}
void	SetJCRAM_4K_Bank( BYTE page, INT bank )
{
	SetJCRAM_1K_Bank( page+0, bank*4+0 );
	SetJCRAM_1K_Bank( page+1, bank*4+1 );
	SetJCRAM_1K_Bank( page+2, bank*4+2 );
	SetJCRAM_1K_Bank( page+3, bank*4+3 );
}
void	SetJCRAM_8K_Bank( INT bank )
{
	for( INT i = 0; i < 8; i++ ) {
		SetJCRAM_1K_Bank( i, bank*8+i );
	}
}
void	SetJCRAM_8K_Bank( INT bank0, INT bank1, INT bank2, INT bank3,
			 INT bank4, INT bank5, INT bank6, INT bank7 )
{
	SetJCRAM_1K_Bank( 0, bank0 );
	SetJCRAM_1K_Bank( 1, bank1 );
	SetJCRAM_1K_Bank( 2, bank2 );
	SetJCRAM_1K_Bank( 3, bank3 );
	SetJCRAM_1K_Bank( 4, bank4 );
	SetJCRAM_1K_Bank( 5, bank5 );
	SetJCRAM_1K_Bank( 6, bank6 );
	SetJCRAM_1K_Bank( 7, bank7 );
}

void	SetOBCRAM_1K_Bank( BYTE page, INT bank )
{
	bank %= (PROM_8K_SIZE*8);
	PPU_MEM_BANK[page] = PROM+0x0400*bank;
	PPU_MEM_TYPE[page] = BANKTYPE_ROM;
	PPU_MEM_PAGE[page] = bank;
}

void	SetPROM_4K_Bank( WORD addr, INT bank )
{
	bank %= (PROM_8K_SIZE*2);
	memcpy( &CPU_MEM_BANK[addr>>13][addr&0x1FFF], PROM+0x1000*bank, 0x1000);
//	memcpy( &CPU_MEM_BANK[addr>>13][addr&0x1FFF], YSRAM+0x1000*bank, 0x1000);
	CPU_MEM_TYPE[addr>>13] = BANKTYPE_ROM;
	CPU_MEM_PAGE[addr>>13] = 0;
}
