using System;
using System.IO;

namespace MyNes.Core
{
    [BoardInfo("MMC5", 5, 8, 16)]
    [WithExternalSound]
    [HassIssues]
    internal class Mapper005 : Board
    {
    	private int ram_protectA;

    	private int ram_protectB;

    	private int ExRAM_mode;

    	private int[] CHROffset_spr;

    	private int[] CHROffsetEX;

    	private int[] CHROffsetSP;

    	private int[] chrRegA;

    	private int[] chrRegB;

    	private int[] prgReg;

    	private bool useSRAMmirroring;

    	private int chr_high;

    	private int chr_mode;

    	private int prg_mode;

    	private bool chr_setB_last;

    	private byte temp_val;

    	private byte temp_fill;

    	private int lastAccessVRAM;

    	private int paletteNo;

    	private int shift;

    	private int EXtilenumber;

    	private byte multiplicand;

    	private byte multiplier;

    	private ushort product;

    	private bool split_enable;

    	private bool split_right;

    	private int split_tile;

    	private int split_yscroll;

    	private bool split_doit;

    	private int split_watch_tile;

    	private byte irq_line;

    	private byte irq_enable;

    	private int irq_pending;

    	private int irq_current_counter;

    	private int irq_current_inframe;

    	private MMC5Sqr snd_1;

    	private MMC5Sqr snd_2;

    	private MMC5Pcm snd_3;

    	private double[] audio_pulse_table;

    	private double[] audio_tnd_table;

    	internal override string Issues => MNInterfaceLanguage.IssueMapper5;

    	internal override void Initialize(IRom rom)
    	{
    		base.Initialize(rom);
    		snd_1 = new MMC5Sqr();
    		snd_2 = new MMC5Sqr();
    		snd_3 = new MMC5Pcm();
    		audio_pulse_table = new double[32];
    		for (int i = 0; i < 32; i++)
    		{
    			audio_pulse_table[i] = 95.52 / (8128.0 / (double)i + 100.0);
    		}
    		audio_tnd_table = new double[204];
    		for (int j = 0; j < 204; j++)
    		{
    			audio_tnd_table[j] = 163.67 / (24329.0 / (double)j + 100.0);
    		}
    	}

    	internal override void HardReset()
    	{
    		base.HardReset();
    		switch (SHA1.ToUpper())
    		{
    		case "37267833C984F176DB4B0BC9D45DABA0FFF45304":
    			useSRAMmirroring = true;
    			break;
    		case "800AEFE756E85A0A78CCB4DAE68EBBA5DF24BF41":
    			useSRAMmirroring = true;
    			break;
    		}
    		Console.WriteLine("MMC5: using PRG RAM mirroring = " + useSRAMmirroring);
    		CHROffset_spr = new int[8];
    		CHROffsetEX = new int[8];
    		CHROffsetSP = new int[8];
    		chrRegA = new int[8];
    		chrRegB = new int[4];
    		prgReg = new int[4];
    		prgReg[3] = PRG_ROM_08KB_Mask;
    		prg_mode = 3;
    		Switch08KPRG(PRG_ROM_08KB_Mask, PRGArea.Area8000);
    		Switch08KPRG(PRG_ROM_08KB_Mask, PRGArea.AreaA000);
    		Switch08KPRG(PRG_ROM_08KB_Mask, PRGArea.AreaC000);
    		Switch08KPRG(PRG_ROM_08KB_Mask, PRGArea.AreaE000);
    		Switch04kCHREX(0, 0);
    		Switch04kCHRSP(0, 0);
    		Switch08kCHR_spr(0);
    		TogglePRGRAMWritableEnable(enable: true);
    		TogglePRGRAMEnable(enable: true);
    		APUApplyChannelsSettings();
    		snd_1.HardReset();
    		snd_2.HardReset();
    		snd_3.HardReset();
    	}

    	internal override void SoftReset()
    	{
    		base.SoftReset();
    		snd_1.SoftReset();
    		snd_2.SoftReset();
    		snd_3.SoftReset();
    	}

