1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-02-21 05:29:13 +00:00

Merge pull request #1321 from TomHarte/Mockingboard

Add Mockingboard support to the Apple II.
This commit is contained in:
Thomas Harte 2024-02-15 10:24:39 -05:00 committed by GitHub
commit ac171d166e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 416 additions and 101 deletions

View File

@ -34,12 +34,14 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
Model model = Model::IIe; Model model = Model::IIe;
DiskController disk_controller = DiskController::None; DiskController disk_controller = DiskController::None;
SCSIController scsi_controller = SCSIController::None; SCSIController scsi_controller = SCSIController::None;
bool has_mockingboard = true;
Target() : Analyser::Static::Target(Machine::AppleII) { Target() : Analyser::Static::Target(Machine::AppleII) {
if(needs_declare()) { if(needs_declare()) {
DeclareField(model); DeclareField(model);
DeclareField(disk_controller); DeclareField(disk_controller);
DeclareField(scsi_controller); DeclareField(scsi_controller);
DeclareField(has_mockingboard);
AnnounceEnum(Model); AnnounceEnum(Model);
AnnounceEnum(DiskController); AnnounceEnum(DiskController);
@ -48,4 +50,8 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
} }
}; };
constexpr bool is_iie(Target::Model model) {
return model == Target::Model::IIe || model == Target::Model::EnhancedIIe;
}
} }

View File

