Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Core/Core.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,7 @@
<ClCompile Include="Shared\Audio\WaveRecorder.cpp" />
<ClCompile Include="WS\APU\WsApu.cpp" />
<ClCompile Include="WS\Carts\WsCart.cpp" />
<ClCompile Include="WS\Carts\WsCartFlash.cpp" />
<ClCompile Include="WS\Carts\WsRtc.cpp" />
<ClCompile Include="WS\Debugger\DummyWsCpu.cpp" />
<ClCompile Include="WS\Debugger\WsDebugger.cpp" />
Expand Down
65 changes: 56 additions & 9 deletions Core/WS/Carts/WsCart.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "WS/Carts/WsRtc.h"
#include "WS/WsMemoryManager.h"
#include "WS/WsEeprom.h"
#include "Utilities/BitUtilities.h"
#include "Utilities/Serializer.h"

void WsCart::Map(uint32_t start, uint32_t end, MemoryType type, uint32_t offset, bool readonly)
Expand All @@ -17,53 +18,99 @@ void WsCart::Unmap(uint32_t start, uint32_t end)

WsCart::WsCart()
{
_state.SelectedBanks[0] = 0xFF;
_state.SelectedBanks[1] = 0xFF;
_state.SelectedBanks[2] = 0xFF;
_state.SelectedBanks[3] = 0xFF;
_state.SelectedBanks[0] = 0x3F;
_state.SelectedBanks[1] = 0x3FF;
_state.SelectedBanks[2] = 0x3FF;
_state.SelectedBanks[3] = 0x3FF;
}

void WsCart::Init(WsMemoryManager* memoryManager, WsEeprom* cartEeprom, WsRtc* cartRtc)
void WsCart::Init(WsMemoryManager* memoryManager, WsEeprom* cartEeprom, WsRtc* cartRtc, uint8_t* prgRom, uint32_t prgRomSize, uint8_t* saveRam, uint32_t saveRamSize)
{
_memoryManager = memoryManager;
_cartEeprom = cartEeprom;
_cartRtc = cartRtc;
_state.HasRtc = cartRtc != nullptr;
_prgRom = prgRom;
_prgRomSize = prgRomSize;
_saveRam = saveRam;
_saveRamSize = saveRamSize;
}

uint32_t WsCart::GetPhysicalAddress(uint32_t addr)
{
if(addr >= 0x40000) {
return (_state.SelectedBanks[0] << 20) | (addr & 0xFFFFF);
} else {
return (_state.SelectedBanks[addr >> 16] << 16) | (addr & 0xFFFF);
}
}

void WsCart::RefreshMappings()
{
Map(0x10000, 0x1FFFF, MemoryType::WsCartRam, _state.SelectedBanks[1] * 0x10000, false);
if(_state.RomInRamBank) {
Map(0x10000, 0x1FFFF, MemoryType::WsPrgRom, _state.SelectedBanks[1] * 0x10000, true);
} else {
Map(0x10000, 0x1FFFF, MemoryType::WsCartRam, _state.SelectedBanks[1] * 0x10000, false);
}
Map(0x20000, 0x2FFFF, MemoryType::WsPrgRom, _state.SelectedBanks[2] * 0x10000, true);
Map(0x30000, 0x3FFFF, MemoryType::WsPrgRom, _state.SelectedBanks[3] * 0x10000, true);
Map(0x40000, 0xFFFFF, MemoryType::WsPrgRom, _state.SelectedBanks[0] * 0x100000 + 0x40000, true);
}