    	internal override void WriteEX(ref ushort address, ref byte value)
    	{
    		if (address >= 23552)
    		{
    			if (ExRAM_mode == 2)
    			{
    				NMT_RAM[2][address & 0x3FF] = value;
    			}
    			else if (ExRAM_mode < 2)
    			{
    				if (irq_current_inframe == 64)
    				{
    					NMT_RAM[2][address & 0x3FF] = value;
    				}
    				else
    				{
    					NMT_RAM[2][address & 0x3FF] = 0;
    				}
    			}
    			return;
    		}
    		switch (address)
    		{
    		case 20480:
    			snd_1.Write0(ref value);
    			break;
    		case 20482:
    			snd_1.Write2(ref value);
    			break;
    		case 20483:
    			snd_1.Write3(ref value);
    			break;
    		case 20484:
    			snd_2.Write0(ref value);
    			break;
    		case 20486:
    			snd_2.Write2(ref value);
    			break;
    		case 20487:
    			snd_2.Write3(ref value);
    			break;
    		case 20496:
    			snd_3.Write5010(value);
    			break;
    		case 20497:
    			snd_3.Write5011(value);
    			break;
    		case 20501:
    			snd_1.WriteEnabled((value & 1) != 0);
    			snd_2.WriteEnabled((value & 2) != 0);
    			break;
    		case 20736:
    			prg_mode = value & 3;
    			break;
    		case 20737:
    			chr_mode = value & 3;
    			break;
    		case 20738:
    			ram_protectA = value & 3;
    			UpdateRamProtect();
    			break;
    		case 20739:
    			ram_protectB = value & 3;
    			UpdateRamProtect();
    			break;
    		case 20740:
    			ExRAM_mode = value & 3;
    			break;
    		case 20741:
    			Switch01KNMT(value);
    			break;
    		case 20755:
    			if (!useSRAMmirroring)
    			{
    				Switch08KPRG(value & 7, PRGArea.Area6000);
    			}
    			else
    			{
    				Switch08KPRG((value >> 2) & 1, PRGArea.Area6000);
    			}
    			break;
    		case 20756:
    			if (prg_mode == 3)
    			{
    				Toggle08KPRG_RAM((value & 0x80) == 0, PRGArea.Area8000);
    				Switch08KPRG(value & 0x7F, PRGArea.Area8000);
    			}
    			break;
    		case 20757:
    			switch (prg_mode)
    			{
    			case 1:
    				Toggle16KPRG_RAM((value & 0x80) == 0, PRGArea.Area8000);
    				Switch16KPRG((value & 0x7F) >> 1, PRGArea.Area8000);
    				break;
    			case 2:
    				Toggle16KPRG_RAM((value & 0x80) == 0, PRGArea.Area8000);
    				Switch16KPRG((value & 0x7F) >> 1, PRGArea.Area8000);
    				break;
    			case 3:
    				Toggle08KPRG_RAM((value & 0x80) == 0, PRGArea.AreaA000);
    				Switch08KPRG(value & 0x7F, PRGArea.AreaA000);
    				break;
    			}
    			break;
    		case 20758:
    		{
    			int num = prg_mode;
    			if ((uint)(num - 2) <= 1u)
    			{
    				Toggle08KPRG_RAM((value & 0x80) == 0, PRGArea.AreaC000);
    				Switch08KPRG(value & 0x7F, PRGArea.AreaC000);
    			}
    			break;
    		}
    		case 20759:
    			switch (prg_mode)
    			{
    			case 0:
    				Switch32KPRG((value & 0x7C) >> 2, PRGArea.Area8000);
    				break;
    			case 1:
    				Switch16KPRG((value & 0x7F) >> 1, PRGArea.AreaC000);
    				break;
    			case 2:
    				Switch08KPRG(value & 0x7F, PRGArea.AreaE000);
    				break;
    			case 3:
    				Switch08KPRG(value & 0x7F, PRGArea.AreaE000);
    				break;
    			}
    			break;
    		case 20768:
    			chr_setB_last = false;
    			if (chr_mode == 3)
    			{
    				Switch01kCHR_spr(value | chr_high, 0);
    			}
    			break;
    		case 20769:
    			chr_setB_last = false;
    			switch (chr_mode)
    			{
    			case 2:
    				Switch02kCHR_spr(value | chr_high, 0);
    				break;
    			case 3:
    				Switch01kCHR_spr(value | chr_high, 1024);
    				break;
    			}
    			break;
    		case 20770:
    			chr_setB_last = false;
    			if (chr_mode == 3)
    			{
    				Switch01kCHR_spr(value | chr_high, 2048);
    			}
    			break;
    		case 20771:
    			chr_setB_last = false;
    			switch (chr_mode)
    			{
    			case 1:
    				Switch04kCHR_spr(value | chr_high, 0);
    				break;
    			case 2:
    				Switch02kCHR_spr(value | chr_high, 2048);
    				break;
    			case 3:
    				Switch01kCHR_spr(value | chr_high, 3072);
    				break;
    			}
    			break;
    		case 20772:
    			chr_setB_last = false;
    			if (chr_mode == 3)
    			{
    				Switch01kCHR_spr(value | chr_high, 4096);
    			}
    			break;
    		case 20773:
    			chr_setB_last = false;
    			switch (chr_mode)
    			{
    			case 2:
    				Switch02kCHR_spr(value | chr_high, 4096);
    				break;
    			case 3:
    				Switch01kCHR_spr(value | chr_high, 5120);
    				break;
    			}
    			break;
    		case 20774:
    			chr_setB_last = false;
    			if (chr_mode == 3)
    			{
    				Switch01kCHR_spr(value | chr_high, 6144);
    			}
    			break;
    		case 20775:
    			chr_setB_last = false;
    			switch (chr_mode)
    			{
    			case 0:
    				Switch08kCHR_spr(value | chr_high);
    				break;
    			case 1:
    				Switch04kCHR_spr(value | chr_high, 4096);
    				break;
    			case 2:
    				Switch02kCHR_spr(value | chr_high, 6144);
    				break;
    			case 3:
    				Switch01kCHR_spr(value | chr_high, 7168);
    				break;
    			}
    			break;
    		case 20776:
    			chr_setB_last = true;
    			if (chr_mode == 3)
    			{
    				Switch01KCHR(value | chr_high, CHRArea.Area0000);
    				Switch01KCHR(value | chr_high, CHRArea.Area1000);
    			}
    			break;
    		case 20777:
    			chr_setB_last = true;
    			switch (chr_mode)
    			{
    			case 2:
    				Switch02KCHR(value | chr_high, CHRArea.Area0000);
    				Switch02KCHR(value | chr_high, CHRArea.Area1000);
    				break;
    			case 3:
    				Switch01KCHR(value | chr_high, CHRArea.Area0400);
    				Switch01KCHR(value | chr_high, CHRArea.Area1400);
    				break;
    			}
    			break;
    		case 20778:
    			chr_setB_last = true;
    			if (chr_mode == 3)
    			{
    				Switch01KCHR(value | chr_high, CHRArea.Area0800);
    				Switch01KCHR(value | chr_high, CHRArea.Area1800);
    			}
    			break;
    		case 20779:
    			chr_setB_last = true;
    			switch (chr_mode)
    			{
    			case 0:
    				Switch04kCHR_bkg(value | chr_high, 0);
    				Switch04kCHR_bkg(value | chr_high, 4096);
    				break;
    			case 1:
    				Switch04KCHR(value | chr_high, CHRArea.Area0000);
    				Switch04KCHR(value | chr_high, CHRArea.Area1000);
    				break;
    			case 2:
    				Switch02KCHR(value | chr_high, CHRArea.Area0800);
    				Switch02KCHR(value | chr_high, CHRArea.Area1800);
    				break;
    			case 3:
    				Switch01KCHR(value | chr_high, CHRArea.Area0C00);
    				Switch01KCHR(value | chr_high, CHRArea.Area1C00);
    				break;
    			}
    			break;
    		case 20784:
    			chr_high = (value & 3) << 8;
    			break;
    		case 20742:
    		{
    			for (int j = 0; j < 960; j++)
    			{
    				NMT_RAM[3][j] = value;
    			}
    			break;
    		}
    		case 20743:
    		{
    			for (int i = 960; i < 1024; i++)
    			{
    				temp_fill = (byte)((uint)(2 << (value & 3)) | (value & 3u));
    				temp_fill |= (byte)((temp_fill & 0xF) << 4);
    				NMT_RAM[3][i] = temp_fill;
    			}
    			break;
    		}
    		case 20992:
    			split_tile = value & 0x1F;
    			split_enable = (value & 0x80) == 128;
    			split_right = (value & 0x40) == 64;
    			break;
    		case 20993:
    			split_yscroll = value;
    			break;
    		case 20994:
    			Switch04kCHRSP(value, address & 0);
    			Switch04kCHRSP(value, address & 0x1000);
    			break;
    		case 20995:
    			irq_line = value;
    			break;
    		case 20996:
    			irq_enable = value;
    			break;
    		case 20997:
    			multiplicand = value;
    			product = (ushort)(multiplicand * multiplier);
    			break;
    		case 20998:
    			multiplier = value;
    			product = (ushort)(multiplicand * multiplier);
    			break;
    		}
    	}