@ -68,7 +68,7 @@ class IRQDelegatePortHandler: public PortHandler {
/// Sets the delegate that will receive notification of changes in the interrupt line. /// Sets the delegate that will receive notification of changes in the interrupt line.
void set_interrupt_delegate(Delegate *delegate); void set_interrupt_delegate(Delegate *delegate);
/// Overrides PortHandler::set_interrupt_status, notifying the delegate if one is set. /// Overrides @c PortHandler::set_interrupt_status, notifying the delegate if one is set.
void set_interrupt_status(bool new_status); void set_interrupt_status(bool new_status);
private: private:

View File

@ -376,6 +376,30 @@ void AY38910SampleSource<is_stereo>::set_control_lines(ControlLines control_line
update_bus(); update_bus();
} }
template <bool is_stereo>
void AY38910SampleSource<is_stereo>::set_reset(bool active) {
if(active == reset_) return;
reset_ = active;
// Reset upon the leading edge; TODO: is this right?
if(reset_) {
reset();
}
}
template <bool is_stereo>
void AY38910SampleSource<is_stereo>::reset() {
// TODO: the below is a guess. Look up real answers.
selected_register_ = 0;
std::fill(registers_, registers_ + 16, 0);
task_queue_.enqueue([&] {
std::fill(output_registers_, output_registers_ + 16, 0);
evaluate_output_volume();
});
}
template <bool is_stereo> template <bool is_stereo>
void AY38910SampleSource<is_stereo>::update_bus() { void AY38910SampleSource<is_stereo>::update_bus() {
// Assume no output, unless this turns out to be a read. // Assume no output, unless this turns out to be a read.

View File

@ -70,6 +70,7 @@ template <bool stereo> class AY38910SampleSource {
public: public:
/// Creates a new AY38910. /// Creates a new AY38910.
AY38910SampleSource(Personality, Concurrency::AsyncTaskQueue<false> &); AY38910SampleSource(Personality, Concurrency::AsyncTaskQueue<false> &);
AY38910SampleSource(const AY38910SampleSource &) = delete;
/// Sets the value the AY would read from its data lines if it were not outputting. /// Sets the value the AY would read from its data lines if it were not outputting.
void set_data_input(uint8_t r); void set_data_input(uint8_t r);
@ -80,6 +81,12 @@ template <bool stereo> class AY38910SampleSource {
/// Sets the current control line state, as a bit field. /// Sets the current control line state, as a bit field.
void set_control_lines(ControlLines control_lines); void set_control_lines(ControlLines control_lines);
/// Strobes the reset line.
void reset();
/// Sets the current value of the reset line.
void set_reset(bool reset);
/*! /*!
Gets the value that would appear on the requested interface port if it were in output mode. Gets the value that would appear on the requested interface port if it were in output mode.
@parameter port_b @c true to get the value for Port B, @c false to get the value for Port A. @parameter port_b @c true to get the value for Port B, @c false to get the value for Port A.
@ -114,13 +121,15 @@ template <bool stereo> class AY38910SampleSource {
private: private:
Concurrency::AsyncTaskQueue<false> &task_queue_; Concurrency::AsyncTaskQueue<false> &task_queue_;
int selected_register_ = 0; bool reset_ = false;
uint8_t registers_[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
uint8_t output_registers_[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int tone_periods_[3] = {0, 0, 0}; int selected_register_ = 0;
int tone_counters_[3] = {0, 0, 0}; uint8_t registers_[16]{};
int tone_outputs_[3] = {0, 0, 0}; uint8_t output_registers_[16]{};
int tone_periods_[3]{};
int tone_counters_[3]{};
int tone_outputs_[3]{};
int noise_period_ = 0; int noise_period_ = 0;
int noise_counter_ = 0; int noise_counter_ = 0;

View File

@ -15,7 +15,9 @@
#include "../../../Processors/6502/6502.hpp" #include "../../../Processors/6502/6502.hpp"
#include "../../../Components/AudioToggle/AudioToggle.hpp" #include "../../../Components/AudioToggle/AudioToggle.hpp"
#include "../../../Components/AY38910/AY38910.hpp"
#include "../../../Outputs/Speaker/Implementation/CompoundSource.hpp"
#include "../../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp" #include "../../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
#include "../../../Outputs/Log.hpp" #include "../../../Outputs/Log.hpp"
@ -24,6 +26,7 @@
#include "DiskIICard.hpp" #include "DiskIICard.hpp"
#include "Joystick.hpp" #include "Joystick.hpp"
#include "LanguageCardSwitches.hpp" #include "LanguageCardSwitches.hpp"
#include "Mockingboard.hpp"
#include "SCSICard.hpp" #include "SCSICard.hpp"
#include "Video.hpp" #include "Video.hpp"
@ -43,15 +46,69 @@ namespace {
constexpr int DiskIISlot = 6; // Apple recommended slot 6 for the (first) Disk II. constexpr int DiskIISlot = 6; // Apple recommended slot 6 for the (first) Disk II.
constexpr int SCSISlot = 7; // Install the SCSI card in slot 7, to one-up any connected Disk II. constexpr int SCSISlot = 7; // Install the SCSI card in slot 7, to one-up any connected Disk II.
constexpr int MockingboardSlot = 4; // Conventional Mockingboard slot.
// The system's master clock rate.
//
// Quick note on this:
//
// * 64 out of 65 CPU cycles last for 14 cycles of the master clock;
// * every 65th cycle lasts for 16 cycles of the master clock;
// * that keeps CPU cycles in-phase with the colour subcarrier: each line of output is 64*14 + 16 = 912 master cycles long;
// * ... and since hsync is placed to make each line 228 colour clocks long that means 4 master clocks per colour clock;
// * ... which is why all Apple II video modes are expressible as four binary outputs per colour clock;
// * ... and hence seven pixels per memory access window clock in high-res mode, 14 in double high-res, etc.
constexpr float master_clock = 14318180.0;
/// Provides an AY that runs at the CPU rate divided by 4 given an input of the master clock divided by 2,
/// allowing for stretched CPU clock cycles.
struct StretchedAYPair:
Apple::II::AYPair,
public Outputs::Speaker::BufferSource<StretchedAYPair, true> {
using AYPair::AYPair;
template <Outputs::Speaker::Action action>
void apply_samples(std::size_t number_of_samples, Outputs::Speaker::StereoSample *target) {
// (1) take 64 windows of 7 input cycles followed by one window of 8 input cycles;
// (2) after each four windows, advance the underlying AY.
//
// i.e. advance after:
//
// * 28 cycles, {16 times, then 15 times, then 15 times, then 15 times};
// * 29 cycles, once.
//
// so:
// 16, 1; 15, 1; 15, 1; 15, 1
//
// i.e. add an extra one on the 17th, 33rd, 49th and 65th ticks in a 65-tick loop.
for(std::size_t c = 0; c < number_of_samples; c++) {
++subdivider_;
if(subdivider_ == 28) {
++phase_;
subdivider_ = (phase_ & 15) ? 0 : -1;
if(phase_ == 65) phase_ = 0;
advance();
}
target[c] = level();
}
}
private:
int phase_ = 0;
int subdivider_ = 0;
};
} }
namespace Apple { namespace Apple {
namespace II { namespace II {
#define is_iie() ((model == Analyser::Static::AppleII::Target::Model::IIe) || (model == Analyser::Static::AppleII::Target::Model::EnhancedIIe)) template <Analyser::Static::AppleII::Target::Model model, bool has_mockingboard> class ConcreteMachine:
template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
public Apple::II::Machine, public Apple::II::Machine,
public MachineTypes::TimedMachine, public MachineTypes::TimedMachine,
public MachineTypes::ScanProducer, public MachineTypes::ScanProducer,
@ -83,14 +140,14 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
false>; false>;
Processor m6502_; Processor m6502_;
VideoBusHandler video_bus_handler_; VideoBusHandler video_bus_handler_;
Apple::II::Video::Video<VideoBusHandler, is_iie()> video_; Apple::II::Video::Video<VideoBusHandler, is_iie(model)> video_;
int cycles_into_current_line_ = 0; int cycles_into_current_line_ = 0;
Cycles cycles_since_video_update_; Cycles cycles_since_video_update_;
void update_video() { void update_video() {
video_.run_for(cycles_since_video_update_.flush<Cycles>()); video_.run_for(cycles_since_video_update_.flush<Cycles>());
} }
static constexpr int audio_divider = 8; static constexpr int audio_divider = has_mockingboard ? 1 : 8;
void update_audio() { void update_audio() {
speaker_.run_for(audio_queue_, cycles_since_audio_update_.divide(Cycles(audio_divider))); speaker_.run_for(audio_queue_, cycles_since_audio_update_.divide(Cycles(audio_divider)));
} }
@ -109,9 +166,23 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
Concurrency::AsyncTaskQueue<false> audio_queue_; Concurrency::AsyncTaskQueue<false> audio_queue_;
Audio::Toggle audio_toggle_; Audio::Toggle audio_toggle_;
Outputs::Speaker::PullLowpass<Audio::Toggle> speaker_; StretchedAYPair ays_;
using SourceT =
std::conditional_t<has_mockingboard, Outputs::Speaker::CompoundSource<StretchedAYPair, Audio::Toggle>, Audio::Toggle>;
using LowpassT = Outputs::Speaker::PullLowpass<SourceT>;
Outputs::Speaker::CompoundSource<StretchedAYPair, Audio::Toggle> mixer_;
Outputs::Speaker::PullLowpass<SourceT> speaker_;
Cycles cycles_since_audio_update_; Cycles cycles_since_audio_update_;
constexpr SourceT &lowpass_source() {
if constexpr (has_mockingboard) {
return mixer_;
} else {
return audio_toggle_;
}
}
// MARK: - Cards // MARK: - Cards
static constexpr size_t NoActiveCard = 7; // There is no 'card 0' in internal numbering. static constexpr size_t NoActiveCard = 7; // There is no 'card 0' in internal numbering.
size_t active_card_ = NoActiveCard; size_t active_card_ = NoActiveCard;
@ -155,6 +226,24 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
pick_card_messaging_group(card); pick_card_messaging_group(card);
} }
void card_did_change_interrupt_flags(Apple::II::Card *) final {
bool nmi = false;
bool irq = false;
for(const auto &card: cards_) {
if(card) {
nmi |= card->nmi();
irq |= card->irq();
}
}
m6502_.set_nmi_line(nmi);
m6502_.set_irq_line(irq);
}
Apple::II::Mockingboard *mockingboard() {
return dynamic_cast<Apple::II::Mockingboard *>(cards_[MockingboardSlot - 1].get());
}
Apple::II::DiskIICard *diskii_card() { Apple::II::DiskIICard *diskii_card() {
return dynamic_cast<Apple::II::DiskIICard *>(cards_[DiskIISlot - 1].get()); return dynamic_cast<Apple::II::DiskIICard *>(cards_[DiskIISlot - 1].get());
} }
@ -225,7 +314,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
const auto zero_state = auxiliary_switches_.zero_state(); const auto zero_state = auxiliary_switches_.zero_state();
uint8_t *const ram = zero_state ? aux_ram_ : ram_; uint8_t *const ram = zero_state ? aux_ram_ : ram_;
uint8_t *const rom = is_iie() ? &rom_[3840] : rom_.data(); uint8_t *const rom = is_iie(model) ? &rom_[3840] : rom_.data();
// Which way the region here is mapped to be banks 1 and 2 is // Which way the region here is mapped to be banks 1 and 2 is
// arbitrary. // arbitrary.
@ -286,7 +375,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
} }
bool set_key_pressed(Key key, char value, bool is_pressed, bool is_repeat) final { bool set_key_pressed(Key key, char value, bool is_pressed, bool is_repeat) final {
if constexpr (!is_iie()) { if constexpr (!is_iie(model)) {
if(is_repeat && !repeat_is_pressed_) return true; if(is_repeat && !repeat_is_pressed_) return true;
} }
@ -297,7 +386,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
case Key::Down: value = 0x0a; break; case Key::Down: value = 0x0a; break;
case Key::Up: value = 0x0b; break; case Key::Up: value = 0x0b; break;
case Key::Backspace: case Key::Backspace:
if(is_iie()) { if(is_iie(model)) {
value = 0x7f; value = 0x7f;
break; break;
} else { } else {
@ -305,7 +394,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
} }
case Key::Enter: value = 0x0d; break; case Key::Enter: value = 0x0d; break;
case Key::Tab: case Key::Tab:
if (is_iie()) { if (is_iie(model)) {
value = '\t'; value = '\t';
break; break;
} else { } else {
@ -316,7 +405,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
case Key::LeftOption: case Key::LeftOption:
case Key::RightMeta: case Key::RightMeta:
if (is_iie()) { if (is_iie(model)) {
open_apple_is_pressed = is_pressed; open_apple_is_pressed = is_pressed;
return true; return true;
} else { } else {
@ -325,7 +414,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
case Key::RightOption: case Key::RightOption:
case Key::LeftMeta: case Key::LeftMeta:
if (is_iie()) { if (is_iie(model)) {
closed_apple_is_pressed = is_pressed; closed_apple_is_pressed = is_pressed;
return true; return true;
} else { } else {
@ -346,7 +435,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
case Key::F9: case Key::F10: case Key::F11: case Key::F9: case Key::F10: case Key::F11:
repeat_is_pressed_ = is_pressed; repeat_is_pressed_ = is_pressed;
if constexpr (!is_iie()) { if constexpr (!is_iie(model)) {
if(is_pressed && (!is_repeat || character_is_pressed_)) { if(is_pressed && (!is_repeat || character_is_pressed_)) {
keyboard_input_ = uint8_t(last_pressed_character_ | 0x80); keyboard_input_ = uint8_t(last_pressed_character_ | 0x80);
} }
@ -374,12 +463,12 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
} }
// Prior to the IIe, the keyboard could produce uppercase only. // Prior to the IIe, the keyboard could produce uppercase only.
if(!is_iie()) value = char(toupper(value)); if(!is_iie(model)) value = char(toupper(value));
if(control_is_pressed_ && isalpha(value)) value &= 0xbf; if(control_is_pressed_ && isalpha(value)) value &= 0xbf;
// TODO: properly map IIe keys // TODO: properly map IIe keys
if(!is_iie() && shift_is_pressed_) { if(!is_iie(model) && shift_is_pressed_) {
switch(value) { switch(value) {
case 0x27: value = 0x22; break; // ' -> " case 0x27: value = 0x22; break; // ' -> "
case 0x2c: value = 0x3c; break; // , -> < case 0x2c: value = 0x3c; break; // , -> <
@ -480,12 +569,12 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
video_bus_handler_(ram_, aux_ram_), video_bus_handler_(ram_, aux_ram_),
video_(video_bus_handler_), video_(video_bus_handler_),
audio_toggle_(audio_queue_), audio_toggle_(audio_queue_),
speaker_(audio_toggle_), ays_(audio_queue_),
mixer_(ays_, audio_toggle_),
speaker_(lowpass_source()),
language_card_(*this), language_card_(*this),
auxiliary_switches_(*this), auxiliary_switches_(*this),
keyboard_(&m6502_) { keyboard_(&m6502_) {
// The system's master clock rate.
constexpr float master_clock = 14318180.0;
// This is where things get slightly convoluted: establish the machine as having a clock rate // This is where things get slightly convoluted: establish the machine as having a clock rate
// equal to the number of cycles of work the 6502 will actually achieve. Which is less than // equal to the number of cycles of work the 6502 will actually achieve. Which is less than
@ -559,6 +648,12 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
install_card(SCSISlot, new Apple::II::SCSICard(roms, int(master_clock / 14.0f))); install_card(SCSISlot, new Apple::II::SCSICard(roms, int(master_clock / 14.0f)));
} }
if(target.has_mockingboard) {
// The Mockingboard has a parasitic relationship with this class due to the way
// that audio outputs are implemented in this emulator.
install_card(MockingboardSlot, new Apple::II::Mockingboard(ays_));
}
rom_ = std::move(roms.find(system)->second); rom_ = std::move(roms.find(system)->second);
// The IIe and Enhanced IIe ROMs often distributed are oversized; trim if necessary. // The IIe and Enhanced IIe ROMs often distributed are oversized; trim if necessary.
if(system == ROM::Name::AppleIIe || system == ROM::Name::AppleIIEnhancedE) { if(system == ROM::Name::AppleIIe || system == ROM::Name::AppleIIEnhancedE) {
@ -629,7 +724,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
if(write_pages_[address >> 8]) write_pages_[address >> 8][address & 0xff] = *value; if(write_pages_[address >> 8]) write_pages_[address >> 8][address & 0xff] = *value;
} }
if(is_iie()) { if(is_iie(model)) {
auxiliary_switches_.access(address, isReadOperation(operation)); auxiliary_switches_.access(address, isReadOperation(operation));
} }
} else { } else {
@ -669,7 +764,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
*value &= 0x7f; *value &= 0x7f;
if( if(
joysticks_.button(0) || joysticks_.button(0) ||
(is_iie() && keyboard_.open_apple_is_pressed) (is_iie(model) && keyboard_.open_apple_is_pressed)
) )
*value |= 0x80; *value |= 0x80;
break; break;
@ -677,7 +772,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
*value &= 0x7f; *value &= 0x7f;
if( if(
joysticks_.button(1) || joysticks_.button(1) ||
(is_iie() && keyboard_.closed_apple_is_pressed) (is_iie(model) && keyboard_.closed_apple_is_pressed)
) )
*value |= 0x80; *value |= 0x80;
break; break;
@ -699,7 +794,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
} break; } break;
// The IIe-only state reads follow... // The IIe-only state reads follow...
#define IIeSwitchRead(s) *value = keyboard_.get_keyboard_input(); if(is_iie()) *value = (*value & 0x7f) | (s ? 0x80 : 0x00); #define IIeSwitchRead(s) *value = keyboard_.get_keyboard_input(); if(is_iie(model)) *value = (*value & 0x7f) | (s ? 0x80 : 0x00);
case 0xc011: IIeSwitchRead(language_card_.state().bank2); break; case 0xc011: IIeSwitchRead(language_card_.state().bank2); break;
case 0xc012: IIeSwitchRead(language_card_.state().read); break; case 0xc012: IIeSwitchRead(language_card_.state().read); break;
case 0xc013: IIeSwitchRead(auxiliary_switches_.switches().read_auxiliary_memory); break; case 0xc013: IIeSwitchRead(auxiliary_switches_.switches().read_auxiliary_memory); break;
@ -718,12 +813,12 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
#undef IIeSwitchRead #undef IIeSwitchRead
case 0xc07f: case 0xc07f:
if(is_iie()) *value = (*value & 0x7f) | (video_.get_annunciator_3() ? 0x80 : 0x00); if(is_iie(model)) *value = (*value & 0x7f) | (video_.get_annunciator_3() ? 0x80 : 0x00);
break; break;
} }
} else { } else {
// Write-only switches. All IIe as currently implemented. // Write-only switches. All IIe as currently implemented.
if(is_iie()) { if(is_iie(model)) {
auxiliary_switches_.access(address, false); auxiliary_switches_.access(address, false);
switch(address) { switch(address) {
default: break; default: break;
@ -775,7 +870,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
case 0xc05e: case 0xc05e:
case 0xc05f: case 0xc05f:
if(is_iie()) { if(is_iie(model)) {
update_video(); update_video();
video_.set_annunciator_3(!(address&1)); video_.set_annunciator_3(!(address&1));
} }
@ -785,7 +880,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
keyboard_.clear_keyboard_input(); keyboard_.clear_keyboard_input();
// On the IIe, reading C010 returns additional key info. // On the IIe, reading C010 returns additional key info.
if(is_iie() && isReadOperation(operation)) { if(is_iie(model) && isReadOperation(operation)) {
*value = (keyboard_.get_key_is_down() ? 0x80 : 0x00) | (keyboard_.get_keyboard_input() & 0x7f); *value = (keyboard_.get_key_is_down() ? 0x80 : 0x00) | (keyboard_.get_keyboard_input() & 0x7f);
} }
break; break;
@ -922,7 +1017,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
} }
bool prefers_logical_input() final { bool prefers_logical_input() final {
return is_iie(); return is_iie(model);
} }
Inputs::Keyboard &get_keyboard() final { Inputs::Keyboard &get_keyboard() final {
@ -988,12 +1083,23 @@ using namespace Apple::II;
std::unique_ptr<Machine> Machine::AppleII(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) { std::unique_ptr<Machine> Machine::AppleII(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
using Target = Analyser::Static::AppleII::Target; using Target = Analyser::Static::AppleII::Target;
const Target *const appleii_target = dynamic_cast<const Target *>(target); const Target *const appleii_target = dynamic_cast<const Target *>(target);
if(appleii_target->has_mockingboard) {
switch(appleii_target->model) { switch(appleii_target->model) {
default: return nullptr; default: return nullptr;
case Target::Model::II: return std::make_unique<ConcreteMachine<Target::Model::II>>(*appleii_target, rom_fetcher); case Target::Model::II: return std::make_unique<ConcreteMachine<Target::Model::II, true>>(*appleii_target, rom_fetcher);
case Target::Model::IIplus: return std::make_unique<ConcreteMachine<Target::Model::IIplus>>(*appleii_target, rom_fetcher); case Target::Model::IIplus: return std::make_unique<ConcreteMachine<Target::Model::IIplus, true>>(*appleii_target, rom_fetcher);
case Target::Model::IIe: return std::make_unique<ConcreteMachine<Target::Model::IIe>>(*appleii_target, rom_fetcher); case Target::Model::IIe: return std::make_unique<ConcreteMachine<Target::Model::IIe, true>>(*appleii_target, rom_fetcher);
case Target::Model::EnhancedIIe: return std::make_unique<ConcreteMachine<Target::Model::EnhancedIIe>>(*appleii_target, rom_fetcher); case Target::Model::EnhancedIIe: return std::make_unique<ConcreteMachine<Target::Model::EnhancedIIe, true>>(*appleii_target, rom_fetcher);
}
} else {
switch(appleii_target->model) {
default: return nullptr;
case Target::Model::II: return std::make_unique<ConcreteMachine<Target::Model::II, false>>(*appleii_target, rom_fetcher);
case Target::Model::IIplus: return std::make_unique<ConcreteMachine<Target::Model::IIplus, false>>(*appleii_target, rom_fetcher);
case Target::Model::IIe: return std::make_unique<ConcreteMachine<Target::Model::IIe, false>>(*appleii_target, rom_fetcher);
case Target::Model::EnhancedIIe: return std::make_unique<ConcreteMachine<Target::Model::EnhancedIIe, false>>(*appleii_target, rom_fetcher);
}
} }
} }

View File

@ -49,12 +49,16 @@ class Card {
// by an access to $cfff. // by an access to $cfff.
}; };
// TODO: I think IO and Device are the wrong way around above.
// See https://retrocomputing.stackexchange.com/questions/5730/how-did-the-address-decode-for-apple-ii-expansion-cards-work
// and/or https://gglabs.us/sites/gglabs.us/files/a2scsi-A00_sch.pdf
/*! /*!
Advances time by @c cycles, of which @c stretches were stretched. Advances time by @c cycles, of which @c stretches were stretched.
This is posted only to cards that announced a select constraint. Cards with This is posted only to cards that announced a select constraint. Cards with
no constraints, that want to be informed of every machine cycle, will receive no constraints, that want to be informed of every machine cycle, will receive
a call to perform_bus_operation every cycle and should use that for time keeping. a call to @c perform_bus_operation every cycle and should use that for time keeping.
*/ */
virtual void run_for([[maybe_unused]] Cycles cycles, [[maybe_unused]] int stretches) {} virtual void run_for([[maybe_unused]] Cycles cycles, [[maybe_unused]] int stretches) {}
@ -93,8 +97,15 @@ class Card {
/*! Cards may supply a target for activity observation if desired. */ /*! Cards may supply a target for activity observation if desired. */
virtual void set_activity_observer([[maybe_unused]] Activity::Observer *observer) {} virtual void set_activity_observer([[maybe_unused]] Activity::Observer *observer) {}
/// @returns The current semantic NMI line output of this card.
virtual bool nmi() { return false; }
/// @returns The current semantic IRQ line output of this card.
virtual bool irq() { return false; }
struct Delegate { struct Delegate {
virtual void card_did_change_select_constraints(Card *card) = 0; virtual void card_did_change_select_constraints(Card *card) = 0;
virtual void card_did_change_interrupt_flags(Card *card) = 0;
}; };
void set_delegate(Delegate *delegate) { void set_delegate(Delegate *delegate) {
delegate_ = delegate; delegate_ = delegate;

View File

@ -0,0 +1,135 @@
//
// Mockingboard.hpp
// Clock Signal
//
// Created by Thomas Harte on 14/02/2024.
// Copyright © 2024 Thomas Harte. All rights reserved.
//
#pragma once
#include "Card.hpp"
#include "../../../Components/6522/6522.hpp"
namespace Apple::II {
class AYPair {
public:
AYPair(Concurrency::AsyncTaskQueue<false> &queue) :
ays_{
{GI::AY38910::Personality::AY38910, queue},
{GI::AY38910::Personality::AY38910, queue},
} {}
void advance() {
ays_[0].advance();
ays_[1].advance();
}
void set_sample_volume_range(std::int16_t range) {
ays_[0].set_sample_volume_range(range);
ays_[1].set_sample_volume_range(range);
}
bool is_zero_level() const {
return ays_[0].is_zero_level() && ays_[1].is_zero_level();
}
Outputs::Speaker::StereoSample level() const {
Outputs::Speaker::StereoSample level;
level.left = ays_[0].level();
level.right = ays_[1].level();
return level;
}
GI::AY38910::AY38910SampleSource<false> &get(int index) {
return ays_[index];
}
private:
GI::AY38910::AY38910SampleSource<false> ays_[2];
};
class Mockingboard: public Card {
public:
Mockingboard(AYPair &ays) :
vias_{ {handlers_[0]}, {handlers_[1]} },
handlers_{ {*this, ays.get(0)}, {*this, ays.get(1)}} {
set_select_constraints(0);
}
void perform_bus_operation(Select select, bool is_read, uint16_t address, uint8_t *value) final {
if(!(select & Device)) {
return;
}
int index = (address >> 7) & 1;
if(is_read) {
*value = vias_[index].read(address);
} else {
vias_[index].write(address, *value);
}
}
void run_for(Cycles cycles, int) final {
vias_[0].run_for(cycles);
vias_[1].run_for(cycles);
}
bool nmi() final {
return handlers_[1].interrupt;
}
bool irq() final {
return handlers_[0].interrupt;
}
void did_change_interrupt_flags() {
delegate_->card_did_change_interrupt_flags(this);
}
private:
struct AYVIA: public MOS::MOS6522::PortHandler {
AYVIA(Mockingboard &card, GI::AY38910::AY38910SampleSource<false> &ay) :
card(card), ay(ay) {}
void set_interrupt_status(bool status) {
interrupt = status;
card.did_change_interrupt_flags();
}
void set_port_output(MOS::MOS6522::Port port, uint8_t value, uint8_t) {
if(port) {
using ControlLines = GI::AY38910::ControlLines;
ay.set_control_lines(
ControlLines(
((value & 1) ? ControlLines::BC1 : 0) |
((value & 2) ? ControlLines::BDIR : 0) |
ControlLines::BC2
)
);
ay.set_reset(!(value & 4));
} else {
ay.set_data_input(value);
}
}
uint8_t get_port_input(MOS::MOS6522::Port port) {
if(!port) {
return ay.get_data_output();
}
return 0xff;
}
bool interrupt;
Mockingboard &card;
GI::AY38910::AY38910SampleSource<false> &ay;
};
MOS::MOS6522::MOS6522<AYVIA> vias_[2];
AYVIA handlers_[2];
};
}

View File

@ -1568,6 +1568,7 @@
4B71368D1F788112008B8ED9 /* Parser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Parser.hpp; sourceTree = "<group>"; }; 4B71368D1F788112008B8ED9 /* Parser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Parser.hpp; sourceTree = "<group>"; };
4B71368F1F789C93008B8ED9 /* SegmentParser.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SegmentParser.cpp; sourceTree = "<group>"; }; 4B71368F1F789C93008B8ED9 /* SegmentParser.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SegmentParser.cpp; sourceTree = "<group>"; };
4B7136901F789C93008B8ED9 /* SegmentParser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SegmentParser.hpp; sourceTree = "<group>"; }; 4B7136901F789C93008B8ED9 /* SegmentParser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SegmentParser.hpp; sourceTree = "<group>"; };
4B72FAEF2B7D51BB000C7E90 /* Mockingboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Mockingboard.hpp; sourceTree = "<group>"; };
4B74CF7F2312FA9C00500CE8 /* HFV.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = HFV.hpp; sourceTree = "<group>"; }; 4B74CF7F2312FA9C00500CE8 /* HFV.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = HFV.hpp; sourceTree = "<group>"; };
4B74CF802312FA9C00500CE8 /* HFV.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HFV.cpp; sourceTree = "<group>"; }; 4B74CF802312FA9C00500CE8 /* HFV.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HFV.cpp; sourceTree = "<group>"; };
4B75F978280D7C5100121055 /* 68000DecoderTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = 68000DecoderTests.mm; sourceTree = "<group>"; }; 4B75F978280D7C5100121055 /* 68000DecoderTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = 68000DecoderTests.mm; sourceTree = "<group>"; };
@ -4840,6 +4841,7 @@
4B2E86E125DC95150024F1E9 /* Joystick.hpp */, 4B2E86E125DC95150024F1E9 /* Joystick.hpp */,
4BF40A5525424C770033EA39 /* LanguageCardSwitches.hpp */, 4BF40A5525424C770033EA39 /* LanguageCardSwitches.hpp */,
4BE0151C286A8C8E00EA42E9 /* MemorySwitches.hpp */, 4BE0151C286A8C8E00EA42E9 /* MemorySwitches.hpp */,
4B72FAEF2B7D51BB000C7E90 /* Mockingboard.hpp */,
4B4C81C428B3C5CD00F84AE9 /* SCSICard.hpp */, 4B4C81C428B3C5CD00F84AE9 /* SCSICard.hpp */,
4BCE004F227CE8CA000CA200 /* Video.hpp */, 4BCE004F227CE8CA000CA200 /* Video.hpp */,
4B8DF4F2254E141700F3433C /* VideoSwitches.hpp */, 4B8DF4F2254E141700F3433C /* VideoSwitches.hpp */,

View File

@ -141,7 +141,7 @@ typedef int Kilobytes;
- (instancetype)initWithAmigaModel:(CSMachineAmigaModel)model chipMemorySize:(Kilobytes)chipMemorySize fastMemorySize:(Kilobytes)fastMemorySize; - (instancetype)initWithAmigaModel:(CSMachineAmigaModel)model chipMemorySize:(Kilobytes)chipMemorySize fastMemorySize:(Kilobytes)fastMemorySize;
- (instancetype)initWithAmstradCPCModel:(CSMachineCPCModel)model; - (instancetype)initWithAmstradCPCModel:(CSMachineCPCModel)model;
- (instancetype)initWithAppleIIModel:(CSMachineAppleIIModel)model diskController:(CSMachineAppleIIDiskController)diskController; - (instancetype)initWithAppleIIModel:(CSMachineAppleIIModel)model diskController:(CSMachineAppleIIDiskController)diskController hasMockingboard:(BOOL)hasMockingboard;
- (instancetype)initWithAppleIIgsModel:(CSMachineAppleIIgsModel)model memorySize:(Kilobytes)memorySize; - (instancetype)initWithAppleIIgsModel:(CSMachineAppleIIgsModel)model memorySize:(Kilobytes)memorySize;
- (instancetype)initWithAtariSTModel:(CSMachineAtariSTModel)model memorySize:(Kilobytes)memorySize; - (instancetype)initWithAtariSTModel:(CSMachineAtariSTModel)model memorySize:(Kilobytes)memorySize;
- (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;

View File

@ -93,7 +93,7 @@
return self; return self;
} }
- (instancetype)initWithAppleIIModel:(CSMachineAppleIIModel)model diskController:(CSMachineAppleIIDiskController)diskController { - (instancetype)initWithAppleIIModel:(CSMachineAppleIIModel)model diskController:(CSMachineAppleIIDiskController)diskController hasMockingboard:(BOOL)hasMockingboard {
self = [super init]; self = [super init];
if(self) { if(self) {
using Target = Analyser::Static::AppleII::Target; using Target = Analyser::Static::AppleII::Target;
@ -110,6 +110,7 @@
case CSMachineAppleIIDiskControllerSixteenSector: target->disk_controller = Target::DiskController::SixteenSector; break; case CSMachineAppleIIDiskControllerSixteenSector: target->disk_controller = Target::DiskController::SixteenSector; break;
case CSMachineAppleIIDiskControllerThirteenSector: target->disk_controller = Target::DiskController::ThirteenSector; break; case CSMachineAppleIIDiskControllerThirteenSector: target->disk_controller = Target::DiskController::ThirteenSector; break;
} }
target->has_mockingboard = hasMockingboard;
_targets.push_back(std::move(target)); _targets.push_back(std::move(target));
} }
return self; return self;

View File

@ -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="21701" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> <document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22505" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies> <dependencies>
<deployment identifier="macosx"/> <deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21701"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22505"/>
<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="369"/> <rect key="contentRect" x="196" y="240" width="590" height="369"/>
<rect key="screenRect" x="0.0" y="0.0" width="1800" height="1131"/> <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1440"/>
<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="369"/> <rect key="frame" x="0.0" y="0.0" width="590" height="369"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@ -49,7 +49,7 @@ Gw
<action selector="cancelCreateMachine:" target="-2" id="lf8-PM-c0m"/> <action selector="cancelCreateMachine:" target="-2" id="lf8-PM-c0m"/>
</connections> </connections>
</button> </button>
<textField horizontalHuggingPriority="1" verticalHuggingPriority="1" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9YM-5x-pc0"> <textField focusRingType="none" horizontalHuggingPriority="1" verticalHuggingPriority="1" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9YM-5x-pc0">
<rect key="frame" x="18" y="14" width="405" height="32"/> <rect key="frame" x="18" y="14" width="405" height="32"/>
<textFieldCell key="cell" allowsUndo="NO" sendsActionOnEndEditing="YES" id="xTm-Oy-oz5"> <textFieldCell key="cell" allowsUndo="NO" sendsActionOnEndEditing="YES" id="xTm-Oy-oz5">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -104,7 +104,7 @@ Gw
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
</scroller> </scroller>
</scrollView> </scrollView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="VAc-6N-O7q"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="VAc-6N-O7q">
<rect key="frame" x="18" y="328" width="554" height="21"/> <rect key="frame" x="18" y="328" width="554" height="21"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Choose a machine" id="32m-Vs-dPO"> <textFieldCell key="cell" lineBreakMode="clipping" title="Choose a machine" id="32m-Vs-dPO">
<font key="font" textStyle="title2" name=".SFNS-Regular"/> <font key="font" textStyle="title2" name=".SFNS-Regular"/>
@ -135,7 +135,7 @@ Gw
</menu> </menu>
</popUpButtonCell> </popUpButtonCell>
</popUpButton> </popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="P6K-dt-stj"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="P6K-dt-stj">
<rect key="frame" x="18" y="216" width="89" height="16"/> <rect key="frame" x="18" y="216" width="89" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Chip Memory:" id="FIO-ZR-rsA"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Chip Memory:" id="FIO-ZR-rsA">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -143,7 +143,7 @@ Gw
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="YD0-OJ-2bY"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="YD0-OJ-2bY">
<rect key="frame" x="18" y="186" width="87" height="16"/> <rect key="frame" x="18" y="186" width="87" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Fast Memory:" id="Rpz-39-jyt"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Fast Memory:" id="Rpz-39-jyt">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -202,7 +202,7 @@ Gw
</menu> </menu>
</popUpButtonCell> </popUpButtonCell>
</popUpButton> </popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="c3g-96-b3x"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="c3g-96-b3x">
<rect key="frame" x="18" y="184" width="46" height="16"/> <rect key="frame" x="18" y="184" width="46" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="53v-92-jmf"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="53v-92-jmf">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -223,19 +223,19 @@ Gw
</tabViewItem> </tabViewItem>
<tabViewItem label="Apple II" identifier="appleii" id="P59-QG-LOa"> <tabViewItem label="Apple II" identifier="appleii" id="P59-QG-LOa">
<view key="view" id="dHz-Yv-GNq"> <view key="view" id="dHz-Yv-GNq">
<rect key="frame" x="10" y="7" width="400" height="222"/> <rect key="frame" x="10" y="7" width="400" height="254"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="V5Z-dX-Ns4"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="V5Z-dX-Ns4">
<rect key="frame" x="18" y="184" width="46" height="16"/> <rect key="frame" x="18" y="216" width="46" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="qV3-2P-3JW"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="qV3-2P-3JW">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="WnO-ef-IC6"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="WnO-ef-IC6">
<rect key="frame" x="18" y="154" width="96" height="16"/> <rect key="frame" x="18" y="186" width="98" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Disk Controller:" id="kbf-rc-Y4M"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Disk Controller:" id="kbf-rc-Y4M">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@ -243,7 +243,7 @@ Gw
</textFieldCell> </textFieldCell>
</textField> </textField>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jli-ac-Sij"> <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jli-ac-Sij">
<rect key="frame" x="67" y="178" width="117" height="25"/> <rect key="frame" x="67" y="210" width="117" height="25"/>
<popUpButtonCell key="cell" type="push" title="Apple II" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="axesIndependently" inset="2" selectedItem="VBQ-JG-AeM" id="U6V-us-O2F"> <popUpButtonCell key="cell" type="push" title="Apple II" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="axesIndependently" inset="2" selectedItem="VBQ-JG-AeM" id="U6V-us-O2F">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/> <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/> <font key="font" metaFont="menu"/>
@ -258,7 +258,7 @@ Gw
</popUpButtonCell> </popUpButtonCell>
</popUpButton> </popUpButton>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="LSB-WP-FMi"> <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="LSB-WP-FMi">
<rect key="frame" x="117" y="148" width="134" height="25"/> <rect key="frame" x="119" y="180" width="134" height="25"/>
<popUpButtonCell key="cell" type="push" title="Sixteen Sector" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="16" imageScaling="axesIndependently" inset="2" selectedItem="QaV-Yr-k9o" id="8BT-RV-2Nm"> <popUpButtonCell key="cell" type="push" title="Sixteen Sector" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="16" imageScaling="axesIndependently" inset="2" selectedItem="QaV-Yr-k9o" id="8BT-RV-2Nm">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/> <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/> <font key="font" metaFont="menu"/>
@ -271,16 +271,25 @@ Gw
</menu> </menu>
</popUpButtonCell> </popUpButtonCell>
</popUpButton> </popUpButton>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="6UC-5k-3b6">
<rect key="frame" x="18" y="159" width="140" height="18"/>
<buttonCell key="cell" type="check" title="Has Mockingboard" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="vpo-D2-Tzo">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
</subviews> </subviews>
<constraints> <constraints>
<constraint firstItem="WnO-ef-IC6" firstAttribute="leading" secondItem="dHz-Yv-GNq" secondAttribute="leading" constant="20" symbolic="YES" id="5RF-1w-9HW"/> <constraint firstItem="WnO-ef-IC6" firstAttribute="leading" secondItem="dHz-Yv-GNq" secondAttribute="leading" constant="20" symbolic="YES" id="5RF-1w-9HW"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="LSB-WP-FMi" secondAttribute="bottom" constant="20" symbolic="YES" id="865-cv-qVk"/> <constraint firstItem="6UC-5k-3b6" firstAttribute="top" secondItem="LSB-WP-FMi" secondAttribute="bottom" constant="8" symbolic="YES" id="5Wg-y8-hWt"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="LSB-WP-FMi" secondAttribute="trailing" constant="20" symbolic="YES" id="9GL-al-1qi"/> <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="LSB-WP-FMi" secondAttribute="trailing" constant="20" symbolic="YES" id="9GL-al-1qi"/>
<constraint firstItem="WnO-ef-IC6" firstAttribute="centerY" secondItem="LSB-WP-FMi" secondAttribute="centerY" id="Fuj-zT-MIm"/> <constraint firstItem="WnO-ef-IC6" firstAttribute="centerY" secondItem="LSB-WP-FMi" secondAttribute="centerY" id="Fuj-zT-MIm"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="jli-ac-Sij" secondAttribute="trailing" constant="20" symbolic="YES" id="I8d-OR-ICN"/> <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="jli-ac-Sij" secondAttribute="trailing" constant="20" symbolic="YES" id="I8d-OR-ICN"/>
<constraint firstItem="jli-ac-Sij" firstAttribute="top" secondItem="dHz-Yv-GNq" secondAttribute="top" constant="20" symbolic="YES" id="Qi1-CV-A0c"/> <constraint firstItem="jli-ac-Sij" firstAttribute="top" secondItem="dHz-Yv-GNq" secondAttribute="top" constant="20" symbolic="YES" id="Qi1-CV-A0c"/>
<constraint firstItem="V5Z-dX-Ns4" firstAttribute="leading" secondItem="dHz-Yv-GNq" secondAttribute="leading" constant="20" symbolic="YES" id="SWc-iX-1We"/> <constraint firstItem="V5Z-dX-Ns4" firstAttribute="leading" secondItem="dHz-Yv-GNq" secondAttribute="leading" constant="20" symbolic="YES" id="SWc-iX-1We"/>
<constraint firstItem="6UC-5k-3b6" firstAttribute="leading" secondItem="dHz-Yv-GNq" secondAttribute="leading" constant="20" symbolic="YES" id="VRC-Bl-5gF"/>
<constraint firstItem="LSB-WP-FMi" firstAttribute="leading" secondItem="WnO-ef-IC6" secondAttribute="trailing" constant="8" symbolic="YES" id="bte-XA-xNQ"/> <constraint firstItem="LSB-WP-FMi" firstAttribute="leading" secondItem="WnO-ef-IC6" secondAttribute="trailing" constant="8" symbolic="YES" id="bte-XA-xNQ"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="6UC-5k-3b6" secondAttribute="bottom" constant="20" symbolic="YES" id="iTH-2B-JJa"/>
<constraint firstItem="LSB-WP-FMi" firstAttribute="top" secondItem="jli-ac-Sij" secondAttribute="bottom" constant="10" symbolic="YES" id="ki5-JR-vRe"/> <constraint firstItem="LSB-WP-FMi" firstAttribute="top" secondItem="jli-ac-Sij" secondAttribute="bottom" constant="10" symbolic="YES" id="ki5-JR-vRe"/>
<constraint firstItem="jli-ac-Sij" firstAttribute="leading" secondItem="V5Z-dX-Ns4" secondAttribute="trailing" constant="8" symbolic="YES" id="qcd-pf-0T3"/> <constraint firstItem="jli-ac-Sij" firstAttribute="leading" secondItem="V5Z-dX-Ns4" secondAttribute="trailing" constant="8" symbolic="YES" id="qcd-pf-0T3"/>
<constraint firstItem="V5Z-dX-Ns4" firstAttribute="centerY" secondItem="jli-ac-Sij" secondAttribute="centerY" id="v25-BK-BoD"/> <constraint firstItem="V5Z-dX-Ns4" firstAttribute="centerY" secondItem="jli-ac-Sij" secondAttribute="centerY" id="v25-BK-BoD"/>
@ -292,7 +301,7 @@ Gw
<rect key="frame" x="10" y="7" width="400" height="222"/> <rect key="frame" x="10" y="7" width="400" height="222"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0d9-IG-gKU"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0d9-IG-gKU">
<rect key="frame" x="18" y="184" width="46" height="16"/> <rect key="frame" x="18" y="184" width="46" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="kiv-1P-FWc"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="kiv-1P-FWc">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -300,7 +309,7 @@ Gw
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="LES-76-Ovz"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="LES-76-Ovz">
<rect key="frame" x="18" y="154" width="85" height="16"/> <rect key="frame" x="18" y="154" width="85" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="OLJ-nC-yyj"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="OLJ-nC-yyj">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -357,7 +366,7 @@ Gw
<rect key="frame" x="10" y="7" width="400" height="222"/> <rect key="frame" x="10" y="7" width="400" height="222"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dKg-qC-BBF"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dKg-qC-BBF">
<rect key="frame" x="18" y="184" width="58" height="16"/> <rect key="frame" x="18" y="184" width="58" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory:" id="ZBF-0r-RNK"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory:" id="ZBF-0r-RNK">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -515,7 +524,7 @@ Gw
</menu> </menu>
</popUpButtonCell> </popUpButtonCell>
</popUpButton> </popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ykc-W1-YaS"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ykc-W1-YaS">
<rect key="frame" x="18" y="124" width="43" height="16"/> <rect key="frame" x="18" y="124" width="43" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="EXOS:" id="gUC-PN-zVL"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="EXOS:" id="gUC-PN-zVL">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -523,7 +532,7 @@ Gw
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="frx-nk-c3P"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="frx-nk-c3P">
<rect key="frame" x="18" y="184" width="59" height="16"/> <rect key="frame" x="18" y="184" width="59" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Machine:" id="uTv-hH-mIC"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Machine:" id="uTv-hH-mIC">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -531,7 +540,7 @@ Gw
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dzd-tH-BjX"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dzd-tH-BjX">
<rect key="frame" x="18" y="94" width="46" height="16"/> <rect key="frame" x="18" y="94" width="46" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="BASIC:" id="ai1-oR-X6Y"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="BASIC:" id="ai1-oR-X6Y">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -539,7 +548,7 @@ Gw
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pxr-Bq-yh0"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pxr-Bq-yh0">
<rect key="frame" x="18" y="64" width="36" height="16"/> <rect key="frame" x="18" y="64" width="36" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="DOS:" id="NFk-cp-DfS"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="DOS:" id="NFk-cp-DfS">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -547,7 +556,7 @@ Gw
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rHr-bh-QMV"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rHr-bh-QMV">
<rect key="frame" x="17" y="154" width="47" height="16"/> <rect key="frame" x="17" y="154" width="47" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Speed:" id="sAw-C9-Sf7"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Speed:" id="sAw-C9-Sf7">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -593,7 +602,7 @@ Gw
<rect key="frame" x="10" y="7" width="400" height="222"/> <rect key="frame" x="10" y="7" width="400" height="222"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZOY-4E-Cfl"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZOY-4E-Cfl">
<rect key="frame" x="18" y="184" width="46" height="16"/> <rect key="frame" x="18" y="184" width="46" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="h9r-i6-66j"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="h9r-i6-66j">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -653,7 +662,7 @@ Gw
</menu> </menu>
</popUpButtonCell> </popUpButtonCell>
</popUpButton> </popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZaD-7v-rMS"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZaD-7v-rMS">
<rect key="frame" x="18" y="154" width="50" height="16"/> <rect key="frame" x="18" y="154" width="50" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Region:" id="x4m-eh-Nif"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Region:" id="x4m-eh-Nif">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -661,7 +670,7 @@ Gw
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gFV-RB-7dB"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gFV-RB-7dB">
<rect key="frame" x="18" y="184" width="46" height="16"/> <rect key="frame" x="18" y="184" width="46" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="r7C-zE-gyj"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="r7C-zE-gyj">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -716,7 +725,7 @@ Gw
<rect key="frame" x="10" y="7" width="400" height="222"/> <rect key="frame" x="10" y="7" width="400" height="222"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0ct-tf-uRH"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0ct-tf-uRH">
<rect key="frame" x="18" y="184" width="46" height="16"/> <rect key="frame" x="18" y="184" width="46" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="Xm1-7x-YVl"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="Xm1-7x-YVl">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -754,7 +763,7 @@ Gw
</menu> </menu>
</popUpButtonCell> </popUpButtonCell>
</popUpButton> </popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="okM-ZI-NbF"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="okM-ZI-NbF">
<rect key="frame" x="18" y="154" width="92" height="16"/> <rect key="frame" x="18" y="154" width="92" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Disk Interface:" id="SFK-hS-tFC"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Disk Interface:" id="SFK-hS-tFC">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -796,7 +805,7 @@ Gw
</menu> </menu>
</popUpButtonCell> </popUpButtonCell>
</popUpButton> </popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="uhf-1k-ibT"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="uhf-1k-ibT">
<rect key="frame" x="18" y="198" width="95" height="16"/> <rect key="frame" x="18" y="198" width="95" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Video Adaptor:" id="ROV-EU-T3W"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Video Adaptor:" id="ROV-EU-T3W">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -804,7 +813,7 @@ Gw
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8vF-eu-ClP"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8vF-eu-ClP">
<rect key="frame" x="18" y="168" width="47" height="16"/> <rect key="frame" x="18" y="168" width="47" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Speed:" id="qXc-wf-5jm"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Speed:" id="qXc-wf-5jm">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -876,7 +885,7 @@ Gw
</menu> </menu>
</popUpButtonCell> </popUpButtonCell>
</popUpButton> </popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MTh-9p-FqC"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MTh-9p-FqC">
<rect key="frame" x="18" y="184" width="50" height="16"/> <rect key="frame" x="18" y="184" width="50" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Region:" id="F3g-Ya-ypU"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Region:" id="F3g-Ya-ypU">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -884,7 +893,7 @@ Gw
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gRS-DK-rIy"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gRS-DK-rIy">
<rect key="frame" x="18" y="154" width="87" height="16"/> <rect key="frame" x="18" y="154" width="87" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="a4I-vG-yCp"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="a4I-vG-yCp">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -937,7 +946,7 @@ Gw
</menu> </menu>
</popUpButtonCell> </popUpButtonCell>
</popUpButton> </popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="NCX-4e-lSu"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="NCX-4e-lSu">
<rect key="frame" x="18" y="184" width="87" height="16"/> <rect key="frame" x="18" y="184" width="87" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="e6x-TE-OC5"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="e6x-TE-OC5">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -985,7 +994,7 @@ Gw
</menu> </menu>
</popUpButtonCell> </popUpButtonCell>
</popUpButton> </popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8tU-73-XEE"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8tU-73-XEE">
<rect key="frame" x="18" y="184" width="87" height="16"/> <rect key="frame" x="18" y="184" width="87" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="z4b-oR-Yl2"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="z4b-oR-Yl2">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -1026,7 +1035,7 @@ Gw
</menu> </menu>
</popUpButtonCell> </popUpButtonCell>
</popUpButton> </popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fJ3-ma-Byy"> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fJ3-ma-Byy">
<rect key="frame" x="18" y="184" width="46" height="16"/> <rect key="frame" x="18" y="184" width="46" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="JId-Tp-LrE"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="JId-Tp-LrE">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -1075,6 +1084,7 @@ Gw
<outlet property="amigaChipRAMButton" destination="qfH-1l-GXp" id="3PG-QC-tyI"/> <outlet property="amigaChipRAMButton" destination="qfH-1l-GXp" id="3PG-QC-tyI"/>
<outlet property="amigaFastRAMButton" destination="af8-pF-qc9" id="laa-1H-JfD"/> <outlet property="amigaFastRAMButton" destination="af8-pF-qc9" id="laa-1H-JfD"/>
<outlet property="appleIIDiskControllerButton" destination="LSB-WP-FMi" id="Ssa-jd-t63"/> <outlet property="appleIIDiskControllerButton" destination="LSB-WP-FMi" id="Ssa-jd-t63"/>
<outlet property="appleIIMockingboardButton" destination="6UC-5k-3b6" id="1h1-bx-edx"/>
<outlet property="appleIIModelButton" destination="jli-ac-Sij" id="Jm3-f7-C17"/> <outlet property="appleIIModelButton" destination="jli-ac-Sij" id="Jm3-f7-C17"/>
<outlet property="appleIIgsMemorySizeButton" destination="nQa-YS-utT" id="pTV-XL-zX3"/> <outlet property="appleIIgsMemorySizeButton" destination="nQa-YS-utT" id="pTV-XL-zX3"/>
<outlet property="appleIIgsModelButton" destination="gcS-uy-mzl" id="Jcc-jC-cV1"/> <outlet property="appleIIgsModelButton" destination="gcS-uy-mzl" id="Jcc-jC-cV1"/>

View File

@ -26,6 +26,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
// MARK: - Apple II properties // MARK: - Apple II properties
@IBOutlet var appleIIModelButton: NSPopUpButton! @IBOutlet var appleIIModelButton: NSPopUpButton!
@IBOutlet var appleIIDiskControllerButton: NSPopUpButton! @IBOutlet var appleIIDiskControllerButton: NSPopUpButton!
@IBOutlet var appleIIMockingboardButton: NSButton!
// MARK: - Apple IIgs properties // MARK: - Apple IIgs properties
@IBOutlet var appleIIgsModelButton: NSPopUpButton! @IBOutlet var appleIIgsModelButton: NSPopUpButton!
@ -117,6 +118,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
// Apple II settings // Apple II settings
appleIIModelButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.appleIIModel")) appleIIModelButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.appleIIModel"))
appleIIDiskControllerButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.appleIIDiskController")) appleIIDiskControllerButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.appleIIDiskController"))
appleIIMockingboardButton.state = standardUserDefaults.bool(forKey: "new.appleIIMockingboard") ? .on : .off
// Apple IIgs settings // Apple IIgs settings
appleIIgsModelButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.appleIIgsModel")) appleIIgsModelButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.appleIIgsModel"))
@ -187,6 +189,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
// Apple II settings // Apple II settings
standardUserDefaults.set(appleIIModelButton.selectedTag(), forKey: "new.appleIIModel") standardUserDefaults.set(appleIIModelButton.selectedTag(), forKey: "new.appleIIModel")
standardUserDefaults.set(appleIIDiskControllerButton.selectedTag(), forKey: "new.appleIIDiskController") standardUserDefaults.set(appleIIDiskControllerButton.selectedTag(), forKey: "new.appleIIDiskController")
standardUserDefaults.set(appleIIMockingboardButton.state == .on, forKey: "new.appleIIMockingboard")
// Apple IIgs settings // Apple IIgs settings
standardUserDefaults.set(appleIIgsModelButton.selectedTag(), forKey: "new.appleIIgsModel") standardUserDefaults.set(appleIIgsModelButton.selectedTag(), forKey: "new.appleIIgsModel")
@ -292,7 +295,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
default: diskController = .none default: diskController = .none
} }
return CSStaticAnalyser(appleIIModel: model, diskController: diskController) return CSStaticAnalyser(appleIIModel: model, diskController: diskController, hasMockingboard: appleIIMockingboardButton.state == .on)
case "appleiigs": case "appleiigs":
var model: CSMachineAppleIIgsModel = .ROM00 var model: CSMachineAppleIIgsModel = .ROM00

