//////////////////////////////////////////////////////////////////////////
// Mapper112  Nintendo MMC3                                             //
//////////////////////////////////////////////////////////////////////////
using static VirtualNes.MMU;
using static VirtualNes.Core.CPU;
using INT = System.Int32;
using BYTE = System.Byte;
using System;
using Codice.CM.Client.Differences;

namespace VirtualNes.Core
{
	public class Mapper112 : Mapper
	{
		BYTE[] reg = new byte[4];
		BYTE prg0, prg1;
		BYTE chr01, chr23, chr4, chr5, chr6, chr7;
		public Mapper112(NES parent) : base(parent)
		{
		}

		public override void Reset()
		{
			for (INT i = 0; i < 4; i++)
			{
				reg[i] = 0x00;
			}

			prg0 = 0;
			prg1 = 1;
			SetBank_CPU();

			chr01 = 0;
			chr23 = 2;
			chr4 = 4;
			chr5 = 5;
			chr6 = 6;
			chr7 = 7;
			SetBank_PPU();
		}

		//void Mapper112::Write(WORD addr, BYTE data)
		public override void Write(ushort addr, byte data)
		{
			switch (addr)
			{
				case 0x8000:
					reg[0] = data;
					SetBank_CPU();
					SetBank_PPU();
					break;
				case 0xA000:
					reg[1] = data;
					switch (reg[0] & 0x07)
					{
						case 0x00:
							prg0 = (byte)((data & (PROM_8K_SIZE - 1)));
							SetBank_CPU();
							break;
						case 0x01:
							prg1 = (byte)((data & (PROM_8K_SIZE - 1)));
							SetBank_CPU();
							break;
						case 0x02:
							chr01 = (byte)(data & 0xFE);
							SetBank_PPU();
							break;
						case 0x03:
							chr23 = (byte)(data & 0xFE);
							SetBank_PPU();
							break;
						case 0x04:
							chr4 = data;
							SetBank_PPU();
							break;
						case 0x05:
							chr5 = data;
							SetBank_PPU();
							break;
						case 0x06:
							chr6 = data;
							SetBank_PPU();
							break;
						case 0x07:
							chr7 = data;
							SetBank_PPU();
							break;
					}
					break;

				case 0xC000:
					reg[3] = data;
					SetBank_PPU();
					break;//hum 源码居然没有break 语法差异呗
				case 0xE000:
					reg[2] = data;
					if (!nes.rom.Is4SCREEN())
					{
						if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
						else SetVRAM_Mirror(VRAM_VMIRROR);
					}
					SetBank_PPU();
					break;
			}
		}

		void SetBank_CPU()
		{
			SetPROM_32K_Bank(prg0, prg1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
		}

		void SetBank_PPU()
		{
			if ((reg[2] & 0x02) != 0)
			{
				SetVROM_8K_Bank(chr01, chr01 + 1, chr23, chr23 + 1, chr4, chr5, chr6, chr7);
			}
			else
			{
				SetVROM_8K_Bank(((reg[3] << 6) & 0x100) + chr01,
						((reg[3] << 6) & 0x100) + chr01 + 1,
						((reg[3] << 5) & 0x100) + chr23,
						((reg[3] << 5) & 0x100) + chr23 + 1,
						((reg[3] << 4) & 0x100) + chr4,
						((reg[3] << 3) & 0x100) + chr5,
						((reg[3] << 2) & 0x100) + chr6,
						((reg[3] << 1) & 0x100) + chr7);
			}
		}

		//void Mapper112::SaveState(LPBYTE p)
		public override void SaveState(byte[] p)
		{
			for (INT i = 0; i < 4; i++)
			{
				p[i] = reg[i];
			}
			p[4] = chr01;
			p[5] = chr23;
			p[6] = chr4;
			p[7] = chr5;
			p[8] = chr6;
			p[9] = chr7;
		}

		//void Mapper112::LoadState(LPBYTE p)
		public override void LoadState(byte[] p)
		{
			for (byte i = 0; i < 4; i++)
			{
				reg[i] = p[i];
			}

			chr01 = p[4];
			chr23 = p[5];
			chr4 = p[6];
			chr5 = p[7];
			chr6 = p[8];
			chr7 = p[9];
		}

		public override bool IsStateSave()
		{
			return true;
		}
	}
}