    	internal override void ReadEX(ref ushort address, out byte data)
    	{
    		if (address >= 23552 && ExRAM_mode >= 2)
    		{
    			data = NMT_RAM[2][address & 0x3FF];
    			return;
    		}
    		switch (address)
    		{
    		case 20496:
    			data = snd_3.Read5010();
    			break;
    		case 20996:
    			data = (byte)(irq_current_inframe | irq_pending);
    			irq_pending = 0;
    			NesEmu.IRQFlags &= -9;
    			break;
    		case 20997:
    			data = (byte)(product & 0xFFu);
    			break;
    		case 20998:
    			data = (byte)((product & 0xFF00) >> 8);
    			break;
    		case 20501:
    			data = (byte)((snd_1.ReadEnable() ? 1u : 0u) | (snd_2.ReadEnable() ? 2u : 0u));
    			data = 0;
    			break;
    		default:
    			data = 0;
    			break;
    		}
    	}

    	internal override void ReadCHR(ref ushort address, out byte data)
    	{
    		if (!NesEmu.ppu_is_sprfetch && split_enable && ExRAM_mode < 2)
    		{
    			split_watch_tile = address & 0x3F;
    			if (!split_right)
    			{
    				split_doit = split_watch_tile < split_tile;
    			}
    			else
    			{
    				split_doit = split_watch_tile >= split_tile;
    			}
    			_ = split_doit;
    		}
    		if (ExRAM_mode == 1)
    		{
    			if (!NesEmu.ppu_is_sprfetch)
    			{
    				EXtilenumber = NMT_RAM[2][lastAccessVRAM] & 0x3F;
    				Switch04kCHREX(EXtilenumber | chr_high, address & 0x1000);
    				data = CHR_ROM[CHROffsetEX[(address >> 10) & 7]][address & 0x3FF];
    			}
    			else
    			{
    				data = CHR_ROM[CHROffset_spr[(address >> 10) & 7]][address & 0x3FF];
    			}
    		}
    		else if (NesEmu.ppu_reg_2000_Sprite_size == 16)
    		{
    			if (!NesEmu.ppu_is_sprfetch)
    			{
    				data = CHR_ROM[CHR_AREA_BLK_INDEX[(address >> 10) & 7]][address & 0x3FF];
    			}
    			else
    			{
    				data = CHR_ROM[CHROffset_spr[(address >> 10) & 7]][address & 0x3FF];
    			}
    		}
    		else if (chr_setB_last)
    		{
    			data = CHR_ROM[CHR_AREA_BLK_INDEX[(address >> 10) & 7]][address & 0x3FF];
    		}
    		else
    		{
    			data = CHR_ROM[CHROffset_spr[(address >> 10) & 7]][address & 0x3FF];
    		}
    	}

