From afeec09902f1caaaffdc4e921079d274e14ae3bc Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 6 Sep 2018 23:23:19 -0400 Subject: [PATCH 1/3] Gets explicit about DHIRES being annunciator 3; implements four-colour high res mode. --- Machines/AppleII/AppleII.cpp | 6 +++--- Machines/AppleII/Video.cpp | 15 +++++++++------ Machines/AppleII/Video.hpp | 34 ++++++++++++++++++---------------- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/Machines/AppleII/AppleII.cpp b/Machines/AppleII/AppleII.cpp index 66527c50d..3c993aacc 100644 --- a/Machines/AppleII/AppleII.cpp +++ b/Machines/AppleII/AppleII.cpp @@ -534,7 +534,7 @@ template class ConcreteMachine: #undef IIeSwitchRead case 0xc07f: - if(is_iie()) *value = (*value & 0x7f) | (video_->get_double_high_resolution() ? 0x80 : 0x00); + if(is_iie()) *value = (*value & 0x7f) | (video_->get_annunciator_3() ? 0x80 : 0x00); break; } } else { @@ -611,7 +611,7 @@ template class ConcreteMachine: analogue_charge_ = 0.0f; } break; - /* Read-write switches. */ + /* Switches triggered by reading or writing. */ case 0xc050: case 0xc051: update_video(); @@ -636,7 +636,7 @@ template class ConcreteMachine: case 0xc05f: if(is_iie()) { update_video(); - video_->set_double_high_resolution(!(address&1)); + video_->set_annunciator_3(!(address&1)); } break; diff --git a/Machines/AppleII/Video.cpp b/Machines/AppleII/Video.cpp index 7435f3c1f..f51ba8606 100644 --- a/Machines/AppleII/Video.cpp +++ b/Machines/AppleII/Video.cpp @@ -131,15 +131,16 @@ bool VideoBase::get_high_resolution() { return set_high_resolution_; } -void VideoBase::set_double_high_resolution(bool double_high_resolution) { - set_double_high_resolution_ = double_high_resolution; +void VideoBase::set_annunciator_3(bool annunciator_3) { + set_annunciator_3_ = annunciator_3; deferrer_.defer(Cycles(2), [=] { - double_high_resolution_ = double_high_resolution; + annunciator_3_ = annunciator_3; + high_resolution_mask_ = annunciator_3_ ? 0x7f : 0xff; }); } -bool VideoBase::get_double_high_resolution() { - return set_double_high_resolution_; +bool VideoBase::get_annunciator_3() { + return set_annunciator_3_; } void VideoBase::set_character_rom(const std::vector &character_rom) { @@ -276,7 +277,9 @@ void VideoBase::output_high_resolution(uint8_t *target, const uint8_t *const sou for(size_t c = 0; c < length; ++c) { // High resolution graphics shift out LSB to MSB, optionally with a delay of half a pixel. // If there is a delay, the previous output level is held to bridge the gap. - if(source[c] & 0x80) { + // Delays may be ignored on a IIe if Annunciator 3 is set; that's the state that + // high_resolution_mask_ models. + if(source[c] & high_resolution_mask_ & 0x80) { target[0] = graphics_carry_; target[1] = target[2] = source[c] & 0x01; target[3] = target[4] = source[c] & 0x02; diff --git a/Machines/AppleII/Video.hpp b/Machines/AppleII/Video.hpp index a992b61a6..fd87541e4 100644 --- a/Machines/AppleII/Video.hpp +++ b/Machines/AppleII/Video.hpp @@ -128,18 +128,19 @@ class VideoBase { bool get_high_resolution(); /*! - Setter for DHIRES ($C05E/$C05F; triggers on write only). + Setter for annunciator 3. - * On: turn on double-high resolution. - * Off: turn off double-high resolution. + * On: turn on annunciator 3. + * Off: turn off annunciator 3. - DHIRES doesn't exist on a II/II+. On the IIe there is another - register usually grouped with the graphics setters called IOUDIS - that affects visibility of this switch. But it has no effect on - video, so it's not modelled by this class. + This exists on both the II/II+ and the IIe, but has no effect on + video on the older machines. It's intended to be used on the IIe + to confirm double-high resolution mode but has side effects in + selecting mixed mode output and discarding high-resolution + delay bits. */ - void set_double_high_resolution(bool); - bool get_double_high_resolution(); + void set_annunciator_3(bool); + bool get_annunciator_3(); // Setup for text mode. void set_character_rom(const std::vector &); @@ -176,13 +177,14 @@ class VideoBase { bool text_ = true, set_text_ = true; bool mixed_ = false, set_mixed_ = false; bool high_resolution_ = false, set_high_resolution_ = false; - bool double_high_resolution_ = false, set_double_high_resolution_ = false; + bool annunciator_3_ = false, set_annunciator_3_ = false; // Graphics carry is the final level output in a fetch window; // it carries on into the next if it's high resolution with // the delay bit set. mutable uint8_t graphics_carry_ = 0; bool was_double_ = false; + uint8_t high_resolution_mask_ = 0xff; // This holds a copy of the character ROM. The regular character // set is assumed to be in the first 64*8 bytes; the alternative @@ -539,14 +541,14 @@ template class Video: public VideoBase { } GraphicsMode graphics_mode(int row) { - if(text_) return columns_80_ ? GraphicsMode::DoubleText : GraphicsMode::Text; - if(mixed_ && row >= 160 && row < 192) { - return (columns_80_ || double_high_resolution_) ? GraphicsMode::DoubleText : GraphicsMode::Text; - } + if( + text_ || + (mixed_ && row >= 160 && row < 192) + ) return columns_80_ ? GraphicsMode::DoubleText : GraphicsMode::Text; if(high_resolution_) { - return double_high_resolution_ ? GraphicsMode::DoubleHighRes : GraphicsMode::HighRes; + return (annunciator_3_ && columns_80_) ? GraphicsMode::DoubleHighRes : GraphicsMode::HighRes; } else { - return double_high_resolution_ ? GraphicsMode::DoubleLowRes : GraphicsMode::LowRes; + return annunciator_3_ ? GraphicsMode::DoubleLowRes : GraphicsMode::LowRes; } } From d70f5da94eec6bd15d54bf3fea8627eab5436c0e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 8 Sep 2018 20:51:15 -0400 Subject: [PATCH 2/3] Attempts an implementation of the undocumented low res + annunciator 3 graphics mode. --- Machines/AppleII/Video.cpp | 22 ++++++++++++++++++++++ Machines/AppleII/Video.hpp | 33 +++++++++++++++++++++++++++------ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/Machines/AppleII/Video.cpp b/Machines/AppleII/Video.cpp index f51ba8606..0a6430e3b 100644 --- a/Machines/AppleII/Video.cpp +++ b/Machines/AppleII/Video.cpp @@ -243,6 +243,28 @@ void VideoBase::output_low_resolution(uint8_t *target, const uint8_t *const sour } } +void VideoBase::output_fat_low_resolution(uint8_t *target, const uint8_t *const source, size_t length, int column, int row) const { + const int row_shift = row&4; + for(size_t c = 0; c < length; ++c) { + // Low-resolution graphics mode shifts the colour code on a loop, but has to account for whether this + // 14-sample output window is starting at the beginning of a colour cycle or halfway through. + if((column + static_cast(c))&1) { + target[0] = target[1] = target[8] = target[9] = (source[c] >> row_shift) & 4; + target[2] = target[3] = target[10] = target[11] = (source[c] >> row_shift) & 8; + target[4] = target[5] = target[12] = target[13] = (source[c] >> row_shift) & 1; + target[6] = target[7] = (source[c] >> row_shift) & 2; + graphics_carry_ = (source[c] >> row_shift) & 8; + } else { + target[0] = target[1] = target[8] = target[9] = (source[c] >> row_shift) & 1; + target[2] = target[3] = target[10] = target[11] = (source[c] >> row_shift) & 2; + target[4] = target[5] = target[12] = target[13] = (source[c] >> row_shift) & 4; + target[6] = target[7] = (source[c] >> row_shift) & 8; + graphics_carry_ = (source[c] >> row_shift) & 2; + } + target += 14; + } +} + void VideoBase::output_double_low_resolution(uint8_t *target, const uint8_t *const source, const uint8_t *const auxiliary_source, size_t length, int column, int row) const { const int row_shift = row&4; for(size_t c = 0; c < length; ++c) { diff --git a/Machines/AppleII/Video.hpp b/Machines/AppleII/Video.hpp index fd87541e4..ceedebd58 100644 --- a/Machines/AppleII/Video.hpp +++ b/Machines/AppleII/Video.hpp @@ -159,14 +159,15 @@ class VideoBase { // Enumerates all Apple II and IIe display modes. enum class GraphicsMode { - LowRes = 0, - DoubleLowRes, + Text = 0, + DoubleText, HighRes, DoubleHighRes, - Text, - DoubleText, + LowRes, + DoubleLowRes, + FatLowRes }; - bool is_text_mode(GraphicsMode m) { return m >= GraphicsMode::Text; } + bool is_text_mode(GraphicsMode m) { return m <= GraphicsMode::DoubleText; } bool is_double_mode(GraphicsMode m) { return !!(static_cast(m)&1); } // Various soft-switch values. @@ -238,6 +239,14 @@ class VideoBase { */ void output_double_high_resolution(uint8_t *target, const uint8_t *source, const uint8_t *auxiliary_source, size_t length) const; + /*! + Outputs 40-column "fat low resolution" graphics to @c target, drawing @c length columns from @c source. + + Fat low-resolution mode is like regular low-resolution mode except that data is shifted out on the 7M + clock rather than the 14M. + */ + void output_fat_low_resolution(uint8_t *target, const uint8_t *source, size_t length, int column, int row) const; + // Maintain a ClockDeferrer for delayed mode switches. ClockDeferrer deferrer_; }; @@ -368,6 +377,7 @@ template class Video: public VideoBase { case GraphicsMode::Text: case GraphicsMode::DoubleText: case GraphicsMode::LowRes: + case GraphicsMode::FatLowRes: case GraphicsMode::DoubleLowRes: { const uint16_t text_address = static_cast(((video_page()+1) * 0x400) + row_address); fetch_address = static_cast(text_address + column_); @@ -441,6 +451,15 @@ template class Video: public VideoBase { pixel_row); break; + case GraphicsMode::FatLowRes: + output_fat_low_resolution( + &pixel_pointer_[pixel_start * 14 + 7], + &base_stream_[static_cast(pixel_start)], + static_cast(pixel_end - pixel_start), + pixel_start, + pixel_row); + break; + case GraphicsMode::DoubleLowRes: output_double_low_resolution( &pixel_pointer_[pixel_start * 14], @@ -548,7 +567,9 @@ template class Video: public VideoBase { if(high_resolution_) { return (annunciator_3_ && columns_80_) ? GraphicsMode::DoubleHighRes : GraphicsMode::HighRes; } else { - return annunciator_3_ ? GraphicsMode::DoubleLowRes : GraphicsMode::LowRes; + if(columns_80_) return GraphicsMode::DoubleLowRes; + if(annunciator_3_) return GraphicsMode::FatLowRes; + return GraphicsMode::LowRes; } } From 8d79a1e381afee7cdda469415b3e258fca07e22e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 9 Sep 2018 10:06:21 -0400 Subject: [PATCH 3/3] Corrected fat low-res implementation. As per comment of awanderin that "the odd addresses don't get their pixels auto-shifted by the hardware as with normal lo-res". --- Machines/AppleII/Video.cpp | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/Machines/AppleII/Video.cpp b/Machines/AppleII/Video.cpp index 0a6430e3b..f1cd5f4b1 100644 --- a/Machines/AppleII/Video.cpp +++ b/Machines/AppleII/Video.cpp @@ -246,21 +246,13 @@ void VideoBase::output_low_resolution(uint8_t *target, const uint8_t *const sour void VideoBase::output_fat_low_resolution(uint8_t *target, const uint8_t *const source, size_t length, int column, int row) const { const int row_shift = row&4; for(size_t c = 0; c < length; ++c) { - // Low-resolution graphics mode shifts the colour code on a loop, but has to account for whether this - // 14-sample output window is starting at the beginning of a colour cycle or halfway through. - if((column + static_cast(c))&1) { - target[0] = target[1] = target[8] = target[9] = (source[c] >> row_shift) & 4; - target[2] = target[3] = target[10] = target[11] = (source[c] >> row_shift) & 8; - target[4] = target[5] = target[12] = target[13] = (source[c] >> row_shift) & 1; - target[6] = target[7] = (source[c] >> row_shift) & 2; - graphics_carry_ = (source[c] >> row_shift) & 8; - } else { - target[0] = target[1] = target[8] = target[9] = (source[c] >> row_shift) & 1; - target[2] = target[3] = target[10] = target[11] = (source[c] >> row_shift) & 2; - target[4] = target[5] = target[12] = target[13] = (source[c] >> row_shift) & 4; - target[6] = target[7] = (source[c] >> row_shift) & 8; - graphics_carry_ = (source[c] >> row_shift) & 2; - } + // Fat low-resolution mode appears not to do anything to try to make odd and + // even columns compatible. + target[0] = target[1] = target[8] = target[9] = (source[c] >> row_shift) & 1; + target[2] = target[3] = target[10] = target[11] = (source[c] >> row_shift) & 2; + target[4] = target[5] = target[12] = target[13] = (source[c] >> row_shift) & 4; + target[6] = target[7] = (source[c] >> row_shift) & 8; + graphics_carry_ = (source[c] >> row_shift) & 4; target += 14; } }