1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-27 01:31:42 +00:00

Merge pull request #541 from TomHarte/Annunciator3

Implements the two undocumented annunciator 3 graphics modes
This commit is contained in:
Thomas Harte 2018-09-09 10:06:52 -04:00 committed by GitHub
commit 5a5fc1ae1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 70 additions and 30 deletions

View File

@ -534,7 +534,7 @@ template <Analyser::Static::AppleII::Target::Model model> 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 <Analyser::Static::AppleII::Target::Model model> 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 <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
case 0xc05f:
if(is_iie()) {
update_video();
video_->set_double_high_resolution(!(address&1));
video_->set_annunciator_3(!(address&1));
}
break;

View File

@ -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<uint8_t> &character_rom) {
@ -242,6 +243,20 @@ 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) {
// 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;
}
}
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) {
@ -276,7 +291,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;

View File

@ -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<uint8_t> &);
@ -158,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<int>(m)&1); }
// Various soft-switch values.
@ -176,13 +178,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
@ -236,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<Cycles> deferrer_;
};
@ -366,6 +377,7 @@ template <class BusHandler, bool is_iie> 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<uint16_t>(((video_page()+1) * 0x400) + row_address);
fetch_address = static_cast<uint16_t>(text_address + column_);
@ -439,6 +451,15 @@ template <class BusHandler, bool is_iie> class Video: public VideoBase {
pixel_row);
break;
case GraphicsMode::FatLowRes:
output_fat_low_resolution(
&pixel_pointer_[pixel_start * 14 + 7],
&base_stream_[static_cast<size_t>(pixel_start)],
static_cast<size_t>(pixel_end - pixel_start),
pixel_start,
pixel_row);
break;
case GraphicsMode::DoubleLowRes:
output_double_low_resolution(
&pixel_pointer_[pixel_start * 14],
@ -539,14 +560,16 @@ template <class BusHandler, bool is_iie> 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;
if(columns_80_) return GraphicsMode::DoubleLowRes;
if(annunciator_3_) return GraphicsMode::FatLowRes;
return GraphicsMode::LowRes;
}
}