    	internal override void ReadNMT(ref ushort address, out byte data)
    	{
    		_ = split_doit;
    		if (ExRAM_mode == 1)
    		{
    			if ((address & 0x3FF) <= 959)
    			{
    				lastAccessVRAM = address & 0x3FF;
    			}
    			else
    			{
    				paletteNo = NMT_RAM[2][lastAccessVRAM] & 0xC0;
    				shift = ((lastAccessVRAM >> 4) & 4) | (lastAccessVRAM & 2);
    				switch (shift)
    				{
    				case 0:
    					data = (byte)(paletteNo >> 6);
    					return;
    				case 2:
    					data = (byte)(paletteNo >> 4);
    					return;
    				case 4:
    					data = (byte)(paletteNo >> 2);
    					return;
    				case 6:
    					data = (byte)paletteNo;
    					return;
    				}
    			}
    		}
    		data = NMT_RAM[NMT_AREA_BLK_INDEX[(address >> 10) & 3]][address & 0x3FF];
    	}

    	internal override void WriteNMT(ref ushort address, ref byte value)
    	{
    		if (ExRAM_mode == 1 && (address & 0x3FF) <= 959)
    		{
    			lastAccessVRAM = address & 0x3FF;
    		}
    		NMT_RAM[NMT_AREA_BLK_INDEX[(address >> 10) & 3]][address & 0x3FF] = value;
    	}

