1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-25 03:32:01 +00:00

Merge pull request #365 from TomHarte/CartridgeDetermination

Works towards eliminating the special cases for Atari 2600 ROM handling.
This commit is contained in:
Thomas Harte 2018-03-08 18:40:58 -05:00 committed by GitHub
commit 03501df9e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 104 additions and 32 deletions

View File

@ -62,8 +62,15 @@ Configurable::Device *MultiMachine::configurable_device() {
}
}
bool MultiMachine::would_collapse(const std::vector<std::unique_ptr<DynamicMachine>> &machines) {
return
(machines.front()->crt_machine()->get_confidence() > 0.9f) ||
(machines.front()->crt_machine()->get_confidence() >= 2.0f * machines[1]->crt_machine()->get_confidence());
}
void MultiMachine::multi_crt_did_run_machines() {
std::lock_guard<std::mutex> machines_lock(machines_mutex_);
#ifdef DEBUG
for(const auto &machine: machines_) {
CRTMachine::Machine *crt = machine->crt_machine();
printf("%0.2f ", crt->get_confidence());
@ -71,6 +78,7 @@ void MultiMachine::multi_crt_did_run_machines() {
printf("; ");
}
printf("\n");
#endif
DynamicMachine *front = machines_.front().get();
std::stable_sort(machines_.begin(), machines_.end(),
@ -84,10 +92,7 @@ void MultiMachine::multi_crt_did_run_machines() {
crt_machine_.did_change_machine_order();
}
if(
(machines_.front()->crt_machine()->get_confidence() > 0.9f) ||
(machines_.front()->crt_machine()->get_confidence() >= 2.0f * machines_[1]->crt_machine()->get_confidence())
) {
if(would_collapse(machines_)) {
pick_first();
}
}

View File

@ -40,6 +40,14 @@ namespace Dynamic {
*/
class MultiMachine: public ::Machine::DynamicMachine, public MultiCRTMachine::Delegate {
public:
/*!
Allows a potential MultiMachine creator to enquire as to whether there's any benefit in
requesting this class as a proxy.
@returns @c true if the multimachine would discard all but the first machine in this list;
@c false otherwise.
*/
static bool would_collapse(const std::vector<std::unique_ptr<DynamicMachine>> &machines);
MultiMachine(std::vector<std::unique_ptr<DynamicMachine>> &&machines);
ConfigurationTarget::Machine *configuration_target() override;

View File

@ -59,7 +59,7 @@ static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
void Analyser::Static::Acorn::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination) {
std::unique_ptr<Target> target(new Target);
target->machine = Machine::Electron;
target->confidence = 1.0; // TODO: a proper estimation
target->confidence = 0.5; // TODO: a proper estimation
target->acorn.has_dfs = false;
target->acorn.has_adfs = false;
target->acorn.should_shift_restart = false;

View File

@ -180,7 +180,7 @@ static bool CheckBootSector(const std::shared_ptr<Storage::Disk::Disk> &disk, co
void Analyser::Static::AmstradCPC::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination) {
std::unique_ptr<Target> target(new Target);
target->machine = Machine::AmstradCPC;
target->confidence = 1.0;
target->confidence = 0.5;
target->media.disks = media.disks;
target->media.tapes = media.tapes;
target->media.cartridges = media.cartridges;

View File

@ -179,10 +179,10 @@ static void DeterminePagingForCartridge(Analyser::Static::Target &target, const
}
void Analyser::Static::Atari::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination) {
// TODO: sanity checking; is this image really for an Atari 2600.
// TODO: sanity checking; is this image really for an Atari 2600?
std::unique_ptr<Target> target(new Target);
target->machine = Machine::Atari2600;
target->confidence = 1.0;
target->confidence = 0.5;
target->media.cartridges = media.cartridges;
target->atari.paging_model = Atari2600PagingModel::None;
target->atari.uses_superchip = false;

View File

@ -55,7 +55,7 @@ static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
void Analyser::Static::Coleco::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination) {
std::unique_ptr<Target> target(new Target);
target->machine = Machine::ColecoVision;
target->confidence = 0.5;
target->confidence = 1.0f - 1.0f / 32768.0f;
target->media.cartridges = ColecoCartridgesFrom(media.cartridges);
if(!target->media.empty())
destination.push_back(std::move(target));

View File

@ -41,7 +41,7 @@ static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
void Analyser::Static::Commodore::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination) {
std::unique_ptr<Target> target(new Target);
target->machine = Machine::Vic20; // TODO: machine estimation
target->confidence = 1.0; // TODO: a proper estimation
target->confidence = 0.5; // TODO: a proper estimation
int device = 0;
std::vector<File> files;

View File

@ -286,7 +286,7 @@ void Analyser::Static::MSX::AddTargets(const Media &media, std::vector<std::uniq
if(!target->media.empty()) {
target->machine = Machine::MSX;
target->confidence = 1.0;
target->confidence = 0.5;
destination.push_back(std::move(target));
}
}

View File

@ -76,7 +76,7 @@ static int Basic11Score(const Analyser::Static::MOS6502::Disassembly &disassembl
void Analyser::Static::Oric::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination) {
std::unique_ptr<Target> target(new Target);
target->machine = Machine::Oric;
target->confidence = 1.0;
target->confidence = 0.5;
int basic10_votes = 0;
int basic11_votes = 0;

View File

@ -88,7 +88,7 @@ static Media GetMediaAndPlatforms(const char *file_name, TargetPlatform::IntType
Format("81", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // 81
Format("a26", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // A26
Format("adf", result.disks, Disk::DiskImageHolder<Storage::Disk::AcornADF>, TargetPlatform::Acorn) // ADF
Format("bin", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // BIN
Format("bin", result.cartridges, Cartridge::BinaryDump, TargetPlatform::AllCartridge) // BIN
Format("cas", result.tapes, Tape::CAS, TargetPlatform::MSX) // CAS
Format("cdt", result.tapes, Tape::TZX, TargetPlatform::AmstradCPC) // CDT
Format("col", result.cartridges, Cartridge::BinaryDump, TargetPlatform::ColecoVision) // COL
@ -120,7 +120,7 @@ static Media GetMediaAndPlatforms(const char *file_name, TargetPlatform::IntType
Format( "rom",
result.cartridges,
Cartridge::BinaryDump,
TargetPlatform::Acorn | TargetPlatform::MSX | TargetPlatform::ColecoVision) // ROM
TargetPlatform::AcornElectron | TargetPlatform::ColecoVision | TargetPlatform::MSX) // ROM
Format("ssd", result.disks, Disk::DiskImageHolder<Storage::Disk::SSD>, TargetPlatform::Acorn) // SSD
Format("tap", result.tapes, Tape::CommodoreTAP, TargetPlatform::Commodore) // TAP (Commodore)
Format("tap", result.tapes, Tape::OricTAP, TargetPlatform::Oric) // TAP (Oric)

View File

@ -74,9 +74,7 @@ class ConcreteMachine:
public Machine,
public Outputs::CRT::Delegate {
public:
ConcreteMachine() :
frame_record_pointer_(0),
is_ntsc_(true) {
ConcreteMachine() {
set_clock_rate(NTSC_clock_rate);
}
@ -179,6 +177,7 @@ class ConcreteMachine:
void run_for(const Cycles cycles) override {
bus_->run_for(cycles);
bus_->apply_confidence(confidence_counter_);
}
// to satisfy Outputs::CRT::Delegate
@ -219,6 +218,10 @@ class ConcreteMachine:
}
}
float get_confidence() override {
return confidence_counter_.get_confidence();
}
private:
// the bus
std::unique_ptr<Bus> bus_;
@ -230,9 +233,12 @@ class ConcreteMachine:
FrameRecord() : number_of_frames(0), number_of_unexpected_vertical_syncs(0) {}
} frame_records_[4];
unsigned int frame_record_pointer_;
bool is_ntsc_;
unsigned int frame_record_pointer_ = 0;
bool is_ntsc_ = true;
std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
// a confidence counter
Analyser::Dynamic::ConfidenceCounter confidence_counter_;
};
}

View File

@ -14,6 +14,7 @@
#include "TIA.hpp"
#include "TIASound.hpp"
#include "../../Analyser/Dynamic/ConfidenceCounter.hpp"
#include "../../ClockReceiver/ClockReceiver.hpp"
#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
@ -23,11 +24,10 @@ class Bus {
public:
Bus() :
tia_sound_(audio_queue_),
speaker_(tia_sound_),
tia_input_value_{0xff, 0xff},
cycles_since_speaker_update_(0) {}
speaker_(tia_sound_) {}
virtual void run_for(const Cycles cycles) = 0;
virtual void apply_confidence(Analyser::Dynamic::ConfidenceCounter &confidence_counter) = 0;
virtual void set_reset_line(bool state) = 0;
// the RIOT, TIA and speaker
@ -39,7 +39,7 @@ class Bus {
Outputs::Speaker::LowpassSpeaker<TIASound> speaker_;
// joystick state
uint8_t tia_input_value_[2];
uint8_t tia_input_value_[2] = {0xff, 0xff};
protected:
// speaker backlog accumlation counter

View File

@ -39,7 +39,23 @@ template<class T> class Cartridge:
// consider doing something less fragile.
}
void run_for(const Cycles cycles) { m6502_.run_for(cycles); }
void run_for(const Cycles cycles) {
// Horizontal counter resets are used as a proxy for whether this really is an Atari 2600
// title. Random memory accesses are likely to trigger random counter resets.
horizontal_counter_resets_ = 0;
cycle_count_ = cycles;
m6502_.run_for(cycles);
}
/*!
Adjusts @c confidence_counter according to the results of the most recent run_for.
*/
void apply_confidence(Analyser::Dynamic::ConfidenceCounter &confidence_counter) {
if(cycle_count_.as_int() < 200) return;
if(horizontal_counter_resets_ > 10)
confidence_counter.add_miss();
}
void set_reset_line(bool state) { m6502_.set_reset_line(state); }
// to satisfy CPU::MOS6502::Processor
@ -108,7 +124,11 @@ template<class T> class Cartridge:
case 0x01: update_video(); tia_->set_blank(*value & 0x02); break;
case 0x02: m6502_.set_ready_line(true); break;
case 0x03: update_video(); tia_->reset_horizontal_counter(); break;
case 0x03:
update_video();
tia_->reset_horizontal_counter();
horizontal_counter_resets_++;
break;
// TODO: audio will now be out of synchronisation — fix
case 0x04:
@ -189,6 +209,9 @@ template<class T> class Cartridge:
private:
T bus_extender_;
int horizontal_counter_resets_ = 0;
Cycles cycle_count_;
};
}

View File

@ -23,6 +23,8 @@
#include "../../Outputs/Speaker/Implementation/CompoundSource.hpp"
#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
#include "../../Analyser/Dynamic/ConfidenceCounter.hpp"
namespace {
const int sn76489_divider = 2;
}
@ -198,6 +200,7 @@ class ConcreteMachine:
uint16_t address = cycle.address ? *cycle.address : 0x0000;
switch(cycle.operation) {
case CPU::Z80::PartialMachineCycle::ReadOpcode:
if(!address) pc_zero_accesses_++;
case CPU::Z80::PartialMachineCycle::Read:
if(address < 0x2000) {
if(super_game_module_.replace_bios) {
@ -248,6 +251,11 @@ class ConcreteMachine:
} else {
*cycle.value = joystick->get_direction_input();
}
// Hitting exactly the recommended joypad input port is an indicator that
// this really is a ColecoVision game. The BIOS won't do this when just waiting
// to start a game (unlike accessing the VDP and SN).
if((address&0xfc) == 0xfc) confidence_counter_.add_hit();
} break;
default:
@ -332,6 +340,11 @@ class ConcreteMachine:
audio_queue_.perform();
}
float get_confidence() override {
if(pc_zero_accesses_ > 1) return 0.0f;
return confidence_counter_.get_confidence();
}
private:
inline void page_megacart(uint16_t address) {
const std::size_t selected_start = (static_cast<std::size_t>(address&63) << 14) % cartridge_.size();
@ -371,6 +384,9 @@ class ConcreteMachine:
HalfCycles time_since_vdp_update_;
HalfCycles time_since_sn76489_update_;
HalfCycles time_until_interrupt_;
Analyser::Dynamic::ConfidenceCounter confidence_counter_;
int pc_zero_accesses_ = 0;
};
}

View File

@ -79,7 +79,13 @@ namespace {
}
}
return new Analyser::Dynamic::MultiMachine(std::move(machines));
// If a multimachine would just instantly collapse the list to a single machine, do
// so without the ongoing baggage of a multimachine.
if(Analyser::Dynamic::MultiMachine::would_collapse(machines)) {
return machines.front().release();
} else {
return new Analyser::Dynamic::MultiMachine(std::move(machines));
}
}
// There's definitely exactly one target.

View File

@ -10,21 +10,22 @@
#import <CommonCrypto/CommonDigest.h>
#include "../../../Analyser/Static/StaticAnalyser.hpp"
#include "../../../Analyser/Static/MSX/Cartridge.hpp"
@interface MSXROMRecord : NSObject
@property(nonatomic, readonly) Analyser::Static::MSXCartridgeType cartridgeType;
+ (instancetype)recordWithCartridgeType:(Analyser::Static::MSXCartridgeType)cartridgeType;
@property(nonatomic, readonly) Analyser::Static::MSX::Cartridge::Type cartridgeType;
+ (instancetype)recordWithCartridgeType:(Analyser::Static::MSX::Cartridge::Type)cartridgeType;
@end
@implementation MSXROMRecord
+ (instancetype)recordWithCartridgeType:(Analyser::Static::MSXCartridgeType)cartridgeType {
+ (instancetype)recordWithCartridgeType:(Analyser::Static::MSX::Cartridge::Type)cartridgeType {
MSXROMRecord *record = [[MSXROMRecord alloc] init];
record->_cartridgeType = cartridgeType;
return record;
}
@end
#define Record(sha, type) sha : [MSXROMRecord recordWithCartridgeType:Analyser::Static::MSXCartridgeType::type],
#define Record(sha, type) sha : [MSXROMRecord recordWithCartridgeType:Analyser::Static::MSX::Cartridge::type],
static NSDictionary<NSString *, MSXROMRecord *> *romRecordsBySHA1 = @{
Record(@"da397e783d677d1a78fff222d9d6cb48b915dada", ASCII8kb) // 1942 (1986)(ASCII)(JP).rom
Record(@"0733cd627467a866846e15caf1770a5594eaf4cc", ASCII8kb) // 1942 (1986)(ASCII)(JP)[a].rom
@ -222,7 +223,13 @@ static NSDictionary<NSString *, MSXROMRecord *> *romRecordsBySHA1 = @{
// assert equality
XCTAssert(!targets.empty(), "%@ should be recognised as an MSX file", testFile);
if(!targets.empty()) {
XCTAssert(targets.front()->msx.cartridge_type == romRecord.cartridgeType, @"%@; should be %d, is %d", testFile, romRecord.cartridgeType, targets.front()->msx.cartridge_type);
XCTAssert(!targets.front()->media.cartridges.empty(), "%@ should be interpreted as a cartridge", testFile);
if(!targets.front()->media.cartridges.empty()) {
const Analyser::Static::MSX::Cartridge *const cartridge =
dynamic_cast<Analyser::Static::MSX::Cartridge *>(targets.front()->media.cartridges.front().get());
XCTAssert(cartridge->type == romRecord.cartridgeType, @"%@; should be %d, is %d", testFile, romRecord.cartridgeType, cartridge->type);
}
}
}
}

View File

@ -29,8 +29,9 @@ enum Type: IntType {
Acorn = AcornAtom | AcornElectron | BBCMaster | BBCModelA | BBCModelB,
ZX8081 = ZX80 | ZX81,
AllTape = Acorn | AmstradCPC | Commodore | Oric | ZX80 | ZX81 | MSX,
AllCartridge = Atari2600 | AcornElectron | ColecoVision | MSX,
AllDisk = Acorn | AmstradCPC | Commodore | Oric | MSX,
AllTape = Acorn | AmstradCPC | Commodore | Oric | ZX80 | ZX81 | MSX,
};
class TypeDistinguisher {