View File

@ -1042,6 +1042,8 @@ void MainWindow::start_appleII() {
case 2: target->disk_controller = Target::DiskController::None; break; case 2: target->disk_controller = Target::DiskController::None; break;
} }
target->has_mockingboard = ui->appleIIMockingboardCheckBox->isChecked();
launchTarget(std::move(target)); launchTarget(std::move(target));
} }

View File

@ -202,6 +202,13 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="QCheckBox" name="appleIIMockingboardCheckBox">
<property name="text">
<string>Has Mockingboard</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="appleIIgsTab"> <widget class="QWidget" name="appleIIgsTab">

View File

@ -77,11 +77,9 @@ template <typename... T> class CompoundSource:
// Map up and return. // Map up and return.
for(std::size_t c = 0; c < number_of_samples; c++) { for(std::size_t c = 0; c < number_of_samples; c++) {
Outputs::Speaker::apply<action>(target[c].left, StereoSample(conversion_source_[c])); Outputs::Speaker::apply<action>(target[c], StereoSample(conversion_source_[c]));
} }
return; } else {
}
constexpr bool is_final_source = sizeof...(R) == 0; constexpr bool is_final_source = sizeof...(R) == 0;
// Get the rest of the output, if any. // Get the rest of the output, if any.
@ -101,6 +99,7 @@ template <typename... T> class CompoundSource:
// Add in this component's output. // Add in this component's output.
source_.template apply_samples<is_final_source ? Action::Store : Action::Mix>(number_of_samples, target); source_.template apply_samples<is_final_source ? Action::Store : Action::Mix>(number_of_samples, target);
} }
}
void set_scaled_volume_range(int16_t range, double *volumes, double scale) { void set_scaled_volume_range(int16_t range, double *volumes, double scale) {
const auto scaled_range = volumes[0] / double(source_.average_output_peak()) * double(range) / scale; const auto scaled_range = volumes[0] / double(source_.average_output_peak()) * double(range) / scale;