Mesen2/Core/NES/Input/BattleBox.h
2022-09-08 21:29:52 -04:00

121 lines
No EOL
3 KiB
C++

#pragma once
#include "pch.h"
#include "Shared/Emulator.h"
#include "Shared/BaseControlDevice.h"
#include "Shared/BatteryManager.h"
#include "Shared/Interfaces/IBattery.h"
#include "Utilities/Serializer.h"
class BattleBox : public BaseControlDevice, public IBattery
{
private:
static constexpr int FileSize = 0x200;
uint8_t _lastWrite = 0;
uint8_t _address = 0;
uint8_t _chipSelect = 0;
uint16_t _data[BattleBox::FileSize / 2] = {};
uint8_t _output = 0;
bool _writeEnabled = false;
uint8_t _inputBitPosition = 0;
uint16_t _inputData = 0;
bool _isWrite = false;
bool _isRead = false;
NesConsole* _console = nullptr;
protected:
void Serialize(Serializer& s) override
{
BaseControlDevice::Serialize(s);
SVArray((uint8_t*)_data, BattleBox::FileSize);
SV(_lastWrite); SV(_address); SV(_chipSelect); SV(_output); SV(_writeEnabled); SV(_inputBitPosition); SV(_isWrite); SV(_isRead); SV(_inputData);
}
public:
BattleBox(NesConsole* console) : BaseControlDevice(console->GetEmulator(), ControllerType::BattleBox, BaseControlDevice::ExpDevicePort)
{
_console = console;
}
void Init() override
{
_console->InitializeRam(_data, BattleBox::FileSize);
_emu->GetBatteryManager()->LoadBattery(".battlebox.sav", (uint8_t*)_data, BattleBox::FileSize);
}
void SaveBattery() override
{
_emu->GetBatteryManager()->SaveBattery(".battlebox.sav", (uint8_t*)_data, BattleBox::FileSize);
}
uint8_t ReadRam(uint16_t addr) override
{
if(addr == 0x4017) {
if(_lastWrite & 0x01) {
_chipSelect ^= 0x01;
_inputData = 0;
_inputBitPosition = 0;
}
_output ^= 0x01;
uint8_t readBit = 0;
if(_isRead) {
readBit = ((_data[(_chipSelect ? 0x80 : 0) | _address] >> _inputBitPosition) & 0x01) << 3;
}
uint8_t writeBit = (_output << 4);
return readBit | writeBit;
}
return 0;
}
void WriteRam(uint16_t addr, uint8_t value) override
{
if(value & 0x01 && !(_lastWrite & 0x01)) {
//Clock
_inputData &= ~(1 << _inputBitPosition);
_inputData |= (_output << _inputBitPosition);
_inputBitPosition++;
if(_inputBitPosition > 15) {
if(_isWrite) {
_data[(_chipSelect ? 0x80 : 0) | _address] = _inputData;
_isWrite = false;
} else {
_isRead = false;
//done reading addr/command or write data
uint8_t address = (_inputData & 0x7F);
uint8_t cmd = ((_inputData & 0x7F00) >> 8) ^ 0x7F;
switch(cmd) {
case 0x01:
//read
_address = address;
_isRead = true;
break;
case 0x06:
//program
if(_writeEnabled) {
_address = address;
_isWrite = true;
}
break;
case 0x0C:
//chip erase
if(_writeEnabled) {
memset(_data, 0, BattleBox::FileSize);
}
break;
case 0x0D: break; //busy monitor
case 0x09: _writeEnabled = true; break; //erase/write enable
case 0x0B: _writeEnabled = false; break; //erase/write disable
}
}
_inputBitPosition = 0;
}
}
_lastWrite = value;
}
};