    	private void UpdateRamProtect()
    	{
    		TogglePRGRAMWritableEnable(ram_protectA == 2 && ram_protectB == 1);
    	}

    	private void Switch04kCHR_bkg(int index, int where)
    	{
    		int num = (where >> 10) & 7;
    		index <<= 2;
    		CHR_AREA_BLK_INDEX[num] = index & CHR_ROM_01KB_Mask;
    		num++;
    		index++;
    		CHR_AREA_BLK_INDEX[num] = index & CHR_ROM_01KB_Mask;
    		num++;
    		index++;
    		CHR_AREA_BLK_INDEX[num] = index & CHR_ROM_01KB_Mask;
    		num++;
    		index++;
    		CHR_AREA_BLK_INDEX[num] = index & CHR_ROM_01KB_Mask;
    	}

    	private void Switch01kCHR_spr(int index, int where)
    	{
    		CHROffset_spr[(where >> 10) & 7] = index & CHR_ROM_01KB_Mask;
    	}

    	private void Switch02kCHR_spr(int index, int where)
    	{
    		int num = (where >> 10) & 7;
    		index <<= 1;
    		CHROffset_spr[num] = index & CHR_ROM_01KB_Mask;
    		index++;
    		CHROffset_spr[num + 1] = index & CHR_ROM_01KB_Mask;
    	}

    	private void Switch04kCHR_spr(int index, int where)
    	{
    		int num = (where >> 10) & 7;
    		index <<= 2;
    		CHROffset_spr[num] = index & CHR_ROM_01KB_Mask;
    		num++;
    		index++;
    		CHROffset_spr[num] = index & CHR_ROM_01KB_Mask;
    		num++;
    		index++;
    		CHROffset_spr[num] = index & CHR_ROM_01KB_Mask;
    		num++;
    		index++;
    		CHROffset_spr[num] = index & CHR_ROM_01KB_Mask;
    	}

    	private void Switch08kCHR_spr(int index)
    	{
    		index <<= 3;
    		CHROffset_spr[0] = index & CHR_ROM_01KB_Mask;
    		index++;
    		CHROffset_spr[1] = index & CHR_ROM_01KB_Mask;
    		index++;
    		CHROffset_spr[2] = index & CHR_ROM_01KB_Mask;
    		index++;
    		CHROffset_spr[3] = index & CHR_ROM_01KB_Mask;
    		index++;
    		CHROffset_spr[4] = index & CHR_ROM_01KB_Mask;
    		index++;
    		CHROffset_spr[5] = index & CHR_ROM_01KB_Mask;
    		index++;
    		CHROffset_spr[6] = index & CHR_ROM_01KB_Mask;
    		index++;
    		CHROffset_spr[7] = index & CHR_ROM_01KB_Mask;
    	}

