1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-30 23:29:08 +00:00

Merge pull request #328 from TomHarte/MSXROMs

Introduces a basic attempt at MSX MegaROM support
This commit is contained in:
Thomas Harte 2018-01-06 12:55:00 -08:00 committed by GitHub
commit 73b4e1722b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 816 additions and 101 deletions

5
.gitignore vendored
View File

@ -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

View 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 */

View 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 */

View 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 */

View 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 */

View File

@ -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_;

View 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 */

View File

@ -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 */,

View File

@ -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;
}

View 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);

View 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;
});
}

View File

@ -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];

View File

@ -15,6 +15,5 @@
@property (nonatomic) BOOL clockLine;
- (void)runForCycles:(NSUInteger)numberOfCycles;
- (void)setROM:(NSData *)ROM;
@end

View File

@ -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 {

View File

@ -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)
}

View 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 Archives version of the TOSEC collection.

View 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

View File

@ -39,55 +39,154 @@ 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, int> address_counts;
for(const auto &instruction_pair : disassembly.instructions_by_address) {
if( instruction_pair.second.operation == Instruction::Operation::LD &&
instruction_pair.second.destination == Instruction::Location::Operand_Indirect &&
instruction_pair.second.source == Instruction::Location::A) {
address_counts[static_cast<uint16_t>(instruction_pair.second.operand)]++;
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;
}
// 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]));
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) {
return a.second > b.second;
});
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 : instructions) {
if( instruction_pair.second.operation == Instruction::Operation::LD &&
instruction_pair.second.destination == Instruction::Location::Operand_Indirect &&
instruction_pair.second.source == Instruction::Location::A) {
address_counts[static_cast<uint16_t>(instruction_pair.second.operand)]++;
}
}
target.msx.paging_model = possibilities[0].first;
// Sort possible cartridge types.
using Possibility = std::pair<StaticAnalyser::MSXCartridgeType, int>;
std::vector<Possibility> possibilities;
// 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]));
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.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)
}));
}

View File

@ -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;
};