mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-01 11:49:58 +00:00
Merge pull request #1136 from TomHarte/MSX-MUSIC
Add MSX-MUSIC (/FM-PAC) emulation.
This commit is contained in:
commit
ec9abbe6a7
@ -18,6 +18,7 @@ namespace Analyser::Static::MSX {
|
|||||||
|
|
||||||
struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<Target> {
|
struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<Target> {
|
||||||
bool has_disk_drive = false;
|
bool has_disk_drive = false;
|
||||||
|
bool has_msx_music = true;
|
||||||
std::string loading_command;
|
std::string loading_command;
|
||||||
|
|
||||||
ReflectableEnum(Model,
|
ReflectableEnum(Model,
|
||||||
@ -36,6 +37,7 @@ struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<
|
|||||||
Target(): Analyser::Static::Target(Machine::MSX) {
|
Target(): Analyser::Static::Target(Machine::MSX) {
|
||||||
if(needs_declare()) {
|
if(needs_declare()) {
|
||||||
DeclareField(has_disk_drive);
|
DeclareField(has_disk_drive);
|
||||||
|
DeclareField(has_msx_music);
|
||||||
DeclareField(region);
|
DeclareField(region);
|
||||||
AnnounceEnum(Region);
|
AnnounceEnum(Region);
|
||||||
DeclareField(model);
|
DeclareField(model);
|
||||||
|
@ -27,8 +27,9 @@
|
|||||||
#include "../../Components/9918/9918.hpp"
|
#include "../../Components/9918/9918.hpp"
|
||||||
#include "../../Components/AudioToggle/AudioToggle.hpp"
|
#include "../../Components/AudioToggle/AudioToggle.hpp"
|
||||||
#include "../../Components/AY38910/AY38910.hpp"
|
#include "../../Components/AY38910/AY38910.hpp"
|
||||||
#include "../../Components/RP5C01/RP5C01.hpp"
|
|
||||||
#include "../../Components/KonamiSCC/KonamiSCC.hpp"
|
#include "../../Components/KonamiSCC/KonamiSCC.hpp"
|
||||||
|
#include "../../Components/OPx/OPLL.hpp"
|
||||||
|
#include "../../Components/RP5C01/RP5C01.hpp"
|
||||||
|
|
||||||
#include "../../Storage/Tape/Parsers/MSX.hpp"
|
#include "../../Storage/Tape/Parsers/MSX.hpp"
|
||||||
#include "../../Storage/Tape/Tape.hpp"
|
#include "../../Storage/Tape/Tape.hpp"
|
||||||
@ -129,9 +130,49 @@ class AYPortHandler: public GI::AY38910::PortHandler {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <bool has_opll> struct Speaker;
|
||||||
|
|
||||||
|
template <> struct Speaker<false> {
|
||||||
|
Speaker() :
|
||||||
|
ay(GI::AY38910::Personality::AY38910, audio_queue),
|
||||||
|
audio_toggle(audio_queue),
|
||||||
|
scc(audio_queue),
|
||||||
|
mixer(ay, audio_toggle, scc),
|
||||||
|
speaker(mixer) {}
|
||||||
|
|
||||||
|
Concurrency::AsyncTaskQueue<false> audio_queue;
|
||||||
|
GI::AY38910::AY38910<false> ay;
|
||||||
|
Audio::Toggle audio_toggle;
|
||||||
|
Konami::SCC scc;
|
||||||
|
|
||||||
|
using CompundSource = Outputs::Speaker::CompoundSource<GI::AY38910::AY38910<false>, Audio::Toggle, Konami::SCC>;
|
||||||
|
CompundSource mixer;
|
||||||
|
Outputs::Speaker::PullLowpass<CompundSource> speaker;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct Speaker<true> {
|
||||||
|
Speaker() :
|
||||||
|
opll(audio_queue, 1),
|
||||||
|
ay(GI::AY38910::Personality::AY38910, audio_queue),
|
||||||
|
audio_toggle(audio_queue),
|
||||||
|
scc(audio_queue),
|
||||||
|
mixer(ay, audio_toggle, scc, opll),
|
||||||
|
speaker(mixer) {}
|
||||||
|
|
||||||
|
Concurrency::AsyncTaskQueue<false> audio_queue;
|
||||||
|
Yamaha::OPL::OPLL opll;
|
||||||
|
GI::AY38910::AY38910<false> ay;
|
||||||
|
Audio::Toggle audio_toggle;
|
||||||
|
Konami::SCC scc;
|
||||||
|
|
||||||
|
using CompundSource = Outputs::Speaker::CompoundSource<GI::AY38910::AY38910<false>, Audio::Toggle, Konami::SCC, Yamaha::OPL::OPLL>;
|
||||||
|
CompundSource mixer;
|
||||||
|
Outputs::Speaker::PullLowpass<CompundSource> speaker;
|
||||||
|
};
|
||||||
|
|
||||||
using Target = Analyser::Static::MSX::Target;
|
using Target = Analyser::Static::MSX::Target;
|
||||||
|
|
||||||
template <Target::Model model>
|
template <Target::Model model, bool has_opll>
|
||||||
class ConcreteMachine:
|
class ConcreteMachine:
|
||||||
public Machine,
|
public Machine,
|
||||||
public CPU::Z80::BusHandler,
|
public CPU::Z80::BusHandler,
|
||||||
@ -148,32 +189,31 @@ class ConcreteMachine:
|
|||||||
private:
|
private:
|
||||||
// Provide 512kb of memory for an MSX 2; 64kb for an MSX 1. 'Slightly' arbitrary.
|
// Provide 512kb of memory for an MSX 2; 64kb for an MSX 1. 'Slightly' arbitrary.
|
||||||
static constexpr size_t RAMSize = model == Target::Model::MSX2 ? 512 * 1024 : 64 * 1024;
|
static constexpr size_t RAMSize = model == Target::Model::MSX2 ? 512 * 1024 : 64 * 1024;
|
||||||
|
|
||||||
static constexpr int ClockRate = 3579545;
|
static constexpr int ClockRate = 3579545;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher):
|
ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher):
|
||||||
z80_(*this),
|
z80_(*this),
|
||||||
i8255_(i8255_port_handler_),
|
i8255_(i8255_port_handler_),
|
||||||
ay_(GI::AY38910::Personality::AY38910, audio_queue_),
|
|
||||||
audio_toggle_(audio_queue_),
|
|
||||||
scc_(audio_queue_),
|
|
||||||
mixer_(ay_, audio_toggle_, scc_),
|
|
||||||
speaker_(mixer_),
|
|
||||||
tape_player_(3579545 * 2),
|
tape_player_(3579545 * 2),
|
||||||
i8255_port_handler_(*this, audio_toggle_, tape_player_),
|
i8255_port_handler_(*this, speaker_.audio_toggle, tape_player_),
|
||||||
ay_port_handler_(tape_player_),
|
ay_port_handler_(tape_player_),
|
||||||
memory_slots_{{*this}, {*this}, {*this}, {*this}},
|
memory_slots_{{*this}, {*this}, {*this}, {*this}},
|
||||||
clock_(ClockRate) {
|
clock_(ClockRate) {
|
||||||
set_clock_rate(ClockRate);
|
set_clock_rate(ClockRate);
|
||||||
clear_all_keys();
|
clear_all_keys();
|
||||||
|
|
||||||
ay_.set_port_handler(&ay_port_handler_);
|
speaker_.ay.set_port_handler(&ay_port_handler_);
|
||||||
speaker_.set_input_rate(3579545.0f / 2.0f);
|
speaker_.speaker.set_input_rate(3579545.0f / 2.0f);
|
||||||
tape_player_.set_clocking_hint_observer(this);
|
tape_player_.set_clocking_hint_observer(this);
|
||||||
|
|
||||||
// Set the AY to 50% of available volume, the toggle to 10% and leave 40% for an SCC.
|
// Set the AY to 50% of available volume, the toggle to 10% and leave 40% for an SCC.
|
||||||
mixer_.set_relative_volumes({0.5f, 0.1f, 0.4f});
|
// If there is an OPLL, give it equal volume to the AY and expect some clipping.
|
||||||
|
if constexpr (has_opll) {
|
||||||
|
speaker_.mixer.set_relative_volumes({0.5f, 0.1f, 0.4f, 0.5f});
|
||||||
|
} else {
|
||||||
|
speaker_.mixer.set_relative_volumes({0.5f, 0.1f, 0.4f});
|
||||||
|
}
|
||||||
|
|
||||||
// Install the proper TV standard and select an ideal BIOS name.
|
// Install the proper TV standard and select an ideal BIOS name.
|
||||||
const std::string machine_name = "MSX";
|
const std::string machine_name = "MSX";
|
||||||
@ -229,11 +269,12 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
// Fetch the necessary ROMs; try the region-specific ROM first,
|
// Fetch the necessary ROMs; try the region-specific ROM first,
|
||||||
// but failing that fall back on patching the main one.
|
// but failing that fall back on patching the main one.
|
||||||
ROM::Request request;
|
ROM::Request request = bios_request;
|
||||||
if(target.has_disk_drive) {
|
if(target.has_disk_drive) {
|
||||||
request = ROM::Request(ROM::Name::MSXDOS) && bios_request;
|
request = request && ROM::Request(ROM::Name::MSXDOS);
|
||||||
} else {
|
}
|
||||||
request = bios_request;
|
if(target.has_msx_music) {
|
||||||
|
request = request && ROM::Request(ROM::Name::MSXMusic);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto roms = rom_fetcher(request);
|
auto roms = rom_fetcher(request);
|
||||||
@ -294,6 +335,14 @@ class ConcreteMachine:
|
|||||||
disk_slot().map_handler(0x6000, 0x2000);
|
disk_slot().map_handler(0x6000, 0x2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Grab the MSX-MUSIC ROM if applicable.
|
||||||
|
if(target.has_msx_music) {
|
||||||
|
std::vector<uint8_t> &msx_music = roms.find(ROM::Name::MSXMusic)->second;
|
||||||
|
msx_music.resize(65536);
|
||||||
|
msx_music_slot().set_source(msx_music);
|
||||||
|
msx_music_slot().map(0, 0, 0x10000);
|
||||||
|
}
|
||||||
|
|
||||||
// Insert the media.
|
// Insert the media.
|
||||||
insert_media(target.media);
|
insert_media(target.media);
|
||||||
|
|
||||||
@ -307,7 +356,7 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
~ConcreteMachine() {
|
~ConcreteMachine() {
|
||||||
audio_queue_.flush();
|
speaker_.audio_queue.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) final {
|
void set_scan_target(Outputs::Display::ScanTarget *scan_target) final {
|
||||||
@ -327,7 +376,7 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Outputs::Speaker::Speaker *get_speaker() final {
|
Outputs::Speaker::Speaker *get_speaker() final {
|
||||||
return &speaker_;
|
return &speaker_.speaker;
|
||||||
}
|
}
|
||||||
|
|
||||||
void run_for(const Cycles cycles) final {
|
void run_for(const Cycles cycles) final {
|
||||||
@ -365,7 +414,7 @@ class ConcreteMachine:
|
|||||||
cartridge_primary().handler = std::make_unique<Cartridge::KonamiROMSlotHandler>(static_cast<MSX::MemorySlot &>(slot));
|
cartridge_primary().handler = std::make_unique<Cartridge::KonamiROMSlotHandler>(static_cast<MSX::MemorySlot &>(slot));
|
||||||
break;
|
break;
|
||||||
case Analyser::Static::MSX::Cartridge::KonamiWithSCC:
|
case Analyser::Static::MSX::Cartridge::KonamiWithSCC:
|
||||||
cartridge_primary().handler = std::make_unique<Cartridge::KonamiWithSCCROMSlotHandler>(static_cast<MSX::MemorySlot &>(slot), scc_);
|
cartridge_primary().handler = std::make_unique<Cartridge::KonamiWithSCCROMSlotHandler>(static_cast<MSX::MemorySlot &>(slot), speaker_.scc);
|
||||||
break;
|
break;
|
||||||
case Analyser::Static::MSX::Cartridge::ASCII8kb:
|
case Analyser::Static::MSX::Cartridge::ASCII8kb:
|
||||||
cartridge_primary().handler = std::make_unique<Cartridge::ASCII8kbROMSlotHandler>(static_cast<MSX::MemorySlot &>(slot));
|
cartridge_primary().handler = std::make_unique<Cartridge::ASCII8kbROMSlotHandler>(static_cast<MSX::MemorySlot &>(slot));
|
||||||
@ -580,7 +629,7 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
case 0xa2:
|
case 0xa2:
|
||||||
update_audio();
|
update_audio();
|
||||||
*cycle.value = GI::AY38910::Utility::read(ay_);
|
*cycle.value = GI::AY38910::Utility::read(speaker_.ay);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xa8: case 0xa9:
|
case 0xa8: case 0xa9:
|
||||||
@ -624,7 +673,7 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
case 0xa0: case 0xa1:
|
case 0xa0: case 0xa1:
|
||||||
update_audio();
|
update_audio();
|
||||||
GI::AY38910::Utility::write(ay_, port == 0xa1, *cycle.value);
|
GI::AY38910::Utility::write(speaker_.ay, port == 0xa1, *cycle.value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xa8: case 0xa9:
|
case 0xa8: case 0xa9:
|
||||||
@ -668,6 +717,13 @@ class ConcreteMachine:
|
|||||||
update_paging();
|
update_paging();
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case 0x7c: case 0x7d:
|
||||||
|
if constexpr (has_opll) {
|
||||||
|
speaker_.opll.write(address, *cycle.value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
[[fallthrough]];
|
||||||
|
|
||||||
default:
|
default:
|
||||||
printf("Unhandled write %02x of %02x\n", address & 0xff, *cycle.value);
|
printf("Unhandled write %02x of %02x\n", address & 0xff, *cycle.value);
|
||||||
break;
|
break;
|
||||||
@ -725,7 +781,7 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
if(outputs & Output::Audio) {
|
if(outputs & Output::Audio) {
|
||||||
update_audio();
|
update_audio();
|
||||||
audio_queue_.perform();
|
speaker_.audio_queue.perform();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -742,8 +798,8 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void set_key_state(uint16_t key, bool is_pressed) final {
|
void set_key_state(uint16_t key, bool is_pressed) final {
|
||||||
int mask = 1 << (key & 7);
|
const int mask = 1 << (key & 7);
|
||||||
int line = key >> 4;
|
const int line = key >> 4;
|
||||||
if(is_pressed) key_states_[line] &= ~mask; else key_states_[line] |= mask;
|
if(is_pressed) key_states_[line] &= ~mask; else key_states_[line] |= mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -774,7 +830,7 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
// MARK: - Activity::Source
|
// MARK: - Activity::Source
|
||||||
void set_activity_observer(Activity::Observer *observer) final {
|
void set_activity_observer(Activity::Observer *observer) final {
|
||||||
DiskROM *handler = disk_handler();
|
DiskROM *const handler = disk_handler();
|
||||||
if(handler) {
|
if(handler) {
|
||||||
handler->set_activity_observer(observer);
|
handler->set_activity_observer(observer);
|
||||||
}
|
}
|
||||||
@ -788,7 +844,7 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void update_audio() {
|
void update_audio() {
|
||||||
speaker_.run_for(audio_queue_, time_since_ay_update_.divide_cycles(Cycles(2)));
|
speaker_.speaker.run_for(speaker_.audio_queue, time_since_ay_update_.divide_cycles(Cycles(2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
class i8255PortHandler: public Intel::i8255::PortHandler {
|
class i8255PortHandler: public Intel::i8255::PortHandler {
|
||||||
@ -855,13 +911,6 @@ class ConcreteMachine:
|
|||||||
JustInTimeActor<TI::TMS::TMS9918<vdp_model()>> vdp_;
|
JustInTimeActor<TI::TMS::TMS9918<vdp_model()>> vdp_;
|
||||||
Intel::i8255::i8255<i8255PortHandler> i8255_;
|
Intel::i8255::i8255<i8255PortHandler> i8255_;
|
||||||
|
|
||||||
Concurrency::AsyncTaskQueue<false> audio_queue_;
|
|
||||||
GI::AY38910::AY38910<false> ay_;
|
|
||||||
Audio::Toggle audio_toggle_;
|
|
||||||
Konami::SCC scc_;
|
|
||||||
Outputs::Speaker::CompoundSource<GI::AY38910::AY38910<false>, Audio::Toggle, Konami::SCC> mixer_;
|
|
||||||
Outputs::Speaker::PullLowpass<Outputs::Speaker::CompoundSource<GI::AY38910::AY38910<false>, Audio::Toggle, Konami::SCC>> speaker_;
|
|
||||||
|
|
||||||
Storage::Tape::BinaryTapePlayer tape_player_;
|
Storage::Tape::BinaryTapePlayer tape_player_;
|
||||||
bool tape_player_is_sleeping_ = false;
|
bool tape_player_is_sleeping_ = false;
|
||||||
bool allow_fast_tape_ = false;
|
bool allow_fast_tape_ = false;
|
||||||
@ -876,6 +925,7 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
i8255PortHandler i8255_port_handler_;
|
i8255PortHandler i8255_port_handler_;
|
||||||
|
Speaker<has_opll> speaker_;
|
||||||
AYPortHandler ay_port_handler_;
|
AYPortHandler ay_port_handler_;
|
||||||
|
|
||||||
/// The current primary and secondary slot selections; the former retains whatever was written
|
/// The current primary and secondary slot selections; the former retains whatever was written
|
||||||
@ -939,7 +989,7 @@ class ConcreteMachine:
|
|||||||
//
|
//
|
||||||
// Slot 3-1 holds the BIOS extension ROM.
|
// Slot 3-1 holds the BIOS extension ROM.
|
||||||
//
|
//
|
||||||
// [Slot 3-2 will likely hold MSX-MUSIC, but that's TODO]
|
// Slot 3-2 holds the MSX-MUSIC.
|
||||||
//
|
//
|
||||||
MemorySlot &bios_slot() {
|
MemorySlot &bios_slot() {
|
||||||
return memory_slots_[0].subslot(0);
|
return memory_slots_[0].subslot(0);
|
||||||
@ -950,6 +1000,9 @@ class ConcreteMachine:
|
|||||||
MemorySlot &extension_rom_slot() {
|
MemorySlot &extension_rom_slot() {
|
||||||
return memory_slots_[3].subslot(1);
|
return memory_slots_[3].subslot(1);
|
||||||
}
|
}
|
||||||
|
MemorySlot &msx_music_slot() {
|
||||||
|
return memory_slots_[3].subslot(2);
|
||||||
|
}
|
||||||
|
|
||||||
MemorySlot &cartridge_slot() {
|
MemorySlot &cartridge_slot() {
|
||||||
return cartridge_primary().subslot(0);
|
return cartridge_primary().subslot(0);
|
||||||
@ -970,7 +1023,8 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
DiskROM *disk_handler() {
|
DiskROM *disk_handler() {
|
||||||
return dynamic_cast<DiskROM *>(disk_primary().handler.get());
|
return dynamic_cast<DiskROM *>(disk_primary().handler.get());
|
||||||
}};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -978,10 +1032,18 @@ using namespace MSX;
|
|||||||
|
|
||||||
Machine *Machine::MSX(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
|
Machine *Machine::MSX(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
|
||||||
const auto msx_target = dynamic_cast<const Target *>(target);
|
const auto msx_target = dynamic_cast<const Target *>(target);
|
||||||
switch(msx_target->model) {
|
if(msx_target->has_msx_music) {
|
||||||
default: return nullptr;
|
switch(msx_target->model) {
|
||||||
case Target::Model::MSX1: return new ConcreteMachine<Target::Model::MSX1>(*msx_target, rom_fetcher);
|
default: return nullptr;
|
||||||
case Target::Model::MSX2: return new ConcreteMachine<Target::Model::MSX2>(*msx_target, rom_fetcher);
|
case Target::Model::MSX1: return new ConcreteMachine<Target::Model::MSX1, true>(*msx_target, rom_fetcher);
|
||||||
|
case Target::Model::MSX2: return new ConcreteMachine<Target::Model::MSX2, true>(*msx_target, rom_fetcher);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch(msx_target->model) {
|
||||||
|
default: return nullptr;
|
||||||
|
case Target::Model::MSX1: return new ConcreteMachine<Target::Model::MSX1, false>(*msx_target, rom_fetcher);
|
||||||
|
case Target::Model::MSX2: return new ConcreteMachine<Target::Model::MSX2, false>(*msx_target, rom_fetcher);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -576,6 +576,7 @@ Description::Description(Name name) {
|
|||||||
|
|
||||||
case Name::MSX2GenericBIOS: *this = Description(name, "MSX", "a generic MSX2 BIOS", "msx2.rom", 32*1024, 0x6cdaf3a5u); break;
|
case Name::MSX2GenericBIOS: *this = Description(name, "MSX", "a generic MSX2 BIOS", "msx2.rom", 32*1024, 0x6cdaf3a5u); break;
|
||||||
case Name::MSX2Extension: *this = Description(name, "MSX", "the MSX2 extension ROM", "msx2ext.rom", 16*1024, 0x66237ecfu); break;
|
case Name::MSX2Extension: *this = Description(name, "MSX", "the MSX2 extension ROM", "msx2ext.rom", 16*1024, 0x66237ecfu); break;
|
||||||
|
case Name::MSXMusic: *this = Description(name, "MSX", "the MSX-MUSIC / FM-PAC ROM", "fmpac.rom", 64*1024, 0x0e84505du); break;
|
||||||
|
|
||||||
case Name::SinclairQLJS:
|
case Name::SinclairQLJS:
|
||||||
*this = Description(name, "SinclairQL", "the Sinclair QL 'JS' ROM", "js.rom", 48*1024, 0x0f95aab5u);
|
*this = Description(name, "SinclairQL", "the Sinclair QL 'JS' ROM", "js.rom", 48*1024, 0x0f95aab5u);
|
||||||
|
@ -119,6 +119,7 @@ enum Name {
|
|||||||
|
|
||||||
MSX2GenericBIOS,
|
MSX2GenericBIOS,
|
||||||
MSX2Extension,
|
MSX2Extension,
|
||||||
|
MSXMusic,
|
||||||
|
|
||||||
// Oric.
|
// Oric.
|
||||||
OricColourROM,
|
OricColourROM,
|
||||||
|
@ -137,7 +137,7 @@ typedef int Kilobytes;
|
|||||||
- (instancetype)initWithElectronDFS:(BOOL)dfs adfs:(BOOL)adfs ap6:(BOOL)ap6 sidewaysRAM:(BOOL)sidewaysRAM;
|
- (instancetype)initWithElectronDFS:(BOOL)dfs adfs:(BOOL)adfs ap6:(BOOL)ap6 sidewaysRAM:(BOOL)sidewaysRAM;
|
||||||
- (instancetype)initWithEnterpriseModel:(CSMachineEnterpriseModel)model speed:(CSMachineEnterpriseSpeed)speed exosVersion:(CSMachineEnterpriseEXOS)exosVersion basicVersion:(CSMachineEnterpriseBASIC)basicVersion dos:(CSMachineEnterpriseDOS)dos;
|
- (instancetype)initWithEnterpriseModel:(CSMachineEnterpriseModel)model speed:(CSMachineEnterpriseSpeed)speed exosVersion:(CSMachineEnterpriseEXOS)exosVersion basicVersion:(CSMachineEnterpriseBASIC)basicVersion dos:(CSMachineEnterpriseDOS)dos;
|
||||||
- (instancetype)initWithMacintoshModel:(CSMachineMacintoshModel)model;
|
- (instancetype)initWithMacintoshModel:(CSMachineMacintoshModel)model;
|
||||||
- (instancetype)initWithMSXModel:(CSMachineMSXModel)model region:(CSMachineMSXRegion)region hasDiskDrive:(BOOL)hasDiskDrive;
|
- (instancetype)initWithMSXModel:(CSMachineMSXModel)model region:(CSMachineMSXRegion)region hasDiskDrive:(BOOL)hasDiskDrive hasMSXMUSIC:(BOOL)hasMSXMUSIC;
|
||||||
- (instancetype)initWithOricModel:(CSMachineOricModel)model diskInterface:(CSMachineOricDiskInterface)diskInterface;
|
- (instancetype)initWithOricModel:(CSMachineOricModel)model diskInterface:(CSMachineOricDiskInterface)diskInterface;
|
||||||
- (instancetype)initWithSpectrumModel:(CSMachineSpectrumModel)model;
|
- (instancetype)initWithSpectrumModel:(CSMachineSpectrumModel)model;
|
||||||
- (instancetype)initWithVic20Region:(CSMachineVic20Region)region memorySize:(Kilobytes)memorySize hasC1540:(BOOL)hasC1540;
|
- (instancetype)initWithVic20Region:(CSMachineVic20Region)region memorySize:(Kilobytes)memorySize hasC1540:(BOOL)hasC1540;
|
||||||
|
@ -229,12 +229,13 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithMSXModel:(CSMachineMSXModel)model region:(CSMachineMSXRegion)region hasDiskDrive:(BOOL)hasDiskDrive {
|
- (instancetype)initWithMSXModel:(CSMachineMSXModel)model region:(CSMachineMSXRegion)region hasDiskDrive:(BOOL)hasDiskDrive hasMSXMUSIC:(BOOL)hasMSXMUSIC {
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if(self) {
|
if(self) {
|
||||||
using Target = Analyser::Static::MSX::Target;
|
using Target = Analyser::Static::MSX::Target;
|
||||||
auto target = std::make_unique<Target>();
|
auto target = std::make_unique<Target>();
|
||||||
target->has_disk_drive = !!hasDiskDrive;
|
target->has_disk_drive = hasDiskDrive;
|
||||||
|
target->has_msx_music = hasMSXMUSIC;
|
||||||
switch(region) {
|
switch(region) {
|
||||||
case CSMachineMSXRegionAmerican: target->region = Target::Region::USA; break;
|
case CSMachineMSXRegionAmerican: target->region = Target::Region::USA; break;
|
||||||
case CSMachineMSXRegionEuropean: target->region = Target::Region::Europe; break;
|
case CSMachineMSXRegionEuropean: target->region = Target::Region::Europe; break;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21701" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21701"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
@ -18,7 +18,7 @@
|
|||||||
<windowStyleMask key="styleMask" titled="YES" documentModal="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" documentModal="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
<rect key="contentRect" x="196" y="240" width="590" height="353"/>
|
<rect key="contentRect" x="196" y="240" width="590" height="353"/>
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1440"/>
|
<rect key="screenRect" x="0.0" y="0.0" width="1800" height="1131"/>
|
||||||
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
|
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="590" height="353"/>
|
<rect key="frame" x="0.0" y="0.0" width="590" height="353"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
@ -628,6 +628,13 @@ Gw
|
|||||||
</menu>
|
</menu>
|
||||||
</popUpButtonCell>
|
</popUpButtonCell>
|
||||||
</popUpButton>
|
</popUpButton>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="V0e-KX-Wqn">
|
||||||
|
<rect key="frame" x="18" y="105" width="145" height="18"/>
|
||||||
|
<buttonCell key="cell" type="check" title="Attach MSX-MUSIC" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="DjG-qd-BYh">
|
||||||
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
</button>
|
||||||
</subviews>
|
</subviews>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="LG6-mP-SeG" secondAttribute="trailing" constant="20" symbolic="YES" id="0Oc-n7-gaM"/>
|
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="LG6-mP-SeG" secondAttribute="trailing" constant="20" symbolic="YES" id="0Oc-n7-gaM"/>
|
||||||
@ -636,11 +643,14 @@ Gw
|
|||||||
<constraint firstItem="44m-4z-PVv" firstAttribute="leading" secondItem="gFV-RB-7dB" secondAttribute="trailing" constant="8" symbolic="YES" id="JG1-Kp-dYk"/>
|
<constraint firstItem="44m-4z-PVv" firstAttribute="leading" secondItem="gFV-RB-7dB" secondAttribute="trailing" constant="8" symbolic="YES" id="JG1-Kp-dYk"/>
|
||||||
<constraint firstItem="8xT-Pr-8SE" firstAttribute="top" secondItem="LG6-mP-SeG" secondAttribute="bottom" constant="8" symbolic="YES" id="LBt-4m-GDc"/>
|
<constraint firstItem="8xT-Pr-8SE" firstAttribute="top" secondItem="LG6-mP-SeG" secondAttribute="bottom" constant="8" symbolic="YES" id="LBt-4m-GDc"/>
|
||||||
<constraint firstItem="gFV-RB-7dB" firstAttribute="centerY" secondItem="44m-4z-PVv" secondAttribute="centerY" id="Rf8-1i-ttI"/>
|
<constraint firstItem="gFV-RB-7dB" firstAttribute="centerY" secondItem="44m-4z-PVv" secondAttribute="centerY" id="Rf8-1i-ttI"/>
|
||||||
|
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="V0e-KX-Wqn" secondAttribute="bottom" constant="20" symbolic="YES" id="YIT-tx-yob"/>
|
||||||
<constraint firstItem="gFV-RB-7dB" firstAttribute="leading" secondItem="mWD-An-tR7" secondAttribute="leading" constant="20" symbolic="YES" id="aaL-4g-iW8"/>
|
<constraint firstItem="gFV-RB-7dB" firstAttribute="leading" secondItem="mWD-An-tR7" secondAttribute="leading" constant="20" symbolic="YES" id="aaL-4g-iW8"/>
|
||||||
|
<constraint firstItem="V0e-KX-Wqn" firstAttribute="top" secondItem="8xT-Pr-8SE" secondAttribute="bottom" constant="6" symbolic="YES" id="fjr-Qf-WzP"/>
|
||||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="8xT-Pr-8SE" secondAttribute="trailing" constant="20" symbolic="YES" id="l8P-UW-8ig"/>
|
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="8xT-Pr-8SE" secondAttribute="trailing" constant="20" symbolic="YES" id="l8P-UW-8ig"/>
|
||||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="8xT-Pr-8SE" secondAttribute="bottom" constant="20" symbolic="YES" id="mga-YX-Bek"/>
|
<constraint firstItem="V0e-KX-Wqn" firstAttribute="leading" secondItem="mWD-An-tR7" secondAttribute="leading" constant="20" symbolic="YES" id="q1J-kN-Gwi"/>
|
||||||
<constraint firstItem="8xT-Pr-8SE" firstAttribute="leading" secondItem="mWD-An-tR7" secondAttribute="leading" constant="20" symbolic="YES" id="q8Q-kh-Opj"/>
|
<constraint firstItem="8xT-Pr-8SE" firstAttribute="leading" secondItem="mWD-An-tR7" secondAttribute="leading" constant="20" symbolic="YES" id="q8Q-kh-Opj"/>
|
||||||
<constraint firstItem="LG6-mP-SeG" firstAttribute="leading" secondItem="ZaD-7v-rMS" secondAttribute="trailing" constant="8" symbolic="YES" id="svb-nH-GlP"/>
|
<constraint firstItem="LG6-mP-SeG" firstAttribute="leading" secondItem="ZaD-7v-rMS" secondAttribute="trailing" constant="8" symbolic="YES" id="svb-nH-GlP"/>
|
||||||
|
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="V0e-KX-Wqn" secondAttribute="trailing" constant="20" symbolic="YES" id="w85-NH-WOz"/>
|
||||||
<constraint firstItem="44m-4z-PVv" firstAttribute="top" secondItem="mWD-An-tR7" secondAttribute="top" constant="20" symbolic="YES" id="zJk-w1-PWX"/>
|
<constraint firstItem="44m-4z-PVv" firstAttribute="top" secondItem="mWD-An-tR7" secondAttribute="top" constant="20" symbolic="YES" id="zJk-w1-PWX"/>
|
||||||
<constraint firstItem="ZaD-7v-rMS" firstAttribute="leading" secondItem="mWD-An-tR7" secondAttribute="leading" constant="20" symbolic="YES" id="zgh-a5-FNF"/>
|
<constraint firstItem="ZaD-7v-rMS" firstAttribute="leading" secondItem="mWD-An-tR7" secondAttribute="leading" constant="20" symbolic="YES" id="zgh-a5-FNF"/>
|
||||||
<constraint firstItem="ZaD-7v-rMS" firstAttribute="centerY" secondItem="LG6-mP-SeG" secondAttribute="centerY" id="zkw-kD-lRV"/>
|
<constraint firstItem="ZaD-7v-rMS" firstAttribute="centerY" secondItem="LG6-mP-SeG" secondAttribute="centerY" id="zkw-kD-lRV"/>
|
||||||
@ -1017,6 +1027,7 @@ Gw
|
|||||||
<outlet property="machineSelector" destination="VUb-QG-x7c" id="crR-hB-jGd"/>
|
<outlet property="machineSelector" destination="VUb-QG-x7c" id="crR-hB-jGd"/>
|
||||||
<outlet property="macintoshModelTypeButton" destination="xa6-NA-JY5" id="2jX-PY-v2z"/>
|
<outlet property="macintoshModelTypeButton" destination="xa6-NA-JY5" id="2jX-PY-v2z"/>
|
||||||
<outlet property="msxHasDiskDriveButton" destination="8xT-Pr-8SE" id="zGH-GA-9QF"/>
|
<outlet property="msxHasDiskDriveButton" destination="8xT-Pr-8SE" id="zGH-GA-9QF"/>
|
||||||
|
<outlet property="msxHasMSXMUSICButton" destination="V0e-KX-Wqn" id="L68-4u-P6r"/>
|
||||||
<outlet property="msxModelButton" destination="44m-4z-PVv" id="qRk-Hf-aBL"/>
|
<outlet property="msxModelButton" destination="44m-4z-PVv" id="qRk-Hf-aBL"/>
|
||||||
<outlet property="msxRegionButton" destination="LG6-mP-SeG" id="3a9-VG-6Wf"/>
|
<outlet property="msxRegionButton" destination="LG6-mP-SeG" id="3a9-VG-6Wf"/>
|
||||||
<outlet property="oricDiskInterfaceButton" destination="fYL-p6-wyn" id="aAt-wM-hRZ"/>
|
<outlet property="oricDiskInterfaceButton" destination="fYL-p6-wyn" id="aAt-wM-hRZ"/>
|
||||||
|
@ -57,6 +57,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
|||||||
@IBOutlet var msxModelButton: NSPopUpButton!
|
@IBOutlet var msxModelButton: NSPopUpButton!
|
||||||
@IBOutlet var msxRegionButton: NSPopUpButton!
|
@IBOutlet var msxRegionButton: NSPopUpButton!
|
||||||
@IBOutlet var msxHasDiskDriveButton: NSButton!
|
@IBOutlet var msxHasDiskDriveButton: NSButton!
|
||||||
|
@IBOutlet var msxHasMSXMUSICButton: NSButton!
|
||||||
|
|
||||||
// MARK: - Oric properties
|
// MARK: - Oric properties
|
||||||
@IBOutlet var oricModelTypeButton: NSPopUpButton!
|
@IBOutlet var oricModelTypeButton: NSPopUpButton!
|
||||||
@ -138,6 +139,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
|||||||
msxModelButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.msxModel"))
|
msxModelButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.msxModel"))
|
||||||
msxRegionButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.msxRegion"))
|
msxRegionButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.msxRegion"))
|
||||||
msxHasDiskDriveButton.state = standardUserDefaults.bool(forKey: "new.msxDiskDrive") ? .on : .off
|
msxHasDiskDriveButton.state = standardUserDefaults.bool(forKey: "new.msxDiskDrive") ? .on : .off
|
||||||
|
msxHasMSXMUSICButton.state = standardUserDefaults.bool(forKey: "new.msxMSXMUSIC") ? .on : .off
|
||||||
|
|
||||||
// Oric settings
|
// Oric settings
|
||||||
oricDiskInterfaceButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.oricDiskInterface"))
|
oricDiskInterfaceButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.oricDiskInterface"))
|
||||||
@ -203,6 +205,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
|||||||
standardUserDefaults.set(msxModelButton.selectedTag(), forKey: "new.msxModel")
|
standardUserDefaults.set(msxModelButton.selectedTag(), forKey: "new.msxModel")
|
||||||
standardUserDefaults.set(msxRegionButton.selectedTag(), forKey: "new.msxRegion")
|
standardUserDefaults.set(msxRegionButton.selectedTag(), forKey: "new.msxRegion")
|
||||||
standardUserDefaults.set(msxHasDiskDriveButton.state == .on, forKey: "new.msxDiskDrive")
|
standardUserDefaults.set(msxHasDiskDriveButton.state == .on, forKey: "new.msxDiskDrive")
|
||||||
|
standardUserDefaults.set(msxHasMSXMUSICButton.state == .on, forKey: "new.msxMSXMUSIC")
|
||||||
|
|
||||||
// Oric settings
|
// Oric settings
|
||||||
standardUserDefaults.set(oricDiskInterfaceButton.selectedTag(), forKey: "new.oricDiskInterface")
|
standardUserDefaults.set(oricDiskInterfaceButton.selectedTag(), forKey: "new.oricDiskInterface")
|
||||||
@ -358,6 +361,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
|||||||
|
|
||||||
case "msx":
|
case "msx":
|
||||||
let hasDiskDrive = msxHasDiskDriveButton.state == .on
|
let hasDiskDrive = msxHasDiskDriveButton.state == .on
|
||||||
|
let hasMSXMUSIC = msxHasMSXMUSICButton.state == .on
|
||||||
var region: CSMachineMSXRegion
|
var region: CSMachineMSXRegion
|
||||||
switch msxRegionButton.selectedTag() {
|
switch msxRegionButton.selectedTag() {
|
||||||
case 2: region = .japanese
|
case 2: region = .japanese
|
||||||
@ -371,7 +375,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
|||||||
case 1: fallthrough
|
case 1: fallthrough
|
||||||
default: model = .MSX1
|
default: model = .MSX1
|
||||||
}
|
}
|
||||||
return CSStaticAnalyser(msxModel: model, region: region, hasDiskDrive: hasDiskDrive)
|
return CSStaticAnalyser(msxModel: model, region: region, hasDiskDrive: hasDiskDrive, hasMSXMUSIC: hasMSXMUSIC)
|
||||||
|
|
||||||
case "oric":
|
case "oric":
|
||||||
var diskInterface: CSMachineOricDiskInterface = .none
|
var diskInterface: CSMachineOricDiskInterface = .none
|
||||||
|
Loading…
Reference in New Issue
Block a user