uint8_t WsCart::ReadPort(uint16_t port)
{
if(port < 0xC4) {
if(port == 0xC0 || port == 0xCF) {
return _state.SelectedBanks[0];
} else if(port < 0xC4) {
return _state.SelectedBanks[port - 0xC0];
} else if(port >= 0xC4 && port < 0xC9 && _cartEeprom) {
return _cartEeprom->ReadPort(port - 0xC4);
} else if(port >= 0xCA && port < 0xCC && _cartRtc) {
return _cartRtc->ReadPort(port);
} else if(port == 0xCE) {
return _state.RomInRamBank ? 0x01 : 0;
} else if(port >= 0xD0 && port < 0xD6) {
return _state.SelectedBanks[1 + ((port - 0xD0) >> 1)] >> ((port & 1) ? 8 : 0);
}

return _memoryManager->GetUnmappedPort();
}

void WsCart::WritePort(uint16_t port, uint8_t value)
{
if(port < 0xC4) {
_state.SelectedBanks[port - 0xC0] = value;
if(port == 0xC0 || port == 0xCF) {
_state.SelectedBanks[0] = value & 0x3F;
_memoryManager->RefreshMappings();
} else if(port < 0xC4) {
BitUtilities::SetBits<0>(_state.SelectedBanks[port - 0xC0], value);
_memoryManager->RefreshMappings();
} else if(port >= 0xC4 && port < 0xC9 && _cartEeprom) {
_cartEeprom->WritePort(port - 0xC4, value);
} else if(port >= 0xCA && port < 0xCC && _cartRtc) {
_cartRtc->WritePort(port, value);
} else if(port == 0xCE) {
_state.RomInRamBank = (value & 0x01) != 0;
_memoryManager->RefreshMappings();
} else if(port >= 0xD0 && port < 0xD6) {
if(port & 1) {
BitUtilities::SetBits<8>(_state.SelectedBanks[1 + ((port - 0xD0) >> 1)], value & 0x03);
} else {
BitUtilities::SetBits<0>(_state.SelectedBanks[1 + ((port - 0xD0) >> 1)], value);
}
_memoryManager->RefreshMappings();
}
}

uint8_t WsCart::ReadMemory(uint32_t addr)
{
assert(false);
}

void WsCart::WriteMemory(uint32_t addr, uint8_t value)
{
assert(false);
}

void WsCart::Serialize(Serializer& s)
{
SVArray(_state.SelectedBanks, 4);
Expand Down
18 changes: 13 additions & 5 deletions Core/WS/Carts/WsCart.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ class WsMemoryManager;
class WsEeprom;
class WsRtc;

//TODOWS Flash

class WsCart final : public ISerializable
class WsCart : public ISerializable
{
protected:
WsCartState _state = {};
Expand All @@ -20,20 +18,30 @@ class WsCart final : public ISerializable
WsEeprom* _cartEeprom = nullptr;
WsRtc* _cartRtc = nullptr;

uint8_t* _prgRom = nullptr;
uint32_t _prgRomSize = 0;
uint8_t* _saveRam = nullptr;
uint32_t _saveRamSize = 0;

void Map(uint32_t start, uint32_t end, MemoryType type, uint32_t offset, bool readonly);
void Unmap(uint32_t start, uint32_t end);

uint32_t GetPhysicalAddress(uint32_t addr);

public:
WsCart();
virtual ~WsCart() {}

void Init(WsMemoryManager* memoryManager, WsEeprom* cartEeprom, WsRtc* cartRtc);
void RefreshMappings();
virtual void Init(WsMemoryManager* memoryManager, WsEeprom* cartEeprom, WsRtc* cartRtc, uint8_t* prgRom, uint32_t prgRomSize, uint8_t* saveRam, uint32_t saveRamSize);
virtual void RefreshMappings();

WsCartState& GetState() { return _state; }
WsEeprom* GetEeprom() { return _cartEeprom; }
WsRtc* GetRtc() { return _cartRtc; }

virtual uint8_t ReadMemory(uint32_t addr);
virtual void WriteMemory(uint32_t addr, uint8_t value);

virtual uint8_t ReadPort(uint16_t port);
virtual void WritePort(uint16_t port, uint8_t value);

Expand Down
139 changes: 139 additions & 0 deletions Core/WS/Carts/WsCartFlash.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#include "pch.h"
#include "WS/WsMemoryManager.h"
#include "WS/Carts/WsCartFlash.h"

//TODOWS sector protection
//TODOWS timing, more accurate status reads during programming
//TODOWS more verification with datasheet and hardware

WsCartFlash::WsCartFlash() : WsCart()
{
}

void WsCartFlash::RefreshMappings()
{
WsCart::RefreshMappings();
_memoryManager->SetCartFlash((_mode[0] != Mode::Read || _mode[1] != Mode::Read) ? WsRegisterAccess::ReadWrite : WsRegisterAccess::Write);
}

uint8_t WsCartFlash::ReadMemory(uint32_t addr)
{
bool sram = ((addr >> 16) == 1) && !_state.RomInRamBank;
uint32_t physAddr = GetPhysicalAddress(addr);

if(sram) {
return _saveRam[physAddr & (_saveRamSize - 1)];
}

physAddr &= _prgRomSize - 1;

uint8_t sector = physAddr >= 0x60000 ? 1 : 0;
uint8_t value = _prgRom[physAddr];
if(_mode[sector] == Mode::Fast) {
return (value & 0x80) | 0x04;
}
if(_mode[sector] == Mode::Autoselect) {
//TODOWS support word bus reads correctly
if((physAddr & 0x87) == 0x00) {
return 0x04;
}
if((physAddr & 0x87) == 0x02) {
return 0x0C;
}
if((physAddr & 0x87) == 0x04) {
return 0x00;
}
//TODOWS what is read at other addresses?
}
return value;
}

void WsCartFlash::WriteMemory(uint32_t addr, uint8_t value)
{
bool sram = ((addr >> 16) == 1) && !_state.RomInRamBank;
uint32_t physAddr = GetPhysicalAddress(addr);

if(sram) {
_saveRam[physAddr & (_saveRamSize - 1)] = value;
return;
}

physAddr &= _prgRomSize - 1;

uint8_t sector = physAddr >= 0x60000 ? 1 : 0;
if(_mode[sector] == Mode::Program) {
_prgRom[physAddr] &= value;
_mode[sector] = Mode::Read;
} else if(_mode[sector] == Mode::FastProgram) {
_prgRom[physAddr] &= value;
_mode[sector] = Mode::Fast;
} else if(_mode[sector] == Mode::Fast) {
if(value == 0xA0) {
_mode[sector] = Mode::FastProgram;
} else if(value == 0x90) {
_mode[sector] = Mode::Read;
}
} else {
//TODOWS how does this operate in erase mode?
if(value == 0xF0) {
_mode[sector] = Mode::Read;
}

if(_unlock == 0 && value == 0xAA && (physAddr & 0xFFF) == 0xAAA) {
_unlock = 1;
} else if(_unlock == 1) {
_unlock = (value == 0x55 && (physAddr & 0xFFF) == 0x555) ? 2 : 0;
} else if(_unlock == 2) {
if(_mode[sector] == Mode::Erase) {
if(value == 0x10) {
memset(_prgRom, 0xFF, _prgRomSize);
} else if(value == 0x30) {
uint32_t offset, size;
if(physAddr < 0x60000) {
offset = physAddr & 0x70000;
size = 0x10000;
} else if(physAddr < 0x64000) {
offset = 0x60000;
size = 0x4000;
} else if(physAddr < 0x6c000) {
offset = 0x64000;
size = 0x8000;
} else if(physAddr < 0x74000) {
offset = physAddr & 0x7e000;
size = 0x2000;
} else if(physAddr < 0x7c000) {
offset = 0x74000;
size = 0x8000;
} else {
offset = 0x7c000;
size = 0x4000;
}
memset(_prgRom + offset, 0xFF, size);
}
_mode[sector] = Mode::Read;
} else {
switch(value) {
case 0x90: _mode[sector] = Mode::Autoselect; break;
case 0xA0: _mode[sector] = Mode::Program; break;
case 0x80: _mode[sector] = Mode::Erase; break;
case 0x20: _mode[sector] = Mode::Fast; break;
}
}

_unlock = 0;
if(_mode[0] != Mode::Read && _mode[1] != Mode::Read) {
_mode[sector ^ 1] = Mode::Read;
}
}
}

_memoryManager->SetCartFlash((_mode[0] != Mode::Read || _mode[1] != Mode::Read) ? WsRegisterAccess::ReadWrite : WsRegisterAccess::Write);
}

void WsCartFlash::Serialize(Serializer& s)
{
WsCart::Serialize(s);

SV(_unlock);
SVArray(_mode, 2);
}
34 changes: 34 additions & 0 deletions Core/WS/Carts/WsCartFlash.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once
#include "pch.h"
#include "WS/WsTypes.h"
#include "WS/Carts/WsCart.h"
#include "Utilities/ISerializable.h"
#include "Shared/MemoryType.h"

class WsCartFlash final : public WsCart
{
private:
enum class Mode : uint8_t
{
Read = 0,
Autoselect,
Fast,
Program,
FastProgram,
Erase
};

Mode _mode[2];
uint8_t _unlock;

public:
WsCartFlash();
virtual ~WsCartFlash() {}

void RefreshMappings() override;

uint8_t ReadMemory(uint32_t addr) override;
void WriteMemory(uint32_t addr, uint8_t value) override;

void Serialize(Serializer& s) override;
};
Loading
Loading