Cores/Nds/NDS.CartSave.cs
namespace Emulotl.Nds;

public sealed partial class NDS
{
	private byte[] SRAM;
	private uint SRAMLength;
	private int SRAMType;

	private byte SRAMStatus;
	private byte SRAMCmd;
	private uint SRAMAddr;
	private uint SRAMPos;

	private uint SRAMSaveAddr;
	private uint SRAMSaveLen;

	private bool SRAMSelected;

	private bool IsIRCart;
	private byte IRCmd;
	private uint IRPos;

	private void ResetCartSave()
	{
		int savememtype = NdsSaveDatabase.SaveMemType( _rom );
		int len = NdsSaveDatabase.Length( savememtype );
		int type = NdsSaveDatabase.SramType( savememtype );

		_save.Setup( len, type );
		SRAM = _save.Data;
		SRAMLength = (uint)_save.Data.Length;
		SRAMType = _save.Type;

		SRAMStatus = 0;
		SRAMCmd = 0;
		SRAMAddr = 0;
		SRAMPos = 0;

		SRAMSaveAddr = 0;
		SRAMSaveLen = 0;

		SRAMSelected = false;

		IsIRCart = _rom != null && _rom.Length > 0x0C && _rom[0x0C] == (byte)'I';
		IRCmd = 0;
		IRPos = 0;
	}

	private void SaveSPISelect()
	{
		SRAMPos = 0;
		IRPos = 0;
	}

	private void SaveSPIRelease()
	{
		if ( (SRAMStatus & (1 << 1)) != 0 && SRAMSaveLen > 0 )
		{
			_save.MarkDirty();

			SRAMStatus &= unchecked((byte)~(1 << 1));
			SRAMSaveAddr = 0;
			SRAMSaveLen = 0;
		}
	}

	private byte SaveSPITransmitReceive( byte val )
	{
		if ( !IsIRCart )
			return SaveFlashTransmitReceive( val );

		if ( IRPos == 0 )
		{
			IRCmd = val;
			IRPos++;
			return 0;
		}

		IRPos++;
		return IRCmd switch
		{
			0x00 => SaveFlashTransmitReceive( val ),
			0x08 => 0xAA,
			_ => 0,
		};
	}

	private byte SaveFlashTransmitReceive( byte val )
	{
		if ( SRAMType == 0 ) return 0;

		byte ret = 0xFF;

		if ( SRAMPos == 0 )
		{
			switch ( val )
			{
				case 0x04: SRAMStatus &= unchecked((byte)~(1 << 1)); return 0;
				case 0x06: SRAMStatus |= 1 << 1; return 0;
				default:
					SRAMCmd = val;
					SRAMAddr = 0;
					break;
			}
		}
		else
		{
			ret = SRAMType switch
			{
				1 => SRAMWrite_EEPROMTiny( val ),
				2 => SRAMWrite_EEPROM( val ),
				3 => SRAMWrite_FLASH( val ),
				_ => 0xFF,
			};
		}

		SRAMPos++;
		return ret;
	}

	private byte SRAMWrite_EEPROMTiny( byte val )
	{
		switch ( SRAMCmd )
		{
			case 0x01:
				if ( SRAMPos == 1 )
					SRAMStatus = (byte)((SRAMStatus & 0x01) | (val & 0x0C));
				return 0;

			case 0x05:
				return (byte)(SRAMStatus | 0xF0);

			case 0x02:
			case 0x0A:
				if ( SRAMPos < 2 )
				{
					SRAMAddr = val;
					SRAMSaveAddr = SRAMAddr + (SRAMCmd == 0x0A ? 0x100u : 0);
					SRAMSaveLen = 0;
				}
				else
				{
					if ( (SRAMStatus & (1 << 1)) != 0 )
					{
						SRAM[(SRAMAddr + (SRAMCmd == 0x0A ? 0x100u : 0)) & 0x1FF] = val;
						SRAMSaveLen++;
					}
					SRAMAddr++;
				}
				return 0;

			case 0x03:
			case 0x0B:
				if ( SRAMPos < 2 )
				{
					SRAMAddr = val;
					return 0;
				}
				else
				{
					byte ret = SRAM[(SRAMAddr + (SRAMCmd == 0x0B ? 0x100u : 0)) & 0x1FF];
					SRAMAddr++;
					return ret;
				}

			case 0x9F:
				return 0xFF;

			default:
				return 0xFF;
		}
	}

