223 lines
7.3 KiB
C#
223 lines
7.3 KiB
C#
using System;
|
|
using static Util;
|
|
namespace OptimeGBA
|
|
{
|
|
public enum FlashState
|
|
{
|
|
InitialState,
|
|
PreCommand0,
|
|
PreCommand1,
|
|
|
|
FullErase,
|
|
EraseSector,
|
|
PrepareWriteByte,
|
|
SetBank,
|
|
}
|
|
|
|
public enum FlashStateSecondary
|
|
{
|
|
Ready,
|
|
PrepareEraseCommand
|
|
}
|
|
|
|
public enum FlashSize
|
|
{
|
|
Flash512k,
|
|
Flash1m
|
|
}
|
|
|
|
public unsafe sealed class Flash : SaveProvider
|
|
{
|
|
Gba Gba;
|
|
|
|
FlashState State = FlashState.InitialState;
|
|
FlashStateSecondary StateSecondary = FlashStateSecondary.Ready;
|
|
|
|
FlashSize Size;
|
|
|
|
bool IdentificationMode = false;
|
|
bool Bank1 = false;
|
|
bool PrepareSetBank = false;
|
|
bool PrepareWrite = false;
|
|
|
|
byte[] Memory;
|
|
|
|
public Flash(Gba gba, FlashSize size)
|
|
{
|
|
Gba = gba;
|
|
Size = size;
|
|
|
|
switch (size)
|
|
{
|
|
case FlashSize.Flash1m:
|
|
Memory = new byte[131072];
|
|
break;
|
|
case FlashSize.Flash512k:
|
|
Memory = new byte[65536];
|
|
break;
|
|
}
|
|
}
|
|
|
|
public override byte Read8(uint addr)
|
|
{
|
|
byte val = 0;
|
|
|
|
addr -= 0xE000000;
|
|
if (Bank1) addr += 0x10000;
|
|
if (addr < Memory.Length)
|
|
{
|
|
val = Memory[addr];
|
|
}
|
|
|
|
if (IdentificationMode)
|
|
{
|
|
// Return correct IDs in identification mode
|
|
if (Size == FlashSize.Flash1m)
|
|
{
|
|
switch (addr)
|
|
{
|
|
case 0x0: return 0x62;
|
|
case 0x1: return 0x13;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (addr)
|
|
{
|
|
case 0x0: return 0x32;
|
|
case 0x1: return 0x1B;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Console.WriteLine("Flash.Read8 addr:" + HexN(addr, 8) + " val:" + HexN(val, 2));
|
|
// Gba.Arm7.Error("read");
|
|
|
|
return val;
|
|
}
|
|
|
|
public override void Write8(uint addr, byte val)
|
|
{
|
|
if (PrepareSetBank && addr == 0xE000000)
|
|
{
|
|
if (Size == FlashSize.Flash1m)
|
|
{
|
|
Bank1 = (val & 1) != 0 ? true : false;
|
|
}
|
|
PrepareSetBank = false;
|
|
return;
|
|
}
|
|
|
|
if (PrepareWrite)
|
|
{
|
|
addr -= 0xE000000;
|
|
if (Bank1) addr += 0x10000;
|
|
if (addr < Memory.Length)
|
|
{
|
|
// Writes can only clear bits
|
|
Memory[addr] &= val;
|
|
Dirty = true;
|
|
}
|
|
PrepareWrite = false;
|
|
return;
|
|
}
|
|
|
|
switch (State)
|
|
{
|
|
case FlashState.InitialState:
|
|
if (addr == 0xE005555 && val == 0xAA)
|
|
{
|
|
// Console.WriteLine("pre-command 0 sent");
|
|
State = FlashState.PreCommand0;
|
|
}
|
|
break;
|
|
case FlashState.PreCommand0:
|
|
if (addr == 0xE002AAA && val == 0x55)
|
|
{
|
|
// Console.WriteLine("pre-command 1 sent, ready to receive commands");
|
|
State = FlashState.PreCommand1;
|
|
}
|
|
break;
|
|
case FlashState.PreCommand1:
|
|
switch (StateSecondary)
|
|
{
|
|
case FlashStateSecondary.Ready:
|
|
if (addr == 0xE005555)
|
|
{
|
|
switch (val)
|
|
{
|
|
case 0x90:
|
|
// Console.WriteLine("enter identification mode");
|
|
IdentificationMode = true;
|
|
break;
|
|
case 0xF0:
|
|
// Console.WriteLine("exit identification mode");
|
|
// Gba.Arm7.Error("here");
|
|
IdentificationMode = false;
|
|
break;
|
|
case 0x80:
|
|
// Console.WriteLine("preparing to erase");
|
|
StateSecondary = FlashStateSecondary.PrepareEraseCommand;
|
|
break;
|
|
case 0xB0:
|
|
// Console.WriteLine("preparing to set bank");
|
|
PrepareSetBank = true;
|
|
break;
|
|
case 0xA0:
|
|
// Console.WriteLine("preparing to write");
|
|
PrepareWrite = true;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case FlashStateSecondary.PrepareEraseCommand:
|
|
switch (val)
|
|
{
|
|
// Erase everything
|
|
case 0x10:
|
|
if (addr == 0xE005555)
|
|
{
|
|
for (uint i = 0; i < Memory.Length; i++)
|
|
{
|
|
Memory[i] = 0xFF;
|
|
}
|
|
Dirty = true;
|
|
}
|
|
StateSecondary = FlashStateSecondary.Ready;
|
|
break;
|
|
// Erase 4 KB
|
|
case 0x30:
|
|
uint page = addr & 0xF000;
|
|
if (Bank1) page += 0x10000;
|
|
for (uint i = 0; i < 0x1000; i++)
|
|
{
|
|
if (i < Memory.Length)
|
|
{
|
|
Memory[page + i] = 0xFF;
|
|
}
|
|
}
|
|
Dirty = true;
|
|
StateSecondary = FlashStateSecondary.Ready;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
State = FlashState.InitialState;
|
|
break;
|
|
}
|
|
}
|
|
|
|
public override byte[] GetSave()
|
|
{
|
|
return Memory;
|
|
}
|
|
|
|
public override void LoadSave(byte[] save)
|
|
{
|
|
for (uint i = 0; i < save.Length && i < Memory.Length; i++)
|
|
{
|
|
Memory[i] = save[i];
|
|
}
|
|
}
|
|
}
|
|
} |