From c58169945cbc6a5e5472d2bf31e31376d6c8e7b9 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Tue, 11 Sep 2018 21:16:58 +1000 Subject: [PATCH] Update to v106r63 release. byuu says: Changelog: - gb/mbc7: rewrote the 93LCx6 EEPROM emulation - sfc/slot/bsmemory: rewrote the flash emulation for Satellaview cartridges As of this release, flash-based BS Memory cartridges will be writable. So without the bsnes patch to disable write limits, some games will lock out after a few plays. --- higan/emulator/emulator.hpp | 4 +- higan/gb/cartridge/mbc7/eeprom.cpp | 241 +++++++++++--------- higan/gb/cartridge/mbc7/mbc7.hpp | 43 ++-- higan/gb/cartridge/mbc7/serialization.cpp | 12 +- higan/sfc/cartridge/load.cpp | 2 +- higan/sfc/coprocessor/mcc/mcc.cpp | 24 +- higan/sfc/coprocessor/mcc/mcc.hpp | 5 +- higan/sfc/coprocessor/mcc/serialization.cpp | 8 +- higan/sfc/slot/bsmemory/bsmemory.cpp | 132 +++++------ higan/sfc/slot/bsmemory/bsmemory.hpp | 32 ++- higan/sfc/slot/bsmemory/serialization.cpp | 5 +- nall/emulation/21fx.hpp | 12 +- 12 files changed, 272 insertions(+), 248 deletions(-) diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 6f416c38..089cf3b1 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -28,13 +28,13 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "106.62"; + static const string Version = "106.63"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "https://byuu.org/"; //incremented only when serialization format changes - static const string SerializerVersion = "106.44"; + static const string SerializerVersion = "106.63"; namespace Constants { namespace Colorburst { diff --git a/higan/gb/cartridge/mbc7/eeprom.cpp b/higan/gb/cartridge/mbc7/eeprom.cpp index cd2564a8..653552d9 100644 --- a/higan/gb/cartridge/mbc7/eeprom.cpp +++ b/higan/gb/cartridge/mbc7/eeprom.cpp @@ -5,31 +5,30 @@ auto Cartridge::MBC7::EEPROM::load(Markup::Node document) -> void { for(auto& byte : data) byte = 0xff; - size = 4096; //EEPROM size is in bits - width = 16; //16-bit configuration + size = 512; //EEPROM size is in bytes + width = 16; //16-bit configuration if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=EEPROM,content=Save)"]}) { - if(memory.size == 128) size = 1024; //manifest size is in bytes - if(memory.size == 256) size = 2048; - if(memory.size == 512) size = 4096; + if(memory.size == 128) size = 128; + if(memory.size == 256) size = 256; + if(memory.size == 512) size = 512; if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Read, File::Optional)) { fp->read(data, min(fp->size(), sizeof(data))); } } - command.length = 3; - if(size == 1024) address.length = width == 8 ? 6 : 7; - if(size == 2048) address.length = width == 8 ? 7 : 8; - if(size == 4096) address.length = width == 8 ? 8 : 9; - input.length = width; - output.length = 1 + width; //there is an extra zero dummy bit on reads + //note: the 93LC56 alone has an extra dummy address bit + if(size == 128) input.addressLength = width == 16 ? 6 : 7; //93LC46 + if(size == 256) input.addressLength = width == 16 ? 8 : 9; //93LC56 + if(size == 512) input.addressLength = width == 16 ? 8 : 9; //93LC66 + input.dataLength = width; } auto Cartridge::MBC7::EEPROM::save(Markup::Node document) -> void { if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=EEPROM,content=Save)"]}) { if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Write)) { - fp->write(data, size >> 3); //bytes -> bits + fp->write(data, size); } } } @@ -42,17 +41,12 @@ auto Cartridge::MBC7::EEPROM::main() -> void { if(busy) busy--; } -auto Cartridge::MBC7::EEPROM::power(bool reset) -> void { - if(!reset) { - select = 0; - writable = 0; - } - +auto Cartridge::MBC7::EEPROM::power() -> void { + select = 0; clock = 0; + writable = 0; busy = 0; - command.flush(); - address.flush(); input.flush(); output.flush(); } @@ -61,13 +55,13 @@ auto Cartridge::MBC7::EEPROM::readIO() -> uint8 { uint8 data = 0b00'1111'00; data.bit(7) = select; data.bit(6) = clock; - data.bit(1) = 1; + data.bit(1) = input.edge(); if(!select) { data.bit(0) = 1; //high-z when the chip is idle (not selected) } else if(busy) { data.bit(0) = 0; //low when a programming command is in progress } else if(output.count) { - data.bit(0) = output.peek(); //shift register data during read commands + data.bit(0) = output.edge(); //shift register data during read commands } else { data.bit(0) = 1; //high-z during all other commands } @@ -75,129 +69,118 @@ auto Cartridge::MBC7::EEPROM::readIO() -> uint8 { } auto Cartridge::MBC7::EEPROM::writeIO(uint8 data) -> void { - //bring chip out of idle state on rising CS edge - if(select.raise(data.bit(7))) return power(true); + //chip enters idle state on falling CS edge + if(select && !data.bit(7)) return power(); - //do nothing if chip is idle - if(!select) return; + //chip leaves idle state on rising CS edge + if(!(select = data.bit(7))) return; - //shift register clocks on rising edge + //input shift register clocks on rising edge if(!clock.raise(data.bit(6))) return; - //sequential read mode + //read mode if(output.count && !data.bit(1)) { - output.read(); - if(output.count == 0) { - address.value++; - read(); + if(input.start() && *input.start() == 1) { + if(input.opcode() && *input.opcode() == 0b10) { + output.read(); + if(output.count == 0) { + //sequential read mode + input.increment(); + read(); + } + } } return; } output.flush(); - //wait for start bit to be set - if(command.count == 0 && !data.bit(1)) return; + input.write(data.bit(1)); - //waiting on command? - if(command.count < command.length) { - command.write(data.bit(1)); - if(command.count < command.length) return; + //wait for start + if(!input.start()) return; + uint start = *input.start(); - return address.flush(); - } - - //waiting on address bits? - if(address.count < address.length) { - address.write(data.bit(1)); - if(address.count < address.length) return; - - uint3 opcode = command.bits(0, command.length - 1); - if(opcode == 0b100) { - uint2 mode = address.bits(address.length - 2, address.length - 1); - if(mode == 0b00) return writeDisable(); - if(mode == 0b01) return input.flush(); //writeAll - if(mode == 0b10) return eraseAll(); - if(mode == 0b11) return writeEnable(); - } - if(opcode == 0b101) return input.flush(); //write - if(opcode == 0b110) return read(); - if(opcode == 0b111) return erase(); - return; - } - - //waiting on data bits from a write or writeAll command? - if(input.count < input.length) { //block new commands and inputs until the next clock edge - input.write(data.bit(1)); - if(input.count < input.length) return; - - uint3 opcode = command.bits(0, command.length - 1); - if(opcode == 0b101) return write(); - if(opcode == 0b100) return writeAll(); - return; + //start bit must be set + if(start != 1) return input.flush(); + + //wait for opcode + if(!input.opcode()) return; + uint opcode = *input.opcode(); + + //wait for address + if(!input.address()) return; + + if(opcode == 0b00) { + auto mode = *input.mode(); + if(mode == 0b00) return writeDisable(); + if(mode == 0b01) return writeAll(); + if(mode == 0b10) return eraseAll(); + if(mode == 0b11) return writeEnable(); } + if(opcode == 0b01) return write(); + if(opcode == 0b10) return read(); + if(opcode == 0b11) return erase(); } // auto Cartridge::MBC7::EEPROM::read() -> void { - command.flush(); - auto address = this->address.value << (width == 16) & (size >> 3) - 1; - output.value = 0; - if(width >= 8) output.value |= data[address++] << 8; - if(width >= 16) output.value |= data[address++] << 0; - output.count = output.length; + uint address = *input.address() << (width == 16) & size - 1; + output.flush(); + for(uint4 index : range(width)) { + output.write(data[address + !index.bit(3)].bit(index.bits(0,2))); + } + output.write(0); //reads have an extra dummy data bit } auto Cartridge::MBC7::EEPROM::write() -> void { - command.flush(); - if(!writable) return; - auto address = this->address.value << (width == 16) & (size >> 3) - 1; - if(width >= 8) data[address++] = input.value >> 8; - if(width >= 16) data[address++] = input.value >> 0; - input.flush(); - busy = 4; //ms + if(!input.data()) return; //wait for data + if(!writable) return input.flush(); + uint address = *input.address() << (width == 16) & size - 1; + for(uint4 index : range(width)) { + data[address + !index.bit(3)].bit(index.bits(0,2)) = input.read(); + } + busy = 4; //milliseconds + return input.flush(); } auto Cartridge::MBC7::EEPROM::erase() -> void { - command.flush(); - if(!writable) return; - auto address = this->address.value << (width == 16) & (size >> 3) - 1; - if(width >= 8) data[address++] = 0xff; - if(width >= 16) data[address++] = 0xff; - busy = 4; //ms + if(!writable) return input.flush(); + uint address = *input.address() << (width == 16) & size - 1; + for(uint index : range(width)) { + data[address + index / 8].bit(index % 8) = 1; + } + busy = 4; //milliseconds + return input.flush(); } auto Cartridge::MBC7::EEPROM::writeAll() -> void { - command.flush(); - if(!writable) return; - uint8 lo = input.byte(0); - uint8 hi = input.byte(width == 16); + if(!input.data()) return; //wait for data + if(!writable) return input.flush(); + auto word = *input.data(); for(uint address = 0; address < 512;) { - data[address++] = hi; - data[address++] = lo; + data[address++] = word.byte(width == 16); + data[address++] = word.byte(0); } - input.flush(); - busy = 16; //ms + busy = 16; //milliseconds + return input.flush(); } auto Cartridge::MBC7::EEPROM::eraseAll() -> void { - command.flush(); - if(!writable) return; - for(uint address; address < 512;) { - data[address++] = 0xff; - data[address++] = 0xff; - } - busy = 8; //ms + if(!writable) return input.flush(); + for(auto& byte : data) byte = 0xff; + busy = 8; //milliseconds + return input.flush(); } auto Cartridge::MBC7::EEPROM::writeEnable() -> void { - command.flush(); writable = true; + return input.flush(); } auto Cartridge::MBC7::EEPROM::writeDisable() -> void { - command.flush(); writable = false; + return input.flush(); } // @@ -207,20 +190,50 @@ auto Cartridge::MBC7::EEPROM::ShiftRegister::flush() -> void { count = 0; } -//read the current bit in the shift register without clocking it -auto Cartridge::MBC7::EEPROM::ShiftRegister::peek() -> bool { - return value.bit(length - 1); +auto Cartridge::MBC7::EEPROM::ShiftRegister::edge() -> uint1 { + return value.bit(0); } -auto Cartridge::MBC7::EEPROM::ShiftRegister::read() -> bool { - bool bit = value.bit(length - 1); - value <<= 1; - if(count) count--; +auto Cartridge::MBC7::EEPROM::ShiftRegister::read() -> uint1 { + uint1 bit = value.bit(0); + value >>= 1; + count--; return bit; } -auto Cartridge::MBC7::EEPROM::ShiftRegister::write(bool bit) -> void { +auto Cartridge::MBC7::EEPROM::ShiftRegister::write(uint1 bit) -> void { value <<= 1; - value |= bit; + value.bit(0) = bit; count++; } + +// + +auto Cartridge::MBC7::EEPROM::InputShiftRegister::start() -> maybe { + if(count < 1) return {}; + return {value >> count - 1 & 1}; +} + +auto Cartridge::MBC7::EEPROM::InputShiftRegister::opcode() -> maybe { + if(count < 1 + 2) return {}; + return {value >> count - 3 & 3}; +} + +auto Cartridge::MBC7::EEPROM::InputShiftRegister::mode() -> maybe { + if(count < 1 + 2 + addressLength) return {}; + return {value >> count - 5 & 3}; +} + +auto Cartridge::MBC7::EEPROM::InputShiftRegister::address() -> maybe { + if(count < 1 + 2 + addressLength) return {}; + return {value >> count - (3 + addressLength) & (1 << addressLength) - 1}; +} + +auto Cartridge::MBC7::EEPROM::InputShiftRegister::data() -> maybe { + if(count < 1 + 2 + addressLength + dataLength) return {}; + return {value >> count - (3 + addressLength + dataLength) & (1 << dataLength) - 1}; +} + +auto Cartridge::MBC7::EEPROM::InputShiftRegister::increment() -> void { + value.bits(0, addressLength - 1)++; +} diff --git a/higan/gb/cartridge/mbc7/mbc7.hpp b/higan/gb/cartridge/mbc7/mbc7.hpp index f9f3db38..9bb731c5 100644 --- a/higan/gb/cartridge/mbc7/mbc7.hpp +++ b/higan/gb/cartridge/mbc7/mbc7.hpp @@ -1,5 +1,5 @@ struct MBC7 : Mapper { - enum : uint { Center = 0x81d0 }; + enum : uint { Center = 0x81d0 }; //not 0x8000 //mbc7.cpp auto load(Markup::Node document) -> void override; @@ -17,7 +17,7 @@ struct MBC7 : Mapper { auto load(Markup::Node document) -> void; auto save(Markup::Node document) -> void; auto main() -> void; - auto power(bool reset = false) -> void; + auto power() -> void; //Game Boy MBC7 interface auto readIO() -> uint8; @@ -36,9 +36,9 @@ struct MBC7 : Mapper { auto serialize(serializer&) -> void; //it is awkward no matter if data is uint1[4096], uint8[512], or uint16[256] - uint8 data[512]; //uint8 was chosen solely for easier serialization and saving - uint size; //in bits; not bytes - uint width; //8-bit (ORG=0) or 16-bit (ORG=1) configuration + uint8 data[512]; //uint8 was chosen solely for easier serialization and saving + uint size; //in bytes + uint width; //8-bit (ORG=0) or 16-bit (ORG=1) configuration boolean select; //CS boolean clock; //CLK @@ -46,21 +46,34 @@ struct MBC7 : Mapper { uint busy; //busy cycles in milliseconds remaining for programming (write) operations to complete struct ShiftRegister { - auto bit(uint index) { return value.bit(index); } - auto bits(uint lo, uint hi) { return value.bits(lo, hi); } - auto byte(uint index) { return value.byte(index); } auto flush() -> void; - auto peek() -> bool; - auto read() -> bool; - auto write(bool data) -> void; + auto edge() -> uint1; + auto read() -> uint1; + auto write(uint1 data) -> void; + + uint32 value; + uint32 count; + }; + + struct InputShiftRegister : ShiftRegister { + auto start() -> maybe; + auto opcode() -> maybe; + auto mode() -> maybe; + auto address() -> maybe; + auto data() -> maybe; + auto increment() -> void; //serialization.cpp auto serialize(serializer&) -> void; - uint32 value; - uint32 count; - uint32 length; - } command, address, input, output; + uint32 addressLength; + uint32 dataLength; + } input; + + struct OutputShiftRegister : ShiftRegister { + //serialization.cpp + auto serialize(serializer&) -> void; + } output; } eeprom; struct IO { diff --git a/higan/gb/cartridge/mbc7/serialization.cpp b/higan/gb/cartridge/mbc7/serialization.cpp index 11a03673..996acee1 100644 --- a/higan/gb/cartridge/mbc7/serialization.cpp +++ b/higan/gb/cartridge/mbc7/serialization.cpp @@ -15,14 +15,18 @@ auto Cartridge::MBC7::EEPROM::serialize(serializer& s) -> void { s.boolean(clock); s.boolean(writable); s.integer(busy); - command.serialize(s); - address.serialize(s); input.serialize(s); output.serialize(s); } -auto Cartridge::MBC7::EEPROM::ShiftRegister::serialize(serializer& s) -> void { +auto Cartridge::MBC7::EEPROM::InputShiftRegister::serialize(serializer& s) -> void { + s.integer(value); + s.integer(count); + s.integer(addressLength); + s.integer(dataLength); +} + +auto Cartridge::MBC7::EEPROM::OutputShiftRegister::serialize(serializer& s) -> void { s.integer(value); s.integer(count); - s.integer(length); } diff --git a/higan/sfc/cartridge/load.cpp b/higan/sfc/cartridge/load.cpp index 056a8da9..ca8932d1 100644 --- a/higan/sfc/cartridge/load.cpp +++ b/higan/sfc/cartridge/load.cpp @@ -77,7 +77,7 @@ auto Cartridge::loadCartridgeGameBoy(Markup::Node node) -> void { auto Cartridge::loadCartridgeBSMemory(Markup::Node node) -> void { if(auto memory = Emulator::Game::Memory{node["game/board/memory(content=Program)"]}) { - bsmemory.readonly = memory.type == "ROM"; + bsmemory.ROM = memory.type == "ROM"; bsmemory.memory.allocate(memory.size); if(auto fp = platform->open(bsmemory.pathID, memory.name(), File::Read, File::Required)) { fp->read(bsmemory.memory.data(), memory.size); diff --git a/higan/sfc/coprocessor/mcc/mcc.cpp b/higan/sfc/coprocessor/mcc/mcc.cpp index 0858094e..3fd27651 100644 --- a/higan/sfc/coprocessor/mcc/mcc.cpp +++ b/higan/sfc/coprocessor/mcc/mcc.cpp @@ -22,11 +22,17 @@ auto MCC::power() -> void { w.exEnableLo = 1; w.exEnableHi = 0; w.exMapping = 1; - w.bsWritable = 0; - w.unknown = 0; + w.bsQueryable = 0; + w.bsFlashable = 0; x.enable = 0; - x.value = 0b0011'1111; - memory::copy(&r, &w, sizeof(Registers)); + x.value = 0b00111111; + commit(); +} + +auto MCC::commit() -> void { + r = w; //memory::copy(&r, &w, sizeof(Registers)); + bsmemory.queryable(r.bsQueryable); + bsmemory.flashable(r.bsFlashable); } auto MCC::read(uint24 address, uint8 data) -> uint8 { @@ -46,8 +52,8 @@ auto MCC::read(uint24 address, uint8 data) -> uint8 { case 9: return r.exEnableLo << 7; case 10: return r.exEnableHi << 7; case 11: return r.exMapping << 7; - case 12: return r.bsWritable << 7; - case 13: return r.unknown << 7; + case 12: return r.bsQueryable << 7; + case 13: return r.bsFlashable << 7; case 14: return 0; //commit (always zero) case 15: return 0; //x.enable (always zero) } @@ -72,9 +78,9 @@ auto MCC::write(uint24 address, uint8 data) -> void { case 9: w.exEnableLo = data.bit(7); break; case 10: w.exEnableHi = data.bit(7); break; case 11: w.exMapping = data.bit(7); break; - case 12: w.bsWritable = data.bit(7); break; - case 13: w.unknown = data.bit(7); break; - case 14: if(data.bit(7)) memory::copy(&r, &w, sizeof(Registers)); break; + case 12: w.bsQueryable = data.bit(7); break; + case 13: w.bsFlashable = data.bit(7); break; + case 14: if(data.bit(7)) commit(); break; case 15: x.enable = data.bit(7); break; } } diff --git a/higan/sfc/coprocessor/mcc/mcc.hpp b/higan/sfc/coprocessor/mcc/mcc.hpp index ec4b9725..ca22e8f1 100644 --- a/higan/sfc/coprocessor/mcc/mcc.hpp +++ b/higan/sfc/coprocessor/mcc/mcc.hpp @@ -8,6 +8,7 @@ struct MCC { //mcc.cpp auto unload() -> void; auto power() -> void; + auto commit() -> void; auto read(uint24 address, uint8 data) -> uint8; auto write(uint24 address, uint8 data) -> void; @@ -40,8 +41,8 @@ private: uint1 exEnableLo; //bit 9 uint1 exEnableHi; //bit 10 uint1 exMapping; //bit 11 - uint1 bsWritable; //bit 12 - uint1 unknown; //bit 13 + uint1 bsQueryable; //bit 12 + uint1 bsFlashable; //bit 13 } r, w; //bit 14 (commit) diff --git a/higan/sfc/coprocessor/mcc/serialization.cpp b/higan/sfc/coprocessor/mcc/serialization.cpp index 117738cf..4b40bf84 100644 --- a/higan/sfc/coprocessor/mcc/serialization.cpp +++ b/higan/sfc/coprocessor/mcc/serialization.cpp @@ -11,8 +11,8 @@ auto MCC::serialize(serializer& s) -> void { s.integer(r.exEnableLo); s.integer(r.exEnableHi); s.integer(r.exMapping); - s.integer(r.bsWritable); - s.integer(r.unknown); + s.integer(r.bsQueryable); + s.integer(r.bsFlashable); s.integer(w.mapping); s.integer(w.psramEnableLo); s.integer(w.psramEnableHi); @@ -22,8 +22,8 @@ auto MCC::serialize(serializer& s) -> void { s.integer(w.exEnableLo); s.integer(w.exEnableHi); s.integer(w.exMapping); - s.integer(w.bsWritable); - s.integer(w.unknown); + s.integer(w.bsQueryable); + s.integer(w.bsFlashable); s.integer(x.enable); s.integer(x.value); } diff --git a/higan/sfc/slot/bsmemory/bsmemory.cpp b/higan/sfc/slot/bsmemory/bsmemory.cpp index 278da8cc..347bd040 100644 --- a/higan/sfc/slot/bsmemory/bsmemory.cpp +++ b/higan/sfc/slot/bsmemory/bsmemory.cpp @@ -6,7 +6,8 @@ namespace SuperFamicom { BSMemory bsmemory; auto BSMemory::load() -> void { - if(!memory.size()) memory.allocate(1024 * 1024); + queryable(true); + flashable(true); } auto BSMemory::unload() -> void { @@ -14,14 +15,8 @@ auto BSMemory::unload() -> void { } auto BSMemory::power() -> void { - regs.command = 0; - regs.writeOld = 0x00; - regs.writeNew = 0x00; - - regs.flashEnable = false; - regs.readEnable = false; - regs.writeEnable = false; - memory.writable(regs.writeEnable); + memory.writable(false); + io = {}; } auto BSMemory::data() -> uint8* { @@ -32,92 +27,71 @@ auto BSMemory::size() const -> uint { return memory.size(); } -auto BSMemory::read(uint24 addr, uint8 data) -> uint8 { - if(readonly) { - return memory.read(bus.mirror(addr, memory.size()), data); +auto BSMemory::read(uint24 address, uint8 data) -> uint8 { + if(!size()) return data; + address = bus.mirror(address, size()); + if(!pin.queryable) return memory.read(address, data); + + if(io.mode == 0x70) { + return 0x80; } - if(addr == 0x0002) { - if(regs.flashEnable) return 0x80; + if(io.mode == 0x71) { + if((uint16)address == 0x0002) return 0x80; + if((uint16)address == 0x0004) return 0x87; + if((uint16)address == 0x0006) return 0x00; //unknown purpose (not always zero) + return 0x00; } - if(addr == 0x5555) { - if(regs.flashEnable) return 0x80; + if(io.mode == 0x75) { + if((uint8)address == 0x00) return 0x4d; //'M' (memory) + if((uint8)address == 0x02) return 0x50; //'P' (pack) + if((uint8)address == 0x04) return 0x04; //unknown purpose + if((uint8)address == 0x06) return Type << 4 | (uint4)log2(size() >> 10); + return random(); //not actually random, but not ROM data either, yet varies per cartridge } - if(regs.readEnable && addr >= 0xff00 && addr <= 0xff13) { - //read flash cartridge vendor information - switch(addr - 0xff00) { - case 0x00: return 0x4d; - case 0x01: return 0x00; - case 0x02: return 0x50; - case 0x03: return 0x00; - case 0x04: return 0x00; - case 0x05: return 0x00; - case 0x06: return 0x2a; //0x2a = 8mbit, 0x2b = 16mbit (not known to exist, though BIOS recognizes ID) - case 0x07: return 0x00; - default: return 0x00; - } - } - - return memory.read(bus.mirror(addr, memory.size()), data); + return memory.read(address, data); } -auto BSMemory::write(uint24 addr, uint8 data) -> void { - if(readonly) { +auto BSMemory::write(uint24 address, uint8 data) -> void { + if(!size() || !pin.queryable) return; + address = bus.mirror(address, size()); + + //write byte + if(io.mode == 0x10 || io.mode == 0x40) { + if(!pin.flashable) return; + memory.writable(true); + memory.write(address, memory.read(address) & data); //writes can only clear bits + memory.writable(false); + io.mode = 0x70; return; } - if((addr & 0xff0000) == 0) { - regs.writeOld = regs.writeNew; - regs.writeNew = data; - - if(regs.writeEnable && regs.writeOld == regs.writeNew) { - return memory.write(addr, data); - } - } else { - if(regs.writeEnable) { - return memory.write(addr, data); - } + //erase 64KB page + if(io.mode == 0x20) { + //completes even if !pin.flashable + memory.writable(true); + for(uint offset : range(1 << 16)) memory.write(address & 0xff0000 | offset, 0xff); + memory.writable(false); + io.mode = 0x70; + return; } - if(addr == 0x0000) { - regs.command <<= 8; - regs.command |= data; + //erase all pages + if(io.mode == 0xa7) { + //completes even if !pin.flashable + if(Type == 3) return; //Type 3 doesn't support this command - if((regs.command & 0xffff) == 0x38d0) { - regs.flashEnable = true; - regs.readEnable = true; - } + memory.writable(true); + for(uint offset : range(size())) memory.write(offset, 0xff); + memory.writable(false); + io.mode = 0x70; + return; } - if(addr == 0x2aaa) { - regs.command <<= 8; - regs.command |= data; - } - - if(addr == 0x5555) { - regs.command <<= 8; - regs.command |= data; - - if((regs.command & 0xffffff) == 0xaa5570) { - regs.writeEnable = false; - } - - if((regs.command & 0xffffff) == 0xaa55a0) { - regs.writeOld = 0x00; - regs.writeNew = 0x00; - regs.flashEnable = true; - regs.writeEnable = true; - } - - if((regs.command & 0xffffff) == 0xaa55f0) { - regs.flashEnable = false; - regs.readEnable = false; - regs.writeEnable = false; - } - - memory.writable(regs.writeEnable); + if((uint16)address == 0x0000) { + io.mode = data; } } diff --git a/higan/sfc/slot/bsmemory/bsmemory.hpp b/higan/sfc/slot/bsmemory/bsmemory.hpp index 3024c56a..795321bc 100644 --- a/higan/sfc/slot/bsmemory/bsmemory.hpp +++ b/higan/sfc/slot/bsmemory/bsmemory.hpp @@ -1,4 +1,7 @@ struct BSMemory : Memory { + auto queryable(bool queryable) { pin.queryable = !ROM && queryable; } + auto flashable(bool flashable) { pin.flashable = !ROM && flashable; } + //bsmemory.cpp auto load() -> void; auto unload() -> void; @@ -6,26 +9,33 @@ struct BSMemory : Memory { auto data() -> uint8* override; auto size() const -> uint override; - auto read(uint24 addr, uint8 data) -> uint8 override; - auto write(uint24 addr, uint8 data) -> void override; + auto read(uint24 address, uint8 data) -> uint8 override; + auto write(uint24 address, uint8 data) -> void override; //serialization.cpp auto serialize(serializer&) -> void; uint pathID = 0; + + //0 = Flash; 1 = MaskROM + uint ROM = 1; + + //valid types are 1,2,3,4 + //type 2 is currently unsupported + //type 1 is the only type to exist (all flash-based BS Memory Cassettes are type 1) + uint Type = 1; + ProtectableMemory memory; - bool readonly; private: - struct { - uint command; - uint8 writeOld; - uint8 writeNew; + struct Pin { + uint1 queryable; + uint1 flashable; + } pin; - bool flashEnable; - bool readEnable; - bool writeEnable; - } regs; + struct IO { + uint8 mode; + } io; }; extern BSMemory bsmemory; diff --git a/higan/sfc/slot/bsmemory/serialization.cpp b/higan/sfc/slot/bsmemory/serialization.cpp index 20128c04..fa306f15 100644 --- a/higan/sfc/slot/bsmemory/serialization.cpp +++ b/higan/sfc/slot/bsmemory/serialization.cpp @@ -1,3 +1,6 @@ auto BSMemory::serialize(serializer& s) -> void { - if(!readonly) s.array(memory.data(), memory.size()); + if(!ROM) s.array(memory.data(), memory.size()); + s.integer(pin.queryable); + s.integer(pin.flashable); + s.integer(io.mode); } diff --git a/nall/emulation/21fx.hpp b/nall/emulation/21fx.hpp index 1cb18e3f..29248553 100644 --- a/nall/emulation/21fx.hpp +++ b/nall/emulation/21fx.hpp @@ -17,7 +17,7 @@ using uint32 = Natural<32>; using uint64 = Natural<64>; struct FX { - auto open(vector& arguments) -> bool; + auto open(vector arguments) -> bool; auto close() -> void; auto readable() -> bool; auto read() -> uint8_t; @@ -35,12 +35,12 @@ struct FX { serial device; }; -auto FX::open(vector& arguments) -> bool { +auto FX::open(vector arguments) -> bool { //device name override support string name; - for(uint n : range(arguments)) { - if(arguments[n].beginsWith("--device=")) { - name = arguments.take(n).trimLeft("--device=", 1L); + for(auto argument : arguments) { + if(argument.beginsWith("--device=")) { + name = argument.trimLeft("--device=", 1L); break; } } @@ -54,7 +54,7 @@ auto FX::open(vector& arguments) -> bool { while(true) { while(readable()) read(); auto iplrom = read(0x2184, 122); - auto sha256 = Hash::SHA256(iplrom.data(), iplrom.size()).digest(); + auto sha256 = Hash::SHA256(iplrom).digest(); if(sha256 == "41b79712a4a2d16d39894ae1b38cde5c41dad22eadc560df631d39f13df1e4b9") break; }