mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
233 lines
6.2 KiB
C++
233 lines
6.2 KiB
C++
#include "pch.h"
|
|
#include "WS/WsEeprom.h"
|
|
#include "WS/WsConsole.h"
|
|
#include "Shared/Emulator.h"
|
|
#include "Shared/BatteryManager.h"
|
|
#include "Utilities/BitUtilities.h"
|
|
#include "Utilities/Serializer.h"
|
|
|
|
WsEeprom::WsEeprom(Emulator* emu, WsConsole* console, WsEepromSize size, uint8_t* eepromData, bool isInternal)
|
|
{
|
|
_emu = emu;
|
|
_console = console;
|
|
_isInternal = isInternal;
|
|
_data = eepromData;
|
|
_state.Size = size;
|
|
_state.Idle = true;
|
|
_state.ReadDone = true;
|
|
_state.WriteDisabled = true;
|
|
|
|
if(_isInternal) {
|
|
InitInternalEepromData();
|
|
}
|
|
}
|
|
|
|
WsEepromCommand WsEeprom::GetCommand()
|
|
{
|
|
uint16_t command = _state.Command;
|
|
if(GetSize() != WsEepromSize::Size128) {
|
|
command >>= 4;
|
|
}
|
|
|
|
switch(command & 0xFFC0) {
|
|
case 0x0180: return WsEepromCommand::Read;
|
|
case 0x0140: return WsEepromCommand::Write;
|
|
case 0x01C0: return WsEepromCommand::Erase;
|
|
case 0x0100:
|
|
switch((command >> 4) & 0x03) {
|
|
case 0x00: return WsEepromCommand::WriteDisable;
|
|
case 0x01: return WsEepromCommand::WriteAll;
|
|
case 0x02: return WsEepromCommand::EraseAll;
|
|
case 0x03: return WsEepromCommand::WriteEnable;
|
|
}
|
|
}
|
|
|
|
return WsEepromCommand::Unknown;
|
|
}
|
|
|
|
WsEepromSize WsEeprom::GetSize()
|
|
{
|
|
if(_isInternal && _state.Size == WsEepromSize::Size2kb && !_console->IsColorMode()) {
|
|
//Process commands as is the eeprom was the monochrome model's 128-byte eeprom when color mode is disabled
|
|
return WsEepromSize::Size128;
|
|
}
|
|
return _state.Size;
|
|
}
|
|
|
|
uint16_t WsEeprom::GetCommandAddress()
|
|
{
|
|
switch(GetSize()) {
|
|
case WsEepromSize::Size128: return _state.Command & 0x3F;
|
|
case WsEepromSize::Size1kb: return _state.Command & 0x1FF;
|
|
case WsEepromSize::Size2kb: return _state.Command & 0x3FF;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void WsEeprom::WriteValue(uint16_t addr, uint16_t value)
|
|
{
|
|
if(!_state.WriteDisabled && (!_state.InternalEepromWriteProtected || addr < 0x30)) {
|
|
if(_isInternal) {
|
|
_emu->ProcessMemoryAccess<CpuType::Ws, MemoryType::WsInternalEeprom, MemoryOperationType::Write>(addr << 1, value);
|
|
} else {
|
|
_emu->ProcessMemoryAccess<CpuType::Ws, MemoryType::WsCartEeprom, MemoryOperationType::Write>(addr << 1, value);
|
|
}
|
|
_data[addr << 1] = (uint8_t)value;
|
|
_data[(addr << 1) + 1] = (value >> 8);
|
|
}
|
|
}
|
|
|
|
string WsEeprom::ConvertToEepromString(string in)
|
|
{
|
|
for(size_t i = 0; i < in.size(); i++) {
|
|
in[i] = in[i] - 0x36;
|
|
}
|
|
return in;
|
|
}
|
|
|
|
void WsEeprom::InitInternalEepromData()
|
|
{
|
|
memset(_data, 0, (uint32_t)_state.Size);
|
|
|
|
string name;
|
|
switch(_console->GetModel()) {
|
|
case WsModel::Monochrome:
|
|
name = "WONDERSWAN";
|
|
break;
|
|
|
|
case WsModel::Color:
|
|
case WsModel::SwanCrystal:
|
|
//Set volume to max by default
|
|
_data[0x83] = 0x03;
|
|
|
|
if(_console->GetModel() == WsModel::Color) {
|
|
name = "WONDERSWANCOLOR";
|
|
} else {
|
|
name = "SWANCRYSTAL";
|
|
}
|
|
break;
|
|
}
|
|
|
|
memcpy(_data + 0x60, ConvertToEepromString(name).c_str(), name.size());
|
|
}
|
|
|
|
void WsEeprom::WritePort(uint8_t port, uint8_t value)
|
|
{
|
|
switch(port) {
|
|
case 0x00: BitUtilities::SetBits<0>(_state.WriteBuffer, value); break;
|
|
case 0x01: BitUtilities::SetBits<8>(_state.WriteBuffer, value); break;
|
|
case 0x02: BitUtilities::SetBits<0>(_state.Command, value); break;
|
|
case 0x03: BitUtilities::SetBits<8>(_state.Command, value); break;
|
|
|
|
case 0x04:
|
|
bool read = value & 0x10;
|
|
bool write = value & 0x20;
|
|
bool other = value & 0x40;
|
|
bool writeProtect = value & 0x80;
|
|
if((uint8_t)read + (uint8_t)write + (uint8_t)other + (uint8_t)writeProtect > 1) {
|
|
//Invalid operation, do nothing
|
|
return;
|
|
}
|
|
|
|
//TODOWS abort (cart eeprom)
|
|
if(writeProtect && _isInternal) {
|
|
_state.InternalEepromWriteProtected = true;
|
|
return;
|
|
}
|
|
|
|
WsEepromCommand cmd = GetCommand();
|
|
uint16_t addr = GetCommandAddress();
|
|
|
|
if(read) {
|
|
if(cmd == WsEepromCommand::Read) {
|
|
//TODOWS
|
|
//For the internal eeprom, "ReadDone" flag should be cleared here, and set again after a small delay
|
|
//On cartridge eeprom, the flag is bugged and not reset when a read starts
|
|
//The read buffer should also only be set after a delay (e.g after the read is done), for both eeproms
|
|
_state.ReadBuffer = _data[addr << 1] | (_data[(addr << 1) + 1] << 8);
|
|
if(_isInternal) {
|
|
_emu->ProcessMemoryAccess<CpuType::Ws, MemoryType::WsInternalEeprom, MemoryOperationType::Read>(addr << 1, _state.ReadBuffer);
|
|
} else {
|
|
_emu->ProcessMemoryAccess<CpuType::Ws, MemoryType::WsCartEeprom, MemoryOperationType::Read>(addr << 1, _state.ReadBuffer);
|
|
}
|
|
_state.ReadDone = true;
|
|
}
|
|
} else if(write) {
|
|
_state.ReadDone = false;
|
|
|
|
if(cmd == WsEepromCommand::Write) {
|
|
WriteValue(addr, _state.WriteBuffer);
|
|
} else if(cmd == WsEepromCommand::WriteAll) {
|
|
for(int i = 0; i < (int)_state.Size; i += 2) {
|
|
WriteValue(i >> 1, _state.WriteBuffer);
|
|
}
|
|
}
|
|
} else if(other) {
|
|
_state.ReadDone = false;
|
|
|
|
switch(cmd) {
|
|
case WsEepromCommand::Erase:
|
|
WriteValue(addr, 0xFFFF);
|
|
break;
|
|
|
|
case WsEepromCommand::WriteDisable:
|
|
_state.WriteDisabled = true;
|
|
break;
|
|
|
|
case WsEepromCommand::EraseAll:
|
|
for(int i = 0; i < (int)_state.Size; i += 2) {
|
|
WriteValue(i >> 1, 0xFFFF);
|
|
}
|
|
break;
|
|
|
|
case WsEepromCommand::WriteEnable:
|
|
_state.WriteDisabled = false;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint8_t WsEeprom::ReadPort(uint8_t port)
|
|
{
|
|
switch(port) {
|
|
case 0x00: return BitUtilities::GetBits<0>(_state.ReadBuffer);
|
|
case 0x01: return BitUtilities::GetBits<8>(_state.ReadBuffer);
|
|
case 0x02: return BitUtilities::GetBits<0>(_state.Command);
|
|
case 0x03: return BitUtilities::GetBits<8>(_state.Command);
|
|
|
|
case 0x04:
|
|
return (
|
|
(_state.ReadDone ? 0x01 : 0) |
|
|
(_state.Idle ? 0x02 : 0) |
|
|
(_state.InternalEepromWriteProtected ? 0x80 : 0)
|
|
);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void WsEeprom::LoadBattery()
|
|
{
|
|
_emu->GetBatteryManager()->LoadBattery(_isInternal ? ".ieeprom" : ".eeprom", _data, (uint32_t)_state.Size);
|
|
}
|
|
|
|
void WsEeprom::SaveBattery()
|
|
{
|
|
_emu->GetBatteryManager()->SaveBattery(_isInternal ? ".ieeprom" : ".eeprom", _data, (uint32_t)_state.Size);
|
|
}
|
|
|
|
void WsEeprom::Serialize(Serializer& s)
|
|
{
|
|
SV(_state.Size);
|
|
SV(_state.ReadBuffer);
|
|
SV(_state.WriteBuffer);
|
|
SV(_state.Command);
|
|
SV(_state.Control);
|
|
SV(_state.WriteDisabled);
|
|
SV(_state.ReadDone);
|
|
SV(_state.Idle);
|
|
SV(_state.InternalEepromWriteProtected);
|
|
}
|