mirror of
https://github.com/TomHarte/CLK.git
synced 2024-09-06 05:54:28 +00:00
Merge pull request #328 from TomHarte/MSXROMs
Introduces a basic attempt at MSX MegaROM support
This commit is contained in:
commit
73b4e1722b
5
.gitignore
vendored
5
.gitignore
vendored
@ -18,9 +18,10 @@ DerivedData
|
||||
*.xcuserstate
|
||||
.DS_Store
|
||||
|
||||
# Exclude system ROMs
|
||||
# Exclude system ROMs and unit test ROMs
|
||||
ROMImages/*
|
||||
OSBindings/Mac/Clock SignalTests/Atari\ ROMs
|
||||
OSBindings/Mac/Clock SignalTests/Atari ROMs
|
||||
OSBindings/Mac/Clock SignalTests/MSX ROMs
|
||||
|
||||
# Exclude intermediate build products
|
||||
*.o
|
||||
|
42
Machines/MSX/Cartridges/ASCII16kb.hpp
Normal file
42
Machines/MSX/Cartridges/ASCII16kb.hpp
Normal file
@ -0,0 +1,42 @@
|
||||
//
|
||||
// ASCII16kb.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 04/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ASCII16kb_hpp
|
||||
#define ASCII16kb_hpp
|
||||
|
||||
#include "../ROMSlotHandler.hpp"
|
||||
|
||||
namespace MSX {
|
||||
namespace Cartridge {
|
||||
|
||||
class ASCII16kbROMSlotHandler: public ROMSlotHandler {
|
||||
public:
|
||||
ASCII16kbROMSlotHandler(MSX::MemoryMap &map, int slot) :
|
||||
map_(map), slot_(slot) {}
|
||||
|
||||
void write(uint16_t address, uint8_t value) {
|
||||
switch(address >> 11) {
|
||||
default: break;
|
||||
case 0xc:
|
||||
map_.map(slot_, value * 8192, 0x4000, 0x4000);
|
||||
break;
|
||||
case 0xe:
|
||||
map_.map(slot_, value * 8192, 0x8000, 0x4000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
MSX::MemoryMap &map_;
|
||||
int slot_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ASCII16kb_hpp */
|
48
Machines/MSX/Cartridges/ASCII8kb.hpp
Normal file
48
Machines/MSX/Cartridges/ASCII8kb.hpp
Normal file
@ -0,0 +1,48 @@
|
||||
//
|
||||
// ASCII8kb.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 04/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ASCII8kb_hpp
|
||||
#define ASCII8kb_hpp
|
||||
|
||||
#include "../ROMSlotHandler.hpp"
|
||||
|
||||
namespace MSX {
|
||||
namespace Cartridge {
|
||||
|
||||
class ASCII8kbROMSlotHandler: public ROMSlotHandler {
|
||||
public:
|
||||
ASCII8kbROMSlotHandler(MSX::MemoryMap &map, int slot) :
|
||||
map_(map), slot_(slot) {}
|
||||
|
||||
void write(uint16_t address, uint8_t value) {
|
||||
switch(address >> 11) {
|
||||
default: break;
|
||||
case 0xc:
|
||||
map_.map(slot_, value * 8192, 0x4000, 0x2000);
|
||||
break;
|
||||
case 0xd:
|
||||
map_.map(slot_, value * 8192, 0x6000, 0x2000);
|
||||
break;
|
||||
case 0xe:
|
||||
map_.map(slot_, value * 8192, 0x8000, 0x2000);
|
||||
break;
|
||||
case 0xf:
|
||||
map_.map(slot_, value * 8192, 0xa000, 0x2000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
MSX::MemoryMap &map_;
|
||||
int slot_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ASCII8kb_hpp */
|
45
Machines/MSX/Cartridges/Konami.hpp
Normal file
45
Machines/MSX/Cartridges/Konami.hpp
Normal file
@ -0,0 +1,45 @@
|
||||
//
|
||||
// Konami.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 04/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Konami_hpp
|
||||
#define Konami_hpp
|
||||
|
||||
#include "../ROMSlotHandler.hpp"
|
||||
|
||||
namespace MSX {
|
||||
namespace Cartridge {
|
||||
|
||||
class KonamiROMSlotHandler: public ROMSlotHandler {
|
||||
public:
|
||||
KonamiROMSlotHandler(MSX::MemoryMap &map, int slot) :
|
||||
map_(map), slot_(slot) {}
|
||||
|
||||
void write(uint16_t address, uint8_t value) {
|
||||
switch(address >> 13) {
|
||||
default: break;
|
||||
case 3:
|
||||
map_.map(slot_, value * 8192, 0x6000, 0x2000);
|
||||
break;
|
||||
case 4:
|
||||
map_.map(slot_, value * 8192, 0x8000, 0x2000);
|
||||
break;
|
||||
case 5:
|
||||
map_.map(slot_, value * 8192, 0xa000, 0x2000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
MSX::MemoryMap &map_;
|
||||
int slot_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* Konami_hpp */
|
48
Machines/MSX/Cartridges/KonamiWithSCC.hpp
Normal file
48
Machines/MSX/Cartridges/KonamiWithSCC.hpp
Normal file
@ -0,0 +1,48 @@
|
||||
//
|
||||
// KonamiWithSCC.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 04/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef KonamiWithSCC_hpp
|
||||
#define KonamiWithSCC_hpp
|
||||
|
||||
#include "../ROMSlotHandler.hpp"
|
||||
|
||||
namespace MSX {
|
||||
namespace Cartridge {
|
||||
|
||||
class KonamiWithSCCROMSlotHandler: public ROMSlotHandler {
|
||||
public:
|
||||
KonamiWithSCCROMSlotHandler(MSX::MemoryMap &map, int slot) :
|
||||
map_(map), slot_(slot) {}
|
||||
|
||||
void write(uint16_t address, uint8_t value) {
|
||||
switch(address >> 11) {
|
||||
default: break;
|
||||
case 0x0a:
|
||||
map_.map(slot_, value * 8192, 0x4000, 0x2000);
|
||||
break;
|
||||
case 0x0e:
|
||||
map_.map(slot_, value * 8192, 0x6000, 0x2000);
|
||||
break;
|
||||
case 0x12:
|
||||
map_.map(slot_, value * 8192, 0x8000, 0x2000);
|
||||
break;
|
||||
case 0x16:
|
||||
map_.map(slot_, value * 8192, 0xa000, 0x2000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
MSX::MemoryMap &map_;
|
||||
int slot_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* KonamiWithSCC_hpp */
|
@ -9,6 +9,12 @@
|
||||
#include "MSX.hpp"
|
||||
|
||||
#include "Keyboard.hpp"
|
||||
#include "ROMSlotHandler.hpp"
|
||||
|
||||
#include "Cartridges/ASCII8kb.hpp"
|
||||
#include "Cartridges/ASCII16kb.hpp"
|
||||
#include "Cartridges/Konami.hpp"
|
||||
#include "Cartridges/KonamiWithSCC.hpp"
|
||||
|
||||
#include "../../Processors/Z80/Z80.hpp"
|
||||
|
||||
@ -106,7 +112,8 @@ class ConcreteMachine:
|
||||
public CRTMachine::Machine,
|
||||
public ConfigurationTarget::Machine,
|
||||
public KeyboardMachine::Machine,
|
||||
public Configurable::Device {
|
||||
public Configurable::Device,
|
||||
public MemoryMap {
|
||||
public:
|
||||
ConcreteMachine():
|
||||
z80_(*this),
|
||||
@ -152,18 +159,30 @@ class ConcreteMachine:
|
||||
if(target.loading_command.length()) {
|
||||
type_string(target.loading_command);
|
||||
}
|
||||
|
||||
switch(target.msx.cartridge_type) {
|
||||
default: break;
|
||||
case StaticAnalyser::MSXCartridgeType::Konami:
|
||||
memory_slots_[1].set_handler(new Cartridge::KonamiROMSlotHandler(*this, 1));
|
||||
break;
|
||||
case StaticAnalyser::MSXCartridgeType::KonamiWithSCC:
|
||||
// TODO: enable an SCC.
|
||||
memory_slots_[1].set_handler(new Cartridge::KonamiWithSCCROMSlotHandler(*this, 1));
|
||||
break;
|
||||
case StaticAnalyser::MSXCartridgeType::ASCII8kb:
|
||||
memory_slots_[1].set_handler(new Cartridge::ASCII8kbROMSlotHandler(*this, 1));
|
||||
break;
|
||||
case StaticAnalyser::MSXCartridgeType::ASCII16kb:
|
||||
memory_slots_[1].set_handler(new Cartridge::ASCII16kbROMSlotHandler(*this, 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool insert_media(const StaticAnalyser::Media &media) override {
|
||||
if(!media.cartridges.empty()) {
|
||||
const auto &segment = media.cartridges.front()->get_segments().front();
|
||||
cartridge_ = segment.data;
|
||||
|
||||
// TODO: should clear other page 1 pointers, should allow for paging cartridges, etc.
|
||||
size_t base = segment.start_address >> 14;
|
||||
for(size_t c = 0; c < cartridge_.size(); c += 16384) {
|
||||
memory_slots_[1].read_pointers[(c >> 14) + base] = cartridge_.data() + c;
|
||||
}
|
||||
memory_slots_[1].source = segment.data;
|
||||
map(1, 0, static_cast<uint16_t>(segment.start_address), std::min(segment.data.size(), 65536 - segment.start_address));
|
||||
}
|
||||
|
||||
if(!media.tapes.empty()) {
|
||||
@ -177,14 +196,47 @@ class ConcreteMachine:
|
||||
input_text_ += string;
|
||||
}
|
||||
|
||||
// MARK: MSX::MemoryMap
|
||||
void map(int slot, std::size_t source_address, uint16_t destination_address, std::size_t length) override {
|
||||
assert(!(destination_address & 8191));
|
||||
assert(!(length & 8191));
|
||||
assert(static_cast<std::size_t>(destination_address) + length <= 65536);
|
||||
|
||||
for(std::size_t c = 0; c < (length >> 13); ++c) {
|
||||
if(memory_slots_[slot].wrapping_strategy == ROMSlotHandler::WrappingStrategy::Repeat) source_address %= memory_slots_[slot].source.size();
|
||||
memory_slots_[slot].read_pointers[(destination_address >> 13) + c] =
|
||||
(source_address < memory_slots_[slot].source.size()) ? &memory_slots_[slot].source[source_address] : unpopulated_;
|
||||
source_address += 8192;
|
||||
}
|
||||
|
||||
page_memory(paged_memory_);
|
||||
}
|
||||
|
||||
void unmap(int slot, uint16_t destination_address, std::size_t length) override {
|
||||
assert(!(destination_address & 8191));
|
||||
assert(!(length & 8191));
|
||||
assert(static_cast<std::size_t>(destination_address) + length <= 65536);
|
||||
|
||||
for(std::size_t c = 0; c < (length >> 13); ++c) {
|
||||
memory_slots_[slot].read_pointers[(destination_address >> 13) + c] = nullptr;
|
||||
}
|
||||
|
||||
page_memory(paged_memory_);
|
||||
}
|
||||
|
||||
// MARK: Ordinary paging.
|
||||
void page_memory(uint8_t value) {
|
||||
for(size_t c = 0; c < 4; ++c) {
|
||||
paged_memory_ = value;
|
||||
for(std::size_t c = 0; c < 8; c += 2) {
|
||||
read_pointers_[c] = memory_slots_[value & 3].read_pointers[c];
|
||||
write_pointers_[c] = memory_slots_[value & 3].write_pointers[c];
|
||||
read_pointers_[c+1] = memory_slots_[value & 3].read_pointers[c+1];
|
||||
write_pointers_[c+1] = memory_slots_[value & 3].write_pointers[c+1];
|
||||
value >>= 2;
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Z80::BusHandler
|
||||
HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
|
||||
if(time_until_interrupt_ > 0) {
|
||||
time_until_interrupt_ -= cycle.length;
|
||||
@ -196,7 +248,7 @@ class ConcreteMachine:
|
||||
uint16_t address = cycle.address ? *cycle.address : 0x0000;
|
||||
switch(cycle.operation) {
|
||||
case CPU::Z80::PartialMachineCycle::ReadOpcode:
|
||||
if(use_fast_tape_) {
|
||||
if(use_fast_tape_ && tape_player_.has_tape()) {
|
||||
if(address == 0x1a63) {
|
||||
// TAPION
|
||||
|
||||
@ -251,12 +303,24 @@ class ConcreteMachine:
|
||||
}
|
||||
}
|
||||
case CPU::Z80::PartialMachineCycle::Read:
|
||||
*cycle.value = read_pointers_[address >> 14][address & 16383];
|
||||
if(read_pointers_[address >> 13]) {
|
||||
*cycle.value = read_pointers_[address >> 13][address & 8191];
|
||||
} else {
|
||||
int slot_hit = (paged_memory_ >> ((address >> 14) * 2)) & 3;
|
||||
memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush());
|
||||
*cycle.value = memory_slots_[slot_hit].handler->read(address);
|
||||
}
|
||||
break;
|
||||
|
||||
case CPU::Z80::PartialMachineCycle::Write:
|
||||
write_pointers_[address >> 14][address & 16383] = *cycle.value;
|
||||
break;
|
||||
case CPU::Z80::PartialMachineCycle::Write: {
|
||||
write_pointers_[address >> 13][address & 8191] = *cycle.value;
|
||||
|
||||
int slot_hit = (paged_memory_ >> ((address >> 14) * 2)) & 3;
|
||||
if(memory_slots_[slot_hit].handler) {
|
||||
memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush());
|
||||
memory_slots_[slot_hit].handler->write(address, *cycle.value);
|
||||
}
|
||||
} break;
|
||||
|
||||
case CPU::Z80::PartialMachineCycle::Input:
|
||||
switch(address & 0xff) {
|
||||
@ -354,6 +418,10 @@ class ConcreteMachine:
|
||||
HalfCycles addition((cycle.operation == CPU::Z80::PartialMachineCycle::ReadOpcode) ? 2 : 0);
|
||||
time_since_vdp_update_ += cycle.length + addition;
|
||||
time_since_ay_update_ += cycle.length + addition;
|
||||
memory_slots_[0].cycles_since_update += cycle.length + addition;
|
||||
memory_slots_[1].cycles_since_update += cycle.length + addition;
|
||||
memory_slots_[2].cycles_since_update += cycle.length + addition;
|
||||
memory_slots_[3].cycles_since_update += cycle.length + addition;
|
||||
return addition;
|
||||
}
|
||||
|
||||
@ -373,26 +441,21 @@ class ConcreteMachine:
|
||||
|
||||
if(!roms[0]) return false;
|
||||
|
||||
rom_ = std::move(*roms[0]);
|
||||
rom_.resize(32768);
|
||||
memory_slots_[0].source = std::move(*roms[0]);
|
||||
memory_slots_[0].source.resize(32768);
|
||||
|
||||
for(size_t c = 0; c < 4; ++c) {
|
||||
for(size_t c = 0; c < 8; ++c) {
|
||||
for(size_t slot = 0; slot < 3; ++slot) {
|
||||
memory_slots_[slot].read_pointers[c] = unpopulated_;
|
||||
memory_slots_[slot].write_pointers[c] = scratch_;
|
||||
}
|
||||
|
||||
memory_slots_[3].read_pointers[c] =
|
||||
memory_slots_[3].write_pointers[c] = &ram_[c * 16384];
|
||||
memory_slots_[3].write_pointers[c] = &ram_[c * 8192];
|
||||
}
|
||||
|
||||
memory_slots_[0].read_pointers[0] = rom_.data();
|
||||
memory_slots_[0].read_pointers[1] = &rom_[16384];
|
||||
|
||||
for(size_t c = 0; c < 4; ++c) {
|
||||
read_pointers_[c] = memory_slots_[0].read_pointers[c];
|
||||
write_pointers_[c] = memory_slots_[0].write_pointers[c];
|
||||
}
|
||||
map(0, 0, 0, 32768);
|
||||
page_memory(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -514,19 +577,28 @@ class ConcreteMachine:
|
||||
i8255PortHandler i8255_port_handler_;
|
||||
AYPortHandler ay_port_handler_;
|
||||
|
||||
uint8_t *read_pointers_[4];
|
||||
uint8_t *write_pointers_[4];
|
||||
uint8_t paged_memory_ = 0;
|
||||
uint8_t *read_pointers_[8];
|
||||
uint8_t *write_pointers_[8];
|
||||
|
||||
struct MemorySlots {
|
||||
uint8_t *read_pointers[4];
|
||||
uint8_t *write_pointers[4];
|
||||
uint8_t *read_pointers[8];
|
||||
uint8_t *write_pointers[8];
|
||||
|
||||
void set_handler(ROMSlotHandler *slot_handler) {
|
||||
handler.reset(slot_handler);
|
||||
wrapping_strategy = handler->wrapping_strategy();
|
||||
}
|
||||
|
||||
std::unique_ptr<ROMSlotHandler> handler;
|
||||
std::vector<uint8_t> source;
|
||||
HalfCycles cycles_since_update;
|
||||
ROMSlotHandler::WrappingStrategy wrapping_strategy = ROMSlotHandler::WrappingStrategy::Repeat;
|
||||
} memory_slots_[4];
|
||||
|
||||
uint8_t ram_[65536];
|
||||
uint8_t scratch_[16384];
|
||||
uint8_t unpopulated_[16384];
|
||||
std::vector<uint8_t> rom_;
|
||||
std::vector<uint8_t> cartridge_;
|
||||
uint8_t scratch_[8192];
|
||||
uint8_t unpopulated_[8192];
|
||||
|
||||
HalfCycles time_since_vdp_update_;
|
||||
HalfCycles time_since_ay_update_;
|
||||
|
70
Machines/MSX/ROMSlotHandler.hpp
Normal file
70
Machines/MSX/ROMSlotHandler.hpp
Normal file
@ -0,0 +1,70 @@
|
||||
//
|
||||
// ROMSlotHandler.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 03/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ROMSlotHandler_hpp
|
||||
#define ROMSlotHandler_hpp
|
||||
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
/*
|
||||
Design assumptions:
|
||||
|
||||
- to-ROM writes and paging events are 'rare', so virtual call costs aren't worrisome;
|
||||
- ROM type variety is sufficiently slender that most of it can be built into the MSX.
|
||||
|
||||
Part of the motivation is also that the MSX has four logical slots, the ROM, RAM plus two
|
||||
things plugged in. So even if the base class were templated to remove the virtual call,
|
||||
there'd just be a switch on what to call.
|
||||
*/
|
||||
namespace MSX {
|
||||
|
||||
class MemoryMap {
|
||||
public:
|
||||
/*!
|
||||
Maps source data from the ROM's source to the given address range.
|
||||
*/
|
||||
virtual void map(int slot, std::size_t source_address, uint16_t destination_address, std::size_t length) = 0;
|
||||
|
||||
/*!
|
||||
Unmaps source data from the given address range; the slot handler's read function will be used
|
||||
to respond to queries in that range.
|
||||
*/
|
||||
virtual void unmap(int slot, uint16_t destination_address, std::size_t length) = 0;
|
||||
};
|
||||
|
||||
class ROMSlotHandler {
|
||||
public:
|
||||
/*! Advances time by @c half_cycles. */
|
||||
virtual void run_for(HalfCycles half_cycles) {}
|
||||
|
||||
/*! Announces an attempt to write @c value to @c address. */
|
||||
virtual void write(uint16_t address, uint8_t value) = 0;
|
||||
|
||||
/*! Seeks the result of a read at @c address; this is used only if the area is unmapped. */
|
||||
virtual uint8_t read(uint16_t address) { return 0xff; }
|
||||
|
||||
enum class WrappingStrategy {
|
||||
/// Repeat causes all accesses to be modulo the size of the ROM.
|
||||
Repeat,
|
||||
/// Empty causes all out-of-bounds accesses to read a vacant bus.
|
||||
Empty
|
||||
};
|
||||
/*!
|
||||
Returns the wrapping strategy to apply to mapping requests from this ROM slot.
|
||||
*/
|
||||
virtual WrappingStrategy wrapping_strategy() const {
|
||||
return WrappingStrategy::Repeat;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* ROMSlotHandler_hpp */
|
@ -267,6 +267,10 @@
|
||||
4B92EACA1B7C112B00246143 /* 6502TimingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */; };
|
||||
4B95FA9D1F11893B0008E395 /* ZX8081OptionsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B95FA9C1F11893B0008E395 /* ZX8081OptionsPanel.swift */; };
|
||||
4B96F7221D75119A0058BB2D /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B96F7201D75119A0058BB2D /* Tape.cpp */; };
|
||||
4B98A05E1FFAD3F600ADF63B /* CSROMFetcher.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B98A05D1FFAD3F600ADF63B /* CSROMFetcher.mm */; };
|
||||
4B98A05F1FFAD62400ADF63B /* CSROMFetcher.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B98A05D1FFAD3F600ADF63B /* CSROMFetcher.mm */; };
|
||||
4B98A0611FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */; };
|
||||
4B98A1CE1FFADEC500ADF63B /* MSX ROMs in Resources */ = {isa = PBXBuildFile; fileRef = 4B98A1CD1FFADEC400ADF63B /* MSX ROMs */; };
|
||||
4B9C9D751FF81CC00030A129 /* Z80.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B9C9D731FF81CC00030A129 /* Z80.cpp */; };
|
||||
4B9C9D781FF81ED30030A129 /* AddressMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B9C9D761FF81ED30030A129 /* AddressMapper.cpp */; };
|
||||
4B9CCDA11DA279CA0098B625 /* Vic20OptionsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B9CCDA01DA279CA0098B625 /* Vic20OptionsPanel.swift */; };
|
||||
@ -673,6 +677,10 @@
|
||||
4B1497971EE4B97F00CE2596 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/ZX8081Options.xib"; sourceTree = SOURCE_ROOT; };
|
||||
4B1558BE1F844ECD006E9A97 /* BitReverse.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = BitReverse.cpp; path = Data/BitReverse.cpp; sourceTree = "<group>"; };
|
||||
4B1558BF1F844ECD006E9A97 /* BitReverse.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = BitReverse.hpp; path = Data/BitReverse.hpp; sourceTree = "<group>"; };
|
||||
4B1667F61FFF1E2400A16032 /* Konami.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Konami.hpp; path = MSX/Cartridges/Konami.hpp; sourceTree = "<group>"; };
|
||||
4B1667F91FFF215E00A16032 /* ASCII16kb.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ASCII16kb.hpp; path = MSX/Cartridges/ASCII16kb.hpp; sourceTree = "<group>"; };
|
||||
4B1667FA1FFF215E00A16032 /* ASCII8kb.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ASCII8kb.hpp; path = MSX/Cartridges/ASCII8kb.hpp; sourceTree = "<group>"; };
|
||||
4B1667FB1FFF215F00A16032 /* KonamiWithSCC.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = KonamiWithSCC.hpp; path = MSX/Cartridges/KonamiWithSCC.hpp; sourceTree = "<group>"; };
|
||||
4B1BA0881FD4967700CB4ADA /* CSMSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSMSX.h; sourceTree = "<group>"; };
|
||||
4B1BA0891FD4967800CB4ADA /* CSMSX.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CSMSX.mm; sourceTree = "<group>"; };
|
||||
4B1BA08C1FD498B000CB4ADA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/MSXOptions.xib"; sourceTree = SOURCE_ROOT; };
|
||||
@ -840,6 +848,7 @@
|
||||
4B6A4C951F58F09E00E3F787 /* 6502Base.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 6502Base.cpp; sourceTree = "<group>"; };
|
||||
4B7041271F92C26900735E45 /* JoystickMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = JoystickMachine.hpp; sourceTree = "<group>"; };
|
||||
4B70412A1F92C2A700735E45 /* Joystick.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Joystick.hpp; sourceTree = "<group>"; };
|
||||
4B70EF6A1FFDCDF400A3494E /* ROMSlotHandler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ROMSlotHandler.hpp; path = MSX/ROMSlotHandler.hpp; sourceTree = "<group>"; };
|
||||
4B7136841F78724F008B8ED9 /* Encoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Encoder.cpp; sourceTree = "<group>"; };
|
||||
4B7136851F78724F008B8ED9 /* Encoder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Encoder.hpp; sourceTree = "<group>"; };
|
||||
4B7136871F78725F008B8ED9 /* Shifter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Shifter.cpp; sourceTree = "<group>"; };
|
||||
@ -901,6 +910,10 @@
|
||||
4B95FA9C1F11893B0008E395 /* ZX8081OptionsPanel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZX8081OptionsPanel.swift; sourceTree = "<group>"; };
|
||||
4B96F7201D75119A0058BB2D /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tape.cpp; path = ../../StaticAnalyser/Acorn/Tape.cpp; sourceTree = "<group>"; };
|
||||
4B96F7211D75119A0058BB2D /* Tape.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Tape.hpp; path = ../../StaticAnalyser/Acorn/Tape.hpp; sourceTree = "<group>"; };
|
||||
4B98A05C1FFAD3F600ADF63B /* CSROMFetcher.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CSROMFetcher.hpp; sourceTree = "<group>"; };
|
||||
4B98A05D1FFAD3F600ADF63B /* CSROMFetcher.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CSROMFetcher.mm; sourceTree = "<group>"; };
|
||||
4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MSXStaticAnalyserTests.mm; sourceTree = "<group>"; };
|
||||
4B98A1CD1FFADEC400ADF63B /* MSX ROMs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "MSX ROMs"; sourceTree = "<group>"; };
|
||||
4B9C9D731FF81CC00030A129 /* Z80.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Z80.cpp; path = ../../StaticAnalyser/Disassembler/Z80.cpp; sourceTree = "<group>"; };
|
||||
4B9C9D741FF81CC00030A129 /* Z80.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Z80.hpp; path = ../../StaticAnalyser/Disassembler/Z80.hpp; sourceTree = "<group>"; };
|
||||
4B9C9D761FF81ED30030A129 /* AddressMapper.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = AddressMapper.cpp; path = ../../StaticAnalyser/Disassembler/AddressMapper.cpp; sourceTree = "<group>"; };
|
||||
@ -1421,6 +1434,7 @@
|
||||
4B1414631B588A1100E04248 /* Test Binaries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B98A1CD1FFADEC400ADF63B /* MSX ROMs */,
|
||||
4B9252CD1E74D28200B76AF1 /* Atari ROMs */,
|
||||
4B44EBF81DC9898E00A7820C /* BCDTEST_beeb */,
|
||||
4B44EBF61DC9883B00A7820C /* 6502_functional_test.bin */,
|
||||
@ -1454,6 +1468,17 @@
|
||||
name = ZX8081;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B1667F81FFF1E2900A16032 /* Cartridges */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B1667FA1FFF215E00A16032 /* ASCII8kb.hpp */,
|
||||
4B1667F91FFF215E00A16032 /* ASCII16kb.hpp */,
|
||||
4B1667FB1FFF215F00A16032 /* KonamiWithSCC.hpp */,
|
||||
4B1667F61FFF1E2400A16032 /* Konami.hpp */,
|
||||
);
|
||||
name = Cartridges;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B1E85791D174DEC001EF87D /* 6532 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1501,9 +1526,11 @@
|
||||
4B2A53951D117D36003C6002 /* CSMachine.h */,
|
||||
4B2A53941D117D36003C6002 /* CSMachine+Subclassing.h */,
|
||||
4B643F3C1D77AE5C00D431D6 /* CSMachine+Target.h */,
|
||||
4B98A05C1FFAD3F600ADF63B /* CSROMFetcher.hpp */,
|
||||
4B2A53971D117D36003C6002 /* KeyCodes.h */,
|
||||
4B8FE2251DA1DE2D0090D3CE /* NSBundle+DataResource.h */,
|
||||
4BA61EAE1D91515900B3C876 /* NSData+StdVector.h */,
|
||||
4B98A05D1FFAD3F600ADF63B /* CSROMFetcher.mm */,
|
||||
4B8FE2261DA1DE2D0090D3CE /* NSBundle+DataResource.m */,
|
||||
4B2A53961D117D36003C6002 /* CSMachine.mm */,
|
||||
4BA61EAF1D91515900B3C876 /* NSData+StdVector.mm */,
|
||||
@ -1973,10 +2000,12 @@
|
||||
4B79A4FC1FC8FF9800EEDAD5 /* MSX */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B79A4FF1FC913C900EEDAD5 /* MSX.cpp */,
|
||||
4B79A5001FC913C900EEDAD5 /* MSX.hpp */,
|
||||
4B12C0EB1FCFA98D005BFD93 /* Keyboard.cpp */,
|
||||
4B79A4FF1FC913C900EEDAD5 /* MSX.cpp */,
|
||||
4B12C0EC1FCFA98D005BFD93 /* Keyboard.hpp */,
|
||||
4B79A5001FC913C900EEDAD5 /* MSX.hpp */,
|
||||
4B70EF6A1FFDCDF400A3494E /* ROMSlotHandler.hpp */,
|
||||
4B1667F81FFF1E2900A16032 /* Cartridges */,
|
||||
);
|
||||
name = MSX;
|
||||
sourceTree = "<group>";
|
||||
@ -2431,6 +2460,7 @@
|
||||
4BB73EB51B587A5100552FC2 /* Clock SignalTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */,
|
||||
4B5073091DDFCFDF00C48FBD /* ArrayBuilderTests.mm */,
|
||||
4B924E981E74D22700B76AF1 /* AtariStaticAnalyserTests.mm */,
|
||||
4BB2A9AE1E13367E001A5C23 /* CRCTests.mm */,
|
||||
@ -3087,6 +3117,7 @@
|
||||
4B9252CE1E74D28200B76AF1 /* Atari ROMs in Resources */,
|
||||
4BB2997D1B587D8400A49093 /* ldxay in Resources */,
|
||||
4BB299D71B587D8400A49093 /* staax in Resources */,
|
||||
4B98A1CE1FFADEC500ADF63B /* MSX ROMs in Resources */,
|
||||
4BB2990C1B587D8400A49093 /* asoax in Resources */,
|
||||
4BB299191B587D8400A49093 /* bita in Resources */,
|
||||
4BB2992A1B587D8400A49093 /* cia2ta in Resources */,
|
||||
@ -3480,6 +3511,7 @@
|
||||
4B4518871F75E91A00926311 /* DigitalPhaseLockedLoop.cpp in Sources */,
|
||||
4B2A53A31D117D36003C6002 /* CSVic20.mm in Sources */,
|
||||
4B2A53A21D117D36003C6002 /* CSElectron.mm in Sources */,
|
||||
4B98A05E1FFAD3F600ADF63B /* CSROMFetcher.mm in Sources */,
|
||||
4B8FE2201DA19D7C0090D3CE /* Atari2600OptionsPanel.swift in Sources */,
|
||||
4B8805F41DCFD22A003085B1 /* Commodore.cpp in Sources */,
|
||||
4B2E2D9A1C3A06EC00138695 /* Atari2600.cpp in Sources */,
|
||||
@ -3527,6 +3559,7 @@
|
||||
files = (
|
||||
4B1E85811D176468001EF87D /* 6532Tests.swift in Sources */,
|
||||
4BDDBA991EF3451200347E61 /* Z80MachineCycleTests.swift in Sources */,
|
||||
4B98A05F1FFAD62400ADF63B /* CSROMFetcher.mm in Sources */,
|
||||
4BC9E1EE1D23449A003FCEE4 /* 6502InterruptTests.swift in Sources */,
|
||||
4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */,
|
||||
4B924E991E74D22700B76AF1 /* AtariStaticAnalyserTests.mm in Sources */,
|
||||
@ -3543,6 +3576,7 @@
|
||||
4BB73EB71B587A5100552FC2 /* AllSuiteATests.swift in Sources */,
|
||||
4B01A6881F22F0DB001FD6E3 /* Z80MemptrTests.swift in Sources */,
|
||||
4B121F9B1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm in Sources */,
|
||||
4B98A0611FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm in Sources */,
|
||||
4BEF6AAC1D35D1C400E73575 /* DPLLTests.swift in Sources */,
|
||||
4B3BA0CF1D318B44005DD7A7 /* MOS6522Bridge.mm in Sources */,
|
||||
4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */,
|
||||
|
@ -10,6 +10,8 @@
|
||||
#import "CSMachine+Subclassing.h"
|
||||
#import "CSMachine+Target.h"
|
||||
|
||||
#include "CSROMFetcher.hpp"
|
||||
|
||||
#include "ConfigurationTarget.hpp"
|
||||
#include "JoystickMachine.hpp"
|
||||
#include "KeyboardMachine.hpp"
|
||||
@ -74,23 +76,7 @@ struct MachineDelegate: CRTMachine::Machine::Delegate, public LockProtectedDeleg
|
||||
_speakerDelegate.machineAccessLock = _delegateMachineAccessLock;
|
||||
|
||||
_machine->crt_machine()->set_delegate(&_machineDelegate);
|
||||
_machine->crt_machine()->set_rom_fetcher( [] (const std::string &machine, const std::vector<std::string> &names) -> std::vector<std::unique_ptr<std::vector<std::uint8_t>>> {
|
||||
NSString *subDirectory = [@"ROMImages/" stringByAppendingString:[NSString stringWithUTF8String:machine.c_str()]];
|
||||
std::vector<std::unique_ptr<std::vector<std::uint8_t>>> results;
|
||||
for(auto &name: names) {
|
||||
NSData *fileData = [[NSBundle mainBundle] dataForResource:[NSString stringWithUTF8String:name.c_str()] withExtension:nil subdirectory:subDirectory];
|
||||
|
||||
if(!fileData)
|
||||
results.emplace_back(nullptr);
|
||||
else {
|
||||
std::unique_ptr<std::vector<std::uint8_t>> data(new std::vector<std::uint8_t>);
|
||||
*data = fileData.stdVector8;
|
||||
results.emplace_back(std::move(data));
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
});
|
||||
CSApplyROMFetcher(*_machine->crt_machine());
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
11
OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.hpp
Normal file
11
OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
//
|
||||
// ROMFetcher.h
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 01/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "ROMMachine.hpp"
|
||||
|
||||
void CSApplyROMFetcher(ROMMachine::Machine &rom_machine);
|
35
OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.mm
Normal file
35
OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.mm
Normal file
@ -0,0 +1,35 @@
|
||||
//
|
||||
// CSROMFetcher.m
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 01/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#include "CSROMFetcher.hpp"
|
||||
|
||||
#import "NSBundle+DataResource.h"
|
||||
#import "NSData+StdVector.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
void CSApplyROMFetcher(ROMMachine::Machine &rom_machine) {
|
||||
rom_machine.set_rom_fetcher( [] (const std::string &machine, const std::vector<std::string> &names) -> std::vector<std::unique_ptr<std::vector<std::uint8_t>>> {
|
||||
NSString *subDirectory = [@"ROMImages/" stringByAppendingString:[NSString stringWithUTF8String:machine.c_str()]];
|
||||
std::vector<std::unique_ptr<std::vector<std::uint8_t>>> results;
|
||||
for(auto &name: names) {
|
||||
NSData *fileData = [[NSBundle mainBundle] dataForResource:[NSString stringWithUTF8String:name.c_str()] withExtension:nil subdirectory:subDirectory];
|
||||
|
||||
if(!fileData)
|
||||
results.emplace_back(nullptr);
|
||||
else {
|
||||
std::unique_ptr<std::vector<std::uint8_t>> data(new std::vector<std::uint8_t>);
|
||||
*data = fileData.stdVector8;
|
||||
results.emplace_back(std::move(data));
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
});
|
||||
}
|
@ -10,8 +10,7 @@
|
||||
|
||||
@implementation NSBundle (DataResource)
|
||||
|
||||
- (NSData *)dataForResource:(NSString *)resource withExtension:(NSString *)extension subdirectory:(NSString *)subdirectory
|
||||
{
|
||||
- (NSData *)dataForResource:(NSString *)resource withExtension:(NSString *)extension subdirectory:(NSString *)subdirectory {
|
||||
NSURL *url = [self URLForResource:resource withExtension:extension subdirectory:subdirectory];
|
||||
if(!url) return nil;
|
||||
return [NSData dataWithContentsOfURL:url];
|
||||
|
@ -15,6 +15,5 @@
|
||||
@property (nonatomic) BOOL clockLine;
|
||||
|
||||
- (void)runForCycles:(NSUInteger)numberOfCycles;
|
||||
- (void)setROM:(NSData *)ROM;
|
||||
|
||||
@end
|
||||
|
@ -9,6 +9,7 @@
|
||||
#import "C1540Bridge.h"
|
||||
#include "C1540.hpp"
|
||||
#include "NSData+StdVector.h"
|
||||
#include "CSROMFetcher.hpp"
|
||||
|
||||
class VanillaSerialPort: public Commodore::Serial::Port {
|
||||
public:
|
||||
@ -20,7 +21,7 @@ class VanillaSerialPort: public Commodore::Serial::Port {
|
||||
};
|
||||
|
||||
@implementation C1540Bridge {
|
||||
Commodore::C1540::Machine _c1540;
|
||||
std::unique_ptr<Commodore::C1540::Machine> _c1540;
|
||||
std::shared_ptr<Commodore::Serial::Bus> _serialBus;
|
||||
std::shared_ptr<VanillaSerialPort> _serialPort;
|
||||
}
|
||||
@ -31,18 +32,16 @@ class VanillaSerialPort: public Commodore::Serial::Port {
|
||||
_serialBus.reset(new ::Commodore::Serial::Bus);
|
||||
_serialPort.reset(new VanillaSerialPort);
|
||||
|
||||
_c1540.set_serial_bus(_serialBus);
|
||||
_c1540.reset(new Commodore::C1540::Machine(Commodore::C1540::Machine::C1540));
|
||||
CSApplyROMFetcher(*_c1540);
|
||||
_c1540->set_serial_bus(_serialBus);
|
||||
Commodore::Serial::AttachPortAndBus(_serialPort, _serialBus);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setROM:(NSData *)ROM {
|
||||
_c1540.set_rom(ROM.stdVector8);
|
||||
}
|
||||
|
||||
- (void)runForCycles:(NSUInteger)numberOfCycles {
|
||||
_c1540.run_for(Cycles((int)numberOfCycles));
|
||||
_c1540->run_for(Cycles((int)numberOfCycles));
|
||||
}
|
||||
|
||||
- (void)setAttentionLine:(BOOL)attentionLine {
|
||||
|
@ -12,12 +12,6 @@ class C1540Tests: XCTestCase {
|
||||
|
||||
fileprivate func with1540(_ action: (C1540Bridge) -> ()) {
|
||||
let bridge = C1540Bridge()
|
||||
|
||||
if let path = Bundle.main.path(forResource: "1541", ofType: "bin", inDirectory: "ROMImages/Commodore1540") {
|
||||
let data = try? Data(contentsOf: URL(fileURLWithPath: path))
|
||||
bridge.setROM(data)
|
||||
}
|
||||
|
||||
action(bridge)
|
||||
}
|
||||
|
||||
|
3
OSBindings/Mac/Clock SignalTests/MSX ROMs/readme.txt
Normal file
3
OSBindings/Mac/Clock SignalTests/MSX ROMs/readme.txt
Normal file
@ -0,0 +1,3 @@
|
||||
This folder is intended to contain commercial MSX ROM images; these are used to unit test the MSX static analyser.
|
||||
|
||||
Those tested are primarily from the Internet Archive’s version of the TOSEC collection.
|
230
OSBindings/Mac/Clock SignalTests/MSXStaticAnalyserTests.mm
Normal file
230
OSBindings/Mac/Clock SignalTests/MSXStaticAnalyserTests.mm
Normal file
@ -0,0 +1,230 @@
|
||||
//
|
||||
// AtariStaticAnalyserTests.m
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 11/03/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
#include "../../../StaticAnalyser/StaticAnalyser.hpp"
|
||||
|
||||
@interface MSXROMRecord : NSObject
|
||||
@property(nonatomic, readonly) StaticAnalyser::MSXCartridgeType cartridgeType;
|
||||
+ (instancetype)recordWithCartridgeType:(StaticAnalyser::MSXCartridgeType)cartridgeType;
|
||||
@end
|
||||
|
||||
@implementation MSXROMRecord
|
||||
+ (instancetype)recordWithCartridgeType:(StaticAnalyser::MSXCartridgeType)cartridgeType {
|
||||
MSXROMRecord *record = [[MSXROMRecord alloc] init];
|
||||
record->_cartridgeType = cartridgeType;
|
||||
return record;
|
||||
}
|
||||
@end
|
||||
|
||||
#define Record(sha, type) sha : [MSXROMRecord recordWithCartridgeType:StaticAnalyser::MSXCartridgeType::type],
|
||||
static NSDictionary<NSString *, MSXROMRecord *> *romRecordsBySHA1 = @{
|
||||
Record(@"da397e783d677d1a78fff222d9d6cb48b915dada", ASCII8kb) // 1942 (1986)(ASCII)(JP).rom
|
||||
Record(@"0733cd627467a866846e15caf1770a5594eaf4cc", ASCII8kb) // 1942 (1986)(ASCII)(JP)[a].rom
|
||||
Record(@"ba07b1b585386f887d4c7e457210b3fce819709a", Konami) // 1942 (1987)(Zemina)(KR).rom
|
||||
Record(@"dd1e87a16e5fb38d9d729ef7edc6da21146a99fe", Konami) // 1942 (1987)(Zemina)(KR)[a].rom
|
||||
Record(@"6bde4e6761286a2909858ecef04155e17072996e", ASCII8kb) // A Life M36 Planet - MotherBrain has Been Aliving (1987)(Pixel)(JP).rom
|
||||
Record(@"937464eb371c68add2236bcef91d24a8ce7c4ed1", KonamiWithSCC) // A1 Spirit - The Way to Formula 1 (1987)(Konami)(JP).rom
|
||||
Record(@"2639792df6f7c7cfaffc2616b0e1849f18897ace", ASCII16kb) // Aliens. Alien 2 (1987)(Square)(JP).rom
|
||||
Record(@"0d9c472cf7687b86f3fe2e5af6545a26a0efd5fc", ASCII16kb) // Aliens. Alien 2 (1987)(Square)(JP)[a2].rom
|
||||
Record(@"5380e913d8dac23470446844cab21f6921101af8", ASCII16kb) // Aliens. Alien 2 (1987)(Square)(JP)[a].rom
|
||||
Record(@"db33011d006201b3bd3bbc4c7c952da2990f36e4", KonamiWithSCC) // Animal Land Murder Case (1987)(Enix)(JP).rom
|
||||
Record(@"495876c504bdc4de24860ea15b25dda8f3b06c49", ASCII8kb) // Animal Land Murder Case (1987)(Enix)(JP)[a].rom
|
||||
Record(@"72815a7213f899a454bcf76733834ba499d35cd8", ASCII16kb) // Astro Marine Corps (1989)(Dinamic Software)(ES).rom
|
||||
Record(@"709fb35338f21897e275237cc4c5615d0a5c2753", ASCII8kb) // Batman (1986)(Pack In Video)(JP).rom
|
||||
Record(@"3739d841abfef971db76ba10915b19b9df833476", ASCII8kb) // Batman (1986)(Pack In Video)(JP)[a].rom
|
||||
Record(@"2588b9ade775b93f03fd4b17fd3f78ba70b556d6", ASCII16kb) // Black Onyx II, The - Search for the Fire Crystal (1986)(ASCII)(JP).rom
|
||||
Record(@"482cd650220e6931f85ee8532c61dac561365e30", ASCII8kb) // Bomber King (1988)(Hudson Soft)(JP).rom
|
||||
Record(@"16c3ced0fb2e360bc7c43d372a0a30eb6bd3963d", ASCII16kb) // Borfesu. Bolu Fez and Five Evil Spirits (1987)(XTalSoft)(JP).rom
|
||||
Record(@"16c692a2c9babfdadd8408d2f0f8fae3a8d96fd5", ASCII8kb) // Cosmic Soldier 2 - Psychic War (1987)(Kogado)(JP).rom
|
||||
Record(@"a731d3d3b5badf33c7602febd32cc4e6ec98c646", Konami) // Craze (1988)(Heart Soft)(JP).rom
|
||||
Record(@"9e0312e72f30a20f556b64fe37dbbfe0d4471823", ASCII16kb) // Craze (1988)(Heart Soft)(JP)[a].rom
|
||||
Record(@"25b28cfe8d6d51f619d182c774f6ceb05b577eeb", ASCII16kb) // Cross Blaim (1986)(dB-Soft)(JP).rom
|
||||
Record(@"bb902e82a2bdda61101a9b3646462adecdd18c8d", FMPac) // Cross Blaim (1986)(dB-Soft)(JP)[b].rom
|
||||
Record(@"1833ffc252d43d3d8239e57d5ac2c015b4367988", Konami) // Daiva Story 4 - Asura's Bloodfeud (1987)(T&E Soft)(JP).rom
|
||||
Record(@"2418c0302abbce8b0f8556b63169c60a849f60ee", ASCII8kb) // Daiva Story 4 - Asura's Bloodfeud (1987)(T&E Soft)(JP)[a].rom
|
||||
Record(@"e6419519c2d3247ea395e4feaa494a2e23e469ce", ASCII8kb) // Deep Dungeon (1988)(Scaptrust)(JP).rom
|
||||
Record(@"d82135a5e28b750c44995af116db890a15f6428a", ASCII8kb) // Deep Dungeon II (1988)(Scaptrust)(JP).rom
|
||||
Record(@"63d4e39c59f24f880809caa534d7a46ae83f4c9f", ASCII16kb) // Demon Crystal Saga II - Knither Special (1987)(Radio Wave Newspaper Publisher)(JP).rom
|
||||
Record(@"d5b164797bc969b55c1a6f4006a4535c3fb03cf0", ASCII8kb) // Demonia (1986)(Microids)(GB)(fr).rom
|
||||
Record(@"7e9a9ce7c18206b325830e9cdcbb27179118de96", Konami) // Dragon Quest (1986)(Enix)(JP).rom
|
||||
Record(@"7b94a728a5945a53d518c18994e1e09a09ec3c1b", ASCII8kb) // Dragon Quest (1986)(Enix)(JP)[a].rom
|
||||
Record(@"3df7c19f739d74d6efdfd8151343e5a55d4ac842", ASCII8kb) // Dragon Quest II (1988)(Enix)(JP).rom
|
||||
Record(@"68691348a29ce59046f993e9abaf3c8651bdda3c", Konami) // Dragon Quest II (1988)(Enix)(JP)[a2].rom
|
||||
Record(@"d7b46aece68c924e09f07e3df45711f337d35d6a", ASCII8kb) // Dragon Quest II (1988)(Enix)(JP)[a].rom
|
||||
Record(@"6c1814c70d69a50ec60e39ef281f0b8cd7bf8598", ASCII8kb) // Dragon Slayer 2 - Xanadu (1987)(Falcom)(JP).rom
|
||||
Record(@"fcdbc5e15dd6b973e0f1112f4599dad985f48042", Konami) // Dragon Slayer 2 - Xanadu (1987)(Zemina)(KR).rom
|
||||
Record(@"2f0db48fbcf3444f52b9c7c76ba9c4bd38bc2a15", ASCII16kb) // Dragon Slayer 3 - Romancia. Dragon Slayer Jr (1987)(Falcom)(JP).rom
|
||||
Record(@"a52c37c1f16ba13d3f39bb5403c82c0187cbff51", ASCII16kb) // Dragon Slayer 3 - Romancia. Dragon Slayer Jr (1987)(Falcom)(JP)[b].rom
|
||||
Record(@"f100a76117e95ab0335e89a901e47d844bbc0ab6", ASCII8kb) // Dragon Slayer 4 - Drasle Family (1987)(Falcom)(JP).rom
|
||||
Record(@"d1fbdbdf2e830139584d7dc796806aa3327720dd", ASCII16kb) // Dungeon Hunter (1989)(ASCII)(JP).rom
|
||||
Record(@"2799d221d5486d48226bbbd3941207e1fc7c985e", ASCII16kb) // Dustin (1987)(Dinamic Software)(ES).rom
|
||||
Record(@"d7109cf20a22558f923c833ff0b4e2311340acb1", ASCII16kb) // Dynamite Bowl (1988)(Toshiba-EMI)(JP).rom
|
||||
Record(@"e92baa5fdfb2715e68700024d964098ef35704d9", ASCII16kb) // Eggerland Mystery 2. Meikyushinwa. Labyrinth Myth (1986)(HAL Laboratory)(JP).rom
|
||||
Record(@"98b7a6ac44b82ccfc45eb51595e2905adabac1c7", ASCII16kb) // Eggerland Mystery 2. Meikyushinwa. Labyrinth Myth (1986)(HAL Laboratory)(JP)[a].rom
|
||||
Record(@"cf344e0f58fd918c089c4d4575caec8843944ff6", ASCII16kb) // Eggerland Mystery 2. Meikyushinwa. Labyrinth Myth (1986)(HAL Laboratory)(JP)[h THEVMA].rom
|
||||
Record(@"ff72a1788d47f9876d9fabd720f6a289fb409090", KonamiWithSCC) // F-1 Spirit - The Way to Formula 1 (1987)(Konami)(JP)[a][RC-752].rom
|
||||
Record(@"42fbb18722df3e34e5b0f935a2dc0ce0d85099e9", KonamiWithSCC) // F-1 Spirit - The Way to Formula 1 (1987)(Konami)(JP)[RC-752].rom
|
||||
Record(@"3880b064dcb851ca221ff67e435137a7cf1141f8", ASCII8kb) // F-1 Spirit - The Way to Formula 1 (1987)(Konami)(JP)[SCC][RC-752].rom
|
||||
Record(@"6e5acdfb1c1610257a7aabf3d5aa858866dbcf2e", KonamiWithSCC) // F-1 Spirit - The Way to Formula 1 (1987)(Zemina)(KR)[RC-752].rom
|
||||
Record(@"84566b5f37ab4f04a2e5b950c5beecbd27b88ea0", Konami) // Fairyland Story, The (1987)(Hot-B)(JP).rom
|
||||
Record(@"e11afc03db4e1d03976d02796b29da9c65d4ff3d", ASCII8kb) // Fairyland Story, The (1987)(Hot-B)(JP)[a2].rom
|
||||
Record(@"2bb837a9051277ba574d8351a9f91f9e57033074", ASCII8kb) // Fairyland Story, The (1987)(Hot-B)(JP)[a].rom
|
||||
Record(@"048737f995eecb1dd8dd341d750efd005267796f", ASCII8kb) // Fantasy Zone (1986)(Pony Canyon)(JP).rom
|
||||
Record(@"442a39b196f19a22fc7fb8f14bf17386a292b60e", Konami) // Fantasy Zone (1987)(Zemina)(KR).rom
|
||||
Record(@"a553c3f204c105c23b227a1e5aeb290671ccdbeb", ASCII8kb) // Final Zone Wolf (1986)(Telenet Japan)(JP).rom
|
||||
Record(@"90ea059c57f011a4fb33a558e868ee639882fe5e", Konami) // Final Zone Wolf (1986)(Zemina)(KR).rom
|
||||
Record(@"2bd311a4baf59cb85b839cca1f55b7462aa96952", Konami) // Flight Simulator (1986)(subLOGIC)(JP).rom
|
||||
Record(@"453eac7568d5d04c8cf7da86f2e8dc79777343da", ASCII8kb) // Flight Simulator (1986)(subLOGIC)(JP)[a].rom
|
||||
Record(@"abe52920e895c148851649ecb9c029fdc41a275f", ASCII16kb) // Gall Force - Defense of Chaos (1986)(Sony)(JP).rom
|
||||
Record(@"e8ab46785e75e19dac497c4a82ac2f5b37ac0580", ASCII16kb) // Gall Force - Defense of Chaos (1986)(Sony)(JP)[a2].rom
|
||||
Record(@"3425eea336140da03d7d7f09a94fd928d70e2212", ASCII16kb) // Gall Force - Defense of Chaos (1986)(Sony)(JP)[a].rom
|
||||
Record(@"89073c052b0fe29b6de077c8bdf5373474081edf", ASCII8kb) // Gambler Jikichushinpa (1988)(Game Arts)(JP).rom
|
||||
Record(@"12b3c31f0fd10ff5823dcc8bf6dfeb785a8af2f7", FMPac) // Genghis Khan (1986)(Koei)(JP).rom
|
||||
Record(@"0413bb3aeacb0c28429b8c85b42796dbe48bef6d", KonamiWithSCC) // Gofer no Yabou Episode II. Nemesis 3 - The Eve of Destruction (1988)(Konami)(JP)[a2][RC-764].rom
|
||||
Record(@"7393f677e0fae5fc83071c6b74756117b7d75e2d", KonamiWithSCC) // Gofer no Yabou Episode II. Nemesis 3 - The Eve of Destruction (1988)(Konami)(JP)[a][RC-764].rom
|
||||
Record(@"40a0f35dccc7572ae53bcd4be70abfe477d49bc9", KonamiWithSCC) // Gofer no Yabou Episode II. Nemesis 3 - The Eve of Destruction (1988)(Konami)(JP)[o][b][RC-764].rom
|
||||
Record(@"5692e41b3a4c5e767cf290fd6c24942d0fd7b2e3", KonamiWithSCC) // Gofer no Yabou Episode II. Nemesis 3 - The Eve of Destruction (1988)(Konami)(JP)[RC-764].rom
|
||||
Record(@"f416b424fb913ca067fe75279c924a20fac5c6a1", ASCII8kb) // Gofer no Yabou Episode II. Nemesis 3 - The Eve of Destruction (1988)(Konami)(JP)[SCC][RC-764].rom
|
||||
Record(@"520739caa1e0aac1b8eabc4305556aa75f3f5a3b", KonamiWithSCC) // Gofer no Yabou Episode II. Nemesis 3 - The Eve of Destruction (1988)(Konami)(JP)[t][RC-764].rom
|
||||
Record(@"7a4126934f9e68c34bf00dd3d9a9e753c05ee73f", ASCII16kb) // Golvellius (1987)(Compile)(JP)[a].rom
|
||||
Record(@"91505fccdcc43230550d101967010bed27f9b573", Konami) // Golvellius (1987)(Compile)(JP)[o].rom
|
||||
Record(@"930df58762e7b5bcf2d362462f03335bad732398", ASCII16kb) // Golvellius (1987)(Compile).rom
|
||||
Record(@"4c2f015685a17db7a8c3893e868e0a84a8dbf1e5", KonamiWithSCC) // Gradius 2. Nemesis 2 (1987)(Konami)(beta)[a][RC-751].rom
|
||||
Record(@"6844758ff5c2c410115d4d7cf12498c42e931732", KonamiWithSCC) // Gradius 2. Nemesis 2 (1987)(Konami)(beta)[RC-751].rom
|
||||
Record(@"d63e20369f98487767810a0c57603bef6a2a07e5", KonamiWithSCC) // Gradius 2. Nemesis 2 (1987)(Konami)(JP)[a][RC-751].rom
|
||||
Record(@"c66483cd0d83292e4f2b54a3e89bd96b8bf9abb2", KonamiWithSCC) // Gradius 2. Nemesis 2 (1987)(Konami)(JP)[penguin version][RC-751].rom
|
||||
Record(@"ab30cdeaacbdf14e6366d43d881338178fc665cb", KonamiWithSCC) // Gradius 2. Nemesis 2 (1987)(Konami)(JP)[RC-751].rom
|
||||
Record(@"fd7b23a4f1c2058b966b5ddd52cf7ae44a0eebb0", ASCII8kb) // Gradius 2. Nemesis 2 (1987)(Konami)(JP)[[SCC]][RC-751].rom
|
||||
Record(@"4127844955388f812e33437f618936dc98944c0a", KonamiWithSCC) // Gradius 2. Nemesis 2 (1987)(Konami)(JP)[t][RC-751].rom
|
||||
Record(@"50efb7040339632cf8bddbc1d3eaae1fb2e2188f", ASCII8kb) // Gradius. Nemesis (1986)(Konami)(JP)[a][RC-742].rom
|
||||
Record(@"f0e4168ea18188fca2581526c2503223b9a28581", Konami) // Gradius. Nemesis (1986)(Konami)(JP)[a][SCC][RC-742].rom
|
||||
Record(@"e31ac6520e912c27ce96431a1dfb112bf71cb7b9", Konami) // Gradius. Nemesis (1986)(Konami)(JP)[SCC][RC-742].rom
|
||||
Record(@"98748c364e7bff50cf073c1a421ebe5b5d8b7025", Konami) // Gradius. Nemesis (1986)(Konami)(JP)[t][SCC][RC-742].rom
|
||||
Record(@"44b23a175d04ca21236a5fef18600b01b12aaf4d", ASCII8kb) // Haja no Fuin (1987)(Kogado)(JP).rom
|
||||
Record(@"a3de07612da7986387a4f5c41bbbc7e3b244e077", ASCII16kb) // Harry Fox MSX Special (1986)(Micro Cabin)(JP).rom
|
||||
Record(@"3626b5dd3188ec2a16e102d05c79f8f242fbd892", FMPac) // Harry Fox Yki no Maoh (1985)(Micro Cabin)(JP).rom
|
||||
Record(@"26a7b0118d158e9bd3ea947fbe68f57b54e0b847", ASCII16kb) // Head over Heels (1987)(Ocean Software)(GB).rom
|
||||
Record(@"da4b44c734029f60388b7cea4ab97c3d5c6a09e9", ASCII16kb) // Hydlide II - Shine of Darkness (1986)(T&E Soft)(JP).rom
|
||||
Record(@"a3537934a4d9dfbf27aca5aaf42e0f18e4975366", ASCII16kb) // Hydlide II - Shine of Darkness (1986)(T&E Soft)(JP)[a].rom
|
||||
Record(@"b18d36cc60d0e3b325138bb98472b685cca89f90", ASCII16kb) // Hydlide II - Shine of Darkness (1986)(T&E Soft)(JP)[b].rom
|
||||
Record(@"74e9ea381e2fed07d989d1056002de5737125aaf", ASCII8kb) // Hydlide III - The Space Memories (1987)(T&E Soft)(JP).rom
|
||||
Record(@"5d2062ecc7176ca2b100724c2a17f877878d721d", Konami) // Hydlide III - The Space Memories (1987)(Zemina)(KR).rom
|
||||
Record(@"842009e0f7d0e977e47be7a56fe60707f477ed93", Konami) // Jagur (1987)(Hudson Soft)(JP).rom
|
||||
Record(@"6fefbe448674b6ea846d0c6b9c8a0d57a11aa410", ASCII16kb) // Jagur (1987)(Hudson Soft)(JP)[a2].rom
|
||||
Record(@"1aeae1180471e9a6e8e866993031b881a341f921", ASCII16kb) // Jagur (1987)(Hudson Soft)(JP)[a3].rom
|
||||
Record(@"3b6200f88561a59ae5d2b3e94515b89cdb96044b", Konami) // Jagur (1987)(Hudson Soft)(JP)[a].rom
|
||||
Record(@"1c462c3629d43297a006ba9055b39a2dccba9f6c", ASCII8kb) // Karuizawa Kidnapping Guidance, The (1986)(Enix)(JP).rom
|
||||
Record(@"46062d3393c49884f84c2dc437ff27854e9d2e49", ASCII16kb) // King's Knight (1986)(Square)(JP).rom
|
||||
Record(@"122f659250a0ae10ce0be0dde626dd3e384affa7", ASCII16kb) // King's Knight (1986)(Square)(JP)[a2].rom
|
||||
Record(@"a2ca7e6e216f8b450eb8db10a4120f0353275b6b", ASCII16kb) // King's Knight (1986)(Square)(JP)[a3].rom
|
||||
Record(@"438bbb3367db4938b3d90fa9d2cfb1f08c072bb7", Konami) // King's Knight (1986)(Square)(JP)[a].rom
|
||||
Record(@"cfd872d005b7bd4cdd6e06c4c0162191f0b0415d", KonamiWithSCC) // King's Valley II - The Seal of El Giza (1988)(Konami)(JP)[RC-760].rom
|
||||
Record(@"f3306e6f25d111da21ce66db3404f5f48acb25a1", ASCII8kb) // King's Valley II - The Seal of El Giza (1988)(Konami)(JP)[SCC][RC-760].rom
|
||||
Record(@"ee60e88ae409ddd93d4259b79586dacb2e5ee372", ASCII8kb) // Knightmare II - The Maze of Galious (1987)(Konami)(JP)[a][RC-749].rom
|
||||
Record(@"4d51d3c5036311392b173a576bc7d91dc9fed6cb", Konami) // Knightmare II - The Maze of Galious (1987)(Konami)(JP)[RC-749].rom
|
||||
Record(@"7a786959d8fc0b518b35341422f096dd6019468d", Konami) // Knightmare II - The Maze of Galious (1987)(Zemina)(KR)[RC-749].rom
|
||||
Record(@"f999e0187023413d839a67337d0595e150b2398f", ASCII8kb) // Knightmare III - Shalom (1987)(Konami)(JP)[a2][RC-754].rom
|
||||
Record(@"3b6f140c99cba5bfa510200df49d1e573d687e4d", Konami) // Knightmare III - Shalom (1987)(Konami)(JP)[a3][RC-754].rom
|
||||
Record(@"240e15fb5d918aa821f226002772dc800d9f20a4", ASCII8kb) // Knightmare III - Shalom (1987)(Konami)(JP)[a][RC-754].rom
|
||||
Record(@"25f5adeca8a2ddb754d25eb18cff0d84e5b003bc", Konami) // Knightmare III - Shalom (1987)(Konami)(JP)[RC-754].rom
|
||||
Record(@"8c609b8bee4245a1bb81e37d888ac5efb66533cf", Konami) // Knightmare III - Shalom (1987)(Konami)(JP)[tr pt R. Bittencourt][RC-754].rom
|
||||
Record(@"6a98d5787dd0e76f04283ce0aec55d45ba81565d", Konami) // Legendly Knight (1988)(Topia)(KR).rom
|
||||
Record(@"2cea9cdd501d77f2b6bd78ae2ae9a63aba64cfed", ASCII8kb) // Legendly Knight (1988)(Topia)(KR)[a].rom
|
||||
Record(@"0442dd29ea0f7b78c9cd5849ec7ef5bb21ec0bf5", ASCII16kb) // Light Corridor, The (1990)(Infogrames)(FR).rom
|
||||
Record(@"3243a5cc562451de527917e9ca85657286b2852f", ASCII16kb) // Magunam. Kiki Ippatsu. Magnum Prohibition 1931 (1988)(Toshiba-EMI)(JP).rom
|
||||
Record(@"9c5c1c40ec30c34b1b436cf8cc494c0b509e81fc", ASCII16kb) // Magunam. Kiki Ippatsu. Magnum Prohibition 1931 (1988)(Toshiba-EMI)(JP)[a].rom
|
||||
Record(@"f7dd6841d280cbffa9f0f2da7af3549f23270ddb", ASCII8kb) // Marchen Veil (1986)(System Sacom)(JP).rom
|
||||
Record(@"0cb11c766bd357d203879bd6bee041a4690cc3df", ASCII16kb) // Meikyuu no Tobira. Gate of Labyrinth (1987)(Radio Wave Newspaper Publisher)(JP).rom
|
||||
Record(@"7abf89652396a648a84ae06e6dabc09735a75798", ASCII8kb) // Mirai. Future (1987)(Xain)(JP).rom
|
||||
Record(@"7341efc039394ec159feebcfaa9d4a61ebf08a18", Konami) // Mitsume ga Tooru. The Three-Eyed One Comes Here (1989)(Natsume)(JP).rom
|
||||
Record(@"176ec8e65a9fdbf59edc245b9e8388cc94195db9", ASCII8kb) // Mitsume ga Tooru. The Three-Eyed One Comes Here (1989)(Natsume)(JP)[a2].rom
|
||||
Record(@"0ec71916791e05d207d7fe0a461a79a76eab52c5", Konami) // Mitsume ga Tooru. The Three-Eyed One Comes Here (1989)(Natsume)(JP)[a].rom
|
||||
Record(@"03b42b77b1a7412f1d7bd0998cf8f2f003f77d0a", Konami) // Monogatari Megami Tensei. Digital Devil Story (1987)(Telenet Japan)(JP).rom
|
||||
Record(@"7dc7f7e3966943280f34836656a7d1bd3ace67cd", ASCII8kb) // Monogatari Megami Tensei. Digital Devil Story (1987)(Telenet Japan)(JP)[a].rom
|
||||
Record(@"e6de8bbd60123444de2a90928853985ceb0b4cbf", FMPac) // Nobunaga no Yabou - Zenkoku Han (1987)(Koei)(JP).rom
|
||||
Record(@"75d3b72d9ceeaa55c76223d935629a30ae4124d6", KonamiWithSCC) // Parodius - Tako Saves Earth (1988)(Konami)(JP)[a][RC-759].rom
|
||||
Record(@"7c066cb763f7a4fec0474b5a09e3ef43bbf9248b", ASCII8kb) // Parodius - Tako Saves Earth (1988)(Konami)(JP)[a][SCC][RC-759].rom
|
||||
Record(@"2220363ae56ef707ab2471fcdb36f4816ad1d32c", KonamiWithSCC) // Parodius - Tako Saves Earth (1988)(Konami)(JP)[RC-759].rom
|
||||
Record(@"0b02dda5316a318a7f75a811aa54200ddd7abc30", ASCII8kb) // Parodius - Tako Saves Earth (1988)(Konami)(JP)[SCC][RC-759].rom
|
||||
Record(@"2111cec4b5ea698d772bb80664f6be690b47391c", KonamiWithSCC) // Parodius - Tako Saves Earth (1988)(Konami)(JP)[t][RC-759].rom
|
||||
Record(@"c95f8edb24edca9f5d38c26d0e8a34c6b61efb0c", ASCII16kb) // Pinball Blaster (1988)(Eurosoft)(NL).rom
|
||||
Record(@"37bd4680a36c3a1a078e2bc47b631d858d9296b8", FMPac) // R-Type (1988)(IREM)(JP).rom
|
||||
Record(@"9c886fff02779267041efe45dadefc5fd7f4b9a2", FMPac) // R-Type v2 (1988)(IREM)(JP).rom
|
||||
Record(@"0b379610cb7085005a56b24a0890b79dd5a7e817", FMPac) // R-Type v2 (1988)(IREM)(JP)[a].rom
|
||||
Record(@"74ae85d44cb8ef1bae428c90200cb74be6d56d3a", ASCII8kb) // Relics (1986)(Bothtec)(JP).rom
|
||||
Record(@"46c98a3143f5a80b2090311a770f9b73000881c0", ASCII16kb) // Robowres 2001 (1987)(Micronet)(JP).rom
|
||||
Record(@"0d459788b6c464b50cbc2436e67a2cef248e0c4a", KonamiWithSCC) // Salamander - Operation X (1987)(Konami)(JP)[RC-758].rom
|
||||
Record(@"0d76a069726fec7326541f75b809b8b72148ed3a", ASCII8kb) // Salamander - Operation X (1987)(Konami)(JP)[SCC][RC-758].rom
|
||||
Record(@"655b15e8fd81866492bcf2b1d6609211b30efce1", KonamiWithSCC) // Salamander - Operation X (1987)(Konami)(JP)[t][RC-758].rom
|
||||
Record(@"d30c17109fa1c4a81e39dca57b79464b0aa0b7c2", KonamiWithSCC) // Salamander - Operation X (1988)(Zemina)(KR)[RC-758].rom
|
||||
Record(@"96e2a7163c755fbea77abfd9d09a798687a5a993", Konami) // Sangokushi. Romance of Three Kingdoms (1986)(Koei)(JP).rom
|
||||
Record(@"3f741ba2ab08c5e9fb658882b36b8e3d01682f58", ASCII8kb) // Sangokushi. Romance of Three Kingdoms (1986)(Koei)(JP)[a2].rom
|
||||
Record(@"7ad40ae512bbf4ba688467ba23e16354b8421d0a", Konami) // Sangokushi. Romance of Three Kingdoms (1986)(Koei)(JP)[a3].rom
|
||||
Record(@"14ff6fe464362c6b7dbb47b2ecda3a8c5f05ef79", ASCII8kb) // Sangokushi. Romance of Three Kingdoms (1986)(Koei)(JP)[a].rom
|
||||
Record(@"2b255c3e5615b2f5e2365419a35e4115a060e93c", ASCII8kb) // Senjyo no Ookami. Wolf's Battlefield. Commando (1987)(ASCII)(JP).rom
|
||||
Record(@"5a12439f74ca5d1c2664f01ff6a8302d3ce907a8", ASCII8kb) // Sofia (1988)(Radio Wave Newspaper Publisher)(JP).rom
|
||||
Record(@"3caeec19423a960d98e4719b301a3d276339e5ae", ASCII8kb) // Sofia (1988)(Radio Wave Newspaper Publisher)(JP)[o].rom
|
||||
Record(@"9bafce699964f4aabc6b60c71a71c9ff5b0cc82d", ASCII8kb) // Super Boy III (1991)(Zemina)(KR).rom
|
||||
Record(@"2dfbca8f5cc3a9e9d151382ebe0da410f5393eaf", ASCII8kb) // Super Laydock - Mission Striker (1987)(T&E Soft)(JP).rom
|
||||
Record(@"244399d67d7851f3daa9bb87a14f5b8ef6d8c160", Konami) // Super Laydock - Mission Striker (1988)(Zemina)(KR).rom
|
||||
Record(@"0d2f86dbb70f4b4a4e4dc1bc95df232f48856037", FMPac) // Super Pierrot (1988)(Nidecom)(JP).rom
|
||||
Record(@"16351e6a7d7fc38c23b54ee15ac6f0275621ba96", ASCII8kb) // Syougun. Shigun (1987)(Nippon Dexter)(JP).rom
|
||||
Record(@"d147f2cac0600527ce49bfffc865c54eb783e5e5", ASCII16kb) // Toobin' (1989)(Domark)(GB).rom
|
||||
Record(@"bfe8046f8ccc6d6016d7752c04f0654420ef81e7", ASCII16kb) // Tumego 120 (1987)(Champion Soft)(JP).rom
|
||||
Record(@"2b10234debd2a6a9a02e0750ba6563768bc4a2f3", ASCII8kb) // Valis - The Fantasm Soldier (1986)(Telenet Japan)(JP).rom
|
||||
Record(@"dccdb2d18c70a94e48b3ec5e0cb986c5d708bbc9", Konami) // Valis - The Fantasm Soldier (1987)(Zemina)(KR).rom
|
||||
Record(@"4340a2580c949d498d2c8e71699fff860214e9ea", ASCII16kb) // Vaxol (1987)(Heart Soft)(JP).rom
|
||||
Record(@"818d91505ad39bba2eaf7f4857c7d41e95fcb233", ASCII8kb) // Wing Man 2 (1987)(Enix)(JP).rom
|
||||
Record(@"b6a5552effcee708b665fa74e5ce7b0fa2541c03", Konami) // Young Sherlock - The Legacy of Doyle (1985)(Pack In Video)(JP).rom
|
||||
Record(@"97e173dac64dbde7d6a60de7606cba0c860813db", ASCII8kb) // Young Sherlock - The Legacy of Doyle (1985)(Pack In Video)(JP)[a].rom
|
||||
Record(@"d0706fd10e418eba2929d515cc0994f49376a63f", Konami) // Yume Tairiku Adventure. Penguin Adventure (1986)(Konami)(JP)[a2][RC-743].rom
|
||||
Record(@"d53e0c8bcd98820afe820f756af35cc97911bfe4", Konami) // Yume Tairiku Adventure. Penguin Adventure (1986)(Konami)(JP)[a][RC-743].rom
|
||||
Record(@"898bda19f882c6d1dffdb2173db84a97dc21f7d6", Konami) // Yume Tairiku Adventure. Penguin Adventure (1986)(Konami)(JP)[cr Screen][RC-743].rom
|
||||
Record(@"fa6c059e14092d023b1f9f2df28e482f966287db", ASCII8kb) // Yume Tairiku Adventure. Penguin Adventure (1986)(Konami)(JP)[RC-743].rom
|
||||
Record(@"d3d411c8b7891aef9c59cbc20bb4fa3ff0ca03ea", Konami) // Yume Tairiku Adventure. Penguin Adventure (1987)(Zemina)(KR)[RC-743].rom
|
||||
};
|
||||
#undef Record
|
||||
|
||||
@interface MSXStaticAnalyserTests : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation MSXStaticAnalyserTests
|
||||
|
||||
- (void)testROMs {
|
||||
NSString *basePath = [[[NSBundle bundleForClass:[self class]] resourcePath] stringByAppendingPathComponent:@"MSX ROMs"];
|
||||
for(NSString *testFile in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:basePath error:nil]) {
|
||||
NSString *fullPath = [basePath stringByAppendingPathComponent:testFile];
|
||||
|
||||
// get a SHA1 for the file
|
||||
NSData *fileData = [NSData dataWithContentsOfFile:fullPath];
|
||||
uint8_t sha1Bytes[CC_SHA1_DIGEST_LENGTH];
|
||||
CC_SHA1([fileData bytes], (CC_LONG)[fileData length], sha1Bytes);
|
||||
NSMutableString *sha1 = [[NSMutableString alloc] init];
|
||||
for(int c = 0; c < CC_SHA1_DIGEST_LENGTH; c++) [sha1 appendFormat:@"%02x", sha1Bytes[c]];
|
||||
|
||||
// get an analysis of the file
|
||||
std::list<StaticAnalyser::Target> targets = StaticAnalyser::GetTargets([fullPath UTF8String]);
|
||||
|
||||
// grab the ROM record
|
||||
MSXROMRecord *romRecord = romRecordsBySHA1[sha1];
|
||||
if(!romRecord) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
@ -39,31 +39,127 @@ static std::list<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
const size_t data_size = segment.data.size();
|
||||
if(data_size < 0x2000 || data_size & 0x3fff) continue;
|
||||
|
||||
// Check for a ROM header at address 0; TODO: if it's not found then try 0x4000
|
||||
// and consider swapping the image.
|
||||
// Check for a ROM header at address 0; if it's not found then try 0x4000
|
||||
// and adjust the start address;
|
||||
uint16_t start_address = 0;
|
||||
bool found_start = false;
|
||||
if(segment.data[0] == 0x41 && segment.data[1] == 0x42) {
|
||||
start_address = 0x4000;
|
||||
found_start = true;
|
||||
} else if(segment.data.size() >= 0x8000 && segment.data[0x4000] == 0x41 && segment.data[0x4001] == 0x42) {
|
||||
start_address = 0;
|
||||
found_start = true;
|
||||
}
|
||||
|
||||
// Check for the expansion ROM header and the reserved bytes.
|
||||
if(segment.data[0] != 0x41 || segment.data[1] != 0x42) continue;
|
||||
// Reject cartridge if the ROM header wasn't found.
|
||||
if(!found_start) continue;
|
||||
|
||||
uint16_t init_address = static_cast<uint16_t>(segment.data[2] | (segment.data[3] << 8));
|
||||
// TODO: check for a rational init address?
|
||||
|
||||
// If this ROM is greater than 32kb in size then some sort of MegaROM scheme must
|
||||
// If this ROM is greater than 48kb in size then some sort of MegaROM scheme must
|
||||
// be at play; disassemble to try to figure it out.
|
||||
if(data_size > 0x4000) {
|
||||
std::vector<uint8_t> first_segment;
|
||||
first_segment.insert(first_segment.begin(), segment.data.begin(), segment.data.begin() + 32768);
|
||||
target.msx.cartridge_type = StaticAnalyser::MSXCartridgeType::None;
|
||||
if(data_size > 0xc000) {
|
||||
std::vector<uint8_t> first_16k;
|
||||
first_16k.insert(first_16k.begin(), segment.data.begin(), segment.data.begin() + 8192);
|
||||
StaticAnalyser::Z80::Disassembly disassembly =
|
||||
StaticAnalyser::Z80::Disassemble(
|
||||
first_segment,
|
||||
StaticAnalyser::Disassembler::OffsetMapper(0x4000),
|
||||
first_16k,
|
||||
StaticAnalyser::Disassembler::OffsetMapper(start_address),
|
||||
{ init_address }
|
||||
);
|
||||
|
||||
// Look for LD (nnnn), A instructions, and collate those addresses.
|
||||
// Look for a indirect store followed by an unconditional JP or CALL into another
|
||||
// segment, that's a fairly explicit sign where found.
|
||||
using Instruction = StaticAnalyser::Z80::Instruction;
|
||||
std::map<uint16_t, Instruction> &instructions = disassembly.instructions_by_address;
|
||||
bool is_ascii = false;
|
||||
auto iterator = instructions.begin();
|
||||
while(iterator != instructions.end()) {
|
||||
auto next_iterator = iterator;
|
||||
next_iterator++;
|
||||
if(next_iterator == instructions.end()) break;
|
||||
|
||||
if( iterator->second.operation == Instruction::Operation::LD &&
|
||||
iterator->second.destination == Instruction::Location::Operand_Indirect &&
|
||||
(
|
||||
iterator->second.operand == 0x5000 ||
|
||||
iterator->second.operand == 0x6000 ||
|
||||
iterator->second.operand == 0x6800 ||
|
||||
iterator->second.operand == 0x7000 ||
|
||||
iterator->second.operand == 0x77ff ||
|
||||
iterator->second.operand == 0x7800 ||
|
||||
iterator->second.operand == 0x8000 ||
|
||||
iterator->second.operand == 0x9000 ||
|
||||
iterator->second.operand == 0xa000
|
||||
) &&
|
||||
(
|
||||
next_iterator->second.operation == Instruction::Operation::CALL ||
|
||||
next_iterator->second.operation == Instruction::Operation::JP
|
||||
) &&
|
||||
((next_iterator->second.operand >> 13) != (0x4000 >> 13))
|
||||
) {
|
||||
const uint16_t address = static_cast<uint16_t>(next_iterator->second.operand);
|
||||
switch(iterator->second.operand) {
|
||||
case 0x6000:
|
||||
if(address >= 0x6000 && address < 0x8000) {
|
||||
target.msx.cartridge_type = StaticAnalyser::MSXCartridgeType::KonamiWithSCC;
|
||||
}
|
||||
break;
|
||||
case 0x6800:
|
||||
if(address >= 0x6000 && address < 0x6800) {
|
||||
target.msx.cartridge_type = StaticAnalyser::MSXCartridgeType::ASCII8kb;
|
||||
}
|
||||
break;
|
||||
case 0x7000:
|
||||
if(address >= 0x6000 && address < 0x8000) {
|
||||
target.msx.cartridge_type = StaticAnalyser::MSXCartridgeType::KonamiWithSCC;
|
||||
}
|
||||
if(address >= 0x7000 && address < 0x7800) {
|
||||
is_ascii = true;
|
||||
}
|
||||
break;
|
||||
case 0x77ff:
|
||||
if(address >= 0x7000 && address < 0x7800) {
|
||||
target.msx.cartridge_type = StaticAnalyser::MSXCartridgeType::ASCII16kb;
|
||||
}
|
||||
break;
|
||||
case 0x7800:
|
||||
if(address >= 0xa000 && address < 0xc000) {
|
||||
target.msx.cartridge_type = StaticAnalyser::MSXCartridgeType::ASCII8kb;
|
||||
}
|
||||
break;
|
||||
case 0x8000:
|
||||
if(address >= 0x8000 && address < 0xa000) {
|
||||
target.msx.cartridge_type = StaticAnalyser::MSXCartridgeType::KonamiWithSCC;
|
||||
}
|
||||
break;
|
||||
case 0x9000:
|
||||
if(address >= 0x8000 && address < 0xa000) {
|
||||
target.msx.cartridge_type = StaticAnalyser::MSXCartridgeType::KonamiWithSCC;
|
||||
}
|
||||
break;
|
||||
case 0xa000:
|
||||
if(address >= 0xa000 && address < 0xc000) {
|
||||
target.msx.cartridge_type = StaticAnalyser::MSXCartridgeType::Konami;
|
||||
}
|
||||
break;
|
||||
case 0xb000:
|
||||
if(address >= 0xa000 && address < 0xc000) {
|
||||
target.msx.cartridge_type = StaticAnalyser::MSXCartridgeType::KonamiWithSCC;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
iterator = next_iterator;
|
||||
}
|
||||
|
||||
if(target.msx.cartridge_type == StaticAnalyser::MSXCartridgeType::None) {
|
||||
// Look for LD (nnnn), A instructions, and collate those addresses.
|
||||
std::map<uint16_t, int> address_counts;
|
||||
for(const auto &instruction_pair : disassembly.instructions_by_address) {
|
||||
for(const auto &instruction_pair : instructions) {
|
||||
if( instruction_pair.second.operation == Instruction::Operation::LD &&
|
||||
instruction_pair.second.destination == Instruction::Location::Operand_Indirect &&
|
||||
instruction_pair.second.source == Instruction::Location::A) {
|
||||
@ -74,20 +170,23 @@ static std::list<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
// Sort possible cartridge types.
|
||||
using Possibility = std::pair<StaticAnalyser::MSXCartridgeType, int>;
|
||||
std::vector<Possibility> possibilities;
|
||||
possibilities.push_back(std::make_pair(StaticAnalyser::MSXCartridgeType::Konami, address_counts[0x6000] + address_counts[0x8000] + address_counts[0xa000]));
|
||||
possibilities.push_back(std::make_pair(StaticAnalyser::MSXCartridgeType::KonamiWithSCC, address_counts[0x5000] + address_counts[0x7000] + address_counts[0x9000] + address_counts[0xb000]));
|
||||
// Add to list in order of declining probability, so that stable_sort below prefers
|
||||
// the more likely option in a tie.
|
||||
possibilities.push_back(std::make_pair(StaticAnalyser::MSXCartridgeType::ASCII8kb, address_counts[0x6000] + address_counts[0x6800] + address_counts[0x7000] + address_counts[0x7800]));
|
||||
possibilities.push_back(std::make_pair(StaticAnalyser::MSXCartridgeType::ASCII16kb, address_counts[0x6000] + address_counts[0x7000] + address_counts[0x77ff]));
|
||||
std::sort(possibilities.begin(), possibilities.end(), [](const Possibility &a, const Possibility &b) {
|
||||
if(!is_ascii) possibilities.push_back(std::make_pair(StaticAnalyser::MSXCartridgeType::Konami, address_counts[0x6000] + address_counts[0x8000] + address_counts[0xa000]));
|
||||
if(!is_ascii) possibilities.push_back(std::make_pair(StaticAnalyser::MSXCartridgeType::KonamiWithSCC, address_counts[0x5000] + address_counts[0x7000] + address_counts[0x9000] + address_counts[0xb000]));
|
||||
std::stable_sort(possibilities.begin(), possibilities.end(), [](const Possibility &a, const Possibility &b) {
|
||||
return a.second > b.second;
|
||||
});
|
||||
|
||||
target.msx.paging_model = possibilities[0].first;
|
||||
target.msx.cartridge_type = possibilities[0].first;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the standard MSX start address.
|
||||
msx_cartridges.emplace_back(new Storage::Cartridge::Cartridge({
|
||||
Storage::Cartridge::Cartridge::Segment(0x4000, segment.data)
|
||||
Storage::Cartridge::Cartridge::Segment(start_address, segment.data)
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ enum class MSXCartridgeType {
|
||||
KonamiWithSCC,
|
||||
ASCII8kb,
|
||||
ASCII16kb,
|
||||
RType
|
||||
FMPac
|
||||
};
|
||||
|
||||
enum class ZX8081MemoryModel {
|
||||
@ -124,7 +124,7 @@ struct Target {
|
||||
} amstradcpc;
|
||||
|
||||
struct {
|
||||
MSXCartridgeType paging_model;
|
||||
MSXCartridgeType cartridge_type;
|
||||
} msx;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user