From ab1374f8018aedaa030fdc91bbd72885800c95a7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 6 Jul 2017 21:46:24 -0400 Subject: [PATCH 01/10] Added an assert on an assumed buffer size alignment. --- Outputs/CRT/CRT.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index c5ba7ee68..96ea6dd41 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -8,9 +8,10 @@ #include "CRT.hpp" #include "CRTOpenGL.hpp" -#include -#include +#include +#include #include +#include using namespace Outputs::CRT; @@ -194,7 +195,8 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo 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) { openGL_output_builder_.texture_builder.flush( - [output_y, input_buffer] (const std::vector &write_areas, size_t number_of_write_areas) { + [=] (const std::vector &write_areas, size_t number_of_write_areas) { + assert(number_of_write_areas == input_size * SourceVertexSize); 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 + 2] = write_areas[run].y; From bfbe12b94b213859d93063d817f3ccdc8d51f42a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 7 Jul 2017 22:25:05 -0400 Subject: [PATCH 02/10] Made an attempt further to tie geometry and texture generation fully together, removing the assumption that the caller will achieve one-to-one calling. --- Outputs/CRT/CRT.cpp | 4 ++- Outputs/CRT/Internals/TextureBuilder.cpp | 30 +++++++++++---------- Outputs/CRT/Internals/TextureBuilder.hpp | 34 +++++++++++++++++++++++- 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 96ea6dd41..d34174fec 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -140,6 +140,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo bool is_output_segment = ((is_output_run && next_run_length) && !horizontal_flywheel_->is_in_retrace() && !vertical_flywheel_->is_in_retrace()); uint8_t *next_run = nullptr; if(is_output_segment && !openGL_output_builder_.composite_output_buffer_is_full()) { + openGL_output_builder_.texture_builder.retain_latest(); next_run = openGL_output_builder_.array_builder.get_input_storage(SourceVertexSize); } @@ -196,7 +197,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo [output_y, this] (uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size) { openGL_output_builder_.texture_builder.flush( [=] (const std::vector &write_areas, size_t number_of_write_areas) { - assert(number_of_write_areas == input_size * SourceVertexSize); + assert(number_of_write_areas * SourceVertexSize == input_size); 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 + 2] = write_areas[run].y; @@ -313,6 +314,7 @@ void CRT::output_blank(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 { .type = Scan::Type::Level, .number_of_cycles = number_of_cycles, diff --git a/Outputs/CRT/Internals/TextureBuilder.cpp b/Outputs/CRT/Internals/TextureBuilder.cpp index d36fbbf3c..6df0a7ab8 100644 --- a/Outputs/CRT/Internals/TextureBuilder.cpp +++ b/Outputs/CRT/Internals/TextureBuilder.cpp @@ -74,7 +74,6 @@ uint8_t *TextureBuilder::allocate_write_area(size_t required_length) { starting_y = write_areas_[number_of_write_areas_ - 1].y; } - WriteArea next_write_area; if(starting_x + required_length + 2 > InputBufferBuilderWidth) { starting_x = 0; starting_y++; @@ -85,33 +84,36 @@ uint8_t *TextureBuilder::allocate_write_area(size_t required_length) { } } - next_write_area.x = starting_x + 1; - next_write_area.y = starting_y; - next_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_++; + write_area_.x = starting_x + 1; + write_area_.y = starting_y; + write_area_.length = (uint16_t)required_length; 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() { return is_full_; } +void TextureBuilder::retain_latest() { + if(is_full_ || !has_write_area_) 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) { if(is_full_ || !has_write_area_) return; - has_write_area_ = false; - WriteArea &write_area = write_areas_[number_of_write_areas_-1]; - 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 // 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_], start_pointer, bytes_per_pixel_); diff --git a/Outputs/CRT/Internals/TextureBuilder.hpp b/Outputs/CRT/Internals/TextureBuilder.hpp index 93777ac92..aa51a19fe 100644 --- a/Outputs/CRT/Internals/TextureBuilder.hpp +++ b/Outputs/CRT/Internals/TextureBuilder.hpp @@ -24,6 +24,32 @@ namespace CRT { 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 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 { public: @@ -41,6 +67,9 @@ class TextureBuilder { /// and indicates that its actual final size was @c 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 /// being full; @c false if calls may succeed. bool is_full(); @@ -64,7 +93,10 @@ class TextureBuilder { std::vector image_; 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 write_areas_; size_t number_of_write_areas_; bool is_full_; From c7fa2ed11a03f59a3dc4b7a34c13cdfe06ec43f0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 7 Jul 2017 23:21:25 -0400 Subject: [PATCH 03/10] It makes more sense not to retain the previous texture builder run until vertex storage is confirmed. --- Outputs/CRT/CRT.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index d34174fec..88e658e93 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -140,12 +140,12 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo bool is_output_segment = ((is_output_run && next_run_length) && !horizontal_flywheel_->is_in_retrace() && !vertical_flywheel_->is_in_retrace()); uint8_t *next_run = nullptr; if(is_output_segment && !openGL_output_builder_.composite_output_buffer_is_full()) { - openGL_output_builder_.texture_builder.retain_latest(); next_run = openGL_output_builder_.array_builder.get_input_storage(SourceVertexSize); } 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 + openGL_output_builder_.texture_builder.retain_latest(); source_output_position_x1() = (uint16_t)horizontal_flywheel_->get_current_output_position(); source_phase() = colour_burst_phase_; source_amplitude() = colour_burst_amplitude_; From 12f7e1b804ecc4b2d9ffeac152b3f42495046f83 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 7 Jul 2017 23:35:14 -0400 Subject: [PATCH 04/10] Enshrined a default colour burst amplitude. Which now everybody relies on. The 102 figure is derived from the burst apparently being 40 IRE. --- Components/6560/6560.hpp | 2 +- Outputs/CRT/CRT.cpp | 10 ++-------- Outputs/CRT/CRT.hpp | 2 +- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp index 88ce2dbc2..829df3e7a 100644 --- a/Components/6560/6560.hpp +++ b/Components/6560/6560.hpp @@ -259,7 +259,7 @@ template class MOS6560 { if(this_state_ != output_state_) { switch(output_state_) { 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::Pixels: crt_->output_data(cycles_in_state_ * 4, 1); break; } diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 88e658e93..85be1eefa 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -194,7 +194,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo output_x2() = (uint16_t)horizontal_flywheel_->get_current_output_position(); } 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( [=] (const std::vector &write_areas, size_t number_of_write_areas) { assert(number_of_write_areas * SourceVertexSize == input_size); @@ -333,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) { - Scan scan { - .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); + output_colour_burst(number_of_cycles, (uint8_t)((phase_numerator_ * 256) / phase_denominator_ + (is_alernate_line_ ? 128 : 0))); } void CRT::output_data(unsigned int number_of_cycles, unsigned int source_divider) { diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index 3f3847cf8..49f6bb206 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -187,7 +187,7 @@ class CRT { @param amplitude The amplitude of the colour burst in 1/256ths of the amplitude of the 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. From 2f90f35478c41a0e3742e09167ecbf8b50f2cf1b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 7 Jul 2017 23:37:44 -0400 Subject: [PATCH 05/10] =?UTF-8?q?Ensured=20the=20same=20write=20area=20can?= =?UTF-8?q?=20be=20submitted=20multiple=20times=20=E2=80=94=20this=20is=20?= =?UTF-8?q?actively=20used=20if=20a=20run=20of=20data=20overlaps=20a=20fly?= =?UTF-8?q?wheel-suggested=20sync.=20Which=20nullifies=20the=20idea=20of?= =?UTF-8?q?=20not=20having=20a=20write=20area=20in=20the=20barrel,=20at=20?= =?UTF-8?q?least=20as=20soon=20as=20any=20one=20has=20been=20allocated.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Outputs/CRT/Internals/TextureBuilder.cpp | 8 ++------ Outputs/CRT/Internals/TextureBuilder.hpp | 1 - 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Outputs/CRT/Internals/TextureBuilder.cpp b/Outputs/CRT/Internals/TextureBuilder.cpp index 6df0a7ab8..98371bd4d 100644 --- a/Outputs/CRT/Internals/TextureBuilder.cpp +++ b/Outputs/CRT/Internals/TextureBuilder.cpp @@ -39,7 +39,6 @@ TextureBuilder::TextureBuilder(size_t bytes_per_pixel, GLenum texture_unit) : write_areas_start_y_(0), is_full_(false), did_submit_(false), - has_write_area_(false), number_of_write_areas_(0) { image_.resize(bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight); glGenTextures(1, &texture_name_); @@ -87,7 +86,6 @@ uint8_t *TextureBuilder::allocate_write_area(size_t required_length) { write_area_.x = starting_x + 1; write_area_.y = starting_y; write_area_.length = (uint16_t)required_length; - has_write_area_ = true; return pointer_to_location(write_area_.x, write_area_.y); } @@ -97,17 +95,16 @@ bool TextureBuilder::is_full() { } 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) { - if(is_full_ || !has_write_area_) return; + if(is_full_) return; write_area_.length = (uint16_t)actual_length; @@ -174,6 +171,5 @@ void TextureBuilder::flush(const std::function } did_submit_ = false; - has_write_area_ = false; number_of_write_areas_ = 0; } diff --git a/Outputs/CRT/Internals/TextureBuilder.hpp b/Outputs/CRT/Internals/TextureBuilder.hpp index aa51a19fe..88b95ee19 100644 --- a/Outputs/CRT/Internals/TextureBuilder.hpp +++ b/Outputs/CRT/Internals/TextureBuilder.hpp @@ -101,7 +101,6 @@ class TextureBuilder { size_t number_of_write_areas_; bool is_full_; bool did_submit_; - bool has_write_area_; inline uint8_t *pointer_to_location(uint16_t x, uint16_t y); // Usually: the start position for the current batch of write areas. From 28412150e6f6ba2ab1ef81f85170bd75885dcaa1 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 8 Jul 2017 17:59:33 -0400 Subject: [PATCH 06/10] Added controls for controlling the tape motor of the ZX80/81, assuming I can find an automatic option. --- .../Clock Signal.xcodeproj/project.pbxproj | 4 ++ .../Clock Signal/Base.lproj/ZX8081Options.xib | 43 +++++++++++++++---- .../Documents/ZX8081OptionsPanel.swift | 20 +++++++++ 3 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 OSBindings/Mac/Clock Signal/Documents/ZX8081OptionsPanel.swift diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 30bd9550c..35d3c1475 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -96,6 +96,7 @@ 4B924E991E74D22700B76AF1 /* AtariStaticAnalyserTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B924E981E74D22700B76AF1 /* AtariStaticAnalyserTests.mm */; }; 4B9252CE1E74D28200B76AF1 /* Atari ROMs in Resources */ = {isa = PBXBuildFile; fileRef = 4B9252CD1E74D28200B76AF1 /* Atari ROMs */; }; 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 */; }; 4B9CCDA11DA279CA0098B625 /* Vic20OptionsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B9CCDA01DA279CA0098B625 /* Vic20OptionsPanel.swift */; }; 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 = ""; }; 4B9252CD1E74D28200B76AF1 /* Atari ROMs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "Atari ROMs"; sourceTree = ""; }; 4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6502TimingTests.swift; sourceTree = ""; }; + 4B95FA9C1F11893B0008E395 /* ZX8081OptionsPanel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZX8081OptionsPanel.swift; sourceTree = ""; }; 4B96F7201D75119A0058BB2D /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tape.cpp; path = ../../StaticAnalyser/Acorn/Tape.cpp; sourceTree = ""; }; 4B96F7211D75119A0058BB2D /* Tape.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Tape.hpp; path = ../../StaticAnalyser/Acorn/Tape.hpp; sourceTree = ""; }; 4B9CCDA01DA279CA0098B625 /* Vic20OptionsPanel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vic20OptionsPanel.swift; sourceTree = ""; }; @@ -1312,6 +1314,7 @@ 4B8FE2211DA19FB20090D3CE /* MachinePanel.swift */, 4B2A332E1DB86869002876E3 /* OricOptionsPanel.swift */, 4B9CCDA01DA279CA0098B625 /* Vic20OptionsPanel.swift */, + 4B95FA9C1F11893B0008E395 /* ZX8081OptionsPanel.swift */, 4B8FE2131DA19D5F0090D3CE /* Atari2600Options.xib */, 4B8FE2171DA19D5F0090D3CE /* ElectronOptions.xib */, 4B8FE2151DA19D5F0090D3CE /* MachineDocument.xib */, @@ -2566,6 +2569,7 @@ 4BD4A8CD1E077E8A0020D856 /* PCMSegment.cpp in Sources */, 4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */, 4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */, + 4B95FA9D1F11893B0008E395 /* ZX8081OptionsPanel.swift in Sources */, 4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */, 4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */, 4BEA525E1DF33323007E74F2 /* Tape.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/ZX8081Options.xib b/OSBindings/Mac/Clock Signal/Base.lproj/ZX8081Options.xib index b1dcdf861..c6129a19e 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/ZX8081Options.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/ZX8081Options.xib @@ -1,7 +1,7 @@ - + - + @@ -12,17 +12,17 @@ - + - + - + + + + - + + + + + + + - + diff --git a/OSBindings/Mac/Clock Signal/Documents/ZX8081OptionsPanel.swift b/OSBindings/Mac/Clock Signal/Documents/ZX8081OptionsPanel.swift new file mode 100644 index 000000000..9f5147006 --- /dev/null +++ b/OSBindings/Mac/Clock Signal/Documents/ZX8081OptionsPanel.swift @@ -0,0 +1,20 @@ +// +// ZX8081OptionsPanel.swift +// Clock Signal +// +// Created by Thomas Harte on 08/07/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +import Cocoa + +class ZX8081OptionsPanel: MachinePanel { + + @IBOutlet var tapeControlButton: NSButton! + @IBAction func setAutomaticTapeConrol(_ sender: NSButton!) { + Swift.print("tape control \(tapeControlButton)") + } + @IBAction func playOrPauseTape(_ sender: NSButton!) { + Swift.print("play/pause") + } +} From 23e989e170af67a0e282f9787fd057f75476584e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 8 Jul 2017 19:12:06 -0400 Subject: [PATCH 07/10] This will likely do for the Swift/XIB side of things: the play/pause button is enabled or disabled as per the user's choice of automatic tape control, and toggles function when pressed. It communicates activity down to the Objective-C[++] layer, giving it a route through to the actual machine. --- .../Clock Signal/Base.lproj/ZX8081Options.xib | 7 ++-- .../ClockSignal-Bridging-Header.h | 1 + .../Documents/ZX8081OptionsPanel.swift | 42 +++++++++++++++---- .../Clock Signal/Machine/Wrappers/CSZX8081.h | 3 ++ 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/ZX8081Options.xib b/OSBindings/Mac/Clock Signal/Base.lproj/ZX8081Options.xib index c6129a19e..5b4b66fd0 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/ZX8081Options.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/ZX8081Options.xib @@ -38,12 +38,12 @@ - +