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:
Tim Allen 2018-09-11 21:16:58 +10:00
parent c2d0ed4ca8
commit c58169945c
12 changed files with 272 additions and 248 deletions

View File

@ -28,13 +28,13 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; 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 Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "https://byuu.org/"; static const string Website = "https://byuu.org/";
//incremented only when serialization format changes //incremented only when serialization format changes
static const string SerializerVersion = "106.44"; static const string SerializerVersion = "106.63";
namespace Constants { namespace Constants {
namespace Colorburst { namespace Colorburst {

View File

@ -5,31 +5,30 @@
auto Cartridge::MBC7::EEPROM::load(Markup::Node document) -> void { auto Cartridge::MBC7::EEPROM::load(Markup::Node document) -> void {
for(auto& byte : data) byte = 0xff; for(auto& byte : data) byte = 0xff;
size = 4096; //EEPROM size is in bits size = 512; //EEPROM size is in bytes
width = 16; //16-bit configuration width = 16; //16-bit configuration
if(auto memory = Emulator::Game::Memory{document["game/board/memory(type=EEPROM,content=Save)"]}) { 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 == 128) size = 128;
if(memory.size == 256) size = 2048; if(memory.size == 256) size = 256;
if(memory.size == 512) size = 4096; if(memory.size == 512) size = 512;
if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Read, File::Optional)) { if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Read, File::Optional)) {
fp->read(data, min(fp->size(), sizeof(data))); fp->read(data, min(fp->size(), sizeof(data)));
} }
} }
command.length = 3; //note: the 93LC56 alone has an extra dummy address bit
if(size == 1024) address.length = width == 8 ? 6 : 7; if(size == 128) input.addressLength = width == 16 ? 6 : 7; //93LC46
if(size == 2048) address.length = width == 8 ? 7 : 8; if(size == 256) input.addressLength = width == 16 ? 8 : 9; //93LC56
if(size == 4096) address.length = width == 8 ? 8 : 9; if(size == 512) input.addressLength = width == 16 ? 8 : 9; //93LC66
input.length = width; input.dataLength = width;
output.length = 1 + width; //there is an extra zero dummy bit on reads
} }
auto Cartridge::MBC7::EEPROM::save(Markup::Node document) -> void { 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 memory = Emulator::Game::Memory{document["game/board/memory(type=EEPROM,content=Save)"]}) {
if(auto fp = platform->open(cartridge.pathID(), memory.name(), File::Write)) { 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--; if(busy) busy--;
} }
auto Cartridge::MBC7::EEPROM::power(bool reset) -> void { auto Cartridge::MBC7::EEPROM::power() -> void {
if(!reset) {
select = 0; select = 0;
writable = 0;
}
clock = 0; clock = 0;
writable = 0;
busy = 0; busy = 0;
command.flush();
address.flush();
input.flush(); input.flush();
output.flush(); output.flush();
} }
@ -61,13 +55,13 @@ auto Cartridge::MBC7::EEPROM::readIO() -> uint8 {
uint8 data = 0b00'1111'00; uint8 data = 0b00'1111'00;
data.bit(7) = select; data.bit(7) = select;
data.bit(6) = clock; data.bit(6) = clock;
data.bit(1) = 1; data.bit(1) = input.edge();
if(!select) { if(!select) {
data.bit(0) = 1; //high-z when the chip is idle (not selected) data.bit(0) = 1; //high-z when the chip is idle (not selected)
} else if(busy) { } else if(busy) {
data.bit(0) = 0; //low when a programming command is in progress data.bit(0) = 0; //low when a programming command is in progress
} else if(output.count) { } 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 { } else {
data.bit(0) = 1; //high-z during all other commands 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 { auto Cartridge::MBC7::EEPROM::writeIO(uint8 data) -> void {
//bring chip out of idle state on rising CS edge //chip enters idle state on falling CS edge
if(select.raise(data.bit(7))) return power(true); if(select && !data.bit(7)) return power();
//do nothing if chip is idle //chip leaves idle state on rising CS edge
if(!select) return; 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; if(!clock.raise(data.bit(6))) return;
//sequential read mode //read mode
if(output.count && !data.bit(1)) { if(output.count && !data.bit(1)) {
if(input.start() && *input.start() == 1) {
if(input.opcode() && *input.opcode() == 0b10) {
output.read(); output.read();
if(output.count == 0) { if(output.count == 0) {
address.value++; //sequential read mode
input.increment();
read(); read();
} }
}
}
return; return;
} }
output.flush(); output.flush();
//wait for start bit to be set input.write(data.bit(1));
if(command.count == 0 && !data.bit(1)) return;
//waiting on command? //wait for start
if(command.count < command.length) { if(!input.start()) return;
command.write(data.bit(1)); uint start = *input.start();
if(command.count < command.length) return;
return address.flush(); //start bit must be set
} if(start != 1) return input.flush();
//waiting on address bits? //wait for opcode
if(address.count < address.length) { if(!input.opcode()) return;
address.write(data.bit(1)); uint opcode = *input.opcode();
if(address.count < address.length) return;
uint3 opcode = command.bits(0, command.length - 1); //wait for address
if(opcode == 0b100) { if(!input.address()) return;
uint2 mode = address.bits(address.length - 2, address.length - 1);
if(opcode == 0b00) {
auto mode = *input.mode();
if(mode == 0b00) return writeDisable(); if(mode == 0b00) return writeDisable();
if(mode == 0b01) return input.flush(); //writeAll if(mode == 0b01) return writeAll();
if(mode == 0b10) return eraseAll(); if(mode == 0b10) return eraseAll();
if(mode == 0b11) return writeEnable(); if(mode == 0b11) return writeEnable();
} }
if(opcode == 0b101) return input.flush(); //write if(opcode == 0b01) return write();
if(opcode == 0b110) return read(); if(opcode == 0b10) return read();
if(opcode == 0b111) return erase(); if(opcode == 0b11) 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;
}
} }
// //
auto Cartridge::MBC7::EEPROM::read() -> void { auto Cartridge::MBC7::EEPROM::read() -> void {
command.flush(); uint address = *input.address() << (width == 16) & size - 1;
auto address = this->address.value << (width == 16) & (size >> 3) - 1; output.flush();
output.value = 0; for(uint4 index : range(width)) {
if(width >= 8) output.value |= data[address++] << 8; output.write(data[address + !index.bit(3)].bit(index.bits(0,2)));
if(width >= 16) output.value |= data[address++] << 0; }
output.count = output.length; output.write(0); //reads have an extra dummy data bit
} }
auto Cartridge::MBC7::EEPROM::write() -> void { auto Cartridge::MBC7::EEPROM::write() -> void {
command.flush(); if(!input.data()) return; //wait for data
if(!writable) return; if(!writable) return input.flush();
auto address = this->address.value << (width == 16) & (size >> 3) - 1; uint address = *input.address() << (width == 16) & size - 1;
if(width >= 8) data[address++] = input.value >> 8; for(uint4 index : range(width)) {
if(width >= 16) data[address++] = input.value >> 0; data[address + !index.bit(3)].bit(index.bits(0,2)) = input.read();
input.flush(); }
busy = 4; //ms busy = 4; //milliseconds
return input.flush();
} }
auto Cartridge::MBC7::EEPROM::erase() -> void { auto Cartridge::MBC7::EEPROM::erase() -> void {
command.flush(); if(!writable) return input.flush();
if(!writable) return; uint address = *input.address() << (width == 16) & size - 1;
auto address = this->address.value << (width == 16) & (size >> 3) - 1; for(uint index : range(width)) {
if(width >= 8) data[address++] = 0xff; data[address + index / 8].bit(index % 8) = 1;
if(width >= 16) data[address++] = 0xff; }
busy = 4; //ms busy = 4; //milliseconds
return input.flush();
} }
auto Cartridge::MBC7::EEPROM::writeAll() -> void { auto Cartridge::MBC7::EEPROM::writeAll() -> void {
command.flush(); if(!input.data()) return; //wait for data
if(!writable) return; if(!writable) return input.flush();
uint8 lo = input.byte(0); auto word = *input.data();
uint8 hi = input.byte(width == 16);
for(uint address = 0; address < 512;) { for(uint address = 0; address < 512;) {
data[address++] = hi; data[address++] = word.byte(width == 16);
data[address++] = lo; data[address++] = word.byte(0);
} }
input.flush(); busy = 16; //milliseconds
busy = 16; //ms return input.flush();
} }
auto Cartridge::MBC7::EEPROM::eraseAll() -> void { auto Cartridge::MBC7::EEPROM::eraseAll() -> void {
command.flush(); if(!writable) return input.flush();
if(!writable) return; for(auto& byte : data) byte = 0xff;
for(uint address; address < 512;) { busy = 8; //milliseconds
data[address++] = 0xff; return input.flush();
data[address++] = 0xff;
}
busy = 8; //ms
} }
auto Cartridge::MBC7::EEPROM::writeEnable() -> void { auto Cartridge::MBC7::EEPROM::writeEnable() -> void {
command.flush();
writable = true; writable = true;
return input.flush();
} }
auto Cartridge::MBC7::EEPROM::writeDisable() -> void { auto Cartridge::MBC7::EEPROM::writeDisable() -> void {
command.flush();
writable = false; writable = false;
return input.flush();
} }
// //
@ -207,20 +190,50 @@ auto Cartridge::MBC7::EEPROM::ShiftRegister::flush() -> void {
count = 0; count = 0;
} }
//read the current bit in the shift register without clocking it auto Cartridge::MBC7::EEPROM::ShiftRegister::edge() -> uint1 {
auto Cartridge::MBC7::EEPROM::ShiftRegister::peek() -> bool { return value.bit(0);
return value.bit(length - 1);
} }
auto Cartridge::MBC7::EEPROM::ShiftRegister::read() -> bool { auto Cartridge::MBC7::EEPROM::ShiftRegister::read() -> uint1 {
bool bit = value.bit(length - 1); uint1 bit = value.bit(0);
value <<= 1; value >>= 1;
if(count) count--; count--;
return bit; return bit;
} }
auto Cartridge::MBC7::EEPROM::ShiftRegister::write(bool bit) -> void { auto Cartridge::MBC7::EEPROM::ShiftRegister::write(uint1 bit) -> void {
value <<= 1; value <<= 1;
value |= bit; value.bit(0) = bit;
count++; 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)++;
}

