mirror of
https://github.com/TomHarte/CLK.git
synced 2024-09-30 22:56:03 +00:00
Merge branch 'master' into FurtherSCC
This commit is contained in:
commit
15bc18b64f
@ -12,45 +12,56 @@
|
||||
|
||||
using namespace GI::AY38910;
|
||||
|
||||
AY38910::AY38910(Concurrency::DeferringAsyncTaskQueue &task_queue) : task_queue_(task_queue) {
|
||||
// set up envelope lookup tables
|
||||
AY38910::AY38910(Personality personality, Concurrency::DeferringAsyncTaskQueue &task_queue) : task_queue_(task_queue) {
|
||||
// Don't use the low bit of the envelope position if this is an AY.
|
||||
envelope_position_mask_ |= personality == Personality::AY38910;
|
||||
|
||||
// Set up envelope lookup tables.
|
||||
for(int c = 0; c < 16; c++) {
|
||||
for(int p = 0; p < 32; p++) {
|
||||
for(int p = 0; p < 64; p++) {
|
||||
switch(c) {
|
||||
case 0: case 1: case 2: case 3: case 9:
|
||||
envelope_shapes_[c][p] = (p < 16) ? (p^0xf) : 0;
|
||||
envelope_overflow_masks_[c] = 0x1f;
|
||||
/* Envelope: \____ */
|
||||
envelope_shapes_[c][p] = (p < 32) ? (p^0x1f) : 0;
|
||||
envelope_overflow_masks_[c] = 0x3f;
|
||||
break;
|
||||
case 4: case 5: case 6: case 7: case 15:
|
||||
envelope_shapes_[c][p] = (p < 16) ? p : 0;
|
||||
envelope_overflow_masks_[c] = 0x1f;
|
||||
/* Envelope: /____ */
|
||||
envelope_shapes_[c][p] = (p < 32) ? p : 0;
|
||||
envelope_overflow_masks_[c] = 0x3f;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
envelope_shapes_[c][p] = (p & 0xf) ^ 0xf;
|
||||
/* Envelope: \\\\\\\\ */
|
||||
envelope_shapes_[c][p] = (p & 0x1f) ^ 0x1f;
|
||||
envelope_overflow_masks_[c] = 0x00;
|
||||
break;
|
||||
case 12:
|
||||
envelope_shapes_[c][p] = (p & 0xf);
|
||||
/* Envelope: //////// */
|
||||
envelope_shapes_[c][p] = (p & 0x1f);
|
||||
envelope_overflow_masks_[c] = 0x00;
|
||||
break;
|
||||
|
||||
case 10:
|
||||
envelope_shapes_[c][p] = (p & 0xf) ^ ((p < 16) ? 0xf : 0x0);
|
||||
/* Envelope: \/\/\/\/ */
|
||||
envelope_shapes_[c][p] = (p & 0x1f) ^ ((p < 32) ? 0x1f : 0x0);
|
||||
envelope_overflow_masks_[c] = 0x00;
|
||||
break;
|
||||
case 14:
|
||||
envelope_shapes_[c][p] = (p & 0xf) ^ ((p < 16) ? 0x0 : 0xf);
|
||||
/* Envelope: /\/\/\/\ */
|
||||
envelope_shapes_[c][p] = (p & 0x1f) ^ ((p < 32) ? 0x0 : 0x1f);
|
||||
envelope_overflow_masks_[c] = 0x00;
|
||||
break;
|
||||
|
||||
case 11:
|
||||
envelope_shapes_[c][p] = (p < 16) ? (p^0xf) : 0xf;
|
||||
envelope_overflow_masks_[c] = 0x1f;
|
||||
/* Envelope: \------ (if - is high) */
|
||||
envelope_shapes_[c][p] = (p < 32) ? (p^0x1f) : 0x1f;
|
||||
envelope_overflow_masks_[c] = 0x3f;
|
||||
break;
|
||||
case 13:
|
||||
envelope_shapes_[c][p] = (p < 16) ? p : 0xf;
|
||||
envelope_overflow_masks_[c] = 0x1f;
|
||||
/* Envelope: /------- */
|
||||
envelope_shapes_[c][p] = (p < 32) ? p : 0x1f;
|
||||
envelope_overflow_masks_[c] = 0x3f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -63,16 +74,24 @@ void AY38910::set_sample_volume_range(std::int16_t range) {
|
||||
// set up volume lookup table
|
||||
const float max_volume = static_cast<float>(range) / 3.0f; // As there are three channels.
|
||||
const float root_two = sqrtf(2.0f);
|
||||
for(int v = 0; v < 16; v++) {
|
||||
volumes_[v] = static_cast<int>(max_volume / powf(root_two, static_cast<float>(v ^ 0xf)));
|
||||
for(int v = 0; v < 32; v++) {
|
||||
volumes_[v] = int(max_volume / powf(root_two, float(v ^ 0x1f) / 2.0f));
|
||||
}
|
||||
volumes_[0] = 0;
|
||||
evaluate_output_volume();
|
||||
}
|
||||
|
||||
void AY38910::get_samples(std::size_t number_of_samples, int16_t *target) {
|
||||
// Note on structure below: the real AY has a built-in divider of 8
|
||||
// prior to applying its tone and noise dividers. But the YM fills the
|
||||
// same total periods for noise and tone with double-precision envelopes.
|
||||
// Therefore this class implements a divider of 4 and doubles the tone
|
||||
// and noise periods. The envelope ticks along at the divide-by-four rate,
|
||||
// but if this is an AY rather than a YM then its lowest bit is forced to 1,
|
||||
// matching the YM datasheet's depiction of envelope level 31 as equal to
|
||||
// programmatic volume 15, envelope level 29 as equal to programmatic 14, etc.
|
||||
|
||||
std::size_t c = 0;
|
||||
while((master_divider_&7) && c < number_of_samples) {
|
||||
while((master_divider_&3) && c < number_of_samples) {
|
||||
target[c] = output_volume_;
|
||||
master_divider_++;
|
||||
c++;
|
||||
@ -83,49 +102,49 @@ void AY38910::get_samples(std::size_t number_of_samples, int16_t *target) {
|
||||
if(tone_counters_[c]) tone_counters_[c]--;\
|
||||
else {\
|
||||
tone_outputs_[c] ^= 1;\
|
||||
tone_counters_[c] = tone_periods_[c];\
|
||||
tone_counters_[c] = tone_periods_[c] << 1;\
|
||||
}
|
||||
|
||||
// update the tone channels
|
||||
// Update the tone channels.
|
||||
step_channel(0);
|
||||
step_channel(1);
|
||||
step_channel(2);
|
||||
|
||||
#undef step_channel
|
||||
|
||||
// ... the noise generator. This recomputes the new bit repeatedly but harmlessly, only shifting
|
||||
// Update the noise generator. This recomputes the new bit repeatedly but harmlessly, only shifting
|
||||
// it into the official 17 upon divider underflow.
|
||||
if(noise_counter_) noise_counter_--;
|
||||
else {
|
||||
noise_counter_ = noise_period_;
|
||||
noise_counter_ = noise_period_ << 1; // To cover the double resolution of envelopes.
|
||||
noise_output_ ^= noise_shift_register_&1;
|
||||
noise_shift_register_ |= ((noise_shift_register_ ^ (noise_shift_register_ >> 3))&1) << 17;
|
||||
noise_shift_register_ >>= 1;
|
||||
}
|
||||
|
||||
// ... and the envelope generator. Table based for pattern lookup, with a 'refill' step: a way of
|
||||
// implementing non-repeating patterns by locking them to table position 0x1f.
|
||||
// Update the envelope generator. Table based for pattern lookup, with a 'refill' step: a way of
|
||||
// implementing non-repeating patterns by locking them to the final table position.
|
||||
if(envelope_divider_) envelope_divider_--;
|
||||
else {
|
||||
envelope_divider_ = envelope_period_;
|
||||
envelope_position_ ++;
|
||||
if(envelope_position_ == 32) envelope_position_ = envelope_overflow_masks_[output_registers_[13]];
|
||||
if(envelope_position_ == 64) envelope_position_ = envelope_overflow_masks_[output_registers_[13]];
|
||||
}
|
||||
|
||||
evaluate_output_volume();
|
||||
|
||||
for(int ic = 0; ic < 8 && c < number_of_samples; ic++) {
|
||||
for(int ic = 0; ic < 4 && c < number_of_samples; ic++) {
|
||||
target[c] = output_volume_;
|
||||
c++;
|
||||
master_divider_++;
|
||||
}
|
||||
}
|
||||
|
||||
master_divider_ &= 7;
|
||||
master_divider_ &= 3;
|
||||
}
|
||||
|
||||
void AY38910::evaluate_output_volume() {
|
||||
int envelope_volume = envelope_shapes_[output_registers_[13]][envelope_position_];
|
||||
int envelope_volume = envelope_shapes_[output_registers_[13]][envelope_position_ | envelope_position_mask_];
|
||||
|
||||
// The output level for a channel is:
|
||||
// 1 if neither tone nor noise is enabled;
|
||||
@ -142,9 +161,10 @@ void AY38910::evaluate_output_volume() {
|
||||
};
|
||||
#undef level
|
||||
|
||||
// Channel volume is a simple selection: if the bit at 0x10 is set, use the envelope volume; otherwise use the lower four bits
|
||||
// Channel volume is a simple selection: if the bit at 0x10 is set, use the envelope volume; otherwise use the lower four bits,
|
||||
// mapped to the range 1–31 in case this is a YM.
|
||||
#define channel_volume(c) \
|
||||
((output_registers_[c] >> 4)&1) * envelope_volume + (((output_registers_[c] >> 4)&1)^1) * (output_registers_[c]&0xf)
|
||||
((output_registers_[c] >> 4)&1) * envelope_volume + (((output_registers_[c] >> 4)&1)^1) * (((output_registers_[c]&0xf) << 1) + 1)
|
||||
|
||||
const int volumes[3] = {
|
||||
channel_volume(8),
|
||||
|
@ -52,6 +52,13 @@ enum ControlLines {
|
||||
BDIR = (1 << 2)
|
||||
};
|
||||
|
||||
enum class Personality {
|
||||
/// Provides 16 volume levels to envelopes.
|
||||
AY38910,
|
||||
/// Provides 32 volume levels to envelopes.
|
||||
YM2149F
|
||||
};
|
||||
|
||||
/*!
|
||||
Provides emulation of an AY-3-8910 / YM2149, which is a three-channel sound chip with a
|
||||
noise generator and a volume envelope generator, which also provides two bidirectional
|
||||
@ -60,7 +67,7 @@ enum ControlLines {
|
||||
class AY38910: public ::Outputs::Speaker::SampleSource {
|
||||
public:
|
||||
/// Creates a new AY38910.
|
||||
AY38910(Concurrency::DeferringAsyncTaskQueue &task_queue);
|
||||
AY38910(Personality, Concurrency::DeferringAsyncTaskQueue &);
|
||||
|
||||
/// Sets the value the AY would read from its data lines if it were not outputting.
|
||||
void set_data_input(uint8_t r);
|
||||
@ -109,11 +116,11 @@ class AY38910: public ::Outputs::Speaker::SampleSource {
|
||||
|
||||
int envelope_period_ = 0;
|
||||
int envelope_divider_ = 0;
|
||||
int envelope_position_ = 0;
|
||||
int envelope_shapes_[16][32];
|
||||
int envelope_position_ = 0, envelope_position_mask_ = 0;
|
||||
int envelope_shapes_[16][64];
|
||||
int envelope_overflow_masks_[16];
|
||||
|
||||
int volumes_[16];
|
||||
int volumes_[32];
|
||||
|
||||
enum ControlState {
|
||||
Inactive,
|
||||
|
@ -124,7 +124,7 @@ class InterruptTimer {
|
||||
class AYDeferrer {
|
||||
public:
|
||||
/// Constructs a new AY instance and sets its clock rate.
|
||||
AYDeferrer() : ay_(audio_queue_), speaker_(ay_) {
|
||||
AYDeferrer() : ay_(GI::AY38910::Personality::AY38910, audio_queue_), speaker_(ay_) {
|
||||
speaker_.set_input_rate(1000000);
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ class ConcreteMachine:
|
||||
mc68000_(*this),
|
||||
keyboard_acia_(Cycles(500000)),
|
||||
midi_acia_(Cycles(500000)),
|
||||
ay_(audio_queue_),
|
||||
ay_(GI::AY38910::Personality::YM2149F, audio_queue_),
|
||||
speaker_(ay_),
|
||||
ikbd_(keyboard_acia_->transmit, keyboard_acia_->receive) {
|
||||
set_clock_rate(CLOCK_RATE);
|
||||
|
@ -123,7 +123,7 @@ class ConcreteMachine:
|
||||
z80_(*this),
|
||||
vdp_(TI::TMS::TMS9918A),
|
||||
sn76489_(TI::SN76489::Personality::SN76489, audio_queue_, sn76489_divider),
|
||||
ay_(audio_queue_),
|
||||
ay_(GI::AY38910::Personality::AY38910, audio_queue_),
|
||||
mixer_(sn76489_, ay_),
|
||||
speaker_(mixer_) {
|
||||
speaker_.set_input_rate(3579545.0f / static_cast<float>(sn76489_divider));
|
||||
|
@ -154,7 +154,7 @@ class ConcreteMachine:
|
||||
z80_(*this),
|
||||
vdp_(TI::TMS::TMS9918A),
|
||||
i8255_(i8255_port_handler_),
|
||||
ay_(audio_queue_),
|
||||
ay_(GI::AY38910::Personality::AY38910, audio_queue_),
|
||||
audio_toggle_(audio_queue_),
|
||||
scc_(audio_queue_),
|
||||
mixer_(ay_, audio_toggle_, scc_),
|
||||
|
@ -213,7 +213,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
ConcreteMachine(const Analyser::Static::Oric::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||
m6502_(*this),
|
||||
video_output_(ram_),
|
||||
ay8910_(audio_queue_),
|
||||
ay8910_(GI::AY38910::Personality::AY38910, audio_queue_),
|
||||
speaker_(ay8910_),
|
||||
via_port_handler_(audio_queue_, ay8910_, speaker_, tape_player_, keyboard_),
|
||||
via_(via_port_handler_),
|
||||
|
@ -69,7 +69,7 @@ template<bool is_zx81> class ConcreteMachine:
|
||||
ConcreteMachine(const Analyser::Static::ZX8081::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||
z80_(*this),
|
||||
tape_player_(ZX8081ClockRate),
|
||||
ay_(audio_queue_),
|
||||
ay_(GI::AY38910::Personality::AY38910, audio_queue_),
|
||||
speaker_(ay_) {
|
||||
set_clock_rate(ZX8081ClockRate);
|
||||
speaker_.set_input_rate(static_cast<float>(ZX8081ClockRate) / 2.0f);
|
||||
|
@ -67,7 +67,7 @@
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Release"
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
enableASanStackUseAfterReturn = "YES"
|
||||
|
@ -23,6 +23,10 @@ typedef NS_ENUM(NSInteger, CSMachineAppleIIDiskController) {
|
||||
CSMachineAppleIIDiskControllerThirteenSector
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSMachineAtariSTModel) {
|
||||
CSMachineAtariSTModel512k,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSMachineCPCModel) {
|
||||
CSMachineCPCModel464,
|
||||
CSMachineCPCModel664,
|
||||
@ -77,6 +81,7 @@ typedef int Kilobytes;
|
||||
- (instancetype)initWithZX81MemorySize:(Kilobytes)memorySize;
|
||||
- (instancetype)initWithAppleIIModel:(CSMachineAppleIIModel)model diskController:(CSMachineAppleIIDiskController)diskController;
|
||||
- (instancetype)initWithMacintoshModel:(CSMachineMacintoshModel)model;
|
||||
- (instancetype)initWithAtariSTModel:(CSMachineAtariSTModel)model;
|
||||
|
||||
@property(nonatomic, readonly) NSString *optionsPanelNibName;
|
||||
@property(nonatomic, readonly) NSString *displayName;
|
||||
|
@ -212,6 +212,17 @@ static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(K
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithAtariSTModel:(CSMachineAtariSTModel)model {
|
||||
self = [super init];
|
||||
if(self) {
|
||||
using Target = Analyser::Static::Macintosh::Target;
|
||||
std::unique_ptr<Target> target(new Target);
|
||||
target->machine = Analyser::Machine::AtariST;
|
||||
_targets.push_back(std::move(target));
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)optionsPanelNibName {
|
||||
switch(_targets.front()->machine) {
|
||||
case Analyser::Machine::AmstradCPC: return @"CompositeOptions";
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="15702" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15702"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@ -17,14 +17,14 @@
|
||||
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" titleVisibility="hidden" id="QvC-M9-y7g">
|
||||
<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="650" height="205"/>
|
||||
<rect key="contentRect" x="196" y="240" width="720" height="205"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="900"/>
|
||||
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="650" height="205"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="720" height="205"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="hKn-1l-OSN">
|
||||
<rect key="frame" x="549" y="13" width="87" height="32"/>
|
||||
<rect key="frame" x="619" y="13" width="87" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Choose" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="MnM-xo-4Qa">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -37,7 +37,7 @@ DQ
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JQy-Cj-AOK">
|
||||
<rect key="frame" x="468" y="13" width="82" height="32"/>
|
||||
<rect key="frame" x="538" y="13" width="82" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="sub-rB-Req">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -59,7 +59,7 @@ Gw
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<tabView translatesAutoresizingMaskIntoConstraints="NO" id="VUb-QG-x7c">
|
||||
<rect key="frame" x="13" y="51" width="624" height="140"/>
|
||||
<rect key="frame" x="13" y="51" width="694" height="140"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<tabViewItems>
|
||||
<tabViewItem label="Apple II" identifier="appleii" id="P59-QG-LOa">
|
||||
@ -87,7 +87,7 @@ Gw
|
||||
<rect key="frame" x="65" y="67" width="116" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Apple II" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="VBQ-JG-AeM" id="U6V-us-O2F">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<menu key="menu" id="esp-ir-7iH">
|
||||
<items>
|
||||
<menuItem title="Apple II" state="on" id="VBQ-JG-AeM"/>
|
||||
@ -102,7 +102,7 @@ Gw
|
||||
<rect key="frame" x="115" y="36" width="133" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Sixteen Sector" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="16" imageScaling="proportionallyDown" inset="2" selectedItem="QaV-Yr-k9o" id="8BT-RV-2Nm">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<menu key="menu" id="udQ-NK-FG8">
|
||||
<items>
|
||||
<menuItem title="Sixteen Sector" state="on" tag="16" id="QaV-Yr-k9o"/>
|
||||
@ -130,14 +130,14 @@ Gw
|
||||
</tabViewItem>
|
||||
<tabViewItem label="Amstrad CPC" identifier="cpc" id="JmB-OF-xcM">
|
||||
<view key="view" id="5zS-Nj-Ynx">
|
||||
<rect key="frame" x="10" y="33" width="604" height="94"/>
|
||||
<rect key="frame" x="10" y="33" width="674" height="94"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="00d-sg-Krh">
|
||||
<rect key="frame" x="65" y="67" width="95" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="CPC6128" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="6128" imageScaling="proportionallyDown" inset="2" selectedItem="klh-ZE-Agp" id="hVJ-h6-iea">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<menu key="menu" id="r3D-C2-Ruq">
|
||||
<items>
|
||||
<menuItem title="CPC464" tag="464" id="5kZ-XF-RFl"/>
|
||||
@ -166,6 +166,27 @@ Gw
|
||||
</constraints>
|
||||
</view>
|
||||
</tabViewItem>
|
||||
<tabViewItem label="Atari ST" identifier="atarist" id="a6Y-mx-yFn">
|
||||
<view key="view" id="nnv-Wi-7hc">
|
||||
<rect key="frame" x="10" y="33" width="674" height="94"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nLf-LI-nWO">
|
||||
<rect key="frame" x="15" y="75" width="644" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="At present only a 512k Atari ST is supported." id="gBA-ke-mur">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="nLf-LI-nWO" firstAttribute="leading" secondItem="nnv-Wi-7hc" secondAttribute="leading" constant="17" id="EIm-9Q-Rcj"/>
|
||||
<constraint firstItem="nLf-LI-nWO" firstAttribute="top" secondItem="nnv-Wi-7hc" secondAttribute="top" constant="3" id="f9w-Lj-5dn"/>
|
||||
<constraint firstAttribute="trailing" secondItem="nLf-LI-nWO" secondAttribute="trailing" constant="17" id="m3L-Nx-Aqz"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</tabViewItem>
|
||||
<tabViewItem label="Electron" identifier="electron" id="muc-z9-Vqc">
|
||||
<view key="view" id="SRc-2D-95G">
|
||||
<rect key="frame" x="10" y="33" width="604" height="94"/>
|
||||
@ -214,7 +235,7 @@ Gw
|
||||
<rect key="frame" x="65" y="67" width="74" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Plus" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="3" imageScaling="proportionallyDown" inset="2" selectedItem="R6T-hg-rOF" id="1Kb-Q2-BGM">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<menu key="menu" id="ofy-j9-YnU">
|
||||
<items>
|
||||
<menuItem title="512ke" tag="2" id="WCG-6u-ANQ"/>
|
||||
@ -250,7 +271,7 @@ Gw
|
||||
<rect key="frame" x="69" y="67" width="145" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="European (PAL)" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="xAh-Ch-tby" id="yR4-yv-Lvu">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<menu key="menu" id="BCR-rp-Kpc">
|
||||
<items>
|
||||
<menuItem title="European (PAL)" state="on" id="xAh-Ch-tby"/>
|
||||
@ -299,7 +320,7 @@ Gw
|
||||
<rect key="frame" x="65" y="67" width="106" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Oric-1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="jGN-1a-biF" id="Jll-EJ-cMr">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<menu key="menu" id="E9d-fH-Eak">
|
||||
<items>
|
||||
<menuItem title="Oric-1" state="on" id="jGN-1a-biF"/>
|
||||
@ -313,7 +334,7 @@ Gw
|
||||
<rect key="frame" x="111" y="36" width="97" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="None" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="XhK-Jh-oTW" id="aYb-m1-H9X">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<menu key="menu" id="hy3-Li-nlW">
|
||||
<items>
|
||||
<menuItem title="None" state="on" id="XhK-Jh-oTW"/>
|
||||
@ -356,7 +377,7 @@ Gw
|
||||
<rect key="frame" x="69" y="67" width="145" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="European (PAL)" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="45i-0n-gau" id="yi7-eo-I0q">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<menu key="menu" id="uCQ-9l-eBb">
|
||||
<items>
|
||||
<menuItem title="European (PAL)" state="on" id="45i-0n-gau"/>
|
||||
@ -372,7 +393,7 @@ Gw
|
||||
<rect key="frame" x="106" y="36" width="115" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Unexpanded" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="fOl-8Q-fsA" id="rH0-7T-pJE">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<menu key="menu" id="e9J-Ie-PjH">
|
||||
<items>
|
||||
<menuItem title="Unexpanded" state="on" id="fOl-8Q-fsA"/>
|
||||
@ -426,14 +447,14 @@ Gw
|
||||
</tabViewItem>
|
||||
<tabViewItem label="ZX80" identifier="zx80" id="tMH-kF-GUz">
|
||||
<view key="view" id="8hL-Vn-Hg0">
|
||||
<rect key="frame" x="10" y="33" width="554" height="94"/>
|
||||
<rect key="frame" x="10" y="33" width="604" height="94"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="I1a-Eu-5UB">
|
||||
<rect key="frame" x="106" y="67" width="115" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Unexpanded" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="4Sa-jR-xOd" id="B8M-do-Yod">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<menu key="menu" id="Of4-DY-mE5">
|
||||
<items>
|
||||
<menuItem title="Unexpanded" state="on" id="4Sa-jR-xOd"/>
|
||||
@ -474,14 +495,14 @@ Gw
|
||||
</tabViewItem>
|
||||
<tabViewItem label="ZX81" identifier="zx81" id="Wnn-nQ-gZ6">
|
||||
<view key="view" id="bmd-gL-gzT">
|
||||
<rect key="frame" x="10" y="33" width="554" height="94"/>
|
||||
<rect key="frame" x="10" y="33" width="604" height="94"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="5aO-UX-HnX">
|
||||
<rect key="frame" x="106" y="67" width="115" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Unexpanded" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="7QC-Ij-hES" id="d3W-Gl-3Mf">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<menu key="menu" id="mua-Lp-9wl">
|
||||
<items>
|
||||
<menuItem title="Unexpanded" state="on" id="7QC-Ij-hES"/>
|
||||
|
@ -158,6 +158,9 @@ class MachinePicker: NSObject {
|
||||
|
||||
return CSStaticAnalyser(appleIIModel: model, diskController: diskController)
|
||||
|
||||
case "atarist":
|
||||
return CSStaticAnalyser(atariSTModel: .model512k)
|
||||
|
||||
case "cpc":
|
||||
switch cpcModelTypeButton!.selectedItem!.tag {
|
||||
case 464: return CSStaticAnalyser(amstradCPCModel: .model464)
|
||||
|
15
README.md
15
README.md
@ -59,9 +59,6 @@ If your machine has a 4k monitor and a 96Khz audio output? Then you'll get a 4k
|
||||
|![Amstrad text, with a classic 1:1 pixel emulation](READMEImages/NaiveCPC.png)|![Amstrad text, with correct aspect ratio and subject to a lowpass filter](READMEImages/FilteredCPC.png)|
|
||||
|![The Amstrad CPC version of Stormlord, with a classic 1:1 pixel emulation](READMEImages/NaiveCPCStormlord.png)|![The Amstrad CPC version of Stormlord, with correct aspect ratio and subject to a lowpass filter](READMEImages/CPCStormlord.png)|
|
||||
|
||||
<img src="READMEImages/ReptonInterlaced.gif" height=400 alt="Repton title screen, interlaced"><img src="READMEImages/AppleIIPrinceOfPersia.png" height=400 alt="Apple IIe Prince of Persia">
|
||||
<img src="READMEImages/MusicWorks.png" height=400 alt="Apple Macintosh MusicWorks">
|
||||
|
||||
## Low Latency
|
||||
|
||||
The display produced is an emulated CRT, with phosphor decay. Therefore if you have a 140Hz monitor it can produce 140 distinct frames per second. Latency is dictated by the output hardware, not the emulated machine.
|
||||
@ -74,10 +71,8 @@ A corollary of emulating the continuous nature CRT, not merely performing end-of
|
||||
|
||||
Cycle-accurate emulation for the supported target machines is fairly trite; this emulator seeks to follow that precedent. All emulation logic is written in C++ for explicit control over costs but, where a conflict arises, the presumption is towards clarity and simplicity of code. This emulator is willing to spend the processing resources available on modern hardware.
|
||||
|
||||
Self-ratings:
|
||||
* the Electron, Oric and Vic-20 are pretty much perfect;
|
||||
* the ZX80, ZX81, ColecoVision, MSX 1 and Apple IIs are very strong;
|
||||
* the Master System is almost perfect, access windows and CRAM dots and all the rest, but further work is required on some small aspects of behaviour;
|
||||
* the Amstrad CPC has known accuracy deficiencies in its 8272 and 6845;
|
||||
* the Atari 2600 has some known accuracy deficiencies in its TIA;
|
||||
* the C-1540(/1) is locked in reading mode and doesn't yet support writing.
|
||||
## Additional Screenshots
|
||||
|
||||
<img src="READMEImages/AppleIIPrinceOfPersia.png" height=400 alt="Apple IIe Prince of Persia">
|
||||
<img src="READMEImages/MusicWorks.png" height=400 alt="Apple Macintosh MusicWorks">
|
||||
<img src="READMEImages/STStuntCarRacer.png" height=400 alt="Atari ST Stunt Car Racer">
|
||||
|
BIN
READMEImages/STStuntCarRacer.png
Normal file
BIN
READMEImages/STStuntCarRacer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 55 KiB |
Loading…
Reference in New Issue
Block a user