mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-23 03:32:32 +00:00
Merge branch 'master' into HiRes
This commit is contained in:
commit
22389a5d2d
@ -259,7 +259,7 @@ template <class T> class MOS6560 {
|
|||||||
if(this_state_ != output_state_) {
|
if(this_state_ != output_state_) {
|
||||||
switch(output_state_) {
|
switch(output_state_) {
|
||||||
case State::Sync: crt_->output_sync(cycles_in_state_ * 4); break;
|
case State::Sync: crt_->output_sync(cycles_in_state_ * 4); break;
|
||||||
case State::ColourBurst: crt_->output_colour_burst(cycles_in_state_ * 4, (is_odd_frame_ || is_odd_line_) ? 128 : 0, 0); break;
|
case State::ColourBurst: crt_->output_colour_burst(cycles_in_state_ * 4, (is_odd_frame_ || is_odd_line_) ? 128 : 0); break;
|
||||||
case State::Border: output_border(cycles_in_state_ * 4); break;
|
case State::Border: output_border(cycles_in_state_ * 4); break;
|
||||||
case State::Pixels: crt_->output_data(cycles_in_state_ * 4, 1); break;
|
case State::Pixels: crt_->output_data(cycles_in_state_ * 4, 1); break;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,9 @@ Machine::Machine() :
|
|||||||
nmi_is_enabled_(false),
|
nmi_is_enabled_(false),
|
||||||
tape_player_(ZX8081ClockRate),
|
tape_player_(ZX8081ClockRate),
|
||||||
use_fast_tape_hack_(false),
|
use_fast_tape_hack_(false),
|
||||||
tape_advance_delay_(0) {
|
tape_advance_delay_(0),
|
||||||
|
tape_is_automatically_playing_(false),
|
||||||
|
tape_is_playing_(false) {
|
||||||
set_clock_rate(ZX8081ClockRate);
|
set_clock_rate(ZX8081ClockRate);
|
||||||
tape_player_.set_motor_control(true);
|
tape_player_.set_motor_control(true);
|
||||||
clear_all_keys();
|
clear_all_keys();
|
||||||
@ -55,7 +57,7 @@ int Machine::perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
|
|||||||
|
|
||||||
if(is_zx81_) horizontal_counter_ %= 207;
|
if(is_zx81_) horizontal_counter_ %= 207;
|
||||||
if(!tape_advance_delay_) {
|
if(!tape_advance_delay_) {
|
||||||
tape_player_.run_for_cycles(cycle.length);
|
if(tape_is_automatically_playing_ || tape_is_playing_) tape_player_.run_for_cycles(cycle.length);
|
||||||
} else {
|
} else {
|
||||||
tape_advance_delay_ = std::max(tape_advance_delay_ - cycle.length, 0);
|
tape_advance_delay_ = std::max(tape_advance_delay_ - cycle.length, 0);
|
||||||
}
|
}
|
||||||
@ -147,6 +149,9 @@ int Machine::perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
|
|||||||
tape_player_.get_tape()->seek(time);
|
tape_player_.get_tape()->seek(time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for automatic tape control.
|
||||||
|
tape_is_automatically_playing_ = use_automatic_tape_motor_control_ && (address >= automatic_tape_motor_start_address_) && (address < automatic_tape_motor_end_address_);
|
||||||
is_opcode_read = true;
|
is_opcode_read = true;
|
||||||
|
|
||||||
case CPU::Z80::PartialMachineCycle::Read:
|
case CPU::Z80::PartialMachineCycle::Read:
|
||||||
@ -209,12 +214,16 @@ void Machine::configure_as_target(const StaticAnalyser::Target &target) {
|
|||||||
tape_return_address_ = 0x380;
|
tape_return_address_ = 0x380;
|
||||||
vsync_start_cycle_ = 16;
|
vsync_start_cycle_ = 16;
|
||||||
vsync_end_cycle_ = 32;
|
vsync_end_cycle_ = 32;
|
||||||
|
automatic_tape_motor_start_address_ = 0x0340;
|
||||||
|
automatic_tape_motor_end_address_ = 0x03c3;
|
||||||
} else {
|
} else {
|
||||||
rom_ = zx80_rom_;
|
rom_ = zx80_rom_;
|
||||||
tape_trap_address_ = 0x220;
|
tape_trap_address_ = 0x220;
|
||||||
tape_return_address_ = 0x248;
|
tape_return_address_ = 0x248;
|
||||||
vsync_start_cycle_ = 13;
|
vsync_start_cycle_ = 13;
|
||||||
vsync_end_cycle_ = 33;
|
vsync_end_cycle_ = 33;
|
||||||
|
automatic_tape_motor_start_address_ = 0x0206;
|
||||||
|
automatic_tape_motor_end_address_ = 0x024d;
|
||||||
}
|
}
|
||||||
rom_mask_ = (uint16_t)(rom_.size() - 1);
|
rom_mask_ = (uint16_t)(rom_.size() - 1);
|
||||||
|
|
||||||
|
@ -63,11 +63,18 @@ class Machine:
|
|||||||
void clear_all_keys();
|
void clear_all_keys();
|
||||||
|
|
||||||
inline void set_use_fast_tape_hack(bool activate) { use_fast_tape_hack_ = activate; }
|
inline void set_use_fast_tape_hack(bool activate) { use_fast_tape_hack_ = activate; }
|
||||||
|
inline void set_use_automatic_tape_motor_control(bool enabled) {
|
||||||
|
use_automatic_tape_motor_control_ = enabled;
|
||||||
|
if(!enabled) tape_is_automatically_playing_ = false;
|
||||||
|
}
|
||||||
|
inline void set_tape_is_playing(bool is_playing) { tape_is_playing_ = is_playing; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Video> video_;
|
std::shared_ptr<Video> video_;
|
||||||
std::vector<uint8_t> zx81_rom_, zx80_rom_;
|
std::vector<uint8_t> zx81_rom_, zx80_rom_;
|
||||||
|
|
||||||
uint16_t tape_trap_address_, tape_return_address_;
|
uint16_t tape_trap_address_, tape_return_address_;
|
||||||
|
uint16_t automatic_tape_motor_start_address_, automatic_tape_motor_end_address_;
|
||||||
|
|
||||||
std::vector<uint8_t> ram_;
|
std::vector<uint8_t> ram_;
|
||||||
uint16_t ram_mask_, ram_base_;
|
uint16_t ram_mask_, ram_base_;
|
||||||
@ -94,6 +101,8 @@ class Machine:
|
|||||||
uint8_t latched_video_byte_;
|
uint8_t latched_video_byte_;
|
||||||
|
|
||||||
bool use_fast_tape_hack_;
|
bool use_fast_tape_hack_;
|
||||||
|
bool use_automatic_tape_motor_control_;
|
||||||
|
bool tape_is_playing_, tape_is_automatically_playing_;
|
||||||
int tape_advance_delay_;
|
int tape_advance_delay_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -96,6 +96,7 @@
|
|||||||
4B924E991E74D22700B76AF1 /* AtariStaticAnalyserTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B924E981E74D22700B76AF1 /* AtariStaticAnalyserTests.mm */; };
|
4B924E991E74D22700B76AF1 /* AtariStaticAnalyserTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B924E981E74D22700B76AF1 /* AtariStaticAnalyserTests.mm */; };
|
||||||
4B9252CE1E74D28200B76AF1 /* Atari ROMs in Resources */ = {isa = PBXBuildFile; fileRef = 4B9252CD1E74D28200B76AF1 /* Atari ROMs */; };
|
4B9252CE1E74D28200B76AF1 /* Atari ROMs in Resources */ = {isa = PBXBuildFile; fileRef = 4B9252CD1E74D28200B76AF1 /* Atari ROMs */; };
|
||||||
4B92EACA1B7C112B00246143 /* 6502TimingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */; };
|
4B92EACA1B7C112B00246143 /* 6502TimingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */; };
|
||||||
|
4B95FA9D1F11893B0008E395 /* ZX8081OptionsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B95FA9C1F11893B0008E395 /* ZX8081OptionsPanel.swift */; };
|
||||||
4B96F7221D75119A0058BB2D /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B96F7201D75119A0058BB2D /* Tape.cpp */; };
|
4B96F7221D75119A0058BB2D /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B96F7201D75119A0058BB2D /* Tape.cpp */; };
|
||||||
4B9CCDA11DA279CA0098B625 /* Vic20OptionsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B9CCDA01DA279CA0098B625 /* Vic20OptionsPanel.swift */; };
|
4B9CCDA11DA279CA0098B625 /* Vic20OptionsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B9CCDA01DA279CA0098B625 /* Vic20OptionsPanel.swift */; };
|
||||||
4BA0F68E1EEA0E8400E9489E /* ZX8081.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA0F68C1EEA0E8400E9489E /* ZX8081.cpp */; };
|
4BA0F68E1EEA0E8400E9489E /* ZX8081.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA0F68C1EEA0E8400E9489E /* ZX8081.cpp */; };
|
||||||
@ -609,6 +610,7 @@
|
|||||||
4B924E981E74D22700B76AF1 /* AtariStaticAnalyserTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AtariStaticAnalyserTests.mm; sourceTree = "<group>"; };
|
4B924E981E74D22700B76AF1 /* AtariStaticAnalyserTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AtariStaticAnalyserTests.mm; sourceTree = "<group>"; };
|
||||||
4B9252CD1E74D28200B76AF1 /* Atari ROMs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "Atari ROMs"; sourceTree = "<group>"; };
|
4B9252CD1E74D28200B76AF1 /* Atari ROMs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "Atari ROMs"; sourceTree = "<group>"; };
|
||||||
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6502TimingTests.swift; sourceTree = "<group>"; };
|
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6502TimingTests.swift; sourceTree = "<group>"; };
|
||||||
|
4B95FA9C1F11893B0008E395 /* ZX8081OptionsPanel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZX8081OptionsPanel.swift; sourceTree = "<group>"; };
|
||||||
4B96F7201D75119A0058BB2D /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tape.cpp; path = ../../StaticAnalyser/Acorn/Tape.cpp; sourceTree = "<group>"; };
|
4B96F7201D75119A0058BB2D /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tape.cpp; path = ../../StaticAnalyser/Acorn/Tape.cpp; sourceTree = "<group>"; };
|
||||||
4B96F7211D75119A0058BB2D /* Tape.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Tape.hpp; path = ../../StaticAnalyser/Acorn/Tape.hpp; sourceTree = "<group>"; };
|
4B96F7211D75119A0058BB2D /* Tape.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Tape.hpp; path = ../../StaticAnalyser/Acorn/Tape.hpp; sourceTree = "<group>"; };
|
||||||
4B9CCDA01DA279CA0098B625 /* Vic20OptionsPanel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vic20OptionsPanel.swift; sourceTree = "<group>"; };
|
4B9CCDA01DA279CA0098B625 /* Vic20OptionsPanel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vic20OptionsPanel.swift; sourceTree = "<group>"; };
|
||||||
@ -1312,6 +1314,7 @@
|
|||||||
4B8FE2211DA19FB20090D3CE /* MachinePanel.swift */,
|
4B8FE2211DA19FB20090D3CE /* MachinePanel.swift */,
|
||||||
4B2A332E1DB86869002876E3 /* OricOptionsPanel.swift */,
|
4B2A332E1DB86869002876E3 /* OricOptionsPanel.swift */,
|
||||||
4B9CCDA01DA279CA0098B625 /* Vic20OptionsPanel.swift */,
|
4B9CCDA01DA279CA0098B625 /* Vic20OptionsPanel.swift */,
|
||||||
|
4B95FA9C1F11893B0008E395 /* ZX8081OptionsPanel.swift */,
|
||||||
4B8FE2131DA19D5F0090D3CE /* Atari2600Options.xib */,
|
4B8FE2131DA19D5F0090D3CE /* Atari2600Options.xib */,
|
||||||
4B8FE2171DA19D5F0090D3CE /* ElectronOptions.xib */,
|
4B8FE2171DA19D5F0090D3CE /* ElectronOptions.xib */,
|
||||||
4B8FE2151DA19D5F0090D3CE /* MachineDocument.xib */,
|
4B8FE2151DA19D5F0090D3CE /* MachineDocument.xib */,
|
||||||
@ -2566,6 +2569,7 @@
|
|||||||
4BD4A8CD1E077E8A0020D856 /* PCMSegment.cpp in Sources */,
|
4BD4A8CD1E077E8A0020D856 /* PCMSegment.cpp in Sources */,
|
||||||
4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */,
|
4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */,
|
||||||
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */,
|
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */,
|
||||||
|
4B95FA9D1F11893B0008E395 /* ZX8081OptionsPanel.swift in Sources */,
|
||||||
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */,
|
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */,
|
||||||
4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */,
|
4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */,
|
||||||
4BEA525E1DF33323007E74F2 /* Tape.cpp in Sources */,
|
4BEA525E1DF33323007E74F2 /* Tape.cpp in Sources */,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12120" systemVersion="16F73" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12121" systemVersion="16F73" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12120"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12121"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
@ -12,17 +12,17 @@
|
|||||||
</customObject>
|
</customObject>
|
||||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
<window title="Options" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="ota-g7-hOL" customClass="MachinePanel" customModule="Clock_Signal" customModuleProvider="target">
|
<window title="Options" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="ota-g7-hOL" customClass="ZX8081OptionsPanel" customModule="Clock_Signal" customModuleProvider="target">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" utility="YES" nonactivatingPanel="YES" HUD="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" utility="YES" nonactivatingPanel="YES" HUD="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
<rect key="contentRect" x="83" y="102" width="200" height="54"/>
|
<rect key="contentRect" x="83" y="102" width="261" height="100"/>
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="1366" height="768"/>
|
<rect key="screenRect" x="0.0" y="0.0" width="1366" height="768"/>
|
||||||
<view key="contentView" id="7Pv-WL-2Rq">
|
<view key="contentView" id="7Pv-WL-2Rq">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="200" height="54"/>
|
<rect key="frame" x="0.0" y="0.0" width="261" height="100"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<button translatesAutoresizingMaskIntoConstraints="NO" id="sBT-cU-h7s">
|
<button translatesAutoresizingMaskIntoConstraints="NO" id="sBT-cU-h7s">
|
||||||
<rect key="frame" x="18" y="18" width="164" height="18"/>
|
<rect key="frame" x="18" y="64" width="225" height="18"/>
|
||||||
<buttonCell key="cell" type="check" title="Load Tapes Quickly" bezelStyle="regularSquare" imagePosition="left" alignment="left" state="on" inset="2" id="w0l-ha-esm">
|
<buttonCell key="cell" type="check" title="Load Tapes Quickly" bezelStyle="regularSquare" imagePosition="left" alignment="left" state="on" inset="2" id="w0l-ha-esm">
|
||||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
@ -31,18 +31,46 @@
|
|||||||
<action selector="setFastLoading:" target="ota-g7-hOL" id="me0-h2-Ga5"/>
|
<action selector="setFastLoading:" target="ota-g7-hOL" id="me0-h2-Ga5"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
|
<button translatesAutoresizingMaskIntoConstraints="NO" id="qSb-72-6Os">
|
||||||
|
<rect key="frame" x="18" y="44" width="225" height="18"/>
|
||||||
|
<buttonCell key="cell" type="check" title="Control Tape Motor Automatically" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="CzC-YT-lgA">
|
||||||
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="setAutomaticTapeMotorConrol:" target="ota-g7-hOL" id="bpF-1P-tga"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="tkN-gI-RmT">
|
||||||
|
<rect key="frame" x="20" y="19" width="221" height="19"/>
|
||||||
|
<buttonCell key="cell" type="roundRect" title="Play Tape" bezelStyle="roundedRect" alignment="center" enabled="NO" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="cTq-f9-Gzx">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="cellTitle"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="playOrPauseTape:" target="ota-g7-hOL" id="O0K-pL-nOr"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
</subviews>
|
</subviews>
|
||||||
<constraints>
|
<constraints>
|
||||||
|
<constraint firstItem="qSb-72-6Os" firstAttribute="leading" secondItem="7Pv-WL-2Rq" secondAttribute="leading" constant="20" id="05p-Jn-ueX"/>
|
||||||
<constraint firstAttribute="trailing" secondItem="sBT-cU-h7s" secondAttribute="trailing" constant="20" id="79b-2A-2c7"/>
|
<constraint firstAttribute="trailing" secondItem="sBT-cU-h7s" secondAttribute="trailing" constant="20" id="79b-2A-2c7"/>
|
||||||
<constraint firstItem="sBT-cU-h7s" firstAttribute="top" secondItem="7Pv-WL-2Rq" secondAttribute="top" constant="20" id="E5m-wo-X92"/>
|
<constraint firstItem="sBT-cU-h7s" firstAttribute="top" secondItem="7Pv-WL-2Rq" secondAttribute="top" constant="20" id="E5m-wo-X92"/>
|
||||||
<constraint firstAttribute="bottom" secondItem="sBT-cU-h7s" secondAttribute="bottom" constant="20" id="eZe-Eu-INg"/>
|
<constraint firstItem="qSb-72-6Os" firstAttribute="top" secondItem="sBT-cU-h7s" secondAttribute="bottom" constant="6" id="WxD-kP-vwf"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="tkN-gI-RmT" secondAttribute="bottom" constant="20" id="Xnu-On-nOA"/>
|
||||||
|
<constraint firstItem="tkN-gI-RmT" firstAttribute="leading" secondItem="7Pv-WL-2Rq" secondAttribute="leading" constant="20" id="fHf-K0-PsU"/>
|
||||||
|
<constraint firstItem="tkN-gI-RmT" firstAttribute="top" secondItem="qSb-72-6Os" secondAttribute="bottom" constant="8" id="gLh-vE-Cqk"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="qSb-72-6Os" secondAttribute="trailing" constant="20" id="mQz-p8-aYf"/>
|
||||||
<constraint firstItem="sBT-cU-h7s" firstAttribute="leading" secondItem="7Pv-WL-2Rq" secondAttribute="leading" constant="20" id="nDy-Xc-Ug9"/>
|
<constraint firstItem="sBT-cU-h7s" firstAttribute="leading" secondItem="7Pv-WL-2Rq" secondAttribute="leading" constant="20" id="nDy-Xc-Ug9"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="tkN-gI-RmT" secondAttribute="trailing" constant="20" id="vgD-A3-m6T"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<connections>
|
<connections>
|
||||||
|
<outlet property="automaticTapeMotorControlButton" destination="qSb-72-6Os" id="bB6-FP-TKM"/>
|
||||||
<outlet property="fastLoadingButton" destination="sBT-cU-h7s" id="uWa-EB-mbd"/>
|
<outlet property="fastLoadingButton" destination="sBT-cU-h7s" id="uWa-EB-mbd"/>
|
||||||
|
<outlet property="playOrPauseTapeButton" destination="tkN-gI-RmT" id="UnJ-nb-3mv"/>
|
||||||
</connections>
|
</connections>
|
||||||
<point key="canvasLocation" x="-2" y="-8"/>
|
<point key="canvasLocation" x="28.5" y="15"/>
|
||||||
</window>
|
</window>
|
||||||
</objects>
|
</objects>
|
||||||
</document>
|
</document>
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#import "CSElectron.h"
|
#import "CSElectron.h"
|
||||||
#import "CSOric.h"
|
#import "CSOric.h"
|
||||||
#import "CSVic20.h"
|
#import "CSVic20.h"
|
||||||
|
#import "CSZX8081.h"
|
||||||
|
|
||||||
#import "CSStaticAnalyser.h"
|
#import "CSStaticAnalyser.h"
|
||||||
|
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
//
|
||||||
|
// ZX8081OptionsPanel.swift
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 08/07/2017.
|
||||||
|
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
class ZX8081OptionsPanel: MachinePanel {
|
||||||
|
var zx8081: CSZX8081! {
|
||||||
|
get {
|
||||||
|
return self.machine as! CSZX8081
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBOutlet var automaticTapeMotorControlButton: NSButton!
|
||||||
|
var automaticTapeMotorControlDefaultsKey: String {
|
||||||
|
get { return prefixedUserDefaultsKey("automaticTapeMotorControl") }
|
||||||
|
}
|
||||||
|
@IBAction func setAutomaticTapeMotorConrol(_ sender: NSButton!) {
|
||||||
|
let isEnabled = sender.state == NSOnState
|
||||||
|
UserDefaults.standard.set(isEnabled, forKey: self.automaticTapeMotorControlDefaultsKey)
|
||||||
|
self.playOrPauseTapeButton.isEnabled = !isEnabled
|
||||||
|
self.zx8081.useAutomaticTapeMotorControl = isEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBOutlet var playOrPauseTapeButton: NSButton!
|
||||||
|
@IBAction func playOrPauseTape(_ sender: NSButton!) {
|
||||||
|
self.zx8081.tapeIsPlaying = !self.zx8081.tapeIsPlaying
|
||||||
|
self.playOrPauseTapeButton.title = self.zx8081.tapeIsPlaying
|
||||||
|
? NSLocalizedString("Stop Tape", comment: "Text for a button that will stop tape playback")
|
||||||
|
: NSLocalizedString("Play Tape", comment: "Text for a button that will start tape playback")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: option restoration
|
||||||
|
override func establishStoredOptions() {
|
||||||
|
super.establishStoredOptions()
|
||||||
|
|
||||||
|
let standardUserDefaults = UserDefaults.standard
|
||||||
|
standardUserDefaults.register(defaults: [
|
||||||
|
self.automaticTapeMotorControlDefaultsKey: true
|
||||||
|
])
|
||||||
|
|
||||||
|
let automaticTapeMotorControlIsEnabled = standardUserDefaults.bool(forKey: self.automaticTapeMotorControlDefaultsKey)
|
||||||
|
self.automaticTapeMotorControlButton.state = automaticTapeMotorControlIsEnabled ? NSOnState : NSOffState
|
||||||
|
self.playOrPauseTapeButton.isEnabled = !automaticTapeMotorControlIsEnabled
|
||||||
|
self.zx8081.useAutomaticTapeMotorControl = automaticTapeMotorControlIsEnabled
|
||||||
|
}
|
||||||
|
}
|
@ -14,4 +14,7 @@
|
|||||||
|
|
||||||
@property (nonatomic, assign) BOOL useFastLoadingHack;
|
@property (nonatomic, assign) BOOL useFastLoadingHack;
|
||||||
|
|
||||||
|
@property (nonatomic, assign) BOOL useAutomaticTapeMotorControl;
|
||||||
|
@property (nonatomic, assign) BOOL tapeIsPlaying;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -110,4 +110,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setTapeIsPlaying:(BOOL)tapeIsPlaying {
|
||||||
|
@synchronized(self) {
|
||||||
|
_tapeIsPlaying = tapeIsPlaying;
|
||||||
|
_zx8081.set_tape_is_playing(tapeIsPlaying ? true : false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setUseAutomaticTapeMotorControl:(BOOL)useAutomaticTapeMotorControl {
|
||||||
|
@synchronized(self) {
|
||||||
|
_useAutomaticTapeMotorControl = useAutomaticTapeMotorControl;
|
||||||
|
_zx8081.set_use_automatic_tape_motor_control(useAutomaticTapeMotorControl ? true : false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -8,9 +8,10 @@
|
|||||||
|
|
||||||
#include "CRT.hpp"
|
#include "CRT.hpp"
|
||||||
#include "CRTOpenGL.hpp"
|
#include "CRTOpenGL.hpp"
|
||||||
#include <stdarg.h>
|
#include <cstdarg>
|
||||||
#include <math.h>
|
#include <cmath>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
using namespace Outputs::CRT;
|
using namespace Outputs::CRT;
|
||||||
|
|
||||||
@ -144,6 +145,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo
|
|||||||
|
|
||||||
if(next_run) {
|
if(next_run) {
|
||||||
// output_y and texture locations will be written later; we won't necessarily know what it is outside of the locked region
|
// output_y and texture locations will be written later; we won't necessarily know what it is outside of the locked region
|
||||||
|
openGL_output_builder_.texture_builder.retain_latest();
|
||||||
source_output_position_x1() = (uint16_t)horizontal_flywheel_->get_current_output_position();
|
source_output_position_x1() = (uint16_t)horizontal_flywheel_->get_current_output_position();
|
||||||
source_phase() = colour_burst_phase_;
|
source_phase() = colour_burst_phase_;
|
||||||
source_amplitude() = colour_burst_amplitude_;
|
source_amplitude() = colour_burst_amplitude_;
|
||||||
@ -192,9 +194,10 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo
|
|||||||
output_x2() = (uint16_t)horizontal_flywheel_->get_current_output_position();
|
output_x2() = (uint16_t)horizontal_flywheel_->get_current_output_position();
|
||||||
}
|
}
|
||||||
openGL_output_builder_.array_builder.flush(
|
openGL_output_builder_.array_builder.flush(
|
||||||
[output_y, this] (uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size) {
|
[=] (uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size) {
|
||||||
openGL_output_builder_.texture_builder.flush(
|
openGL_output_builder_.texture_builder.flush(
|
||||||
[output_y, input_buffer] (const std::vector<TextureBuilder::WriteArea> &write_areas, size_t number_of_write_areas) {
|
[=] (const std::vector<TextureBuilder::WriteArea> &write_areas, size_t number_of_write_areas) {
|
||||||
|
assert(number_of_write_areas * SourceVertexSize == input_size);
|
||||||
for(size_t run = 0; run < number_of_write_areas; run++) {
|
for(size_t run = 0; run < number_of_write_areas; run++) {
|
||||||
*(uint16_t *)&input_buffer[run * SourceVertexSize + SourceVertexOffsetOfInputStart + 0] = write_areas[run].x;
|
*(uint16_t *)&input_buffer[run * SourceVertexSize + SourceVertexOffsetOfInputStart + 0] = write_areas[run].x;
|
||||||
*(uint16_t *)&input_buffer[run * SourceVertexSize + SourceVertexOffsetOfInputStart + 2] = write_areas[run].y;
|
*(uint16_t *)&input_buffer[run * SourceVertexSize + SourceVertexOffsetOfInputStart + 2] = write_areas[run].y;
|
||||||
@ -311,6 +314,7 @@ void CRT::output_blank(unsigned int number_of_cycles) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CRT::output_level(unsigned int number_of_cycles) {
|
void CRT::output_level(unsigned int number_of_cycles) {
|
||||||
|
openGL_output_builder_.texture_builder.reduce_previous_allocation_to(1);
|
||||||
Scan scan {
|
Scan scan {
|
||||||
.type = Scan::Type::Level,
|
.type = Scan::Type::Level,
|
||||||
.number_of_cycles = number_of_cycles,
|
.number_of_cycles = number_of_cycles,
|
||||||
@ -329,13 +333,7 @@ void CRT::output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CRT::output_default_colour_burst(unsigned int number_of_cycles) {
|
void CRT::output_default_colour_burst(unsigned int number_of_cycles) {
|
||||||
Scan scan {
|
output_colour_burst(number_of_cycles, (uint8_t)((phase_numerator_ * 256) / phase_denominator_ + (is_alernate_line_ ? 128 : 0)));
|
||||||
.type = Scan::Type::ColourBurst,
|
|
||||||
.number_of_cycles = number_of_cycles,
|
|
||||||
.phase = (uint8_t)((phase_numerator_ * 256) / phase_denominator_ + (is_alernate_line_ ? 128 : 0)),
|
|
||||||
.amplitude = 32
|
|
||||||
};
|
|
||||||
output_scan(&scan);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRT::output_data(unsigned int number_of_cycles, unsigned int source_divider) {
|
void CRT::output_data(unsigned int number_of_cycles, unsigned int source_divider) {
|
||||||
|
@ -187,7 +187,7 @@ class CRT {
|
|||||||
@param amplitude The amplitude of the colour burst in 1/256ths of the amplitude of the
|
@param amplitude The amplitude of the colour burst in 1/256ths of the amplitude of the
|
||||||
positive portion of the wave.
|
positive portion of the wave.
|
||||||
*/
|
*/
|
||||||
void output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint8_t amplitude);
|
void output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint8_t amplitude = 102);
|
||||||
|
|
||||||
/*! Outputs a colour burst exactly in phase with CRT expectations using the idiomatic amplitude.
|
/*! Outputs a colour burst exactly in phase with CRT expectations using the idiomatic amplitude.
|
||||||
|
|
||||||
|
@ -39,7 +39,6 @@ TextureBuilder::TextureBuilder(size_t bytes_per_pixel, GLenum texture_unit) :
|
|||||||
write_areas_start_y_(0),
|
write_areas_start_y_(0),
|
||||||
is_full_(false),
|
is_full_(false),
|
||||||
did_submit_(false),
|
did_submit_(false),
|
||||||
has_write_area_(false),
|
|
||||||
number_of_write_areas_(0) {
|
number_of_write_areas_(0) {
|
||||||
image_.resize(bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight);
|
image_.resize(bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight);
|
||||||
glGenTextures(1, &texture_name_);
|
glGenTextures(1, &texture_name_);
|
||||||
@ -74,7 +73,6 @@ uint8_t *TextureBuilder::allocate_write_area(size_t required_length) {
|
|||||||
starting_y = write_areas_[number_of_write_areas_ - 1].y;
|
starting_y = write_areas_[number_of_write_areas_ - 1].y;
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteArea next_write_area;
|
|
||||||
if(starting_x + required_length + 2 > InputBufferBuilderWidth) {
|
if(starting_x + required_length + 2 > InputBufferBuilderWidth) {
|
||||||
starting_x = 0;
|
starting_x = 0;
|
||||||
starting_y++;
|
starting_y++;
|
||||||
@ -85,33 +83,34 @@ uint8_t *TextureBuilder::allocate_write_area(size_t required_length) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
next_write_area.x = starting_x + 1;
|
write_area_.x = starting_x + 1;
|
||||||
next_write_area.y = starting_y;
|
write_area_.y = starting_y;
|
||||||
next_write_area.length = (uint16_t)required_length;
|
write_area_.length = (uint16_t)required_length;
|
||||||
if(number_of_write_areas_ < write_areas_.size())
|
|
||||||
write_areas_[number_of_write_areas_] = next_write_area;
|
|
||||||
else
|
|
||||||
write_areas_.push_back(next_write_area);
|
|
||||||
number_of_write_areas_++;
|
|
||||||
has_write_area_ = true;
|
|
||||||
|
|
||||||
return pointer_to_location(next_write_area.x, next_write_area.y);
|
return pointer_to_location(write_area_.x, write_area_.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextureBuilder::is_full() {
|
bool TextureBuilder::is_full() {
|
||||||
return is_full_;
|
return is_full_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureBuilder::reduce_previous_allocation_to(size_t actual_length) {
|
void TextureBuilder::retain_latest() {
|
||||||
if(is_full_ || !has_write_area_) return;
|
if(is_full_) return;
|
||||||
|
if(number_of_write_areas_ < write_areas_.size())
|
||||||
|
write_areas_[number_of_write_areas_] = write_area_;
|
||||||
|
else
|
||||||
|
write_areas_.push_back(write_area_);
|
||||||
|
number_of_write_areas_++;
|
||||||
|
}
|
||||||
|
|
||||||
has_write_area_ = false;
|
void TextureBuilder::reduce_previous_allocation_to(size_t actual_length) {
|
||||||
WriteArea &write_area = write_areas_[number_of_write_areas_-1];
|
if(is_full_) return;
|
||||||
write_area.length = (uint16_t)actual_length;
|
|
||||||
|
write_area_.length = (uint16_t)actual_length;
|
||||||
|
|
||||||
// book end the allocation with duplicates of the first and last pixel, to protect
|
// book end the allocation with duplicates of the first and last pixel, to protect
|
||||||
// against rounding errors when this run is drawn
|
// against rounding errors when this run is drawn
|
||||||
uint8_t *start_pointer = pointer_to_location(write_area.x, write_area.y);
|
uint8_t *start_pointer = pointer_to_location(write_area_.x, write_area_.y);
|
||||||
memcpy( &start_pointer[-bytes_per_pixel_],
|
memcpy( &start_pointer[-bytes_per_pixel_],
|
||||||
start_pointer,
|
start_pointer,
|
||||||
bytes_per_pixel_);
|
bytes_per_pixel_);
|
||||||
@ -172,6 +171,5 @@ void TextureBuilder::flush(const std::function<void(const std::vector<WriteArea>
|
|||||||
}
|
}
|
||||||
|
|
||||||
did_submit_ = false;
|
did_submit_ = false;
|
||||||
has_write_area_ = false;
|
|
||||||
number_of_write_areas_ = 0;
|
number_of_write_areas_ = 0;
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,32 @@ namespace CRT {
|
|||||||
Owns an OpenGL texture resource and provides mechanisms to fill it from bottom left to top right
|
Owns an OpenGL texture resource and provides mechanisms to fill it from bottom left to top right
|
||||||
with runs of data, ensuring each run is neighboured immediately to the left and right by copies of its
|
with runs of data, ensuring each run is neighboured immediately to the left and right by copies of its
|
||||||
first and last pixels.
|
first and last pixels.
|
||||||
|
|
||||||
|
Intended usage:
|
||||||
|
|
||||||
|
(i) allocate a write area with allocate_write_area, supplying a maximum size.
|
||||||
|
(ii) call reduce_previous_allocation_to to announce the actual size written.
|
||||||
|
|
||||||
|
This will cause you to have added source data to the target texture. You can then either use that data
|
||||||
|
or allow it to expire.
|
||||||
|
|
||||||
|
(iii) call retain_latest to add the most recently written write area to the flush queue.
|
||||||
|
|
||||||
|
The flush queue contains provisional data, that can sit in the CPU's memory space indefinitely. This facility
|
||||||
|
is provided because it is expected that a texture will be built alontside some other collection of data —
|
||||||
|
that data in the flush queue is expected to become useful in coordination with something else but should
|
||||||
|
be retained at least until then.
|
||||||
|
|
||||||
|
(iv) call flush to move data to the submit queue.
|
||||||
|
|
||||||
|
When you flush, you'll receive a record of the bounds of all newly-flushed areas of source data. That gives
|
||||||
|
an opportunity to correlate the data with whatever else it is being tied to. It will continue to sit in
|
||||||
|
the CPU's memory space but has now passed beyond any further modification or reporting.
|
||||||
|
|
||||||
|
(v) call submit to move data to the GPU and free up its CPU-side resources.
|
||||||
|
|
||||||
|
The data is now on the GPU, for whatever use the caller desires.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
class TextureBuilder {
|
class TextureBuilder {
|
||||||
public:
|
public:
|
||||||
@ -41,6 +67,9 @@ class TextureBuilder {
|
|||||||
/// and indicates that its actual final size was @c actual_length.
|
/// and indicates that its actual final size was @c actual_length.
|
||||||
void reduce_previous_allocation_to(size_t actual_length);
|
void reduce_previous_allocation_to(size_t actual_length);
|
||||||
|
|
||||||
|
/// Allocated runs are provisional; they will not appear in the next flush queue unless retained.
|
||||||
|
void retain_latest();
|
||||||
|
|
||||||
/// @returns @c true if all future calls to @c allocate_write_area will fail on account of the input texture
|
/// @returns @c true if all future calls to @c allocate_write_area will fail on account of the input texture
|
||||||
/// being full; @c false if calls may succeed.
|
/// being full; @c false if calls may succeed.
|
||||||
bool is_full();
|
bool is_full();
|
||||||
@ -64,12 +93,14 @@ class TextureBuilder {
|
|||||||
std::vector<uint8_t> image_;
|
std::vector<uint8_t> image_;
|
||||||
GLuint texture_name_;
|
GLuint texture_name_;
|
||||||
|
|
||||||
// the current list of write areas
|
// the current write area
|
||||||
|
WriteArea write_area_;
|
||||||
|
|
||||||
|
// the list of write areas that have ascended to the flush queue
|
||||||
std::vector<WriteArea> write_areas_;
|
std::vector<WriteArea> write_areas_;
|
||||||
size_t number_of_write_areas_;
|
size_t number_of_write_areas_;
|
||||||
bool is_full_;
|
bool is_full_;
|
||||||
bool did_submit_;
|
bool did_submit_;
|
||||||
bool has_write_area_;
|
|
||||||
inline uint8_t *pointer_to_location(uint16_t x, uint16_t y);
|
inline uint8_t *pointer_to_location(uint16_t x, uint16_t y);
|
||||||
|
|
||||||
// Usually: the start position for the current batch of write areas.
|
// Usually: the start position for the current batch of write areas.
|
||||||
|
@ -54,7 +54,7 @@ Tape::Pulse ZX80O81P::virtual_get_next_pulse() {
|
|||||||
// Start with 1 second of silence.
|
// Start with 1 second of silence.
|
||||||
if(!is_past_silence_ || has_finished_data()) {
|
if(!is_past_silence_ || has_finished_data()) {
|
||||||
pulse.type = Pulse::Type::Low;
|
pulse.type = Pulse::Type::Low;
|
||||||
pulse.length.length = 10;
|
pulse.length.length = 1;
|
||||||
pulse.length.clock_rate = 1;
|
pulse.length.clock_rate = 1;
|
||||||
is_past_silence_ = true;
|
is_past_silence_ = true;
|
||||||
has_ended_final_byte_ = has_finished_data();
|
has_ended_final_byte_ = has_finished_data();
|
||||||
|
Loading…
Reference in New Issue
Block a user