mirror of https://github.com/bsnes-emu/bsnes.git
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.
This commit is contained in:
parent
c2d0ed4ca8
commit
c58169945c
|
@ -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 {
|
||||
|
|
|
@ -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<uint1> {
|
||||
if(count < 1) return {};
|
||||
return {value >> count - 1 & 1};
|
||||
}
|
||||
|
||||
auto Cartridge::MBC7::EEPROM::InputShiftRegister::opcode() -> maybe<uint2> {
|
||||
if(count < 1 + 2) return {};
|
||||
return {value >> count - 3 & 3};
|
||||
}
|
||||
|
||||
auto Cartridge::MBC7::EEPROM::InputShiftRegister::mode() -> maybe<uint2> {
|
||||
if(count < 1 + 2 + addressLength) return {};
|
||||
return {value >> count - 5 & 3};
|
||||
}
|
||||
|
||||
auto Cartridge::MBC7::EEPROM::InputShiftRegister::address() -> maybe<uint9> {
|
||||
if(count < 1 + 2 + addressLength) return {};
|
||||
return {value >> count - (3 + addressLength) & (1 << addressLength) - 1};
|
||||
}
|
||||
|
||||
auto Cartridge::MBC7::EEPROM::InputShiftRegister::data() -> maybe<uint16> {
|
||||
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)++;
|
||||
}
|
||||
|
|
|
@ -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<uint1>;
|
||||
auto opcode() -> maybe<uint2>;
|
||||
auto mode() -> maybe<uint2>;
|
||||
auto address() -> maybe<uint9>;
|
||||
auto data() -> maybe<uint16>;
|
||||
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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ using uint32 = Natural<32>;
|
|||
using uint64 = Natural<64>;
|
||||
|
||||
struct FX {
|
||||
auto open(vector<string>& arguments) -> bool;
|
||||
auto open(vector<string> arguments) -> bool;
|
||||
auto close() -> void;
|
||||
auto readable() -> bool;
|
||||
auto read() -> uint8_t;
|
||||
|
@ -35,12 +35,12 @@ struct FX {
|
|||
serial device;
|
||||
};
|
||||
|
||||
auto FX::open(vector<string>& arguments) -> bool {
|
||||
auto FX::open(vector<string> 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<string>& 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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue