mirror of
https://github.com/TomHarte/CLK.git
synced 2024-08-24 23:29:00 +00:00
Merge pull request #541 from TomHarte/Annunciator3
Implements the two undocumented annunciator 3 graphics modes
This commit is contained in:
commit
5a5fc1ae1a
@ -534,7 +534,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
|||||||
#undef IIeSwitchRead
|
#undef IIeSwitchRead
|
||||||
|
|
||||||
case 0xc07f:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -611,7 +611,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
|||||||
analogue_charge_ = 0.0f;
|
analogue_charge_ = 0.0f;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
/* Read-write switches. */
|
/* Switches triggered by reading or writing. */
|
||||||
case 0xc050:
|
case 0xc050:
|
||||||
case 0xc051:
|
case 0xc051:
|
||||||
update_video();
|
update_video();
|
||||||
@ -636,7 +636,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
|||||||
case 0xc05f:
|
case 0xc05f:
|
||||||
if(is_iie()) {
|
if(is_iie()) {
|
||||||
update_video();
|
update_video();
|
||||||
video_->set_double_high_resolution(!(address&1));
|
video_->set_annunciator_3(!(address&1));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -131,15 +131,16 @@ bool VideoBase::get_high_resolution() {
|
|||||||
return set_high_resolution_;
|
return set_high_resolution_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoBase::set_double_high_resolution(bool double_high_resolution) {
|
void VideoBase::set_annunciator_3(bool annunciator_3) {
|
||||||
set_double_high_resolution_ = double_high_resolution;
|
set_annunciator_3_ = annunciator_3;
|
||||||
deferrer_.defer(Cycles(2), [=] {
|
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() {
|
bool VideoBase::get_annunciator_3() {
|
||||||
return set_double_high_resolution_;
|
return set_annunciator_3_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoBase::set_character_rom(const std::vector<uint8_t> &character_rom) {
|
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 {
|
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;
|
const int row_shift = row&4;
|
||||||
for(size_t c = 0; c < length; ++c) {
|
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) {
|
for(size_t c = 0; c < length; ++c) {
|
||||||
// High resolution graphics shift out LSB to MSB, optionally with a delay of half a pixel.
|
// 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 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[0] = graphics_carry_;
|
||||||
target[1] = target[2] = source[c] & 0x01;
|
target[1] = target[2] = source[c] & 0x01;
|
||||||
target[3] = target[4] = source[c] & 0x02;
|
target[3] = target[4] = source[c] & 0x02;
|
||||||
|
@ -128,18 +128,19 @@ class VideoBase {
|
|||||||
bool get_high_resolution();
|
bool get_high_resolution();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Setter for DHIRES ($C05E/$C05F; triggers on write only).
|
Setter for annunciator 3.
|
||||||
|
|
||||||
* On: turn on double-high resolution.
|
* On: turn on annunciator 3.
|
||||||
* Off: turn off double-high resolution.
|
* Off: turn off annunciator 3.
|
||||||
|
|
||||||
DHIRES doesn't exist on a II/II+. On the IIe there is another
|
This exists on both the II/II+ and the IIe, but has no effect on
|
||||||
register usually grouped with the graphics setters called IOUDIS
|
video on the older machines. It's intended to be used on the IIe
|
||||||
that affects visibility of this switch. But it has no effect on
|
to confirm double-high resolution mode but has side effects in
|
||||||
video, so it's not modelled by this class.
|
selecting mixed mode output and discarding high-resolution
|
||||||
|
delay bits.
|
||||||
*/
|
*/
|
||||||
void set_double_high_resolution(bool);
|
void set_annunciator_3(bool);
|
||||||
bool get_double_high_resolution();
|
bool get_annunciator_3();
|
||||||
|
|
||||||
// Setup for text mode.
|
// Setup for text mode.
|
||||||
void set_character_rom(const std::vector<uint8_t> &);
|
void set_character_rom(const std::vector<uint8_t> &);
|
||||||
@ -158,14 +159,15 @@ class VideoBase {
|
|||||||
|
|
||||||
// Enumerates all Apple II and IIe display modes.
|
// Enumerates all Apple II and IIe display modes.
|
||||||
enum class GraphicsMode {
|
enum class GraphicsMode {
|
||||||
LowRes = 0,
|
Text = 0,
|
||||||
DoubleLowRes,
|
DoubleText,
|
||||||
HighRes,
|
HighRes,
|
||||||
DoubleHighRes,
|
DoubleHighRes,
|
||||||
Text,
|
LowRes,
|
||||||
DoubleText,
|
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); }
|
bool is_double_mode(GraphicsMode m) { return !!(static_cast<int>(m)&1); }
|
||||||
|
|
||||||
// Various soft-switch values.
|
// Various soft-switch values.
|
||||||
@ -176,13 +178,14 @@ class VideoBase {
|
|||||||
bool text_ = true, set_text_ = true;
|
bool text_ = true, set_text_ = true;
|
||||||
bool mixed_ = false, set_mixed_ = false;
|
bool mixed_ = false, set_mixed_ = false;
|
||||||
bool high_resolution_ = false, set_high_resolution_ = 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;
|
// Graphics carry is the final level output in a fetch window;
|
||||||
// it carries on into the next if it's high resolution with
|
// it carries on into the next if it's high resolution with
|
||||||
// the delay bit set.
|
// the delay bit set.
|
||||||
mutable uint8_t graphics_carry_ = 0;
|
mutable uint8_t graphics_carry_ = 0;
|
||||||
bool was_double_ = false;
|
bool was_double_ = false;
|
||||||
|
uint8_t high_resolution_mask_ = 0xff;
|
||||||
|
|
||||||
// This holds a copy of the character ROM. The regular character
|
// This holds a copy of the character ROM. The regular character
|
||||||
// set is assumed to be in the first 64*8 bytes; the alternative
|
// 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;
|
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.
|
// Maintain a ClockDeferrer for delayed mode switches.
|
||||||
ClockDeferrer<Cycles> deferrer_;
|
ClockDeferrer<Cycles> deferrer_;
|
||||||
};
|
};
|
||||||
@ -366,6 +377,7 @@ template <class BusHandler, bool is_iie> class Video: public VideoBase {
|
|||||||
case GraphicsMode::Text:
|
case GraphicsMode::Text:
|
||||||
case GraphicsMode::DoubleText:
|
case GraphicsMode::DoubleText:
|
||||||
case GraphicsMode::LowRes:
|
case GraphicsMode::LowRes:
|
||||||
|
case GraphicsMode::FatLowRes:
|
||||||
case GraphicsMode::DoubleLowRes: {
|
case GraphicsMode::DoubleLowRes: {
|
||||||
const uint16_t text_address = static_cast<uint16_t>(((video_page()+1) * 0x400) + row_address);
|
const uint16_t text_address = static_cast<uint16_t>(((video_page()+1) * 0x400) + row_address);
|
||||||
fetch_address = static_cast<uint16_t>(text_address + column_);
|
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);
|
pixel_row);
|
||||||
break;
|
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:
|
case GraphicsMode::DoubleLowRes:
|
||||||
output_double_low_resolution(
|
output_double_low_resolution(
|
||||||
&pixel_pointer_[pixel_start * 14],
|
&pixel_pointer_[pixel_start * 14],
|
||||||
@ -539,14 +560,16 @@ template <class BusHandler, bool is_iie> class Video: public VideoBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
GraphicsMode graphics_mode(int row) {
|
GraphicsMode graphics_mode(int row) {
|
||||||
if(text_) return columns_80_ ? GraphicsMode::DoubleText : GraphicsMode::Text;
|
if(
|
||||||
if(mixed_ && row >= 160 && row < 192) {
|
text_ ||
|
||||||
return (columns_80_ || double_high_resolution_) ? GraphicsMode::DoubleText : GraphicsMode::Text;
|
(mixed_ && row >= 160 && row < 192)
|
||||||
}
|
) return columns_80_ ? GraphicsMode::DoubleText : GraphicsMode::Text;
|
||||||
if(high_resolution_) {
|
if(high_resolution_) {
|
||||||
return double_high_resolution_ ? GraphicsMode::DoubleHighRes : GraphicsMode::HighRes;
|
return (annunciator_3_ && columns_80_) ? GraphicsMode::DoubleHighRes : GraphicsMode::HighRes;
|
||||||
} else {
|
} else {
|
||||||
return double_high_resolution_ ? GraphicsMode::DoubleLowRes : GraphicsMode::LowRes;
|
if(columns_80_) return GraphicsMode::DoubleLowRes;
|
||||||
|
if(annunciator_3_) return GraphicsMode::FatLowRes;
|
||||||
|
return GraphicsMode::LowRes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user