    	private void Switch04kCHREX(int index, int where)
    	{
    		int num = (where >> 10) & 7;
    		index <<= 2;
    		CHROffsetEX[num] = index & CHR_ROM_01KB_Mask;
    		num++;
    		index++;
    		CHROffsetEX[num] = index & CHR_ROM_01KB_Mask;
    		num++;
    		index++;
    		CHROffsetEX[num] = index & CHR_ROM_01KB_Mask;
    		num++;
    		index++;
    		CHROffsetEX[num] = index & CHR_ROM_01KB_Mask;
    	}

    	private void Switch04kCHRSP(int index, int where)
    	{
    		int num = (where >> 10) & 7;
    		index <<= 2;
    		CHROffsetSP[num] = index & CHR_ROM_01KB_Mask;
    		num++;
    		index++;
    		CHROffsetSP[num] = index & CHR_ROM_01KB_Mask;
    		num++;
    		index++;
    		CHROffsetSP[num] = index & CHR_ROM_01KB_Mask;
    		num++;
    		index++;
    		CHROffsetSP[num] = index & CHR_ROM_01KB_Mask;
    	}

    	internal override void OnPPUScanlineTick()
    	{
    		irq_current_inframe = ((NesEmu.IsInRender() && NesEmu.IsRenderingOn()) ? 64 : 0);
    		if (irq_current_inframe == 0)
    		{
    			irq_current_inframe = 64;
    			irq_current_counter = 0;
    			irq_pending = 0;
    			NesEmu.IRQFlags &= -9;
    			return;
    		}
    		irq_current_counter++;
    		if (irq_current_counter == irq_line)
    		{
    			irq_pending = 128;
    			if (irq_enable == 128)
    			{
    				NesEmu.IRQFlags |= 8;
    			}
    		}
    	}

    	internal override void OnAPUClock()
    	{
    		base.OnAPUClock();
    		snd_1.Clock();
    		snd_2.Clock();
    	}

    	internal override void OnAPUClockEnvelope()
    	{
    		base.OnAPUClockEnvelope();
    		snd_1.ClockLength();
    		snd_2.ClockLength();
    		snd_1.ClockEnvelope();
    		snd_2.ClockEnvelope();
    	}

    	internal override double APUGetSample()
    	{
    		return audio_pulse_table[snd_1.output + snd_2.output] + audio_tnd_table[snd_3.output];
    	}

    	internal override void APUApplyChannelsSettings()
    	{
    		base.APUApplyChannelsSettings();
    		snd_1.Outputable = MyNesMain.RendererSettings.Audio_ChannelEnabled_MMC5_SQ1;
    		snd_2.Outputable = MyNesMain.RendererSettings.Audio_ChannelEnabled_MMC5_SQ2;
    		snd_3.Outputable = MyNesMain.RendererSettings.Audio_ChannelEnabled_MMC5_PCM;
    	}

