mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-16 18:30:32 +00:00
Merge pull request #1321 from TomHarte/Mockingboard
Add Mockingboard support to the Apple II.
This commit is contained in:
commit
ac171d166e
@ -34,12 +34,14 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
||||
Model model = Model::IIe;
|
||||
DiskController disk_controller = DiskController::None;
|
||||
SCSIController scsi_controller = SCSIController::None;
|
||||
bool has_mockingboard = true;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::AppleII) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(model);
|
||||
DeclareField(disk_controller);
|
||||
DeclareField(scsi_controller);
|
||||
DeclareField(has_mockingboard);
|
||||
|
||||
AnnounceEnum(Model);
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ class IRQDelegatePortHandler: public PortHandler {
|
||||
/// Sets the delegate that will receive notification of changes in the interrupt line.
|
||||
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);
|
||||
|
||||
private:
|
||||
|
@ -376,6 +376,30 @@ void AY38910SampleSource<is_stereo>::set_control_lines(ControlLines control_line
|
||||
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>
|
||||
void AY38910SampleSource<is_stereo>::update_bus() {
|
||||
// Assume no output, unless this turns out to be a read.
|
||||
|
@ -70,6 +70,7 @@ template <bool stereo> class AY38910SampleSource {
|
||||
public:
|
||||
/// Creates a new AY38910.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
@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:
|
||||
Concurrency::AsyncTaskQueue<false> &task_queue_;
|
||||
|
||||
int selected_register_ = 0;
|
||||
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};
|
||||
bool reset_ = false;
|
||||
|
||||
int tone_periods_[3] = {0, 0, 0};
|
||||
int tone_counters_[3] = {0, 0, 0};
|
||||
int tone_outputs_[3] = {0, 0, 0};
|
||||
int selected_register_ = 0;
|
||||
uint8_t registers_[16]{};
|
||||
uint8_t output_registers_[16]{};
|
||||
|
||||
int tone_periods_[3]{};
|
||||
int tone_counters_[3]{};
|
||||
int tone_outputs_[3]{};
|
||||
|
||||
int noise_period_ = 0;
|
||||
int noise_counter_ = 0;
|
||||
|
@ -15,7 +15,9 @@
|
||||
|
||||
#include "../../../Processors/6502/6502.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/Log.hpp"
|
||||
|
||||
@ -24,6 +26,7 @@
|
||||
#include "DiskIICard.hpp"
|
||||
#include "Joystick.hpp"
|
||||
#include "LanguageCardSwitches.hpp"
|
||||
#include "Mockingboard.hpp"
|
||||
#include "SCSICard.hpp"
|
||||
#include "Video.hpp"
|
||||
|
||||
@ -41,17 +44,71 @@
|
||||
|
||||
namespace {
|
||||
|
||||
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 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 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 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> class ConcreteMachine:
|
||||
template <Analyser::Static::AppleII::Target::Model model, bool has_mockingboard> class ConcreteMachine:
|
||||
public Apple::II::Machine,
|
||||
public MachineTypes::TimedMachine,
|
||||
public MachineTypes::ScanProducer,
|
||||
@ -83,14 +140,14 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
false>;
|
||||
Processor m6502_;
|
||||
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;
|
||||
Cycles cycles_since_video_update_;
|
||||
|
||||
void update_video() {
|
||||
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() {
|
||||
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_;
|
||||
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_;
|
||||
|
||||
constexpr SourceT &lowpass_source() {
|
||||
if constexpr (has_mockingboard) {
|
||||
return mixer_;
|
||||
} else {
|
||||
return audio_toggle_;
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Cards
|
||||
static constexpr size_t NoActiveCard = 7; // There is no 'card 0' in internal numbering.
|
||||
size_t active_card_ = NoActiveCard;
|
||||
@ -155,6 +226,24 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
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() {
|
||||
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();
|
||||
|
||||
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
|
||||
// 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 {
|
||||
if constexpr (!is_iie()) {
|
||||
if constexpr (!is_iie(model)) {
|
||||
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::Up: value = 0x0b; break;
|
||||
case Key::Backspace:
|
||||
if(is_iie()) {
|
||||
if(is_iie(model)) {
|
||||
value = 0x7f;
|
||||
break;
|
||||
} else {
|
||||
@ -305,7 +394,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
}
|
||||
case Key::Enter: value = 0x0d; break;
|
||||
case Key::Tab:
|
||||
if (is_iie()) {
|
||||
if (is_iie(model)) {
|
||||
value = '\t';
|
||||
break;
|
||||
} else {
|
||||
@ -316,7 +405,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
|
||||
case Key::LeftOption:
|
||||
case Key::RightMeta:
|
||||
if (is_iie()) {
|
||||
if (is_iie(model)) {
|
||||
open_apple_is_pressed = is_pressed;
|
||||
return true;
|
||||
} else {
|
||||
@ -325,7 +414,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
|
||||
case Key::RightOption:
|
||||
case Key::LeftMeta:
|
||||
if (is_iie()) {
|
||||
if (is_iie(model)) {
|
||||
closed_apple_is_pressed = is_pressed;
|
||||
return true;
|
||||
} else {
|
||||
@ -346,7 +435,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
case Key::F9: case Key::F10: case Key::F11:
|
||||
repeat_is_pressed_ = is_pressed;
|
||||
|
||||
if constexpr (!is_iie()) {
|
||||
if constexpr (!is_iie(model)) {
|
||||
if(is_pressed && (!is_repeat || character_is_pressed_)) {
|
||||
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.
|
||||
if(!is_iie()) value = char(toupper(value));
|
||||
if(!is_iie(model)) value = char(toupper(value));
|
||||
|
||||
if(control_is_pressed_ && isalpha(value)) value &= 0xbf;
|
||||
|
||||
// TODO: properly map IIe keys
|
||||
if(!is_iie() && shift_is_pressed_) {
|
||||
if(!is_iie(model) && shift_is_pressed_) {
|
||||
switch(value) {
|
||||
case 0x27: value = 0x22; 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_(video_bus_handler_),
|
||||
audio_toggle_(audio_queue_),
|
||||
speaker_(audio_toggle_),
|
||||
ays_(audio_queue_),
|
||||
mixer_(ays_, audio_toggle_),
|
||||
speaker_(lowpass_source()),
|
||||
language_card_(*this),
|
||||
auxiliary_switches_(*this),
|
||||
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
|
||||
// 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)));
|
||||
}
|
||||
|
||||
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);
|
||||
// The IIe and Enhanced IIe ROMs often distributed are oversized; trim if necessary.
|
||||
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(is_iie()) {
|
||||
if(is_iie(model)) {
|
||||
auxiliary_switches_.access(address, isReadOperation(operation));
|
||||
}
|
||||
} else {
|
||||
@ -669,7 +764,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
*value &= 0x7f;
|
||||
if(
|
||||
joysticks_.button(0) ||
|
||||
(is_iie() && keyboard_.open_apple_is_pressed)
|
||||
(is_iie(model) && keyboard_.open_apple_is_pressed)
|
||||
)
|
||||
*value |= 0x80;
|
||||
break;
|
||||
@ -677,7 +772,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
*value &= 0x7f;
|
||||
if(
|
||||
joysticks_.button(1) ||
|
||||
(is_iie() && keyboard_.closed_apple_is_pressed)
|
||||
(is_iie(model) && keyboard_.closed_apple_is_pressed)
|
||||
)
|
||||
*value |= 0x80;
|
||||
break;
|
||||
@ -699,7 +794,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
} break;
|
||||
|
||||
// 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 0xc012: IIeSwitchRead(language_card_.state().read); 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
|
||||
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
// Write-only switches. All IIe as currently implemented.
|
||||
if(is_iie()) {
|
||||
if(is_iie(model)) {
|
||||
auxiliary_switches_.access(address, false);
|
||||
switch(address) {
|
||||
default: break;
|
||||
@ -775,7 +870,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
|
||||
case 0xc05e:
|
||||
case 0xc05f:
|
||||
if(is_iie()) {
|
||||
if(is_iie(model)) {
|
||||
update_video();
|
||||
video_.set_annunciator_3(!(address&1));
|
||||
}
|
||||
@ -785,7 +880,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
keyboard_.clear_keyboard_input();
|
||||
|
||||
// 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);
|
||||
}
|
||||
break;
|
||||
@ -922,7 +1017,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
}
|
||||
|
||||
bool prefers_logical_input() final {
|
||||
return is_iie();
|
||||
return is_iie(model);
|
||||
}
|
||||
|
||||
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) {
|
||||
using Target = Analyser::Static::AppleII::Target;
|
||||
const Target *const appleii_target = dynamic_cast<const Target *>(target);
|
||||
switch(appleii_target->model) {
|
||||
default: return nullptr;
|
||||
case Target::Model::II: return std::make_unique<ConcreteMachine<Target::Model::II>>(*appleii_target, rom_fetcher);
|
||||
case Target::Model::IIplus: return std::make_unique<ConcreteMachine<Target::Model::IIplus>>(*appleii_target, rom_fetcher);
|
||||
case Target::Model::IIe: return std::make_unique<ConcreteMachine<Target::Model::IIe>>(*appleii_target, rom_fetcher);
|
||||
case Target::Model::EnhancedIIe: return std::make_unique<ConcreteMachine<Target::Model::EnhancedIIe>>(*appleii_target, rom_fetcher);
|
||||
|
||||
if(appleii_target->has_mockingboard) {
|
||||
switch(appleii_target->model) {
|
||||
default: return nullptr;
|
||||
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, true>>(*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, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,12 +49,16 @@ class Card {
|
||||
// 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.
|
||||
|
||||
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
|
||||
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) {}
|
||||
|
||||
@ -93,8 +97,15 @@ class Card {
|
||||
/*! Cards may supply a target for activity observation if desired. */
|
||||
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 {
|
||||
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) {
|
||||
delegate_ = delegate;
|
||||
|
135
Machines/Apple/AppleII/Mockingboard.hpp
Normal file
135
Machines/Apple/AppleII/Mockingboard.hpp
Normal 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];
|
||||
};
|
||||
|
||||
}
|
@ -1568,6 +1568,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -4840,6 +4841,7 @@
|
||||
4B2E86E125DC95150024F1E9 /* Joystick.hpp */,
|
||||
4BF40A5525424C770033EA39 /* LanguageCardSwitches.hpp */,
|
||||
4BE0151C286A8C8E00EA42E9 /* MemorySwitches.hpp */,
|
||||
4B72FAEF2B7D51BB000C7E90 /* Mockingboard.hpp */,
|
||||
4B4C81C428B3C5CD00F84AE9 /* SCSICard.hpp */,
|
||||
4BCE004F227CE8CA000CA200 /* Video.hpp */,
|
||||
4B8DF4F2254E141700F3433C /* VideoSwitches.hpp */,
|
||||
|
@ -141,7 +141,7 @@ typedef int Kilobytes;
|
||||
|
||||
- (instancetype)initWithAmigaModel:(CSMachineAmigaModel)model chipMemorySize:(Kilobytes)chipMemorySize fastMemorySize:(Kilobytes)fastMemorySize;
|
||||
- (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)initWithAtariSTModel:(CSMachineAtariSTModel)model memorySize:(Kilobytes)memorySize;
|
||||
- (instancetype)initWithElectronDFS:(BOOL)dfs adfs:(BOOL)adfs ap6:(BOOL)ap6 sidewaysRAM:(BOOL)sidewaysRAM;
|
||||
|
@ -93,7 +93,7 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithAppleIIModel:(CSMachineAppleIIModel)model diskController:(CSMachineAppleIIDiskController)diskController {
|
||||
- (instancetype)initWithAppleIIModel:(CSMachineAppleIIModel)model diskController:(CSMachineAppleIIDiskController)diskController hasMockingboard:(BOOL)hasMockingboard {
|
||||
self = [super init];
|
||||
if(self) {
|
||||
using Target = Analyser::Static::AppleII::Target;
|
||||
@ -110,6 +110,7 @@
|
||||
case CSMachineAppleIIDiskControllerSixteenSector: target->disk_controller = Target::DiskController::SixteenSector; break;
|
||||
case CSMachineAppleIIDiskControllerThirteenSector: target->disk_controller = Target::DiskController::ThirteenSector; break;
|
||||
}
|
||||
target->has_mockingboard = hasMockingboard;
|
||||
_targets.push_back(std::move(target));
|
||||
}
|
||||
return self;
|
||||
|
@ -1,8 +1,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>
|
||||
<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"/>
|
||||
</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="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">
|
||||
<rect key="frame" x="0.0" y="0.0" width="590" height="369"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
@ -49,7 +49,7 @@ Gw
|
||||
<action selector="cancelCreateMachine:" target="-2" id="lf8-PM-c0m"/>
|
||||
</connections>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" allowsUndo="NO" sendsActionOnEndEditing="YES" id="xTm-Oy-oz5">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -104,7 +104,7 @@ Gw
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Choose a machine" id="32m-Vs-dPO">
|
||||
<font key="font" textStyle="title2" name=".SFNS-Regular"/>
|
||||
@ -135,7 +135,7 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Chip Memory:" id="FIO-ZR-rsA">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -143,7 +143,7 @@ Gw
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Fast Memory:" id="Rpz-39-jyt">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -202,7 +202,7 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="53v-92-jmf">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -223,19 +223,19 @@ Gw
|
||||
</tabViewItem>
|
||||
<tabViewItem label="Apple II" identifier="appleii" id="P59-QG-LOa">
|
||||
<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"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="V5Z-dX-Ns4">
|
||||
<rect key="frame" x="18" y="184" width="46" height="16"/>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="V5Z-dX-Ns4">
|
||||
<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">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="WnO-ef-IC6">
|
||||
<rect key="frame" x="18" y="154" width="96" height="16"/>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="WnO-ef-IC6">
|
||||
<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">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -243,7 +243,7 @@ Gw
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<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">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@ -258,7 +258,7 @@ Gw
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<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">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@ -271,16 +271,25 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</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>
|
||||
<constraints>
|
||||
<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 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 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="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 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="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"/>
|
||||
@ -292,7 +301,7 @@ Gw
|
||||
<rect key="frame" x="10" y="7" width="400" height="222"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="kiv-1P-FWc">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -300,7 +309,7 @@ Gw
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="OLJ-nC-yyj">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -357,7 +366,7 @@ Gw
|
||||
<rect key="frame" x="10" y="7" width="400" height="222"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory:" id="ZBF-0r-RNK">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -515,7 +524,7 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="EXOS:" id="gUC-PN-zVL">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -523,7 +532,7 @@ Gw
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Machine:" id="uTv-hH-mIC">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -531,7 +540,7 @@ Gw
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="BASIC:" id="ai1-oR-X6Y">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -539,7 +548,7 @@ Gw
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="DOS:" id="NFk-cp-DfS">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -547,7 +556,7 @@ Gw
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Speed:" id="sAw-C9-Sf7">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -593,7 +602,7 @@ Gw
|
||||
<rect key="frame" x="10" y="7" width="400" height="222"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="h9r-i6-66j">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -653,7 +662,7 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Region:" id="x4m-eh-Nif">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -661,7 +670,7 @@ Gw
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="r7C-zE-gyj">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -716,7 +725,7 @@ Gw
|
||||
<rect key="frame" x="10" y="7" width="400" height="222"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="Xm1-7x-YVl">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -754,7 +763,7 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Disk Interface:" id="SFK-hS-tFC">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -796,7 +805,7 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Video Adaptor:" id="ROV-EU-T3W">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -804,7 +813,7 @@ Gw
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Speed:" id="qXc-wf-5jm">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -876,7 +885,7 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Region:" id="F3g-Ya-ypU">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -884,7 +893,7 @@ Gw
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="a4I-vG-yCp">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -937,7 +946,7 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="e6x-TE-OC5">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -985,7 +994,7 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="z4b-oR-Yl2">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -1026,7 +1035,7 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</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"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="JId-Tp-LrE">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -1075,6 +1084,7 @@ Gw
|
||||
<outlet property="amigaChipRAMButton" destination="qfH-1l-GXp" id="3PG-QC-tyI"/>
|
||||
<outlet property="amigaFastRAMButton" destination="af8-pF-qc9" id="laa-1H-JfD"/>
|
||||
<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="appleIIgsMemorySizeButton" destination="nQa-YS-utT" id="pTV-XL-zX3"/>
|
||||
<outlet property="appleIIgsModelButton" destination="gcS-uy-mzl" id="Jcc-jC-cV1"/>
|
||||
|
@ -26,6 +26,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
// MARK: - Apple II properties
|
||||
@IBOutlet var appleIIModelButton: NSPopUpButton!
|
||||
@IBOutlet var appleIIDiskControllerButton: NSPopUpButton!
|
||||
@IBOutlet var appleIIMockingboardButton: NSButton!
|
||||
|
||||
// MARK: - Apple IIgs properties
|
||||
@IBOutlet var appleIIgsModelButton: NSPopUpButton!
|
||||
@ -117,6 +118,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
// Apple II settings
|
||||
appleIIModelButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.appleIIModel"))
|
||||
appleIIDiskControllerButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.appleIIDiskController"))
|
||||
appleIIMockingboardButton.state = standardUserDefaults.bool(forKey: "new.appleIIMockingboard") ? .on : .off
|
||||
|
||||
// Apple IIgs settings
|
||||
appleIIgsModelButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.appleIIgsModel"))
|
||||
@ -187,6 +189,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
// Apple II settings
|
||||
standardUserDefaults.set(appleIIModelButton.selectedTag(), forKey: "new.appleIIModel")
|
||||
standardUserDefaults.set(appleIIDiskControllerButton.selectedTag(), forKey: "new.appleIIDiskController")
|
||||
standardUserDefaults.set(appleIIMockingboardButton.state == .on, forKey: "new.appleIIMockingboard")
|
||||
|
||||
// Apple IIgs settings
|
||||
standardUserDefaults.set(appleIIgsModelButton.selectedTag(), forKey: "new.appleIIgsModel")
|
||||
@ -292,7 +295,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
default: diskController = .none
|
||||
}
|
||||
|
||||
return CSStaticAnalyser(appleIIModel: model, diskController: diskController)
|
||||
return CSStaticAnalyser(appleIIModel: model, diskController: diskController, hasMockingboard: appleIIMockingboardButton.state == .on)
|
||||
|
||||
case "appleiigs":
|
||||
var model: CSMachineAppleIIgsModel = .ROM00
|
||||
|
@ -1042,6 +1042,8 @@ void MainWindow::start_appleII() {
|
||||
case 2: target->disk_controller = Target::DiskController::None; break;
|
||||
}
|
||||
|
||||
target->has_mockingboard = ui->appleIIMockingboardCheckBox->isChecked();
|
||||
|
||||
launchTarget(std::move(target));
|
||||
}
|
||||
|
||||
|
@ -202,6 +202,13 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="appleIIMockingboardCheckBox">
|
||||
<property name="text">
|
||||
<string>Has Mockingboard</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="appleIIgsTab">
|
||||
|
@ -77,29 +77,28 @@ template <typename... T> class CompoundSource:
|
||||
|
||||
// Map up and return.
|
||||
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.
|
||||
if constexpr (!is_final_source) {
|
||||
next_source_.template apply_samples<action, output_stereo>(number_of_samples, target);
|
||||
}
|
||||
|
||||
if(source_.is_zero_level()) {
|
||||
// This component is currently outputting silence; therefore don't add anything to the output
|
||||
// audio. Zero fill only if this is the final source (as everything above will be additive).
|
||||
if constexpr (is_final_source) {
|
||||
Outputs::Speaker::fill<action>(target, target + number_of_samples, typename SampleT<output_stereo>::type());
|
||||
// Get the rest of the output, if any.
|
||||
if constexpr (!is_final_source) {
|
||||
next_source_.template apply_samples<action, output_stereo>(number_of_samples, target);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Add in this component's output.
|
||||
source_.template apply_samples<is_final_source ? Action::Store : Action::Mix>(number_of_samples, target);
|
||||
if(source_.is_zero_level()) {
|
||||
// This component is currently outputting silence; therefore don't add anything to the output
|
||||
// audio. Zero fill only if this is the final source (as everything above will be additive).
|
||||
if constexpr (is_final_source) {
|
||||
Outputs::Speaker::fill<action>(target, target + number_of_samples, typename SampleT<output_stereo>::type());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Add in this component's output.
|
||||
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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user