diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index eb5029fcc..dea7aefb0 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -1100,6 +1100,7 @@ + diff --git a/Core/WS/Carts/WsCart.cpp b/Core/WS/Carts/WsCart.cpp index 6895417a7..1cd5678c8 100644 --- a/Core/WS/Carts/WsCart.cpp +++ b/Core/WS/Carts/WsCart.cpp @@ -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) @@ -17,23 +18,40 @@ 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); @@ -41,12 +59,18 @@ void WsCart::RefreshMappings() 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(); @@ -54,16 +78,39 @@ uint8_t WsCart::ReadPort(uint16_t port) 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); diff --git a/Core/WS/Carts/WsCart.h b/Core/WS/Carts/WsCart.h index 1779c395d..9a269b01c 100644 --- a/Core/WS/Carts/WsCart.h +++ b/Core/WS/Carts/WsCart.h @@ -9,9 +9,7 @@ class WsMemoryManager; class WsEeprom; class WsRtc; -//TODOWS Flash - -class WsCart final : public ISerializable +class WsCart : public ISerializable { protected: WsCartState _state = {}; @@ -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); diff --git a/Core/WS/Carts/WsCartFlash.cpp b/Core/WS/Carts/WsCartFlash.cpp new file mode 100644 index 000000000..364e6e8b9 --- /dev/null +++ b/Core/WS/Carts/WsCartFlash.cpp @@ -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); +} diff --git a/Core/WS/Carts/WsCartFlash.h b/Core/WS/Carts/WsCartFlash.h new file mode 100644 index 000000000..4799ea2fb --- /dev/null +++ b/Core/WS/Carts/WsCartFlash.h @@ -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; +}; diff --git a/Core/WS/WsConsole.cpp b/Core/WS/WsConsole.cpp index 2881a4d0d..f75cbe504 100644 --- a/Core/WS/WsConsole.cpp +++ b/Core/WS/WsConsole.cpp @@ -1,9 +1,11 @@ +#include "WS/Carts/WsCartFlash.h" #include "pch.h" #include "WS/WsConsole.h" #include "WS/WsCpu.h" #include "WS/WsPpu.h" #include "WS/WsTimer.h" #include "WS/Carts/WsCart.h" +#include "WS/Carts/WsCartFlash.h" #include "WS/Carts/WsRtc.h" #include "WS/WsControlManager.h" #include "WS/WsMemoryManager.h" @@ -33,6 +35,16 @@ WsConsole::~WsConsole() delete[] _cartEepromData; } +bool WsConsole::IsWWCart() +{ + //TODOWS exclude static FreyaBIOS cartridges + return _prgRomSize == 0x80000 && _prgRom[0x70000] == 'E' // FreyaBIOS header + && _prgRom[0x70001] == 'L' && _prgRom[0x70002] == 'I' && _prgRom[0x70003] == 'S' && _prgRom[0x70004] == 'A' && _prgRom[0x7fff6] == 0x00 // Publisher ID + && _prgRom[0x7fff8] == 0x00 // Game ID + && _prgRom[0x7fffb] == 0x04 // Save format + && _prgRom[0x7fffd] == 0x01; // Mapper +} + LoadRomResult WsConsole::LoadRom(VirtualFile& romFile) { vector romData; @@ -78,7 +90,7 @@ LoadRomResult WsConsole::LoadRom(VirtualFile& romFile) MessageManager::Log(string("Color supported: ") + (hasColorSupport ? "Yes" : "No")); MessageManager::Log("Save RAM size: " + std::to_string(_saveRamSize / 1024) + " KB"); MessageManager::Log("Cart EEPROM size: " + std::to_string(_cartEepromSize) + " bytes"); - MessageManager::Log(string("Mapper: ") + (mapperType == 0 ? "Bandai 2001 / KARNAK" : (mapperType == 1 ? "Bandai 2003" : ("Unknown: " + std::to_string(mapperType))))); + MessageManager::Log(string("Mapper: ") + (IsWWCart() ? "Bandai 2003 + NOR flash" : (mapperType == 0 ? "Bandai 2001 / KARNAK" : (mapperType == 1 ? "Bandai 2003" : ("Unknown: " + std::to_string(mapperType)))))); MessageManager::Log("------------------------------"); @@ -139,9 +151,9 @@ LoadRomResult WsConsole::LoadRom(VirtualFile& romFile) _dmaController.reset(new WsDmaController()); _ppu.reset(new WsPpu(_emu, this, _memoryManager.get(), _timer.get(), _workRam)); _apu.reset(new WsApu(_emu, this, _memoryManager.get(), _dmaController.get())); - _cart.reset(new WsCart()); + _cart.reset(IsWWCart() ? new WsCartFlash() : new WsCart()); - _cart->Init(_memoryManager.get(), _cartEeprom.get(), _cartRtc.get()); + _cart->Init(_memoryManager.get(), _cartEeprom.get(), _cartRtc.get(), _prgRom, _prgRomSize, _saveRam, _saveRamSize); _memoryManager->Init(_emu, this, _cpu.get(), _ppu.get(), _controlManager.get(), _cart.get(), _timer.get(), _dmaController.get(), _internalEeprom.get(), _apu.get(), _serial.get()); _timer->Init(_memoryManager.get()); _dmaController->Init(_memoryManager.get(), _apu.get()); @@ -347,6 +359,9 @@ void WsConsole::LoadBattery() _cartRtc->LoadBattery(); } + if(IsWWCart()) { + _emu->GetBatteryManager()->LoadBattery(".flash", _prgRom, _prgRomSize); + } if(_saveRam) { _emu->GetBatteryManager()->LoadBattery(".sav", _saveRam, _saveRamSize); } @@ -362,6 +377,9 @@ void WsConsole::SaveBattery() _cartRtc->SaveBattery(); } + if(IsWWCart()) { + _emu->GetBatteryManager()->SaveBattery(".flash", _prgRom, _prgRomSize); + } if(_saveRam) { _emu->GetBatteryManager()->SaveBattery(".sav", _saveRam, _saveRamSize); } diff --git a/Core/WS/WsConsole.h b/Core/WS/WsConsole.h index 17e6102ef..0a1514893 100644 --- a/Core/WS/WsConsole.h +++ b/Core/WS/WsConsole.h @@ -58,6 +58,7 @@ class WsConsole final : public IConsole bool _colorModel = false; bool _verticalMode = false; + bool IsWWCart(); void InitPostBootRomState(); public: diff --git a/Core/WS/WsMemoryManager.cpp b/Core/WS/WsMemoryManager.cpp index 23ad46cb1..8d901cf0f 100644 --- a/Core/WS/WsMemoryManager.cpp +++ b/Core/WS/WsMemoryManager.cpp @@ -54,6 +54,11 @@ void WsMemoryManager::RefreshMappings() } } +void WsMemoryManager::SetCartFlash(WsRegisterAccess cartFlash) +{ + _cartFlash = cartFlash; +} + void WsMemoryManager::Map(uint32_t start, uint32_t end, MemoryType type, uint32_t offset, bool readonly) { uint8_t* src = (uint8_t*)_emu->GetMemory(type).Memory; @@ -93,6 +98,9 @@ void WsMemoryManager::Unmap(uint32_t start, uint32_t end) uint8_t WsMemoryManager::DebugRead(uint32_t addr) { + if(((int)_cartFlash & (int)WsRegisterAccess::Read) && addr >= 0x10000) { + return _cart->ReadMemory(addr); + } uint8_t* handler = _reads[addr >> 12]; if(handler) { return handler[addr & 0xFFF]; @@ -102,6 +110,10 @@ uint8_t WsMemoryManager::DebugRead(uint32_t addr) void WsMemoryManager::DebugWrite(uint32_t addr, uint8_t value) { + if(((int)_cartFlash & (int)WsRegisterAccess::Write) && addr >= 0x10000) { + _cart->WriteMemory(addr, value); + return; + } uint8_t* handler = _writes[addr >> 12]; if(handler) { handler[addr & 0xFFF] = value; diff --git a/Core/WS/WsMemoryManager.h b/Core/WS/WsMemoryManager.h index 2ca926254..86e60591b 100644 --- a/Core/WS/WsMemoryManager.h +++ b/Core/WS/WsMemoryManager.h @@ -2,6 +2,7 @@ #include "pch.h" #include "WS/WsCpu.h" #include "WS/APU/WsApu.h" +#include "WS/Carts/WsCart.h" #include "WS/WsPpu.h" #include "WS/WsTypes.h" #include "Utilities/ISerializable.h" @@ -9,7 +10,6 @@ class WsConsole; class WsTimer; class WsControlManager; -class WsCart; class WsSerial; class WsDmaController; class WsEeprom; @@ -40,6 +40,7 @@ class WsMemoryManager final : public ISerializable WsMemoryManagerState _state = {}; + WsRegisterAccess _cartFlash; uint8_t* _reads[256] = {}; uint8_t* _writes[256] = {}; @@ -57,6 +58,7 @@ class WsMemoryManager final : public ISerializable uint8_t GetUnmappedPort(); + void SetCartFlash(WsRegisterAccess cartFlash); void Map(uint32_t start, uint32_t end, MemoryType type, uint32_t offset, bool readonly); void Unmap(uint32_t start, uint32_t end); @@ -69,10 +71,14 @@ class WsMemoryManager final : public ISerializable __forceinline uint8_t InternalRead(uint32_t addr) { - uint8_t* handler = _reads[addr >> 12]; uint8_t value = 0x90; - if(handler) { - value = handler[addr & 0xFFF]; + if(((int)_cartFlash & (int)WsRegisterAccess::Read) && addr >= 0x10000) { + value = _cart->ReadMemory(addr); + } else { + uint8_t* handler = _reads[addr >> 12]; + if(handler) { + value = handler[addr & 0xFFF]; + } } //TODOWS open bus @@ -82,9 +88,13 @@ class WsMemoryManager final : public ISerializable __forceinline void InternalWrite(uint32_t addr, uint8_t value) { //TODOWS open bus - uint8_t* handler = _writes[addr >> 12]; - if(handler) { - handler[addr & 0xFFF] = value; + if(((int)_cartFlash & (int)WsRegisterAccess::Write) && addr >= 0x10000) { + _cart->WriteMemory(addr, value); + } else { + uint8_t* handler = _writes[addr >> 12]; + if(handler) { + handler[addr & 0xFFF] = value; + } } } diff --git a/Core/WS/WsTypes.h b/Core/WS/WsTypes.h index 367d22327..030f570ca 100644 --- a/Core/WS/WsTypes.h +++ b/Core/WS/WsTypes.h @@ -221,6 +221,14 @@ enum class WsIrqSource : uint8_t HorizontalBlankTimer = 0x80 }; +enum class WsRegisterAccess +{ + None = 0, + Read = 1, + Write = 2, + ReadWrite = 3 +}; + struct WsMemoryManagerState { uint8_t ActiveIrqs; @@ -446,7 +454,8 @@ struct WsEepromState struct WsCartState { bool HasRtc; - uint8_t SelectedBanks[4]; + bool RomInRamBank; + uint16_t SelectedBanks[4]; }; struct WsRtcState diff --git a/UI/Debugger/RegisterViewer/WsRegisterViewer.cs b/UI/Debugger/RegisterViewer/WsRegisterViewer.cs index 1a15a674b..4966f269d 100644 --- a/UI/Debugger/RegisterViewer/WsRegisterViewer.cs +++ b/UI/Debugger/RegisterViewer/WsRegisterViewer.cs @@ -366,9 +366,13 @@ private static RegisterViewerTab GetCartTab(ref WsState ws) entries.AddRange(new List() { new RegEntry("$C0", "ROM Linear Bank", cart.SelectedBanks[0], Format.X8), - new RegEntry("$C1", "RAM Bank", cart.SelectedBanks[1], Format.X8), - new RegEntry("$C2", "ROM0 Bank", cart.SelectedBanks[2], Format.X8), - new RegEntry("$C3", "ROM1 Bank", cart.SelectedBanks[3], Format.X8), + new RegEntry("$C1", "RAM Bank", cart.SelectedBanks[1] & 0xFF, Format.X8), + new RegEntry("$C2", "ROM0 Bank", cart.SelectedBanks[2] & 0xFF, Format.X8), + new RegEntry("$C3", "ROM1 Bank", cart.SelectedBanks[3] & 0xFF, Format.X8), + new RegEntry("$CE.0", "ROM in RAM Bank", cart.RomInRamBank), + new RegEntry("$D0", "Expanded RAM Bank", cart.SelectedBanks[1], Format.X16), + new RegEntry("$D2", "Expanded ROM0 Bank", cart.SelectedBanks[2], Format.X16), + new RegEntry("$D4", "Expanded ROM1 Bank", cart.SelectedBanks[3], Format.X16), }); if(ws.CartEeprom.Size != WsEepromSize.Size0) { diff --git a/UI/Interop/ConsoleState/WsState.cs b/UI/Interop/ConsoleState/WsState.cs index 7df108e42..ead3ec765 100644 --- a/UI/Interop/ConsoleState/WsState.cs +++ b/UI/Interop/ConsoleState/WsState.cs @@ -443,8 +443,9 @@ public struct WsEepromState public struct WsCartState { [MarshalAs(UnmanagedType.I1)] public bool HasRtc; + [MarshalAs(UnmanagedType.I1)] public bool RomInRamBank; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public byte[] SelectedBanks; + public UInt16[] SelectedBanks; } public struct WsRtcState