    	internal override void WriteStateData(ref BinaryWriter stream)
    	{
    		base.WriteStateData(ref stream);
    		stream.Write(ram_protectA);
    		stream.Write(ram_protectB);
    		stream.Write(ExRAM_mode);
    		for (int i = 0; i < CHROffset_spr.Length; i++)
    		{
    			stream.Write(CHROffset_spr[i]);
    		}
    		for (int j = 0; j < CHROffsetEX.Length; j++)
    		{
    			stream.Write(CHROffsetEX[j]);
    		}
    		for (int k = 0; k < CHROffsetSP.Length; k++)
    		{
    			stream.Write(CHROffsetSP[k]);
    		}
    		for (int l = 0; l < chrRegA.Length; l++)
    		{
    			stream.Write(chrRegA[l]);
    		}
    		for (int m = 0; m < chrRegB.Length; m++)
    		{
    			stream.Write(chrRegB[m]);
    		}
    		for (int n = 0; n < prgReg.Length; n++)
    		{
    			stream.Write(prgReg[n]);
    		}
    		stream.Write(useSRAMmirroring);
    		stream.Write(chr_high);
    		stream.Write(chr_mode);
    		stream.Write(prg_mode);
    		stream.Write(chr_setB_last);
    		stream.Write(temp_val);
    		stream.Write(temp_fill);
    		stream.Write(lastAccessVRAM);
    		stream.Write(paletteNo);
    		stream.Write(shift);
    		stream.Write(EXtilenumber);
    		stream.Write(multiplicand);
    		stream.Write(multiplier);
    		stream.Write(product);
    		stream.Write(split_enable);
    		stream.Write(split_right);
    		stream.Write(split_tile);
    		stream.Write(split_yscroll);
    		stream.Write(split_doit);
    		stream.Write(split_watch_tile);
    		stream.Write(irq_line);
    		stream.Write(irq_enable);
    		stream.Write(irq_pending);
    		stream.Write(irq_current_counter);
    		stream.Write(irq_current_inframe);
    		snd_1.WriteStateData(ref stream);
    		snd_2.WriteStateData(ref stream);
    		snd_3.SaveState(ref stream);
    	}

    	internal override void ReadStateData(ref BinaryReader stream)
    	{
    		base.ReadStateData(ref stream);
    		ram_protectA = stream.ReadInt32();
    		ram_protectB = stream.ReadInt32();
    		ExRAM_mode = stream.ReadInt32();
    		for (int i = 0; i < CHROffset_spr.Length; i++)
    		{
    			CHROffset_spr[i] = stream.ReadInt32();
    		}
    		for (int j = 0; j < CHROffsetEX.Length; j++)
    		{
    			CHROffsetEX[j] = stream.ReadInt32();
    		}
    		for (int k = 0; k < CHROffsetSP.Length; k++)
    		{
    			CHROffsetSP[k] = stream.ReadInt32();
    		}
    		for (int l = 0; l < chrRegA.Length; l++)
    		{
    			chrRegA[l] = stream.ReadInt32();
    		}
    		for (int m = 0; m < chrRegB.Length; m++)
    		{
    			chrRegB[m] = stream.ReadInt32();
    		}
    		for (int n = 0; n < prgReg.Length; n++)
    		{
    			prgReg[n] = stream.ReadInt32();
    		}
    		useSRAMmirroring = stream.ReadBoolean();
    		chr_high = stream.ReadInt32();
    		chr_mode = stream.ReadInt32();
    		prg_mode = stream.ReadInt32();
    		chr_setB_last = stream.ReadBoolean();
    		temp_val = stream.ReadByte();
    		temp_fill = stream.ReadByte();
    		lastAccessVRAM = stream.ReadInt32();
    		paletteNo = stream.ReadInt32();
    		shift = stream.ReadInt32();
    		EXtilenumber = stream.ReadInt32();
    		multiplicand = stream.ReadByte();
    		multiplier = stream.ReadByte();
    		product = stream.ReadUInt16();
    		split_enable = stream.ReadBoolean();
    		split_right = stream.ReadBoolean();
    		split_tile = stream.ReadInt32();
    		split_yscroll = stream.ReadInt32();
    		split_doit = stream.ReadBoolean();
    		split_watch_tile = stream.ReadInt32();
    		irq_line = stream.ReadByte();
    		irq_enable = stream.ReadByte();
    		irq_pending = stream.ReadInt32();
    		irq_current_counter = stream.ReadInt32();
    		irq_current_inframe = stream.ReadInt32();
    		snd_1.ReadStateData(ref stream);
    		snd_2.ReadStateData(ref stream);
    		snd_3.LoadState(ref stream);
    	}
    }
}