From 47508d50a7a19404788b8dec571ce92e6ea7bf8f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 20 Dec 2019 20:49:14 -0500 Subject: [PATCH 1/3] Wires through a composite video option for the ST. Which is great and all, except that I've not yet inserted a colour burst. So it's monochrome. --- Machines/Atari/ST/AtariST.cpp | 36 +++++++++++++++++++ Machines/Atari/ST/AtariST.hpp | 3 ++ Machines/Atari/ST/DMAController.cpp | 4 +-- Machines/Atari/ST/Video.cpp | 4 +++ Machines/Atari/ST/Video.hpp | 5 +++ Machines/Utility/MachineForTarget.cpp | 1 + .../StaticAnalyser/CSStaticAnalyser.mm | 1 + 7 files changed, 52 insertions(+), 2 deletions(-) diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp index 35fc403c0..9d2c00a85 100644 --- a/Machines/Atari/ST/AtariST.cpp +++ b/Machines/Atari/ST/AtariST.cpp @@ -28,6 +28,7 @@ #include "../../../ClockReceiver/JustInTime.hpp" #include "../../../ClockReceiver/ForceInline.hpp" +#include "../../../Configurable/StandardOptions.hpp" #include "../../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp" @@ -40,6 +41,12 @@ namespace Atari { namespace ST { +std::vector> get_options() { + return Configurable::standard_options( + static_cast(Configurable::DisplayRGB | Configurable::DisplayCompositeColour | Configurable::QuickLoadTape) + ); +} + const int CLOCK_RATE = 8021247; using Target = Analyser::Static::Target; @@ -57,6 +64,7 @@ class ConcreteMachine: public Activity::Source, public MediaTarget::Machine, public GI::AY38910::PortHandler, + public Configurable::Device, public Video::RangeObserver { public: ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : @@ -130,6 +138,10 @@ class ConcreteMachine: video_->set_scan_target(scan_target); } + void set_display_type(Outputs::Display::DisplayType display_type) final { + video_->set_display_type(display_type); + } + Outputs::Speaker::Speaker *get_speaker() final { return &speaker_; } @@ -613,6 +625,30 @@ class ConcreteMachine: void video_did_change_access_range(Video *video) final { video_range_ = video->get_memory_access_range(); } + + // MARK: - Configuration options. + std::vector> get_options() final { + return Atari::ST::get_options(); + } + + void set_selections(const Configurable::SelectionSet &selections_by_option) final { + Configurable::Display display; + if(Configurable::get_display(selections_by_option, display)) { + set_video_signal_configurable(display); + } + } + + Configurable::SelectionSet get_accurate_selections() final { + Configurable::SelectionSet selection_set; + Configurable::append_display_selection(selection_set, Configurable::Display::CompositeColour); + return selection_set; + } + + Configurable::SelectionSet get_user_friendly_selections() final { + Configurable::SelectionSet selection_set; + Configurable::append_display_selection(selection_set, Configurable::Display::RGB); + return selection_set; + } }; } diff --git a/Machines/Atari/ST/AtariST.hpp b/Machines/Atari/ST/AtariST.hpp index 5a4f6a213..6345620cd 100644 --- a/Machines/Atari/ST/AtariST.hpp +++ b/Machines/Atari/ST/AtariST.hpp @@ -16,6 +16,9 @@ namespace Atari { namespace ST { +/// @returns The options available for an Atari ST. +std::vector> get_options(); + class Machine { public: virtual ~Machine(); diff --git a/Machines/Atari/ST/DMAController.cpp b/Machines/Atari/ST/DMAController.cpp index 723ff1248..477a113c9 100644 --- a/Machines/Atari/ST/DMAController.cpp +++ b/Machines/Atari/ST/DMAController.cpp @@ -107,11 +107,11 @@ void DMAController::write(int address, uint16_t value) { // of the byte it is adjusting. case 4: address_ = int((address_ & 0x00ffff) | ((value & 0xff) << 16)); break; case 5: - if((value << 8) ^ address_ & ~(value << 8) & 0x8000) address_ += 0x10000; + if(((value << 8) ^ address_) & ~(value << 8) & 0x8000) address_ += 0x10000; address_ = int((address_ & 0xff00ff) | ((value & 0xff) << 8)); break; case 6: - if(value ^ address_ & ~value & 0x80) address_ += 0x100; + if((value ^ address_) & ~value & 0x80) address_ += 0x100; address_ = int((address_ & 0xffff00) | ((value & 0xfe) << 0)); break; // Lowest bit: discarded. } diff --git a/Machines/Atari/ST/Video.cpp b/Machines/Atari/ST/Video.cpp index 94446c59b..943c865db 100644 --- a/Machines/Atari/ST/Video.cpp +++ b/Machines/Atari/ST/Video.cpp @@ -122,6 +122,10 @@ void Video::set_scan_target(Outputs::Display::ScanTarget *scan_target) { crt_.set_scan_target(scan_target); } +void Video::set_display_type(Outputs::Display::DisplayType display_type) { + crt_.set_display_type(display_type); +} + void Video::run_for(HalfCycles duration) { deferrer_.run_for(duration); } diff --git a/Machines/Atari/ST/Video.hpp b/Machines/Atari/ST/Video.hpp index 971d31ac1..269d92d59 100644 --- a/Machines/Atari/ST/Video.hpp +++ b/Machines/Atari/ST/Video.hpp @@ -37,6 +37,11 @@ class Video { */ void set_scan_target(Outputs::Display::ScanTarget *scan_target); + /*! + Sets the type of output. + */ + void set_display_type(Outputs::Display::DisplayType); + /*! Produces the next @c duration period of pixels. */ diff --git a/Machines/Utility/MachineForTarget.cpp b/Machines/Utility/MachineForTarget.cpp index 9a1622e3b..ece5a9f02 100644 --- a/Machines/Utility/MachineForTarget.cpp +++ b/Machines/Utility/MachineForTarget.cpp @@ -143,6 +143,7 @@ std::map>> Machin options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::AmstradCPC), AmstradCPC::get_options())); options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::AppleII), Apple::II::get_options())); + options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::AtariST), Atari::ST::get_options())); options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::ColecoVision), Coleco::Vision::get_options())); options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::Electron), Electron::get_options())); options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::Macintosh), Apple::Macintosh::get_options())); diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm index 13eab0a63..a73c3e4ea 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm @@ -228,6 +228,7 @@ static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(K case Analyser::Machine::AmstradCPC: return @"CompositeOptions"; case Analyser::Machine::AppleII: return @"AppleIIOptions"; case Analyser::Machine::Atari2600: return @"Atari2600Options"; + case Analyser::Machine::AtariST: return @"CompositeOptions"; case Analyser::Machine::ColecoVision: return @"CompositeOptions"; case Analyser::Machine::Electron: return @"QuickLoadCompositeOptions"; case Analyser::Machine::Macintosh: return @"MacintoshOptions"; From 57ce10418f29a01a82c9f27423421d64abd26706 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 20 Dec 2019 23:33:14 -0500 Subject: [PATCH 2/3] Switches prescale logic, the better to deal with changes in prescaler. According to my assumptions about the behaviour, anyway. --- Components/68901/MFP68901.cpp | 33 +++++++++++++++---- Components/68901/MFP68901.hpp | 2 +- .../Clock Signal.xcodeproj/project.pbxproj | 2 +- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/Components/68901/MFP68901.cpp b/Components/68901/MFP68901.cpp index efb1de927..95c2a1992 100644 --- a/Components/68901/MFP68901.cpp +++ b/Components/68901/MFP68901.cpp @@ -8,6 +8,7 @@ #include "MFP68901.hpp" +#include #include #define LOG_PREFIX "[MFP] " @@ -179,12 +180,26 @@ void MFP68901::run_for(HalfCycles time) { cycles_left_ += time; const int cycles = int(cycles_left_.flush().as_integral()); + if(!cycles) return; + for(int c = 0; c < 4; ++c) { if(timers_[c].mode >= TimerMode::Delay) { - const int dividend = (cycles + timers_[c].prescale - timers_[c].divisor); - const int decrements = dividend / timers_[c].prescale; - timers_[c].divisor = timers_[c].prescale - (dividend % timers_[c].prescale); - if(decrements) decrement_timer(c, decrements); + // This code applies the timer prescaling only. prescale_count is used to count + // upwards rather than downwards for simplicity, but on the real hardware it's + // pretty safe to assume it actually counted downwards. So the clamp to 0 is + // because gymnastics may need to occur when the prescale value is altered, e.g. + // if a prescale of 256 is set and the prescale_count is currently 2 then the + // counter should roll over in 254 cycles. If the user at that point changes the + // prescale_count to 1 then the counter will need to be altered to -253 and + // allowed to keep counting up until it crosses both 0 and 1. + const int dividend = timers_[c].prescale_count + cycles; + const int decrements = std::max(dividend / timers_[c].prescale, 0); + if(decrements) { + decrement_timer(c, decrements); + timers_[c].prescale_count = dividend % timers_[c].prescale; + } else { + timers_[c].prescale_count += cycles; + } } } } @@ -198,11 +213,17 @@ HalfCycles MFP68901::get_next_sequence_point() { void MFP68901::set_timer_mode(int timer, TimerMode mode, int prescale, bool reset_timer) { LOG("Timer " << timer << " mode set: " << int(mode) << "; prescale: " << prescale); timers_[timer].mode = mode; - timers_[timer].prescale = prescale; if(reset_timer) { - timers_[timer].divisor = prescale; + timers_[timer].prescale_count = 0; timers_[timer].value = timers_[timer].reload_value; + } else { + // This hoop is because the prescale_count here goes upward but I'm assuming it goes downward in + // real hardware. Therefore this deals with the "switched to a lower prescaling" case whereby the + // old cycle should be allowed naturally to expire. + timers_[timer].prescale_count = prescale - (timers_[timer].prescale - timers_[timer].prescale_count); } + + timers_[timer].prescale = prescale; } void MFP68901::set_timer_data(int timer, uint8_t value) { diff --git a/Components/68901/MFP68901.hpp b/Components/68901/MFP68901.hpp index d49fe55bb..974e195e7 100644 --- a/Components/68901/MFP68901.hpp +++ b/Components/68901/MFP68901.hpp @@ -93,7 +93,7 @@ class MFP68901: public ClockingHint::Source { uint8_t value = 0; uint8_t reload_value = 0; int prescale = 1; - int divisor = 1; + int prescale_count = 1; bool event_input = false; } timers_[4]; uint8_t timer_ab_control_[2] = { 0, 0 }; diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index e7513b49a..3c527c9e1 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -4916,7 +4916,7 @@ "$(USER_LIBRARY_DIR)/Frameworks", ); GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_OPTIMIZATION_LEVEL = 3; + GCC_OPTIMIZATION_LEVEL = fast; MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_NAME = "$(TARGET_NAME)"; }; From e5440a414674cfd35540100d6c4d7b53ea2879b9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 20 Dec 2019 23:49:38 -0500 Subject: [PATCH 3/3] Hacks in a colour burst. With a major flaw: it's implicit. I think I need a minor rethink of various components here. --- Machines/Atari/ST/Video.cpp | 25 +++++++++++++++++++++++-- Machines/Atari/ST/Video.hpp | 3 ++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Machines/Atari/ST/Video.cpp b/Machines/Atari/ST/Video.cpp index 943c865db..b321e01fe 100644 --- a/Machines/Atari/ST/Video.cpp +++ b/Machines/Atari/ST/Video.cpp @@ -491,8 +491,9 @@ void Video::update_output_mode() { void Video::Shifter::flush_output(OutputMode next_mode) { switch(output_mode_) { - case OutputMode::Sync: crt_.output_sync(duration_); break; - case OutputMode::Blank: crt_.output_blank(duration_); break; + case OutputMode::Sync: crt_.output_sync(duration_); break; + case OutputMode::Blank: crt_.output_blank(duration_); break; + case OutputMode::ColourBurst: crt_.output_default_colour_burst(duration_); break; case OutputMode::Border: { // if(!border_colour_) { // crt_.output_blank(duration_); @@ -512,9 +513,29 @@ void Video::Shifter::flush_output(OutputMode next_mode) { output_mode_ = next_mode; } +void Video::Shifter::output_colour_burst(int duration) { + // More hackery afoot here; if and when duration_ crosses a threshold of 40, + // output 40 cycles of colour burst and then redirect to blank. + if(output_mode_ != OutputMode::ColourBurst) { + flush_output(OutputMode::ColourBurst); + } + duration_ += duration; + if(duration_ >= 40) { + const int blank_duration = duration_ - 40; + duration_ = 40; + flush_output(OutputMode::Blank); + output_blank(blank_duration); + } +} void Video::Shifter::output_blank(int duration) { if(output_mode_ != OutputMode::Blank) { + // Bit of a hack: if this is a transition from sync or we're really in + // colour burst, divert into that. + if(output_mode_ == OutputMode::Sync || output_mode_ == OutputMode::ColourBurst) { + output_colour_burst(duration); + return; + } flush_output(OutputMode::Blank); } duration_ += duration; diff --git a/Machines/Atari/ST/Video.hpp b/Machines/Atari/ST/Video.hpp index 269d92d59..4e240b9c1 100644 --- a/Machines/Atari/ST/Video.hpp +++ b/Machines/Atari/ST/Video.hpp @@ -173,13 +173,14 @@ class Video { void output_sync(int duration); void output_border(int duration, OutputBpp bpp); void output_pixels(int duration, OutputBpp bpp); + void output_colour_burst(int duration); void load(uint64_t value); private: int duration_ = 0; enum class OutputMode { - Sync, Blank, Border, Pixels + Sync, Blank, Border, Pixels, ColourBurst } output_mode_ = OutputMode::Sync; uint16_t border_colour_ = 0; OutputBpp bpp_ = OutputBpp::Four;