mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-25 16:31:42 +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> {
|
||||
bool has_disk_drive = false;
|
||||
bool has_msx_music = true;
|
||||
std::string loading_command;
|
||||
|
||||
ReflectableEnum(Model,
|
||||
@ -36,6 +37,7 @@ struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<
|
||||
Target(): Analyser::Static::Target(Machine::MSX) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(has_disk_drive);
|
||||
DeclareField(has_msx_music);
|
||||
DeclareField(region);
|
||||
AnnounceEnum(Region);
|
||||
DeclareField(model);
|
||||
|
@ -27,8 +27,9 @@
|
||||
#include "../../Components/9918/9918.hpp"
|
||||
#include "../../Components/AudioToggle/AudioToggle.hpp"
|
||||
#include "../../Components/AY38910/AY38910.hpp"
|
||||
#include "../../Components/RP5C01/RP5C01.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/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;
|
||||
|
||||
template <Target::Model model>
|
||||
template <Target::Model model, bool has_opll>
|
||||
class ConcreteMachine:
|
||||
public Machine,
|
||||
public CPU::Z80::BusHandler,
|
||||
@ -148,32 +189,31 @@ class ConcreteMachine:
|
||||
private:
|
||||
// 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 int ClockRate = 3579545;
|
||||
|
||||
public:
|
||||
ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher):
|
||||
z80_(*this),
|
||||
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),
|
||||
i8255_port_handler_(*this, audio_toggle_, tape_player_),
|
||||
i8255_port_handler_(*this, speaker_.audio_toggle, tape_player_),
|
||||
ay_port_handler_(tape_player_),
|
||||
memory_slots_{{*this}, {*this}, {*this}, {*this}},
|
||||
clock_(ClockRate) {
|
||||
set_clock_rate(ClockRate);
|
||||
clear_all_keys();
|
||||
|
||||
ay_.set_port_handler(&ay_port_handler_);
|
||||
speaker_.set_input_rate(3579545.0f / 2.0f);
|
||||
speaker_.ay.set_port_handler(&ay_port_handler_);
|
||||
speaker_.speaker.set_input_rate(3579545.0f / 2.0f);
|
||||
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.
|
||||
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.
|
||||
const std::string machine_name = "MSX";
|
||||
@ -229,11 +269,12 @@ class ConcreteMachine:
|
||||
|
||||
// Fetch the necessary ROMs; try the region-specific ROM first,
|
||||
// but failing that fall back on patching the main one.
|
||||
ROM::Request request;
|
||||
ROM::Request request = bios_request;
|
||||
if(target.has_disk_drive) {
|
||||
request = ROM::Request(ROM::Name::MSXDOS) && bios_request;
|
||||
} else {
|
||||
request = bios_request;
|
||||
request = request && ROM::Request(ROM::Name::MSXDOS);
|
||||
}
|
||||
if(target.has_msx_music) {
|
||||
request = request && ROM::Request(ROM::Name::MSXMusic);
|
||||
}
|
||||
|
||||
auto roms = rom_fetcher(request);
|
||||
@ -294,6 +335,14 @@ class ConcreteMachine:
|
||||
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_media(target.media);
|
||||
|
||||
@ -307,7 +356,7 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
~ConcreteMachine() {
|
||||
audio_queue_.flush();
|
||||
speaker_.audio_queue.flush();
|
||||
}
|
||||
|
||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) final {
|
||||
@ -327,7 +376,7 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
Outputs::Speaker::Speaker *get_speaker() final {
|
||||
return &speaker_;
|
||||
return &speaker_.speaker;
|
||||
}
|
||||
|
||||
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));
|
||||
break;
|
||||
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;
|
||||
case Analyser::Static::MSX::Cartridge::ASCII8kb:
|
||||
cartridge_primary().handler = std::make_unique<Cartridge::ASCII8kbROMSlotHandler>(static_cast<MSX::MemorySlot &>(slot));
|
||||
@ -580,7 +629,7 @@ class ConcreteMachine:
|
||||
|
||||
case 0xa2:
|
||||
update_audio();
|
||||
*cycle.value = GI::AY38910::Utility::read(ay_);
|
||||
*cycle.value = GI::AY38910::Utility::read(speaker_.ay);
|
||||
break;
|
||||
|
||||
case 0xa8: case 0xa9:
|
||||
@ -624,7 +673,7 @@ class ConcreteMachine:
|
||||
|
||||
case 0xa0: case 0xa1:
|
||||
update_audio();
|
||||
GI::AY38910::Utility::write(ay_, port == 0xa1, *cycle.value);
|
||||
GI::AY38910::Utility::write(speaker_.ay, port == 0xa1, *cycle.value);
|
||||
break;
|
||||
|
||||
case 0xa8: case 0xa9:
|
||||
@ -668,6 +717,13 @@ class ConcreteMachine:
|
||||
update_paging();
|
||||
} break;
|
||||
|
||||
case 0x7c: case 0x7d:
|
||||
if constexpr (has_opll) {
|
||||
speaker_.opll.write(address, *cycle.value);
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
|
||||
default:
|
||||
printf("Unhandled write %02x of %02x\n", address & 0xff, *cycle.value);
|
||||
break;
|
||||
@ -725,7 +781,7 @@ class ConcreteMachine:
|
||||
}
|
||||
if(outputs & Output::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 {
|
||||
int mask = 1 << (key & 7);
|
||||
int line = key >> 4;
|
||||
const int mask = 1 << (key & 7);
|
||||
const int line = key >> 4;
|
||||
if(is_pressed) key_states_[line] &= ~mask; else key_states_[line] |= mask;
|
||||
}
|
||||
|
||||
@ -774,7 +830,7 @@ class ConcreteMachine:
|
||||
|
||||
// MARK: - Activity::Source
|
||||
void set_activity_observer(Activity::Observer *observer) final {
|
||||
DiskROM *handler = disk_handler();
|
||||
DiskROM *const handler = disk_handler();
|
||||
if(handler) {
|
||||
handler->set_activity_observer(observer);
|
||||
}
|
||||
@ -788,7 +844,7 @@ class ConcreteMachine:
|
||||
|
||||
private:
|
||||
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 {
|
||||
@ -855,13 +911,6 @@ class ConcreteMachine:
|
||||
JustInTimeActor<TI::TMS::TMS9918<vdp_model()>> vdp_;
|
||||
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_;
|
||||
bool tape_player_is_sleeping_ = false;
|
||||
bool allow_fast_tape_ = false;
|
||||
@ -876,6 +925,7 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
i8255PortHandler i8255_port_handler_;
|
||||
Speaker<has_opll> speaker_;
|
||||
AYPortHandler ay_port_handler_;
|
||||
|
||||
/// 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-2 will likely hold MSX-MUSIC, but that's TODO]
|
||||
// Slot 3-2 holds the MSX-MUSIC.
|
||||
//
|
||||
MemorySlot &bios_slot() {
|
||||
return memory_slots_[0].subslot(0);
|
||||
@ -950,6 +1000,9 @@ class ConcreteMachine:
|
||||
MemorySlot &extension_rom_slot() {
|
||||
return memory_slots_[3].subslot(1);
|
||||
}
|
||||
MemorySlot &msx_music_slot() {
|
||||
return memory_slots_[3].subslot(2);
|
||||
}
|
||||
|
||||
MemorySlot &cartridge_slot() {
|
||||
return cartridge_primary().subslot(0);
|
||||
@ -970,7 +1023,8 @@ class ConcreteMachine:
|
||||
}
|
||||
DiskROM *disk_handler() {
|
||||
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) {
|
||||
const auto msx_target = dynamic_cast<const Target *>(target);
|
||||
switch(msx_target->model) {
|
||||
default: return nullptr;
|
||||
case Target::Model::MSX1: return new ConcreteMachine<Target::Model::MSX1>(*msx_target, rom_fetcher);
|
||||
case Target::Model::MSX2: return new ConcreteMachine<Target::Model::MSX2>(*msx_target, rom_fetcher);
|
||||
if(msx_target->has_msx_music) {
|
||||
switch(msx_target->model) {
|
||||
default: return nullptr;
|
||||
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::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:
|
||||
*this = Description(name, "SinclairQL", "the Sinclair QL 'JS' ROM", "js.rom", 48*1024, 0x0f95aab5u);
|
||||
|
@ -119,6 +119,7 @@ enum Name {
|
||||
|
||||
MSX2GenericBIOS,
|
||||
MSX2Extension,
|
||||
MSXMusic,
|
||||
|
||||
// Oric.
|
||||
OricColourROM,
|
||||
|
@ -137,7 +137,7 @@ typedef int Kilobytes;
|
||||
- (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)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)initWithSpectrumModel:(CSMachineSpectrumModel)model;
|
||||
- (instancetype)initWithVic20Region:(CSMachineVic20Region)region memorySize:(Kilobytes)memorySize hasC1540:(BOOL)hasC1540;
|
||||
|
@ -229,12 +229,13 @@
|
||||
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];
|
||||
if(self) {
|
||||
using Target = Analyser::Static::MSX::Target;
|
||||
auto target = std::make_unique<Target>();
|
||||
target->has_disk_drive = !!hasDiskDrive;
|
||||
target->has_disk_drive = hasDiskDrive;
|
||||
target->has_msx_music = hasMSXMUSIC;
|
||||
switch(region) {
|
||||
case CSMachineMSXRegionAmerican: target->region = Target::Region::USA; break;
|
||||
case CSMachineMSXRegionEuropean: target->region = Target::Region::Europe; break;
|
||||
|
@ -1,8 +1,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>
|
||||
<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"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@ -18,7 +18,7 @@
|
||||
<windowStyleMask key="styleMask" titled="YES" documentModal="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="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">
|
||||
<rect key="frame" x="0.0" y="0.0" width="590" height="353"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
@ -628,6 +628,13 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</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>
|
||||
<constraints>
|
||||
<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="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 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="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="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="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="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"/>
|
||||
@ -1017,6 +1027,7 @@ Gw
|
||||
<outlet property="machineSelector" destination="VUb-QG-x7c" id="crR-hB-jGd"/>
|
||||
<outlet property="macintoshModelTypeButton" destination="xa6-NA-JY5" id="2jX-PY-v2z"/>
|
||||
<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="msxRegionButton" destination="LG6-mP-SeG" id="3a9-VG-6Wf"/>
|
||||
<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 msxRegionButton: NSPopUpButton!
|
||||
@IBOutlet var msxHasDiskDriveButton: NSButton!
|
||||
@IBOutlet var msxHasMSXMUSICButton: NSButton!
|
||||
|
||||
// MARK: - Oric properties
|
||||
@IBOutlet var oricModelTypeButton: NSPopUpButton!
|
||||
@ -138,6 +139,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
msxModelButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.msxModel"))
|
||||
msxRegionButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.msxRegion"))
|
||||
msxHasDiskDriveButton.state = standardUserDefaults.bool(forKey: "new.msxDiskDrive") ? .on : .off
|
||||
msxHasMSXMUSICButton.state = standardUserDefaults.bool(forKey: "new.msxMSXMUSIC") ? .on : .off
|
||||
|
||||
// Oric settings
|
||||
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(msxRegionButton.selectedTag(), forKey: "new.msxRegion")
|
||||
standardUserDefaults.set(msxHasDiskDriveButton.state == .on, forKey: "new.msxDiskDrive")
|
||||
standardUserDefaults.set(msxHasMSXMUSICButton.state == .on, forKey: "new.msxMSXMUSIC")
|
||||
|
||||
// Oric settings
|
||||
standardUserDefaults.set(oricDiskInterfaceButton.selectedTag(), forKey: "new.oricDiskInterface")
|
||||
@ -358,6 +361,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
|
||||
case "msx":
|
||||
let hasDiskDrive = msxHasDiskDriveButton.state == .on
|
||||
let hasMSXMUSIC = msxHasMSXMUSICButton.state == .on
|
||||
var region: CSMachineMSXRegion
|
||||
switch msxRegionButton.selectedTag() {
|
||||
case 2: region = .japanese
|
||||
@ -371,7 +375,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
case 1: fallthrough
|
||||
default: model = .MSX1
|
||||
}
|
||||
return CSStaticAnalyser(msxModel: model, region: region, hasDiskDrive: hasDiskDrive)
|
||||
return CSStaticAnalyser(msxModel: model, region: region, hasDiskDrive: hasDiskDrive, hasMSXMUSIC: hasMSXMUSIC)
|
||||
|
||||
case "oric":
|
||||
var diskInterface: CSMachineOricDiskInterface = .none
|
||||
|
Loading…
Reference in New Issue
Block a user