View File

@ -1,5 +1,5 @@
struct MBC7 : Mapper { struct MBC7 : Mapper {
enum : uint { Center = 0x81d0 }; enum : uint { Center = 0x81d0 }; //not 0x8000
//mbc7.cpp //mbc7.cpp
auto load(Markup::Node document) -> void override; auto load(Markup::Node document) -> void override;
@ -17,7 +17,7 @@ struct MBC7 : Mapper {
auto load(Markup::Node document) -> void; auto load(Markup::Node document) -> void;
auto save(Markup::Node document) -> void; auto save(Markup::Node document) -> void;
auto main() -> void; auto main() -> void;
auto power(bool reset = false) -> void; auto power() -> void;
//Game Boy MBC7 interface //Game Boy MBC7 interface
auto readIO() -> uint8; auto readIO() -> uint8;
@ -37,7 +37,7 @@ struct MBC7 : Mapper {
//it is awkward no matter if data is uint1[4096], uint8[512], or uint16[256] //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 uint8 data[512]; //uint8 was chosen solely for easier serialization and saving
uint size; //in bits; not bytes uint size; //in bytes
uint width; //8-bit (ORG=0) or 16-bit (ORG=1) configuration uint width; //8-bit (ORG=0) or 16-bit (ORG=1) configuration
boolean select; //CS boolean select; //CS
@ -46,21 +46,34 @@ struct MBC7 : Mapper {
uint busy; //busy cycles in milliseconds remaining for programming (write) operations to complete uint busy; //busy cycles in milliseconds remaining for programming (write) operations to complete
struct ShiftRegister { 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 flush() -> void;
auto peek() -> bool; auto edge() -> uint1;
auto read() -> bool; auto read() -> uint1;
auto write(bool data) -> void; 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 //serialization.cpp
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
uint32 value; uint32 addressLength;
uint32 count; uint32 dataLength;
uint32 length; } input;
} command, address, input, output;
struct OutputShiftRegister : ShiftRegister {
//serialization.cpp
auto serialize(serializer&) -> void;
} output;
} eeprom; } eeprom;
struct IO { struct IO {

View File

@ -15,14 +15,18 @@ auto Cartridge::MBC7::EEPROM::serialize(serializer& s) -> void {
s.boolean(clock); s.boolean(clock);
s.boolean(writable); s.boolean(writable);
s.integer(busy); s.integer(busy);
command.serialize(s);
address.serialize(s);
input.serialize(s); input.serialize(s);
output.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(value);
s.integer(count); s.integer(count);
s.integer(length);
} }

View File

@ -77,7 +77,7 @@ auto Cartridge::loadCartridgeGameBoy(Markup::Node node) -> void {
auto Cartridge::loadCartridgeBSMemory(Markup::Node node) -> void { auto Cartridge::loadCartridgeBSMemory(Markup::Node node) -> void {
if(auto memory = Emulator::Game::Memory{node["game/board/memory(content=Program)"]}) { 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); bsmemory.memory.allocate(memory.size);
if(auto fp = platform->open(bsmemory.pathID, memory.name(), File::Read, File::Required)) { if(auto fp = platform->open(bsmemory.pathID, memory.name(), File::Read, File::Required)) {
fp->read(bsmemory.memory.data(), memory.size); fp->read(bsmemory.memory.data(), memory.size);

View File

@ -22,11 +22,17 @@ auto MCC::power() -> void {
w.exEnableLo = 1; w.exEnableLo = 1;
w.exEnableHi = 0; w.exEnableHi = 0;
w.exMapping = 1; w.exMapping = 1;
w.bsWritable = 0; w.bsQueryable = 0;
w.unknown = 0; w.bsFlashable = 0;
x.enable = 0; x.enable = 0;
x.value = 0b0011'1111; x.value = 0b00111111;
memory::copy(&r, &w, sizeof(Registers)); 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 { 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 9: return r.exEnableLo << 7;
case 10: return r.exEnableHi << 7; case 10: return r.exEnableHi << 7;
case 11: return r.exMapping << 7; case 11: return r.exMapping << 7;
case 12: return r.bsWritable << 7; case 12: return r.bsQueryable << 7;
case 13: return r.unknown << 7; case 13: return r.bsFlashable << 7;
case 14: return 0; //commit (always zero) case 14: return 0; //commit (always zero)
case 15: return 0; //x.enable (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 9: w.exEnableLo = data.bit(7); break;
case 10: w.exEnableHi = data.bit(7); break; case 10: w.exEnableHi = data.bit(7); break;
case 11: w.exMapping = data.bit(7); break; case 11: w.exMapping = data.bit(7); break;
case 12: w.bsWritable = data.bit(7); break; case 12: w.bsQueryable = data.bit(7); break;
case 13: w.unknown = data.bit(7); break; case 13: w.bsFlashable = data.bit(7); break;
case 14: if(data.bit(7)) memory::copy(&r, &w, sizeof(Registers)); break; case 14: if(data.bit(7)) commit(); break;
case 15: x.enable = data.bit(7); break; case 15: x.enable = data.bit(7); break;
} }
} }

View File

@ -8,6 +8,7 @@ struct MCC {
//mcc.cpp //mcc.cpp
auto unload() -> void; auto unload() -> void;
auto power() -> void; auto power() -> void;
auto commit() -> void;
auto read(uint24 address, uint8 data) -> uint8; auto read(uint24 address, uint8 data) -> uint8;
auto write(uint24 address, uint8 data) -> void; auto write(uint24 address, uint8 data) -> void;
@ -40,8 +41,8 @@ private:
uint1 exEnableLo; //bit 9 uint1 exEnableLo; //bit 9
uint1 exEnableHi; //bit 10 uint1 exEnableHi; //bit 10
uint1 exMapping; //bit 11 uint1 exMapping; //bit 11
uint1 bsWritable; //bit 12 uint1 bsQueryable; //bit 12
uint1 unknown; //bit 13 uint1 bsFlashable; //bit 13
} r, w; } r, w;
//bit 14 (commit) //bit 14 (commit)

View File

@ -11,8 +11,8 @@ auto MCC::serialize(serializer& s) -> void {
s.integer(r.exEnableLo); s.integer(r.exEnableLo);
s.integer(r.exEnableHi); s.integer(r.exEnableHi);
s.integer(r.exMapping); s.integer(r.exMapping);
s.integer(r.bsWritable); s.integer(r.bsQueryable);
s.integer(r.unknown); s.integer(r.bsFlashable);
s.integer(w.mapping); s.integer(w.mapping);
s.integer(w.psramEnableLo); s.integer(w.psramEnableLo);
s.integer(w.psramEnableHi); s.integer(w.psramEnableHi);
@ -22,8 +22,8 @@ auto MCC::serialize(serializer& s) -> void {
s.integer(w.exEnableLo); s.integer(w.exEnableLo);
s.integer(w.exEnableHi); s.integer(w.exEnableHi);
s.integer(w.exMapping); s.integer(w.exMapping);
s.integer(w.bsWritable); s.integer(w.bsQueryable);
s.integer(w.unknown); s.integer(w.bsFlashable);
s.integer(x.enable); s.integer(x.enable);
s.integer(x.value); s.integer(x.value);
} }

View File

@ -6,7 +6,8 @@ namespace SuperFamicom {
BSMemory bsmemory; BSMemory bsmemory;
auto BSMemory::load() -> void { auto BSMemory::load() -> void {
if(!memory.size()) memory.allocate(1024 * 1024); queryable(true);
flashable(true);
} }
auto BSMemory::unload() -> void { auto BSMemory::unload() -> void {
@ -14,14 +15,8 @@ auto BSMemory::unload() -> void {
} }
auto BSMemory::power() -> void { auto BSMemory::power() -> void {
regs.command = 0; memory.writable(false);
regs.writeOld = 0x00; io = {};
regs.writeNew = 0x00;
regs.flashEnable = false;
regs.readEnable = false;
regs.writeEnable = false;
memory.writable(regs.writeEnable);
} }
auto BSMemory::data() -> uint8* { auto BSMemory::data() -> uint8* {
@ -32,92 +27,71 @@ auto BSMemory::size() const -> uint {
return memory.size(); return memory.size();
} }
auto BSMemory::read(uint24 addr, uint8 data) -> uint8 { auto BSMemory::read(uint24 address, uint8 data) -> uint8 {
if(readonly) { if(!size()) return data;
return memory.read(bus.mirror(addr, memory.size()), data); address = bus.mirror(address, size());
if(!pin.queryable) return memory.read(address, data);
if(io.mode == 0x70) {
return 0x80;
} }
if(addr == 0x0002) { if(io.mode == 0x71) {
if(regs.flashEnable) return 0x80; 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(io.mode == 0x75) {
if(regs.flashEnable) return 0x80; 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) { return memory.read(address, data);
//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);
} }
auto BSMemory::write(uint24 addr, uint8 data) -> void { auto BSMemory::write(uint24 address, uint8 data) -> void {
if(readonly) { 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; return;
} }
if((addr & 0xff0000) == 0) { //erase 64KB page
regs.writeOld = regs.writeNew; if(io.mode == 0x20) {
regs.writeNew = data; //completes even if !pin.flashable
memory.writable(true);
if(regs.writeEnable && regs.writeOld == regs.writeNew) { for(uint offset : range(1 << 16)) memory.write(address & 0xff0000 | offset, 0xff);
return memory.write(addr, data); memory.writable(false);
} io.mode = 0x70;
} else { return;
if(regs.writeEnable) {
return memory.write(addr, data);
}
} }
if(addr == 0x0000) { //erase all pages
regs.command <<= 8; if(io.mode == 0xa7) {
regs.command |= data; //completes even if !pin.flashable
if(Type == 3) return; //Type 3 doesn't support this command
if((regs.command & 0xffff) == 0x38d0) { memory.writable(true);
regs.flashEnable = true; for(uint offset : range(size())) memory.write(offset, 0xff);
regs.readEnable = true; memory.writable(false);
} io.mode = 0x70;
return;
} }
if(addr == 0x2aaa) { if((uint16)address == 0x0000) {
regs.command <<= 8; io.mode = data;
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);
} }
} }

View File

@ -1,4 +1,7 @@
struct BSMemory : Memory { struct BSMemory : Memory {
auto queryable(bool queryable) { pin.queryable = !ROM && queryable; }
auto flashable(bool flashable) { pin.flashable = !ROM && flashable; }
//bsmemory.cpp //bsmemory.cpp
auto load() -> void; auto load() -> void;
auto unload() -> void; auto unload() -> void;
@ -6,26 +9,33 @@ struct BSMemory : Memory {
auto data() -> uint8* override; auto data() -> uint8* override;
auto size() const -> uint override; auto size() const -> uint override;
auto read(uint24 addr, uint8 data) -> uint8 override; auto read(uint24 address, uint8 data) -> uint8 override;
auto write(uint24 addr, uint8 data) -> void override; auto write(uint24 address, uint8 data) -> void override;
//serialization.cpp //serialization.cpp
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
uint pathID = 0; 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; ProtectableMemory memory;
bool readonly;
private: private:
struct { struct Pin {
uint command; uint1 queryable;
uint8 writeOld; uint1 flashable;
uint8 writeNew; } pin;
bool flashEnable; struct IO {
bool readEnable; uint8 mode;
bool writeEnable; } io;
} regs;
}; };
extern BSMemory bsmemory; extern BSMemory bsmemory;

View File

@ -1,3 +1,6 @@
auto BSMemory::serialize(serializer& s) -> void { 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);
} }

View File

@ -17,7 +17,7 @@ using uint32 = Natural<32>;
using uint64 = Natural<64>; using uint64 = Natural<64>;
struct FX { struct FX {
auto open(vector<string>& arguments) -> bool; auto open(vector<string> arguments) -> bool;
auto close() -> void; auto close() -> void;
auto readable() -> bool; auto readable() -> bool;
auto read() -> uint8_t; auto read() -> uint8_t;
@ -35,12 +35,12 @@ struct FX {
serial device; serial device;
}; };
auto FX::open(vector<string>& arguments) -> bool { auto FX::open(vector<string> arguments) -> bool {
//device name override support //device name override support
string name; string name;
for(uint n : range(arguments)) { for(auto argument : arguments) {
if(arguments[n].beginsWith("--device=")) { if(argument.beginsWith("--device=")) {
name = arguments.take(n).trimLeft("--device=", 1L); name = argument.trimLeft("--device=", 1L);
break; break;
} }
} }
@ -54,7 +54,7 @@ auto FX::open(vector<string>& arguments) -> bool {
while(true) { while(true) {
while(readable()) read(); while(readable()) read();
auto iplrom = read(0x2184, 122); auto iplrom = read(0x2184, 122);
auto sha256 = Hash::SHA256(iplrom.data(), iplrom.size()).digest(); auto sha256 = Hash::SHA256(iplrom).digest();
if(sha256 == "41b79712a4a2d16d39894ae1b38cde5c41dad22eadc560df631d39f13df1e4b9") break; if(sha256 == "41b79712a4a2d16d39894ae1b38cde5c41dad22eadc560df631d39f13df1e4b9") break;
} }