mirror of
https://github.com/ares-emulator/ares.git
synced 2026-10-12 02:11:04 +00:00
fc: implement Datach external EEPROM
This commit is contained in:
@@ -73,6 +73,7 @@ ares_add_sources(
|
||||
cartridge/board/ave-nina-001.cpp
|
||||
cartridge/board/ave-nina-06.cpp
|
||||
cartridge/board/bandai-74161.cpp
|
||||
cartridge/board/bandai-datach.cpp
|
||||
cartridge/board/bandai-fcg.cpp
|
||||
cartridge/board/bandai-karaoke.cpp
|
||||
cartridge/board/bandai-lz93d50.cpp
|
||||
@@ -156,6 +157,7 @@ ares_add_sources(
|
||||
CORE #
|
||||
fc
|
||||
INCLUDED #
|
||||
cartridge/board/ic/bandai-24c01.hpp
|
||||
cartridge/board/ic/jv001.hpp
|
||||
cartridge/board/ic/txc05-00002-010.hpp
|
||||
)
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
//Datach's serial-memory bus; barcode input is not implemented.
|
||||
struct BandaiDatach : BandaiLZ93D50 {
|
||||
static auto create(string id) -> Interface* {
|
||||
if(id == "BANDAI-DATACH") return new BandaiDatach;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Bandai24C01 external;
|
||||
bool hasExternal = false;
|
||||
|
||||
auto load() -> void override {
|
||||
BandaiLZ93D50::load();
|
||||
if(auto fp = pak->read("external.eeprom")) {
|
||||
if(hasExternal = fp->size() == 128) fp->read(external.memory, 128);
|
||||
}
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
BandaiLZ93D50::save();
|
||||
if(hasExternal) {
|
||||
if(auto fp = pak->write("external.eeprom")) fp->write(external.memory, 128);
|
||||
}
|
||||
}
|
||||
|
||||
auto readPRG(n32 address, n8 data) -> n8 override {
|
||||
data = BandaiLZ93D50::readPRG(address, data);
|
||||
if(address >= 0x6000 && address < 0x8000) {
|
||||
data.bit(3) = 0; //idle barcode signal
|
||||
if(hasExternal) data.bit(4) = data.bit(4) && external.read();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto writePRG(n32 address, n8 data) -> void override {
|
||||
if(address < 0x8000) return;
|
||||
auto reg = address & 15;
|
||||
if(reg <= 7) {
|
||||
if(reg <= 3 && hasExternal) external.write(data.bit(3), external.data);
|
||||
return;
|
||||
}
|
||||
BandaiLZ93D50::writePRG(address, data);
|
||||
if(reg == 13 && hasExternal) external.write(external.clock, data.bit(6));
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
BandaiLZ93D50::power();
|
||||
external.power();
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
BandaiLZ93D50::serialize(s);
|
||||
if(hasExternal) s(external);
|
||||
}
|
||||
};
|
||||
@@ -7,6 +7,7 @@ namespace Board {
|
||||
#include "bandai-fcg.cpp"
|
||||
#include "bandai-karaoke.cpp"
|
||||
#include "bandai-lz93d50.cpp"
|
||||
#include "bandai-datach.cpp"
|
||||
#include "bandai-oeka.cpp"
|
||||
#include "camerica-bf909x.cpp"
|
||||
#include "colordreams-74x377.cpp"
|
||||
@@ -76,6 +77,7 @@ auto Interface::create(string board) -> Interface* {
|
||||
if(!p) p = BandaiFCG::create(board);
|
||||
if(!p) p = BandaiKaraoke::create(board);
|
||||
if(!p) p = BandaiLZ93D50::create(board);
|
||||
if(!p) p = BandaiDatach::create(board);
|
||||
if(!p) p = BandaiOeka::create(board);
|
||||
if(!p) p = Camerica_BF909x::create(board);
|
||||
if(!p) p = ColorDreams_74x377::create(board);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
namespace Board {
|
||||
#include "ic/bandai-24c01.hpp"
|
||||
#include "ic/jv001.hpp"
|
||||
#include "ic/txc05-00002-010.hpp"
|
||||
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
|
||||
//LSB-first 24C01 serial interface used by Bandai Datach cartridges.
|
||||
struct Bandai24C01 {
|
||||
auto power() -> void {
|
||||
mode = Mode::Standby;
|
||||
clock = data = response = 1;
|
||||
counter = address = input = output = reading = pending = 0;
|
||||
for(auto& byte : page) byte = 0;
|
||||
}
|
||||
auto read() const -> bool {
|
||||
return response;
|
||||
}
|
||||
auto write(bool scl, bool sda) -> void {
|
||||
if(clock && scl && data && !sda) {
|
||||
mode = Mode::Address;
|
||||
counter = address = pending = 0;
|
||||
response = 1;
|
||||
} else if(clock && scl && !data && sda) {
|
||||
//The four-byte page buffer is programmed on STOP. Programming is instantaneous.
|
||||
for(u32 index : range(4)) {
|
||||
if(pending.bit(index)) memory[(address & 0x7c) | index] = page[index];
|
||||
}
|
||||
pending = 0;
|
||||
mode = Mode::Standby;
|
||||
response = 1;
|
||||
} else if(!clock && scl) {
|
||||
switch(mode) {
|
||||
case Mode::Address:
|
||||
if(counter < 7) address.bit(counter++) = sda;
|
||||
else if(counter == 7) reading = sda, counter++;
|
||||
break;
|
||||
case Mode::Read:
|
||||
if(counter < 8) counter++;
|
||||
break;
|
||||
case Mode::ReadAck:
|
||||
reading = !sda;
|
||||
break;
|
||||
case Mode::Write:
|
||||
if(counter < 8) input.bit(counter++) = sda;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if(clock && !scl) {
|
||||
switch(mode) {
|
||||
case Mode::Address:
|
||||
if(counter == 8) mode = Mode::AddressAck, response = 0;
|
||||
break;
|
||||
case Mode::AddressAck:
|
||||
counter = 0;
|
||||
mode = reading ? Mode::Read : Mode::Write;
|
||||
output = memory[address];
|
||||
response = reading ? output.bit(0) : 1;
|
||||
break;
|
||||
case Mode::Read:
|
||||
if(counter == 8) {
|
||||
mode = Mode::ReadAck;
|
||||
response = 1;
|
||||
} else {
|
||||
response = output.bit(counter);
|
||||
}
|
||||
break;
|
||||
case Mode::ReadAck:
|
||||
if(reading) {
|
||||
address++;
|
||||
counter = 0;
|
||||
output = memory[address];
|
||||
response = output.bit(0);
|
||||
mode = Mode::Read;
|
||||
} else {
|
||||
mode = Mode::Standby;
|
||||
response = 1;
|
||||
}
|
||||
break;
|
||||
case Mode::Write:
|
||||
if(counter == 8) {
|
||||
page[address.bit(0,1)] = input;
|
||||
pending.bit(address.bit(0,1)) = 1;
|
||||
address.bit(0,1)++;
|
||||
mode = Mode::WriteAck;
|
||||
response = 0;
|
||||
}
|
||||
break;
|
||||
case Mode::WriteAck:
|
||||
mode = Mode::Write;
|
||||
counter = 0;
|
||||
response = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
clock = scl;
|
||||
data = sda;
|
||||
}
|
||||
auto serialize(serializer& s) -> void {
|
||||
s(memory);
|
||||
s(clock);
|
||||
s(data);
|
||||
s((u32&)mode);
|
||||
s(counter);
|
||||
s(address);
|
||||
s(input);
|
||||
s(output);
|
||||
s(reading);
|
||||
s(response);
|
||||
s(page);
|
||||
s(pending);
|
||||
}
|
||||
|
||||
n8 memory[128];
|
||||
n1 clock = 1;
|
||||
n1 data = 1;
|
||||
|
||||
private:
|
||||
enum class Mode : u32 { Standby, Address, AddressAck, Read, ReadAck, Write, WriteAck };
|
||||
Mode mode = Mode::Standby;
|
||||
n4 counter;
|
||||
n7 address;
|
||||
n8 input;
|
||||
n8 output;
|
||||
n1 reading;
|
||||
n1 response = 1;
|
||||
n8 page[4];
|
||||
n4 pending;
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
static const string SerializerVersion = "v155";
|
||||
static const string SerializerVersion = "v156";
|
||||
|
||||
auto System::serialize(bool synchronize) -> serializer {
|
||||
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
|
||||
|
||||
@@ -7300,7 +7300,7 @@ game
|
||||
name: Datach - Battle Rush - Build Up Robot Tournament (Japan)
|
||||
title: Datach - Battle Rush - Build Up Robot Tournament (Japan)
|
||||
region: NTSC-J
|
||||
board: BANDAI-LZ93D50
|
||||
board: BANDAI-DATACH
|
||||
chip
|
||||
type: LZ93D50
|
||||
memory
|
||||
@@ -7316,13 +7316,17 @@ game
|
||||
type: EEPROM
|
||||
size: 0x100
|
||||
content: Save
|
||||
memory
|
||||
type: EEPROM
|
||||
size: 0x80
|
||||
content: External
|
||||
|
||||
game
|
||||
sha256: 0acc10d731fbd33ca07cc6c81ce44850b9668f743012260b526e1053d332e37b
|
||||
name: Datach - Crayon Shin-chan - Ora to Poi Poi (Japan)
|
||||
title: Datach - Crayon Shin-chan - Ora to Poi Poi (Japan)
|
||||
region: NTSC-J
|
||||
board: BANDAI-LZ93D50
|
||||
board: BANDAI-DATACH
|
||||
chip
|
||||
type: LZ93D50
|
||||
memory
|
||||
@@ -7344,7 +7348,7 @@ game
|
||||
name: Datach - Dragon Ball Z - Gekitou Tenkaichi Budoukai (Japan)
|
||||
title: Datach - Dragon Ball Z - Gekitou Tenkaichi Budoukai (Japan)
|
||||
region: NTSC-J
|
||||
board: BANDAI-LZ93D50
|
||||
board: BANDAI-DATACH
|
||||
chip
|
||||
type: LZ93D50
|
||||
memory
|
||||
@@ -7366,7 +7370,7 @@ game
|
||||
name: Datach - J.League Super Top Players (Japan)
|
||||
title: Datach - J.League Super Top Players (Japan)
|
||||
region: NTSC-J
|
||||
board: BANDAI-LZ93D50
|
||||
board: BANDAI-DATACH
|
||||
chip
|
||||
type: LZ93D50
|
||||
memory
|
||||
@@ -7388,7 +7392,7 @@ game
|
||||
name: Datach - SD Gundam - Gundam Wars (Japan)
|
||||
title: Datach - SD Gundam - Gundam Wars (Japan)
|
||||
region: NTSC-J
|
||||
board: BANDAI-LZ93D50
|
||||
board: BANDAI-DATACH
|
||||
chip
|
||||
type: LZ93D50
|
||||
memory
|
||||
@@ -7410,7 +7414,7 @@ game
|
||||
name: Datach - Ultraman Club - Supokon Fight! (Japan)
|
||||
title: Datach - Ultraman Club - Supokon Fight! (Japan)
|
||||
region: NTSC-J
|
||||
board: BANDAI-LZ93D50
|
||||
board: BANDAI-DATACH
|
||||
chip
|
||||
type: LZ93D50
|
||||
memory
|
||||
@@ -7432,7 +7436,7 @@ game
|
||||
name: Datach - Yu Yu Hakusho - Bakutou Ankoku Bujutsukai (Japan)
|
||||
title: Datach - Yu Yu Hakusho - Bakutou Ankoku Bujutsukai (Japan)
|
||||
region: NTSC-J
|
||||
board: BANDAI-LZ93D50
|
||||
board: BANDAI-DATACH
|
||||
chip
|
||||
type: LZ93D50
|
||||
memory
|
||||
|
||||
+17
-3
@@ -66,6 +66,9 @@ auto Famicom::load(string location) -> LoadResult {
|
||||
if(auto node = document["game/board/memory(type=EEPROM,content=Save)"]) {
|
||||
Medium::load(node, ".eeprom");
|
||||
}
|
||||
if(auto node = document["game/board/memory(type=EEPROM,content=External)"]) {
|
||||
Medium::load(node, ".external.eeprom");
|
||||
}
|
||||
if(auto node = document["game/board/memory(type=RAM,content=Character)"]) {
|
||||
Medium::load(node, ".chr");
|
||||
}
|
||||
@@ -82,6 +85,9 @@ auto Famicom::save(string location) -> bool {
|
||||
if(auto node = document["game/board/memory(type=EEPROM,content=Save)"]) {
|
||||
Medium::save(node, ".eeprom");
|
||||
}
|
||||
if(auto node = document["game/board/memory(type=EEPROM,content=External)"]) {
|
||||
Medium::save(node, ".external.eeprom");
|
||||
}
|
||||
if(auto node = document["game/board/memory(type=Flash,content=Program)"]) {
|
||||
Pak::save("program.flash", ".flash");
|
||||
}
|
||||
@@ -178,6 +184,7 @@ auto Famicom::analyzeINES(std::vector<u8>& data) -> string {
|
||||
string system = "Regular";
|
||||
bool battery = (data[6] & 0x02) != 0;
|
||||
bool eepromMapper = false;
|
||||
bool externalEEPROM = false;
|
||||
bool prgromFlash = false;
|
||||
|
||||
string region = "NTSC-J, NTSC-U, PAL"; //iNES 1.0 requires database to detect region
|
||||
@@ -673,10 +680,10 @@ auto Famicom::analyzeINES(std::vector<u8>& data) -> string {
|
||||
break;
|
||||
|
||||
case 157:
|
||||
// TODO: Implement external EEPROM support.
|
||||
// For now, we force values on this mapper.
|
||||
s += " board: BANDAI-LZ93D50\n";
|
||||
s += " board: BANDAI-DATACH\n";
|
||||
s += " chip type=LZ93D50\n";
|
||||
//NES 2.0 describes the cartridge EEPROM, not Datach's internal 256 bytes.
|
||||
externalEEPROM = iNes2 ? prgnvram == 128 : battery;
|
||||
prgnvram = 256;
|
||||
eepromMapper = true;
|
||||
break;
|
||||
@@ -832,6 +839,13 @@ auto Famicom::analyzeINES(std::vector<u8>& data) -> string {
|
||||
s += " content: Save\n";
|
||||
}
|
||||
|
||||
if(externalEEPROM) {
|
||||
s += " memory\n";
|
||||
s += " type: EEPROM\n";
|
||||
s += " size: 0x80\n";
|
||||
s += " content: External\n";
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user