	private byte SRAMWrite_EEPROM( byte val )
	{
		uint addrsize = 2;
		if ( SRAMLength > 65536 ) addrsize++;

		switch ( SRAMCmd )
		{
			case 0x01:
				if ( SRAMPos == 1 )
					SRAMStatus = (byte)((SRAMStatus & 0x01) | (val & 0x0C));
				return 0;

			case 0x05:
				return SRAMStatus;

			case 0x02:
				if ( SRAMPos <= addrsize )
				{
					SRAMAddr <<= 8;
					SRAMAddr |= val;
					SRAMSaveAddr = SRAMAddr;
					SRAMSaveLen = 0;
				}
				else
				{
					if ( (SRAMStatus & (1 << 1)) != 0 )
					{
						SRAM[SRAMAddr & (SRAMLength - 1)] = val;
						SRAMSaveLen++;
					}
					SRAMAddr++;
				}
				return 0;

			case 0x03:
				if ( SRAMPos <= addrsize )
				{
					SRAMAddr <<= 8;
					SRAMAddr |= val;
					return 0;
				}
				else
				{
					byte ret = SRAM[SRAMAddr & (SRAMLength - 1)];
					SRAMAddr++;
					return ret;
				}

			case 0x9F:
				return 0xFF;

			default:
				return 0xFF;
		}
	}

	private byte SRAMWrite_FLASH( byte val )
	{
		switch ( SRAMCmd )
		{
			case 0x05:
				return SRAMStatus;

			case 0x02:
				if ( SRAMPos <= 3 )
				{
					SRAMAddr <<= 8;
					SRAMAddr |= val;
					SRAMSaveAddr = SRAMAddr;
					SRAMSaveLen = 0;
				}
				else
				{
					if ( (SRAMStatus & (1 << 1)) != 0 )
					{
						SRAM[SRAMAddr & (SRAMLength - 1)] = 0;
						SRAMSaveLen++;
					}
					SRAMAddr++;
				}
				return 0;

			case 0x03:
				if ( SRAMPos <= 3 )
				{
					SRAMAddr <<= 8;
					SRAMAddr |= val;
					return 0;
				}
				else
				{
					byte ret = SRAM[SRAMAddr & (SRAMLength - 1)];
					SRAMAddr++;
					return ret;
				}

			case 0x0A:
				if ( SRAMPos <= 3 )
				{
					SRAMAddr <<= 8;
					SRAMAddr |= val;
					SRAMSaveAddr = SRAMAddr;
					SRAMSaveLen = 0;
				}
				else
				{
					if ( (SRAMStatus & (1 << 1)) != 0 )
					{
						SRAM[SRAMAddr & (SRAMLength - 1)] = val;
						SRAMSaveLen++;
					}
					SRAMAddr++;
				}
				return 0;

			case 0x0B:
				if ( SRAMPos <= 3 )
				{
					SRAMAddr <<= 8;
					SRAMAddr |= val;
					return 0;
				}
				else if ( SRAMPos == 4 )
				{
					return 0;
				}
				else
				{
					byte ret = SRAM[SRAMAddr & (SRAMLength - 1)];
					SRAMAddr++;
					return ret;
				}

			case 0x9F:
				return 0xFF;

			case 0xD8:
				if ( SRAMPos <= 3 )
				{
					SRAMAddr <<= 8;
					SRAMAddr |= val;
					SRAMSaveAddr = SRAMAddr;
					SRAMSaveLen = 0;
				}
				if ( SRAMPos == 3 && (SRAMStatus & (1 << 1)) != 0 )
				{
					for ( uint i = 0; i < 0x10000; i++ )
					{
						SRAM[SRAMAddr & (SRAMLength - 1)] = 0;
						SRAMAddr++;
					}
					SRAMSaveLen = 0x10000;
				}
				return 0;

			case 0xDB:
				if ( SRAMPos <= 3 )
				{
					SRAMAddr <<= 8;
					SRAMAddr |= val;
					SRAMSaveAddr = SRAMAddr;
					SRAMSaveLen = 0;
				}
				if ( SRAMPos == 3 && (SRAMStatus & (1 << 1)) != 0 )
				{
					for ( uint i = 0; i < 0x100; i++ )
					{
						SRAM[SRAMAddr & (SRAMLength - 1)] = 0;
						SRAMAddr++;
					}
					SRAMSaveLen = 0x100;
				}
				return 0;

			default:
				return 0xFF;
		}
	}

	public byte ReadAuxSpiData( uint cpu )
	{
		if ( (AuxSpiCnt[cpu] & (1 << 15)) == 0 ) return 0;
		if ( (AuxSpiCnt[cpu] & (1 << 13)) == 0 ) return 0;
		if ( (AuxSpiCnt[cpu] & (1 << 7)) != 0 ) return 0;

		return AuxSpiData[cpu];
	}

	public void WriteAuxSpiData( uint cpu, byte val )
	{
		if ( (AuxSpiCnt[cpu] & (1 << 15)) == 0 ) return;
		if ( (AuxSpiCnt[cpu] & (1 << 13)) == 0 ) return;
		if ( (AuxSpiCnt[cpu] & (1 << 7)) != 0 ) return;

		bool hold = (AuxSpiCnt[cpu] & (1 << 6)) != 0;

		if ( CartInserted )
		{
			if ( !SRAMSelected )
				SaveSPISelect();

			AuxSpiData[cpu] = SaveSPITransmitReceive( val );

			if ( !hold )
				SaveSPIRelease();
		}
		else
		{
			AuxSpiData[cpu] = 0;
		}

		SRAMSelected = hold;
	}
}