Commit c5816994 authored by Tim Allen's avatar Tim Allen

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.
parent c2d0ed4c
Pipeline #29901675 failed with stage
in 13 minutes and 24 seconds
......@@ -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();
}
//start bit must be set
if(start != 1) return input.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;
}
//wait for opcode
if(!input.opcode()) return;
uint opcode = *input.opcode();
//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;
//wait for address
if(!input.address()) return;
uint3 opcode = command.bits(0, command.length - 1);
if(opcode == 0b101) return write();
if(opcode == 0b100) return writeAll();
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)++;
}
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,</