mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-24 12:30:17 +00:00
Merge pull request #1262 from TomHarte/PCTurbo
Add turbo option for PC execution.
This commit is contained in:
commit
2a0176dde6
@ -20,10 +20,17 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
||||
CGA);
|
||||
VideoAdaptor adaptor = VideoAdaptor::CGA;
|
||||
|
||||
ReflectableEnum(Speed,
|
||||
ApproximatelyOriginal,
|
||||
Fast);
|
||||
Speed speed = Speed::Fast;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::PCCompatible) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(adaptor);
|
||||
AnnounceEnum(VideoAdaptor);
|
||||
AnnounceEnum(Speed);
|
||||
DeclareField(adaptor);
|
||||
DeclareField(speed);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -67,7 +67,7 @@ class CGA {
|
||||
// b1: 1 => positive edge from light pen has set trigger;
|
||||
// b0: 1 => safe to write to VRAM now without causing snow.
|
||||
(crtc_.get_bus_state().vsync ? 0b1001 : 0b0000) |
|
||||
(crtc_.get_bus_state().hsync ? 0b0001 : 0b0000) |
|
||||
(crtc_.get_bus_state().display_enable ? 0b0000 : 0b0001) |
|
||||
0b0100;
|
||||
|
||||
default: return 0xff;
|
||||
@ -178,6 +178,7 @@ class CGA {
|
||||
// Determine new output state.
|
||||
update_hsync(state.hsync);
|
||||
const OutputState new_state = implied_state(state);
|
||||
static constexpr uint8_t colour_phase = 200;
|
||||
|
||||
// Upon either a state change or just having accumulated too much local time...
|
||||
if(
|
||||
@ -190,7 +191,7 @@ class CGA {
|
||||
// (1) flush preexisting state.
|
||||
if(count) {
|
||||
switch(output_state) {
|
||||
case OutputState::Sync: crt.output_sync(count * active_clock_divider); break;
|
||||
case OutputState::Sync: crt.output_sync(count * active_clock_divider); break;
|
||||
case OutputState::Border:
|
||||
if(active_border_colour) {
|
||||
crt.output_blank(count * active_clock_divider);
|
||||
@ -198,8 +199,8 @@ class CGA {
|
||||
crt.output_level<uint8_t>(count * active_clock_divider, active_border_colour);
|
||||
}
|
||||
break;
|
||||
case OutputState::ColourBurst: crt.output_colour_burst(count * active_clock_divider, 0); break;
|
||||
case OutputState::Pixels: flush_pixels(); break;
|
||||
case OutputState::ColourBurst: crt.output_colour_burst(count * active_clock_divider, colour_phase); break;
|
||||
case OutputState::Pixels: flush_pixels(); break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -306,15 +307,18 @@ class CGA {
|
||||
|
||||
// Apply blink or background intensity.
|
||||
if(control_ & 0x20) {
|
||||
// Potentially remap dark yellow to brown.
|
||||
colours[0] = yellow_to_brown(colours[0]);
|
||||
|
||||
// Set both colours to black if within a blink; otherwise consider a yellow-to-brown conversion.
|
||||
if((attributes & 0x80) && (state.field_count & 16)) {
|
||||
std::swap(colours[0], colours[1]);
|
||||
colours[0] = colours[1] = 0;
|
||||
} else {
|
||||
colours[0] = yellow_to_brown(colours[0]);
|
||||
}
|
||||
} else {
|
||||
if(attributes & 0x80) {
|
||||
colours[0] = bright(colours[0]);
|
||||
} else {
|
||||
// Yellow to brown definitely doesn't apply if the colour has been brightened.
|
||||
colours[0] = yellow_to_brown(colours[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@ class MDA {
|
||||
// TODO: really this should be a Luminance8 and set an appropriate modal tint colour;
|
||||
// consider whether that's worth building into the scan target.
|
||||
{
|
||||
// crt.set_visible_area(Outputs::Display::Rect(0.1072f, 0.1f, 0.842105263157895f, 0.842105263157895f));
|
||||
crt.set_visible_area(Outputs::Display::Rect(0.028f, 0.025f, 0.98f, 0.98f));
|
||||
crt.set_display_type(Outputs::Display::DisplayType::RGB);
|
||||
}
|
||||
|
||||
|
@ -908,7 +908,7 @@ class FlowController {
|
||||
bool halted_ = false;
|
||||
};
|
||||
|
||||
template <VideoAdaptor video>
|
||||
template <VideoAdaptor video, bool turbo>
|
||||
class ConcreteMachine:
|
||||
public Machine,
|
||||
public MachineTypes::TimedMachine,
|
||||
@ -975,9 +975,15 @@ class ConcreteMachine:
|
||||
// MARK: - TimedMachine.
|
||||
void run_for(const Cycles duration) override {
|
||||
const auto pit_ticks = duration.as_integral();
|
||||
cpu_divisor_ += pit_ticks;
|
||||
int ticks = cpu_divisor_ / 3;
|
||||
cpu_divisor_ %= 3;
|
||||
|
||||
int ticks;
|
||||
if constexpr (!turbo) {
|
||||
cpu_divisor_ += pit_ticks;
|
||||
ticks = cpu_divisor_ / 3;
|
||||
cpu_divisor_ %= 3;
|
||||
} else {
|
||||
ticks = pit_ticks;
|
||||
}
|
||||
|
||||
while(ticks--) {
|
||||
//
|
||||
@ -989,23 +995,30 @@ class ConcreteMachine:
|
||||
//
|
||||
pit_.run_for(1);
|
||||
++speaker_.cycles_since_update;
|
||||
pit_.run_for(1);
|
||||
++speaker_.cycles_since_update;
|
||||
pit_.run_for(1);
|
||||
++speaker_.cycles_since_update;
|
||||
|
||||
if constexpr (!turbo) {
|
||||
pit_.run_for(1);
|
||||
++speaker_.cycles_since_update;
|
||||
pit_.run_for(1);
|
||||
++speaker_.cycles_since_update;
|
||||
}
|
||||
|
||||
//
|
||||
// Advance CRTC at a more approximate rate.
|
||||
//
|
||||
video_.run_for(Cycles(3));
|
||||
video_.run_for(turbo ? Cycles(1) : Cycles(3));
|
||||
|
||||
//
|
||||
// Give the keyboard a notification of passing time; it's very approximately clocked,
|
||||
// really just including 'some' delays to avoid being instant.
|
||||
//
|
||||
keyboard_.run_for(Cycles(1));
|
||||
|
||||
//
|
||||
// Perform one CPU instruction every three PIT cycles.
|
||||
// i.e. CPU instruction rate is 1/3 * ~1.19Mhz ~= 0.4 MIPS.
|
||||
//
|
||||
|
||||
keyboard_.run_for(Cycles(1));
|
||||
|
||||
// Query for interrupts and apply if pending.
|
||||
if(pic_.pending() && context.flags.template flag<InstructionSet::x86::Flag::Interrupt>()) {
|
||||
// Regress the IP if a REP is in-progress so as to resume it later.
|
||||
@ -1022,58 +1035,77 @@ class ConcreteMachine:
|
||||
);
|
||||
}
|
||||
|
||||
// Do nothing if halted.
|
||||
// Do nothing if currently halted.
|
||||
if(context.flow_controller.halted()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the next thing to execute.
|
||||
if(!context.flow_controller.should_repeat()) {
|
||||
// Decode from the current IP.
|
||||
decoded_ip_ = context.registers.ip();
|
||||
const auto remainder = context.memory.next_code();
|
||||
decoded = decoder.decode(remainder.first, remainder.second);
|
||||
|
||||
// If that didn't yield a whole instruction then the end of memory must have been hit;
|
||||
// continue from the beginning.
|
||||
if(decoded.first <= 0) {
|
||||
const auto all = context.memory.all();
|
||||
decoded = decoder.decode(all.first, all.second);
|
||||
}
|
||||
|
||||
context.registers.ip() += decoded.first;
|
||||
if constexpr (turbo) {
|
||||
// There's no divider applied, so this makes for 2*PI = around 2.4 MIPS.
|
||||
// That's broadly 80286 speed, if MIPS were a valid measure.
|
||||
perform_instruction();
|
||||
perform_instruction();
|
||||
} else {
|
||||
context.flow_controller.begin_instruction();
|
||||
// With the clock divider above, this makes for a net of PIT/3 = around 0.4 MIPS.
|
||||
// i.e. a shade more than 8086 speed, if MIPS were meaningful.
|
||||
perform_instruction();
|
||||
}
|
||||
|
||||
/* if(decoded_ip_ >= 0x7c00 && decoded_ip_ < 0x7c00 + 1024) {
|
||||
const auto next = to_string(decoded, InstructionSet::x86::Model::i8086);
|
||||
// if(next != previous) {
|
||||
std::cout << std::hex << decoded_ip_ << " " << next;
|
||||
|
||||
if(decoded.second.operation() == InstructionSet::x86::Operation::INT) {
|
||||
std::cout << " dl:" << std::hex << +context.registers.dl() << "; ";
|
||||
std::cout << "ah:" << std::hex << +context.registers.ah() << "; ";
|
||||
std::cout << "ch:" << std::hex << +context.registers.ch() << "; ";
|
||||
std::cout << "cl:" << std::hex << +context.registers.cl() << "; ";
|
||||
std::cout << "dh:" << std::hex << +context.registers.dh() << "; ";
|
||||
std::cout << "es:" << std::hex << +context.registers.es() << "; ";
|
||||
std::cout << "bx:" << std::hex << +context.registers.bx();
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
// previous = next;
|
||||
// }
|
||||
}*/
|
||||
|
||||
// Execute it.
|
||||
InstructionSet::x86::perform(
|
||||
decoded.second,
|
||||
context
|
||||
);
|
||||
// Other inevitably broad and fuzzy and inconsistent MIPS counts for my own potential future play:
|
||||
//
|
||||
// 80386 @ 20Mhz: 4–5 MIPS.
|
||||
// 80486 @ 66Mhz: 25 MIPS.
|
||||
// Pentium @ 100Mhz: 188 MIPS.
|
||||
}
|
||||
}
|
||||
|
||||
void perform_instruction() {
|
||||
// Get the next thing to execute.
|
||||
if(!context.flow_controller.should_repeat()) {
|
||||
// Decode from the current IP.
|
||||
decoded_ip_ = context.registers.ip();
|
||||
const auto remainder = context.memory.next_code();
|
||||
decoded = decoder.decode(remainder.first, remainder.second);
|
||||
|
||||
// If that didn't yield a whole instruction then the end of memory must have been hit;
|
||||
// continue from the beginning.
|
||||
if(decoded.first <= 0) {
|
||||
const auto all = context.memory.all();
|
||||
decoded = decoder.decode(all.first, all.second);
|
||||
}
|
||||
|
||||
context.registers.ip() += decoded.first;
|
||||
} else {
|
||||
context.flow_controller.begin_instruction();
|
||||
}
|
||||
|
||||
/* if(decoded_ip_ >= 0x7c00 && decoded_ip_ < 0x7c00 + 1024) {
|
||||
const auto next = to_string(decoded, InstructionSet::x86::Model::i8086);
|
||||
// if(next != previous) {
|
||||
std::cout << std::hex << decoded_ip_ << " " << next;
|
||||
|
||||
if(decoded.second.operation() == InstructionSet::x86::Operation::INT) {
|
||||
std::cout << " dl:" << std::hex << +context.registers.dl() << "; ";
|
||||
std::cout << "ah:" << std::hex << +context.registers.ah() << "; ";
|
||||
std::cout << "ch:" << std::hex << +context.registers.ch() << "; ";
|
||||
std::cout << "cl:" << std::hex << +context.registers.cl() << "; ";
|
||||
std::cout << "dh:" << std::hex << +context.registers.dh() << "; ";
|
||||
std::cout << "es:" << std::hex << +context.registers.es() << "; ";
|
||||
std::cout << "bx:" << std::hex << +context.registers.bx();
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
// previous = next;
|
||||
// }
|
||||
}*/
|
||||
|
||||
// Execute it.
|
||||
InstructionSet::x86::perform(
|
||||
decoded.second,
|
||||
context
|
||||
);
|
||||
}
|
||||
|
||||
// MARK: - ScanProducer.
|
||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
|
||||
video_.set_scan_target(scan_target);
|
||||
@ -1204,10 +1236,18 @@ Machine *Machine::PCCompatible(const Analyser::Static::Target *target, const ROM
|
||||
using Target = Analyser::Static::PCCompatible::Target;
|
||||
const Target *const pc_target = dynamic_cast<const Target *>(target);
|
||||
|
||||
switch(pc_target->adaptor) {
|
||||
case VideoAdaptor::MDA: return new PCCompatible::ConcreteMachine<VideoAdaptor::MDA>(*pc_target, rom_fetcher);
|
||||
case VideoAdaptor::CGA: return new PCCompatible::ConcreteMachine<VideoAdaptor::CGA>(*pc_target, rom_fetcher);
|
||||
default: return nullptr;
|
||||
if(pc_target->speed == Target::Speed::Fast) {
|
||||
switch(pc_target->adaptor) {
|
||||
case VideoAdaptor::MDA: return new PCCompatible::ConcreteMachine<VideoAdaptor::MDA, true>(*pc_target, rom_fetcher);
|
||||
case VideoAdaptor::CGA: return new PCCompatible::ConcreteMachine<VideoAdaptor::CGA, true>(*pc_target, rom_fetcher);
|
||||
default: return nullptr;
|
||||
}
|
||||
} else {
|
||||
switch(pc_target->adaptor) {
|
||||
case VideoAdaptor::MDA: return new PCCompatible::ConcreteMachine<VideoAdaptor::MDA, false>(*pc_target, rom_fetcher);
|
||||
case VideoAdaptor::CGA: return new PCCompatible::ConcreteMachine<VideoAdaptor::CGA, false>(*pc_target, rom_fetcher);
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,8 +123,9 @@ typedef NS_ENUM(NSInteger, CSMachineMSXRegion) {
|
||||
CSMachineMSXRegionJapanese,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSPCCompatibleModel) {
|
||||
CSPCCompatibleModelTurboXT,
|
||||
typedef NS_ENUM(NSInteger, CSPCCompatibleSpeed) {
|
||||
CSPCCompatibleSpeedOriginal,
|
||||
CSPCCompatibleSpeedTurbo,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSPCCompatibleVideoAdaptor) {
|
||||
@ -152,7 +153,7 @@ typedef int Kilobytes;
|
||||
- (instancetype)initWithVic20Region:(CSMachineVic20Region)region memorySize:(Kilobytes)memorySize hasC1540:(BOOL)hasC1540;
|
||||
- (instancetype)initWithZX80MemorySize:(Kilobytes)memorySize useZX81ROM:(BOOL)useZX81ROM;
|
||||
- (instancetype)initWithZX81MemorySize:(Kilobytes)memorySize;
|
||||
- (instancetype)initWithPCCompatibleModel:(CSPCCompatibleModel)model videoAdaptor:(CSPCCompatibleVideoAdaptor)adaptor;
|
||||
- (instancetype)initWithPCCompatibleSpeed:(CSPCCompatibleSpeed)speed videoAdaptor:(CSPCCompatibleVideoAdaptor)adaptor;
|
||||
|
||||
@property(nonatomic, readonly, nullable) NSString *optionsNibName;
|
||||
@property(nonatomic, readonly) NSString *displayName;
|
||||
|
@ -273,7 +273,7 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithPCCompatibleModel:(CSPCCompatibleModel)model videoAdaptor:(CSPCCompatibleVideoAdaptor)adaptor {
|
||||
- (instancetype)initWithPCCompatibleSpeed:(CSPCCompatibleSpeed)speed videoAdaptor:(CSPCCompatibleVideoAdaptor)adaptor {
|
||||
self = [super init];
|
||||
if(self) {
|
||||
using Target = Analyser::Static::PCCompatible::Target;
|
||||
@ -282,6 +282,10 @@
|
||||
case CSPCCompatibleVideoAdaptorMDA: target->adaptor = Target::VideoAdaptor::MDA; break;
|
||||
case CSPCCompatibleVideoAdaptorCGA: target->adaptor = Target::VideoAdaptor::CGA; break;
|
||||
}
|
||||
switch(speed) {
|
||||
case CSPCCompatibleSpeedOriginal: target->speed = Target::Speed::ApproximatelyOriginal; break;
|
||||
case CSPCCompatibleSpeedTurbo: target->speed = Target::Speed::Fast; break;
|
||||
}
|
||||
_targets.push_back(std::move(target));
|
||||
}
|
||||
return self;
|
||||
|
@ -750,13 +750,39 @@ Gw
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField 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"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ci6-TG-5tW">
|
||||
<rect key="frame" x="68" y="162" width="146" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Similar to Original" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="8086" imageScaling="axesIndependently" inset="2" selectedItem="R4W-s4-KFx" id="9i0-UG-B2c">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="KQw-8g-SrI">
|
||||
<items>
|
||||
<menuItem title="Similar to Original" state="on" tag="8086" id="R4W-s4-KFx"/>
|
||||
<menuItem title="Turbo" tag="80286" id="LeD-N4-avf"/>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="Ci6-TG-5tW" firstAttribute="leading" secondItem="8vF-eu-ClP" secondAttribute="trailing" constant="8" symbolic="YES" id="0Sb-TO-UVM"/>
|
||||
<constraint firstItem="Ci6-TG-5tW" firstAttribute="top" secondItem="stw-i3-ikG" secondAttribute="bottom" constant="10" symbolic="YES" id="4E9-4l-fat"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="Ci6-TG-5tW" secondAttribute="bottom" constant="20" symbolic="YES" id="9C6-PK-12w"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="stw-i3-ikG" secondAttribute="trailing" constant="20" symbolic="YES" id="A0n-lr-LHf"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="Ci6-TG-5tW" secondAttribute="trailing" constant="20" symbolic="YES" id="G4D-zS-Q1t"/>
|
||||
<constraint firstItem="uhf-1k-ibT" firstAttribute="leading" secondItem="gJD-vd-WWu" secondAttribute="leading" constant="20" symbolic="YES" id="JNr-Tg-gDV"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="stw-i3-ikG" secondAttribute="bottom" constant="20" symbolic="YES" id="RSc-OQ-KNO"/>
|
||||
<constraint firstItem="uhf-1k-ibT" firstAttribute="centerY" secondItem="stw-i3-ikG" secondAttribute="centerY" id="bfD-kx-eOc"/>
|
||||
<constraint firstItem="stw-i3-ikG" firstAttribute="leading" secondItem="uhf-1k-ibT" secondAttribute="trailing" constant="8" symbolic="YES" id="f6l-B2-nBw"/>
|
||||
<constraint firstItem="8vF-eu-ClP" firstAttribute="leading" secondItem="gJD-vd-WWu" secondAttribute="leading" constant="20" symbolic="YES" id="ftj-Es-CRQ"/>
|
||||
<constraint firstItem="8vF-eu-ClP" firstAttribute="centerY" secondItem="Ci6-TG-5tW" secondAttribute="centerY" id="wUL-sD-fae"/>
|
||||
<constraint firstItem="stw-i3-ikG" firstAttribute="top" secondItem="gJD-vd-WWu" secondAttribute="top" constant="20" symbolic="YES" id="wjD-TM-5CL"/>
|
||||
</constraints>
|
||||
</view>
|
||||
@ -1069,6 +1095,7 @@ Gw
|
||||
<outlet property="msxRegionButton" destination="LG6-mP-SeG" id="3a9-VG-6Wf"/>
|
||||
<outlet property="oricDiskInterfaceButton" destination="fYL-p6-wyn" id="aAt-wM-hRZ"/>
|
||||
<outlet property="oricModelTypeButton" destination="ENP-hI-BVZ" id="n9i-Ym-miE"/>
|
||||
<outlet property="pcSpeedButton" destination="Ci6-TG-5tW" id="34Y-5H-5dN"/>
|
||||
<outlet property="pcVideoAdaptorButton" destination="stw-i3-ikG" id="6VO-Q9-4kV"/>
|
||||
<outlet property="spectrumModelTypeButton" destination="gFZ-d4-WFv" id="tdX-Cv-Swe"/>
|
||||
<outlet property="vic20HasC1540Button" destination="Lrf-gL-6EI" id="21g-dJ-mOo"/>
|
||||
|
@ -65,6 +65,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
|
||||
// MARK: - PC compatible properties
|
||||
@IBOutlet var pcVideoAdaptorButton: NSPopUpButton!
|
||||
@IBOutlet var pcSpeedButton: NSPopUpButton!
|
||||
|
||||
// MARK: - Spectrum properties
|
||||
@IBOutlet var spectrumModelTypeButton: NSPopUpButton!
|
||||
@ -155,6 +156,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
|
||||
// PC settings
|
||||
pcVideoAdaptorButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.pcVideoAdaptor"))
|
||||
pcSpeedButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.pcSpeed"))
|
||||
|
||||
// Spectrum settings
|
||||
spectrumModelTypeButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.spectrumModel"))
|
||||
@ -224,6 +226,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
|
||||
// PC settings
|
||||
standardUserDefaults.set(pcVideoAdaptorButton.selectedTag(), forKey: "new.pcVideoAdaptor")
|
||||
standardUserDefaults.set(pcSpeedButton.selectedTag(), forKey: "new.pcSpeed")
|
||||
|
||||
// Spectrum settings
|
||||
standardUserDefaults.set(spectrumModelTypeButton.selectedTag(), forKey: "new.spectrumModel")
|
||||
@ -416,7 +419,12 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
case 1: videoAdaptor = .CGA
|
||||
default: break
|
||||
}
|
||||
return CSStaticAnalyser(pcCompatibleModel: .turboXT, videoAdaptor: videoAdaptor)
|
||||
var speed: CSPCCompatibleSpeed = .original
|
||||
switch pcSpeedButton.selectedTag() {
|
||||
case 80286: speed = .turbo
|
||||
default: break
|
||||
}
|
||||
return CSStaticAnalyser(pcCompatibleSpeed: speed, videoAdaptor: videoAdaptor)
|
||||
|
||||
case "spectrum":
|
||||
var model: CSMachineSpectrumModel = .plus2a
|
||||
|
Loading…
Reference in New Issue
Block a user